Further work on emulating faulting opcodes using distorm
This commit is contained in:
parent
5acc167a07
commit
3a5a2a237c
|
@ -45,6 +45,7 @@ namespace xboxkrnl
|
|||
|
||||
#include "CxbxKrnl.h"
|
||||
#include "Emu.h"
|
||||
#include "EmuX86.h"
|
||||
#include "EmuFile.h"
|
||||
#include "EmuFS.h"
|
||||
#include "EmuShared.h"
|
||||
|
@ -616,6 +617,7 @@ extern "C" CXBXKRNL_API void CxbxKrnlInit
|
|||
EmuGenerateFS(pTLS, pTLSData);
|
||||
}
|
||||
|
||||
EmuX86_Init();
|
||||
|
||||
DbgPrintf("EmuMain (0x%X): Initial thread starting.\n", GetCurrentThreadId());
|
||||
|
||||
|
|
|
@ -50,8 +50,6 @@
|
|||
#include "EmuX86.h"
|
||||
#include "EmuNV2A.h"
|
||||
|
||||
_DecodeResult distorm_decompose64(_CodeInfo* ci, _DInst result[], unsigned int maxInstructions, unsigned int* usedInstructionsCount);
|
||||
|
||||
uint32_t EmuX86_IORead32(uint32_t addr)
|
||||
{
|
||||
EmuWarning("EmuX86_IORead32(0x%08X) Not Implemented", addr);
|
||||
|
@ -142,59 +140,166 @@ void EmuX86_Write8(uint32_t addr, uint8_t value)
|
|||
EmuWarning("EmuX86_Write8(0x%08X, 0x%02X) [Unknown address]", addr, value);
|
||||
}
|
||||
|
||||
inline DWORD* EmuX86_GetRegisterPointer(LPEXCEPTION_POINTERS e, uint8_t reg)
|
||||
int ContextRecordOffsetByRegisterType[/*_RegisterType*/R_DR7 + 1] = { 0 };
|
||||
|
||||
// Populate ContextRecordOffsetByRegisterType for each distorm::_RegisterType
|
||||
// supported by the Xbox1's Coppermine Pentium III.
|
||||
// Based on https://maximumcrack.wordpress.com/2011/08/07/fpu-mmx-xmm-and-bbq/
|
||||
void EmuX86_InitContextRecordOffsetByRegisterType()
|
||||
{
|
||||
switch (reg) {
|
||||
case R_AL: case R_AH: case R_AX: case R_EAX:
|
||||
return &e->ContextRecord->Eax;
|
||||
case R_BL: case R_BH: case R_BX: case R_EBX:
|
||||
return &e->ContextRecord->Ebx;
|
||||
case R_CL: case R_CH: case R_CX: case R_ECX:
|
||||
return &e->ContextRecord->Ecx;
|
||||
case R_DL: case R_DH: case R_DX: case R_EDX:
|
||||
return &e->ContextRecord->Edx;
|
||||
case R_EDI:
|
||||
return &e->ContextRecord->Edi;
|
||||
case R_ESI:
|
||||
return &e->ContextRecord->Esi;
|
||||
case R_EBP:
|
||||
return &e->ContextRecord->Ebp;
|
||||
}
|
||||
/* R_RAX, R_RCX, R_RDX, R_RBX, R_RSP, R_RBP, R_RSI, R_RDI, R_R8, R_R9, R_R10, R_R11, R_R12, R_R13, R_R14, R_R15,*/
|
||||
ContextRecordOffsetByRegisterType[R_EAX] = offsetof(CONTEXT, Eax);
|
||||
ContextRecordOffsetByRegisterType[R_ECX] = offsetof(CONTEXT, Ecx);
|
||||
ContextRecordOffsetByRegisterType[R_EDX] = offsetof(CONTEXT, Edx);
|
||||
ContextRecordOffsetByRegisterType[R_EBX] = offsetof(CONTEXT, Ebx);
|
||||
ContextRecordOffsetByRegisterType[R_ESP] = offsetof(CONTEXT, Esp);
|
||||
ContextRecordOffsetByRegisterType[R_EBP] = offsetof(CONTEXT, Ebp);
|
||||
ContextRecordOffsetByRegisterType[R_ESI] = offsetof(CONTEXT, Esi);
|
||||
ContextRecordOffsetByRegisterType[R_EDI] = offsetof(CONTEXT, Edi);
|
||||
/* R_R8D, R_R9D, R_R10D, R_R11D, R_R12D, R_R13D, R_R14D, R_R15D,*/
|
||||
ContextRecordOffsetByRegisterType[R_AX] = offsetof(CONTEXT, Eax);
|
||||
ContextRecordOffsetByRegisterType[R_CX] = offsetof(CONTEXT, Ecx);
|
||||
ContextRecordOffsetByRegisterType[R_DX] = offsetof(CONTEXT, Edx);
|
||||
ContextRecordOffsetByRegisterType[R_BX] = offsetof(CONTEXT, Ebx);
|
||||
ContextRecordOffsetByRegisterType[R_SP] = offsetof(CONTEXT, Esp); // ??
|
||||
ContextRecordOffsetByRegisterType[R_BP] = offsetof(CONTEXT, Ebp); // ??
|
||||
ContextRecordOffsetByRegisterType[R_SI] = offsetof(CONTEXT, Esi); // ??
|
||||
ContextRecordOffsetByRegisterType[R_DI] = offsetof(CONTEXT, Edi); // ??
|
||||
/* R_R8W, R_R9W, R_R10W, R_R11W, R_R12W, R_R13W, R_R14W, R_R15W, */
|
||||
ContextRecordOffsetByRegisterType[R_AL] = offsetof(CONTEXT, Eax);
|
||||
ContextRecordOffsetByRegisterType[R_CL] = offsetof(CONTEXT, Ecx);
|
||||
ContextRecordOffsetByRegisterType[R_DL] = offsetof(CONTEXT, Edx);
|
||||
ContextRecordOffsetByRegisterType[R_BL] = offsetof(CONTEXT, Ebx);
|
||||
ContextRecordOffsetByRegisterType[R_AH] = offsetof(CONTEXT, Eax) + 1;
|
||||
ContextRecordOffsetByRegisterType[R_CH] = offsetof(CONTEXT, Ecx) + 1;
|
||||
ContextRecordOffsetByRegisterType[R_DH] = offsetof(CONTEXT, Edx) + 1;
|
||||
ContextRecordOffsetByRegisterType[R_BH] = offsetof(CONTEXT, Ebx) + 1;
|
||||
/* R_R8B, R_R9B, R_R10B, R_R11B, R_R12B, R_R13B, R_R14B, R_R15B, */
|
||||
ContextRecordOffsetByRegisterType[R_SPL] = offsetof(CONTEXT, Esp); // ??
|
||||
ContextRecordOffsetByRegisterType[R_BPL] = offsetof(CONTEXT, Ebp); // ??
|
||||
ContextRecordOffsetByRegisterType[R_SIL] = offsetof(CONTEXT, Esi); // ??
|
||||
ContextRecordOffsetByRegisterType[R_DIL] = offsetof(CONTEXT, Edi); // ??
|
||||
ContextRecordOffsetByRegisterType[R_ES] = offsetof(CONTEXT, SegEs);
|
||||
ContextRecordOffsetByRegisterType[R_CS] = offsetof(CONTEXT, SegCs);
|
||||
ContextRecordOffsetByRegisterType[R_SS] = offsetof(CONTEXT, SegSs);
|
||||
ContextRecordOffsetByRegisterType[R_DS] = offsetof(CONTEXT, SegDs);
|
||||
ContextRecordOffsetByRegisterType[R_FS] = offsetof(CONTEXT, SegFs);
|
||||
ContextRecordOffsetByRegisterType[R_GS] = offsetof(CONTEXT, SegGs);
|
||||
/* R_RIP, R_ST0, R_ST1, R_ST2, R_ST3, R_ST4, R_ST5, R_ST6, R_ST7, */
|
||||
ContextRecordOffsetByRegisterType[R_MM0] = offsetof(CONTEXT, ExtendedRegisters[(10 + 0) * 16]);
|
||||
ContextRecordOffsetByRegisterType[R_MM1] = offsetof(CONTEXT, ExtendedRegisters[(10 + 1) * 16]);
|
||||
ContextRecordOffsetByRegisterType[R_MM2] = offsetof(CONTEXT, ExtendedRegisters[(10 + 2) * 16]);
|
||||
ContextRecordOffsetByRegisterType[R_MM3] = offsetof(CONTEXT, ExtendedRegisters[(10 + 3) * 16]);
|
||||
ContextRecordOffsetByRegisterType[R_MM4] = offsetof(CONTEXT, ExtendedRegisters[(10 + 4) * 16]);
|
||||
ContextRecordOffsetByRegisterType[R_MM5] = offsetof(CONTEXT, ExtendedRegisters[(10 + 5) * 16]);
|
||||
ContextRecordOffsetByRegisterType[R_MM6] = offsetof(CONTEXT, ExtendedRegisters[(10 + 6) * 16]);
|
||||
ContextRecordOffsetByRegisterType[R_MM7] = offsetof(CONTEXT, ExtendedRegisters[(10 + 7) * 16]);
|
||||
/* R_XMM0, R_XMM1, R_XMM2, R_XMM3, R_XMM4, R_XMM5, R_XMM6, R_XMM7, R_XMM8, R_XMM9, R_XMM10, R_XMM11, R_XMM12, R_XMM13, R_XMM14, R_XMM15, */
|
||||
/* R_YMM0, R_YMM1, R_YMM2, R_YMM3, R_YMM4, R_YMM5, R_YMM6, R_YMM7, R_YMM8, R_YMM9, R_YMM10, R_YMM11, R_YMM12, R_YMM13, R_YMM14, R_YMM15, */
|
||||
/* R_CR0, R_UNUSED0, R_CR2, R_CR3, R_CR4, R_UNUSED1, R_UNUSED2, R_UNUSED3, R_CR8, */
|
||||
ContextRecordOffsetByRegisterType[R_DR0] = offsetof(CONTEXT, Dr0);
|
||||
ContextRecordOffsetByRegisterType[R_DR1] = offsetof(CONTEXT, Dr1);
|
||||
ContextRecordOffsetByRegisterType[R_DR2] = offsetof(CONTEXT, Dr2);
|
||||
ContextRecordOffsetByRegisterType[R_DR3] = offsetof(CONTEXT, Dr3);
|
||||
/* R_UNUSED4, R_UNUSED5, */
|
||||
ContextRecordOffsetByRegisterType[R_DR6] = offsetof(CONTEXT, Dr6);
|
||||
ContextRecordOffsetByRegisterType[R_DR7] = offsetof(CONTEXT, Dr7);
|
||||
|
||||
/* struct CONTEXT { // ! markers below, are used in the above offsetof calls
|
||||
DWORD ContextFlags;
|
||||
!DWORD Dr0;
|
||||
!DWORD Dr1;
|
||||
!DWORD Dr2;
|
||||
!DWORD Dr3;
|
||||
!DWORD Dr6;
|
||||
!DWORD Dr7;
|
||||
struct _FLOATING_SAVE_AREA {
|
||||
DWORD ControlWord;
|
||||
DWORD StatusWord;
|
||||
DWORD TagWord;
|
||||
DWORD ErrorOffset;
|
||||
DWORD ErrorSelector;
|
||||
DWORD DataOffset;
|
||||
DWORD DataSelector;
|
||||
BYTE RegisterArea[SIZE_OF_80387_REGISTERS];
|
||||
DWORD Spare0;
|
||||
} FLOATING_SAVE_AREA FloatSave;
|
||||
!DWORD SegGs;
|
||||
!DWORD SegFs;
|
||||
!DWORD SegEs;
|
||||
!DWORD SegDs;
|
||||
!DWORD Edi;
|
||||
!DWORD Esi;
|
||||
!DWORD Ebx;
|
||||
!DWORD Edx;
|
||||
!DWORD Ecx;
|
||||
!DWORD Eax;
|
||||
!DWORD Ebp;
|
||||
DWORD Eip;
|
||||
!DWORD SegCs; // MUST BE SANITIZED
|
||||
DWORD EFlags; // MUST BE SANITIZED
|
||||
!DWORD Esp;
|
||||
!DWORD SegSs;
|
||||
!BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];*/
|
||||
}
|
||||
|
||||
inline void * EmuX86_GetRegisterPointer(LPEXCEPTION_POINTERS e, uint8_t reg)
|
||||
{
|
||||
int offset = ContextRecordOffsetByRegisterType[reg];
|
||||
if (offset > 0)
|
||||
return (void*)((uintptr_t)(e->ContextRecord) + offset);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline bool EmuX86_GetRegisterValue(uint32_t* output, LPEXCEPTION_POINTERS e, uint8_t reg)
|
||||
inline uint32_t EmuX86_GetRegisterValue32(LPEXCEPTION_POINTERS e, uint8_t reg)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
if (reg != R_NONE)
|
||||
{
|
||||
DWORD* regptr = EmuX86_GetRegisterPointer(e, reg);
|
||||
if (regptr == nullptr)
|
||||
return false;
|
||||
|
||||
value = *regptr;
|
||||
void* regptr = EmuX86_GetRegisterPointer(e, reg);
|
||||
if (regptr != nullptr)
|
||||
return *(uint32_t *)regptr;
|
||||
}
|
||||
|
||||
*output = value;
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline uint16_t EmuX86_GetRegisterValue16(LPEXCEPTION_POINTERS e, uint8_t reg)
|
||||
{
|
||||
if (reg != R_NONE)
|
||||
{
|
||||
void* regptr = EmuX86_GetRegisterPointer(e, reg);
|
||||
if (regptr != nullptr)
|
||||
return *(uint16_t *)regptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline uint8_t EmuX86_GetRegisterValue8(LPEXCEPTION_POINTERS e, uint8_t reg)
|
||||
{
|
||||
if (reg != R_NONE)
|
||||
{
|
||||
void* regptr = EmuX86_GetRegisterPointer(e, reg);
|
||||
if (regptr != nullptr)
|
||||
return *(uint8_t *)regptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t EmuX86_Distorm_O_SMEM_Addr(LPEXCEPTION_POINTERS e, _DInst& info, int operand)
|
||||
{
|
||||
uint32_t base;
|
||||
EmuX86_GetRegisterValue(&base, e, info.ops[operand].index);
|
||||
uint32_t base = EmuX86_GetRegisterValue32(e, info.ops[operand].index);
|
||||
|
||||
return base + info.disp;
|
||||
}
|
||||
|
||||
uint32_t EmuX86_Distorm_O_MEM_Addr(LPEXCEPTION_POINTERS e, _DInst& info, int operand)
|
||||
{
|
||||
uint32_t base = 0;
|
||||
EmuX86_GetRegisterValue(&base, e, info.base);
|
||||
uint32_t base = EmuX86_GetRegisterValue32(e, info.base);
|
||||
|
||||
uint32_t index = 0;
|
||||
EmuX86_GetRegisterValue(&index, e, info.ops[operand].index);
|
||||
uint32_t index = EmuX86_GetRegisterValue32(e, info.ops[operand].index);
|
||||
|
||||
if (info.scale >= 2)
|
||||
return base + (index * info.scale) + info.disp;
|
||||
|
@ -202,71 +307,75 @@ uint32_t EmuX86_Distorm_O_MEM_Addr(LPEXCEPTION_POINTERS e, _DInst& info, int ope
|
|||
return base + index + info.disp;
|
||||
}
|
||||
|
||||
void EmuX86_ReadAddr(uint32_t srcAddr, uint16_t size, OUT uint32_t *value)
|
||||
void EmuX86_Addr_Read(uint32_t srcAddr, uint16_t size, OUT uint32_t *value)
|
||||
{
|
||||
switch (size) {
|
||||
case 8:
|
||||
*value = EmuX86_Read8(srcAddr);
|
||||
break;
|
||||
return;
|
||||
case 16:
|
||||
*value = EmuX86_Read16(srcAddr);
|
||||
break;
|
||||
return;
|
||||
case 32:
|
||||
*value = EmuX86_Read32(srcAddr);
|
||||
break;
|
||||
default:
|
||||
// TODO : Handle other sizes?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EmuX86_WriteAddr(uint32_t destAddr, uint16_t size, uint32_t value)
|
||||
void EmuX86_Addr_Write(uint32_t destAddr, uint16_t size, uint32_t value)
|
||||
{
|
||||
switch (size) {
|
||||
case 8:
|
||||
EmuX86_Write8(destAddr, value & 0xFF);
|
||||
break;
|
||||
return;
|
||||
case 16:
|
||||
EmuX86_Write16(destAddr, value & 0xFFFF);
|
||||
break;
|
||||
return;
|
||||
case 32:
|
||||
EmuX86_Write32(destAddr, value);
|
||||
break;
|
||||
default:
|
||||
// TODO : Handle other sizes?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuX86_ReadValueFromSource(LPEXCEPTION_POINTERS e, _DInst& info, int operand, OUT uint32_t *value)
|
||||
bool EmuX86_Operand_Read(LPEXCEPTION_POINTERS e, _DInst& info, int operand, OUT uint32_t *value)
|
||||
{
|
||||
switch (info.ops[operand].type) {
|
||||
case O_NONE:
|
||||
{
|
||||
// ignore operand
|
||||
return true;
|
||||
}
|
||||
case O_REG:
|
||||
{
|
||||
if (!EmuX86_GetRegisterValue(value, e, info.ops[operand].index))
|
||||
void* regAddr = EmuX86_GetRegisterPointer(e, info.ops[operand].index);
|
||||
if (regAddr == nullptr)
|
||||
return false;
|
||||
|
||||
break;
|
||||
switch (info.ops[operand].size) {
|
||||
case 8:
|
||||
*value = *((uint8_t*)regAddr);
|
||||
return true;
|
||||
case 16:
|
||||
*value = *((uint16_t*)regAddr);
|
||||
return true;
|
||||
case 32:
|
||||
*value = *((uint32_t*)regAddr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case O_IMM:
|
||||
{
|
||||
switch (info.ops[operand].size) {
|
||||
case 8:
|
||||
*value = info.imm.byte;
|
||||
break;
|
||||
return true;
|
||||
case 16:
|
||||
*value = info.imm.word;
|
||||
break;
|
||||
return true;
|
||||
case 32:
|
||||
*value = info.imm.dword;
|
||||
break;
|
||||
// TODO : Handle other sizes?
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
case O_IMM1:
|
||||
{
|
||||
|
@ -281,20 +390,20 @@ bool EmuX86_ReadValueFromSource(LPEXCEPTION_POINTERS e, _DInst& info, int operan
|
|||
case O_DISP:
|
||||
{
|
||||
uint32_t srcAddr = info.disp;
|
||||
EmuX86_ReadAddr(srcAddr, info.ops[operand].size, value);
|
||||
break;
|
||||
EmuX86_Addr_Read(srcAddr, info.ops[operand].size, value);
|
||||
return true;
|
||||
}
|
||||
case O_SMEM:
|
||||
{
|
||||
uint32_t srcAddr = EmuX86_Distorm_O_SMEM_Addr(e, info, operand);
|
||||
EmuX86_ReadAddr(srcAddr, info.ops[operand].size, value);
|
||||
break;
|
||||
EmuX86_Addr_Read(srcAddr, info.ops[operand].size, value);
|
||||
return true;
|
||||
}
|
||||
case O_MEM:
|
||||
{
|
||||
uint32_t srcAddr = EmuX86_Distorm_O_MEM_Addr(e, info, operand);
|
||||
EmuX86_ReadAddr(srcAddr, info.ops[operand].size, value);
|
||||
break;
|
||||
EmuX86_Addr_Read(srcAddr, info.ops[operand].size, value);
|
||||
return true;
|
||||
}
|
||||
case O_PC:
|
||||
{
|
||||
|
@ -310,36 +419,37 @@ bool EmuX86_ReadValueFromSource(LPEXCEPTION_POINTERS e, _DInst& info, int operan
|
|||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EmuX86_WriteValueToDestination(LPEXCEPTION_POINTERS e, _DInst& info, int operand, uint32_t value)
|
||||
bool EmuX86_Operand_Write(LPEXCEPTION_POINTERS e, _DInst& info, int operand, uint32_t value)
|
||||
{
|
||||
switch (info.ops[operand].type) {
|
||||
case O_NONE:
|
||||
{
|
||||
// ignore operand
|
||||
return true;
|
||||
}
|
||||
case O_REG:
|
||||
{
|
||||
DWORD* pDstReg = EmuX86_GetRegisterPointer(e, info.ops[operand].index);
|
||||
if (pDstReg == nullptr)
|
||||
void* regAddr = EmuX86_GetRegisterPointer(e, info.ops[operand].index);
|
||||
if (regAddr == nullptr)
|
||||
return false;
|
||||
|
||||
switch (info.ops[operand].size) {
|
||||
case 8:
|
||||
*((uint8_t*)pDstReg + 3) = value & 0xFF;
|
||||
break;
|
||||
*((uint8_t*)regAddr) = (uint8_t)value;
|
||||
return true;
|
||||
case 16:
|
||||
*((uint16_t*)pDstReg + 2) = value & 0xFFFF;
|
||||
break;
|
||||
*((uint16_t*)regAddr) = (uint16_t)value;
|
||||
return true;
|
||||
case 32:
|
||||
*pDstReg = value;
|
||||
break;
|
||||
*((uint32_t*)regAddr) = value;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
case O_IMM:
|
||||
{
|
||||
|
@ -359,20 +469,20 @@ bool EmuX86_WriteValueToDestination(LPEXCEPTION_POINTERS e, _DInst& info, int op
|
|||
case O_DISP:
|
||||
{
|
||||
uint32_t destAddr = info.disp;
|
||||
EmuX86_WriteAddr(destAddr, info.ops[operand].size, value);
|
||||
break;
|
||||
EmuX86_Addr_Write(destAddr, info.ops[operand].size, value);
|
||||
return true;
|
||||
}
|
||||
case O_SMEM:
|
||||
{
|
||||
uint32_t destAddr = EmuX86_Distorm_O_SMEM_Addr(e, info, operand);
|
||||
EmuX86_WriteAddr(destAddr, info.ops[operand].size, value);
|
||||
break;
|
||||
EmuX86_Addr_Write(destAddr, info.ops[operand].size, value);
|
||||
return true;
|
||||
}
|
||||
case O_MEM:
|
||||
{
|
||||
uint32_t destAddr = EmuX86_Distorm_O_MEM_Addr(e, info, operand);
|
||||
EmuX86_WriteAddr(destAddr, info.ops[operand].size, value);
|
||||
break;
|
||||
EmuX86_Addr_Write(destAddr, info.ops[operand].size, value);
|
||||
return true;
|
||||
}
|
||||
case O_PC:
|
||||
{
|
||||
|
@ -388,20 +498,57 @@ bool EmuX86_WriteValueToDestination(LPEXCEPTION_POINTERS e, _DInst& info, int op
|
|||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EmuX86_Opcode_ADD(LPEXCEPTION_POINTERS e, _DInst& info)
|
||||
{
|
||||
// ADD reads value from source :
|
||||
uint32_t value;
|
||||
if (!EmuX86_Operand_Read(e, info, 1, &value))
|
||||
return false;
|
||||
|
||||
// ADD reads and writes destination :
|
||||
uint32_t addr;
|
||||
// TODO : Implement EmuX86_Operand_Addr, then enable the following line :
|
||||
// if (!EmuX86_Operand_Addr(e, info, 0, &addr))
|
||||
return false;
|
||||
|
||||
// TODO : Do this better
|
||||
switch (info.ops[0].size) {
|
||||
case 8: {
|
||||
uint8_t current = EmuX86_Read8(addr);
|
||||
EmuX86_Write8(addr, (uint8_t)current + value);
|
||||
return true;
|
||||
}
|
||||
case 16: {
|
||||
uint16_t current = EmuX86_Read16(addr);
|
||||
EmuX86_Write16(addr, (uint16_t)current + value);
|
||||
return true;
|
||||
}
|
||||
case 32: {
|
||||
uint32_t current = EmuX86_Read32(addr);
|
||||
EmuX86_Write32(addr, (uint32_t)current + value);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO : update CPU flags
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmuX86_MOV(LPEXCEPTION_POINTERS e, _DInst& info)
|
||||
bool EmuX86_Opcode_MOV(LPEXCEPTION_POINTERS e, _DInst& info)
|
||||
{
|
||||
// TODO : Test this refactoring
|
||||
|
||||
// MOV reads value from source :
|
||||
uint32_t value = 0;
|
||||
if (!EmuX86_ReadValueFromSource(e, info, 1, &value))
|
||||
if (!EmuX86_Operand_Read(e, info, 1, &value))
|
||||
return false;
|
||||
|
||||
// MOV writes value to destination :
|
||||
if (!EmuX86_WriteValueToDestination(e, info, 0, value))
|
||||
if (!EmuX86_Operand_Write(e, info, 0, value))
|
||||
return false;
|
||||
|
||||
// Note : MOV instructions never update CPU flags
|
||||
|
@ -409,17 +556,17 @@ bool EmuX86_MOV(LPEXCEPTION_POINTERS e, _DInst& info)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool EmuX86_MOVZX(LPEXCEPTION_POINTERS e, _DInst& info)
|
||||
bool EmuX86_Opcode_MOVZX(LPEXCEPTION_POINTERS e, _DInst& info)
|
||||
{
|
||||
// TODO : Test this refactoring
|
||||
|
||||
// MOV reads value from source :
|
||||
// MOVZX reads value from source :
|
||||
uint32_t value = 0;
|
||||
if (!EmuX86_ReadValueFromSource(e, info, 1, &value))
|
||||
if (!EmuX86_Operand_Read(e, info, 1, &value))
|
||||
return false;
|
||||
|
||||
// MOV writes value to destination :
|
||||
if (!EmuX86_WriteValueToDestination(e, info, 0, value))
|
||||
// TODO : Implement MOVZX zero-extension!
|
||||
|
||||
// MOVZX writes value to destination :
|
||||
if (!EmuX86_Operand_Write(e, info, 0, value))
|
||||
return false;
|
||||
|
||||
// Note : MOV instructions never update CPU flags
|
||||
|
@ -432,16 +579,16 @@ inline void EmuX86_SetFlag(LPEXCEPTION_POINTERS e, int flag, int value)
|
|||
e->ContextRecord->EFlags ^= (-value ^ e->ContextRecord->EFlags) & (1 << flag);
|
||||
}
|
||||
|
||||
bool EmuX86_TEST(LPEXCEPTION_POINTERS e, _DInst& info)
|
||||
bool EmuX86_Opcode_TEST(LPEXCEPTION_POINTERS e, _DInst& info)
|
||||
{
|
||||
// TEST reads first value :
|
||||
uint32_t result = 0;
|
||||
if (!EmuX86_ReadValueFromSource(e, info, 0, &result))
|
||||
if (!EmuX86_Operand_Read(e, info, 0, &result))
|
||||
return false;
|
||||
|
||||
// TEST reads second value :
|
||||
uint32_t value = 0;
|
||||
if (!EmuX86_ReadValueFromSource(e, info, 1, &value))
|
||||
if (!EmuX86_Operand_Read(e, info, 1, &value))
|
||||
return false;
|
||||
|
||||
// TEST performs bitwise AND between first and second value :
|
||||
|
@ -465,6 +612,85 @@ bool EmuX86_TEST(LPEXCEPTION_POINTERS e, _DInst& info)
|
|||
return true;
|
||||
}
|
||||
|
||||
void EmuX86_Opcode_CPUID(LPEXCEPTION_POINTERS e, _DInst& info)
|
||||
{
|
||||
// This CPUID emulation is based on :
|
||||
// https://github.com/docbrown/vxb/wiki/Xbox-CPUID-Information
|
||||
// https://github.com/docbrown/vxb/wiki/Xbox-Hardware-Information and
|
||||
// http://www.sandpile.org/x86/cpuid.htm
|
||||
switch (e->ContextRecord->Eax) // simpler than EmuX86_GetRegisterValue32(e, R_EAX)
|
||||
{
|
||||
case 0: // CPUID Function 0, return the maximum supported standard level and vendor ID string
|
||||
{
|
||||
// Maximum supported standard level
|
||||
e->ContextRecord->Eax = 2;
|
||||
// "GenuineIntel" Intel processor
|
||||
e->ContextRecord->Ebx = (ULONG)'uneG';
|
||||
e->ContextRecord->Edx = (ULONG)'Ieni';
|
||||
e->ContextRecord->Ecx = (ULONG)'letn';
|
||||
return;
|
||||
}
|
||||
case 1: // CPUID Function 1, Return the processor type / family / model / stepping and feature flags
|
||||
{
|
||||
// Family 6, Model 8, Stepping 10
|
||||
e->ContextRecord->Eax = 0x68a;
|
||||
e->ContextRecord->Ebx = 0;
|
||||
e->ContextRecord->Ecx = 0;
|
||||
// Feature Flags
|
||||
e->ContextRecord->Edx = 0x383F9FF; // FPU, VME, DE, PSE, TSC, MSR, PAE, MCE, CX8, SEP, MTRR, PGE, MCA, CMOV, PAT, PSE36, MMX, FXSR, SSE
|
||||
return;
|
||||
}
|
||||
case 2: // CPUID Function 2, Return the processor configuration descriptors
|
||||
{
|
||||
// AL : 01 = number of times this level must be queried to obtain all configuration descriptors
|
||||
// EAX nibble 1 = 01h : code TLB, 4K pages, 4 ways, 32 entries
|
||||
// EAX nibble 2 = 02h : code TLB, 4M pages, fully, 2 entries
|
||||
// EAX nibble 3 = 03h : data TLB, 4K pages, 4 ways, 64 entries
|
||||
e->ContextRecord->Eax = 0x3020101;
|
||||
// EBX and ECX nibbles = 00h : null descriptor (=unused descriptor)
|
||||
e->ContextRecord->Ebx = 0;
|
||||
e->ContextRecord->Ecx = 0;
|
||||
// EDX nibble 0 = 41h : code and data L2 cache, 128 KB, 4 ways, 32 byte lines
|
||||
// EDX nibble 1 = 08h : code L1 cache, 16 KB, 4 ways, 32 byte lines
|
||||
// EDX nibble 2 = 04h : data TLB, 4M pages, 4 ways, 8 entries
|
||||
// EDX nibble 3 = 0Ch : data L1 cache, 16 KB, 4 ways, 32 byte lines
|
||||
e->ContextRecord->Edx = 0xC040841;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuX86_Opcode_OUT(LPEXCEPTION_POINTERS e, _DInst& info)
|
||||
{
|
||||
// OUT will address the first operand :
|
||||
uint32_t addr;
|
||||
if (!EmuX86_Operand_Read(e, info, 0, &addr))
|
||||
return false;
|
||||
|
||||
uint32_t value;
|
||||
if (!EmuX86_Operand_Read(e, info, 1, &value))
|
||||
return false;
|
||||
|
||||
// OUT does an I/O write on the address, using the value from the second operand :
|
||||
switch (info.ops[1].size) {
|
||||
case 8: {
|
||||
EmuX86_IOWrite8(addr, (uint8_t)value);
|
||||
return true;
|
||||
}
|
||||
case 16: {
|
||||
EmuX86_IOWrite16(addr, (uint16_t)value);
|
||||
return true;
|
||||
}
|
||||
case 32: {
|
||||
EmuX86_IOWrite32(addr, value);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
|
||||
{
|
||||
// Only decode instructions within Xbox memory space
|
||||
|
@ -494,27 +720,40 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
|
|||
}
|
||||
else
|
||||
{
|
||||
switch (info.opcode)
|
||||
switch (info.opcode) // Keep these cases alphabetically ordered
|
||||
{
|
||||
case I_MOV:
|
||||
if (EmuX86_MOV(e, info)) {
|
||||
case I_ADD:
|
||||
if (EmuX86_Opcode_ADD(e, info))
|
||||
break;
|
||||
|
||||
goto unimplemented_opcode;
|
||||
case I_CPUID:
|
||||
EmuX86_Opcode_CPUID(e, info);
|
||||
break;
|
||||
case I_INVD: // Flush internal caches; initiate flushing of external caches.
|
||||
// We can safely ignore this
|
||||
break;
|
||||
case I_MOV:
|
||||
if (EmuX86_Opcode_MOV(e, info))
|
||||
break;
|
||||
}
|
||||
|
||||
goto unimplemented_opcode;
|
||||
case I_MOVZX:
|
||||
if (EmuX86_MOVZX(e, info)) {
|
||||
if (EmuX86_Opcode_MOVZX(e, info))
|
||||
break;
|
||||
|
||||
goto unimplemented_opcode;
|
||||
case I_OUT:
|
||||
if (EmuX86_Opcode_OUT(e, info))
|
||||
break;
|
||||
}
|
||||
|
||||
goto unimplemented_opcode;
|
||||
case I_TEST:
|
||||
if (EmuX86_TEST(e, info)) {
|
||||
if (EmuX86_Opcode_TEST(e, info))
|
||||
break;
|
||||
}
|
||||
|
||||
goto unimplemented_opcode;
|
||||
case I_WBINVD:
|
||||
case I_WBINVD: // Write back and flush internal caches; initiate writing-back and flushing of external caches.
|
||||
// We can safely ignore this
|
||||
break;
|
||||
default:
|
||||
|
@ -533,3 +772,9 @@ unimplemented_opcode:
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EmuX86_Init()
|
||||
{
|
||||
EmuX86_InitContextRecordOffsetByRegisterType();
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#define EMUX86_EFLAG_VIP 20
|
||||
#define EMUX86_EFLAG_ID 21
|
||||
|
||||
void EmuX86_Init();
|
||||
bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e);
|
||||
uint32_t EmuX86_IORead32(uint32_t addr);
|
||||
uint16_t EmuX86_IORead16(uint32_t addr);
|
||||
|
|
Loading…
Reference in New Issue