635 lines
16 KiB
C#
635 lines
16 KiB
C#
using System.IO;
|
|
|
|
namespace MyNes.Core
|
|
{
|
|
[BoardInfo("Pirate MMC5-style", 90)]
|
|
[HassIssues]
|
|
internal class Mapper090 : Board
|
|
{
|
|
protected bool MAPPER90MODE;
|
|
|
|
private int[] prg_reg;
|
|
|
|
private int[] chr_reg;
|
|
|
|
private int[] nt_reg;
|
|
|
|
private int prg_mode;
|
|
|
|
private int chr_mode;
|
|
|
|
private bool chr_block_mode;
|
|
|
|
private int chr_block;
|
|
|
|
private bool chr_m;
|
|
|
|
private bool flag_s;
|
|
|
|
private int irqCounter;
|
|
|
|
private bool IrqEnable;
|
|
|
|
private bool irqCountDownMode;
|
|
|
|
private bool irqCountUpMode;
|
|
|
|
private bool irqFunkyMode;
|
|
|
|
private bool irqPrescalerSize;
|
|
|
|
private int irqSource;
|
|
|
|
private int irqPrescaler;
|
|
|
|
private int irqPrescalerXOR;
|
|
|
|
private byte irqFunkyModeReg;
|
|
|
|
private byte Dipswitch;
|
|
|
|
private byte multiplication_a;
|
|
|
|
private byte multiplication_b;
|
|
|
|
private ushort multiplication;
|
|
|
|
private byte RAM5803;
|
|
|
|
private bool nt_advanced_enable;
|
|
|
|
private bool nt_rom_only;
|
|
|
|
private int nt_ram_select;
|
|
|
|
internal override string Issues => MNInterfaceLanguage.IssueMapper90;
|
|
|
|
internal override void HardReset()
|
|
{
|
|
base.HardReset();
|
|
MAPPER90MODE = true;
|
|
prg_reg = new int[4];
|
|
chr_reg = new int[8];
|
|
nt_reg = new int[4];
|
|
prg_mode = (chr_mode = 0);
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
prg_reg[i] = i;
|
|
nt_reg[i] = i;
|
|
}
|
|
for (int j = 0; j < 8; j++)
|
|
{
|
|
chr_reg[j] = j;
|
|
}
|
|
SetupPRG();
|
|
SetupCHR();
|
|
Dipswitch = 0;
|
|
irqCounter = 0;
|
|
IrqEnable = false;
|
|
irqCountDownMode = false;
|
|
irqCountUpMode = false;
|
|
irqFunkyMode = false;
|
|
irqPrescalerSize = false;
|
|
irqSource = 0;
|
|
irqPrescaler = 0;
|
|
irqPrescalerXOR = 0;
|
|
irqFunkyModeReg = 0;
|
|
RAM5803 = 0;
|
|
flag_s = false;
|
|
multiplication_a = 0;
|
|
multiplication_b = 0;
|
|
multiplication = 0;
|
|
}
|
|
|
|
internal override void SoftReset()
|
|
{
|
|
base.SoftReset();
|
|
if (Dipswitch == 0)
|
|
{
|
|
Dipswitch = byte.MaxValue;
|
|
}
|
|
else
|
|
{
|
|
Dipswitch = 0;
|
|
}
|
|
}
|
|
|
|
internal override void WritePRG(ref ushort address, ref byte data)
|
|
{
|
|
switch (address & 0xF007)
|
|
{
|
|
case 32768:
|
|
case 32769:
|
|
case 32770:
|
|
case 32771:
|
|
case 32772:
|
|
case 32773:
|
|
case 32774:
|
|
case 32775:
|
|
prg_reg[address & 3] = data & 0x7F;
|
|
SetupPRG();
|
|
break;
|
|
case 36864:
|
|
case 36865:
|
|
case 36866:
|
|
case 36867:
|
|
case 36868:
|
|
case 36869:
|
|
case 36870:
|
|
case 36871:
|
|
chr_reg[address & 7] = (chr_reg[address & 7] & 0xFF00) | data;
|
|
SetupCHR();
|
|
break;
|
|
case 40960:
|
|
case 40961:
|
|
case 40962:
|
|
case 40963:
|
|
case 40964:
|
|
case 40965:
|
|
case 40966:
|
|
case 40967:
|
|
chr_reg[address & 7] = (chr_reg[address & 7] & 0xFF) | (data << 8);
|
|
SetupCHR();
|
|
break;
|
|
case 45056:
|
|
case 45057:
|
|
case 45058:
|
|
case 45059:
|
|
nt_reg[address & 3] = (nt_reg[address & 3] & 0xFF00) | data;
|
|
break;
|
|
case 45060:
|
|
case 45061:
|
|
case 45062:
|
|
case 45063:
|
|
nt_reg[address & 3] = (nt_reg[address & 3] & 0xFF) | (data << 8);
|
|
break;
|
|
case 49152:
|
|
IrqEnable = (data & 1) == 1;
|
|
if (!IrqEnable)
|
|
{
|
|
NesEmu.IRQFlags &= -9;
|
|
}
|
|
break;
|
|
case 49153:
|
|
irqCountDownMode = (data & 0x80) == 128;
|
|
irqCountUpMode = (data & 0x40) == 64;
|
|
irqFunkyMode = (data & 8) == 8;
|
|
irqPrescalerSize = (data & 4) == 4;
|
|
irqSource = data & 3;
|
|
break;
|
|
case 49154:
|
|
IrqEnable = false;
|
|
NesEmu.IRQFlags &= -9;
|
|
break;
|
|
case 49155:
|
|
IrqEnable = true;
|
|
break;
|
|
case 49156:
|
|
irqPrescaler = data ^ irqPrescalerXOR;
|
|
break;
|
|
case 49157:
|
|
irqCounter = data ^ irqPrescalerXOR;
|
|
break;
|
|
case 49158:
|
|
irqPrescalerXOR = data;
|
|
break;
|
|
case 49159:
|
|
irqFunkyModeReg = data;
|
|
break;
|
|
case 53248:
|
|
flag_s = (data & 0x80) == 128;
|
|
prg_mode = data & 7;
|
|
chr_mode = (data >> 3) & 3;
|
|
nt_advanced_enable = (data & 0x20) == 32;
|
|
nt_rom_only = (data & 0x40) == 64;
|
|
SetupPRG();
|
|
SetupCHR();
|
|
break;
|
|
case 53249:
|
|
switch (data & 3)
|
|
{
|
|
case 0:
|
|
Switch01KNMTFromMirroring(Mirroring.Vert);
|
|
break;
|
|
case 1:
|
|
Switch01KNMTFromMirroring(Mirroring.Horz);
|
|
break;
|
|
case 2:
|
|
Switch01KNMTFromMirroring(Mirroring.OneScA);
|
|
break;
|
|
case 3:
|
|
Switch01KNMTFromMirroring(Mirroring.OneScB);
|
|
break;
|
|
}
|
|
break;
|
|
case 53250:
|
|
nt_ram_select = data & 0x80;
|
|
break;
|
|
case 53251:
|
|
chr_m = (data & 0x80) == 128;
|
|
chr_block_mode = (data & 0x20) == 32;
|
|
chr_block = (data & 0x1F) << 8;
|
|
SetupCHR();
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal override void WriteSRM(ref ushort address, ref byte data)
|
|
{
|
|
}
|
|
|
|
internal override void ReadSRM(ref ushort address, out byte data)
|
|
{
|
|
if (flag_s)
|
|
{
|
|
base.ReadSRM(ref address, out data);
|
|
}
|
|
else
|
|
{
|
|
data = 0;
|
|
}
|
|
}
|
|
|
|
internal override void ReadEX(ref ushort address, out byte data)
|
|
{
|
|
switch (address)
|
|
{
|
|
case 20480:
|
|
data = Dipswitch;
|
|
break;
|
|
case 22528:
|
|
data = (byte)(multiplication & 0xFFu);
|
|
break;
|
|
case 22529:
|
|
data = (byte)((multiplication & 0xFF00) >> 8);
|
|
break;
|
|
case 22531:
|
|
data = RAM5803;
|
|
break;
|
|
default:
|
|
data = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal override void WriteEX(ref ushort address, ref byte data)
|
|
{
|
|
switch (address)
|
|
{
|
|
case 22528:
|
|
multiplication_a = data;
|
|
multiplication = (ushort)(multiplication_a * multiplication_b);
|
|
break;
|
|
case 22529:
|
|
multiplication_b = data;
|
|
multiplication = (ushort)(multiplication_a * multiplication_b);
|
|
break;
|
|
case 22531:
|
|
RAM5803 = data;
|
|
break;
|
|
case 22530:
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal override void ReadNMT(ref ushort address, out byte data)
|
|
{
|
|
if (MAPPER90MODE)
|
|
{
|
|
data = NMT_RAM[NMT_AREA_BLK_INDEX[(address >> 10) & 3]][address & 0x3FF];
|
|
}
|
|
if (!nt_advanced_enable)
|
|
{
|
|
data = NMT_RAM[NMT_AREA_BLK_INDEX[(address >> 10) & 3]][address & 0x3FF];
|
|
}
|
|
else if (nt_rom_only)
|
|
{
|
|
data = CHR_ROM[nt_reg[(address >> 10) & 3]][address & 0x3FF];
|
|
}
|
|
else if ((nt_reg[(address >> 10) & 3] & 0x80) != nt_ram_select)
|
|
{
|
|
data = CHR_ROM[nt_reg[(address >> 10) & 3]][address & 0x3FF];
|
|
}
|
|
else
|
|
{
|
|
data = NMT_RAM[nt_reg[(address >> 10) & 3] & 1][address & 0x3FF];
|
|
}
|
|
}
|
|
|
|
internal override void WriteNMT(ref ushort address, ref byte data)
|
|
{
|
|
if (MAPPER90MODE)
|
|
{
|
|
NMT_RAM[NMT_AREA_BLK_INDEX[(address >> 10) & 3]][address & 0x3FF] = data;
|
|
}
|
|
else if (!nt_advanced_enable)
|
|
{
|
|
NMT_RAM[NMT_AREA_BLK_INDEX[(address >> 10) & 3]][address & 0x3FF] = data;
|
|
}
|
|
else if (!nt_rom_only && (nt_reg[(address >> 10) & 3] & 0x80) == nt_ram_select)
|
|
{
|
|
NMT_RAM[nt_reg[(address >> 10) & 3] & 1][address & 0x3FF] = data;
|
|
}
|
|
}
|
|
|
|
private void SetupPRG()
|
|
{
|
|
switch (prg_mode)
|
|
{
|
|
case 0:
|
|
Switch08KPRG(prg_reg[3] * 4 + 3, PRGArea.Area6000);
|
|
Switch32KPRG(PRG_ROM_32KB_Mask, PRGArea.Area8000);
|
|
break;
|
|
case 1:
|
|
Switch08KPRG(prg_reg[3] * 2 + 1, PRGArea.Area6000);
|
|
Switch16KPRG(prg_reg[1], PRGArea.Area8000);
|
|
Switch16KPRG(PRG_ROM_16KB_Mask, PRGArea.AreaC000);
|
|
break;
|
|
case 2:
|
|
Switch08KPRG(prg_reg[3], PRGArea.Area6000);
|
|
Switch08KPRG(prg_reg[0], PRGArea.Area8000);
|
|
Switch08KPRG(prg_reg[1], PRGArea.AreaA000);
|
|
Switch08KPRG(prg_reg[2], PRGArea.AreaC000);
|
|
Switch08KPRG(PRG_ROM_08KB_Mask, PRGArea.AreaE000);
|
|
break;
|
|
case 3:
|
|
Switch08KPRG(ReverseByte(prg_reg[3]), PRGArea.Area6000);
|
|
Switch08KPRG(ReverseByte(prg_reg[0]), PRGArea.Area8000);
|
|
Switch08KPRG(ReverseByte(prg_reg[1]), PRGArea.AreaA000);
|
|
Switch08KPRG(ReverseByte(prg_reg[2]), PRGArea.AreaC000);
|
|
Switch08KPRG(PRG_ROM_16KB_Mask, PRGArea.AreaE000);
|
|
break;
|
|
case 4:
|
|
Switch08KPRG(prg_reg[3] * 4 + 3, PRGArea.Area6000);
|
|
Switch32KPRG(prg_reg[3], PRGArea.Area8000);
|
|
break;
|
|
case 5:
|
|
Switch08KPRG(prg_reg[3] * 2 + 1, PRGArea.Area6000);
|
|
Switch16KPRG(prg_reg[1], PRGArea.Area8000);
|
|
Switch16KPRG(prg_reg[3], PRGArea.AreaC000);
|
|
break;
|
|
case 6:
|
|
Switch08KPRG(prg_reg[3], PRGArea.Area6000);
|
|
Switch08KPRG(prg_reg[0], PRGArea.Area8000);
|
|
Switch08KPRG(prg_reg[1], PRGArea.AreaA000);
|
|
Switch08KPRG(prg_reg[2], PRGArea.AreaC000);
|
|
Switch08KPRG(prg_reg[3], PRGArea.AreaE000);
|
|
break;
|
|
case 7:
|
|
Switch08KPRG(ReverseByte(prg_reg[3]), PRGArea.Area6000);
|
|
Switch08KPRG(ReverseByte(prg_reg[0]), PRGArea.Area8000);
|
|
Switch08KPRG(ReverseByte(prg_reg[1]), PRGArea.AreaA000);
|
|
Switch08KPRG(ReverseByte(prg_reg[2]), PRGArea.AreaC000);
|
|
Switch08KPRG(ReverseByte(prg_reg[3]), PRGArea.AreaE000);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void SetupCHR()
|
|
{
|
|
switch (chr_mode)
|
|
{
|
|
case 0:
|
|
if (chr_block_mode)
|
|
{
|
|
Switch08KCHR(chr_reg[0]);
|
|
}
|
|
else
|
|
{
|
|
Switch08KCHR((chr_reg[0] & 0xFF) | chr_block);
|
|
}
|
|
break;
|
|
case 1:
|
|
if (chr_block_mode)
|
|
{
|
|
Switch04KCHR(chr_reg[0], CHRArea.Area0000);
|
|
Switch04KCHR(chr_reg[4], CHRArea.Area1000);
|
|
}
|
|
else
|
|
{
|
|
Switch04KCHR((chr_reg[0] & 0xFF) | chr_block, CHRArea.Area0000);
|
|
Switch04KCHR((chr_reg[4] & 0xFF) | chr_block, CHRArea.Area1000);
|
|
}
|
|
break;
|
|
case 2:
|
|
if (chr_block_mode)
|
|
{
|
|
Switch02KCHR(chr_reg[0], CHRArea.Area0000);
|
|
Switch02KCHR(chr_m ? chr_reg[0] : chr_reg[2], CHRArea.Area0800);
|
|
Switch02KCHR(chr_reg[4], CHRArea.Area1000);
|
|
Switch02KCHR(chr_reg[6], CHRArea.Area1800);
|
|
}
|
|
else
|
|
{
|
|
Switch02KCHR((chr_reg[0] & 0xFF) | chr_block, CHRArea.Area0000);
|
|
Switch02KCHR(((chr_m ? chr_reg[0] : chr_reg[2]) & 0xFF) | chr_block, CHRArea.Area0800);
|
|
Switch02KCHR((chr_reg[4] & 0xFF) | chr_block, CHRArea.Area1000);
|
|
Switch02KCHR((chr_reg[6] & 0xFF) | chr_block, CHRArea.Area1800);
|
|
}
|
|
break;
|
|
case 3:
|
|
if (chr_block_mode)
|
|
{
|
|
Switch01KCHR(chr_reg[0], CHRArea.Area0000);
|
|
Switch01KCHR(chr_reg[1], CHRArea.Area0400);
|
|
Switch01KCHR(chr_m ? chr_reg[0] : chr_reg[2], CHRArea.Area0800);
|
|
Switch01KCHR(chr_m ? chr_reg[1] : chr_reg[3], CHRArea.Area0C00);
|
|
Switch01KCHR(chr_reg[4], CHRArea.Area1000);
|
|
Switch01KCHR(chr_reg[5], CHRArea.Area1400);
|
|
Switch01KCHR(chr_reg[6], CHRArea.Area1800);
|
|
Switch01KCHR(chr_reg[7], CHRArea.Area1C00);
|
|
}
|
|
else
|
|
{
|
|
Switch01KCHR((chr_reg[0] & 0xFF) | chr_block, CHRArea.Area0000);
|
|
Switch01KCHR((chr_reg[1] & 0xFF) | chr_block, CHRArea.Area0400);
|
|
Switch01KCHR(((chr_m ? chr_reg[0] : chr_reg[2]) & 0xFF) | chr_block, CHRArea.Area0800);
|
|
Switch01KCHR(((chr_m ? chr_reg[1] : chr_reg[3]) & 0xFF) | chr_block, CHRArea.Area0C00);
|
|
Switch01KCHR((chr_reg[4] & 0xFF) | chr_block, CHRArea.Area1000);
|
|
Switch01KCHR((chr_reg[5] & 0xFF) | chr_block, CHRArea.Area1400);
|
|
Switch01KCHR((chr_reg[6] & 0xFF) | chr_block, CHRArea.Area1800);
|
|
Switch01KCHR((chr_reg[7] & 0xFF) | chr_block, CHRArea.Area1C00);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private byte ReverseByte(int value)
|
|
{
|
|
return (byte)((uint)(((value & 0x40) >> 6) | ((value & 0x20) >> 4) | ((value & 0x10) >> 2)) | ((uint)value & 8u) | (uint)((value & 4) << 2) | (uint)((value & 2) << 4) | (uint)((value & 1) << 6));
|
|
}
|
|
|
|
internal override void OnCPUClock()
|
|
{
|
|
if (irqSource != 0)
|
|
{
|
|
return;
|
|
}
|
|
if (irqPrescalerSize)
|
|
{
|
|
irqPrescaler = (irqPrescaler & 0xF8) | (((irqPrescaler & 7) + 1) & 7);
|
|
if ((irqPrescaler & 7) == 7)
|
|
{
|
|
ClockIRQCounter();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
irqPrescaler++;
|
|
if (irqPrescaler == 255)
|
|
{
|
|
ClockIRQCounter();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override void OnPPUAddressUpdate(ref ushort address)
|
|
{
|
|
if (irqSource != 1)
|
|
{
|
|
return;
|
|
}
|
|
old_vram_address = new_vram_address;
|
|
new_vram_address = address & 0x1000;
|
|
if (old_vram_address >= new_vram_address)
|
|
{
|
|
return;
|
|
}
|
|
if (irqPrescalerSize)
|
|
{
|
|
irqPrescaler = (irqPrescaler & 0xF8) | (((irqPrescaler & 7) + 1) & 7);
|
|
if ((irqPrescaler & 7) == 7)
|
|
{
|
|
ClockIRQCounter();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
irqPrescaler++;
|
|
if (irqPrescaler == 255)
|
|
{
|
|
ClockIRQCounter();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ClockIRQCounter()
|
|
{
|
|
if (irqCountDownMode && irqCountUpMode)
|
|
{
|
|
return;
|
|
}
|
|
if (irqCountDownMode)
|
|
{
|
|
irqCounter--;
|
|
if (irqCounter == 0)
|
|
{
|
|
irqCounter = 255;
|
|
if (IrqEnable)
|
|
{
|
|
NesEmu.IRQFlags |= 8;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!irqCountUpMode)
|
|
{
|
|
return;
|
|
}
|
|
irqCounter++;
|
|
if (irqCounter == 255)
|
|
{
|
|
irqCounter = 0;
|
|
if (IrqEnable)
|
|
{
|
|
NesEmu.IRQFlags |= 8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override void WriteStateData(ref BinaryWriter stream)
|
|
{
|
|
base.WriteStateData(ref stream);
|
|
for (int i = 0; i < prg_reg.Length; i++)
|
|
{
|
|
stream.Write(prg_reg[i]);
|
|
}
|
|
for (int j = 0; j < chr_reg.Length; j++)
|
|
{
|
|
stream.Write(chr_reg[j]);
|
|
}
|
|
for (int k = 0; k < nt_reg.Length; k++)
|
|
{
|
|
stream.Write(nt_reg[k]);
|
|
}
|
|
stream.Write(prg_mode);
|
|
stream.Write(chr_mode);
|
|
stream.Write(chr_block_mode);
|
|
stream.Write(chr_block);
|
|
stream.Write(chr_m);
|
|
stream.Write(flag_s);
|
|
stream.Write(irqCounter);
|
|
stream.Write(IrqEnable);
|
|
stream.Write(irqCountDownMode);
|
|
stream.Write(irqCountUpMode);
|
|
stream.Write(irqFunkyMode);
|
|
stream.Write(irqPrescalerSize);
|
|
stream.Write(irqSource);
|
|
stream.Write(irqPrescaler);
|
|
stream.Write(irqPrescalerXOR);
|
|
stream.Write(irqFunkyModeReg);
|
|
stream.Write(Dipswitch);
|
|
stream.Write(multiplication_a);
|
|
stream.Write(multiplication_b);
|
|
stream.Write(multiplication);
|
|
stream.Write(RAM5803);
|
|
stream.Write(nt_advanced_enable);
|
|
stream.Write(nt_rom_only);
|
|
stream.Write(nt_ram_select);
|
|
}
|
|
|
|
internal override void ReadStateData(ref BinaryReader stream)
|
|
{
|
|
base.ReadStateData(ref stream);
|
|
for (int i = 0; i < prg_reg.Length; i++)
|
|
{
|
|
prg_reg[i] = stream.ReadInt32();
|
|
}
|
|
for (int j = 0; j < chr_reg.Length; j++)
|
|
{
|
|
chr_reg[j] = stream.ReadInt32();
|
|
}
|
|
for (int k = 0; k < nt_reg.Length; k++)
|
|
{
|
|
nt_reg[k] = stream.ReadInt32();
|
|
}
|
|
prg_mode = stream.ReadInt32();
|
|
chr_mode = stream.ReadInt32();
|
|
chr_block_mode = stream.ReadBoolean();
|
|
chr_block = stream.ReadInt32();
|
|
chr_m = stream.ReadBoolean();
|
|
flag_s = stream.ReadBoolean();
|
|
irqCounter = stream.ReadInt32();
|
|
IrqEnable = stream.ReadBoolean();
|
|
irqCountDownMode = stream.ReadBoolean();
|
|
irqCountUpMode = stream.ReadBoolean();
|
|
irqFunkyMode = stream.ReadBoolean();
|
|
irqPrescalerSize = stream.ReadBoolean();
|
|
irqSource = stream.ReadInt32();
|
|
irqPrescaler = stream.ReadInt32();
|
|
irqPrescalerXOR = stream.ReadInt32();
|
|
irqFunkyModeReg = stream.ReadByte();
|
|
Dipswitch = stream.ReadByte();
|
|
multiplication_a = stream.ReadByte();
|
|
multiplication_b = stream.ReadByte();
|
|
multiplication = stream.ReadUInt16();
|
|
RAM5803 = stream.ReadByte();
|
|
nt_advanced_enable = stream.ReadBoolean();
|
|
nt_rom_only = stream.ReadBoolean();
|
|
nt_ram_select = stream.ReadInt32();
|
|
}
|
|
}
|
|
}
|