mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
359 lines
7.6 KiB
C++
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 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|