NES: take a stab at the VRC6 clusterfuck (thanks, obama!). speed loss of about 2%. as far as we know, no game ever used any of these features.

This commit is contained in:
goyuken 2014-02-15 18:45:52 +00:00
parent 9c071bf45e
commit c333e2f6f3
1 changed files with 136 additions and 41 deletions

View File

@ -6,14 +6,109 @@ using BizHawk.Emulation.Common.Components;
namespace BizHawk.Emulation.Cores.Nintendo.NES
{
//mapper 24 + 26
//If you change any of the IRQ logic here, be sure to change it in VRC 2/3/4/7 as well.
//If you change any of the IRQ logic here, be sure to change it in VRC 4/7 as well.
public sealed class VRC6 : NES.NESBoardBase
{
#region CHRLUT
// what did i do in a previous life to deserve this?
// given the bottom four bits of $b003, and a 1K address region in PPU $0000:$3fff,
static byte[] Banks = new byte[16 * 16]; // which of the 8 chr regs is used to determine the bank here?
static byte[] Masks = new byte[16 * 16]; // what is the resulting 8 bit chr reg value ANDed with?
static byte[] A10s = new byte[16 * 16]; // and then what is it ORed with?
static byte[] PTables = new byte[]
{
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x80,0xc0,0x81,0xc1,0x82,0xc2,0x83,0xc3,
0x00,0x01,0x02,0x03,0x84,0xc4,0x85,0xc5,
};
static void GetBankByte(int b003, int banknum, out byte bank, out byte mask, out byte a10)
{
if (banknum < 8) // pattern tables
{
int ptidx = b003 & 3;
if (ptidx == 3) ptidx--;
byte pt = PTables[ptidx * 8 + banknum];
bank = (byte)(pt & 7);
mask = (byte)(pt.Bit(7) ? 0xfe : 0xff);
a10 = (byte)(pt.Bit(7) && pt.Bit(6) ? 1 : 0);
}
else // nametables
{
banknum &= 3;
switch (b003 & 7)
{
case 0:
case 6:
case 7: // H-mirror
banknum >>= 1;
break;
case 2:
case 3:
case 4: // V-mirror
banknum &= 1;
break;
case 1:
case 5: // 4 screen
break;
}
bank = (byte)(banknum + 4);
switch (b003)
{
case 0:
case 7: // V-mirror
mask = 0xfe;
a10 = (byte)(banknum & 1);
break;
case 3:
case 4: // H-mirror
mask = 0xfe;
a10 = (byte)(banknum >> 1);
break;
case 8:
case 15: // 1scA
mask = 0xfe;
a10 = 0;
break;
case 11:
case 12: // 1scB
mask = 0xfe;
a10 = 1;
break;
default: // no replacement
mask = 0xff;
a10 = 0;
break;
}
}
}
static VRC6()
{
int idx = 0;
byte bank, mask, a10;
for (int b003 = 0; b003 < 16; b003++)
{
for (int banknum = 0; banknum < 16; banknum++)
{
GetBankByte(b003, banknum, out bank, out mask, out a10);
Banks[idx] = bank;
Masks[idx] = mask;
A10s[idx] = a10;
idx++;
}
}
}
#endregion
//configuration
int prg_bank_mask_8k, chr_bank_mask_1k;
int chr_byte_mask;
bool newer_variant;
//Sound.VRC6 VRC6Sound = new Sound.VRC6();
VRC6Alt VRC6Sound;
//state
@ -26,13 +121,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
byte irq_counter;
int irq_prescaler;
bool chrA10replace;
bool NTROM;
int PPUBankingMode;
public override void Dispose()
{
base.Dispose();
prg_banks_8k.Dispose();
chr_banks_1k.Dispose();
//if (VRC6Sound != null)
// VRC6Sound.Dispose();
}
public override void SyncState(Serializer ser)
@ -50,6 +147,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
ser.Sync("irq_counter", ref irq_counter);
ser.Sync("irq_prescaler", ref irq_prescaler);
ser.Sync("chrA10replace", ref chrA10replace);
ser.Sync("NTROM", ref NTROM);
ser.Sync("PPUBankingMode", ref PPUBankingMode);
SyncPRG();
SyncIRQ();
}
@ -91,11 +192,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
prg_bank_mask_8k = Cart.prg_size / 8 - 1;
chr_bank_mask_1k = Cart.chr_size - 1;
chr_byte_mask = Cart.chr_size * 1024 - 1;
prg_bank_16k = 0;
prg_bank_8k = 0;
SyncPRG();
SetMirrorType(EMirrorType.Vertical);
if (NES.apu != null) // don't start up sound when in configurator
VRC6Sound = new VRC6Alt((uint)NES.cpuclockrate, NES.apu.ExternalQueue);
@ -112,18 +213,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
return ROM[addr];
}
int MapPPU(int addr)
{
int lutidx = addr >> 10 | PPUBankingMode << 4;
int bank = chr_banks_1k[Banks[lutidx]];
if (chrA10replace)
{
bank &= Masks[lutidx];
bank |= A10s[lutidx];
}
return addr & 0x3ff | bank << 10;
}
public override byte ReadPPU(int addr)
{
if (addr < 0x2000)
{
int bank_1k = addr >> 10;
int ofs = addr & ((1 << 10) - 1);
bank_1k = chr_banks_1k[bank_1k];
bank_1k &= chr_bank_mask_1k;
addr = (bank_1k << 10) | ofs;
return VROM[addr];
}
else return base.ReadPPU(addr);
if (addr >= 0x2000 && !NTROM)
return NES.CIRAM[MapPPU(addr) & 0x7ff];
else
return VROM[MapPPU(addr) & chr_byte_mask];
}
public override void WritePPU(int addr, byte value)
{
if (addr >= 0x2000 && !NTROM)
NES.CIRAM[MapPPU(addr) & 0x7ff] = value;
}
public override void WritePRG(int addr, byte value)
@ -141,7 +254,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
prg_bank_16k = value;
SyncPRG();
break;
case 0x1000: //$9000
VRC6Sound.Write9000(value);
break;
@ -174,15 +287,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
case 0x3002: //$B002
VRC6Sound.WriteB002(value);
break;
case 0x3003: //$B003
switch ((value>>2) & 3)
{
case 0: SetMirrorType(NES.NESBoardBase.EMirrorType.Vertical); break;
case 1: SetMirrorType(NES.NESBoardBase.EMirrorType.Horizontal); break;
case 2: SetMirrorType(NES.NESBoardBase.EMirrorType.OneScreenA); break;
case 3: SetMirrorType(NES.NESBoardBase.EMirrorType.OneScreenB); break;
}
PPUBankingMode = value & 15;
NTROM = value.Bit(4);
chrA10replace = value.Bit(5);
break;
case 0x4000: //$C000
@ -204,7 +313,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
case 0x6001: //$E001
case 0x6002: //$E002
case 0x6003: //$E003
chr_banks_1k[4+ addr - 0x6000] = value;
chr_banks_1k[4 + addr - 0x6000] = value;
break;
case 0x7000: //$F000 (reload)
@ -233,7 +342,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
SyncIRQ();
break;
case 0x7002: //$F002 (ack)
irq_pending = false;
irq_enabled = irq_autoen;
@ -275,19 +384,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
}
//public override void ApplyCustomAudio(short[] samples)
//{
/*
short[] fmsamples = new short[samples.Length];
VRC6Sound.GetSamples(fmsamples);
int len = samples.Length;
for (int i = 0; i < len; i++)
{
samples[i] = (short)((samples[i] >> 1) + (fmsamples[i] >> 1));
}
*/
// VRC6Sound.ApplyCustomAudio(samples);
//}
}
}
}