; $Id: EMAllA.asm 20278 2007-04-09 11:56:29Z sandervl $
;; @file
; EM Assembly Routines.
;

;
; Copyright (C) 2006-2007 innotek GmbH
;
; 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.
;

;*******************************************************************************
;* Header Files                                                                *
;*******************************************************************************
%include "VBox/asmdefs.mac"
%include "VBox/err.mac"
%include "VBox/x86.mac"

BEGINCODE

;;
; Emulate LOCK CMPXCHG instruction, CDECL calling conv.
; EMGCDECL(uint32_t) EMGCEmulateLockCmpXchg(RTGCPTR pu32Param1, uint32_t *pu32Param2, uint32_t u32Param3, size_t cbSize, uint32_t *pEflags);
;
; @returns eax=0 if data written, other code - invalid access, #PF was generated.
; @param    [esp + 04h]    Param 1 - First parameter - pointer to first parameter
; @param    [esp + 08h]    Param 2 - Second parameter - pointer to second parameter (eax)
; @param    [esp + 0ch]    Param 3 - Third parameter - third parameter
; @param    [esp + 10h]    Param 4 - Size of parameters, only 1/2/4 is valid.
; @param    [esp + 14h]    Param 4 - Pointer to eflags (out)
; @uses     eax, ecx, edx
;
align 16
BEGINPROC   EMGCEmulateLockCmpXchg
    push    ebx
    mov     ecx, [esp + 04h + 4]        ; ecx = first parameter
    mov     ebx, [esp + 08h + 4]        ; ebx = 2nd parameter (eax)
    mov     edx, [esp + 0ch + 4]        ; edx = third parameter
    mov     eax, [esp + 10h + 4]        ; eax = size of parameters

    cmp     al, 4
    je short .do_dword                  ; 4 bytes variant
    cmp     al, 2
    je short .do_word                   ; 2 byte variant
    cmp     al, 1
    je short .do_byte                   ; 1 bytes variant
    int3

.do_dword:
    ; load 2nd parameter's value
    mov     eax, dword [ebx]

    lock cmpxchg dword [ecx], edx            ; do 4 bytes CMPXCHG
    mov     dword [ebx], eax
    jmp     short .done

.do_word:
    ; load 2nd parameter's value
    mov     eax, dword [ebx]

    lock cmpxchg word [ecx], dx              ; do 2 bytes CMPXCHG
    mov     word [ebx], ax
    jmp     short .done

.do_byte:
    ; load 2nd parameter's value
    mov     eax, dword [ebx]

    lock cmpxchg byte [ecx], dl              ; do 1 bytes CMPXCHG
    mov     byte [ebx], al

.done:
    ; collect flags and return.
    pushf
    pop     eax

    mov     edx, [esp + 14h + 4]            ; eflags pointer
    mov     dword [edx], eax

    pop     ebx
    mov     eax, VINF_SUCCESS
    retn

; Read error - we will be here after our page fault handler.
GLOBALNAME EMGCEmulateLockCmpXchg_Error
    pop     ebx
    mov     eax, VERR_ACCESS_DENIED
    ret

ENDPROC     EMGCEmulateLockCmpXchg

;;
; Emulate CMPXCHG instruction, CDECL calling conv.
; EMGCDECL(uint32_t) EMGCEmulateCmpXchg(RTGCPTR pu32Param1, uint32_t *pu32Param2, uint32_t u32Param3, size_t cbSize, uint32_t *pEflags);
;
; @returns eax=0 if data written, other code - invalid access, #PF was generated.
; @param    [esp + 04h]    Param 1 - First parameter - pointer to first parameter
; @param    [esp + 08h]    Param 2 - Second parameter - pointer to second parameter (eax)
; @param    [esp + 0ch]    Param 3 - Third parameter - third parameter
; @param    [esp + 10h]    Param 4 - Size of parameters, only 1/2/4 is valid.
; @param    [esp + 14h]    Param 4 - Pointer to eflags (out)
; @uses     eax, ecx, edx
;
align 16
BEGINPROC   EMGCEmulateCmpXchg
    push    ebx
    mov     ecx, [esp + 04h + 4]        ; ecx = first parameter
    mov     ebx, [esp + 08h + 4]        ; ebx = 2nd parameter (eax)
    mov     edx, [esp + 0ch + 4]        ; edx = third parameter
    mov     eax, [esp + 10h + 4]        ; eax = size of parameters

    cmp     al, 4
    je short .do_dword                  ; 4 bytes variant
    cmp     al, 2
    je short .do_word                   ; 2 byte variant
    cmp     al, 1
    je short .do_byte                   ; 1 bytes variant
    int3

.do_dword:
    ; load 2nd parameter's value
    mov     eax, dword [ebx]

    cmpxchg dword [ecx], edx            ; do 4 bytes CMPXCHG
    mov     dword [ebx], eax
    jmp     short .done

.do_word:
    ; load 2nd parameter's value
    mov     eax, dword [ebx]

    cmpxchg word [ecx], dx              ; do 2 bytes CMPXCHG
    mov     word [ebx], ax
    jmp     short .done

.do_byte:
    ; load 2nd parameter's value
    mov     eax, dword [ebx]

    cmpxchg byte [ecx], dl              ; do 1 bytes CMPXCHG
    mov     byte [ebx], al

.done:
    ; collect flags and return.
    pushf
    pop     eax

    mov     edx, [esp + 14h + 4]        ; eflags pointer
    mov     dword [edx], eax

    pop     ebx
    mov     eax, VINF_SUCCESS
    retn

; Read error - we will be here after our page fault handler.
GLOBALNAME EMGCEmulateCmpXchg_Error
    pop     ebx
    mov     eax, VERR_ACCESS_DENIED
    ret
ENDPROC     EMGCEmulateCmpXchg

;;
; Emulate LOCK XADD instruction, CDECL calling conv.
; EMGCDECL(uint32_t) EMGCEmulateLockXAdd(RTGCPTR pu32Param1, uint32_t *pu32Param2, uint32_t u32Param3, size_t cbSize, uint32_t *pEflags);
;
; @returns eax=0 if data exchanged, other code - invalid access, #PF was generated.
; @param    [esp + 04h]    Param 1 - First parameter - pointer to first parameter
; @param    [esp + 08h]    Param 2 - Second parameter - pointer to second parameter (general register)
; @param    [esp + 0ch]    Param 3 - Size of parameters, only 1/2/4 is valid.
; @param    [esp + 10h]    Param 4 - Pointer to eflags (out)
; @uses     eax, ecx, edx
;
align 16
BEGINPROC   EMGCEmulateLockXAdd
    mov     ecx, [esp + 04h + 0]        ; ecx = first parameter
    mov     edx, [esp + 08h + 0]        ; edx = 2nd parameter
    mov     eax, [esp + 0ch + 0]        ; eax = size of parameters

    cmp     al, 4
    je short .do_dword                  ; 4 bytes variant
    cmp     al, 2
    je short .do_word                   ; 2 byte variant
    cmp     al, 1
    je short .do_byte                   ; 1 bytes variant
    int3

.do_dword:
    ; load 2nd parameter's value
    mov     eax, dword [edx]
    lock xadd dword [ecx], eax              ; do 4 bytes XADD
    mov     dword [edx], eax
    jmp     short .done

.do_word:
    ; load 2nd parameter's value
    mov     eax, dword [edx]
    lock xadd word [ecx], ax                ; do 2 bytes XADD
    mov     word [edx], ax
    jmp     short .done

.do_byte:
    ; load 2nd parameter's value
    mov     eax, dword [edx]
    lock xadd byte [ecx], al                ; do 1 bytes XADD
    mov     byte [edx], al

.done:
    ; collect flags and return.
    mov     edx, [esp + 10h + 0]            ; eflags pointer
    pushf
    pop     dword [edx]

    mov     eax, VINF_SUCCESS
    retn

; Read error - we will be here after our page fault handler.
GLOBALNAME EMGCEmulateLockXAdd_Error
    mov     eax, VERR_ACCESS_DENIED
    ret

ENDPROC     EMGCEmulateLockXAdd

;;
; Emulate XADD instruction, CDECL calling conv.
; EMGCDECL(uint32_t) EMGCEmulateXAdd(RTGCPTR pu32Param1, uint32_t *pu32Param2, uint32_t u32Param3, size_t cbSize, uint32_t *pEflags);
;
; @returns eax=0 if data written, other code - invalid access, #PF was generated.
; @param    [esp + 04h]    Param 1 - First parameter - pointer to first parameter
; @param    [esp + 08h]    Param 2 - Second parameter - pointer to second parameter (general register)
; @param    [esp + 0ch]    Param 3 - Size of parameters, only 1/2/4 is valid.
; @param    [esp + 10h]    Param 4 - Pointer to eflags (out)
; @uses     eax, ecx, edx
;
align 16
BEGINPROC   EMGCEmulateXAdd
    mov     ecx, [esp + 04h + 0]        ; ecx = first parameter
    mov     edx, [esp + 08h + 0]        ; edx = 2nd parameter (eax)
    mov     eax, [esp + 0ch + 0]        ; eax = size of parameters

    cmp     al, 4
    je short .do_dword                  ; 4 bytes variant
    cmp     al, 2
    je short .do_word                   ; 2 byte variant
    cmp     al, 1
    je short .do_byte                   ; 1 bytes variant
    int3

.do_dword:
    ; load 2nd parameter's value
    mov     eax, dword [edx]
    xadd    dword [ecx], eax            ; do 4 bytes XADD
    mov     dword [edx], eax
    jmp     short .done

.do_word:
    ; load 2nd parameter's value
    mov     eax, dword [edx]
    xadd    word [ecx], ax              ; do 2 bytes XADD
    mov     word [edx], ax
    jmp     short .done

.do_byte:
    ; load 2nd parameter's value
    mov     eax, dword [edx]
    xadd    byte [ecx], al              ; do 1 bytes XADD
    mov     byte [edx], al

.done:
    ; collect flags and return.
    mov     edx, [esp + 10h + 0]        ; eflags pointer
    pushf
    pop     dword [edx]

    mov     eax, VINF_SUCCESS
    retn

; Read error - we will be here after our page fault handler.
GLOBALNAME EMGCEmulateXAdd_Error
    mov     eax, VERR_ACCESS_DENIED
    ret
ENDPROC     EMGCEmulateXAdd
