391 lines
12 KiB
C#
391 lines
12 KiB
C#
|
//////////////////////////////////////////////////////////////////////////
|
|||
|
// Mapper045 1000000-in-1 //
|
|||
|
//////////////////////////////////////////////////////////////////////////
|
|||
|
using System;
|
|||
|
using static VirtualNes.Core.CPU;
|
|||
|
using static VirtualNes.MMU;
|
|||
|
using BYTE = System.Byte;
|
|||
|
using INT = System.Int32;
|
|||
|
|
|||
|
|
|||
|
namespace VirtualNes.Core
|
|||
|
{
|
|||
|
public class Mapper045 : Mapper
|
|||
|
{
|
|||
|
BYTE[] reg = new byte[8];
|
|||
|
BYTE patch;
|
|||
|
BYTE prg0, prg1, prg2, prg3;
|
|||
|
BYTE chr0, chr1, chr2, chr3, chr4, chr5, chr6, chr7;
|
|||
|
BYTE[] p = new byte[4];
|
|||
|
INT[] c = new int[8];
|
|||
|
BYTE irq_enable;
|
|||
|
BYTE irq_counter;
|
|||
|
BYTE irq_latch;
|
|||
|
BYTE irq_latched;
|
|||
|
BYTE irq_reset;
|
|||
|
public Mapper045(NES parent) : base(parent)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public override void Reset()
|
|||
|
{
|
|||
|
patch = 0;
|
|||
|
for (INT i = 0; i < 8; i++)
|
|||
|
{
|
|||
|
reg[i] = 0;
|
|||
|
}
|
|||
|
|
|||
|
prg0 = 0;
|
|||
|
prg1 = 1;
|
|||
|
prg2 = (byte)(PROM_8K_SIZE - 2);
|
|||
|
prg3 = (byte)(PROM_8K_SIZE - 1);
|
|||
|
|
|||
|
uint crc = nes.rom.GetPROM_CRC();
|
|||
|
if (crc == 0x58bcacf6 // Kunio 8-in-1 (Pirate Cart)
|
|||
|
|| crc == 0x9103cfd6 // HIK 7-in-1 (Pirate Cart)
|
|||
|
|| crc == 0xc082e6d3)
|
|||
|
{ // Super 8-in-1 (Pirate Cart)
|
|||
|
patch = 1;
|
|||
|
prg2 = 62;
|
|||
|
prg3 = 63;
|
|||
|
}
|
|||
|
if (crc == 0xe0dd259d)
|
|||
|
{ // Super 3-in-1 (Pirate Cart)
|
|||
|
patch = 2;
|
|||
|
}
|
|||
|
SetPROM_32K_Bank(prg0, prg1, prg2, prg3);
|
|||
|
p[0] = prg0;
|
|||
|
p[1] = prg1;
|
|||
|
p[2] = prg2;
|
|||
|
p[3] = prg3;
|
|||
|
|
|||
|
SetVROM_8K_Bank(0);
|
|||
|
|
|||
|
// chr0 = c[0] = 0;
|
|||
|
// chr1 = c[1] = 0
|
|||
|
// chr2 = c[2] = 0;
|
|||
|
// chr3 = c[3] = 0;
|
|||
|
// chr4 = c[4] = 0;
|
|||
|
// chr5 = c[5] = 0;
|
|||
|
// chr6 = c[6] = 0;
|
|||
|
// chr7 = c[7] = 0;
|
|||
|
|
|||
|
c[0] = chr0 = 0;
|
|||
|
c[1] = chr1 = 1;
|
|||
|
c[2] = chr2 = 2;
|
|||
|
c[3] = chr3 = 3;
|
|||
|
c[4] = chr4 = 4;
|
|||
|
c[5] = chr5 = 5;
|
|||
|
c[6] = chr6 = 6;
|
|||
|
c[7] = chr7 = 7;
|
|||
|
|
|||
|
irq_enable = 0;
|
|||
|
irq_counter = 0;
|
|||
|
irq_latch = 0;
|
|||
|
irq_latched = 0;
|
|||
|
irq_reset = 0;
|
|||
|
}
|
|||
|
|
|||
|
//void Mapper045::WriteLow(WORD addr, BYTE data)
|
|||
|
public override void WriteLow(ushort addr, byte data)
|
|||
|
{
|
|||
|
// if( addr == 0x6000 ) {
|
|||
|
// if( addr == 0x6000 && !(reg[3]&0x40) ) {
|
|||
|
if ((reg[3] & 0x40) == 0)
|
|||
|
{
|
|||
|
reg[reg[5]] = data;
|
|||
|
reg[5] = (byte)((reg[5] + 1) & 0x03);
|
|||
|
|
|||
|
SetBank_CPU_4(prg0);
|
|||
|
SetBank_CPU_5(prg1);
|
|||
|
SetBank_CPU_6(prg2);
|
|||
|
SetBank_CPU_7(prg3);
|
|||
|
SetBank_PPU();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//void Mapper045::Write(WORD addr, BYTE data)
|
|||
|
public override void Write(ushort addr, byte data)
|
|||
|
{
|
|||
|
switch (addr & 0xE001)
|
|||
|
{
|
|||
|
case 0x8000:
|
|||
|
if ((data & 0x40) != (reg[6] & 0x40))
|
|||
|
{
|
|||
|
BYTE swp;
|
|||
|
swp = prg0; prg0 = prg2; prg2 = swp;
|
|||
|
swp = p[0]; p[0] = p[2]; p[2] = swp;
|
|||
|
SetBank_CPU_4(p[0]);
|
|||
|
SetBank_CPU_5(p[1]);
|
|||
|
}
|
|||
|
if (VROM_1K_SIZE != 0)
|
|||
|
{
|
|||
|
if ((data & 0x80) != (reg[6] & 0x80))
|
|||
|
{
|
|||
|
INT swp;
|
|||
|
swp = chr4; chr4 = chr0; chr0 = (byte)swp;
|
|||
|
swp = chr5; chr5 = chr1; chr1 = (byte)swp;
|
|||
|
swp = chr6; chr6 = chr2; chr2 = (byte)swp;
|
|||
|
swp = chr7; chr7 = chr3; chr3 = (byte)swp;
|
|||
|
swp = c[4]; c[4] = c[0]; c[0] = swp;
|
|||
|
swp = c[5]; c[5] = c[1]; c[1] = swp;
|
|||
|
swp = c[6]; c[6] = c[2]; c[2] = swp;
|
|||
|
swp = c[7]; c[7] = c[3]; c[3] = swp;
|
|||
|
SetVROM_8K_Bank(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
|
|||
|
}
|
|||
|
}
|
|||
|
reg[6] = data;
|
|||
|
break;
|
|||
|
case 0x8001:
|
|||
|
switch (reg[6] & 0x07)
|
|||
|
{
|
|||
|
case 0x00:
|
|||
|
chr0 = (byte)((data & 0xFE) + 0);
|
|||
|
chr1 = (byte)((data & 0xFE) + 1);
|
|||
|
SetBank_PPU();
|
|||
|
break;
|
|||
|
case 0x01:
|
|||
|
chr2 = (byte)((data & 0xFE) + 0);
|
|||
|
chr3 = (byte)((data & 0xFE) + 1);
|
|||
|
SetBank_PPU();
|
|||
|
break;
|
|||
|
case 0x02:
|
|||
|
chr4 = data;
|
|||
|
SetBank_PPU();
|
|||
|
break;
|
|||
|
case 0x03:
|
|||
|
chr5 = data;
|
|||
|
SetBank_PPU();
|
|||
|
break;
|
|||
|
case 0x04:
|
|||
|
chr6 = data;
|
|||
|
SetBank_PPU();
|
|||
|
break;
|
|||
|
case 0x05:
|
|||
|
chr7 = data;
|
|||
|
SetBank_PPU();
|
|||
|
break;
|
|||
|
case 0x06:
|
|||
|
if ((reg[6] & 0x40) != 0)
|
|||
|
{
|
|||
|
prg2 = (byte)(data & 0x3F);
|
|||
|
SetBank_CPU_6(data);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
prg0 = (byte)(data & 0x3F);
|
|||
|
SetBank_CPU_4(data);
|
|||
|
}
|
|||
|
break;
|
|||
|
case 0x07:
|
|||
|
prg1 = (byte)(data & 0x3F);
|
|||
|
SetBank_CPU_5(data);
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
case 0xA000:
|
|||
|
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
|||
|
else SetVRAM_Mirror(VRAM_VMIRROR);
|
|||
|
break;
|
|||
|
case 0xC000:
|
|||
|
if (patch == 2)
|
|||
|
{
|
|||
|
if (data == 0x29 || data == 0x70)
|
|||
|
data = 0x07;
|
|||
|
}
|
|||
|
irq_latch = data;
|
|||
|
irq_latched = 1;
|
|||
|
if (irq_reset != 0)
|
|||
|
{
|
|||
|
irq_counter = data;
|
|||
|
irq_latched = 0;
|
|||
|
}
|
|||
|
// irq_counter = data;
|
|||
|
break;
|
|||
|
case 0xC001:
|
|||
|
// irq_latch = data;
|
|||
|
irq_counter = irq_latch;
|
|||
|
break;
|
|||
|
case 0xE000:
|
|||
|
irq_enable = 0;
|
|||
|
irq_reset = 1;
|
|||
|
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
|||
|
break;
|
|||
|
case 0xE001:
|
|||
|
irq_enable = 1;
|
|||
|
if (irq_latched != 0)
|
|||
|
{
|
|||
|
irq_counter = irq_latch;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//void Mapper045::HSync(INT scanline)
|
|||
|
public override void HSync(int scanline)
|
|||
|
{
|
|||
|
irq_reset = 0;
|
|||
|
if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON())
|
|||
|
{
|
|||
|
if (irq_counter != 0)
|
|||
|
{
|
|||
|
irq_counter--;
|
|||
|
if (irq_counter == 0)
|
|||
|
{
|
|||
|
if (irq_enable != 0)
|
|||
|
{
|
|||
|
nes.cpu.SetIRQ(IRQ_MAPPER);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void SetBank_CPU_4(INT data)
|
|||
|
{
|
|||
|
data &= (reg[3] & 0x3F) ^ 0xFF;
|
|||
|
data &= 0x3F;
|
|||
|
data |= reg[1];
|
|||
|
SetPROM_8K_Bank(4, data);
|
|||
|
p[0] = (byte)data;
|
|||
|
}
|
|||
|
|
|||
|
void SetBank_CPU_5(INT data)
|
|||
|
{
|
|||
|
data &= (reg[3] & 0x3F) ^ 0xFF;
|
|||
|
data &= 0x3F;
|
|||
|
data |= reg[1];
|
|||
|
SetPROM_8K_Bank(5, data);
|
|||
|
p[1] = (byte)data;
|
|||
|
}
|
|||
|
|
|||
|
void SetBank_CPU_6(INT data)
|
|||
|
{
|
|||
|
data &= (reg[3] & 0x3F) ^ 0xFF;
|
|||
|
data &= 0x3F;
|
|||
|
data |= reg[1];
|
|||
|
SetPROM_8K_Bank(6, data);
|
|||
|
p[2] = (byte)data;
|
|||
|
}
|
|||
|
|
|||
|
void SetBank_CPU_7(INT data)
|
|||
|
{
|
|||
|
data &= (reg[3] & 0x3F) ^ 0xFF;
|
|||
|
data &= 0x3F;
|
|||
|
data |= reg[1];
|
|||
|
SetPROM_8K_Bank(7, data);
|
|||
|
p[3] = (byte)data;
|
|||
|
}
|
|||
|
|
|||
|
void SetBank_PPU()
|
|||
|
{
|
|||
|
BYTE[] table = new byte[] {
|
|||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|||
|
0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF
|
|||
|
};
|
|||
|
|
|||
|
c[0] = chr0;
|
|||
|
c[1] = chr1;
|
|||
|
c[2] = chr2;
|
|||
|
c[3] = chr3;
|
|||
|
c[4] = chr4;
|
|||
|
c[5] = chr5;
|
|||
|
c[6] = chr6;
|
|||
|
c[7] = chr7;
|
|||
|
|
|||
|
for (INT i = 0; i < 8; i++)
|
|||
|
{
|
|||
|
c[i] &= table[reg[2] & 0x0F];
|
|||
|
c[i] |= reg[0] & ((patch != 1) ? 0xFF : 0xC0);
|
|||
|
c[i] += (reg[2] & ((patch != 1) ? 0x10 : 0x30)) << 4;
|
|||
|
}
|
|||
|
|
|||
|
if ((reg[6] & 0x80) != 0)
|
|||
|
{
|
|||
|
SetVROM_8K_Bank(c[4], c[5], c[6], c[7], c[0], c[1], c[2], c[3]);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
SetVROM_8K_Bank(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//void Mapper045::SaveState(LPBYTE ps)
|
|||
|
public override void SaveState(byte[] ps)
|
|||
|
{
|
|||
|
int i;
|
|||
|
for (i = 0; i < 8; i++)
|
|||
|
{
|
|||
|
ps[i] = reg[i];
|
|||
|
}
|
|||
|
for (i = 0; i < 4; i++)
|
|||
|
{
|
|||
|
ps[i + 8] = p[i];
|
|||
|
}
|
|||
|
for (i = 0; i < 8; i++)
|
|||
|
{
|
|||
|
//*(INT*)&ps[i * 4 + 64] = c[i];
|
|||
|
BitConverter.GetBytes(c[i]).CopyTo(ps, i * 4 + 64);
|
|||
|
}
|
|||
|
ps[20] = prg0;
|
|||
|
ps[21] = prg1;
|
|||
|
ps[22] = prg2;
|
|||
|
ps[23] = prg3;
|
|||
|
ps[24] = chr0;
|
|||
|
ps[25] = chr1;
|
|||
|
ps[26] = chr2;
|
|||
|
ps[27] = chr3;
|
|||
|
ps[28] = chr4;
|
|||
|
ps[29] = chr5;
|
|||
|
ps[30] = chr6;
|
|||
|
ps[31] = chr7;
|
|||
|
ps[32] = irq_enable;
|
|||
|
ps[33] = irq_counter;
|
|||
|
ps[34] = irq_latch;
|
|||
|
}
|
|||
|
|
|||
|
//void Mapper045::LoadState(LPBYTE ps)
|
|||
|
public override void LoadState(byte[] ps)
|
|||
|
{
|
|||
|
int i;
|
|||
|
for (i = 0; i < 8; i++)
|
|||
|
{
|
|||
|
reg[i] = ps[i];
|
|||
|
}
|
|||
|
for (i = 0; i < 4; i++)
|
|||
|
{
|
|||
|
p[i] = ps[i + 8];
|
|||
|
}
|
|||
|
for (i = 0; i < 8; i++)
|
|||
|
{
|
|||
|
//c[i] = *(INT*)&ps[i * 4 + 64];
|
|||
|
c[i] = BitConverter.ToInt32(ps, i * 4 + 64);
|
|||
|
}
|
|||
|
prg0 = ps[20];
|
|||
|
prg1 = ps[21];
|
|||
|
prg2 = ps[22];
|
|||
|
prg3 = ps[23];
|
|||
|
chr0 = ps[24];
|
|||
|
chr1 = ps[25];
|
|||
|
chr2 = ps[26];
|
|||
|
chr3 = ps[27];
|
|||
|
chr4 = ps[28];
|
|||
|
chr5 = ps[29];
|
|||
|
chr6 = ps[30];
|
|||
|
chr7 = ps[31];
|
|||
|
irq_enable = ps[32];
|
|||
|
irq_counter = ps[33];
|
|||
|
irq_latch = ps[34];
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public override bool IsStateSave()
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|