diff --git a/include/cpu/cpu.hh b/include/cpu/cpu.hh index 88f8673..54b394c 100644 --- a/include/cpu/cpu.hh +++ b/include/cpu/cpu.hh @@ -19,11 +19,14 @@ class GdbRsp; class Cpu { public: - Cpu(std::shared_ptr) noexcept; + Cpu(std::shared_ptr bus) noexcept; void step(); void chg_mode(const Mode to); + void exec(arm::Instruction& instruction); + void exec(thumb::Instruction& instruction); + #ifdef GDB_DEBUG bool breakpoint_reached() { if (breakpoints.contains(pc - 2 * (cpsr.state() == State::Arm @@ -92,10 +95,9 @@ class Cpu { // raw instructions in the pipeline std::array opcodes = {}; - void advance_pc_arm() { pc += arm::INSTRUCTION_SIZE; }; - void advance_pc_thumb() { pc += thumb::INSTRUCTION_SIZE; } + void advance_pc_arm(); + void advance_pc_thumb(); - bool is_flushed = false; void flush_pipeline(); #ifdef GDB_DEBUG diff --git a/include/cpu/thumb/instruction.hh b/include/cpu/thumb/instruction.hh index cc25c2d..d791e9d 100644 --- a/include/cpu/thumb/instruction.hh +++ b/include/cpu/thumb/instruction.hh @@ -251,7 +251,7 @@ struct UnconditionalBranch { struct LongBranchWithLink { uint16_t offset; - bool high; + bool low; }; using InstructionData = std::variant reading instruction in step() N -> fetch from the new address in branch @@ -32,30 +36,30 @@ Instruction::exec(Cpu& cpu) { 1S done, S+N taken care of by flush_pipeline() */ - uint32_t addr = cpu.gpr[data.rn]; + uint32_t addr = gpr[data.rn]; State state = static_cast(get_bit(addr, 0)); pc_warn(data.rn); - if (state != cpu.cpsr.state()) + if (state != cpsr.state()) glogger.info_bold("State changed"); // set state - cpu.cpsr.set_state(state); + cpsr.set_state(state); // copy to PC - cpu.pc = addr; + pc = addr; // ignore [1:0] bits for arm and 0 bit for thumb - rst_bit(cpu.pc, 0); + rst_bit(pc, 0); if (state == State::Arm) - rst_bit(cpu.pc, 1); + rst_bit(pc, 1); // PC is affected so flush the pipeline - cpu.is_flushed = true; + is_flushed = true; }, - [&cpu](Branch& data) { + [this, &is_flushed](Branch& data) { /* S -> reading instruction in step() N -> fetch from the new address in branch @@ -65,14 +69,14 @@ Instruction::exec(Cpu& cpu) { */ if (data.link) - cpu.gpr[14] = cpu.pc - INSTRUCTION_SIZE; + gpr[14] = pc - INSTRUCTION_SIZE; - cpu.pc += data.offset; + pc += data.offset; // pc is affected so flush the pipeline - cpu.is_flushed = true; + is_flushed = true; }, - [&cpu, pc_error](Multiply& data) { + [this, pc_error](Multiply& data) { /* S -> reading instruction in step() mI -> m internal cycles @@ -95,36 +99,34 @@ Instruction::exec(Cpu& cpu) { pc_error(data.rd); // mI - for (int i = 0; i < multiplier_array_cycles(cpu.gpr[data.rs]); i++) - cpu.internal_cycle(); + for (int i = 0; i < multiplier_array_cycles(gpr[data.rs]); i++) + internal_cycle(); - cpu.gpr[data.rd] = cpu.gpr[data.rm] * cpu.gpr[data.rs]; + gpr[data.rd] = gpr[data.rm] * gpr[data.rs]; if (data.acc) { - cpu.gpr[data.rd] += cpu.gpr[data.rn]; + gpr[data.rd] += gpr[data.rn]; // 1I - cpu.internal_cycle(); + internal_cycle(); } if (data.set) { - cpu.cpsr.set_z(cpu.gpr[data.rd] == 0); - cpu.cpsr.set_n(get_bit(cpu.gpr[data.rd], 31)); - cpu.cpsr.set_c(0); + cpsr.set_z(gpr[data.rd] == 0); + cpsr.set_n(get_bit(gpr[data.rd], 31)); + cpsr.set_c(0); } }, - [&cpu, pc_error](MultiplyLong& data) { + [this, pc_error](MultiplyLong& data) { /* S -> reading instruction in step() (m+1)I -> m + 1 internal cycles I -> only when accumulating - let v = data at rn + let v = data at rs m = 1 if bits [32:8] of v are all zeroes (or all ones if signed) m = 2 [32:16] m = 3 [32:24] m = 4 otherwise - Total = S + mI or S + (m+1)I - Total = S + (m+1)I or S + (m+2)I */ @@ -140,56 +142,54 @@ Instruction::exec(Cpu& cpu) { // 1I if (data.acc) - cpu.internal_cycle(); + internal_cycle(); // m+1 internal cycles for (int i = 0; - i <= multiplier_array_cycles(cpu.gpr[data.rs], data.uns); + i <= multiplier_array_cycles(gpr[data.rs], data.uns); i++) - cpu.internal_cycle(); + internal_cycle(); if (data.uns) { auto cast = [](uint32_t x) -> uint64_t { return static_cast(x); }; - uint64_t eval = - cast(cpu.gpr[data.rm]) * cast(cpu.gpr[data.rs]) + - (data.acc ? (cast(cpu.gpr[data.rdhi]) << 32) | - cast(cpu.gpr[data.rdlo]) - : 0); + uint64_t eval = cast(gpr[data.rm]) * cast(gpr[data.rs]) + + (data.acc ? (cast(gpr[data.rdhi]) << 32) | + cast(gpr[data.rdlo]) + : 0); - cpu.gpr[data.rdlo] = bit_range(eval, 0, 31); - cpu.gpr[data.rdhi] = bit_range(eval, 32, 63); + gpr[data.rdlo] = bit_range(eval, 0, 31); + gpr[data.rdhi] = bit_range(eval, 32, 63); } else { auto cast = [](uint32_t x) -> int64_t { return static_cast(static_cast(x)); }; - int64_t eval = cast(cpu.gpr[data.rm]) * cast(cpu.gpr[data.rs]) + - (data.acc ? (cast(cpu.gpr[data.rdhi]) << 32) | - cast(cpu.gpr[data.rdlo]) + int64_t eval = cast(gpr[data.rm]) * cast(gpr[data.rs]) + + (data.acc ? (cast(gpr[data.rdhi]) << 32) | + cast(gpr[data.rdlo]) : 0); - cpu.gpr[data.rdlo] = bit_range(eval, 0, 31); - cpu.gpr[data.rdhi] = bit_range(eval, 32, 63); + gpr[data.rdlo] = bit_range(eval, 0, 31); + gpr[data.rdhi] = bit_range(eval, 32, 63); } if (data.set) { - cpu.cpsr.set_z(cpu.gpr[data.rdhi] == 0 && - cpu.gpr[data.rdlo] == 0); - cpu.cpsr.set_n(get_bit(cpu.gpr[data.rdhi], 31)); - cpu.cpsr.set_c(0); - cpu.cpsr.set_v(0); + cpsr.set_z(gpr[data.rdhi] == 0 && gpr[data.rdlo] == 0); + cpsr.set_n(get_bit(gpr[data.rdhi], 31)); + cpsr.set_c(0); + cpsr.set_v(0); } }, [](Undefined) { - // this should be 2S + N + I, should i flush the pipeline? i dont - // know. TODO: study + // this should be 2S + N + I, should i flush the pipeline? i + // dont know. TODO: study glogger.warn("Undefined instruction"); }, - [&cpu, pc_error](SingleDataSwap& data) { + [this, pc_error](SingleDataSwap& data) { /* N -> reading instruction in step() N -> unrelated read @@ -203,23 +203,22 @@ Instruction::exec(Cpu& cpu) { pc_error(data.rd); if (data.byte) { - cpu.gpr[data.rd] = cpu.bus->read_byte(cpu.gpr[data.rn], - CpuAccess::NonSequential); - cpu.bus->write_byte(cpu.gpr[data.rn], - cpu.gpr[data.rm] & 0xFF, - CpuAccess::Sequential); + gpr[data.rd] = + bus->read_byte(gpr[data.rn], CpuAccess::NonSequential); + bus->write_byte( + gpr[data.rn], gpr[data.rm] & 0xFF, CpuAccess::Sequential); } else { - cpu.gpr[data.rd] = cpu.bus->read_word(cpu.gpr[data.rn], - CpuAccess::NonSequential); - cpu.bus->write_word( - cpu.gpr[data.rn], cpu.gpr[data.rm], CpuAccess::Sequential); + gpr[data.rd] = + bus->read_word(gpr[data.rn], CpuAccess::NonSequential); + bus->write_word( + gpr[data.rn], gpr[data.rm], CpuAccess::Sequential); } - cpu.internal_cycle(); + internal_cycle(); // last write address is unrelated to next - cpu.next_access = CpuAccess::NonSequential; + next_access = CpuAccess::NonSequential; }, - [&cpu, pc_warn, pc_error](SingleDataTransfer& data) { + [this, pc_warn, pc_error, &is_flushed](SingleDataTransfer& data) { /* Load ==== @@ -236,13 +235,13 @@ Instruction::exec(Cpu& cpu) { Total = 2N */ uint32_t offset = 0; - uint32_t address = cpu.gpr[data.rn]; + uint32_t address = gpr[data.rn]; if (!data.pre && data.write) glogger.warn("Write-back enabled with post-indexing in {}", typeid(data).name()); - if (data.rn == cpu.PC_INDEX && data.write) + if (data.rn == PC_INDEX && data.write) glogger.warn("Write-back enabled with base register as PC {}", typeid(data).name()); @@ -256,18 +255,18 @@ Instruction::exec(Cpu& cpu) { } else if (const Shift* shift = std::get_if(&data.offset)) { uint8_t amount = (shift->data.immediate ? shift->data.operand - : cpu.gpr[shift->data.operand] & 0xFF); + : gpr[shift->data.operand] & 0xFF); - bool carry = cpu.cpsr.c(); + bool carry = cpsr.c(); if (!shift->data.immediate) pc_error(shift->data.operand); pc_error(shift->rm); - offset = eval_shift( - shift->data.type, cpu.gpr[shift->rm], amount, carry); + offset = + eval_shift(shift->data.type, gpr[shift->rm], amount, carry); - cpu.cpsr.set_c(carry); + cpsr.set_c(carry); } if (data.pre) @@ -277,46 +276,47 @@ Instruction::exec(Cpu& cpu) { if (data.load) { // byte if (data.byte) - cpu.gpr[data.rd] = - cpu.bus->read_byte(address, CpuAccess::NonSequential); + gpr[data.rd] = + bus->read_byte(address, CpuAccess::NonSequential); // word else - cpu.gpr[data.rd] = - cpu.bus->read_word(address, CpuAccess::NonSequential); + gpr[data.rd] = + bus->read_word(address, CpuAccess::NonSequential); // N + S - if (data.rd == cpu.PC_INDEX) - cpu.is_flushed = true; + if (data.rd == PC_INDEX) + is_flushed = true; // I - cpu.internal_cycle(); + internal_cycle(); // store } else { // take PC into consideration - if (data.rd == cpu.PC_INDEX) - address += INSTRUCTION_SIZE; + uint32_t value = gpr[data.rd]; + + if (data.rd == PC_INDEX) + value += INSTRUCTION_SIZE; // byte if (data.byte) - cpu.bus->write_byte(address, - cpu.gpr[data.rd] & 0xFF, - CpuAccess::NonSequential); + bus->write_byte( + address, value & 0xFF, CpuAccess::NonSequential); // word else - cpu.bus->write_word( - address, cpu.gpr[data.rd], CpuAccess::NonSequential); + bus->write_word(address, value, CpuAccess::NonSequential); } if (!data.pre) address += (data.up ? offset : -offset); if (!data.pre || data.write) - cpu.gpr[data.rn] = address; + gpr[data.rn] = address; - // last read/write is unrelated, this will be overwriten if flushed - cpu.next_access = CpuAccess::NonSequential; + // last read/write is unrelated, this will be overwriten if + // flushed + next_access = CpuAccess::NonSequential; }, - [&cpu, pc_warn, pc_error](HalfwordTransfer& data) { + [this, pc_warn, pc_error, &is_flushed](HalfwordTransfer& data) { /* Load ==== @@ -332,7 +332,7 @@ Instruction::exec(Cpu& cpu) { N -> write at target Total = 2N */ - uint32_t address = cpu.gpr[data.rn]; + uint32_t address = gpr[data.rn]; uint32_t offset = 0; if (!data.pre && data.write) @@ -348,15 +348,11 @@ Instruction::exec(Cpu& cpu) { // offset is register number (4 bits) when not an immediate if (!data.imm) { pc_error(data.offset); - offset = cpu.gpr[data.offset]; + offset = gpr[data.offset]; } else { offset = data.offset; } - // PC is always two instructions ahead - if (data.rn == cpu.PC_INDEX) - address -= 2 * INSTRUCTION_SIZE; - if (data.pre) address += (data.up ? offset : -offset); @@ -366,56 +362,59 @@ Instruction::exec(Cpu& cpu) { if (data.sign) { // halfword if (data.half) { - cpu.gpr[data.rd] = cpu.bus->read_halfword( - address, CpuAccess::NonSequential); + gpr[data.rd] = + bus->read_halfword(address, CpuAccess::NonSequential); // sign extend the halfword - cpu.gpr[data.rd] = - (static_cast(cpu.gpr[data.rd]) << 16) >> 16; + gpr[data.rd] = + (static_cast(gpr[data.rd]) << 16) >> 16; // byte } else { - cpu.gpr[data.rd] = - cpu.bus->read_byte(address, CpuAccess::NonSequential); + gpr[data.rd] = + bus->read_byte(address, CpuAccess::NonSequential); // sign extend the byte - cpu.gpr[data.rd] = - (static_cast(cpu.gpr[data.rd]) << 24) >> 24; + gpr[data.rd] = + (static_cast(gpr[data.rd]) << 24) >> 24; } // unsigned halfword } else if (data.half) { - cpu.gpr[data.rd] = - cpu.bus->read_halfword(address, CpuAccess::NonSequential); + gpr[data.rd] = + bus->read_halfword(address, CpuAccess::NonSequential); } // I - cpu.internal_cycle(); + internal_cycle(); - if (data.rd == cpu.PC_INDEX) - cpu.is_flushed = true; + if (data.rd == PC_INDEX) + is_flushed = true; // store } else { + uint32_t value = gpr[data.rd]; + // take PC into consideration - if (data.rd == cpu.PC_INDEX) - address += INSTRUCTION_SIZE; + if (data.rd == PC_INDEX) + value += INSTRUCTION_SIZE; // halfword if (data.half) - cpu.bus->write_halfword( - address, cpu.gpr[data.rd], CpuAccess::NonSequential); + bus->write_halfword( + address, value & 0xFFFF, CpuAccess::NonSequential); } if (!data.pre) address += (data.up ? offset : -offset); if (!data.pre || data.write) - cpu.gpr[data.rn] = address; + gpr[data.rn] = address; - // last read/write is unrelated, this will be overwriten if flushed - cpu.next_access = CpuAccess::NonSequential; + // last read/write is unrelated, this will be overwriten if + // flushed + next_access = CpuAccess::NonSequential; }, - [&cpu, pc_error](BlockDataTransfer& data) { + [this, pc_error, &is_flushed](BlockDataTransfer& data) { /* Load ==== @@ -423,8 +422,8 @@ Instruction::exec(Cpu& cpu) { N -> unrelated read from target (n-1) S -> next n - 1 related reads from target I -> stored in register - N+S -> if PC is written - taken care of by flush_pipeline() - Total = nS + N + I or (n+1)S + 2N + I + N+S -> if PC is written - taken care of by + flush_pipeline() Total = nS + N + I or (n+1)S + 2N + I Store ===== @@ -436,22 +435,22 @@ Instruction::exec(Cpu& cpu) { static constexpr uint8_t alignment = 4; // word - uint32_t address = cpu.gpr[data.rn]; - Mode mode = cpu.cpsr.mode(); + uint32_t address = gpr[data.rn]; + Mode mode = cpsr.mode(); int8_t i = 0; CpuAccess access = CpuAccess::NonSequential; pc_error(data.rn); - if (cpu.cpsr.mode() == Mode::User && data.s) { + if (cpsr.mode() == Mode::User && data.s) { glogger.error("Bit S is set outside priviliged modes in block " "data transfer"); } // we just change modes to load user registers - if ((!get_bit(data.regs, cpu.PC_INDEX) && data.s) || + if ((!get_bit(data.regs, PC_INDEX) && data.s) || (!data.load && data.s)) { - cpu.chg_mode(Mode::User); + chg_mode(Mode::User); if (data.write) { glogger.error("Write-back enable for user bank registers " @@ -464,27 +463,27 @@ Instruction::exec(Cpu& cpu) { address += (data.up ? alignment : -alignment); if (data.load) { - if (get_bit(data.regs, cpu.PC_INDEX)) { - cpu.is_flushed = true; + if (get_bit(data.regs, PC_INDEX)) { + is_flushed = true; - // current mode's cpu.spsr is already loaded when it was + // current mode's spsr is already loaded when it was // switched if (data.s) - cpu.spsr = cpu.cpsr; + spsr = cpsr; } if (data.up) { - for (i = 0; i < cpu.GPR_COUNT; i++) { + for (i = 0; i < GPR_COUNT; i++) { if (get_bit(data.regs, i)) { - cpu.gpr[i] = cpu.bus->read_word(address, access); + gpr[i] = bus->read_word(address, access); address += alignment; access = CpuAccess::Sequential; } } } else { - for (i = cpu.GPR_COUNT - 1; i >= 0; i--) { + for (i = GPR_COUNT - 1; i >= 0; i--) { if (get_bit(data.regs, i)) { - cpu.gpr[i] = cpu.bus->read_word(address, access); + gpr[i] = bus->read_word(address, access); address -= alignment; access = CpuAccess::Sequential; } @@ -492,20 +491,20 @@ Instruction::exec(Cpu& cpu) { } // I - cpu.internal_cycle(); + internal_cycle(); } else { if (data.up) { - for (i = 0; i < cpu.GPR_COUNT; i++) { + for (i = 0; i < GPR_COUNT; i++) { if (get_bit(data.regs, i)) { - cpu.bus->write_word(address, cpu.gpr[i], access); + bus->write_word(address, gpr[i], access); address += alignment; access = CpuAccess::Sequential; } } } else { - for (i = cpu.GPR_COUNT - 1; i >= 0; i--) { + for (i = GPR_COUNT - 1; i >= 0; i--) { if (get_bit(data.regs, i)) { - cpu.bus->write_word(address, cpu.gpr[i], access); + bus->write_word(address, gpr[i], access); address -= alignment; access = CpuAccess::Sequential; } @@ -518,47 +517,48 @@ Instruction::exec(Cpu& cpu) { address += (data.up ? -alignment : alignment); if (!data.pre || data.write) - cpu.gpr[data.rn] = address; + gpr[data.rn] = address; // load back the original mode registers - cpu.chg_mode(mode); + chg_mode(mode); - // last read/write is unrelated, this will be overwriten if flushed - cpu.next_access = CpuAccess::NonSequential; + // last read/write is unrelated, this will be overwriten if + // flushed + next_access = CpuAccess::NonSequential; }, - [&cpu, pc_error](PsrTransfer& data) { + [this, pc_error](PsrTransfer& data) { /* S -> prefetched instruction in step() Total = 1S cycle */ - if (data.spsr && cpu.cpsr.mode() == Mode::User) { - glogger.error("Accessing CPU.SPSR in User mode in {}", + if (data.spsr && cpsr.mode() == Mode::User) { + glogger.error("Accessing SPSR in User mode in {}", typeid(data).name()); } - Psr& psr = data.spsr ? cpu.spsr : cpu.cpsr; + Psr& psr = data.spsr ? spsr : cpsr; switch (data.type) { case PsrTransfer::Type::Mrs: pc_error(data.operand); - cpu.gpr[data.operand] = psr.raw(); + gpr[data.operand] = psr.raw(); break; case PsrTransfer::Type::Msr: pc_error(data.operand); - if (cpu.cpsr.mode() != Mode::User) { + if (cpsr.mode() != Mode::User) { if (!data.spsr) { - Psr tmp = Psr(cpu.gpr[data.operand]); - cpu.chg_mode(tmp.mode()); + Psr tmp = Psr(gpr[data.operand]); + chg_mode(tmp.mode()); } - psr.set_all(cpu.gpr[data.operand]); + psr.set_all(gpr[data.operand]); } break; case PsrTransfer::Type::Msr_flg: uint32_t operand = - (data.imm ? data.operand : cpu.gpr[data.operand]); + (data.imm ? data.operand : gpr[data.operand]); psr.set_n(get_bit(operand, 31)); psr.set_z(get_bit(operand, 30)); psr.set_c(get_bit(operand, 29)); @@ -566,7 +566,7 @@ Instruction::exec(Cpu& cpu) { break; } }, - [&cpu, pc_error](DataProcessing& data) { + [this, pc_error, &is_flushed](DataProcessing& data) { /* Always ====== @@ -587,7 +587,7 @@ Instruction::exec(Cpu& cpu) { using OpCode = DataProcessing::OpCode; - uint32_t op_1 = cpu.gpr[data.rn]; + uint32_t op_1 = gpr[data.rn]; uint32_t op_2 = 0; uint32_t result = 0; @@ -598,30 +598,30 @@ Instruction::exec(Cpu& cpu) { } else if (const Shift* shift = std::get_if(&data.operand)) { uint8_t amount = (shift->data.immediate ? shift->data.operand - : cpu.gpr[shift->data.operand] & 0xFF); + : gpr[shift->data.operand] & 0xFF); - bool carry = cpu.cpsr.c(); + bool carry = cpsr.c(); if (!shift->data.immediate) pc_error(shift->data.operand); pc_error(shift->rm); - op_2 = eval_shift( - shift->data.type, cpu.gpr[shift->rm], amount, carry); + op_2 = + eval_shift(shift->data.type, gpr[shift->rm], amount, carry); - cpu.cpsr.set_c(carry); + cpsr.set_c(carry); // PC is 12 bytes ahead when shifting - if (data.rn == cpu.PC_INDEX) + if (data.rn == PC_INDEX) op_1 += INSTRUCTION_SIZE; // 1I when register specified shift - if (shift->data.operand) - cpu.internal_cycle(); + if (!shift->data.immediate) + internal_cycle(); } - bool overflow = cpu.cpsr.v(); - bool carry = cpu.cpsr.c(); + bool overflow = cpsr.v(); + bool carry = cpsr.c(); switch (data.opcode) { case OpCode::AND: @@ -667,19 +667,19 @@ Instruction::exec(Cpu& cpu) { break; } - auto set_conditions = [&cpu, carry, overflow, result]() { - cpu.cpsr.set_c(carry); - cpu.cpsr.set_v(overflow); - cpu.cpsr.set_n(get_bit(result, 31)); - cpu.cpsr.set_z(result == 0); + auto set_conditions = [this, carry, overflow, result]() { + cpsr.set_c(carry); + cpsr.set_v(overflow); + cpsr.set_n(get_bit(result, 31)); + cpsr.set_z(result == 0); }; if (data.set) { - if (data.rd == cpu.PC_INDEX) { - if (cpu.cpsr.mode() == Mode::User) + if (data.rd == PC_INDEX) { + if (cpsr.mode() == Mode::User) glogger.error("Running {} in User mode", typeid(data).name()); - cpu.spsr = cpu.cpsr; + spsr = cpsr; } else { set_conditions(); } @@ -689,19 +689,29 @@ Instruction::exec(Cpu& cpu) { data.opcode == OpCode::CMP || data.opcode == OpCode::CMN) { set_conditions(); } else { - cpu.gpr[data.rd] = result; - if (data.rd == cpu.PC_INDEX || data.opcode == OpCode::MVN) - cpu.is_flushed = true; + gpr[data.rd] = result; + if (data.rd == PC_INDEX || data.opcode == OpCode::MVN) + is_flushed = true; } }, - [&cpu](SoftwareInterrupt) { - cpu.chg_mode(Mode::Supervisor); - cpu.pc = 0x08; - cpu.spsr = cpu.cpsr; + [this, &is_flushed](SoftwareInterrupt) { + chg_mode(Mode::Supervisor); + pc = 0x00; + spsr = cpsr; + is_flushed = true; }, [](auto& data) { glogger.error("Unimplemented {} instruction", typeid(data).name()); } }, - data); + instruction.data); + + if (is_flushed) { + opcodes[0] = bus->read_word(pc, CpuAccess::NonSequential); + advance_pc_arm(); + opcodes[1] = bus->read_word(pc, CpuAccess::Sequential); + advance_pc_arm(); + next_access = CpuAccess::Sequential; + } else + advance_pc_arm(); } } diff --git a/src/cpu/cpu.cc b/src/cpu/cpu.cc index c2b0187..8ebbc1c 100644 --- a/src/cpu/cpu.cc +++ b/src/cpu/cpu.cc @@ -140,13 +140,7 @@ Cpu::step() { instruction.disassemble()); #endif - instruction.exec(*this); - - if (is_flushed) { - flush_pipeline(); - is_flushed = false; - } else - advance_pc_arm(); + exec(instruction); } else { thumb::Instruction instruction(opcodes[0]); @@ -159,33 +153,22 @@ Cpu::step() { instruction.disassemble()); #endif - instruction.exec(*this); - - if (is_flushed) { - flush_pipeline(); - is_flushed = false; - } else - advance_pc_thumb(); + exec(instruction); } } void -Cpu::flush_pipeline() { - // halfword align +Cpu::advance_pc_arm() { rst_bit(pc, 0); - if (cpsr.state() == State::Arm) { - // word align - rst_bit(pc, 1); - opcodes[0] = bus->read_word(pc, CpuAccess::NonSequential); - advance_pc_arm(); - opcodes[1] = bus->read_word(pc, CpuAccess::Sequential); - advance_pc_arm(); - } else { - opcodes[0] = bus->read_halfword(pc, CpuAccess::NonSequential); - advance_pc_thumb(); - opcodes[1] = bus->read_halfword(pc, CpuAccess::Sequential); - advance_pc_thumb(); - } - next_access = CpuAccess::Sequential; + rst_bit(pc, 1); + pc += arm::INSTRUCTION_SIZE; }; +void +Cpu::advance_pc_thumb() { + rst_bit(pc, 0); + pc += thumb::INSTRUCTION_SIZE; +} + +void +Cpu::flush_pipeline() {}; } diff --git a/src/cpu/thumb/disassembler.cc b/src/cpu/thumb/disassembler.cc index f180fc4..272cd11 100644 --- a/src/cpu/thumb/disassembler.cc +++ b/src/cpu/thumb/disassembler.cc @@ -147,7 +147,7 @@ Instruction::disassemble() { [](LongBranchWithLink& data) { // duh this manual be empty for H = 0 return std::format( - "BL{} #{:d}", (data.high ? "H" : ""), data.offset); + "BL{} #{:d}", (data.low ? "" : "H"), data.offset); }, [](auto) { return std::string("unknown instruction"); } }, data); diff --git a/src/cpu/thumb/exec.cc b/src/cpu/thumb/exec.cc index 4763d7b..2578265 100644 --- a/src/cpu/thumb/exec.cc +++ b/src/cpu/thumb/exec.cc @@ -1,21 +1,27 @@ +#include "bus.hh" #include "cpu/alu.hh" #include "cpu/cpu.hh" #include "util/bits.hh" #include "util/log.hh" -namespace matar::thumb { +namespace matar { void -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); - cpu.cpsr.set_n(n); - cpu.cpsr.set_z(z); +Cpu::exec(thumb::Instruction& instruction) { + bool is_flushed = false; + dbg(pc); + + auto set_cc = [this](bool c, bool v, bool n, bool z) { + cpsr.set_c(c); + cpsr.set_v(v); + cpsr.set_n(n); + cpsr.set_z(z); }; + using namespace thumb; + std::visit( overloaded{ - [&cpu, set_cc](MoveShiftedRegister& data) { + [this, set_cc](MoveShiftedRegister& data) { /* S -> prefetched instruction in step() @@ -24,16 +30,16 @@ Instruction::exec(Cpu& cpu) { if (data.opcode == ShiftType::ROR) glogger.error("Invalid opcode in {}", typeid(data).name()); - bool carry = cpu.cpsr.c(); + bool carry = cpsr.c(); uint32_t shifted = - eval_shift(data.opcode, cpu.gpr[data.rs], data.offset, carry); + eval_shift(data.opcode, gpr[data.rs], data.offset, carry); - cpu.gpr[data.rd] = shifted; + gpr[data.rd] = shifted; - set_cc(carry, cpu.cpsr.v(), get_bit(shifted, 31), shifted == 0); + set_cc(carry, cpsr.v(), get_bit(shifted, 31), shifted == 0); }, - [&cpu, set_cc](AddSubtract& data) { + [this, set_cc](AddSubtract& data) { /* S -> prefetched instruction in step() @@ -41,32 +47,32 @@ Instruction::exec(Cpu& cpu) { */ uint32_t offset = data.imm ? static_cast(static_cast(data.offset)) - : cpu.gpr[data.offset]; + : gpr[data.offset]; uint32_t result = 0; - bool carry = cpu.cpsr.c(); - bool overflow = cpu.cpsr.v(); + bool carry = cpsr.c(); + bool overflow = cpsr.v(); switch (data.opcode) { case AddSubtract::OpCode::ADD: - result = add(cpu.gpr[data.rs], offset, carry, overflow); + result = add(gpr[data.rs], offset, carry, overflow); break; case AddSubtract::OpCode::SUB: - result = sub(cpu.gpr[data.rs], offset, carry, overflow); + result = sub(gpr[data.rs], offset, carry, overflow); break; } - cpu.gpr[data.rd] = result; + gpr[data.rd] = result; set_cc(carry, overflow, get_bit(result, 31), result == 0); }, - [&cpu, set_cc](MovCmpAddSubImmediate& data) { + [this, set_cc](MovCmpAddSubImmediate& data) { /* S -> prefetched instruction in step() Total = S cycle */ uint32_t result = 0; - bool carry = cpu.cpsr.c(); - bool overflow = cpu.cpsr.v(); + bool carry = cpsr.c(); + bool overflow = cpsr.v(); switch (data.opcode) { case MovCmpAddSubImmediate::OpCode::MOV: @@ -74,21 +80,19 @@ Instruction::exec(Cpu& cpu) { carry = 0; break; case MovCmpAddSubImmediate::OpCode::ADD: - result = - add(cpu.gpr[data.rd], data.offset, carry, overflow); + result = add(gpr[data.rd], data.offset, carry, overflow); break; case MovCmpAddSubImmediate::OpCode::SUB: case MovCmpAddSubImmediate::OpCode::CMP: - result = - sub(cpu.gpr[data.rd], data.offset, carry, overflow); + result = sub(gpr[data.rd], data.offset, carry, overflow); break; } set_cc(carry, overflow, get_bit(result, 31), result == 0); if (data.opcode != MovCmpAddSubImmediate::OpCode::CMP) - cpu.gpr[data.rd] = result; + gpr[data.rd] = result; }, - [&cpu, set_cc](AluOperations& data) { + [this, set_cc](AluOperations& data) { /* Data Processing =============== @@ -108,12 +112,12 @@ Instruction::exec(Cpu& cpu) { Total = S + mI cycles */ - uint32_t op_1 = cpu.gpr[data.rd]; - uint32_t op_2 = cpu.gpr[data.rs]; + uint32_t op_1 = gpr[data.rd]; + uint32_t op_2 = gpr[data.rs]; uint32_t result = 0; - bool carry = cpu.cpsr.c(); - bool overflow = cpu.cpsr.v(); + bool carry = cpsr.c(); + bool overflow = cpsr.v(); switch (data.opcode) { case AluOperations::OpCode::AND: @@ -125,15 +129,15 @@ Instruction::exec(Cpu& cpu) { break; case AluOperations::OpCode::LSL: result = eval_shift(ShiftType::LSL, op_1, op_2, carry); - cpu.internal_cycle(); + internal_cycle(); break; case AluOperations::OpCode::LSR: result = eval_shift(ShiftType::LSR, op_1, op_2, carry); - cpu.internal_cycle(); + internal_cycle(); break; case AluOperations::OpCode::ASR: result = eval_shift(ShiftType::ASR, op_1, op_2, carry); - cpu.internal_cycle(); + internal_cycle(); break; case AluOperations::OpCode::ADC: result = add(op_1, op_2, carry, overflow, carry); @@ -143,7 +147,7 @@ Instruction::exec(Cpu& cpu) { break; case AluOperations::OpCode::ROR: result = eval_shift(ShiftType::ROR, op_1, op_2, carry); - cpu.internal_cycle(); + internal_cycle(); break; case AluOperations::OpCode::NEG: result = -op_2; @@ -161,7 +165,7 @@ Instruction::exec(Cpu& cpu) { result = op_1 * op_2; // mI cycles for (int i = 0; i < multiplier_array_cycles(op_2); i++) - cpu.internal_cycle(); + internal_cycle(); break; case AluOperations::OpCode::BIC: result = op_1 & ~op_2; @@ -174,11 +178,11 @@ Instruction::exec(Cpu& cpu) { if (data.opcode != AluOperations::OpCode::TST && data.opcode != AluOperations::OpCode::CMP && data.opcode != AluOperations::OpCode::CMN) - cpu.gpr[data.rd] = result; + gpr[data.rd] = result; set_cc(carry, overflow, get_bit(result, 31), result == 0); }, - [&cpu, set_cc](HiRegisterOperations& data) { + [this, set_cc, &is_flushed](HiRegisterOperations& data) { /* Always ====== @@ -193,79 +197,79 @@ Instruction::exec(Cpu& cpu) { Total = S or 2S + N cycles */ - uint32_t op_1 = cpu.gpr[data.rd]; - uint32_t op_2 = cpu.gpr[data.rs]; + uint32_t op_1 = gpr[data.rd]; + uint32_t op_2 = gpr[data.rs]; - bool carry = cpu.cpsr.c(); - bool overflow = cpu.cpsr.v(); + bool carry = cpsr.c(); + bool overflow = cpsr.v(); // PC is already current + 4, so dont need to do that - if (data.rd == cpu.PC_INDEX) + if (data.rd == PC_INDEX) rst_bit(op_1, 0); - if (data.rs == cpu.PC_INDEX) + if (data.rs == PC_INDEX) rst_bit(op_2, 0); switch (data.opcode) { case HiRegisterOperations::OpCode::ADD: { - cpu.gpr[data.rd] = add(op_1, op_2, carry, overflow); + gpr[data.rd] = add(op_1, op_2, carry, overflow); - if (data.rd == cpu.PC_INDEX) - cpu.is_flushed = true; + if (data.rd == PC_INDEX) + is_flushed = true; } break; case HiRegisterOperations::OpCode::CMP: { uint32_t result = sub(op_1, op_2, carry, overflow); set_cc(carry, overflow, get_bit(result, 31), result == 0); } break; case HiRegisterOperations::OpCode::MOV: { - cpu.gpr[data.rd] = op_2; + gpr[data.rd] = op_2; - if (data.rd == cpu.PC_INDEX) - cpu.is_flushed = true; + if (data.rd == PC_INDEX) + is_flushed = true; } break; case HiRegisterOperations::OpCode::BX: { State state = static_cast(get_bit(op_2, 0)); - if (state != cpu.cpsr.state()) + if (state != cpsr.state()) glogger.info_bold("State changed"); // set state - cpu.cpsr.set_state(state); + cpsr.set_state(state); // copy to PC - cpu.pc = op_2; + pc = op_2; // ignore [1:0] bits for arm and 0 bit for thumb - rst_bit(cpu.pc, 0); + rst_bit(pc, 0); if (state == State::Arm) - rst_bit(cpu.pc, 1); + rst_bit(pc, 1); // pc is affected so flush the pipeline - cpu.is_flushed = true; + is_flushed = true; } break; } }, - [&cpu](PcRelativeLoad& data) { + [this](PcRelativeLoad& data) { /* S -> reading instruction in step() N -> read from target I -> stored in register Total = S + N + I cycles */ - uint32_t pc = cpu.pc; - rst_bit(pc, 0); - rst_bit(pc, 1); + uint32_t pc_ = pc; + rst_bit(pc_, 0); + rst_bit(pc_, 1); - cpu.gpr[data.rd] = - cpu.bus->read_word(pc + data.word, CpuAccess::NonSequential); + gpr[data.rd] = + bus->read_word(pc_ + data.word, CpuAccess::NonSequential); - cpu.internal_cycle(); + internal_cycle(); // last read is unrelated - cpu.next_access = CpuAccess::NonSequential; + next_access = CpuAccess::NonSequential; }, - [&cpu](LoadStoreRegisterOffset& data) { + [this](LoadStoreRegisterOffset& data) { /* Load ==== @@ -281,63 +285,60 @@ Instruction::exec(Cpu& cpu) { Total = 2N */ - uint32_t address = cpu.gpr[data.rb] + cpu.gpr[data.ro]; + uint32_t address = gpr[data.rb] + gpr[data.ro]; if (data.load) { if (data.byte) { - cpu.gpr[data.rd] = - cpu.bus->read_byte(address, CpuAccess::NonSequential); + gpr[data.rd] = + bus->read_byte(address, CpuAccess::NonSequential); } else { - cpu.gpr[data.rd] = - cpu.bus->read_word(address, CpuAccess::NonSequential); + gpr[data.rd] = + bus->read_word(address, CpuAccess::NonSequential); } - cpu.internal_cycle(); + internal_cycle(); } else { if (data.byte) { - cpu.bus->write_byte(address, - cpu.gpr[data.rd] & 0xFF, - CpuAccess::NonSequential); + bus->write_byte( + address, gpr[data.rd] & 0xFF, CpuAccess::NonSequential); } else { - cpu.bus->write_word( - address, cpu.gpr[data.rd], CpuAccess::NonSequential); + bus->write_word( + address, gpr[data.rd], CpuAccess::NonSequential); } } // last read/write is unrelated - cpu.next_access = CpuAccess::NonSequential; + next_access = CpuAccess::NonSequential; }, - [&cpu](LoadStoreSignExtendedHalfword& data) { + [this](LoadStoreSignExtendedHalfword& data) { // Same cycles as above - uint32_t address = cpu.gpr[data.rb] + cpu.gpr[data.ro]; + uint32_t address = gpr[data.rb] + gpr[data.ro]; switch (data.s << 1 | data.h) { case 0b00: - cpu.bus->write_halfword(address, - cpu.gpr[data.rd] & 0xFFFF, - CpuAccess::NonSequential); + bus->write_halfword( + address, gpr[data.rd] & 0xFFFF, CpuAccess::NonSequential); break; case 0b01: - cpu.gpr[data.rd] = - cpu.bus->read_halfword(address, CpuAccess::NonSequential); - cpu.internal_cycle(); + gpr[data.rd] = + bus->read_halfword(address, CpuAccess::NonSequential); + internal_cycle(); break; case 0b10: // sign extend and load the byte - cpu.gpr[data.rd] = (static_cast(cpu.bus->read_byte( - address, CpuAccess::NonSequential)) - << 24) >> - 24; - cpu.internal_cycle(); + gpr[data.rd] = (static_cast(bus->read_byte( + address, CpuAccess::NonSequential)) + << 24) >> + 24; + internal_cycle(); break; case 0b11: // sign extend the halfword - cpu.gpr[data.rd] = - (static_cast(cpu.bus->read_halfword( - address, CpuAccess::NonSequential)) - << 16) >> - 16; - cpu.internal_cycle(); + gpr[data.rd] = (static_cast(bus->read_halfword( + address, CpuAccess::NonSequential)) + << 16) >> + 16; + internal_cycle(); break; // unreachable @@ -346,87 +347,85 @@ Instruction::exec(Cpu& cpu) { } // last read/write is unrelated - cpu.next_access = CpuAccess::NonSequential; + next_access = CpuAccess::NonSequential; }, - [&cpu](LoadStoreImmediateOffset& data) { + [this](LoadStoreImmediateOffset& data) { // Same cycles as above - uint32_t address = cpu.gpr[data.rb] + data.offset; + uint32_t address = gpr[data.rb] + data.offset; + dbg(address); if (data.load) { if (data.byte) { - cpu.gpr[data.rd] = - cpu.bus->read_byte(address, CpuAccess::NonSequential); + gpr[data.rd] = + bus->read_byte(address, CpuAccess::NonSequential); } else { - cpu.gpr[data.rd] = - cpu.bus->read_word(address, CpuAccess::NonSequential); + gpr[data.rd] = + bus->read_word(address, CpuAccess::NonSequential); } - cpu.internal_cycle(); + internal_cycle(); } else { if (data.byte) { - cpu.bus->write_byte(address, - cpu.gpr[data.rd] & 0xFF, - CpuAccess::NonSequential); + bus->write_byte( + address, gpr[data.rd] & 0xFF, CpuAccess::NonSequential); } else { - cpu.bus->write_word( - address, cpu.gpr[data.rd], CpuAccess::NonSequential); + bus->write_word( + address, gpr[data.rd], CpuAccess::NonSequential); } } // last read/write is unrelated - cpu.next_access = CpuAccess::NonSequential; + next_access = CpuAccess::NonSequential; }, - [&cpu](LoadStoreHalfword& data) { + [this](LoadStoreHalfword& data) { // Same cycles as above - uint32_t address = cpu.gpr[data.rb] + data.offset; + uint32_t address = gpr[data.rb] + data.offset; if (data.load) { - cpu.gpr[data.rd] = - cpu.bus->read_halfword(address, CpuAccess::NonSequential); - cpu.internal_cycle(); + gpr[data.rd] = + bus->read_halfword(address, CpuAccess::NonSequential); + internal_cycle(); } else { - cpu.bus->write_halfword( - address, cpu.gpr[data.rd] & 0xFFFF, CpuAccess::NonSequential); + bus->write_halfword( + address, gpr[data.rd] & 0xFFFF, CpuAccess::NonSequential); } // last read/write is unrelated - cpu.next_access = CpuAccess::NonSequential; + next_access = CpuAccess::NonSequential; }, - [&cpu](SpRelativeLoad& data) { + [this](SpRelativeLoad& data) { // Same cycles as above - uint32_t address = cpu.sp + data.word; + uint32_t address = sp + data.word; if (data.load) { - cpu.gpr[data.rd] = - cpu.bus->read_word(address, CpuAccess::Sequential); - cpu.internal_cycle(); + gpr[data.rd] = bus->read_word(address, CpuAccess::Sequential); + internal_cycle(); } else { - cpu.bus->write_word( - address, cpu.gpr[data.rd], CpuAccess::Sequential); + bus->write_word(address, gpr[data.rd], CpuAccess::Sequential); } // last read/write is unrelated - cpu.next_access = CpuAccess::NonSequential; + next_access = CpuAccess::NonSequential; }, - [&cpu](LoadAddress& data) { + [this](LoadAddress& data) { // 1S cycle in step() if (data.sp) { - cpu.gpr[data.rd] = cpu.sp + data.word; + gpr[data.rd] = sp + data.word; } else { // PC is already current + 4, so dont need to do that // force bit 1 to 0 - cpu.gpr[data.rd] = (cpu.pc & ~(1 << 1)) + data.word; + gpr[data.rd] = (pc & ~(1 << 1)) + data.word; } }, - [&cpu](AddOffsetStackPointer& data) { + [this](AddOffsetStackPointer& data) { // 1S cycle in step() - cpu.sp += data.word; + sp += data.word; }, - [&cpu](PushPopRegister& data) { + [this, &is_flushed](PushPopRegister& data) { /* Load ==== @@ -435,14 +434,16 @@ Instruction::exec(Cpu& cpu) { (n-1) S -> next n - 1 related reads from target I -> stored in register N+S -> if PC is written - taken care of by flush_pipeline() - Total = nS + N + I or (n+1)S + 2N + I + S -> if PC, memory read for PC write + Total = nS + N + I or (n+2)S + 2N + I Store ===== N -> calculating memory address - N -> unrelated write at target + N -> if LR, memory read for PC write + N/S -> unrelated write at target (n-1) S -> next n - 1 related writes - Total = 2N + (n-1)S + Total = 2N + nS or 2N + (n-1)S */ static constexpr uint8_t alignment = 4; CpuAccess access = CpuAccess::NonSequential; @@ -450,40 +451,40 @@ Instruction::exec(Cpu& cpu) { if (data.load) { for (uint8_t i = 0; i < 8; i++) { if (get_bit(data.regs, i)) { - cpu.gpr[i] = cpu.bus->read_word(cpu.sp, access); - cpu.sp += alignment; + gpr[i] = bus->read_word(sp, access); + sp += alignment; access = CpuAccess::Sequential; } } if (data.pclr) { - cpu.pc = cpu.bus->read_word(cpu.sp, access); - cpu.sp += alignment; - cpu.is_flushed = true; + pc = bus->read_word(sp, access); + sp += alignment; + is_flushed = true; } // I - cpu.internal_cycle(); + internal_cycle(); } else { if (data.pclr) { - cpu.sp -= alignment; - cpu.bus->write_word(cpu.sp, cpu.lr, access); + sp -= alignment; + bus->write_word(sp, lr, access); access = CpuAccess::Sequential; } for (int8_t i = 7; i >= 0; i--) { if (get_bit(data.regs, i)) { - cpu.sp -= alignment; - cpu.bus->write_word(cpu.sp, cpu.gpr[i], access); + sp -= alignment; + bus->write_word(sp, gpr[i], access); access = CpuAccess::Sequential; } } } // last read/write is unrelated - cpu.next_access = CpuAccess::NonSequential; + next_access = CpuAccess::NonSequential; }, - [&cpu](MultipleLoad& data) { + [this](MultipleLoad& data) { /* Load ==== @@ -503,33 +504,34 @@ Instruction::exec(Cpu& cpu) { static constexpr uint8_t alignment = 4; - uint32_t rb = cpu.gpr[data.rb]; + uint32_t rb = gpr[data.rb]; CpuAccess access = CpuAccess::NonSequential; if (data.load) { for (uint8_t i = 0; i < 8; i++) { if (get_bit(data.regs, i)) { - cpu.gpr[i] = cpu.bus->read_word(rb, access); + gpr[i] = bus->read_word(rb, access); rb += alignment; access = CpuAccess::Sequential; } } + internal_cycle(); } else { for (uint8_t i = 0; i < 8; i++) { if (get_bit(data.regs, i)) { - cpu.bus->write_word(rb, cpu.gpr[i], access); + bus->write_word(rb, gpr[i], access); rb += alignment; access = CpuAccess::Sequential; } } } - cpu.gpr[data.rb] = rb; + gpr[data.rb] = rb; // last read/write is unrelated - cpu.next_access = CpuAccess::NonSequential; + next_access = CpuAccess::NonSequential; }, - [&cpu](ConditionalBranch& data) { + [this, &is_flushed](ConditionalBranch& data) { /* S -> reading instruction in step() N+S -> if condition is true, branch and refill pipeline @@ -539,13 +541,13 @@ Instruction::exec(Cpu& cpu) { if (data.condition == Condition::AL) glogger.warn("Condition 1110 (AL) is undefined"); - if (!cpu.cpsr.condition(data.condition)) + if (!cpsr.condition(data.condition)) return; - cpu.pc += data.offset; - cpu.is_flushed = true; + pc += data.offset; + is_flushed = true; }, - [&cpu](SoftwareInterrupt& data) { + [this, &is_flushed](SoftwareInterrupt& data) { /* S -> reading instruction in step() N+S -> refill pipeline @@ -553,24 +555,24 @@ Instruction::exec(Cpu& cpu) { */ // next instruction is one instruction behind PC - cpu.lr = cpu.pc - INSTRUCTION_SIZE; - cpu.spsr = cpu.cpsr; - cpu.pc = data.vector; - cpu.cpsr.set_state(State::Arm); - cpu.chg_mode(Mode::Supervisor); - cpu.is_flushed = true; + lr = pc - INSTRUCTION_SIZE; + spsr = cpsr; + pc = data.vector; + cpsr.set_state(State::Arm); + chg_mode(Mode::Supervisor); + is_flushed = true; }, - [&cpu](UnconditionalBranch& data) { + [this, &is_flushed](UnconditionalBranch& data) { /* S -> reading instruction in step() N+S -> branch and refill pipeline Total = 2S + N */ - cpu.pc += data.offset; - cpu.is_flushed = true; + pc += data.offset; + is_flushed = true; }, - [&cpu](LongBranchWithLink& data) { + [this, &is_flushed](LongBranchWithLink& data) { /* S -> prefetched instruction in step() N -> fetch from the new address in branch @@ -582,23 +584,33 @@ Instruction::exec(Cpu& cpu) { // 12 bit integer int32_t offset = data.offset; - if (data.high) { - uint32_t old_pc = cpu.pc; + if (data.low) { + uint32_t old_pc = pc; + offset <<= 1; - cpu.pc = cpu.lr + offset; - cpu.lr = (old_pc - INSTRUCTION_SIZE) | 1; - cpu.is_flushed = true; + pc = lr + offset; + lr = (old_pc - INSTRUCTION_SIZE) | 1; + is_flushed = true; } else { // 12 + 11 = 23 bit - offset <<= 11; + offset <<= 12; // sign extend offset = (offset << 9) >> 9; - cpu.lr = cpu.pc + offset; + lr = pc + offset; } }, [](auto& data) { glogger.error("Unknown thumb format : {}", typeid(data).name()); } }, - data); + instruction.data); + + if (is_flushed) { + opcodes[0] = bus->read_halfword(pc, CpuAccess::NonSequential); + advance_pc_thumb(); + opcodes[1] = bus->read_halfword(pc, CpuAccess::Sequential); + advance_pc_thumb(); + next_access = CpuAccess::Sequential; + } else + advance_pc_thumb(); } } diff --git a/src/cpu/thumb/instruction.cc b/src/cpu/thumb/instruction.cc index 2fd6900..c9e29a4 100644 --- a/src/cpu/thumb/instruction.cc +++ b/src/cpu/thumb/instruction.cc @@ -203,11 +203,9 @@ Instruction::Instruction(uint16_t insn) { // Format 19: Long branch with link } else if ((insn & 0xF000) == 0xF000) { uint16_t offset = bit_range(insn, 0, 10); - bool high = get_bit(insn, 11); + bool low = get_bit(insn, 11); - offset <<= 1; - - data = LongBranchWithLink{ .offset = offset, .high = high }; + data = LongBranchWithLink{ .offset = offset, .low = low }; } } } diff --git a/tests/cpu/arm/exec.cc b/tests/cpu/arm/exec.cc index a8dd4ff..bce51bd 100644 --- a/tests/cpu/arm/exec.cc +++ b/tests/cpu/arm/exec.cc @@ -15,9 +15,16 @@ TEST_CASE_METHOD(CpuFixture, "Branch and Exchange", TAG) { setr(3, 342800); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); - CHECK(getr(15) == 342800); + INFO(getr(15)); + INFO(getr(15)); + INFO(getr(15)); + INFO(getr(15)); + // +8 cuz pipeline flush + CHECK(getr(15) == 342808); } TEST_CASE_METHOD(CpuFixture, "Branch", TAG) { @@ -27,10 +34,13 @@ TEST_CASE_METHOD(CpuFixture, "Branch", TAG) { // set PC to 48 setr(15, 48); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // 48 + offset - CHECK(getr(15) == 3489796); + // +8 cuz pipeline flush + CHECK(getr(15) == 3489804); CHECK(getr(14) == 0); // with link @@ -40,7 +50,8 @@ TEST_CASE_METHOD(CpuFixture, "Branch", TAG) { exec(data); // 48 + offset - CHECK(getr(15) == 3489796); + // +8 cuz pipeline flush + CHECK(getr(15) == 3489804); // pc was set to 48 CHECK(getr(14) == 48 - INSTRUCTION_SIZE); } @@ -53,11 +64,13 @@ TEST_CASE_METHOD(CpuFixture, "Multiply", TAG) { setr(10, 234912349); setr(11, 124897); - setr(3, 99999); + setr(3, 99999); // m = 3 since [32:24] bits are 0 { uint32_t result = 234912349ull * 124897ull & 0xFFFFFFFF; + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 4); // S + mI CHECK(getr(5) == result); } @@ -66,7 +79,9 @@ TEST_CASE_METHOD(CpuFixture, "Multiply", TAG) { { uint32_t result = (234912349ull * 124897ull + 99999ull) & 0xFFFFFFFF; multiply->acc = true; + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 5); // S + mI + I CHECK(getr(5) == result); } @@ -105,12 +120,14 @@ TEST_CASE_METHOD(CpuFixture, "Multiply Long", TAG) { MultiplyLong* multiply_long = std::get_if(&data); setr(10, 234912349); - setr(11, 124897); + setr(11, 124897); // m = 3 // unsigned { uint64_t result = 234912349ull * 124897ull; + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 5); // S + (m+1)I CHECK(getr(3) == bit_range(result, 0, 31)); CHECK(getr(5) == bit_range(result, 32, 63)); @@ -121,7 +138,9 @@ TEST_CASE_METHOD(CpuFixture, "Multiply Long", TAG) { int64_t result = 234912349ll * -124897ll; setr(11, getr(11) * -1); multiply_long->uns = false; + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 5); // S + (m+1)I CHECK(getr(3) == static_cast(bit_range(result, 0, 31))); CHECK(getr(5) == static_cast(bit_range(result, 32, 63))); @@ -136,7 +155,9 @@ TEST_CASE_METHOD(CpuFixture, "Multiply Long", TAG) { 234912349ll * -124897ll + (99999ll | -444333391ll << 32); multiply_long->acc = true; + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 6); // S + (m+2)I CHECK(getr(3) == static_cast(bit_range(result, 0, 31))); CHECK(getr(5) == static_cast(bit_range(result, 32, 63))); @@ -185,7 +206,9 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Swap", TAG) { bus->write_word(getr(9), 3241011111); SECTION("word") { + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 4); // S + 2N + I CHECK(getr(4) == 3241011111); CHECK(bus->read_word(getr(9)) == static_cast(-259039045)); @@ -227,7 +250,9 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { { // 0x31E + 0x3000004 bus->write_word(0x30031E4, 95995); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + N + I CHECK(getr(5) == 95995); setr(5, 0); @@ -305,7 +330,9 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { { data_transfer->load = false; + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 2); // 2N for store CHECK(bus->read_word(0x30042CB) == 61119); // 0x30042CB - 0xDA1 @@ -315,13 +342,15 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { // r15 as rn { data_transfer->rn = 15; - setr(15, 0x300352A); + setr(15, 0x300352C); // word aligned exec(data); - CHECK(bus->read_word(0x300352A) == 61119); - // 0x300352A - 0xDA1 - CHECK(getr(15) == 0x3002789); + CHECK(bus->read_word(0x300352C) == 61119); + // 0x300352C - 0xDA1 + // +4 cuz PC advanced + // and then word aligned + CHECK(getr(15) == 0x300278C); // cleanup data_transfer->rn = 7; @@ -334,13 +363,12 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { exec(data); - CHECK(bus->read_word(0x300352A + INSTRUCTION_SIZE) == 444444); + CHECK(bus->read_word(0x300352A) == 444444 + 4); // 0x300352A - 0xDA1 - CHECK(getr(7) == 0x3002789 + INSTRUCTION_SIZE); + CHECK(getr(7) == 0x3002789); // cleanup data_transfer->rd = 5; - setr(7, getr(7) - INSTRUCTION_SIZE); } // byte @@ -355,6 +383,29 @@ TEST_CASE_METHOD(CpuFixture, "Single Data Transfer", TAG) { // 0x3002789 - 0xDA1 CHECK(getr(7) == 0x30019E8); } + + // r15 as rd with load + { + data_transfer->rd = 15; + data_transfer->load = true; + setr(15, 0); + bus->write_byte(0x30019E8, 0xE2); + + uint32_t cycles = bus->get_cycles(); + exec(data); + CHECK(bus->get_cycles() == + cycles + 5); // 2S + 2N + I for load with rd=15 + + // +8 cuz pipeline flushed then word aligned + // so +6 + CHECK(getr(15) == 0xE8); + + // 0x30019E8 - 0xDA1 + CHECK(getr(7) == 0x3000C47); + + // cleanup + data_transfer->rd = 5; + } } TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) { @@ -378,7 +429,10 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) { { // 0x300611E + 0x384 bus->write_word(0x30064A2, 3948123487); + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + N + I CHECK(getr(11) == (3948123487 & 0xFFFF)); } @@ -436,7 +490,9 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) { { hw_transfer->load = false; + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 2); // 2N CHECK(bus->read_halfword(0x3005FD0) == (6111909 & 0xFFFF)); // 0x3005FD0 - 0xA7 @@ -446,14 +502,15 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) { // r15 as rn { hw_transfer->rn = 15; - setr(15, 0x3005F29); + setr(15, 0x3005F28); // word aligned exec(data); - CHECK(bus->read_halfword(0x3005F29 - 2 * INSTRUCTION_SIZE) == - (6111909 & 0xFFFF)); - // 0x3005F29 - 0xA7 - CHECK(getr(15) == 0x3005E82 - 2 * INSTRUCTION_SIZE); + CHECK(bus->read_halfword(0x3005F28) == (6111909 & 0xFFFF)); + // 0x3005F28 - 0xA7 + // +4 cuz PC advanced + // and then word aligned + CHECK(getr(15) == 0x3005E84); // cleanup hw_transfer->rn = 10; @@ -466,13 +523,12 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) { exec(data); - CHECK(bus->read_halfword(0x3005F29 + INSTRUCTION_SIZE) == 224); + CHECK(bus->read_halfword(0x3005F29) == 224 + 4); // 0x3005F29 - 0xA7 - CHECK(getr(10) == 0x3005E82 + INSTRUCTION_SIZE); + CHECK(getr(10) == 0x3005E82); // cleanup hw_transfer->rd = 11; - setr(10, getr(10) - INSTRUCTION_SIZE); } // signed halfword @@ -499,6 +555,28 @@ TEST_CASE_METHOD(CpuFixture, "Halfword Transfer", TAG) { // 0x3005DDB - 0xA7 CHECK(getr(10) == 0x3005D34); } + + // r15 as rd with load + { + hw_transfer->rd = 15; + hw_transfer->load = true; + setr(15, 0); + bus->write_byte(0x3005D34, 56); + + uint32_t cycles = bus->get_cycles(); + exec(data); + CHECK(bus->get_cycles() == + cycles + 5); // 2S + 2N + I for load with rd=15 + + // +8 cuz pipeline flushed then word aligned + CHECK(getr(15) == static_cast(56 + 8)); + + // 0x3005D34 - 0xA7 + CHECK(getr(10) == 0x3005C8D); + + // cleanup + hw_transfer->rd = 11; + } } TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { @@ -542,7 +620,10 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { CHECK(getr(12) == 0); CHECK(getr(13) == 989231); CHECK(getr(14) == 0); - CHECK(getr(15) == 6); + + // setting r15 as 6, flushes the pipeline causing it to go 6 + 8 + // i.e, 14. word aligning this, gives us 12 + CHECK(getr(15) == 12); for (uint8_t i = 0; i < 16; i++) { setr(i, 0); @@ -550,7 +631,9 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { }; setr(10, address); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 11); // (n+1)S + 2N + I checker(address); // with write @@ -610,23 +693,30 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { setr(8, 131313333); setr(11, 131); setr(13, 989231); - setr(15, 6); + setr(15, 4); // word align - auto checker = [this]() { + // we will count the number of steps to count PC advances + uint8_t steps = 0; + + auto checker = [this, &steps]() { CHECK(bus->read_word(address + alignment) == 237164); CHECK(bus->read_word(address + alignment * 2) == 679785111); CHECK(bus->read_word(address + alignment * 3) == 905895898); CHECK(bus->read_word(address + alignment * 4) == 131313333); CHECK(bus->read_word(address + alignment * 5) == 131); CHECK(bus->read_word(address + alignment * 6) == 989231); - CHECK(bus->read_word(address + alignment * 7) == 6); + CHECK(bus->read_word(address + alignment * 7) == + 4 + (4 * (steps - 1))); for (uint8_t i = 1; i < 8; i++) bus->write_word(address + alignment * i, 0); }; setr(10, address); // base + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 8); // 2N + (n-1)S + steps++; checker(); // decrement @@ -635,6 +725,7 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { // adjust rn setr(10, address + alignment * 8); exec(data); + steps++; checker(); // post increment @@ -643,6 +734,7 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { // adjust rn setr(10, address + alignment); exec(data); + steps++; checker(); // post decrement @@ -650,12 +742,14 @@ TEST_CASE_METHOD(CpuFixture, "Block Data Transfer", TAG) { // adjust rn setr(10, address + alignment * 7); exec(data); + steps++; checker(); // with s bit cpu.chg_mode(Mode::Fiq); block_transfer->s = true; exec(data); + steps++; // User's R13 is different (unset at this point) CHECK(bus->read_word(address + alignment * 6) == 0); } @@ -674,7 +768,9 @@ TEST_CASE_METHOD(CpuFixture, "PSR Transfer", TAG) { setr(12, 12389398); CHECK(psr().raw() != getr(12)); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S CHECK(psr().raw() == getr(12)); psr_transfer->spsr = true; @@ -691,7 +787,9 @@ TEST_CASE_METHOD(CpuFixture, "PSR Transfer", TAG) { setr(12, 16556u << 8); CHECK(psr().raw() != getr(12)); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S CHECK(psr().raw() == getr(12)); psr_transfer->spsr = true; @@ -708,7 +806,9 @@ TEST_CASE_METHOD(CpuFixture, "PSR Transfer", TAG) { setr(12, 1490352945); // go to the reserved bits + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S CHECK(psr().n() == get_bit(1490352945, 31)); CHECK(psr().z() == get_bit(1490352945, 30)); CHECK(psr().c() == get_bit(1490352945, 29)); @@ -719,6 +819,7 @@ TEST_CASE_METHOD(CpuFixture, "PSR Transfer", TAG) { psr_transfer->imm = true; psr_transfer->spsr = true; exec(data); + CHECK(psr().n() == get_bit(1490352945, 31)); CHECK(psr(true).n() == get_bit(9933394, 31)); CHECK(psr(true).z() == get_bit(9933394, 30)); CHECK(psr(true).c() == get_bit(9933394, 29)); @@ -750,7 +851,9 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { { // rm setr(3, 1596); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S // -28717 & 12768 CHECK(getr(5) == 448); } @@ -767,7 +870,11 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { setr(3, 1596); // rs setr(12, 2); + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 2); // 1S + 1I + // -28717 & 6384 CHECK(getr(5) == 2256); } @@ -1063,10 +1170,12 @@ TEST_CASE_METHOD(CpuFixture, "Data Processing", TAG) { processing->rd = 15; setr(15, 0); CHECK(psr(true).raw() != psr().raw()); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // 2S + N - // ~54924809 - CHECK(getr(15) == static_cast(-54924810)); + // ~54924809 + 8 (for flush) and then word adjust + CHECK(getr(15) == static_cast(-54924804)); // flags are not set flags(false, false, false, false); diff --git a/tests/cpu/cpu-fixture.cc b/tests/cpu/cpu-fixture.cc index 6e3663b..963d23f 100644 --- a/tests/cpu/cpu-fixture.cc +++ b/tests/cpu/cpu-fixture.cc @@ -2,6 +2,7 @@ Psr CpuFixture::psr(bool spsr) { + uint32_t pc = getr(15); Psr psr(0); Cpu tmp = cpu; arm::Instruction instruction( @@ -11,17 +12,19 @@ CpuFixture::psr(bool spsr) { .type = arm::PsrTransfer::Type::Mrs, .imm = false }); - instruction.exec(tmp); + tmp.exec(instruction); psr.set_all(getr_(0, tmp)); + + // reset pc + setr(15, pc); return psr; } void CpuFixture::set_psr(Psr psr, bool spsr) { - // R0 + uint32_t pc = getr(15); uint32_t old = getr(0); - setr(0, psr.raw()); arm::Instruction instruction( @@ -31,22 +34,23 @@ CpuFixture::set_psr(Psr psr, bool spsr) { .type = arm::PsrTransfer::Type::Msr, .imm = false }); - instruction.exec(cpu); + cpu.exec(instruction); setr(0, old); + + // reset PC + setr(15, pc); } // We need these workarounds to just use the public API and not private // 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, Cpu& cpu) { - uint32_t addr = 0x02000000; - uint8_t offset = r == 15 ? 4 : 0; - uint32_t word = bus->read_word(addr + offset); - Cpu tmp = cpu; - uint32_t ret = 0xFFFFFFFF; - uint8_t base = r ? 0 : 1; +CpuFixture::getr_(uint8_t r, Cpu tmp) { + uint32_t addr = 0x02000000; + uint32_t word = bus->read_word(addr); + uint32_t ret = 0xFFFFFFFF; + uint8_t base = r ? 0 : 1; // set R0/R1 = addr arm::Instruction zero( @@ -69,16 +73,14 @@ CpuFixture::getr_(uint8_t r, Cpu& cpu) { .up = true, .pre = true }); - zero.exec(tmp); - get.exec(tmp); - - addr += offset; + tmp.exec(zero); + tmp.exec(get); ret = bus->read_word(addr); bus->write_word(addr, word); - return ret; + return ret - (r == 15 ? 4 : 0); // +4 for rd = 15 in str } void @@ -86,11 +88,12 @@ CpuFixture::setr_(uint8_t r, uint32_t value, Cpu& cpu) { // set register arm::Instruction set( Condition::AL, - arm::DataProcessing{ .operand = value, - .rd = r, - .rn = 0, - .set = false, - .opcode = arm::DataProcessing::OpCode::MOV }); + arm::DataProcessing{ + .operand = (r == 15 ? value - 8 : value), // account for pipeline flush + .rd = r, + .rn = 0, + .set = false, + .opcode = arm::DataProcessing::OpCode::MOV }); - set.exec(cpu); + cpu.exec(set); } diff --git a/tests/cpu/cpu-fixture.hh b/tests/cpu/cpu-fixture.hh index 2350375..35f0f6e 100644 --- a/tests/cpu/cpu-fixture.hh +++ b/tests/cpu/cpu-fixture.hh @@ -11,20 +11,49 @@ class CpuFixture { protected: void exec(arm::InstructionData data, Condition condition = Condition::AL) { + // hack to account for one fetch cycle + bus->internal_cycle(); + arm::Instruction instruction(condition, data); - instruction.exec(cpu); + cpu.exec(instruction); } void exec(thumb::InstructionData data) { + // hack to account for one fetch cycle + bus->internal_cycle(); + thumb::Instruction instruction(data); - instruction.exec(cpu); + cpu.exec(instruction); } void reset(uint32_t value = 0) { setr(15, value + 8); } - uint32_t getr(uint8_t r) { return getr_(r, cpu); } + uint32_t getr(uint8_t r) { + uint32_t pc = 0; - void setr(uint8_t r, uint32_t value) { setr_(r, value, cpu); } + if (r != 15) + pc = getr_(15, cpu); + + uint32_t ret = getr_(r, cpu); + + if (r == 15) + pc = ret; + + // undo PC advance + setr_(15, pc, cpu); + + return ret; + } + + void setr(uint8_t r, uint32_t value) { + uint32_t pc = getr_(15, cpu); + setr_(r, value, cpu); + + // undo PC advance when r != 15 + // when r is 15, setr_ takes account of pipeline flush + if (r != 15) + setr_(15, pc, cpu); + } Psr psr(bool spsr = false); @@ -35,7 +64,7 @@ class CpuFixture { private: // hack to get a register - uint32_t getr_(uint8_t r, Cpu& cpu); + uint32_t getr_(uint8_t r, Cpu tmp); // hack to set a register 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 aa056a4..2701c7e 100644 --- a/tests/cpu/thumb/exec.cc +++ b/tests/cpu/thumb/exec.cc @@ -18,7 +18,9 @@ TEST_CASE_METHOD(CpuFixture, "Move Shifted Register", TAG) { setr(3, 0); setr(5, 6687); // LSL + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S CHECK(getr(3) == 219119616); setr(5, 0); @@ -32,7 +34,11 @@ TEST_CASE_METHOD(CpuFixture, "Move Shifted Register", TAG) { move->opcode = ShiftType::LSR; setr(5, -1827489745); // LSR + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S + CHECK(getr(3) == 75301); CHECK(!psr().n()); @@ -47,7 +53,11 @@ TEST_CASE_METHOD(CpuFixture, "Move Shifted Register", TAG) { setr(5, -1827489745); move->opcode = ShiftType::ASR; // ASR + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S + CHECK(psr().n()); CHECK(getr(3) == 4294911525); @@ -71,7 +81,10 @@ TEST_CASE_METHOD(CpuFixture, "Add/Subtract", TAG) { SECTION("ADD") { // register + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S + CHECK(getr(5) == 377761225); add->imm = true; @@ -94,7 +107,11 @@ TEST_CASE_METHOD(CpuFixture, "Add/Subtract", TAG) { add->opcode = AddSubtract::OpCode::SUB; setr(2, -((1u << 31) - 1)); add->offset = 4; + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S + CHECK(getr(5) == 2147483645); CHECK(psr().v()); @@ -122,7 +139,10 @@ TEST_CASE_METHOD(CpuFixture, "Move/Compare/Add/Subtract Immediate", TAG) { MovCmpAddSubImmediate* move = std::get_if(&data); SECTION("MOV") { + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S + CHECK(getr(5) == 251); move->offset = 0; @@ -136,7 +156,11 @@ TEST_CASE_METHOD(CpuFixture, "Move/Compare/Add/Subtract Immediate", TAG) { setr(5, 251); move->opcode = MovCmpAddSubImmediate::OpCode::CMP; CHECK(!psr().z()); + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S + CHECK(getr(5) == 251); CHECK(psr().z()); @@ -152,7 +176,11 @@ TEST_CASE_METHOD(CpuFixture, "Move/Compare/Add/Subtract Immediate", TAG) { move->opcode = MovCmpAddSubImmediate::OpCode::ADD; setr(5, (1u << 31) - 1); // immediate and overflow + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S + CHECK(getr(5) == 2147483898); CHECK(psr().v()); @@ -168,7 +196,11 @@ TEST_CASE_METHOD(CpuFixture, "Move/Compare/Add/Subtract Immediate", TAG) { setr(5, 251); move->opcode = MovCmpAddSubImmediate::OpCode::SUB; CHECK(!psr().z()); + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S + CHECK(getr(5) == 0); CHECK(psr().z()); @@ -190,8 +222,11 @@ TEST_CASE_METHOD(CpuFixture, "ALU Operations", TAG) { setr(3, -991); SECTION("AND") { + uint32_t cycles = bus->get_cycles(); // 328940001 & -991 exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S + CHECK(getr(1) == 328939553); CHECK(!psr().n()); @@ -221,8 +256,12 @@ TEST_CASE_METHOD(CpuFixture, "ALU Operations", TAG) { SECTION("LSL") { setr(3, 3); alu->opcode = AluOperations::OpCode::LSL; + + uint32_t cycles = bus->get_cycles(); // 328940001 << 3 exec(data); + CHECK(bus->get_cycles() == cycles + 2); // 1S + 1I (shift) + CHECK(getr(1) == 2631520008); CHECK(psr().n()); @@ -410,8 +449,12 @@ TEST_CASE_METHOD(CpuFixture, "ALU Operations", TAG) { SECTION("MUL") { alu->opcode = AluOperations::OpCode::MUL; + + uint32_t cycles = bus->get_cycles(); // 328940001 * -991 (lower 32 bits) (-325979540991 & 0xFFFFFFFF) exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + mI (m = 2 for -991) + CHECK(getr(1) == 437973505); setr(3, 0); @@ -462,19 +505,22 @@ TEST_CASE_METHOD(CpuFixture, "Hi Register Operations/Branch Exchange", TAG) { }; HiRegisterOperations* hi = std::get_if(&data); - setr(15, 3452948950); + setr(15, 3452948948); setr(5, 958656720); SECTION("ADD") { + uint32_t cycles = bus->get_cycles(); exec(data); - CHECK(getr(5) == 116638374); + CHECK(bus->get_cycles() == cycles + 1); // 1S + + CHECK(getr(5) == 116638372); // hi + hi hi->rd = 14; hi->rs = 15; setr(14, 42589); exec(data); - CHECK(getr(14) == 3452991539); + CHECK(getr(14) == 3452991537); } SECTION("CMP") { @@ -500,7 +546,7 @@ TEST_CASE_METHOD(CpuFixture, "Hi Register Operations/Branch Exchange", TAG) { hi->opcode = HiRegisterOperations::OpCode::MOV; exec(data); - CHECK(getr(5) == 3452948950); + CHECK(getr(5) == 3452948948); } SECTION("BX") { @@ -509,8 +555,13 @@ TEST_CASE_METHOD(CpuFixture, "Hi Register Operations/Branch Exchange", TAG) { SECTION("Arm") { setr(10, 2189988); + + uint32_t cycles = bus->get_cycles(); exec(data); - CHECK(getr(15) == 2189988); + CHECK(bus->get_cycles() == cycles + 3); // 2S + N cycles + + // +4 for pipeline flush + CHECK(getr(15) == 2189988 + 4); // switched to arm CHECK(psr().state() == State::Arm); } @@ -518,7 +569,9 @@ TEST_CASE_METHOD(CpuFixture, "Hi Register Operations/Branch Exchange", TAG) { SECTION("Thumb") { setr(10, 2189989); exec(data); - CHECK(getr(15) == 2189988); + + // +4 for pipeline flush + CHECK(getr(15) == 2189988 + 4); // switched to thumb CHECK(psr().state() == State::Thumb); @@ -535,7 +588,11 @@ TEST_CASE_METHOD(CpuFixture, "PC Relative Load", TAG) { bus->write_word(0x300454C, 489753492); CHECK(getr(0) == 0); + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + N + I cycles + CHECK(getr(0) == 489753492); } @@ -552,7 +609,11 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store with Register Offset", TAG) { SECTION("store") { // 0x3003000 + 0x332 CHECK(bus->read_word(0x3003332) == 0); + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 2); // 2N cycles + CHECK(bus->read_word(0x3003332) == 389524259); // byte @@ -565,7 +626,11 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store with Register Offset", TAG) { SECTION("load") { load->load = true; bus->write_word(0x3003332, 11123489); + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + N + I cycles + CHECK(getr(3) == 11123489); // byte @@ -589,21 +654,27 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store Sign Extended Byte/Halfword", TAG) { SECTION("SH = 00") { // 0x3003000 + 0x332 CHECK(bus->read_word(0x3003332) == 0); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 2); // 2N cycles CHECK(bus->read_word(0x3003332) == 43811); } SECTION("SH = 01") { load->h = true; bus->write_word(0x3003332, 11123489); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + N + I cycles CHECK(getr(3) == 47905); } SECTION("SH = 10") { load->s = true; bus->write_word(0x3003332, 34521594); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + N + I cycles // sign extended 250 byte (0xFA) CHECK(getr(3) == 4294967290); } @@ -613,7 +684,9 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store Sign Extended Byte/Halfword", TAG) { load->h = true; bus->write_word(0x3003332, 11123489); // sign extended 47905 halfword (0xBB21) + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + N + I cycles CHECK(getr(3) == 4294949665); } } @@ -631,7 +704,9 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store with Immediate Offset", TAG) { SECTION("store") { // 0x30066A + 0x6E CHECK(bus->read_word(0x30066D8) == 0); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 2); // 2N cycles CHECK(bus->read_word(0x30066D8) == 389524259); // byte @@ -644,7 +719,9 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store with Immediate Offset", TAG) { SECTION("load") { load->load = true; bus->write_word(0x30066D8, 11123489); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + N + I cycles CHECK(getr(3) == 11123489); // byte @@ -665,14 +742,18 @@ TEST_CASE_METHOD(CpuFixture, "Load/Store Halfword", TAG) { SECTION("store") { // 0x300666A + 0x6E CHECK(bus->read_word(0x30066D8) == 0); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 2); // 2N cycles CHECK(bus->read_word(0x30066D8) == 43811); } SECTION("load") { load->load = true; bus->write_word(0x30066D8, 11123489); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + N + I cycles CHECK(getr(3) == 47905); } } @@ -689,14 +770,18 @@ TEST_CASE_METHOD(CpuFixture, "SP Relative Load", TAG) { SECTION("store") { // 0x3004A8A + 0x328 CHECK(bus->read_word(0x3004DB2) == 0); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 2); // 2N cycles CHECK(bus->read_word(0x3004DB2) == 2349505744); } SECTION("load") { load->load = true; bus->write_word(0x3004DB2, 11123489); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // S + N + I cycles CHECK(getr(1) == 11123489); } } @@ -711,8 +796,11 @@ TEST_CASE_METHOD(CpuFixture, "Load Address", TAG) { setr(13, 69879977); SECTION("PC") { + uint32_t cycles = bus->get_cycles(); exec(data); - CHECK(getr(1) == 337293); + CHECK(bus->get_cycles() == cycles + 1); // 1S + // word align 337293 + CHECK(getr(1) == 337292); } SECTION("SP") { @@ -730,7 +818,9 @@ TEST_CASE_METHOD(CpuFixture, "Add Offset to Stack Pointer", TAG) { setr(13, 69879977); SECTION("positive") { + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 1); // 1S CHECK(getr(13) == 69880450); } @@ -772,7 +862,9 @@ TEST_CASE_METHOD(CpuFixture, "Push/Pop Registers", TAG) { setr(13, address + alignment * 5); SECTION("without LR") { + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 6); // 2N + (n-1)S, n = 5 checker(); CHECK(getr(13) == address); } @@ -783,7 +875,10 @@ TEST_CASE_METHOD(CpuFixture, "Push/Pop Registers", TAG) { setr(14, 999304); // add another word on stack (top + 4) setr(13, address + alignment * 6); + + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 7); // 2N + nS, n = 5 CHECK(bus->read_word(address + alignment * 5) == 999304); checker(); @@ -819,19 +914,25 @@ TEST_CASE_METHOD(CpuFixture, "Push/Pop Registers", TAG) { // set stack pointer to bottom of stack setr(13, address); - SECTION("without SP") { + SECTION("without PC") { + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 7); // nS + N + I, n = 5 checker(); CHECK(getr(13) == address + alignment * 5); } - SECTION("with SP") { + SECTION("with PC") { push->pclr = true; // populate next address bus->write_word(address + alignment * 5, 93333912); - exec(data); - CHECK(getr(15) == 93333912); + uint32_t cycles = bus->get_cycles(); + exec(data); + CHECK(bus->get_cycles() == cycles + 10); //(n+2)S + 2N + I, n = 5 + + // +4 for flushed pipeline + CHECK(getr(15) == 93333912 + 4); checker(); CHECK(getr(13) == address + alignment * 6); } @@ -858,7 +959,9 @@ TEST_CASE_METHOD(CpuFixture, "Multiple Load/Store", TAG) { // base setr(2, address); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 6); //(n-1)S + 2N, n = 5 CHECK(bus->read_word(address) == 237164); CHECK(bus->read_word(address + alignment) == address); @@ -883,7 +986,10 @@ TEST_CASE_METHOD(CpuFixture, "Multiple Load/Store", TAG) { // base setr(2, address); + uint32_t cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 7); // nS + N + 1, n = 5 + CHECK(getr(0) == 237164); CHECK(getr(1) == 0); CHECK(getr(2) == address + alignment * 5); // write back @@ -900,72 +1006,93 @@ TEST_CASE_METHOD(CpuFixture, "Conditional Branch", TAG) { ConditionalBranch{ .offset = -192, .condition = Condition::EQ }; ConditionalBranch* branch = std::get_if(&data); + Psr cpsr = psr(); + cpsr.set_state(State::Thumb); + setr(15, 4589344); SECTION("z") { - Psr cpsr = psr(); + uint32_t cycles = bus->get_cycles(); // condition is false exec(data); - CHECK(getr(15) == 4589344); + CHECK(bus->get_cycles() == cycles + 1); // 1S + + // +2 for pc advance + CHECK(getr(15) == 4589344 + 2); cpsr.set_z(true); set_psr(cpsr); + cycles = bus->get_cycles(); // condition is true exec(data); - CHECK(getr(15) == 4589152); + CHECK(bus->get_cycles() == cycles + 3); // 2S + N + // +4 for pipeline flush + CHECK(getr(15) == 4589156); } SECTION("c") { branch->condition = Condition::CS; - Psr cpsr = psr(); // condition is false exec(data); - CHECK(getr(15) == 4589344); + + // +2 for pc advance + CHECK(getr(15) == 4589346); cpsr.set_c(true); set_psr(cpsr); // condition is true exec(data); - CHECK(getr(15) == 4589152); + // +4 for pipeline flush + CHECK(getr(15) == 4589156); } SECTION("n") { branch->condition = Condition::MI; - Psr cpsr = psr(); // condition is false exec(data); - CHECK(getr(15) == 4589344); + + // +2 for pc advance + CHECK(getr(15) == 4589346); cpsr.set_n(true); set_psr(cpsr); // condition is true exec(data); - CHECK(getr(15) == 4589152); + // +4 for pipeline flush + CHECK(getr(15) == 4589156); } SECTION("v") { branch->condition = Condition::VS; - Psr cpsr = psr(); // condition is false exec(data); - CHECK(getr(15) == 4589344); + + // +2 for pc advance + CHECK(getr(15) == 4589346); cpsr.set_v(true); set_psr(cpsr); // condition is true exec(data); - CHECK(getr(15) == 4589152); + // +4 for pipeline flush + CHECK(getr(15) == 4589156); } } TEST_CASE_METHOD(CpuFixture, "Software Interrupt", TAG) { - InstructionData data = SoftwareInterrupt{ .vector = 33 }; + InstructionData data = SoftwareInterrupt{ .vector = 32 }; setr(15, 4492); + + uint32_t cycles = bus->get_cycles(); + // condition is true exec(data); + CHECK(bus->get_cycles() == cycles + 3); // 2S + N + CHECK(psr().raw() == psr(true).raw()); CHECK(getr(14) == 4490); - CHECK(getr(15) == 33); + // +4 for flushed pipeline + CHECK(getr(15) == 36); CHECK(psr().state() == State::Arm); CHECK(psr().mode() == Mode::Supervisor); } @@ -974,23 +1101,39 @@ TEST_CASE_METHOD(CpuFixture, "Unconditional Branch", TAG) { InstructionData data = UnconditionalBranch{ .offset = -920 }; setr(15, 4589344); + + uint32_t cycles = bus->get_cycles(); exec(data); - CHECK(getr(15) == 4588424); + CHECK(bus->get_cycles() == cycles + 3); // 2S + N + + // +4 for flushed pipeline + CHECK(getr(15) == 4588428); } TEST_CASE_METHOD(CpuFixture, "Long Branch With Link", TAG) { - InstructionData data = LongBranchWithLink{ .offset = 3262, .high = false }; + InstructionData data = + LongBranchWithLink{ .offset = 0b10010111110, .low = false }; LongBranchWithLink* branch = std::get_if(&data); // high setr(15, 4589344); + uint32_t cycles = bus->get_cycles(); exec(data); - CHECK(getr(14) == 2881312); + CHECK(bus->get_cycles() == cycles + 1); // 1S + + CHECK(getr(14) == 1173280); // low - branch->high = true; + branch->low = true; + + cycles = bus->get_cycles(); exec(data); + CHECK(bus->get_cycles() == cycles + 3); // 2S + N + + // +2 for advancing thumb, then -2 to get the next instruciton of current + // executing instruction, then set bit 0 CHECK(getr(14) == 4589343); - CHECK(getr(15) == 2884574); + // 1175712 + 4 for flushed pipeline + CHECK(getr(15) == 1175712); } diff --git a/tests/cpu/thumb/instruction.cc b/tests/cpu/thumb/instruction.cc index a97182a..09f28eb 100644 --- a/tests/cpu/thumb/instruction.cc +++ b/tests/cpu/thumb/instruction.cc @@ -447,20 +447,20 @@ TEST_CASE("Unconditional Branch") { } TEST_CASE("Long Branch with link") { - uint16_t raw = 0b1111010011101100; + uint16_t raw = 0b1111110011101100; Instruction instruction(raw); LongBranchWithLink* bl = nullptr; REQUIRE((bl = std::get_if(&instruction.data))); // 1260 << 1 - CHECK(bl->offset == 2520); - CHECK(bl->high == false); + CHECK(bl->offset == 1260); + CHECK(bl->low == true); #ifdef DISASSEMBLER - CHECK(instruction.disassemble() == "BL #2520"); + CHECK(instruction.disassemble() == "BL #1260"); - bl->high = true; - CHECK(instruction.disassemble() == "BLH #2520"); + bl->low = false; + CHECK(instruction.disassemble() == "BLH #1260"); #endif }