Update core to 1.52.0

This commit is contained in:
rdanbrook 2022-12-29 09:30:40 -06:00
parent 170b3bebbe
commit 63a4a991d3
29 changed files with 834 additions and 170 deletions

View File

@ -248,13 +248,16 @@ SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardTxcMxmdhtwo.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardTxcPoliceman.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardTxcTw.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardTxRom.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnl158b.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlA9746.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlCc21.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlEdu2000.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlKingOfFighters96.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlKingOfFighters97.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlMortalKombat2.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlMmc3BigPrgRom.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlN625092.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlRetX7Gbl.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlSuperFighter3.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlTf1201.cpp
SOURCES_CXX += $(CORE_DIR)/source/core/board/NstBoardUnlWorldHero.cpp

View File

@ -22,7 +22,7 @@
#include "nstdatabase.hpp"
#define NST_VERSION "1.51.1"
#define NST_VERSION "1.52.0"
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
@ -1557,7 +1557,6 @@ bool retro_load_game(const struct retro_game_info *info)
ivideo.SetRenderState(state);
Api::Sound isound(emulator);
isound.SetSampleBits(16);
isound.SetSampleRate(SAMPLERATE);
isound.SetSpeaker(Api::Sound::SPEAKER_MONO);

View File

@ -238,7 +238,7 @@ namespace Nes
stream = NULL;
buffer.Reset( settings.bits );
buffer.Reset();
if (on)
{
@ -297,7 +297,7 @@ namespace Nes
if (!rate)
return RESULT_ERR_INVALID_PARAM;
if (rate < 11025 || rate > 96000)
if (rate < 44100 || rate > 96000)
return RESULT_ERR_UNSUPPORTED;
settings.rate = rate;
@ -306,23 +306,6 @@ namespace Nes
return RESULT_OK;
}
Result Apu::SetSampleBits(const uint bits)
{
if (settings.bits == bits)
return RESULT_NOP;
if (!bits)
return RESULT_ERR_INVALID_PARAM;
if (bits != 8 && bits != 16)
return RESULT_ERR_UNSUPPORTED;
settings.bits = bits;
UpdateSettings();
return RESULT_OK;
}
Result Apu::SetVolume(const uint channels,const uint volume)
{
if (volume > 100)
@ -421,7 +404,7 @@ namespace Nes
cycles.Update( settings.rate, settings.speed, cpu );
synchronizer.Reset( settings.speed, settings.rate, cpu );
dcBlocker.Reset();
buffer.Reset( settings.bits );
buffer.Reset();
Cycle rate; uint fixed;
CalculateOscillatorClock( rate, fixed );
@ -863,20 +846,10 @@ namespace Nes
{
streamed = stream->length[0] + stream->length[1];
if (settings.bits == 16)
{
if (!settings.stereo)
FlushSound<iword,false>();
else
FlushSound<iword,true>();
}
if (!settings.stereo)
FlushSound<iword,false>();
else
{
if (!settings.stereo)
FlushSound<byte,false>();
else
FlushSound<byte,true>();
}
FlushSound<iword,true>();
Sound::Output::unlockCallback( *stream );
}
@ -921,7 +894,7 @@ namespace Nes
#endif
Apu::Settings::Settings()
: rate(44100), bits(16), speed(0), muted(false), transpose(false), stereo(false), audible(true)
: rate(44100), speed(0), muted(false), transpose(false), stereo(false), audible(true)
{
for (uint i=0; i < MAX_CHANNELS; ++i)
volumes[i] = Channel::DEFAULT_VOLUME;
@ -2474,7 +2447,7 @@ namespace Nes
dcBlocker.Reset();
buffer.Reset( settings.bits, false );
buffer.Reset( false );
}
#ifdef NST_MSVC_OPTIMIZE

View File

@ -60,7 +60,6 @@ namespace Nes
void ClockDMA(uint=0);
Result SetSampleRate(dword);
Result SetSampleBits(uint);
Result SetSpeed(uint);
Result SetVolume(uint,uint);
uint GetVolume(uint) const;
@ -601,7 +600,6 @@ namespace Nes
Settings();
dword rate;
uint bits;
byte speed;
bool muted;
bool transpose;
@ -643,11 +641,6 @@ namespace Nes
return settings.rate;
}
uint GetSampleBits() const
{
return settings.bits;
}
uint GetSpeed() const
{
return settings.speed;

View File

@ -147,7 +147,7 @@ namespace Nes
Log::Flush( "Ines: PRG-ROM was patched" NST_LINEBREAK );
if (Load( chr, 16 + prg.Size() ))
Log::Flush( "Ines: PRG-ROM was patched" NST_LINEBREAK );
Log::Flush( "Ines: CHR-ROM was patched" NST_LINEBREAK );
}
private:

View File

@ -124,7 +124,7 @@ namespace Nes
0x00, 0x20, 0x00, 0x20, 0x04, 0x04, 0x04, 0x04,
0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
0x00, 0x20, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -2128,11 +2128,12 @@ namespace Nes
}
// Unofficial Opcodes SHX/SHY are edge cases
#define NES_I_W_U(instr_,addr_,hex_) \
#define NES_I_W_U(instr_,hex_) \
\
void Cpu::op##hex_() \
{ \
const uint dst = addr_##_W(); \
const uint dst = FetchPc16(); \
cycles.count += cycles.clock[3]; \
instr_(dst); \
}
@ -2393,8 +2394,8 @@ namespace Nes
NES_I_W_A( Sha, AbsY, 0x9F )
NES_I_W_A( Sha, IndY, 0x93 )
NES_I_W_A( Shs, AbsY, 0x9B )
NES_I_W_U( Shx, Abs, 0x9E ) // Edge case: AbsY done internally
NES_I_W_U( Shy, Abs, 0x9C ) // Edge case: AbsX done internally
NES_I_W_U( Shx, 0x9E ) // Edge case: AbsY done internally
NES_I_W_U( Shy, 0x9C ) // Edge case: AbsX done internally
NES_IRW__( Slo, Zpg, 0x07 )
NES_IRW__( Slo, ZpgX, 0x17 )
NES_IRW__( Slo, Abs, 0x0F )

View File

@ -221,17 +221,36 @@ namespace Nes
{
NST_ASSERT( it->length );
bool underflow = false;
dword uflow_length = 0;
if (it->offset < offset)
continue;
{
if (it->offset + it->length > offset)
{
underflow = true;
uflow_length = offset - it->offset;
}
else
{
continue;
}
}
if (it->offset >= offset + length)
break;
const dword pos = it->offset - offset;
const dword part = NST_MIN(it->length,length-pos);
dword pos = it->offset - offset;
dword part = NST_MIN(it->length,length-pos);
if (underflow)
{
part -= uflow_length;
pos += uflow_length;
}
if (it->fill == NO_FILL)
std::memcpy( dst + pos, it->data, part );
std::memcpy( dst + pos, it->data + uflow_length, part );
else
std::memset( dst + pos, it->fill, part );

View File

@ -401,6 +401,8 @@ namespace Nes
state.End();
}
output.bgColor = palette.ram[0] & uint(Palette::COLOR);
UpdateStates();
}
@ -774,6 +776,17 @@ namespace Nes
return (regs.ctrl[1] & Regs::CTRL1_EMPHASIS) << 1;
}
NST_FORCE_INLINE void Ppu::UpdateDecay(byte mask)
{
Cycle curCyc = cpu.GetCycles();
for (uint i = 0; i < 8; ++i)
{
if (mask & (1 << i))
decay.timestamp[i] = curCyc;
}
}
NES_POKE_D(Ppu,2000)
{
Update( cycles.one );
@ -786,6 +799,8 @@ namespace Nes
oam.height = (data >> 2 & 8) + 8;
io.latch = data;
UpdateDecay(0xFF);
data = regs.ctrl[0] ;
regs.ctrl[0] = io.latch;
@ -825,6 +840,8 @@ namespace Nes
}
io.latch = data;
UpdateDecay(0xFF);
data = (regs.ctrl[1] ^ data) & (Regs::CTRL1_EMPHASIS|Regs::CTRL1_MONOCHROME);
regs.ctrl[1] = io.latch;
@ -852,13 +869,22 @@ namespace Nes
{
Update( cycles.one, address );
byte mask = 0xE0;
uint status = regs.status & 0xFF;
regs.status &= (Regs::STATUS_VBLANK^0xFFU);
scroll.toggle = 0;
io.latch = (io.latch & Regs::STATUS_LATCH) | status;
UpdateDecay(mask);
return io.latch;
Cycle curCyc = cpu.GetCycles();
for (uint i = 0; i < 5; ++i)
{
if ((curCyc - decay.timestamp[i]) < 24576)
mask |= (1 << i);
}
return io.latch & mask;
}
NES_PEEK_A(Ppu,2002_RC2C05_01_04)
@ -882,6 +908,7 @@ namespace Nes
regs.oam = data;
io.latch = data;
UpdateDecay(0xFF);
}
NES_POKE_D(Ppu,2004)
@ -891,6 +918,9 @@ namespace Nes
NST_ASSERT( regs.oam < Oam::SIZE );
NST_VERIFY( IsDead() );
io.latch = data;
UpdateDecay(0xFF);
if (IsDead())
{
if ((regs.oam & 0x03) == 0x02)
@ -903,7 +933,6 @@ namespace Nes
byte* const NST_RESTRICT value = oam.ram + regs.oam;
regs.oam = (regs.oam + 1) & 0xFF;
io.latch = data;
*value = data;
}
@ -914,12 +943,14 @@ namespace Nes
if (!(regs.ctrl[1] & Regs::CTRL1_BG_SP_ENABLED) || cpu.GetCycles() - (cpu.GetFrameCycles() - (341 * 241) * cycles.one) >= (341 * 240) * cycles.one)
{
io.latch = oam.ram[regs.oam];
UpdateDecay(0xFF);
}
else
{
Update( cycles.one );
io.latch = oam.latch;
UpdateDecay(0xFF);
}
return io.latch;
@ -934,6 +965,7 @@ namespace Nes
if (cpu.GetCycles() >= cycles.reset)
{
io.latch = data;
UpdateDecay(0xFF);
if (scroll.toggle ^= 1)
{
@ -956,6 +988,7 @@ namespace Nes
if (cpu.GetCycles() >= cycles.reset)
{
io.latch = data;
UpdateDecay(0xFF);
if (scroll.toggle ^= 1)
{
@ -984,6 +1017,7 @@ namespace Nes
return;
io.latch = data;
UpdateDecay(0xFF);
if ((address & 0x3F00) == 0x3F00)
{
@ -999,7 +1033,7 @@ namespace Nes
palette.ram[address ^ 0x10] = data;
output.palette[address ^ 0x10] = final;
}
output.bgColor = palette.ram[0] & uint(Palette::COLOR);
}
else
@ -1015,27 +1049,54 @@ namespace Nes
NES_PEEK_A(Ppu,2007)
{
byte mask = 0xFF;
byte cache = io.latch;
Update( cycles.one, address );
Cycle curCyc = cpu.GetCycles();
Cycle delta = curCyc - decay.rd2007;
decay.rd2007 = curCyc;
bool fastread = (delta <= 12);
address = scroll.address & 0x3FFF;
UpdateVramAddress();
if (!(regs.ctrl[1] & Regs::CTRL1_BG_SP_ENABLED) || (scanline == SCANLINE_VBLANK))
UpdateAddressLine(scroll.address & 0x3fff);
io.latch = (address & 0x3F00) != 0x3F00 ? io.buffer : palette.ram[address & 0x1F] & Coloring();
if ((address & 0x3F00) == 0x3F00) // Palette
{
io.latch = (io.latch & 0xC0) | palette.ram[address & 0x1F] & Coloring();
mask = 0x3F;
}
else // Non-Palette
{
io.latch = io.buffer;
}
UpdateDecay(mask);
io.buffer = (address >= 0x2000 ? nmt.FetchName( address ) : chr.FetchPattern( address ));
if (fastread)
io.latch = cache;
return io.latch;
}
NES_POKE_D(Ppu,2xxx)
{
io.latch = data;
UpdateDecay(0xFF);
}
NES_PEEK(Ppu,2xxx)
{
if ((cpu.GetCycles() - decay.timestamp[0]) > 24576)
return 0;
return io.latch;
}
@ -1074,10 +1135,13 @@ namespace Nes
}
io.latch = oamRam[0xFF];
UpdateDecay(0xFF);
}
else do
{
io.latch = cpu.Peek( data++ );
UpdateDecay(0xFF);
cpu.StealCycles( cpu.GetClock() );
Update( cycles.one );

View File

@ -186,6 +186,7 @@ namespace Nes
NST_FORCE_INLINE uint FetchSpPattern() const;
NST_FORCE_INLINE void FetchBgPattern0();
NST_FORCE_INLINE void FetchBgPattern1();
NST_FORCE_INLINE void UpdateDecay(byte);
NST_FORCE_INLINE void EvaluateSpritesEven();
NST_FORCE_INLINE void EvaluateSpritesOdd();
@ -407,6 +408,12 @@ namespace Nes
Cycle reset;
} cycles;
struct
{
Cycle timestamp[8];
Cycle rd2007;
} decay;
Io io;
Regs regs;
Scroll scroll;
@ -419,7 +426,6 @@ namespace Nes
public:
Output output;
private:
PpuModel model;
Hook hActiveHook;

View File

@ -39,7 +39,7 @@ namespace Nes
Buffer::Buffer(uint bits)
: output(new iword [SIZE])
{
Reset( bits, true );
Reset( true );
}
Buffer::~Buffer()
@ -47,12 +47,12 @@ namespace Nes
delete [] output;
}
void Buffer::Reset(uint bits,bool clear)
void Buffer::Reset(bool clear)
{
pos = start = 0;
history.pos = 0;
std::fill( history.buffer, history.buffer+History::SIZE, iword(bits == 16 ? 0 : 0x80) );
std::fill( history.buffer, history.buffer+History::SIZE, iword(0) );
if (clear)
std::fill( output, output+SIZE, iword(0) );

View File

@ -59,7 +59,7 @@ namespace Nes
uint length;
};
void Reset(uint,bool=true);
void Reset(bool=true);
void operator >> (Block&);
template<typename,uint>

View File

@ -136,7 +136,6 @@ namespace Nes
enabled (e),
good (false),
stereo (false),
bits (0),
rate (0),
index (0),
buffer (NULL),
@ -268,14 +267,13 @@ namespace Nes
bool Tracker::Rewinder::ReverseSound::Update()
{
const dword old = (bits == 16 ? size * sizeof(iword) : size * sizeof(byte));
const dword old = size * sizeof(iword);
bits = apu.GetSampleBits();
rate = apu.GetSampleRate();
stereo = apu.InStereo();
size = rate << (stereo+1);
const dword total = (bits == 16 ? size * sizeof(iword) : size * sizeof(byte));
const dword total = size * sizeof(iword);
NST_ASSERT( total );
if (!buffer || total != old)
@ -296,10 +294,7 @@ namespace Nes
good = true;
index = 0;
if (bits == 16)
std::fill( static_cast<iword*>(buffer), static_cast<iword*>(buffer) + size, iword(0) );
else
std::memset( buffer, 0x80, size );
std::fill( static_cast<iword*>(buffer), static_cast<iword*>(buffer) + size, iword(0) );
return true;
}
@ -587,13 +582,13 @@ namespace Nes
{
NST_COMPILE_ASSERT( NUM_FRAMES % 2 == 0 );
if (!buffer || (bits ^ apu.GetSampleBits()) | (rate ^ apu.GetSampleRate()) | (stereo ^ uint(bool(apu.InStereo()))))
if (!buffer || (rate ^ apu.GetSampleRate()) | (stereo ^ uint(bool(apu.InStereo()))))
{
if (!good || !Update() || !enabled)
return NULL;
}
return bits == 16 ? StoreType<iword>() : StoreType<byte>();
return StoreType<iword>();
}
template<typename T,int SILENCE>
@ -632,14 +627,11 @@ namespace Nes
{
if (enabled & good)
{
input = (bits == 16) ? ReverseCopy<iword>( *target ) : ReverseCopy<byte>( *target );
input = ReverseCopy<iword>( *target );
}
else
{
if (bits == 16)
ReverseSilence<iword,0>( *target );
else
ReverseSilence<byte,0x80>( *target );
ReverseSilence<iword,0>( *target );
}
mutex.Unlock( *target );

View File

@ -181,7 +181,6 @@ namespace Nes
bool enabled;
bool good;
byte stereo;
byte bits;
dword rate;
uint index;
void* buffer;

View File

@ -42,7 +42,7 @@ namespace Nes
void Renderer::FilterNtsc::BlitType(const Input& input,const Output& output,uint phase) const
{
NST_ASSERT( phase < 3 );
const uint bgcolor = this->bgColor;
const Input::Pixel* NST_RESTRICT src = input.pixels;
Pixel* NST_RESTRICT dst = static_cast<Pixel*>(output.pixels);

View File

@ -828,7 +828,7 @@ namespace Nes
if (Output::lockCallback( output ))
{
NST_VERIFY( std::labs(output.pitch) >= dword(state.width) << (filter->format.bpp / 16) );
filter->bgColor = bgColor;
if (std::labs(output.pitch) >= dword(state.width) << (filter->format.bpp / 16))

View File

@ -67,7 +67,7 @@ namespace Nes
HEIGHT = Input::HEIGHT,
PIXELS = Input::PIXELS,
PALETTE = Input::PALETTE,
DEFAULT_PALETTE = PALETTE_YUV
NST_DEFAULT_PALETTE = PALETTE_YUV
};
Result SetState(const RenderState&);

View File

@ -62,11 +62,6 @@ namespace Nes
return emulator.cpu.GetApu().SetSampleRate( rate );
}
Result Sound::SetSampleBits(uint bits) throw()
{
return emulator.cpu.GetApu().SetSampleBits( bits );
}
Result Sound::SetVolume(uint channels,uint volume) throw()
{
return emulator.cpu.GetApu().SetVolume( channels, volume );
@ -97,11 +92,6 @@ namespace Nes
return emulator.cpu.GetApu().GetSampleRate();
}
uint Sound::GetSampleBits() const throw()
{
return emulator.cpu.GetApu().GetSampleBits();
}
uint Sound::GetVolume(uint channel) const throw()
{
return emulator.cpu.GetApu().GetVolume( channel );

View File

@ -262,7 +262,7 @@ namespace Nes
/**
* Sets the sample rate.
*
* @param rate value in the range 11025 to 96000, default is 44100
* @param rate value in the range 44100 to 96000, default is 44100
* @return result code
*/
Result SetSampleRate(ulong rate) throw();
@ -274,21 +274,6 @@ namespace Nes
*/
ulong GetSampleRate() const throw();
/**
* Sets the sample bits.
*
* @param bits value of 8 or 16, default is 16
* @return result code
*/
Result SetSampleBits(uint bits) throw();
/**
* Returns the sample bits.
*
* @return number
*/
uint GetSampleBits() const throw();
/**
* Sets the speaker type.
*

View File

@ -334,7 +334,7 @@ namespace Nes
Video::Palette::Mode Video::Palette::GetDefaultMode() const throw()
{
NST_COMPILE_ASSERT( Core::Video::Renderer::DEFAULT_PALETTE - Core::Video::Renderer::PALETTE_YUV == 0 );
NST_COMPILE_ASSERT( Core::Video::Renderer::NST_DEFAULT_PALETTE - Core::Video::Renderer::PALETTE_YUV == 0 );
return MODE_YUV;
}

View File

@ -97,13 +97,16 @@
#include "NstBoardWaixing.hpp"
#include "NstBoardWhirlwind.hpp"
#include "NstBoardBenshengBs5.hpp"
#include "NstBoardUnl158b.hpp"
#include "NstBoardUnlA9746.hpp"
#include "NstBoardUnlCc21.hpp"
#include "NstBoardUnlEdu2000.hpp"
#include "NstBoardUnlKingOfFighters96.hpp"
#include "NstBoardUnlKingOfFighters97.hpp"
#include "NstBoardUnlMmc3BigPrgRom.hpp"
#include "NstBoardUnlMortalKombat2.hpp"
#include "NstBoardUnlN625092.hpp"
#include "NstBoardUnlRetX7Gbl.hpp"
#include "NstBoardUnlSuperFighter3.hpp"
#include "NstBoardUnlTf1201.hpp"
#include "NstBoardUnlWorldHero.hpp"
@ -1096,6 +1099,7 @@ namespace Nes
{ "TENGEN-800032", Type::TENGEN_800032 },
{ "TENGEN-800037", Type::TENGEN_800037 },
{ "TENGEN-800042", Type::TENGEN_800042 },
{ "UNL-158B", Type::UNL_158B },
{ "UNL-22211", Type::TXC_22211A },
{ "UNL-603-5052", Type::BTL_6035052 },
{ "UNL-8237", Type::SUPERGAME_POCAHONTAS2 },
@ -1472,6 +1476,13 @@ namespace Nes
break;
}
if (prg > SIZE_512K)
{
name = "UNL-MMC3BIGPRGROM";
id = Type::UNL_MMC3BIGPRGROM;
break;
}
if (nmt == Type::NMT_FOURSCREEN)
{
if (prg == SIZE_64K && (chr == SIZE_32K || chr == SIZE_64K) && !wram && !useWramAuto)
@ -1803,35 +1814,54 @@ namespace Nes
break;
case 21:
if (submapper == 1)
if (submapper == 2 || wram >= SIZE_8K) // VRC4c
{
Chips::Type& chip = chips.Add(L"Konami VRC IV");
chip.Pin(3) = L"PRG A7";
chip.Pin(4) = L"PRG A6";
name = "KONAMI VRC4";
id = Type::KONAMI_VRC4_2;
}
else // VRC4a - submapper 1
{
Chips::Type& chip = chips.Add(L"Konami VRC IV");
chip.Pin(3) = L"PRG A2";
chip.Pin(4) = L"PRG A1";
name = "KONAMI VRC4";
id = Type::KONAMI_VRC4_0;
}
case 25:
if (submapper == 2)
{ // The correct board is VRC2 but the functionality is implemented in the VRC4 code currently
Chips::Type& chip = chips.Add(L"Konami VRC IV");
chip.Pin(3) = L"PRG A0";
chip.Pin(4) = L"PRG A1";
name = "KONAMI VRC2";
id = Type::KONAMI_VRC4_2;
break;
}
if (!this->chips.Has(L"Konami VRC IV"))
return false;
name = "KONAMI VRC4";
id = Type::KONAMI_VRC4_2;
break;
case 25:
if (submapper == 3 || wram >= SIZE_8K) // VRC2c, but emulated as VRC4
{
Chips::Type& chip = chips.Add(L"Konami VRC IV");
chip.Pin(3) = L"PRG A0";
chip.Pin(4) = L"PRG A1";
id = Type::KONAMI_VRC4_2;
name = "KONAMI VRC2";
break;
}
else if (submapper == 2 || prg >= SIZE_256K) // VRC4d
{
Chips::Type& chip = chips.Add(L"Konami VRC IV");
chip.Pin(3) = L"PRG A2";
chip.Pin(4) = L"PRG A3";
id = Type::KONAMI_VRC4_0;
}
else // VRC4b - submapper 1
{
Chips::Type& chip = chips.Add(L"Konami VRC IV");
chip.Pin(3) = L"PRG A0";
chip.Pin(4) = L"PRG A1";
id = Type::KONAMI_VRC4_1;
}
name = "KONAMI VRC4";
break;
case 22:
@ -1858,50 +1888,49 @@ namespace Nes
case 23:
if (submapper == 2)
if (submapper == 1) // VRC4f - Unknown, but plausibly World Hero?
{
Chips::Type& chip = chips.Add(L"Konami VRC IV");
chip.Pin(3) = L"PRG A3";
chip.Pin(4) = L"PRG A2";
chip.Pin(3) = L"PRG A1";
chip.Pin(4) = L"PRG A0";
name = "KONAMI VRC4";
id = Type::KONAMI_VRC4_0;
}
else if (submapper == 3)
else if (submapper == 2) // VRC4e
{
name = "KONAMI VRC2";
id = Type::KONAMI_VRC2;
break;
Chips::Type& chip = chips.Add(L"Konami VRC IV");
chip.Pin(3) = L"PRG A3";
chip.Pin(4) = L"PRG A2";
name = "KONAMI VRC4";
id = Type::KONAMI_VRC4_1;
}
if (prg >= SIZE_512K)
else if (prg >= SIZE_512K)
{
if (!this->chips.Has(L"Konami VRC IV"))
{
Chips::Type& chip = chips.Add(L"Konami VRC IV");
chip.Pin(3) = L"PRG A3";
chip.Pin(4) = L"PRG A2";
}
Chips::Type& chip = chips.Add(L"Konami VRC IV");
chip.Pin(3) = L"PRG A3";
chip.Pin(4) = L"PRG A2";
name = "BMC VRC4";
id = Type::BMC_VRC4;
}
else if (this->chips.Has(L"Konami VRC II"))
else // VRC2b - submapper 3
{
Chips::Type& chip = chips.Add(L"Konami VRC II");
chip.Pin(3) = L"PRG A1";
chip.Pin(4) = L"PRG A0";
chip.Pin(21) = L"CHR A10";
chip.Pin(22) = L"CHR A16";
chip.Pin(23) = L"CHR A11";
chip.Pin(24) = L"CHR A13";
chip.Pin(25) = L"CHR A14";
chip.Pin(26) = L"CHR A12";
chip.Pin(27) = L"CHR A15";
chip.Pin(28) = (chr >= SIZE_256K ? L"CHR A17" : L"NC");
name = "KONAMI VRC2";
id = Type::KONAMI_VRC2;
}
else if (this->chips.Has(L"Konami VRC IV"))
{
name = "KONAMI VRC4";
id = Type::KONAMI_VRC4_2;
}
else
{
return false;
}
break;
case 24:
@ -2344,13 +2373,6 @@ namespace Nes
case 78:
if (submapper == 1)
{
name = "JALECO JF-16";
id = Type::JALECO_JF16;
break;
}
if (submapper == 3)
{
name = "IREM-HOLYDIVER";
@ -2358,6 +2380,7 @@ namespace Nes
break;
}
// Default to submapper 1
name = "JALECO JF-16";
id = Type::JALECO_JF16;
break;
@ -3385,6 +3408,12 @@ namespace Nes
id = Type::BMC_110IN1;
break;
case 258:
name = "UNL-158B";
id = Type::UNL_158B;
break;
case 262:
name = "UNL-SHERO";
@ -3494,6 +3523,12 @@ namespace Nes
id = Type::BMC_SUPER_40IN1;
break;
case 400:
name = "UNL-RET-X7-GBL";
id = Type::UNL_RETX7GBL;
break;
case 521:
name = "DREAMTECH01";
@ -3887,14 +3922,17 @@ namespace Nes
case Type::TXC_MXMDHTWO : return new Txc::Mxmdhtwo(c);
case Type::TXC_POLICEMAN : return new Txc::Policeman(c);
case Type::TXC_TW : return new Txc::Tw(c);
case Type::UNL_158B : return new Unlicensed::Gd98158b(c);
case Type::UNL_A9746 : return new Unlicensed::A9746(c);
case Type::UNL_CC21 : return new Unlicensed::Cc21(c);
case Type::UNL_EDU2000 : return new Unlicensed::Edu2000(c);
case Type::UNL_FS304 : return new Waixing::Fs304(c);
case Type::UNL_KINGOFFIGHTERS96 : return new Unlicensed::KingOfFighters96(c);
case Type::UNL_KINGOFFIGHTERS97 : return new Unlicensed::KingOfFighters97(c);
case Type::UNL_MMC3BIGPRGROM : return new Unlicensed::Mmc3BigPrgRom(c);
case Type::UNL_MORTALKOMBAT2 : return new Unlicensed::MortalKombat2(c);
case Type::UNL_N625092 : return new Unlicensed::N625092(c);
case Type::UNL_RETX7GBL : return new Unlicensed::RetX7Gbl(c);
case Type::UNL_SUPERFIGHTER3 : return new Unlicensed::SuperFighter3(c);
case Type::UNL_SHERO : return new Sachen::StreetHeroes(c);
case Type::UNL_TF1201 : return new Unlicensed::Tf1201(c);

View File

@ -540,6 +540,7 @@ namespace Nes
TXC_POLICEMAN = MakeId< 36, 512, 128, 0, 0, CRM_0, NMT_X, 0 >::ID,
TXC_TW = MakeId< 189, 128, 256, 0, 0, CRM_0, NMT_X, 0 >::ID,
// Unlicensed
UNL_158B = MakeId< 258, 128, 128, 0, 0, CRM_0, NMT_X, 0 >::ID,
UNL_A9746 = MakeId< 219, 128, 256, 0, 0, CRM_0, NMT_X, 0 >::ID,
UNL_CC21 = MakeId< 27, 32, 8, 0, 0, CRM_0, NMT_Z, 0 >::ID,
UNL_EDU2000 = MakeId< 329, 1024, 0, 0, 32, CRM_8, NMT_Z, 0 >::ID,
@ -548,6 +549,7 @@ namespace Nes
UNL_KINGOFFIGHTERS97 = MakeId< 263, 256, 256, 0, 0, CRM_0, NMT_X, 0 >::ID,
UNL_MORTALKOMBAT2 = MakeId< 91, 256, 512, 0, 0, CRM_0, NMT_X, 0 >::ID,
UNL_N625092 = MakeId< 221, 1024, 8, 0, 0, CRM_0, NMT_V, 0 >::ID,
UNL_RETX7GBL = MakeId< 400, 2048, 0, 0, 0, CRM_32, NMT_X, 0 >::ID,
UNL_SUPERFIGHTER3 = MakeId< 197, 512, 512, 0, 0, CRM_0, NMT_X, 0 >::ID,
UNL_SHERO = MakeId< 262, 512, 512, 0, 0, CRM_8, NMT_4, 0 >::ID,
UNL_TF1201 = MakeId< 298, 128, 128, 0, 0, CRM_0, NMT_V, 0 >::ID,
@ -562,6 +564,7 @@ namespace Nes
UNL_TRXROM = MakeId< 4, 512, 256, 8, 0, CRM_0, NMT_4, 0 >::ID,
UNL_XZY = MakeId< 176, 1024, 256, 8, 0, CRM_0, NMT_X, 0 >::ID,
UNL_MMC3BIGCHRRAM = MakeId< 4, 512, 0, 0, 0, CRM_32, NMT_X, 0 >::ID,
UNL_MMC3BIGPRGROM = MakeId< 4, 1024, 0, 8, 0, CRM_8, NMT_X, 0 >::ID,
// Waixing
WAIXING_PS2_0 = MakeId< 15, 1024, 0, 0, 0, CRM_8, NMT_V, 0 >::ID,
WAIXING_PS2_1 = MakeId< 15, 1024, 0, 8, 0, CRM_8, NMT_V, 0 >::ID,

View File

@ -34,7 +34,7 @@ namespace Nes
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("s", on)
#endif
void InlNsf::SubReset(const bool hard)
{
Map ( 0x5000U, 0x5FFFU, &InlNsf::Poke_5000 );
@ -86,7 +86,7 @@ namespace Nes
{
Bank(address & 7, data);
}
NES_PEEK_A(InlNsf,8000)
{
// Not an ideal way to do this, but Nestopia does not seem to support 4K banks directly?

View File

@ -162,8 +162,6 @@ namespace Nes
byte chr[8];
} banks;
private:
Irq<> irq;
};
}

View File

@ -0,0 +1,129 @@
////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2022 Rupert Carmichael
//
// 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 "NstBoardUnl158b.hpp"
/* References:
https://github.com/SourMesen/Mesen/blob/master/Core/Unl158B.h
https://github.com/TASVideos/fceux/blob/master/src/boards/158B.cpp
*/
namespace Nes
{
namespace Core
{
namespace Boards
{
namespace Unlicensed
{
Gd98158b::Gd98158b(const Context& c)
: Mmc3(c) {}
void Gd98158b::SubReset(const bool hard)
{
lut[0] = lut[1] = lut[2] = lut[7] = 0x00;
lut[3] = 0x01;
lut[4] = 0x02;
lut[5] = 0x04;
lut[6] = 0x0F;
if (hard)
reg = 0;
banks.prg[0] = 0x00;
banks.prg[1] = 0x01;
banks.prg[2] = 0x3E;
banks.prg[3] = 0x3F;
Mmc3::SubReset( hard );
Map( 0x5000U, 0x5FFFU, &Gd98158b::Peek_5000 );
Map( 0x5000U, 0x5FFFU, &Gd98158b::Poke_5000 );
}
void Gd98158b::SubLoad(State::Loader& state,const dword baseChunk)
{
if (baseChunk == AsciiId<'1','5','8'>::V)
{
while (const dword chunk = state.Begin())
{
if (chunk == AsciiId<'R','E','G'>::V)
reg = state.Read8();
state.End();
}
}
else
{
Mmc3::SubLoad( state, baseChunk );
}
}
void Gd98158b::SubSave(State::Saver& state) const
{
Mmc3::SubSave( state );
state.Begin( AsciiId<'1','5','8'>::V ).Begin( AsciiId<'R','E','G'>::V ).Write8( reg ).End().End();
}
void NST_FASTCALL Gd98158b::UpdatePrg(uint address, uint data)
{
if (reg & 0x80)
{
const uint bank = reg & 0x7;
if (reg & 0x20)
{
prg.SwapBank<SIZE_32K>( 0x0000, bank >> 1 );
}
else
{
prg.SwapBank<SIZE_16K>( 0x0000, bank );
prg.SwapBank<SIZE_16K>( 0x4000, bank );
}
}
else
{
prg.SwapBank<SIZE_8K>( (address & 0x6000), data & 0xF );
}
}
NES_PEEK_A(Gd98158b,5000)
{
// Fake Open Bus read by always using 0x50
return 0x50 | lut[address & 0x7];
}
NES_POKE_AD(Gd98158b,5000)
{
if ((address & 0x7) == 0)
{
reg = data;
Mmc3::UpdatePrg();
}
}
}
}
}
}

View File

@ -0,0 +1,67 @@
////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2022 Rupert Carmichael
//
// 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
//
////////////////////////////////////////////////////////////////////////////////////////
#ifndef NST_BOARD_UNL_158B_H
#define NST_BOARD_UNL_158B_H
#ifdef NST_PRAGMA_ONCE
#pragma once
#endif
#include "NstBoardMmc3.hpp"
namespace Nes
{
namespace Core
{
namespace Boards
{
namespace Unlicensed
{
class Gd98158b : public Mmc3
{
public:
explicit Gd98158b(const Context&);
private:
void SubReset(bool);
void SubLoad(State::Loader&,dword);
void SubSave(State::Saver&) const;
void NST_FASTCALL UpdatePrg(uint, uint);
NES_DECL_PEEK( 5000 );
NES_DECL_POKE( 5000 );
byte lut[8];
uint reg;
};
}
}
}
}
#endif

View File

@ -0,0 +1,159 @@
////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
// Copyright (C) 2020-2022 Rupert Carmichael
//
// 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
//
////////////////////////////////////////////////////////////////////////////////////////
/* Note: At the time this was written, MMC3 boards with larger than 512K PRG
are fantasy hardware. This may change in the future if it is implemented in
a product along the lines of the PowerPak or Everdrive.
*/
#include "NstBoard.hpp"
#include "NstBoardMmc3.hpp"
#include "NstBoardUnlMmc3BigPrgRom.hpp"
#include "../NstFile.hpp"
namespace Nes
{
namespace Core
{
namespace Boards
{
namespace Unlicensed
{
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("s", on)
#endif
void Mmc3BigPrgRom::SubReset(const bool hard)
{
Mmc3::SubReset( hard );
for (uint i=0x0000; i < 0x2000; i += 0x2)
{
Map( 0x8001 + i, &Mmc3BigPrgRom::Poke_8001 );
}
}
void Mmc3BigPrgRom::SubLoad(State::Loader& state,const dword baseChunk)
{
NST_VERIFY( baseChunk == (AsciiId<'M','M','3'>::V) );
if (baseChunk == AsciiId<'M','M','3'>::V)
{
while (const dword chunk = state.Begin())
{
switch (chunk)
{
case AsciiId<'R','E','G'>::V:
{
State::Loader::Data<12> data( state );
regs.ctrl0 = data[0];
regs.ctrl1 = data[1];
banks.prg[0] = data[2] & 0x7FU;
banks.prg[1] = data[3] & 0x7FU;
banks.chr[0] = (data[6] & 0x7FU) << 1;
banks.chr[1] = banks.chr[0] | 0x01U;
banks.chr[2] = (data[7] & 0x7FU) << 1;
banks.chr[3] = banks.chr[2] | 0x01U;
banks.chr[4] = data[8];
banks.chr[5] = data[9];
banks.chr[6] = data[10];
banks.chr[7] = data[11];
break;
}
case AsciiId<'I','R','Q'>::V:
irq.unit.LoadState( state );
break;
}
state.End();
}
}
}
void Mmc3BigPrgRom::SubSave(State::Saver& state) const
{
state.Begin( AsciiId<'M','M','3'>::V );
const byte data[12] =
{
regs.ctrl0,
regs.ctrl1,
banks.prg[0],
banks.prg[1],
0x7E,
0x7F,
banks.chr[0] >> 1,
banks.chr[2] >> 1,
banks.chr[4],
banks.chr[5],
banks.chr[6],
banks.chr[7]
};
state.Begin( AsciiId<'R','E','G'>::V ).Write( data ).End();
irq.unit.SaveState( state, AsciiId<'I','R','Q'>::V );
state.End();
}
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("", on)
#endif
NES_POKE_D(Mmc3BigPrgRom,8001)
{
uint address = regs.ctrl0 & 0x7;
if (address < 6)
{
ppu.Update();
uint base = regs.ctrl0 << 5 & 0x1000;
if (address < 2)
{
address <<= 1;
base |= address << 10;
UpdateChr( base | 0x0000, (banks.chr[address+0] = data & 0xFE) );
UpdateChr( base | 0x0400, (banks.chr[address+1] = data | 0x01) );
}
else
{
UpdateChr( (base ^ 0x1000) | (address-2) << 10, (banks.chr[address+2] = data) );
}
}
else
{
UpdatePrg( (address == 6) ? (regs.ctrl0 << 8 & 0x4000) : 0x2000, (banks.prg[address-6] = data & 0x7F) );
}
}
}
}
}
}

View File

@ -0,0 +1,60 @@
////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
// Copyright (C) 2020-2022 Rupert Carmichael
//
// 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
//
////////////////////////////////////////////////////////////////////////////////////////
#ifndef NST_BOARD_UNL_MMC3BIGPRGROM_H
#define NST_BOARD_UNL_MMC3BIGPRGROM_H
#ifdef NST_PRAGMA_ONCE
#pragma once
#endif
namespace Nes
{
namespace Core
{
namespace Boards
{
namespace Unlicensed
{
class NST_NO_VTABLE Mmc3BigPrgRom : public Mmc3
{
public:
explicit Mmc3BigPrgRom(const Context& c)
: Mmc3(c) {}
private:
void SubReset(bool);
void SubSave(State::Saver&) const;
void SubLoad(State::Loader&,dword);
NES_DECL_POKE( 8001 );
};
}
}
}
}
#endif

View File

@ -0,0 +1,119 @@
////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2022 Rupert Carmichael
//
// 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
//
////////////////////////////////////////////////////////////////////////////////////////
/* References:
https://github.com/NovaSquirrel/Mesen-X/blob/master/Core/Mapper400.h
*/
#include "NstBoard.hpp"
#include "NstBoardUnlRetX7Gbl.hpp"
namespace Nes
{
namespace Core
{
namespace Boards
{
namespace Unlicensed
{
void RetX7Gbl::SubReset(bool)
{
Map( 0x7800U, 0x7FFFU, &RetX7Gbl::Poke_7800 );
Map( 0x8000U, 0xBFFFU, &RetX7Gbl::Poke_8000 );
Map( 0xC000U, 0xFFFFU, &RetX7Gbl::Poke_C000 );
regs[0] = 0x80;
regs[1] = 0x00;
UpdatePrg();
}
void RetX7Gbl::SubLoad(State::Loader& state,const dword baseChunk)
{
if (baseChunk == AsciiId<'R','X','G'>::V)
{
while (const dword chunk = state.Begin())
{
if (chunk == AsciiId<'R','E','G'>::V)
{
State::Loader::Data<3> data( state );
regs[0] = data[0];
regs[1] = data[1];
led = data[2];
}
state.End();
}
}
UpdatePrg();
}
void RetX7Gbl::SubSave(State::Saver& state) const
{
state.Begin( AsciiId<'R','X','G'>::V );
const byte data[3] =
{
regs[0], regs[1], led
};
state.Begin( AsciiId<'R','E','G'>::V ).Write( data ).End();
state.End();
}
void RetX7Gbl::UpdatePrg()
{
prg.SwapBank<SIZE_16K,0x0000>( (regs[0] & 0x78) | (regs[1] & 0x7) );
prg.SwapBank<SIZE_16K,0x4000>( (regs[0] & 0x78) | 0x7 );
if (regs[0] != 0x80)
{
ppu.SetMirroring( regs[0] & 0x20 ? Ppu::NMT_H : Ppu::NMT_V );
}
}
NES_POKE_D(RetX7Gbl,7800)
{
regs[0] = data;
UpdatePrg();
}
NES_POKE_D(RetX7Gbl,8000)
{
led = data;
}
NES_POKE_D(RetX7Gbl,C000)
{
regs[1] = data;
UpdatePrg();
chr.SwapBank<SIZE_8K,0x0000>( (data >> 5) & 0x3 );
}
}
}
}
}

View File

@ -0,0 +1,67 @@
////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2022 Rupert Carmichael
//
// 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
//
////////////////////////////////////////////////////////////////////////////////////////
#ifndef NST_BOARD_UNL_RETX7GBL_H
#define NST_BOARD_UNL_RETX7GBL_H
#ifdef NST_PRAGMA_ONCE
#pragma once
#endif
namespace Nes
{
namespace Core
{
namespace Boards
{
namespace Unlicensed
{
class RetX7Gbl : public Board
{
public:
explicit RetX7Gbl(const Context& c)
: Board(c) {}
private:
void SubReset(bool);
void SubLoad(State::Loader&,dword);
void SubSave(State::Saver&) const;
void UpdatePrg();
NES_DECL_POKE( 7800 );
NES_DECL_POKE( 8000 );
NES_DECL_POKE( C000 );
uint regs[2];
uint led;
};
}
}
}
}
#endif