diff --git a/.clangd/index/Bus.h.692EB0605776C694.idx b/.clangd/index/Bus.h.692EB0605776C694.idx index 666c2ab..41bfb3c 100644 Binary files a/.clangd/index/Bus.h.692EB0605776C694.idx and b/.clangd/index/Bus.h.692EB0605776C694.idx differ diff --git a/.clangd/index/EmotionEngine.cpp.1BC64813B6E780E3.idx b/.clangd/index/EmotionEngine.cpp.1BC64813B6E780E3.idx index b025fc2..aa852b6 100644 Binary files a/.clangd/index/EmotionEngine.cpp.1BC64813B6E780E3.idx and b/.clangd/index/EmotionEngine.cpp.1BC64813B6E780E3.idx differ diff --git a/.clangd/index/EmotionEngine.h.57C5F101B17A7B44.idx b/.clangd/index/EmotionEngine.h.57C5F101B17A7B44.idx index a2394bf..a83a05a 100644 Binary files a/.clangd/index/EmotionEngine.h.57C5F101B17A7B44.idx and b/.clangd/index/EmotionEngine.h.57C5F101B17A7B44.idx differ diff --git a/README.md b/README.md index 31e3f74..918bdcd 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,5 @@ A WIP PlayStation 2 emulator Currently implemented: + Some Emotion Engine opcodes \ No newline at end of file diff --git a/src/emu/Bus.cpp b/src/emu/Bus.cpp index c55b253..8aca2fb 100644 --- a/src/emu/Bus.cpp +++ b/src/emu/Bus.cpp @@ -30,4 +30,6 @@ Bus::Bus(std::string fileName, bool& s) s = true; printf("[emu/Bus]: %s: Bus initialized\n", __FUNCTION__); + + console.open("log.txt"); } \ No newline at end of file diff --git a/src/emu/Bus.h b/src/emu/Bus.h index 2331d02..e87466b 100644 --- a/src/emu/Bus.h +++ b/src/emu/Bus.h @@ -2,6 +2,7 @@ #include #include +#include class Bus { @@ -10,6 +11,8 @@ private: uint8_t bios[0x400000]; uint8_t scratchpad[0x4000]; + std::ofstream console; + uint32_t Translate(uint32_t addr) { if (addr <= 0x7FFFFFFF) @@ -35,6 +38,14 @@ public: if (addr >= 0x1FC00000 && addr < 0x21C00000) return *(T*)&bios[addr - 0x1FC00000]; + if (addr >= 0x70000000 && addr < 0x70004000) + return *(T*)&scratchpad[addr - 0x70000000]; + + switch (addr) + { + case 0x1000f130: // SIO status + return 0; + } printf("[emu/Bus]: %s: Failed to read from address 0x%08x\n", __FUNCTION__, addr); exit(1); @@ -53,10 +64,17 @@ public: switch (addr) { + // misc SIO settings case 0x1000f100: case 0x1000f120: + case 0x1000f140: + case 0x1000f150: case 0x1000f500: return; + case 0x1000f180: + console << (char)data; + console.flush(); + return; } printf("[emu/Bus]: %s: Write to unknown addr 0x%08x\n", __FUNCTION__, addr); diff --git a/src/emu/cpu/EmotionEngine.cpp b/src/emu/cpu/EmotionEngine.cpp index cb8d2dd..3dffd65 100644 --- a/src/emu/cpu/EmotionEngine.cpp +++ b/src/emu/cpu/EmotionEngine.cpp @@ -13,12 +13,20 @@ EmotionEngine::EmotionEngine(Bus* bus) next_pc = pc + 4; cop0_regs[15] = 0x2E20; + + for (int i = 0; i < 128; i++) + { + icache[i].tag[0] = 1 << 31; + icache[i].tag[1] = 1 << 31; + icache[i].lfu[0] = false; + icache[i].lfu[1] = false; + } } void EmotionEngine::Clock() { Opcode instr; - instr.full = Read32Instr(pc); + instr.full = Read32(pc, true); AdvancePC(); @@ -37,6 +45,9 @@ void EmotionEngine::Clock() case 0x00: sll(instr); break; + case 0x03: + sra(instr); + break; case 0x08: jr(instr); break; @@ -46,9 +57,15 @@ void EmotionEngine::Clock() case 0x0F: printf("sync\n"); break; + case 0x12: + mflo(instr); + break; case 0x18: mult(instr); break; + case 0x1B: + divu(instr); + break; case 0x25: op_or(instr); break; @@ -62,9 +79,26 @@ void EmotionEngine::Clock() } } break; - case 0x03: + case 0x01: + { + switch (instr.i_type.rt) + { + case 0x00: + bltz(instr); + break; + default: + printf("[emu/CPU]: %s: Unknown regimm instruction 0x%08x (0x%02x)\n", __FUNCTION__, instr.full, instr.i_type.rt); + Application::Exit(1); + break; + } + } + break; + case 0x02: j(instr); break; + case 0x03: + jal(instr); + break; case 0x04: beq(instr); break; @@ -77,6 +111,9 @@ void EmotionEngine::Clock() case 0x0A: slti(instr); break; + case 0x0B: + sltiu(instr); + break; case 0x0C: andi(instr); break; @@ -106,9 +143,33 @@ void EmotionEngine::Clock() } } break; + case 0x14: + beql(instr); + break; + case 0x15: + bnel(instr); + break; + case 0x20: + lb(instr); + break; + case 0x23: + lw(instr); + break; + case 0x24: + lbu(instr); + break; + case 0x28: + sb(instr); + break; case 0x2B: sw(instr); break; + case 0x37: + ld(instr); + break; + case 0x39: + swc1(instr); + break; case 0x3f: sd(instr); break; @@ -124,4 +185,5 @@ void EmotionEngine::Dump() { for (int i = 0; i < 32; i++) printf("[emu/CPU]: %s: %s\t->\t%s\n", __FUNCTION__, Reg(i), print_128(regs[i])); + printf("[emu/CPU]: %s: pc\t->\t0x%08x\n", __FUNCTION__, pc-4); } \ No newline at end of file diff --git a/src/emu/cpu/EmotionEngine.h b/src/emu/cpu/EmotionEngine.h index 372ed48..cb002b4 100644 --- a/src/emu/cpu/EmotionEngine.h +++ b/src/emu/cpu/EmotionEngine.h @@ -15,6 +15,15 @@ private: uint32_t pc, next_pc; uint64_t hi, lo; + struct COP1 + { + union + { + float f[32] = {0.0f}; + uint32_t i[32]; + }; + } cop1; + struct CacheTag { bool valid = false; @@ -23,47 +32,68 @@ private: uint32_t page = 0; }; - struct Cache + struct ICacheLine { - CacheTag tag[2]; - uint8_t data[2][64] = {0}; - } icache[128], dcache[64]; + bool lfu[2]; + uint32_t tag[2]; + uint8_t data[2][64]; + }; + + ICacheLine icache[128]; bool isCacheEnabled = false; - uint32_t Read32Instr(uint32_t addr) + uint32_t Read32(uint32_t addr, bool isInstr = false) { - if (!bus->IsCacheable(addr) || !isCacheEnabled) + if (!bus->IsCacheable(addr) || !isCacheEnabled || !isInstr) return bus->read(addr); - uint32_t page = (addr >> 14); - uint32_t index = (addr >> 5) & 8; - uint32_t offset = addr & 0x3F; + int index = (addr >> 6) & 0x7F; + uint16_t tag = addr >> 13; + int off = addr & 0x3F; - Cache& line = icache[index]; + ICacheLine& line = icache[index]; - for (int way = 0; way < 2; way++) + if (line.tag[0] != tag) { - if (line.tag[way].page == page && line.tag[way].valid) + if (line.tag[1] != tag) { - return *((uint32_t*)&line.data[way][offset]); + printf("Cache miss for addr 0x%08x\n", addr); + if (line.tag[0] & (1 << 31)) + { + line.lfu[0] ^= true; + line.tag[0] = tag; + for (int i = 0; i < 64; i++) + line.data[0][i] = bus->read((addr & 0xFFFFFFC0) + i); + return *(uint32_t*)&line.data[0][off]; + } + else if (line.tag[1] & (1 << 31)) + { + line.lfu[1] ^= true; + line.tag[1] = tag; + for (int i = 0; i < 64; i++) + line.data[1][i] = bus->read((addr & 0xFFFFFFC0) + i); + return *(uint32_t*)&line.data[1][off]; + } + else + { + int replace = line.lfu[0] ^ line.lfu[1]; + line.lfu[replace] ^= true; + line.lfu[replace] = tag; + for (int i = 0; i < 64; i++) + line.data[replace][i] = bus->read((addr & 0xFFFFFFC0) + i); + return *(uint32_t*)&line.data[replace][off]; + } + } + else + { + return *(uint32_t*)&line.data[1][off]; } } - - // Welp, cache miss - printf("[emu/CPU]: Cache miss for address 0x%08x\n", addr); - - int replace = 0; - if (!line.tag[0].valid && line.tag[1].valid) - replace = 0; - else if (line.tag[0].valid && !line.tag[1].valid) - replace = 1; else - replace = line.tag[0].lrf ^ line.tag[1].lrf; - - for (int i = 0; i < 64; i++) - line.data[replace][i] = bus->read((page << 14) + i); - return *(uint32_t*)&line.data[replace][offset]; + { + return *(uint32_t*)&line.data[0][off]; + } } void Write32(uint32_t addr, uint32_t data) @@ -74,43 +104,7 @@ private: return; } - uint32_t page = (addr >> 13); - uint32_t index = (addr >> 5) & 0b1111111; - uint32_t offset = addr & 0x3F; - - Cache& line = dcache[index]; - - for (int way = 0; way < 2; way++) - { - if (line.tag[way].page == page && line.tag[way].valid) - { - *((uint32_t*)&line.data[way][offset]) = data; - line.tag[way].dirty = true; // We mark as dirty to flush back to main memory on eviction - } - } - - printf("[emu/CPU]: Cache miss for address 0x%08x\n", addr); - - int replace = 0; - if (!line.tag[0].valid && line.tag[1].valid) - replace = 0; - else if (line.tag[0].valid && !line.tag[1].valid) - replace = 1; - else - replace = line.tag[0].lrf ^ line.tag[1].lrf; - - if (line.tag[replace].dirty) - { - for (int i = 0; i < 64; i++) - bus->write((page << 13) + i, line.data[replace][i]); - } - for (int i = 0; i < 64; i++) - { - line.data[replace][i] = bus->read((page << 13) + i); - } - *(uint32_t*)&line.data[replace][offset] = data; - line.tag[replace].dirty = true; } void Write64(uint32_t addr, uint64_t data) @@ -120,66 +114,43 @@ private: bus->write(addr, data); return; } - - uint32_t page = (addr >> 13); - uint32_t index = (addr >> 5) & 0b1111111; - uint32_t offset = addr & 0x3F; - - Cache& line = dcache[index]; - - for (int way = 0; way < 2; way++) - { - if (line.tag[way].page == page && line.tag[way].valid) - { - *((uint64_t*)&line.data[way][offset]) = data; - line.tag[way].dirty = true; // We mark as dirty to flush back to main memory on eviction - } - } - - printf("[emu/CPU]: Cache miss for address 0x%08x\n", addr); - - int replace = 0; - if (!line.tag[0].valid && line.tag[1].valid) - replace = 0; - else if (line.tag[0].valid && !line.tag[1].valid) - replace = 1; - else - replace = line.tag[0].lrf ^ line.tag[1].lrf; - - if (line.tag[replace].dirty) - { - for (int i = 0; i < 64; i++) - bus->write((page << 13) + i, line.data[replace][i]); - } - - for (int i = 0; i < 64; i++) - { - line.data[replace][i] = bus->read((page << 13) + i); - } - *(uint64_t*)&line.data[replace][offset] = data; - line.tag[replace].dirty = true; } - void j(Opcode i); // 0x03 + void j(Opcode i); // 0x02 + void jal(Opcode i); // 0x03 void beq(Opcode i); // 0x04 void bne(Opcode i); // 0x05 void addiu(Opcode i); // 0x09 void slti(Opcode i); // 0x0A + void sltiu(Opcode i); // 0x0B void andi(Opcode i); // 0x0C void ori(Opcode i); // 0x0D void lui(Opcode i); // 0x0F void mfc0(Opcode i); // 0x10 0x00 void mtc0(Opcode i); // 0x10 0x04 + void beql(Opcode i); // 0x14 + void bnel(Opcode i); // 0x15 + void lb(Opcode i); // 0x20 + void lw(Opcode i); // 0x23 + void lbu(Opcode i); // 0x24 + void sb(Opcode i); // 0x28 void sw(Opcode i); // 0x2B + void ld(Opcode i); // 0x37 + void swc1(Opcode i); // 0x39 void sd(Opcode i); // 0x3f void sll(Opcode i); // 0x00 + void sra(Opcode i); // 0x03 void jr(Opcode i); // 0x08 void jalr(Opcode i); // 0x09 + void mflo(Opcode i); // 0x12 void mult(Opcode i); // 0x18 + void divu(Opcode i); // 0x1B void op_or(Opcode i); // 0x25 void daddu(Opcode i); // 0x2d + void bltz(Opcode i); // 0x00 + void AdvancePC() { pc = next_pc; diff --git a/src/emu/cpu/instructions.cpp b/src/emu/cpu/instructions.cpp index 8176b06..f2c3a1d 100644 --- a/src/emu/cpu/instructions.cpp +++ b/src/emu/cpu/instructions.cpp @@ -4,6 +4,7 @@ #include #include + void EmotionEngine::j(Opcode i) { uint32_t target = (i.j_type.target << 2); @@ -11,6 +12,14 @@ void EmotionEngine::j(Opcode i) printf("j 0x%08x\n", next_pc); } +void EmotionEngine::jal(Opcode i) +{ + uint32_t target = (i.j_type.target << 2); + regs[31].u64[0] = next_pc; + next_pc = (pc & 0xF0000000) | target; + printf("jal 0x%08x\n", next_pc); +} + void EmotionEngine::beq(Opcode i) { int32_t imm = (int32_t)((uint32_t)i.i_type.imm << 2); @@ -41,38 +50,44 @@ void EmotionEngine::addiu(Opcode i) int rs = i.i_type.rs; int32_t imm = (int16_t)(uint32_t)i.i_type.imm; - regs[rt].u32[0] = (uint32_t)((int32_t)regs[rt].u32[0] + imm); + regs[rt].u64[0] = (uint32_t)((int32_t)regs[rt].u32[0] + imm); printf("addiu %s, %s, %d\n", Reg(rt), Reg(rs), imm); } void EmotionEngine::slti(Opcode i) { - regs[i.i_type.rt].u32[0] = (int32_t)regs[i.i_type.rs].u32[0] < (int32_t)(uint32_t)i.i_type.imm; + regs[i.i_type.rt].u64[0] = (int32_t)regs[i.i_type.rs].u32[0] < (int32_t)(uint32_t)i.i_type.imm; printf("slti %s, %s, 0x%04x\n", Reg(i.i_type.rt), Reg(i.i_type.rs), i.i_type.imm); } +void EmotionEngine::sltiu(Opcode i) +{ + regs[i.i_type.rt].u64[0] = regs[i.i_type.rs].u32[0] < (uint32_t)i.i_type.imm; + printf("sltiu %s, %s, 0x%04x\n", Reg(i.i_type.rt), Reg(i.i_type.rs), i.i_type.imm); +} + void EmotionEngine::andi(Opcode i) { - regs[i.i_type.rt].u32[0] = regs[i.i_type.rs].u32[0] & (uint32_t)(int32_t)i.i_type.imm; + regs[i.i_type.rt].u64[0] = regs[i.i_type.rs].u32[0] & (uint32_t)(int32_t)i.i_type.imm; printf("andi %s, %s, 0x%08x\n", Reg(i.i_type.rt), Reg(i.i_type.rs), i.i_type.imm); } void EmotionEngine::ori(Opcode i) { - regs[i.i_type.rt].u32[0] = regs[i.i_type.rs].u32[0] | (uint32_t)(int32_t)i.i_type.imm; + regs[i.i_type.rt].u64[0] = regs[i.i_type.rs].u32[0] | (uint32_t)(int32_t)i.i_type.imm; printf("ori %s, %s, 0x%08x\n", Reg(i.i_type.rt), Reg(i.i_type.rs), i.i_type.imm); } void EmotionEngine::lui(Opcode i) { - regs[i.i_type.rt].u32[0] = (int32_t)((uint32_t)i.i_type.imm << 16); + regs[i.i_type.rt].u64[0] = ((uint32_t)i.i_type.imm << 16); printf("lui %s, 0x%08x\n", Reg(i.i_type.rt), regs[i.i_type.rt].u32[0]); } void EmotionEngine::mfc0(Opcode i) { - regs[i.r_type.rt].u32[0] = cop0_regs[i.r_type.rd]; + regs[i.r_type.rt].u64[0] = cop0_regs[i.r_type.rd]; printf("mfc0 %s, r%d\n", Reg(i.r_type.rt), i.r_type.rd); } @@ -82,6 +97,89 @@ void EmotionEngine::mtc0(Opcode i) printf("mtc0 %s, r%d\n", Reg(i.r_type.rt), i.r_type.rd); } +void EmotionEngine::beql(Opcode i) +{ + int32_t imm = (int32_t)((uint32_t)i.i_type.imm << 2); + + printf("beql %s, %s, 0x%08x\n", Reg(i.i_type.rs), Reg(i.i_type.rt), next_pc + imm); + + if ((int32_t)regs[i.i_type.rs].u32[0] == (int32_t)regs[i.i_type.rt].u32[0]) + { + next_pc = pc + imm; + } + else + AdvancePC(); // Skip delay slot +} + +void EmotionEngine::bnel(Opcode i) +{ + int32_t imm = (int32_t)((uint32_t)i.i_type.imm << 2); + + bool taken = false; + + if ((int32_t)regs[i.i_type.rs].u32[0] != (int32_t)regs[i.i_type.rt].u32[0]) + { + next_pc = pc + imm; + taken = true; + } + else + AdvancePC(); // Skip delay slot + + printf("bnel %s, %s, 0x%08x (%s)\n", Reg(i.i_type.rs), Reg(i.i_type.rt), pc + imm, taken ? "taken" : ""); +} + +void EmotionEngine::lb(Opcode i) +{ + int base = i.i_type.rs; + int rt = i.i_type.rt; + int16_t off = (int16_t)i.i_type.imm; + + uint32_t addr = regs[base].u32[0] + off; + + regs[rt].u64[0] = (int64_t)((uint8_t)(Read32(addr) & 0xFF)); + + printf("lb %s, %d(%s)\n", Reg(rt), off, Reg(base)); +} + +void EmotionEngine::lw(Opcode i) +{ + int base = i.i_type.rs; + int rt = i.i_type.rt; + int16_t off = (int16_t)i.i_type.imm; + + uint32_t addr = regs[base].u32[0] + off; + + regs[rt].u32[0] = bus->read(addr); + + printf("lw %s, %d(%s)\n", Reg(rt), off, Reg(base)); +} + +void EmotionEngine::lbu(Opcode i) +{ + int base = i.i_type.rs; + int rt = i.i_type.rt; + int16_t off = (int16_t)i.i_type.imm; + + uint32_t addr = regs[base].u32[0] + off; + + regs[rt].u64[0] = Read32(addr) & 0xFF; + + printf("lbu %s, %d(%s)\n", Reg(rt), off, Reg(base)); +} + +void EmotionEngine::sb(Opcode i) +{ + int base = i.i_type.rs; + int rt = i.i_type.rt; + int16_t off = (int16_t)i.i_type.imm; + + uint32_t addr = regs[base].u32[0] + off; + + bus->write(addr, regs[rt].u32[0] & 0xFF); + + printf("sb %s, %d(%s)\n", Reg(rt), off, Reg(base)); +} + void EmotionEngine::sw(Opcode i) { int base = i.i_type.rs; @@ -95,6 +193,32 @@ void EmotionEngine::sw(Opcode i) printf("sw %s, %d(%s)\n", Reg(rt), off, Reg(base)); } +void EmotionEngine::ld(Opcode i) +{ + int base = i.i_type.rs; + int rt = i.i_type.rt; + int16_t off = (int16_t)i.i_type.imm; + + uint32_t addr = regs[base].u32[0] + off; + + regs[rt].u64[0] = bus->read(addr); + + printf("ld %s, %d(%s)\n", Reg(rt), off, Reg(base)); +} + +void EmotionEngine::swc1(Opcode i) +{ + int base = i.i_type.rs; + int rt = i.i_type.rt; + int16_t off = (int16_t)i.i_type.imm; + + uint32_t addr = regs[base].u32[0] + off; + + Write32(addr, cop1.i[rt]); + + printf("swc1 f%d, %d(%s)\n", rt, off, Reg(base)); +} + void EmotionEngine::sd(Opcode i) { int base = i.i_type.rs; @@ -122,22 +246,43 @@ void EmotionEngine::sll(Opcode i) int rt = i.r_type.rt; int sa = i.r_type.sa; - regs[rd].u32[0] = regs[rt].u32[0] << sa; + regs[rd].u64[0] = regs[rt].u32[0] << sa; printf("sll %s, %s, %d\n", Reg(rd), Reg(rt), sa); } +void EmotionEngine::sra(Opcode i) +{ + int rd = i.r_type.rd; + int rt = i.r_type.rt; + int sa = i.r_type.sa; + + int32_t reg = (int32_t)regs[rt].u32[0]; + regs[rd].u64[0] = reg >> sa; + + printf("sra %s, %s, %d\n", Reg(rd), Reg(rt), sa); +} + void EmotionEngine::jalr(Opcode i) { int rs = i.r_type.rs; int rd = i.r_type.rd; - regs[rd].u32[0] = next_pc; + regs[rd].u64[0] = next_pc; next_pc = regs[rs].u32[0]; printf("jalr %s, %s\n", Reg(rs), Reg(rd)); } +void EmotionEngine::mflo(Opcode i) +{ + int rd = i.r_type.rd; + + regs[rd].u64[0] = lo; + + printf("mflo %s\n", Reg(rd)); +} + void EmotionEngine::mult(Opcode i) { int rs = i.r_type.rs; @@ -148,10 +293,29 @@ void EmotionEngine::mult(Opcode i) int64_t reg2 = (int64_t)regs[rt].u32[0]; int64_t result = reg1 * reg2; - regs[rd].u32[0] = lo = (int32_t)(result & 0xFFFFFFFF); + regs[rd].u64[0] = lo = (int32_t)(result & 0xFFFFFFFF); hi = (int32_t)(result >> 32); - printf("mult %s, %s, %s\n", Reg(rd), Reg(rs), Reg(rt)); + printf("mult %s, %s, %s (0x%08x, 0x%08x)\n", Reg(rd), Reg(rs), Reg(rt), hi, lo); +} + +void EmotionEngine::divu(Opcode i) +{ + int rs = i.r_type.rs; + int rt = i.r_type.rt; + + if (regs[rt].u32[0] == 0) + { + hi = (int32_t)regs[rs].u32[0]; + lo = (int32_t)0xffffffff; + } + else + { + hi = (int32_t)(regs[rs].u32[0] % regs[rt].u32[0]); + lo = (int32_t)(regs[rs].u32[0] / regs[rt].u32[0]); + } + + printf("divu %s (0x%08x), %s (0x%08x) (0x%08x, 0x%08x)\n", Reg(rs), regs[rs].u32[0], Reg(rt), regs[rt].u32[0], hi, lo); } void EmotionEngine::op_or(Opcode i) @@ -160,7 +324,7 @@ void EmotionEngine::op_or(Opcode i) int rs = i.r_type.rs; int rt = i.r_type.rt; - regs[rd].u32[0] = regs[rt].u32[0] | regs[rs].u32[0]; + regs[rd].u64[0] = regs[rt].u32[0] | regs[rs].u32[0]; printf("or %s, %s, %s\n", Reg(rd), Reg(rt), Reg(rs)); } @@ -177,4 +341,19 @@ void EmotionEngine::daddu(Opcode i) regs[rd].u64[0] = reg1 + reg2; printf("daddu %s, %s, %s\n", Reg(rd), Reg(rs), Reg(rt)); +} + +void EmotionEngine::bltz(Opcode i) +{ + int rs = i.i_type.rs; + + int32_t val = (int32_t)regs[rs].u32[0]; + int32_t off = (int32_t)(i.i_type.imm << 2); + + printf("bltz %s, 0x%08x\n", Reg(rs), pc + off); + + if (val < 0) + { + next_pc = pc + off; + } } \ No newline at end of file