;*****************************************************************************
;*
;*   Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
;*   This program and the accompanying materials                          
;*   are licensed and made available under the terms and conditions of the BSD License         
;*   which accompanies this distribution.  The full text of the license may be found at        
;*   http://opensource.org/licenses/bsd-license.php                                            
;*                                                                                             
;*   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
;*   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             
;*   
;*   Module Name:
;*
;*    Thunk.asm
;*  
;*   Abstract:
;*  
;*    Real mode thunk
;*  
;*****************************************************************************

    .686p

EXTERNDEF   C   mCode16Size:DWORD

CONST   SEGMENT FLAT    "DATA"  READONLY

mCode16Size     DD      _TEXT16SIZE

CONST   ENDS

_DATA   SEGMENT FLAT    "DATA"

NullSegSel      DQ      0
_16BitCsSel     LABEL   QWORD
                DW      -1
                DW      0
                DB      0
                DB      9bh
                DB      8fh             ; 16-bit segment
                DB      0
_16BitSsSel     LABEL   QWORD
                DW      -1
                DW      0
                DB      0
                DB      93h
                DB      8fh            ; 16-bit segment
                DB      0

_16Gdtr         LABEL   FWORD
                DW      $ - offset NullSegSel - 1
                DD      offset NullSegSel

_DATA   ENDS

_TEXT   SEGMENT FLAT    "CODE"  PARA

STACK_PARAM_SIZE  EQU  16

IA32_REGS   STRUC   4t
_EDI        DD      ?
_ESI        DD      ?
_EBP        DD      ?
_ESP        DD      ?
_EBX        DD      ?
_EDX        DD      ?
_ECX        DD      ?
_EAX        DD      ?
_DS         DW      ?
_ES         DW      ?
_FS         DW      ?
_GS         DW      ?
_EFLAGS     DD      ?
_EIP        DD      ?
_CS         DW      ?
_SS         DW      ?
IA32_REGS   ENDS

_STK16      STRUC   1t
RetEip      DD      ?
RetCs       DW      ?
ThunkFlags  DW      ?
SavedEsp    DD      ?
SavedSs     DW      ?
SavedGdtr   FWORD   ?
SavedCr0    DD      ?
SavedCr4    DD      ?
_STK16      ENDS

    ASSUME  ds:_DATA

__Thunk16   PROC    USES    ebp ebx esi edi ds  es  fs  gs
    ASSUME  esi:PTR IA32_REGS
    mov     esi, [esp + 36]
    movzx   edx, [esi]._SS
    mov     edi, [esi]._ESP
    add     edi, - sizeof (_STK16) - sizeof (IA32_REGS)
    push    edi                         ; save stack offset
    imul    eax, edx, 16                ; eax <- edx*16
    add     edi, eax                    ; edi <- linear address of 16-bit stack
    push    sizeof (IA32_REGS) / 4
    pop     ecx
    rep     movsd                       ; copy context to 16-bit stack

    ; copy eflags to stack frame
    mov     eax, [esi - sizeof(IA32_REGS)]._EFLAGS
    mov     [edi - sizeof(IA32_REGS) - STACK_PARAM_SIZE - 4], eax

    pop     ebx                         ; ebx <- 16-bit stack offset
    mov     eax, offset @F              ; return offset
    stosd
    mov     eax, cs                     ; return segment
    stosw
    mov     eax, [esp + 40]             ; THUNK flags
    stosw
    mov     eax, esp
    stosd                               ; save esp
    mov     eax, ss                     ; save ss
    stosw
    sgdt    fword ptr [edi]             ; save GDTR
    sidt    fword ptr [esp + 36]        ; save IDTR
    mov     esi, cr0
    mov     [edi + 6], esi              ; save CR0
    and     esi, NOT 80000001h          ; esi <- CR0 to set
    mov     eax, cr4
    mov     [edi + 10], eax             ; save CR4
    and     al, NOT 30h                 ; clear PAE & PSE
    mov     edi, edx                    ; edi <- 16-bit stack segment
    mov     edx, [esp + 44]
    shl     edx, 16
    push    edx
    pop     edx
    mov     dx, _16BitSsSel - NullSegSel
    lgdt    _16Gdtr                     ; load 16-bit GDTR
    DB      0eah
    DD      offset @16Bit
    DW      _16BitCsSel - NullSegSel    ; jmp far 8:@16Bit
@16Bit:
    mov     ss, dx
    mov     cr0, esi                    ; disable protected mode
    mov     cr4, eax                    ; disable PAE & PSE
    db      67h, 0FFh, 06Ch, 024h, 0FCh ; jmp     dword ptr [esp-4]
@@:
    xor     eax, eax
    mov     ax, ss
    shl     eax, 4
    add     eax, esp                    ; eax <- address of 16-bit stack
    lss     esp, fword ptr (_STK16 ptr [esp + sizeof (IA32_REGS)]).SavedEsp
    lidt    fword ptr [esp + 36]        ; restore IDTR
    ret
__Thunk16   ENDP

_TEXT   ENDS

_TEXT16 SEGMENT USE16   "CODE"  PARA

_Code16Addr PROC    C
_Code16Addr ENDP

RealMode    PROC
    mov     ss, di                      ; set up stack
    mov     esp, ebx
    lidt    fword ptr cs:[_16Idtr - _Code16Addr]
    popad
    pop     ds
    pop     es
    pop     fs
    pop     gs
    sub     esp, (sizeof(IA32_REGS) - 12) + STACK_PARAM_SIZE + 4
    popfd
    test    (_STK16 ptr [esp + STACK_PARAM_SIZE + sizeof(IA32_REGS)]).ThunkFlags, 1
    jz      @F
    pushf                               ; push Flags when it's INT#
@@:
    push    cs
;    push    @FarCallRet - _Code16Addr
    DB      68h                         ; push /iw
    DW      @FarCallRet - _Code16Addr
    jz      @F
    jmp     fword ptr [esp + 6 + STACK_PARAM_SIZE + sizeof(IA32_REGS) - 8]
@@:
    jmp     fword ptr [esp + 4 + STACK_PARAM_SIZE + sizeof(IA32_REGS) - 8]
@FarCallRet:
    add     esp, (sizeof(IA32_REGS) - 12) + STACK_PARAM_SIZE + 4
    pushfd
    push    gs
    push    fs
    push    es
    push    ds
    pushad
    cli
    DB      66h
    lgdt    (_STK16 ptr [esp + sizeof (IA32_REGS)]).SavedGdtr
    mov     eax, (_STK16 ptr [esp + sizeof (IA32_REGS)]).SavedCr4
    mov     cr4, eax
    mov     eax, (_STK16 ptr [esp + sizeof (IA32_REGS)]).SavedCr0
    mov     cr0, eax                    ; restore CR0
    jmp     fword ptr (_STK16 ptr [esp + sizeof (IA32_REGS)]).RetEip
RealMode    ENDP

_16Idtr     FWORD   (1 SHL 10) - 1

_TEXT16END:

_TEXT16SIZE = _TEXT16END - _Code16Addr

_TEXT16 ENDS

    END
