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:
@@ -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());
|
||||||
|
@@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
@@ -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
96
tests/cpu/arm/fixture.cc
Normal 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
37
tests/cpu/arm/fixture.hh
Normal 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);
|
||||||
|
};
|
@@ -1,4 +1,5 @@
|
|||||||
tests_sources += files(
|
tests_sources += files(
|
||||||
|
'fixture.cc',
|
||||||
'instruction.cc',
|
'instruction.cc',
|
||||||
'exec.cc'
|
'exec.cc'
|
||||||
)
|
)
|
Reference in New Issue
Block a user