bus: separate out read/write that count cycles
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
@@ -5,10 +5,20 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace matar {
|
||||
enum CpuAccess {
|
||||
Sequential,
|
||||
NonSequential
|
||||
};
|
||||
|
||||
enum CpuAccessWidth {
|
||||
Word,
|
||||
Halfword,
|
||||
Byte
|
||||
};
|
||||
|
||||
class Bus {
|
||||
private:
|
||||
struct Private {
|
||||
@@ -23,20 +33,57 @@ class Bus {
|
||||
static std::shared_ptr<Bus> init(std::array<uint8_t, BIOS_SIZE>&&,
|
||||
std::vector<uint8_t>&&);
|
||||
|
||||
uint8_t read_byte(uint32_t, bool = true);
|
||||
void write_byte(uint32_t, uint8_t, bool = true);
|
||||
uint8_t read_byte(uint32_t address, CpuAccess access) {
|
||||
add_cpu_cycles<CpuAccessWidth::Byte>(address, access);
|
||||
return read_byte(address);
|
||||
};
|
||||
void write_byte(uint32_t address, uint8_t byte, CpuAccess access) {
|
||||
add_cpu_cycles<CpuAccessWidth::Byte>(address, access);
|
||||
write_byte(address, byte);
|
||||
};
|
||||
|
||||
uint16_t read_halfword(uint32_t, bool = true);
|
||||
void write_halfword(uint32_t, uint16_t, bool = true);
|
||||
uint16_t read_halfword(uint32_t address, CpuAccess access) {
|
||||
add_cpu_cycles<CpuAccessWidth::Halfword>(address, access);
|
||||
return read_halfword(address);
|
||||
}
|
||||
void write_halfword(uint32_t address, uint16_t halfword, CpuAccess access) {
|
||||
add_cpu_cycles<CpuAccessWidth::Halfword>(address, access);
|
||||
write_halfword(address, halfword);
|
||||
}
|
||||
|
||||
uint32_t read_word(uint32_t address, CpuAccess access) {
|
||||
add_cpu_cycles<CpuAccessWidth::Word>(address, access);
|
||||
return read_word(address);
|
||||
}
|
||||
void write_word(uint32_t address, uint32_t word, CpuAccess access) {
|
||||
add_cpu_cycles<CpuAccessWidth::Word>(address, access);
|
||||
write_word(address, word);
|
||||
}
|
||||
|
||||
uint8_t read_byte(uint32_t address);
|
||||
void write_byte(uint32_t address, uint8_t byte);
|
||||
|
||||
uint16_t read_halfword(uint32_t address);
|
||||
void write_halfword(uint32_t address, uint16_t halfword);
|
||||
|
||||
uint32_t read_word(uint32_t address);
|
||||
void write_word(uint32_t address, uint32_t word);
|
||||
|
||||
uint32_t read_word(uint32_t, bool = true);
|
||||
void write_word(uint32_t, uint32_t, bool = true);
|
||||
// not sure what else to do?
|
||||
inline void internal_cycle() { cycles++; }
|
||||
|
||||
inline uint32_t get_cycles() { return cycles; }
|
||||
void internal_cycle() { cycles++; }
|
||||
uint32_t get_cycles() { return cycles; }
|
||||
|
||||
private:
|
||||
template<CpuAccessWidth W>
|
||||
void add_cpu_cycles(uint32_t address, CpuAccess access) {
|
||||
auto cc = cycle_map[address >> 24 & 0xF];
|
||||
if constexpr (W == CpuAccessWidth::Word) {
|
||||
cycles += (access == CpuAccess::Sequential ? cc.s32 : cc.n32);
|
||||
} else {
|
||||
cycles += (access == CpuAccess::Sequential ? cc.s16 : cc.n16);
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned int>
|
||||
std::optional<std::span<const uint8_t>> read(uint32_t) const;
|
||||
|
||||
|
@@ -24,12 +24,17 @@ class Cpu {
|
||||
void step();
|
||||
void chg_mode(const Mode to);
|
||||
|
||||
inline bool is_halted() { return halted; }
|
||||
inline void resume() { halted = false; }
|
||||
|
||||
#ifdef GDB_DEBUG
|
||||
bool breakpoint_reached() {
|
||||
if (breakpoints.contains(pc - 2 * (cpsr.state() == State::Arm
|
||||
? arm::INSTRUCTION_SIZE
|
||||
: thumb::INSTRUCTION_SIZE))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
private:
|
||||
bool halted = false;
|
||||
|
||||
friend void arm::Instruction::exec(Cpu& cpu);
|
||||
friend void thumb::Instruction::exec(Cpu& cpu);
|
||||
|
||||
@@ -79,32 +84,19 @@ class Cpu {
|
||||
Psr und;
|
||||
} spsr_banked = {}; // banked saved program status registers
|
||||
|
||||
inline void internal_cycle() { bus->internal_cycle(); }
|
||||
void internal_cycle() { bus->internal_cycle(); }
|
||||
|
||||
// whether read is going to be sequential or not
|
||||
bool sequential = true;
|
||||
CpuAccess next_access = CpuAccess::Sequential;
|
||||
|
||||
// raw instructions in the pipeline
|
||||
std::array<uint32_t, 2> opcodes = {};
|
||||
|
||||
inline void advance_pc_arm() { pc += arm::INSTRUCTION_SIZE; };
|
||||
inline void advance_pc_thumb() { pc += thumb::INSTRUCTION_SIZE; }
|
||||
void advance_pc_arm() { pc += arm::INSTRUCTION_SIZE; };
|
||||
void advance_pc_thumb() { pc += thumb::INSTRUCTION_SIZE; }
|
||||
|
||||
bool is_flushed = false;
|
||||
inline void flush_pipeline() {
|
||||
if (cpsr.state() == State::Arm) {
|
||||
opcodes[0] = bus->read_word(pc, false);
|
||||
advance_pc_arm();
|
||||
opcodes[1] = bus->read_word(pc);
|
||||
advance_pc_arm();
|
||||
} else {
|
||||
opcodes[0] = bus->read_halfword(pc, false);
|
||||
advance_pc_thumb();
|
||||
opcodes[1] = bus->read_halfword(pc);
|
||||
advance_pc_thumb();
|
||||
}
|
||||
sequential = true;
|
||||
};
|
||||
void flush_pipeline();
|
||||
|
||||
#ifdef GDB_DEBUG
|
||||
friend class GdbRsp;
|
||||
|
31
src/bus.cc
31
src/bus.cc
@@ -1,4 +1,5 @@
|
||||
#include "bus.hh"
|
||||
#include "io/io.hh"
|
||||
#include "util/crypto.hh"
|
||||
#include "util/log.hh"
|
||||
|
||||
@@ -141,10 +142,7 @@ Bus::write(uint32_t address) {
|
||||
}
|
||||
|
||||
uint8_t
|
||||
Bus::read_byte(uint32_t address, bool sequential) {
|
||||
auto cc = cycle_map[address >> 24 & 0xF];
|
||||
cycles += sequential ? cc.s16 : cc.n16;
|
||||
|
||||
Bus::read_byte(uint32_t address) {
|
||||
if (address >= IO_START && address <= IO_END)
|
||||
return io->read_byte(address);
|
||||
|
||||
@@ -153,10 +151,7 @@ Bus::read_byte(uint32_t address, bool sequential) {
|
||||
}
|
||||
|
||||
void
|
||||
Bus::write_byte(uint32_t address, uint8_t byte, bool sequential) {
|
||||
auto cc = cycle_map[address >> 24 & 0xF];
|
||||
cycles += sequential ? cc.s16 : cc.n16;
|
||||
|
||||
Bus::write_byte(uint32_t address, uint8_t byte) {
|
||||
if (address >= IO_START && address <= IO_END) {
|
||||
io->write_byte(address, byte);
|
||||
return;
|
||||
@@ -169,13 +164,10 @@ Bus::write_byte(uint32_t address, uint8_t byte, bool sequential) {
|
||||
}
|
||||
|
||||
uint16_t
|
||||
Bus::read_halfword(uint32_t address, bool sequential) {
|
||||
Bus::read_halfword(uint32_t address) {
|
||||
if (address & 0b01)
|
||||
glogger.warn("Reading a non aligned halfword address");
|
||||
|
||||
auto cc = cycle_map[address >> 24 & 0xF];
|
||||
cycles += sequential ? cc.s16 : cc.n16;
|
||||
|
||||
if (address >= IO_START && address <= IO_END)
|
||||
return io->read_halfword(address);
|
||||
|
||||
@@ -185,13 +177,10 @@ Bus::read_halfword(uint32_t address, bool sequential) {
|
||||
}
|
||||
|
||||
void
|
||||
Bus::write_halfword(uint32_t address, uint16_t halfword, bool sequential) {
|
||||
Bus::write_halfword(uint32_t address, uint16_t halfword) {
|
||||
if (address & 0b01)
|
||||
glogger.warn("Writing to a non aligned halfword address");
|
||||
|
||||
auto cc = cycle_map[address >> 24 & 0xF];
|
||||
cycles += sequential ? cc.s16 : cc.n16;
|
||||
|
||||
if (address >= IO_START && address <= IO_END) {
|
||||
io->write_halfword(address, halfword);
|
||||
return;
|
||||
@@ -207,13 +196,10 @@ Bus::write_halfword(uint32_t address, uint16_t halfword, bool sequential) {
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Bus::read_word(uint32_t address, bool sequential) {
|
||||
Bus::read_word(uint32_t address) {
|
||||
if (address & 0b11)
|
||||
glogger.warn("Reading a non aligned word address");
|
||||
|
||||
auto cc = cycle_map[address >> 24 & 0xF];
|
||||
cycles += sequential ? cc.s32 : cc.n32;
|
||||
|
||||
if (address >= IO_START && address <= IO_END)
|
||||
return io->read_word(address);
|
||||
|
||||
@@ -225,13 +211,10 @@ Bus::read_word(uint32_t address, bool sequential) {
|
||||
}
|
||||
|
||||
void
|
||||
Bus::write_word(uint32_t address, uint32_t word, bool sequential) {
|
||||
Bus::write_word(uint32_t address, uint32_t word) {
|
||||
if (address & 0b11)
|
||||
glogger.warn("Writing to a non aligned word address");
|
||||
|
||||
auto cc = cycle_map[address >> 24 & 0xF];
|
||||
cycles += sequential ? cc.s32 : cc.n32;
|
||||
|
||||
if (address >= IO_START && address <= IO_END) {
|
||||
io->write_word(address, word);
|
||||
return;
|
||||
|
@@ -203,17 +203,21 @@ Instruction::exec(Cpu& cpu) {
|
||||
pc_error(data.rd);
|
||||
|
||||
if (data.byte) {
|
||||
cpu.gpr[data.rd] = cpu.bus->read_byte(cpu.gpr[data.rn], false);
|
||||
cpu.bus->write_byte(
|
||||
cpu.gpr[data.rn], cpu.gpr[data.rm] & 0xFF, true);
|
||||
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);
|
||||
} else {
|
||||
cpu.gpr[data.rd] = cpu.bus->read_word(cpu.gpr[data.rn], false);
|
||||
cpu.bus->write_word(cpu.gpr[data.rn], cpu.gpr[data.rm], true);
|
||||
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);
|
||||
}
|
||||
|
||||
cpu.internal_cycle();
|
||||
// last write address is unrelated to next
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu, pc_warn, pc_error](SingleDataTransfer& data) {
|
||||
/*
|
||||
@@ -273,10 +277,12 @@ Instruction::exec(Cpu& cpu) {
|
||||
if (data.load) {
|
||||
// byte
|
||||
if (data.byte)
|
||||
cpu.gpr[data.rd] = cpu.bus->read_byte(address, false);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_byte(address, CpuAccess::NonSequential);
|
||||
// word
|
||||
else
|
||||
cpu.gpr[data.rd] = cpu.bus->read_word(address, false);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_word(address, CpuAccess::NonSequential);
|
||||
|
||||
// N + S
|
||||
if (data.rd == cpu.PC_INDEX)
|
||||
@@ -292,11 +298,13 @@ Instruction::exec(Cpu& cpu) {
|
||||
|
||||
// byte
|
||||
if (data.byte)
|
||||
cpu.bus->write_byte(
|
||||
address, cpu.gpr[data.rd] & 0xFF, false);
|
||||
cpu.bus->write_byte(address,
|
||||
cpu.gpr[data.rd] & 0xFF,
|
||||
CpuAccess::NonSequential);
|
||||
// word
|
||||
else
|
||||
cpu.bus->write_word(address, cpu.gpr[data.rd], false);
|
||||
cpu.bus->write_word(
|
||||
address, cpu.gpr[data.rd], CpuAccess::NonSequential);
|
||||
}
|
||||
|
||||
if (!data.pre)
|
||||
@@ -306,7 +314,7 @@ Instruction::exec(Cpu& cpu) {
|
||||
cpu.gpr[data.rn] = address;
|
||||
|
||||
// last read/write is unrelated, this will be overwriten if flushed
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu, pc_warn, pc_error](HalfwordTransfer& data) {
|
||||
/*
|
||||
@@ -324,7 +332,6 @@ Instruction::exec(Cpu& cpu) {
|
||||
N -> write at target
|
||||
Total = 2N
|
||||
*/
|
||||
|
||||
uint32_t address = cpu.gpr[data.rn];
|
||||
uint32_t offset = 0;
|
||||
|
||||
@@ -359,8 +366,8 @@ Instruction::exec(Cpu& cpu) {
|
||||
if (data.sign) {
|
||||
// halfword
|
||||
if (data.half) {
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_halfword(address, false);
|
||||
cpu.gpr[data.rd] = cpu.bus->read_halfword(
|
||||
address, CpuAccess::NonSequential);
|
||||
|
||||
// sign extend the halfword
|
||||
cpu.gpr[data.rd] =
|
||||
@@ -368,7 +375,8 @@ Instruction::exec(Cpu& cpu) {
|
||||
|
||||
// byte
|
||||
} else {
|
||||
cpu.gpr[data.rd] = cpu.bus->read_byte(address, false);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_byte(address, CpuAccess::NonSequential);
|
||||
|
||||
// sign extend the byte
|
||||
cpu.gpr[data.rd] =
|
||||
@@ -376,7 +384,8 @@ Instruction::exec(Cpu& cpu) {
|
||||
}
|
||||
// unsigned halfword
|
||||
} else if (data.half) {
|
||||
cpu.gpr[data.rd] = cpu.bus->read_halfword(address, false);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_halfword(address, CpuAccess::NonSequential);
|
||||
}
|
||||
|
||||
// I
|
||||
@@ -393,7 +402,8 @@ Instruction::exec(Cpu& cpu) {
|
||||
|
||||
// halfword
|
||||
if (data.half)
|
||||
cpu.bus->write_halfword(address, cpu.gpr[data.rd], false);
|
||||
cpu.bus->write_halfword(
|
||||
address, cpu.gpr[data.rd], CpuAccess::NonSequential);
|
||||
}
|
||||
|
||||
if (!data.pre)
|
||||
@@ -403,7 +413,7 @@ Instruction::exec(Cpu& cpu) {
|
||||
cpu.gpr[data.rn] = address;
|
||||
|
||||
// last read/write is unrelated, this will be overwriten if flushed
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu, pc_error](BlockDataTransfer& data) {
|
||||
/*
|
||||
@@ -429,7 +439,7 @@ Instruction::exec(Cpu& cpu) {
|
||||
uint32_t address = cpu.gpr[data.rn];
|
||||
Mode mode = cpu.cpsr.mode();
|
||||
int8_t i = 0;
|
||||
bool sequential = false;
|
||||
CpuAccess access = CpuAccess::NonSequential;
|
||||
|
||||
pc_error(data.rn);
|
||||
|
||||
@@ -466,19 +476,17 @@ Instruction::exec(Cpu& cpu) {
|
||||
if (data.up) {
|
||||
for (i = 0; i < cpu.GPR_COUNT; i++) {
|
||||
if (get_bit(data.regs, i)) {
|
||||
cpu.gpr[i] =
|
||||
cpu.bus->read_word(address, sequential);
|
||||
cpu.gpr[i] = cpu.bus->read_word(address, access);
|
||||
address += alignment;
|
||||
sequential = true;
|
||||
access = CpuAccess::Sequential;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = cpu.GPR_COUNT - 1; i >= 0; i--) {
|
||||
if (get_bit(data.regs, i)) {
|
||||
cpu.gpr[i] =
|
||||
cpu.bus->read_word(address, sequential);
|
||||
cpu.gpr[i] = cpu.bus->read_word(address, access);
|
||||
address -= alignment;
|
||||
sequential = true;
|
||||
access = CpuAccess::Sequential;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -489,19 +497,17 @@ Instruction::exec(Cpu& cpu) {
|
||||
if (data.up) {
|
||||
for (i = 0; i < cpu.GPR_COUNT; i++) {
|
||||
if (get_bit(data.regs, i)) {
|
||||
cpu.bus->write_word(
|
||||
address, cpu.gpr[i], sequential);
|
||||
cpu.bus->write_word(address, cpu.gpr[i], access);
|
||||
address += alignment;
|
||||
sequential = true;
|
||||
access = CpuAccess::Sequential;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = cpu.GPR_COUNT - 1; i >= 0; i--) {
|
||||
if (get_bit(data.regs, i)) {
|
||||
cpu.bus->write_word(
|
||||
address, cpu.gpr[i], sequential);
|
||||
cpu.bus->write_word(address, cpu.gpr[i], access);
|
||||
address -= alignment;
|
||||
sequential = true;
|
||||
access = CpuAccess::Sequential;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -518,7 +524,7 @@ Instruction::exec(Cpu& cpu) {
|
||||
cpu.chg_mode(mode);
|
||||
|
||||
// last read/write is unrelated, this will be overwriten if flushed
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu, pc_error](PsrTransfer& data) {
|
||||
/*
|
||||
|
@@ -132,7 +132,7 @@ Cpu::step() {
|
||||
arm::Instruction instruction(opcodes[0]);
|
||||
|
||||
opcodes[0] = opcodes[1];
|
||||
opcodes[1] = bus->read_word(pc, sequential);
|
||||
opcodes[1] = bus->read_word(pc, next_access);
|
||||
|
||||
#ifdef DISASSEMBLER
|
||||
glogger.info("0x{:08X} : {}",
|
||||
@@ -151,7 +151,7 @@ Cpu::step() {
|
||||
thumb::Instruction instruction(opcodes[0]);
|
||||
|
||||
opcodes[0] = opcodes[1];
|
||||
opcodes[1] = bus->read_halfword(pc, sequential);
|
||||
opcodes[1] = bus->read_halfword(pc, next_access);
|
||||
|
||||
#ifdef DISASSEMBLER
|
||||
glogger.info("0x{:08X} : {}",
|
||||
@@ -168,4 +168,24 @@ Cpu::step() {
|
||||
advance_pc_thumb();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cpu::flush_pipeline() {
|
||||
// halfword align
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
@@ -257,12 +257,13 @@ Instruction::exec(Cpu& cpu) {
|
||||
rst_bit(pc, 0);
|
||||
rst_bit(pc, 1);
|
||||
|
||||
cpu.gpr[data.rd] = cpu.bus->read_word(pc + data.word, false);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_word(pc + data.word, CpuAccess::NonSequential);
|
||||
|
||||
cpu.internal_cycle();
|
||||
|
||||
// last read is unrelated
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu](LoadStoreRegisterOffset& data) {
|
||||
/*
|
||||
@@ -284,22 +285,26 @@ Instruction::exec(Cpu& cpu) {
|
||||
|
||||
if (data.load) {
|
||||
if (data.byte) {
|
||||
cpu.gpr[data.rd] = cpu.bus->read_byte(address, false);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_byte(address, CpuAccess::NonSequential);
|
||||
} else {
|
||||
cpu.gpr[data.rd] = cpu.bus->read_word(address, false);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_word(address, CpuAccess::NonSequential);
|
||||
}
|
||||
cpu.internal_cycle();
|
||||
} else {
|
||||
if (data.byte) {
|
||||
cpu.bus->write_byte(
|
||||
address, cpu.gpr[data.rd] & 0xFF, false);
|
||||
cpu.bus->write_byte(address,
|
||||
cpu.gpr[data.rd] & 0xFF,
|
||||
CpuAccess::NonSequential);
|
||||
} else {
|
||||
cpu.bus->write_word(address, cpu.gpr[data.rd], false);
|
||||
cpu.bus->write_word(
|
||||
address, cpu.gpr[data.rd], CpuAccess::NonSequential);
|
||||
}
|
||||
}
|
||||
|
||||
// last read/write is unrelated
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu](LoadStoreSignExtendedHalfword& data) {
|
||||
// Same cycles as above
|
||||
@@ -308,26 +313,28 @@ Instruction::exec(Cpu& cpu) {
|
||||
|
||||
switch (data.s << 1 | data.h) {
|
||||
case 0b00:
|
||||
cpu.bus->write_halfword(
|
||||
address, cpu.gpr[data.rd] & 0xFFFF, false);
|
||||
cpu.bus->write_halfword(address,
|
||||
cpu.gpr[data.rd] & 0xFFFF,
|
||||
CpuAccess::NonSequential);
|
||||
break;
|
||||
case 0b01:
|
||||
cpu.gpr[data.rd] = cpu.bus->read_halfword(address, false);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_halfword(address, CpuAccess::NonSequential);
|
||||
cpu.internal_cycle();
|
||||
break;
|
||||
case 0b10:
|
||||
// sign extend and load the byte
|
||||
cpu.gpr[data.rd] =
|
||||
(static_cast<int32_t>(cpu.bus->read_byte(address, false))
|
||||
<< 24) >>
|
||||
24;
|
||||
cpu.gpr[data.rd] = (static_cast<int32_t>(cpu.bus->read_byte(
|
||||
address, CpuAccess::NonSequential))
|
||||
<< 24) >>
|
||||
24;
|
||||
cpu.internal_cycle();
|
||||
break;
|
||||
case 0b11:
|
||||
// sign extend the halfword
|
||||
cpu.gpr[data.rd] =
|
||||
(static_cast<int32_t>(
|
||||
cpu.bus->read_halfword(address, false))
|
||||
(static_cast<int32_t>(cpu.bus->read_halfword(
|
||||
address, CpuAccess::NonSequential))
|
||||
<< 16) >>
|
||||
16;
|
||||
cpu.internal_cycle();
|
||||
@@ -339,7 +346,7 @@ Instruction::exec(Cpu& cpu) {
|
||||
}
|
||||
|
||||
// last read/write is unrelated
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu](LoadStoreImmediateOffset& data) {
|
||||
// Same cycles as above
|
||||
@@ -348,22 +355,26 @@ Instruction::exec(Cpu& cpu) {
|
||||
|
||||
if (data.load) {
|
||||
if (data.byte) {
|
||||
cpu.gpr[data.rd] = cpu.bus->read_byte(address, false);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_byte(address, CpuAccess::NonSequential);
|
||||
} else {
|
||||
cpu.gpr[data.rd] = cpu.bus->read_word(address, false);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_word(address, CpuAccess::NonSequential);
|
||||
}
|
||||
cpu.internal_cycle();
|
||||
} else {
|
||||
if (data.byte) {
|
||||
cpu.bus->write_byte(
|
||||
address, cpu.gpr[data.rd] & 0xFF, false);
|
||||
cpu.bus->write_byte(address,
|
||||
cpu.gpr[data.rd] & 0xFF,
|
||||
CpuAccess::NonSequential);
|
||||
} else {
|
||||
cpu.bus->write_word(address, cpu.gpr[data.rd], false);
|
||||
cpu.bus->write_word(
|
||||
address, cpu.gpr[data.rd], CpuAccess::NonSequential);
|
||||
}
|
||||
}
|
||||
|
||||
// last read/write is unrelated
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu](LoadStoreHalfword& data) {
|
||||
// Same cycles as above
|
||||
@@ -371,14 +382,16 @@ Instruction::exec(Cpu& cpu) {
|
||||
uint32_t address = cpu.gpr[data.rb] + data.offset;
|
||||
|
||||
if (data.load) {
|
||||
cpu.gpr[data.rd] = cpu.bus->read_halfword(address);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_halfword(address, CpuAccess::NonSequential);
|
||||
cpu.internal_cycle();
|
||||
} else {
|
||||
cpu.bus->write_halfword(address, cpu.gpr[data.rd] & 0xFFFF);
|
||||
cpu.bus->write_halfword(
|
||||
address, cpu.gpr[data.rd] & 0xFFFF, CpuAccess::NonSequential);
|
||||
}
|
||||
|
||||
// last read/write is unrelated
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu](SpRelativeLoad& data) {
|
||||
// Same cycles as above
|
||||
@@ -386,14 +399,16 @@ Instruction::exec(Cpu& cpu) {
|
||||
uint32_t address = cpu.sp + data.word;
|
||||
|
||||
if (data.load) {
|
||||
cpu.gpr[data.rd] = cpu.bus->read_word(address);
|
||||
cpu.gpr[data.rd] =
|
||||
cpu.bus->read_word(address, CpuAccess::Sequential);
|
||||
cpu.internal_cycle();
|
||||
} else {
|
||||
cpu.bus->write_word(address, cpu.gpr[data.rd]);
|
||||
cpu.bus->write_word(
|
||||
address, cpu.gpr[data.rd], CpuAccess::Sequential);
|
||||
}
|
||||
|
||||
// last read/write is unrelated
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu](LoadAddress& data) {
|
||||
// 1S cycle in step()
|
||||
@@ -430,19 +445,19 @@ Instruction::exec(Cpu& cpu) {
|
||||
Total = 2N + (n-1)S
|
||||
*/
|
||||
static constexpr uint8_t alignment = 4;
|
||||
bool sequential = false;
|
||||
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(cpu.sp, sequential);
|
||||
cpu.gpr[i] = cpu.bus->read_word(cpu.sp, access);
|
||||
cpu.sp += alignment;
|
||||
sequential = true;
|
||||
access = CpuAccess::Sequential;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.pclr) {
|
||||
cpu.pc = cpu.bus->read_word(cpu.sp);
|
||||
cpu.pc = cpu.bus->read_word(cpu.sp, access);
|
||||
cpu.sp += alignment;
|
||||
cpu.is_flushed = true;
|
||||
}
|
||||
@@ -452,20 +467,21 @@ Instruction::exec(Cpu& cpu) {
|
||||
} else {
|
||||
if (data.pclr) {
|
||||
cpu.sp -= alignment;
|
||||
cpu.bus->write_word(cpu.sp, cpu.lr);
|
||||
cpu.bus->write_word(cpu.sp, cpu.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], sequential);
|
||||
sequential = true;
|
||||
cpu.bus->write_word(cpu.sp, cpu.gpr[i], access);
|
||||
access = CpuAccess::Sequential;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// last read/write is unrelated
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu](MultipleLoad& data) {
|
||||
/*
|
||||
@@ -487,23 +503,23 @@ Instruction::exec(Cpu& cpu) {
|
||||
|
||||
static constexpr uint8_t alignment = 4;
|
||||
|
||||
uint32_t rb = cpu.gpr[data.rb];
|
||||
bool sequential = false;
|
||||
uint32_t rb = cpu.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, sequential);
|
||||
cpu.gpr[i] = cpu.bus->read_word(rb, access);
|
||||
rb += alignment;
|
||||
sequential = true;
|
||||
access = CpuAccess::Sequential;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
if (get_bit(data.regs, i)) {
|
||||
cpu.bus->write_word(rb, cpu.gpr[i], sequential);
|
||||
cpu.bus->write_word(rb, cpu.gpr[i], access);
|
||||
rb += alignment;
|
||||
sequential = true;
|
||||
access = CpuAccess::Sequential;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -511,7 +527,7 @@ Instruction::exec(Cpu& cpu) {
|
||||
cpu.gpr[data.rb] = rb;
|
||||
|
||||
// last read/write is unrelated
|
||||
cpu.sequential = false;
|
||||
cpu.next_access = CpuAccess::NonSequential;
|
||||
},
|
||||
[&cpu](ConditionalBranch& data) {
|
||||
/*
|
||||
|
120
src/gdb_rsp.cc
120
src/gdb_rsp.cc
@@ -91,54 +91,59 @@ GdbRsp::step(std::string msg) {
|
||||
cmd_halted();
|
||||
break;
|
||||
case '$': {
|
||||
if (msg.starts_with("$qSupported")) {
|
||||
acknowledge();
|
||||
cmd_supported(msg);
|
||||
} else if (msg.starts_with("$qAttached")) {
|
||||
acknowledge();
|
||||
cmd_attached();
|
||||
} else {
|
||||
acknowledge();
|
||||
switch (msg[1]) {
|
||||
case '?':
|
||||
cmd_halted();
|
||||
acknowledge();
|
||||
switch (msg[1]) {
|
||||
case '?':
|
||||
cmd_halted();
|
||||
break;
|
||||
case 'g':
|
||||
cmd_read_registers();
|
||||
break;
|
||||
case 'G':
|
||||
cmd_write_registers(msg);
|
||||
break;
|
||||
case 'p':
|
||||
cmd_read_register(msg);
|
||||
break;
|
||||
case 'P':
|
||||
cmd_write_register(msg);
|
||||
break;
|
||||
case 'm':
|
||||
cmd_read_memory(msg);
|
||||
break;
|
||||
case 'M':
|
||||
cmd_write_memory(msg);
|
||||
break;
|
||||
case 'z':
|
||||
cmd_rm_breakpoint(msg);
|
||||
break;
|
||||
case 'Z':
|
||||
cmd_add_breakpoint(msg);
|
||||
break;
|
||||
case 'c':
|
||||
cmd_continue();
|
||||
break;
|
||||
case 'D':
|
||||
cmd_detach();
|
||||
break;
|
||||
case 'Q':
|
||||
if (msg == "$QStartNoAckMode")
|
||||
ack_mode = true;
|
||||
send_ok();
|
||||
break;
|
||||
case 'q':
|
||||
if (msg.starts_with("$qSupported")) {
|
||||
cmd_supported(msg);
|
||||
break;
|
||||
case 'g':
|
||||
cmd_read_registers();
|
||||
} else if (msg == "$qAttached") {
|
||||
cmd_attached();
|
||||
break;
|
||||
case 'G':
|
||||
cmd_write_registers(msg);
|
||||
break;
|
||||
case 'p':
|
||||
cmd_read_register(msg);
|
||||
break;
|
||||
case 'P':
|
||||
cmd_write_register(msg);
|
||||
break;
|
||||
case 'm':
|
||||
cmd_read_memory(msg);
|
||||
break;
|
||||
case 'M':
|
||||
cmd_write_memory(msg);
|
||||
break;
|
||||
case 'z':
|
||||
cmd_rm_breakpoint(msg);
|
||||
break;
|
||||
case 'Z':
|
||||
cmd_add_breakpoint(msg);
|
||||
break;
|
||||
case 'c':
|
||||
cmd_continue();
|
||||
break;
|
||||
case 'D':
|
||||
cmd_detach();
|
||||
break;
|
||||
default:
|
||||
gdb_log("unknown command");
|
||||
send_empty();
|
||||
}
|
||||
}
|
||||
[[fallthrough]];
|
||||
default:
|
||||
gdb_log("unknown command");
|
||||
send_empty();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -150,7 +155,7 @@ std::string
|
||||
GdbRsp::receive() {
|
||||
std::string msg = server.receive(1);
|
||||
char ch = msg[0];
|
||||
int checksum = 0;
|
||||
uint checksum = 0;
|
||||
|
||||
if (ch == '$') {
|
||||
while ((ch = server.receive(1)[0]) != '#') {
|
||||
@@ -179,7 +184,8 @@ GdbRsp::make_packet(std::string raw) {
|
||||
|
||||
void
|
||||
GdbRsp::acknowledge() {
|
||||
server.send("+");
|
||||
if (ack_mode)
|
||||
server.send("+");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -214,6 +220,9 @@ GdbRsp::cmd_supported(std::string msg) {
|
||||
if (msg.find("hwbreak+;") != std::string::npos)
|
||||
response += "hwbreak+;";
|
||||
|
||||
// no acknowledgement mode
|
||||
response += "QStartNoAckMode+";
|
||||
|
||||
gdb_log("sending response for qSupported");
|
||||
server.send(make_packet(response));
|
||||
}
|
||||
@@ -327,7 +336,6 @@ GdbRsp::cmd_write_register(std::string msg) {
|
||||
void
|
||||
GdbRsp::cmd_read_memory(std::string msg) {
|
||||
std::string response;
|
||||
bool sequential = false;
|
||||
|
||||
static std::regex rgx("\\$m([0-9A-Fa-f]+),([0-9A-Fa-f]+)");
|
||||
std::smatch sm;
|
||||
@@ -351,20 +359,15 @@ GdbRsp::cmd_read_memory(std::string msg) {
|
||||
}
|
||||
|
||||
for (uint i = 0; i < length; i++) {
|
||||
response +=
|
||||
std::format("{:02x}", cpu->bus->read_byte(address + i), sequential);
|
||||
sequential = true;
|
||||
response += std::format("{:02x}", cpu->bus->read_byte(address + i));
|
||||
}
|
||||
|
||||
cpu->sequential = false;
|
||||
gdb_log("sending memory values values");
|
||||
server.send(make_packet(response));
|
||||
}
|
||||
|
||||
void
|
||||
GdbRsp::cmd_write_memory(std::string msg) {
|
||||
bool sequential = false;
|
||||
|
||||
static std::regex rgx("\\$M([0-9A-Fa-f]+),([0-9A-Fa-f]+):([0-9A-Fa-f]+)");
|
||||
std::smatch sm;
|
||||
regex_match(msg, sm, rgx);
|
||||
@@ -382,15 +385,10 @@ GdbRsp::cmd_write_memory(std::string msg) {
|
||||
std::string values = sm[3].str();
|
||||
|
||||
for (uint i = 0, j = 0; i < length && j < values.size(); i++, j += 2) {
|
||||
cpu->bus->write_byte(address + i,
|
||||
std::stoul(values.substr(j, 2), nullptr, 16) &
|
||||
0xFF,
|
||||
sequential);
|
||||
glogger.warn("hi {:02x}", cpu->bus->read_byte(address + i));
|
||||
sequential = true;
|
||||
cpu->bus->write_byte(
|
||||
address + i, std::stoul(values.substr(j, 2), nullptr, 16) & 0xFF);
|
||||
}
|
||||
|
||||
cpu->sequential = false;
|
||||
gdb_log("register values written");
|
||||
send_ok();
|
||||
} catch (const std::exception& e) {
|
||||
|
@@ -21,6 +21,8 @@ class GdbRsp {
|
||||
net::TcpServer server;
|
||||
std::string receive();
|
||||
std::string make_packet(std::string raw);
|
||||
|
||||
bool ack_mode = true;
|
||||
void acknowledge();
|
||||
void send_empty();
|
||||
void send_ok();
|
||||
|
84
tests/bus.cc
84
tests/bus.cc
@@ -26,18 +26,12 @@ TEST_CASE("bios", TAG) {
|
||||
auto bus =
|
||||
Bus::init(std::move(bios), std::vector<uint8_t>(Header::HEADER_SIZE));
|
||||
|
||||
uint32_t cycles = bus->get_cycles();
|
||||
|
||||
CHECK(bus->read_byte(0) == 0xAC);
|
||||
CHECK(bus->read_byte(0x3FFF) == 0x48);
|
||||
CHECK(bus->read_byte(0x2A56) == 0x10);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 3);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(BusFixture, "board wram", TAG) {
|
||||
uint32_t cycles = bus->get_cycles();
|
||||
|
||||
bus->write_byte(0x2000000, 0xAC);
|
||||
CHECK(bus->read_byte(0x2000000) == 0xAC);
|
||||
|
||||
@@ -46,25 +40,9 @@ TEST_CASE_METHOD(BusFixture, "board wram", TAG) {
|
||||
|
||||
bus->write_byte(0x2022A56, 0x10);
|
||||
CHECK(bus->read_byte(0x2022A56) == 0x10);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2 * 9);
|
||||
cycles = bus->get_cycles();
|
||||
|
||||
bus->write_halfword(0x2022A56, 0x1009);
|
||||
CHECK(bus->read_halfword(0x2022A56) == 0x1009);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2 * 3);
|
||||
cycles = bus->get_cycles();
|
||||
|
||||
bus->write_word(0x2022A56, 0x10FF9903);
|
||||
CHECK(bus->read_word(0x2022A56) == 0x10FF9903);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2 * 6);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(BusFixture, "chip wram", TAG) {
|
||||
uint32_t cycles = bus->get_cycles();
|
||||
|
||||
bus->write_byte(0x3000000, 0xAC);
|
||||
CHECK(bus->read_byte(0x3000000) == 0xAC);
|
||||
|
||||
@@ -73,25 +51,9 @@ TEST_CASE_METHOD(BusFixture, "chip wram", TAG) {
|
||||
|
||||
bus->write_byte(0x3002A56, 0x10);
|
||||
CHECK(bus->read_byte(0x3002A56) == 0x10);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2 * 3);
|
||||
cycles = bus->get_cycles();
|
||||
|
||||
bus->write_halfword(0x3002A56, 0xF0F0);
|
||||
CHECK(bus->read_halfword(0x3002A56) == 0xF0F0);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2);
|
||||
cycles = bus->get_cycles();
|
||||
|
||||
bus->write_word(0x3002A56, 0xF9399010);
|
||||
CHECK(bus->read_word(0x3002A56) == 0xF9399010);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(BusFixture, "palette ram", TAG) {
|
||||
uint32_t cycles = bus->get_cycles();
|
||||
|
||||
bus->write_byte(0x5000000, 0xAC);
|
||||
CHECK(bus->read_byte(0x5000000) == 0xAC);
|
||||
|
||||
@@ -100,25 +62,9 @@ TEST_CASE_METHOD(BusFixture, "palette ram", TAG) {
|
||||
|
||||
bus->write_byte(0x5000156, 0x10);
|
||||
CHECK(bus->read_byte(0x5000156) == 0x10);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2 * 3);
|
||||
cycles = bus->get_cycles();
|
||||
|
||||
bus->write_halfword(0x5000156, 0xEEE1);
|
||||
CHECK(bus->read_halfword(0x5000156) == 0xEEE1);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2);
|
||||
cycles = bus->get_cycles();
|
||||
|
||||
bus->write_word(0x5000156, 0x938566E0);
|
||||
CHECK(bus->read_word(0x5000156) == 0x938566E0);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2 * 2);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(BusFixture, "video ram", TAG) {
|
||||
uint32_t cycles = bus->get_cycles();
|
||||
|
||||
bus->write_byte(0x6000000, 0xAC);
|
||||
CHECK(bus->read_byte(0x6000000) == 0xAC);
|
||||
|
||||
@@ -127,25 +73,9 @@ TEST_CASE_METHOD(BusFixture, "video ram", TAG) {
|
||||
|
||||
bus->write_byte(0x6012A56, 0x10);
|
||||
CHECK(bus->read_byte(0x6012A56) == 0x10);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2 * 3);
|
||||
cycles = bus->get_cycles();
|
||||
|
||||
bus->write_halfword(0x6012A56, 0xB100);
|
||||
CHECK(bus->read_halfword(0x6012A56) == 0xB100);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2);
|
||||
cycles = bus->get_cycles();
|
||||
|
||||
bus->write_word(0x6012A56, 0x9322093E);
|
||||
CHECK(bus->read_word(0x6012A56) == 0x9322093E);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2 * 2);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(BusFixture, "oam obj ram", TAG) {
|
||||
uint32_t cycles = bus->get_cycles();
|
||||
|
||||
bus->write_byte(0x7000000, 0xAC);
|
||||
CHECK(bus->read_byte(0x7000000) == 0xAC);
|
||||
|
||||
@@ -154,20 +84,6 @@ TEST_CASE_METHOD(BusFixture, "oam obj ram", TAG) {
|
||||
|
||||
bus->write_byte(0x7000156, 0x10);
|
||||
CHECK(bus->read_byte(0x7000156) == 0x10);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2 * 3);
|
||||
cycles = bus->get_cycles();
|
||||
|
||||
bus->write_halfword(0x7000156, 0x946C);
|
||||
CHECK(bus->read_halfword(0x7000156) == 0x946C);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2);
|
||||
cycles = bus->get_cycles();
|
||||
|
||||
bus->write_word(0x7000156, 0x93C5D1E0);
|
||||
CHECK(bus->read_word(0x7000156) == 0x93C5D1E0);
|
||||
|
||||
CHECK(bus->get_cycles() == cycles + 2);
|
||||
}
|
||||
|
||||
TEST_CASE("rom", TAG) {
|
||||
|
Reference in New Issue
Block a user