Initial import.

git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
bbahnsen
2006-04-21 22:54:32 +00:00
commit 878ddf1fc3
2651 changed files with 624620 additions and 0 deletions

View File

@@ -0,0 +1,148 @@
page ,132
title VM ASSEMBLY LANGUAGE ROUTINES
;****************************************************************************
;*
;* Copyright (c) 2006, Intel Corporation
;* All rights reserved. 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.
;*
;****************************************************************************
;****************************************************************************
; REV 1.0
;****************************************************************************
;
; Rev Date Description
; --- -------- ------------------------------------------------------------
; 1.0 03/14/01 Initial creation of file.
;
;****************************************************************************
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; This code provides low level routines that support the Virtual Machine
; for option ROMs.
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;---------------------------------------------------------------------------
; Equate files needed.
;---------------------------------------------------------------------------
.XLIST
.LIST
;---------------------------------------------------------------------------
; Assembler options
;---------------------------------------------------------------------------
.686p
.model flat
.code
;---------------------------------------------------------------------------
;;GenericPostSegment SEGMENT USE16
;---------------------------------------------------------------------------
;****************************************************************************
; EbcLLCALLEXNative
;
; This function is called to execute an EBC CALLEX instruction
; to native code.
; This instruction requires that we thunk out to external native
; code. For IA32, we simply switch stacks and jump to the
; specified function. On return, we restore the stack pointer
; to its original location.
;
; Destroys no working registers.
;****************************************************************************
; VOID EbcLLCALLEXNative(UINTN FuncAddr, UINTN NewStackPointer, VOID *FramePtr)
_EbcLLCALLEXNative PROC NEAR PUBLIC
push ebp
mov ebp, esp ; standard function prolog
; Get function address in a register
; mov ecx, FuncAddr => mov ecx, dword ptr [FuncAddr]
mov ecx, dword ptr [esp]+8
; Set stack pointer to new value
; mov eax, NewStackPointer => mov eax, dword ptr [NewSp]
mov eax, dword ptr [esp] + 0Ch
mov esp, eax
; Now call the external routine
call ecx
; ebp is preserved by the callee. In this function it
; equals the original esp, so set them equal
mov esp, ebp
; Standard function epilog
mov esp, ebp
pop ebp
ret
_EbcLLCALLEXNative ENDP
; UINTN EbcLLGetEbcEntryPoint(VOID);
; Routine Description:
; The VM thunk code stuffs an EBC entry point into a processor
; register. Since we can't use inline assembly to get it from
; the interpreter C code, stuff it into the return value
; register and return.
;
; Arguments:
; None.
;
; Returns:
; The contents of the register in which the entry point is passed.
;
_EbcLLGetEbcEntryPoint PROC NEAR PUBLIC
ret
_EbcLLGetEbcEntryPoint ENDP
;/*++
;
;Routine Description:
;
; Return the caller's value of the stack pointer.
;
;Arguments:
;
; None.
;
;Returns:
;
; The current value of the stack pointer for the caller. We
; adjust it by 4 here because when they called us, the return address
; is put on the stack, thereby lowering it by 4 bytes.
;
;--*/
; UINTN EbcLLGetStackPointer()
_EbcLLGetStackPointer PROC NEAR PUBLIC
mov eax, esp ; get current stack pointer
add eax, 4 ; stack adjusted by this much when we were called
ret
_EbcLLGetStackPointer ENDP
; UINT64 EbcLLGetReturnValue(VOID);
; Routine Description:
; When EBC calls native, on return the VM has to stuff the return
; value into a VM register. It's assumed here that the value is still
; in the register, so simply return and the caller should get the
; return result properly.
;
; Arguments:
; None.
;
; Returns:
; The unmodified value returned by the native code.
;
_EbcLLGetReturnValue PROC NEAR PUBLIC
ret
_EbcLLGetReturnValue ENDP
END

View File

@@ -0,0 +1,482 @@
/*++
Copyright (c) 2006, Intel Corporation
All rights reserved. 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:
EbcSupport.c
Abstract:
This module contains EBC support routines that are customized based on
the target processor.
--*/
#include "EbcInt.h"
#include "EbcExecute.h"
//
// NOTE: This is the stack size allocated for the interpreter
// when it executes an EBC image. The requirements can change
// based on whether or not a debugger is present, and other
// platform-specific configurations.
//
#define VM_STACK_SIZE (1024 * 4)
#define EBC_THUNK_SIZE 32
VOID
EbcLLCALLEX (
IN VM_CONTEXT *VmPtr,
IN UINTN FuncAddr,
IN UINTN NewStackPointer,
IN VOID *FramePtr,
IN UINT8 Size
)
/*++
Routine Description:
This function is called to execute an EBC CALLEX instruction.
The function check the callee's content to see whether it is common native
code or a thunk to another piece of EBC code.
If the callee is common native code, use EbcLLCAllEXASM to manipulate,
otherwise, set the VM->IP to target EBC code directly to avoid another VM
be startup which cost time and stack space.
Arguments:
VmPtr - Pointer to a VM context.
FuncAddr - Callee's address
NewStackPointer - New stack pointer after the call
FramePtr - New frame pointer after the call
Size - The size of call instruction
Returns:
None.
--*/
{
UINTN IsThunk;
UINTN TargetEbcAddr;
IsThunk = 1;
TargetEbcAddr = 0;
//
// Processor specific code to check whether the callee is a thunk to EBC.
//
if (*((UINT8 *)FuncAddr) != 0xB8) {
IsThunk = 0;
goto Action;
}
if (*((UINT8 *)FuncAddr + 1) != 0xBC) {
IsThunk = 0;
goto Action;
}
if (*((UINT8 *)FuncAddr + 2) != 0x2E) {
IsThunk = 0;
goto Action;
}
if (*((UINT8 *)FuncAddr + 3) != 0x11) {
IsThunk = 0;
goto Action;
}
if (*((UINT8 *)FuncAddr + 4) != 0xCA) {
IsThunk = 0;
goto Action;
}
if (*((UINT8 *)FuncAddr + 5) != 0xB8) {
IsThunk = 0;
goto Action;
}
if (*((UINT8 *)FuncAddr + 10) != 0xB9) {
IsThunk = 0;
goto Action;
}
if (*((UINT8 *)FuncAddr + 15) != 0xFF) {
IsThunk = 0;
goto Action;
}
if (*((UINT8 *)FuncAddr + 16) != 0xE1) {
IsThunk = 0;
goto Action;
}
TargetEbcAddr = ((UINTN)(*((UINT8 *)FuncAddr + 9)) << 24) + ((UINTN)(*((UINT8 *)FuncAddr + 8)) << 16) +
((UINTN)(*((UINT8 *)FuncAddr + 7)) << 8) + ((UINTN)(*((UINT8 *)FuncAddr + 6)));
Action:
if (IsThunk == 1){
//
// The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
// put our return address and frame pointer on the VM stack.
// Then set the VM's IP to new EBC code.
//
VmPtr->R[0] -= 8;
VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
VmPtr->R[0] -= 8;
VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (UINTN) (VmPtr->Ip + Size));
VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
} else {
//
// The callee is not a thunk to EBC, call native code.
//
EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
//
// Get return value and advance the IP.
//
VmPtr->R[7] = EbcLLGetReturnValue ();
VmPtr->Ip += Size;
}
}
STATIC
UINT64
EbcInterpret (
IN OUT UINTN Arg1,
IN OUT UINTN Arg2,
IN OUT UINTN Arg3,
IN OUT UINTN Arg4,
IN OUT UINTN Arg5,
IN OUT UINTN Arg6,
IN OUT UINTN Arg7,
IN OUT UINTN Arg8
)
/*++
Routine Description:
Begin executing an EBC image. The address of the entry point is passed
in via a processor register, so we'll need to make a call to get the
value.
Arguments:
None. Since we're called from a fixed up thunk (which we want to keep
small), our only so-called argument is the EBC entry point passed in
to us in a processor register.
Returns:
The value returned by the EBC application we're going to run.
--*/
{
//
// Create a new VM context on the stack
//
VM_CONTEXT VmContext;
UINTN Addr;
//
// Get the EBC entry point from the processor register.
//
Addr = EbcLLGetEbcEntryPoint ();
//
// Now clear out our context
//
ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
//
// Set the VM instruction pointer to the correct location in memory.
//
VmContext.Ip = (VMIP) Addr;
//
// Initialize the stack pointer for the EBC. Get the current system stack
// pointer and adjust it down by the max needed for the interpreter.
//
Addr = EbcLLGetStackPointer ();
VmContext.R[0] = (UINT64) Addr;
VmContext.R[0] -= VM_STACK_SIZE;
//
// Align the stack on a natural boundary
//
VmContext.R[0] &= ~(sizeof (UINTN) - 1);
//
// Put a magic value in the stack gap, then adjust down again
//
*(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) VM_STACK_KEY_VALUE;
VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
VmContext.R[0] -= sizeof (UINTN);
//
// For IA32, this is where we say our return address is
//
VmContext.StackRetAddr = (UINT64) VmContext.R[0];
VmContext.LowStackTop = (UINTN) VmContext.R[0];
//
// We need to keep track of where the EBC stack starts. This way, if the EBC
// accesses any stack variables above its initial stack setting, then we know
// it's accessing variables passed into it, which means the data is on the
// VM's stack.
// When we're called, on the stack (high to low) we have the parameters, the
// return address, then the saved ebp. Save the pointer to the return address.
// EBC code knows that's there, so should look above it for function parameters.
// The offset is the size of locals (VMContext + Addr + saved ebp).
// Note that the interpreter assumes there is a 16 bytes of return address on
// the stack too, so adjust accordingly.
// VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
//
VmContext.HighStackBottom = (UINTN) &Arg1 - 16;
//
// Begin executing the EBC code
//
EbcExecute (&VmContext);
//
// Return the value in R[7] unless there was an error
//
return (UINT64) VmContext.R[7];
}
STATIC
UINT64
ExecuteEbcImageEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
/*++
Routine Description:
Begin executing an EBC image. The address of the entry point is passed
in via a processor register, so we'll need to make a call to get the
value.
Arguments:
ImageHandle - image handle for the EBC application we're executing
SystemTable - standard system table passed into an driver's entry point
Returns:
The value returned by the EBC application we're going to run.
--*/
{
//
// Create a new VM context on the stack
//
VM_CONTEXT VmContext;
UINTN Addr;
//
// Get the EBC entry point from the processor register. Make sure you don't
// call any functions before this or you could mess up the register the
// entry point is passed in.
//
Addr = EbcLLGetEbcEntryPoint ();
//
// Print(L"*** Thunked into EBC entry point - ImageHandle = 0x%X\n", (UINTN)ImageHandle);
// Print(L"EBC entry point is 0x%X\n", (UINT32)(UINTN)Addr);
//
// Now clear out our context
//
ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
//
// Save the image handle so we can track the thunks created for this image
//
VmContext.ImageHandle = ImageHandle;
VmContext.SystemTable = SystemTable;
//
// Set the VM instruction pointer to the correct location in memory.
//
VmContext.Ip = (VMIP) Addr;
//
// Initialize the stack pointer for the EBC. Get the current system stack
// pointer and adjust it down by the max needed for the interpreter.
//
Addr = EbcLLGetStackPointer ();
VmContext.R[0] = (UINT64) Addr;
VmContext.R[0] -= VM_STACK_SIZE;
//
// Put a magic value in the stack gap, then adjust down again
//
*(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) VM_STACK_KEY_VALUE;
VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
VmContext.R[0] -= sizeof (UINTN);
//
// Align the stack on a natural boundary
// VmContext.R[0] &= ~(sizeof(UINTN) - 1);
//
VmContext.StackRetAddr = (UINT64) VmContext.R[0];
VmContext.LowStackTop = (UINTN) VmContext.R[0];
//
// VM pushes 16-bytes for return address. Simulate that here.
//
VmContext.HighStackBottom = (UINTN) &ImageHandle - 16;
//
// Begin executing the EBC code
//
EbcExecute (&VmContext);
//
// Return the value in R[7] unless there was an error
//
return (UINT64) VmContext.R[7];
}
EFI_STATUS
EbcCreateThunks (
IN EFI_HANDLE ImageHandle,
IN VOID *EbcEntryPoint,
OUT VOID **Thunk,
IN UINT32 Flags
)
/*++
Routine Description:
Create an IA32 thunk for the given EBC entry point.
Arguments:
ImageHandle - Handle of image for which this thunk is being created
EbcEntryPoint - Address of the EBC code that the thunk is to call
Thunk - Returned thunk we create here
Returns:
Standard EFI status.
--*/
{
UINT8 *Ptr;
UINT8 *ThunkBase;
UINT32 I;
UINT32 Addr;
INT32 Size;
INT32 ThunkSize;
EFI_STATUS Status;
//
// Check alignment of pointer to EBC code
//
if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
return EFI_INVALID_PARAMETER;
}
Size = EBC_THUNK_SIZE;
ThunkSize = Size;
Status = gBS->AllocatePool (
EfiBootServicesData,
Size,
(VOID *) &Ptr
);
if (Status != EFI_SUCCESS) {
return EFI_OUT_OF_RESOURCES;
}
//
// Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
//
// Save the start address so we can add a pointer to it to a list later.
//
ThunkBase = Ptr;
//
// Give them the address of our buffer we're going to fix up
//
*Thunk = (VOID *) Ptr;
//
// Add a magic code here to help the VM recognize the thunk..
// mov eax, 0xca112ebc => B8 BC 2E 11 CA
//
*Ptr = 0xB8;
Ptr++;
Size--;
Addr = (UINT32) 0xCA112EBC;
for (I = 0; I < sizeof (Addr); I++) {
*Ptr = (UINT8) (UINTN) Addr;
Addr >>= 8;
Ptr++;
Size--;
}
//
// Add code bytes to load up a processor register with the EBC entry point.
// mov eax, 0xaa55aa55 => B8 55 AA 55 AA
// The first 8 bytes of the thunk entry is the address of the EBC
// entry point.
//
*Ptr = 0xB8;
Ptr++;
Size--;
Addr = (UINT32) EbcEntryPoint;
for (I = 0; I < sizeof (Addr); I++) {
*Ptr = (UINT8) (UINTN) Addr;
Addr >>= 8;
Ptr++;
Size--;
}
//
// Stick in a load of ecx with the address of appropriate VM function.
// mov ecx 12345678h => 0xB9 0x78 0x56 0x34 0x12
//
if (Flags & FLAG_THUNK_ENTRY_POINT) {
Addr = (UINT32) (UINTN) ExecuteEbcImageEntryPoint;
} else {
Addr = (UINT32) (UINTN) EbcInterpret;
}
//
// MOV ecx
//
*Ptr = 0xB9;
Ptr++;
Size--;
for (I = 0; I < sizeof (Addr); I++) {
*Ptr = (UINT8) Addr;
Addr >>= 8;
Ptr++;
Size--;
}
//
// Stick in jump opcode bytes for jmp ecx => 0xFF 0xE1
//
*Ptr = 0xFF;
Ptr++;
Size--;
*Ptr = 0xE1;
Size--;
//
// Double check that our defined size is ok (application error)
//
if (Size < 0) {
ASSERT (FALSE);
return EFI_BUFFER_TOO_SMALL;
}
//
// Add the thunk to the list for this image. Do this last since the add
// function flushes the cache for us.
//
EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
return EFI_SUCCESS;
}

View File

@@ -0,0 +1,622 @@
TITLE Ia32math.asm: Generic math routines for EBC interpreter running on IA32 processor
;------------------------------------------------------------------------------
;
; Copyright (c) 2006, Intel Corporation
; All rights reserved. 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:
;
; Ia32math.asm
;
; Abstract:
;
; Generic math routines for EBC interpreter running on IA32 processor
;
;------------------------------------------------------------------------------
.686P
.XMM
.MODEL SMALL
.CODE
LeftShiftU64 PROTO C Operand: QWORD, CountIn: QWORD
RightShiftU64 PROTO C Operand: QWORD, CountIn: QWORD
ARightShift64 PROTO C Operand: QWORD, CountIn: QWORD
MulU64x64 PROTO C Value1: QWORD, Value2: QWORD, ResultHigh: DWORD
MulS64x64 PROTO C Value1: QWORD, Value2: QWORD, ResultHigh: DWORD
DivU64x64 PROTO C Dividend: QWORD, Divisor: QWORD, Remainder: DWORD, Error: DWORD
DivS64x64 PROTO C Dividend: QWORD, Divisor: QWORD, Remainder: DWORD, Error: DWORD
LeftShiftU64 PROC C Operand: QWORD, CountIn: QWORD
;------------------------------------------------------------------------------
; UINT64
; LeftShiftU64 (
; IN UINT64 Operand,
; IN UINT64 CountIn
; )
;
; Routine Description:
;
; Left-shift a 64-bit value.
;
; Arguments:
;
; Operand - the value to shift
; Count - shift count
;
; Returns:
;
; Operand << Count
;------------------------------------------------------------------------------
push ecx
;
; if (CountIn > 63) return 0;
;
cmp dword ptr CountIn[4], 0
jne _LeftShiftU64_Overflow
mov ecx, dword ptr CountIn[0]
cmp ecx, 63
jbe _LeftShiftU64_Calc
_LeftShiftU64_Overflow:
xor eax, eax
xor edx, edx
jmp _LeftShiftU64_Done
_LeftShiftU64_Calc:
mov eax, dword ptr Operand[0]
mov edx, dword ptr Operand[4]
shld edx, eax, cl
shl eax, cl
cmp ecx, 32
jc short _LeftShiftU64_Done
mov edx, eax
xor eax, eax
_LeftShiftU64_Done:
pop ecx
ret
LeftShiftU64 ENDP
RightShiftU64 PROC C Operand: QWORD, CountIn: QWORD
;------------------------------------------------------------------------------
; UINT64
; RightShiftU64 (
; IN UINT64 Operand,
; IN UINT64 CountIn
; )
;
; Routine Description:
;
; Right-shift an unsigned 64-bit value.
;
; Arguments:
;
; Operand - the value to shift
; Count - shift count
;
; Returns:
;
; Operand >> Count
;------------------------------------------------------------------------------
push ecx
;
; if (CountIn > 63) return 0;
;
cmp dword ptr CountIn[4], 0
jne _RightShiftU64_Overflow
mov ecx, dword ptr CountIn[0]
cmp ecx, 63
jbe _RightShiftU64_Calc
_RightShiftU64_Overflow:
xor eax, eax
xor edx, edx
jmp _RightShiftU64_Done
_RightShiftU64_Calc:
mov eax, dword ptr Operand[0]
mov edx, dword ptr Operand[4]
shrd edx, eax, cl
shr eax, cl
cmp ecx, 32
jc short _RightShiftU64_Done
mov eax, edx
xor edx, edx
_RightShiftU64_Done:
pop ecx
ret
RightShiftU64 ENDP
ARightShift64 PROC C Operand: QWORD, CountIn: QWORD
;------------------------------------------------------------------------------
; INT64
; ARightShift64 (
; IN INT64 Operand,
; IN UINT64 CountIn
; )
;
; Routine Description:
;
; Arithmatic shift a 64 bit signed value.
;
; Arguments:
;
; Operand - the value to shift
; Count - shift count
;
; Returns:
;
; Operand >> Count
;------------------------------------------------------------------------------
push ecx
;
; If they exceeded the max shift count, then return either 0 or all F's
; depending on the sign bit.
;
cmp dword ptr CountIn[4], 0
jne _ARightShiftU64_Overflow
mov ecx, dword ptr CountIn[0]
cmp ecx, 63
jbe _ARightShiftU64_Calc
_ARightShiftU64_Overflow:
;
; Check the sign bit of Operand
;
bt dword ptr Operand[4], 31
jnc _ARightShiftU64_Return_Zero
;
; return -1
;
or eax, 0FFFFFFFFh
or edx, 0FFFFFFFFh
jmp _ARightShiftU64_Done
_ARightShiftU64_Return_Zero:
xor eax, eax
xor edx, edx
jmp _ARightShiftU64_Done
_ARightShiftU64_Calc:
mov eax, dword ptr Operand[0]
mov edx, dword ptr Operand[4]
shrd eax, edx, cl
sar edx, cl
cmp ecx, 32
jc short _ARightShiftU64_Done
;
; if ecx >= 32, then eax = edx, and edx = sign bit
;
mov eax, edx
sar edx, 31
_ARightShiftU64_Done:
pop ecx
ret
ARightShift64 ENDP
MulU64x64 PROC C Value1: QWORD, Value2: QWORD, ResultHigh: DWORD
;------------------------------------------------------------------------------
; UINT64
; MulU64x64 (
; UINT64 Value1,
; UINT64 Value2,
; UINT64 *ResultHigh
; )
;
; Routine Description:
;
; Multiply two unsigned 64-bit values.
;
; Arguments:
;
; Value1 - first value to multiply
; Value2 - value to multiply by Value1
; ResultHigh - result to flag overflows
;
; Returns:
;
; Value1 * Value2
; The 128-bit result is the concatenation of *ResultHigh and the return value
;------------------------------------------------------------------------------
push ebx
push ecx
mov ebx, ResultHigh ; ebx points to the high 4 words of result
;
; The result consists of four double-words.
; Here we assume their names from low to high: dw0, dw1, dw2, dw3
;
mov eax, dword ptr Value1[0]
mul dword ptr Value2[0]
push eax ; eax contains final result of dw0, push it
mov ecx, edx ; ecx contains partial result of dw1
mov eax, dword ptr Value1[4]
mul dword ptr Value2[0]
add ecx, eax ; add eax to partial result of dw1
adc edx, 0
mov dword ptr [ebx], edx ; lower double-word of ResultHigh contains partial result of dw2
mov eax, dword ptr Value1[0]
mul dword ptr Value2[4]
add ecx, eax ; add eax to partial result of dw1
push ecx ; ecx contains final result of dw1, push it
adc edx, 0
mov ecx, edx ; ecx contains partial result of dw2, together with ResultHigh
mov eax, dword ptr Value1[4]
mul dword ptr Value2[4]
add ecx, eax ; add eax to partial result of dw2
adc edx, 0
add dword ptr [ebx], ecx ; lower double-word of ResultHigh contains final result of dw2
adc edx, 0
mov dword ptr [ebx + 4], edx ; high double-word of ResultHigh contains final result of dw3
pop edx ; edx contains the final result of dw1
pop eax ; edx contains the final result of dw0
pop ecx
pop ebx
ret
MulU64x64 ENDP
MulS64x64 PROC C Value1: QWORD, Value2: QWORD, ResultHigh: DWORD
;------------------------------------------------------------------------------
; INT64
; MulS64x64 (
; INT64 Value1,
; INT64 Value2,
; INT64 *ResultHigh
; )
;
; Routine Description:
;
; Multiply two signed 64-bit values.
;
; Arguments:
;
; Value1 - first value to multiply
; Value2 - value to multiply by Value1
; ResultHigh - result to flag overflows
;
; Returns:
;
; Value1 * Value2
; The 128-bit result is the concatenation of *ResultHigh and the return value
;------------------------------------------------------------------------------
push ebx
push ecx
mov ebx, ResultHigh ; ebx points to the high 4 words of result
xor ecx, ecx ; the lowest bit of ecx flags the sign
mov edx, dword ptr Value1[4]
bt edx, 31
jnc short _MulS64x64_A_Positive
;
; a is negative
;
mov eax, dword ptr Value1[0]
not edx
not eax
add eax, 1
adc edx, 0
mov dword ptr Value1[0], eax
mov dword ptr Value1[4], edx
btc ecx, 0
_MulS64x64_A_Positive:
mov edx, dword ptr Value2[4]
bt edx, 31
jnc short _MulS64x64_B_Positive
;
; b is negative
;
mov eax, dword ptr Value2[0]
not edx
not eax
add eax, 1
adc edx, 0
mov dword ptr Value2[0], eax
mov dword ptr Value2[4], edx
btc ecx, 0
_MulS64x64_B_Positive:
invoke MulU64x64, Value1, Value2, ResultHigh
bt ecx, 0
jnc short _MulS64x64_Done
;
;negate the result
;
not eax
not edx
not dword ptr [ebx]
not dword ptr [ebx + 4]
add eax, 1
adc edx, 0
adc dword ptr [ebx], 0
adc dword ptr [ebx + 4], 0
_MulS64x64_Done:
pop ecx
pop ebx
ret
MulS64x64 ENDP
DivU64x64 PROC C Dividend: QWORD, Divisor: QWORD, Remainder: DWORD, Error: DWORD,
;------------------------------------------------------------------------------
; UINT64
; DivU64x64 (
; IN UINT64 Dividend,
; IN UINT64 Divisor,
; OUT UINT64 *Remainder OPTIONAL,
; OUT UINT32 *Error
; )
;
; Routine Description:
;
; This routine allows a 64 bit value to be divided with a 64 bit value returns
; 64bit result and the Remainder
;
; Arguments:
;
; Dividend - dividend
; Divisor - divisor
; ResultHigh - result to flag overflows
; Error - flag for error
;
; Returns:
;
; Dividend / Divisor
; Remainder = Dividend mod Divisor
;------------------------------------------------------------------------------
push ecx
mov eax, Error
mov dword ptr [eax], 0
cmp dword ptr Divisor[0], 0
jne _DivU64x64_Valid
cmp dword ptr Divisor[4], 0
jne _DivU64x64_Valid
;
; the divisor is zero
;
mov dword ptr [eax], 1
cmp Remainder, 0
je _DivU64x64_Invalid_Return
;
; fill the remainder if the pointer is not null
;
mov eax, Remainder
mov dword ptr [eax], 0
mov dword ptr [eax + 4], 80000000h
_DivU64x64_Invalid_Return:
xor eax, eax
mov edx, 80000000h
jmp _DivU64x64_Done
_DivU64x64_Valid:
;
; let edx and eax contain the intermediate result of remainder
;
xor edx, edx
xor eax, eax
mov ecx, 64
_DivU64x64_Wend:
;
; shift dividend left one
;
shl dword ptr Dividend[0], 1
rcl dword ptr Dividend[4], 1
;
; rotate intermediate result of remainder left one
;
rcl eax, 1
rcl edx, 1
cmp edx, dword ptr Divisor[4]
ja _DivU64x64_Sub_Divisor
jb _DivU64x64_Cont
cmp eax, dword ptr Divisor[0]
jb _DivU64x64_Cont
_DivU64x64_Sub_Divisor:
;
; If intermediate result of remainder is larger than
; or equal to divisor, then set the lowest bit of dividend,
; and subtract divisor from intermediate remainder
;
bts dword ptr Dividend[0], 0
sub eax, dword ptr Divisor[0]
sbb edx, dword ptr Divisor[4]
_DivU64x64_Cont:
loop _DivU64x64_Wend
cmp Remainder, 0
je _DivU64x64_Assign
mov ecx, Remainder
mov dword ptr [ecx], eax
mov dword ptr [ecx + 4], edx
_DivU64x64_Assign:
mov eax, dword ptr Dividend[0]
mov edx, dword ptr Dividend[4]
_DivU64x64_Done:
pop ecx
ret
DivU64x64 ENDP
DivS64x64 PROC C Dividend: QWORD, Divisor: QWORD, Remainder: DWORD, Error: DWORD,
;------------------------------------------------------------------------------
; INT64
; DivU64x64 (
; IN INT64 Dividend,
; IN INT64 Divisor,
; OUT UINT64 *Remainder OPTIONAL,
; OUT UINT32 *Error
; )
;
; Routine Description:
;
; This routine allows a 64 bit signed value to be divided with a 64 bit
; signed value returns 64bit result and the Remainder.
;
; Arguments:
;
; Dividend - dividend
; Divisor - divisor
; ResultHigh - result to flag overflows
; Error - flag for error
;
; Returns:
;
; Dividend / Divisor
; Remainder = Dividend mod Divisor
;------------------------------------------------------------------------------
push ecx
mov eax, Error
mov dword ptr [eax], 0
cmp dword ptr Divisor[0], 0
jne _DivS64x64_Valid
cmp dword ptr Divisor[4], 0
jne _DivS64x64_Valid
;
; the divisor is zero
;
mov dword ptr [eax], 1
cmp Remainder, 0
je _DivS64x64_Invalid_Return
;
; fill the remainder if the pointer is not null
;
mov eax, Remainder
mov dword ptr [eax], 0
mov dword ptr [eax + 4], 80000000h
_DivS64x64_Invalid_Return:
xor eax, eax
mov edx, 80000000h
jmp _DivS64x64_Done
_DivS64x64_Valid:
;
; The lowest bit of ecx flags the sign of quotient,
; The seconde lowest bit flags the sign of remainder
;
xor ecx, ecx
mov edx, dword ptr Dividend[4]
bt edx, 31
jnc short _DivS64x64_Dividend_Positive
;
; dividend is negative
;
mov eax, dword ptr Dividend[0]
not edx
not eax
add eax, 1
adc edx, 0
mov dword ptr Dividend[0], eax
mov dword ptr Dividend[4], edx
;
; set both the flags for signs of quotient and remainder
;
btc ecx, 0
btc ecx, 1
_DivS64x64_Dividend_Positive:
mov edx, dword ptr Divisor[4]
bt edx, 31
jnc short _DivS64x64_Divisor_Positive
;
; divisor is negative
;
mov eax, dword ptr Divisor[0]
not edx
not eax
add eax, 1
adc edx, 0
mov dword ptr Divisor[0], eax
mov dword ptr Divisor[4], edx
;
; just complement the flag for sign of quotient
;
btc ecx, 0
_DivS64x64_Divisor_Positive:
invoke DivU64x64, Dividend, Divisor, Remainder, Error
bt ecx, 0
jnc short _DivS64x64_Remainder
;
; negate the quotient
;
not eax
not edx
add eax, 1
adc edx, 0
_DivS64x64_Remainder:
bt ecx, 1
jnc short _DivS64x64_Done
;
; negate the remainder
;
mov ecx, remainder
not dword ptr [ecx]
not dword ptr [ecx + 4]
add dword ptr [ecx], 1
adc dword ptr [ecx + 4], 0
_DivS64x64_Done:
pop ecx
ret
DivS64x64 ENDP
END