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