I'm trying to learn how to use some features of the 80386+ protected mode, in particular V86 mode. Before I get to that stage however, I need to establish code to successfully enter and exit protected mode.
I am developing and testing my program under VMware ("Player", the free version). I am wondering if the emulation might be broken/faulty, but if that were so, why would Win98 work properly?
I've been debugging this for days, and I am out of ideas. I'm posting the whole program below -- the PM entry/exit code is near the top. Below are interrupt handlers (really simple stubs), and the GDT and IDT tables. I'm using direct writes to the video memory at 0xB8000 to output debugging flags. Sometimes, for some reason (help!), stage "C" (referring to the debugging output) is reached and interrupt 8 (timer, IRQ0) is working, as is verified by the "08" debug output and the following character that is incremented each time the interrupt is triggered. Sometimes, I get an interrupt "09" (keyboard). Sometimes, I can't reach even stage "B" before it locks up or reboots (VMware says the CPU has entered shutdown state).
The test/development platform is MS-DOS (the version that comes with Win98SE).
OK, the code:
Code: Select all
ORG 100h
SEGMENT _TEXT ;start=0 vstart=100h
RMMSGID_V86ALREADY EQU 0
RMMSGID_DOSRESIZE EQU 1
RMMSGCOUNT EQU 2
main:
CPU 386
BITS 16
smsw ax
test ax, 1
jnz .v86already
;
mov sp, stack_ends
mov bp, bss_starts
;
mov bx, sp
add bx, 15
shr bx, 4
mov ah, 4Ah
int 21h
jc .dosresize
;
BSS_GDTR EQU 2 ; align properly at odd word address
mov word [bp+BSS_GDTR], our_gdt.end-our_gdt-1 ; GDT upper limit
mov ax, cs
xor dx, dx
mov dl, ah
mov cl, 4
shr dx, cl
shl ax, cl
add [our_gdt.sel08+2], ax ; base addr bits 0-15
adc [our_gdt.sel08+4], dl ; base addr bits 16-24
add [our_gdt.sel10+2], ax ; base addr bits 0-15
adc [our_gdt.sel10+4], dl ; base addr bits 16-24
mov cx, ax ; save
mov bx, dx ; "
add ax, our_gdt
adc dl, 0
mov [bp+BSS_GDTR+2], ax ; GDT lin. addr; low word
mov [bp+BSS_GDTR+4], dx ; " high word
BSS_OLD_SEG EQU 8
BSS_IDTR EQU 0Ah ; should be at odd word addr.
BSS_SAVE_IDTR EQU 0x12 ; likewise
BSS_SAVE_GDTR EQU 0x1A ; likewise
int3
sgdt [bp+BSS_SAVE_GDTR]
sidt [bp+BSS_SAVE_IDTR]
mov word [bp+BSS_IDTR], our_idt.end - our_idt - 1 ; IDT limit
mov ax, cx ; restore ax
mov dx, bx ; and dx
add ax, our_idt
adc dl, 0
mov [bp+BSS_IDTR+2], ax
mov [bp+BSS_IDTR+4], dx
mov [bp+BSS_OLD_SEG], cs
;
int3
mov ax,0xB800
mov es, ax
mov di, (24*80+79)*2
mov ax, 0x0F00 + 'A'
stosw
int3
; fixup selector part of IDT entries
mov cx, (our_idt.end - our_idt) / 8 + 1
mov di, our_idt+2
.iloop: mov word [di], 08h ; 08 is code segment selector.
add di, 8 ; advance 1 IDT entry.
loop .iloop
call waitesc16
in al,70h ; disable NMI
or al,80h
out 70h,al
cli
lgdt [bp+BSS_GDTR]
lidt [bp+BSS_IDTR]
mov eax,cr0
or al, 1
shl eax,1 ; clear
shr eax,1 ; paging bit
mov cr0,eax
mov ax, 10h ; data/stack selector
mov ds, ax
movzx esp,sp
mov ss, ax
add al, 8 ; 18h = video selector
mov es, ax
mov di, (24*80+79)*2
mov ax, 0x0F00 + 'B'
stosw
call waitesc16
push word 08h
push dword .pm32ent
retfd
ALIGN 4
.pm32ent:
BITS 32
mov eax,ebp
shr eax, 16
test ax, ax
mov edi, (24*80+78)*2
mov ax, 0x0F00
jz .skip1
mov al, 'B'
stosw
jmp .skip2
.skip1:
mov al, 'N'
stosw
.skip2:
movzx esp,sp
movzx ebp,bp
mov edi, (24*80+79)*2
mov ax, 0x0F00 + 'C'
stosw
sti
call waitesc32
cli
mov eax,cr0
and eax, ~(1|0x80000000) ; clear PE and PG bits
mov cr0,eax
lidt [bp+BSS_SAVE_IDTR]
lgdt [bp+BSS_SAVE_GDTR]
mov ax, 0xB800
mov es, ax
mov ax, [bp+BSS_OLD_SEG]
mov ds, ax
mov ss, ax
call waitesc32
;.kbloop2:
;in al, 60h
;cmp al, 1
;jne .kbloop2
mov edi, (24*80+79)*2
mov ax, 0x0F00 + 'D'
stosw
mov ax,[bp+BSS_OLD_SEG]
mov cs,ax
push word cs
;push word 0
push dword .exit32
retfd
.exit32:
BITS 16
mov edi, (24*80+79)*2
mov ax, 0x0F00 + 'E'
stosw
.kbloop0:
in al, 60h
cmp al, 1
jne .kbloop0
;sti
mov edi, (24*80+79)*2
mov ax, 0x0F00 + 'F'
stosw
.kbloop1:
in al, 60h
cmp al, 1
jne .kbloop1
BITS 16
mov ax,4C00h
int 21h
............. snip for brevity
BITS 32
waitesc16:
pushf
push ax
.1: in al, 60h
cmp al, 1
jne .1
.2: in al, 60h
cmp al, 1
je .2
pop ax
popf
retn
waitesc32:
pushfd
push eax
.1: in al, 60h
cmp al, 1
jne .1
.2: in al, 60h
cmp al, 1
je .2
pop eax
popfd
retn
BITS 32
int_00:
pushad
push es
mov ax,18h
mov es, ax
mov edi, (16*80+0)*2
mov ax, 0x0F00 + '0'
stosw
mov al, '0'
stosw
pop es
popad
iretd
........................ snip for brevity
int_08:
pushad
push es
mov ax,18h
mov es, ax
mov edi, (16*80+32)*2
mov ax, 0x0F00 + '0'
stosw
mov al, '8'
stosw
inc byte [es:edi]
mov al, 60h ; specific EOI for IRQ0
; mov al, 20h ; non-specific EOI
out 20h, al
pop es
popad
iretd
int_09:
pushad
push es
mov ax,18h
mov es, ax
mov edi, (16*80+36)*2
mov ax, 0x0F00 + '0'
stosw
mov al, '9'
stosw
inc byte [es:edi]
mov al, 61h ; specific EOI for IRQ1
; mov al, 20h ; non-specific EOI
out 20h, al
pop es
popad
iretd
........................................ snip for brevity
SEGMENT _DATA
..................................... snip
ALIGN 8
; SELECTOR FORMAT
;15 8 7 4 3 2 0
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+
; | INDEX |LDT| DPL |
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+
;
; DESCRIPTOR FORMAT:
;
;15 8 7 0 BYTE
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+
; | BASE 24-31 || G |D/B| L |AVL| LIMIT 16-19 |
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+ +6
; | P | DPL | S | TYPE || BASE ADDRESS 16-23 |
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+ +4
; | BASE ADDRESS 0-15 |
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+ +2
; | LIMIT 0-15 |
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+ 0
;
; TYPE field:
; 3 0
; +---+---+---+---+
; |C/D| | | A | C/D=code/data; 1=code, 0=data. A=accessed
; +---+---+---+---+
; | 0 | E | W | A | E=expand down, W=write access
; +---+---+---+---+
; | 1 | C | R | A | C=conforming, R=read access
; +---+---+---+---+
our_gdt:
dd 0, 0 ; entry 0: unused
.sel08: ; code segment selector (32-bit)
dw 0xFFFF ; +0 limit bits 0-15
dw 0 ; +2 base addr. 0-15
db 0 ; +4 base addr. 16-23
db 0x9B ; +5 PDdSType, *P=present, D=DPL, S=0=system descr. type
db 0x40 ; +6 GBLALimt, G=gran., B=32bit, L=64bit-, A=avail.
db 0 ; +7 base addr. 24-31
.sel10: ; data and stack segment selector (16-bit)
dw 0xFFFF ; +0 limit bits 0-15
dw 0 ; +2 base addr. 0-15
db 0 ; +4 base addr. 16-23
db 0x93 ; +5 PDdSType, *P=present, D=DPL, S=0=sys.desc, d.seg+w
db 0x40 ; +6 GBLALimt, G=gran., B=32bit*, L=64bit-, A=avail.
db 0x00 ; +7 base addr. 24-31
.sel18: ; text mode VGA memory
dw 0xFFF ; +0 limit 4095
dw 0x8000 ; +2 low 16 bits of base addr.
db 0x0B ; +4 bits 16-23 of base addr.
db 0x93 ; +5 PDdSType, *P=present, D=DPL, S=0=sys.desc, d.seg+w
db 0x00 ; +6 GBLALimt, G=gran., B=16bit*, L=64bit-, A=avail.
db 0x00 ; +7 base addr. 24-31
.end:
; INTERRUPT GATE DESCRIPTOR FORMAT:
;
;15 8 7 0 BYTE
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+
; | OFFSET 16-31 |
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+ +6
; | P | DPL |S=0| D 1 1 0 || 0 0 0 | - - - - - |
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+ +4
; | SELECTOR |
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+ +2
; | OFFSET 0-15 |
; +---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---+ 0
; D=size of gate; 1=32 bits.
our_idt:
.ent00: dw int_00 ; offset bits 0-15
dw 0 ; selector
db 0x00 ; bits 0-4 reserved, bits 5-7 must be 0
db 0x8e ; 32-bit interrupt gate
dw 0 ; offset 16-31
................................ snip
.ent08: dw int_08 ; offset bits 0-15
dw 0 ; selector
db 0x00 ; bits 0-4 reserved, bits 5-7 must be 0
db 0x8e ; 32-bit interrupt gate
dw 0 ; offset 16-31
.ent09: dw int_09 ; offset bits 0-15
dw 0 ; selector
db 0x00 ; bits 0-4 reserved, bits 5-7 must be 0
db 0x8e ; 32-bit interrupt gate
dw 0 ; offset 16-31
......................................... snip
.ent1F: dw int_1F ; offset bits 0-15
dw 0 ; selector
db 0x00 ; bits 0-4 reserved, bits 5-7 must be 0
db 0x8e ; 32-bit interrupt gate
dw 0 ; offset 16-31
.end:
SEGMENT .bss ; must be .bss not _BSS to make NASM understand
ALIGN 16
bss_starts:
resw 4096 ; 8192 bytes reserved for stack+BSS
stack_ends:
; vim: set syn=nasm:
Regards,
Albert Wik