; $Id: bs3-cmn-RegCtxSave.asm $
;; @file
; BS3Kit - Bs3RegCtxSave.
;

;
; Copyright (C) 2007-2020 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"


BS3_EXTERN_SYSTEM16 Bs3Gdt
BS3_EXTERN_DATA16 g_bBs3CurrentMode
%if TMPL_BITS != 64
BS3_EXTERN_DATA16 g_uBs3CpuDetected
%endif
TMPL_BEGIN_TEXT



;;
; Saves the current register context.
;
; @param        pRegCtx
; @uses         None.
;
BS3_PROC_BEGIN_CMN Bs3RegCtxSave, BS3_PBC_HYBRID_SAFE
TONLY16 CPU 8086
        BS3_CALL_CONV_PROLOG 1
        push    xBP
        mov     xBP, xSP
        xPUSHF                          ; xBP - xCB*1: save the incoming flags exactly.
        push    xAX                     ; xBP - xCB*2: save incoming xAX
        push    xCX                     ; xBP - xCB*3: save incoming xCX
        push    xDI                     ; xBP - xCB*4: save incoming xDI
BONLY16 push    es                      ; xBP - xCB*5
BONLY16 push    ds                      ; xBP - xCB*6

        ;
        ; Clear the whole structure first.
        ;
        xor     xAX, xAX
        cld
        AssertCompileSizeAlignment(BS3REGCTX, 4)
%if TMPL_BITS == 16
        les     xDI, [xBP + xCB + cbCurRetAddr]
        mov     xCX, BS3REGCTX_size / 2
        rep stosw
%else
        mov     xDI, [xBP + xCB + cbCurRetAddr]
        mov     xCX, BS3REGCTX_size / 4
        rep stosd
%endif
        mov     xDI, [xBP + xCB + cbCurRetAddr]

        ;
        ; Save the current mode.
        ;
        mov     cl, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
        mov     [BS3_ONLY_16BIT(es:) xDI + BS3REGCTX.bMode], cl
%if TMPL_BITS == 16

        ;
        ; In 16-bit mode we could be running on really ancient CPUs, so check
        ; mode and detected CPU and proceed with care.
        ;
        cmp     cl, BS3_MODE_PP16
        jae     .save_full

        mov     cl, [BS3_DATA16_WRT(g_uBs3CpuDetected)]
        cmp     cl, BS3CPU_80386
        jae     .save_full

        ; load ES into DS so we can save some segment prefix bytes.
        push    es
        pop     ds

        ; 16-bit GPRs not on the stack.
        mov     [xDI + BS3REGCTX.rdx], dx
        mov     [xDI + BS3REGCTX.rbx], bx
        mov     [xDI + BS3REGCTX.rsi], si

        ; Join the common code.
        cmp     cl, BS3CPU_80286
        jb      .common_ancient
        CPU 286
        smsw    [xDI + BS3REGCTX.cr0]

        mov     cl, [xDI + BS3REGCTX.bMode] ; assumed by jump destination
        jmp     .common_80286

        CPU 386
%endif


.save_full:
        ;
        ; 80386 or later.
        ;
%if TMPL_BITS != 64
        ; Check for CR4 here while we've got a working DS in all contexts.
        test    byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8)
        jnz     .save_full_have_cr4
        or      byte [BS3_ONLY_16BIT(es:) xDI + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
.save_full_have_cr4:
%endif
%if TMPL_BITS == 16
        ; Load es into ds so we can save ourselves some segment prefix bytes.
        push    es
        pop     ds
%endif

        ; GPRs first.
        mov     [xDI + BS3REGCTX.rdx], sDX
        mov     [xDI + BS3REGCTX.rbx], sBX
        mov     [xDI + BS3REGCTX.rsi], sSI
%if TMPL_BITS == 64
        mov     [xDI + BS3REGCTX.r8], r8
        mov     [xDI + BS3REGCTX.r9], r9
        mov     [xDI + BS3REGCTX.r10], r10
        mov     [xDI + BS3REGCTX.r11], r11
        mov     [xDI + BS3REGCTX.r12], r12
        mov     [xDI + BS3REGCTX.r13], r13
        mov     [xDI + BS3REGCTX.r14], r14
        mov     [xDI + BS3REGCTX.r15], r15
%else
        or      byte [xDI + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64
%endif
%if TMPL_BITS == 16 ; Save high bits.
        mov     [xDI + BS3REGCTX.rax], eax
        mov     [xDI + BS3REGCTX.rcx], ecx
        mov     [xDI + BS3REGCTX.rdi], edi
        mov     [xDI + BS3REGCTX.rbp], ebp
        mov     [xDI + BS3REGCTX.rsp], esp
        pushfd
        pop     dword [xDI + BS3REGCTX.rflags]
%endif
%if TMPL_BITS != 64
        ; The VM flag is never on the stack, so derive it from the bMode we saved above.
        test    byte [xDI + BS3REGCTX.bMode], BS3_MODE_CODE_V86
        jz      .not_v8086
        or      byte [xDI + BS3REGCTX.rflags + 2], X86_EFL_VM >> 16
        mov     byte [xDI + BS3REGCTX.bCpl], 3
.not_v8086:
%endif

        ; 386 segment registers.
        mov     [xDI + BS3REGCTX.fs], fs
        mov     [xDI + BS3REGCTX.gs], gs

%if TMPL_BITS == 16 ; v8086 and real mode woes.
        mov     cl, [xDI + BS3REGCTX.bMode]
        cmp     cl, BS3_MODE_RM
        je      .common_full_control_regs
        test    cl, BS3_MODE_CODE_V86
        jnz     .common_full_no_control_regs
%endif
        mov     ax, ss
        test    al, 3
        jnz     .common_full_no_control_regs

        ; Control registers (ring-0 and real-mode only).
.common_full_control_regs:
        mov     sAX, cr0
        mov     [xDI + BS3REGCTX.cr0], sAX
        mov     sAX, cr2
        mov     [xDI + BS3REGCTX.cr2], sAX
        mov     sAX, cr3
        mov     [xDI + BS3REGCTX.cr3], sAX
%if TMPL_BITS != 64
        test    byte [xDI + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
        jnz     .common_80286
%endif
        mov     sAX, cr4
        mov     [xDI + BS3REGCTX.cr4], sAX
        jmp     .common_80286

.common_full_no_control_regs:
        or      byte [xDI + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR0_IS_MSW | BS3REG_CTX_F_NO_CR2_CR3 | BS3REG_CTX_F_NO_CR4
        smsw    [xDI + BS3REGCTX.cr0]

        ; 80286 control registers.
.common_80286:
TONLY16 CPU 286
%if TMPL_BITS != 64
        cmp     cl, BS3_MODE_RM
        je      .no_str_sldt
        test    cl, BS3_MODE_CODE_V86
        jnz     .no_str_sldt
%endif
        str     [xDI + BS3REGCTX.tr]
        sldt    [xDI + BS3REGCTX.ldtr]
        jmp     .common_ancient

.no_str_sldt:
        or      byte [xDI + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_TR_LDTR

        ; Common stuff - stuff on the stack, 286 segment registers.
.common_ancient:
TONLY16 CPU 8086
        mov     xAX, [xBP - xCB*1]
        mov     [xDI + BS3REGCTX.rflags], xAX
        mov     xAX, [xBP - xCB*2]
        mov     [xDI + BS3REGCTX.rax], xAX
        mov     xAX, [xBP - xCB*3]
        mov     [xDI + BS3REGCTX.rcx], xAX
        mov     xAX, [xBP - xCB*4]
        mov     [xDI + BS3REGCTX.rdi], xAX
        mov     xAX, [xBP]
        mov     [xDI + BS3REGCTX.rbp], xAX
        mov     xAX, [xBP + xCB]
        mov     [xDI + BS3REGCTX.rip], xAX
        lea     xAX, [xBP + xCB + cbCurRetAddr]
        mov     [xDI + BS3REGCTX.rsp], xAX

%if TMPL_BITS == 16
        mov     ax, [xBP + xCB + 2]
        mov     [xDI + BS3REGCTX.cs], ax
        mov     ax, [xBP - xCB*6]
        mov     [xDI + BS3REGCTX.ds], ax
        mov     ax, [xBP - xCB*5]
        mov     [xDI + BS3REGCTX.es], ax
%else
        mov     [xDI + BS3REGCTX.cs], cs
        mov     [xDI + BS3REGCTX.ds], ds
        mov     [xDI + BS3REGCTX.es], es
%endif
        mov     [xDI + BS3REGCTX.ss], ss

        ;
        ; Return.
        ;
.return:
BONLY16 pop     ds
BONLY16 pop     es
        pop     xDI
        pop     xCX
        pop     xAX
        xPOPF
        pop     xBP
        BS3_HYBRID_RET
BS3_PROC_END_CMN   Bs3RegCtxSave

