2016-06-11 17:18:47 +00:00
|
|
|
#pragma once
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "BaseMapper.h"
|
|
|
|
#include "Namco163Audio.h"
|
2019-01-10 01:53:51 +00:00
|
|
|
#include "Console.h"
|
|
|
|
#include "BatteryManager.h"
|
2016-06-11 17:18:47 +00:00
|
|
|
|
|
|
|
enum class NamcoVariant
|
|
|
|
{
|
|
|
|
Namco163,
|
|
|
|
Namco175,
|
|
|
|
Namco340,
|
|
|
|
Unknown,
|
|
|
|
};
|
|
|
|
|
|
|
|
class Namco163 : public BaseMapper
|
|
|
|
{
|
|
|
|
private:
|
2018-07-01 19:21:05 +00:00
|
|
|
unique_ptr<Namco163Audio> _audio;
|
2016-06-11 17:18:47 +00:00
|
|
|
|
|
|
|
NamcoVariant _variant;
|
|
|
|
bool _notNamco340;
|
|
|
|
bool _autoDetectVariant;
|
|
|
|
uint8_t _writeProtect;
|
|
|
|
bool _lowChrNtMode;
|
|
|
|
bool _highChrNtMode;
|
|
|
|
uint16_t _irqCounter;
|
|
|
|
|
|
|
|
void SetVariant(NamcoVariant variant)
|
|
|
|
{
|
|
|
|
if(_autoDetectVariant) {
|
|
|
|
if(!_notNamco340 || variant != NamcoVariant::Namco340) {
|
|
|
|
_variant = variant;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void UpdateSaveRamAccess()
|
|
|
|
{
|
|
|
|
PrgMemoryType memType = HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam;
|
|
|
|
if(_variant == NamcoVariant::Namco163) {
|
|
|
|
bool globalWriteEnable = (_writeProtect & 0x40) == 0x40;
|
|
|
|
SetCpuMemoryMapping(0x6000, 0x67FF, 0, memType, globalWriteEnable && (_writeProtect & 0x01) == 0x00 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
|
|
|
|
SetCpuMemoryMapping(0x6800, 0x6FFF, 1, memType, globalWriteEnable && (_writeProtect & 0x02) == 0x00 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
|
|
|
|
SetCpuMemoryMapping(0x7000, 0x77FF, 2, memType, globalWriteEnable && (_writeProtect & 0x04) == 0x00 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
|
|
|
|
SetCpuMemoryMapping(0x7800, 0x7FFF, 3, memType, globalWriteEnable && (_writeProtect & 0x08) == 0x00 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
|
|
|
|
} else if(_variant == NamcoVariant::Namco175) {
|
|
|
|
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, memType, (_writeProtect & 0x01) == 0x01 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
|
|
|
|
} else {
|
|
|
|
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, memType, MemoryAccessType::NoAccess);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2016-12-18 04:14:47 +00:00
|
|
|
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
|
|
|
|
virtual uint16_t GetCHRPageSize() override { return 0x400; }
|
|
|
|
virtual uint32_t GetSaveRamPageSize() override { return 0x800; }
|
|
|
|
virtual bool AllowRegisterRead() override { return true; }
|
2016-06-11 17:18:47 +00:00
|
|
|
|
2016-12-18 04:14:47 +00:00
|
|
|
void InitMapper() override
|
2016-06-11 17:18:47 +00:00
|
|
|
{
|
2018-07-01 19:21:05 +00:00
|
|
|
_audio.reset(new Namco163Audio(_console));
|
2023-05-13 01:37:50 +00:00
|
|
|
_audio->InitializeInternalRam(HasBattery());
|
2018-07-01 19:21:05 +00:00
|
|
|
|
2018-07-07 18:52:51 +00:00
|
|
|
switch(_romInfo.MapperID) {
|
2018-05-26 14:13:22 +00:00
|
|
|
case 19:
|
|
|
|
_variant = NamcoVariant::Namco163;
|
2018-07-07 18:52:51 +00:00
|
|
|
if(_romInfo.DatabaseInfo.Board == "NAMCOT-163") {
|
2018-05-26 14:13:22 +00:00
|
|
|
_variant = NamcoVariant::Namco163;
|
|
|
|
_autoDetectVariant = false;
|
2018-07-07 18:52:51 +00:00
|
|
|
} else if(_romInfo.DatabaseInfo.Board == "NAMCOT-175") {
|
2018-05-26 14:13:22 +00:00
|
|
|
_variant = NamcoVariant::Namco175;
|
|
|
|
_autoDetectVariant = false;
|
2018-07-07 18:52:51 +00:00
|
|
|
} else if(_romInfo.DatabaseInfo.Board == "NAMCOT-340") {
|
2018-05-26 14:13:22 +00:00
|
|
|
_variant = NamcoVariant::Namco340;
|
|
|
|
_autoDetectVariant = false;
|
|
|
|
} else {
|
|
|
|
_autoDetectVariant = true;
|
|
|
|
}
|
|
|
|
break;
|
2016-06-11 17:18:47 +00:00
|
|
|
case 210:
|
2018-07-07 18:52:51 +00:00
|
|
|
switch(_romInfo.SubMapperID) {
|
2016-06-11 17:18:47 +00:00
|
|
|
case 0: _variant = NamcoVariant::Unknown; _autoDetectVariant = true; break;
|
|
|
|
case 1: _variant = NamcoVariant::Namco175; _autoDetectVariant = false; break;
|
|
|
|
case 2: _variant = NamcoVariant::Namco340; _autoDetectVariant = false; break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
_notNamco340 = false;
|
|
|
|
|
|
|
|
_writeProtect = 0;
|
|
|
|
_lowChrNtMode = false;
|
|
|
|
_highChrNtMode = false;
|
|
|
|
_irqCounter = 0;
|
|
|
|
|
|
|
|
AddRegisterRange(0x4800, 0x5FFF, MemoryOperation::Any);
|
|
|
|
RemoveRegisterRange(0x6000, 0xFFFF, MemoryOperation::Read);
|
|
|
|
|
|
|
|
SelectPRGPage(3, -1);
|
|
|
|
UpdateSaveRamAccess();
|
|
|
|
}
|
|
|
|
|
2016-12-18 04:14:47 +00:00
|
|
|
void StreamState(bool saving) override
|
2016-06-11 17:18:47 +00:00
|
|
|
{
|
|
|
|
BaseMapper::StreamState(saving);
|
|
|
|
|
2018-07-01 19:21:05 +00:00
|
|
|
SnapshotInfo audio{ _audio.get() };
|
2016-06-11 17:18:47 +00:00
|
|
|
Stream(_variant, _notNamco340, _autoDetectVariant, _writeProtect, _lowChrNtMode, _highChrNtMode, _irqCounter, audio);
|
|
|
|
if(!saving) {
|
|
|
|
UpdateSaveRamAccess();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-10 01:53:51 +00:00
|
|
|
void LoadBattery() override
|
|
|
|
{
|
|
|
|
if(HasBattery()) {
|
|
|
|
vector<uint8_t> batteryContent(_saveRamSize + Namco163Audio::AudioRamSize, 0);
|
|
|
|
_console->GetBatteryManager()->LoadBattery(".sav", batteryContent.data(), (uint32_t)batteryContent.size());
|
|
|
|
|
|
|
|
memcpy(_saveRam, batteryContent.data(), _saveRamSize);
|
|
|
|
memcpy(_audio->GetInternalRam(), batteryContent.data()+_saveRamSize, Namco163Audio::AudioRamSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SaveBattery() override
|
|
|
|
{
|
|
|
|
if(HasBattery()) {
|
|
|
|
vector<uint8_t> batteryContent(_saveRamSize + Namco163Audio::AudioRamSize, 0);
|
|
|
|
memcpy(batteryContent.data(), _saveRam, _saveRamSize);
|
|
|
|
memcpy(batteryContent.data() + _saveRamSize, _audio->GetInternalRam(), Namco163Audio::AudioRamSize);
|
|
|
|
|
|
|
|
_console->GetBatteryManager()->SaveBattery(".sav", batteryContent.data(), (uint32_t)batteryContent.size());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-18 04:14:47 +00:00
|
|
|
void ProcessCpuClock() override
|
2016-06-11 17:18:47 +00:00
|
|
|
{
|
|
|
|
if(_irqCounter & 0x8000 && (_irqCounter & 0x7FFF) != 0x7FFF) {
|
|
|
|
_irqCounter++;
|
|
|
|
if((_irqCounter & 0x7FFF) == 0x7FFF) {
|
2018-07-01 19:21:05 +00:00
|
|
|
_console->GetCpu()->SetIrqSource(IRQSource::External);
|
2016-06-11 17:18:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_variant == NamcoVariant::Namco163) {
|
2018-07-01 19:21:05 +00:00
|
|
|
_audio->Clock();
|
2016-06-11 17:18:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-18 04:14:47 +00:00
|
|
|
void WriteRAM(uint16_t addr, uint8_t value) override
|
2016-06-11 17:18:47 +00:00
|
|
|
{
|
|
|
|
if(addr >= 0x6000 && addr <= 0x7FFF) {
|
|
|
|
_notNamco340 = true;
|
|
|
|
if(_variant == NamcoVariant::Namco340) {
|
|
|
|
SetVariant(NamcoVariant::Unknown);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BaseMapper::WriteRAM(addr, value);
|
|
|
|
}
|
|
|
|
|
2016-12-18 04:14:47 +00:00
|
|
|
uint8_t ReadRegister(uint16_t addr) override
|
2016-06-11 17:18:47 +00:00
|
|
|
{
|
|
|
|
switch(addr & 0xF800) {
|
2018-07-01 19:21:05 +00:00
|
|
|
case 0x4800: return _audio->ReadRegister(addr);
|
2016-06-11 17:18:47 +00:00
|
|
|
case 0x5000: return _irqCounter & 0xFF;
|
|
|
|
case 0x5800: return (_irqCounter >> 8);
|
|
|
|
default: return BaseMapper::ReadRegister(addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-18 04:14:47 +00:00
|
|
|
void WriteRegister(uint16_t addr, uint8_t value) override
|
2016-06-11 17:18:47 +00:00
|
|
|
{
|
|
|
|
addr &= 0xF800;
|
|
|
|
|
|
|
|
switch(addr) {
|
|
|
|
case 0x4800:
|
|
|
|
SetVariant(NamcoVariant::Namco163);
|
2018-07-01 19:21:05 +00:00
|
|
|
_audio->WriteRegister(addr, value);
|
2016-06-11 17:18:47 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5000:
|
|
|
|
SetVariant(NamcoVariant::Namco163);
|
|
|
|
_irqCounter = (_irqCounter & 0xFF00) | value;
|
2018-07-01 19:21:05 +00:00
|
|
|
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
2016-06-11 17:18:47 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5800:
|
|
|
|
SetVariant(NamcoVariant::Namco163);
|
|
|
|
_irqCounter = (_irqCounter & 0x00FF) | (value << 8);
|
2018-07-01 19:21:05 +00:00
|
|
|
_console->GetCpu()->ClearIrqSource(IRQSource::External);
|
2016-06-11 17:18:47 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x8000: case 0x8800: case 0x9000: case 0x9800: {
|
|
|
|
uint8_t bankNumber = (addr - 0x8000) >> 11;
|
|
|
|
if(!_lowChrNtMode && value >= 0xE0 && _variant == NamcoVariant::Namco163) {
|
2019-01-10 01:19:16 +00:00
|
|
|
SelectCHRPage(bankNumber, value & 0x01, ChrMemoryType::NametableRam);
|
2016-06-11 17:18:47 +00:00
|
|
|
} else {
|
|
|
|
SelectCHRPage(bankNumber, value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0xA000: case 0xA800: case 0xB000: case 0xB800: {
|
|
|
|
uint8_t bankNumber = ((addr - 0xA000) >> 11) + 4;
|
|
|
|
if(!_highChrNtMode && value >= 0xE0 && _variant == NamcoVariant::Namco163) {
|
2019-01-10 01:19:16 +00:00
|
|
|
SelectCHRPage(bankNumber, value & 0x01, ChrMemoryType::NametableRam);
|
2016-06-11 17:18:47 +00:00
|
|
|
} else {
|
|
|
|
SelectCHRPage(bankNumber, value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0xC000: case 0xC800: case 0xD000: case 0xD800:
|
2016-12-18 04:14:47 +00:00
|
|
|
if(addr >= 0xC800) {
|
2016-06-11 17:18:47 +00:00
|
|
|
SetVariant(NamcoVariant::Namco163);
|
|
|
|
} else if(_variant != NamcoVariant::Namco163) {
|
|
|
|
SetVariant(NamcoVariant::Namco175);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_variant == NamcoVariant::Namco175) {
|
|
|
|
_writeProtect = value;
|
|
|
|
UpdateSaveRamAccess();
|
|
|
|
} else {
|
|
|
|
uint8_t bankNumber = ((addr - 0xC000) >> 11) + 8;
|
|
|
|
if(value >= 0xE0) {
|
2019-01-10 01:19:16 +00:00
|
|
|
SelectCHRPage(bankNumber, value & 0x01, ChrMemoryType::NametableRam);
|
2016-06-11 17:18:47 +00:00
|
|
|
} else {
|
|
|
|
SelectCHRPage(bankNumber, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE000:
|
|
|
|
if((value & 0x80) == 0x80) {
|
|
|
|
SetVariant(NamcoVariant::Namco340);
|
|
|
|
} else if((value & 0x40) == 0x40 && _variant != NamcoVariant::Namco163) {
|
|
|
|
SetVariant(NamcoVariant::Namco340);
|
|
|
|
}
|
|
|
|
|
|
|
|
SelectPRGPage(0, value & 0x3F);
|
|
|
|
|
|
|
|
if(_variant == NamcoVariant::Namco340) {
|
|
|
|
switch((value & 0xC0) >> 6) {
|
|
|
|
case 0: SetMirroringType(MirroringType::ScreenAOnly); break;
|
|
|
|
case 1: SetMirroringType(MirroringType::Vertical); break;
|
|
|
|
case 2: SetMirroringType(MirroringType::Horizontal); break;
|
|
|
|
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
|
|
|
|
}
|
|
|
|
} else if(_variant == NamcoVariant::Namco163) {
|
2018-07-01 19:21:05 +00:00
|
|
|
_audio->WriteRegister(addr, value);
|
2016-06-11 17:18:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE800:
|
|
|
|
SelectPRGPage(1, value & 0x3F);
|
|
|
|
if(_variant == NamcoVariant::Namco163) {
|
|
|
|
_lowChrNtMode = (value & 0x40) == 0x40;
|
|
|
|
_highChrNtMode = (value & 0x80) == 0x80;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF000:
|
|
|
|
SelectPRGPage(2, value & 0x3F);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF800:
|
|
|
|
SetVariant(NamcoVariant::Namco163);
|
|
|
|
if(_variant == NamcoVariant::Namco163) {
|
|
|
|
_writeProtect = value;
|
|
|
|
UpdateSaveRamAccess();
|
|
|
|
|
2018-07-01 19:21:05 +00:00
|
|
|
_audio->WriteRegister(addr, value);
|
2016-06-11 17:18:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|