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