;***************************************************************
; Routines for switching to protect and real mode. Also includes
; routines for loading task register and jumping to task.
; Assemble with TURBO TASM 1.0
;***************************************************************

   .MODEL SMALL
   .386P

   .DATA
; this is where we stuff address of GDT that is passed
gdtptr LABEL PWORD
   DW 50h               ; size in bytes of GDT, enough for 10 entries
   DD ?                 ; this is where we will put physical address of GDT

; this is where we will store the address of task (the selector) that
; we will jump to in jump_to_task
new_task LABEL DWORD
   DW 00h
new_select LABEL WORD
   DW 00h

; this is where store address to jump to when we  enter protected mode
; in protected_mode
; offset of code where we will jump
p_mode  LABEL DWORD
   DW offset protect
; put selector of protected mode code segment here
p_mode_select LABEL WORD
   DW 0

   .CODE

   PUBLIC _real_mode, _protected_mode,  _jump_to_task
   PUBLIC _load_task_register

;******************************************************************
; void protected_mode(unsigned long gdt_ptr,unsigned int cseg,
; unsigned int dseg) - put 386 in protected mode and loads segment
; registers with code and data selectorrs passed (cs and ds
; paremeters). Pass this routine a 32 bit physical address of the
; Global Descriptor Table (gdt_ptr parameter). Turns interrupts
; off while we run in protected mode
;******************************************************************
_protected_mode  PROC NEAR
   push bp
   mov  bp,sp
   mov  ax,[bp+4]            ; get low word of address of GDT
   mov  dx,[bp+6]            ; get high word of address of GDT
   mov  WORD PTR gdtptr+4,dx ; store high word of address of GDT
   mov  WORD PTR gdtptr+2,ax ; store low  word of address of GDT
   mov  ax,[bp+8]            ; get selector for code descriptor
   mov  dx,[bp+10]           ; get selector for data descriptor
   mov  p_mode_select,ax     ; put code selector in our jmp  pointer
   mov  eax,0                ; prepare to zero out eflags
   push eax
   popfd                      ; zero out eflags
   lgdt PWORD PTR gdtptr     ; load gdt register with limit and ptr
   mov  eax,cr0
   or   eax,1
   mov  cr0,eax              ; turn protected mode on
   jmp  DWORD PTR p_mode     ; this will jump to protect through ptr
                              ; (cs will be loaded with code selector)
protect:
; we now running in protect mode, and we will load segment registers
; with selectors that look like our code is still in real mode
   mov  ss,dx
   mov  ds,dx
   mov  es,dx
   mov  fs,dx
   mov  gs,dx
   mov  ax,0                 ; make sure ldt register has 0
   lldt ax
   pop  bp
   ret
_protected_mode ENDP

;******************************************************
; void load_task_register(unsigned int tss_selector) -
; loads task register with TSS selector
;******************************************************
_load_task_register     PROC NEAR
   push bp
   mov  bp,sp
   ltr  [bp+4]    ; load task register with selector for current task
   pop  bp
   ret
_load_task_register     ENDP

;**************************************************************
; void real_mode(unsigned int dseg) -
; return 386 to real mode. Pass this routine selector for
; a 64K data segment so we can return to real mode. This
; routine assumes we are executing from a 64K data segment.
; (80386 segment registers must have selector of segment with
; 64K limit to return to real mode ).
;**************************************************************
_real_mode  PROC NEAR
   push bp
   mov  bp,sp
   mov  ax,[bp+4]            ; get selector for data segment
   mov  ds,ax                ; now make sure all segment registers
   mov  es,ax                ; contain selector to 64K segment
   mov  fs,ax                ; must have this to return to real mode
   mov  gs,ax
   mov  ss,ax
   mov  eax,cr0
   and  eax,07FFFFFFEh
   mov  cr0,eax              ; turn protectd mode off
   jmp  FAR PTR flush        ; flush queue and set CS for real mode
                              ; now CS will be loaded with correct
                              ; segment for real mode
flush:
   mov  ax,DGROUP            ; restore data seg registers for real mode
   mov  ds,ax
   mov  ss,ax
   mov   es,ax
   sti                        ; interrupts back on for real mode
   pop  bp
   ret
_real_mode  ENDP

;**************************************************************
; void jump_to_task(unsigned int tss_selector) -
; jumps to 386 TSS task. Pass this routine the selector of the
; TSS of the task you want to jump to.
;**************************************************************
_jump_to_task   PROC    NEAR
   push bp
   mov  bp,sp
   mov  ax,[bp+4]      ; get selector of new task
   mov  new_select,ax  ; store it in pointer
   jmp  DWORD PTR new_task ; jump to task through selector:offset ptr
   pop  bp
   ret
_jump_to_task   ENDP

   END
