1 | #include <cstdlib> |
2 | #include <cstring> |
3 | #include <iostream> |
4 | #include "apu.hpp" |
5 | #include "cartridge.hpp" |
6 | #include "joypad.hpp" |
7 | #include "ppu.hpp" |
8 | #include "cpu.hpp" |
9 | |
10 | namespace CPU { |
11 | |
12 | |
13 | /* CPU state */ |
14 | u8 ram[0x800]; |
15 | u8 A, X, Y, S; |
16 | u16 PC; |
17 | Flags P; |
18 | bool nmi, irq; |
19 | |
20 | // Remaining clocks to end frame: |
21 | const int TOTAL_CYCLES = 29781; |
22 | int remainingCycles; |
23 | inline int elapsed() { return TOTAL_CYCLES - remainingCycles; } |
24 | |
25 | /* Cycle emulation */ |
26 | #define T tick() |
27 | inline void tick() { PPU::step(); PPU::step(); PPU::step(); remainingCycles--; } |
28 | |
29 | /* Flags updating */ |
30 | inline void upd_cv(u8 x, u8 y, s16 r) { P[C] = (r>0xFF); P[V] = ~(x^y) & (x^r) & 0x80; } |
31 | inline void upd_nz(u8 x) { P[N] = x & 0x80; P[Z] = (x == 0); } |
32 | // Does adding I to A cross a page? |
33 | inline bool cross(u16 a, u8 i) { return ((a+i) & 0xFF00) != ((a & 0xFF00)); } |
34 | |
35 | /* Memory access */ |
36 | void dma_oam(u8 bank); |
37 | template<bool wr> inline u8 access(u16 addr, u8 v = 0) |
38 | { |
39 | u8* r; |
40 | switch (addr) |
41 | { |
42 | case 0x0000 ... 0x1FFF: r = &ram[addr % 0x800]; if (wr) *r = v; return *r; // RAM. |
43 | case 0x2000 ... 0x3FFF: return PPU::access<wr>(addr % 8, v); // PPU. |
44 | |
45 | // APU: |
46 | case 0x4000 ... 0x4013: |
47 | case 0x4015: return APU::access<wr>(elapsed(), addr, v); |
48 | case 0x4017: if (wr) return APU::access<wr>(elapsed(), addr, v); |
49 | else return Joypad::read_state(1); // Joypad 1. |
50 | |
51 | case 0x4014: if (wr) dma_oam(v); break; // OAM DMA. |
52 | case 0x4016: if (wr) { Joypad::write_strobe(v & 1); break; } // Joypad strobe. |
53 | else return Joypad::read_state(0); // Joypad 0. |
54 | case 0x4018 ... 0xFFFF: return Cartridge::access<wr>(addr, v); // Cartridge. |
55 | } |
56 | return 0; |
57 | } |
58 | inline u8 wr(u16 a, u8 v) { T; return access<1>(a, v); } |
59 | inline u8 rd(u16 a) { T; return access<0>(a); } |
60 | inline u16 rd16_d(u16 a, u16 b) { return rd(a) | (rd(b) << 8); } // Read from A and B and merge. |
61 | inline u16 rd16(u16 a) { return rd16_d(a, a+1); } |
62 | inline u8 push(u8 v) { return wr(0x100 + (S--), v); } |
63 | inline u8 pop() { return rd(0x100 + (++S)); } |
64 | void dma_oam(u8 bank) { for (int i = 0; i < 256; i++) wr(0x2014, rd(bank*0x100 + i)); } |
65 | |
66 | /* Addressing modes */ |
67 | inline u16 imm() { return PC++; } |
68 | inline u16 imm16() { PC += 2; return PC - 2; } |
69 | inline u16 abs() { return rd16(imm16()); } |
70 | inline u16 _abx() { T; return abs() + X; } // Exception. |
71 | inline u16 abx() { u16 a = abs(); if (cross(a, X)) T; return a + X; } |
72 | inline u16 aby() { u16 a = abs(); if (cross(a, Y)) T; return a + Y; } |
73 | inline u16 zp() { return rd(imm()); } |
74 | inline u16 zpx() { T; return (zp() + X) % 0x100; } |
75 | inline u16 zpy() { T; return (zp() + Y) % 0x100; } |
76 | inline u16 izx() { u8 i = zpx(); return rd16_d(i, (i+1) % 0x100); } |
77 | inline u16 _izy() { u8 i = zp(); return rd16_d(i, (i+1) % 0x100) + Y; } // Exception. |
78 | inline u16 izy() { u16 a = _izy(); if (cross(a-Y, Y)) T; return a; } |
79 | |
80 | /* STx */ |
81 | template<u8& r, Mode m> void st() { wr( m() , r); } |
82 | template<> void st<A,izy>() { T; wr(_izy() , A); } // Exceptions. |
83 | template<> void st<A,abx>() { T; wr( abs() + X, A); } // ... |
84 | template<> void st<A,aby>() { T; wr( abs() + Y, A); } // ... |
85 | |
86 | #define G u16 a = m(); u8 p = rd(a) /* Fetch parameter */ |
87 | template<u8& r, Mode m> void ld() { G; upd_nz(r = p); } // LDx |
88 | template<u8& r, Mode m> void cmp() { G; upd_nz(r - p); P[C] = (r >= p); } // CMP, CPx |
89 | /* Arithmetic and bitwise */ |
90 | template<Mode m> void ADC() { G ; s16 r = A + p + P[C]; upd_cv(A, p, r); upd_nz(A = r); } |
91 | template<Mode m> void SBC() { G ^ 0xFF; s16 r = A + p + P[C]; upd_cv(A, p, r); upd_nz(A = r); } |
92 | template<Mode m> void BIT() { G; P[Z] = !(A & p); P[N] = p & 0x80; P[V] = p & 0x40; } |
93 | template<Mode m> void AND() { G; upd_nz(A &= p); } |
94 | template<Mode m> void EOR() { G; upd_nz(A ^= p); } |
95 | template<Mode m> void ORA() { G; upd_nz(A |= p); } |
96 | /* Read-Modify-Write */ |
97 | template<Mode m> void ASL() { G; P[C] = p & 0x80; T; upd_nz(wr(a, p << 1)); } |
98 | template<Mode m> void LSR() { G; P[C] = p & 0x01; T; upd_nz(wr(a, p >> 1)); } |
99 | template<Mode m> void ROL() { G; u8 c = P[C] ; P[C] = p & 0x80; T; upd_nz(wr(a, (p << 1) | c) ); } |
100 | template<Mode m> void ROR() { G; u8 c = P[C] << 7; P[C] = p & 0x01; T; upd_nz(wr(a, c | (p >> 1)) ); } |
101 | template<Mode m> void DEC() { G; T; upd_nz(wr(a, --p)); } |
102 | template<Mode m> void INC() { G; T; upd_nz(wr(a, ++p)); } |
103 | #undef G |
104 | |
105 | /* DEx, INx */ |
106 | template<u8& r> void dec() { upd_nz(--r); T; } |
107 | template<u8& r> void inc() { upd_nz(++r); T; } |
108 | /* Bit shifting on the accumulator */ |
109 | void ASL_A() { P[C] = A & 0x80; upd_nz(A <<= 1); T; } |
110 | void LSR_A() { P[C] = A & 0x01; upd_nz(A >>= 1); T; } |
111 | void ROL_A() { u8 c = P[C] ; P[C] = A & 0x80; upd_nz(A = ((A << 1) | c) ); T; } |
112 | void ROR_A() { u8 c = P[C] << 7; P[C] = A & 0x01; upd_nz(A = (c | (A >> 1)) ); T; } |
113 | |
114 | /* Txx (move values between registers) */ |
115 | template<u8& s, u8& d> void tr() { upd_nz(d = s); T; } |
116 | template<> void tr<X,S>() { S = X; T; } // TSX, exception. |
117 | |
118 | /* Stack operations */ |
119 | void PLP() { T; T; P.set(pop()); } |
120 | void PHP() { T; push(P.get() | (1 << 4)); } // B flag set. |
121 | void PLA() { T; T; A = pop(); upd_nz(A); } |
122 | void PHA() { T; push(A); } |
123 | |
124 | /* Flow control (branches, jumps) */ |
125 | template<Flag f, bool v> void br() |
126 | { |
127 | s8 j = rd(imm()); |
128 | if (P[f] == v) { |
129 | if (cross(PC, j)) T; |
130 | T; PC += j; |
131 | } |
132 | } |
133 | void JMP_IND() { u16 i = rd16(imm16()); PC = rd16_d(i, (i&0xFF00) | ((i+1) % 0x100)); } |
134 | void JMP() { PC = rd16(imm16()); } |
135 | void JSR() { u16 t = PC+1; T; push(t >> 8); push(t); PC = rd16(imm16()); } |
136 | |
137 | /* Return instructions */ |
138 | void RTS() { T; T; PC = (pop() | (pop() << 8)) + 1; T; } |
139 | void RTI() { PLP(); PC = pop() | (pop() << 8); } |
140 | |
141 | template<Flag f, bool v> void flag() { P[f] = v; T; } // Clear and set flags. |
142 | template<IntType t> void INT() |
143 | { |
144 | T; if (t != BRK) T; // BRK already performed the fetch. |
145 | if (t != RESET) // Writes on stack are inhibited on RESET. |
146 | { |
147 | push(PC >> 8); push(PC & 0xFF); |
148 | push(P.get() | ((t == BRK) << 4)); // Set B if BRK. |
149 | } |
150 | else { S -= 3; T; T; T; } |
151 | P[I] = true; |
152 | /* NMI Reset IRQ BRK */ |
153 | constexpr u16 vect[] = { 0xFFFA, 0xFFFC, 0xFFFE, 0xFFFE }; |
154 | PC = rd16(vect[t]); |
155 | if (t == NMI) nmi = false; |
156 | } |
157 | void NOP() { T; } |
158 | |
159 | /* Execute a CPU instruction */ |
160 | void exec() |
161 | { |
162 | switch (rd(PC++)) // Fetch the opcode. |
163 | { |
164 | // Select the right function to emulate the instruction: |
165 | case 0x00: return INT<BRK>() ; case 0x01: return ORA<izx>() ; |
166 | case 0x05: return ORA<zp>() ; case 0x06: return ASL<zp>() ; |
167 | case 0x08: return PHP() ; case 0x09: return ORA<imm>() ; |
168 | case 0x0A: return ASL_A() ; case 0x0D: return ORA<abs>() ; |
169 | case 0x0E: return ASL<abs>() ; case 0x10: return br<N,0>() ; |
170 | case 0x11: return ORA<izy>() ; case 0x15: return ORA<zpx>() ; |
171 | case 0x16: return ASL<zpx>() ; case 0x18: return flag<C,0>() ; |
172 | case 0x19: return ORA<aby>() ; case 0x1D: return ORA<abx>() ; |
173 | case 0x1E: return ASL<_abx>() ; case 0x20: return JSR() ; |
174 | case 0x21: return AND<izx>() ; case 0x24: return BIT<zp>() ; |
175 | case 0x25: return AND<zp>() ; case 0x26: return ROL<zp>() ; |
176 | case 0x28: return PLP() ; case 0x29: return AND<imm>() ; |
177 | case 0x2A: return ROL_A() ; case 0x2C: return BIT<abs>() ; |
178 | case 0x2D: return AND<abs>() ; case 0x2E: return ROL<abs>() ; |
179 | case 0x30: return br<N,1>() ; case 0x31: return AND<izy>() ; |
180 | case 0x35: return AND<zpx>() ; case 0x36: return ROL<zpx>() ; |
181 | case 0x38: return flag<C,1>() ; case 0x39: return AND<aby>() ; |
182 | case 0x3D: return AND<abx>() ; case 0x3E: return ROL<_abx>() ; |
183 | case 0x40: return RTI() ; case 0x41: return EOR<izx>() ; |
184 | case 0x45: return EOR<zp>() ; case 0x46: return LSR<zp>() ; |
185 | case 0x48: return PHA() ; case 0x49: return EOR<imm>() ; |
186 | case 0x4A: return LSR_A() ; case 0x4C: return JMP() ; |
187 | case 0x4D: return EOR<abs>() ; case 0x4E: return LSR<abs>() ; |
188 | case 0x50: return br<V,0>() ; case 0x51: return EOR<izy>() ; |
189 | case 0x55: return EOR<zpx>() ; case 0x56: return LSR<zpx>() ; |
190 | case 0x58: return flag<I,0>() ; case 0x59: return EOR<aby>() ; |
191 | case 0x5D: return EOR<abx>() ; case 0x5E: return LSR<_abx>() ; |
192 | case 0x60: return RTS() ; case 0x61: return ADC<izx>() ; |
193 | case 0x65: return ADC<zp>() ; case 0x66: return ROR<zp>() ; |
194 | case 0x68: return PLA() ; case 0x69: return ADC<imm>() ; |
195 | case 0x6A: return ROR_A() ; case 0x6C: return JMP_IND() ; |
196 | case 0x6D: return ADC<abs>() ; case 0x6E: return ROR<abs>() ; |
197 | case 0x70: return br<V,1>() ; case 0x71: return ADC<izy>() ; |
198 | case 0x75: return ADC<zpx>() ; case 0x76: return ROR<zpx>() ; |
199 | case 0x78: return flag<I,1>() ; case 0x79: return ADC<aby>() ; |
200 | case 0x7D: return ADC<abx>() ; case 0x7E: return ROR<_abx>() ; |
201 | case 0x81: return st<A,izx>() ; case 0x84: return st<Y,zp>() ; |
202 | case 0x85: return st<A,zp>() ; case 0x86: return st<X,zp>() ; |
203 | case 0x88: return dec<Y>() ; case 0x8A: return tr<X,A>() ; |
204 | case 0x8C: return st<Y,abs>() ; case 0x8D: return st<A,abs>() ; |
205 | case 0x8E: return st<X,abs>() ; case 0x90: return br<C,0>() ; |
206 | case 0x91: return st<A,izy>() ; case 0x94: return st<Y,zpx>() ; |
207 | case 0x95: return st<A,zpx>() ; case 0x96: return st<X,zpy>() ; |
208 | case 0x98: return tr<Y,A>() ; case 0x99: return st<A,aby>() ; |
209 | case 0x9A: return tr<X,S>() ; case 0x9D: return st<A,abx>() ; |
210 | case 0xA0: return ld<Y,imm>() ; case 0xA1: return ld<A,izx>() ; |
211 | case 0xA2: return ld<X,imm>() ; case 0xA4: return ld<Y,zp>() ; |
212 | case 0xA5: return ld<A,zp>() ; case 0xA6: return ld<X,zp>() ; |
213 | case 0xA8: return tr<A,Y>() ; case 0xA9: return ld<A,imm>() ; |
214 | case 0xAA: return tr<A,X>() ; case 0xAC: return ld<Y,abs>() ; |
215 | case 0xAD: return ld<A,abs>() ; case 0xAE: return ld<X,abs>() ; |
216 | case 0xB0: return br<C,1>() ; case 0xB1: return ld<A,izy>() ; |
217 | case 0xB4: return ld<Y,zpx>() ; case 0xB5: return ld<A,zpx>() ; |
218 | case 0xB6: return ld<X,zpy>() ; case 0xB8: return flag<V,0>() ; |
219 | case 0xB9: return ld<A,aby>() ; case 0xBA: return tr<S,X>() ; |
220 | case 0xBC: return ld<Y,abx>() ; case 0xBD: return ld<A,abx>() ; |
221 | case 0xBE: return ld<X,aby>() ; case 0xC0: return cmp<Y,imm>(); |
222 | case 0xC1: return cmp<A,izx>(); case 0xC4: return cmp<Y,zp>() ; |
223 | case 0xC5: return cmp<A,zp>() ; case 0xC6: return DEC<zp>() ; |
224 | case 0xC8: return inc<Y>() ; case 0xC9: return cmp<A,imm>(); |
225 | case 0xCA: return dec<X>() ; case 0xCC: return cmp<Y,abs>(); |
226 | case 0xCD: return cmp<A,abs>(); case 0xCE: return DEC<abs>() ; |
227 | case 0xD0: return br<Z,0>() ; case 0xD1: return cmp<A,izy>(); |
228 | case 0xD5: return cmp<A,zpx>(); case 0xD6: return DEC<zpx>() ; |
229 | case 0xD8: return flag<D,0>() ; case 0xD9: return cmp<A,aby>(); |
230 | case 0xDD: return cmp<A,abx>(); case 0xDE: return DEC<_abx>() ; |
231 | case 0xE0: return cmp<X,imm>(); case 0xE1: return SBC<izx>() ; |
232 | case 0xE4: return cmp<X,zp>() ; case 0xE5: return SBC<zp>() ; |
233 | case 0xE6: return INC<zp>() ; case 0xE8: return inc<X>() ; |
234 | case 0xE9: return SBC<imm>() ; case 0xEA: return NOP() ; |
235 | case 0xEC: return cmp<X,abs>(); case 0xED: return SBC<abs>() ; |
236 | case 0xEE: return INC<abs>() ; case 0xF0: return br<Z,1>() ; |
237 | case 0xF1: return SBC<izy>() ; case 0xF5: return SBC<zpx>() ; |
238 | case 0xF6: return INC<zpx>() ; case 0xF8: return flag<D,1>() ; |
239 | case 0xF9: return SBC<aby>() ; case 0xFD: return SBC<abx>() ; |
240 | case 0xFE: return INC<_abx>() ; |
241 | default: |
242 | std::cout << "Invalid Opcode! PC: " << PC << " Opcode: 0x" << std::hex << (int)(rd(PC - 1)) << "\n" ; |
243 | return NOP(); |
244 | } |
245 | } |
246 | |
247 | void set_nmi(bool v) { nmi = v; } |
248 | void set_irq(bool v) { irq = v; } |
249 | |
250 | int dmc_read(void*, cpu_addr_t addr) { return access<0>(addr); } |
251 | |
252 | /* Turn on the CPU */ |
253 | void power() |
254 | { |
255 | remainingCycles = 0; |
256 | |
257 | P.set(0x04); |
258 | A = X = Y = S = 0x00; |
259 | memset(ram, 0xFF, sizeof(ram)); |
260 | |
261 | nmi = irq = false; |
262 | INT<RESET>(); |
263 | } |
264 | |
265 | /* Run the CPU for roughly a frame */ |
266 | void run_frame() |
267 | { |
268 | remainingCycles += TOTAL_CYCLES; |
269 | |
270 | while (remainingCycles > 0) |
271 | { |
272 | if (nmi) INT<NMI>(); |
273 | else if (irq and !P[I]) INT<IRQ>(); |
274 | |
275 | exec(); |
276 | } |
277 | |
278 | APU::run_frame(elapsed()); |
279 | } |
280 | |
281 | |
282 | } |
283 | |