12#define WRITE_STATE(file, member) file.write(reinterpret_cast<const char*>(&member), sizeof(member))
13#define READ_STATE(file, member) file.read(reinterpret_cast<char*>(&member), sizeof(member))
37 LOG_DEBUG(
"SNES",
"Initializing emulator with ROM size %zu bytes",
rom_data.size());
51 LOG_DEBUG(
"SNES",
"Emulator initialization complete");
55 LOG_DEBUG(
"SNES",
"Reset called (hard=%d)", hard);
66 if (hard) memset(
ram, 0,
sizeof(
ram));
100 static int frame_log_count = 0;
101 if (frame_log_count % 60 == 0) {
102 LOG_DEBUG(
"SNES",
"Frame %d: CPU=$%02X:%04X vblank=%d frames_=%d",
108 static int vblank_loop_count = 0;
110 LOG_DEBUG(
"SNES",
"RunFrame: Entering vblank loop (in_vblank_=true)");
120 static int active_loop_count = 0;
121 if (!
in_vblank_ && active_loop_count++ < 10) {
122 LOG_DEBUG(
"SNES",
"RunFrame: Entering active frame loop (in_vblank_=false, frame=%d, frames_=%d)",
143 static int debug_count = 0;
145 LOG_DEBUG(
"SNES",
"HandleInput: current_state=0x%04X auto_joy_read_=%d (A button active)",
150 input_latch(&
input1,
true);
151 input_latch(&
input2,
true);
152 input_latch(&
input1,
false);
153 input_latch(&
input2,
false);
154 for (
int i = 0; i < 16; i++) {
155 uint8_t val = input_read(&
input1);
158 val = input_read(&
input2);
164 static int debug_result_count = 0;
166 if (debug_result_count++ < 30) {
167 LOG_DEBUG(
"SNES",
"HandleInput END: current_state=0x%04X, port_auto_read[0]=0x%04X (A button status)",
244 bool starting_vblank =
false;
247 static int vblank_end_count = 0;
248 if (vblank_end_count++ < 10) {
249 LOG_DEBUG(
"SNES",
"VBlank END - v_pos=0, setting in_vblank_=false at frame %d",
frames_);
263 if (starting_vblank) {
275 static int vblank_start_count = 0;
276 if (vblank_start_count++ < 10) {
277 LOG_DEBUG(
"SNES",
"VBlank START - v_pos=%d, setting in_vblank_=true at frame %d",
289 static int handle_input_log = 0;
291 LOG_DEBUG(
"SNES",
">>> VBLANK: HandleInput() done, port_auto_read[0]=0x%04X, about to call Nmi() <<<",
295 static int nmi_log_count = 0;
296 if (nmi_log_count++ < 10) {
297 LOG_DEBUG(
"SNES",
"VBlank NMI check: nmi_enabled_=%d, calling Nmi()=%s",
316 for (
int i = 0; i < cycles; i += 2) {
325 count = sync_cycles - (
cycles_ % sync_cycles);
340 static int cpu_port_read_count = 0;
341 static uint8_t last_f4 = 0xFF, last_f5 = 0xFF;
342 bool value_changed = ((adr & 0x3) == 0 && val != last_f4) || ((adr & 0x3) == 1 && val != last_f5);
343 if (value_changed || cpu_port_read_count++ < 5) {
344 LOG_DEBUG(
"SNES",
"CPU read APU port $21%02X (F%d) = $%02X at PC=$%02X:%04X [AFTER CatchUp: APU_cycles=%llu CPU_cycles=%llu]",
346 if ((adr & 0x3) == 0) last_f4 = val;
347 if ((adr & 0x3) == 1) last_f5 = val;
400 static int read_count = 0;
402 LOG_DEBUG(
"SNES",
">>> Game read $4218 = $%02X (port_auto_read[0]=$%04X, current=$%04X) at PC=$%02X:%04X <<<",
413 static int read_count = 0;
415 LOG_DEBUG(
"SNES",
">>> Game read $4219 = $%02X (port_auto_read[0]=$%04X, current=$%04X) at PC=$%02X:%04X <<<",
427 uint8_t bank = adr >> 16;
429 if (bank == 0x7e || bank == 0x7f) {
430 return ram[((bank & 1) << 16) | adr];
432 if (bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) {
436 if (adr >= 0x2100 && adr < 0x2200) {
446 if (adr >= 0x4200 && adr < 0x4220) {
448 static int rread_count = 0;
449 if ((adr == 0x4218 || adr == 0x4219) && rread_count++ < 100) {
450 LOG_DEBUG(
"SNES",
">>> Rread($%04X) from bank=$%02X PC=$%04X - calling ReadReg <<<",
455 if (adr >= 0x4300 && adr < 0x4380) {
464 uint8_t val =
Rread(adr);
479 uint32_t full_pc = (
static_cast<uint32_t
>(
cpu_.
PB) << 16) |
cpu_.
PC;
482 static int cpu_port_write_count = 0;
483 if (cpu_port_write_count++ < 10) {
484 LOG_DEBUG(
"SNES",
"CPU wrote APU port $21%02X (F%d) = $%02X at PC=$%02X:%04X",
485 0x40 + (adr & 0x3), (adr & 0x3) + 4, val,
cpu_.
PB,
cpu_.
PC);
518 static int write_4200_count = 0;
519 if (write_4200_count++ < 20) {
520 LOG_DEBUG(
"SNES",
"Write $%02X to $4200 at PC=$%02X:%04X (NMI=%d IRQ_H=%d IRQ_V=%d JOY=%d)",
521 val,
cpu_.
PB,
cpu_.
PC, (val & 0x80) ? 1 : 0, (val & 0x10) ? 1 : 0,
522 (val & 0x20) ? 1 : 0, (val & 0x01) ? 1 : 0);
529 static int auto_joy_log = 0;
530 static bool last_auto_joy =
false;
532 LOG_DEBUG(
"SNES",
">>> AUTO-JOY-READ %s at PC=$%02X:%04X <<<",
549 LOG_DEBUG(
"SNES",
">>> NMI enabled CHANGED: %d -> %d <<<",
625 uint8_t bank = adr >> 16;
627 if (bank == 0x7e || bank == 0x7f) {
628 ram[((bank & 1) << 16) | adr] = val;
630 if (bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) {
634 if (adr >= 0x2100 && adr < 0x2200) {
638 input_latch(&
input1, val & 1);
639 input_latch(&
input2, val & 1);
641 if (adr >= 0x4200 && adr < 0x4220) {
644 if (adr >= 0x4300 && adr < 0x4380) {
654 uint8_t bank = adr >> 16;
656 if ((bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) && adr < 0x8000) {
658 if (adr < 0x2000 || adr >= 0x6000)
return 8;
659 if (adr < 0x4000 || adr >= 0x4200)
return 6;
672 uint8_t rv =
Read(adr);
707 if (button < 0 || button > 11)
return;
721 std::ifstream file(path, std::ios::binary);
769 std::ofstream file(path, std::ios::binary);
774 uint32_t version = 1;
814 int start = (recalc) ? 0x800000 : 0;
816 for (
int i = start; i < 0x1000000; i++) {
void set_handshake_tracker(debug::ApuHandshakeTracker *tracker)
void RunCycles(uint64_t cycles)
std::array< uint8_t, 4 > out_ports_
uint64_t GetCycles() const
std::array< uint8_t, 6 > in_ports_
void set_int_delay(bool delay)
void Reset(bool hard=false)
auto open_bus() const -> uint8_t override
void set_h_pos(uint16_t value) override
void init_hdma_request() override
void set_v_pos(uint16_t value) override
auto v_pos() const -> uint16_t override
auto h_pos() const -> uint16_t override
void set_open_bus(uint8_t value) override
uint8_t cart_read(uint8_t bank, uint16_t adr)
void run_hdma_request() override
void cart_write(uint8_t bank, uint16_t adr, uint8_t val)
auto pal_timing() const -> bool override
void Initialize(const std::vector< uint8_t > &romData, bool verbose=false)
void Write(uint8_t adr, uint8_t val)
void PutPixels(uint8_t *pixel_data)
uint8_t Read(uint8_t adr, bool latch)
void SetSamples(int16_t *sample_data, int wanted_samples)
uint8_t Rread(uint32_t adr)
void RunCycles(int cycles)
uint16_t port_auto_read_[4]
void WriteBBus(uint8_t adr, uint8_t val)
uint8_t CpuRead(uint32_t adr)
void SetButtonState(int player, int button, bool pressed)
uint8_t Read(uint32_t adr)
void saveState(const std::string &path)
std::vector< uint8_t > access_time
debug::ApuHandshakeTracker apu_handshake_tracker_
void Reset(bool hard=false)
uint8_t ReadReg(uint16_t adr)
void CpuIdle(bool waiting)
void WriteReg(uint16_t adr, uint8_t val)
void CpuWrite(uint32_t adr, uint8_t val)
uint16_t multiply_result_
uint32_t next_horiz_event
uint8_t ReadBBus(uint8_t adr)
void Write(uint32_t adr, uint8_t val)
void InitAccessTime(bool recalc)
void loadState(const std::string &path)
void SyncCycles(bool start, int sync_cycles)
std::vector< uint8_t > rom_data
void Init(std::vector< uint8_t > &rom_data)
void SetPixels(uint8_t *pixel_data)
double apu_catchup_cycles_
int GetAccessTime(uint32_t adr)
void OnCpuPortWrite(uint8_t port, uint8_t value, uint32_t pc)
#define LOG_DEBUG(category, format,...)
void input_latch(Input *input, bool value)
uint8_t input_read(Input *input)
void ResetDma(MemoryImpl *memory)
void WriteDma(MemoryImpl *memory, uint16_t adr, uint8_t val)
void HandleDma(Snes *snes, MemoryImpl *memory, int cpu_cycles)
uint8_t ReadDma(MemoryImpl *memory, uint16_t adr)
void StartDma(MemoryImpl *memory, uint8_t val, bool hdma)
Main namespace for the application.
#define WRITE_STATE(file, member)
#define READ_STATE(file, member)