diff --git a/apps/target/main.cc b/apps/target/main.cc index 1dcf994..128ac19 100644 --- a/apps/target/main.cc +++ b/apps/target/main.cc @@ -7,8 +7,7 @@ #include #include #include -#include -#include +#include #include // NOLINTBEGIN @@ -93,7 +92,7 @@ main(int argc, const char* argv[]) { matar::Cpu cpu(bus); while (true) { cpu.step(); - sleep(2); + std::this_thread::sleep_for(std::chrono::seconds(1)); } } catch (const std::exception& e) { std::cerr << "Exception: " << e.what() << std::endl; diff --git a/src/cpu/alu.hh b/include/cpu/alu.hh similarity index 100% rename from src/cpu/alu.hh rename to include/cpu/alu.hh diff --git a/src/cpu/arm/instruction.hh b/include/cpu/arm/instruction.hh similarity index 98% rename from src/cpu/arm/instruction.hh rename to include/cpu/arm/instruction.hh index 25e09d0..92724ae 100644 --- a/src/cpu/arm/instruction.hh +++ b/include/cpu/arm/instruction.hh @@ -6,7 +6,7 @@ #include namespace matar { -class CpuImpl; +class Cpu; namespace arm { @@ -217,7 +217,7 @@ struct Instruction { : condition(condition) , data(data){}; - void exec(CpuImpl& cpu); + void exec(Cpu& cpu); #ifdef DISASSEMBLER std::string disassemble(); diff --git a/include/cpu/arm/meson.build b/include/cpu/arm/meson.build new file mode 100644 index 0000000..90a7d9e --- /dev/null +++ b/include/cpu/arm/meson.build @@ -0,0 +1,3 @@ +headers += files( + 'instruction.hh' +) \ No newline at end of file diff --git a/include/cpu/cpu.hh b/include/cpu/cpu.hh index 6a9b260..ab0e4aa 100644 --- a/include/cpu/cpu.hh +++ b/include/cpu/cpu.hh @@ -1,21 +1,70 @@ +#pragma once + +#include "arm/instruction.hh" #include "bus.hh" +#include "cpu/psr.hh" +#include "thumb/instruction.hh" + +#include namespace matar { -class CpuImpl; - class Cpu { public: Cpu(const Bus& bus) noexcept; - Cpu(const Cpu&) = delete; - Cpu(Cpu&&) = delete; - Cpu& operator=(const Cpu&) = delete; - Cpu& operator=(Cpu&&) = delete; - - ~Cpu(); void step(); + void chg_mode(const Mode to); private: - std::unique_ptr impl; + friend void arm::Instruction::exec(Cpu& cpu); + friend void thumb::Instruction::exec(Cpu& cpu); + + static constexpr uint8_t GPR_COUNT = 16; + + static constexpr uint8_t GPR_FIQ_FIRST = 8; + static constexpr uint8_t GPR_SVC_FIRST = 13; + static constexpr uint8_t GPR_ABT_FIRST = 13; + static constexpr uint8_t GPR_IRQ_FIRST = 13; + static constexpr uint8_t GPR_UND_FIRST = 13; + static constexpr uint8_t GPR_SYS_USR_FIRST = 8; + + std::shared_ptr bus; + std::array gpr; // general purpose registers + + Psr cpsr; // current program status register + Psr spsr; // status program status register + + static constexpr uint8_t SP_INDEX = 13; + static_assert(SP_INDEX < GPR_COUNT); + uint32_t& sp = gpr[SP_INDEX]; + + static constexpr uint8_t LR_INDEX = 14; + static_assert(LR_INDEX < GPR_COUNT); + uint32_t& lr = gpr[LR_INDEX]; + + static constexpr uint8_t PC_INDEX = 15; + static_assert(PC_INDEX < GPR_COUNT); + uint32_t& pc = gpr[PC_INDEX]; + + struct { + std::array fiq; + std::array svc; + std::array abt; + std::array irq; + std::array und; + + // visible registers before the mode switch + std::array old; + } gpr_banked; // banked general purpose registers + + struct { + Psr fiq; + Psr svc; + Psr abt; + Psr irq; + Psr und; + } spsr_banked; // banked saved program status registers + + bool is_flushed; }; } diff --git a/include/cpu/meson.build b/include/cpu/meson.build index 4f9f321..4a8cbe7 100644 --- a/include/cpu/meson.build +++ b/include/cpu/meson.build @@ -1,3 +1,8 @@ headers += files( + 'alu.hh', 'cpu.hh', -) \ No newline at end of file + 'psr.hh' +) + +subdir('arm') +subdir('thumb') \ No newline at end of file diff --git a/src/cpu/psr.hh b/include/cpu/psr.hh similarity index 100% rename from src/cpu/psr.hh rename to include/cpu/psr.hh diff --git a/src/cpu/thumb/instruction.hh b/include/cpu/thumb/instruction.hh similarity index 99% rename from src/cpu/thumb/instruction.hh rename to include/cpu/thumb/instruction.hh index 6a165ee..cbc11f7 100644 --- a/src/cpu/thumb/instruction.hh +++ b/include/cpu/thumb/instruction.hh @@ -7,7 +7,7 @@ #include namespace matar { -class CpuImpl; +class Cpu; namespace thumb { @@ -279,7 +279,7 @@ struct Instruction { Instruction(InstructionData data) : data(data) {} - void exec(CpuImpl& cpu); + void exec(Cpu& cpu); #ifdef DISASSEMBLER std::string disassemble(uint32_t pc = 0); diff --git a/include/cpu/thumb/meson.build b/include/cpu/thumb/meson.build new file mode 100644 index 0000000..90a7d9e --- /dev/null +++ b/include/cpu/thumb/meson.build @@ -0,0 +1,3 @@ +headers += files( + 'instruction.hh' +) \ No newline at end of file diff --git a/include/memory.hh b/include/memory.hh index b2bf34f..11824a3 100644 --- a/include/memory.hh +++ b/include/memory.hh @@ -18,17 +18,14 @@ class Memory { void write(size_t address, uint8_t byte); private: -#define MEMORY_REGION(name, start, end) \ - static constexpr size_t name##_START = start; \ - static constexpr size_t name##_END = end; +#define MEMORY_REGION(name, start) static constexpr size_t name##_START = start; #define DECL_MEMORY(name, ident, start, end) \ - MEMORY_REGION(name, start, end) \ - std::array ident; + MEMORY_REGION(name, start) \ + std::array ident; - MEMORY_REGION(BIOS, 0x00000000, 0x00003FFF) + MEMORY_REGION(BIOS, 0x00000000) std::array bios; - static_assert(BIOS_END - BIOS_START + 1 == BIOS_SIZE); // board working RAM DECL_MEMORY(BOARD_WRAM, board_wram, 0x02000000, 0x0203FFFF) @@ -47,9 +44,9 @@ class Memory { #undef DECL_MEMORY - MEMORY_REGION(ROM_0, 0x08000000, 0x09FFFFFF) - MEMORY_REGION(ROM_1, 0x0A000000, 0x0BFFFFFF) - MEMORY_REGION(ROM_2, 0x0C000000, 0x0DFFFFFF) + MEMORY_REGION(ROM_0, 0x08000000) + MEMORY_REGION(ROM_1, 0x0A000000) + MEMORY_REGION(ROM_2, 0x0C000000) #undef MEMORY_REGION diff --git a/src/bus.cc b/src/bus.cc index 5349d42..69f7737 100644 --- a/src/bus.cc +++ b/src/bus.cc @@ -21,7 +21,7 @@ Bus::read_halfword(size_t address) { if (address & 0b01) glogger.warn("Reading a non aligned halfword address"); - return memory->read(address) | memory->read(address + 1) << 8; + return read_byte(address) | read_byte(address + 1) << 8; } void @@ -29,8 +29,8 @@ Bus::write_halfword(size_t address, uint16_t halfword) { if (address & 0b01) glogger.warn("Writing to a non aligned halfword address"); - memory->write(address, halfword & 0xFF); - memory->write(address + 1, halfword >> 8 & 0xFF); + write_byte(address, halfword & 0xFF); + write_byte(address + 1, halfword >> 8 & 0xFF); } uint32_t @@ -38,8 +38,8 @@ Bus::read_word(size_t address) { if (address & 0b11) glogger.warn("Reading a non aligned word address"); - return memory->read(address) | memory->read(address + 1) << 8 | - memory->read(address + 2) << 16 | memory->read(address + 3) << 24; + return read_byte(address) | read_byte(address + 1) << 8 | + read_byte(address + 2) << 16 | read_byte(address + 3) << 24; } void @@ -47,9 +47,9 @@ Bus::write_word(size_t address, uint32_t word) { if (address & 0b11) glogger.warn("Writing to a non aligned word address"); - memory->write(address, word & 0xFF); - memory->write(address + 1, word >> 8 & 0xFF); - memory->write(address + 2, word >> 16 & 0xFF); - memory->write(address + 3, word >> 24 & 0xFF); + write_byte(address, word & 0xFF); + write_byte(address + 1, word >> 8 & 0xFF); + write_byte(address + 2, word >> 16 & 0xFF); + write_byte(address + 3, word >> 24 & 0xFF); } } diff --git a/src/cpu/alu.cc b/src/cpu/alu.cc index 7a74e49..8e81347 100644 --- a/src/cpu/alu.cc +++ b/src/cpu/alu.cc @@ -1,4 +1,4 @@ -#include "alu.hh" +#include "cpu/alu.hh" #include "util/bits.hh" namespace matar { diff --git a/src/cpu/arm/disassembler.cc b/src/cpu/arm/disassembler.cc index 20884c0..13f3c46 100644 --- a/src/cpu/arm/disassembler.cc +++ b/src/cpu/arm/disassembler.cc @@ -1,4 +1,4 @@ -#include "instruction.hh" +#include "cpu/arm/instruction.hh" #include "util/bits.hh" namespace matar::arm { diff --git a/src/cpu/arm/exec.cc b/src/cpu/arm/exec.cc index 3b8f255..806455b 100644 --- a/src/cpu/arm/exec.cc +++ b/src/cpu/arm/exec.cc @@ -1,10 +1,10 @@ -#include "cpu/cpu-impl.hh" +#include "cpu/cpu.hh" #include "util/bits.hh" #include "util/log.hh" namespace matar::arm { void -Instruction::exec(CpuImpl& cpu) { +Instruction::exec(Cpu& cpu) { if (!cpu.cpsr.condition(condition)) { return; } diff --git a/src/cpu/arm/instruction.cc b/src/cpu/arm/instruction.cc index 80d51da..eb77310 100644 --- a/src/cpu/arm/instruction.cc +++ b/src/cpu/arm/instruction.cc @@ -1,4 +1,4 @@ -#include "instruction.hh" +#include "cpu/arm/instruction.hh" #include "util/bits.hh" #include diff --git a/src/cpu/cpu-impl.cc b/src/cpu/cpu-impl.cc deleted file mode 100644 index 0255714..0000000 --- a/src/cpu/cpu-impl.cc +++ /dev/null @@ -1,160 +0,0 @@ -#include "cpu-impl.hh" -#include "cpu/arm/instruction.hh" -#include "cpu/thumb/instruction.hh" -#include "util/bits.hh" -#include "util/log.hh" -#include -#include -#include - -namespace matar { -CpuImpl::CpuImpl(const Bus& bus) noexcept - : bus(std::make_shared(bus)) - , gpr({ 0 }) - , cpsr(0) - , spsr(0) - , gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } }) - , spsr_banked({ 0, 0, 0, 0, 0 }) - , is_flushed(false) { - cpsr.set_mode(Mode::Supervisor); - cpsr.set_irq_disabled(true); - cpsr.set_fiq_disabled(true); - cpsr.set_state(State::Arm); - glogger.info("CPU successfully initialised"); - - // PC always points to two instructions ahead - // PC - 2 is the instruction being executed - pc += 2 * arm::INSTRUCTION_SIZE; -} - -/* change modes */ -void -CpuImpl::chg_mode(const Mode to) { - Mode from = cpsr.mode(); - - if (from == to) - return; - -/* TODO: replace visible registers with view once I understand how to - * concatenate views */ -#define STORE_BANKED(mode, MODE) \ - std::copy(gpr.begin() + GPR_##MODE##_FIRST, \ - gpr.begin() + gpr.size() - 1, \ - gpr_banked.mode.begin()) - - switch (from) { - case Mode::Fiq: - STORE_BANKED(fiq, FIQ); - spsr_banked.fiq = spsr; - break; - - case Mode::Supervisor: - STORE_BANKED(svc, SVC); - spsr_banked.svc = spsr; - break; - - case Mode::Abort: - STORE_BANKED(abt, ABT); - spsr_banked.abt = spsr; - break; - - case Mode::Irq: - STORE_BANKED(irq, IRQ); - spsr_banked.irq = spsr; - break; - - case Mode::Undefined: - STORE_BANKED(und, UND); - spsr_banked.und = spsr; - break; - - case Mode::User: - case Mode::System: - STORE_BANKED(old, SYS_USR); - break; - } - -#define RESTORE_BANKED(mode, MODE) \ - std::copy(gpr_banked.mode.begin(), \ - gpr_banked.mode.end(), \ - gpr.begin() + GPR_##MODE##_FIRST) - - switch (to) { - case Mode::Fiq: - RESTORE_BANKED(fiq, FIQ); - spsr = spsr_banked.fiq; - break; - - case Mode::Supervisor: - RESTORE_BANKED(svc, SVC); - spsr = spsr_banked.svc; - break; - - case Mode::Abort: - RESTORE_BANKED(abt, ABT); - spsr = spsr_banked.abt; - break; - - case Mode::Irq: - RESTORE_BANKED(irq, IRQ); - spsr = spsr_banked.irq; - break; - - case Mode::Undefined: - RESTORE_BANKED(und, UND); - spsr = spsr_banked.und; - break; - - case Mode::User: - case Mode::System: - STORE_BANKED(old, SYS_USR); - break; - } - -#undef RESTORE_BANKED - - cpsr.set_mode(to); -} - -void -CpuImpl::step() { - // Current instruction is two instructions behind PC - uint32_t cur_pc = pc - 2 * arm::INSTRUCTION_SIZE; - - if (cpsr.state() == State::Arm) { - arm::Instruction instruction(bus->read_word(cur_pc)); - -#ifdef DISASSEMBLER - glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble()); -#endif - - instruction.exec(*this); - - } else { - thumb::Instruction instruction(bus->read_halfword(cur_pc)); - -#ifdef DISASSEMBLER - glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble(cur_pc)); -#endif - - instruction.exec(*this); - } - - // advance PC - { - size_t size = cpsr.state() == State::Arm ? arm::INSTRUCTION_SIZE - : thumb::INSTRUCTION_SIZE; - - if (is_flushed) { - // if flushed, do not increment the PC, instead set it to two - // instructions ahead to account for flushed "fetch" and "decode" - // instructions - pc += 2 * size; - is_flushed = false; - } else { - // if not flushed continue like normal - pc += size; - } - } -} -} diff --git a/src/cpu/cpu-impl.hh b/src/cpu/cpu-impl.hh deleted file mode 100644 index fa7b2ce..0000000 --- a/src/cpu/cpu-impl.hh +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include "arm/instruction.hh" -#include "bus.hh" -#include "cpu/psr.hh" -#include "thumb/instruction.hh" - -#include - -namespace matar { -class CpuImpl { - public: - CpuImpl(const Bus& bus) noexcept; - - void step(); - void chg_mode(const Mode to); - - private: - friend void arm::Instruction::exec(CpuImpl& cpu); - friend void thumb::Instruction::exec(CpuImpl& cpu); - - static constexpr uint8_t GPR_COUNT = 16; - - static constexpr uint8_t GPR_FIQ_FIRST = 8; - static constexpr uint8_t GPR_SVC_FIRST = 13; - static constexpr uint8_t GPR_ABT_FIRST = 13; - static constexpr uint8_t GPR_IRQ_FIRST = 13; - static constexpr uint8_t GPR_UND_FIRST = 13; - static constexpr uint8_t GPR_SYS_USR_FIRST = 8; - - std::shared_ptr bus; - std::array gpr; // general purpose registers - - Psr cpsr; // current program status register - Psr spsr; // status program status register - - static constexpr uint8_t SP_INDEX = 13; - static_assert(SP_INDEX < GPR_COUNT); - uint32_t& sp = gpr[SP_INDEX]; - - static constexpr uint8_t LR_INDEX = 14; - static_assert(LR_INDEX < GPR_COUNT); - uint32_t& lr = gpr[LR_INDEX]; - - static constexpr uint8_t PC_INDEX = 15; - static_assert(PC_INDEX < GPR_COUNT); - uint32_t& pc = gpr[PC_INDEX]; - - struct { - std::array fiq; - std::array svc; - std::array abt; - std::array irq; - std::array und; - - // visible registers before the mode switch - std::array old; - } gpr_banked; // banked general purpose registers - - struct { - Psr fiq; - Psr svc; - Psr abt; - Psr irq; - Psr und; - } spsr_banked; // banked saved program status registers - - bool is_flushed; -}; -} diff --git a/src/cpu/cpu.cc b/src/cpu/cpu.cc index 868aa7b..58e4348 100644 --- a/src/cpu/cpu.cc +++ b/src/cpu/cpu.cc @@ -1,14 +1,160 @@ #include "cpu/cpu.hh" -#include "cpu-impl.hh" +#include "cpu/arm/instruction.hh" +#include "cpu/thumb/instruction.hh" +#include "util/bits.hh" +#include "util/log.hh" +#include +#include +#include namespace matar { Cpu::Cpu(const Bus& bus) noexcept - : impl(std::make_unique(bus)){}; + : bus(std::make_shared(bus)) + , gpr({ 0 }) + , cpsr(0) + , spsr(0) + , gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } }) + , spsr_banked({ 0, 0, 0, 0, 0 }) + , is_flushed(false) { + cpsr.set_mode(Mode::Supervisor); + cpsr.set_irq_disabled(true); + cpsr.set_fiq_disabled(true); + cpsr.set_state(State::Arm); + glogger.info("CPU successfully initialised"); -Cpu::~Cpu() = default; + // PC always points to two instructions ahead + // PC - 2 is the instruction being executed + pc += 2 * arm::INSTRUCTION_SIZE; +} + +/* change modes */ +void +Cpu::chg_mode(const Mode to) { + Mode from = cpsr.mode(); + + if (from == to) + return; + +/* TODO: replace visible registers with view once I understand how to + * concatenate views */ +#define STORE_BANKED(mode, MODE) \ + std::copy(gpr.begin() + GPR_##MODE##_FIRST, \ + gpr.begin() + gpr.size() - 1, \ + gpr_banked.mode.begin()) + + switch (from) { + case Mode::Fiq: + STORE_BANKED(fiq, FIQ); + spsr_banked.fiq = spsr; + break; + + case Mode::Supervisor: + STORE_BANKED(svc, SVC); + spsr_banked.svc = spsr; + break; + + case Mode::Abort: + STORE_BANKED(abt, ABT); + spsr_banked.abt = spsr; + break; + + case Mode::Irq: + STORE_BANKED(irq, IRQ); + spsr_banked.irq = spsr; + break; + + case Mode::Undefined: + STORE_BANKED(und, UND); + spsr_banked.und = spsr; + break; + + case Mode::User: + case Mode::System: + STORE_BANKED(old, SYS_USR); + break; + } + +#define RESTORE_BANKED(mode, MODE) \ + std::copy(gpr_banked.mode.begin(), \ + gpr_banked.mode.end(), \ + gpr.begin() + GPR_##MODE##_FIRST) + + switch (to) { + case Mode::Fiq: + RESTORE_BANKED(fiq, FIQ); + spsr = spsr_banked.fiq; + break; + + case Mode::Supervisor: + RESTORE_BANKED(svc, SVC); + spsr = spsr_banked.svc; + break; + + case Mode::Abort: + RESTORE_BANKED(abt, ABT); + spsr = spsr_banked.abt; + break; + + case Mode::Irq: + RESTORE_BANKED(irq, IRQ); + spsr = spsr_banked.irq; + break; + + case Mode::Undefined: + RESTORE_BANKED(und, UND); + spsr = spsr_banked.und; + break; + + case Mode::User: + case Mode::System: + STORE_BANKED(old, SYS_USR); + break; + } + +#undef RESTORE_BANKED + + cpsr.set_mode(to); +} void Cpu::step() { - impl->step(); -}; + // Current instruction is two instructions behind PC + uint32_t cur_pc = pc - 2 * arm::INSTRUCTION_SIZE; + + if (cpsr.state() == State::Arm) { + arm::Instruction instruction(bus->read_word(cur_pc)); + +#ifdef DISASSEMBLER + glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble()); +#endif + + instruction.exec(*this); + + } else { + thumb::Instruction instruction(bus->read_halfword(cur_pc)); + +#ifdef DISASSEMBLER + glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble(cur_pc)); +#endif + + instruction.exec(*this); + } + + // advance PC + { + size_t size = cpsr.state() == State::Arm ? arm::INSTRUCTION_SIZE + : thumb::INSTRUCTION_SIZE; + + if (is_flushed) { + // if flushed, do not increment the PC, instead set it to two + // instructions ahead to account for flushed "fetch" and "decode" + // instructions + pc += 2 * size; + is_flushed = false; + } else { + // if not flushed continue like normal + pc += size; + } + } +} } diff --git a/src/cpu/meson.build b/src/cpu/meson.build index 64368a2..204f6f5 100644 --- a/src/cpu/meson.build +++ b/src/cpu/meson.build @@ -1,5 +1,4 @@ lib_sources += files( - 'cpu-impl.cc', 'cpu.cc', 'psr.cc', 'alu.cc' diff --git a/src/cpu/psr.cc b/src/cpu/psr.cc index 23ebfd7..4893f64 100644 --- a/src/cpu/psr.cc +++ b/src/cpu/psr.cc @@ -1,4 +1,4 @@ -#include "psr.hh" +#include "cpu/psr.hh" #include "util/bits.hh" #include "util/log.hh" diff --git a/src/cpu/thumb/disassembler.cc b/src/cpu/thumb/disassembler.cc index 0fde846..79bd974 100644 --- a/src/cpu/thumb/disassembler.cc +++ b/src/cpu/thumb/disassembler.cc @@ -1,4 +1,4 @@ -#include "instruction.hh" +#include "cpu/thumb/instruction.hh" #include "util/bits.hh" namespace matar::thumb { diff --git a/src/cpu/thumb/exec.cc b/src/cpu/thumb/exec.cc index d925669..8e43b98 100644 --- a/src/cpu/thumb/exec.cc +++ b/src/cpu/thumb/exec.cc @@ -1,11 +1,10 @@ -#include "cpu/cpu-impl.hh" -#include "instruction.hh" +#include "cpu/cpu.hh" #include "util/bits.hh" #include "util/log.hh" namespace matar::thumb { void -Instruction::exec(CpuImpl& cpu) { +Instruction::exec(Cpu& cpu) { auto set_cc = [&cpu](bool c, bool v, bool n, bool z) { cpu.cpsr.set_c(c); cpu.cpsr.set_v(v); diff --git a/src/cpu/thumb/instruction.cc b/src/cpu/thumb/instruction.cc index 673a4f4..2fd6900 100644 --- a/src/cpu/thumb/instruction.cc +++ b/src/cpu/thumb/instruction.cc @@ -1,4 +1,4 @@ -#include "instruction.hh" +#include "cpu/thumb/instruction.hh" #include "util/bits.hh" #include "util/log.hh" diff --git a/src/memory.cc b/src/memory.cc index bcf2a30..e96bfc9 100644 --- a/src/memory.cc +++ b/src/memory.cc @@ -34,64 +34,55 @@ Memory::Memory(std::array&& bios, glogger.info("Cartridge Title: {}", header.title); }; -#define MATCHES(area) address >= area##_START&& address <= area##_END - uint8_t Memory::read(size_t address) const { - if (MATCHES(BIOS)) { - return bios[address]; - } else if (MATCHES(BOARD_WRAM)) { - return board_wram[address - BOARD_WRAM_START]; - } else if (MATCHES(CHIP_WRAM)) { - return chip_wram[address - CHIP_WRAM_START]; - } else if (MATCHES(PALETTE_RAM)) { - return palette_ram[address - PALETTE_RAM_START]; - } else if (MATCHES(VRAM)) { - return vram[address - VRAM_START]; - } else if (MATCHES(OAM_OBJ_ATTR)) { - return oam_obj_attr[address - OAM_OBJ_ATTR_START]; - } else if (MATCHES(ROM_0)) { - return rom[address - ROM_0_START]; - } else if (MATCHES(ROM_1)) { - return rom[address - ROM_1_START]; - } else if (MATCHES(ROM_2)) { - return rom[address - ROM_2_START]; - } else { - glogger.error("Invalid memory region accessed"); - return 0xFF; - } +#define MATCHES(AREA, area) \ + if (address >= AREA##_START && address < AREA##_START + area.size()) \ + return area[address - AREA##_START]; + + MATCHES(BIOS, bios) + MATCHES(BOARD_WRAM, board_wram) + MATCHES(CHIP_WRAM, chip_wram) + MATCHES(PALETTE_RAM, palette_ram) + MATCHES(VRAM, vram) + MATCHES(OAM_OBJ_ATTR, oam_obj_attr) + MATCHES(ROM_0, rom) + MATCHES(ROM_1, rom) + MATCHES(ROM_2, rom) + + glogger.error("Invalid memory region accessed"); + return 0xFF; + +#undef MATCHES } void Memory::write(size_t address, uint8_t byte) { - if (MATCHES(BIOS)) { - bios[address] = byte; - } else if (MATCHES(BOARD_WRAM)) { - board_wram[address - BOARD_WRAM_START] = byte; - } else if (MATCHES(CHIP_WRAM)) { - chip_wram[address - CHIP_WRAM_START] = byte; - } else if (MATCHES(PALETTE_RAM)) { - palette_ram[address - PALETTE_RAM_START] = byte; - } else if (MATCHES(VRAM)) { - vram[address - VRAM_START] = byte; - } else if (MATCHES(OAM_OBJ_ATTR)) { - oam_obj_attr[address - OAM_OBJ_ATTR_START] = byte; - } else if (MATCHES(ROM_0)) { - rom[address - ROM_0_START] = byte; - } else if (MATCHES(ROM_1)) { - rom[address - ROM_1_START] = byte; - } else if (MATCHES(ROM_2)) { - rom[address - ROM_2_START] = byte; - } else { - glogger.error("Invalid memory region accessed"); +#define MATCHES(AREA, area) \ + if (address >= AREA##_START && address < AREA##_START + area.size()) { \ + area[address - AREA##_START] = byte; \ + return; \ } + + MATCHES(BIOS, bios) + MATCHES(BOARD_WRAM, board_wram) + MATCHES(CHIP_WRAM, chip_wram) + MATCHES(PALETTE_RAM, palette_ram) + MATCHES(VRAM, vram) + MATCHES(OAM_OBJ_ATTR, oam_obj_attr) + MATCHES(ROM_0, rom) + MATCHES(ROM_1, rom) + MATCHES(ROM_2, rom) + + glogger.error("Invalid memory region accessed"); + +#undef MATCHES } #undef MATCHES void Memory::parse_header() { - if (rom.size() < header.HEADER_SIZE) { throw std::out_of_range( "ROM is not large enough to even have a header"); diff --git a/tests/cpu/arm/exec.cc b/tests/cpu/arm/exec.cc index fcb3791..48239a8 100644 --- a/tests/cpu/arm/exec.cc +++ b/tests/cpu/arm/exec.cc @@ -1,5 +1,4 @@ #include "cpu/cpu-fixture.hh" -#include "cpu/cpu-impl.hh" #include "util/bits.hh" #include diff --git a/tests/cpu/cpu-fixture.cc b/tests/cpu/cpu-fixture.cc index dcbec86..08fd050 100644 --- a/tests/cpu/cpu-fixture.cc +++ b/tests/cpu/cpu-fixture.cc @@ -3,7 +3,7 @@ Psr CpuFixture::psr(bool spsr) { Psr psr(0); - CpuImpl tmp = cpu; + Cpu tmp = cpu; arm::Instruction instruction( Condition::AL, arm::PsrTransfer{ .operand = 0, @@ -40,11 +40,11 @@ CpuFixture::set_psr(Psr psr, bool spsr) { // fields. Assuming that these work correctly is necessary. Besides, all that // matters is that the public API is correct. uint32_t -CpuFixture::getr_(uint8_t r, CpuImpl& cpu) { +CpuFixture::getr_(uint8_t r, Cpu& cpu) { size_t addr = 13000; size_t offset = r == 15 ? 4 : 0; uint32_t word = bus.read_word(addr + offset); - CpuImpl tmp = cpu; + Cpu tmp = cpu; uint32_t ret = 0xFFFFFFFF; uint8_t base = r ? 0 : 1; @@ -82,7 +82,7 @@ CpuFixture::getr_(uint8_t r, CpuImpl& cpu) { } void -CpuFixture::setr_(uint8_t r, uint32_t value, CpuImpl& cpu) { +CpuFixture::setr_(uint8_t r, uint32_t value, Cpu& cpu) { // set register arm::Instruction set( Condition::AL, diff --git a/tests/cpu/cpu-fixture.hh b/tests/cpu/cpu-fixture.hh index 8d57471..fe8fff0 100644 --- a/tests/cpu/cpu-fixture.hh +++ b/tests/cpu/cpu-fixture.hh @@ -1,4 +1,4 @@ -#include "cpu/cpu-impl.hh" +#include "cpu/cpu.hh" using namespace matar; @@ -31,12 +31,12 @@ class CpuFixture { void set_psr(Psr psr, bool spsr = false); Bus bus; - CpuImpl cpu; + Cpu cpu; private: // hack to get a register - uint32_t getr_(uint8_t r, CpuImpl& cpu); + uint32_t getr_(uint8_t r, Cpu& cpu); // hack to set a register - void setr_(uint8_t r, uint32_t value, CpuImpl& cpu); + void setr_(uint8_t r, uint32_t value, Cpu& cpu); }; diff --git a/tests/cpu/thumb/exec.cc b/tests/cpu/thumb/exec.cc index 3098cde..6feabad 100644 --- a/tests/cpu/thumb/exec.cc +++ b/tests/cpu/thumb/exec.cc @@ -1,5 +1,4 @@ #include "cpu/cpu-fixture.hh" -#include "cpu/cpu-impl.hh" #include "cpu/thumb/instruction.hh" #include "util/bits.hh" #include