yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
snes.cc
Go to the documentation of this file.
1#include "app/emu/snes.h"
2
3#include <cstdint>
4#include <fstream>
5
6#include "app/emu/audio/apu.h"
9#include "app/emu/video/ppu.h"
10#include "util/log.h"
11
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))
14
15namespace yaze {
16namespace emu {
17
18namespace {
19void input_latch(Input* input, bool value) {
20 input->latch_line_ = value;
21 if (input->latch_line_) {
22 input->latched_state_ = input->current_state_;
23 }
24}
25
26uint8_t input_read(Input* input) {
27 if (input->latch_line_) input->latched_state_ = input->current_state_;
28 uint8_t ret = input->latched_state_ & 1;
29
30 input->latched_state_ >>= 1;
31 input->latched_state_ |= 0x8000;
32 return ret;
33}
34} // namespace
35
36void Snes::Init(std::vector<uint8_t>& rom_data) {
37 LOG_DEBUG("SNES", "Initializing emulator with ROM size %zu bytes", rom_data.size());
38
39 // Initialize the CPU, PPU, and APU
40 ppu_.Init();
41 apu_.Init();
42
43 // Connect handshake tracker to APU for debugging
45
46 // Load the ROM into memory and set up the memory mapping
48 Reset(true);
49
50 running_ = true;
51 LOG_DEBUG("SNES", "Emulator initialization complete");
52}
53
54void Snes::Reset(bool hard) {
55 LOG_DEBUG("SNES", "Reset called (hard=%d)", hard);
56 cpu_.Reset(hard);
57 apu_.Reset();
58 ppu_.Reset();
60 input1.latch_line_ = false;
61 input2.latch_line_ = false;
62 input1.current_state_ = 0; // Clear current button states
63 input2.current_state_ = 0; // Clear current button states
66 if (hard) memset(ram, 0, sizeof(ram));
67 ram_adr_ = 0;
70 frames_ = 0;
71 cycles_ = 0;
72 sync_cycle_ = 0;
74 h_irq_enabled_ = false;
75 v_irq_enabled_ = false;
76 nmi_enabled_ = false;
77 h_timer_ = 0x1ff * 4;
78 v_timer_ = 0x1ff;
79 in_nmi_ = false;
80 irq_condition_ = false;
81 in_irq_ = false;
82 in_vblank_ = false;
83 memset(port_auto_read_, 0, sizeof(port_auto_read_));
84 auto_joy_read_ = false;
86 ppu_latch_ = false;
87 multiply_a_ = 0xff;
88 multiply_result_ = 0xFE01;
89 divide_a_ = 0xffFF;
90 divide_result_ = 0x101;
91 fast_mem_ = false;
94 InitAccessTime(false);
95 LOG_DEBUG("SNES", "Reset complete - CPU will start at $%02X:%04X", cpu_.PB, cpu_.PC);
96}
97
99 // Debug: Log every 60th frame
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",
103 frame_log_count, cpu_.PB, cpu_.PC, in_vblank_, frames_);
104 }
105 frame_log_count++;
106
107 // Debug: Log vblank loop entry
108 static int vblank_loop_count = 0;
109 if (in_vblank_ && vblank_loop_count++ < 10) {
110 LOG_DEBUG("SNES", "RunFrame: Entering vblank loop (in_vblank_=true)");
111 }
112
113 while (in_vblank_) {
114 cpu_.RunOpcode();
115 }
116
117 uint32_t frame = frames_;
118
119 // Debug: Log active frame loop entry
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)",
123 frame, frames_);
124 }
125
126 while (!in_vblank_ && frame == frames_) {
127 cpu_.RunOpcode();
128 }
129}
130
132 // Bring APU up to the same master cycle count since last catch-up.
133 // cycles_ is monotonically increasing in RunCycle().
135}
136
138 // IMPORTANT: Clear and repopulate auto-read data
139 // This data persists until the next call, allowing NMI to read it
140 memset(port_auto_read_, 0, sizeof(port_auto_read_));
141
142 // Debug: Log input state when A button is active
143 static int debug_count = 0;
144 if ((input1.current_state_ & 0x0100) != 0 && debug_count++ < 30) {
145 LOG_DEBUG("SNES", "HandleInput: current_state=0x%04X auto_joy_read_=%d (A button active)",
147 }
148
149 // latch controllers
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);
156 port_auto_read_[0] |= ((val & 1) << (15 - i)); // Bits are read LSB first, stored MSB first
157 port_auto_read_[2] |= (((val >> 1) & 1) << (15 - i));
158 val = input_read(&input2);
159 port_auto_read_[1] |= ((val & 1) << (15 - i));
160 port_auto_read_[3] |= (((val >> 1) & 1) << (15 - i));
161 }
162
163 // Debug: Log auto-read result when A button was active
164 static int debug_result_count = 0;
165 if ((input1.current_state_ & 0x0100) != 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)",
169 }
170 }
171}
172
174 cycles_ += 2;
175
176 // check for h/v timer irq's
177 bool condition = ((v_irq_enabled_ || h_irq_enabled_) &&
180
181 if (!irq_condition_ && condition) {
182 in_irq_ = true;
183 cpu_.SetIrq(true);
184 }
185 irq_condition_ = condition;
186
187 // increment position; must come after irq checks! (hagane, cybernator)
189
190 // handle positional stuff
191 if (memory_.h_pos() == next_horiz_event) {
192 switch (memory_.h_pos()) {
193 case 16: {
194 next_horiz_event = 512;
196 } break;
197 case 512: {
198 next_horiz_event = 1104;
199 // render the line halfway of the screen for better compatibility
200 if (!in_vblank_ && memory_.v_pos() > 0) ppu_.RunLine(memory_.v_pos());
201 } break;
202 case 1104: {
204 if (!memory_.pal_timing()) {
205 // line 240 of odd frame with no interlace is 4 cycles shorter
206 next_horiz_event = (memory_.v_pos() == 240 && !ppu_.even_frame &&
208 ? 1360
209 : 1364;
210 } else {
211 // line 311 of odd frame with interlace is 4 cycles longer
214 ? 1364
215 : 1368;
216 }
217 } break;
218 case 1360:
219 case 1364:
220 case 1368: { // this is the end (of the h-line)
221 next_horiz_event = 16;
222
225 if (!memory_.pal_timing()) {
226 // even interlace frame is 263 lines
227 if ((memory_.v_pos() == 262 &&
229 memory_.v_pos() == 263) {
231 frames_++;
232 }
233 } else {
234 // even interlace frame is 313 lines
235 if ((memory_.v_pos() == 312 &&
237 memory_.v_pos() == 313) {
239 frames_++;
240 }
241 }
242
243 // end of hblank, do most memory_.v_pos()-tests
244 bool starting_vblank = false;
245 if (memory_.v_pos() == 0) {
246 // end of vblank
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_);
250 }
251 in_vblank_ = false;
252 in_nmi_ = false;
254 } else if (memory_.v_pos() == 225) {
255 // ask the ppu if we start vblank now or at memory_.v_pos() 240
256 // (overscan)
257 starting_vblank = !ppu_.CheckOverscan();
258 } else if (memory_.v_pos() == 240) {
259 // if we are not yet in vblank, we had an overscan frame, set
260 // starting_vblank
261 if (!in_vblank_) starting_vblank = true;
262 }
263 if (starting_vblank) {
264 // catch up the apu at end of emulated frame (we end frame @ start of
265 // vblank)
266 CatchUpApu();
267 // notify dsp of frame-end, because sometimes dma will extend much
268 // further past vblank (or even into the next frame) Megaman X2
269 // (titlescreen animation), Tales of Phantasia (game demo), Actraiser
270 // 2 (fade-in @ bootup)
271 apu_.dsp().NewFrame();
272 // we are starting vblank
274
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",
279 }
280
281 in_vblank_ = true;
282 in_nmi_ = true;
283 if (auto_joy_read_) {
284 // TODO: this starts a little after start of vblank
285 auto_joy_timer_ = 4224;
286 HandleInput();
287
288 // Debug: Log that we populated auto-read data BEFORE NMI
289 static int handle_input_log = 0;
290 if (handle_input_log++ < 50 && port_auto_read_[0] != 0) {
291 LOG_DEBUG("SNES", ">>> VBLANK: HandleInput() done, port_auto_read[0]=0x%04X, about to call Nmi() <<<",
292 port_auto_read_[0]);
293 }
294 }
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",
298 nmi_enabled_, nmi_enabled_ ? "YES" : "NO");
299 }
300 if (nmi_enabled_) {
301 cpu_.Nmi();
302 }
303 }
304 } break;
305 }
306 }
307 // handle auto_joy_read_-timer
308 if (auto_joy_timer_ > 0) auto_joy_timer_ -= 2;
309}
310
311void Snes::RunCycles(int cycles) {
312 if (memory_.h_pos() + cycles >= 536 && memory_.h_pos() < 536) {
313 // if we go past 536, add 40 cycles for dram refersh
314 cycles += 40;
315 }
316 for (int i = 0; i < cycles; i += 2) {
317 RunCycle();
318 }
319}
320
321void Snes::SyncCycles(bool start, int sync_cycles) {
322 int count = 0;
323 if (start) {
325 count = sync_cycles - (cycles_ % sync_cycles);
326 } else {
327 count = sync_cycles - ((cycles_ - sync_cycle_) % sync_cycles);
328 }
329 RunCycles(count);
330}
331
332uint8_t Snes::ReadBBus(uint8_t adr) {
333 if (adr < 0x40) {
334 return ppu_.Read(adr, ppu_latch_);
335 }
336 if (adr < 0x80) {
337 CatchUpApu(); // catch up the apu before reading
338 uint8_t val = apu_.out_ports_[adr & 0x3];
339 // Log port reads when value changes or during critical phase
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]",
345 0x40 + (adr & 0x3), (adr & 0x3) + 4, val, cpu_.PB, cpu_.PC, apu_.GetCycles(), cycles_);
346 if ((adr & 0x3) == 0) last_f4 = val;
347 if ((adr & 0x3) == 1) last_f5 = val;
348 }
349 return val;
350 }
351 if (adr == 0x80) {
352 uint8_t ret = ram[ram_adr_++];
353 ram_adr_ &= 0x1ffff;
354 return ret;
355 }
356 return memory_.open_bus();
357}
358
359uint8_t Snes::ReadReg(uint16_t adr) {
360 switch (adr) {
361 case 0x4210: {
362 uint8_t val = 0x2; // CPU version (4 bit)
363 val |= in_nmi_ << 7;
364 in_nmi_ = false;
365 return val | (memory_.open_bus() & 0x70);
366 }
367 case 0x4211: {
368 uint8_t val = in_irq_ << 7;
369 in_irq_ = false;
370 cpu_.SetIrq(false);
371 return val | (memory_.open_bus() & 0x7f);
372 }
373 case 0x4212: {
374 uint8_t val = (auto_joy_timer_ > 0);
375 val |= (memory_.h_pos() < 4 || memory_.h_pos() >= 1096) << 6;
376 val |= in_vblank_ << 7;
377 return val | (memory_.open_bus() & 0x3e);
378 }
379 case 0x4213: {
380 return ppu_latch_ << 7; // IO-port
381 }
382 case 0x4214: {
383 return divide_result_ & 0xff;
384 }
385 case 0x4215: {
386 return divide_result_ >> 8;
387 }
388 case 0x4216: {
389 return multiply_result_ & 0xff;
390 }
391 case 0x4217: {
392 return multiply_result_ >> 8;
393 }
394 case 0x4218:
395 case 0x421a:
396 case 0x421c:
397 case 0x421e: {
398 uint8_t result = port_auto_read_[(adr - 0x4218) / 2] & 0xff;
399 // Debug: Log reads when port_auto_read has data (non-zero)
400 static int read_count = 0;
401 if (adr == 0x4218 && port_auto_read_[0] != 0 && read_count++ < 200) {
402 LOG_DEBUG("SNES", ">>> Game read $4218 = $%02X (port_auto_read[0]=$%04X, current=$%04X) at PC=$%02X:%04X <<<",
404 }
405 return result;
406 }
407 case 0x4219:
408 case 0x421b:
409 case 0x421d:
410 case 0x421f: {
411 uint8_t result = port_auto_read_[(adr - 0x4219) / 2] >> 8;
412 // Debug: Log reads when port_auto_read has data (non-zero)
413 static int read_count = 0;
414 if (adr == 0x4219 && port_auto_read_[0] != 0 && read_count++ < 200) {
415 LOG_DEBUG("SNES", ">>> Game read $4219 = $%02X (port_auto_read[0]=$%04X, current=$%04X) at PC=$%02X:%04X <<<",
417 }
418 return result;
419 }
420 default: {
421 return memory_.open_bus();
422 }
423 }
424}
425
426uint8_t Snes::Rread(uint32_t adr) {
427 uint8_t bank = adr >> 16;
428 adr &= 0xffff;
429 if (bank == 0x7e || bank == 0x7f) {
430 return ram[((bank & 1) << 16) | adr]; // ram
431 }
432 if (bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) {
433 if (adr < 0x2000) {
434 return ram[adr]; // ram mirror
435 }
436 if (adr >= 0x2100 && adr < 0x2200) {
437 return ReadBBus(adr & 0xff); // B-bus
438 }
439 if (adr == 0x4016) {
440 uint8_t result = input_read(&input1) | (memory_.open_bus() & 0xfc);
441 return result;
442 }
443 if (adr == 0x4017) {
444 return input_read(&input2) | (memory_.open_bus() & 0xe0) | 0x1c;
445 }
446 if (adr >= 0x4200 && adr < 0x4220) {
447 // Debug: Log ANY reads to $4218/$4219 BEFORE calling ReadReg
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 <<<",
451 adr, bank, cpu_.PC);
452 }
453 return ReadReg(adr); // internal registers
454 }
455 if (adr >= 0x4300 && adr < 0x4380) {
456 return ReadDma(&memory_, adr); // dma registers
457 }
458 }
459 // read from cart
460 return memory_.cart_read(bank, adr);
461}
462
463uint8_t Snes::Read(uint32_t adr) {
464 uint8_t val = Rread(adr);
466 return val;
467}
468
469void Snes::WriteBBus(uint8_t adr, uint8_t val) {
470 if (adr < 0x40) {
471 ppu_.Write(adr, val);
472 return;
473 }
474 if (adr < 0x80) {
475 CatchUpApu(); // catch up the apu before writing
476 apu_.in_ports_[adr & 0x3] = val;
477
478 // Track CPU port writes for handshake debugging
479 uint32_t full_pc = (static_cast<uint32_t>(cpu_.PB) << 16) | cpu_.PC;
480 apu_handshake_tracker_.OnCpuPortWrite(adr & 0x3, val, full_pc);
481
482 static int cpu_port_write_count = 0;
483 if (cpu_port_write_count++ < 10) { // Reduced to prevent crash
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);
486 }
487
488 // NOTE: Auto-reset disabled - relying on complete IPL ROM with counter protocol
489 // The IPL ROM will handle multi-upload sequences via its transfer loop
490
491 return;
492 }
493 switch (adr) {
494 case 0x80: {
495 ram[ram_adr_++] = val;
496 ram_adr_ &= 0x1ffff;
497 break;
498 }
499 case 0x81: {
500 ram_adr_ = (ram_adr_ & 0x1ff00) | val;
501 break;
502 }
503 case 0x82: {
504 ram_adr_ = (ram_adr_ & 0x100ff) | (val << 8);
505 break;
506 }
507 case 0x83: {
508 ram_adr_ = (ram_adr_ & 0x0ffff) | ((val & 1) << 16);
509 break;
510 }
511 }
512}
513
514void Snes::WriteReg(uint16_t adr, uint8_t val) {
515 switch (adr) {
516 case 0x4200: {
517 // Log ALL writes to $4200 unconditionally
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);
523 }
524
525 auto_joy_read_ = val & 0x1;
527
528 // Debug: Log when auto-joy-read is enabled/disabled
529 static int auto_joy_log = 0;
530 static bool last_auto_joy = false;
531 if (auto_joy_read_ != last_auto_joy && auto_joy_log++ < 10) {
532 LOG_DEBUG("SNES", ">>> AUTO-JOY-READ %s at PC=$%02X:%04X <<<",
533 auto_joy_read_ ? "ENABLED" : "DISABLED", cpu_.PB, cpu_.PC);
534 last_auto_joy = auto_joy_read_;
535 }
536 h_irq_enabled_ = val & 0x10;
537 v_irq_enabled_ = val & 0x20;
539 in_irq_ = false;
540 cpu_.SetIrq(false);
541 }
542 // if nmi is enabled while in_nmi_ is still set, immediately generate nmi
543 if (!nmi_enabled_ && (val & 0x80) && in_nmi_) {
544 cpu_.Nmi();
545 }
546 bool old_nmi = nmi_enabled_;
547 nmi_enabled_ = val & 0x80;
548 if (old_nmi != nmi_enabled_) {
549 LOG_DEBUG("SNES", ">>> NMI enabled CHANGED: %d -> %d <<<",
550 old_nmi, nmi_enabled_);
551 }
552 cpu_.set_int_delay(true);
553 break;
554 }
555 case 0x4201: {
556 if (!(val & 0x80) && ppu_latch_) {
557 // latch the ppu
558 ppu_.LatchHV();
559 }
560 ppu_latch_ = val & 0x80;
561 break;
562 }
563 case 0x4202: {
564 multiply_a_ = val;
565 break;
566 }
567 case 0x4203: {
569 break;
570 }
571 case 0x4204: {
572 divide_a_ = (divide_a_ & 0xff00) | val;
573 break;
574 }
575 case 0x4205: {
576 divide_a_ = (divide_a_ & 0x00ff) | (val << 8);
577 break;
578 }
579 case 0x4206: {
580 if (val == 0) {
581 divide_result_ = 0xffff;
583 } else {
586 }
587 break;
588 }
589 case 0x4207: {
590 h_timer_ = (h_timer_ & 0x100) | val;
591 break;
592 }
593 case 0x4208: {
594 h_timer_ = (h_timer_ & 0x0ff) | ((val & 1) << 8);
595 break;
596 }
597 case 0x4209: {
598 v_timer_ = (v_timer_ & 0x100) | val;
599 break;
600 }
601 case 0x420a: {
602 v_timer_ = (v_timer_ & 0x0ff) | ((val & 1) << 8);
603 break;
604 }
605 case 0x420b: {
606 StartDma(&memory_, val, false);
607 break;
608 }
609 case 0x420c: {
610 StartDma(&memory_, val, true);
611 break;
612 }
613 case 0x420d: {
614 fast_mem_ = val & 0x1;
615 break;
616 }
617 default: {
618 break;
619 }
620 }
621}
622
623void Snes::Write(uint32_t adr, uint8_t val) {
625 uint8_t bank = adr >> 16;
626 adr &= 0xffff;
627 if (bank == 0x7e || bank == 0x7f) {
628 ram[((bank & 1) << 16) | adr] = val; // ram
629 }
630 if (bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) {
631 if (adr < 0x2000) {
632 ram[adr] = val; // ram mirror
633 }
634 if (adr >= 0x2100 && adr < 0x2200) {
635 WriteBBus(adr & 0xff, val); // B-bus
636 }
637 if (adr == 0x4016) {
638 input_latch(&input1, val & 1); // input latch
639 input_latch(&input2, val & 1);
640 }
641 if (adr >= 0x4200 && adr < 0x4220) {
642 WriteReg(adr, val); // internal registers
643 }
644 if (adr >= 0x4300 && adr < 0x4380) {
645 WriteDma(&memory_, adr, val); // dma registers
646 }
647 }
648
649 // write to cart
650 memory_.cart_write(bank, adr, val);
651}
652
653int Snes::GetAccessTime(uint32_t adr) {
654 uint8_t bank = adr >> 16;
655 adr &= 0xffff;
656 if ((bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) && adr < 0x8000) {
657 // 00-3f,80-bf:0-7fff
658 if (adr < 0x2000 || adr >= 0x6000) return 8; // 0-1fff, 6000-7fff
659 if (adr < 0x4000 || adr >= 0x4200) return 6; // 2000-3fff, 4200-5fff
660 return 12; // 4000-41ff
661 }
662 // 40-7f,co-ff:0000-ffff, 00-3f,80-bf:8000-ffff
663 return (fast_mem_ && bank >= 0x80) ? 6
664 : 8; // depends on setting in banks 80+
665}
666
667uint8_t Snes::CpuRead(uint32_t adr) {
668 cpu_.set_int_delay(false);
669 const int cycles = access_time[adr] - 4;
670 HandleDma(this, &memory_, cycles);
671 RunCycles(cycles);
672 uint8_t rv = Read(adr);
673 HandleDma(this, &memory_, 4);
674 RunCycles(4);
675 return rv;
676}
677
678void Snes::CpuWrite(uint32_t adr, uint8_t val) {
679 cpu_.set_int_delay(false);
680 const int cycles = access_time[adr];
681 HandleDma(this, &memory_, cycles);
682 RunCycles(cycles);
683 Write(adr, val);
684}
685
686void Snes::CpuIdle(bool waiting) {
687 cpu_.set_int_delay(false);
688 HandleDma(this, &memory_, 6);
689 RunCycles(6);
690}
691
692void Snes::SetSamples(int16_t* sample_data, int wanted_samples) {
693 apu_.dsp().GetSamples(sample_data, wanted_samples, memory_.pal_timing());
694}
695
696void Snes::SetPixels(uint8_t* pixel_data) { ppu_.PutPixels(pixel_data); }
697
698void Snes::SetButtonState(int player, int button, bool pressed) {
699 // Select the appropriate input based on player number
700 Input* input = (player == 1) ? &input1 : &input2;
701
702 // SNES controller button mapping (standard layout)
703 // Bit 0: B, Bit 1: Y, Bit 2: Select, Bit 3: Start
704 // Bit 4: Up, Bit 5: Down, Bit 6: Left, Bit 7: Right
705 // Bit 8: A, Bit 9: X, Bit 10: L, Bit 11: R
706
707 if (button < 0 || button > 11) return; // Validate button range
708
709 uint16_t old_state = input->current_state_;
710
711 if (pressed) {
712 // Set the button bit
713 input->current_state_ |= (1 << button);
714 } else {
715 // Clear the button bit
716 input->current_state_ &= ~(1 << button);
717 }
718}
719
720void Snes::loadState(const std::string& path) {
721 std::ifstream file(path, std::ios::binary);
722 if (!file) {
723 return;
724 }
725
726 uint32_t version;
727 READ_STATE(file, version);
728 if (version != 1) {
729 return;
730 }
731
732 // SNES state
733 READ_STATE(file, ram);
734 READ_STATE(file, ram_adr_);
735 READ_STATE(file, cycles_);
736 READ_STATE(file, sync_cycle_);
741 READ_STATE(file, h_timer_);
742 READ_STATE(file, v_timer_);
743 READ_STATE(file, in_nmi_);
745 READ_STATE(file, in_irq_);
746 READ_STATE(file, in_vblank_);
750 READ_STATE(file, ppu_latch_);
751 READ_STATE(file, multiply_a_);
753 READ_STATE(file, divide_a_);
755 READ_STATE(file, fast_mem_);
757
758 // CPU state
759 READ_STATE(file, cpu_);
760
761 // PPU state
762 READ_STATE(file, ppu_);
763
764 // APU state
765 READ_STATE(file, apu_);
766}
767
768void Snes::saveState(const std::string& path) {
769 std::ofstream file(path, std::ios::binary);
770 if (!file) {
771 return;
772 }
773
774 uint32_t version = 1;
775 WRITE_STATE(file, version);
776
777 // SNES state
778 WRITE_STATE(file, ram);
779 WRITE_STATE(file, ram_adr_);
780 WRITE_STATE(file, cycles_);
786 WRITE_STATE(file, h_timer_);
787 WRITE_STATE(file, v_timer_);
788 WRITE_STATE(file, in_nmi_);
790 WRITE_STATE(file, in_irq_);
791 WRITE_STATE(file, in_vblank_);
795 WRITE_STATE(file, ppu_latch_);
798 WRITE_STATE(file, divide_a_);
800 WRITE_STATE(file, fast_mem_);
802
803 // CPU state
804 WRITE_STATE(file, cpu_);
805
806 // PPU state
807 WRITE_STATE(file, ppu_);
808
809 // APU state
810 WRITE_STATE(file, apu_);
811}
812
813void Snes::InitAccessTime(bool recalc) {
814 int start = (recalc) ? 0x800000 : 0; // recalc only updates fast rom
815 access_time.resize(0x1000000);
816 for (int i = start; i < 0x1000000; i++) {
818 }
819}
820
821} // namespace emu
822} // namespace yaze
auto dsp() -> Dsp &
Definition apu.h:70
void Init()
Definition apu.cc:45
void Reset()
Definition apu.cc:52
void set_handshake_tracker(debug::ApuHandshakeTracker *tracker)
Definition apu.h:76
void RunCycles(uint64_t cycles)
Definition apu.cc:84
std::array< uint8_t, 4 > out_ports_
Definition apu.h:92
uint64_t GetCycles() const
Definition apu.h:73
std::array< uint8_t, 6 > in_ports_
Definition apu.h:91
void SetIrq(bool state)
Definition cpu.h:51
void Nmi()
Definition cpu.h:52
void set_int_delay(bool delay)
Definition cpu.h:79
uint16_t PC
Definition cpu.h:75
uint8_t PB
Definition cpu.h:74
void Reset(bool hard=false)
Definition cpu.cc:32
void RunOpcode()
Definition cpu.cc:54
auto open_bus() const -> uint8_t override
Definition memory.h:258
void set_h_pos(uint16_t value) override
Definition memory.h:274
void init_hdma_request() override
Definition memory.h:265
void set_v_pos(uint16_t value) override
Definition memory.h:275
auto v_pos() const -> uint16_t override
Definition memory.h:277
auto h_pos() const -> uint16_t override
Definition memory.h:276
void set_open_bus(uint8_t value) override
Definition memory.h:257
uint8_t cart_read(uint8_t bank, uint16_t adr)
Definition memory.cc:35
void run_hdma_request() override
Definition memory.h:266
void cart_write(uint8_t bank, uint16_t adr, uint8_t val)
Definition memory.cc:50
auto pal_timing() const -> bool override
Definition memory.h:278
void Initialize(const std::vector< uint8_t > &romData, bool verbose=false)
Definition memory.cc:12
void LatchHV()
Definition ppu.h:271
bool frame_interlace
Definition ppu.h:297
void Reset()
Definition ppu.cc:39
void HandleFrameStart()
Definition ppu.cc:137
void Write(uint8_t adr, uint8_t val)
Definition ppu.cc:747
void PutPixels(uint8_t *pixel_data)
Definition ppu.cc:1052
uint8_t Read(uint8_t adr, bool latch)
Definition ppu.cc:612
bool CheckOverscan()
Definition ppu.h:301
bool even_frame
Definition ppu.h:294
void Init()
Definition ppu.h:259
void HandleVblank()
Definition ppu.cc:602
void RunLine(int line)
Definition ppu.cc:145
bool nmi_enabled_
Definition snes.h:107
uint32_t frames_
Definition snes.h:98
Input input1
Definition snes.h:122
void SetSamples(int16_t *sample_data, int wanted_samples)
Definition snes.cc:692
void CatchUpApu()
Definition snes.cc:131
uint8_t Rread(uint32_t adr)
Definition snes.cc:426
uint64_t cycles_
Definition snes.h:99
void RunCycles(int cycles)
Definition snes.cc:311
uint16_t v_timer_
Definition snes.h:109
uint16_t port_auto_read_[4]
Definition snes.h:124
void WriteBBus(uint8_t adr, uint8_t val)
Definition snes.cc:469
uint8_t CpuRead(uint32_t adr)
Definition snes.cc:667
void SetButtonState(int player, int button, bool pressed)
Definition snes.cc:698
uint8_t Read(uint32_t adr)
Definition snes.cc:463
uint16_t h_timer_
Definition snes.h:108
void saveState(const std::string &path)
Definition snes.cc:768
std::vector< uint8_t > access_time
Definition snes.h:61
debug::ApuHandshakeTracker apu_handshake_tracker_
Definition snes.h:130
bool irq_condition_
Definition snes.h:111
bool h_irq_enabled_
Definition snes.h:105
void Reset(bool hard=false)
Definition snes.cc:54
bool fast_mem_
Definition snes.h:81
void RunFrame()
Definition snes.cc:98
uint8_t ReadReg(uint16_t adr)
Definition snes.cc:359
void CpuIdle(bool waiting)
Definition snes.cc:686
uint16_t divide_result_
Definition snes.h:119
void WriteReg(uint16_t adr, uint8_t val)
Definition snes.cc:514
void CpuWrite(uint32_t adr, uint8_t val)
Definition snes.cc:678
bool v_irq_enabled_
Definition snes.h:106
bool in_nmi_
Definition snes.h:110
uint16_t multiply_result_
Definition snes.h:117
uint32_t next_horiz_event
Definition snes.h:102
bool in_irq_
Definition snes.h:112
uint8_t ReadBBus(uint8_t adr)
Definition snes.cc:332
bool running_
Definition snes.h:91
void Write(uint32_t adr, uint8_t val)
Definition snes.cc:623
uint64_t sync_cycle_
Definition snes.h:100
uint16_t divide_a_
Definition snes.h:118
void InitAccessTime(bool recalc)
Definition snes.cc:813
bool auto_joy_read_
Definition snes.h:125
void loadState(const std::string &path)
Definition snes.cc:720
bool in_vblank_
Definition snes.h:113
void SyncCycles(bool start, int sync_cycles)
Definition snes.cc:321
std::vector< uint8_t > rom_data
Definition snes.h:89
void Init(std::vector< uint8_t > &rom_data)
Definition snes.cc:36
Input input2
Definition snes.h:123
uint8_t multiply_a_
Definition snes.h:116
void SetPixels(uint8_t *pixel_data)
Definition snes.cc:696
uint8_t ram[0x20000]
Definition snes.h:94
uint16_t auto_joy_timer_
Definition snes.h:126
void HandleInput()
Definition snes.cc:137
MemoryImpl memory_
Definition snes.h:84
void RunCycle()
Definition snes.cc:173
bool ppu_latch_
Definition snes.h:127
uint32_t ram_adr_
Definition snes.h:95
double apu_catchup_cycles_
Definition snes.h:101
int GetAccessTime(uint32_t adr)
Definition snes.cc:653
void OnCpuPortWrite(uint8_t port, uint8_t value, uint32_t pc)
#define LOG_DEBUG(category, format,...)
Definition log.h:104
void input_latch(Input *input, bool value)
Definition snes.cc:19
uint8_t input_read(Input *input)
Definition snes.cc:26
void ResetDma(MemoryImpl *memory)
Definition dma.cc:12
void WriteDma(MemoryImpl *memory, uint16_t adr, uint8_t val)
Definition dma.cc:92
void HandleDma(Snes *snes, MemoryImpl *memory, int cpu_cycles)
Definition dma.cc:190
uint8_t ReadDma(MemoryImpl *memory, uint16_t adr)
Definition dma.cc:39
void StartDma(MemoryImpl *memory, uint8_t val, bool hdma)
Definition dma.cc:356
Main namespace for the application.
#define WRITE_STATE(file, member)
Definition snes.cc:12
#define READ_STATE(file, member)
Definition snes.cc:13
uint16_t current_state_
Definition snes.h:21
bool latch_line_
Definition snes.h:19
uint16_t latched_state_
Definition snes.h:22