bus: separate out read/write that count cycles

Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
2024-06-19 14:53:31 +05:30
parent f5aa73e7ca
commit 7d3996526f
9 changed files with 262 additions and 282 deletions

View File

@@ -5,10 +5,20 @@
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <span> #include <span>
#include <unordered_map>
#include <vector> #include <vector>
namespace matar { namespace matar {
enum CpuAccess {
Sequential,
NonSequential
};
enum CpuAccessWidth {
Word,
Halfword,
Byte
};
class Bus { class Bus {
private: private:
struct Private { struct Private {
@@ -23,20 +33,57 @@ class Bus {
static std::shared_ptr<Bus> init(std::array<uint8_t, BIOS_SIZE>&&, static std::shared_ptr<Bus> init(std::array<uint8_t, BIOS_SIZE>&&,
std::vector<uint8_t>&&); std::vector<uint8_t>&&);
uint8_t read_byte(uint32_t, bool = true); uint8_t read_byte(uint32_t address, CpuAccess access) {
void write_byte(uint32_t, uint8_t, bool = true); 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); uint16_t read_halfword(uint32_t address, CpuAccess access) {
void write_halfword(uint32_t, uint16_t, bool = true); 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? // not sure what else to do?
inline void internal_cycle() { cycles++; } void internal_cycle() { cycles++; }
uint32_t get_cycles() { return cycles; }
inline uint32_t get_cycles() { return cycles; }
private: 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> template<unsigned int>
std::optional<std::span<const uint8_t>> read(uint32_t) const; std::optional<std::span<const uint8_t>> read(uint32_t) const;

View File

@@ -24,12 +24,17 @@ class Cpu {
void step(); void step();
void chg_mode(const Mode to); void chg_mode(const Mode to);
inline bool is_halted() { return halted; } #ifdef GDB_DEBUG
inline void resume() { halted = false; } bool breakpoint_reached() {
if (breakpoints.contains(pc - 2 * (cpsr.state() == State::Arm
? arm::INSTRUCTION_SIZE
: thumb::INSTRUCTION_SIZE))) {
return true;
}
return false;
}
#endif
private: private:
bool halted = false;
friend void arm::Instruction::exec(Cpu& cpu); friend void arm::Instruction::exec(Cpu& cpu);
friend void thumb::Instruction::exec(Cpu& cpu); friend void thumb::Instruction::exec(Cpu& cpu);
@@ -79,32 +84,19 @@ class Cpu {
Psr und; Psr und;
} spsr_banked = {}; // banked saved program status registers } 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 // whether read is going to be sequential or not
bool sequential = true; CpuAccess next_access = CpuAccess::Sequential;
// raw instructions in the pipeline // raw instructions in the pipeline
std::array<uint32_t, 2> opcodes = {}; std::array<uint32_t, 2> opcodes = {};
inline void advance_pc_arm() { pc += arm::INSTRUCTION_SIZE; }; void advance_pc_arm() { pc += arm::INSTRUCTION_SIZE; };
inline void advance_pc_thumb() { pc += thumb::INSTRUCTION_SIZE; } void advance_pc_thumb() { pc += thumb::INSTRUCTION_SIZE; }
bool is_flushed = false; bool is_flushed = false;
inline void flush_pipeline() { 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;
};
#ifdef GDB_DEBUG #ifdef GDB_DEBUG
friend class GdbRsp; friend class GdbRsp;

View File

@@ -1,4 +1,5 @@
#include "bus.hh" #include "bus.hh"
#include "io/io.hh"
#include "util/crypto.hh" #include "util/crypto.hh"
#include "util/log.hh" #include "util/log.hh"
@@ -141,10 +142,7 @@ Bus::write(uint32_t address) {
} }
uint8_t uint8_t
Bus::read_byte(uint32_t address, bool sequential) { Bus::read_byte(uint32_t address) {
auto cc = cycle_map[address >> 24 & 0xF];
cycles += sequential ? cc.s16 : cc.n16;
if (address >= IO_START && address <= IO_END) if (address >= IO_START && address <= IO_END)
return io->read_byte(address); return io->read_byte(address);
@@ -153,10 +151,7 @@ Bus::read_byte(uint32_t address, bool sequential) {
} }
void void
Bus::write_byte(uint32_t address, uint8_t byte, bool sequential) { Bus::write_byte(uint32_t address, uint8_t byte) {
auto cc = cycle_map[address >> 24 & 0xF];
cycles += sequential ? cc.s16 : cc.n16;
if (address >= IO_START && address <= IO_END) { if (address >= IO_START && address <= IO_END) {
io->write_byte(address, byte); io->write_byte(address, byte);
return; return;
@@ -169,13 +164,10 @@ Bus::write_byte(uint32_t address, uint8_t byte, bool sequential) {
} }
uint16_t uint16_t
Bus::read_halfword(uint32_t address, bool sequential) { Bus::read_halfword(uint32_t address) {
if (address & 0b01) if (address & 0b01)
glogger.warn("Reading a non aligned halfword address"); 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) if (address >= IO_START && address <= IO_END)
return io->read_halfword(address); return io->read_halfword(address);
@@ -185,13 +177,10 @@ Bus::read_halfword(uint32_t address, bool sequential) {
} }
void void
Bus::write_halfword(uint32_t address, uint16_t halfword, bool sequential) { Bus::write_halfword(uint32_t address, uint16_t halfword) {
if (address & 0b01) if (address & 0b01)
glogger.warn("Writing to a non aligned halfword address"); 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) { if (address >= IO_START && address <= IO_END) {
io->write_halfword(address, halfword); io->write_halfword(address, halfword);
return; return;
@@ -207,13 +196,10 @@ Bus::write_halfword(uint32_t address, uint16_t halfword, bool sequential) {
} }
uint32_t uint32_t
Bus::read_word(uint32_t address, bool sequential) { Bus::read_word(uint32_t address) {
if (address & 0b11) if (address & 0b11)
glogger.warn("Reading a non aligned word address"); 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) if (address >= IO_START && address <= IO_END)
return io->read_word(address); return io->read_word(address);
@@ -225,13 +211,10 @@ Bus::read_word(uint32_t address, bool sequential) {
} }
void void
Bus::write_word(uint32_t address, uint32_t word, bool sequential) { Bus::write_word(uint32_t address, uint32_t word) {
if (address & 0b11) if (address & 0b11)
glogger.warn("Writing to a non aligned word address"); 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) { if (address >= IO_START && address <= IO_END) {
io->write_word(address, word); io->write_word(address, word);
return; return;

View File

@@ -203,17 +203,21 @@ Instruction::exec(Cpu& cpu) {
pc_error(data.rd); pc_error(data.rd);
if (data.byte) { if (data.byte) {
cpu.gpr[data.rd] = cpu.bus->read_byte(cpu.gpr[data.rn], false); cpu.gpr[data.rd] = cpu.bus->read_byte(cpu.gpr[data.rn],
cpu.bus->write_byte( CpuAccess::NonSequential);
cpu.gpr[data.rn], cpu.gpr[data.rm] & 0xFF, true); cpu.bus->write_byte(cpu.gpr[data.rn],
cpu.gpr[data.rm] & 0xFF,
CpuAccess::Sequential);
} else { } else {
cpu.gpr[data.rd] = cpu.bus->read_word(cpu.gpr[data.rn], false); cpu.gpr[data.rd] = cpu.bus->read_word(cpu.gpr[data.rn],
cpu.bus->write_word(cpu.gpr[data.rn], cpu.gpr[data.rm], true); CpuAccess::NonSequential);
cpu.bus->write_word(
cpu.gpr[data.rn], cpu.gpr[data.rm], CpuAccess::Sequential);
} }
cpu.internal_cycle(); cpu.internal_cycle();
// last write address is unrelated to next // last write address is unrelated to next
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu, pc_warn, pc_error](SingleDataTransfer& data) { [&cpu, pc_warn, pc_error](SingleDataTransfer& data) {
/* /*
@@ -273,10 +277,12 @@ Instruction::exec(Cpu& cpu) {
if (data.load) { if (data.load) {
// byte // byte
if (data.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 // word
else else
cpu.gpr[data.rd] = cpu.bus->read_word(address, false); cpu.gpr[data.rd] =
cpu.bus->read_word(address, CpuAccess::NonSequential);
// N + S // N + S
if (data.rd == cpu.PC_INDEX) if (data.rd == cpu.PC_INDEX)
@@ -292,11 +298,13 @@ Instruction::exec(Cpu& cpu) {
// byte // byte
if (data.byte) if (data.byte)
cpu.bus->write_byte( cpu.bus->write_byte(address,
address, cpu.gpr[data.rd] & 0xFF, false); cpu.gpr[data.rd] & 0xFF,
CpuAccess::NonSequential);
// word // word
else 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) if (!data.pre)
@@ -306,7 +314,7 @@ Instruction::exec(Cpu& cpu) {
cpu.gpr[data.rn] = address; cpu.gpr[data.rn] = address;
// last read/write is unrelated, this will be overwriten if flushed // 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) { [&cpu, pc_warn, pc_error](HalfwordTransfer& data) {
/* /*
@@ -324,7 +332,6 @@ Instruction::exec(Cpu& cpu) {
N -> write at target N -> write at target
Total = 2N Total = 2N
*/ */
uint32_t address = cpu.gpr[data.rn]; uint32_t address = cpu.gpr[data.rn];
uint32_t offset = 0; uint32_t offset = 0;
@@ -359,8 +366,8 @@ Instruction::exec(Cpu& cpu) {
if (data.sign) { if (data.sign) {
// halfword // halfword
if (data.half) { if (data.half) {
cpu.gpr[data.rd] = cpu.gpr[data.rd] = cpu.bus->read_halfword(
cpu.bus->read_halfword(address, false); address, CpuAccess::NonSequential);
// sign extend the halfword // sign extend the halfword
cpu.gpr[data.rd] = cpu.gpr[data.rd] =
@@ -368,7 +375,8 @@ Instruction::exec(Cpu& cpu) {
// byte // byte
} else { } 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 // sign extend the byte
cpu.gpr[data.rd] = cpu.gpr[data.rd] =
@@ -376,7 +384,8 @@ Instruction::exec(Cpu& cpu) {
} }
// unsigned halfword // unsigned halfword
} else if (data.half) { } 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 // I
@@ -393,7 +402,8 @@ Instruction::exec(Cpu& cpu) {
// halfword // halfword
if (data.half) 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) if (!data.pre)
@@ -403,7 +413,7 @@ Instruction::exec(Cpu& cpu) {
cpu.gpr[data.rn] = address; cpu.gpr[data.rn] = address;
// last read/write is unrelated, this will be overwriten if flushed // last read/write is unrelated, this will be overwriten if flushed
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu, pc_error](BlockDataTransfer& data) { [&cpu, pc_error](BlockDataTransfer& data) {
/* /*
@@ -429,7 +439,7 @@ Instruction::exec(Cpu& cpu) {
uint32_t address = cpu.gpr[data.rn]; uint32_t address = cpu.gpr[data.rn];
Mode mode = cpu.cpsr.mode(); Mode mode = cpu.cpsr.mode();
int8_t i = 0; int8_t i = 0;
bool sequential = false; CpuAccess access = CpuAccess::NonSequential;
pc_error(data.rn); pc_error(data.rn);
@@ -466,19 +476,17 @@ Instruction::exec(Cpu& cpu) {
if (data.up) { if (data.up) {
for (i = 0; i < cpu.GPR_COUNT; i++) { for (i = 0; i < cpu.GPR_COUNT; i++) {
if (get_bit(data.regs, i)) { if (get_bit(data.regs, i)) {
cpu.gpr[i] = cpu.gpr[i] = cpu.bus->read_word(address, access);
cpu.bus->read_word(address, sequential);
address += alignment; address += alignment;
sequential = true; access = CpuAccess::Sequential;
} }
} }
} else { } else {
for (i = cpu.GPR_COUNT - 1; i >= 0; i--) { for (i = cpu.GPR_COUNT - 1; i >= 0; i--) {
if (get_bit(data.regs, i)) { if (get_bit(data.regs, i)) {
cpu.gpr[i] = cpu.gpr[i] = cpu.bus->read_word(address, access);
cpu.bus->read_word(address, sequential);
address -= alignment; address -= alignment;
sequential = true; access = CpuAccess::Sequential;
} }
} }
} }
@@ -489,19 +497,17 @@ Instruction::exec(Cpu& cpu) {
if (data.up) { if (data.up) {
for (i = 0; i < cpu.GPR_COUNT; i++) { for (i = 0; i < cpu.GPR_COUNT; i++) {
if (get_bit(data.regs, i)) { if (get_bit(data.regs, i)) {
cpu.bus->write_word( cpu.bus->write_word(address, cpu.gpr[i], access);
address, cpu.gpr[i], sequential);
address += alignment; address += alignment;
sequential = true; access = CpuAccess::Sequential;
} }
} }
} else { } else {
for (i = cpu.GPR_COUNT - 1; i >= 0; i--) { for (i = cpu.GPR_COUNT - 1; i >= 0; i--) {
if (get_bit(data.regs, i)) { if (get_bit(data.regs, i)) {
cpu.bus->write_word( cpu.bus->write_word(address, cpu.gpr[i], access);
address, cpu.gpr[i], sequential);
address -= alignment; address -= alignment;
sequential = true; access = CpuAccess::Sequential;
} }
} }
} }
@@ -518,7 +524,7 @@ Instruction::exec(Cpu& cpu) {
cpu.chg_mode(mode); cpu.chg_mode(mode);
// last read/write is unrelated, this will be overwriten if flushed // last read/write is unrelated, this will be overwriten if flushed
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu, pc_error](PsrTransfer& data) { [&cpu, pc_error](PsrTransfer& data) {
/* /*

View File

@@ -132,7 +132,7 @@ Cpu::step() {
arm::Instruction instruction(opcodes[0]); arm::Instruction instruction(opcodes[0]);
opcodes[0] = opcodes[1]; opcodes[0] = opcodes[1];
opcodes[1] = bus->read_word(pc, sequential); opcodes[1] = bus->read_word(pc, next_access);
#ifdef DISASSEMBLER #ifdef DISASSEMBLER
glogger.info("0x{:08X} : {}", glogger.info("0x{:08X} : {}",
@@ -151,7 +151,7 @@ Cpu::step() {
thumb::Instruction instruction(opcodes[0]); thumb::Instruction instruction(opcodes[0]);
opcodes[0] = opcodes[1]; opcodes[0] = opcodes[1];
opcodes[1] = bus->read_halfword(pc, sequential); opcodes[1] = bus->read_halfword(pc, next_access);
#ifdef DISASSEMBLER #ifdef DISASSEMBLER
glogger.info("0x{:08X} : {}", glogger.info("0x{:08X} : {}",
@@ -168,4 +168,24 @@ Cpu::step() {
advance_pc_thumb(); 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;
};
} }

View File

@@ -257,12 +257,13 @@ Instruction::exec(Cpu& cpu) {
rst_bit(pc, 0); rst_bit(pc, 0);
rst_bit(pc, 1); 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(); cpu.internal_cycle();
// last read is unrelated // last read is unrelated
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu](LoadStoreRegisterOffset& data) { [&cpu](LoadStoreRegisterOffset& data) {
/* /*
@@ -284,22 +285,26 @@ Instruction::exec(Cpu& cpu) {
if (data.load) { if (data.load) {
if (data.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);
} else { } 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(); cpu.internal_cycle();
} else { } else {
if (data.byte) { if (data.byte) {
cpu.bus->write_byte( cpu.bus->write_byte(address,
address, cpu.gpr[data.rd] & 0xFF, false); cpu.gpr[data.rd] & 0xFF,
CpuAccess::NonSequential);
} else { } 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 // last read/write is unrelated
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu](LoadStoreSignExtendedHalfword& data) { [&cpu](LoadStoreSignExtendedHalfword& data) {
// Same cycles as above // Same cycles as above
@@ -308,26 +313,28 @@ Instruction::exec(Cpu& cpu) {
switch (data.s << 1 | data.h) { switch (data.s << 1 | data.h) {
case 0b00: case 0b00:
cpu.bus->write_halfword( cpu.bus->write_halfword(address,
address, cpu.gpr[data.rd] & 0xFFFF, false); cpu.gpr[data.rd] & 0xFFFF,
CpuAccess::NonSequential);
break; break;
case 0b01: 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(); cpu.internal_cycle();
break; break;
case 0b10: case 0b10:
// sign extend and load the byte // sign extend and load the byte
cpu.gpr[data.rd] = cpu.gpr[data.rd] = (static_cast<int32_t>(cpu.bus->read_byte(
(static_cast<int32_t>(cpu.bus->read_byte(address, false)) address, CpuAccess::NonSequential))
<< 24) >> << 24) >>
24; 24;
cpu.internal_cycle(); cpu.internal_cycle();
break; break;
case 0b11: case 0b11:
// sign extend the halfword // sign extend the halfword
cpu.gpr[data.rd] = cpu.gpr[data.rd] =
(static_cast<int32_t>( (static_cast<int32_t>(cpu.bus->read_halfword(
cpu.bus->read_halfword(address, false)) address, CpuAccess::NonSequential))
<< 16) >> << 16) >>
16; 16;
cpu.internal_cycle(); cpu.internal_cycle();
@@ -339,7 +346,7 @@ Instruction::exec(Cpu& cpu) {
} }
// last read/write is unrelated // last read/write is unrelated
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu](LoadStoreImmediateOffset& data) { [&cpu](LoadStoreImmediateOffset& data) {
// Same cycles as above // Same cycles as above
@@ -348,22 +355,26 @@ Instruction::exec(Cpu& cpu) {
if (data.load) { if (data.load) {
if (data.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);
} else { } 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(); cpu.internal_cycle();
} else { } else {
if (data.byte) { if (data.byte) {
cpu.bus->write_byte( cpu.bus->write_byte(address,
address, cpu.gpr[data.rd] & 0xFF, false); cpu.gpr[data.rd] & 0xFF,
CpuAccess::NonSequential);
} else { } 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 // last read/write is unrelated
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu](LoadStoreHalfword& data) { [&cpu](LoadStoreHalfword& data) {
// Same cycles as above // Same cycles as above
@@ -371,14 +382,16 @@ Instruction::exec(Cpu& cpu) {
uint32_t address = cpu.gpr[data.rb] + data.offset; uint32_t address = cpu.gpr[data.rb] + data.offset;
if (data.load) { 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(); cpu.internal_cycle();
} else { } 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 // last read/write is unrelated
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu](SpRelativeLoad& data) { [&cpu](SpRelativeLoad& data) {
// Same cycles as above // Same cycles as above
@@ -386,14 +399,16 @@ Instruction::exec(Cpu& cpu) {
uint32_t address = cpu.sp + data.word; uint32_t address = cpu.sp + data.word;
if (data.load) { 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(); cpu.internal_cycle();
} else { } 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 // last read/write is unrelated
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu](LoadAddress& data) { [&cpu](LoadAddress& data) {
// 1S cycle in step() // 1S cycle in step()
@@ -430,19 +445,19 @@ Instruction::exec(Cpu& cpu) {
Total = 2N + (n-1)S Total = 2N + (n-1)S
*/ */
static constexpr uint8_t alignment = 4; static constexpr uint8_t alignment = 4;
bool sequential = false; CpuAccess access = CpuAccess::NonSequential;
if (data.load) { if (data.load) {
for (uint8_t i = 0; i < 8; i++) { for (uint8_t i = 0; i < 8; i++) {
if (get_bit(data.regs, 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; cpu.sp += alignment;
sequential = true; access = CpuAccess::Sequential;
} }
} }
if (data.pclr) { if (data.pclr) {
cpu.pc = cpu.bus->read_word(cpu.sp); cpu.pc = cpu.bus->read_word(cpu.sp, access);
cpu.sp += alignment; cpu.sp += alignment;
cpu.is_flushed = true; cpu.is_flushed = true;
} }
@@ -452,20 +467,21 @@ Instruction::exec(Cpu& cpu) {
} else { } else {
if (data.pclr) { if (data.pclr) {
cpu.sp -= alignment; 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--) { for (int8_t i = 7; i >= 0; i--) {
if (get_bit(data.regs, i)) { if (get_bit(data.regs, i)) {
cpu.sp -= alignment; cpu.sp -= alignment;
cpu.bus->write_word(cpu.sp, cpu.gpr[i], sequential); cpu.bus->write_word(cpu.sp, cpu.gpr[i], access);
sequential = true; access = CpuAccess::Sequential;
} }
} }
} }
// last read/write is unrelated // last read/write is unrelated
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu](MultipleLoad& data) { [&cpu](MultipleLoad& data) {
/* /*
@@ -487,23 +503,23 @@ Instruction::exec(Cpu& cpu) {
static constexpr uint8_t alignment = 4; static constexpr uint8_t alignment = 4;
uint32_t rb = cpu.gpr[data.rb]; uint32_t rb = cpu.gpr[data.rb];
bool sequential = false; CpuAccess access = CpuAccess::NonSequential;
if (data.load) { if (data.load) {
for (uint8_t i = 0; i < 8; i++) { for (uint8_t i = 0; i < 8; i++) {
if (get_bit(data.regs, 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; rb += alignment;
sequential = true; access = CpuAccess::Sequential;
} }
} }
} else { } else {
for (uint8_t i = 0; i < 8; i++) { for (uint8_t i = 0; i < 8; i++) {
if (get_bit(data.regs, 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; rb += alignment;
sequential = true; access = CpuAccess::Sequential;
} }
} }
} }
@@ -511,7 +527,7 @@ Instruction::exec(Cpu& cpu) {
cpu.gpr[data.rb] = rb; cpu.gpr[data.rb] = rb;
// last read/write is unrelated // last read/write is unrelated
cpu.sequential = false; cpu.next_access = CpuAccess::NonSequential;
}, },
[&cpu](ConditionalBranch& data) { [&cpu](ConditionalBranch& data) {
/* /*

View File

@@ -91,54 +91,59 @@ GdbRsp::step(std::string msg) {
cmd_halted(); cmd_halted();
break; break;
case '$': { case '$': {
if (msg.starts_with("$qSupported")) { acknowledge();
acknowledge(); switch (msg[1]) {
cmd_supported(msg); case '?':
} else if (msg.starts_with("$qAttached")) { cmd_halted();
acknowledge(); break;
cmd_attached(); case 'g':
} else { cmd_read_registers();
acknowledge(); break;
switch (msg[1]) { case 'G':
case '?': cmd_write_registers(msg);
cmd_halted(); 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; break;
case 'g': } else if (msg == "$qAttached") {
cmd_read_registers(); cmd_attached();
break; break;
case 'G': }
cmd_write_registers(msg); [[fallthrough]];
break; default:
case 'p': gdb_log("unknown command");
cmd_read_register(msg); send_empty();
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();
}
} }
break; break;
} }
default: default:
@@ -150,7 +155,7 @@ std::string
GdbRsp::receive() { GdbRsp::receive() {
std::string msg = server.receive(1); std::string msg = server.receive(1);
char ch = msg[0]; char ch = msg[0];
int checksum = 0; uint checksum = 0;
if (ch == '$') { if (ch == '$') {
while ((ch = server.receive(1)[0]) != '#') { while ((ch = server.receive(1)[0]) != '#') {
@@ -179,7 +184,8 @@ GdbRsp::make_packet(std::string raw) {
void void
GdbRsp::acknowledge() { GdbRsp::acknowledge() {
server.send("+"); if (ack_mode)
server.send("+");
} }
void void
@@ -214,6 +220,9 @@ GdbRsp::cmd_supported(std::string msg) {
if (msg.find("hwbreak+;") != std::string::npos) if (msg.find("hwbreak+;") != std::string::npos)
response += "hwbreak+;"; response += "hwbreak+;";
// no acknowledgement mode
response += "QStartNoAckMode+";
gdb_log("sending response for qSupported"); gdb_log("sending response for qSupported");
server.send(make_packet(response)); server.send(make_packet(response));
} }
@@ -327,7 +336,6 @@ GdbRsp::cmd_write_register(std::string msg) {
void void
GdbRsp::cmd_read_memory(std::string msg) { GdbRsp::cmd_read_memory(std::string msg) {
std::string response; std::string response;
bool sequential = false;
static std::regex rgx("\\$m([0-9A-Fa-f]+),([0-9A-Fa-f]+)"); static std::regex rgx("\\$m([0-9A-Fa-f]+),([0-9A-Fa-f]+)");
std::smatch sm; std::smatch sm;
@@ -351,20 +359,15 @@ GdbRsp::cmd_read_memory(std::string msg) {
} }
for (uint i = 0; i < length; i++) { for (uint i = 0; i < length; i++) {
response += response += std::format("{:02x}", cpu->bus->read_byte(address + i));
std::format("{:02x}", cpu->bus->read_byte(address + i), sequential);
sequential = true;
} }
cpu->sequential = false;
gdb_log("sending memory values values"); gdb_log("sending memory values values");
server.send(make_packet(response)); server.send(make_packet(response));
} }
void void
GdbRsp::cmd_write_memory(std::string msg) { 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]+)"); static std::regex rgx("\\$M([0-9A-Fa-f]+),([0-9A-Fa-f]+):([0-9A-Fa-f]+)");
std::smatch sm; std::smatch sm;
regex_match(msg, sm, rgx); regex_match(msg, sm, rgx);
@@ -382,15 +385,10 @@ GdbRsp::cmd_write_memory(std::string msg) {
std::string values = sm[3].str(); std::string values = sm[3].str();
for (uint i = 0, j = 0; i < length && j < values.size(); i++, j += 2) { for (uint i = 0, j = 0; i < length && j < values.size(); i++, j += 2) {
cpu->bus->write_byte(address + i, cpu->bus->write_byte(
std::stoul(values.substr(j, 2), nullptr, 16) & address + i, std::stoul(values.substr(j, 2), nullptr, 16) & 0xFF);
0xFF,
sequential);
glogger.warn("hi {:02x}", cpu->bus->read_byte(address + i));
sequential = true;
} }
cpu->sequential = false;
gdb_log("register values written"); gdb_log("register values written");
send_ok(); send_ok();
} catch (const std::exception& e) { } catch (const std::exception& e) {

View File

@@ -21,6 +21,8 @@ class GdbRsp {
net::TcpServer server; net::TcpServer server;
std::string receive(); std::string receive();
std::string make_packet(std::string raw); std::string make_packet(std::string raw);
bool ack_mode = true;
void acknowledge(); void acknowledge();
void send_empty(); void send_empty();
void send_ok(); void send_ok();

View File

@@ -26,18 +26,12 @@ TEST_CASE("bios", TAG) {
auto bus = auto bus =
Bus::init(std::move(bios), std::vector<uint8_t>(Header::HEADER_SIZE)); 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(0) == 0xAC);
CHECK(bus->read_byte(0x3FFF) == 0x48); CHECK(bus->read_byte(0x3FFF) == 0x48);
CHECK(bus->read_byte(0x2A56) == 0x10); CHECK(bus->read_byte(0x2A56) == 0x10);
CHECK(bus->get_cycles() == cycles + 3);
} }
TEST_CASE_METHOD(BusFixture, "board wram", TAG) { TEST_CASE_METHOD(BusFixture, "board wram", TAG) {
uint32_t cycles = bus->get_cycles();
bus->write_byte(0x2000000, 0xAC); bus->write_byte(0x2000000, 0xAC);
CHECK(bus->read_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); bus->write_byte(0x2022A56, 0x10);
CHECK(bus->read_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) { TEST_CASE_METHOD(BusFixture, "chip wram", TAG) {
uint32_t cycles = bus->get_cycles();
bus->write_byte(0x3000000, 0xAC); bus->write_byte(0x3000000, 0xAC);
CHECK(bus->read_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); bus->write_byte(0x3002A56, 0x10);
CHECK(bus->read_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) { TEST_CASE_METHOD(BusFixture, "palette ram", TAG) {
uint32_t cycles = bus->get_cycles();
bus->write_byte(0x5000000, 0xAC); bus->write_byte(0x5000000, 0xAC);
CHECK(bus->read_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); bus->write_byte(0x5000156, 0x10);
CHECK(bus->read_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) { TEST_CASE_METHOD(BusFixture, "video ram", TAG) {
uint32_t cycles = bus->get_cycles();
bus->write_byte(0x6000000, 0xAC); bus->write_byte(0x6000000, 0xAC);
CHECK(bus->read_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); bus->write_byte(0x6012A56, 0x10);
CHECK(bus->read_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) { TEST_CASE_METHOD(BusFixture, "oam obj ram", TAG) {
uint32_t cycles = bus->get_cycles();
bus->write_byte(0x7000000, 0xAC); bus->write_byte(0x7000000, 0xAC);
CHECK(bus->read_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); bus->write_byte(0x7000156, 0x10);
CHECK(bus->read_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) { TEST_CASE("rom", TAG) {