#ifdef SUPERMODEL_DEBUGGER #include "PPCDebug.h" #include "CPU/PowerPC/ppc.h" #include "CPU/PowerPC/PPCDisasm.h" #include #include #define M_AA 0x00000002 #define M_LK 0x00000001 #define M_BO 0x03E00000 #define M_BD 0x0000FFFC #define M_LI 0x03FFFFFC #define MSR_IP 0x00000040 namespace Debugger { UINT32 GetSpecialReg(CCPUDebug *cpu, unsigned id) { switch (id) { case PPCSPECIAL_LR: return ::ppc_get_lr(); case PPCSPECIAL_FPSCR: return 0; // TODO default: return 0; } } bool SetSpecialReg(CCPUDebug *cpu, unsigned id, UINT32 data) { switch (id) { case PPCSPECIAL_LR: /* TODO */ return false; case PPCSPECIAL_FPSCR: /* TODO */ return false; default: return false; } } UINT8 GetCR(CCPUDebug *cpu, unsigned id) { return ::ppc_get_cr(id); } bool SetCR(CCPUDebug *cpu, unsigned id, UINT8 data) { ::ppc_set_cr(id, data); return true; } UINT32 GetSPR(CCPUDebug *cpu, unsigned id) { return ::ppc_read_spr(id); } bool SetSPR(CCPUDebug *cpu, unsigned id, UINT32 data) { ::ppc_write_spr(id, data); return true; } UINT32 GetGPR(CCPUDebug *cpu, unsigned id) { return ::ppc_get_gpr(id); } bool SetGPR(CCPUDebug *cpu, unsigned id, UINT32 data) { ::ppc_set_gpr(id, data); return true; } double GetFPR(CCPUDebug *cpu, unsigned id) { return ::ppc_get_fpr(id); } bool SetFPR(CCPUDebug *cpu, unsigned id, double data) { ::ppc_set_fpr(id, data); return true; } static const char *srGroup = "Special Registers"; static const char *crGroup = "Condition Registers"; static const char *grGroup = "GPR Registers"; static const char *frGroup = "FPR Registers"; static const char *giGroup = "Game Inputs"; static const char *sbGroup = "Sound Board"; CPPCDebug::CPPCDebug() : CCPUDebug("PPC", 4, 4, true, 32, 7), m_irqState(0) { // PC & Link registers AddPCRegister ("pc", srGroup); AddAddrRegister("lr", srGroup, PPCSPECIAL_LR, GetSpecialReg, SetSpecialReg); // SPR registers AddInt32Register ("ctr", srGroup, SPR_LR, GetSPR, SetSPR); AddStatus32Register("xer", srGroup, SPR_XER, "SOC", GetSPR, SetSPR); AddInt32Register ("srr0", srGroup, SPR_SRR0, GetSPR, SetSPR); AddInt32Register ("srr1", srGroup, SPR_SRR1, GetSPR, SetSPR); // etc... // Condition registers for (unsigned id = 0; id < 8; id++) { sprintf(m_crNames[id], "cr%u", id); AddStatus8Register(m_crNames[id], crGroup, id, "O=><", GetCR, SetCR); } //AddStatus16Register("fpscr", "Condition Registers", PPCSPECIAL_FPSCR, "FEVOUZX789ABCRI 0123", GetSpecial, SetSpecial); // GPR registers for (unsigned id = 0; id < 32; id++) { sprintf(m_gprNames[id], "r%u", id); AddInt32Register(m_gprNames[id], grGroup, id, GetGPR, SetGPR); } // FPR registers for (unsigned id = 0; id < 32; id++) { sprintf(m_fprNames[id], "f%u", id); AddFPointRegister(m_fprNames[id], frGroup, id, GetFPR, SetFPR); } // Exceptions AddException("IRQ", EXCEPTION_IRQ, "External Interrupt"); AddException("DEC", EXCEPTION_DECREMENTER, "Decrement Overflow"); AddException("TRAP", EXCEPTION_TRAP, "Program Exception/Trap"); AddException("SYSCALL", EXCEPTION_SYSTEM_CALL, "System Call"); AddException("SMI", EXCEPTION_SMI, "SMI"); AddException("DSI", EXCEPTION_DSI, "DSI"); AddException("ISI", EXCEPTION_ISI, "ISI"); // TODO - following Model3-specific stuff should be moved to SupermodelDebugger // Interrupts AddInterrupt("VD0", 0, "Unknown video-related"); AddInterrupt("VBL", 1, "VBlank start"); AddInterrupt("VD2", 2, "Unknown video-related"); AddInterrupt("VD3", 3, "Unknown video-related"); AddInterrupt("NET", 4, "Network"); AddInterrupt("UN5", 5, "Unknown"); AddInterrupt("SND", 6, "SCSP (sound)"); AddInterrupt("UN7", 7, "Unknown"); // Memory regions AddRegion(0x00000000, 0x007FFFFF, true, false, "RAM"); AddRegion(0x84000000, 0x8400003F, false, false, "Real3D Status Registers"); AddRegion(0x88000000, 0x88000007, false, false, "Real3D Command Port"); AddRegion(0x8C000000, 0x8C3FFFFF, false, false, "Real3D Culling RAM (Low)"); AddRegion(0x8E000000, 0x8E0FFFFF, false, false, "Real3D Culling RAM (High)"); AddRegion(0x90000000, 0x9000000B, false, false, "Real3D VROM Texture Port"); AddRegion(0x94000000, 0x940FFFFF, false, false, "Real3D Texture FIFO"); AddRegion(0x98000000, 0x980FFFFF, false, false, "Real3D Polygon RAM"); AddRegion(0xC0000000, 0xC00000FF, false, false, "SCSI (Step 1.x)"); AddRegion(0xC1000000, 0xC10000FF, false, false, "SCSI (Step 1.x) (Lost World expects it here)"); AddRegion(0xC2000000, 0xC20000FF, false, false, "Real3D DMA (Step 2.x)"); AddRegion(0xF0040000, 0xF004003F, false, false, "Input (Controls) Registers"); AddRegion(0xF0080000, 0xF0080007, false, false, "Sound Board Registers"); AddRegion(0xF00C0000, 0xF00DFFFF, false, false, "Backup RAM"); AddRegion(0xF0100000, 0xF010003F, false, false, "System Registers"); AddRegion(0xF0140000, 0xF014003F, false, false, "Real, 0xTime Clock"); AddRegion(0xF0180000, 0xF019FFFF, false, false, "Security Board RAM"); AddRegion(0xF01A0000, 0xF01A003F, false, false, "Security Board Registers"); AddRegion(0xF0800CF8, 0xF0800CFF, false, false, "MPC105 CONFIG_ADDR (Step 1.x)"); AddRegion(0xF0C00CF8, 0xF0C00CFF, false, false, "MPC105 CONFIG_DATA (Step 1.x)"); AddRegion(0xF1000000, 0xF10F7FFF, false, false, "Tile Generator Pattern Table"); AddRegion(0xF10F8000, 0xF10FFFFF, false, false, "Tile Generator Name Table"); AddRegion(0xF1100000, 0xF111FFFF, false, false, "Tile Generator Palette"); AddRegion(0xF1180000, 0xF11800FF, false, false, "Tile Generator Registers"); AddRegion(0xF8FFF000, 0xF8FFF0FF, false, false, "MPC105 (Step 1.x) or MPC106 (Step 2.x) Registers"); AddRegion(0xF9000000, 0xF90000FF, false, false, "NCR 53C810 Registers (Step 1.x?)"); AddRegion(0xFE040000, 0xFE04003F, false, false, "Mirrored Input Registers"); AddRegion(0xFEC00000, 0xFEDFFFFF, false, false, "MPC106 CONFIG_ADDR (Step 2.x)"); AddRegion(0xFEE00000, 0xFEFFFFFF, false, false, "MPC106 CONFIG_DATA (Step 2.x)"); AddRegion(0xFF000000, 0xFF7FFFFF, true, true, "Banked CROM (CROMxx)"); AddRegion(0xFF800000, 0xFFFFFFFF, true, true, "Fixed CROM"); // Memory-mapped IO AddMappedIO(0xF0040000, 1, "Input Bank Select", giGroup); AddMappedIO(0xF0040004, 1, "Current Input Bank", giGroup); AddMappedIO(0xF0040008, 1, "Game Specific Inputs 1", giGroup); AddMappedIO(0xF004000C, 1, "Game Specific Inputs 2", giGroup); AddMappedIO(0xF0040010, 1, "Drive Board", giGroup); AddMappedIO(0xF0040014, 1, "LED Outputs?", giGroup); AddMappedIO(0xF0040018, 1, "Unknown?", giGroup); AddMappedIO(0xF0040024, 1, "Serial FIFO 1 Control", giGroup); AddMappedIO(0xF0040028, 1, "Serial FIFO 2 Control", giGroup); AddMappedIO(0xF004002C, 1, "Serial FIFO 1", giGroup); AddMappedIO(0xF0040030, 1, "Serial FIFO 2", giGroup); AddMappedIO(0xF0040034, 1, "Serial FIFO Flags", giGroup); AddMappedIO(0xF004003C, 1, "ADC", giGroup); AddMappedIO(0xF0080000, 1, "MIDI", sbGroup); AddMappedIO(0xF0080004, 1, "Control", sbGroup); } CPPCDebug::~CPPCDebug() { DetachFromCPU(); } void CPPCDebug::AttachToCPU() { ::ppc_attach_debugger(this); } ::CBus *CPPCDebug::AttachBus(::CBus *bus) { m_bus = bus; return this; } void CPPCDebug::DetachFromCPU() { ::ppc_detach_debugger(); } ::CBus *CPPCDebug::DetachBus() { ::CBus *bus = m_bus; m_bus = NULL; return bus; } UINT32 CPPCDebug::GetResetAddr() { // Reset address appears to be hardcoded to 0xFFF00100 return 0xFFF00100; } bool CPPCDebug::UpdatePC(UINT32 pc) { ::ppc_set_pc(pc); return true; } bool CPPCDebug::ForceException(CException *ex) { // TODO - no way to force exceptions return false; } bool CPPCDebug::ForceInterrupt(CInterrupt *in) { if (in->code > 7) return false; UINT8 irqState = m_bus->Read8(0xF0100018) | 1<code; m_bus->Write8(0xF0100018, irqState); ::ppc_set_irq_line(1); // TODO - what is irqline arg for? not actually used? return true; } UINT64 CPPCDebug::ReadMem(UINT32 addr, unsigned dataSize) { switch (dataSize) { case 1: return (UINT64)m_bus->Read8(addr); case 2: return (UINT64)m_bus->Read16(addr); case 4: return (UINT64)m_bus->Read32(addr); case 8: return m_bus->Read64(addr); default: return 0; } } bool CPPCDebug::WriteMem(UINT32 addr, unsigned dataSize, UINT64 data) { switch (dataSize) { case 1: m_bus->Write8(addr, (UINT8)data); return true; case 2: m_bus->Write16(addr, (UINT16)data); return true; case 4: m_bus->Write32(addr, (UINT32)data); return true; case 8: m_bus->Write64(addr, data); return true; default: return false; } } void CPPCDebug::CheckException(UINT16 exCode) { CCPUDebug::CheckException(exCode); if (exCode == EXCEPTION_IRQ) { UINT8 irqState = m_bus->Read8(0xF0100018); // TODO - replace this with function pointer UINT8 newIRQs = (irqState^m_irqState)&irqState; for (int intCode = 0; newIRQs && intCode < 8; intCode++) { if (newIRQs&0x01) CheckInterrupt(intCode); newIRQs >>= 1; } m_irqState = irqState; } } int CPPCDebug::Disassemble(UINT32 addr, char *mnemonic, char *operands) { char opStr[255]; char valStr[40]; UINT32 opcode = m_bus->Read32(addr); operands[0] = '\0'; if (!::DisassemblePowerPC(opcode, addr, mnemonic, opStr, TRUE)) { char *o = opStr; char *s = strstr(o, "0x"); while (s) { strncpy(operands, o, s - o); operands[s - o] = '\0'; s += 2; char *p = s; unsigned len = 0; UINT64 data = 0; while (p) { char c = toupper(*(p++)); if (c >= '0' && c <= '9') { data <<= 4; data += (UINT64)(c - '0'); } else if (c >= 'A' && c <= 'F') { data <<= 4; data += (UINT64)(10 + c - 'A'); } else break; len++; } unsigned dataSize = (p - s) / 2; if (dataSize == (unsigned)(memBusWidth / 8)) { EOpFlags opFlags = GetOpFlags(addr, opcode); FormatJumpAddress(valStr, (UINT32)data, opFlags); } else FormatData(valStr, dataSize, data); strcat(operands, valStr); operands += strlen(operands); o = p - 1; s = strstr(o, "0x"); } strcat(operands, o); return 4; } else return -4; } EOpFlags CPPCDebug::GetOpFlags(UINT32 addr, UINT32 opcode) { EOpFlags opFlags; UINT32 op = opcode>>26; if (op == 0x10) { // Instruction is branch conditional: bc, bca, bcl or bcla UINT32 bo = (opcode&M_BO)>>21; if (opcode&M_LK) { if (opcode&M_AA) opFlags = JumpSub; // bcla else if (bo&0x04) opFlags = (EOpFlags)(JumpSub | Relative); // bcl without counter else opFlags = (EOpFlags)(JumpSub | Relative); // bcl with counter } else { if (opcode&M_AA) opFlags = JumpSimple; // bca else if (bo&0x04) opFlags = (EOpFlags)(JumpSimple | Relative); // bc without counter else opFlags = (EOpFlags)(JumpLoop | Relative); // bc with counter } // Check BO is not just branch always return ((bo&0x14) == 0x14 ? opFlags : (EOpFlags)(opFlags | Conditional)); } else if (op == 0x12) { // Instruction is branch: b, ba, bl or bla if (opcode&M_LK) { if (opcode&M_AA) return JumpSub; // bla else return (EOpFlags)(JumpSub | Relative); // bl } else { if (opcode&M_AA) return JumpSimple; // ba else return (EOpFlags)(JumpSimple | Relative); // b } } else if (op == 0x13) { UINT32 exOp = (opcode>>1)&0x3ff; UINT32 bo = (opcode&M_BO)>>21; if (exOp == 0x0210) { // Instruction is branch conditional to count register: bcctr or bcctrl if (opcode&M_LK) opFlags = (EOpFlags)(JumpSub | Relative); // bcctrl else if (bo&0x04) opFlags = (EOpFlags)(JumpSimple | Relative); // bcctr without counter else opFlags = (EOpFlags)(JumpLoop | Relative); // bcctr with counter // Check BO is not just branch always return ((bo&0x14) == 0x14 ? opFlags : (EOpFlags)(opFlags | Conditional)); } else if (exOp == 0x0010) { // Instruction is branch conditional to link register: bclr or bclrl if (opcode&M_LK) opFlags = (EOpFlags)(JumpSub | ReturnSub); // bclrl else opFlags = ReturnSub; // bclr // Check BO is not just branch always return ((bo&0x14) == 0x14 ? opFlags : (EOpFlags)(opFlags | Conditional)); } else if (exOp == 0x0032) { // Instruction is return from interrupt: rfi return ReturnEx; } // TODO - traps etc } return NormalOp; } bool CPPCDebug::GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr) { // Check instruction is one of following branches: b, ba, bl, bla, bc, bca, bcl or bcla UINT32 disp; UINT32 op = opcode>>26; if (op == 0x10) { // Instruction is b, ba, bl or bla, so calculate branch displacement disp = ((opcode&M_BD)>>2) * 4; if (disp & 0x00008000) disp |= 0xFFFF0000; // Sign extended if (opcode&M_AA) jumpAddr = disp; // ba or bla else jumpAddr = addr + disp; // b or bl return true; } else if (op == 0x12) { // Instruction is bc, bca, bcl or bcla, so calculate branch displacement disp = ((opcode&M_LI) >> 2) * 4; if (disp & 0x02000000) disp |= 0xFC000000; // Sign extended if (opcode&M_AA) jumpAddr = disp; // bca or bcla else jumpAddr = addr + disp; // bc or bcl return true; } return false; } bool CPPCDebug::GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) { UINT32 op = opcode>>26; if ((op == 0x10 || op == 0x12) && (opcode&M_LK)) { // Instruction is bl, bla, bcl or bcla (TODO - add bclrl?) retAddr = addr + 4; return true; } return false; } bool CPPCDebug::GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) { // Check instruction is one of following: bclr, bclrl or rfi if ((opcode>>26) != 0x13) return false; UINT32 exOp = (opcode>>1)&0x3ff; if (exOp == 0x0010) { // For bclr and blclr, return address is in link register retAddr = ::ppc_get_lr(); return true; } else if (exOp == 0x0032) { // For rfi, return address is in SRR0 retAddr = ::ppc_read_spr(SPR_SRR0); return true; } return false; } bool CPPCDebug::GetHandlerAddr(CException *ex, UINT32 &handlerAddr) { UINT32 msr = ::ppc_read_msr(); UINT32 base = (msr&MSR_IP ? 0xFFF00000 : 0x00000000); switch (ex->code) { case EXCEPTION_DSI: handlerAddr = base + 0x0300; return true; case EXCEPTION_ISI: handlerAddr = base + 0x0400; return true; case EXCEPTION_IRQ: handlerAddr = base + 0x0500; return true; case EXCEPTION_TRAP: handlerAddr = base + 0x0700; return true; case EXCEPTION_DECREMENTER: handlerAddr = base + 0x0900; return true; case EXCEPTION_SYSTEM_CALL: handlerAddr = base + 0x0C00; return true; case EXCEPTION_SMI: handlerAddr = base + 0x1400; return true; default: return false; } } bool CPPCDebug::GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr) { UINT32 msr = ::ppc_read_msr(); handlerAddr = (msr&MSR_IP ? 0xFFF00500 : 0x00000500); return true; } // CBus methods UINT8 CPPCDebug::Read8(UINT32 addr) { UINT8 data = m_bus->Read8(addr); CheckRead8(addr, data); return data; } UINT16 CPPCDebug::Read16(UINT32 addr) { UINT16 data = m_bus->Read16(addr); CheckRead16(addr, data); return data; } UINT32 CPPCDebug::Read32(UINT32 addr) { UINT32 data = m_bus->Read32(addr); CheckRead32(addr, data); return data; } UINT64 CPPCDebug::Read64(UINT32 addr) { UINT64 data = m_bus->Read64(addr); CheckRead64(addr, data); return data; } void CPPCDebug::Write8(UINT32 addr, UINT8 data) { m_bus->Write8(addr, data); CheckWrite8(addr, data); } void CPPCDebug::Write16(UINT32 addr, UINT16 data) { m_bus->Write16(addr, data); CheckWrite16(addr, data); } void CPPCDebug::Write32(UINT32 addr, UINT32 data) { m_bus->Write32(addr, data); CheckWrite32(addr, data); } void CPPCDebug::Write64(UINT32 addr, UINT64 data) { m_bus->Write64(addr, data); CheckWrite64(addr, data); } } #endif // SUPERMODEL_DEBUGGER