nestopia/source/core/board/NstBoardTengenRambo1.cpp
2021-03-13 18:39:56 -05:00

359 lines
7.6 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Nestopia is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
////////////////////////////////////////////////////////////////////////////////////////
#include "NstBoard.hpp"
#include "NstBoardTengenRambo1.hpp"
namespace Nes
{
namespace Core
{
namespace Boards
{
namespace Tengen
{
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("s", on)
#endif
Rambo1::Irq::Irq(Cpu& cpu,Ppu& ppu)
:
a12 ( cpu, ppu, unit ),
m2 ( cpu, unit )
{}
Rambo1::Rambo1(const Context& c)
:
Board (c),
irq (*c.cpu,*c.ppu)
{}
void Rambo1::Irq::Unit::Reset(const bool hard)
{
if (hard)
{
count = 0;
cycles = 0;
reload = false;
latch = 0;
enabled = false;
}
}
void Rambo1::Regs::Reset()
{
for (uint i=0; i < 8; ++i)
chr[i] = i;
for (uint i=0; i < 3; ++i)
prg[i] = i;
ctrl = 0;
}
void Rambo1::SubReset(const bool hard)
{
irq.a12.Reset( hard, !irq.m2.Connected() );
irq.m2.Reset( hard, irq.m2.Connected() );
if (hard)
regs.Reset();
for (uint i=0x0000; i < 0x1000; i += 0x2)
{
Map( 0x8000 + i, &Rambo1::Poke_8000 );
Map( 0x8001 + i, &Rambo1::Poke_8001 );
Map( 0xA000 + i, NMT_SWAP_HV );
Map( 0xC000 + i, &Rambo1::Poke_C000 );
Map( 0xC001 + i, &Rambo1::Poke_C001 );
Map( 0xE000 + i, &Rambo1::Poke_E000 );
Map( 0xE001 + i, &Rambo1::Poke_E001 );
}
UpdateChr();
UpdatePrg();
}
void Rambo1::SubLoad(State::Loader& state,const dword baseChunk)
{
NST_VERIFY( baseChunk == (AsciiId<'T','R','1'>::V) );
if (baseChunk == AsciiId<'T','R','1'>::V)
{
while (const dword chunk = state.Begin())
{
switch (chunk)
{
case AsciiId<'R','E','G'>::V:
{
State::Loader::Data<1+8+3> data( state );
regs.ctrl = data[0];
for (uint i=0; i < 3; ++i)
regs.prg[i] = data[1+i];
for (uint i=0; i < 8; ++i)
regs.chr[i] = data[1+3+i];
break;
}
case AsciiId<'I','R','Q'>::V:
{
State::Loader::Data<4> data( state );
irq.unit.enabled = data[0] & 0x1;
irq.unit.mode = data[0] & 0x2 ? 1 : 0;
irq.a12.Connect( data[0] & 0x2 );
irq.m2.Connect( data[0] & 0x2 );
irq.unit.reload = data[0] & 0x4;
irq.unit.latch = data[1];
irq.unit.count = data[2];
irq.unit.cycles = data[3];
break;
}
}
state.End();
}
}
}
void Rambo1::SubSave(State::Saver& state) const
{
state.Begin( AsciiId<'T','R','1'>::V );
{
const byte data[1+8+3] =
{
regs.ctrl,
regs.prg[0],
regs.prg[1],
regs.prg[2],
regs.chr[0],
regs.chr[1],
regs.chr[2],
regs.chr[3],
regs.chr[4],
regs.chr[5],
regs.chr[6],
regs.chr[7]
};
state.Begin( AsciiId<'R','E','G'>::V ).Write( data ).End();
}
{
const byte data[4] =
{
(irq.unit.enabled ? 0x1U : 0x0U) |
(irq.m2.Connected() ? 0x2U : 0x0U) |
(irq.unit.reload ? 0x4U : 0x0U),
irq.unit.latch,
irq.unit.count & 0xFF,
irq.unit.cycles,
};
state.Begin( AsciiId<'I','R','Q'>::V ).Write( data ).End();
}
state.End();
}
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("", on)
#endif
bool Rambo1::Irq::Unit::Clock()
{
cycles++;
if (latch == 1)
{
count = 0;
}
else if (reload)
{
reload = false;
count = latch | (latch ? 1 : 0);
if (mode)
count |= 2;
if (!latch && cycles > A12_FILTER)
count = 1;
else if (latch && (cycles > (A12_FILTER * 3)))
count++;
cycles = 0;
}
else if (!count)
{
count = latch;
if (cycles > A12_FILTER)
cycles = 0;
}
else
{
count--;
}
return (!count && enabled);
}
void Rambo1::Irq::Update()
{
a12.Update();
m2.Update();
}
void Rambo1::UpdatePrg()
{
prg.SwapBanks<SIZE_8K,0x0000>
(
regs.prg[(regs.ctrl & 0x40U) ? 2 : 0],
regs.prg[(regs.ctrl & 0x40U) ? 0 : 1],
regs.prg[(regs.ctrl & 0x40U) ? 1 : 2],
0xFF
);
}
void Rambo1::UpdateChr() const
{
ppu.Update();
const uint offset = (regs.ctrl & 0x80U) << 5;
if (regs.ctrl & 0x20U)
chr.SwapBanks<SIZE_1K>( offset, regs.chr[0], regs.chr[6], regs.chr[1], regs.chr[7] );
else
chr.SwapBanks<SIZE_2K>( offset, regs.chr[0] >> 1, regs.chr[1] >> 1 );
chr.SwapBanks<SIZE_1K>( offset ^ 0x1000, regs.chr[2], regs.chr[3], regs.chr[4], regs.chr[5] );
}
NES_POKE_D(Rambo1,8000)
{
const uint diff = regs.ctrl ^ data;
regs.ctrl = data;
if (diff & 0x40)
UpdatePrg();
if (diff & (0x20|0x80))
UpdateChr();
}
NES_POKE_D(Rambo1,8001)
{
const uint index = regs.ctrl & 0xFU;
if (index < 0x6)
{
if (regs.chr[index] != data)
{
regs.chr[index] = data;
UpdateChr();
}
}
else switch (index)
{
case 0x6:
case 0x7:
if (regs.prg[index - 0x6] != data)
{
regs.prg[index - 0x6] = data;
UpdatePrg();
}
break;
case 0x8:
case 0x9:
if (regs.chr[index - 0x2] != data)
{
regs.chr[index - 0x2] = data;
UpdateChr();
}
break;
case 0xF:
if (regs.prg[2] != data)
{
regs.prg[2] = data;
UpdatePrg();
}
break;
}
}
NES_POKE_D(Rambo1,C000)
{
irq.Update();
irq.unit.latch = data;
irq.unit.mode = irq.m2.Connected();
}
NES_POKE_D(Rambo1,C001)
{
irq.Update();
irq.unit.reload = true;
data &= Irq::SOURCE;
irq.a12.Connect( data == Irq::SOURCE_PPU );
irq.m2.Connect( data == Irq::SOURCE_CPU );
}
NES_POKE(Rambo1,E000)
{
irq.Update();
irq.unit.enabled = false;
cpu.ClearIRQ();
}
NES_POKE(Rambo1,E001)
{
irq.Update();
irq.unit.enabled = true;
}
void Rambo1::Sync(Event event,Input::Controllers* controllers)
{
if (event == EVENT_END_FRAME)
{
irq.a12.VSync();
irq.m2.VSync();
}
Board::Sync( event, controllers );
}
}
}
}
}