
;           Copyright Oliver Kowalke 2009.
;  Distributed under the Boost Software License, Version 1.0.
;     (See accompanying file LICENSE_1_0.txt or copy at
;           http://www.boost.org/LICENSE_1_0.txt)

;  ---------------------------------------------------------------------------------
;  |    0    |    1    |    2    |    3    |    4    |    5    |    6    |    7    |
;  ---------------------------------------------------------------------------------
;  |    0h   |   04h   |   08h   |   0ch   |   010h  |   014h  |   018h  |   01ch  |
;  ---------------------------------------------------------------------------------
;  | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo|  limit  |   base  |  fc_seh |   EDI   |
;  ---------------------------------------------------------------------------------
;  ---------------------------------------------------------------------------------
;  |    8    |    9    |   10    |    11   |    12   |    13   |    14   |    15   |
;  ---------------------------------------------------------------------------------
;  |   020h  |  024h   |  028h   |   02ch  |   030h  |   034h  |   038h  |   03ch  |
;  ---------------------------------------------------------------------------------
;  |   ESI   |   EBX   |   EBP   |   EIP   |   EXIT  |         | SEH NXT |SEH HNDLR|
;  ---------------------------------------------------------------------------------

.386
.XMM
.model flat, c
.code

jump_fcontext PROC EXPORT
    ; fourth arg of jump_fcontext() == flag indicating preserving FPU
    mov  ecx, [esp+010h]

    push  ebp  ; save EBP
    push  ebx  ; save EBX
    push  esi  ; save ESI
    push  edi  ; save EDI

    assume  fs:nothing
    ; load NT_TIB into ECX
    mov  edx, fs:[018h]
    assume  fs:error

    ; load current SEH exception list
    mov  eax, [edx]
    push  eax

    ; load current stack base
    mov  eax, [edx+04h]
    push  eax

    ; load current stack limit
    mov  eax, [edx+08h]
    push  eax

    ; load current deallocation stack
    mov  eax, [edx+0e0ch]
    push  eax

    ; load fiber local storage
    mov  eax, [edx+010h]
    push  eax

    ; prepare stack for FPU
    lea  esp, [esp-08h]

    ; test for flag preserve_fpu
    test  ecx, ecx
    je  nxt1

    ; save MMX control- and status-word
    stmxcsr  [esp]
    ; save x87 control-word
    fnstcw  [esp+04h]

nxt1:
    ; first arg of jump_fcontext() == context jumping from
    mov  eax, [esp+030h]

    ; store ESP (pointing to context-data) in EAX
    mov  [eax], esp

    ; second arg of jump_fcontext() == context jumping to
    mov  edx, [esp+034h]

    ; third arg of jump_fcontext() == value to be returned after jump
    mov  eax, [esp+038h]

    ; restore ESP (pointing to context-data) from EDX
    mov  esp, edx

    ; test for flag preserve_fpu
    test  ecx, ecx
    je  nxt2

    ; restore MMX control- and status-word
    ldmxcsr  [esp]
    ; restore x87 control-word
    fldcw  [esp+04h]

nxt2:
    ; prepare stack for FPU
    lea  esp, [esp+08h]

    assume  fs:nothing
    ; load NT_TIB into ECX
    mov  edx, fs:[018h]
    assume  fs:error

    ; restore fiber local storage
    pop  ecx
    mov  [edx+010h], ecx

    ; restore current deallocation stack
    pop  ecx
    mov  [edx+0e0ch], ecx

    ; restore current stack limit
    pop  ecx
    mov  [edx+08h], ecx

    ; restore current stack base
    pop  ecx
    mov  [edx+04h], ecx

    ; restore current SEH exception list
    pop  ecx
    mov  [edx], ecx

    pop  edi  ; save EDI
    pop  esi  ; save ESI
    pop  ebx  ; save EBX
    pop  ebp  ; save EBP

    ; restore return-address
    pop  edx

    ; use value in EAX as return-value after jump
    ; use value in EAX as first arg in context function
    mov  [esp+04h], eax

    ; indirect jump to context
    jmp  edx
jump_fcontext ENDP
END