mirror of
https://github.com/libretro/Mesen.git
synced 2024-11-23 17:19:39 +00:00
Work/Save ram general improvements.
-Added support for work ram size field in NES 2.0 -Updated DB to list both work ram & save ram sizes -Fixed MMC1/MMC3 implementation (and implemented the wram/sram protection bits) Holy Diver tests now all pass (except a minor IRQ timing issue for FME7)
This commit is contained in:
parent
dffe4cb26e
commit
062d22ae80
@ -49,7 +49,7 @@ void BaseMapper::SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, int16
|
||||
break;
|
||||
case PrgMemoryType::WorkRam:
|
||||
source = _workRam;
|
||||
pageCount = GetWorkRamSize() / GetWorkRamPageSize();
|
||||
pageCount = _workRamSize / GetWorkRamPageSize();
|
||||
pageSize = GetWorkRamPageSize();
|
||||
defaultAccessType |= MemoryAccessType::Write;
|
||||
break;
|
||||
@ -57,6 +57,13 @@ void BaseMapper::SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, int16
|
||||
throw new std::runtime_error("Invalid parameter");
|
||||
}
|
||||
|
||||
if(pageCount == 0) {
|
||||
#ifdef _DEBUG
|
||||
MessageManager::DisplayMessage("Debug", "Tried to map undefined save/work ram.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if(pageNumber < 0) {
|
||||
//Can't use modulo for negative number because pageCount is sometimes not a power of 2. (Fixes some Mapper 191 games)
|
||||
pageNumber = pageCount + pageNumber;
|
||||
@ -247,12 +254,14 @@ void BaseMapper::LoadBattery()
|
||||
|
||||
void BaseMapper::SaveBattery()
|
||||
{
|
||||
ofstream batteryFile(_batteryFilename, ios::out | ios::binary);
|
||||
if(HasBattery()) {
|
||||
ofstream batteryFile(_batteryFilename, ios::out | ios::binary);
|
||||
|
||||
if(batteryFile) {
|
||||
batteryFile.write((char*)_saveRam, _saveRamSize);
|
||||
if(batteryFile) {
|
||||
batteryFile.write((char*)_saveRam, _saveRamSize);
|
||||
|
||||
batteryFile.close();
|
||||
batteryFile.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,7 +327,7 @@ void BaseMapper::RemoveRegisterRange(uint16_t startAddr, uint16_t endAddr, Memor
|
||||
void BaseMapper::StreamState(bool saving)
|
||||
{
|
||||
ArrayInfo<uint8_t> chrRam = { _chrRam, _chrRamSize };
|
||||
ArrayInfo<uint8_t> workRam = { _workRam, GetWorkRamSize() };
|
||||
ArrayInfo<uint8_t> workRam = { _workRam, _workRamSize };
|
||||
ArrayInfo<uint8_t> saveRam = { _saveRam, _saveRamSize };
|
||||
ArrayInfo<uint32_t> prgPageNumbers = { _prgPageNumbers, 64 };
|
||||
ArrayInfo<uint32_t> chrPageNumbers = { _chrPageNumbers, 64 };
|
||||
@ -349,10 +358,23 @@ void BaseMapper::Initialize(RomData &romData)
|
||||
_mapperID = romData.MapperID;
|
||||
_subMapperID = romData.SubMapperID;
|
||||
|
||||
_databaseInfo = romData.DatabaseInfo;
|
||||
|
||||
_romName = romData.RomName;
|
||||
_romFilename = romData.Filename;
|
||||
_batteryFilename = GetBatteryFilename();
|
||||
_saveRamSize = GetSaveRamSize(); //Needed because we need to call SaveBattery() in the destructor (and calling virtual functions in the destructor doesn't work correctly)
|
||||
|
||||
if(romData.SaveRamSize == -1) {
|
||||
_saveRamSize = GetSaveRamSize(); //Needed because we need to call SaveBattery() in the destructor (and calling virtual functions in the destructor doesn't work correctly)
|
||||
} else {
|
||||
_saveRamSize = romData.SaveRamSize;
|
||||
}
|
||||
|
||||
if(romData.WorkRamSize == -1) {
|
||||
_workRamSize = GetWorkRamSize();
|
||||
} else {
|
||||
_workRamSize = romData.WorkRamSize;
|
||||
}
|
||||
|
||||
_allowRegisterRead = AllowRegisterRead();
|
||||
|
||||
@ -382,11 +404,11 @@ void BaseMapper::Initialize(RomData &romData)
|
||||
_hasBusConflicts = HasBusConflicts();
|
||||
|
||||
_saveRam = new uint8_t[_saveRamSize];
|
||||
_workRam = new uint8_t[GetWorkRamSize()];
|
||||
_workRam = new uint8_t[_workRamSize];
|
||||
|
||||
memset(_saveRam, 0, _saveRamSize);
|
||||
memset(_workRam, 0, GetWorkRamSize());
|
||||
if(romData.HasTrainer && GetWorkRamSize() >= 0x2000) {
|
||||
memset(_workRam, 0, _workRamSize);
|
||||
if(romData.HasTrainer && _workRamSize >= 0x2000) {
|
||||
memcpy(_workRam + 0x1000, romData.TrainerData.data(), 512);
|
||||
}
|
||||
|
||||
@ -424,7 +446,11 @@ void BaseMapper::Initialize(RomData &romData)
|
||||
}
|
||||
|
||||
//Setup a default work/save ram in 0x6000-0x7FFF space
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam);
|
||||
if(HasBattery() && _saveRamSize > 0) {
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, PrgMemoryType::SaveRam);
|
||||
} else if(_workRamSize > 0) {
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, PrgMemoryType::WorkRam);
|
||||
}
|
||||
|
||||
InitMapper();
|
||||
InitMapper(romData);
|
||||
@ -436,9 +462,8 @@ void BaseMapper::Initialize(RomData &romData)
|
||||
|
||||
BaseMapper::~BaseMapper()
|
||||
{
|
||||
if(HasBattery()) {
|
||||
SaveBattery();
|
||||
}
|
||||
SaveBattery();
|
||||
|
||||
delete[] _chrRam;
|
||||
delete[] _chrRom;
|
||||
delete[] _prgRom;
|
||||
@ -623,6 +648,11 @@ void BaseMapper::NotifyVRAMAddressChange(uint16_t addr)
|
||||
//Used by MMC3/MMC5/etc
|
||||
}
|
||||
|
||||
bool BaseMapper::IsNes20()
|
||||
{
|
||||
return _nesHeader.GetRomHeaderVersion() == RomHeaderVersion::Nes2_0;
|
||||
}
|
||||
|
||||
//Debugger Helper Functions
|
||||
uint8_t* BaseMapper::GetPrgRom()
|
||||
{
|
||||
@ -642,7 +672,7 @@ void BaseMapper::GetPrgCopy(uint8_t **buffer)
|
||||
|
||||
uint32_t BaseMapper::GetPrgSize(bool getWorkRamSize)
|
||||
{
|
||||
return getWorkRamSize ? GetWorkRamSize() : _prgSize;
|
||||
return getWorkRamSize ? _workRamSize : _prgSize;
|
||||
}
|
||||
|
||||
void BaseMapper::GetChrRomCopy(uint8_t **buffer)
|
||||
@ -674,7 +704,7 @@ int32_t BaseMapper::ToAbsoluteAddress(uint16_t addr)
|
||||
int32_t BaseMapper::ToAbsoluteRamAddress(uint16_t addr)
|
||||
{
|
||||
uint8_t *prgRamAddr = _prgPages[addr >> 8] + (addr & 0xFF);
|
||||
if(prgRamAddr >= _workRam && prgRamAddr < _workRam + GetWorkRamSize()) {
|
||||
if(prgRamAddr >= _workRam && prgRamAddr < _workRam + _workRamSize) {
|
||||
return (uint32_t)(prgRamAddr - _workRam);
|
||||
}
|
||||
return -1;
|
||||
|
@ -77,6 +77,8 @@ private:
|
||||
|
||||
protected:
|
||||
NESHeader _nesHeader;
|
||||
GameInfo _databaseInfo;
|
||||
|
||||
uint16_t _mapperID;
|
||||
uint8_t _subMapperID;
|
||||
GameSystem _gameSystem;
|
||||
@ -90,6 +92,7 @@ protected:
|
||||
|
||||
uint8_t* _saveRam = nullptr;
|
||||
uint32_t _saveRamSize = 0;
|
||||
uint32_t _workRamSize = 0;
|
||||
uint8_t* _workRam = nullptr;
|
||||
bool _hasBattery = false;
|
||||
|
||||
@ -98,6 +101,8 @@ protected:
|
||||
virtual uint16_t GetPRGPageSize() = 0;
|
||||
virtual uint16_t GetCHRPageSize() = 0;
|
||||
|
||||
bool IsNes20();
|
||||
|
||||
virtual uint16_t GetChrRamPageSize() { return 0x2000; }
|
||||
|
||||
//Save ram is battery backed and saved to disk
|
||||
@ -139,7 +144,6 @@ protected:
|
||||
|
||||
bool HasBattery();
|
||||
void LoadBattery();
|
||||
void SaveBattery();
|
||||
string GetBatteryFilename();
|
||||
|
||||
uint32_t GetPRGPageCount();
|
||||
@ -163,6 +167,7 @@ protected:
|
||||
|
||||
public:
|
||||
void Initialize(RomData &romData);
|
||||
|
||||
virtual ~BaseMapper();
|
||||
virtual void Reset(bool softReset);
|
||||
|
||||
@ -173,6 +178,7 @@ public:
|
||||
virtual void GetMemoryRanges(MemoryRanges &ranges);
|
||||
|
||||
void ApplyCheats();
|
||||
void SaveBattery();
|
||||
|
||||
virtual void SetDefaultNametables(uint8_t* nametableA, uint8_t* nametableB);
|
||||
|
||||
|
@ -42,6 +42,11 @@ void Console::Initialize(string romFilename, stringstream *filestream, string ip
|
||||
{
|
||||
SoundMixer::StopAudio();
|
||||
|
||||
if(_mapper) {
|
||||
//Ensure we save any battery file before loading a new game
|
||||
_mapper->SaveBattery();
|
||||
}
|
||||
|
||||
MessageManager::SendNotification(ConsoleNotificationType::GameStopped);
|
||||
shared_ptr<BaseMapper> mapper = MapperFactory::InitializeFromFile(romFilename, filestream, ipsFilename, archiveFileIndex);
|
||||
|
||||
|
@ -39,7 +39,7 @@ void GameDatabase::InitDatabase()
|
||||
continue;
|
||||
}
|
||||
vector<string> values = split(lineContent, ',');
|
||||
if(values.size() >= 12) {
|
||||
if(values.size() >= 13) {
|
||||
GameInfo gameInfo{
|
||||
(uint32_t)std::stoll(values[0], nullptr, 16),
|
||||
values[1],
|
||||
@ -51,9 +51,10 @@ void GameDatabase::InitDatabase()
|
||||
ToInt<uint32_t>(values[7]),
|
||||
ToInt<uint32_t>(values[8]),
|
||||
ToInt<uint32_t>(values[9]),
|
||||
ToInt<uint32_t>(values[10]) == 0 ? false : true,
|
||||
values[11],
|
||||
values.size() > 12 ? values[12] : ""
|
||||
ToInt<uint32_t>(values[10]),
|
||||
ToInt<uint32_t>(values[11]) == 0 ? false : true,
|
||||
values[12],
|
||||
values.size() > 13 ? values[13] : ""
|
||||
};
|
||||
_gameDatabase[gameInfo.Crc] = gameInfo;
|
||||
}
|
||||
@ -126,6 +127,15 @@ void GameDatabase::InitializeInputDevices(string inputType, GameSystem system)
|
||||
uint8_t GameDatabase::GetSubMapper(GameInfo &info)
|
||||
{
|
||||
switch(info.MapperID) {
|
||||
case 1:
|
||||
if(info.Board.find("SEROM") != string::npos ||
|
||||
info.Board.find("SHROM") != string::npos ||
|
||||
info.Board.find("SH1ROM") != string::npos) {
|
||||
//SEROM, SHROM, SH1ROM have fixed PRG banking
|
||||
return 5;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if(info.Board.compare("NES-CNROM") == 0) {
|
||||
//Enable bus conflicts for CNROM games
|
||||
@ -136,6 +146,8 @@ uint8_t GameDatabase::GetSubMapper(GameInfo &info)
|
||||
case 4:
|
||||
if(info.Board.compare("ACCLAIM-MC-ACC") == 0) {
|
||||
return 3; //Acclaim MC-ACC (MMC3 clone)
|
||||
} else if(info.Chip.compare("MMC6B") == 0) {
|
||||
return 1; //MMC6 (Star Tropics)
|
||||
}
|
||||
break;
|
||||
|
||||
@ -192,30 +204,24 @@ uint8_t GameDatabase::GetSubMapper(GameInfo &info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GameDatabase::UpdateRomData(uint32_t romCrc, RomData &romData)
|
||||
void GameDatabase::SetGameInfo(uint32_t romCrc, RomData &romData, bool updateRomData)
|
||||
{
|
||||
GameInfo info = {};
|
||||
|
||||
InitDatabase();
|
||||
|
||||
auto result = _gameDatabase.find(romCrc);
|
||||
|
||||
if(result != _gameDatabase.end()) {
|
||||
MessageManager::Log("[DB] Game found in database");
|
||||
GameInfo info = result->second;
|
||||
info = result->second;
|
||||
|
||||
romData.MapperID = info.MapperID;
|
||||
romData.System = GetGameSystem(info.System);
|
||||
romData.SubMapperID = GetSubMapper(info);
|
||||
if(info.ChrRamSize > 0) {
|
||||
romData.ChrRamSize = info.ChrRamSize * 1024;
|
||||
}
|
||||
romData.HasBattery |= info.HasBattery;
|
||||
|
||||
if(!info.Mirroring.empty()) {
|
||||
romData.MirroringType = info.Mirroring.compare("h") == 0 ? MirroringType::Horizontal : MirroringType::Vertical;
|
||||
}
|
||||
|
||||
MessageManager::Log("[DB] Mapper: " + std::to_string(romData.MapperID) + " Sub: " + std::to_string(romData.SubMapperID));
|
||||
MessageManager::Log("[DB] Mapper: " + std::to_string(info.MapperID) + " Sub: " + std::to_string(GetSubMapper(info)));
|
||||
MessageManager::Log("[DB] System : " + info.System);
|
||||
MessageManager::Log("[DB] Board: " + info.Board);
|
||||
MessageManager::Log("[DB] Chip: " + info.Chip);
|
||||
|
||||
if(!info.Mirroring.empty()) {
|
||||
MessageManager::Log("[DB] Mirroring: " + string(info.Mirroring.compare("h") == 0 ? "Horizontal" : "Vertical"));
|
||||
}
|
||||
@ -224,19 +230,51 @@ void GameDatabase::UpdateRomData(uint32_t romCrc, RomData &romData)
|
||||
if(info.ChrRamSize > 0) {
|
||||
MessageManager::Log("[DB] CHR RAM: " + std::to_string(info.ChrRamSize) + " KB");
|
||||
}
|
||||
if(info.WorkRamSize > 0) {
|
||||
MessageManager::Log("[DB] Work RAM: " + std::to_string(info.WorkRamSize) + " KB");
|
||||
}
|
||||
if(info.SaveRamSize > 0) {
|
||||
MessageManager::Log("[DB] Save RAM: " + std::to_string(info.SaveRamSize) + " KB");
|
||||
}
|
||||
MessageManager::Log("[DB] Battery: " + string(info.HasBattery ? "Yes" : "No"));
|
||||
|
||||
if(EmulationSettings::CheckFlag(EmulationFlags::AutoConfigureInput)) {
|
||||
InitializeInputDevices(info.InputType, romData.System);
|
||||
if(updateRomData) {
|
||||
MessageManager::Log("[DB] Database info will be used instead of file header.");
|
||||
UpdateRomData(info, romData);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
#ifdef _DEBUG
|
||||
MessageManager::DisplayMessage("DB", "Mapper: " + std::to_string(romData.MapperID) + " Sub: " + std::to_string(romData.SubMapperID) + " System: " + info.System);
|
||||
#endif
|
||||
#endif
|
||||
} else {
|
||||
MessageManager::Log("[DB] Game not found in database");
|
||||
if(EmulationSettings::CheckFlag(EmulationFlags::AutoConfigureInput)) {
|
||||
InitializeInputDevices("", romData.System);
|
||||
}
|
||||
}
|
||||
|
||||
if(EmulationSettings::CheckFlag(EmulationFlags::AutoConfigureInput)) {
|
||||
InitializeInputDevices(info.InputType, romData.System);
|
||||
}
|
||||
|
||||
romData.DatabaseInfo = info;
|
||||
}
|
||||
|
||||
void GameDatabase::UpdateRomData(GameInfo &info, RomData &romData)
|
||||
{
|
||||
romData.MapperID = info.MapperID;
|
||||
romData.System = GetGameSystem(info.System);
|
||||
romData.SubMapperID = GetSubMapper(info);
|
||||
if(info.ChrRamSize > 0) {
|
||||
romData.ChrRamSize = info.ChrRamSize * 1024;
|
||||
}
|
||||
if(info.WorkRamSize > 0) {
|
||||
romData.WorkRamSize = info.WorkRamSize * 1024;
|
||||
}
|
||||
if(info.SaveRamSize > 0) {
|
||||
romData.SaveRamSize = info.SaveRamSize * 1024;
|
||||
}
|
||||
romData.HasBattery |= info.HasBattery;
|
||||
|
||||
if(!info.Mirroring.empty()) {
|
||||
romData.MirroringType = info.Mirroring.compare("h") == 0 ? MirroringType::Horizontal : MirroringType::Vertical;
|
||||
}
|
||||
}
|
@ -1,23 +1,7 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <unordered_map>
|
||||
|
||||
struct GameInfo
|
||||
{
|
||||
uint32_t Crc;
|
||||
string System;
|
||||
string Board;
|
||||
string Pcb;
|
||||
string Chip;
|
||||
uint8_t MapperID;
|
||||
uint32_t PrgRomSize;
|
||||
uint32_t ChrRomSize;
|
||||
uint32_t ChrRamSize;
|
||||
uint32_t WorkRamSize;
|
||||
bool HasBattery;
|
||||
string Mirroring;
|
||||
string InputType;
|
||||
};
|
||||
#include "RomData.h"
|
||||
|
||||
class GameDatabase
|
||||
{
|
||||
@ -32,7 +16,8 @@ private:
|
||||
static void InitializeInputDevices(string inputType, GameSystem system);
|
||||
|
||||
static void InitDatabase();
|
||||
static void UpdateRomData(GameInfo &info, RomData &romData);
|
||||
|
||||
public:
|
||||
static void UpdateRomData(uint32_t romCrc, RomData &romData);
|
||||
static void SetGameInfo(uint32_t romCrc, RomData &romData, bool updateRomData);
|
||||
};
|
71
Core/MMC1.h
71
Core/MMC1.h
@ -46,6 +46,9 @@ class MMC1 : public BaseMapper
|
||||
|
||||
int32_t _lastWriteCycle = -1;
|
||||
|
||||
bool _forceWramOn;
|
||||
MMC1Registers _lastChrReg;
|
||||
|
||||
private:
|
||||
bool HasResetFlag(uint8_t value)
|
||||
{
|
||||
@ -107,24 +110,48 @@ class MMC1 : public BaseMapper
|
||||
_chrReg1 = _state.RegC000 & 0x1F;
|
||||
_prgReg = _state.RegE000 & 0x0F;
|
||||
|
||||
uint8_t extraReg = _lastChrReg == MMC1Registers::RegC000 && _chrMode == ChrMode::_4k ? _chrReg1 : _chrReg0;
|
||||
uint8_t prgBankSelect = 0;
|
||||
if(_prgSize == 0x80000) {
|
||||
//512kb carts use bit 7 of $A000/$C000 to select page
|
||||
//This is used for SUROM (Dragon Warrior 3/4, Dragon Quest 4)
|
||||
prgBankSelect = extraReg & 0x10;
|
||||
}
|
||||
|
||||
if(_wramDisable && !_forceWramOn) {
|
||||
RemoveCpuMemoryMapping(0x6000, 0x7FFF);
|
||||
} else {
|
||||
if(_saveRamSize + _workRamSize > 0x4000) {
|
||||
//SXROM, 32kb of save ram
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, (extraReg >> 2) & 0x03, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam);
|
||||
} else if(_saveRamSize + _workRamSize > 0x2000) {
|
||||
if(_saveRamSize == 0x2000 && _workRamSize == 0x2000) {
|
||||
//SOROM, half of the 16kb ram is battery backed
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, (extraReg >> 3) & 0x01 ? PrgMemoryType::WorkRam : PrgMemoryType::SaveRam);
|
||||
} else {
|
||||
//Unknown, shouldn't happen
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, (extraReg >> 2) & 0x01, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam);
|
||||
}
|
||||
} else {
|
||||
//Everything else - 8kb of work or save ram
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam);
|
||||
}
|
||||
}
|
||||
|
||||
if(_subMapperID == 5) {
|
||||
//SubMapper 5
|
||||
//"001: 5 Fixed PRG SEROM, SHROM, SH1ROM use a fixed 32k PRG ROM with no banking support.
|
||||
SelectPrgPage2x(0, 0);
|
||||
} else {
|
||||
//This is used for SUROM (Dragon Warrior 3/4, Dragon Quest 4)
|
||||
uint8_t prgPageAdjust = (_state.RegA000 & 0x10);
|
||||
|
||||
if(_prgMode == PrgMode::_32k) {
|
||||
SelectPRGPage(0, _prgReg + prgPageAdjust);
|
||||
SelectPRGPage(1, _prgReg + prgPageAdjust + 1);
|
||||
SelectPrgPage2x(0, (_prgReg & 0xFE) | prgBankSelect);
|
||||
} else if(_prgMode == PrgMode::_16k) {
|
||||
if(_slotSelect == SlotSelect::x8000) {
|
||||
SelectPRGPage(0, _prgReg + prgPageAdjust);
|
||||
SelectPRGPage(1, 0xF + prgPageAdjust);
|
||||
SelectPRGPage(0, _prgReg | prgBankSelect);
|
||||
SelectPRGPage(1, 0x0F | prgBankSelect);
|
||||
} else if(_slotSelect == SlotSelect::xC000) {
|
||||
SelectPRGPage(0, prgPageAdjust);
|
||||
SelectPRGPage(1, _prgReg + prgPageAdjust);
|
||||
SelectPRGPage(0, 0 | prgBankSelect);
|
||||
SelectPRGPage(1, _prgReg | prgBankSelect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,7 +168,10 @@ class MMC1 : public BaseMapper
|
||||
void StreamState(bool saving)
|
||||
{
|
||||
BaseMapper::StreamState(saving);
|
||||
Stream(_state.Reg8000, _state.RegA000, _state.RegC000, _state.RegE000, _writeBuffer, _shiftCount, _lastWriteCycle);
|
||||
Stream(_state.Reg8000, _state.RegA000, _state.RegC000, _state.RegE000, _writeBuffer, _shiftCount, _lastWriteCycle, _lastChrReg);
|
||||
if(!saving) {
|
||||
UpdateState();
|
||||
}
|
||||
}
|
||||
|
||||
virtual uint16_t GetPRGPageSize() { return 0x4000; }
|
||||
@ -149,12 +179,15 @@ class MMC1 : public BaseMapper
|
||||
|
||||
void InitMapper()
|
||||
{
|
||||
_state.Reg8000 = 0x0C; //On powerup: bits 2,3 of $8000 are set
|
||||
_state.Reg8000 = 0x0C; //On powerup: bits 2,3 of $8000 are set (this ensures the $8000 is bank 0, and $C000 is the last bank - needed for SEROM/SHROM/SH1ROM which do no support banking)
|
||||
_state.RegA000 = 0x00;
|
||||
_state.RegC000 = 0x00;
|
||||
_state.RegE000 = 0x10; //WRAM Disable: assume it's enabled at startup
|
||||
_state.RegE000 = (_databaseInfo.Board.find("MMC1B") != string::npos ? 0x10 : 0x00); //WRAM Disable: enabled by default for MMC1B
|
||||
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam);
|
||||
//"MMC1A: PRG RAM is always enabled" - Normally these roms should be classified as mapper 155
|
||||
_forceWramOn = (_databaseInfo.Board.compare("MMC1A") == 0);
|
||||
|
||||
_lastChrReg = MMC1Registers::RegA000;
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
@ -168,8 +201,16 @@ class MMC1 : public BaseMapper
|
||||
if(IsBufferFull(value)) {
|
||||
switch((MMC1Registers)((addr & 0x6000) >> 13)) {
|
||||
case MMC1Registers::Reg8000: _state.Reg8000 = _writeBuffer; break;
|
||||
case MMC1Registers::RegA000: _state.RegA000 = _writeBuffer; break;
|
||||
case MMC1Registers::RegC000: _state.RegC000 = _writeBuffer; break;
|
||||
case MMC1Registers::RegA000:
|
||||
_lastChrReg = MMC1Registers::RegA000;
|
||||
_state.RegA000 = _writeBuffer;
|
||||
break;
|
||||
|
||||
case MMC1Registers::RegC000:
|
||||
_lastChrReg = MMC1Registers::RegC000;
|
||||
_state.RegC000 = _writeBuffer;
|
||||
break;
|
||||
|
||||
case MMC1Registers::RegE000: _state.RegE000 = _writeBuffer; break;
|
||||
}
|
||||
|
||||
|
27
Core/MMC3.h
27
Core/MMC3.h
@ -145,8 +145,29 @@ class MMC3 : public BaseMapper
|
||||
|
||||
UpdateMirroring();
|
||||
|
||||
_wramEnabled = (_state.RegA001 & 0x80) == 0x80;
|
||||
_wramWriteProtected = (_state.RegA001 & 0x40) == 0x40;
|
||||
if(_subMapperID == 1) {
|
||||
bool wramEnabled = (_state.Reg8000 & 0x20) == 0x20;
|
||||
RemoveCpuMemoryMapping(0x6000, 0x7000);
|
||||
|
||||
uint8_t firstBankAccess = (_state.RegA001 & 0x10 ? MemoryAccessType::Write : 0) | (_state.RegA001 & 0x20 ? MemoryAccessType::Read : 0);
|
||||
uint8_t lastBankAccess = (_state.RegA001 & 0x40 ? MemoryAccessType::Write : 0) | (_state.RegA001 & 0x80 ? MemoryAccessType::Read : 0);
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
SetCpuMemoryMapping(0x7000 + i * 0x400, 0x71FF + i * 0x400, 0, PrgMemoryType::SaveRam, firstBankAccess);
|
||||
SetCpuMemoryMapping(0x7200 + i * 0x400, 0x73FF + i * 0x400, 1, PrgMemoryType::SaveRam, lastBankAccess);
|
||||
}
|
||||
} else {
|
||||
_wramEnabled = (_state.RegA001 & 0x80) == 0x80;
|
||||
_wramWriteProtected = (_state.RegA001 & 0x40) == 0x40;
|
||||
|
||||
if(IsNes20() && _subMapperID == 0) {
|
||||
if(_wramEnabled) {
|
||||
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam, CanWriteToWorkRam() ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
|
||||
} else {
|
||||
RemoveCpuMemoryMapping(0x6000, 0x7FFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdatePrgMapping();
|
||||
UpdateChrMapping();
|
||||
@ -163,6 +184,8 @@ class MMC3 : public BaseMapper
|
||||
|
||||
virtual uint16_t GetPRGPageSize() { return 0x2000; }
|
||||
virtual uint16_t GetCHRPageSize() { return 0x0400; }
|
||||
virtual uint32_t GetSaveRamPageSize() { return _subMapperID == 1 ? 0x200 : 0x2000; }
|
||||
virtual uint32_t GetSaveRamSize() { return _subMapperID == 1 ? 0x400 : 0x2000; }
|
||||
|
||||
virtual void InitMapper()
|
||||
{
|
||||
|
@ -146,14 +146,22 @@ struct NESHeader
|
||||
|
||||
uint32_t GetWorkRamSize()
|
||||
{
|
||||
uint8_t value = Byte10 & 0x0F;
|
||||
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
|
||||
if(GetRomHeaderVersion() == RomHeaderVersion::Nes2_0) {
|
||||
uint8_t value = Byte10 & 0x0F;
|
||||
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetSaveRamSize()
|
||||
{
|
||||
uint8_t value = (Byte10 & 0xF0) >> 4;
|
||||
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
|
||||
if(GetRomHeaderVersion() == RomHeaderVersion::Nes2_0) {
|
||||
uint8_t value = (Byte10 & 0xF0) >> 4;
|
||||
return value == 0 ? 0 : 128 * (uint32_t)std::pow(2, value);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t GetChrRamSize()
|
||||
@ -232,6 +240,25 @@ struct NsfHeader
|
||||
int32_t TrackFade[256];
|
||||
};
|
||||
|
||||
struct GameInfo
|
||||
{
|
||||
uint32_t Crc;
|
||||
string System;
|
||||
string Board;
|
||||
string Pcb;
|
||||
string Chip;
|
||||
uint8_t MapperID;
|
||||
uint32_t PrgRomSize;
|
||||
uint32_t ChrRomSize;
|
||||
uint32_t ChrRamSize;
|
||||
uint32_t WorkRamSize;
|
||||
uint32_t SaveRamSize;
|
||||
bool HasBattery;
|
||||
string Mirroring;
|
||||
string InputType;
|
||||
bool Valid;
|
||||
};
|
||||
|
||||
struct RomData
|
||||
{
|
||||
string RomName;
|
||||
@ -243,7 +270,10 @@ struct RomData
|
||||
bool HasBattery = false;
|
||||
bool HasTrainer = false;
|
||||
MirroringType MirroringType = MirroringType::Horizontal;
|
||||
|
||||
int32_t ChrRamSize = -1;
|
||||
int32_t SaveRamSize = -1;
|
||||
int32_t WorkRamSize = -1;
|
||||
|
||||
bool IsNes20Header = false;
|
||||
|
||||
@ -260,4 +290,5 @@ struct RomData
|
||||
|
||||
NESHeader NesHeader;
|
||||
NsfHeader NsfHeader;
|
||||
GameInfo DatabaseInfo;
|
||||
};
|
@ -29,6 +29,8 @@ RomData iNesLoader::LoadRom(vector<uint8_t>& romFile)
|
||||
}
|
||||
romData.HasTrainer = header.HasTrainer();
|
||||
romData.ChrRamSize = header.GetChrRamSize();
|
||||
romData.WorkRamSize = header.GetWorkRamSize();
|
||||
romData.SaveRamSize = header.GetSaveRamSize();
|
||||
romData.NesHeader = header;
|
||||
|
||||
if(romData.HasTrainer) {
|
||||
@ -54,20 +56,25 @@ RomData iNesLoader::LoadRom(vector<uint8_t>& romFile)
|
||||
MessageManager::Log("[iNes] Mapper: " + std::to_string(romData.MapperID) + " Sub:" + std::to_string(romData.SubMapperID));
|
||||
MessageManager::Log("[iNes] PRG ROM: " + std::to_string(romData.PrgRom.size()/1024) + " KB");
|
||||
MessageManager::Log("[iNes] CHR ROM: " + std::to_string(romData.ChrRom.size()/1024) + " KB");
|
||||
if(romData.ChrRamSize > 0) {
|
||||
MessageManager::Log("[iNes] CHR RAM: " + std::to_string(romData.ChrRamSize) + " KB");
|
||||
if(romData.ChrRamSize > 0 || romData.IsNes20Header) {
|
||||
MessageManager::Log("[iNes] CHR RAM: " + std::to_string(romData.ChrRamSize / 1024) + " KB");
|
||||
} else if(romData.ChrRom.size() == 0) {
|
||||
MessageManager::Log("[iNes] CHR RAM: 8 KB");
|
||||
}
|
||||
if(romData.WorkRamSize > 0 || romData.IsNes20Header) {
|
||||
MessageManager::Log("[iNes] Work RAM: " + std::to_string(romData.WorkRamSize / 1024) + " KB");
|
||||
}
|
||||
if(romData.SaveRamSize > 0 || romData.IsNes20Header) {
|
||||
MessageManager::Log("[iNes] Save RAM: " + std::to_string(romData.SaveRamSize / 1024) + " KB");
|
||||
}
|
||||
|
||||
MessageManager::Log("[iNes] Mirroring: " + string(romData.MirroringType == MirroringType::Horizontal ? "Horizontal" : romData.MirroringType == MirroringType::Vertical ? "Vertical" : "Four Screens"));
|
||||
MessageManager::Log("[iNes] Battery: " + string(romData.HasBattery ? "Yes" : "No"));
|
||||
if(romData.HasTrainer) {
|
||||
MessageManager::Log("[iNes] Trainer: Yes");
|
||||
}
|
||||
|
||||
if(!EmulationSettings::CheckFlag(EmulationFlags::DisableGameDatabase) && header.GetRomHeaderVersion() != RomHeaderVersion::Nes2_0) {
|
||||
GameDatabase::UpdateRomData(romCrc, romData);
|
||||
}
|
||||
GameDatabase::SetGameInfo(romCrc, romData, !EmulationSettings::CheckFlag(EmulationFlags::DisableGameDatabase) && header.GetRomHeaderVersion() != RomHeaderVersion::Nes2_0);
|
||||
|
||||
return romData;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user