2014-06-14 15:27:55 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "stdafx.h"
|
2014-06-26 01:52:37 +00:00
|
|
|
#include "Snapshotable.h"
|
2014-06-14 15:27:55 +00:00
|
|
|
#include "IMemoryHandler.h"
|
2014-06-24 06:47:32 +00:00
|
|
|
#include "ROMLoader.h"
|
2014-06-28 17:22:42 +00:00
|
|
|
#include <assert.h>
|
2014-07-03 00:28:29 +00:00
|
|
|
#include "../Utilities/FolderUtilities.h"
|
2015-07-05 23:05:33 +00:00
|
|
|
#include "CheatManager.h"
|
|
|
|
#include "MessageManager.h"
|
2014-06-14 15:27:55 +00:00
|
|
|
|
2015-07-05 23:05:33 +00:00
|
|
|
class BaseMapper : public IMemoryHandler, public Snapshotable, public INotificationListener
|
2014-06-14 15:27:55 +00:00
|
|
|
{
|
|
|
|
protected:
|
2014-07-13 02:22:40 +00:00
|
|
|
const int ExpansionRAMSize = 0x2000;
|
|
|
|
const int SRAMSize = 0x2000;
|
|
|
|
|
2014-06-24 06:47:32 +00:00
|
|
|
uint8_t* _prgRAM;
|
2015-07-05 23:05:33 +00:00
|
|
|
uint8_t* _originalPrgRam;
|
2014-06-24 06:47:32 +00:00
|
|
|
uint8_t* _chrRAM;
|
|
|
|
uint32_t _prgSize;
|
|
|
|
uint32_t _chrSize;
|
2014-07-13 02:22:40 +00:00
|
|
|
|
|
|
|
uint8_t* _SRAM;
|
|
|
|
uint8_t* _expansionRAM;
|
|
|
|
bool _hasExpansionRAM;
|
2014-06-24 19:11:04 +00:00
|
|
|
|
2014-06-26 01:52:37 +00:00
|
|
|
bool _hasCHRRAM;
|
2014-06-24 20:16:23 +00:00
|
|
|
bool _hasBattery;
|
|
|
|
wstring _romFilename;
|
|
|
|
|
2014-06-24 19:11:04 +00:00
|
|
|
MirroringType _mirroringType;
|
2014-06-14 15:27:55 +00:00
|
|
|
|
2014-06-24 14:19:24 +00:00
|
|
|
vector<uint8_t*> _prgPages;
|
|
|
|
vector<uint8_t*> _chrPages;
|
|
|
|
|
2014-06-26 01:52:37 +00:00
|
|
|
uint32_t* _prgSlotPages;
|
|
|
|
uint32_t* _chrSlotPages;
|
|
|
|
|
2014-06-25 16:22:48 +00:00
|
|
|
uint32_t _chrShift = -1;
|
2015-07-05 02:21:14 +00:00
|
|
|
uint32_t _chrSlotMaxIndex = -1;
|
2014-06-25 16:22:48 +00:00
|
|
|
uint32_t _prgShift = -1;
|
2015-07-05 02:21:14 +00:00
|
|
|
uint32_t _prgSlotMaxIndex = -1;
|
2014-06-25 16:22:48 +00:00
|
|
|
|
2015-07-05 02:21:14 +00:00
|
|
|
uint32_t _chrPageMask = -1;
|
|
|
|
uint32_t _prgPageMask = -1;
|
|
|
|
|
2014-06-14 22:20:56 +00:00
|
|
|
virtual void InitMapper() = 0;
|
|
|
|
|
2014-06-14 15:27:55 +00:00
|
|
|
public:
|
2014-06-24 06:47:32 +00:00
|
|
|
const static int PRGSize = 0x8000;
|
|
|
|
const static int CHRSize = 0x2000;
|
2014-06-24 20:16:23 +00:00
|
|
|
const static int PRGRAMSize = 0x2000;
|
2014-06-24 06:47:32 +00:00
|
|
|
|
2014-06-24 14:19:24 +00:00
|
|
|
protected:
|
|
|
|
virtual uint32_t GetPRGPageSize() = 0;
|
|
|
|
virtual uint32_t GetCHRPageSize() = 0;
|
|
|
|
|
|
|
|
void SelectPRGPage(uint32_t slot, uint32_t page)
|
|
|
|
{
|
2014-06-26 22:48:55 +00:00
|
|
|
//std::cout << std::dec << "PRG Slot " << (short)slot << ": " << (short)(page & (GetPRGPageCount() - 1)) << std::endl;
|
2015-06-24 23:26:19 +00:00
|
|
|
_prgPages[slot] = &_prgRAM[(page % GetPRGPageCount()) * GetPRGPageSize()];
|
2014-06-26 01:52:37 +00:00
|
|
|
_prgSlotPages[slot] = page;
|
2014-06-24 14:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SelectCHRPage(uint32_t slot, uint32_t page)
|
|
|
|
{
|
|
|
|
//std::cout << std::dec << "CHR Slot " << (short)slot << ": " << (short)page << std::endl;
|
2015-06-24 23:26:19 +00:00
|
|
|
_chrPages[slot] = &_chrRAM[(page % GetCHRPageCount()) * GetCHRPageSize()];
|
2014-06-26 01:52:37 +00:00
|
|
|
_chrSlotPages[slot] = page;
|
2014-06-24 14:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t GetPRGSlotCount()
|
|
|
|
{
|
|
|
|
return BaseMapper::PRGSize / GetPRGPageSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t GetCHRSlotCount()
|
|
|
|
{
|
|
|
|
return BaseMapper::CHRSize / GetCHRPageSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t GetPRGPageCount()
|
|
|
|
{
|
|
|
|
return _prgSize / GetPRGPageSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t GetCHRPageCount()
|
|
|
|
{
|
|
|
|
return _chrSize / GetCHRPageSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t log2(uint32_t value)
|
|
|
|
{
|
|
|
|
uint32_t counter = 0;
|
|
|
|
while(value >>= 1) {
|
|
|
|
counter++;
|
|
|
|
}
|
|
|
|
return counter;
|
|
|
|
}
|
|
|
|
|
2014-06-24 14:31:33 +00:00
|
|
|
uint32_t AddrToPRGSlot(uint16_t addr)
|
2014-06-24 14:19:24 +00:00
|
|
|
{
|
2015-07-05 02:21:14 +00:00
|
|
|
return (addr >> _prgShift) & _prgSlotMaxIndex;
|
2014-06-24 14:19:24 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 14:31:33 +00:00
|
|
|
uint32_t AddrToCHRSlot(uint16_t addr)
|
2014-06-24 14:19:24 +00:00
|
|
|
{
|
2015-07-05 02:21:14 +00:00
|
|
|
return (addr >> _chrShift) & _chrSlotMaxIndex;
|
2014-06-24 14:19:24 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 20:16:23 +00:00
|
|
|
wstring GetBatteryFilename()
|
|
|
|
{
|
2014-07-13 00:56:22 +00:00
|
|
|
return FolderUtilities::GetSaveFolder() + _romFilename + L".sav";
|
2014-06-24 20:16:23 +00:00
|
|
|
}
|
2015-07-05 23:05:33 +00:00
|
|
|
|
|
|
|
void RestoreOriginalPrgRam()
|
|
|
|
{
|
|
|
|
memcpy(_prgRAM, _originalPrgRam, GetPRGSize());
|
|
|
|
}
|
2014-06-24 20:16:23 +00:00
|
|
|
|
2014-06-26 01:52:37 +00:00
|
|
|
protected:
|
|
|
|
virtual void StreamState(bool saving)
|
|
|
|
{
|
|
|
|
StreamArray<uint32_t>(_prgSlotPages, GetPRGSlotCount());
|
|
|
|
StreamArray<uint32_t>(_chrSlotPages, GetCHRSlotCount());
|
|
|
|
|
|
|
|
Stream<bool>(_hasCHRRAM);
|
|
|
|
if(_hasCHRRAM) {
|
|
|
|
StreamArray<uint8_t>(_chrRAM, BaseMapper::CHRSize);
|
|
|
|
}
|
|
|
|
|
2014-06-26 22:48:55 +00:00
|
|
|
Stream<MirroringType>(_mirroringType);
|
|
|
|
|
2014-06-26 01:52:37 +00:00
|
|
|
if(!saving) {
|
|
|
|
for(int i = GetPRGSlotCount() - 1; i >= 0; i--) {
|
|
|
|
SelectPRGPage(i, _prgSlotPages[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i = GetCHRSlotCount() - 1; i >= 0; i--) {
|
|
|
|
SelectCHRPage(i, _chrSlotPages[i]);
|
|
|
|
}
|
|
|
|
}
|
2014-07-13 02:22:40 +00:00
|
|
|
|
|
|
|
Stream<bool>(_hasExpansionRAM);
|
|
|
|
if(_hasExpansionRAM) {
|
|
|
|
StreamArray<uint8_t>(_expansionRAM, BaseMapper::ExpansionRAMSize);
|
|
|
|
}
|
|
|
|
StreamArray<uint8_t>(_SRAM, BaseMapper::SRAMSize);
|
2014-06-26 01:52:37 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 06:47:32 +00:00
|
|
|
public:
|
2014-06-24 14:19:24 +00:00
|
|
|
void Initialize(ROMLoader &romLoader)
|
2014-06-14 15:27:55 +00:00
|
|
|
{
|
2014-06-24 14:19:24 +00:00
|
|
|
_mirroringType = romLoader.GetMirroringType();
|
2015-07-05 23:05:33 +00:00
|
|
|
romLoader.GetPRGRam(&_prgRAM);
|
|
|
|
romLoader.GetPRGRam(&_originalPrgRam);
|
|
|
|
romLoader.GetCHRRam(&_chrRAM);
|
2014-06-24 06:47:32 +00:00
|
|
|
_prgSize = romLoader.GetPRGSize();
|
|
|
|
_chrSize = romLoader.GetCHRSize();
|
2014-06-24 20:16:23 +00:00
|
|
|
_hasBattery = romLoader.HasBattery();
|
|
|
|
_romFilename = romLoader.GetFilename();
|
2014-06-24 06:47:32 +00:00
|
|
|
|
2014-07-13 02:22:40 +00:00
|
|
|
_hasExpansionRAM = false;
|
|
|
|
_SRAM = new uint8_t[SRAMSize];
|
|
|
|
_expansionRAM = new uint8_t[ExpansionRAMSize];
|
|
|
|
|
|
|
|
memset(_SRAM, 0, SRAMSize);
|
|
|
|
memset(_expansionRAM, 0, ExpansionRAMSize);
|
|
|
|
|
|
|
|
//Load battery data if present
|
|
|
|
if(HasBattery()) {
|
|
|
|
LoadBattery();
|
|
|
|
}
|
|
|
|
|
2014-06-24 06:47:32 +00:00
|
|
|
if(_chrSize == 0) {
|
2014-06-26 01:52:37 +00:00
|
|
|
_hasCHRRAM = true;
|
2014-06-24 06:47:32 +00:00
|
|
|
_chrRAM = new uint8_t[BaseMapper::CHRSize];
|
|
|
|
_chrSize = BaseMapper::CHRSize;
|
|
|
|
}
|
2014-06-14 22:20:56 +00:00
|
|
|
|
2014-06-24 14:19:24 +00:00
|
|
|
for(int i = GetPRGSlotCount(); i > 0; i--) {
|
|
|
|
_prgPages.push_back(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i = GetCHRSlotCount(); i > 0; i--) {
|
|
|
|
_chrPages.push_back(nullptr);
|
|
|
|
}
|
|
|
|
|
2014-06-26 01:52:37 +00:00
|
|
|
_prgSlotPages = new uint32_t[GetPRGSlotCount()];
|
|
|
|
_chrSlotPages = new uint32_t[GetCHRSlotCount()];
|
|
|
|
|
2015-07-05 02:21:14 +00:00
|
|
|
_prgShift = 15 - this->log2(GetPRGSlotCount());
|
|
|
|
_prgSlotMaxIndex = GetPRGSlotCount() - 1;
|
|
|
|
_chrShift = 13 - this->log2(GetCHRSlotCount());
|
|
|
|
_chrSlotMaxIndex = GetCHRSlotCount() - 1;
|
|
|
|
|
|
|
|
_chrPageMask = GetCHRPageSize() - 1;
|
|
|
|
_prgPageMask = GetPRGPageSize() - 1;
|
|
|
|
|
2014-06-14 22:20:56 +00:00
|
|
|
InitMapper();
|
2015-07-05 23:05:33 +00:00
|
|
|
|
|
|
|
MessageManager::RegisterNotificationListener(this);
|
|
|
|
|
|
|
|
ApplyCheats();
|
2014-06-14 15:27:55 +00:00
|
|
|
}
|
2014-06-19 23:58:15 +00:00
|
|
|
|
2014-06-26 15:41:27 +00:00
|
|
|
virtual ~BaseMapper()
|
2014-06-19 23:58:15 +00:00
|
|
|
{
|
2014-07-13 02:22:40 +00:00
|
|
|
if(HasBattery()) {
|
|
|
|
SaveBattery();
|
|
|
|
}
|
2014-06-24 06:47:32 +00:00
|
|
|
delete[] _prgRAM;
|
|
|
|
delete[] _chrRAM;
|
2015-07-05 23:05:33 +00:00
|
|
|
delete[] _originalPrgRam;
|
2014-06-28 23:56:51 +00:00
|
|
|
delete[] _prgSlotPages;
|
|
|
|
delete[] _chrSlotPages;
|
2014-07-13 02:22:40 +00:00
|
|
|
|
|
|
|
delete[] _SRAM;
|
|
|
|
delete[] _expansionRAM;
|
2015-07-05 23:05:33 +00:00
|
|
|
|
|
|
|
MessageManager::UnregisterNotificationListener(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessNotification(ConsoleNotificationType type)
|
|
|
|
{
|
|
|
|
switch(type) {
|
|
|
|
case ConsoleNotificationType::CheatAdded:
|
|
|
|
case ConsoleNotificationType::CheatRemoved:
|
|
|
|
ApplyCheats();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplyCheats()
|
|
|
|
{
|
|
|
|
RestoreOriginalPrgRam();
|
|
|
|
CheatManager::ApplyPrgCodes(_prgRAM, GetPRGSize());
|
2014-06-24 06:47:32 +00:00
|
|
|
}
|
|
|
|
|
2014-06-25 16:22:48 +00:00
|
|
|
void GetMemoryRanges(MemoryRanges &ranges)
|
2014-06-16 01:45:36 +00:00
|
|
|
{
|
2014-07-13 02:22:40 +00:00
|
|
|
ranges.AddHandler(MemoryType::RAM, MemoryOperation::Read, 0x4018, 0xFFFF);
|
|
|
|
ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x4018, 0xFFFF);
|
2014-06-25 16:22:48 +00:00
|
|
|
|
|
|
|
ranges.AddHandler(MemoryType::VRAM, MemoryOperation::Read, 0x0000, 0x1FFF);
|
|
|
|
ranges.AddHandler(MemoryType::VRAM, MemoryOperation::Write, 0x0000, 0x1FFF);
|
2014-06-14 15:27:55 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 20:16:23 +00:00
|
|
|
bool HasBattery()
|
|
|
|
{
|
|
|
|
return _hasBattery;
|
|
|
|
}
|
|
|
|
|
|
|
|
MirroringType GetMirroringType()
|
2014-06-14 15:27:55 +00:00
|
|
|
{
|
2014-06-24 14:19:24 +00:00
|
|
|
return _mirroringType;
|
2014-06-14 15:27:55 +00:00
|
|
|
}
|
2014-06-24 20:16:23 +00:00
|
|
|
|
2014-07-13 02:22:40 +00:00
|
|
|
void LoadBattery()
|
2014-06-24 20:16:23 +00:00
|
|
|
{
|
|
|
|
ifstream batteryFile(GetBatteryFilename(), ios::in | ios::binary);
|
|
|
|
|
|
|
|
if(batteryFile) {
|
2014-07-13 02:22:40 +00:00
|
|
|
batteryFile.read((char*)_SRAM, BaseMapper::PRGRAMSize);
|
2014-06-24 20:16:23 +00:00
|
|
|
|
|
|
|
batteryFile.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-13 02:22:40 +00:00
|
|
|
void SaveBattery()
|
2014-06-24 20:16:23 +00:00
|
|
|
{
|
|
|
|
ofstream batteryFile(GetBatteryFilename(), ios::out | ios::binary);
|
|
|
|
|
|
|
|
if(batteryFile) {
|
2014-07-13 02:22:40 +00:00
|
|
|
batteryFile.write((char*)_SRAM, BaseMapper::PRGRAMSize);
|
2014-06-24 20:16:23 +00:00
|
|
|
|
|
|
|
batteryFile.close();
|
|
|
|
}
|
|
|
|
}
|
2014-07-13 02:22:40 +00:00
|
|
|
|
|
|
|
virtual uint8_t ReadRAM(uint16_t addr)
|
2014-06-14 15:27:55 +00:00
|
|
|
{
|
2014-07-13 02:22:40 +00:00
|
|
|
if(addr >= 0x8000) {
|
2015-07-05 02:21:14 +00:00
|
|
|
return _prgPages[AddrToPRGSlot(addr)][addr & _prgPageMask];
|
2014-07-13 02:22:40 +00:00
|
|
|
} else if(addr >= 0x6000) {
|
|
|
|
return _SRAM[addr & 0x1FFF];
|
|
|
|
} else if(addr >= 0x4000) {
|
|
|
|
return _expansionRAM[addr & 0x1FFF];
|
|
|
|
}
|
|
|
|
return 0;
|
2014-06-14 15:27:55 +00:00
|
|
|
}
|
2014-06-16 01:45:36 +00:00
|
|
|
|
2015-06-24 23:26:19 +00:00
|
|
|
uint8_t* GetPRGCopy()
|
|
|
|
{
|
|
|
|
uint8_t* prgCopy = new uint8_t[_prgSize];
|
|
|
|
memcpy(prgCopy, _prgRAM, _prgSize);
|
|
|
|
return prgCopy;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t GetPRGSize()
|
|
|
|
{
|
|
|
|
return _prgSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t ToAbsoluteAddress(uint16_t addr)
|
|
|
|
{
|
2015-07-05 02:21:14 +00:00
|
|
|
return GetPRGPageSize() * (_prgSlotPages[AddrToPRGSlot(addr)] % GetPRGPageCount()) + (addr & _prgPageMask);
|
2015-06-24 23:26:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int32_t FromAbsoluteAddress(uint32_t addr)
|
|
|
|
{
|
|
|
|
uint32_t page = addr / GetPRGPageSize();
|
|
|
|
for(int i = 0, len = GetPRGSlotCount(); i < len; i++) {
|
|
|
|
if((_prgSlotPages[i] % GetPRGPageCount()) == page) {
|
|
|
|
uint32_t offset = addr - (page * GetPRGPageSize());
|
|
|
|
return GetPRGPageSize() * i + offset + 0x8000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Address is currently not mapped
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<uint32_t> GetPRGRanges()
|
|
|
|
{
|
|
|
|
vector<uint32_t> memoryRanges;
|
|
|
|
uint32_t slotCount = GetPRGSlotCount();
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < slotCount; i++) {
|
|
|
|
uint32_t page = _prgSlotPages[i] % GetPRGPageCount();
|
|
|
|
uint32_t pageStart = page * GetPRGPageSize();
|
|
|
|
uint32_t pageEnd = (page + 1) * GetPRGPageSize();
|
|
|
|
memoryRanges.push_back(pageStart);
|
|
|
|
memoryRanges.push_back(pageEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return memoryRanges;
|
|
|
|
}
|
|
|
|
|
2014-07-13 02:22:40 +00:00
|
|
|
virtual uint16_t RegisterStartAddress() { return 0x8000; }
|
|
|
|
virtual uint16_t RegisterEndAddress() { return 0xFFFF; }
|
|
|
|
virtual void WriteRegister(uint16_t addr, uint8_t value) { }
|
|
|
|
|
|
|
|
virtual void WriteRAM(uint16_t addr, uint8_t value)
|
2014-06-24 14:19:24 +00:00
|
|
|
{
|
2014-07-13 02:22:40 +00:00
|
|
|
if(addr >= RegisterStartAddress() && addr <= RegisterEndAddress()) {
|
|
|
|
WriteRegister(addr, value);
|
|
|
|
} else if(addr >= 0x6000) {
|
|
|
|
_SRAM[addr & 0x1FFF] = value;
|
|
|
|
} else if(addr >= 0x4000) {
|
|
|
|
_hasExpansionRAM = true;
|
|
|
|
_expansionRAM[addr & 0x1FFF] = value;
|
|
|
|
}
|
2014-06-24 14:19:24 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 06:47:32 +00:00
|
|
|
virtual uint8_t ReadVRAM(uint16_t addr)
|
2014-06-16 01:45:36 +00:00
|
|
|
{
|
2015-07-05 02:21:14 +00:00
|
|
|
return _chrPages[AddrToCHRSlot(addr)][addr & _chrPageMask];
|
2014-06-16 01:45:36 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 06:47:32 +00:00
|
|
|
virtual void WriteVRAM(uint16_t addr, uint8_t value)
|
2014-06-16 01:45:36 +00:00
|
|
|
{
|
2014-06-28 17:22:42 +00:00
|
|
|
if(_hasCHRRAM) {
|
2015-07-05 02:21:14 +00:00
|
|
|
_chrPages[AddrToCHRSlot(addr)][addr & _chrPageMask] = value;
|
2014-06-28 17:22:42 +00:00
|
|
|
} else {
|
2015-07-02 03:17:14 +00:00
|
|
|
//assert(false);
|
2014-06-28 17:22:42 +00:00
|
|
|
}
|
2014-06-16 01:45:36 +00:00
|
|
|
}
|
2014-06-25 21:30:35 +00:00
|
|
|
|
|
|
|
virtual void NotifyVRAMAddressChange(uint16_t addr)
|
|
|
|
{
|
|
|
|
//Used for MMC3 IRQ counter
|
|
|
|
}
|
2014-06-24 06:47:32 +00:00
|
|
|
};
|