DOS locking on entering UnrealMode & call to INT 21h

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

DOS locking on entering UnrealMode & call to INT 21h

Post by ~ »

I'm having problems with DOS and I fear that these are bugs in my code that would later could affect my actual kernel project.

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
User avatar
Troy Martin
Member
Member
Posts: 1686
Joined: Fri Apr 18, 2008 4:40 pm
Location: Langley, Vancouver, BC, Canada
Contact:

Re: DOS locking on entering UnrealMode & call to INT 21h

Post by Troy Martin »

Methinks it has something to do with a flat address space, so DOS gets confused. I guess you could try restoring everything back to a real mode state and then 4Ch'ing out.
Image
Image
Solar wrote:It keeps stunning me how friendly we - as a community - are towards people who start programming "their first OS" who don't even have a solid understanding of pointers, their compiler, or how a OS is structured.
I wish I could add more tex
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Re: DOS locking on entering UnrealMode & call to INT 21h

Post by ~ »

OK, I managed to determine that the problem had to do with not properly clearing the instruction queue once leaving Protected Mode to return to Real Mode. So, these lines:

Code: Select all

   ;;Attemtp to further flush the Protected Mode
   ;;Instruction Queue to stabilize (Un)real Mode:
   ;;;
    jmp exit_
    exit_:
Should be changed like this:

Code: Select all

    jmp 0:exit_
    exit_:

Now, the problem is that DOS might put the program anywhere, so should this instruction be modified to configure a valid CS segment instead of the 0 shown in { 0:exit_ }?.

Does anybody know of a more elegant way of performing a far jump instruction without modifying the machine code at that location, or is that the most direct path?
ru2aqare
Member
Member
Posts: 342
Joined: Fri Jul 11, 2008 5:15 am
Location: Hungary

Re: DOS locking on entering UnrealMode & call to INT 21h

Post by ru2aqare »

~ wrote:Does anybody know of a more elegant way of performing a far jump instruction without modifying the machine code at that location, or is that the most direct path?
Try

Code: Select all

   ...
   push cs
   push offset exit_
   retf
exit_:
   nop
   ...
Post Reply