neshawk - rough sketch of NSF player. basically functional for some games
This commit is contained in:
parent
e485882aed
commit
db9fbced86
|
@ -1855,9 +1855,9 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
ofd.Filter = FormatFilter(
|
ofd.Filter = FormatFilter(
|
||||||
"Rom Files", "*.nes;*.fds;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.col;.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;%ARCH%",
|
"Rom Files", "*.nes;*.fds;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.col;.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;%ARCH%",
|
||||||
"Music Files", "*.psf;*.sid",
|
"Music Files", "*.psf;*.sid;*.nsf",
|
||||||
"Disc Images", "*.cue;*.ccd;*.m3u",
|
"Disc Images", "*.cue;*.ccd;*.m3u",
|
||||||
"NES", "*.nes;*.fds;%ARCH%",
|
"NES", "*.nes;*.fds;*.nsf;%ARCH%",
|
||||||
"Super NES", "*.smc;*.sfc;*.xml;%ARCH%",
|
"Super NES", "*.smc;*.sfc;*.xml;%ARCH%",
|
||||||
"Master System", "*.sms;*.gg;*.sg;%ARCH%",
|
"Master System", "*.sms;*.gg;*.sg;%ARCH%",
|
||||||
"PC Engine", "*.pce;*.sgx;*.cue;*.ccd;%ARCH%",
|
"PC Engine", "*.pce;*.sgx;*.cue;*.ccd;%ARCH%",
|
||||||
|
@ -1885,7 +1885,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
ofd.Filter = FormatFilter(
|
ofd.Filter = FormatFilter(
|
||||||
"Rom Files", "*.nes;*.fds;*.sms;*.gg;*.sg;*.gb;*.gbc;*.gba;*.pce;*.sgx;*.bin;*.smd;*.gen;*.md;*.smc;*.sfc;*.a26;*.a78;*.lnx;*.col;*.rom;*.cue;*.ccd;*.sgb;*.z64;*.v64;*.n64;*.ws;*.wsc;*.xml;%ARCH%",
|
"Rom Files", "*.nes;*.fds;*.sms;*.gg;*.sg;*.gb;*.gbc;*.gba;*.pce;*.sgx;*.bin;*.smd;*.gen;*.md;*.smc;*.sfc;*.a26;*.a78;*.lnx;*.col;*.rom;*.cue;*.ccd;*.sgb;*.z64;*.v64;*.n64;*.ws;*.wsc;*.xml;%ARCH%",
|
||||||
"Disc Images", "*.cue;*.ccd;*.m3u",
|
"Disc Images", "*.cue;*.ccd;*.m3u",
|
||||||
"NES", "*.nes;*.fds;%ARCH%",
|
"NES", "*.nes;*.fds;*.nsf;%ARCH%",
|
||||||
"Super NES", "*.smc;*.sfc;*.xml;%ARCH%",
|
"Super NES", "*.smc;*.sfc;*.xml;%ARCH%",
|
||||||
"Nintendo 64", "*.z64;*.v64;*.n64",
|
"Nintendo 64", "*.z64;*.v64;*.n64",
|
||||||
"Gameboy", "*.gb;*.gbc;*.sgb;%ARCH%",
|
"Gameboy", "*.gb;*.gbc;*.sgb;%ARCH%",
|
||||||
|
|
|
@ -602,6 +602,7 @@
|
||||||
<Compile Include="Consoles\Nintendo\NES\Boards\NROM.cs">
|
<Compile Include="Consoles\Nintendo\NES\Boards\NROM.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Consoles\Nintendo\NES\Boards\NSFBoard.cs" />
|
||||||
<Compile Include="Consoles\Nintendo\NES\Boards\PxROM_FxROM.cs" />
|
<Compile Include="Consoles\Nintendo\NES\Boards\PxROM_FxROM.cs" />
|
||||||
<Compile Include="Consoles\Nintendo\NES\Boards\Sachen8259.cs" />
|
<Compile Include="Consoles\Nintendo\NES\Boards\Sachen8259.cs" />
|
||||||
<Compile Include="Consoles\Nintendo\NES\Boards\SachenSimple.cs" />
|
<Compile Include="Consoles\Nintendo\NES\Boards\SachenSimple.cs" />
|
||||||
|
@ -664,6 +665,7 @@
|
||||||
<DependentUpon>NES.cs</DependentUpon>
|
<DependentUpon>NES.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Consoles\Nintendo\NES\NESControllers.cs" />
|
<Compile Include="Consoles\Nintendo\NES\NESControllers.cs" />
|
||||||
|
<Compile Include="Consoles\Nintendo\NES\NSFFormat.cs" />
|
||||||
<Compile Include="Consoles\Nintendo\NES\Palettes.cs" />
|
<Compile Include="Consoles\Nintendo\NES\Palettes.cs" />
|
||||||
<Compile Include="Consoles\Nintendo\NES\PPU.cs" />
|
<Compile Include="Consoles\Nintendo\NES\PPU.cs" />
|
||||||
<Compile Include="Consoles\Nintendo\NES\PPU.regs.cs" />
|
<Compile Include="Consoles\Nintendo\NES\PPU.regs.cs" />
|
||||||
|
|
|
@ -0,0 +1,369 @@
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using BizHawk.Common;
|
||||||
|
|
||||||
|
//NSF ROM and general approaches are taken from FCEUX. however, i've improvised/simplified/broken things so the rom is doing some pointless stuff now.
|
||||||
|
|
||||||
|
//check nsfspec.txt for more on why FDS is weird. lets try not following FCEUX too much there.
|
||||||
|
|
||||||
|
//TODO - add a sleep mode to the cpu and patch the rom program to use it?
|
||||||
|
//some NSF players know when a song ends.
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
{
|
||||||
|
[NES.INESBoardImplCancel]
|
||||||
|
public sealed class NSFBoard : NES.NESBoardBase
|
||||||
|
{
|
||||||
|
//configuration
|
||||||
|
internal NSFFormat nsf;
|
||||||
|
byte[] InitBankSwitches = new byte[8];
|
||||||
|
byte[] FakePRG = new byte[32768];
|
||||||
|
bool BankSwitched;
|
||||||
|
|
||||||
|
//------------------------------
|
||||||
|
//state
|
||||||
|
IntBuffer prg_banks_4k = new IntBuffer(8);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// whether vectors are currently patched. they should not be patched when running init/play routines because data from the ends of banks might get used
|
||||||
|
/// </summary>
|
||||||
|
bool Patch_Vectors;
|
||||||
|
|
||||||
|
int CurrentSong;
|
||||||
|
bool ResetSignal;
|
||||||
|
int ButtonState;
|
||||||
|
|
||||||
|
public override bool Configure(NES.EDetectionOrigin origin)
|
||||||
|
{
|
||||||
|
Cart.wram_size = 8;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
prg_banks_4k.Dispose();
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitNSF(NSFFormat nsf)
|
||||||
|
{
|
||||||
|
this.nsf = nsf;
|
||||||
|
|
||||||
|
//patch the NSF rom with the init and play addresses
|
||||||
|
NSFROM[0x12] = (byte)(nsf.InitAddress);
|
||||||
|
NSFROM[0x13] = (byte)(nsf.InitAddress >> 8);
|
||||||
|
NSFROM[0x19] = (byte)(nsf.PlayAddress);
|
||||||
|
NSFROM[0x1A] = (byte)(nsf.PlayAddress >> 8);
|
||||||
|
|
||||||
|
//complicated anlysis straight from FCEUX
|
||||||
|
//apparently, it converts a non-bankswitched configuration into a bankswitched configuration
|
||||||
|
//since the non-bankswitched configuration is seemingly almost pointless.
|
||||||
|
//I'm not too sure how we would really get a non-bankswitched file, using the code below.
|
||||||
|
//It would need to be loaded below 0x8000 I think?
|
||||||
|
BankSwitched = false;
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
InitBankSwitches[i] = nsf.BankswitchInitValues[i];
|
||||||
|
if (InitBankSwitches[i] != 0)
|
||||||
|
BankSwitched = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BankSwitched)
|
||||||
|
{
|
||||||
|
if ((nsf.LoadAddress & 0x7000) >= 0x7000)
|
||||||
|
{
|
||||||
|
//"Ice Climber, and other F000 base address tunes need this"
|
||||||
|
BankSwitched = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
byte bankCounter = 0;
|
||||||
|
for (int x = (nsf.LoadAddress >> 12) & 0x7; x < 8; x++)
|
||||||
|
{
|
||||||
|
InitBankSwitches[x] = bankCounter;
|
||||||
|
bankCounter++;
|
||||||
|
}
|
||||||
|
BankSwitched = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
if (InitBankSwitches[i] != 0)
|
||||||
|
BankSwitched = true;
|
||||||
|
|
||||||
|
if (!BankSwitched)
|
||||||
|
{
|
||||||
|
throw new Exception("Test");
|
||||||
|
//setup FakePRG by copying in
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplayInit();
|
||||||
|
CurrentSong = nsf.StartingSong;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReplayInit()
|
||||||
|
{
|
||||||
|
ResetSignal = true;
|
||||||
|
Patch_Vectors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void NESSoftReset()
|
||||||
|
{
|
||||||
|
ReplayInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SyncState(Serializer ser)
|
||||||
|
{
|
||||||
|
base.SyncState(ser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteEXP(int addr, byte value)
|
||||||
|
{
|
||||||
|
switch (addr)
|
||||||
|
{
|
||||||
|
case 0x1FF6:
|
||||||
|
case 0x1FF7:
|
||||||
|
//if (!(NSFHeader.SoundChip & 4)) return; //FDS
|
||||||
|
break;
|
||||||
|
case 0x1FF8:
|
||||||
|
case 0x1FF9:
|
||||||
|
case 0x1FFA:
|
||||||
|
case 0x1FFB:
|
||||||
|
case 0x1FFC:
|
||||||
|
case 0x1FFD:
|
||||||
|
case 0x1FFE:
|
||||||
|
case 0x1FFF:
|
||||||
|
if (!BankSwitched) break;
|
||||||
|
addr -= 0x1FF8;
|
||||||
|
prg_banks_4k[addr] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void WriteReg2xxx(int addr, byte value)
|
||||||
|
{
|
||||||
|
switch (addr)
|
||||||
|
{
|
||||||
|
case 0x3FF3: Patch_Vectors = true; break;
|
||||||
|
case 0x3FF4: Patch_Vectors = false; break;
|
||||||
|
case 0x3FF5: Patch_Vectors = true; break;
|
||||||
|
default:
|
||||||
|
base.WriteReg2xxx(addr, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte PeekReg2xxx(int addr)
|
||||||
|
{
|
||||||
|
if (addr < 0x3FF0)
|
||||||
|
return NSFROM[addr - 0x3800];
|
||||||
|
else return base.PeekReg2xxx(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte ReadReg2xxx(int addr)
|
||||||
|
{
|
||||||
|
if (addr < 0x3800)
|
||||||
|
return base.ReadReg2xxx(addr);
|
||||||
|
else if (addr >= 0x3FF0)
|
||||||
|
{
|
||||||
|
if (addr == 0x3FF0)
|
||||||
|
{
|
||||||
|
byte ret = 0;
|
||||||
|
if (ResetSignal) ret = 1;
|
||||||
|
ResetSignal = false;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else if (addr == 0x3FF1)
|
||||||
|
{
|
||||||
|
//kevtris's reset process seems not to work. dunno what all is going on in there
|
||||||
|
|
||||||
|
//our own innovation, should work OK..
|
||||||
|
NES.apu.NESSoftReset();
|
||||||
|
|
||||||
|
//mostly fceux's guidance
|
||||||
|
NES.WriteMemory(0x4015, 0);
|
||||||
|
for (int i = 0; i < 14; i++)
|
||||||
|
NES.WriteMemory((ushort)(0x4000 + i), 0);
|
||||||
|
NES.WriteMemory(0x4015, 0x0F);
|
||||||
|
|
||||||
|
//clearing APU misc stuff, maybe not needed with soft reset above
|
||||||
|
//NES.WriteMemory(0x4017, 0xC0);
|
||||||
|
//NES.WriteMemory(0x4017, 0xC0);
|
||||||
|
//NES.WriteMemory(0x4017, 0x40);
|
||||||
|
|
||||||
|
//important to NSF standard for ram to be cleared, otherwise replayers are confused on account of not initializing memory themselves
|
||||||
|
var ram = NES.ram;
|
||||||
|
var wram = this.WRAM;
|
||||||
|
int wram_size = wram.Length;
|
||||||
|
for (int i = 0; i < 0x800; i++)
|
||||||
|
ram[i] = 0;
|
||||||
|
for (int i = 0; i < wram_size; i++)
|
||||||
|
wram[i] = 0;
|
||||||
|
|
||||||
|
//store specified initial bank state
|
||||||
|
if (BankSwitched)
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
WriteEXP(0x5FF8 + i - 0x4000, InitBankSwitches[i]);
|
||||||
|
|
||||||
|
return (byte)(CurrentSong - 1);
|
||||||
|
}
|
||||||
|
else if (addr == 0x3FF3) return 0; //always return NTSC I guess
|
||||||
|
else return base.ReadReg2xxx(addr);
|
||||||
|
}
|
||||||
|
else if (addr - 0x3800 < NSFROM.Length) return NSFROM[addr - 0x3800];
|
||||||
|
else return base.ReadReg2xxx(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//; @NMIVector
|
||||||
|
//00:XX00:8D F4 3F STA $3FF4 = #$00 ; clear NMI_2 (the value of A is unimportant)
|
||||||
|
//00:XX03:A2 FF LDX #$FF
|
||||||
|
//00:XX05:9A TXS ; stack pointer is initialized
|
||||||
|
//00:XX06:AD F0 3F LDA $3FF0 = #$00 ; read a flag that says whether we need to run init
|
||||||
|
//00:XX09:F0 09 BEQ $8014 ; If we dont need init, go to @PastInit
|
||||||
|
//00:XX0B:AD F1 3F LDA $3FF1 = #$00 ; reading this value causes a reset
|
||||||
|
//00:XX0E:AE F3 3F LDX $3FF3 = #$00 ; reads the PAL flag
|
||||||
|
//00:XX11:20 00 00 JSR $0000 ; JSR to INIT routine
|
||||||
|
//; @PastInit
|
||||||
|
//00:XX14:A9 00 LDA #$00
|
||||||
|
//00:XX16:AA TAX
|
||||||
|
//00:XX17:A8 TAY ; X and Y are cleared
|
||||||
|
//00:XX18:20 00 00 JSR $0000 ; JSR to PLAY routine
|
||||||
|
//00:XX1B:8D F5 3F STA $3FF5 = #$FF ; set NMI_2 flag
|
||||||
|
//00:XX1E:90 FE BCC $XX1E ; infinite loop.. when the song is over?
|
||||||
|
//; @ResetVector
|
||||||
|
//00:XX20:8D F3 3F STA $3FF3 = #$00 ; set NMI_1 flag (the value of A is unimportant); since the rom boots here, this was needed for the initial NMI. but we also get it from having the reset signal set, so..
|
||||||
|
//00:XX23:18 CLC
|
||||||
|
//00:XX24:90 FE BCC $XX24 ;infinite loop to wait for first NMI
|
||||||
|
|
||||||
|
const ushort NMI_VECTOR = 0x3800;
|
||||||
|
const ushort RESET_VECTOR = 0x3820;
|
||||||
|
|
||||||
|
//for reasons unknown, this is a little extra long
|
||||||
|
byte[] NSFROM = new byte[0x30 + 6]
|
||||||
|
{
|
||||||
|
//0x00 - NMI
|
||||||
|
0x8D,0xF4,0x3F, //Stop play routine NMIs.
|
||||||
|
0xA2,0xFF,0x9A, //Initialize the stack pointer.
|
||||||
|
0xAD,0xF0,0x3F, //See if we need to init.
|
||||||
|
0xF0,0x09, //If 0, go to play routine playing.
|
||||||
|
|
||||||
|
0xAD,0xF1,0x3F, //Confirm and load A
|
||||||
|
0xAE,0xF3,0x3F, //Load X with PAL/NTSC byte
|
||||||
|
|
||||||
|
//0x11
|
||||||
|
0x20,0x00,0x00, //JSR to init routine (WILL BE PATCHED)
|
||||||
|
|
||||||
|
0xA9,0x00,
|
||||||
|
0xAA,
|
||||||
|
0xA8,
|
||||||
|
|
||||||
|
//0x18
|
||||||
|
0x20,0x00,0x00, //JSR to play routine (WILL BE PATCHED)
|
||||||
|
0x8D,0xF5,0x3F, //Start play routine NMIs.
|
||||||
|
0x90,0xFE, //Loopie time.
|
||||||
|
|
||||||
|
// 0x20
|
||||||
|
0x8D,0xF3,0x3F, //Init init NMIs
|
||||||
|
0x18,
|
||||||
|
0x90,0xFE, //Loopie time.
|
||||||
|
|
||||||
|
//0x26
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
//0x30
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
public override void AtVsyncNMI()
|
||||||
|
{
|
||||||
|
if(Patch_Vectors)
|
||||||
|
NES.cpu.NMI = true;
|
||||||
|
|
||||||
|
//strobe pad
|
||||||
|
NES.WriteMemory(0x4016, 1);
|
||||||
|
NES.WriteMemory(0x4016, 0);
|
||||||
|
|
||||||
|
//read pad and create rising edge button signals so we dont trigger events as quickly as we hold the button down
|
||||||
|
int currButtons = 0;
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
currButtons <<= 1;
|
||||||
|
currButtons |= (NES.ReadMemory(0x4016) & 1);
|
||||||
|
}
|
||||||
|
int justDown = (~ButtonState) & currButtons;
|
||||||
|
Bit a = (justDown >> 7) & 1;
|
||||||
|
Bit b = (justDown >> 6) & 1;
|
||||||
|
Bit sel = (justDown >> 5) & 1;
|
||||||
|
Bit start = (justDown >> 4) & 1;
|
||||||
|
Bit up = (justDown >> 3) & 1;
|
||||||
|
Bit down = (justDown >> 2) & 1;
|
||||||
|
Bit left = (justDown >> 1) & 1;
|
||||||
|
Bit right = (justDown >> 0) & 1;
|
||||||
|
ButtonState = currButtons;
|
||||||
|
|
||||||
|
//RIGHT: next song
|
||||||
|
//LEFT: prev song
|
||||||
|
//A: restart song
|
||||||
|
|
||||||
|
bool reset = false;
|
||||||
|
if (right)
|
||||||
|
{
|
||||||
|
CurrentSong++;
|
||||||
|
reset = true;
|
||||||
|
}
|
||||||
|
if (left)
|
||||||
|
{
|
||||||
|
CurrentSong--;
|
||||||
|
reset = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a)
|
||||||
|
reset = true;
|
||||||
|
|
||||||
|
if (reset)
|
||||||
|
{
|
||||||
|
ReplayInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte ReadPPU(int addr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte ReadWRAM(int addr)
|
||||||
|
{
|
||||||
|
return base.ReadWRAM(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte ReadPRG(int addr)
|
||||||
|
{
|
||||||
|
//patch in vector reading
|
||||||
|
if (Patch_Vectors)
|
||||||
|
{
|
||||||
|
if (addr == 0x7FFA) return (byte)(NMI_VECTOR & 0xFF);
|
||||||
|
else if (addr == 0x7FFB) return (byte)((NMI_VECTOR >> 8) & 0xFF);
|
||||||
|
else if (addr == 0x7FFC) return (byte)(RESET_VECTOR & 0xFF);
|
||||||
|
else if (addr == 0x7FFD) { return (byte)((RESET_VECTOR >> 8) & 0xFF); }
|
||||||
|
return NES.DB;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int bank_4k = addr >> 12;
|
||||||
|
int ofs = addr & ((1 << 12) - 1);
|
||||||
|
bank_4k = prg_banks_4k[bank_4k];
|
||||||
|
addr = (bank_4k << 12) | ofs;
|
||||||
|
|
||||||
|
if (BankSwitched)
|
||||||
|
{
|
||||||
|
//rom data began at 0x80 of the NSF file
|
||||||
|
addr += 0x80;
|
||||||
|
|
||||||
|
return ROM[addr];
|
||||||
|
}
|
||||||
|
else return NES.DB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,11 +35,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
void AddressPPU(int addr);
|
void AddressPPU(int addr);
|
||||||
byte ReadWRAM(int addr);
|
byte ReadWRAM(int addr);
|
||||||
byte ReadEXP(int addr);
|
byte ReadEXP(int addr);
|
||||||
|
byte ReadReg2xxx(int addr);
|
||||||
|
byte PeekReg2xxx(int addr);
|
||||||
void WritePRG(int addr, byte value);
|
void WritePRG(int addr, byte value);
|
||||||
void WritePPU(int addr, byte value);
|
void WritePPU(int addr, byte value);
|
||||||
void WriteWRAM(int addr, byte value);
|
void WriteWRAM(int addr, byte value);
|
||||||
void WriteEXP(int addr, byte value);
|
void WriteEXP(int addr, byte value);
|
||||||
|
void WriteReg2xxx(int addr, byte value);
|
||||||
void NESSoftReset();
|
void NESSoftReset();
|
||||||
|
void AtVsyncNMI();
|
||||||
byte[] SaveRam { get; }
|
byte[] SaveRam { get; }
|
||||||
byte[] WRAM { get; set; }
|
byte[] WRAM { get; set; }
|
||||||
byte[] VRAM { get; set; }
|
byte[] VRAM { get; set; }
|
||||||
|
@ -83,6 +87,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
public abstract bool Configure(NES.EDetectionOrigin origin);
|
public abstract bool Configure(NES.EDetectionOrigin origin);
|
||||||
public virtual void ClockPPU() { }
|
public virtual void ClockPPU() { }
|
||||||
public virtual void ClockCPU() { }
|
public virtual void ClockCPU() { }
|
||||||
|
public virtual void AtVsyncNMI() { }
|
||||||
|
|
||||||
public CartInfo Cart { get { return NES.cart; } }
|
public CartInfo Cart { get { return NES.cart; } }
|
||||||
public NES NES { get; set; }
|
public NES NES { get; set; }
|
||||||
|
@ -207,6 +212,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
return NES.DB;
|
return NES.DB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual byte ReadReg2xxx(int addr)
|
||||||
|
{
|
||||||
|
return NES.ppu.ReadReg(addr & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual byte PeekReg2xxx(int addr)
|
||||||
|
{
|
||||||
|
return NES.ppu.PeekReg(addr & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void WriteReg2xxx(int addr, byte value)
|
||||||
|
{
|
||||||
|
NES.ppu.WriteReg(addr & 7, value);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void WritePPU(int addr, byte value)
|
public virtual void WritePPU(int addr, byte value)
|
||||||
{
|
{
|
||||||
if (addr < 0x2000)
|
if (addr < 0x2000)
|
||||||
|
@ -338,7 +358,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
void BoardSystemHardReset()
|
void BoardSystemHardReset()
|
||||||
{
|
{
|
||||||
INESBoard newboard;
|
INESBoard newboard;
|
||||||
// fds has a unique activation setup
|
// FDS and NSF have a unique activation setup
|
||||||
if (Board is FDS)
|
if (Board is FDS)
|
||||||
{
|
{
|
||||||
var newfds = new FDS();
|
var newfds = new FDS();
|
||||||
|
@ -347,6 +367,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
newfds.SetDiskImage(oldfds.GetDiskImage());
|
newfds.SetDiskImage(oldfds.GetDiskImage());
|
||||||
newboard = newfds;
|
newboard = newfds;
|
||||||
}
|
}
|
||||||
|
else if (Board is NSFBoard)
|
||||||
|
{
|
||||||
|
var newnsf = new NSFBoard();
|
||||||
|
var oldnsf = Board as NSFBoard;
|
||||||
|
newnsf.InitNSF(oldnsf.nsf);
|
||||||
|
newboard = newnsf;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newboard = CreateBoardInstance(Board.GetType());
|
newboard = CreateBoardInstance(Board.GetType());
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
int cpu_accumulate; //cpu timekeeper
|
int cpu_accumulate; //cpu timekeeper
|
||||||
public PPU ppu;
|
public PPU ppu;
|
||||||
public APU apu;
|
public APU apu;
|
||||||
byte[] ram;
|
public byte[] ram;
|
||||||
NESWatch[] sysbus_watch = new NESWatch[65536];
|
NESWatch[] sysbus_watch = new NESWatch[65536];
|
||||||
public byte[] CIRAM; //AKA nametables
|
public byte[] CIRAM; //AKA nametables
|
||||||
string game_name = string.Empty; //friendly name exposed to user and used as filename base
|
string game_name = string.Empty; //friendly name exposed to user and used as filename base
|
||||||
|
@ -505,7 +505,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
}
|
}
|
||||||
else if (addr < 0x4000)
|
else if (addr < 0x4000)
|
||||||
{
|
{
|
||||||
ret = ppu.PeekReg(addr & 7);
|
ret = Board.PeekReg2xxx(addr);
|
||||||
}
|
}
|
||||||
else if (addr < 0x4020)
|
else if (addr < 0x4020)
|
||||||
{
|
{
|
||||||
|
@ -546,7 +546,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
}
|
}
|
||||||
else if (addr < 0x4000)
|
else if (addr < 0x4000)
|
||||||
{
|
{
|
||||||
ret = ppu.ReadReg(addr & 7);
|
ret = Board.ReadReg2xxx(addr);
|
||||||
}
|
}
|
||||||
else if (addr < 0x4020)
|
else if (addr < 0x4020)
|
||||||
{
|
{
|
||||||
|
@ -605,7 +605,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
}
|
}
|
||||||
else if (addr < 0x4000)
|
else if (addr < 0x4000)
|
||||||
{
|
{
|
||||||
ppu.WriteReg(addr & 7, value);
|
Board.WriteReg2xxx(addr,value);
|
||||||
}
|
}
|
||||||
else if (addr < 0x4020)
|
else if (addr < 0x4020)
|
||||||
{
|
{
|
||||||
|
|
|
@ -346,7 +346,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
|
||||||
public enum EDetectionOrigin
|
public enum EDetectionOrigin
|
||||||
{
|
{
|
||||||
None, BootGodDB, GameDB, INES, UNIF, FDS
|
None, BootGodDB, GameDB, INES, UNIF, FDS, NSF
|
||||||
}
|
}
|
||||||
|
|
||||||
StringWriter LoadReport;
|
StringWriter LoadReport;
|
||||||
|
@ -406,6 +406,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
hash_sha1_several.Add(hash_sha1);
|
hash_sha1_several.Add(hash_sha1);
|
||||||
LoadWriteLine("headerless rom hash: {0}", hash_sha1);
|
LoadWriteLine("headerless rom hash: {0}", hash_sha1);
|
||||||
}
|
}
|
||||||
|
else if(file.Take(5).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("NESM\x1A")))
|
||||||
|
{
|
||||||
|
origin = EDetectionOrigin.NSF;
|
||||||
|
LoadWriteLine("Loading as NSF");
|
||||||
|
var nsf = new NSFFormat();
|
||||||
|
nsf.WrapByteArray(file);
|
||||||
|
|
||||||
|
cart = new CartInfo();
|
||||||
|
var nsfboard = new NSFBoard();
|
||||||
|
nsfboard.Create(this);
|
||||||
|
nsfboard.ROM = rom;
|
||||||
|
nsfboard.InitNSF( nsf);
|
||||||
|
nsfboard.InitialRegisterValues = InitialMapperRegisterValues;
|
||||||
|
nsfboard.Configure(origin);
|
||||||
|
nsfboard.WRAM = new byte[cart.wram_size * 1024];
|
||||||
|
Board = nsfboard;
|
||||||
|
Board.PostConfigure();
|
||||||
|
|
||||||
|
Console.WriteLine("Using NTSC display type for NSF for now");
|
||||||
|
_display_type = Common.DisplayType.NTSC;
|
||||||
|
|
||||||
|
HardReset();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
else if (file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("FDS\x1A"))
|
else if (file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("FDS\x1A"))
|
||||||
|| file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("\x01*NI")))
|
|| file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("\x01*NI")))
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using BizHawk.Common;
|
||||||
|
using BizHawk.Common.BufferExtensions;
|
||||||
|
using BizHawk.Common.IOExtensions;
|
||||||
|
|
||||||
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
{
|
||||||
|
//http://kevtris.org/nes/nsfspec.txt
|
||||||
|
//http://en.wikipedia.org/wiki/NES_Sound_Format
|
||||||
|
public class NSFFormat
|
||||||
|
{
|
||||||
|
public byte[] NSFData;
|
||||||
|
|
||||||
|
public byte Version;
|
||||||
|
public byte TotalSongs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 1-indexed. 0 is an invalid value, I guess
|
||||||
|
/// </summary>
|
||||||
|
public byte StartingSong;
|
||||||
|
|
||||||
|
public ushort LoadAddress;
|
||||||
|
|
||||||
|
public ushort InitAddress;
|
||||||
|
|
||||||
|
public ushort PlayAddress;
|
||||||
|
|
||||||
|
public string SongName;
|
||||||
|
|
||||||
|
public string ArtistName;
|
||||||
|
|
||||||
|
public string CopyrightHolder;
|
||||||
|
|
||||||
|
public ushort SpeedNTSC;
|
||||||
|
|
||||||
|
public byte[] BankswitchInitValues = new byte[8];
|
||||||
|
|
||||||
|
public ushort SpeedPAL;
|
||||||
|
|
||||||
|
public bool IsNTSC;
|
||||||
|
|
||||||
|
public bool IsPAL;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum eExtraChips
|
||||||
|
{
|
||||||
|
None = 0, VRC6 = 1, VRC7 = 2, FDS = 4, MMC5 = 8, Namco106 = 16, FME7 = 32
|
||||||
|
}
|
||||||
|
|
||||||
|
public eExtraChips ExtraChips;
|
||||||
|
|
||||||
|
public void WrapByteArray(byte[] data)
|
||||||
|
{
|
||||||
|
NSFData = data;
|
||||||
|
|
||||||
|
var ms = new MemoryStream(data);
|
||||||
|
var br = new BinaryReader(ms);
|
||||||
|
br.BaseStream.Position += 5;
|
||||||
|
|
||||||
|
Version = br.ReadByte();
|
||||||
|
TotalSongs = br.ReadByte();
|
||||||
|
StartingSong = br.ReadByte();
|
||||||
|
LoadAddress = br.ReadUInt16();
|
||||||
|
InitAddress = br.ReadUInt16();
|
||||||
|
PlayAddress = br.ReadUInt16();
|
||||||
|
SongName = br.ReadStringFixedAscii(32);
|
||||||
|
ArtistName = br.ReadStringFixedAscii(32);
|
||||||
|
CopyrightHolder = br.ReadStringFixedAscii(32);
|
||||||
|
SpeedNTSC = br.ReadUInt16();
|
||||||
|
br.Read(BankswitchInitValues, 0, 8);
|
||||||
|
SpeedPAL = br.ReadUInt16();
|
||||||
|
byte temp = br.ReadByte();
|
||||||
|
if ((temp & 2) != 0) IsNTSC = IsPAL = true;
|
||||||
|
else if ((temp & 1) != 0) IsPAL = true; else IsNTSC = true;
|
||||||
|
ExtraChips = (eExtraChips)br.ReadByte();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -124,6 +124,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
bool nmi_destiny = reg_2000.vblank_nmi_gen && Reg2002_vblank_active;
|
bool nmi_destiny = reg_2000.vblank_nmi_gen && Reg2002_vblank_active;
|
||||||
runppu(3);
|
runppu(3);
|
||||||
if (nmi_destiny) TriggerNMI();
|
if (nmi_destiny) TriggerNMI();
|
||||||
|
nes.Board.AtVsyncNMI();
|
||||||
runppu(postNMIlines * kLineTime - delay);
|
runppu(postNMIlines * kLineTime - delay);
|
||||||
|
|
||||||
//this seems to run just before the dummy scanline begins
|
//this seems to run just before the dummy scanline begins
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace BizHawk.Emulation.Cores
|
||||||
Saturn, MegaCD,
|
Saturn, MegaCD,
|
||||||
|
|
||||||
PCE, SGX, TurboCD,
|
PCE, SGX, TurboCD,
|
||||||
INES, FDS, UNIF,
|
INES, FDS, UNIF, NSF,
|
||||||
SFC, N64,
|
SFC, N64,
|
||||||
GB, GBC, GBA, NDS,
|
GB, GBC, GBA, NDS,
|
||||||
COL,
|
COL,
|
||||||
|
@ -257,6 +257,7 @@ namespace BizHawk.Emulation.Cores
|
||||||
{
|
{
|
||||||
public static SimpleMagicRecord INES = new SimpleMagicRecord { Offset = 0, Key = "NES" };
|
public static SimpleMagicRecord INES = new SimpleMagicRecord { Offset = 0, Key = "NES" };
|
||||||
public static SimpleMagicRecord UNIF = new SimpleMagicRecord { Offset = 0, Key = "UNIF" };
|
public static SimpleMagicRecord UNIF = new SimpleMagicRecord { Offset = 0, Key = "UNIF" };
|
||||||
|
public static SimpleMagicRecord NSF = new SimpleMagicRecord { Offset = 0, Key = "NESM\x1A" };
|
||||||
|
|
||||||
public static SimpleMagicRecord FDS_HEADERLESS = new SimpleMagicRecord { Offset = 0, Key = "\x01*NINTENDO-HVC*" };
|
public static SimpleMagicRecord FDS_HEADERLESS = new SimpleMagicRecord { Offset = 0, Key = "\x01*NINTENDO-HVC*" };
|
||||||
public static SimpleMagicRecord FDS_HEADER = new SimpleMagicRecord { Offset = 0, Key = "FDS\x1A" };
|
public static SimpleMagicRecord FDS_HEADER = new SimpleMagicRecord { Offset = 0, Key = "FDS\x1A" };
|
||||||
|
|
Loading…
Reference in New Issue