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