format  MZ
heap    0
entry   fos_seg:start

segment fos_seg use32
int32_routine:
        db 66h,0E8h     ; 16 bit call(equivalent to push 16-bit int number)
        rb 255*4
        dw 0

int32_vect:
        rb 256*8
use32
int0_hand:             ; int 0 had to be put here
        push    word 0 ;   because the lowest call statement would return 4
intx_jmp:
        cli
        inc     word [gs:0]             ; Flicker something on the screen
        push    eax
        mov     ax,2 shl 3              ; Set es to code segment
        mov     es,eax
        pop     dword [es:setax-4]      ; Save current eax
        pop     word [es:teax2-2]       ; Save interrupt number
        pop     dword [es:ijump-6]      ; Save return jump
        pop     word [es:ijump-2]
        pop     ax                      ; Discard hi word from seg
        mov     [es:setsp-4],esp        ; Save stack pointer
        pop     word [es:setf-2]        ; Save 16 bit flags
        jmp     3 shl 3:pm16_start
    pm16_start:
        use16
        mov     eax,cr0
        dec     ax                      ; Switch back to real mode
        mov     cr0,eax
        jmp     fos_seg:pm16_end        ; Set cs to 16-bit real mode
    pm16_end:
        lidt    [cs:real16_IDTR]        ; Load old interrupt table    
        mov     ax,cs                   ; Setup 16bit stack
        mov     ss,ax
        mov     ax,stend
        mov     sp,ax
        mov     eax,0                   ; Retreive save eax      
   setax:
        push    word 0                  ; Retreive calling flags
   setf:
        push    cs                      ; Emulate 16 bit interrupt
        push    word teax2.real16
        push    dword [0]               ; Jump to 16 bit vector
teax2:
        retfw
  .real16:
        pushfw                          ; Save return flags(software ints)
        cli
        mov     [cs:setax2-4],eax       ; Save return eax
        pop     word [cs:setf2-2]       ; Save flags for later
        mov     eax,cr0                 
        inc     ax                      ; Switch to protected mode
        mov     cr0,eax
        jmp     1 shl 3:pm32_start2     ; Jump back to 32-bit protected mode
    pm32_start2:
        use32
        mov     ax,4 shl 3              ; Restore ds, es, and ss
        mov     ds,eax                  ;   these and ss are the only segs
        mov     es,eax                  ;   that we care about
        mov     ss,eax
        mov     esp,0
    setsp:
        mov     ax,0
   setf2:
        mov     [esp],ax                ; Restore return flags
        lidt    [cs:real32_IDTR]        ; Lets go back to 32 bit ints
        popf                            ; Restore flags
        mov     eax,0                   ; Restore return eax
   setax2:
        jmp     0:0             ; Switching from real/protected makes
   ijump:                       ;   the cpu forget what typ of int, so
                                ;   we have to do a jump instead

align 8
real32_GDT:
                   dw 5*8-1             ; limit of GDT
real32_GDT_address dd real32_GDT        ; linear address of GDT
                   dw 0                 ; padding for null descriptor
           dw 0FFFFh,0,9A00h,0CFh       ; 4 GB 32-bit fos_seg code descriptor 
           dw 0FFFFh,0,9200h,0CFh       ; 4 GB 32-bit fos_seg data descriptor
           dw 0FFFFh,0,9A00h,08Fh       ; 4 GB 16-bit fos_seg code descriptor
           dw 0FFFFh,0,9200h,0CFh       ; 4 GB data descriptor base 0

label real32_IDTR pword         ; pointer to 32 bit int vector
real32_IDT_limit dw 7FFh
real32_IDT_base dd int32_vect

label real16_IDTR pword         ; pointer to 16 bit int vector
real16_IDT_limit dw 3FFh
real16_IDT_base dd 0

use16
start:
        mov     ax,7202h        ; check for 386
        push    ax
        popf
        pushf
        pop     bx
        cmp     ax,bx
        je      processor_ok
        call    no_386
        db      '80386 or better required$'

    no_386:
        push    cs
        pop     ds
        pop     dx
        mov     ah,9            ; Print Message
        int     21h
        mov     ax,0x4CFF       ; Quit program
        int     21h

    processor_ok:
        xor     eax,eax
        mov     ax,cs                   ; calculate linear address of GDT
        mov     ds,ax                   ; Set DS to fos_seg
        shl     eax,4
        or      [real32_GDT+10],eax     ; Adjust GDT pointer to fos_seg
        or      [real32_GDT+10+8],eax
        or      [real32_GDT+10+16],eax
        add     [real32_GDT_address],eax
        add     [real32_IDT_base],eax   ; IDT has to be adjusted as well
        add     [initstack-4],eax       ; As well as the stack
        mov     cx,100h-1
        mov     bx,int32_vect
        mov     word [bx],int0_hand     ; Fill int 0 vector
        mov     word [bx+2],1 shl 3     ; Fos_seg
        mov     dword [bx+4],0x8e00     ; Present, DPL=0, 32 bit, 0 for 16-31 of offset
        xor     di,di
        mov     eax,0e8660000h+intx_jmp-4 ; 16 bit call-code
    init_interrupts:
        add     bx,8
        mov     word [bx],di            ; Offset of currect int routine
        mov     word [bx+2],1 shl 3     ; Fos_seg
        mov     dword [bx+4],0x8e00     ; Present, DPL=0, 32 bit, 0 for 16-31 of offset
        mov     [di+2],eax              ; Our call routine code
        add     di,4                    ; Next vector
        sub     ax,4                    ; Adjust call code
        loop    init_interrupts         ; Next interrupt
        mov     ax,0xb800               ; Used for storing on screen
        mov     gs,ax
        cli
        lidt    [cs:real32_IDTR]        ; load 32 bit int table
        lgdt    [cs:real32_GDT]         ; load GDT register
        mov     eax,cr0                 ; switch to protected modes
        inc     ax
        mov     cr0,eax
        jmp     1 shl 3:pm32_start      ; jump to protected mode
    pm32_start:
        use32
        mov     eax,4 shl 3             ; load 32-bit data descriptor
        mov     ss,eax
        mov     esp,stend2
   initstack:
        mov     es,eax                  ; set our datasegment resgisters
        mov     ds,eax
        sti                           ; restore the interrupts and go for it
.l9:
        inc     word [gs:2]
        bt      word [0x417],1          ; Check if left shift is pressed
        jnc     .l9
        mov     ax,0x4c00               ; If so, end program
        int     21h

ststart:                ; Our 16 bit stack
        rb 4024
stend:                  ; Our 32 bit stack
        rb 4024
stend2:
