yaze 0.2.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
5#include "app/emu/audio/apu.h"
8#include "app/emu/video/ppu.h"
9
10namespace yaze {
11namespace emu {
12
13namespace {
14void input_latch(Input* input, bool value) {
15 input->latch_line_ = value;
16 if (input->latch_line_) input->latched_state_ = input->current_state_;
17}
18
19uint8_t input_read(Input* input) {
20 if (input->latch_line_) input->latched_state_ = input->current_state_;
21 uint8_t ret = input->latched_state_ & 1;
22 input->latched_state_ >>= 1;
23 input->latched_state_ |= 0x8000;
24 return ret;
25}
26} // namespace
27
28void Snes::Init(std::vector<uint8_t>& rom_data) {
29 // Initialize the CPU, PPU, and APU
30 ppu_.Init();
31 apu_.Init();
32
33 // Load the ROM into memory and set up the memory mapping
34 memory_.Initialize(rom_data);
35 Reset(true);
36
37 running_ = true;
38}
39
40void Snes::Reset(bool hard) {
41 cpu_.Reset(hard);
42 apu_.Reset();
43 ppu_.Reset();
45 input1.latch_line_ = false;
46 input2.latch_line_ = false;
47 input1.latched_state_ = 0;
48 input2.latched_state_ = 0;
49 if (hard) memset(ram, 0, sizeof(ram));
50 ram_adr_ = 0;
51 memory_.set_h_pos(0);
52 memory_.set_v_pos(0);
53 frames_ = 0;
54 cycles_ = 0;
55 sync_cycle_ = 0;
57 h_irq_enabled_ = false;
58 v_irq_enabled_ = false;
59 nmi_enabled_ = false;
60 h_timer_ = 0x1ff * 4;
61 v_timer_ = 0x1ff;
62 in_nmi_ = false;
63 irq_condition_ = false;
64 in_irq_ = false;
65 in_vblank_ = false;
66 memset(port_auto_read_, 0, sizeof(port_auto_read_));
67 auto_joy_read_ = false;
69 ppu_latch_ = false;
70 multiply_a_ = 0xff;
71 multiply_result_ = 0xFE01;
72 divide_a_ = 0xffFF;
73 divide_result_ = 0x101;
74 fast_mem_ = false;
75 memory_.set_open_bus(0);
77 InitAccessTime(false);
78}
79
81 while (in_vblank_) {
82 cpu_.RunOpcode();
83 }
84 uint32_t frame = frames_;
85 while (!in_vblank_ && frame == frames_) {
86 cpu_.RunOpcode();
87 }
88}
89
90void Snes::CatchUpApu() { apu_.RunCycles(cycles_); }
91
93 memset(port_auto_read_, 0, sizeof(port_auto_read_));
94 // latch controllers
95 input_latch(&input1, true);
96 input_latch(&input2, true);
97 input_latch(&input1, false);
98 input_latch(&input2, false);
99 for (int i = 0; i < 16; i++) {
100 uint8_t val = input_read(&input1);
101 port_auto_read_[0] |= ((val & 1) << (15 - i));
102 port_auto_read_[2] |= (((val >> 1) & 1) << (15 - i));
103 val = input_read(&input2);
104 port_auto_read_[1] |= ((val & 1) << (15 - i));
105 port_auto_read_[3] |= (((val >> 1) & 1) << (15 - i));
106 }
107}
108
110 cycles_ += 2;
111
112 // check for h/v timer irq's
113 bool condition = ((v_irq_enabled_ || h_irq_enabled_) &&
114 (memory_.v_pos() == v_timer_ || !v_irq_enabled_) &&
115 (memory_.h_pos() == h_timer_ || !h_irq_enabled_));
116
117 if (!irq_condition_ && condition) {
118 in_irq_ = true;
119 cpu_.SetIrq(true);
120 }
121 irq_condition_ = condition;
122
123 // increment position; must come after irq checks! (hagane, cybernator)
124 memory_.set_h_pos(memory_.h_pos() + 2);
125
126 // handle positional stuff
127 if (memory_.h_pos() == next_horiz_event) {
128 switch (memory_.h_pos()) {
129 case 16: {
130 next_horiz_event = 512;
131 if (memory_.v_pos() == 0) memory_.init_hdma_request();
132 } break;
133 case 512: {
134 next_horiz_event = 1104;
135 // render the line halfway of the screen for better compatibility
136 if (!in_vblank_ && memory_.v_pos() > 0) ppu_.RunLine(memory_.v_pos());
137 } break;
138 case 1104: {
139 if (!in_vblank_) memory_.run_hdma_request();
140 if (!memory_.pal_timing()) {
141 // line 240 of odd frame with no interlace is 4 cycles shorter
142 next_horiz_event = (memory_.v_pos() == 240 && !ppu_.even_frame &&
143 !ppu_.frame_interlace)
144 ? 1360
145 : 1364;
146 } else {
147 // line 311 of odd frame with interlace is 4 cycles longer
148 next_horiz_event = (memory_.v_pos() != 311 || ppu_.even_frame ||
149 !ppu_.frame_interlace)
150 ? 1364
151 : 1368;
152 }
153 } break;
154 case 1360:
155 case 1364:
156 case 1368: { // this is the end (of the h-line)
157 next_horiz_event = 16;
158
159 memory_.set_h_pos(0);
160 memory_.set_v_pos(memory_.v_pos() + 1);
161 if (!memory_.pal_timing()) {
162 // even interlace frame is 263 lines
163 if ((memory_.v_pos() == 262 &&
164 (!ppu_.frame_interlace || !ppu_.even_frame)) ||
165 memory_.v_pos() == 263) {
166 memory_.set_v_pos(0);
167 frames_++;
168 }
169 } else {
170 // even interlace frame is 313 lines
171 if ((memory_.v_pos() == 312 &&
172 (!ppu_.frame_interlace || !ppu_.even_frame)) ||
173 memory_.v_pos() == 313) {
174 memory_.set_v_pos(0);
175 frames_++;
176 }
177 }
178
179 // end of hblank, do most memory_.v_pos()-tests
180 bool starting_vblank = false;
181 if (memory_.v_pos() == 0) {
182 // end of vblank
183 in_vblank_ = false;
184 in_nmi_ = false;
185 ppu_.HandleFrameStart();
186 } else if (memory_.v_pos() == 225) {
187 // ask the ppu if we start vblank now or at memory_.v_pos() 240
188 // (overscan)
189 starting_vblank = !ppu_.CheckOverscan();
190 } else if (memory_.v_pos() == 240) {
191 // if we are not yet in vblank, we had an overscan frame, set
192 // starting_vblank
193 if (!in_vblank_) starting_vblank = true;
194 }
195 if (starting_vblank) {
196 // catch up the apu at end of emulated frame (we end frame @ start of
197 // vblank)
198 CatchUpApu();
199 // notify dsp of frame-end, because sometimes dma will extend much
200 // further past vblank (or even into the next frame) Megaman X2
201 // (titlescreen animation), Tales of Phantasia (game demo), Actraiser
202 // 2 (fade-in @ bootup)
203 apu_.dsp().NewFrame();
204 // we are starting vblank
205 ppu_.HandleVblank();
206 in_vblank_ = true;
207 in_nmi_ = true;
208 if (auto_joy_read_) {
209 // TODO: this starts a little after start of vblank
210 auto_joy_timer_ = 4224;
211 HandleInput();
212 }
213 if (nmi_enabled_) {
214 cpu_.Nmi();
215 }
216 }
217 } break;
218 }
219 }
220 // handle auto_joy_read_-timer
221 if (auto_joy_timer_ > 0) auto_joy_timer_ -= 2;
222}
223
224void Snes::RunCycles(int cycles) {
225 if (memory_.h_pos() + cycles >= 536 && memory_.h_pos() < 536) {
226 // if we go past 536, add 40 cycles for dram refersh
227 cycles += 40;
228 }
229 for (int i = 0; i < cycles; i += 2) {
230 RunCycle();
231 }
232}
233
234void Snes::SyncCycles(bool start, int sync_cycles) {
235 int count = 0;
236 if (start) {
238 count = sync_cycles - (cycles_ % sync_cycles);
239 } else {
240 count = sync_cycles - ((cycles_ - sync_cycle_) % sync_cycles);
241 }
242 RunCycles(count);
243}
244
245uint8_t Snes::ReadBBus(uint8_t adr) {
246 if (adr < 0x40) {
247 return ppu_.Read(adr, ppu_latch_);
248 }
249 if (adr < 0x80) {
250 CatchUpApu(); // catch up the apu before reading
251 return apu_.out_ports_[adr & 0x3];
252 }
253 if (adr == 0x80) {
254 uint8_t ret = ram[ram_adr_++];
255 ram_adr_ &= 0x1ffff;
256 return ret;
257 }
258 return memory_.open_bus();
259}
260
261uint8_t Snes::ReadReg(uint16_t adr) {
262 switch (adr) {
263 case 0x4210: {
264 uint8_t val = 0x2; // CPU version (4 bit)
265 val |= in_nmi_ << 7;
266 in_nmi_ = false;
267 return val | (memory_.open_bus() & 0x70);
268 }
269 case 0x4211: {
270 uint8_t val = in_irq_ << 7;
271 in_irq_ = false;
272 cpu_.SetIrq(false);
273 return val | (memory_.open_bus() & 0x7f);
274 }
275 case 0x4212: {
276 uint8_t val = (auto_joy_timer_ > 0);
277 val |= (memory_.h_pos() < 4 || memory_.h_pos() >= 1096) << 6;
278 val |= in_vblank_ << 7;
279 return val | (memory_.open_bus() & 0x3e);
280 }
281 case 0x4213: {
282 return ppu_latch_ << 7; // IO-port
283 }
284 case 0x4214: {
285 return divide_result_ & 0xff;
286 }
287 case 0x4215: {
288 return divide_result_ >> 8;
289 }
290 case 0x4216: {
291 return multiply_result_ & 0xff;
292 }
293 case 0x4217: {
294 return multiply_result_ >> 8;
295 }
296 case 0x4218:
297 case 0x421a:
298 case 0x421c:
299 case 0x421e: {
300 return port_auto_read_[(adr - 0x4218) / 2] & 0xff;
301 }
302 case 0x4219:
303 case 0x421b:
304 case 0x421d:
305 case 0x421f: {
306 return port_auto_read_[(adr - 0x4219) / 2] >> 8;
307 }
308 default: {
309 return memory_.open_bus();
310 }
311 }
312}
313
314uint8_t Snes::Rread(uint32_t adr) {
315 uint8_t bank = adr >> 16;
316 adr &= 0xffff;
317 if (bank == 0x7e || bank == 0x7f) {
318 return ram[((bank & 1) << 16) | adr]; // ram
319 }
320 if (bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) {
321 if (adr < 0x2000) {
322 return ram[adr]; // ram mirror
323 }
324 if (adr >= 0x2100 && adr < 0x2200) {
325 return ReadBBus(adr & 0xff); // B-bus
326 }
327 if (adr == 0x4016) {
328 return input_read(&input1) | (memory_.open_bus() & 0xfc);
329 }
330 if (adr == 0x4017) {
331 return input_read(&input2) | (memory_.open_bus() & 0xe0) | 0x1c;
332 }
333 if (adr >= 0x4200 && adr < 0x4220) {
334 return ReadReg(adr); // internal registers
335 }
336 if (adr >= 0x4300 && adr < 0x4380) {
337 return ReadDma(&memory_, adr); // dma registers
338 }
339 }
340 // read from cart
341 return memory_.cart_read(bank, adr);
342}
343
344uint8_t Snes::Read(uint32_t adr) {
345 uint8_t val = Rread(adr);
346 memory_.set_open_bus(val);
347 return val;
348}
349
350void Snes::WriteBBus(uint8_t adr, uint8_t val) {
351 if (adr < 0x40) {
352 ppu_.Write(adr, val);
353 return;
354 }
355 if (adr < 0x80) {
356 CatchUpApu(); // catch up the apu before writing
357 apu_.in_ports_[adr & 0x3] = val;
358 return;
359 }
360 switch (adr) {
361 case 0x80: {
362 ram[ram_adr_++] = val;
363 ram_adr_ &= 0x1ffff;
364 break;
365 }
366 case 0x81: {
367 ram_adr_ = (ram_adr_ & 0x1ff00) | val;
368 break;
369 }
370 case 0x82: {
371 ram_adr_ = (ram_adr_ & 0x100ff) | (val << 8);
372 break;
373 }
374 case 0x83: {
375 ram_adr_ = (ram_adr_ & 0x0ffff) | ((val & 1) << 16);
376 break;
377 }
378 }
379}
380
381void Snes::WriteReg(uint16_t adr, uint8_t val) {
382 switch (adr) {
383 case 0x4200: {
384 auto_joy_read_ = val & 0x1;
386 h_irq_enabled_ = val & 0x10;
387 v_irq_enabled_ = val & 0x20;
389 in_irq_ = false;
390 cpu_.SetIrq(false);
391 }
392 // if nmi is enabled while in_nmi_ is still set, immediately generate nmi
393 if (!nmi_enabled_ && (val & 0x80) && in_nmi_) {
394 cpu_.Nmi();
395 }
396 nmi_enabled_ = val & 0x80;
397 cpu_.set_int_delay(true);
398 break;
399 }
400 case 0x4201: {
401 if (!(val & 0x80) && ppu_latch_) {
402 // latch the ppu
403 ppu_.LatchHV();
404 }
405 ppu_latch_ = val & 0x80;
406 break;
407 }
408 case 0x4202: {
409 multiply_a_ = val;
410 break;
411 }
412 case 0x4203: {
414 break;
415 }
416 case 0x4204: {
417 divide_a_ = (divide_a_ & 0xff00) | val;
418 break;
419 }
420 case 0x4205: {
421 divide_a_ = (divide_a_ & 0x00ff) | (val << 8);
422 break;
423 }
424 case 0x4206: {
425 if (val == 0) {
426 divide_result_ = 0xffff;
428 } else {
431 }
432 break;
433 }
434 case 0x4207: {
435 h_timer_ = (h_timer_ & 0x100) | val;
436 break;
437 }
438 case 0x4208: {
439 h_timer_ = (h_timer_ & 0x0ff) | ((val & 1) << 8);
440 break;
441 }
442 case 0x4209: {
443 v_timer_ = (v_timer_ & 0x100) | val;
444 break;
445 }
446 case 0x420a: {
447 v_timer_ = (v_timer_ & 0x0ff) | ((val & 1) << 8);
448 break;
449 }
450 case 0x420b: {
451 StartDma(&memory_, val, false);
452 break;
453 }
454 case 0x420c: {
455 StartDma(&memory_, val, true);
456 break;
457 }
458 case 0x420d: {
459 fast_mem_ = val & 0x1;
460 break;
461 }
462 default: {
463 break;
464 }
465 }
466}
467
468void Snes::Write(uint32_t adr, uint8_t val) {
469 memory_.set_open_bus(val);
470 uint8_t bank = adr >> 16;
471 adr &= 0xffff;
472 if (bank == 0x7e || bank == 0x7f) {
473 ram[((bank & 1) << 16) | adr] = val; // ram
474 }
475 if (bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) {
476 if (adr < 0x2000) {
477 ram[adr] = val; // ram mirror
478 }
479 if (adr >= 0x2100 && adr < 0x2200) {
480 WriteBBus(adr & 0xff, val); // B-bus
481 }
482 if (adr == 0x4016) {
483 input_latch(&input1, val & 1); // input latch
484 input_latch(&input2, val & 1);
485 }
486 if (adr >= 0x4200 && adr < 0x4220) {
487 WriteReg(adr, val); // internal registers
488 }
489 if (adr >= 0x4300 && adr < 0x4380) {
490 WriteDma(&memory_, adr, val); // dma registers
491 }
492 }
493
494 // write to cart
495 memory_.cart_write(bank, adr, val);
496}
497
498int Snes::GetAccessTime(uint32_t adr) {
499 uint8_t bank = adr >> 16;
500 adr &= 0xffff;
501 if ((bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) && adr < 0x8000) {
502 // 00-3f,80-bf:0-7fff
503 if (adr < 0x2000 || adr >= 0x6000) return 8; // 0-1fff, 6000-7fff
504 if (adr < 0x4000 || adr >= 0x4200) return 6; // 2000-3fff, 4200-5fff
505 return 12; // 4000-41ff
506 }
507 // 40-7f,co-ff:0000-ffff, 00-3f,80-bf:8000-ffff
508 return (fast_mem_ && bank >= 0x80) ? 6
509 : 8; // depends on setting in banks 80+
510}
511
512uint8_t Snes::CpuRead(uint32_t adr) {
513 cpu_.set_int_delay(false);
514 const int cycles = access_time[adr] - 4;
515 HandleDma(this, &memory_, cycles);
516 RunCycles(cycles);
517 uint8_t rv = Read(adr);
518 HandleDma(this, &memory_, 4);
519 RunCycles(4);
520 return rv;
521}
522
523void Snes::CpuWrite(uint32_t adr, uint8_t val) {
524 cpu_.set_int_delay(false);
525 const int cycles = access_time[adr];
526 HandleDma(this, &memory_, cycles);
527 RunCycles(cycles);
528 Write(adr, val);
529}
530
531void Snes::CpuIdle(bool waiting) {
532 cpu_.set_int_delay(false);
533 HandleDma(this, &memory_, 6);
534 RunCycles(6);
535}
536
537void Snes::SetSamples(int16_t* sample_data, int wanted_samples) {
538 apu_.dsp().GetSamples(sample_data, wanted_samples, memory_.pal_timing());
539}
540
541void Snes::SetPixels(uint8_t* pixel_data) { ppu_.PutPixels(pixel_data); }
542
543void Snes::SetButtonState(int player, int button, bool pressed) {
544 // set key in controller
545 if (player == 1) {
546 if (pressed) {
547 input1.current_state_ |= 1 << button;
548 } else {
549 input1.current_state_ &= ~(1 << button);
550 }
551 } else {
552 if (pressed) {
553 input2.current_state_ |= 1 << button;
554 } else {
555 input2.current_state_ &= ~(1 << button);
556 }
557 }
558}
559
560void Snes::InitAccessTime(bool recalc) {
561 int start = (recalc) ? 0x800000 : 0; // recalc only updates fast rom
562 access_time.resize(0x1000000);
563 for (int i = start; i < 0x1000000; i++) {
565 }
566}
567
568} // namespace emu
569} // namespace yaze
bool nmi_enabled_
Definition snes.h:93
uint32_t frames_
Definition snes.h:84
Input input1
Definition snes.h:108
void SetSamples(int16_t *sample_data, int wanted_samples)
Definition snes.cc:537
void CatchUpApu()
Definition snes.cc:90
uint8_t Rread(uint32_t adr)
Definition snes.cc:314
uint64_t cycles_
Definition snes.h:85
void RunCycles(int cycles)
Definition snes.cc:224
uint16_t v_timer_
Definition snes.h:95
uint16_t port_auto_read_[4]
Definition snes.h:110
void WriteBBus(uint8_t adr, uint8_t val)
Definition snes.cc:350
uint8_t CpuRead(uint32_t adr)
Definition snes.cc:512
void SetButtonState(int player, int button, bool pressed)
Definition snes.cc:543
uint8_t Read(uint32_t adr)
Definition snes.cc:344
uint16_t h_timer_
Definition snes.h:94
std::vector< uint8_t > access_time
Definition snes.h:50
bool irq_condition_
Definition snes.h:97
bool h_irq_enabled_
Definition snes.h:91
void Reset(bool hard=false)
Definition snes.cc:40
bool fast_mem_
Definition snes.h:115
void RunFrame()
Definition snes.cc:80
uint8_t ReadReg(uint16_t adr)
Definition snes.cc:261
void CpuIdle(bool waiting)
Definition snes.cc:531
uint16_t divide_result_
Definition snes.h:105
void WriteReg(uint16_t adr, uint8_t val)
Definition snes.cc:381
void CpuWrite(uint32_t adr, uint8_t val)
Definition snes.cc:523
bool v_irq_enabled_
Definition snes.h:92
bool in_nmi_
Definition snes.h:96
uint16_t multiply_result_
Definition snes.h:103
uint32_t next_horiz_event
Definition snes.h:88
bool in_irq_
Definition snes.h:98
uint8_t ReadBBus(uint8_t adr)
Definition snes.cc:245
bool running_
Definition snes.h:77
void Write(uint32_t adr, uint8_t val)
Definition snes.cc:468
uint64_t sync_cycle_
Definition snes.h:86
uint16_t divide_a_
Definition snes.h:104
void InitAccessTime(bool recalc)
Definition snes.cc:560
bool auto_joy_read_
Definition snes.h:111
bool in_vblank_
Definition snes.h:99
void SyncCycles(bool start, int sync_cycles)
Definition snes.cc:234
std::vector< uint8_t > rom_data
Definition snes.h:75
void Init(std::vector< uint8_t > &rom_data)
Definition snes.cc:28
Input input2
Definition snes.h:109
uint8_t multiply_a_
Definition snes.h:102
void SetPixels(uint8_t *pixel_data)
Definition snes.cc:541
uint8_t ram[0x20000]
Definition snes.h:80
uint16_t auto_joy_timer_
Definition snes.h:112
void HandleInput()
Definition snes.cc:92
MemoryImpl memory_
Definition snes.h:65
void RunCycle()
Definition snes.cc:109
bool ppu_latch_
Definition snes.h:113
uint32_t ram_adr_
Definition snes.h:81
double apu_catchup_cycles_
Definition snes.h:87
int GetAccessTime(uint32_t adr)
Definition snes.cc:498
void input_latch(Input *input, bool value)
Definition snes.cc:14
uint8_t input_read(Input *input)
Definition snes.cc:19
SNES Emulation and debugging tools.
Definition apu.cc:13
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:353
Main namespace for the application.
Definition controller.cc:18
uint16_t current_state_
Definition snes.h:18
bool latch_line_
Definition snes.h:16
uint16_t latched_state_
Definition snes.h:19