diff --git a/CMakeLists.txt b/CMakeLists.txt index f50441c..82e90e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ add_subdirectory(external/capstone) add_executable(weee main.cpp core/mem.cpp core/loaders/elf.cpp core/loaders/dol.cpp core/broadway.cpp - core/broadway/instructions.cpp) + core/broadway/instructions.cpp + core/broadway/mmio/vi.cpp) target_link_libraries(weee PUBLIC capstone) target_include_directories(weee PUBLIC core) \ No newline at end of file diff --git a/core/broadway.cpp b/core/broadway.cpp index 51ac944..028d3d2 100644 --- a/core/broadway.cpp +++ b/core/broadway.cpp @@ -22,7 +22,7 @@ void broadway::run(mem &mem) { } ircolib::u32 broadway::fetch(mem &mem) { - ircolib::u32 val = mem.read(pc); + ircolib::u32 val = mem.read(pc); pc += 4; return val; @@ -30,12 +30,27 @@ ircolib::u32 broadway::fetch(mem &mem) { void broadway::execute(ircolib::u32 instr, mem &mem) { switch (utils::primary(instr)) { + case 14: // addi rd, ra, simm + addi(instr); + break; case 15: // addis rd, ra, simm addis(instr); break; + case 18: // bx target + bx(instr); + break; case 24: // ori ra, rs, uimm ori(instr); break; + case 31: // mfspr rd, spr + move_spr(instr); + break; + case 37: // stwu rs, d(ra) + stwu(instr, mem); + break; + case 44: // stwu rs, d(ra) + sth(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 4cc228a..9dab31b 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; + ircolib::u32 pc = 0, lr = 0, ctr = 0; std::array gpr{}; // ircolib::u32 const_gpr_lookup{}; csh capstone; @@ -25,6 +25,11 @@ struct broadway { // instructions void addis(ircolib::u32); + void addi(ircolib::u32); void ori(ircolib::u32); + void bx(ircolib::u32); + void move_spr(ircolib::u32); + void stwu(ircolib::u32, mem &); + void sth(ircolib::u32, mem &); }; } // namespace weee::core diff --git a/core/broadway/instructions.cpp b/core/broadway/instructions.cpp index 416d800..159b043 100644 --- a/core/broadway/instructions.cpp +++ b/core/broadway/instructions.cpp @@ -1,6 +1,17 @@ #include +#include "ircolib/log.hpp" +#include namespace weee::core { +void broadway::addi(ircolib::u32 instr) { + if (utils::RA(instr) == 0) { // lis + gpr[utils::RD(instr)] = ircolib::s32(utils::SIMM(instr)); + return; + } + + gpr[utils::RD(instr)] = gpr[utils::RA(instr)] + (ircolib::s32(utils::SIMM(instr))); +} + void broadway::addis(ircolib::u32 instr) { if (utils::RA(instr) == 0) { // lis gpr[utils::RD(instr)] = ircolib::s32(utils::SIMM(instr)) << 16; @@ -11,4 +22,76 @@ void broadway::addis(ircolib::u32 instr) { } void broadway::ori(ircolib::u32 instr) { gpr[utils::RA(instr)] = gpr[utils::RS(instr)] | utils::UIMM(instr); } + +void broadway::bx(ircolib::u32 instr) { + const bool link = instr & 1; + const bool absolute = instr & 2; + + ircolib::s32 LI = (ircolib::s32(instr << 6) >> 6) & 0x03FFFFFC; + if (!absolute) + LI += pc - 4; + + if (link) + lr = pc; + + pc = LI; +} + +void broadway::move_spr(ircolib::u32 instr) { + const size_t spr_field = (instr >> 12) & 0x3FF; + const size_t direction = (instr >> 1) & 0x3FF; + + if (direction == 339) { + switch (spr_field) { + case 0x80: + gpr[utils::RD(instr)] = lr; + break; + case 0x90: + gpr[utils::RD(instr)] = ctr; + break; + default: + ircolib::panic("broadway::mfspr with unimplemented spr field of value {}", spr_field); + } + + return; + } + + if (direction == 467) { + switch (spr_field) { + case 0x80: + lr = gpr[utils::RS(instr)]; + break; + case 0x90: + ctr = gpr[utils::RS(instr)]; + break; + default: + ircolib::panic("broadway::mtspr with unimplemented spr field of value {}", spr_field); + } + + return; + } + + ircolib::panic("broadway::move_spr with unknown sub op {}", direction); +} + +void broadway::stwu(ircolib::u32 instr, mem &mem) { + if (utils::RA(instr) == 0) + ircolib::panic("broadway::stwu with ra == 0"); + + const ircolib::s32 d = utils::SIMM(instr); + const ircolib::u32 EA = gpr[utils::RA(instr)] + d; + mem.write(EA, gpr[utils::RS(instr)]); + gpr[utils::RA(instr)] = EA; +} + +void broadway::sth(ircolib::u32 instr, mem &mem) { + ircolib::u32 b = gpr[utils::RA(instr)]; + if (utils::RA(instr) == 0) + b = 0; + + const ircolib::s32 d = utils::SIMM(instr); + const ircolib::u32 EA = b + d; + + mem.write(EA, ircolib::u16(gpr[utils::RS(instr)])); +} } // namespace weee::core diff --git a/core/broadway/mmio/vi.cpp b/core/broadway/mmio/vi.cpp new file mode 100644 index 0000000..75e92e4 --- /dev/null +++ b/core/broadway/mmio/vi.cpp @@ -0,0 +1,15 @@ +#include +#include "ircolib/log.hpp" + +namespace weee::core { +void video_interface::write(ircolib::u32 addr, ircolib::u16 value) { + addr -= 0x0c002000; + switch (addr) { + case 2: + dcr.raw = value; + break; + default: + ircolib::panic("video_interface::write to unimplemented addr 0x{:04X} with value 0x{:04X}", addr, value); + } +} +} // namespace weee::core diff --git a/core/broadway/mmio/vi.hpp b/core/broadway/mmio/vi.hpp new file mode 100644 index 0000000..49e11f6 --- /dev/null +++ b/core/broadway/mmio/vi.hpp @@ -0,0 +1,26 @@ +#pragma once +#include + +namespace weee::core { +union DCR { + struct { + unsigned e : 1; + unsigned r : 1; + unsigned i : 1; + unsigned d : 1; + unsigned le0 : 2; + unsigned le1 : 2; + unsigned fmt : 2; + unsigned : 6; + }; + + ircolib::u16 raw; +}; + +struct video_interface { + void write(ircolib::u32, ircolib::u16); + + private: + DCR dcr{}; +}; +} // namespace weee::core diff --git a/core/mem.cpp b/core/mem.cpp index 2d67693..61253af 100644 --- a/core/mem.cpp +++ b/core/mem.cpp @@ -35,6 +35,7 @@ void mem::set(const ircolib::u8 val, const ircolib::u32 size, const ircolib::u32 memset(&mem1[offset], val, size); } +template <> ircolib::u32 mem::read(ircolib::u32 addr) { addr &= 0x0FFFFFFF; if (addr > 0x017FFFFF) @@ -43,6 +44,15 @@ ircolib::u32 mem::read(ircolib::u32 addr) { return ircolib::read_access(mem1, addr); } +template <> +ircolib::u16 mem::read(ircolib::u32 addr) { + addr &= 0x0FFFFFFF; + if (addr > 0x017FFFFF) + ircolib::panic("mem::read unimplemented outside mem1 (0x{:08X})", addr); + + return ircolib::read_access(mem1, addr); +} + void mem::write(ircolib::u32 addr, ircolib::u32 value) { addr &= 0x0FFFFFFF; if (addr > 0x017FFFFF) @@ -50,4 +60,17 @@ void mem::write(ircolib::u32 addr, ircolib::u32 value) { ircolib::write_access(mem1, addr, value); } + +void mem::write(ircolib::u32 addr, ircolib::u16 value) { + addr &= 0x0FFFFFFF; + if (addr > 0x017FFFFF && !ircolib::is_inside_range(addr, 0x0C002000, 0x0C0020FF)) + ircolib::panic("mem::write unimplemented outside mem1 and vi (0x{:08X} = 0x{:08X})", addr, value); + + if (addr <= 0x017FFFFF) { + ircolib::write_access(mem1, addr, value); + return; + } + + vi.write(addr, value); +} } // namespace weee::core diff --git a/core/mem.hpp b/core/mem.hpp index 74c191e..4371348 100644 --- a/core/mem.hpp +++ b/core/mem.hpp @@ -1,18 +1,22 @@ #pragma once #include #include +#include "broadway/mmio/vi.hpp" namespace weee::core { struct mem { mem(); - ircolib::u32 read(ircolib::u32); + template + T read(ircolib::u32); void write(ircolib::u32, ircolib::u32); + void write(ircolib::u32, ircolib::u16); void copy(std::vector &src, const ircolib::u32 offset); void copy(ircolib::u8 *src, const ircolib::u32 size, const ircolib::u32 offset); void set(const ircolib::u8 val, const ircolib::u32 size, const ircolib::u32 offset); private: std::vector mem1; + video_interface vi; }; } // namespace weee::core