What I try to do is set Unreal Mode, nothing about multitasking, interrupts, etc., just change the stack and data segments for later restoring them to their original 16-bit values.
Then, immediately after that, I call AH=9, INT 21h, for printing a '$' terminated string in the screen.
It gets printed, but after that I cannot return control to DOS by calling AH=4Ch INT 21h.
I have checked the stack and data segments to make sure everything is set as in the beginning, and it looks like that.
The strange thing is that if I just set Unreal Mode and leave, then from a different COM program I print the string with INT 21h, the system doesn't lock, so what could be the problem?
I put here the problematic code. Note that if you remove the:...
mov dx,rmpmurmsg
mov ah,9
int 0x21
...lines and execute them in a different program, it won't lock? So what should be the next thing to check after making sure the data segments, (E)IP and the stack pointer and segments inside my project (not BIOS or DOS calls themselves) are correct, or what else should I check about them?
Code: Select all
;;Protected mode enabling/disabling sample for DOS.
;;On termination, it leaves A20 enabled and the
;;processor into Unreal Mode.
;;
;; Characteristics:
;;
;; DOS-Free Ready? [???]
;;;
org 100h ;DOS Load Base
bits 16 ;Our program is 16-bit
;;Prepare our Protected-Mode-Ready GDTR and selectors
;;to DOS/Real Mode addressing for load and set easy
;;32-bit base addresses selectors for using like in
;;DOS.
;;;
call ConfigGDTselectors
cli ;Disable our interrupts...
call enableA20 ; ...to enable Gate A20
;;;INIT: Here we will finally enter full 32-bit Protected Mode
;;;INIT: Here we will finally enter full 32-bit Protected Mode
;;;INIT: Here we will finally enter full 32-bit Protected Mode
;;Save our Real Mode Segment Registers:
;;Save our Real Mode Segment Registers:
;;Save our Real Mode Segment Registers:
;;;
push ds
push es
push fs
push gs
;;Save original SS in the BP register.
;;If we want to use it freely, we MUST
;;push it into the Protected Mode stack
;;only and only after setting it properly:
;;;
mov bp,ss ;Save SS in BP
mov [oldesp],esp ;Save original ESP in a Real Mode variable
;;Load our GDT:
;;Load our GDT:
;;Load our GDT:
;;;
lgdt [GDT]
;;Enable PE Bit and Thus, Initial Protected Mde:
;;Enable PE Bit and Thus, Initial Protected Mde:
;;Enable PE Bit and Thus, Initial Protected Mde:
;;;
mov eax,cr0
inc ax
mov cr0,eax
;;Enter to Full Basic Protected Mode using a DOS Base-addressed Code Selector:
;;Enter to Full Basic Protected Mode using a DOS Base-addressed Code Selector:
;;Enter to Full Basic Protected Mode using a DOS Base-addressed Code Selector:
;;;
jmp SELCod32DOS:bits32
bits 32 ;Now we use 32-bit instructions
bits32: ;This is our label for 32-bit PM Entry Point
;;;INIT: Protected Mode CPU Configuration Examples
;;;INIT: Protected Mode CPU Configuration Examples
;;;INIT: Protected Mode CPU Configuration Examples
;;;INIT: Protected Mode CPU Configuration Examples
;;;INIT: Protected Mode CPU Configuration Examples
;;;INIT: Protected Mode CPU Configuration Examples
;;;INIT: Protected Mode CPU Configuration Examples
;;;INIT: Protected Mode CPU Configuration Examples
;;;INIT: Protected Mode CPU Configuration Examples
;;;INIT: Protected Mode CPU Configuration Examples
;;This is a sample of how to load 32-bit
;;data segments with a DOS Base-addressed
;;Data Selector:
;;;
mov ax,SELDat32DOS
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
;;Set up a Protected Mode Stack in a high address:
;;;
mov ax,SELDat32 ;Our full 4GB data segment
mov ss,ax ;Put it into SS
mov esp,(1048576*2)-1 ;Our Protected Mode Stack Pointer at end of stack
;;Safeguard original SS value in our Protected Mode Stack (VitalOper#SS):
;;Safeguard original SS value in our Protected Mode Stack (VitalOper#SS):
;;Safeguard original SS value in our Protected Mode Stack (VitalOper#SS):
;;;
push ebp
;;Restore original SS value into BP (VitalOper#SS):
;;Restore original SS value into BP (VitalOper#SS):
;;Restore original SS value into BP (VitalOper#SS):
;;;
pop ebp
;;;END: Protected Mode CPU Configuration Examples
;;;END: Protected Mode CPU Configuration Examples
;;;END: Protected Mode CPU Configuration Examples
;;;END: Protected Mode CPU Configuration Examples
;;;END: Protected Mode CPU Configuration Examples
;;;END: Protected Mode CPU Configuration Examples
;;;END: Protected Mode CPU Configuration Examples
;;;END: Protected Mode CPU Configuration Examples
;;;END: Protected Mode CPU Configuration Examples
;;;END: Protected Mode CPU Configuration Examples
;;Now we jump to a 16-bit Protected Mode code
;;area, for preparation to return to Real Mode,
;;using a selector base Address according to where
;;DOS put us:
;;;
jmp SELCod16DOS:bits16
bits 16 ;Now we use 32-bit instructions
bits16: ;This is our label for 32-bit PM Entry Point
;;;INIT: At this stage we must not make any further calls or stack use
;;;INIT: At this stage we must not make any further calls or stack use
;;;INIT: At this stage we must not make any further calls or stack use
;;;INIT: At this stage we must not make any further calls or stack use
;;;INIT: At this stage we must not make any further calls or stack use
;;;INIT: At this stage we must not make any further calls or stack use
;;;INIT: At this stage we must not make any further calls or stack use
;;;INIT: At this stage we must not make any further calls or stack use
;;;INIT: At this stage we must not make any further calls or stack use
;;;INIT: At this stage we must not make any further calls or stack use
;;Temporarily take our DOS-like 32-bit selector
;;into DS to reload the original ESP value we
;;saved in the [oldesp] variable which is found
;;at a Real-Mode reachable address:
;;;
mov ax,SELDat32DOS ;Take it
mov ds,ax ;Load DS
mov esp,[oldesp] ;Our opportunity to get the original ESP value
;;Here we will leave our code segments with
;;32-bit addressable data segments. Without
;;it, we would return to Real Mode and not
;;to Unreal Mode if we used "SELDat16" or
;;"SELDat16DOS" instead of SELDat32:
;;;
mov ax,SELDat32
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
;;Configure SS back so it has a 16-bit
;;address size for Real Mode, so that
;;there are no strange problems and
;;lockups:
;;;
mov ax,SELDat16
mov ss,ax
;;Disable PE Bit and thus, go back to (Un)real Mode:
;;Disable PE Bit and thus, go back to (Un)real Mode:
;;Disable PE Bit and thus, go back to (Un)real Mode:
;;;
mov eax,cr0
dec ax
mov cr0,eax
;;Attemtp to further flush the Protected Mode
;;Instruction Queue to stabilize (Un)real Mode:
;;;
jmp exit_
exit_:
;;We must reload SS only after
;;disabling Protected Mode; otherwise,
;;loading it with its original Real Mode
;;value would be considered as loading an
;;invalid data selector into SS.
;;;
mov ss,bp
;;;END: At this stage we must not make any further calls or stack use
;;;END: At this stage we must not make any further calls or stack use
;;;END: At this stage we must not make any further calls or stack use
;;;END: At this stage we must not make any further calls or stack use
;;;END: At this stage we must not make any further calls or stack use
;;;END: At this stage we must not make any further calls or stack use
;;;END: At this stage we must not make any further calls or stack use
;;;END: At this stage we must not make any further calls or stack use
;;;END: At this stage we must not make any further calls or stack use
;;;END: At this stage we must not make any further calls or stack use
;;Restore our Real Mode Segment Registers:
;;Restore our Real Mode Segment Registers:
;;Restore our Real Mode Segment Registers:
;;;
pop gs
pop fs
pop es
pop ds
sti ;Enable our interrupts...
mov dx,rmpmurmsg
mov ah,9
int 0x21
mov ah,0x4C ; ...to return...
int 0x21 ; ...to DOS.
;;;END: Here we will finally enter full 32-bit Protected Mode
;;;END: Here we will finally enter full 32-bit Protected Mode
;;;END: Here we will finally enter full 32-bit Protected Mode
;;INIT: Data/Constants definitions and Arch. Structures
;;INIT: Data/Constants definitions and Arch. Structures
;;INIT: Data/Constants definitions and Arch. Structures
;INIT: GDT
;INIT: GDT
;INIT: GDT
GDT:
SELNull equ 0 ;As you may see, we have used
GDT_size: ;the Null Selector as the GDT
dw GDTsize ;pointer to load with LGDT.
GDT_actualPtr: ;
dd GDT ;You can see that the WORD at
dw 0x0000 ;the end of these 8 bytes are
;2 padding bytes with 0x0000.
SELCod32 equ 8
SelCod32:
dw 0FFFFh ; bits 0-15 length (Bytes 0-1)
dw 00000h ; bits 0-15 base addr (Bytes 2-3)
db 0 ; bits 16-23 base addr (Byte 4)
db 10011010b ; bits 0-3 Type, 4 DT, 5-6 DPL & 7 P (Byte 5)
db 11001111b ; bits 16-19 length into 0-3, 6 D & 7 G (Byte 6)
db 0 ; bits 24-31 base addr (Byte 7)
SELDat32 equ 16
SelDat32:
dw 0FFFFh ; bits 0-15 length (Bytes 0-1)
dw 00000h ; bits 0-15 base addr (Bytes 2-3)
db 0 ; bits 16-23 base addr (Byte 4)
db 10010010b ; bits 0-3 Type, 4 DT, 5-6 DPL & 7 P (Byte 5)
db 11001111b ; bits 16-19 length into 0-3, 6 D & 7 G (Byte 6)
db 0 ; bits 24-31 base addr (Byte 7)
SELCod16 equ 24
SelCod16:
dw 0FFFFh ; bits 0-15 length (Bytes 0-1)
dw 00000h ; bits 0-15 base addr (Bytes 2-3)
db 0 ; bits 16-23 base addr (Byte 4)
db 10011010b ; bits 0-3 Type, 4 DT, 5-6 DPL & 7 P (Byte 5)
db 00001111b ; bits 16-19 length into 0-3, 6 D & 7 G (Byte 6)
db 0 ; bits 24-31 base addr (Byte 7)
SELDat16 equ 32
SelDat16:
dw 0FFFFh ; bits 0-15 length (Bytes 0-1)
dw 00000h ; bits 0-15 base addr (Bytes 2-3)
db 0 ; bits 16-23 base addr (Byte 4)
db 10010010b ; bits 0-3 Type, 4 DT, 5-6 DPL & 7 P (Byte 5)
db 00000000b ; bits 16-19 length into 0-3, 6 D & 7 G (Byte 6)
db 0 ; bits 24-31 base addr (Byte 7)
SELCod32DOS equ 40
SelCod32DOS:
dw 0FFFFh ; bits 0-15 length (Bytes 0-1)
dw 00000h ; bits 0-15 base addr (Bytes 2-3)
db 0 ; bits 16-23 base addr (Byte 4)
db 10011010b ; bits 0-3 Type, 4 DT, 5-6 DPL & 7 P (Byte 5)
db 11001111b ; bits 16-19 length into 0-3, 6 D & 7 G (Byte 6)
db 0 ; bits 24-31 base addr (Byte 7)
SELDat32DOS equ 48
SelDat32DOS:
dw 0FFFFh ; bits 0-15 length (Bytes 0-1)
dw 00000h ; bits 0-15 base addr (Bytes 2-3)
db 0 ; bits 16-23 base addr (Byte 4)
db 10010010b ; bits 0-3 Type, 4 DT, 5-6 DPL & 7 P (Byte 5)
db 11000000b ; bits 16-19 length into 0-3, 6 D & 7 G (Byte 6)
db 0 ; bits 24-31 base addr (Byte 7)
SELCod16DOS equ 56
SelCod16DOS:
dw 0FFFFh ; bits 0-15 length (Bytes 0-1)
dw 00000h ; bits 0-15 base addr (Bytes 2-3)
db 0 ; bits 16-23 base addr (Byte 4)
db 10011010b ; bits 0-3 Type, 4 DT, 5-6 DPL & 7 P (Byte 5)
db 00000000b ; bits 16-19 length into 0-3, 6 D & 7 G (Byte 6)
db 0 ; bits 24-31 base addr (Byte 7)
SELDat16DOS equ 64
SelDat16DOS:
dw 0FFFFh ; bits 0-15 length (Bytes 0-1)
dw 00000h ; bits 0-15 base addr (Bytes 2-3)
db 0 ; bits 16-23 base addr (Byte 4)
db 10010010b ; bits 0-3 Type, 4 DT, 5-6 DPL & 7 P (Byte 5)
db 00000000b ; bits 16-19 length into 0-3, 6 D & 7 G (Byte 6)
db 0 ; bits 24-31 base addr (Byte 7)
GDT_end:
GDTsize equ (GDT_end-GDT)-1
;END: GDT
;END: GDT
;END: GDT
;;END: Data/Constants definitions and Arch. Structures
;;END: Data/Constants definitions and Arch. Structures
;;END: Data/Constants definitions and Arch. Structures
ConfigGDTselectors:
push eax
xor eax,eax
mov ax,ds
shl eax,4
push eax
mov [SelDat32DOS+2],ax
mov [SelDat16DOS+2],ax
shr eax,16
mov [SelDat32DOS+4],al
mov [SelDat16DOS+4],al
mov [SelDat32DOS+7],ah
mov [SelDat16DOS+7],ah
pop eax
add eax,GDT
mov [GDT_actualPtr],eax
xor eax,eax
mov ax,cs
shl eax,4
mov [SelCod32DOS+2],ax
mov [SelCod16DOS+2],ax
shr eax,16
mov [SelCod32DOS+4],al
mov [SelCod16DOS+4],al
mov [SelCod32DOS+7],ah
mov [SelCod16DOS+7],ah
pop eax
ret
;This function waits until the
;Keybard Controller is ready to
;accept commands.
;;
;;;
KeybCtrl_waitUntilCommandable:
push ax
.waitUntilReady:
in al,0x64 ;Port 0x64 is the Status
;Register of the keyboard
;controller port located
;on the motherboard.
test al,00000010b ;We see if bit 1 of the returned
;byte at 0x64 is set to 1, which
;means that the controller is NOT
;ready to accept commands. It means
;"Input Buffer Full (port 0x60 or 0x64);
;don't write yet".
jnz .waitUntilReady ;Repeat until bit 1 of byte from
;port 0x64 is cleared to 0 or, in
;other words, until the keyboard
;controller is ready to accept
;commands. It would mean "Input
;Buffer Empty (port 0x60 or 0x64); can
;be written".
;Note that writing the Input Buffer through
;port 0x64 causes the byte to be interpreted
;as a command.
;
;And if we write the Input Buffer through
;port 0x60 causes the byte to be interpreted
;as data, usually as a parameter to an immediate
;previously sent command at port 0x64.
pop ax
ret
;This function waits until the
;Keybard Controller has a data
;byte ready for us to gather.
;;
;;;
KeybCtrl_waitForDataForUs:
push ax
.waitUntilReady:
in al,0x64 ;Port 0x64 is the Status
;Register of the keyboard
;controller port located
;on the motherboard.
test al,00000001b ;We see if bit 0 of the returned
;byte at 0x64 is set to 0, which
;means that the controller does NOT
;have a data byte for us yet. It means
;"Output Buffer Empty (port 0x60);
;don't read yet".
jz .waitUntilReady ;Repeat until bit 0 of byte from
;port 0x64 is set to 1 or, in
;other words, until the keyboard
;controller has a data byte to
;give us. It would mean "Output
;Buffer Full (port 0x60); can
;be read".
pop ax
ret
enableA20:
;;;Wait for controller to accept
;;;commands
;;
;
call KeybCtrl_waitUntilCommandable
;;;Write Output Port (Command -- Step 1/2):
;;;Write Output Port (Command -- Step 1/2):
;;;Write Output Port (Command -- Step 1/2):
mov al,0xD1 ;This command is to write the
;status byte. This command is
;called WRITE OUTPUT PORT.
out 0x64,al ;Here we send the command, and
;it gets processed.
;;;Wait for controller to accept
;;;commands
;;
;
call KeybCtrl_waitUntilCommandable
;;;Write The Output Port (Command Parameter -- Step 2/2):
;;;Write The Output Port (Command Parameter -- Step 2/2):
;;;Write The Output Port (Command Parameter -- Step 2/2):
mov al,11111111b ;This is a data byte to serve
;as a parameter to the previous
;controller command (0xD1). We
;are setting the necessary bits
;here to the normal operation
;of the controller, and, of course,
;it is here that we enable the A20
;Address Line by setting its bit
;to 1.
;bit 0-Normal (1) or Reset CPU (0)
;bit 1-Enable A20 (1) or Disable A20 (0)
;bit 2-Data to Mouse (1) or Disabled (0)
;bit 3-Mouse Clock (1) or Disabled (0)
;bit 4-IRQ1 Active (1) or IRQ1 Inactive (0)
;bit 5-IRQ12 Active (1) or IRQ12 Inactive (0)
;bit 6-Keyboard Clock (1) or Disabled (0)
;bit 7-Data to Keyboard (1) or Disabled (0)
out 0x60,al ;We send this bit-set to the
;data port of the keyboard. At
;this point is where really the
;A20 line gets enabled.
ret
rmpmurmsg db "Test Message",0x0D,0x0A,'$'
oldesp dd 0