mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
381 lines
8.7 KiB
C++
381 lines
8.7 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 <cstring>
|
|
#include "../NstTimer.hpp"
|
|
#include "NstBoard.hpp"
|
|
#include "NstBoardBandai.hpp"
|
|
|
|
namespace Nes
|
|
{
|
|
namespace Core
|
|
{
|
|
namespace Boards
|
|
{
|
|
namespace Bandai
|
|
{
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("s", on)
|
|
#endif
|
|
|
|
Datach::Datach(const Context& c)
|
|
:
|
|
Lz93d50Ex (c),
|
|
barcodeReader (*c.cpu)
|
|
{
|
|
}
|
|
|
|
Datach::Reader::Reader(Cpu& c)
|
|
: cpu(c)
|
|
{
|
|
Reset( false );
|
|
}
|
|
|
|
Datach::Device Datach::QueryDevice(DeviceType devType)
|
|
{
|
|
if (devType == DEVICE_BARCODE_READER)
|
|
return static_cast<BarcodeReader*>(&barcodeReader);
|
|
else
|
|
return Lz93d50Ex::QueryDevice( devType );
|
|
}
|
|
|
|
void Datach::SubReset(const bool hard)
|
|
{
|
|
Lz93d50Ex::SubReset( hard );
|
|
|
|
barcodeReader.Reset();
|
|
p6000 = cpu.Map( 0x6000 );
|
|
|
|
for (uint i=0x6000; i < 0x8000; i += 0x100)
|
|
Map( i, &Datach::Peek_6000 );
|
|
}
|
|
|
|
void Datach::SubLoad(State::Loader& state,const dword baseChunk)
|
|
{
|
|
if (baseChunk == AsciiId<'B','D','A'>::V)
|
|
{
|
|
while (const dword chunk = state.Begin())
|
|
{
|
|
if (chunk == AsciiId<'B','R','C'>::V)
|
|
barcodeReader.LoadState( state );
|
|
|
|
state.End();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Lz93d50Ex::SubLoad( state, baseChunk );
|
|
}
|
|
}
|
|
|
|
void Datach::SubSave(State::Saver& state) const
|
|
{
|
|
Lz93d50Ex::SubSave( state );
|
|
|
|
state.Begin( AsciiId<'B','D','A'>::V );
|
|
barcodeReader.SaveState( state, AsciiId<'B','R','C'>::V );
|
|
state.End();
|
|
}
|
|
|
|
void Datach::Reader::Reset(bool initHook)
|
|
{
|
|
cycles = Cpu::CYCLE_MAX;
|
|
output = 0x00;
|
|
stream = data;
|
|
std::memset( data, END, MAX_DATA_LENGTH );
|
|
|
|
if (initHook)
|
|
cpu.AddHook( Hook(this,&Reader::Hook_Fetcher) );
|
|
}
|
|
|
|
bool Datach::Reader::IsTransferring() const
|
|
{
|
|
return *stream != END;
|
|
}
|
|
|
|
bool Datach::Reader::IsDigitsSupported(uint count) const
|
|
{
|
|
return count == MIN_DIGITS || count == MAX_DIGITS;
|
|
}
|
|
|
|
void Datach::Reader::LoadState(State::Loader& state)
|
|
{
|
|
Reset( false );
|
|
|
|
while (const dword chunk = state.Begin())
|
|
{
|
|
switch (chunk)
|
|
{
|
|
case AsciiId<'P','T','R'>::V:
|
|
|
|
stream = data + (state.Read8() & (MAX_DATA_LENGTH-1));
|
|
break;
|
|
|
|
case AsciiId<'D','A','T'>::V:
|
|
|
|
state.Uncompress( data );
|
|
data[MAX_DATA_LENGTH-1] = END;
|
|
break;
|
|
|
|
case AsciiId<'C','Y','C'>::V:
|
|
|
|
cycles = state.Read16();
|
|
break;
|
|
}
|
|
|
|
state.End();
|
|
}
|
|
|
|
if (Reader::IsTransferring())
|
|
{
|
|
output = (stream != data) ? stream[-1] : 0x00;
|
|
|
|
if (cycles > CC_INTERVAL)
|
|
cycles = CC_INTERVAL;
|
|
|
|
cycles = cpu.GetCycles() + cpu.GetClock() * cycles;
|
|
}
|
|
else
|
|
{
|
|
cycles = Cpu::CYCLE_MAX;
|
|
output = 0x00;
|
|
}
|
|
}
|
|
|
|
void Datach::Reader::SaveState(State::Saver& state,const dword baseChunk) const
|
|
{
|
|
if (Reader::IsTransferring())
|
|
{
|
|
NST_ASSERT( cycles != Cpu::CYCLE_MAX );
|
|
|
|
state.Begin( baseChunk );
|
|
|
|
state.Begin( AsciiId<'P','T','R'>::V ).Write8( stream - data ).End();
|
|
state.Begin( AsciiId<'D','A','T'>::V ).Compress( data ).End();
|
|
|
|
uint next;
|
|
|
|
if (cycles > cpu.GetCycles())
|
|
next = (cycles - cpu.GetCycles()) / cpu.GetClock();
|
|
else
|
|
next = 0;
|
|
|
|
state.Begin( AsciiId<'C','Y','C'>::V ).Write16( next ).End();
|
|
|
|
state.End();
|
|
}
|
|
}
|
|
|
|
bool Datach::Reader::Transfer(cstring const string,const uint length)
|
|
{
|
|
Reset( false );
|
|
|
|
if (!string || (length != MAX_DIGITS && length != MIN_DIGITS))
|
|
return false;
|
|
|
|
static const byte prefixParityType[10][6] =
|
|
{
|
|
{8,8,8,8,8,8}, {8,8,0,8,0,0},
|
|
{8,8,0,0,8,0}, {8,8,0,0,0,8},
|
|
{8,0,8,8,0,0}, {8,0,0,8,8,0},
|
|
{8,0,0,0,8,8}, {8,0,8,0,8,0},
|
|
{8,0,8,0,0,8}, {8,0,0,8,0,8}
|
|
};
|
|
|
|
static const byte dataLeftOdd[10][7] =
|
|
{
|
|
{8,8,8,0,0,8,0}, {8,8,0,0,8,8,0},
|
|
{8,8,0,8,8,0,0}, {8,0,0,0,0,8,0},
|
|
{8,0,8,8,8,0,0}, {8,0,0,8,8,8,0},
|
|
{8,0,8,0,0,0,0}, {8,0,0,0,8,0,0},
|
|
{8,0,0,8,0,0,0}, {8,8,8,0,8,0,0}
|
|
};
|
|
|
|
static const byte dataLeftEven[10][7] =
|
|
{
|
|
{8,0,8,8,0,0,0}, {8,0,0,8,8,0,0},
|
|
{8,8,0,0,8,0,0}, {8,0,8,8,8,8,0},
|
|
{8,8,0,0,0,8,0}, {8,0,0,0,8,8,0},
|
|
{8,8,8,8,0,8,0}, {8,8,0,8,8,8,0},
|
|
{8,8,8,0,8,8,0}, {8,8,0,8,0,0,0}
|
|
};
|
|
|
|
static const byte dataRight[10][7] =
|
|
{
|
|
{0,0,0,8,8,0,8}, {0,0,8,8,0,0,8},
|
|
{0,0,8,0,0,8,8}, {0,8,8,8,8,0,8},
|
|
{0,8,0,0,0,8,8}, {0,8,8,0,0,0,8},
|
|
{0,8,0,8,8,8,8}, {0,8,8,8,0,8,8},
|
|
{0,8,8,0,8,8,8}, {0,0,0,8,0,8,8}
|
|
};
|
|
|
|
byte code[16];
|
|
|
|
for (uint i=0; i < length; ++i)
|
|
{
|
|
if (string[i] >= '0' && string[i] <= '9')
|
|
code[i] = string[i] - '0';
|
|
else
|
|
return false;
|
|
}
|
|
|
|
byte* NST_RESTRICT output = data;
|
|
|
|
for (uint i=0; i < 1+32; ++i)
|
|
*output++ = 8;
|
|
|
|
*output++ = 0;
|
|
*output++ = 8;
|
|
*output++ = 0;
|
|
|
|
uint sum = 0;
|
|
|
|
if (length == MAX_DIGITS)
|
|
{
|
|
for (uint i=0; i < 6; ++i)
|
|
{
|
|
if (prefixParityType[code[0]][i])
|
|
{
|
|
for (uint j=0; j < 7; ++j)
|
|
*output++ = dataLeftOdd[code[i+1]][j];
|
|
}
|
|
else
|
|
{
|
|
for (uint j=0; j < 7; ++j)
|
|
*output++ = dataLeftEven[code[i+1]][j];
|
|
}
|
|
}
|
|
|
|
*output++ = 8;
|
|
*output++ = 0;
|
|
*output++ = 8;
|
|
*output++ = 0;
|
|
*output++ = 8;
|
|
|
|
for (uint i=7; i < 12; ++i)
|
|
{
|
|
for (uint j=0; j < 7; ++j)
|
|
*output++ = dataRight[code[i]][j];
|
|
}
|
|
|
|
for (uint i=0; i < 12; ++i)
|
|
sum += (i & 1) ? (code[i] * 3) : (code[i] * 1);
|
|
}
|
|
else
|
|
{
|
|
for (uint i=0; i < 4; ++i)
|
|
{
|
|
for (uint j=0; j < 7; ++j)
|
|
*output++ = dataLeftOdd[code[i]][j];
|
|
}
|
|
|
|
*output++ = 8;
|
|
*output++ = 0;
|
|
*output++ = 8;
|
|
*output++ = 0;
|
|
*output++ = 8;
|
|
|
|
for (uint i=4; i < 7; ++i)
|
|
{
|
|
for (uint j=0; j < 7; ++j)
|
|
*output++ = dataRight[code[i]][j];
|
|
}
|
|
|
|
for (uint i=0; i < 7; ++i)
|
|
sum += (i & 1) ? (code[i] * 1) : (code[i] * 3);
|
|
}
|
|
|
|
sum = (10 - (sum % 10)) % 10;
|
|
|
|
for (uint i=0; i < 7; ++i)
|
|
*output++ = dataRight[sum][i];
|
|
|
|
*output++ = 0;
|
|
*output++ = 8;
|
|
*output++ = 0;
|
|
|
|
for (uint i=0; i < 32; ++i)
|
|
*output++ = 8;
|
|
|
|
cycles = cpu.GetCycles() + cpu.GetClock() * CC_INTERVAL;
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
NES_HOOK(Datach::Reader,Fetcher)
|
|
{
|
|
while (cycles <= cpu.GetCycles())
|
|
{
|
|
output = *stream;
|
|
stream += (output != END);
|
|
|
|
if (output != END)
|
|
{
|
|
cycles += cpu.GetClock() * CC_INTERVAL;
|
|
}
|
|
else
|
|
{
|
|
output = 0x00;
|
|
cycles = Cpu::CYCLE_MAX;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline uint Datach::Reader::GetOutput() const
|
|
{
|
|
return output;
|
|
}
|
|
|
|
NES_PEEK_A(Datach,6000)
|
|
{
|
|
return barcodeReader.GetOutput() | p6000.Peek( address );
|
|
}
|
|
|
|
inline void Datach::Reader::Sync()
|
|
{
|
|
if (cycles != Cpu::CYCLE_MAX)
|
|
{
|
|
if (cycles >= cpu.GetFrameCycles())
|
|
cycles -= cpu.GetFrameCycles();
|
|
else
|
|
cycles = 0;
|
|
}
|
|
}
|
|
|
|
void Datach::Sync(Event event,Input::Controllers* controllers)
|
|
{
|
|
if (event == EVENT_END_FRAME)
|
|
barcodeReader.Sync();
|
|
|
|
Lz93d50Ex::Sync( event, controllers );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|