diff --git a/meson_options.txt b/meson_options.txt index 113ea48..37fbfbf 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ option('tests', type : 'boolean', value : true, description: 'enable tests') +option('disassembler', type: 'boolean', value: false, description: 'enable disassembler') diff --git a/src/cpu/arm/disassembler.cc b/src/cpu/arm/disassembler.cc new file mode 100644 index 0000000..9354336 --- /dev/null +++ b/src/cpu/arm/disassembler.cc @@ -0,0 +1,235 @@ +#include "instruction.hh" +#include "util/bits.hh" + +namespace matar { +namespace arm { +std::string +Instruction::disassemble() { + auto condition = stringify(this->condition); + + return std::visit( + overloaded{ + [condition](BranchAndExchange& data) { + return fmt::format("BX{} R{:d}", condition, data.rn); + }, + [condition](Branch& data) { + return fmt::format( + "B{}{} 0x{:06X}", (data.link ? "L" : ""), condition, data.offset); + }, + [condition](Multiply& data) { + if (data.acc) { + return fmt::format("MLA{}{} R{:d},R{:d},R{:d},R{:d}", + condition, + (data.set ? "S" : ""), + data.rd, + data.rm, + data.rs, + data.rn); + } else { + return fmt::format("MUL{}{} R{:d},R{:d},R{:d}", + condition, + (data.set ? "S" : ""), + data.rd, + data.rm, + data.rs); + } + }, + [condition](MultiplyLong& data) { + return fmt::format("{}{}{}{} R{:d},R{:d},R{:d},R{:d}", + (data.uns ? 'U' : 'S'), + (data.acc ? "MLAL" : "MULL"), + condition, + (data.set ? "S" : ""), + data.rdlo, + data.rdhi, + data.rm, + data.rs); + }, + [](Undefined) { return std::string("UND"); }, + [condition](SingleDataSwap& data) { + return fmt::format("SWP{}{} R{:d},R{:d},[R{:d}]", + condition, + (data.byte ? "B" : ""), + data.rd, + data.rm, + data.rn); + }, + [condition](SingleDataTransfer& data) { + std::string expression; + std::string address; + + if (const uint16_t* offset = std::get_if(&data.offset)) { + if (*offset == 0) { + expression = ""; + } else { + expression = + fmt::format(",{}#{:d}", (data.up ? '+' : '-'), *offset); + } + } else if (const Shift* shift = std::get_if(&data.offset)) { + // Shifts are always immediate in single data transfer + expression = fmt::format(",{}R{:d},{} #{:d}", + (data.up ? '+' : '-'), + shift->rm, + stringify(shift->data.type), + shift->data.operand); + } + + return fmt::format( + "{}{}{}{} R{:d},[R{:d}{}]{}", + (data.load ? "LDR" : "STR"), + condition, + (data.byte ? "B" : ""), + (!data.pre && data.write ? "T" : ""), + data.rd, + data.rn, + (data.pre ? expression : ""), + (data.pre ? (data.write ? "!" : "") : expression)); + }, + [condition](HalfwordTransfer& data) { + std::string expression; + + if (data.imm) { + if (data.offset == 0) { + expression = ""; + } else { + expression = fmt::format( + ",{}#{:d}", (data.up ? '+' : '-'), data.offset); + } + } else { + expression = + fmt::format(",{}R{:d}", (data.up ? '+' : '-'), data.offset); + } + + return fmt::format( + "{}{}{}{} R{:d},[R{:d}{}]{}", + (data.load ? "LDR" : "STR"), + condition, + (data.sign ? "S" : ""), + (data.half ? 'H' : 'B'), + data.rd, + data.rn, + (data.pre ? expression : ""), + (data.pre ? (data.write ? "!" : "") : expression)); + }, + [condition](BlockDataTransfer& data) { + std::string regs; + + for (uint8_t i = 0; i < 16; i++) { + if (get_bit(data.regs, i)) + fmt::format_to(std::back_inserter(regs), "R{:d},", i); + }; + + regs.pop_back(); + + return fmt::format("{}{}{}{} R{:d}{},{{{}}}{}", + (data.load ? "LDM" : "STM"), + condition, + (data.up ? 'I' : 'D'), + (data.pre ? 'B' : 'A'), + data.rn, + (data.write ? "!" : ""), + regs, + (data.s ? "^" : "")); + }, + [condition](PsrTransfer& data) { + if (data.type == PsrTransfer::Type::Mrs) { + return fmt::format("MRS{} R{:d},{}", + condition, + data.operand, + (data.spsr ? "SPSR_all" : "CPSR_all")); + } else { + return fmt::format( + "MSR{} {}_{},{}{}", + condition, + (data.spsr ? "SPSR" : "CPSR"), + (data.type == PsrTransfer::Type::Msr_flg ? "flg" : "all"), + (data.imm ? '#' : 'R'), + data.operand); + } + }, + [condition](DataProcessing& data) { + using OpCode = DataProcessing::OpCode; + + std::string op_2; + + if (const uint32_t* operand = + std::get_if(&data.operand)) { + op_2 = fmt::format("#{:d}", *operand); + } else if (const Shift* shift = std::get_if(&data.operand)) { + op_2 = fmt::format("R{:d},{} {}{:d}", + shift->rm, + stringify(shift->data.type), + (shift->data.immediate ? '#' : 'R'), + shift->data.operand); + } + + switch (data.opcode) { + case OpCode::MOV: + case OpCode::MVN: + return fmt::format("{}{}{} R{:d},{}", + stringify(data.opcode), + condition, + (data.set ? "S" : ""), + data.rd, + op_2); + case OpCode::TST: + case OpCode::TEQ: + case OpCode::CMP: + case OpCode::CMN: + return fmt::format("{}{} R{:d},{}", + stringify(data.opcode), + condition, + data.rn, + op_2); + default: + return fmt::format("{}{}{} R{:d},R{:d},{}", + stringify(data.opcode), + condition, + (data.set ? "S" : ""), + data.rd, + data.rn, + op_2); + } + }, + [condition](SoftwareInterrupt) { + return fmt::format("SWI{}", condition); + }, + [condition](CoprocessorDataTransfer& data) { + std::string expression = fmt::format(",#{:d}", data.offset); + return fmt::format( + "{}{}{} p{:d},c{:d},[R{:d}{}]{}", + (data.load ? "LDC" : "STC"), + condition, + (data.len ? "L" : ""), + data.cpn, + data.crd, + data.rn, + (data.pre ? expression : ""), + (data.pre ? (data.write ? "!" : "") : expression)); + }, + [condition](CoprocessorDataOperation& data) { + return fmt::format("CDP{} p{},{},c{},c{},c{},{}", + condition, + data.cpn, + data.cp_opc, + data.crd, + data.crn, + data.crm, + data.cp); + }, + [condition](CoprocessorRegisterTransfer& data) { + return fmt::format("{}{} p{},{},R{},c{},c{},{}", + (data.load ? "MRC" : "MCR"), + condition, + data.cpn, + data.cp_opc, + data.rd, + data.crn, + data.crm, + data.cp); + }, + [](auto) { return std::string("unknown instruction"); } }, + data); +} +} +} diff --git a/src/cpu/arm/instruction.cc b/src/cpu/arm/instruction.cc index d0c70fc..b53682a 100644 --- a/src/cpu/arm/instruction.cc +++ b/src/cpu/arm/instruction.cc @@ -274,230 +274,5 @@ Instruction::Instruction(uint32_t insn) data = Undefined{}; } } - -std::string -Instruction::disassemble() { - // goddamn this is gore - // TODO: make this less ugly - return std::visit( - overloaded{ - [this](BranchAndExchange& data) { - return fmt::format("BX{} R{:d}", condition, data.rn); - }, - [this](Branch& data) { - return fmt::format( - "B{}{} 0x{:06X}", (data.link ? "L" : ""), condition, data.offset); - }, - [this](Multiply& data) { - if (data.acc) { - return fmt::format("MLA{}{} R{:d},R{:d},R{:d},R{:d}", - condition, - (data.set ? "S" : ""), - data.rd, - data.rm, - data.rs, - data.rn); - } else { - return fmt::format("MUL{}{} R{:d},R{:d},R{:d}", - condition, - (data.set ? "S" : ""), - data.rd, - data.rm, - data.rs); - } - }, - [this](MultiplyLong& data) { - return fmt::format("{}{}{}{} R{:d},R{:d},R{:d},R{:d}", - (data.uns ? 'U' : 'S'), - (data.acc ? "MLAL" : "MULL"), - condition, - (data.set ? "S" : ""), - data.rdlo, - data.rdhi, - data.rm, - data.rs); - }, - [](Undefined) { return std::string("UND"); }, - [this](SingleDataSwap& data) { - return fmt::format("SWP{}{} R{:d},R{:d},[R{:d}]", - condition, - (data.byte ? "B" : ""), - data.rd, - data.rm, - data.rn); - }, - [this](SingleDataTransfer& data) { - std::string expression; - std::string address; - - if (const uint16_t* offset = std::get_if(&data.offset)) { - if (*offset == 0) { - expression = ""; - } else { - expression = - fmt::format(",{}#{:d}", (data.up ? '+' : '-'), *offset); - } - } else if (const Shift* shift = std::get_if(&data.offset)) { - // Shifts are always immediate in single data transfer - expression = fmt::format(",{}R{:d},{} #{:d}", - (data.up ? '+' : '-'), - shift->rm, - shift->data.type, - shift->data.operand); - } - - return fmt::format( - "{}{}{}{} R{:d},[R{:d}{}]{}", - (data.load ? "LDR" : "STR"), - condition, - (data.byte ? "B" : ""), - (!data.pre && data.write ? "T" : ""), - data.rd, - data.rn, - (data.pre ? expression : ""), - (data.pre ? (data.write ? "!" : "") : expression)); - }, - [this](HalfwordTransfer& data) { - std::string expression; - - if (data.imm) { - if (data.offset == 0) { - expression = ""; - } else { - expression = fmt::format( - ",{}#{:d}", (data.up ? '+' : '-'), data.offset); - } - } else { - expression = - fmt::format(",{}R{:d}", (data.up ? '+' : '-'), data.offset); - } - - return fmt::format( - "{}{}{}{} R{:d},[R{:d}{}]{}", - (data.load ? "LDR" : "STR"), - condition, - (data.sign ? "S" : ""), - (data.half ? 'H' : 'B'), - data.rd, - data.rn, - (data.pre ? expression : ""), - (data.pre ? (data.write ? "!" : "") : expression)); - }, - [this](BlockDataTransfer& data) { - std::string regs; - - for (uint8_t i = 0; i < 16; i++) { - if (get_bit(data.regs, i)) - fmt::format_to(std::back_inserter(regs), "R{:d},", i); - }; - - regs.pop_back(); - - return fmt::format("{}{}{}{} R{:d}{},{{{}}}{}", - (data.load ? "LDM" : "STM"), - condition, - (data.up ? 'I' : 'D'), - (data.pre ? 'B' : 'A'), - data.rn, - (data.write ? "!" : ""), - regs, - (data.s ? "^" : "")); - }, - [this](PsrTransfer& data) { - if (data.type == PsrTransfer::Type::Mrs) { - return fmt::format("MRS{} R{:d},{}", - condition, - data.operand, - (data.spsr ? "SPSR_all" : "CPSR_all")); - } else { - return fmt::format( - "MSR{} {}_{},{}{}", - condition, - (data.spsr ? "SPSR" : "CPSR"), - (data.type == PsrTransfer::Type::Msr_flg ? "flg" : "all"), - (data.imm ? '#' : 'R'), - data.operand); - } - }, - [this](DataProcessing& data) { - using OpCode = DataProcessing::OpCode; - - std::string op_2; - - if (const uint32_t* operand = - std::get_if(&data.operand)) { - op_2 = fmt::format("#{:d}", *operand); - } else if (const Shift* shift = std::get_if(&data.operand)) { - op_2 = fmt::format("R{:d},{} {}{:d}", - shift->rm, - shift->data.type, - (shift->data.immediate ? '#' : 'R'), - shift->data.operand); - } - - switch (data.opcode) { - case OpCode::MOV: - case OpCode::MVN: - return fmt::format("{}{}{} R{:d},{}", - data.opcode, - condition, - (data.set ? "S" : ""), - data.rd, - op_2); - case OpCode::TST: - case OpCode::TEQ: - case OpCode::CMP: - case OpCode::CMN: - return fmt::format( - "{}{} R{:d},{}", data.opcode, condition, data.rn, op_2); - default: - return fmt::format("{}{}{} R{:d},R{:d},{}", - data.opcode, - condition, - (data.set ? "S" : ""), - data.rd, - data.rn, - op_2); - } - }, - [this](SoftwareInterrupt) { return fmt::format("SWI{}", condition); }, - [this](CoprocessorDataTransfer& data) { - std::string expression = fmt::format(",#{:d}", data.offset); - return fmt::format( - "{}{}{} p{:d},c{:d},[R{:d}{}]{}", - (data.load ? "LDC" : "STC"), - condition, - (data.len ? "L" : ""), - data.cpn, - data.crd, - data.rn, - (data.pre ? expression : ""), - (data.pre ? (data.write ? "!" : "") : expression)); - }, - [this](CoprocessorDataOperation& data) { - return fmt::format("CDP{} p{},{},c{},c{},c{},{}", - condition, - data.cpn, - data.cp_opc, - data.crd, - data.crn, - data.crm, - data.cp); - }, - [this](CoprocessorRegisterTransfer& data) { - return fmt::format("{}{} p{},{},R{},c{},c{},{}", - (data.load ? "MRC" : "MCR"), - condition, - data.cpn, - data.cp_opc, - data.rd, - data.crn, - data.crm, - data.cp); - }, - [](auto) { return std::string("unknown instruction"); } }, - data); -} - } } diff --git a/src/cpu/arm/instruction.hh b/src/cpu/arm/instruction.hh index 6211495..024cf9c 100644 --- a/src/cpu/arm/instruction.hh +++ b/src/cpu/arm/instruction.hh @@ -218,7 +218,9 @@ struct Instruction { : condition(condition) , data(data){}; +#ifdef DISASSEMBLER std::string disassemble(); +#endif }; } } diff --git a/src/cpu/arm/meson.build b/src/cpu/arm/meson.build index f5ef827..788697e 100644 --- a/src/cpu/arm/meson.build +++ b/src/cpu/arm/meson.build @@ -1,4 +1,8 @@ lib_sources += files( 'instruction.cc', 'exec.cc' -) \ No newline at end of file +) + +if get_option('disassembler') + lib_sources += files('disassembler.cc') +endif \ No newline at end of file diff --git a/src/cpu/cpu-impl.cc b/src/cpu/cpu-impl.cc index 4c85cb8..e00d561 100644 --- a/src/cpu/cpu-impl.cc +++ b/src/cpu/cpu-impl.cc @@ -121,11 +121,13 @@ CpuImpl::step() { if (cpsr.state() == State::Arm) { uint32_t x = bus->read_word(cur_pc); arm::Instruction instruction(x); + + exec(instruction); + +#ifdef DISASSEMBLER glogger.info("{:#034b}", x); - - arm(instruction); - glogger.info("0x{:08X} : {}", cur_pc, instruction.disassemble()); +#endif if (is_flushed) { // if flushed, do not increment the PC, instead set it to two diff --git a/src/meson.build b/src/meson.build index b1eff10..566f98c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,7 +11,11 @@ lib_cpp_args = [ ] fmt = dependency('fmt', version : '>=10.1.0', static: true) if not fmt.found() fmt = dependency('fmt', version : '>=10.1.0', static: false) - lib_cpp_args += 'DFMT_HEADER_ONLY' + lib_cpp_args += '-DFMT_HEADER_ONLY' +endif + +if get_option('disassembler') + lib_cpp_args += '-DDISASSEMBLER' endif lib = library( diff --git a/tests/cpu/arm/instruction.cc b/tests/cpu/arm/instruction.cc index 8581b20..00e7d98 100644 --- a/tests/cpu/arm/instruction.cc +++ b/tests/cpu/arm/instruction.cc @@ -16,7 +16,9 @@ TEST_CASE("Branch and Exchange", TAG) { CHECK(bx->rn == 10); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "BXGT R10"); +#endif } TEST_CASE("Branch", TAG) { @@ -33,10 +35,12 @@ TEST_CASE("Branch", TAG) { CHECK(b->offset == 0xFE15FF14); CHECK(b->link == true); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "BL 0xFE15FF14"); b->link = false; CHECK(instruction.disassemble() == "B 0xFE15FF14"); +#endif } TEST_CASE("Multiply", TAG) { @@ -54,11 +58,13 @@ TEST_CASE("Multiply", TAG) { CHECK(mul->acc == true); CHECK(mul->set == true); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "MLAEQS R10,R0,R15,R14"); mul->acc = false; mul->set = false; CHECK(instruction.disassemble() == "MULEQ R10,R0,R15"); +#endif } TEST_CASE("Multiply Long", TAG) { @@ -77,6 +83,7 @@ TEST_CASE("Multiply Long", TAG) { CHECK(mull->set == true); CHECK(mull->uns == true); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "UMULLNES R7,R14,R2,R6"); mull->acc = true; @@ -85,6 +92,7 @@ TEST_CASE("Multiply Long", TAG) { mull->uns = false; mull->set = false; CHECK(instruction.disassemble() == "SMLALNE R7,R14,R2,R6"); +#endif } TEST_CASE("Undefined", TAG) { @@ -94,7 +102,10 @@ TEST_CASE("Undefined", TAG) { Instruction instruction(raw); CHECK(instruction.condition == Condition::AL); + +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "UND"); +#endif } TEST_CASE("Single Data Swap", TAG) { @@ -110,10 +121,12 @@ TEST_CASE("Single Data Swap", TAG) { CHECK(swp->rn == 9); CHECK(swp->byte == false); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "SWPGE R5,R6,[R9]"); swp->byte = true; CHECK(instruction.disassemble() == "SWPGEB R5,R6,[R9]"); +#endif } TEST_CASE("Single Data Transfer", TAG) { @@ -138,6 +151,7 @@ TEST_CASE("Single Data Transfer", TAG) { CHECK(ldr->up == true); CHECK(ldr->pre == true); +#ifdef DISASSEMBLER ldr->load = true; ldr->byte = true; ldr->write = false; @@ -153,6 +167,7 @@ TEST_CASE("Single Data Transfer", TAG) { ldr->pre = true; CHECK(instruction.disassemble() == "LDRB R10,[R2,-#9023]"); +#endif } TEST_CASE("Halfword Transfer", TAG) { @@ -176,6 +191,7 @@ TEST_CASE("Halfword Transfer", TAG) { CHECK(ldr->up == true); CHECK(ldr->pre == true); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "STRCCH R2,[R15,+R6]!"); ldr->pre = false; @@ -193,6 +209,7 @@ TEST_CASE("Halfword Transfer", TAG) { ldr->imm = 1; ldr->offset = 90; CHECK(instruction.disassemble() == "STRCCSB R2,[R15],-#90"); +#endif } TEST_CASE("Block Data Transfer", TAG) { @@ -223,6 +240,7 @@ TEST_CASE("Block Data Transfer", TAG) { CHECK(ldm->up == false); CHECK(ldm->pre == true); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "LDMLSDB R7,{R0,R2,R3,R5,R6,R8,R14}^"); ldm->write = true; @@ -238,6 +256,7 @@ TEST_CASE("Block Data Transfer", TAG) { ldm->pre = false; CHECK(instruction.disassemble() == "STMLSIA R7!,{R0,R2,R5,R14}"); +#endif } TEST_CASE("PSR Transfer", TAG) { @@ -256,7 +275,9 @@ TEST_CASE("PSR Transfer", TAG) { CHECK(mrs->operand == 10); CHECK(mrs->spsr == true); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "MRSMI R10,SPSR_all"); +#endif } SECTION("MSR") { @@ -272,7 +293,9 @@ TEST_CASE("PSR Transfer", TAG) { CHECK(msr->operand == 8); CHECK(msr->spsr == false); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "MSR CPSR_all,R8"); +#endif } SECTION("MSR_flg with register operand") { @@ -287,7 +310,9 @@ TEST_CASE("PSR Transfer", TAG) { CHECK(msr->operand == 8); CHECK(msr->spsr == false); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "MSRVS CPSR_flg,R8"); +#endif } SECTION("MSR_flg with immediate operand") { @@ -304,7 +329,9 @@ TEST_CASE("PSR Transfer", TAG) { CHECK(msr->operand == 27262976); CHECK(msr->spsr == true); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "MSR SPSR_flg,#27262976"); +#endif } } @@ -331,6 +358,7 @@ TEST_CASE("Data Processing", TAG) { CHECK(alu->set == true); CHECK(alu->opcode == OpCode::AND); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "ANDS R7,R14,R1,ROR #22"); shift->data.immediate = false; @@ -392,6 +420,7 @@ TEST_CASE("Data Processing", TAG) { alu->opcode = OpCode::MVN; CHECK(instruction.disassemble() == "MVN R7,#3300012"); } +#endif } TEST_CASE("Coprocessor Data Transfer", TAG) { @@ -412,6 +441,7 @@ TEST_CASE("Coprocessor Data Transfer", TAG) { CHECK(ldc->up == true); CHECK(ldc->pre == true); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "STCGE p1,c15,[R5,#70]!"); ldc->load = true; @@ -420,6 +450,7 @@ TEST_CASE("Coprocessor Data Transfer", TAG) { ldc->len = true; CHECK(instruction.disassemble() == "LDCGEL p1,c15,[R5],#70"); +#endif } TEST_CASE("Coprocessor Operand Operation", TAG) { @@ -437,7 +468,9 @@ TEST_CASE("Coprocessor Operand Operation", TAG) { CHECK(cdp->crn == 5); CHECK(cdp->cp_opc == 10); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "CDP p1,10,c15,c5,c6,2"); +#endif } TEST_CASE("Coprocessor Register Transfer", TAG) { @@ -457,7 +490,9 @@ TEST_CASE("Coprocessor Register Transfer", TAG) { CHECK(mrc->load == false); CHECK(mrc->cp_opc == 5); +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "MCR p1,5,R15,c5,c6,2"); +#endif } TEST_CASE("Software Interrupt", TAG) { @@ -465,5 +500,8 @@ TEST_CASE("Software Interrupt", TAG) { Instruction instruction(raw); CHECK(instruction.condition == Condition::EQ); + +#ifdef DISASSEMBLER CHECK(instruction.disassemble() == "SWIEQ"); +#endif } diff --git a/tests/meson.build b/tests/meson.build index acc5726..e0574ec 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -13,6 +13,12 @@ tests_sources = files( subdir('cpu') subdir('util') +tests_cpp_args = [] + +if get_option('disassembler') + tests_cpp_args += '-DDISASSEMBLER' +endif + catch2 = dependency('catch2', version: '>=3.4.0', static: true) catch2_tests = executable( 'matar_tests', @@ -21,6 +27,7 @@ catch2_tests = executable( link_with: tests_deps, include_directories: [inc, src], build_by_default: false, + cpp_args: tests_cpp_args ) test('catch2 tests', catch2_tests)