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 }