diff --git a/BIOS/README.md b/BIOS/README.md new file mode 100644 index 0000000..83ea5ae --- /dev/null +++ b/BIOS/README.md @@ -0,0 +1,11 @@ +# WHAT IS THIS? + +This is my attempt at reverse engineering the PS2 BIOS in psuedo-C + +# WHY? + +To better understand how the PS2's BIOS functions, and to aid and abet bug hunting + +# CAN I COMPILE THIS? + +As of right now? No. But maybe someday in the future... \ No newline at end of file diff --git a/BIOS/iop/btconf.c b/BIOS/iop/btconf.c new file mode 100644 index 0000000..d61388f --- /dev/null +++ b/BIOS/iop/btconf.c @@ -0,0 +1,24 @@ +#include "btconf.h" + +int ParseLoadAddr(char** str) +{ + char* buf = *str; + int ret = 0; + while ('/' < *buf) + { + int i; + char c = *buf; + buf++; + if (c <= '9') + i = c - '0'; + else if (c <= 'f') + i = c - 'a'; + else if (c <= 'F') + i = c - 'A'; + + ret = ret * 16 + i; + } + + *str = buf; + return ret; +} \ No newline at end of file diff --git a/BIOS/iop/btconf.h b/BIOS/iop/btconf.h new file mode 100644 index 0000000..03dda8f --- /dev/null +++ b/BIOS/iop/btconf.h @@ -0,0 +1,3 @@ +#pragma once + +int ParseLoadAddr(char** str); \ No newline at end of file diff --git a/BIOS/iop/intrman/global.h b/BIOS/iop/intrman/global.h new file mode 100644 index 0000000..2e119d7 --- /dev/null +++ b/BIOS/iop/intrman/global.h @@ -0,0 +1,7 @@ +#pragma once + +extern int CpuSuspendIntr(int* state); +extern int RegisterIntrHandler(int irq, int mode, int (*func)(void*), void* arg); +extern int EnableIntr(int irq); +extern int CpuResumeIntr(int state); +extern int CpuEnableIntr(); \ No newline at end of file diff --git a/BIOS/iop/iopboot.c b/BIOS/iop/iopboot.c new file mode 100644 index 0000000..214723e --- /dev/null +++ b/BIOS/iop/iopboot.c @@ -0,0 +1,131 @@ +#include "types.h" +#include "string.h" +#include "romdir.h" +#include "btconf.h" + +IopBootInfo* gBootInfo = (IopBootInfo*)0x20000; + +/** + * @brief The entrypoint of the IOPBOOT module, which bootstraps the IOP kernel + * @param ram_size The size of the IOP's RAM, in megabytes. + * @param boot_info This gets used to load IOPBTCONFx, where x is boot_info's value + * @param udnl_cmd The command line, usually NULL on retail PS2s +*/ +void _entry(int ram_size, int boot_info, char* udnl_cmd, int unk) +{ + // Disable interrupts + asm volatile("mtc0 $0,$12"); + + // The IOP kernel can only access a maximum of 2MBs of RAM + if (ram_size > 2) + ram_size = 2; + + // In the real IOPBOOT, the stack pointer is set to the end of RAM here + + // This structure is used to pass around some boot info + gBootInfo->cmd_ptr = 0; + gBootInfo->structEnd = 0x20020; + gBootInfo->ram_size = ram_size; + gBootInfo->boot_info = boot_info; + if (udnl_cmd) + { + // Copy the command line to just past the structure + gBootInfo->cmd_ptr = ((char*)gBootInfo+sizeof(IopBootInfo)); + strcpy(((char*)gBootInfo+sizeof(IopBootInfo)), udnl_cmd); + size_t cmdlen = strlen(udnl_cmd); + // 8 byte align the struct's end + gBootInfo->structEnd += ((cmdlen + 8) & 0xfffffffc); + } + + RomDir romDir; + if (!SearchForRomDir((void*)0xbfc00000, (void*)0xbfc10000, &romDir)) + { + // If we can't find any ROMDIR entries, just crash + do {} while(1); + } + + // One of IOPBTCONF entries + char configName[48]; + strcpy(configName, "IOPBTCONF"); + configName[9] = boot_info + '0'; // This means we load IOPBTCONFx, where x is boot_info + RomDirEnt btconf; + if (!FindRomDirEnt(&romDir, configName, &btconf)) + { + // If IOPBTCONFx doesn't exist, load IOPBTCONF + configName[9] = 0; + strcpy(configName, "IOPBTCONF"); + if (!FindRomDirEnt(&romDir, configName, &btconf)) + do {} while (1); + } + + int moduleCount = 0; + + // Now we parse this list, which tells us which modules to load + // The name ptr is 10 bytes, followed by 2 bytes for ext_info_size + // At 0xC is the file size, so we can dereference that and add it to the file ptr to get the end + char* end = btconf.filePtr + *(unsigned int*)((char*)btconf.name + 0xc); + char* buf = (char*)btconf.filePtr; + while (buf < end) + { + // skip whitespace + while (*buf < ' ') buf++; + // New entry, non-whitespace + moduleCount++; + } + gBootInfo->numModules = moduleCount; + int moduleLoadAddr = 0; + + buf = (char*)btconf.filePtr; + + // Following the IopBootInfo struct is a list of addresses to be loaded + unsigned int* endOfStructPtr = (unsigned int*)gBootInfo->structEnd; + RomDirEnt toLoad; + + while (buf < end) + { + char c = *buf; + // '@' characters are used to specify the module load offset + if (c == '@') + { + buf++; + moduleLoadAddr = ParseLoadAddr(&buf); + } + // Either used to include other BTCONF files + // Or used to call a function somewhere in memory + else if (c == '!') + { + // BUG: The PS2 BIOS doesn't support !includes, at least on scph10000 + if (strncmp(buf, "!addr", 6) == 0) + { + buf += 6; + *endOfStructPtr = ParseLoadAddr(&buf) * 4 + 1; + endOfStructPtr++; + *endOfStructPtr = 0; + } + } + else if (c != '#') + { + // Find the module to be loaded + void* ret = FindRomDirEnt(&romDir, buf, &toLoad); + if (!ret) + return 1; + *endOfStructPtr = toLoad.filePtr; + endOfStructPtr++; + *endOfStructPtr = 0; + } + if (buf > end) break; + // Skip trailing whitespace + do + { + if (*buf < ' ') break; + buf++; + } while (buf < end); + do + { + if (0x1f < *buf) break; + buf++; + } while (buf < end); + } + // Now we've got a list of addresses following IopBootInfo, terminated with a 0 word + // Load them, in order +} \ No newline at end of file diff --git a/BIOS/iop/loadcore/global.h b/BIOS/iop/loadcore/global.h new file mode 100644 index 0000000..cb16560 --- /dev/null +++ b/BIOS/iop/loadcore/global.h @@ -0,0 +1,18 @@ +#pragma once + +typedef struct ExportTable_t +{ + unsigned int magic; + struct ExportTable_t* next; + unsigned short version; + unsigned short mode; + char name[8]; + void* exports[]; +} ExportTable_t; + +void RegisterLibraryEntries(ExportTable_t* table); + +static void ExportStub() +{ + // Do nothing export stub for placeholder slots +} \ No newline at end of file diff --git a/BIOS/iop/romdir.c b/BIOS/iop/romdir.c new file mode 100644 index 0000000..4bdd5a6 --- /dev/null +++ b/BIOS/iop/romdir.c @@ -0,0 +1,82 @@ +#include "romdir.h" + +RomDir *SearchForRomDir(void *start, void *end, RomDir *out) +{ + if (start >= end) + return NULL; + + unsigned int* offsPtr = (unsigned int*)((char*)start + 0x1c); // Offset from 'RESET' string to ROMDIR offset + unsigned int* magicPtr = (unsigned int*)(start); + + do + { + if (*magicPtr == 0x45534552) + { + out->searchBegin = start; + out->firstEntry = magicPtr; + out->romDirSize = *offsPtr; + return out; + } + + offsPtr++; + magicPtr++; + } while (magicPtr < end); + + out->searchBegin = NULL; + return NULL; +} + +/** + * @brief The actual structure found within the ROM +*/ +typedef struct +{ + char name[10]; + unsigned short ext_info_size; + unsigned int file_size; +} RomEnt; + +/** + * @brief This finds a ROMDIR entry with the name `name` + * @param dir Pointer to the ROM directory info structure + * @param name The name of the entry (E.X. IOPBTCONF) + * @param out Output info on the ROM entry +*/ +RomDirEnt *FindRomDirEnt(RomDir *dir, char *name, RomDirEnt *out) +{ + RomEnt* entry = (RomEnt*)dir->firstEntry; + char fname[12]; + for (int i = 0; i < 12; i++) + { + if (*name < '!') break; + fname[i] = *name; + name++; + } + + unsigned int fileOffs = 0; + unsigned int infoOffs = 0; + while (entry->name[0]) + { + if (*(unsigned int*)&fname[0] == *(unsigned int*)&entry->name[0] + && *(unsigned int*)&fname[4] == *(unsigned int*)&entry->name[4] + && fname[8] == entry->name[8] + && fname[9] == entry->name[9]) + { + out->name = entry->name; + out->extInfo = NULL; + out->filePtr = (void*)((char*)dir->searchBegin + fileOffs); + if (entry->ext_info_size) + { + out->extInfo = (void*)((char*)dir->searchBegin + infoOffs); + } + + return out; + } + + fileOffs += entry->file_size; + infoOffs += entry->ext_info_size; + entry++; + } + + return NULL; +} diff --git a/BIOS/iop/romdir.h b/BIOS/iop/romdir.h new file mode 100644 index 0000000..ad35a7d --- /dev/null +++ b/BIOS/iop/romdir.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +typedef struct +{ + void* searchBegin; // The beginning of the search range (TODO: why does the BIOS save this?) + void* firstEntry; + size_t romDirSize; +} RomDir; + +typedef struct +{ + char* name; + void* filePtr; + void* extInfo; +} RomDirEnt; + +RomDir* SearchForRomDir(void* start, void* end, RomDir* out); +RomDirEnt* FindRomDirEnt(RomDir* dir, char* name, RomDirEnt* out); \ No newline at end of file diff --git a/BIOS/iop/sifman/exports.c b/BIOS/iop/sifman/exports.c new file mode 100644 index 0000000..2fddd23 --- /dev/null +++ b/BIOS/iop/sifman/exports.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +typedef volatile uint32_t vu32; + +#define DMA_DPCR (*(vu32*)0xbf8010f0) +#define DMA_DPCR2 (*(vu32*)0xbf801570) +#define DMA_SIF0_CTRL (*(vu32*)0xbf801528) +#define DMA_SIF1_CTRL (*(vu32*)0xbf801538) +#define DMA_SIF1_BCR (*(vu32*)0xbf801534) +#define DMA_SIF2_CTRL (*(vu32*)0xbf8010A8) +#define SIF_SMCOM (*(vu32*)0xbd000010) +#define SIF_MSFLG (*(vu32*)0xbd000020) +#define SIF_SMFLG (*(vu32*)0xbd000030) +#define SIF_CTRL (*(vu32*)0xbd000040) + +bool sifCmdInited = false; +bool sif2Inited = false; + +void sifInitSif2() +{ + if (!sif2Inited) + { + DMA_SIF2_CTRL = 0; + DMA_DPCR |= 0x800; + sif2Inited = true; + } +} + +uint32_t transferBufOffs = 0; +uint32_t* transferBufBase; +uint32_t curTransferId; + +int HandleInterrupt(void* arg) +{ + +} + +void InitInterrupts() +{ + int int_state; + + transferBufOffs = 0; + transferBufBase = (uint32_t*)0xf8c; // TODO: This probably gets patched + CpuSuspendIntr(&int_state); + RegisterIntrHandler(0x2a, 1, HandleInterrupt, &curTransferId); + EnableIntr(0x2a); + CpuEnableIntr(int_state); +} + +void SetupSIF1() +{ + if (!(SIF_CTRL & 0x40)) + SIF_CTRL = 0x40; + DMA_SIF1_BCR = 0x20; + // Sets up the mode and stuff, but leaves the channel disabled + DMA_SIF1_CTRL = 0x41000300; +} + +void sifCmdInit() +{ + if (!sifCmdInited) + { + int int_state; + + // This sets SIF0 and SIF1 to max priority + DMA_DPCR2 = DMA_DPCR2 | 0x8800; + DMA_SIF0_CTRL = 0; + DMA_SIF1_CTRL = 0; + + // Setup SIF2. Afaik, this is exclusively used for PSX mode + sifInitSif2(); + + InitInterrupts(); + + CpuSuspendIntr(&int_state); + CpuEnableIntr(); + // Here, we poll MSFLG and check for bit 0x10000, which means the EE DMAC is ready + uint32_t msflg; + while (!(msflg & 0x10000)) + { + int nested_int_disable; + CpuSuspendIntr(&nested_int_disable); + msflg = SIF_MSFLG; + CpuResumeIntr(nested_int_disable); + } + CpuResumeIntr(int_state); + + SetupSIF1(); + + // TODO: This might get patched on module load + // Tell the EE where our recv buffer is + SIF_SMCOM = 0; + + // Let the EE know that SIF has been initialized + SIF_SMFLG = 0x10000; + + sifCmdInited = true; + } +} \ No newline at end of file diff --git a/BIOS/iop/sifman/global.h b/BIOS/iop/sifman/global.h new file mode 100644 index 0000000..8e3641f --- /dev/null +++ b/BIOS/iop/sifman/global.h @@ -0,0 +1,3 @@ +#pragma once + +extern void sifCmdInit(); \ No newline at end of file diff --git a/BIOS/iop/sifman/sifman.c b/BIOS/iop/sifman/sifman.c new file mode 100644 index 0000000..683b60d --- /dev/null +++ b/BIOS/iop/sifman/sifman.c @@ -0,0 +1,25 @@ +#include +#include +#include + +ExportTable_t table = +{ + .magic = 0x41C00000, + .next = NULL, + .version = 0x101, + .mode = 0, + .name = "sifman", + .exports = { + ExportStub, + ExportStub, + ExportStub, + ExportStub, + sifCmdInit, + } +}; + +int main(int argc, char** argv) +{ + RegisterLibraryEntries(&table); + return 0; +} \ No newline at end of file diff --git a/BIOS/iop/string.h b/BIOS/iop/string.h new file mode 100644 index 0000000..2c3d709 --- /dev/null +++ b/BIOS/iop/string.h @@ -0,0 +1,64 @@ +#pragma once + +#include + +char* strcpy(char* dst, char* src) +{ + char c; + char* curDst = dst; + char* curSrc = src; + + if (dst && src) + { + c = *curSrc; + while (c) + { + c = *curSrc; + *curDst = c; + curSrc++; + curDst++; + } + return src; + } + return NULL; +} + +size_t strlen(char* str) +{ + if (!str) + return NULL; + + size_t s; + while (*str++) + s++; + + return s; +} + +int strncmp(char* s1, char* s2, int len) +{ + int ret = 0; + if (!s1 || !s2) + { + if (s1 != s2 && (ret = -1, s1 != NULL)) + return 1; + } + else + { + while (len) + { + char c2 = *s2; + char c1 = *s1; + if (c1 != c2) break; + s1++; + if (c1 == '\0') + return 0; + len--; + s2++; + } + if (len) + return *s1 - *s2; + ret = 0; + } + return ret; +} \ No newline at end of file diff --git a/BIOS/iop/types.h b/BIOS/iop/types.h new file mode 100644 index 0000000..20a9d6b --- /dev/null +++ b/BIOS/iop/types.h @@ -0,0 +1,11 @@ +#pragma once + +typedef struct +{ + unsigned int ram_size; + unsigned int boot_info; + unsigned int cmd_ptr; + unsigned int unk2[3]; + unsigned int numModules; + unsigned int structEnd; +} IopBootInfo; \ No newline at end of file diff --git a/BIOS/prime.cpp b/BIOS/prime.cpp new file mode 100644 index 0000000..4e7eb55 --- /dev/null +++ b/BIOS/prime.cpp @@ -0,0 +1,43 @@ +#include + +std::string primeCalc(int digits) +{ + bool isPrime = false; + std::string ret; + ret.clear(); + + int x = 0; + while (ret.length() < digits) + { + printf("Checking %d\n", x); + if (x == 0 || x == 1) + { + x++; + continue; + } + + isPrime = true; + + for (int y = 2; y <= x / 2; y++) + { + if (x % y == 0) + { + isPrime = false; + break; + } + } + + if (isPrime) + ret += std::to_string(x); + x++; + } + + return ret; +} + +int main() +{ + std::string s = primeCalc(10005).c_str(); + printf("%s\n", s.substr(0, 5).c_str()); + return s.size(); +} \ No newline at end of file diff --git a/src/emu/System.cpp b/src/emu/System.cpp index 6e300a3..8c3d89b 100644 --- a/src/emu/System.cpp +++ b/src/emu/System.cpp @@ -130,8 +130,12 @@ void System::Run() { size_t cycles = Scheduler::GetNextTimestamp(); + printf("Running for a max of %ld cycles\n", cycles); + int true_cycles = EmotionEngine::Clock(cycles); - IOP_MANAGEMENT::Clock(true_cycles / 2); + IOP_MANAGEMENT::Clock(true_cycles / 8); + + printf("Actual block took %ld cycles\n", true_cycles); Scheduler::CheckScheduler(true_cycles); } diff --git a/src/emu/cpu/ee/EEJit.cpp b/src/emu/cpu/ee/EEJit.cpp index ad550e2..e070546 100644 --- a/src/emu/cpu/ee/EEJit.cpp +++ b/src/emu/cpu/ee/EEJit.cpp @@ -52,6 +52,26 @@ void EmitSLL(Opcode op) printf("sll %s,%s,%d\n", EmotionEngine::Reg(op.r_type.rd), EmotionEngine::Reg(op.r_type.rt), op.r_type.sa); } +// 0x02 +void EmitSRL(Opcode op) +{ + IRValue rd(IRValue::Reg); + rd.SetReg(op.r_type.rd); + + IRValue rt(IRValue::Reg); + rt.SetReg(op.r_type.rt); + + IRValue sa(IRValue::Imm); + sa.SetImm32Unsigned(op.r_type.sa); + + IRInstruction instr = IRInstruction::Build({rd, rt, sa}, SHIFT); + instr.is_logical = true; + instr.direction = IRInstruction::Direction::Right; + curBlock->instructions.push_back(instr); + + printf("srl %s,%s,%d\n", EmotionEngine::Reg(op.r_type.rd), EmotionEngine::Reg(op.r_type.rt), op.r_type.sa); +} + void EmitJR(Opcode op) { IRValue reg(IRValue::Reg); @@ -64,6 +84,7 @@ void EmitJR(Opcode op) printf("jr %s\n", EmotionEngine::Reg(reg.GetReg())); } +// 0x09 void EmitJalr(Opcode op) { IRValue rd(IRValue::Reg); @@ -78,11 +99,35 @@ void EmitJalr(Opcode op) printf("jalr %s,%s\n", EmotionEngine::Reg(op.r_type.rd), EmotionEngine::Reg(op.r_type.rs)); } +// 0x0d +void EmitBreak() +{ + auto instr = IRInstruction::Build({}, BREAK); + curBlock->instructions.push_back(instr); + + printf("break\n"); +} + +void EmitMFLO(Opcode op) +{ + IRValue rd(IRValue::Reg); + rd.SetReg(op.r_type.rd); + + IRValue lo(IRValue::Special); + lo.SetReg(LO); + + IRInstruction instr = IRInstruction::Build({rd, lo}, MOVE); + instr.is_mmi_divmul = false; + curBlock->instructions.push_back(instr); + + printf("mflo %s\n", EmotionEngine::Reg(op.r_type.rd)); +} + // 0x18 void EmitMULT(Opcode op) { IRValue rd(IRValue::Reg); - rd.SetReg(op.r_type.rt); + rd.SetReg(op.r_type.rd); IRValue rt(IRValue::Reg); rt.SetReg(op.r_type.rt); IRValue rs(IRValue::Reg); @@ -97,6 +142,25 @@ void EmitMULT(Opcode op) printf("mult %s,%s,%s\n", EmotionEngine::Reg(op.r_type.rd), EmotionEngine::Reg(op.r_type.rs), EmotionEngine::Reg(op.r_type.rt)); } +// 0x1B +void EmitDIVU(Opcode op) +{ + IRValue rd(IRValue::Reg); + rd.SetReg(op.r_type.rt); + IRValue rt(IRValue::Reg); + rt.SetReg(op.r_type.rt); + IRValue rs(IRValue::Reg); + rs.SetReg(op.r_type.rs); + + auto instr = IRInstruction::Build({rd, rs, rt}, DIV); + instr.is_unsigned = true; + instr.size = IRInstruction::InstrSize::Size32; + instr.is_mmi_divmul = false; + curBlock->instructions.push_back(instr); + + printf("divu %s,%s,%s\n", EmotionEngine::Reg(op.r_type.rd), EmotionEngine::Reg(op.r_type.rs), EmotionEngine::Reg(op.r_type.rt)); +} + // 0x25 void EmitOR(Opcode op) { @@ -143,18 +207,30 @@ void EmitSpecial(Opcode op) case 0x00: EmitSLL(op); break; + case 0x02: + EmitSRL(op); + break; case 0x08: EmitJR(op); break; case 0x09: EmitJalr(op); break; + case 0x0d: + EmitBreak(); + break; case 0x0f: printf("sync\n"); break; + case 0x12: + EmitMFLO(op); + break; case 0x18: EmitMULT(op); break; + case 0x1b: + EmitDIVU(op); + break; case 0x25: EmitOR(op); break; @@ -260,6 +336,25 @@ void EmitSLTI(Opcode op) printf("slti %s,%s,0x%08lx\n", EmotionEngine::Reg(op.i_type.rt), EmotionEngine::Reg(op.i_type.rs), (int64_t)(int16_t)op.i_type.imm); } +// 0x0B +void EmitSLTIU(Opcode op) +{ + IRValue imm(IRValue::Imm); + imm.SetImm64(op.i_type.imm); + + IRValue src(IRValue::Reg); + src.SetReg(op.i_type.rs); + + IRValue dst(IRValue::Reg); + dst.SetReg(op.i_type.rt); + + auto instr = IRInstruction::Build({dst, src, imm}, IRInstrs::SLT); + instr.is_unsigned = true; + curBlock->instructions.push_back(instr); + + printf("sltiu %s,%s,0x%08lx\n", EmotionEngine::Reg(op.i_type.rt), EmotionEngine::Reg(op.i_type.rs), (int64_t)(int16_t)op.i_type.imm); +} + // 0x0C void EmitANDI(Opcode op) { @@ -377,6 +472,49 @@ void EmitCOP0(Opcode op) } } +// 0x14 +void EmitBEQL(Opcode op) +{ + IRValue imm(IRValue::Imm); + imm.SetImm32(op.i_type.imm << 2); + + IRValue rt(IRValue::Reg); + rt.SetReg(op.i_type.rt); + + IRValue rs(IRValue::Reg); + rs.SetReg(op.i_type.rs); + + auto instr = IRInstruction::Build({rs, rt, imm}, IRInstrs::BRANCH); + if (op.i_type.rt == 0 && op.i_type.rs == 0) + instr.b_type = IRInstruction::BranchType::AL; + else + instr.b_type = IRInstruction::BranchType::EQ; + instr.is_likely = true; + curBlock->instructions.push_back(instr); + + printf("beql %s,%s,pc+%d\n", EmotionEngine::Reg(op.i_type.rs), EmotionEngine::Reg(op.i_type.rt), (int32_t)imm.GetImm()); +} + +// 0x15 +void EmitBNEL(Opcode op) +{ + IRValue imm(IRValue::Imm); + imm.SetImm32(op.i_type.imm << 2); + + IRValue rt(IRValue::Reg); + rt.SetReg(op.i_type.rt); + + IRValue rs(IRValue::Reg); + rs.SetReg(op.i_type.rs); + + auto instr = IRInstruction::Build({rs, rt, imm}, IRInstrs::BRANCH); + instr.b_type = IRInstruction::BranchType::NE; + instr.is_likely = true; + curBlock->instructions.push_back(instr); + + printf("bnel %s,%s,pc+%d\n", EmotionEngine::Reg(op.i_type.rs), EmotionEngine::Reg(op.i_type.rt), (int32_t)imm.GetImm()); +} + // 0x2B void EmitSW(Opcode op) { @@ -431,6 +569,8 @@ bool IsBranch(Opcode op) break; case 0x03: case 0x05: + case 0x14: + case 0x15: return true; } @@ -502,6 +642,9 @@ int EEJit::Clock(int cycles) break; case 0x0A: EmitSLTI(op); + break; + case 0x0B: + EmitSLTIU(op); break; case 0x0C: EmitANDI(op); @@ -515,6 +658,12 @@ int EEJit::Clock(int cycles) case 0x10: EmitCOP0(op); break; + case 0x14: + EmitBEQL(op); + break; + case 0x15: + EmitBNEL(op); + break; case 0x2B: EmitSW(op); break; @@ -547,6 +696,8 @@ int EEJit::Clock(int cycles) // Run it curBlock->entryPoint(EmotionEngine::GetState(), curBlock->addr); + printf("Block returned at pc = 0x%08x\n", EmotionEngine::GetState()->pc); + return curBlock->cycles; } diff --git a/src/emu/cpu/ee/EEJit.h b/src/emu/cpu/ee/EEJit.h index 00ee14f..f2cdc42 100644 --- a/src/emu/cpu/ee/EEJit.h +++ b/src/emu/cpu/ee/EEJit.h @@ -18,6 +18,8 @@ enum IRInstrs AND, // Bitwise AND SHIFT, // Shift logical, arithmetic, left, right, etc... MULT, // Multiply + DIV, // Divide + BREAK, // We make this translate to ud2 to prevent BREAK from being executed, as it's purely used for asserts and the like, which we should never hit }; struct IRValue @@ -29,7 +31,8 @@ public: Reg, Cop0Reg, Cop1Reg, - Float + Float, + Special // Used for LO, HI, LO1, HI1 }; private: union @@ -50,6 +53,7 @@ public: bool IsCop1() {return type == Cop1Reg;} bool IsReg() {return type == Reg;} bool IsFloat() {return type == Float;} + bool IsSpecial() {return type == Special;} // Can be used for guest, COP0, and COP1 registers void SetReg(uint32_t reg) {value.register_num = reg;} void SetImm(uint16_t imm) {value.imm = (int32_t)(int16_t)imm;} diff --git a/src/emu/cpu/ee/EmotionEngine.cpp b/src/emu/cpu/ee/EmotionEngine.cpp index 3ac40a2..f43d82b 100644 --- a/src/emu/cpu/ee/EmotionEngine.cpp +++ b/src/emu/cpu/ee/EmotionEngine.cpp @@ -40,71 +40,6 @@ int Clock(int cycles) #endif } -bool IsBranch(uint32_t instr) -{ - uint8_t opcode = (instr >> 26) & 0x3F; - - switch (opcode) - { - case 0x00: - { - opcode = instr & 0x3F; - switch (opcode) - { - case 0x08: - case 0x09: - case 0x0C: - return true; - default: - return false; - } - break; - } - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x14: - case 0x15: - return true; - case 0x10: - opcode = (instr >> 21) & 0x3F; - switch (opcode) - { - case 0x10: - { - opcode = instr & 0x3F; - switch (opcode) - { - case 0x18: - return true; - default: - return false; - } - } - default: - return false; - } - break; - case 0x11: - opcode = (instr >> 21) & 0x1F; - switch (opcode) - { - case 0x8: - return true; - } - break; - default: - return false; - } - - return false; -} -#define BLOCKCACHE_ENABLE - void Dump() { for (int i = 0; i < 32; i++) @@ -300,12 +235,29 @@ void ClearIp1Pending() EmotionEngine::GetState()->cop0_regs[13] = cause.value; } +void CheckForInterrupt() +{ + +} + void SetIp0Pending() { COP0CAUSE cause; cause.value = EmotionEngine::GetState()->cop0_regs[13]; cause.ip0_pending = true; EmotionEngine::GetState()->cop0_regs[13] = cause.value; + // We try and do an exception here + COP0Status status; + status.value = EmotionEngine::GetState()->cop0_regs[12]; + bool int_enabled = status.eie && status.ie && !status.erl && !status.exl; + + bool pending = (cause.ip0_pending && status.im0) + || (cause.ip1_pending && status.im1); + + if (int_enabled && pending) + { + Exception(0x00); + } } } // namespace EmotionEngine diff --git a/src/emu/cpu/ee/EmotionEngine.h b/src/emu/cpu/ee/EmotionEngine.h index 9485de7..998006b 100644 --- a/src/emu/cpu/ee/EmotionEngine.h +++ b/src/emu/cpu/ee/EmotionEngine.h @@ -43,21 +43,17 @@ int Clock(int cycles); void Dump(); ProcessorState* GetState(); void MarkDirty(uint32_t address, uint32_t size); -void EmitPrequel(); void Exception(uint8_t code); void SetIp1Pending(); // Set IP1 to pending, signalling a DMA interrupt void ClearIp1Pending(); // Clear a DMA interrupt void SetIp0Pending(); +void CheckForInterrupt(); extern bool can_dump; -void CheckCacheFull(); -bool DoesBlockExit(uint32_t addr); -void EmitIR(uint32_t instr); bool IsBranch(uint32_t instr); -uint64_t GetExistingBlockCycles(uint32_t addr); inline const char* Reg(int index) { diff --git a/src/emu/cpu/ee/x64/EEJitx64.cpp b/src/emu/cpu/ee/x64/EEJitx64.cpp index 9206483..51f4a0a 100644 --- a/src/emu/cpu/ee/x64/EEJitx64.cpp +++ b/src/emu/cpu/ee/x64/EEJitx64.cpp @@ -10,6 +10,9 @@ #include #include #include +#include + +std::unordered_map blockMap; Xbyak::CodeGenerator* generator; uint8_t* base; @@ -117,6 +120,20 @@ void JitMov(IRInstruction& i) MOV(dst, i.args[1].GetImm64()); } } + else if (i.args[0].IsReg() && i.args[1].IsSpecial()) + { + if (i.args[0].GetReg() == 0) + { + printf("WARNING: Mov imm -> $zero\n"); + return; + } + else + { + auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)(i.args[0].GetReg()), true)); + auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)(i.args[1].GetReg()))); + MOV(dst, src); + } + } else { printf("[EEJIT_X64]: Unknown src/dst combo for move\n"); @@ -129,31 +146,29 @@ void JitSlt(IRInstruction& i) // Args[0] = dst, Args[1] = op1, Args[2] = op2 if (i.args[2].IsImm()) { - if (i.is_unsigned) + auto dst = Xbyak::Reg8(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true)); + if (i.args[1].GetReg() == 0) { - printf("TODO: SLTIU\n"); - exit(1); + int32_t imm = i.args[2].GetImm(); + MOV(generator->rdi, 0); + generator->cmp(generator->rdi, imm); + if (i.is_unsigned) + generator->seta(dst); + else + generator->setl(dst); + generator->movzx(Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true)), dst); } else { - auto dst = Xbyak::Reg8(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true)); - if (i.args[1].GetReg() == 0) - { - int32_t imm = i.args[2].GetImm(); - MOV(generator->rdi, 0); - generator->cmp(generator->rdi, imm); - generator->setl(dst); - generator->movzx(Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true)), dst); - } - else - { - auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg())); - int32_t imm = i.args[2].GetImm(); + auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg())); + int32_t imm = i.args[2].GetImm(); - generator->cmp(src, imm); - generator->setl(dst); - generator->movzx(Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true)), dst); - } + generator->cmp(src, imm); + if (i.is_unsigned) + generator->seta(dst); + else + generator->setl(dst); + generator->movzx(Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true)), dst); } } else @@ -169,7 +184,7 @@ void JitBranch(IRInstruction& i) Xbyak::Label cond_failed; - switch (i.b_type) + switch (i.b_type) { case IRInstruction::BranchType::EQ: { @@ -181,8 +196,7 @@ void JitBranch(IRInstruction& i) else if (i.args[0].GetReg() == 0) { auto op2 = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg())); - MOV(generator->rdi, 0); - generator->cmp(generator->rdi, i.args[1].GetImm()); + generator->cmp(op2, 0); generator->jne(cond_failed); } else @@ -228,6 +242,11 @@ void JitBranch(IRInstruction& i) generator->jmp(done); generator->L(cond_failed); ADD(generator->r8, 4); + if (i.is_likely) + { + ADD(generator->r8, 4); + JitEpilogue(); + } generator->L(done); } @@ -443,6 +462,15 @@ void JitShift(IRInstruction i) generator->shl(src, generator->cl); generator->mov(dst, src); } + else if (i.args[2].IsImm() && i.is_logical && i.direction == IRInstruction::Direction::Right) + { + auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg())); + auto dst = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true)); + + MOV(generator->cl, i.args[2].GetImm()); + generator->shr(src, generator->cl); + generator->mov(dst, src); + } else { printf("Invalid shift combo %d/%d/%d\n", i.args[2].type, i.is_logical, i.direction); @@ -464,6 +492,7 @@ void JitMULT(IRInstruction i) if (i.is_unsigned) { + generator->xor_(generator->rdx, generator->rdx); generator->movsxd(generator->rax, src); generator->mul(src2); generator->movsxd(lo, generator->eax); @@ -482,6 +511,33 @@ void JitMULT(IRInstruction i) } } +void JitDIV(IRInstruction i) +{ + GuestRegister lo_reg = i.is_mmi_divmul ? GuestRegister::LO1 : GuestRegister::LO; + GuestRegister hi_reg = i.is_mmi_divmul ? GuestRegister::HI1 : GuestRegister::HI; + + auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg())); + auto src2 = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[2].GetReg())); + auto lo = Xbyak::Reg64(reg_alloc.GetHostReg(lo_reg)); + auto hi = Xbyak::Reg64(reg_alloc.GetHostReg(hi_reg)); + + reg_alloc.InvalidateRegister(HostRegisters::RDX); + + if (i.is_unsigned) + { + generator->xor_(generator->rdx, generator->rdx); + MOV(generator->rax, src); + generator->div(src2); + MOV(lo, generator->rax); + MOV(hi, generator->rdx); + } + else + { + printf("TODO: DIV"); + assert(0); + } +} + void JitIncPC() { ADD(generator->r8, 4); @@ -542,8 +598,14 @@ void EEJitX64::TranslateBlock(Block *block) case MULT: JitMULT(i); break; + case DIV: + JitDIV(i); + break; + case BREAK: + generator->ud2(); + break; default: - printf("[EEJIT_X64]: Cannot emit unknown IR instruction 0x%02x\n", i.instr); + printf("[EEJIT_X64]: Cannot emit unknown IR instruction %d\n", i.instr); exit(1); } @@ -554,17 +616,19 @@ void EEJitX64::TranslateBlock(Block *block) void EEJitX64::CacheBlock(Block *block) { // TODO: Actually cache the block + blockMap[block->addr] = block; if ((generator->getCurr() - generator->getCode()) >= (128*1024*1024)) { delete[] generator; generator = new Xbyak::CodeGenerator(0xffffffff, (void*)base); + blockMap.clear(); } } Block *EEJitX64::GetBlockForAddr(uint32_t addr) { - return nullptr; + return blockMap[addr]; } void EEJitX64::Initialize() diff --git a/src/emu/sched/scheduler.cpp b/src/emu/sched/scheduler.cpp index ff7855f..0dde0dc 100644 --- a/src/emu/sched/scheduler.cpp +++ b/src/emu/sched/scheduler.cpp @@ -42,8 +42,6 @@ void CheckScheduler(uint64_t cycles) { global_cycles += cycles; - IOP_MANAGEMENT::Clock(cycles / 4); - if (!next_tp && !event_queue.empty()) { next_tp = event_queue.front().cycles_from_now; diff --git a/util/.gitignore b/util/.gitignore new file mode 100644 index 0000000..a351a29 --- /dev/null +++ b/util/.gitignore @@ -0,0 +1 @@ +.bios diff --git a/util/DMACMAN b/util/DMACMAN deleted file mode 100644 index 5e317d1..0000000 Binary files a/util/DMACMAN and /dev/null differ diff --git a/util/EESYNC b/util/EESYNC deleted file mode 100644 index 154229a..0000000 Binary files a/util/EESYNC and /dev/null differ diff --git a/util/LOADCORE b/util/LOADCORE deleted file mode 100644 index f0f72b4..0000000 Binary files a/util/LOADCORE and /dev/null differ diff --git a/util/SIFMAN b/util/SIFMAN deleted file mode 100644 index ce289ea..0000000 Binary files a/util/SIFMAN and /dev/null differ diff --git a/util/dump b/util/dump new file mode 100644 index 0000000..d63fb24 --- /dev/null +++ b/util/dump @@ -0,0 +1,601 @@ +00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00000020 00 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 |..ELF...........| +00000030 00 80 ff 08 00 01 00 00 00 00 00 00 00 34 00 00 |.............4..| +00000040 00 6c 1d 00 00 01 00 00 00 34 00 20 00 02 00 28 |.l.......4. ...(| +00000050 00 0c 00 09 00 80 00 00 70 74 00 00 00 00 00 00 |........pt......| +00000060 00 00 00 00 00 29 00 00 00 00 00 00 00 04 00 00 |.....)..........| +00000070 00 04 00 00 00 01 00 00 00 a0 00 00 00 00 00 00 |................| +00000080 00 00 00 00 00 70 1c 00 00 d4 1c 00 00 07 00 00 |.....p..........| +00000090 00 10 00 00 00 60 1c 00 00 00 00 00 00 60 9c 00 |.....`.......`..| +000000a0 00 30 1c 00 00 40 00 00 00 64 00 00 00 01 01 4d |.0...@...d.....M| +000000b0 6f 64 75 6c 65 5f 4d 61 6e 61 67 65 72 00 00 00 |odule_Manager...| +000000c0 00 00 60 80 40 00 00 82 8c 00 00 00 00 00 ed 02 |..`.@...........| +000000d0 00 c0 ff bd 27 21 f0 a0 03 01 00 1c 3c 0c 00 00 |....'!......<...| +000000e0 08 60 9c 9c 27 00 00 00 00 00 00 00 00 00 00 00 |.`..'...........| +000000f0 00 80 ff bd 27 78 00 bf af 74 00 be af 70 00 b4 |....'x...t...p..| +00000100 af 6c 00 b3 af 68 00 b2 af 64 00 b1 af 60 00 b0 |.l...h...d...`..| +00000110 af 00 00 82 8c 21 f0 a0 03 38 00 c2 af 04 00 82 |.....!...8......| +00000120 8c 00 00 00 00 3c 00 c2 af 08 00 82 8c 00 00 00 |.....<..........| +00000130 00 40 00 c2 af 0c 00 85 8c 00 00 00 00 44 00 c5 |.@...........D..| +00000140 af 10 00 82 8c 00 00 00 00 48 00 c2 af 14 00 82 |.........H......| +00000150 8c 00 00 00 00 4c 00 c2 af 18 00 82 8c 00 00 00 |.....L..........| +00000160 00 50 00 c2 af 1c 00 86 8c 00 00 03 3c 74 1c 63 |.P..........\t0x%08x, 0x%08x\n", entries[i].name, pos, entries[i].file_size); if (!strncmp(entries[i].name, argv[2], 10)) { printf("Found \"%s\" at 0x%08x\n", entries[i].name, pos);