| 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 |  | 
|---|