1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-21 21:33:54 +00:00

Add a 2Mhz tick for timers.

This commit is contained in:
Thomas Harte 2024-03-07 11:12:40 -05:00
parent a0f0f73bde
commit ae3cd924e8

View File

@ -151,6 +151,38 @@ struct Video {
};
struct Interrupts {
// TODO: timers, which decrement at 2Mhz.
void tick_timers() {
}
bool read(uint32_t address, uint8_t &value) {
switch(address & 0x7f) {
default: break;
case 0x10: // IRQ status A
value = irq_status_a();
return true;
case 0x20: // IRQ status B
value = 0x00;
return true;
case 0x30: // FIQ status
value = 0x80;
return true;
}
logger.error().append("TODO: IO controller read from %08x", address);
return false;
}
bool write(uint32_t address, uint8_t value) {
logger.error().append("TODO: IO controller write of %02x at %08x", value, address);
return false;
}
private:
uint8_t irq_status_a() const {
return irq_status_a_;
}
@ -210,9 +242,7 @@ struct Memory {
return true;
} break;
case Zone::IOControllers:
logger.error().append("TODO: Write to IO controllers of %08x to %08x", source, address);
break;
case Zone::IOControllers: return ioc_.write(address, source);
case Zone::VideoController:
// TODO: handle byte writes correctly.
@ -273,24 +303,19 @@ struct Memory {
source = high_rom<IntT>(address);
return true;
case Zone::IOControllers:
switch(address & 0x7f) {
default: break;
case 0x10: // IRQ status A
source = ioc_.irq_status_a();
return true;
case 0x20: // IRQ status B
source = 0x00;
return true;
case 0x30: // FIQ status
source = 0x80;
case Zone::IOControllers: {
if constexpr (std::is_same_v<IntT, uint8_t>) {
return ioc_.read(address, source);
} else {
// TODO: generalise this adaptation of an 8-bit device to the 32-bit bus, which probably isn't right anyway.
uint8_t value;
if(!ioc_.read(address, value)) {
return false;
}
source = value;
return true;
}
logger.error().append("TODO: IO controller read from %08x", address);
break;
}
default:
logger.error().append("TODO: read from %08x", address);
@ -306,6 +331,10 @@ struct Memory {
update_mapping();
}
void tick_timers() {
ioc_.tick_timers();
}
private:
bool has_moved_rom_ = false;
std::array<uint8_t, 4*1024*1024> ram_{};
@ -522,11 +551,20 @@ class ConcreteMachine:
public MachineTypes::TimedMachine,
public MachineTypes::ScanProducer
{
// TODO: pick a sensible clock rate; this is just code for '20 MIPS, please'.
static constexpr int ClockRate = 20'000'000;
// Timers tick at 2Mhz, so figure out the proper divider for that.
static constexpr int TimerTarget = ClockRate / 2'000'000;
int timer_divider_ = TimerTarget;
public:
ConcreteMachine(
const Analyser::Static::Target &target,
const ROMMachine::ROMFetcher &rom_fetcher
) {
set_clock_rate(ClockRate);
constexpr ROM::Name risc_os = ROM::Name::AcornRISCOS319;
ROM::Request request(risc_os);
auto roms = rom_fetcher(request);
@ -535,10 +573,6 @@ class ConcreteMachine:
}
executor_.bus.set_rom(roms.find(risc_os)->second);
// TODO: pick a sensible clock rate; this is just code for '20 MIPS, please'.
set_clock_rate(20'000'000);
insert_media(target.media);
}
@ -557,25 +591,39 @@ class ConcreteMachine:
static uint32_t last_pc = 0;
auto instructions = cycles.as<int>();
while(instructions--) {
uint32_t instruction;
if(!executor_.bus.read(executor_.pc(), instruction, executor_.registers().mode(), false)) {
logger.info().append("Prefetch abort at %08x; last good was at %08x", executor_.pc(), last_pc);
executor_.prefetch_abort();
// TODO: does a double abort cause a reset?
executor_.bus.read(executor_.pc(), instruction, executor_.registers().mode(), false);
} else {
last_pc = executor_.pc();
while(instructions) {
auto run_length = std::min(timer_divider_, instructions);
instructions -= run_length;
timer_divider_ -= run_length;
while(run_length--) {
uint32_t instruction;
if(!executor_.bus.read(executor_.pc(), instruction, executor_.registers().mode(), false)) {
logger.info().append("Prefetch abort at %08x; last good was at %08x", executor_.pc(), last_pc);
executor_.prefetch_abort();
// TODO: does a double abort cause a reset?
executor_.bus.read(executor_.pc(), instruction, executor_.registers().mode(), false);
} else {
last_pc = executor_.pc();
}
// TODO: pipeline prefetch?
// static bool log = false;
// if(log) {
// logger.info().append("%08x: %08x [r14:%08x]", executor_.pc(), instruction, executor_.registers()[14]);
// }
InstructionSet::ARM::execute<arm_model>(instruction, executor_);
}
// TODO: pipeline prefetch?
// static bool log = false;
// if(log) {
// logger.info().append("%08x: %08x [r14:%08x]", executor_.pc(), instruction, executor_.registers()[14]);
// }
InstructionSet::ARM::execute<arm_model>(instruction, executor_);
if(!timer_divider_) {
executor_.bus.tick_timers();
timer_divider_ = TimerTarget;
}
}
}
// MARK: - MediaTarget