diff --git a/CMakeLists.txt b/CMakeLists.txt index 23931f0..312f969 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ if(WIN32) add_compile_definitions(_CRT_SECURE_NO_WARNINGS) add_compile_definitions(NOMINMAX) endif() +add_compile_options(-fno-operator-names) set(CAPSTONE_ARCHITECTURE_DEFAULT OFF) set(CAPSTONE_PPC_SUPPORT ON) diff --git a/core/broadway.cpp b/core/broadway.cpp index 6d198aa..e6c4d6d 100644 --- a/core/broadway.cpp +++ b/core/broadway.cpp @@ -17,27 +17,66 @@ void broadway::set_pc(ircolib::u32 value) { pc = value; } void broadway::run(mem &mem) { for (int i = 0; i < 12150000; i++) { + // std::println("pc: 0x{:08X}", pc); execute(fetch(mem), mem); } } ircolib::u32 broadway::fetch(mem &mem) { - ircolib::u32 val = mem.read32(pc); + ircolib::u32 val = mem.read32(pc) + .and_then([&](ircolib::u32 val) { return std::expected(val); }) + .or_else([&](std::string e) { + ircolib::panic("broadway read failed. Reason {} (pc: 0x{:08X})", e, pc); + return std::expected(); + }) + .value(); pc += 4; return val; } +void broadway::decode_special3(ircolib::u32 instr, mem &mem) { + auto secondary = utils::secondary(instr); + switch (secondary) { + case 38: + mtfsb1(instr); + break; + case 711: + mtfsf(instr); + break; + default: + ircolib::panic("broadway unknown special3 {} (0x{:04X}) (pc 0x{:08X})", secondary, secondary, pc - 4); + } +} + void broadway::decode_special2(ircolib::u32 instr, mem &mem) { auto secondary = utils::secondary(instr); switch (secondary) { + case 0x000: + cmp(instr); + break; + case 0x020: + cmpl(instr); + break; + case 0x028: + subf(instr); + break; + case 0x053: + mfmsr(instr); + break; case 0x0D2: ircolib::warn("broadway TODO mtsr (pc 0x{:08X})", pc - 4); break; + case 0x1BC: + or (instr); + break; case 0x153: case 0x1D3: mftspr(ircolib::is_bit_set(secondary), instr); break; + case 0x1DC: + nand(instr); + break; case 0x10A: add(instr); break; @@ -66,6 +105,9 @@ void broadway::decode_special1(ircolib::u32 instr, mem &mem) { void broadway::execute(ircolib::u32 instr, mem &mem) { switch (utils::primary(instr)) { + case 10: + cmpli(instr); + break; case 11: cmpi(instr); break; @@ -111,12 +153,18 @@ void broadway::execute(ircolib::u32 instr, mem &mem) { case 37: stwu(instr, mem); break; + case 39: + stbu(instr, mem); + break; case 44: sth(instr, mem); break; case 50: lfd(instr, mem); break; + case 63: + decode_special3(instr, mem); + break; default: std::println("broadway::execute unimplemented instruction 0x{:08X} / 0b{:032b}", instr, instr); std::println("disassembly:"); diff --git a/core/broadway.hpp b/core/broadway.hpp index c700a9c..3fe2e41 100644 --- a/core/broadway.hpp +++ b/core/broadway.hpp @@ -17,7 +17,7 @@ struct broadway { void execute(ircolib::u32, mem &); bool disasm_available = true; - ircolib::u32 pc = 0, lr = 0, ctr = 0, cr = 0, srr0; + ircolib::u32 pc = 0, lr = 0, ctr = 0, cr = 0, srr0 = 0, fpscr = 0, msr = 0; union { struct { unsigned bytecount : 7; @@ -43,7 +43,13 @@ struct broadway { std::array ibat, dbat; std::array gpr{}; - std::array fpr{}; + + struct Fgr { + ircolib::u64 ps0, ps1; + }; + + std::array fgrs{}; + // ircolib::u32 const_gpr_lookup{}; csh capstone; // Xbyak::CodeGenerator code; @@ -51,6 +57,7 @@ struct broadway { // instructions void decode_special1(ircolib::u32, mem &); void decode_special2(ircolib::u32, mem &); + void decode_special3(ircolib::u32, mem &); void branch(bool, bool, ircolib::u32); bool test_cond_and_ctr(ircolib::u32); @@ -66,14 +73,25 @@ struct broadway { void mftspr(bool, ircolib::u32); void stw(ircolib::u32, mem &); void stwu(ircolib::u32, mem &); + void stbu(ircolib::u32, mem &); void sth(ircolib::u32, mem &); void lwz(ircolib::u32, mem &); void lfd(ircolib::u32, mem &); void bclrx(ircolib::u32); void cmpi(ircolib::u32); + void cmpli(ircolib::u32); + void cmp(ircolib::u32); + void cmpl(ircolib::u32); void rlwinm(ircolib::u32); void lwzu(ircolib::u32, mem &); void rfi(ircolib::u32); + void mtfsf(ircolib::u32); + void mtfsb1(ircolib::u32); + void mtfsb0(ircolib::u32); + void mfmsr(ircolib::u32); + void nand(ircolib::u32); + void subf(ircolib::u32); + void or (ircolib::u32); inline void cr0_update(bool condition, ircolib::s32 result) { if (condition) { @@ -84,5 +102,12 @@ struct broadway { set_cr((b0 << 3) | (b1 << 2) | (b2 << 1) | (b3 << 0), 0); } } + + inline void cr1_update(bool condition) { + if (condition) { + const ircolib::u8 field = (fpscr >> 28) & 0xf; + set_cr(field, 1); + } + } }; } // namespace weee::core diff --git a/core/broadway/instructions.cpp b/core/broadway/instructions.cpp index ba4f3cd..1f7feda 100644 --- a/core/broadway/instructions.cpp +++ b/core/broadway/instructions.cpp @@ -1,5 +1,5 @@ -#include #include "ircolib/log.hpp" +#include #include namespace weee::core { @@ -49,6 +49,7 @@ void broadway::bx(ircolib::u32 instr) { bool broadway::test_cond_and_ctr(ircolib::u32 instr) { const ircolib::u8 bo = (instr >> 21) & 0x1f; const ircolib::u8 bi = (instr >> 16) & 0x1f; + const bool ctr_ok = ircolib::is_bit_set(bo, 2) || ((--ctr == 0) == (ircolib::is_bit_set(bo, 1))); const bool cond_ok = ircolib::is_bit_set(bo, 4) || (get_cr_bit(0, bi) == ircolib::is_bit_set(bo, 3)); @@ -132,7 +133,8 @@ void broadway::mftspr(bool dir, ircolib::u32 instr) { move_to_or_from_spr(dbat[BAT_UPPER_OFFSET + 3]); break; default: - ircolib::warn("broadway::m{}spr with unimplemented spr field of value 0b{:010b} (0x{:04X}) (pc 0x{:08X})", + ircolib::warn("broadway::m{}spr with unimplemented spr field of value " + "0b{:010b} (0x{:04X}) (pc 0x{:08X})", dir ? 't' : 'f', spr_field, spr_field, pc - 4); } } @@ -142,7 +144,26 @@ void broadway::stwu(ircolib::u32 instr, mem &mem) { ircolib::panic("broadway::stwu with ra == 0"); const ircolib::u32 EA = ircolib::s32(gpr[utils::RA(instr)]) + utils::SIMM(instr); - mem.write32(EA, gpr[utils::RS(instr)]); + auto _ = mem.write32(EA, gpr[utils::RS(instr)]) + .and_then([&] { return std::expected(); }) + .or_else([&](std::string e) { + ircolib::panic("broadway write failed. Reason: {} (pc: 0x{:08X})", e, pc - 4); + return std::expected(); + }); + gpr[utils::RA(instr)] = EA; +} + +void broadway::stbu(ircolib::u32 instr, mem &mem) { + if (utils::RA(instr) == 0) + ircolib::panic("broadway::stbu with ra == 0"); + + const ircolib::u32 EA = ircolib::s32(gpr[utils::RA(instr)]) + utils::SIMM(instr); + auto _ = mem.write8(EA, gpr[utils::RS(instr)]) + .and_then([&] { return std::expected(); }) + .or_else([&](std::string e) { + ircolib::panic("broadway write failed. Reason: {} (pc: 0x{:08X})", e, pc - 4); + return std::expected(); + }); gpr[utils::RA(instr)] = EA; } @@ -152,7 +173,12 @@ void broadway::stw(ircolib::u32 instr, mem &mem) { b = 0; const ircolib::u32 EA = b + utils::SIMM(instr); - mem.write32(EA, gpr[utils::RS(instr)]); + auto _ = mem.write32(EA, gpr[utils::RS(instr)]) + .and_then([&] { return std::expected(); }) + .or_else([&](std::string e) { + ircolib::panic("broadway write failed. Reason: {} (pc: 0x{:08X})", e, pc - 4); + return std::expected(); + }); } void broadway::sth(ircolib::u32 instr, mem &mem) { @@ -163,7 +189,12 @@ void broadway::sth(ircolib::u32 instr, mem &mem) { const ircolib::s32 d = utils::SIMM(instr); const ircolib::u32 EA = b + d; - mem.write16(EA, ircolib::u16(gpr[utils::RS(instr)])); + auto _ = mem.write16(EA, ircolib::u16(gpr[utils::RS(instr)])) + .and_then([&] { return std::expected(); }) + .or_else([&](std::string e) { + ircolib::panic("broadway write failed. Reason: {} (pc: 0x{:08X})", e, pc - 4); + return std::expected(); + }); } void broadway::lwz(ircolib::u32 instr, mem &mem) { @@ -172,7 +203,14 @@ void broadway::lwz(ircolib::u32 instr, mem &mem) { b = 0; ircolib::u32 ea = ircolib::s32(b) + utils::SIMM(instr); - gpr[utils::RD(instr)] = mem.read32(ea); + + gpr[utils::RD(instr)] = mem.read32(ea) + .and_then([&](ircolib::u32 val) { return std::expected(val); }) + .or_else([&](std::string e) { + ircolib::panic("broadway read failed. Reason {} (pc: 0x{:08X})", e, pc - 4); + return std::expected(); + }) + .value(); } void broadway::lfd(ircolib::u32 instr, mem &mem) { @@ -182,9 +220,15 @@ void broadway::lfd(ircolib::u32 instr, mem &mem) { ircolib::u32 ea = ircolib::s32(b) + utils::SIMM(instr); double val; - ircolib::u64 read = mem.read64(ea); + ircolib::u64 read = mem.read64(ea) + .and_then([&](ircolib::u64 val) { return std::expected(val); }) + .or_else([&](std::string e) { + ircolib::panic("broadway read failed. Reason {} (pc: 0x{:08X})", e, pc - 4); + return std::expected(); + }) + .value(); memcpy(&val, &read, 8); - fpr[utils::RD(instr)] = val; + fgrs[utils::RD(instr)].ps0 = val; } void broadway::bclrx(ircolib::u32 instr) { @@ -211,6 +255,51 @@ void broadway::cmpi(ircolib::u32 instr) { set_cr(result, utils::crfD(instr)); } +void broadway::cmpli(ircolib::u32 instr) { + const ircolib::u32 a = gpr[utils::RA(instr)]; + const ircolib::u32 uimm = utils::UIMM(instr); + ircolib::u8 c; + if (a < uimm) + c = 0b001; + else if (a > uimm) + c = 0b010; + else + c = 0b100; + + const ircolib::u8 result = (xer.so << 3) | c; + set_cr(result, utils::crfD(instr)); +} + +void broadway::cmp(ircolib::u32 instr) { + const ircolib::s32 a = gpr[utils::RA(instr)]; + const ircolib::s32 b = gpr[utils::RB(instr)]; + ircolib::u8 c; + if (a < b) + c = 0b001; + else if (a > b) + c = 0b010; + else + c = 0b100; + + const ircolib::u8 result = (xer.so << 3) | c; + set_cr(result, utils::crfD(instr)); +} + +void broadway::cmpl(ircolib::u32 instr) { + const ircolib::u32 a = gpr[utils::RA(instr)]; + const ircolib::u32 b = gpr[utils::RB(instr)]; + ircolib::u8 c; + if (a < b) + c = 0b001; + else if (a > b) + c = 0b010; + else + c = 0b100; + + const ircolib::u8 result = (xer.so << 3) | c; + set_cr(result, utils::crfD(instr)); +} + void broadway::rlwinm(ircolib::u32 instr) { const ircolib::u8 sh = (instr >> 11) & 0x1f; ircolib::u32 r = std::rotl(gpr[utils::RS(instr)], sh); @@ -226,7 +315,13 @@ void broadway::lwzu(ircolib::u32 instr, mem &mem) { ircolib::panic("broadway::lwzu with ra == 0"); const ircolib::u32 EA = ircolib::s32(gpr[utils::RA(instr)]) + utils::SIMM(instr); - gpr[utils::RD(instr)] = mem.read32(EA); + gpr[utils::RD(instr)] = mem.read32(EA) + .and_then([&](ircolib::u32 val) { return std::expected(val); }) + .or_else([&](std::string e) { + ircolib::panic("broadway read failed. Reason {} (pc: 0x{:08X})", e, pc - 4); + return std::expected(); + }) + .value(); gpr[utils::RA(instr)] = EA; } @@ -237,4 +332,50 @@ void broadway::andi(ircolib::u32 instr) { gpr[utils::RA(instr)] = result; cr0_update(true, result); } + +void broadway::mtfsf(ircolib::u32 instr) { + const ircolib::u8 fm = (instr >> 17) & 0xff; + ircolib::u8 mask = 0; + for (int i = 0; i < 8; i++) { + if (ircolib::is_bit_set(fm, i)) { + mask |= 0xf << (i * 4); + } + } + + ircolib::u32 frb = fgrs[utils::RB(instr)].ps0; + fpscr = (fpscr & ~mask) | (frb & mask); + cr1_update(instr & 1); +} + +void broadway::mtfsb0(ircolib::u32 instr) { + ircolib::u32 mask = ~(1 << utils::RD(instr)); + + fpscr = fpscr & mask; + cr1_update(instr & 1); +} + +void broadway::mtfsb1(ircolib::u32 instr) { + ircolib::u32 bit = 1 << utils::RD(instr); + ircolib::u32 mask = ~bit; + + fpscr = (fpscr & mask) | bit; + cr1_update(instr & 1); +} + +void broadway::mfmsr(ircolib::u32 instr) { gpr[utils::RS(instr)] = msr; } + +void broadway::nand(ircolib::u32 instr) { + gpr[utils::RA(instr)] = ~(gpr[utils::RS(instr)] & gpr[utils::RB(instr)]); + cr0_update(instr & 1, gpr[utils::RA(instr)]); +} + +void broadway::subf(ircolib::u32 instr) { + gpr[utils::RD(instr)] = ~(gpr[utils::RA(instr)] + gpr[utils::RB(instr)] + 1); + cr0_update(instr & 1, gpr[utils::RD(instr)]); +} + +void broadway:: or (ircolib::u32 instr) { + gpr[utils::RA(instr)] = gpr[utils::RS(instr)] & gpr[utils::RB(instr)]; + cr0_update(instr & 1, gpr[utils::RA(instr)]); +} } // namespace weee::core diff --git a/core/mem.cpp b/core/mem.cpp index 8e38fc1..6adb030 100644 --- a/core/mem.cpp +++ b/core/mem.cpp @@ -7,6 +7,9 @@ namespace weee::core { mem::mem() : vi(*this) { mem1.resize(24_mib); std::fill(mem1.begin(), mem1.end(), 0); + + register_read8_handler(0x00000000, 0x017fffff, [&](ircolib::u32 addr) { return mem1[addr]; }); + register_read16_handler(0x00000000, 0x017fffff, [&](ircolib::u32 addr) { return ircolib::read_access(mem1, addr); }); @@ -16,6 +19,8 @@ mem::mem() : vi(*this) { register_read64_handler(0x00000000, 0x017fffff, [&](ircolib::u32 addr) { return ircolib::read_access(mem1, addr); }); + register_write8_handler(0x00000000, 0x017fffff, [&](ircolib::u32 addr, ircolib::u8 value) { mem1[addr] = value; }); + register_write16_handler(0x00000000, 0x017fffff, [&](ircolib::u32 addr, ircolib::u16 value) { ircolib::write_access(mem1, addr, value); }); @@ -52,7 +57,7 @@ void mem::set(const ircolib::u8 val, const ircolib::u32 size, const ircolib::u32 memset(&mem1[offset], val, size); } -ircolib::u8 mem::read8(ircolib::u32 addr) { +std::expected mem::read8(ircolib::u32 addr) { addr &= 0x0FFFFFFF; for (const auto &handler : read8_handlers) { if (ircolib::is_inside_range(addr, handler.start, handler.end)) { @@ -60,11 +65,10 @@ ircolib::u8 mem::read8(ircolib::u32 addr) { } } - ircolib::panic("mem::read8 unimplemented addr 0x{:08X}", addr); - return 0; + return std::unexpected(std::format("mem::read8 unimplemented addr 0x{:08X}", addr)); } -ircolib::u16 mem::read16(ircolib::u32 addr) { +std::expected mem::read16(ircolib::u32 addr) { addr &= 0x0FFFFFFF; for (const auto &handler : read16_handlers) { if (ircolib::is_inside_range(addr, handler.start, handler.end)) { @@ -72,11 +76,10 @@ ircolib::u16 mem::read16(ircolib::u32 addr) { } } - ircolib::panic("mem::read16 unimplemented addr 0x{:08X}", addr); - return 0; + return std::unexpected(std::format("mem::read16 unimplemented addr 0x{:08X}", addr)); } -ircolib::u32 mem::read32(ircolib::u32 addr) { +std::expected mem::read32(ircolib::u32 addr) { addr &= 0x0FFFFFFF; for (const auto &handler : read32_handlers) { if (ircolib::is_inside_range(addr, handler.start, handler.end)) { @@ -84,11 +87,10 @@ ircolib::u32 mem::read32(ircolib::u32 addr) { } } - ircolib::panic("mem::read32 unimplemented addr 0x{:08X}", addr); - return 0; + return std::unexpected(std::format("mem::read32 unimplemented addr 0x{:08X}", addr)); } -ircolib::u64 mem::read64(ircolib::u32 addr) { +std::expected mem::read64(ircolib::u32 addr) { addr &= 0x0FFFFFFF; for (const auto &handler : read64_handlers) { if (ircolib::is_inside_range(addr, handler.start, handler.end)) { @@ -96,51 +98,54 @@ ircolib::u64 mem::read64(ircolib::u32 addr) { } } - ircolib::panic("mem::read64 unimplemented addr 0x{:08X}", addr); - return 0; + return std::unexpected(std::format("mem::read64 unimplemented addr 0x{:08X}", addr)); } -void mem::write8(ircolib::u32 addr, ircolib::u8 value) { +std::expected mem::write8(ircolib::u32 addr, ircolib::u8 value) { addr &= 0x0FFFFFFF; for (const auto &handler : write8_handlers) { if (ircolib::is_inside_range(addr, handler.start, handler.end)) { - return handler.func(addr - handler.start, value); + handler.func(addr - handler.start, value); + return std::expected(); } } - ircolib::panic("mem::write8 unimplemented addr 0x{:08X} = 0x{:02X}", addr, value); + return std::unexpected(std::format("mem::write8 unimplemented addr 0x{:08X} = 0x{:02X}", addr, value)); } -void mem::write16(ircolib::u32 addr, ircolib::u16 value) { +std::expected mem::write16(ircolib::u32 addr, ircolib::u16 value) { addr &= 0x0FFFFFFF; for (const auto &handler : write16_handlers) { if (ircolib::is_inside_range(addr, handler.start, handler.end)) { - return handler.func(addr - handler.start, value); + handler.func(addr - handler.start, value); + return std::expected(); } } - ircolib::panic("mem::write16 unimplemented addr 0x{:08X} = 0x{:04X}", addr, value); + return std::unexpected(std::format("mem::write16 unimplemented addr 0x{:08X} = 0x{:04X}", addr, value)); } -void mem::write32(ircolib::u32 addr, ircolib::u32 value) { +std::expected mem::write32(ircolib::u32 addr, ircolib::u32 value) { addr &= 0x0FFFFFFF; for (const auto &handler : write32_handlers) { if (ircolib::is_inside_range(addr, handler.start, handler.end)) { - return handler.func(addr - handler.start, value); + handler.func(addr - handler.start, value); + return std::expected(); } } - ircolib::panic("mem::write32 unimplemented addr 0x{:08X} = 0x{:08X}", addr, value); + return std::unexpected(std::format("mem::write32 unimplemented addr 0x{:08X} = 0x{:08X}", addr, value)); } -void mem::write64(ircolib::u32 addr, ircolib::u64 value) { +std::expected mem::write64(ircolib::u32 addr, ircolib::u64 value) { addr &= 0x0FFFFFFF; for (const auto &handler : write64_handlers) { if (ircolib::is_inside_range(addr, handler.start, handler.end)) { - return handler.func(addr - handler.start, value); + handler.func(addr - handler.start, value); + return std::expected(); } } - ircolib::panic("mem::write64 unimplemented addr 0x{:08X} = 0x{:016X}", addr, value); + return std::unexpected(std::format("mem::write64 unimplemented addr 0x{:08X} = 0x{:016X}", addr, value)); } } // namespace weee::core diff --git a/core/mem.hpp b/core/mem.hpp index b0aae3f..a436069 100644 --- a/core/mem.hpp +++ b/core/mem.hpp @@ -1,8 +1,10 @@ #pragma once +#include #include #include #include #include +#include namespace weee::core { template @@ -53,14 +55,14 @@ struct mem { write64_handlers.push_back({std::move(func), start, end}); } - ircolib::u8 read8(ircolib::u32); - ircolib::u16 read16(ircolib::u32); - ircolib::u32 read32(ircolib::u32); - ircolib::u64 read64(ircolib::u32); - void write8(ircolib::u32, ircolib::u8); - void write16(ircolib::u32, ircolib::u16); - void write32(ircolib::u32, ircolib::u32); - void write64(ircolib::u32, ircolib::u64); + std::expected read8(ircolib::u32); + std::expected read16(ircolib::u32); + std::expected read32(ircolib::u32); + std::expected read64(ircolib::u32); + std::expected write8(ircolib::u32, ircolib::u8); + std::expected write16(ircolib::u32, ircolib::u16); + std::expected write32(ircolib::u32, ircolib::u32); + std::expected write64(ircolib::u32, ircolib::u64); void copy(std::vector &src, const ircolib::u32 offset); void copy(ircolib::u8 *src, const ircolib::u32 size, const ircolib::u32 offset);