; $Id: bs3-cmn-RegCtxSaveEx.asm 60749 2016-04-28 19:41:14Z vboxsync $
;; @file
; BS3Kit - Bs3RegCtxSaveEx.
;

;
; Copyright (C) 2007-2016 Oracle Corporation
;
; This file is part of VirtualBox Open Source Edition (OSE), as
; available from http://www.virtualbox.org. This file is free software;
; you can redistribute it and/or modify it under the terms of the GNU
; General Public License (GPL) as published by the Free Software
; Foundation, in version 2 as it comes in the "COPYING" file of the
; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
;
; The contents of this file may alternatively be used under the terms
; of the Common Development and Distribution License Version 1.0
; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
; VirtualBox OSE distribution, in which case the provisions of the
; CDDL are applicable instead of those of the GPL.
;
; You may elect to license modified versions of this file under the
; terms and conditions of either the GPL or the CDDL or both.
;

%include "bs3kit-template-header.mac"



;*********************************************************************************************************************************
;*  External Symbols                                                                                                             *
;*********************************************************************************************************************************
BS3_EXTERN_DATA16   g_bBs3CurrentMode

TMPL_BEGIN_TEXT
BS3_EXTERN_CMN      Bs3Panic
BS3_EXTERN_CMN      Bs3RegCtxSave
BS3_EXTERN_CMN      Bs3SwitchTo16Bit
%if TMPL_BITS != 64
BS3_EXTERN_CMN      Bs3SwitchTo16BitV86
%endif
%if TMPL_BITS != 32
BS3_EXTERN_CMN      Bs3SwitchTo32Bit
%endif
%if TMPL_BITS != 64
BS3_EXTERN_CMN      Bs3SwitchTo64Bit
%endif
%if TMPL_BITS == 16
BS3_EXTERN_CMN      Bs3SelRealModeDataToProtFar16
BS3_EXTERN_CMN      Bs3SelProtFar16DataToRealMode
BS3_EXTERN_CMN      Bs3SelRealModeDataToFlat
BS3_EXTERN_CMN      Bs3SelProtFar16DataToFlat
%else
BS3_EXTERN_CMN      Bs3SelFlatDataToProtFar16
%endif
%if TMPL_BITS == 32
BS3_EXTERN_CMN      Bs3SelFlatDataToRealMode
%endif

BS3_BEGIN_TEXT16
%if TMPL_BITS != 16
extern              _Bs3RegCtxSave_c16
extern              _Bs3SwitchTo%[TMPL_BITS]Bit_c16
%endif

BS3_BEGIN_TEXT32
%if TMPL_BITS != 32
extern              _Bs3RegCtxSave_c32
extern              _Bs3SwitchTo%[TMPL_BITS]Bit_c32
%endif
%if TMPL_BITS == 16
extern              _Bs3SwitchTo16BitV86_c32
%endif

BS3_BEGIN_TEXT64
%if TMPL_BITS != 64
extern              _Bs3RegCtxSave_c64
extern              _Bs3SwitchTo%[TMPL_BITS]Bit_c64
%endif

TMPL_BEGIN_TEXT



;;
; Saves the current register context.
;
; @param        pRegCtx
; @param        bBitMode     (8)
; @param        cbExtraStack (16)
; @uses         xAX, xDX, xCX
;
BS3_PROC_BEGIN_CMN Bs3RegCtxSaveEx, BS3_PBC_NEAR        ; Far stub generated by the makefile/bs3kit.h.
TONLY16 CPU 8086
        BS3_CALL_CONV_PROLOG 3
        push    xBP
        mov     xBP, xSP

        ;
        ; Get the CPU bitcount part of the current mode.
        ;
        mov     dl, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
        and     dl, BS3_MODE_CODE_MASK
%if TMPL_BITS == 16
        push    dx                          ; bp - 2: previous CPU mode (16-bit)
%endif

        ;
        ; Reserve extra stack space.  Make sure we've got 20h here in case we
        ; are saving a 64-bit context.
        ;
        mov     ax, [xBP + xCB + cbCurRetAddr + sCB + xCB]
%ifdef BS3_STRICT
        cmp     ax, 4096
        jb      .extra_stack_ok
        call    Bs3Panic
.extra_stack_ok:
%endif
        cmp     ax, 20h
        jae     .at_least_20h_extra_stack
        add     ax, 20h
.at_least_20h_extra_stack:
        sub     sp, ax

        ;
        ; Are we just saving the mode we're already in?
        ;
        mov     al, [xBP + xCB + cbCurRetAddr + sCB]
        and     al, BS3_MODE_CODE_MASK
        cmp     dl, al
        jne     .not_the_same_mode

%if TMPL_BITS == 16
        push    word [xBP + xCB + cbCurRetAddr + 2]
        push    word [xBP + xCB + cbCurRetAddr]
%elif TMPL_BITS == 32
        push    dword [xBP + xCB + cbCurRetAddr]
%endif
        call    Bs3RegCtxSave               ; 64-bit: rcx is untouched thus far.


        ;
        ; Return - no need to pop xAX and xDX as the last two
        ;          operations preserves all registers.
        ;
.return:
        mov     xSP, xBP
        pop     xBP
        BS3_CALL_CONV_EPILOG 3
        BS3_HYBRID_RET


        ;
        ; Turns out we have to do switch to a different bitcount before saving.
        ;
.not_the_same_mode:
        cmp     al, BS3_MODE_CODE_16
        je      .code_16

TONLY16 CPU 386
%if TMPL_BITS != 32
        cmp     al, BS3_MODE_CODE_32
        je      .code_32
%endif
%if TMPL_BITS != 64
        cmp     al, BS3_MODE_CODE_V86
        je      .code_v86
        cmp     al, BS3_MODE_CODE_64
        jne     .bad_input_mode
        jmp     .code_64
%endif

        ; Bad input (al=input, dl=current).
.bad_input_mode:
        call    Bs3Panic


        ;
        ; Save a 16-bit context.
        ;
        ; Convert pRegCtx to 16:16 protected mode and make sure we're in the
        ; 16-bit code segment.
        ;
.code_16:
%if TMPL_BITS == 16
 %ifdef BS3_STRICT
        cmp     dl, BS3_MODE_CODE_V86
        jne     .bad_input_mode
 %endif
        push    word [xBP + xCB + cbCurRetAddr + 2]
        push    word [xBP + xCB + cbCurRetAddr]
        call    Bs3SelRealModeDataToProtFar16
        add     sp, 4h
        push    dx                          ; Parameter #0 for _Bs3RegCtxSave_c16
        push    ax
%else
 %if TMPL_BITS == 32
        push    dword [xBP + xCB + cbCurRetAddr]
 %endif
        call    Bs3SelFlatDataToProtFar16   ; 64-bit: BS3_CALL not needed, ecx not touched thus far.
        mov     [xSP], eax                  ; Parameter #0 for _Bs3RegCtxSave_c16
        jmp     .code_16_safe_segment
        BS3_BEGIN_TEXT16
        BS3_SET_BITS TMPL_BITS
.code_16_safe_segment:
%endif
        call    Bs3SwitchTo16Bit
        BS3_SET_BITS 16

        call    _Bs3RegCtxSave_c16

%if TMPL_BITS == 16
        call    _Bs3SwitchTo16BitV86_c16
%else
        call    _Bs3SwitchTo%[TMPL_BITS]Bit_c16
%endif
        BS3_SET_BITS TMPL_BITS
        jmp     .return
        TMPL_BEGIN_TEXT

TONLY16 CPU 386


%if TMPL_BITS != 64
        ;
        ; Save a v8086 context.
        ;
.code_v86:
 %if TMPL_BITS == 16
  %ifdef BS3_STRICT
        cmp     dl, BS3_MODE_CODE_16
        jne     .bad_input_mode
  %endif
        push    word [xBP + xCB + cbCurRetAddr + 2]
        push    word [xBP + xCB + cbCurRetAddr]
        call    Bs3SelProtFar16DataToRealMode
        add     sp, 4h
        push    dx                          ; Parameter #0 for _Bs3RegCtxSave_c16
        push    ax
 %else
        push    dword [xBP + xCB + cbCurRetAddr]
        call    Bs3SelFlatDataToRealMode
        mov     [xSP], eax                  ; Parameter #0 for _Bs3RegCtxSave_c16
        jmp     .code_v86_safe_segment
        BS3_BEGIN_TEXT16
        BS3_SET_BITS TMPL_BITS
.code_v86_safe_segment:
 %endif
        call    Bs3SwitchTo16BitV86
        BS3_SET_BITS 16

        call    _Bs3RegCtxSave_c16

        call    _Bs3SwitchTo%[TMPL_BITS]Bit_c16
        BS3_SET_BITS TMPL_BITS
        jmp     .return
TMPL_BEGIN_TEXT
%endif


%if TMPL_BITS != 32
        ;
        ; Save a 32-bit context.
        ;
.code_32:
 %if TMPL_BITS == 16
        push    word [xBP + xCB + cbCurRetAddr + 2]
        push    word [xBP + xCB + cbCurRetAddr]
        test    dl, BS3_MODE_CODE_V86
        jnz     .code_32_from_v86
        call    Bs3SelProtFar16DataToFlat
        jmp     .code_32_flat_ptr
.code_32_from_v86:
        call    Bs3SelRealModeDataToFlat
.code_32_flat_ptr:
        add     sp, 4h
        push    dx                          ; Parameter #0 for _Bs3RegCtxSave_c32
        push    ax
 %else
        mov     [rsp], ecx                  ; Parameter #0 for _Bs3RegCtxSave_c16
 %endif
        call    Bs3SwitchTo32Bit
        BS3_SET_BITS 32

        call    _Bs3RegCtxSave_c32

 %if TMPL_BITS == 16
        cmp     byte [bp - 2], BS3_MODE_CODE_V86
        je      .code_32_back_to_v86
        call    _Bs3SwitchTo16Bit_c32
        BS3_SET_BITS TMPL_BITS
        jmp     .return
.code_32_back_to_v86:
        BS3_SET_BITS 32
        call    _Bs3SwitchTo16BitV86_c32
 %else
        call    _Bs3SwitchTo64Bit_c32
 %endif
        BS3_SET_BITS TMPL_BITS
        jmp     .return
%endif


%if TMPL_BITS != 64
        ;
        ; Save a 64-bit context.
        ;
        CPU     x86-64
.code_64:
 %if TMPL_BITS == 16
  %ifdef BS3_STRICT
        cmp     dl, BS3_MODE_CODE_16
        jne     .bad_input_mode
  %endif
        push    word [xBP + xCB + cbCurRetAddr + 2]
        push    word [xBP + xCB + cbCurRetAddr]
        call    Bs3SelProtFar16DataToFlat
        add     sp, 4h
        mov     cx, dx                      ; Parameter #0 for _Bs3RegCtxSave_c64
        shl     ecx, 16
        mov     cx, ax
 %else
        mov     ecx, [xBP + xCB + cbCurRetAddr] ; Parameter #0 for _Bs3RegCtxSave_c64
 %endif
        call    Bs3SwitchTo64Bit            ; (preserves all 32-bit GPRs)
        BS3_SET_BITS 64

        call    _Bs3RegCtxSave_c64          ; No BS3_CALL as rcx is already ready.

        call    _Bs3SwitchTo%[TMPL_BITS]Bit_c64
        BS3_SET_BITS TMPL_BITS
        jmp     .return
%endif
BS3_PROC_END_CMN   Bs3RegCtxSaveEx


