1 #include "stdafx.h"
2 #include <random>
3 #include <assert.h>
4 #include "../Utilities/FolderUtilities.h"
5 #include "../Utilities/IpsPatcher.h"
6 #include "BaseMapper.h"
7 #include "Console.h"
8 #include "CheatManager.h"
9 #include "Debugger.h"
10 #include "MemoryManager.h"
11 #include "BatteryManager.h"
12 #include "MessageManager.h"
13 #include "EmulationSettings.h"
14 
WriteRegister(uint16_t addr,uint8_t value)15 void BaseMapper::WriteRegister(uint16_t addr, uint8_t value) { }
ReadRegister(uint16_t addr)16 uint8_t BaseMapper::ReadRegister(uint16_t addr) { return 0; }
InitMapper(RomData & romData)17 void BaseMapper::InitMapper(RomData &romData) { }
Reset(bool softReset)18 void BaseMapper::Reset(bool softReset) { }
19 
20 //Make sure the page size is no bigger than the size of the ROM itself
21 //Otherwise we will end up reading from unallocated memory
InternalGetPrgPageSize()22 uint16_t BaseMapper::InternalGetPrgPageSize() { return std::min((uint32_t)GetPRGPageSize(), _prgSize); }
InternalGetSaveRamPageSize()23 uint16_t BaseMapper::InternalGetSaveRamPageSize() { return std::min((uint32_t)GetSaveRamPageSize(), _saveRamSize); }
InternalGetWorkRamPageSize()24 uint16_t BaseMapper::InternalGetWorkRamPageSize() { return std::min((uint32_t)GetWorkRamPageSize(), _workRamSize); }
InternalGetChrPageSize()25 uint16_t BaseMapper::InternalGetChrPageSize() { return std::min((uint32_t)GetCHRPageSize(), _chrRomSize); }
InternalGetChrRamPageSize()26 uint16_t BaseMapper::InternalGetChrRamPageSize() { return std::min((uint32_t)GetChrRamPageSize(), _chrRamSize); }
27 
ValidateAddressRange(uint16_t startAddr,uint16_t endAddr)28 bool BaseMapper::ValidateAddressRange(uint16_t startAddr, uint16_t endAddr)
29 {
30 	if((startAddr & 0xFF) || (endAddr & 0xFF) != 0xFF) {
31 		#ifdef _DEBUG
32 			throw new std::runtime_error("Start/End address must be multiples of 256/0x100");
33 		#else
34 			//Ignore this request in release mode - granularity smaller than 256 bytes is not supported
35 			return false;
36 		#endif
37 	}
38 	return true;
39 }
40 
SetCpuMemoryMapping(uint16_t startAddr,uint16_t endAddr,int16_t pageNumber,PrgMemoryType type,int8_t accessType)41 void BaseMapper::SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, int16_t pageNumber, PrgMemoryType type, int8_t accessType)
42 {
43 	if(!ValidateAddressRange(startAddr, endAddr) || startAddr > 0xFF00 || endAddr <= startAddr) {
44 		return;
45 	}
46 
47 	uint32_t pageCount;
48 	uint32_t pageSize;
49 	uint8_t defaultAccessType = MemoryAccessType::Read;
50 	switch(type) {
51 		case PrgMemoryType::PrgRom:
52 			pageCount = GetPRGPageCount();
53 			pageSize = InternalGetPrgPageSize();
54 			break;
55 		case PrgMemoryType::SaveRam:
56 			pageSize = InternalGetSaveRamPageSize();
57 			if(pageSize == 0) {
58 				#ifdef _DEBUG
59 				MessageManager::DisplayMessage("Debug", "Tried to map undefined save ram.");
60 				#endif
61 				return;
62 			}
63 			pageCount = _saveRamSize / pageSize;
64 
65 			defaultAccessType |= MemoryAccessType::Write;
66 			break;
67 		case PrgMemoryType::WorkRam:
68 			pageSize = InternalGetWorkRamPageSize();
69 			if(pageSize == 0) {
70 				#ifdef _DEBUG
71 				MessageManager::DisplayMessage("Debug", "Tried to map undefined work ram.");
72 				#endif
73 				return;
74 			}
75 
76 			pageCount = _workRamSize / pageSize;
77 
78 			defaultAccessType |= MemoryAccessType::Write;
79 			break;
80 		default:
81 			throw new std::runtime_error("Invalid parameter");
82 	}
83 
84 	if(pageCount == 0) {
85 		#ifdef _DEBUG
86 		MessageManager::DisplayMessage("Debug", "Tried to map undefined save/work ram.");
87 		#endif
88 		return;
89 	}
90 
91 	auto wrapPageNumber = [=](int16_t &page) -> void {
92 		if(page < 0) {
93 			//Can't use modulo for negative number because pageCount is sometimes not a power of 2.  (Fixes some Mapper 191 games)
94 			page = pageCount + page;
95 		} else {
96 			page = page % pageCount;
97 		}
98 	};
99 	wrapPageNumber(pageNumber);
100 
101 	accessType = accessType != -1 ? accessType : defaultAccessType;
102 
103 	if((uint16_t)(endAddr - startAddr) >= pageSize) {
104 		#ifdef _DEBUG
105 		MessageManager::DisplayMessage("Debug", "Tried to map undefined prg - page size too small for selected range.");
106 		#endif
107 
108 		//If range is bigger than a single page, keep going until we reach the last page
109 		uint32_t addr = startAddr;
110 		while(addr <= endAddr - pageSize + 1) {
111 			SetCpuMemoryMapping(addr, addr + pageSize - 1, type, pageNumber * pageSize, accessType);
112 			addr += pageSize;
113 			pageNumber++;
114 			wrapPageNumber(pageNumber);
115 		}
116 	} else {
117 		SetCpuMemoryMapping(startAddr, endAddr, type, pageNumber * pageSize, accessType);
118 	}
119 }
120 
SetCpuMemoryMapping(uint16_t startAddr,uint16_t endAddr,PrgMemoryType type,uint32_t sourceOffset,int8_t accessType)121 void BaseMapper::SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, PrgMemoryType type, uint32_t sourceOffset, int8_t accessType)
122 {
123 	uint8_t* source = nullptr;
124 	switch(type) {
125 		default:
126 		case PrgMemoryType::PrgRom: source = _prgRom; break;
127 		case PrgMemoryType::SaveRam: source = _saveRam; break;
128 		case PrgMemoryType::WorkRam: source = _workRam; break;
129 	}
130 
131 	int firstSlot = startAddr >> 8;
132 	int slotCount = (endAddr - startAddr + 1) >> 8;
133 	for(int i = 0; i < slotCount; i++) {
134 		_prgMemoryOffset[firstSlot + i] = sourceOffset + i * 0x100;
135 		_prgMemoryType[firstSlot + i] = type;
136 		_prgMemoryAccess[firstSlot + i] = (MemoryAccessType)accessType;
137 	}
138 
139 	SetCpuMemoryMapping(startAddr, endAddr, source+sourceOffset, accessType);
140 }
141 
SetCpuMemoryMapping(uint16_t startAddr,uint16_t endAddr,uint8_t * source,int8_t accessType)142 void BaseMapper::SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, uint8_t *source, int8_t accessType)
143 {
144 	if(!ValidateAddressRange(startAddr, endAddr)) {
145 		return;
146 	}
147 
148 	startAddr >>= 8;
149 	endAddr >>= 8;
150 	for(uint16_t i = startAddr; i <= endAddr; i++) {
151 		_prgPages[i] = source;
152 		_prgMemoryAccess[i] = accessType != -1 ? (MemoryAccessType)accessType : MemoryAccessType::Read;
153 
154 		source += 0x100;
155 	}
156 }
157 
RemoveCpuMemoryMapping(uint16_t startAddr,uint16_t endAddr)158 void BaseMapper::RemoveCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr)
159 {
160 	//Unmap this section of memory (causing open bus behavior)
161 	int firstSlot = startAddr >> 8;
162 	int slotCount = (endAddr - startAddr + 1) >> 8;
163 	for(int i = 0; i < slotCount; i++) {
164 		_prgMemoryOffset[firstSlot + i] = -1;
165 		_prgMemoryType[firstSlot + i] = PrgMemoryType::PrgRom;
166 		_prgMemoryAccess[firstSlot + i] = MemoryAccessType::NoAccess;
167 	}
168 
169 	SetCpuMemoryMapping(startAddr, endAddr, nullptr, MemoryAccessType::NoAccess);
170 }
171 
SetPpuMemoryMapping(uint16_t startAddr,uint16_t endAddr,uint16_t pageNumber,ChrMemoryType type,int8_t accessType)172 void BaseMapper::SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, uint16_t pageNumber, ChrMemoryType type, int8_t accessType)
173 {
174 	if(!ValidateAddressRange(startAddr, endAddr) || startAddr > 0x3F00 || endAddr > 0x3FFF || endAddr <= startAddr) {
175 		return;
176 	}
177 
178 	uint32_t pageCount = 0;
179 	uint32_t pageSize = 0;
180 	uint8_t defaultAccessType = MemoryAccessType::Read;
181 	switch(type) {
182 		case ChrMemoryType::Default:
183 			pageSize = InternalGetChrPageSize();
184 			if(pageSize == 0) {
185 				#ifdef _DEBUG
186 				MessageManager::DisplayMessage("Debug", "Tried to map undefined chr rom/ram.");
187 				#endif
188 				return;
189 			}
190 			pageCount = GetCHRPageCount();
191 			if(_onlyChrRam) {
192 				defaultAccessType |= MemoryAccessType::Write;
193 			}
194 			break;
195 
196 		case ChrMemoryType::ChrRom:
197 			pageSize = InternalGetChrPageSize();
198 			if(pageSize == 0) {
199 				#ifdef _DEBUG
200 				MessageManager::DisplayMessage("Debug", "Tried to map undefined chr rom.");
201 				#endif
202 				return;
203 			}
204 			pageCount = GetCHRPageCount();
205 			break;
206 
207 		case ChrMemoryType::ChrRam:
208 			pageSize = InternalGetChrRamPageSize();
209 			if(pageSize == 0) {
210 				#ifdef _DEBUG
211 				MessageManager::DisplayMessage("Debug", "Tried to map undefined chr ram.");
212 				#endif
213 				return;
214 			}
215 			pageCount = _chrRamSize / pageSize;
216 			defaultAccessType |= MemoryAccessType::Write;
217 			break;
218 
219 		case ChrMemoryType::NametableRam:
220 			pageSize = BaseMapper::NametableSize;
221 			pageCount = BaseMapper::NametableCount;
222 			defaultAccessType |= MemoryAccessType::Write;
223 			break;
224 	}
225 
226 	if(pageCount == 0) {
227 		#ifdef _DEBUG
228 		MessageManager::DisplayMessage("Debug", "Tried to map undefined chr ram.");
229 		#endif
230 		return;
231 	}
232 
233 	pageNumber = pageNumber % pageCount;
234 
235 	if((uint16_t)(endAddr - startAddr) >= pageSize) {
236 		#ifdef _DEBUG
237 		MessageManager::DisplayMessage("Debug", "Tried to map undefined chr - page size too small for selected range.");
238 		#endif
239 
240 		uint32_t addr = startAddr;
241 		while(addr <= endAddr - pageSize + 1) {
242 			SetPpuMemoryMapping(addr, addr + pageSize - 1, type, pageNumber * pageSize, accessType);
243 			addr += pageSize;
244 			pageNumber = (pageNumber + 1) % pageCount;
245 		}
246 	} else {
247 		SetPpuMemoryMapping(startAddr, endAddr, type, pageNumber * pageSize, accessType == -1 ? defaultAccessType : accessType);
248 	}
249 }
250 
SetPpuMemoryMapping(uint16_t startAddr,uint16_t endAddr,ChrMemoryType type,uint32_t sourceOffset,int8_t accessType)251 void BaseMapper::SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, ChrMemoryType type, uint32_t sourceOffset, int8_t accessType)
252 {
253 	uint8_t* sourceMemory = nullptr;
254 	switch(type) {
255 		default:
256 		case ChrMemoryType::Default: sourceMemory = _onlyChrRam ? _chrRam : _chrRom; break;
257 		case ChrMemoryType::ChrRom: sourceMemory = _chrRom; break;
258 		case ChrMemoryType::ChrRam: sourceMemory = _chrRam; break;
259 		case ChrMemoryType::NametableRam: sourceMemory = _nametableRam; break;
260 	}
261 	int firstSlot = startAddr >> 8;
262 	int slotCount = (endAddr - startAddr + 1) >> 8;
263 	for(int i = 0; i < slotCount; i++) {
264 		_chrMemoryOffset[firstSlot + i] = sourceOffset + i * 256;
265 		_chrMemoryType[firstSlot + i] = type;
266 		_chrMemoryAccess[firstSlot + i] = (MemoryAccessType)accessType;
267 	}
268 
269 	SetPpuMemoryMapping(startAddr, endAddr, sourceMemory + sourceOffset, accessType);
270 }
271 
SetPpuMemoryMapping(uint16_t startAddr,uint16_t endAddr,uint8_t * sourceMemory,int8_t accessType)272 void BaseMapper::SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, uint8_t* sourceMemory, int8_t accessType)
273 {
274 	if(!ValidateAddressRange(startAddr, endAddr)) {
275 		return;
276 	}
277 
278 	startAddr >>= 8;
279 	endAddr >>= 8;
280 	for(uint16_t i = startAddr; i <= endAddr; i++) {
281 		_chrPages[i] = sourceMemory;
282 		_chrMemoryAccess[i] = accessType != -1 ? (MemoryAccessType)accessType : MemoryAccessType::ReadWrite;
283 
284 		if(sourceMemory != nullptr) {
285 			sourceMemory += 0x100;
286 		}
287 	}
288 }
289 
RemovePpuMemoryMapping(uint16_t startAddr,uint16_t endAddr)290 void BaseMapper::RemovePpuMemoryMapping(uint16_t startAddr, uint16_t endAddr)
291 {
292 	//Unmap this section of memory (causing open bus behavior)
293 	int firstSlot = startAddr >> 8;
294 	int slotCount = (endAddr - startAddr + 1) >> 8;
295 	for(int i = 0; i < slotCount; i++) {
296 		_chrMemoryOffset[firstSlot + i] = -1;
297 		_chrMemoryType[firstSlot + i] = ChrMemoryType::Default;
298 		_chrMemoryAccess[firstSlot + i] = MemoryAccessType::NoAccess;
299 	}
300 
301 	SetPpuMemoryMapping(startAddr, endAddr, nullptr, MemoryAccessType::NoAccess);
302 }
303 
InternalReadRam(uint16_t addr)304 uint8_t BaseMapper::InternalReadRam(uint16_t addr)
305 {
306 	return _prgPages[addr >> 8] ? _prgPages[addr >> 8][(uint8_t)addr] : 0;
307 }
308 
SelectPrgPage4x(uint16_t slot,uint16_t page,PrgMemoryType memoryType)309 void BaseMapper::SelectPrgPage4x(uint16_t slot, uint16_t page, PrgMemoryType memoryType)
310 {
311 	BaseMapper::SelectPrgPage2x(slot*2, page, memoryType);
312 	BaseMapper::SelectPrgPage2x(slot*2+1, page+2, memoryType);
313 }
314 
SelectPrgPage2x(uint16_t slot,uint16_t page,PrgMemoryType memoryType)315 void BaseMapper::SelectPrgPage2x(uint16_t slot, uint16_t page, PrgMemoryType memoryType)
316 {
317 	BaseMapper::SelectPRGPage(slot*2, page, memoryType);
318 	BaseMapper::SelectPRGPage(slot*2+1, page+1, memoryType);
319 }
320 
SelectPRGPage(uint16_t slot,uint16_t page,PrgMemoryType memoryType)321 void BaseMapper::SelectPRGPage(uint16_t slot, uint16_t page, PrgMemoryType memoryType)
322 {
323 	if(_prgSize < 0x8000 && GetPRGPageSize() > _prgSize) {
324 		//Total PRG size is smaller than available memory range, map the entire PRG to all slots
325 		//i.e same logic as NROM (mapper 0) when PRG is 16kb
326 		//Needed by "Pyramid" (mapper 79)
327 		#ifdef _DEBUG
328 			MessageManager::DisplayMessage("Debug", "PrgSizeWarning");
329 		#endif
330 
331 		for(slot = 0; slot < 0x8000 / _prgSize; slot++) {
332 			uint16_t startAddr = 0x8000 + slot * _prgSize;
333 			uint16_t endAddr = startAddr + _prgSize - 1;
334 			SetCpuMemoryMapping(startAddr, endAddr, 0, memoryType);
335 		}
336 	} else {
337 		uint16_t startAddr = 0x8000 + slot * InternalGetPrgPageSize();
338 		uint16_t endAddr = startAddr + InternalGetPrgPageSize() - 1;
339 		SetCpuMemoryMapping(startAddr, endAddr, page, memoryType);
340 	}
341 }
342 
SelectChrPage8x(uint16_t slot,uint16_t page,ChrMemoryType memoryType)343 void BaseMapper::SelectChrPage8x(uint16_t slot, uint16_t page, ChrMemoryType memoryType)
344 {
345 	BaseMapper::SelectChrPage4x(slot, page, memoryType);
346 	BaseMapper::SelectChrPage4x(slot*2+1, page+4, memoryType);
347 }
348 
SelectChrPage4x(uint16_t slot,uint16_t page,ChrMemoryType memoryType)349 void BaseMapper::SelectChrPage4x(uint16_t slot, uint16_t page, ChrMemoryType memoryType)
350 {
351 	BaseMapper::SelectChrPage2x(slot*2, page, memoryType);
352 	BaseMapper::SelectChrPage2x(slot*2+1, page+2, memoryType);
353 }
354 
SelectChrPage2x(uint16_t slot,uint16_t page,ChrMemoryType memoryType)355 void BaseMapper::SelectChrPage2x(uint16_t slot, uint16_t page, ChrMemoryType memoryType)
356 {
357 	BaseMapper::SelectCHRPage(slot*2, page, memoryType);
358 	BaseMapper::SelectCHRPage(slot*2+1, page+1, memoryType);
359 }
360 
SelectCHRPage(uint16_t slot,uint16_t page,ChrMemoryType memoryType)361 void BaseMapper::SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType)
362 {
363 	uint16_t pageSize;
364 	if(memoryType == ChrMemoryType::NametableRam) {
365 		pageSize = BaseMapper::NametableSize;
366 	} else {
367 		pageSize = memoryType == ChrMemoryType::ChrRam ? InternalGetChrRamPageSize() : InternalGetChrPageSize();
368 	}
369 
370 	uint16_t startAddr = slot * pageSize;
371 	uint16_t endAddr = startAddr + pageSize - 1;
372 	SetPpuMemoryMapping(startAddr, endAddr, page, memoryType);
373 }
374 
GetPowerOnByte(uint8_t defaultValue)375 uint8_t BaseMapper::GetPowerOnByte(uint8_t defaultValue)
376 {
377 	if(_console->GetSettings()->CheckFlag(EmulationFlags::RandomizeMapperPowerOnState)) {
378 		std::random_device rd;
379 		std::mt19937 mt(rd());
380 		std::uniform_int_distribution<> dist(0, 255);
381 		return dist(mt);
382 	} else {
383 		return defaultValue;
384 	}
385 }
386 
GetDipSwitches()387 uint32_t BaseMapper::GetDipSwitches()
388 {
389 	uint32_t mask = (1 << GetDipSwitchCount()) - 1;
390 	return _console->GetSettings()->GetDipSwitches() & mask;
391 }
392 
HasBattery()393 bool BaseMapper::HasBattery()
394 {
395 	return _romInfo.HasBattery;
396 }
397 
LoadBattery()398 void BaseMapper::LoadBattery()
399 {
400 	if(HasBattery() && _saveRamSize > 0) {
401 		_console->GetBatteryManager()->LoadBattery(".sav", _saveRam, _saveRamSize);
402 	}
403 
404 	if(_hasChrBattery && _chrRamSize > 0) {
405 		_console->GetBatteryManager()->LoadBattery(".sav.chr", _chrRam, _chrRamSize);
406 	}
407 }
408 
SaveBattery()409 void BaseMapper::SaveBattery()
410 {
411 	if(HasBattery() && _saveRamSize > 0) {
412 		_console->GetBatteryManager()->SaveBattery(".sav", _saveRam, _saveRamSize);
413 	}
414 
415 	if(_hasChrBattery && _chrRamSize > 0) {
416 		_console->GetBatteryManager()->SaveBattery(".sav.chr", _chrRam, _chrRamSize);
417 	}
418 }
419 
GetPRGPageCount()420 uint32_t BaseMapper::GetPRGPageCount()
421 {
422 	uint16_t pageSize = InternalGetPrgPageSize();
423 	return pageSize ? (_prgSize / pageSize) : 0;
424 }
425 
GetCHRPageCount()426 uint32_t BaseMapper::GetCHRPageCount()
427 {
428 	uint16_t pageSize = InternalGetChrPageSize();
429 	return pageSize ? (_chrRomSize / pageSize) : 0;
430 }
431 
GetBatteryFilename()432 string BaseMapper::GetBatteryFilename()
433 {
434 	return FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), FolderUtilities::GetFilename(_romInfo.RomName, false) + ".sav");
435 }
436 
RestoreOriginalPrgRam()437 void BaseMapper::RestoreOriginalPrgRam()
438 {
439 	memcpy(_prgRom, _originalPrgRom.data(), _originalPrgRom.size());
440 }
441 
InitializeChrRam(int32_t chrRamSize)442 void BaseMapper::InitializeChrRam(int32_t chrRamSize)
443 {
444 	uint32_t defaultRamSize = GetChrRamSize() ? GetChrRamSize() : 0x2000;
445 	_chrRamSize = chrRamSize >= 0 ? chrRamSize : defaultRamSize;
446 	if(_chrRamSize > 0) {
447 		_chrRam = new uint8_t[_chrRamSize];
448 		_console->InitializeRam(_chrRam, _chrRamSize);
449 	}
450 }
451 
SetupDefaultWorkRam()452 void BaseMapper::SetupDefaultWorkRam()
453 {
454 	//Setup a default work/save ram in 0x6000-0x7FFF space
455 	if(HasBattery() && _saveRamSize > 0) {
456 		SetCpuMemoryMapping(0x6000, 0x7FFF, 0, PrgMemoryType::SaveRam);
457 	} else if(_workRamSize > 0) {
458 		SetCpuMemoryMapping(0x6000, 0x7FFF, 0, PrgMemoryType::WorkRam);
459 	}
460 }
461 
HasChrRam()462 bool BaseMapper::HasChrRam()
463 {
464 	return _chrRamSize > 0;
465 }
466 
HasChrRom()467 bool BaseMapper::HasChrRom()
468 {
469 	return !_onlyChrRam;
470 }
471 
AddRegisterRange(uint16_t startAddr,uint16_t endAddr,MemoryOperation operation)472 void BaseMapper::AddRegisterRange(uint16_t startAddr, uint16_t endAddr, MemoryOperation operation)
473 {
474 	for(int i = startAddr; i <= endAddr; i++) {
475 		if((int)operation & (int)MemoryOperation::Read) {
476 			_isReadRegisterAddr[i] = true;
477 		}
478 		if((int)operation & (int)MemoryOperation::Write) {
479 			_isWriteRegisterAddr[i] = true;
480 		}
481 	}
482 }
483 
RemoveRegisterRange(uint16_t startAddr,uint16_t endAddr,MemoryOperation operation)484 void BaseMapper::RemoveRegisterRange(uint16_t startAddr, uint16_t endAddr, MemoryOperation operation)
485 {
486 	for(int i = startAddr; i <= endAddr; i++) {
487 		if((int)operation & (int)MemoryOperation::Read) {
488 			_isReadRegisterAddr[i] = false;
489 		}
490 		if((int)operation & (int)MemoryOperation::Write) {
491 			_isWriteRegisterAddr[i] = false;
492 		}
493 	}
494 }
495 
StreamState(bool saving)496 void BaseMapper::StreamState(bool saving)
497 {
498 	//Need to get the number of nametables in the state first, before we try to stream the nametable ram array
499 	Stream(_nametableCount);
500 
501 	ArrayInfo<uint8_t> chrRam = { _chrRam, _chrRamSize };
502 	ArrayInfo<uint8_t> workRam = { _workRam, _workRamSize };
503 	ArrayInfo<uint8_t> saveRam = { _saveRam, _saveRamSize };
504 	ArrayInfo<uint8_t> nametableRam = { _nametableRam, _nametableCount * BaseMapper::NametableSize };
505 
506 	ArrayInfo<int32_t> prgMemoryOffset = { _prgMemoryOffset, 0x100 };
507 	ArrayInfo<int32_t> chrMemoryOffset = { _chrMemoryOffset, 0x40 };
508 	ArrayInfo<PrgMemoryType> prgMemoryType = { _prgMemoryType, 0x100 };
509 	ArrayInfo<ChrMemoryType> chrMemoryType = { _chrMemoryType, 0x40 };
510 	ArrayInfo<MemoryAccessType> prgMemoryAccess = { _prgMemoryAccess, 0x100 };
511 	ArrayInfo<MemoryAccessType> chrMemoryAccess = { _chrMemoryAccess, 0x40 };
512 
513 	Stream(_mirroringType, chrRam, workRam, saveRam, nametableRam, prgMemoryOffset, chrMemoryOffset, prgMemoryType, chrMemoryType, prgMemoryAccess, chrMemoryAccess);
514 
515 	if(!saving) {
516 		RestorePrgChrState();
517 	}
518 }
519 
RestorePrgChrState()520 void BaseMapper::RestorePrgChrState()
521 {
522 	for(uint16_t i = 0; i < 0x100; i++) {
523 		uint16_t startAddr = i << 8;
524 		if(_prgMemoryAccess[i] != MemoryAccessType::NoAccess) {
525 			SetCpuMemoryMapping(startAddr, startAddr + 0xFF, _prgMemoryType[i], _prgMemoryOffset[i], _prgMemoryAccess[i]);
526 		} else {
527 			RemoveCpuMemoryMapping(startAddr, startAddr + 0xFF);
528 		}
529 	}
530 
531 	for(uint16_t i = 0; i < 0x40; i++) {
532 		uint16_t startAddr = i << 8;
533 		if(_chrMemoryAccess[i] != MemoryAccessType::NoAccess) {
534 			SetPpuMemoryMapping(startAddr, startAddr + 0xFF, _chrMemoryType[i], _chrMemoryOffset[i], _chrMemoryAccess[i]);
535 		} else {
536 			RemovePpuMemoryMapping(startAddr, startAddr + 0xFF);
537 		}
538 	}
539 }
540 
Initialize(RomData & romData)541 void BaseMapper::Initialize(RomData &romData)
542 {
543 	_romInfo = romData.Info;
544 
545 	_batteryFilename = GetBatteryFilename();
546 
547 	if(romData.SaveRamSize == -1 || ForceSaveRamSize()) {
548 		_saveRamSize = GetSaveRamSize();
549 	} else {
550 		_saveRamSize = romData.SaveRamSize;
551 	}
552 
553 	if(romData.WorkRamSize == -1 || ForceWorkRamSize()) {
554 		_workRamSize = GetWorkRamSize();
555 	} else {
556 		_workRamSize = romData.WorkRamSize;
557 	}
558 
559 	_allowRegisterRead = AllowRegisterRead();
560 
561 	memset(_isReadRegisterAddr, 0, sizeof(_isReadRegisterAddr));
562 	memset(_isWriteRegisterAddr, 0, sizeof(_isWriteRegisterAddr));
563 	AddRegisterRange(RegisterStartAddress(), RegisterEndAddress(), MemoryOperation::Any);
564 
565 	_prgSize = (uint32_t)romData.PrgRom.size();
566 	_chrRomSize = (uint32_t)romData.ChrRom.size();
567 	_originalPrgRom = romData.PrgRom;
568 	_originalChrRom = romData.ChrRom;
569 
570 	_prgRom = new uint8_t[_prgSize];
571 	_chrRom = new uint8_t[_chrRomSize];
572 	memcpy(_prgRom, romData.PrgRom.data(), _prgSize);
573 	if(_chrRomSize > 0) {
574 		memcpy(_chrRom, romData.ChrRom.data(), _chrRomSize);
575 	}
576 
577 	_hasChrBattery = romData.SaveChrRamSize > 0 || ForceChrBattery();
578 
579 	switch(romData.Info.BusConflicts) {
580 		case BusConflictType::Default: _hasBusConflicts = HasBusConflicts(); break;
581 		case BusConflictType::Yes: _hasBusConflicts = true; break;
582 		case BusConflictType::No: _hasBusConflicts = false; break;
583 	}
584 
585 	_saveRam = new uint8_t[_saveRamSize];
586 	_workRam = new uint8_t[_workRamSize];
587 
588 	_console->InitializeRam(_saveRam, _saveRamSize);
589 	_console->InitializeRam(_workRam, _workRamSize);
590 
591 	_nametableCount = 2;
592 	_nametableRam = new uint8_t[BaseMapper::NametableSize*BaseMapper::NametableCount];
593 	_console->InitializeRam(_nametableRam, BaseMapper::NametableSize*BaseMapper::NametableCount);
594 
595 	for(int i = 0; i < 0x100; i++) {
596 		//Allow us to map a different page every 256 bytes
597 		_prgPages[i] = nullptr;
598 		_prgMemoryOffset[i] = -1;
599 		_prgMemoryType[i] = PrgMemoryType::PrgRom;
600 		_prgMemoryAccess[i] = MemoryAccessType::NoAccess;
601 
602 		_chrPages[i] = nullptr;
603 		_chrMemoryOffset[i] = -1;
604 		_chrMemoryType[i] = ChrMemoryType::Default;
605 		_chrMemoryAccess[i] = MemoryAccessType::NoAccess;
606 	}
607 
608 	if(_chrRomSize == 0) {
609 		//Assume there is CHR RAM if no CHR ROM exists
610 		_onlyChrRam = true;
611 		InitializeChrRam(romData.ChrRamSize);
612 
613 		//Map CHR RAM to 0x0000-0x1FFF by default when no CHR ROM exists
614 		SetPpuMemoryMapping(0x0000, 0x1FFF, 0, ChrMemoryType::ChrRam);
615 		_chrRomSize = _chrRamSize;
616 	} else if(romData.ChrRamSize >= 0) {
617 		InitializeChrRam(romData.ChrRamSize);
618 	} else if(GetChrRamSize()) {
619 		InitializeChrRam();
620 	}
621 
622 	if(romData.Info.HasTrainer) {
623 		if(_workRamSize >= 0x2000) {
624 			memcpy(_workRam + 0x1000, romData.TrainerData.data(), 512);
625 		} else if(_saveRamSize >= 0x2000) {
626 			memcpy(_saveRam + 0x1000, romData.TrainerData.data(), 512);
627 		}
628 	}
629 
630 	SetupDefaultWorkRam();
631 
632 	SetMirroringType(romData.Info.Mirroring);
633 
634 	InitMapper();
635 	InitMapper(romData);
636 
637 	//Load battery data if present
638 	LoadBattery();
639 
640 	ApplyCheats();
641 
642 	_romInfo.HasChrRam = HasChrRam();
643 }
644 
~BaseMapper()645 BaseMapper::~BaseMapper()
646 {
647 	delete[] _chrRam;
648 	delete[] _chrRom;
649 	delete[] _prgRom;
650 	delete[] _saveRam;
651 	delete[] _workRam;
652 	delete[] _nametableRam;
653 }
654 
ProcessNotification(ConsoleNotificationType type,void * parameter)655 void BaseMapper::ProcessNotification(ConsoleNotificationType type, void* parameter)
656 {
657 	switch(type) {
658 		case ConsoleNotificationType::CheatAdded:
659 		case ConsoleNotificationType::CheatRemoved:
660 			ApplyCheats();
661 			break;
662 		default:
663 			break;
664 	}
665 }
666 
ApplyCheats()667 void BaseMapper::ApplyCheats()
668 {
669 	RestoreOriginalPrgRam();
670 	_console->GetCheatManager()->ApplyPrgCodes(_prgRom, _prgSize);
671 }
672 
GetMemoryRanges(MemoryRanges & ranges)673 void BaseMapper::GetMemoryRanges(MemoryRanges &ranges)
674 {
675 	if(_romInfo.System == GameSystem::VsSystem) {
676 		ranges.AddHandler(MemoryOperation::Read, 0x6000, 0xFFFF);
677 		ranges.AddHandler(MemoryOperation::Write, 0x6000, 0xFFFF);
678 	} else {
679 		ranges.AddHandler(MemoryOperation::Read, 0x4018, 0xFFFF);
680 		ranges.AddHandler(MemoryOperation::Write, 0x4018, 0xFFFF);
681 	}
682 }
683 
SetConsole(shared_ptr<Console> console)684 void BaseMapper::SetConsole(shared_ptr<Console> console)
685 {
686 	_console = console;
687 }
688 
GetNametable(uint8_t nametableIndex)689 uint8_t* BaseMapper::GetNametable(uint8_t nametableIndex)
690 {
691 	if(nametableIndex >= BaseMapper::NametableCount) {
692 		#ifdef _DEBUG
693 		MessageManager::Log("Invalid nametable index");
694 		#endif
695 		return _nametableRam;
696 	}
697 	_nametableCount = std::max<uint8_t>(_nametableCount, nametableIndex + 1);
698 
699 	return _nametableRam + (nametableIndex * BaseMapper::NametableSize);
700 }
701 
SetNametable(uint8_t index,uint8_t nametableIndex)702 void BaseMapper::SetNametable(uint8_t index, uint8_t nametableIndex)
703 {
704 	if(nametableIndex >= BaseMapper::NametableCount) {
705 		#ifdef _DEBUG
706 		MessageManager::Log("Invalid nametable index");
707 		#endif
708 		return;
709 	}
710 	_nametableCount = std::max<uint8_t>(_nametableCount, nametableIndex + 1);
711 
712 	SetPpuMemoryMapping(0x2000 + index * 0x400, 0x2000 + (index + 1) * 0x400 - 1, nametableIndex, ChrMemoryType::NametableRam);
713 
714 	//Mirror $2000-$2FFF to $3000-$3FFF, while keeping a distinction between the addresses
715 	//Previously, $3000-$3FFF was being "redirected" to $2000-$2FFF to avoid MMC3 IRQ issues (which is incorrect)
716 	//More info here: https://forums.nesdev.com/viewtopic.php?p=132145#p132145
717 	SetPpuMemoryMapping(0x3000 + index * 0x400, 0x3000 + (index + 1) * 0x400 - 1, nametableIndex, ChrMemoryType::NametableRam);
718 }
719 
SetNametables(uint8_t nametable1Index,uint8_t nametable2Index,uint8_t nametable3Index,uint8_t nametable4Index)720 void BaseMapper::SetNametables(uint8_t nametable1Index, uint8_t nametable2Index, uint8_t nametable3Index, uint8_t nametable4Index)
721 {
722 	SetNametable(0, nametable1Index);
723 	SetNametable(1, nametable2Index);
724 	SetNametable(2, nametable3Index);
725 	SetNametable(3, nametable4Index);
726 }
727 
SetMirroringType(MirroringType type)728 void BaseMapper::SetMirroringType(MirroringType type)
729 {
730 	_mirroringType = type;
731 	switch(type) {
732 		case MirroringType::Vertical: SetNametables(0, 1, 0, 1); break;
733 		case MirroringType::Horizontal: SetNametables(0, 0, 1, 1); break;
734 		case MirroringType::FourScreens:	SetNametables(0, 1, 2, 3); break;
735 		case MirroringType::ScreenAOnly: SetNametables(0, 0, 0, 0);	break;
736 		case MirroringType::ScreenBOnly: SetNametables(1, 1, 1, 1);	break;
737 	}
738 }
739 
GetAvailableFeatures()740 ConsoleFeatures BaseMapper::GetAvailableFeatures()
741 {
742 	return ConsoleFeatures::None;
743 }
744 
GetMapperControlDevice()745 shared_ptr<BaseControlDevice> BaseMapper::GetMapperControlDevice()
746 {
747 	return _mapperControlDevice;
748 }
749 
GetRomInfo()750 RomInfo BaseMapper::GetRomInfo()
751 {
752 	return _romInfo;
753 }
754 
GetMapperDipSwitchCount()755 uint32_t BaseMapper::GetMapperDipSwitchCount()
756 {
757 	return GetDipSwitchCount();
758 }
759 
GetMirroringType()760 MirroringType BaseMapper::GetMirroringType()
761 {
762 	return _mirroringType;
763 }
764 
ReadRAM(uint16_t addr)765 uint8_t BaseMapper::ReadRAM(uint16_t addr)
766 {
767 	if(_allowRegisterRead && _isReadRegisterAddr[addr]) {
768 		return ReadRegister(addr);
769 	} else if(_prgMemoryAccess[addr >> 8] & MemoryAccessType::Read) {
770 		return _prgPages[addr >> 8][(uint8_t)addr];
771 	} else {
772 		//assert(false);
773 	}
774 	return _console->GetMemoryManager()->GetOpenBus();
775 }
776 
PeekRAM(uint16_t addr)777 uint8_t BaseMapper::PeekRAM(uint16_t addr)
778 {
779 	return DebugReadRAM(addr);
780 }
781 
DebugReadRAM(uint16_t addr)782 uint8_t BaseMapper::DebugReadRAM(uint16_t addr)
783 {
784 	if(_prgMemoryAccess[addr >> 8] & MemoryAccessType::Read) {
785 		return _prgPages[addr >> 8][(uint8_t)addr];
786 	} else {
787 		//assert(false);
788 	}
789 
790 	//Fake open bus
791 	return addr >> 8;
792 }
793 
WriteRAM(uint16_t addr,uint8_t value)794 void BaseMapper::WriteRAM(uint16_t addr, uint8_t value)
795 {
796 	if(_isWriteRegisterAddr[addr]) {
797 		if(_hasBusConflicts) {
798 			value &= _prgPages[addr >> 8][(uint8_t)addr];
799 		}
800 		WriteRegister(addr, value);
801 	} else {
802 		WritePrgRam(addr, value);
803 	}
804 }
805 
DebugWriteRAM(uint16_t addr,uint8_t value)806 void BaseMapper::DebugWriteRAM(uint16_t addr, uint8_t value)
807 {
808 	if(_isWriteRegisterAddr[addr]) {
809 		if(_hasBusConflicts) {
810 			value &= _prgPages[addr >> 8][(uint8_t)addr];
811 		}
812 	} else {
813 		WritePrgRam(addr, value);
814 	}
815 }
816 
WritePrgRam(uint16_t addr,uint8_t value)817 void BaseMapper::WritePrgRam(uint16_t addr, uint8_t value)
818 {
819 	if(_prgMemoryAccess[addr >> 8] & MemoryAccessType::Write) {
820 		_prgPages[addr >> 8][(uint8_t)addr] = value;
821 	}
822 }
823 
NotifyVRAMAddressChange(uint16_t addr)824 void BaseMapper::NotifyVRAMAddressChange(uint16_t addr)
825 {
826 	//This is called when the VRAM addr on the PPU memory bus changes
827 	//Used by MMC3/MMC5/etc
828 }
829 
InternalReadVRAM(uint16_t addr)830 uint8_t BaseMapper::InternalReadVRAM(uint16_t addr)
831 {
832 	if(_chrMemoryAccess[addr >> 8] & MemoryAccessType::Read) {
833 		return _chrPages[addr >> 8][(uint8_t)addr];
834 	}
835 
836 	//Open bus - "When CHR is disabled, the pattern tables are open bus. Theoretically, this should return the LSB of the address read, but real-world behavior varies."
837 	return _vramOpenBusValue >= 0 ? _vramOpenBusValue : addr;
838 }
839 
DebugReadVRAM(uint16_t addr,bool disableSideEffects)840 uint8_t BaseMapper::DebugReadVRAM(uint16_t addr, bool disableSideEffects)
841 {
842 	addr &= 0x3FFF;
843 	if(!disableSideEffects) {
844 		NotifyVRAMAddressChange(addr);
845 	}
846 	return InternalReadVRAM(addr);
847 }
848 
MapperReadVRAM(uint16_t addr,MemoryOperationType operationType)849 uint8_t BaseMapper::MapperReadVRAM(uint16_t addr, MemoryOperationType operationType)
850 {
851 	return InternalReadVRAM(addr);
852 }
853 
DebugWriteVRAM(uint16_t addr,uint8_t value,bool disableSideEffects)854 void BaseMapper::DebugWriteVRAM(uint16_t addr, uint8_t value, bool disableSideEffects)
855 {
856 	addr &= 0x3FFF;
857 	if(disableSideEffects) {
858 		if(_chrPages[addr >> 8]) {
859 			//Always allow writes when side-effects are disabled
860 			_chrPages[addr >> 8][(uint8_t)addr] = value;
861 		}
862 	} else {
863 		NotifyVRAMAddressChange(addr);
864 		if(_chrMemoryAccess[addr >> 8] & MemoryAccessType::Write) {
865 			_chrPages[addr >> 8][(uint8_t)addr] = value;
866 		}
867 	}
868 }
869 
WriteVRAM(uint16_t addr,uint8_t value)870 void BaseMapper::WriteVRAM(uint16_t addr, uint8_t value)
871 {
872 	_console->DebugProcessVramWriteOperation(addr, value);
873 
874 	if(_chrMemoryAccess[addr >> 8] & MemoryAccessType::Write) {
875 		_chrPages[addr >> 8][(uint8_t)addr] = value;
876 	}
877 }
878 
IsNes20()879 bool BaseMapper::IsNes20()
880 {
881 	return _romInfo.NesHeader.GetRomHeaderVersion() == RomHeaderVersion::Nes2_0;
882 }
883 
884 //Debugger Helper Functions
GetPrgRom()885 uint8_t* BaseMapper::GetPrgRom()
886 {
887 	return _prgRom;
888 }
889 
GetSaveRam()890 uint8_t* BaseMapper::GetSaveRam()
891 {
892 	return _saveRam;
893 }
894 
GetWorkRam()895 uint8_t* BaseMapper::GetWorkRam()
896 {
897 	return _workRam;
898 }
899 
CopyMemory(DebugMemoryType type,uint8_t * buffer)900 uint32_t BaseMapper::CopyMemory(DebugMemoryType type, uint8_t* buffer)
901 {
902 	uint32_t size = GetMemorySize(type);
903 	switch(type) {
904 		default: break;
905 		case DebugMemoryType::ChrRam: memcpy(buffer, _chrRam, size); break;
906 		case DebugMemoryType::ChrRom: memcpy(buffer, _chrRom, size); break;
907 		case DebugMemoryType::NametableRam: memcpy(buffer, _nametableRam, size); break;
908 		case DebugMemoryType::SaveRam: memcpy(buffer, _saveRam, size); break;
909 		case DebugMemoryType::PrgRom: memcpy(buffer, _prgRom, size); break;
910 		case DebugMemoryType::WorkRam: memcpy(buffer, _workRam, size); break;
911 	}
912 	return size;
913 }
914 
WriteMemory(DebugMemoryType type,uint8_t * buffer,int32_t length)915 void BaseMapper::WriteMemory(DebugMemoryType type, uint8_t* buffer, int32_t length)
916 {
917 	int32_t size = std::min(length, (int32_t)GetMemorySize(type));
918 	switch(type) {
919 		default: break;
920 		case DebugMemoryType::ChrRam: memcpy(_chrRam, buffer, size); break;
921 		case DebugMemoryType::SaveRam: memcpy(_saveRam, buffer, size); break;
922 		case DebugMemoryType::WorkRam: memcpy(_workRam, buffer, size); break;
923 		case DebugMemoryType::NametableRam: memcpy(_nametableRam, buffer, size); break;
924 	}
925 }
926 
GetMemorySize(DebugMemoryType type)927 uint32_t BaseMapper::GetMemorySize(DebugMemoryType type)
928 {
929 	switch(type) {
930 		default: return 0;
931 		case DebugMemoryType::ChrRom: return _onlyChrRam ? 0 : _chrRomSize;
932 		case DebugMemoryType::ChrRam: return _chrRamSize;
933 		case DebugMemoryType::NametableRam: return _nametableCount * BaseMapper::NametableSize;
934 		case DebugMemoryType::SaveRam: return _saveRamSize;
935 		case DebugMemoryType::PrgRom: return _prgSize;
936 		case DebugMemoryType::WorkRam: return _workRamSize;
937 	}
938 }
939 
CopyChrTile(uint32_t address,uint8_t * dest)940 void BaseMapper::CopyChrTile(uint32_t address, uint8_t *dest)
941 {
942 	if(_chrRamSize > 0 && address <= _chrRamSize - 16) {
943 		memcpy(dest, _chrRam + address, 16);
944 	} else if(_chrRomSize > 0 && address <= _chrRomSize - 16) {
945 		memcpy(dest, _chrRom + address, 16);
946 	}
947 }
948 
GetMemoryValue(DebugMemoryType memoryType,uint32_t address)949 uint8_t BaseMapper::GetMemoryValue(DebugMemoryType memoryType, uint32_t address)
950 {
951 	uint32_t memorySize = GetMemorySize(memoryType);
952 	if(memorySize > 0) {
953 		if(address > memorySize) {
954 			address %= memorySize;
955 		}
956 
957 		switch(memoryType) {
958 			default: break;
959 			case DebugMemoryType::ChrRom: return _chrRom[address];
960 			case DebugMemoryType::ChrRam: return _chrRam[address];
961 			case DebugMemoryType::SaveRam: return _saveRam[address];
962 			case DebugMemoryType::PrgRom: return _prgRom[address];
963 			case DebugMemoryType::WorkRam: return _workRam[address];
964 			case DebugMemoryType::NametableRam: return _nametableRam[address];
965 		}
966 	}
967 	return 0;
968 }
969 
SetMemoryValue(DebugMemoryType memoryType,uint32_t address,uint8_t value)970 void BaseMapper::SetMemoryValue(DebugMemoryType memoryType, uint32_t address, uint8_t value)
971 {
972 	uint32_t memorySize = GetMemorySize(memoryType);
973 	if(memorySize > 0) {
974 		if(address > memorySize) {
975 			address %= memorySize;
976 		}
977 
978 		switch(memoryType) {
979 			default: break;
980 			case DebugMemoryType::ChrRom: _chrRom[address] = value; break;
981 			case DebugMemoryType::ChrRam: _chrRam[address] = value; break;
982 			case DebugMemoryType::SaveRam: _saveRam[address] = value; break;
983 			case DebugMemoryType::PrgRom: _prgRom[address] = value; break;
984 			case DebugMemoryType::WorkRam: _workRam[address] = value; break;
985 			case DebugMemoryType::NametableRam: _nametableRam[address] = value; break;
986 		}
987 	}
988 }
989 
GetAbsoluteAddressAndType(uint32_t relativeAddr,AddressTypeInfo * info)990 void BaseMapper::GetAbsoluteAddressAndType(uint32_t relativeAddr, AddressTypeInfo* info)
991 {
992 	if(relativeAddr < 0x2000) {
993 		info->Address = relativeAddr;
994 		info->Type = AddressType::InternalRam;
995 	} else {
996 		uint8_t *prgAddr = _prgPages[relativeAddr >> 8] + (uint8_t)relativeAddr;
997 		if(prgAddr >= _prgRom && prgAddr < _prgRom + _prgSize) {
998 			info->Address = (uint32_t)(prgAddr - _prgRom);
999 			info->Type = AddressType::PrgRom;
1000 		} else if(prgAddr >= _workRam && prgAddr < _workRam + _workRamSize) {
1001 			info->Address = (uint32_t)(prgAddr - _workRam);
1002 			info->Type = AddressType::WorkRam;
1003 		} else if(prgAddr >= _saveRam && prgAddr < _saveRam + _saveRamSize) {
1004 			info->Address = (uint32_t)(prgAddr - _saveRam);
1005 			info->Type = AddressType::SaveRam;
1006 		} else {
1007 			info->Address = -1;
1008 			info->Type = AddressType::InternalRam;
1009 		}
1010 	}
1011 }
1012 
ToAbsoluteAddress(uint16_t addr)1013 int32_t BaseMapper::ToAbsoluteAddress(uint16_t addr)
1014 {
1015 	uint8_t *prgAddr = _prgPages[addr >> 8] + (uint8_t)addr;
1016 	if(prgAddr >= _prgRom && prgAddr < _prgRom + _prgSize) {
1017 		return (uint32_t)(prgAddr - _prgRom);
1018 	}
1019 	return -1;
1020 }
1021 
ToAbsoluteWorkRamAddress(uint16_t addr)1022 int32_t BaseMapper::ToAbsoluteWorkRamAddress(uint16_t addr)
1023 {
1024 	uint8_t *prgRamAddr = _prgPages[addr >> 8] + (uint8_t)addr;
1025 	if(prgRamAddr >= _workRam && prgRamAddr < _workRam + _workRamSize) {
1026 		return (uint32_t)(prgRamAddr - _workRam);
1027 	}
1028 	return -1;
1029 }
1030 
ToAbsoluteSaveRamAddress(uint16_t addr)1031 int32_t BaseMapper::ToAbsoluteSaveRamAddress(uint16_t addr)
1032 {
1033 	uint8_t *prgRamAddr = _prgPages[addr >> 8] + (uint8_t)addr;
1034 	if(prgRamAddr >= _saveRam && prgRamAddr < _saveRam + _saveRamSize) {
1035 		return (uint32_t)(prgRamAddr - _saveRam);
1036 	}
1037 	return -1;
1038 }
1039 
GetPpuAbsoluteAddressAndType(uint32_t relativeAddr,PpuAddressTypeInfo * info)1040 void BaseMapper::GetPpuAbsoluteAddressAndType(uint32_t relativeAddr, PpuAddressTypeInfo* info)
1041 {
1042 	if(relativeAddr >= 0x3F00) {
1043 		info->Address = relativeAddr & 0x1F;
1044 		info->Type = PpuAddressType::PaletteRam;
1045 	} else {
1046 		uint8_t *addr = _chrPages[relativeAddr >> 8] + (uint8_t)relativeAddr;
1047 		if(addr >= _chrRom && addr < _chrRom + _chrRomSize) {
1048 			info->Address = (uint32_t)(addr - _chrRom);
1049 			info->Type = PpuAddressType::ChrRom;
1050 		} else if(addr >= _chrRam && addr < _chrRam + _chrRamSize) {
1051 			info->Address = (uint32_t)(addr - _chrRam);
1052 			info->Type = PpuAddressType::ChrRam;
1053 		} else if(addr >= _nametableRam && addr < _nametableRam + BaseMapper::NametableSize * BaseMapper::NametableCount) {
1054 			info->Address = (uint32_t)(addr - _nametableRam);
1055 			info->Type = PpuAddressType::NametableRam;
1056 		} else {
1057 			info->Address = -1;
1058 			info->Type = PpuAddressType::None;
1059 		}
1060 	}
1061 }
1062 
ToAbsoluteChrAddress(uint16_t addr)1063 int32_t BaseMapper::ToAbsoluteChrAddress(uint16_t addr)
1064 {
1065 	uint8_t *chrAddr = _chrPages[addr >> 8] + (uint8_t)addr;
1066 	if(chrAddr >= _chrRom && chrAddr < _chrRom + _chrRomSize) {
1067 		return (uint32_t)(chrAddr - _chrRom);
1068 	}
1069 	if(chrAddr >= _chrRam && chrAddr < _chrRam + _chrRamSize) {
1070 		return (uint32_t)(chrAddr - _chrRam);
1071 	}
1072 	return -1;
1073 }
1074 
ToAbsoluteChrRamAddress(uint16_t addr)1075 int32_t BaseMapper::ToAbsoluteChrRamAddress(uint16_t addr)
1076 {
1077 	uint8_t *chrAddr = _chrPages[addr >> 8] + (uint8_t)addr;
1078 	if(chrAddr >= _chrRam && chrAddr < _chrRam + _chrRamSize) {
1079 		return (uint32_t)(chrAddr - _chrRam);
1080 	}
1081 	return -1;
1082 }
1083 
ToAbsoluteChrRomAddress(uint16_t addr)1084 int32_t BaseMapper::ToAbsoluteChrRomAddress(uint16_t addr)
1085 {
1086 	uint8_t *chrAddr = _chrPages[addr >> 8] + (uint8_t)addr;
1087 	if(chrAddr >= _chrRom && chrAddr < _chrRom + _chrRomSize) {
1088 		return (uint32_t)(chrAddr - _chrRom);
1089 	}
1090 	return -1;
1091 }
1092 
FromAbsoluteChrAddress(uint32_t addr)1093 int32_t BaseMapper::FromAbsoluteChrAddress(uint32_t addr)
1094 {
1095 	uint8_t* ptrAddress = (_onlyChrRam ? _chrRam : _chrRom) + (addr & 0x3FFF);
1096 
1097 	for(int i = 0; i < 64; i++) {
1098 		uint8_t* pageAddress = _chrPages[i];
1099 		if(pageAddress != nullptr && ptrAddress >= pageAddress && ptrAddress <= pageAddress + 0xFF) {
1100 			return (i << 8) + (uint32_t)(ptrAddress - pageAddress);
1101 		}
1102 	}
1103 
1104 	//Address is currently not mapped
1105 	return -1;
1106 }
1107 
FromAbsoluteAddress(uint32_t addr,AddressType type)1108 int32_t BaseMapper::FromAbsoluteAddress(uint32_t addr, AddressType type)
1109 {
1110 	uint8_t* ptrAddress;
1111 
1112 	switch(type) {
1113 		case AddressType::PrgRom: ptrAddress = _prgRom; break;
1114 		case AddressType::WorkRam: ptrAddress = _workRam; break;
1115 		case AddressType::SaveRam: ptrAddress = _saveRam; break;
1116 		case AddressType::Register: return addr & 0xFFFF; break;
1117 		case AddressType::InternalRam: return addr & 0x1FFF; break;
1118 		default: return -1;
1119 	}
1120 	ptrAddress += addr;
1121 
1122 	for(int i = 0; i < 256; i++) {
1123 		uint8_t* pageAddress = _prgPages[i];
1124 		if(pageAddress != nullptr && ptrAddress >= pageAddress && ptrAddress <= pageAddress + 0xFF) {
1125 			return (i << 8) + (uint32_t)(ptrAddress - pageAddress);
1126 		}
1127 	}
1128 
1129 	//Address is currently not mapped
1130 	return -1;
1131 }
1132 
FromAbsolutePpuAddress(uint32_t addr,PpuAddressType type)1133 int32_t BaseMapper::FromAbsolutePpuAddress(uint32_t addr, PpuAddressType type)
1134 {
1135 	uint8_t* ptrAddress;
1136 
1137 	switch(type) {
1138 		case PpuAddressType::ChrRom: ptrAddress = _chrRom; break;
1139 		case PpuAddressType::ChrRam: ptrAddress = _chrRam; break;
1140 		case PpuAddressType::NametableRam: ptrAddress = _nametableRam; break;
1141 		default: return -1;
1142 	}
1143 	ptrAddress += addr;
1144 
1145 	for(int i = 0; i < 0x40; i++) {
1146 		uint8_t* pageAddress = _chrPages[i];
1147 		if(pageAddress != nullptr && ptrAddress >= pageAddress && ptrAddress <= pageAddress + 0xFF) {
1148 			return (i << 8) + (uint32_t)(ptrAddress - pageAddress);
1149 		}
1150 	}
1151 
1152 	//Address is currently not mapped
1153 	return -1;
1154 }
1155 
IsWriteRegister(uint16_t addr)1156 bool BaseMapper::IsWriteRegister(uint16_t addr)
1157 {
1158 	return _isWriteRegisterAddr[addr];
1159 }
1160 
IsReadRegister(uint16_t addr)1161 bool BaseMapper::IsReadRegister(uint16_t addr)
1162 {
1163 	return _allowRegisterRead && _isReadRegisterAddr[addr];
1164 }
1165 
GetState()1166 CartridgeState BaseMapper::GetState()
1167 {
1168 	CartridgeState state;
1169 
1170 	state.Mirroring = _mirroringType;
1171 	state.HasBattery = _romInfo.HasBattery;
1172 
1173 	state.PrgRomSize = _prgSize;
1174 	state.ChrRomSize = _onlyChrRam ? 0 : _chrRomSize;
1175 	state.ChrRamSize = _chrRamSize;
1176 
1177 	state.PrgPageCount = GetPRGPageCount();
1178 	state.PrgPageSize = InternalGetPrgPageSize();
1179 	state.ChrPageCount = GetCHRPageCount();
1180 	state.ChrPageSize = InternalGetChrPageSize();
1181 	for(int i = 0; i < 0x100; i++) {
1182 		state.PrgMemoryOffset[i] = _prgMemoryOffset[i];
1183 		state.PrgType[i] = _prgMemoryType[i];
1184 		state.PrgMemoryAccess[i] = _prgMemoryAccess[i];
1185 	}
1186 	for(int i = 0; i < 0x40; i++) {
1187 		state.ChrMemoryOffset[i] = _chrMemoryOffset[i];
1188 		state.ChrType[i] = _chrMemoryType[i];
1189 		state.ChrMemoryAccess[i] = _chrMemoryAccess[i];
1190 	}
1191 
1192 	state.WorkRamPageSize = GetWorkRamPageSize();
1193 	state.SaveRamPageSize = GetSaveRamPageSize();
1194 
1195 	return state;
1196 }
1197 
GetRomFileData(vector<uint8_t> & out,bool asIpsFile,uint8_t * header)1198 void BaseMapper::GetRomFileData(vector<uint8_t> &out, bool asIpsFile, uint8_t* header)
1199 {
1200 	if(header) {
1201 		//Get original rom with edited header
1202 		vector<uint8_t> originalFile;
1203 		_console->GetRomPath().ReadFile(originalFile);
1204 
1205 		out.insert(out.end(), header, header+sizeof(NESHeader));
1206 		if(_romInfo.IsHeaderlessRom) {
1207 			out.insert(out.end(), originalFile.begin(), originalFile.end());
1208 		} else {
1209 			out.insert(out.end(), originalFile.begin() + sizeof(NESHeader), originalFile.end());
1210 		}
1211 	} else {
1212 		vector<uint8_t> newFile;
1213 		newFile.insert(newFile.end(), (uint8_t*)&_romInfo.NesHeader, ((uint8_t*)&_romInfo.NesHeader) + sizeof(NESHeader));
1214 		newFile.insert(newFile.end(), _prgRom, _prgRom + _prgSize);
1215 		if(HasChrRom()) {
1216 			newFile.insert(newFile.end(), _chrRom, _chrRom + _chrRomSize);
1217 		}
1218 
1219 		//Get edited rom
1220 		if(asIpsFile) {
1221 			vector<uint8_t> originalFile;
1222 			_console->GetRomPath().ReadFile(originalFile);
1223 
1224 			vector<uint8_t> patchData = IpsPatcher::CreatePatch(originalFile, newFile);
1225 			out.insert(out.end(), patchData.begin(), patchData.end());
1226 		} else {
1227 			out.insert(out.end(), newFile.begin(), newFile.end());
1228 		}
1229 	}
1230 }
1231 
GetPrgChrCopy()1232 vector<uint8_t> BaseMapper::GetPrgChrCopy()
1233 {
1234 	vector<uint8_t> data;
1235 	data.resize(_prgSize + (_onlyChrRam ? 0 : _chrRomSize));
1236 	memcpy(data.data(), _prgRom, _prgSize);
1237 	if(!_onlyChrRam) {
1238 		memcpy(data.data() + _prgSize, _chrRom, _chrRomSize);
1239 	}
1240 	return data;
1241 }
1242 
RestorePrgChrBackup(vector<uint8_t> & backupData)1243 void BaseMapper::RestorePrgChrBackup(vector<uint8_t> &backupData)
1244 {
1245 	memcpy(_prgRom, backupData.data(), _prgSize);
1246 	if(!_onlyChrRam) {
1247 		memcpy(_chrRom, backupData.data() + _prgSize, _chrRomSize);
1248 	}
1249 }
1250 
RevertPrgChrChanges()1251 void BaseMapper::RevertPrgChrChanges()
1252 {
1253 	memcpy(_prgRom, _originalPrgRom.data(), _originalPrgRom.size());
1254 	if(_chrRom) {
1255 		memcpy(_chrRom, _originalChrRom.data(), _originalChrRom.size());
1256 	}
1257 }
1258 
HasPrgChrChanges()1259 bool BaseMapper::HasPrgChrChanges()
1260 {
1261 	if(memcmp(_prgRom, _originalPrgRom.data(), _originalPrgRom.size()) != 0) {
1262 		return true;
1263 	}
1264 	if(_chrRom) {
1265 		if(memcmp(_chrRom, _originalChrRom.data(), _originalChrRom.size()) != 0) {
1266 			return true;
1267 		}
1268 	}
1269 	return false;
1270 }