21static constexpr uint64_t kApuCyclesNumerator = 32040 * 32;
22static constexpr uint64_t kApuCyclesDenominator =
26static constexpr uint64_t kApuCyclesNumeratorPal = 32040 * 32;
27static constexpr uint64_t kApuCyclesDenominatorPal =
37static const uint8_t bootRom[0x40] = {
38 0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0, 0xfc, 0x8f, 0xaa,
39 0xf4, 0x8f, 0xbb, 0xf5, 0x78, 0xcc, 0xf4, 0xd0, 0xfb, 0x2f, 0x19,
40 0xeb, 0xf4, 0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5, 0xcb,
41 0xf4, 0xd7, 0x00, 0xfc, 0xd0, 0xf3, 0xab, 0x01, 0x10, 0xef, 0x7e,
42 0xf4, 0x10, 0xeb, 0xba, 0xf6, 0xda, 0x00, 0xba, 0xf4, 0xc4, 0xf4,
43 0xdd, 0x5d, 0xd0, 0xdb, 0x1f, 0x00, 0x00, 0xc0, 0xff};
49 for (
int i = 0; i < 0x10000; i++) {
58 for (
int i = 0; i < 0x10000; i++) {
63 LOG_INFO(
"APU",
"Init: Num=%llu, Den=%llu, Ratio=%.4f", kApuCyclesNumerator, kApuCyclesDenominator, (
double)kApuCyclesNumerator / kApuCyclesDenominator);
71 for (
int i = 0; i < 3; i++) {
84 LOG_DEBUG(
"APU",
"Reset complete - IPL ROM readable, PC will be at $%04X",
101 uint64_t denominator =
104 const uint64_t target_apu_cycles =
105 cycles_ + (master_delta * numerator) / denominator;
108 static uint64_t last_debug_log = 0;
109 static uint64_t total_master_delta = 0;
110 static uint64_t total_apu_cycles_run = 0;
111 static int call_count = 0;
113 uint64_t expected_this_call = (master_delta * numerator) / denominator;
114 total_master_delta += master_delta;
118 static int verbose_log_count = 0;
119 if (verbose_log_count < 10 || (call_count % 1000 == 0)) {
120 LOG_INFO(
"APU",
"RunCycles ENTRY: master_delta=%llu, expected=%llu, cycles_=%llu, target=%llu",
121 master_delta, expected_this_call,
cycles_, target_apu_cycles);
126 static uint64_t last_log_cycle = 0;
127 static uint16_t last_pc = 0;
128 static int stuck_counter = 0;
132 static bool logged_transfer_state =
false;
134 while (
cycles_ < target_apu_cycles) {
143 static uint64_t last_ipl_log = 0;
144 if (
rom_readable_ && current_pc >= 0xFFD6 && current_pc <= 0xFFED) {
145 if (
cycles_ - last_ipl_log > 10000) {
147 "IPL ROM loop: PC=$%04X Y=$%02X Ports: F4=$%02X F5=$%02X "
152 " Out ports: F4=$%02X F5=$%02X F6=$%02X F7=$%02X ZP: "
153 "$00=$%02X $01=$%02X",
161 if (current_pc == last_pc) {
163 if (stuck_counter > 10000 &&
cycles_ - last_log_cycle > 10000) {
164 LOG_DEBUG(
"APU",
"SPC700 stuck at PC=$%04X for %d iterations",
165 current_pc, stuck_counter);
166 LOG_DEBUG(
"APU",
"Port Status: F4=$%02X F5=$%02X F6=$%02X F7=$%02X",
168 LOG_DEBUG(
"APU",
"Out Ports: F4=$%02X F5=$%02X F6=$%02X F7=$%02X",
173 if (!logged_transfer_state &&
ram[0x00] == 0x19 &&
ram[0x01] == 0x00) {
174 LOG_DEBUG(
"APU",
"Uploaded byte at $0019 = $%02X",
ram[0x0019]);
175 logged_transfer_state =
true;
183 last_pc = current_pc;
196 for (
int i = 0; i < spc_cycles; ++i) {
202 uint64_t apu_actually_run =
cycles_ - apu_before;
203 total_apu_cycles_run += apu_actually_run;
206 if (verbose_log_count <= 10) {
207 LOG_INFO(
"APU",
"RunCycles EXIT: ran=%llu, expected=%llu, overshoot=%lld, cycles_=%llu",
208 apu_actually_run, expected_this_call,
209 (int64_t)apu_actually_run - (int64_t)expected_this_call,
214 if (
cycles_ - last_debug_log > 1000000) {
215 uint64_t expected_apu = (total_master_delta * numerator) / denominator;
216 double ratio = (double)total_apu_cycles_run / (
double)expected_apu;
217 LOG_INFO(
"APU",
"TIMING: calls=%d, master_delta=%llu, expected_apu=%llu, actual_apu=%llu, ratio=%.2fx",
218 call_count, total_master_delta, expected_apu, total_apu_cycles_run, ratio);
220 total_master_delta = 0;
221 total_apu_cycles_run = 0;
233 for (
int i = 0; i < 3; i++) {
234 if (
timer_[i].cycles == 0) {
235 timer_[i].cycles = i == 2 ? 16 : 128;
254 static int port_read_count = 0;
289 uint8_t ret =
timer_[adr - 0xfd].counter;
290 timer_[adr - 0xfd].counter = 0;
295 return bootRom[adr - 0xffc0];
301 static int port_write_count = 0;
309 for (
int i = 0; i < 3; i++) {
310 if (!
timer_[i].enabled && (val & (1 << i))) {
314 timer_[i].enabled = val & (1 << i);
328 "Control register $F1 = $%02X - IPL ROM %s at PC=$%04X", val,
366 if (port_write_count < 10) {
369 "SPC wrote port $%04X (F%d) = $%02X at PC=$%04X [APU_cycles=%llu]",
381 if (
ram[0x00] < 0x80) {
385 "Detected small transfer start: dest=$%02X%02X, size=%d",
404 LOG_INFO(
"APU",
"Timer 0 Target set to %d ($%02X)", val, val);
429 stream.write(
reinterpret_cast<const char*
>(&
cycles_),
sizeof(
cycles_));
433 stream.write(
reinterpret_cast<const char*
>(
timer_.data()),
sizeof(
timer_));
438 constexpr uint32_t kMaxRamSize = 0x10000;
439 uint32_t ram_size =
static_cast<uint32_t
>(std::min<uint32_t>(
ram.size(), kMaxRamSize));
440 stream.write(
reinterpret_cast<const char*
>(&ram_size),
sizeof(ram_size));
442 stream.write(
reinterpret_cast<const char*
>(
ram.data()), ram_size *
sizeof(uint8_t));
456 stream.read(
reinterpret_cast<char*
>(
timer_.data()),
sizeof(
timer_));
462 stream.read(
reinterpret_cast<char*
>(&ram_size),
sizeof(ram_size));
463 constexpr uint32_t kMaxRamSize = 0x10000;
464 uint32_t safe_size = std::min<uint32_t>(ram_size, kMaxRamSize);
465 ram.resize(safe_size);
467 stream.read(
reinterpret_cast<char*
>(
ram.data()), safe_size *
sizeof(uint8_t));
469 if (ram_size > safe_size) {
470 std::vector<char> discard((ram_size - safe_size) *
sizeof(uint8_t));
471 stream.read(discard.data(), discard.size());
479 LOG_INFO(
"APU",
"BootstrapDirect: Setting PC to $%04X", entry_point);
506 for (
int i = 0; i < 4; i++) {
515 LOG_INFO(
"APU",
"BootstrapDirect complete: IPL ROM disabled, driver ready at $%04X",
std::array< Timer, 3 > timer_
uint8_t SpcRead(uint16_t address)
uint8_t Read(uint16_t address)
void LoadState(std::istream &stream)
debug::ApuHandshakeTracker * handshake_tracker_
void Write(uint16_t address, uint8_t data)
void SpcIdle(bool waiting)
uint64_t last_master_cycles_
void RunCycles(uint64_t cycles)
void SpcWrite(uint16_t address, uint8_t data)
std::vector< uint8_t > ram
std::array< uint8_t, 4 > out_ports_
void BootstrapDirect(uint16_t entry_point)
Bootstrap SPC directly to driver code (bypasses IPL ROM handshake)
std::array< uint8_t, 6 > in_ports_
void SaveState(std::ostream &stream)
uint8_t Read(uint8_t adr)
void Write(uint8_t adr, uint8_t val)
void LoadState(std::istream &stream)
void SaveState(std::ostream &stream)
auto pal_timing() const -> bool override
void Reset(bool hard=false)
void LoadState(std::istream &stream)
void SaveState(std::ostream &stream)
uint16_t read_word(uint16_t address)
void OnSpcPCChange(uint16_t old_pc, uint16_t new_pc)
void OnSpcPortWrite(uint8_t port, uint8_t value, uint16_t pc)
#define LOG_DEBUG(category, format,...)
#define LOG_INFO(category, format,...)
SDL2/SDL3 compatibility layer.