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