1 #include "stdafx.h"
2 #include "CPU.h"
3 #include "PPU.h"
4 #include "APU.h"
5 #include "DeltaModulationChannel.h"
6 #include "Debugger.h"
7 #include "NsfMapper.h"
8 #include "Console.h"
9
CPU(shared_ptr<Console> console)10 CPU::CPU(shared_ptr<Console> console)
11 {
12 _console = console;
13 _memoryManager = _console->GetMemoryManager();
14
15 Func opTable[] = {
16 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
17 &CPU::BRK, &CPU::ORA, &CPU::HLT, &CPU::SLO, &CPU::NOP, &CPU::ORA, &CPU::ASL_Memory, &CPU::SLO, &CPU::PHP, &CPU::ORA, &CPU::ASL_Acc, &CPU::AAC, &CPU::NOP, &CPU::ORA, &CPU::ASL_Memory, &CPU::SLO, //0
18 &CPU::BPL, &CPU::ORA, &CPU::HLT, &CPU::SLO, &CPU::NOP, &CPU::ORA, &CPU::ASL_Memory, &CPU::SLO, &CPU::CLC, &CPU::ORA, &CPU::NOP, &CPU::SLO, &CPU::NOP, &CPU::ORA, &CPU::ASL_Memory, &CPU::SLO, //1
19 &CPU::JSR, &CPU::AND, &CPU::HLT, &CPU::RLA, &CPU::BIT, &CPU::AND, &CPU::ROL_Memory, &CPU::RLA, &CPU::PLP, &CPU::AND, &CPU::ROL_Acc, &CPU::AAC, &CPU::BIT, &CPU::AND, &CPU::ROL_Memory, &CPU::RLA, //2
20 &CPU::BMI, &CPU::AND, &CPU::HLT, &CPU::RLA, &CPU::NOP, &CPU::AND, &CPU::ROL_Memory, &CPU::RLA, &CPU::SEC, &CPU::AND, &CPU::NOP, &CPU::RLA, &CPU::NOP, &CPU::AND, &CPU::ROL_Memory, &CPU::RLA, //3
21 &CPU::RTI, &CPU::EOR, &CPU::HLT, &CPU::SRE, &CPU::NOP, &CPU::EOR, &CPU::LSR_Memory, &CPU::SRE, &CPU::PHA, &CPU::EOR, &CPU::LSR_Acc, &CPU::ASR, &CPU::JMP_Abs, &CPU::EOR, &CPU::LSR_Memory, &CPU::SRE, //4
22 &CPU::BVC, &CPU::EOR, &CPU::HLT, &CPU::SRE, &CPU::NOP, &CPU::EOR, &CPU::LSR_Memory, &CPU::SRE, &CPU::CLI, &CPU::EOR, &CPU::NOP, &CPU::SRE, &CPU::NOP, &CPU::EOR, &CPU::LSR_Memory, &CPU::SRE, //5
23 &CPU::RTS, &CPU::ADC, &CPU::HLT, &CPU::RRA, &CPU::NOP, &CPU::ADC, &CPU::ROR_Memory, &CPU::RRA, &CPU::PLA, &CPU::ADC, &CPU::ROR_Acc, &CPU::ARR, &CPU::JMP_Ind, &CPU::ADC, &CPU::ROR_Memory, &CPU::RRA, //6
24 &CPU::BVS, &CPU::ADC, &CPU::HLT, &CPU::RRA, &CPU::NOP, &CPU::ADC, &CPU::ROR_Memory, &CPU::RRA, &CPU::SEI, &CPU::ADC, &CPU::NOP, &CPU::RRA, &CPU::NOP, &CPU::ADC, &CPU::ROR_Memory, &CPU::RRA, //7
25 &CPU::NOP, &CPU::STA, &CPU::NOP, &CPU::SAX, &CPU::STY, &CPU::STA, &CPU::STX, &CPU::SAX, &CPU::DEY, &CPU::NOP, &CPU::TXA, &CPU::UNK, &CPU::STY, &CPU::STA, &CPU::STX, &CPU::SAX, //8
26 &CPU::BCC, &CPU::STA, &CPU::HLT, &CPU::AXA, &CPU::STY, &CPU::STA, &CPU::STX, &CPU::SAX, &CPU::TYA, &CPU::STA, &CPU::TXS, &CPU::TAS, &CPU::SYA, &CPU::STA, &CPU::SXA, &CPU::AXA, //9
27 &CPU::LDY, &CPU::LDA, &CPU::LDX, &CPU::LAX, &CPU::LDY, &CPU::LDA, &CPU::LDX, &CPU::LAX, &CPU::TAY, &CPU::LDA, &CPU::TAX, &CPU::ATX, &CPU::LDY, &CPU::LDA, &CPU::LDX, &CPU::LAX, //A
28 &CPU::BCS, &CPU::LDA, &CPU::HLT, &CPU::LAX, &CPU::LDY, &CPU::LDA, &CPU::LDX, &CPU::LAX, &CPU::CLV, &CPU::LDA, &CPU::TSX, &CPU::LAS, &CPU::LDY, &CPU::LDA, &CPU::LDX, &CPU::LAX, //B
29 &CPU::CPY, &CPU::CPA, &CPU::NOP, &CPU::DCP, &CPU::CPY, &CPU::CPA, &CPU::DEC, &CPU::DCP, &CPU::INY, &CPU::CPA, &CPU::DEX, &CPU::AXS, &CPU::CPY, &CPU::CPA, &CPU::DEC, &CPU::DCP, //C
30 &CPU::BNE, &CPU::CPA, &CPU::HLT, &CPU::DCP, &CPU::NOP, &CPU::CPA, &CPU::DEC, &CPU::DCP, &CPU::CLD, &CPU::CPA, &CPU::NOP, &CPU::DCP, &CPU::NOP, &CPU::CPA, &CPU::DEC, &CPU::DCP, //D
31 &CPU::CPX, &CPU::SBC, &CPU::NOP, &CPU::ISB, &CPU::CPX, &CPU::SBC, &CPU::INC, &CPU::ISB, &CPU::INX, &CPU::SBC, &CPU::NOP, &CPU::SBC, &CPU::CPX, &CPU::SBC, &CPU::INC, &CPU::ISB, //E
32 &CPU::BEQ, &CPU::SBC, &CPU::HLT, &CPU::ISB, &CPU::NOP, &CPU::SBC, &CPU::INC, &CPU::ISB, &CPU::SED, &CPU::SBC, &CPU::NOP, &CPU::ISB, &CPU::NOP, &CPU::SBC, &CPU::INC, &CPU::ISB //F
33 };
34
35 typedef AddrMode M;
36 AddrMode addrMode[] = {
37 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
38 M::Imp, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //0
39 M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//1
40 M::Abs, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //2
41 M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//3
42 M::Imp, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //4
43 M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//5
44 M::Imp, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Ind, M::Abs, M::Abs, M::Abs, //6
45 M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//7
46 M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //8
47 M::Rel, M::IndYW, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroY, M::ZeroY, M::Imp, M::AbsYW,M::Imp, M::AbsYW,M::AbsXW,M::AbsXW,M::AbsYW,M::AbsYW,//9
48 M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //A
49 M::Rel, M::IndY, M::None, M::IndY, M::ZeroX, M::ZeroX, M::ZeroY, M::ZeroY, M::Imp, M::AbsY, M::Imp, M::AbsY, M::AbsX, M::AbsX, M::AbsY, M::AbsY, //B
50 M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //C
51 M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//D
52 M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //E
53 M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//F
54 };
55
56 memcpy(_opTable, opTable, sizeof(opTable));
57 memcpy(_addrMode, addrMode, sizeof(addrMode));
58
59 _instAddrMode = AddrMode::None;
60 _state = {};
61 _cycleCount = 0;
62 _operand = 0;
63 _spriteDmaCounter = 0;
64 _spriteDmaTransfer = false;
65 _dmcCounter = 0;
66 _dmcDmaRunning = false;
67 _cpuWrite = false;
68 _writeAddr = 0;
69 _irqMask = 0;
70 _state = {};
71 _prevRunIrq = false;
72 _runIrq = false;
73 }
74
Reset(bool softReset,NesModel model)75 void CPU::Reset(bool softReset, NesModel model)
76 {
77 _state.NMIFlag = false;
78 _state.IRQFlag = 0;
79 _cycleCount = -1;
80
81 _spriteDmaTransfer = false;
82 _spriteDmaCounter = 0;
83
84 _dmcCounter = -1;
85 _dmcDmaRunning = false;
86 _warnOnCrash = true;
87
88 //Used by NSF code to disable Frame Counter & DMC interrupts
89 _irqMask = 0xFF;
90
91 //Use _memoryManager->Read() directly to prevent clocking the PPU/APU when setting PC at reset
92 _state.PC = _memoryManager->Read(CPU::ResetVector) | _memoryManager->Read(CPU::ResetVector+1) << 8;
93 _state.DebugPC = _state.PC;
94 _state.PreviousDebugPC = _state.PC;
95
96 if(softReset) {
97 SetFlags(PSFlags::Interrupt);
98 _state.SP -= 0x03;
99 } else {
100 _state.A = 0;
101 _state.SP = 0xFD;
102 _state.X = 0;
103 _state.Y = 0;
104 _state.PS = PSFlags::Interrupt;
105
106 _runIrq = false;
107 }
108
109 //The CPU takes some cycles before starting its execution after a reset/power up
110 for(int i = 0; i < (model == NesModel::NTSC ? 28 : 30); i++) {
111 _console->GetPpu()->Exec();
112 }
113
114 for(int i = 0; i < 10; i++) {
115 _console->GetApu()->ProcessCpuClock();
116 }
117 }
118
Exec()119 void CPU::Exec()
120 {
121 uint8_t opCode = GetOPCode();
122 _instAddrMode = _addrMode[opCode];
123 _operand = FetchOperand();
124 (this->*_opTable[opCode])();
125
126 if(_prevRunIrq) {
127 IRQ();
128 }
129 }
130
IRQ()131 void CPU::IRQ()
132 {
133 #ifndef DUMMYCPU
134 uint16_t originalPc = PC();
135 #endif
136
137 DummyRead(); //fetch opcode (and discard it - $00 (BRK) is forced into the opcode register instead)
138 DummyRead(); //read next instruction byte (actually the same as above, since PC increment is suppressed. Also discarded.)
139 Push((uint16_t)(PC()));
140
141 if(_state.NMIFlag) {
142 Push((uint8_t)(PS() | PSFlags::Reserved));
143 SetFlags(PSFlags::Interrupt);
144
145 SetPC(MemoryReadWord(CPU::NMIVector));
146 _state.NMIFlag = false;
147
148 #ifndef DUMMYCPU
149 _console->DebugAddTrace("NMI");
150 _console->DebugProcessInterrupt(originalPc, _state.PC, true);
151 #endif
152 } else {
153 Push((uint8_t)(PS() | PSFlags::Reserved));
154 SetFlags(PSFlags::Interrupt);
155
156 SetPC(MemoryReadWord(CPU::IRQVector));
157
158 #ifndef DUMMYCPU
159 _console->DebugAddTrace("IRQ");
160 _console->DebugProcessInterrupt(originalPc, _state.PC, false);
161 #endif
162 }
163 }
164
BRK()165 void CPU::BRK() {
166 Push((uint16_t)(PC() + 1));
167
168 uint8_t flags = PS() | PSFlags::Break | PSFlags::Reserved;
169 if(_state.NMIFlag) {
170 Push((uint8_t)flags);
171 SetFlags(PSFlags::Interrupt);
172
173 SetPC(MemoryReadWord(CPU::NMIVector));
174
175 #ifndef DUMMYCPU
176 _console->DebugAddTrace("NMI");
177 #endif
178 } else {
179 Push((uint8_t)flags);
180 SetFlags(PSFlags::Interrupt);
181
182 SetPC(MemoryReadWord(CPU::IRQVector));
183
184 #ifndef DUMMYCPU
185 _console->DebugAddTrace("IRQ");
186 #endif
187 }
188
189 //Since we just set the flag to prevent interrupts, do not run one right away after this (fixes nmi_and_brk & nmi_and_irq tests)
190 _prevRunIrq = false;
191 }
192
MemoryWrite(uint16_t addr,uint8_t value,MemoryOperationType operationType)193 void CPU::MemoryWrite(uint16_t addr, uint8_t value, MemoryOperationType operationType)
194 {
195 #ifdef DUMMYCPU
196 if(operationType == MemoryOperationType::Write || operationType == MemoryOperationType::DummyWrite) {
197 _writeAddresses[_writeCounter] = addr;
198 _isDummyWrite[_writeCounter] = operationType == MemoryOperationType::DummyWrite;
199 _writeValue[_writeCounter] = value;
200 _writeCounter++;
201 }
202 #else
203 _cpuWrite = true;;
204 _writeAddr = addr;
205 IncCycleCount();
206 while(_dmcDmaRunning) {
207 IncCycleCount();
208 }
209
210 _memoryManager->Write(addr, value, operationType);
211
212 //DMA DMC might have started after a write to $4015, stall CPU if needed
213 while(_dmcDmaRunning) {
214 IncCycleCount();
215 }
216 _cpuWrite = false;
217 #endif
218 }
219
MemoryRead(uint16_t addr,MemoryOperationType operationType)220 uint8_t CPU::MemoryRead(uint16_t addr, MemoryOperationType operationType) {
221 #ifdef DUMMYCPU
222 uint8_t value = _memoryManager->DebugRead(addr);
223 if(operationType == MemoryOperationType::Read || operationType == MemoryOperationType::DummyRead) {
224 _readAddresses[_readCounter] = addr;
225 _readValue[_readCounter] = value;
226 _isDummyRead[_readCounter] = operationType == MemoryOperationType::DummyRead;
227 _readCounter++;
228 }
229 return value;
230 #else
231 IncCycleCount();
232 while(_dmcDmaRunning) {
233 //Stall CPU until we can process a DMC read
234 if((addr != 0x4016 && addr != 0x4017 && (_cycleCount & 0x01)) || _dmcCounter == 1) {
235 //While the CPU is stalled, reads are performed on the current address
236 //Reads are only performed every other cycle? This fixes "dma_2007_read" test
237 //This behavior causes the $4016/7 data corruption when a DMC is running.
238 //When reading $4016/7, only the last read counts (because this only occurs to low-to-high transitions, i.e once in this case)
239 _memoryManager->Read(addr);
240 }
241 IncCycleCount();
242 }
243
244 uint8_t value = _memoryManager->Read(addr, operationType);
245 return value;
246 #endif
247 }
248
FetchOperand()249 uint16_t CPU::FetchOperand()
250 {
251 switch(_instAddrMode) {
252 case AddrMode::Acc:
253 case AddrMode::Imp: DummyRead(); return 0;
254 case AddrMode::Imm:
255 case AddrMode::Rel: return GetImmediate();
256 case AddrMode::Zero: return GetZeroAddr();
257 case AddrMode::ZeroX: return GetZeroXAddr();
258 case AddrMode::ZeroY: return GetZeroYAddr();
259 case AddrMode::Ind: return GetIndAddr();
260 case AddrMode::IndX: return GetIndXAddr();
261 case AddrMode::IndY: return GetIndYAddr(false);
262 case AddrMode::IndYW: return GetIndYAddr(true);
263 case AddrMode::Abs: return GetAbsAddr();
264 case AddrMode::AbsX: return GetAbsXAddr(false);
265 case AddrMode::AbsXW: return GetAbsXAddr(true);
266 case AddrMode::AbsY: return GetAbsYAddr(false);
267 case AddrMode::AbsYW: return GetAbsYAddr(true);
268 default: break;
269 }
270
271 #if !defined(LIBRETRO) && !defined(DUMMYCPU)
272 if(_warnOnCrash && _console->GetSettings()->CheckFlag(EmulationFlags::DeveloperMode)) {
273 MessageManager::DisplayMessage("Error", "GameCrash", "Invalid OP code - CPU crashed.");
274 _warnOnCrash = false;
275 }
276
277 _console->BreakIfDebugging();
278
279 if(_console->IsNsf()) {
280 //Don't stop emulation on CPU crash when playing NSFs, reset cpu instead
281 _console->Reset(true);
282 return 0;
283 } else if(!_console->GetDebugger(false) && !_console->GetSettings()->CheckFlag(EmulationFlags::DeveloperMode)) {
284 //Throw an error and stop emulation core (if debugger is not enabled)
285 throw std::runtime_error("Invalid OP code - CPU crashed");
286 } else {
287 return 0;
288 }
289 #else
290 return 0;
291 #endif
292
293 }
294
IncCycleCount()295 void CPU::IncCycleCount()
296 {
297 _cycleCount++;
298
299 if(_dmcDmaRunning) {
300 //CPU is being stalled by the DMC's DMA transfer
301 _dmcCounter--;
302 if(_dmcCounter == 0) {
303 //Update the DMC buffer when the stall period is completed
304 _dmcDmaRunning = false;
305 #ifndef DUMMYCPU
306 _console->GetApu()->FillDmcReadBuffer();
307 _console->DebugAddTrace("DMC DMA End");
308 #endif
309 }
310 }
311
312 _console->ProcessCpuClock();
313
314 if(!_spriteDmaTransfer && !_dmcDmaRunning) {
315 //IRQ flags are ignored during Sprite DMA - fixes irq_and_dma
316
317 //"it's really the status of the interrupt lines at the end of the second-to-last cycle that matters."
318 //Keep the irq lines values from the previous cycle. The before-to-last cycle's values will be used
319 _prevRunIrq = _runIrq;
320 _runIrq = _state.NMIFlag || ((_state.IRQFlag & _irqMask) > 0 && !CheckFlag(PSFlags::Interrupt));
321 }
322 }
323
RunDMATransfer(uint8_t offsetValue)324 void CPU::RunDMATransfer(uint8_t offsetValue)
325 {
326 _console->DebugAddTrace("Sprite DMA Start");
327 _spriteDmaTransfer = true;
328
329 //"The CPU is suspended during the transfer, which will take 513 or 514 cycles after the $4014 write tick."
330 //"(1 dummy read cycle while waiting for writes to complete, +1 if on an odd CPU cycle, then 256 alternating read/write cycles.)"
331 if(_cycleCount % 2 != 0) {
332 DummyRead();
333 }
334 DummyRead();
335
336 _spriteDmaCounter = 256;
337
338 //DMA transfer starts at SpriteRamAddr and wraps around
339 for(int i = 0; i < 0x100; i++) {
340 //Read value
341 uint8_t readValue = MemoryRead(offsetValue * 0x100 + i);
342
343 //Write to sprite ram via $2004 ("DMA is implemented in the 2A03/7 chip and works by repeatedly writing to OAMDATA")
344 MemoryWrite(0x2004, readValue);
345
346 _spriteDmaCounter--;
347 }
348
349 _spriteDmaTransfer = false;
350
351 _console->DebugAddTrace("Sprite DMA End");
352 }
353
StartDmcTransfer()354 void CPU::StartDmcTransfer()
355 {
356 //"DMC DMA adds 4 cycles normally, 2 if it lands on the $4014 write or during OAM DMA"
357 //3 cycles if it lands on the last write cycle of any instruction
358 _console->DebugAddTrace("DMC DMA Start");
359 _dmcDmaRunning = true;
360 if(_spriteDmaTransfer) {
361 if(_spriteDmaCounter == 2) {
362 _dmcCounter = 1;
363 } else if(_spriteDmaCounter == 1) {
364 _dmcCounter = 3;
365 } else {
366 _dmcCounter = 2;
367 }
368 } else {
369 if(_cpuWrite) {
370 if(_writeAddr == 0x4014) {
371 _dmcCounter = 2;
372 } else {
373 _dmcCounter = 3;
374 }
375 } else {
376 _dmcCounter = 4;
377 }
378 }
379 }
380
GetClockRate(NesModel model)381 uint32_t CPU::GetClockRate(NesModel model)
382 {
383 switch(model) {
384 default:
385 case NesModel::NTSC: return CPU::ClockRateNtsc; break;
386 case NesModel::PAL: return CPU::ClockRatePal; break;
387 case NesModel::Dendy: return CPU::ClockRateDendy; break;
388 }
389 }
390
StreamState(bool saving)391 void CPU::StreamState(bool saving)
392 {
393 EmulationSettings* settings = _console->GetSettings();
394 uint32_t overclockRate = settings->GetOverclockRateSetting();
395 bool overclockAdjustApu = settings->GetOverclockAdjustApu();
396 uint32_t extraScanlinesBeforeNmi = settings->GetPpuExtraScanlinesBeforeNmi();
397 uint32_t extraScanlinesAfterNmi = settings->GetPpuExtraScanlinesAfterNmi();
398 uint32_t dipSwitches = _console->GetSettings()->GetDipSwitches();
399
400 Stream(_state.PC, _state.SP, _state.PS, _state.A, _state.X, _state.Y, _cycleCount, _state.NMIFlag,
401 _state.IRQFlag, _dmcCounter, _dmcDmaRunning, _spriteDmaCounter, _spriteDmaTransfer,
402 overclockRate, overclockAdjustApu, extraScanlinesBeforeNmi, extraScanlinesBeforeNmi, dipSwitches);
403
404 if(!saving) {
405 settings->SetOverclockRate(overclockRate, overclockAdjustApu);
406 settings->SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi);
407 settings->SetDipSwitches(dipSwitches);
408 }
409 }