Files
DbNesEmulator/nescorelib/boards/board.cpp
2018-12-16 22:19:06 +01:00

583 lines
13 KiB
C++

#include "board.h"
// system includes
#include <algorithm>
// local includes
#include "rom.h"
Board::Board(NesEmulator &emu, const Rom &rom) :
m_emu(emu),
m_rom(rom)
{
// PRG RAM
m_sramSaveRequired = false;
/* foreach from db */
{
const int SIZE = prgRam8KbDefaultBlkCount() * 8;
const bool BATTERY = true;
if (BATTERY)
m_sramSaveRequired = true;
if (SIZE > 0)
{
int kb4_count = SIZE / 2;
for (int i = 0; i < kb4_count; i++)
m_prgRam.append({
/* .ram = */ {},
/* .enabled = */ true,
/* .writeable = */ true,
/* .battery = */ BATTERY
});
}
}
// TRAINER, it should be copied into the RAM blk at 0x7000. In this case, it should be copied into ram blk 3
if (rom.hasTrainer)
std::copy(std::begin(rom.trainer), std::end(rom.trainer), std::begin(m_prgRam[3].ram));
// CHR RAM
// Map 8 Kb for now
const int chr_ram_banks_1k = chrRom1KbDefaultBlkCount(); // from db
m_chrRam.reserve(chr_ram_banks_1k);
for (int i = 0; i < chr_ram_banks_1k; i++)
m_chrRam.append({
/* .ram = */ {},
/* .enabled = */ true,
/* .writeable = */ true,
/* .battery = */ false
});
// 6 Nametables
}
Board::~Board()
{
}
QString Board::name() const
{
throw std::runtime_error("has not been implemented");
}
quint8 Board::mapper() const
{
throw std::runtime_error("has not been implemented");
}
void Board::hardReset()
{
// Use the configuration of mapper 0 (NRAM).
// PRG Switching
// Toggle ram/rom
// Switch 16KB ram, from 0x4000 into 0x7000
toggle16kPrgRam(true, PRGArea::Area4000);
switch16kPrg(0, PRGArea::Area4000);
// Switch 32KB rom, from 0x8000 into 0xF000
toggle32kPrgRam(false, PRGArea::Area8000);
switch32kPrg(0, PRGArea::Area8000);
// CHR Switching
// Pattern tables
toggle8kChrRam((chrRom1KbCount() == 0));
switch8kChr(0);
// Nametables
switch1kNmt(m_rom.mirroring);
}
void Board::softReset()
{
}
quint8 Board::_readEx(quint16 address)
{
int prgTmpArea = address >> 12 & 0xF;
if (m_prgAreaBlk[prgTmpArea].ram)
{
int prgTmpIndex = m_prgAreaBlk[prgTmpArea].index & prgRam4KbMask();
if (m_prgRam[prgTmpIndex].enabled)
return m_prgRam[prgTmpIndex].ram[address & 0xFFF];
else
return 0;
}
else
{
int prgTmpIndex = m_prgAreaBlk[prgTmpArea].index & prgRom4KbMask();
return m_rom.prg[prgTmpIndex][address & 0xFFF];
}
}
quint8 Board::readEx(quint16 address)
{
auto result = _readEx(address);
return result;
}
void Board::writeEx(quint16 address, quint8 value)
{
int prgTmpArea = address >> 12 & 0xF;
if (m_prgAreaBlk[prgTmpArea].ram)
{
int prgTmpIndex = m_prgAreaBlk[prgTmpArea].index & prgRam4KbMask();
if (m_prgRam[prgTmpIndex].enabled)
if (m_prgRam[prgTmpIndex].writeable)
m_prgRam[prgTmpIndex].ram[address & 0xFFF] = value;
}
}
quint8 Board::_readSrm(quint16 address)
{
int prgTmpArea = address >> 12 & 0xF;
if (m_prgAreaBlk[prgTmpArea].ram)
{
int prgTmpIndex = m_prgAreaBlk[prgTmpArea].index & prgRam4KbMask();
if (m_prgRam[prgTmpIndex].enabled)
return m_prgRam[prgTmpIndex].ram[address & 0xFFF];
else
return 0;
}
else
{
int prgTmpIndex = m_prgAreaBlk[prgTmpArea].index & prgRom4KbMask();
return m_rom.prg[prgTmpIndex][address & 0xFFF];
}
}
quint8 Board::readSrm(quint16 address)
{
auto result = _readSrm(address);
return result;
}
void Board::writeSrm(quint16 address, quint8 value)
{
int prgTmpArea = address >> 12 & 0xF;
if (m_prgAreaBlk[prgTmpArea].ram)
{
int prgTmpIndex = m_prgAreaBlk[prgTmpArea].index & prgRam4KbMask();
if (m_prgRam[prgTmpIndex].enabled)
if (m_prgRam[prgTmpIndex].writeable)
m_prgRam[prgTmpIndex].ram[address & 0xFFF] = value;
}
}
quint8 Board::_readPrg(quint16 address)
{
int prgTmpArea = address >> 12 & 0xF;
if (m_prgAreaBlk[prgTmpArea].ram)
{
const int prgTmpIndex = m_prgAreaBlk[prgTmpArea].index & prgRam4KbMask();
const int temp = address & 0xFFF;
if (m_prgRam[prgTmpIndex].enabled)
return m_prgRam[prgTmpIndex].ram[temp];
else
return 0;
}
else
{
const int prgTmpIndex = m_prgAreaBlk[prgTmpArea].index & prgRom4KbMask();
const int temp = address & 0xFFF;
return m_rom.prg[prgTmpIndex][temp];
}
/*
if (IsGameGenieActive)
{
foreach (GameGenieCode code in GameGenieCodes)
{
if (!code.Enabled)
continue;
if (code.Address != addr)
continue;
if (!code.IsCompare || code.Compare == return_value)
return code.Value;
}
}
*/
}
quint8 Board::readPrg(quint16 address)
{
auto result = _readPrg(address);
return result;
}
void Board::writePrg(quint16 address, quint8 value)
{
int prgTmpArea = address >> 12 & 0xF;
if (m_prgAreaBlk[prgTmpArea].ram)
{
int prgTmpIndex = m_prgAreaBlk[prgTmpArea].index & prgRam4KbMask();
if (m_prgRam[prgTmpIndex].enabled)
if (m_prgRam[prgTmpIndex].writeable)
m_prgRam[prgTmpIndex].ram[address & 0xFFF] = value;
}
}
quint8 Board::_readChr(quint16 address)
{
// 00-07 means patterntables
// 08-11 means nametables, should not included
// 12-15 nametables mirrors, should not included as well
int chrTmpArea = (address >> 10) & 0x7;// 0x0000 - 0x1FFF, 0-7.
int chrTmpIndex = m_chrAreaBlk[chrTmpArea].index;
if (m_chrAreaBlk[chrTmpArea].ram)
{
chrTmpIndex &= chrRam1KbMask();
if (m_chrRam[chrTmpIndex].enabled)
return m_chrRam[chrTmpIndex].ram[address & 0x3FF];
else
return 0;
}
else
{
chrTmpIndex &= chrRom1KbMask();
return m_rom.chr[chrTmpIndex][address & 0x3FF];
}
}
quint8 Board::readChr(quint16 address)
{
auto result = _readChr(address);
return result;
}
void Board::writeChr(quint16 address, quint8 value)
{
// 00-07 means patterntables
// 08-11 means nametables, should not included
// 12-15 nametables mirrors, should not included as well
int chrTmpArea = (address >> 10) & 0x7;// 0x0000 - 0x1FFF, 0-7.
if (m_chrAreaBlk[chrTmpArea].ram)
{
int chrTmpIndex = m_chrAreaBlk[chrTmpArea].index & chrRam1KbMask();
if (m_chrRam[chrTmpIndex].enabled)
if (m_chrRam[chrTmpIndex].writeable)
m_chrRam[chrTmpIndex].ram[address & 0x3FF] = value;
}
}
quint8 Board::_readNmt(quint16 address)
{
int nmtTmpArea = (address >> 10) & 0x3;// 0x2000 - 0x2C00, 0-3.
int nmtTmpIndex = m_nmtRam[nmtTmpArea].index;
return m_nmtRam[nmtTmpIndex].ram[address & 0x3FF];
}
quint8 Board::readNmt(quint16 address)
{
auto result = _readNmt(address);
return result;
}
void Board::writeNmt(quint16 address, quint8 value)
{
int nmtTmpArea = (address >> 10) & 0x3;// 0x2000 - 0x2C00, 0-3.
int nmtTmpIndex = m_nmtRam[nmtTmpArea].index;
m_nmtRam[nmtTmpIndex].ram[address & 0x3FF] = value;
}
void Board::onPpuAddressUpdate(quint16 address)
{
if (ppuA12ToggleTimerEnabled())
{
m_oldVramAddress = m_newVramAddress;
m_newVramAddress = address & 0x1000;
if (ppuA12TogglesOnRaisingEdge())
{
if (m_oldVramAddress < m_newVramAddress)
{
if (m_ppuCyclesTimer > 8)
onPpuA12RaisingEdge();
m_ppuCyclesTimer = 0;
}
}
else
{
if (m_oldVramAddress > m_newVramAddress)
{
if (m_ppuCyclesTimer > 8)
onPpuA12RaisingEdge();
m_ppuCyclesTimer = 0;
}
}
}
}
void Board::onCpuClock()
{
}
void Board::onPpuClock()
{
if (ppuA12ToggleTimerEnabled())
m_ppuCyclesTimer++;
}
void Board::onPpuA12RaisingEdge()
{
}
void Board::onPpuScanlineTick()
{
}
void Board::onApuClockDuration()
{
}
void Board::onApuClockEnvelope()
{
}
void Board::onApuClockSingle()
{
}
void Board::onApuClock()
{
}
double Board::apuGetSample() const
{
return 0.;
}
void Board::apuApplyChannelsSettings()
{
}
void Board::readState(QDataStream &dataStream)
{
Q_UNUSED(dataStream)
}
void Board::writeState(QDataStream &dataStream) const
{
Q_UNUSED(dataStream)
}
int Board::prgRam8KbDefaultBlkCount() const
{
return 1;
}
int Board::chrRom1KbDefaultBlkCount() const
{
return 8;
}
bool Board::ppuA12ToggleTimerEnabled() const
{
return false;
}
bool Board::ppuA12TogglesOnRaisingEdge() const
{
return false;
}
void Board::switch4kPrg(int index, PRGArea area)
{
m_prgAreaBlk[int(area)].index = index;
}
void Board::switch8kPrg(int index, PRGArea area)
{
index *= 2;
m_prgAreaBlk[int(area)].index = index;
m_prgAreaBlk[int(area) + 1].index = index + 1;
}
void Board::switch16kPrg(int index, PRGArea area)
{
index *= 4;
m_prgAreaBlk[int(area)].index = index;
m_prgAreaBlk[int(area) + 1].index = index + 1;
m_prgAreaBlk[int(area) + 2].index = index + 2;
m_prgAreaBlk[int(area) + 3].index = index + 3;
}
void Board::switch32kPrg(int index, PRGArea area)
{
index *= 8;
m_prgAreaBlk[int(area)].index = index;
m_prgAreaBlk[int(area) + 1].index = index + 1;
m_prgAreaBlk[int(area) + 2].index = index + 2;
m_prgAreaBlk[int(area) + 3].index = index + 3;
m_prgAreaBlk[int(area) + 4].index = index + 4;
m_prgAreaBlk[int(area) + 5].index = index + 5;
m_prgAreaBlk[int(area) + 6].index = index + 6;
m_prgAreaBlk[int(area) + 7].index = index + 7;
}
void Board::toggle4kPrgRam(bool ram, PRGArea area)
{
m_prgAreaBlk[int(area)].ram = ram;
}
void Board::toggle8kPrgRam(bool ram, PRGArea area)
{
m_prgAreaBlk[int(area)].ram = ram;
m_prgAreaBlk[int(area) + 1].ram = ram;
}
void Board::toggle16kPrgRam(bool ram, PRGArea area)
{
m_prgAreaBlk[int(area)].ram = ram;
m_prgAreaBlk[int(area) + 1].ram = ram;
m_prgAreaBlk[int(area) + 2].ram = ram;
m_prgAreaBlk[int(area) + 3].ram = ram;
}
void Board::toggle32kPrgRam(bool ram, PRGArea area)
{
m_prgAreaBlk[int(area)].ram = ram;
m_prgAreaBlk[int(area) + 1].ram = ram;
m_prgAreaBlk[int(area) + 2].ram = ram;
m_prgAreaBlk[int(area) + 3].ram = ram;
m_prgAreaBlk[int(area) + 4].ram = ram;
m_prgAreaBlk[int(area) + 5].ram = ram;
m_prgAreaBlk[int(area) + 6].ram = ram;
m_prgAreaBlk[int(area) + 7].ram = ram;
}
void Board::togglePrgRamEnable(bool enable)
{
for(auto &page : m_prgRam)
page.enabled = enable;
}
void Board::togglePrgRamWritableEnable(bool enable)
{
for(auto &page : m_prgRam)
page.writeable = enable;
}
void Board::toggle4kPrgRamEnabled(bool enable, int index)
{
m_prgRam[index].enabled = enable;
}
void Board::toggle4kPrgRamWritable(bool enable, int index)
{
m_prgRam[index].writeable = enable;
}
void Board::toggle4kPrgRamBattery(bool enable, int index)
{
m_prgRam[index].battery = enable;
}
void Board::switch1kChr(int index, CHRArea area)
{
m_chrAreaBlk[int(area)].index = index;
}
void Board::switch2kChr(int index, CHRArea area)
{
index *= 2;
m_chrAreaBlk[int(area)].index = index;
m_chrAreaBlk[int(area) + 1].index = index + 1;
}
void Board::switch4kChr(int index, CHRArea area)
{
index *= 4;
m_chrAreaBlk[int(area)].index = index;
m_chrAreaBlk[int(area) + 1].index = index + 1;
m_chrAreaBlk[int(area) + 2].index = index + 2;
m_chrAreaBlk[int(area) + 3].index = index + 3;
}
void Board::switch8kChr(int index)
{
index *= 8;
for(int i = 0; i < 8; i++)
m_chrAreaBlk[i].index = index + i;
}
void Board::toggle1kChrRam(bool ram, CHRArea area)
{
m_chrAreaBlk[int(area)].ram = ram;
}
void Board::toggle2kChrRam(bool ram, CHRArea area)
{
m_chrAreaBlk[int(area)].ram = ram;
m_chrAreaBlk[int(area) + 1].ram = ram;
}
void Board::toggle4kChrRam(bool ram, CHRArea area)
{
m_chrAreaBlk[int(area)].ram = ram;
m_chrAreaBlk[int(area) + 1].ram = ram;
m_chrAreaBlk[int(area) + 2].ram = ram;
m_chrAreaBlk[int(area) + 3].ram = ram;
}
void Board::toggle8kChrRam(bool ram)
{
for(auto &areaBlk : m_chrAreaBlk)
areaBlk.ram = ram;
}
void Board::toggle1kChrRamEnabled(bool enable, int index)
{
m_chrRam[index].enabled = enable;
}
void Board::toggle1kChrRamWritable(bool enable, int index)
{
m_chrRam[index].writeable = enable;
}
void Board::toggleChrRamWritableEnable(bool enable)
{
for (auto &ramPage : m_chrRam)
ramPage.writeable = enable;
}
void Board::toggle1kChrRamBattery(bool enable, int index)
{
m_chrRam[index].battery = enable;
}
void Board::switch1kNmt(int index, quint8 area)
{
m_nmtRam[area].index = index;
}
void Board::switch1kNmt(Mirroring mirroring)
{
// Mirroring value:
// 0000 0000
// ddcc bbaa
// aa: index for area 0x2000
// bb: index for area 0x2400
// cc: index for area 0x2800
// dd: index for area 0x2C00
m_nmtRam[0].index = int(mirroring) & 0x3;
m_nmtRam[1].index = (int(mirroring) >> 2) & 0x3;
m_nmtRam[2].index = (int(mirroring) >> 4) & 0x3;
m_nmtRam[3].index = (int(mirroring) >> 6) & 0x3;
}
bool Board::enableExternalSound() const
{
return false;
}