1 #include "nes.h"
2 
3 #include <cstdio>
4 
5 // The constructor creates the individual NES components, and "wires them up"
6 // to one antother.
NES(const NES_Params & params)7 NES::NES(const NES_Params& params) :
8 // RAM Modules
9 cpu_wram(0x800, "WRAM"),
10 ppu_vram(0x800, "CIRAM"),
11 ppu_pram(32, "Palette"),
12 // Processors
13 // (techincally UB since we pass references to objects that have not been
14 // initialized yet...)
15 cpu(params, this->cpu_mmu, this->interrupts),
16 apu(params, this->cpu_mmu, this->interrupts),
17 ppu(params,
18   this->ppu_mmu,
19   this->dma,
20   this->interrupts
21 ),
22 // Wiring
23 cpu_mmu(
24   /* ram */ this->cpu_wram,
25   /* ppu */ this->ppu,
26   /* apu */ this->apu,
27   /* joy */ this->joy
28 ),
29 ppu_mmu(
30   /* vram */ this->ppu_vram,
31   /* pram */ this->ppu_pram
32 ),
33 joy(),
34 dma(this->cpu_mmu),
35 interrupts(),
36 params(params)
37 {}
38 
updated_params()39 void NES::updated_params() {
40   this->apu.set_speed(this->params.speed / 100.0);
41 }
42 
loadCartridge(Mapper * cart)43 bool NES::loadCartridge(Mapper* cart) {
44   if (cart == nullptr)
45     return false;
46 
47   this->cart = cart;
48   this->cart->set_interrupt_line(&this->interrupts);
49 
50   this->cpu_mmu.loadCartridge(this->cart);
51   this->ppu_mmu.loadCartridge(this->cart);
52 
53   _callbacks.cart_changed.run(this->cart);
54 
55   return true;
56 }
57 
removeCartridge()58 void NES::removeCartridge() {
59   if (this->cart)
60     this->cart->set_interrupt_line(nullptr);
61   this->cart = nullptr;
62 
63   this->cpu_mmu.removeCartridge();
64   this->ppu_mmu.removeCartridge();
65 
66   _callbacks.cart_changed.run(nullptr);
67 }
68 
attach_joy(uint port,Memory * joy)69 void NES::attach_joy(uint port, Memory* joy) { this->joy.attach_joy(port, joy); }
detach_joy(uint port)70 void NES::detach_joy(uint port)              { this->joy.detach_joy(port);      }
71 
72 // Power Cycling initializes all the components to their "power on" state
power_cycle()73 void NES::power_cycle() {
74   if (this->params.apu_sample_rate == 0) {
75     fprintf(stderr, "[NES] Fatal Error! No APU sample rate defined. "
76                     "Check the NES_Params object passed to NES::NES()!\n");
77     assert(this->params.apu_sample_rate != 0);
78   }
79 
80   this->is_running = true;
81 
82   this->cpu_wram.clear();
83   this->ppu_pram.clear();
84   this->ppu_vram.clear();
85 
86   this->interrupts.clear();
87   this->interrupts.request(Interrupts::RESET);
88 
89   this->apu.power_cycle();
90   this->cpu.power_cycle();
91   this->ppu.power_cycle();
92 
93   if (this->cart)
94     this->cart->power_cycle();
95 
96   fprintf(stderr, "[NES] Power Cycled\n");
97 }
98 
reset()99 void NES::reset() {
100   this->is_running = true;
101 
102   this->interrupts.clear();
103   this->interrupts.request(Interrupts::RESET);
104 
105   // cpu_wram, ppu_pram, and ppu_vram are not affected by resets
106   // (i.e: they keep previous state)
107 
108   this->apu.reset();
109   this->cpu.reset();
110   this->ppu.reset();
111 
112   if (this->cart)
113     this->cart->reset();
114 
115   fprintf(stderr, "[NES] Reset\n");
116 }
117 
cycle()118 void NES::cycle() {
119   if (this->is_running == false) return;
120 
121   // Execute a CPU instruction
122   uint cpu_cycles = this->cpu.step();
123 
124   // Run APU 1x per cpu_cycle
125   for (uint i = 0; i < cpu_cycles; i++)
126     this->apu.cycle();
127 
128   if (this->apu.stall_cpu())
129     cpu_cycles += 4; // not entirely accurate... depends on other factors
130 
131   // Run PPU + Cartridge 3x per cpu_cycle
132   for (uint i = 0; i < cpu_cycles * 3; i++) {
133     this->ppu.cycle();
134     this->cart->cycle();
135   }
136 
137   if (!this->cpu.isRunning())
138     this->is_running = false;
139 }
140 
step_frame()141 void NES::step_frame() {
142   if (this->is_running == false) return;
143 
144   const uint curr_frame = this->ppu.getNumFrames();
145   while (this->is_running && this->ppu.getNumFrames() == curr_frame) {
146     this->cycle();
147   }
148 }
149 
getFramebuff(const u8 ** framebuffer) const150 void NES::getFramebuff(const u8** framebuffer) const {
151   this->ppu.getFramebuff(framebuffer);
152 }
153 
getAudiobuff(float ** samples,uint * len)154 void NES::getAudiobuff(float** samples, uint* len) {
155   this->apu.getAudiobuff(samples, len);
156 }
157