I have been reading everything I can on an Expand Down stack in
Protected Mode.
However, no matter what I try, when I change my code to an Expand Down
stack,
it always reboots when it reaches Step 10. In my code, I have 2
questions -
Question 1 - When I change the GDT Descriptor in Question 2, should
I change any of these three stack definition
instructions?
Question 2 - What should a GDT Descriptor be for an Expand Down,
32-bit, 4KB Granular, Protected mode Stack?
My code is a simple floppy diskette bootsector/bootloader that always
works properly with an Expand Up, 32-bit, 4KB Granular, Protected mode
Stack Descriptor(0x0018). Basically, the code -
1. Disables interupts
2. Sets up a Global Descriptor Table
3. Enables Protected mode
4. Enables 32-bit coding
5. Defines an Expand Up, 32-bit, 4KB Granual, Protected mode stack
6. Re-enables Real mode (however, since the computer is
still using 32-bits, we're actually in 'unreal' mode)
7. Enables the A20 line for high RAM
8. Re-enables interupts
9. Loads al and edx with some ASCII values for the screen
10. Pushes and Pops ebx(a 32-bit register
to make sure the 32-bit stack works)
11. Sends al and edx directly to the Text Screen
12. Re-enables Protected mode
13. Re-defines a 16-bit, Protected mode, Expand Up stack
14. Re-enables 16-bit coding
15. Re-enables Real mode
16. Re-defines a Real mode stack
17. Performs an endless loop
However, whenever I change GDT Descriptor 0x0018 Byte [5]
from Expand Up (0x92) to Expand Down (0x96), and run the
code, the computer reboots when it gets to the push ebx
instruction in Step 10. I have changed the Base and Limit,
in Descriptor 0x0018, to dozens of different values. I
have also changed the values in Step 5 many times. But
no matter what I try, the computer always reboots when
it reaches the push ebx instruction in Step 10.
I'm hoping that someone that has experience with setting
up an Expand Down stack in Protected mode will correct
my code so it will work properly.
Thank you in advance for your time and work.
- code start -
; compiled using nasm -
; C:\NASM.EXE -f bin C:\BOOTLOAD.asm -l C:\BOOTLOAD.lst -o C:
\BOOTLOAD.bin
cpu 586
bits 16
org 0x7c00
section .text align=16
jmp short loc0010
; These 8 db lines are a Header File for Microsoft compatability
-------------
db 0x90,0x42,0x4f,0x4f,0x54,0x4c,0x4f,0x44
db 0x52,0x00,0x02,0x01,0x01,0x00,0x02,0xE0
db 0x00,0x40,0x0B,0xF0,0x09,0x00,0x12,0x00
db 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00
db 0x00,0x00,0x00,0x00,0x29,0x20,0x42,0x4f
db 0x4f,0x54,0x4c,0x4f,0x44,0x52,0x20,0x20
db 0x20,0x20,0x20,0x20,0x46,0x41,0x54,0x31
db 0x32,0x20,0x20,0x20,0x90,0x90
; End 8 db lines
-------------------------------------------------------------
loc0010
; Delay for 3 seconds so floppy drive
----------------------------------------
; can spin down before we disable interupts
xor cx,cx
xor dx,dx
mov byte ah,0x01
int 0x001A
loc0020 xor ah,ah
int 0x001A
cmp word dx,0x0037 ; 0x0037 = 55 clock ticks (3
secs)
jl loc0020
; End Delay
------------------------------------------------------------------
jmp word 0x0000:loc0030 ; set cs = 0x0000
loc0030
mov word ax,cs ; ax = 0x0000
mov word ds,ax ; ds = 0x0000
mov word es,ax ; es = 0x0000
mov word fs,ax ; fs = 0x0000
mov word gs,ax ; gs = 0x0000
cli ; disable Interupts and ...
mov word dx,0x0070
in byte al,dx
or byte al,0x80 ; disable Non-Maskable
out word dx,al ; Hardware Interupts
; For those learning -
; The lgdt [n300010] instruction loads a pointer to information ABOUT
; the GDT, (basically, the GDT's size minus 1, and the GDT's linear
address),
; into the GDT Register (GDTR).
; In other words, lgdt does NOT load a pointer to the GDT itself.
lgdt [n300030]
mov dword eax,cr0 ; set bit 0, the first bit of
cr0,
or byte al,0x01 ; to 1 to disable real mode,
and
mov dword cr0,eax ; to enable Protected Mode
; The jmp dword 0x0008:loc0040 sets CS = GDT Descriptor 0x0008,
; (Descriptor 0x0008 in the GDT engages 32bit coding), and far jumps
; into 32bit code, (which also clears pipeline/prefetch queue/cache).
jmp dword 0x0008:loc0040
bits 32
section .text align=32
; Now in 32bit coding
loc0040
mov dword eax,0x00000010 ; Set ax and segment registers
mov word ds,ax ; to Descriptor 0x0010 in the
GDT
mov word es,ax
mov word fs,ax
mov word gs,ax
; QUESTION 1 - WHEN I CHANGE THE GDT STACK DESCRIPTOR 0x0018 (IN
QUESTION 2)
; TO EXPAND DOWN, SHOULD ANY OF THESE THREE INSTRUCTIONS
CHANGE?
mov dword eax,0x00000018 ; Set ax and Stack segment
register
mov word ss,ax ; to Descriptor 0x0018 in the
GDT
mov dword esp,0x00000010 ; which allows us to use 32bit
esp...
; for a visual reference, the
Stack
; Selector is in the Text
Screen RAM
; End QUESTION 1
-------------------------------------------------------------
mov dword eax,cr0
and dword eax,0xfffffffe ; reset bit 0, the first bit
of cr0,
; to 0 to disable Protected
Mode
mov dword cr0,eax ; and enable 'unreal' mode.
; Now in 32bit 'unreal' mode
mov byte al,0xd1 ; enable the A20 line for high
memory
out byte 0x64,al
mov byte al,0x03
out byte 0x60,al
in byte al,0x70 ; re-enable Non-Maskable
and byte al,0x7f ; Hardware Interupts and ...
out byte 0x70,al
sti ; ... re-enable Interupts
; Simple test code to make sure everything is working
========================
; properly in 32bit flat real (unreal) mode.
mov byte al,'Y' ; al = 'Y'
mov dword esi,0x002fff00 ; esi = 0x002fff00
mov dword edx,0x20412031 ; edx = '1A' with a green
background
mov dword [esi],edx ; [0x002fff00] = 0x20 0x41
0x20 0x31
mov dword ebx,[esi] ; ebx = 0x31204120
; NOTE 1 - THIS PUSH EBX INSTRUCTION MAKES MY COMPUTER TRIPLE FAULT/
REBOOT ---
; WHEN I CHANGE THE GDT STACK DESCRIPTOR 0x0018 TO EXPAND
DOWN.
push ebx ; push the 32bit register ebx
; End NOTE 1
-----------------------------------------------------------------
pop ebx ; and pop it again to make
sure all
; 32bits got pushed and popped
cmp dword ebx,0x20412031 ; Verify all 32bits are still
in ebx
je loc0070 ; All 32bits are in ebx
mov byte al,'N' ; A problem with ebx losing
some bits
; of it's data has occured
loc0070
mov byte [0x000B8da0],al ; Store a Y or N on the screen
; Y if everything worked
properly
; N if there was a problem
mov dword [0x000b8da8],edx ; Store 1A with a green
background
; End Simple Test
============================================================
; Go back to 16bit code and stack segments so
; the BIOS Interupts will work properly again
mov dword eax,cr0 ; set bit 0, the first bit of
cr0,
or byte al,0x01 ; to 1 to disable 'unreal'
mode, and
mov dword cr0,eax ; to enable Protected Mode
mov dword eax,0x00000028 ; Load the 16bit stack
Descriptor
mov word ss,ax ; into the ss register
jmp dword 0x0020:loc0080
bits 16
section .text align=16
; Now back in 16bit coding
loc0080
mov dword eax,cr0
and word ax,0xfffe ; reset bit 0, the first bit
of cr0,
; to 0 to disable Protected
Mode
mov dword cr0,eax ; again, and re-enable the
original
; real mode that the CPU
booted with
jmp word 0x0000:loc0090 ; Set cs = 0x0000 and far jump
; to the next instruction
loc0090
xor word ax,ax ; store 0x0000 in ax
mov word ds,ax ; and the segment registers
mov word es,ax
mov word fs,ax
mov word gs,ax
mov word ax,0xb000 ; store 0xb000 (the Text
Screen RAM
mov word ss,ax ; segment) in ax and the Stack
segment
mov word sp,0x87ce ; and set sp to the middle of
the
; Text Screen so we can see
the stack
loc00a0
jmp loc00a0 ; endless loop
n300010 equ $ - $$
section .data align=4
; Global Descriptor Table
; Descriptor format of 0x0008 - 0000 0000 0000 1000
; 00 - Privilege level
; 0 - GDT
; 0000000000001 - GDT Descriptor
Index
; DESCRIPTOR SIZE
TYPE
n800010 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; 0x0000 -
null
n800020 db 0xff,0xff,0x00,0x00,0x00,0x9c,0xcf,0x00 ; 0x0008 32bit
code
n800030 db 0xff,0xff,0x00,0x00,0x00,0x92,0xcf,0x00 ; 0x0010 32bit
data
; QUESTION 2 - WHAT SHOULD THE 8 BYTES, IN THIS GDT STACK DESCRIPTOR,
--------
; BE SET TO FOR AN EXPAND DOWN(EXPAND BIT SET),
; 4KB(GRANULARITY BIT SET), 32BIT(BIG BIT SET), STACK?
n800040 db 0xb9,0x00,0xce,0x82,0x0b,0x92,0xc0,0x00 ; 0x0018 32bit
stack
; End QUESTION 2
-------------------------------------------------------------
n800050 db 0xff,0xff,0x00,0x00,0x00,0x98,0x00,0x00 ; 0x0020 16bit
code
n800060 db 0xff,0xff,0x00,0x00,0x00,0x92,0x00,0x00 ; 0x0028 16bit
stack
; BYTE - 0 1 2 3 4 [5] [6] 7
; - -- -bits- - - - -bits -
; BYTE[5] 7 65 4 3 2 1 0 BYTE[6] 7 6 5 4
; CODE 1 00 1 1 C 1 0 CODE G D 0 0
; DATA 1 00 1 0 0 1 0 DATA G D 0 0
; STACK 1 00 1 0 E 1 0 STACK G B 0 0
; C-Conforming bit E-Expand bit G-Granularity bit D-Default bit B-
Big bit
n300020 equ $ - n800010 - 1 ; size of the GDT minus 1
; The size and linear address of the Global Descriptor Table
n300030 dw n300020 ; size of the GDT minus 1
dd n800010 ; 32bit physical address of
the GDT
n300040 equ 0x01fe - ($ - $$) - n300010 ; FILLER CALCULATION
; Because of varying
alignments in
; section .text, this formula
is not
; exact. It is supposed to
; calculate number of Bytes
needed for
; filler (512 Bytes minus the
; Total size of all code)
times n300040 db 0xfc ; I use 0xfc as a harmless
filler
; 0xfc = cld (clear direction
flag)
db 0x55,0xaa ; 55 aa (bootsector
convention)
- code end -
1. your stack seem to be very small:
ESP = 0x10h but limited to 0xB9 ? (Little Endians ...)
and what's wrong with a type 92h stack ?
"expand down" only reverses the means of 'segment limit'
and you start "already below limit" when using expand down !
Stack will always grow downwards on x86 CPUs.
2. one classical bug beside the problem:
after any write to port 70h (RTCL-Index)
port 71h must be either read or written.
3. the A20-ON may work different and I'd check if it happened.
4. I think enabling IRQs in 32-bit unreal mode will also cause
you some problems.
BTW: if your posted source is for NASM,
then you got lots of redundant size casts in there.
You could shorten your code:
"MOV DS,eax" will anyway do what you want.
The shorter forms of IN/OUT work also for OUT 0x70,al IN al,0x71.
"AND AL,0xFE" would be enough to clear bit0 of eax ;)
What do you expect to see when you write to 0x002fff00 ?
the screen RAM in default text mode starts at 0x000B8000.
You probably meant to check if A20 is on with this ?
...
> ; QUESTION 2 - WHAT SHOULD THE 8 BYTES, IN THIS GDT STACK DESCRIPTOR,
> --------
> ; BE SET TO FOR AN EXPAND DOWN(EXPAND BIT SET),
> ; 4KB(GRANULARITY BIT SET), 32BIT(BIG BIT SET), STACK?
>
> n800040 db 0xb9,0x00,0xce,0x82,0x0b,0x92,0xc0,0x00 ; 0x0018 32bit
> stack
> ; End QUESTION 2
...
Answer: 0, 1 limit bits 0..15 (top if type 92h , bottom if 96h)
2..4 base bits 0..23
5 92h for normal data/stack 96h for expand down
6 C0h + limit bits 16..19
7 base bits 24..31
I really wont recommend at all to have the stack in the Video-RAM,
because VRAM-readback is awful slow, but it might work for your test.
__
wolfgang
if you like make this a .htm and print it out:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<!--
translated from page 366 from the "Holy Book of KESYS" Jan.1999,
Author: Wolfgang Kern, Vienna Austria (LEOC, KESYS-development) >
<title>x86descriptors</title>
<style type="text/css">
<!--
tr {nowrap; font-weight:thin; font-family:Courier New; font-size:11pt; }
td {nowrap; align:center; }
-->
</style>
</head>
<body bgcolor="#FFFFFF" text="#000000" align="left">
<basefont face="Courier New">
<basefont size="1">
<u>IA32 Descriptors</caption></u>
<table border="1" frame="box" rules="all" bgcolor="#FFFFFF" height="200"
cellspacing="0" cellpadding="0" bordercolor="#808080">
<colgroup>
<col width="8">
<col width="24" align="middle" span=8>
<col width="200" align="left" valign="top">
</colgtroup>
<tr> <td>7</td>
<td colspan="8">BASE 31..24</td>
<td rowspan="9">
<pre><b><u>
DATA [93][GDT,LDT]</b></u>
G 4Kb granular limit
B 32-bit stack
P present
E expand down (stack)
W writable
A accessed</pre>
</td>
</tr>
<tr> <td>6</td>
<td><b>G</td>
<td><b>B</td>
<td><b>0</td>
<td>x</td>
<td nowrap colspan="4">LIM 19..16</td> </tr>
<tr> <td>5</td>
<td>P</td>
<td colspan="2">DPL</td>
<td colspan="2" style="border-width:medium;border-color:#000000;
border-style:double;">
<b>1 0</td>
<td><b>E</td>
<td><b>W</td>
<td><b>A</td> </tr>
<tr> <td>4</td>
<td rowspan="3" colspan="8">BASE 0..23</td> </tr>
<tr> <td>3</td></tr>
<tr> <td>2</td></tr>
<tr> <td>1</td>
<td rowspan="2" colspan="8">LIMIT 0..15</td> </tr>
<tr><td>0</td></tr>
<tr style="font-size:7pt;">
<td></td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
<td>1</td>
<td>0</td> </tr>
</table>
<br>
<table border="1" frame="box" rules="all" bgcolor="#FFFFFF" height="200"
cellspacing="0" cellpadding="0" bordercolor="#808080">
<colgroup>
<col width="8">
<col width="24" align="middle" span=8>
<col width="200" align="left" valign="top">
</colgtroup>
<tr> <td>7</td>
<td colspan="8">BASE 31..24</td>
<td rowspan="9">
<pre><b><u>
CODE [9b][GDT,LDT]</u></b>
G 4Kb granular
B 32-bit
P present
C confirming
R readable
A accessed</pre>
</td></tr>
<tr> <td>6</td>
<td><b>G</td>
<td><b>B</td>
<td><b>0</td>
<td>x</td>
<td nowrap colspan="4">LIM 19..16</td> </tr>
<tr> <td>5</td>
<td>P</td>
<td colspan="2">DPL</td>
<td colspan="2" style="border-width:medium;border-color:#000000;
border-style:double; padding:0px;"><b>1 1</td>
<td><b>C</td>
<td><b>R</td>
<td><b>A</td> </tr>
<tr> <td>4</td>
<td rowspan="3" colspan="8">BASE 0..23</td> </tr>
<tr> <td>3</td></tr>
<tr> <td>2</td></tr>
<tr> <td>1</td>
<td rowspan="2" colspan="8">LIMIT 0..15</td> </tr>
<tr><td>0</td></tr>
<tr style="font-size:7pt;">
<td></td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
<td>1</td>
<td>0</td> </tr>
</table>
<br>
<table border="1" frame="box" rules="all" bgcolor="#ffffff" height="200"
cellspacing="0" cellpadding="0" bordercolor="#808080">
<colgroup>
<col width="8">
<col width="24" align="middle" span=8>
<col width="200" align="left" valign="top">
</colgtroup>
<tr> <td>7</td>
<td colspan="8">BASE 31..24</td>
<td rowspan="9"><pre><u><b>
TASK-switch [81/89]</u>
[GDT]</b>
G 4Kb granular limit
P present
BT 32-bit
BS task is busy</pre> </td></tr>
<tr> <td>6</td>
<td><b>G</td>
<td><b>0</td>
<td><b>0</td>
<td>x</td>
<td nowrap colspan="4">LIM 19..16</td> </tr>
<tr> <td>5</td>
<td>P</td>
<td colspan="2">DPL</td>
<td
style="border-color:#000000;border-width:medium;border-style:double"><b>0</t
d>
<td><b>BT</td>
<td colspan= "3"
style="border-color:#000000;border-width:medium;border-style:double">
<b>0 BS 1</td>
</tr>
<tr> <td>4</td>
<td rowspan="3" colspan="8">BASE 0..23</td> </tr>
<tr> <td>3</td></tr>
<tr> <td>2</td></tr>
<tr> <td>1</td>
<td rowspan="2" colspan="8">LIMIT 0..15</td> </tr>
<tr><td>0</td></tr>
<tr style="font-size:7pt;">
<td></td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
<td>1</td>
<td>0</td> </tr>
</table>
<br>
<table border="1" frame="box" rules="all" bgcolor="#ffffff" height="200"
cellspacing="0" cellpadding="0" bordercolor="#808080">
<colgroup>
<col width="8">
<col width="24" align="middle" span=8>
<col width="200" align="left" valign="top">
</colgtroup>
<tr> <td>7</td>
<td rowspan="2" colspan="8">Linear Address 31..16</td>
<td rowspan="9"><pre><u><b>
LDT [82][GDT]</b></u></td></tr>
<tr> <td>6</td> </tr>
<tr> <td>5</td>
<td>P</td>
<td colspan="2">--</td>
<td style="border-color:#000000;border-width:medium;border-style:double">
<b>0</td>
<td><b>0</td>
<td colspan= "3"
style="border-color:#000000;border-width:medium;border-style:double">
<b>0 1 0</td>
</tr>
<tr> <td>4</td>
<td colspan="8">reserved</td> </tr>
<tr> <td>3</td>
<td colspan="8">SEGMENT-</td> </tr>
<tr> <td>2</td>
<td colspan="5">SELECTOR</td>
<td>x</td>
<td colspan="2">RPL</td> </tr>
<tr> <td>1</td>
<td rowspan="2" colspan="8">Linear Address 15..0</td> </tr>
<tr><td>0</td></tr>
<tr style="font-size:7pt;">
<td></td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
<td>1</td>
<td>0</td> </tr>
</table>
<br>
<br>
<table border="1" frame="box" rules="all" bgcolor="#ffffff" height="200"
cellspacing="0" cellpadding="0" bordercolor="#808080">
<colgroup>
<col width="8">
<col width="24" align="middle" span=8>
<col width="200" align="left" valign="top">
</colgtroup>
<tr> <td>7</td>
<td rowspan="2" colspan="8">Offset 31..16</td>
<td rowspan="9"><pre><u><b>
Call-GATE [84/8c]
[GDT,LDT]</b></u>
T 32-bit
L LDT (else GDT)
Dwords copied from
callers stack.
</td></tr>
<tr> <td>6</td> </tr>
<tr> <td>5</td>
<td>P</td>
<td colspan="2">DPL</td>
<td style="border-color:#000000;border-width:medium;border-style:double">
<b>0</td>
<td><b>T</td>
<td colspan= "3"
style="border-color:#000000;border-width:medium;border-style:double">
<b>1 0 0</td>
</tr>
<tr> <td>4</td>
<td colspan="4">0 0 0 0</td>
<td colspan="4">Dwords </td>
</tr>
<tr> <td>3</td>
<td colspan="8">SEGMENT-</td> </tr>
<tr> <td>2</td>
<td colspan="5">SELECTOR</td>
<td>L</td>
<td colspan="2">RPL</td> </tr>
<tr> <td>1</td>
<td rowspan="2" colspan="8">Offset 15..0</td> </tr>
<tr><td>0</td></tr>
<tr style="font-size:7pt;">
<td></td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
<td>1</td>
<td>0</td> </tr>
</table>
<br>
<table border="1" frame="box" rules="all" bgcolor="#ffffff" height="200"
cellspacing="0" cellpadding="0" bordercolor="#808080">
<colgroup>
<col width="8">
<col width="24" align="middle" span=8>
<col width="200" align="left" valign="top">
</colgtroup>
<tr> <td>7</td>
<td rowspan="2" colspan="8">reserved</td>
<td rowspan="9"><pre><u><b>
TASK-GATE [85/8d]
[GDT,IDT,LDT]</b></u>
T: 32-bit
</td></tr>
<tr> <td>6</td> </tr>
<tr> <td>5</td>
<td>P</td>
<td colspan="2">DPL</td>
<td style="border-color:#000000;border-width:medium;border-style:double">
<b>0</td>
<td><b>T</td>
<td colspan= "3"
style="border-color:#000000;border-width:medium;border-style:double">
<b>1 0 1</td>
</tr>
<tr> <td>4</td>
<td colspan="8">reserved</td> </tr>
<tr> <td>3</td>
<td colspan="8">SEGMENT-</td> </tr>
<tr> <td>2</td>
<td colspan="5">SELECTOR</td>
<td>x</td>
<td colspan="2">RPL</td> </tr>
<tr> <td>1</td>
<td rowspan="2" colspan="8">reserved</td> </tr>
<tr><td>0</td></tr>
<tr style="font-size:7pt;">
<td></td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
<td>1</td>
<td>0</td> </tr>
</table>
<br>
<table border="1" frame="box" rules="all" bgcolor="#ffffff" height="200"
cellspacing="0" cellpadding="0" bordercolor="#808080">
<colgroup>
<col width="8">
<col width="24" align="middle" span=8>
<col width="200" align="left" valign="top">
</colgtroup>
<tr> <td>7</td>
<td rowspan="2" colspan="8">Offset 31..16</td>
<td rowspan="9"><pre><u><b>
INT-GATE [86/8e][IDT]</b></u>
T 32-bit
disables IRQ,
TRAP and NT cleared
until IRET
</td></tr>
<tr> <td>6</td> </tr>
<tr> <td>5</td>
<td>P</td>
<td colspan="2">DPL</td>
<td style="border-color:#000000;border-width:medium;border-style:double">
<b>0</td>
<td><b>T</td>
<td colspan= "3"
style="border-color:#000000;border-width:medium;border-style:double">
<b>1 1 0</td>
</tr>
<tr> <td>4</td>
<td colspan="8">reserved</td> </tr>
<tr> <td>3</td>
<td colspan="8">SEGMENT-</td> </tr>
<tr> <td>2</td>
<td colspan="5">SELECTOR</td>
<td>x</td>
<td colspan="2">RPL</td> </tr>
<tr> <td>1</td>
<td rowspan="2" colspan="8">Offset 15..0</td> </tr>
<tr><td>0</td></tr>
<tr style="font-size:7pt;">
<td></td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
<td>1</td>
<td>0</td> </tr>
</table>
<br>
<table border="1" frame="box" rules="all" bgcolor="#ffffff" height="200"
cellspacing="0" cellpadding="0" bordercolor="#808080">
<colgroup>
<col width="8">
<col width="24" align="middle" span=8>
<col width="200" align="left" valign="top">
</colgtroup>
<tr> <td>7</td>
<td rowspan="2" colspan="8">Offset 31..16</td>
<td rowspan="9"><pre><u><b>
INT-TRAP [87/8f][IDT]</b></u>
T 32-bit
IRQ-status unchanged,
TRAP and NT cleared
until IRET
</td></tr>
<tr> <td>6</td> </tr>
<tr> <td>5</td>
<td>P</td>
<td colspan="2">DPL</td>
<td style="border-color:#000000;border-width:medium;border-style:double">
<b>0</td>
<td><b>T</td>
<td colspan= "3"
style="border-color:#000000;border-width:medium;border-style:double">
<b>1 1 1</td>
</tr>
<tr> <td>4</td>
<td colspan="8">reserved</td> </tr>
<tr> <td>3</td>
<td colspan="8">SEGMENT-</td> </tr>
<tr> <td>2</td>
<td colspan="5">SELECTOR</td>
<td>x</td>
<td colspan="2">RPL</td> </tr>
<tr> <td>1</td>
<td rowspan="2" colspan="8">Offset 15..0</td> </tr>
<tr><td>0</td></tr>
<tr style="font-size:7pt;">
<td></td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
<td>1</td>
<td>0</td> </tr>
</table>
<br>
</font>
</body>
</html>
I have read, and considered/acted on, what you suggested -
> 1. your stack seem to be very small:
> ESP = 0x10h but limited to 0xB9 ? (Little Endians ...)
> and what's wrong with a type 92h stack ?
> "expand down" only reverses the means of 'segment limit'
> and you start "already below limit" when using expand down !
> Stack will always grow downwards on x86 CPUs.
I assumed (incorrectly I guess) that stack growth direction could
be either upwards or downwards on x86 CPUs. Seeing as it can only
grow downwards, and an Expand Down segment can be enlarged downward
during run-time to allow for a larger stack (which grows downward),
why would someone want to have an Expand up segment for a stack?
That aside, I found one of my mistakes in the Question 1 portion of
my original code -
I just needed to change the 'mov dword esp,0x00000010'
to 'mov dword esp,0xfffffff0', as the stack pointer
is an offset of, I'm guessing here, the Base portion of
the segment descriptor.
> 2. one classical bug beside the problem:
> after any write to port 70h (RTCL-Index)
> port 71h must be either read or written.
Although a search of my limited documents did not describe any bug
by not accessing port 0x71 after accessing port 0x70, I decided
to be safe, and added a read to port 0x71.
> 3. the A20-ON may work different and I'd check if it happened.
> What do you expect to see when you write to 0x002fff00 ?
> the screen RAM in default text mode starts at 0x000B8000.
> You probably meant to check if A20 is on with this ?
Since you answered your own question here, I won't have to :)
> 4. I think enabling IRQs in 32-bit unreal mode will also cause
> you some problems.
Oops, you're absolutely right. I modified the code accordingly.
> BTW: if your posted source is for NASM,
> then you got lots of redundant size casts in there.
> You could shorten your code:
> "MOV DS,eax" will anyway do what you want.
> The shorter forms of IN/OUT work also for OUT 0x70,al IN al,0x71.
> "AND AL,0xFE" would be enough to clear bit0 of eax ;)
Since I'm obviously new to assembly language programming on the x86,
I decided to include size casts in my code for learning/debugging
purposes. Just a personal preference, I guess.
>> ; QUESTION 2 - WHAT SHOULD THE 8 BYTES, IN THIS GDT STACK DESCRIPTOR,
>> --------
>> ; BE SET TO FOR AN EXPAND DOWN(EXPAND BIT SET),
>> ; 4KB(GRANULARITY BIT SET), 32BIT(BIG BIT SET), STACK?
>> n800040 db 0xb9,0x00,0xce,0x82,0x0b,0x92,0xc0,0x00 ; 0x0018 32bit
>> stack
>> ; End QUESTION 2
> Answer: 0, 1 limit bits 0..15 (top if type 92h , bottom if 96h)
> 2..4 base bits 0..23
> 5 92h for normal data/stack 96h for expand down
> 6 C0h + limit bits 16..19
> 7 base bits 24..31
Yep, I already had that information. I found my mistake, though.
I misunderstood what the Limit's purpose of the descriptor was.
The Limit is the *size* of the segment in 4KB increments
(when the Granularity bit is 1), not the upper/lower
physical address bound of the segment, like I orginally thought.
So a limit of 0x00000 equals a segment size of 4,096 Bytes,
0x00001 equals a segment size of 8,192 Bytes, etc.
Also, the Base portion of the Descriptor should be aligned to 16
Bytes,
so I changed the Base from 0x000b82ce to 0x000b82d0.
So I modified my code from -
n800040 db 0xb9,0x00,0xce,0x82,0x0b,0x92,0xc0,0x00 ; 0x0018 32bit
stack
to -
n800040 db 0x00,0x00,0xd0,0x82,0x0b,0x96,0xc0,0x00 ; 0x0018 32bit
stack
Everything now seems to be working properly with my 32-bit Expand Down
Protected mode stack.
> I really wont recommend at all to have the stack in the Video-RAM,
> because VRAM-readback is awful slow, but it might work for your test.
True. I wanted an easy way to verify the stack was working as I
intended it to.
Thanks again, wolfgang.
This is my newly modified floppy diskette bootsector/bootloader which
-
1. Disables standard Interupts and Non-Maskable Interupts
2. Enables the A20 line which allows access to RAM past the first 1MB
3. Loads the Global Descriptor Table Register
4. Disables 16-bit Real mode and enables 32-bit Protected mode
5. Enables 32-bit segments and an expand down 32-bit stack
6. Disables Protected mode and re-enables Real(now 32-bit 'unreal')
mode
7. Performs a simple test to verify that high RAM is accessable and
all
32-bit instructions/registers/stack are working properly -
a. Loads al and edx with some ASCII values for the screen
b. Stores edx in a high RAM location and
loads ebx from that same RAM location
b. Pushes and Pops ebx to verify
the 32-bit expand down stack is working
c. Sends al and ebx directly to the Text Screen
8. Disables Real(unreal) mode and re-enables Protected mode
9. Re-enables 16-bit segments and an expand up 16-bit stack
(To Do - find out what the original bootup stack values are
and have the 16-bit stack descriptor match them)
10. Re-enables 16-bit coding
11. Disables Protected mode and re-enables Real mode
12. Re-enables Non-Maskable Interupts and standard Interupts
13. Zeros out 16-bit segment registers and sets
the 16-bit stack to the center of the Text Screen
14. Performs an endless loop.
(Verifying the Interupts and stack are working properly
by pressing a key on the keyboard)
- code start -
; Assembled using nasm v.0.98.38 -
jmp short loc0010
in byte al,0x70
mov byte dl,al ; store al
in byte al,0x71 ; (port 0x70 bug patch)
mov byte al,dl ; restore al
or byte al,0x80 ; ... disable Non-Maskable
out byte 0x70,al ; Hardware Interupts
in byte al,0x71 ; (port 0x70 bug patch)
mov byte al,0xd1 ; enable the A20 line for high
memory
out byte 0x64,al ; access beyond the first 1MB
range
mov byte al,0x03
out byte 0x60,al
; For those learning -
; The lgdt [n300010] instruction does NOT load a pointer to the GDT
itself.
; lgdt loads a pointer to information ABOUT the GDT, (the GDT's size
minus 1,
; and the GDT's linear address), into the GDT Register (GDTR).
lgdt [n301010]
mov dword eax,cr0 ; set bit 0, the first bit of
cr0,
or byte al,0x01 ; to 1 to disable real mode,
and
mov dword cr0,eax ; to enable Protected Mode
; The jmp dword 0x0008:loc0040 sets CS = GDT Selector 0x0008,
; (Selector 0x0008 in the GDT enables 32bit coding), and far jumps
; into 32bit code. Far jumps are also performed after changing CPU
modes,
; and after changing alignment, to clear any residual instructions/
data in
; the pipeline/prefetch queue/cache.
jmp dword 0x0008:loc0040
bits 32
; Now coding in 32bit Protected mode
loc0040
mov dword eax,0x00000010 ; Set segment registers to
32bit
mov word ds,ax ; via Selector 0x0010 in the
GDT
mov word es,ax
mov word fs,ax
mov word gs,ax
mov dword eax,0x00000018 ; Set Stack segment register
to 32bit
mov word ss,ax ; via Selector 0x0018 in the
GDT
; which allows us to use the
esp
; register. For testing, the
Stack
; Selector is in the Text
Screen RAM
mov dword esp,0xfffffff0 ; esp is an offset from the
base
mov dword eax,cr0 ; reset bit 0, the first bit
of cr0,
and dword eax,0xfffffffe ; to 0 to disable Protected
Mode
mov dword cr0,eax ; and enable 'Unreal' mode.
; Now coding in 32bit 'Unreal' mode
; Simple test code to make sure everything is
================================
; working properly in 32bit 'Unreal' mode.
mov byte al,'Y' ; al = 'Y'
mov dword esi,0x002fff00 ; esi = 0x002fff00
mov dword edx,0x20412031 ; edx = '1A' with a green
background
mov dword [esi],edx ; [0x002fff00] = 0x20 0x41
0x20 0x31
mov dword ebx,[esi] ; ebx = 0x31204120
push dword ebx ; push the 32bit register ebx
pop dword ebx ; and pop it again to make
sure all
; 32bits got pushed and popped
cmp dword ebx,0x20412031 ; Verify all 32bits are still
in ebx
je loc0070 ; All 32bits are in ebx
mov byte al,'N' ; If this point reached, a
problem
; has occured
loc0070
mov byte [0x000b8da0],al ; Store a Y or N on the screen
; Y if everything worked
properly
; N if there was a problem
mov dword [0x000b8da8],ebx ; Store 1A with a green
background
; End Simple test
============================================================
mov dword eax,cr0 ; set bit 0, the first bit of
cr0,
or byte al,0x01 ; to 1 to disable 'unreal'
mode, and
mov dword cr0,eax ; to re-enable Protected Mode
; Now coding in 32bit Protected mode
mov dword eax,0x00000028 ; Reset segment registers to
16bit
mov word ds,ax ; via Selector 0x0028 in the
GDT
mov word es,ax
mov word fs,ax
mov word gs,ax
mov dword eax,0x00000030 ; Reset Stack segment register
to
mov word ss,ax ; 16bit via Selector 0x0030 in
the
; GDT. The 16bit sp register
is
; now used instead of esp.
jmp dword 0x0020:loc0080 ; set CS to GDT Selector
0x0020
; (16bit coding)
bits 16
; Now coding in 16bit Protected mode
loc0080
mov dword eax,cr0 ; reset bit 0, the first bit
of cr0,
and word ax,0xfffe ; to 0 to disable Protected
Mode
mov dword cr0,eax ; again, and re-enable Real
mode
jmp word 0x0000:loc0090 ; Set cs = 0x0000 and far jump
; to the next instruction
loc0090
; Now coding in 16bit Real mode
; The BIOS Interupts should work properly again
in byte al,0x70 ; re-enable Non-Maskable
; Hardware Interupts and ...
mov byte dl,al ; store al
in byte al,0x71 ; (port 0x70 bug patch)
mov byte al,dl ; restore al
and byte al,0x7f
out byte 0x70,al
in byte al,0x71 ; port 0x70 bug patch
sti ; ... re-enable Interupts
xor word ax,ax ; store 0x0000 in ax
mov word ds,ax ; and the segment registers
mov word es,ax
mov word fs,ax
mov word gs,ax
mov word ax,0xb000 ; store 0xb000 (the Text
Screen RAM
mov word ss,ax ; segment) in ax and the Stack
segment
mov word sp,0x87d0 ; and set sp to the middle of
the
; Text Screen so we can see
the stack
loc00a0
jmp loc00a0 ; endless loop
n300100 equ $ - $$
section .data align=4
; Global Descriptor Table
; Selector format of 0x0008 - 0000 0000 0000 1000
; 00 - Requestor
Privilege level
; 0 - Table Indicator
(GDT)
; 0000000000001 - GDT Descriptor
Index
; S E G M E N T D E S C R I P T O R S SELECTOR SIZE
TYPE
n800010 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; 0x0000 -
null
n800020 db 0xff,0xff,0x00,0x00,0x00,0x9e,0xcf,0x00 ; 0x0008 32bit
code
n800030 db 0xff,0xff,0x00,0x00,0x00,0x92,0xcf,0x00 ; 0x0010 32bit
data
; The following stack segment descriptor defines -
; 0x9 = Present bit = 1 so the segment is present in RAM
; Descriptor Privilege Level = 00
; Descriptor type bit = 1 so this is a segment
; 0x6 = data/code bit = 0 so this is a data segment
; Expand bit = 1 so the segment grows downward
; Write bit = 1 so the segment is writable
; Accessed bit = 0 so the segment has not been loaded
into
; a segment register yet
; 0xc = Granular bit = 1 so the segment size = Limit X 0x1000
+ 0x1000
; Big bit = 1 so Stack is 32bit, uses esp, and the
; maximum RAM address is 0xffffffff
; RESERVED = 0 (per Intel)
; User bit = 0 (unused)
;
; Base = 0x000b82d0
; Limit = 0x00000 so the segment's size is 0x00000 X 0x1000 +
0x1000 =
; 0x00001000, therefore this segment is located
in RAM
; from 0x000b72d0 through 0x000b82d0
n800040 db 0x00,0x00,0xd0,0x82,0x0b,0x96,0xc0,0x00 ; 0x0018 32bit
stack
n800050 db 0xff,0xff,0x00,0x00,0x00,0x9a,0x0f,0x00 ; 0x0020 16bit
code
n800060 db 0xff,0xff,0x00,0x00,0x00,0x92,0x0f,0x00 ; 0x0028 16bit
data
n800070 db 0xff,0xff,0x00,0x00,0x00,0x92,0x00,0x00 ; 0x0030 16bit
stack
; BYTE - 0 1 2 3 4 [5] [6] 7
; - -- -bits- - - - -bits -
; BYTE[5] 7 65 4 3 2 1 0 BYTE[6] 7 6 5 4
; CODE 1 00 1 1 C 1 0 CODE G D 0 0
; DATA 1 00 1 0 E 1 0 DATA G B 0 0
; STACK 1 00 1 0 E 1 0 STACK G B 0 0
; C-Conforming bit E-Expand bit G-Granularity bit D-Default bit B-
Big bit
n301000 equ $ - n800010 - 1 ; size of the GDT minus 1
; The size and linear address of the Global Descriptor Table
n301010 dw n301000 ; size of the GDT minus 1
dd n800010 ; 32bit physical address of
the GDT
; FILLER CALCULATION -
(imperfect)
n300110 equ 0x000001fe - ($ - $$) - n300100
; Because nasm is strictly a
two-pass
; assembler, it cannot
determine,
; before the assembly process,
how
; many zeros will be padding
between
; section .text and
section .data
times n300110 db 0xfc ; I use 0xfc as a harmless
> Thank you very much for your response, wolfgang.
good for an old man to get the feeling to be of some use ;)
...
> why would someone want to have an Expand up segment for a stack?
it wouldn't make much difference either way, but I and many others
see the stack-segment as nothing else than a special data-segment,
and I wont ever expand a stack during run-time anyway.
So you will find 'expand up stacks' quite often.
> That aside, I found one of my mistakes in the Question 1 portion of
> my original code -
> I just needed to change the 'mov dword esp,0x00000010'
> to 'mov dword esp,0xfffffff0', as the stack pointer
> is an offset of, I'm guessing here, the Base portion of
> the segment descriptor.
Yes, SS:ESP are the two registers which covers the stack-region,
and any access of [esp] must be in the limit range.
I never played with expand down stacks, so if your setting
ss-base: 0x000B82D0
ss-limit: 0 ;expand down,big
esp: 0xfffffff0 ;(-0x10)
works then I'd see an overroll to 0x000B81C0, which means that
your stack is acually below the base, so the means of stack-base
changes from botton to top if expand down ?
>> 2. one classical bug beside the problem:
>> after any write to port 70h (RTCL-Index)
>> port 71h must be either read or written.
> Although a search of my limited documents did not describe any bug
> by not accessing port 0x71 after accessing port 0x70, I decided
> to be safe, and added a read to port 0x71.
It's just that the hardware (CMOS-RTCL) requires it (see RBIL-ports).
Without it the clock may stop or behave strange on following accesses.
[..code..] Looks fine so far, only one point:
I'd check if A20 is ON at an address which got a set A20 bit,
0x002fff00 ...isn't the "2" in this A21 ? ;)
__
wolfgang
There's a few things I learned about expand-down stacks -
1. The Base of an Expand-Down data segment descriptor, represents
the high end boundry of the segment.
2. If the Granular bit of that descriptor is set, the Limit of
that Expand-Down segment, still represents the size of that
segment.
However, now, if the Limit is 0x00000, it means the size of the
segment
is 4,096 Bytes. If the Limit is 0x00001, it means the size of the
segment is 8,192 Bytes. If the Limit is 0x00002, it means the
size
of the segment is 12,288 Bytes, etc.
3. If the Big bit of that descriptor is set, the Base's physical RAM
location may be up to 0xffffffff. (If the Big bit is not set, the
Base's physical RAM location may only be up to 0xfffff.)
So my GDT Descriptor -
n800040 db 0x00,0x00,0xd0,0x82,0x0b,0x96,0xc0,0x00 ; 0x0018 32bit
stack
translates into an Expand-Down Data segment, which is readable/
writable,
has a Base at physical RAM location 0x000b82d0, is 4,096 Bytes in
size,
so it's low end boundry is at physical RAM location 0x000b72d0. This
means my stack is from 0x000b72d0 through 0x000b82d0.
> so the means of stack-base changes from botton to top if expand down ?
Yes, that's correct. According to Intel's 241430.pdf
(mine is actually 24143004.pdf) -
'Expand-down segments reverse the sense of the Limit field...This is
done to allow segments to be created in which increasing the value
held in the Limit field allocates new memory at the bottom of the
segment's address space, rather than at the top. Expand-down segments
are intended to hold stacks...'
> 0x002fff00 ...isn't the "2" in this A21 ? ;)
3 2 1
1098 7654 3210 9876 5432 1098 7654 3210
0000 0000 0010 1111 1111 1111 0000 0000
^
I assume you mean this bit? (I'm getting a little confused
here now). Am I wrong in my understanding that the A20 line is a
gate that must be enabled in order to access RAM above the 1MB range?
(You seem to be inferring that bit 20 (the 21'st bit) of the
address value 0x002fff00, has something to do with enabling
the A20 gate.)
Whatever the case, I did a simple int 15h function 2402h (Get A20
Gate status) and it verified my A20 gate was not getting enabled.
I found some similar code elsewhere and modified my program
accordingly -
; Enable A20 gate
------------------------------------------------------------
mov byte al,0xd1 ; a
out byte 0x64,al ; b
call loc0040 ; c
mov byte al,0x03 ; d
out byte 0x60,al ; e
call loc0040 ; f
jmp short loc0050
loc0040
in byte al,0x64 ; g
test al,0x02 ; h
jnz loc0040 ; i
ret
; End Enable A20 gate
--------------------------------------------------------
Unfortunately, the code was not commented, so I could not
learn
how the A20 gate gets enabled from it. Maybe someone with a better
understanding can explain what each (a - i) instruction's purpose is,
so others, including myself, may learn more about the A20 gate.
Or, if someone has more efficient code for enabling the A20 gate and
could post it here, even better.
Modified (again) floppy diskette bootsector/bootloader which -
1. Disables standard Interupts and Non-Maskable Interupts
2. (FIXED) Enables the A20 gate which allows access to RAM past the
- code start -
jmp short loc0010
; Enable A20 gate
------------------------------------------------------------
mov byte al,0xd1 ; a
out byte 0x64,al ; b
call loc0040 ; c
mov byte al,0x03 ; d
out byte 0x60,al ; e
call loc0040 ; f
jmp short loc0050
loc0040
in byte al,0x64 ; g
test al,0x02 ; h
jnz loc0040 ; i
ret
; End Enable A20 gate
--------------------------------------------------------
; For those learning -
; The lgdt [n300010] instruction does NOT load a pointer to the GDT
itself.
; lgdt loads a pointer to information ABOUT the GDT, (the GDT's size
minus 1,
; and the GDT's linear address), into the GDT Register (GDTR).
loc0050 lgdt [n301010]
mov dword eax,cr0 ; set bit 0, the first bit of
cr0,
or byte al,0x01 ; to 1 to disable real mode,
and
mov dword cr0,eax ; to enable Protected Mode
; The jmp dword 0x0008:loc0040 sets CS = GDT Selector 0x0008,
; (Selector 0x0008 in the GDT enables 32bit coding), and far jumps
; into 32bit code. Far jumps are also performed after changing CPU
modes,
; and after changing alignment, to clear any residual instructions/
data in
; the pipeline/prefetch queue/cache.
jmp dword 0x0008:loc0060
bits 32
; Now coding in 32bit Protected mode
loc0060
sti ; ... re-enable Interupts
; FILLER CALCULATION -
(imperfect)
n300110 equ 0x000001fd - ($ - $$) - n300100
RBIL->inter61d.zip->Ports.a
--------K-P0060006F--------------------------
PORT 0060-006F - KEYBOARD CONTROLLER 804x (8041, 8042) (or PPI (8255) on
PC,XT)
Note: XT uses ports 60h-63h, AT uses ports 60h-64h
0060 RW KB controller data port or keyboard input buffer (ISA, EISA)
should only be read from after status port bit0 = 1
should only be written to if status port bit1 = 0
...
0064 R- keyboard controller read status (see #P0398,#P0399,#P0400)
0064 -W keyboard controller input buffer (ISA, EISA) (see #P0401)
...
Bitfields for keyboard controller read status (ISA, EISA):
Bit(s) Description (Table P0398)
1 input buffer full (input 60/64 has data for 8042)
no write access allowed until bit clears
...
(Table P0401)
Values for keyboard controller commands (data goes to PORT 0060h):
D1h double write output port.
The next byte written to PORT 0060h will be written to the 804x output
port; the original IBM AT and many compatibles such as the OPTi 82C392
use bit 1 of the output port to control the A20 gate.
DFh sngl enable address line A20 (HP Vectra only???)
> Or, if someone has more efficient code for enabling the A20 gate and
> could post it here, even better.
I use this code:
;-----------------------------
A20_EN: call W_8042 ; wait for 8042
jnz BACK
mov al, 0D1h ; send command
out 64h, al
call W_8042 ; ready to receive?
jnz BACK
mov al, 0DFh ; enable A20
out 60h, al
W_8042: xor cx, cx
STATUS: in al, 64h ; get status
and al, 2 ; buffer filled?
loopnz STATUS ; no, or timeout
BACK: ret
;-----------------------------
Additional take a look to:
http://www.linux.org/docs/ldp/howto/Linux-i386-Boot-Code-HOWTO/setup.html
4.8. Enable A20
The modern version:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=arch/x86/boot/a20.c;hb=HEAD
Dirk
>> I never played with expand down stacks...
> There's a few things I learned about expand-down stacks -
> 1. The Base of an Expand-Down data segment descriptor, represents
> the high end boundry of the segment.
Yeah, it sounds logical to reverse the meaning of BASE 'and' LIMIT
for expand UP or Down stacks.
> 2. If the Granular bit of that descriptor is set, the Limit of
> that Expand-Down segment, still represents the size of that
> segment.
> However, now, if the Limit is 0x00000, it means the size of the
> segment
> is 4,096 Bytes. If the Limit is 0x00001, it means the size of the
> segment is 8,192 Bytes. If the Limit is 0x00002, it means the
> size
> of the segment is 12,288 Bytes, etc.
Ok.
> 3. If the Big bit of that descriptor is set, the Base's physical RAM
> location may be up to 0xffffffff. (If the Big bit is not set, the
> Base's physical RAM location may only be up to 0xfffff.)
> So my GDT Descriptor -
>
> n800040 db 0x00,0x00,0xd0,0x82,0x0b,0x96,0xc0,0x00 ; 0x0018 32bit
> stack
>
> translates into an Expand-Down Data segment, which is readable/
> writable,
> has a Base at physical RAM location 0x000b82d0, is 4,096 Bytes in
> size,
> so it's low end boundry is at physical RAM location 0x000b72d0. This
> means my stack is from 0x000b72d0 through 0x000b82d0.
and therefore you initialise ESP with a negative value ?
>> so the means of stack-base changes from botton to top if expand down ?
> Yes, that's correct. According to Intel's 241430.pdf
> (mine is actually 24143004.pdf) -
> 'Expand-down segments reverse the sense of the Limit field...This is
> done to allow segments to be created in which increasing the value
> held in the Limit field allocates new memory at the bottom of the
> segment's address space, rather than at the top. Expand-down segments
> are intended to hold stacks...'
OK ;)
> > 0x002fff00 ...isn't the "2" in this A21 ? ;)
>
> 3 2 1
> 1098 7654 3210 9876 5432 1098 7654 3210
> 0000 0000 0010 1111 1111 1111 0000 0000
> ^
> I assume you mean this bit? (I'm getting a little confused
> here now). Am I wrong in my understanding that the A20 line is a
> gate that must be enabled in order to access RAM above the 1MB range?
the A20 gate may affect the A20 address-pin only ...because this
is enough to prohibit real-mode code to access the HMA-range,
ie: FFFF:0010... = linear 0x100000...
> (You seem to be inferring that bit 20 (the 21'st bit) of the
> address value 0x002fff00, has something to do with enabling
> the A20 gate.)
Didn't you just write/read this address to check if A20 is on ?
> Whatever the case, I did a simple int 15h function 2402h (Get A20
> Gate status) and it verified my A20 gate was not getting enabled.
> I found some similar code elsewhere and modified my program
> accordingly -
>
> ; Enable A20 gate
> ------------------------------------------------------------
> mov byte al,0xd1 ; KEYBD command
> out byte 0x64,al ;
> call loc0040 ; wait for NOT-BUSY
> mov byte al,0x03 ; ?? I have 0xDF here
> out byte 0x60,al ; parameter for command 0xD1
> call loc0040 ; not sure if this is needed
> jmp short loc0050
>
> loc0040
> in byte al,0x64 ; WAIT for bit1[status]=0
> test al,0x02 ;
> jnz loc0040 ;
> ret
> ; End Enable A20 gate
> --------------------------------------------------------
Other methodes (depending on the hardware present) are:
________________
almost standard:
in al,0x92
or al,02
out 0x92,al ;set bit1 on PS/2-portA
________________
rare seen:
in al,0x65
or al,04
out 0x65,al ;set bit2
________________
even more rare:
mov dx,0x1f8
in al,dx
or al,01 ;set bit0
out dx,al
________________
To check if A20 is ON you could just compare a known value
ie: INT00-RM-vector dword at 0000:0000 with its A20-set friend
at FFFF:0010, if both are equal then A20 is not enabled.
> Unfortunately, the code was not commented, ...
I tried ;)
look at the other bits involved in KEYBD command '0xD1'
IIRC: an '03' may disable the keyboard (-interrupt).
...
> mov dword eax,cr0 ; reset bit 0, the first bit of cr0,
> and dword eax,0xfffffffe ; to 0 to disable Protected Mode
AND AL,0xFE ;saves 3 bytes
> mov dword cr0,eax ; and enable 'Unreal' mode.
...
> times n300110 db 0xfc ; I use 0xfc as a harmless filler
; 0xfc = cld (clear direction flag)
harmless ?
if it ever become executed, beside it takes more time than 0x90 NOP,
it would run until this:
> db 0x55,0xaa ; 55 aa (bootsector convention)
55 push bp ;
AA stosb ;
> - code end -
and will continue in the unknow desert ... :)
I'd have an endless loop as a save code end, ie:
end:
STI ;enable interrupts (rebootable by keyboard then)
HALT ;save some power if you like
JMP end ;and nothing more
__
wolfgang
As you read later, I've made some corrections to some of my previous
mis-understandings. But now, I >THINK< I have this Expand-Down Stack
in Protected mode stuff pretty well figured out....(ducking the bolt
of lightning)...now, onto my (probably) last post for this thread...
>>> 0x002fff00 ...isn't the "2" in this A21 ? ;)
>>
>> 3 2 1
>> 1098 7654 3210 9876 5432 1098 7654 3210
>> 0000 0000 0010 1111 1111 1111 0000 0000
>> ^
>> I assume you mean this bit? (I'm getting a little confused
>> here now). Am I wrong in my understanding that the A20 line is a
>> gate that must be enabled in order to access RAM above the 1MB range?
>>
>> (You seem to be inferring that bit 20 (the 21'st bit) of the
>> address value 0x002fff00, has something to do with enabling
>> the A20 gate.)
> the A20 gate may affect the A20 address-pin only ...because this
> is enough to prohibit real-mode code to access the HMA-range,
> ie: FFFF:0010... = linear 0x100000...
(Light turns on above my head) OK, I think I finally understand what
you were saying, and the purpose of the A20 Gate. (I was wrong in
thinking the A20 Gate must be enabled to access RAM above the 1MB
range).
Apparently, the A20 Gate is only relavent to bit 20 (the 21'st
bit) of a physical(linear) RAM address. Whether the Address value is
above 1MB is really insignificant.
So my current understanding is that the A20 Gate must be
enabled first before any physical(linear) RAM address, with
bit 20 set(1), can be accessed. If the A20 Gate is disabled,
then any attempt to access that RAM address, will result in
the access of a completely different physical(linear) RAM address -
a RAM address in which all the bits are identical, EXCEPT for bit 20
(which remains 0).
From what I've read, the reason for this is to maintain
compatability with older x86 software, in the days when Personal
Computers did not have RAM greater then 1 Megabyte. Some of this
software would base their code on RAM addresses with bit 20 set to 0.
>> Or, if someone has more efficient code for enabling the A20 gate and
>> could post it here, even better.
> almost standard:
> in al,0x92
> or al,02
> out 0x92,al ;set bit1 on PS/2-portA
(Amazed) That is JUST what I was looking for, wolfgang!
That routine enables the A20 Gate on my computer, everytime.
I modified my code accordingly. Thank You!
I have recently learned, what seems to be more accurate information
about
Expand Down Stack Descriptors in the Global Descriptor Table, and I
realize I misunderstood what I thought I knew before -
> - THIS INFORMATION IS ALMOST TOTALLY INCORRECT -----------------------------
> There's a few things I learned about expand-down stacks -
> 1. The Base of an Expand-Down data segment descriptor, represents
> the high end boundry of the segment.
> 2. If the Granular bit of that descriptor is set, the Limit of
> that Expand-Down segment, still represents the size of that segment.
> However, now, if the Limit is 0x00000, it means the size of the segment
> is 4,096 Bytes. If the Limit is 0x00001, it means the size of the
> segment is 8,192 Bytes. If the Limit is 0x00002, it means the size
> of the segment is 12,288 Bytes, etc.
> 3. If the Big bit of that descriptor is set, the Base's physical RAM
> location may be up to 0xffffffff. (If the Big bit is not set, the
> Base's physical RAM location may only be up to 0xfffff.)
> So my GDT Descriptor -
> n800040 db 0x00,0x00,0xd0,0x82,0x0b,0x96,0xc0,0x00 ; 0x0018 32bit stack
> translates into an Expand-Down Data segment, which is readable/writable,
> has a Base at physical RAM location 0x000b82d0, is 4,096 Bytes in size,
> so it's low end boundry is at physical RAM location 0x000b72d0. This
> means my stack is from 0x000b72d0 through 0x000b82d0.
> END - THIS INFORMATION IS ALMOST TOTALLY INCORRECT -------------------------
- THE CORRECTED VERSION
------------------------------------------------------
1. The Base of an Expand-Down Protected mode, data segment
descriptor,
represents a 32bit Address that the (e)sp value will be added,
to obtain a physical(linear) RAM Address. Even if the Protected
mode data segment is 16bit, all 32 bits of the Base are used.
2. a. If the Granular bit of an Expand-Down Protected mode, data
segment
descriptor, is reset(0), then -
the minimum valid sp, and esp, value allowed is the Limit
value of the
Stack descriptor in the GDT
However, if the Granular bit is set(1), then -
the minimum valid sp, and esp, value allowed is (the Limit
value of the
Stack descriptor in the GDT + 1) X 0x1000
b. - VERY IMPORTANT - After switching from Protected mode to Real
Mode,
the 16bit Stack descriptor Limit value, and
the
32bit Stack descriptor (Limit + 1) X 0x1000
value,
stay active. So the value in sp, and esp,
must
remain greater then these values.
In other words, these minimum valid
sp,
and esp, values remain in effect,
and MUST be honored.
(My computer triple faulted and rebooted when I was ignorant
of 2.b. and tried to, after switching to Real mode from
Protected mode, load sp with a value that was lower then
the Limit, or esp with a value that was lower then
the (Limit + 1) X 0x1000 in the Stack descriptor in the GDT)
3. If the Big bit of an Expand-Down Protected mode, data segment
descriptor,
is reset(0), then -
a. The data segment is aligned to 16 bits.
b. If the data segment is a stack segment -
i. the sp register is added to the Base of the descriptor.
ii. the sp register's maximum value allowed is 0xffff.
iii. the sp register's value is never negative. A sp value
of 0xffff is not -1, it's 65,535.
However, if the Big bit is set(1), then -
a. The data segment is aligned to 32 bits.
b. If the data segment is a stack segment -
i. the esp register is added to the Base of the descriptor.
ii. the esp register's maximum value allowed is 0xffffffff.
iii. the esp register's value is never negative. An esp value
of 0xffffffff is not -1, it's 4,294,967,295.
(although in my previous code, it may have been more then
a
coincidence that my esp value of 0xfffffff0 equated to a
physical(linear) RAM address of 16 Bytes less then my
descriptor's Base address...I'll have to look into this
more at another time)
So my GDT Descriptor -
n800040 db 0x07,0x00,0x00,0x00,0x0b,0x96,0xc0,0x00 ; 0x0018 32bit
Stack
translates into an Expand-Down Data segment, which is
readable/writable, has a Base of 0x000b0000, can be up to
4,294,213,631 Bytes
in size, has it's low end boundry at physical RAM location 0x000b8000
(0x00007 + 1 = 0x00008 X 0x1000 = 0x00008000 + Base = 0x000b8000).
This means my stack is from 0x000b8000 through 0xffffffff (or if my
'wrap around' theory is valid, and esp has a value of 0xffffffff,
then from 0x000b8000 through 0xffffffff continuing past 0x00000000
and back up to 0x000b7fff).
END - THE CORRECTED VERSION
--------------------------------------------------
Sorry for any problems I may have caused with my previous
misunderstanding
of Expand-Down stack segments in Protected mode. (I'm hoping everyone
read one of my previous posts where I state - 'Since I'm obviously new
to assembly language programming on the x86' and not take what I say
here as Gospel)
Because of this new understanding, I found I incorrectly
set my esp register in my previous code.
The original esp register value -
mov dword esp,0xfffffff0 ; esp is an offset from the
base
is now
mov dword esp,0x00008ee0 ; esp is an offset of the
Stack
; segment Descriptor's Base in
the GDT
My esp value changed because I also found that I incorrectly set
up my Protected mode 16bit and 32bit Stack Descriptors in the GDT -
The original Descriptors -
n800040 db 0x00,0x00,0xd0,0x82,0x0b,0x96,0xc0,0x00 ; 0x0018 32bit
stack
n800070 db 0xff,0xff,0x00,0x00,0x00,0x92,0x00,0x00 ; 0x0030 16bit
stack
are now -
n800040 db 0x07,0x00,0x00,0x00,0x0b,0x96,0xc0,0x00 ; 0x0018 32bit
stack
n800070 db 0x00,0x80,0x00,0x00,0x0b,0x96,0x00,0x00 ; 0x0030 16bit
stack
>> times n300110 db 0xfc ; I use 0xfc as a harmless filler ...
>> ; 0xfc = cld (clear direction flag)
> harmless ?
> if it ever become executed, beside it takes more time than 0x90 NOP,
> it would run until this: ...
Well, I figure since Microsoft uses 0x00 or 0xf6 as filler when they
format a floppy diskette, clearing the direction flag is harmless
compared to -
00 00 add [bx+si],al
00 00 add [bx+si],al
- or -
f6 f6 div dh
f6 f6 div dh
Modified (FINAL?) floppy diskette bootsector/bootloader which -
1. Stores a Header in the Bootsector of the Floppy Diskette for
Windows
compatability
2. Delays for 3 seconds, allowing the Floppy Drive motor to spin down
3. Initializes 16-bit Real mode segment registers
4. Disables standard Interupts and Non-Maskable Interupts
5. (IMPROVED EVEN MORE) Enables the A20 gate which allows RAM
addresses,
with bit 20 (the 21'st bit) set(1), to now be accessed
6. Loads the Global Descriptor Table Register
7. Disables 16-bit Real mode and enables 16-bit Protected mode, then
loads the cs register with Descriptor 0x0008 which enables 32-bit
code
8. Initializes 32-bit Protected mode segments and an Expand-Down 32-
bit
Protected mode Stack
9. Verifies that the A20 Gate is enabled, high RAM is accessable, and
32-bit instructions/registers/stack are working properly in
Protected
mode by pushing 'P 32' with a cyan background to the Text Screen
10. Disables Protected mode and enables Real(now 32-bit 'Unreal') mode
11. Verifies the 32-bit Real mode Stack is working by pushing 'R 32'
with a green background to the Text Screen
12. Disables 32-bit Real(or 'unreal') mode and enables 32-bit
Protected
mode, then loads the cs register with Descriptor 0x0020 which
enables 16-bit Protected mode code
13. Initializes 16-bit Protected mode segments and an Expand-Down
16-bit Protected mode Stack
14. Verifies the 16-bit Protected mode Stack is working by pushing 'P
16'
with a cyan background to the Text Screen
15. Disables Protected mode and re-enables Real mode
16. Initializes a 16-bit Real mode Stack
17. Re-enables Non-Maskable Interupts and standard Interupts
18. Initializes 16-bit Real mode segment registers and verifies the
16-bit Real mode Stack is working by pushing 'R 16' with a green
background to the screen
19. The BIOS Interupts are now enabled again, so other values are
pushed to our 16-bit Real mode Stack as evident by the other
characters being displayed on the Text Screen
20. Performs an endless loop
(We can verify the Interupts and stack are working properly
by pressing a key on the keyboard)
- code start -
; Assembled using nasm v.0.98.38 -
; C:\NASM.EXE -f bin C:\BOOTLOAD.asm -l C:\BOOTLOAD.lst -o C:
\BOOTLOAD.bin
cpu 586
bits 16
org 0x7c00
section .text align=16
; Now coding in 16bit Real mode
jmp short loc0010
; Header for Microsoft Floppy Diskette compatability
-------------------------
db 0x90,0x42,0x4f,0x4f,0x54,0x4c,0x4f,0x44
db 0x52,0x00,0x02,0x01,0x01,0x00,0x02,0xE0
db 0x00,0x40,0x0B,0xF0,0x09,0x00,0x12,0x00
db 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00
db 0x00,0x00,0x00,0x00,0x29,0x20,0x42,0x4f
db 0x4f,0x54,0x4c,0x4f,0x44,0x52,0x20,0x20
db 0x20,0x20,0x20,0x20,0x46,0x41,0x54,0x31
db 0x32,0x20,0x20,0x20,0x90,0x90
; End Header
-----------------------------------------------------------------
loc0010
; Delay for 3 seconds so floppy drive
----------------------------------------
; can spin down before we disable interupts
xor cx,cx
xor dx,dx
mov byte ah,0x01
int 0x001A
loc0020 xor ah,ah
int 0x001A
cmp word dx,0x0037 ; 0x0037 = 55 clock ticks (3
secs)
jl loc0020
; End Delay
------------------------------------------------------------------
jmp word 0x0000:loc0030 ; This sets cs = 0x0000 and
performs
; a far jump to loc0030, which
clears
; any residual instructions/
data in
; the pipeline/prefetch queue/
cache
loc0030
mov word ax,cs ; ax = 0x0000
mov word ds,ax ; ds = 0x0000
mov word es,ax ; es = 0x0000
mov word fs,ax ; fs = 0x0000
mov word gs,ax ; gs = 0x0000
cli ; disable Interupts and ...
in byte al,0x70
mov byte dl,al ; store al
in byte al,0x71 ; (port 0x70 bug patch)
mov byte al,dl ; restore al
or byte al,0x80 ; ... disable Non-Maskable
out byte 0x70,al ; Hardware Interupts
in byte al,0x71 ; (port 0x70 bug patch)
; Enable A20 gate
------------------------------------------------------------
in byte al,0x92
or byte al,0x02
out byte 0x92,al ; set bit1 on PS/2-portA
; End Enable A20 gate
--------------------------------------------------------
; The lgdt [n300010] instruction does NOT load a pointer to the GDT
itself.
; lgdt loads a pointer to information ABOUT the GDT, (the GDT's size
minus 1,
; and the GDT's linear address), into the GDT Register (GDTR)
loc0050 lgdt [n301010]
mov dword eax,cr0 ; set bit 0, the first bit of
cr0,
or byte al,0x01 ; to 1 to disable real mode,
and
mov dword cr0,eax ; to enable Protected Mode
; Now coding in 16bit Protected mode
; The jmp dword 0x0008:loc0040 sets CS = GDT Selector 0x0008,
; (Selector 0x0008 in the GDT enables 32bit coding), and far jumps
; into 32bit code
jmp dword 0x0008:loc0060
bits 32
; Now coding in 32bit Protected mode
loc0060
mov dword eax,0x00000010 ; Set segment registers to
32bit
mov word ds,ax ; via Selector 0x0010 in the
GDT
mov word es,ax
mov word fs,ax
mov word gs,ax
mov dword eax,0x00000018 ; Set Stack segment register
to 32bit
mov word ss,ax ; via Selector 0x0018 in the
GDT
; which allows the use of the
esp
; register
mov dword esp,0x00008ee0 ; esp is an offset of the
Stack
; segment Descriptor's Base in
the GDT
; so this Stack is in the
lower right
; hand side of the Text
screen. This
; allows us to see the Stack
itself
; Verify A20 Gate Enabled
----------------------------------------------------
; Verify A20 is enabled by
proving
; RAM address 0x00107e80 is
not
; equating to 0x00007e80.
; If A20 was disabled, bit 20
; (the 21'st bit) of
0x00107e80
; would not stay set(1) and
the
; address would equate to
0x00007e80
; Set [0x00007e80] to the
proper
; value of '32' -
mov dword esi,0x00007e80 ; esi = 0x00007e80
mov dword edx,0x30323033 ; edx = '32' with a cyan
background
mov dword [esi],edx ; [0x00007e80] = '32'
; Set [0x00107e80] with the
incorrect
; value of 'XX' -
mov dword esi,0x00107e80 ; esi = 0x00107e80
mov dword edx,0x30583058 ; edx = 'XX' with a cyan
background
mov dword [esi],edx ; If A20 Gate enabled -
; [0x00107e80] = 'XX'.
; If A20 Gate disabled -
; [0x00107e80] will equate to
; [0x00007e80] which will then
cause
; [0x00007e80] = 'XX'.
mov dword esi,0x00007e80 ; So if the A20 Gate was
enabled,
mov dword ebx,[esi] ; then ebx = [0x00007e80] =
'32'.
; But if the A20 Gate was not
enabled,
; then ebx = [0x00007e80] =
'XX'
; End Verify A20 Gate Enabled
------------------------------------------------
; Verify this 32bit Stack is
working
; properly in Protected mode -
push dword ebx ; push the 32bit register ebx
mov dword ebx,0x30203050 ; ebx = 'P ' with a cyan
background
push dword ebx
pop dword ebx
pop dword ebx
mov dword eax,cr0 ; reset bit 0, the first bit
of cr0,
and dword eax,0xfffffff0 ; to 0 to disable Protected
Mode
mov dword cr0,eax ; and re-enable Real mode
; (or 'Unreal' mode).
; Now coding in 32bit Real mode (or 'Unreal' mode)
jmp dword 0x0000:loc0070 ; clear the pipeline/prefetch
; queue/cache
loc0070
mov dword eax,0x00000000 ; Set this Stack to the lower
middle
mov word ss,ax ; of the Text Screen
mov dword esp,0x000b8e00 ; - NOTE - This esp value MUST
be
; greater then
; the (Limit + 1) X 0x1000
value
; that was defined in the
; 32bit Stack Descriptor in
the GDT
; Verify this 32bit Stack is
working
; properly in Real mode -
mov dword ebx,0x20322033 ; ebx = '32' with a green
background
push dword ebx ; push the 32bit register ebx
mov dword ebx,0x20202052 ; ebx = 'R ' with a green
background
push dword ebx
pop dword ebx
pop dword ebx
mov dword eax,cr0 ; set bit 0, the first bit of
cr0,
or byte al,0x01 ; to 1 to disable 'unreal'
mode, and
mov dword cr0,eax ; to re-enable Protected Mode
; Now coding back in 32bit Protected mode
jmp dword 0x0020:loc0080 ; set CS to GDT Selector
0x0020
; (16bit coding) and clear the
; pipeline/prefetch queue/
cache
bits 16
; Now coding in 16bit Protected mode
loc0080
mov word ax,0x0028 ; Reset segment registers to
16bit
mov word ds,ax ; via Selector 0x0028 in the
GDT
mov word es,ax
mov word fs,ax
mov word gs,ax
mov word ax,0x0030 ; Reset Stack segment register
to
mov word ss,ax ; 16bit via Selector 0x0030 in
the
; GDT. The 16bit sp register
is
; now used instead of esp
mov word sp,0x8260 ; sp is an offset of the Stack
; segment Descriptor's Base in
; the GDT. So this Stack is
to the
; upper right of the Text
Screen
mov word bx,0x3036 ; Verify this 16bit stack is
working
push word bx ; properly in Protected mode -
mov word bx,0x3031 ; Push 'P 16' with a cyan
background
push word bx
mov word bx,0x3020
push word bx
mov word bx,0x3050
push word bx
pop word bx
pop word bx
pop word bx
pop word bx
mov dword eax,cr0 ; reset bit 0, the first bit
of cr0,
and word ax,0xfffe ; to 0 to disable Protected
Mode
mov dword cr0,eax ; again, and re-enable Real
mode
; Now coding again in 16bit Real mode
jmp word 0x0000:loc0090 ; Set cs = 0x0000 and clear
the
; pipeline/prefetch queue/
cache ; to the next instruction
loc0090
mov word ax,0xb000 ; Set this stack to the upper
left of
mov word ss,ax ; the Text Screen
mov word sp,0x8180 ; - NOTE - This sp value MUST
be
; greater then the Limit value
that
; was defined in the
; 16bit Stack Descriptor in
the GDT
; The BIOS Interupts should, again, work properly at this point
in byte al,0x70 ; re-enable Non-Maskable
; Hardware Interupts and ...
mov byte dl,al ; store al
in byte al,0x71 ; (port 0x70 bug patch)
mov byte al,dl ; restore al
and byte al,0x7f
out byte 0x70,al
in byte al,0x71 ; port 0x70 bug patch
sti ; ... re-enable Interupts
xor word ax,ax ; store 0x0000 in ax
mov word ds,ax ; and the segment registers
mov word es,ax
mov word fs,ax
mov word gs,ax
mov word bx,0x2036 ; Verify this 16bit stack is
working
push word bx ; properly in Real mode -
mov word bx,0x2031 ; Push 'R 16' with a green
background
push word bx
mov word bx,0x2020
push word bx
mov word bx,0x2052
push word bx
; To keep the BIOS Interupts
from
; overwriting this 'R 16'
text,
; I intentially did not pop
these
; bx registers
; Also, if you press a key on
the
; keyboard, you should see a
; 'live update' of this last
stack,
; as the BIOS Interupts
activate
loc00a0
jmp loc00a0 ; endless loop
n300100 equ $ - $$
section .data align=4
; Global Descriptor Table
; Selector format of 0x0008 - 0000 0000 0000 1000
; 00 - Requestor
Privilege level
; 0 - Table Indicator
(GDT)
; 0000000000001 - GDT Descriptor
Index
; S E G M E N T D E S C R I P T O R S SELECTOR SIZE
TYPE
n800010 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; 0x0000 -
Null
n800020 db 0xff,0xff,0x00,0x00,0x00,0x9e,0xcf,0x00 ; 0x0008 32bit
Code
n800030 db 0xff,0xff,0x00,0x00,0x00,0x92,0xcf,0x00 ; 0x0010 32bit
Data
; The following 32bit stack descriptor defines -
; 0x9 = Present bit = 1 the segment is present in RAM
; Descriptor Priv. Level = 00
; Descriptor type bit = 1 this is a segment
; 0x6 = data/code bit = 0 this is a data segment
; Expand bit = 1 the segment grows downward
; Write bit = 1 the segment is writable
; Accessed bit = 0 the segment has not been loaded into
; a segment register yet
; 0xc = Granular bit = 1 the minimum esp offset = (Limit + 1) X
0x1000
; Big bit = 1 Stack is 32bit aligned, uses esp, and the
; maximum esp offset is 0xffffffff
; RESERVED = 0 (per Intel)
; User bit = 0 (unused)
;
; Base = 0x000b0000
; esp minimum offset = Limit + 1 X 0x1000 = 0x00008000
n800040 db 0x07,0x00,0x00,0x00,0x0b,0x96,0xc0,0x00 ; 0x0018 32bit
Stack
n800050 db 0xff,0xff,0x00,0x00,0x00,0x9a,0x0f,0x00 ; 0x0020 16bit
Code
n800060 db 0xff,0xff,0x00,0x00,0x00,0x92,0x0f,0x00 ; 0x0028 16bit
Data
; The following 16bit stack descriptor defines -
; 0x9 = Present bit = 1 the segment is present in RAM
; Descriptor Priv. Level = 00
; Descriptor type bit = 1 this is a segment
; 0x6 = data/code bit = 0 this is a data segment
; Expand bit = 1 the segment grows downward
; Write bit = 1 the segment is writable
; Accessed bit = 0 the segment has not been loaded into
; a segment register yet
; 0x0 = Granular bit = 0 the minimum esp offset = Limit
; Big bit = 0 Stack is 16bit aligned, uses sp, and the
; maximum sp offset is 0xffff
; RESERVED = 0 (per Intel)
; User bit = 0 (unused)
;
; Base = 0x000b0000
; sp minimum offset = Limit = 0x8000
n800070 db 0x00,0x80,0x00,0x00,0x0b,0x96,0x00,0x00 ; 0x0030 16bit
Stack
times n300110 db 0xfc ; I use 0xfc as a harmless
filler
; 0xfc = cld (clear direction
flag)
db 0x55,0xaa ; 55 aa (bootsector
convention)
- code end -
> First of all, Thank You's go to Wolfgang Kern and Dirk Wolfgang Glomp
> who responded to my original posts. It would have taken me a lot
> longer to get this far without your help.
glad to be of help.
> As you read later, I've made some corrections to some of my previous
> mis-understandings. But now, I >THINK< I have this Expand-Down Stack
> in Protected mode stuff pretty well figured out....(ducking the bolt
> of lightning)...now, onto my (probably) last post for this thread...
Fine so far, and I think the AND eax,0xfffffff0 in the switch
to unreal32 is just a typo. AND AL, 0xfe would do what you want.
...
>> almost standard:
>> in al,0x92
>> or al,02
>> out 0x92,al ;set bit1 on PS/2-portA
> (Amazed) That is JUST what I was looking for, wolfgang!
> That routine enables the A20 Gate on my computer, everytime.
> I modified my code accordingly. Thank You!
But be aware that not all PCs around got this port at all, I try
first this 0x92 thing and if it fails I use port 0x60/64.
> I have recently learned, what seems to be more accurate information
> about Expand Down Stack Descriptors in the Global Descriptor Table,
> and I realize I misunderstood what I thought I knew before -
[...] yes, and it confirms my previous assumption that only
the meaning of the limit changes with expand up or down.
And now I'm happy because I see a positive valued esp in your code :)
> b. - VERY IMPORTANT -
> After switching from Protected mode to Real Mode,
> the 16bit Stack descriptor Limit value, and the 32bit Stack
> descriptor (Limit + 1) X 0x1000 value, stay active.
This intermediate state is UNREAL-mode, the final switch to True
Real Mode takes place when you reload segregs with 16-bit limits
before the far jump to RM-code. Standard for all data seg-regs
including stack seem to be: base=0 limit=0xffff noBIG noDOWN
...
> An esp value of 0xffffffff is not -1, it's 4,294,967,295.
> (although in my previous code, it may have been more then a
> coincidence that my esp value of 0xfffffff0 equated to a
> physical(linear) RAM address of 16 Bytes less then my
> descriptor's Base address...I'll have to look into this
> more at another time)
Because PUSH/POP/CALL/RET will always alter esp by two or four
it may overroll within the segments limit without crossing it.
A similiar thing as this example for RM code limit overroll:
:RM16
addr|opcode | source |
0000 ..... ;any code..
....
7ff0 e9 00 80 jmp 0xFFF3 ;(7ff0+3+8000)
;the 0x8000 is a negative figure
;but it jumps forward
.... ;
c000 e9 00 44 jmp 0x0403 ;(c000+3+4400)= (1)0403
;the relative branch is positve yet
;but it jumps backwards
fffe eb 00 jmp short $+2 ;it actually jumps to the 0000 above !!
the latter can be seen as fffe+2+0 ANDed by the limit 0000ffff,
all other code and dats address calculation works the same way.
while:
ffff eb 00 ;will cross the limit and invoke a seg-fault.
So a RM-stack can also overroll on one end und you often see a
misaligned SP (ie: ffff) to make it cross the limit on stack
underrun and invoke a seg-fault then.
For speed reasons I use an dword aligned stack pointer and
initialise it with zero, but then I push several -1's to never
return into the wild on buggy code because a return address of
0xffffffff will produce an exception(0D) in any case.
[...] the filler bytes...
If you can make sure this part will never be executed as code
you might type in your girlfriend's phonenumber(s) as well :)
__
wolfgang
There is a write-up on A20 enabling which may help confirm your
thoughts at
It looks like you have some good knowledge so feel free to add to the
wiki if you feel so inclined. You may also want to use the
alt.os.development newsgroup. It deals with exactly the kind of things
you are doing more specifically than comp.lang.asm.x86 though some
folks - such as Wolfgang - use both groups.
--
HTH,
James
Works on most modern hardware; known to disable the screen on some
Olivetti laptops; breaks suspend/resume on some BIOSes.
> rare seen:
> in al,0x65
> or al,04
> out 0x65,al ;set bit2
> ________________
> even more rare:
> mov dx,0x1f8
> in al,dx
> or al,01 ;set bit0
> out dx,al
I have personally not encountered these; any details about which
hardware would use any of these ports, and if so, if they have INT 15h,
AX=2401h support and/or support for the standard KBC?
-hpa
Sorry for I didn't note in my docs where the info come from,
I think to have seen these ports mentioned in RBIL.
__
wolfgang
Hm. Both of them are surrounded by lots of question marks and no
details; I think they can probably best be considered vague rumours.
Pity -- the details would have been useful.
-hpa
Don't worry about this two, I think they were used on some early 286
PC-AT mainboards where most of the logic circuitry were discrete chips.
But IIRC I've seen port 0x65 also in an Intel-chipset (82380.. ?).
__
wolfgang
286 boards I do not care about.
82380 (part of the 440ZX chipset) doesn't have any A20-gating logic at
all. The corresponding southbridge is the 82371AB, and it definitely
doesn't contain any port 65h logic - only port 92h logic plus an input
pin from the KBC, a KBC chip select for ports 60h and 64h, and a MCCS
chip select for ports 62h and 66h. Nothing on port 65h, unless you
count the "programmable chip selects".
-hpa
Sorry for I cannot remember the exact source. Many years passed
since I wrote my notes on A20, so it may be out-dated anyway.
__
wolfgang