cpu: get rid of the test workaround

now can we remove the pimpl?

Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
2023-09-27 22:40:44 +05:30
parent 03ebc6378a
commit 0f09874929
10 changed files with 551 additions and 420 deletions

View File

@@ -2,23 +2,20 @@
#include "util/bits.hh" #include "util/bits.hh"
#include "util/log.hh" #include "util/log.hh"
namespace matar { namespace matar::arm {
void void
CpuImpl::exec(const arm::Instruction instruction) { Instruction::exec(CpuImpl& cpu) {
Condition cond = instruction.condition; if (!cpu.cpsr.condition(condition)) {
arm::InstructionData data = instruction.data;
if (!cpsr.condition(cond)) {
return; return;
} }
auto pc_error = [](uint8_t r) { auto pc_error = [cpu](uint8_t r) {
if (r == PC_INDEX) if (r == cpu.PC_INDEX)
glogger.error("Using PC (R15) as operand register"); glogger.error("Using PC (R15) as operand register");
}; };
auto pc_warn = [](uint8_t r) { auto pc_warn = [cpu](uint8_t r) {
if (r == PC_INDEX) if (r == cpu.PC_INDEX)
glogger.warn("Using PC (R15) as operand register"); glogger.warn("Using PC (R15) as operand register");
}; };
@@ -26,38 +23,39 @@ CpuImpl::exec(const arm::Instruction instruction) {
std::visit( std::visit(
overloaded{ overloaded{
[this, pc_warn](BranchAndExchange& data) { [&cpu, pc_warn](BranchAndExchange& data) {
State state = static_cast<State>(data.rn & 1); State state = static_cast<State>(data.rn & 1);
pc_warn(data.rn); pc_warn(data.rn);
// set state // set state
cpsr.set_state(state); cpu.cpsr.set_state(state);
// copy to PC // copy to PC
pc = gpr[data.rn]; cpu.pc = cpu.gpr[data.rn];
// ignore [1:0] bits for arm and 0 bit for thumb // ignore [1:0] bits for arm and 0 bit for thumb
rst_bit(pc, 0); rst_bit(cpu.pc, 0);
if (state == State::Arm) if (state == State::Arm)
rst_bit(pc, 1); rst_bit(cpu.pc, 1);
// pc is affected so flush the pipeline // pc is affected so flush the pipeline
is_flushed = true; cpu.is_flushed = true;
}, },
[this](Branch& data) { [&cpu](Branch& data) {
if (data.link) if (data.link)
gpr[14] = pc - INSTRUCTION_SIZE; cpu.gpr[14] = cpu.pc - INSTRUCTION_SIZE;
// data.offset accounts for two instructions ahead when // data.offset accounts for two instructions ahead when
// disassembling, so need to adjust // disassembling, so need to adjust
pc = static_cast<int32_t>(pc) - 2 * INSTRUCTION_SIZE + data.offset; cpu.pc =
static_cast<int32_t>(cpu.pc) - 2 * INSTRUCTION_SIZE + data.offset;
// pc is affected so flush the pipeline // pc is affected so flush the pipeline
is_flushed = true; cpu.is_flushed = true;
}, },
[this, pc_error](Multiply& data) { [&cpu, pc_error](Multiply& data) {
if (data.rd == data.rm) if (data.rd == data.rm)
glogger.error("rd and rm are not distinct in {}", glogger.error("rd and rm are not distinct in {}",
typeid(data).name()); typeid(data).name());
@@ -66,16 +64,16 @@ CpuImpl::exec(const arm::Instruction instruction) {
pc_error(data.rd); pc_error(data.rd);
pc_error(data.rd); pc_error(data.rd);
gpr[data.rd] = cpu.gpr[data.rd] = cpu.gpr[data.rm] * cpu.gpr[data.rs] +
gpr[data.rm] * gpr[data.rs] + (data.acc ? gpr[data.rn] : 0); (data.acc ? cpu.gpr[data.rn] : 0);
if (data.set) { if (data.set) {
cpsr.set_z(gpr[data.rd] == 0); cpu.cpsr.set_z(cpu.gpr[data.rd] == 0);
cpsr.set_n(get_bit(gpr[data.rd], 31)); cpu.cpsr.set_n(get_bit(cpu.gpr[data.rd], 31));
cpsr.set_c(0); cpu.cpsr.set_c(0);
} }
}, },
[this, pc_error](MultiplyLong& data) { [&cpu, pc_error](MultiplyLong& data) {
if (data.rdhi == data.rdlo || data.rdhi == data.rm || if (data.rdhi == data.rdlo || data.rdhi == data.rm ||
data.rdlo == data.rm) data.rdlo == data.rm)
glogger.error("rdhi, rdlo and rm are not distinct in {}", glogger.error("rdhi, rdlo and rm are not distinct in {}",
@@ -91,58 +89,60 @@ CpuImpl::exec(const arm::Instruction instruction) {
return static_cast<uint64_t>(x); return static_cast<uint64_t>(x);
}; };
uint64_t eval = cast(gpr[data.rm]) * cast(gpr[data.rs]) + uint64_t eval =
(data.acc ? (cast(gpr[data.rdhi]) << 32) | cast(cpu.gpr[data.rm]) * cast(cpu.gpr[data.rs]) +
cast(gpr[data.rdlo]) (data.acc ? (cast(cpu.gpr[data.rdhi]) << 32) |
: 0); cast(cpu.gpr[data.rdlo])
: 0);
gpr[data.rdlo] = bit_range(eval, 0, 31); cpu.gpr[data.rdlo] = bit_range(eval, 0, 31);
gpr[data.rdhi] = bit_range(eval, 32, 63); cpu.gpr[data.rdhi] = bit_range(eval, 32, 63);
} else { } else {
auto cast = [](uint32_t x) -> int64_t { auto cast = [](uint32_t x) -> int64_t {
return static_cast<int64_t>(static_cast<int32_t>(x)); return static_cast<int64_t>(static_cast<int32_t>(x));
}; };
int64_t eval = cast(gpr[data.rm]) * cast(gpr[data.rs]) + int64_t eval = cast(cpu.gpr[data.rm]) * cast(cpu.gpr[data.rs]) +
(data.acc ? (cast(gpr[data.rdhi]) << 32) | (data.acc ? (cast(cpu.gpr[data.rdhi]) << 32) |
cast(gpr[data.rdlo]) cast(cpu.gpr[data.rdlo])
: 0); : 0);
gpr[data.rdlo] = bit_range(eval, 0, 31); cpu.gpr[data.rdlo] = bit_range(eval, 0, 31);
gpr[data.rdhi] = bit_range(eval, 32, 63); cpu.gpr[data.rdhi] = bit_range(eval, 32, 63);
} }
if (data.set) { if (data.set) {
cpsr.set_z(gpr[data.rdhi] == 0 && gpr[data.rdlo] == 0); cpu.cpsr.set_z(cpu.gpr[data.rdhi] == 0 &&
cpsr.set_n(get_bit(gpr[data.rdhi], 31)); cpu.gpr[data.rdlo] == 0);
cpsr.set_c(0); cpu.cpsr.set_n(get_bit(cpu.gpr[data.rdhi], 31));
cpsr.set_v(0); cpu.cpsr.set_c(0);
cpu.cpsr.set_v(0);
} }
}, },
[](Undefined) { glogger.warn("Undefined instruction"); }, [](Undefined) { glogger.warn("Undefined instruction"); },
[this, pc_error](SingleDataSwap& data) { [&cpu, pc_error](SingleDataSwap& data) {
pc_error(data.rm); pc_error(data.rm);
pc_error(data.rn); pc_error(data.rn);
pc_error(data.rd); pc_error(data.rd);
if (data.byte) { if (data.byte) {
gpr[data.rd] = bus->read_byte(gpr[data.rn]); cpu.gpr[data.rd] = cpu.bus->read_byte(cpu.gpr[data.rn]);
bus->write_byte(gpr[data.rn], gpr[data.rm] & 0xFF); cpu.bus->write_byte(cpu.gpr[data.rn], cpu.gpr[data.rm] & 0xFF);
} else { } else {
gpr[data.rd] = bus->read_word(gpr[data.rn]); cpu.gpr[data.rd] = cpu.bus->read_word(cpu.gpr[data.rn]);
bus->write_word(gpr[data.rn], gpr[data.rm]); cpu.bus->write_word(cpu.gpr[data.rn], cpu.gpr[data.rm]);
} }
}, },
[this, pc_warn, pc_error](SingleDataTransfer& data) { [&cpu, pc_warn, pc_error](SingleDataTransfer& data) {
uint32_t offset = 0; uint32_t offset = 0;
uint32_t address = gpr[data.rn]; uint32_t address = cpu.gpr[data.rn];
if (!data.pre && data.write) if (!data.pre && data.write)
glogger.warn("Write-back enabled with post-indexing in {}", glogger.warn("Write-back enabled with post-indexing in {}",
typeid(data).name()); typeid(data).name());
if (data.rn == PC_INDEX && data.write) if (data.rn == cpu.PC_INDEX && data.write)
glogger.warn("Write-back enabled with base register as PC {}", glogger.warn("Write-back enabled with base register as PC {}",
typeid(data).name()); typeid(data).name());
@@ -156,22 +156,22 @@ CpuImpl::exec(const arm::Instruction instruction) {
} else if (const Shift* shift = std::get_if<Shift>(&data.offset)) { } else if (const Shift* shift = std::get_if<Shift>(&data.offset)) {
uint8_t amount = uint8_t amount =
(shift->data.immediate ? shift->data.operand (shift->data.immediate ? shift->data.operand
: gpr[shift->data.operand] & 0xFF); : cpu.gpr[shift->data.operand] & 0xFF);
bool carry = cpsr.c(); bool carry = cpu.cpsr.c();
if (!shift->data.immediate) if (!shift->data.immediate)
pc_error(shift->data.operand); pc_error(shift->data.operand);
pc_error(shift->rm); pc_error(shift->rm);
offset = offset = eval_shift(
eval_shift(shift->data.type, gpr[shift->rm], amount, carry); shift->data.type, cpu.gpr[shift->rm], amount, carry);
cpsr.set_c(carry); cpu.cpsr.set_c(carry);
} }
// PC is always two instructions ahead // PC is always two instructions ahead
if (data.rn == PC_INDEX) if (data.rn == cpu.PC_INDEX)
address -= 2 * INSTRUCTION_SIZE; address -= 2 * INSTRUCTION_SIZE;
if (data.pre) if (data.pre)
@@ -181,35 +181,35 @@ CpuImpl::exec(const arm::Instruction instruction) {
if (data.load) { if (data.load) {
// byte // byte
if (data.byte) if (data.byte)
gpr[data.rd] = bus->read_byte(address); cpu.gpr[data.rd] = cpu.bus->read_byte(address);
// word // word
else else
gpr[data.rd] = bus->read_word(address); cpu.gpr[data.rd] = cpu.bus->read_word(address);
// store // store
} else { } else {
// take PC into consideration // take PC into consideration
if (data.rd == PC_INDEX) if (data.rd == cpu.PC_INDEX)
address += INSTRUCTION_SIZE; address += INSTRUCTION_SIZE;
// byte // byte
if (data.byte) if (data.byte)
bus->write_byte(address, gpr[data.rd] & 0xFF); cpu.bus->write_byte(address, cpu.gpr[data.rd] & 0xFF);
// word // word
else else
bus->write_word(address, gpr[data.rd]); cpu.bus->write_word(address, cpu.gpr[data.rd]);
} }
if (!data.pre) if (!data.pre)
address += (data.up ? offset : -offset); address += (data.up ? offset : -offset);
if (!data.pre || data.write) if (!data.pre || data.write)
gpr[data.rn] = address; cpu.gpr[data.rn] = address;
if (data.rd == PC_INDEX && data.load) if (data.rd == cpu.PC_INDEX && data.load)
is_flushed = true; cpu.is_flushed = true;
}, },
[this, pc_warn, pc_error](HalfwordTransfer& data) { [&cpu, pc_warn, pc_error](HalfwordTransfer& data) {
uint32_t address = gpr[data.rn]; uint32_t address = cpu.gpr[data.rn];
uint32_t offset = 0; uint32_t offset = 0;
if (!data.pre && data.write) if (!data.pre && data.write)
@@ -225,13 +225,13 @@ CpuImpl::exec(const arm::Instruction instruction) {
// offset is register number (4 bits) when not an immediate // offset is register number (4 bits) when not an immediate
if (!data.imm) { if (!data.imm) {
pc_error(data.offset); pc_error(data.offset);
offset = gpr[data.offset]; offset = cpu.gpr[data.offset];
} else { } else {
offset = data.offset; offset = data.offset;
} }
// PC is always two instructions ahead // PC is always two instructions ahead
if (data.rn == PC_INDEX) if (data.rn == cpu.PC_INDEX)
address -= 2 * INSTRUCTION_SIZE; address -= 2 * INSTRUCTION_SIZE;
if (data.pre) if (data.pre)
@@ -243,62 +243,62 @@ CpuImpl::exec(const arm::Instruction instruction) {
if (data.sign) { if (data.sign) {
// halfword // halfword
if (data.half) { if (data.half) {
gpr[data.rd] = bus->read_halfword(address); cpu.gpr[data.rd] = cpu.bus->read_halfword(address);
// sign extend the halfword // sign extend the halfword
gpr[data.rd] = cpu.gpr[data.rd] =
(static_cast<int32_t>(gpr[data.rd]) << 16) >> 16; (static_cast<int32_t>(cpu.gpr[data.rd]) << 16) >> 16;
// byte // byte
} else { } else {
gpr[data.rd] = bus->read_byte(address); cpu.gpr[data.rd] = cpu.bus->read_byte(address);
// sign extend the byte // sign extend the byte
gpr[data.rd] = cpu.gpr[data.rd] =
(static_cast<int32_t>(gpr[data.rd]) << 24) >> 24; (static_cast<int32_t>(cpu.gpr[data.rd]) << 24) >> 24;
} }
// unsigned halfword // unsigned halfword
} else if (data.half) { } else if (data.half) {
gpr[data.rd] = bus->read_halfword(address); cpu.gpr[data.rd] = cpu.bus->read_halfword(address);
} }
// store // store
} else { } else {
// take PC into consideration // take PC into consideration
if (data.rd == PC_INDEX) if (data.rd == cpu.PC_INDEX)
address += INSTRUCTION_SIZE; address += INSTRUCTION_SIZE;
// halfword // halfword
if (data.half) if (data.half)
bus->write_halfword(address, gpr[data.rd]); cpu.bus->write_halfword(address, cpu.gpr[data.rd]);
} }
if (!data.pre) if (!data.pre)
address += (data.up ? offset : -offset); address += (data.up ? offset : -offset);
if (!data.pre || data.write) if (!data.pre || data.write)
gpr[data.rn] = address; cpu.gpr[data.rn] = address;
if (data.rd == PC_INDEX && data.load) if (data.rd == cpu.PC_INDEX && data.load)
is_flushed = true; cpu.is_flushed = true;
}, },
[this, pc_error](BlockDataTransfer& data) { [&cpu, pc_error](BlockDataTransfer& data) {
uint32_t address = gpr[data.rn]; uint32_t address = cpu.gpr[data.rn];
Mode mode = cpsr.mode(); Mode mode = cpu.cpsr.mode();
uint8_t alignment = 4; // word uint8_t alignment = 4; // word
uint8_t i = 0; uint8_t i = 0;
uint8_t n_regs = std::popcount(data.regs); uint8_t n_regs = std::popcount(data.regs);
pc_error(data.rn); pc_error(data.rn);
if (cpsr.mode() == Mode::User && data.s) { if (cpu.cpsr.mode() == Mode::User && data.s) {
glogger.error("Bit S is set outside priviliged modes in {}", glogger.error("Bit S is set outside priviliged modes in {}",
typeid(data).name()); typeid(data).name());
} }
// we just change modes to load user registers // we just change modes to load user registers
if ((!get_bit(data.regs, PC_INDEX) && data.s) || if ((!get_bit(data.regs, cpu.PC_INDEX) && data.s) ||
(!data.load && data.s)) { (!data.load && data.s)) {
chg_mode(Mode::User); cpu.chg_mode(Mode::User);
if (data.write) { if (data.write) {
glogger.error( glogger.error(
@@ -315,22 +315,22 @@ CpuImpl::exec(const arm::Instruction instruction) {
address += (data.up ? alignment : -alignment); address += (data.up ? alignment : -alignment);
if (data.load) { if (data.load) {
if (get_bit(data.regs, PC_INDEX) && data.s && data.load) { if (get_bit(data.regs, cpu.PC_INDEX) && data.s && data.load) {
// current mode's spsr is already loaded when it was // current mode's cpu.spsr is already loaded when it was
// switched // switched
spsr = cpsr; cpu.spsr = cpu.cpsr;
} }
for (i = 0; i < GPR_COUNT; i++) { for (i = 0; i < cpu.GPR_COUNT; i++) {
if (get_bit(data.regs, i)) { if (get_bit(data.regs, i)) {
gpr[i] = bus->read_word(address); cpu.gpr[i] = cpu.bus->read_word(address);
address += alignment; address += alignment;
} }
} }
} else { } else {
for (i = 0; i < GPR_COUNT; i++) { for (i = 0; i < cpu.GPR_COUNT; i++) {
if (get_bit(data.regs, i)) { if (get_bit(data.regs, i)) {
bus->write_word(address, gpr[i]); cpu.bus->write_word(address, cpu.gpr[i]);
address += alignment; address += alignment;
} }
} }
@@ -346,37 +346,37 @@ CpuImpl::exec(const arm::Instruction instruction) {
address -= alignment; address -= alignment;
if (!data.pre || data.write) if (!data.pre || data.write)
gpr[data.rn] = address; cpu.gpr[data.rn] = address;
if (data.load && get_bit(data.regs, PC_INDEX)) if (data.load && get_bit(data.regs, cpu.PC_INDEX))
is_flushed = true; cpu.is_flushed = true;
// load back the original mode registers // load back the original mode registers
chg_mode(mode); cpu.chg_mode(mode);
}, },
[this, pc_error](PsrTransfer& data) { [&cpu, pc_error](PsrTransfer& data) {
if (data.spsr && cpsr.mode() == Mode::User) { if (data.spsr && cpu.cpsr.mode() == Mode::User) {
glogger.error("Accessing SPSR in User mode in {}", glogger.error("Accessing CPU.SPSR in User mode in {}",
typeid(data).name()); typeid(data).name());
} }
Psr& psr = data.spsr ? spsr : cpsr; Psr& psr = data.spsr ? cpu.spsr : cpu.cpsr;
switch (data.type) { switch (data.type) {
case PsrTransfer::Type::Mrs: case PsrTransfer::Type::Mrs:
pc_error(data.operand); pc_error(data.operand);
gpr[data.operand] = psr.raw(); cpu.gpr[data.operand] = psr.raw();
break; break;
case PsrTransfer::Type::Msr: case PsrTransfer::Type::Msr:
pc_error(data.operand); pc_error(data.operand);
if (cpsr.mode() != Mode::User) { if (cpu.cpsr.mode() != Mode::User) {
psr.set_all(gpr[data.operand]); psr.set_all(cpu.gpr[data.operand]);
} }
break; break;
case PsrTransfer::Type::Msr_flg: case PsrTransfer::Type::Msr_flg:
uint32_t operand = uint32_t operand =
(data.imm ? data.operand : gpr[data.operand]); (data.imm ? data.operand : cpu.gpr[data.operand]);
psr.set_n(get_bit(operand, 31)); psr.set_n(get_bit(operand, 31));
psr.set_z(get_bit(operand, 30)); psr.set_z(get_bit(operand, 30));
psr.set_c(get_bit(operand, 29)); psr.set_c(get_bit(operand, 29));
@@ -384,10 +384,10 @@ CpuImpl::exec(const arm::Instruction instruction) {
break; break;
} }
}, },
[this, pc_error](DataProcessing& data) { [&cpu, pc_error](DataProcessing& data) {
using OpCode = DataProcessing::OpCode; using OpCode = DataProcessing::OpCode;
uint32_t op_1 = gpr[data.rn]; uint32_t op_1 = cpu.gpr[data.rn];
uint32_t op_2 = 0; uint32_t op_2 = 0;
uint32_t result = 0; uint32_t result = 0;
@@ -398,26 +398,26 @@ CpuImpl::exec(const arm::Instruction instruction) {
} else if (const Shift* shift = std::get_if<Shift>(&data.operand)) { } else if (const Shift* shift = std::get_if<Shift>(&data.operand)) {
uint8_t amount = uint8_t amount =
(shift->data.immediate ? shift->data.operand (shift->data.immediate ? shift->data.operand
: gpr[shift->data.operand] & 0xFF); : cpu.gpr[shift->data.operand] & 0xFF);
bool carry = cpsr.c(); bool carry = cpu.cpsr.c();
if (!shift->data.immediate) if (!shift->data.immediate)
pc_error(shift->data.operand); pc_error(shift->data.operand);
pc_error(shift->rm); pc_error(shift->rm);
op_2 = op_2 = eval_shift(
eval_shift(shift->data.type, gpr[shift->rm], amount, carry); shift->data.type, cpu.gpr[shift->rm], amount, carry);
cpsr.set_c(carry); cpu.cpsr.set_c(carry);
// PC is 12 bytes ahead when shifting // PC is 12 bytes ahead when shifting
if (data.rn == PC_INDEX) if (data.rn == cpu.PC_INDEX)
op_1 += INSTRUCTION_SIZE; op_1 += INSTRUCTION_SIZE;
} }
bool overflow = cpsr.v(); bool overflow = cpu.cpsr.v();
bool carry = cpsr.c(); bool carry = cpu.cpsr.c();
auto sub = [&carry, &overflow](uint32_t a, uint32_t b) -> uint32_t { auto sub = [&carry, &overflow](uint32_t a, uint32_t b) -> uint32_t {
bool s1 = get_bit(a, 31); bool s1 = get_bit(a, 31);
@@ -501,19 +501,19 @@ CpuImpl::exec(const arm::Instruction instruction) {
break; break;
} }
auto set_conditions = [this, carry, overflow, result]() { auto set_conditions = [&cpu, carry, overflow, result]() {
cpsr.set_c(carry); cpu.cpsr.set_c(carry);
cpsr.set_v(overflow); cpu.cpsr.set_v(overflow);
cpsr.set_n(get_bit(result, 31)); cpu.cpsr.set_n(get_bit(result, 31));
cpsr.set_z(result == 0); cpu.cpsr.set_z(result == 0);
}; };
if (data.set) { if (data.set) {
if (data.rd == PC_INDEX) { if (data.rd == cpu.PC_INDEX) {
if (cpsr.mode() == Mode::User) if (cpu.cpsr.mode() == Mode::User)
glogger.error("Running {} in User mode", glogger.error("Running {} in User mode",
typeid(data).name()); typeid(data).name());
spsr = cpsr; cpu.spsr = cpu.cpsr;
} else { } else {
set_conditions(); set_conditions();
} }
@@ -523,15 +523,15 @@ CpuImpl::exec(const arm::Instruction instruction) {
data.opcode == OpCode::CMP || data.opcode == OpCode::CMN) { data.opcode == OpCode::CMP || data.opcode == OpCode::CMN) {
set_conditions(); set_conditions();
} else { } else {
gpr[data.rd] = result; cpu.gpr[data.rd] = result;
if (data.rd == PC_INDEX || data.opcode == OpCode::MVN) if (data.rd == cpu.PC_INDEX || data.opcode == OpCode::MVN)
is_flushed = true; cpu.is_flushed = true;
} }
}, },
[this](SoftwareInterrupt) { [&cpu](SoftwareInterrupt) {
chg_mode(Mode::Supervisor); cpu.chg_mode(Mode::Supervisor);
pc = 0x08; cpu.pc = 0x08;
spsr = cpsr; cpu.spsr = cpu.cpsr;
}, },
[](auto& data) { [](auto& data) {
glogger.error("Unimplemented {} instruction", typeid(data).name()); glogger.error("Unimplemented {} instruction", typeid(data).name());

View File

@@ -5,7 +5,10 @@
#include <fmt/ostream.h> #include <fmt/ostream.h>
#include <variant> #include <variant>
namespace matar::arm { namespace matar {
class CpuImpl;
namespace arm {
// https://en.cppreference.com/w/cpp/utility/variant/visit // https://en.cppreference.com/w/cpp/utility/variant/visit
template<class... Ts> template<class... Ts>
@@ -216,9 +219,11 @@ struct Instruction {
Instruction(Condition condition, InstructionData data) noexcept Instruction(Condition condition, InstructionData data) noexcept
: condition(condition) : condition(condition)
, data(data){}; , data(data){};
void exec(CpuImpl& cpu);
#ifdef DISASSEMBLER #ifdef DISASSEMBLER
std::string disassemble(); std::string disassemble();
#endif #endif
}; };
} }
}

View File

@@ -123,7 +123,7 @@ CpuImpl::step() {
uint32_t x = bus->read_word(cur_pc); uint32_t x = bus->read_word(cur_pc);
arm::Instruction instruction(x); arm::Instruction instruction(x);
exec(instruction); instruction.exec(*this);
#ifdef DISASSEMBLER #ifdef DISASSEMBLER
glogger.info("{:#034b}", x); glogger.info("{:#034b}", x);

View File

@@ -13,12 +13,9 @@ class CpuImpl {
void step(); void step();
void chg_mode(const Mode to); void chg_mode(const Mode to);
void exec(const arm::Instruction instruction);
// TODO: get rid of this
#ifndef MATAR_CPU_TESTS
private: private:
#endif friend void arm::Instruction::exec(CpuImpl& cpu);
static constexpr uint8_t GPR_COUNT = 16; static constexpr uint8_t GPR_COUNT = 16;

View File

@@ -13,7 +13,7 @@ Psr::raw() const {
void void
Psr::set_all(uint32_t raw) { Psr::set_all(uint32_t raw) {
psr = raw & ~PSR_CLEAR_RESERVED; psr = raw;
} }
Mode Mode
@@ -91,7 +91,7 @@ Psr::condition(Condition cond) const {
case Condition::LE: case Condition::LE:
return z() || (n() != v()); return z() || (n() != v());
case Condition::AL: case Condition::AL:
return true && state() == State::Arm; return true;
} }
return false; return false;

View File

@@ -61,7 +61,7 @@ stringify(Condition cond) {
CASE(GT) CASE(GT)
CASE(LE) CASE(LE)
case Condition::AL: { case Condition::AL: {
// empty return "";
} }
} }

File diff suppressed because it is too large Load Diff

96
tests/cpu/arm/fixture.cc Normal file
View File

@@ -0,0 +1,96 @@
#include "fixture.hh"
Psr
CpuFixture::psr(bool spsr) {
Psr psr(0);
CpuImpl tmp = cpu;
arm::Instruction instruction(
Condition::AL,
arm::PsrTransfer{ .operand = 0,
.spsr = spsr,
.type = arm::PsrTransfer::Type::Mrs,
.imm = false });
instruction.exec(tmp);
psr.set_all(getr_(0, tmp));
return psr;
}
void
CpuFixture::set_psr(Psr psr, bool spsr) {
// R0
uint32_t old = getr(0);
setr(0, psr.raw());
arm::Instruction instruction(
Condition::AL,
arm::PsrTransfer{ .operand = 0,
.spsr = spsr,
.type = arm::PsrTransfer::Type::Msr,
.imm = false });
instruction.exec(cpu);
setr(0, old);
}
// We need these workarounds to just use the public API and not private
// fields. Assuming that these work correctly is necessary. Besides, all it
// matters is that the public API is correct.
uint32_t
CpuFixture::getr_(uint8_t r, CpuImpl& cpu) {
size_t addr = 13000;
size_t offset = r == 15 ? 4 : 0;
uint32_t word = bus.read_word(addr + offset);
CpuImpl tmp = cpu;
uint32_t ret = 0xFFFFFFFF;
uint8_t base = r ? 0 : 1;
// set R0/R1 = 0
arm::Instruction zero(
Condition::AL,
arm::DataProcessing{ .operand = 0u,
.rd = base,
.rn = 0,
.set = false,
.opcode = arm::DataProcessing::OpCode::MOV });
// get register
arm::Instruction get(
Condition::AL,
arm::SingleDataTransfer{ .offset = static_cast<uint16_t>(addr),
.rd = r,
.rn = base,
.load = false,
.write = false,
.byte = false,
.up = true,
.pre = true });
zero.exec(tmp);
get.exec(tmp);
addr += offset;
ret = bus.read_word(addr);
bus.write_word(addr, word);
return ret;
}
void
CpuFixture::setr_(uint8_t r, uint32_t value, CpuImpl& cpu) {
// set register
arm::Instruction set(
Condition::AL,
arm::DataProcessing{ .operand = value,
.rd = r,
.rn = 0,
.set = false,
.opcode = arm::DataProcessing::OpCode::MOV });
set.exec(cpu);
}

37
tests/cpu/arm/fixture.hh Normal file
View File

@@ -0,0 +1,37 @@
#include "cpu/cpu-impl.hh"
using namespace matar;
class CpuFixture {
public:
CpuFixture()
: bus(Memory(std::array<uint8_t, Memory::BIOS_SIZE>(),
std::vector<uint8_t>(Header::HEADER_SIZE)))
, cpu(bus) {}
protected:
void exec(arm::InstructionData data, Condition condition = Condition::AL) {
arm::Instruction instruction(condition, data);
instruction.exec(cpu);
}
void reset(uint32_t value = 0) { setr(15, value + 8); }
uint32_t getr(uint8_t r) { return getr_(r, cpu); }
void setr(uint8_t r, uint32_t value) { setr_(r, value, cpu); }
Psr psr(bool spsr = false);
void set_psr(Psr psr, bool spsr = false);
Bus bus;
CpuImpl cpu;
private:
// hack to get a register
uint32_t getr_(uint8_t r, CpuImpl& cpu);
// hack to set a register
void setr_(uint8_t r, uint32_t value, CpuImpl& cpu);
};

View File

@@ -1,4 +1,5 @@
tests_sources += files( tests_sources += files(
'fixture.cc',
'instruction.cc', 'instruction.cc',
'exec.cc' 'exec.cc'
) )