From 3a5a2a237ccb6faf238062b4bb48434b5b81c496 Mon Sep 17 00:00:00 2001 From: PatrickvL Date: Tue, 10 Jan 2017 12:36:59 +0100 Subject: [PATCH] Further work on emulating faulting opcodes using distorm --- src/CxbxKrnl/CxbxKrnl.cpp | 2 + src/CxbxKrnl/EmuX86.cpp | 451 +++++++++++++++++++++++++++++--------- src/CxbxKrnl/EmuX86.h | 1 + 3 files changed, 351 insertions(+), 103 deletions(-) diff --git a/src/CxbxKrnl/CxbxKrnl.cpp b/src/CxbxKrnl/CxbxKrnl.cpp index 098cc11f7..414cad5a6 100644 --- a/src/CxbxKrnl/CxbxKrnl.cpp +++ b/src/CxbxKrnl/CxbxKrnl.cpp @@ -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()); diff --git a/src/CxbxKrnl/EmuX86.cpp b/src/CxbxKrnl/EmuX86.cpp index 8bfcf179e..721bd5207 100644 --- a/src/CxbxKrnl/EmuX86.cpp +++ b/src/CxbxKrnl/EmuX86.cpp @@ -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(); +} + diff --git a/src/CxbxKrnl/EmuX86.h b/src/CxbxKrnl/EmuX86.h index bbeab9452..b067dcd9e 100644 --- a/src/CxbxKrnl/EmuX86.h +++ b/src/CxbxKrnl/EmuX86.h @@ -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);