补充Mapper from VirtuaNESex

This commit is contained in:
sin365 2024-09-11 16:04:55 +08:00
parent 2510b2b7e3
commit a66835c091
10 changed files with 719 additions and 3 deletions

View File

@ -1,4 +1,5 @@
using VirtualNes.Core;
using Codice.CM.Client.Differences;
using VirtualNes.Core;
namespace VirtualNes
{
@ -84,6 +85,20 @@ namespace VirtualNes
CPU_MEM_PAGE[page] = 0;
}
internal static void SetPROM_4K_Bank(ushort addr, int bank)
{
throw new System.NotImplementedException();
bank %= (PROM_8K_SIZE * 2);
//TODO
//memcpy(&CPU_MEM_BANK[addr >> 13][addr & 0x1FFF], PROM + 0x1000 * bank, 0x1000);
//// memcpy( &CPU_MEM_BANK[addr>>13][addr&0x1FFF], YSRAM+0x1000*bank, 0x1000);
CPU_MEM_TYPE[addr >> 13] = BANKTYPE_ROM;
CPU_MEM_PAGE[addr >> 13] = 0;
}
internal static void SetPROM_8K_Bank(byte page, int bank)
{
bank %= PROM_8K_SIZE;

View File

@ -1,4 +1,6 @@
namespace VirtualNes.Core
using System;
namespace VirtualNes.Core
{
public class Mapper004 : Mapper
{
@ -569,5 +571,49 @@
MMU.SetPROM_32K_Bank(prg0, prg1, MMU.PROM_8K_SIZE - 2, MMU.PROM_8K_SIZE - 1);
}
}
public override void SaveState(byte[] p)
{
for (int i = 0; i < 8; i++)
{
p[i] = reg[i];
}
p[8] = prg0;
p[9] = prg1;
p[10] = chr01;
p[11] = chr23;
p[12] = chr4;
p[13] = chr5;
p[14] = chr6;
p[15] = chr7;
p[16] = irq_enable;
p[17] = (byte)irq_counter;
p[18] = irq_latch;
p[19] = irq_request;
p[20] = irq_preset;
p[21] = irq_preset_vbl;
}
public override void LoadState(byte[] p)
{
for (int i = 0; i < 8; i++)
{
reg[i] = p[i];
}
prg0 = p[8];
prg1 = p[9];
chr01 = p[10];
chr23 = p[11];
chr4 = p[12];
chr5 = p[13];
chr6 = p[14];
chr7 = p[15];
irq_enable = p[16];
irq_counter = (Byte)p[17];
irq_latch = p[18];
irq_request = p[19];
irq_preset = p[20];
irq_preset_vbl = p[21];
}
}
}

View File

@ -0,0 +1,198 @@
//////////////////////////////////////////////////////////////////////////
// Mapper052 Konami VRC2 type B //
//////////////////////////////////////////////////////////////////////////
using VirtualNes.Core.Debug;
using static VirtualNes.MMU;
using BYTE = System.Byte;
using INT = System.Int32;
namespace VirtualNes.Core
{
public class Mapper052 : Mapper
{
BYTE[] reg = new byte[9];
BYTE irq_enable;
BYTE irq_counter;
BYTE irq_latch;
INT irq_clock;
public Mapper052(NES parent) : base(parent) { }
public override void Reset()
{
for (byte i = 0; i < 8; i++)
{
reg[i] = i;
}
reg[8] = 0;
irq_enable = 0;
irq_counter = 0;
irq_latch = 0;
irq_clock = 0;
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
SetVROM_8K_Bank(0);
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
// nes->SetRenderMethod( NES::POST_RENDER );
// nes->SetRenderMethod( NES::POST_ALL_RENDER );
}
public override void Write(ushort addr, byte data)
{
if (addr >= 0xf000) Debuger.Log($"MPRWR A={addr & 0xFFFF} D={data & 0xFF} L={nes.GetScanline()} CYC={nes.cpu.GetTotalCycles()}\n");
if (addr >= 0xf000) Debuger.Log($"MPRWR A={addr & 0xFFFF} RAM={RAM[0x1c0] & 0xFF} L={nes.GetScanline()} CYC={nes.cpu.GetTotalCycles()}\n");
switch (addr & 0xFFFF)
{
case 0x8000:
if (reg[8] != 0) SetPROM_8K_Bank(6, data);
else SetPROM_8K_Bank(4, data);
break;
case 0x9002:
reg[8] = (byte)(data & 0x02);
break;
case 0x9004:
data &= 0x03;
if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR);
else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR);
else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L);
else SetVRAM_Mirror(VRAM_MIRROR4H);
break;
case 0xA000:
SetPROM_8K_Bank(5, data);
break;
case 0xB000:
reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F));
SetVROM_1K_Bank(0, reg[0]);
break;
case 0xB001:
reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4));
SetVROM_1K_Bank(0, reg[0]);
break;
case 0xB002:
reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F));
SetVROM_1K_Bank(1, reg[1]);
break;
case 0xB003:
reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4));
SetVROM_1K_Bank(1, reg[1]);
break;
case 0xC000:
reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F));
SetVROM_1K_Bank(2, reg[2]);
break;
case 0xC001:
reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4));
SetVROM_1K_Bank(2, reg[2]);
break;
case 0xC002:
reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F));
SetVROM_1K_Bank(3, reg[3]);
break;
case 0xC003:
reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4));
SetVROM_1K_Bank(3, reg[3]);
break;
case 0xD000:
reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F));
SetVROM_1K_Bank(4, reg[4]);
break;
case 0xD001:
reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4));
SetVROM_1K_Bank(4, reg[4]);
break;
case 0xD002:
reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F));
SetVROM_1K_Bank(5, reg[5]);
break;
case 0xD003:
reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4));
SetVROM_1K_Bank(5, reg[5]);
break;
case 0xE000:
reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F));
SetVROM_1K_Bank(6, reg[6]);
break;
case 0xE001:
reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4));
SetVROM_1K_Bank(6, reg[6]);
break;
case 0xE002:
reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F));
SetVROM_1K_Bank(7, reg[7]);
break;
case 0xE003:
reg[7] = (byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4));
SetVROM_1K_Bank(7, reg[7]);
break;
case 0xF000:
break;
case 0xF004:
case 0xFF04:
RAM[0x7f8] = 1;
break;
case 0xF008:
case 0xFF08:
irq_enable = 1;
// irq_latch = ((RAM[0x7f8]*2)+0x11)^0xFF; //Akumajou Special - Boku Dracula-kun
irq_latch = (byte)(((RAM[0x1c0] * 2) + 0x11) ^ 0xFF); //Teenage Mutant Ninja Turtles
irq_counter = irq_latch;
irq_clock = 0;
nes.cpu.ClrIRQ(CPU.IRQ_MAPPER);
break;
case 0xF00C:
irq_enable = 0;
nes.cpu.ClrIRQ(CPU.IRQ_MAPPER);
break;
}
}
public override void HSync(int scanline)
{
//
}
public override void Clock(int cycles)
{
if (irq_enable != null)
{
irq_clock += cycles * 3;
while (irq_clock >= 341)
{
irq_clock -= 341;
irq_counter++;
if (irq_counter == 0)
{
irq_counter = irq_latch;
nes.cpu.SetIRQ(CPU.IRQ_MAPPER);
}
}
}
}
public override void SaveState(byte[] p)
{
//
}
public override void LoadState(byte[] p)
{
//
}
public override bool IsStateSave()
{
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bbcd8b5589ac667448a1be830aac1d4a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,166 @@
//////////////////////////////////////////////////////////////////////////
// Mapper168 Subor (PPUExtLatch) //
//////////////////////////////////////////////////////////////////////////
using VirtualNes.Core.Debug;
using static VirtualNes.MMU;
using BYTE = System.Byte;
using INT = System.Int32;
namespace VirtualNes.Core
{
public class Mapper168 : Mapper
{
byte reg5000, reg5200, reg5300;
byte PPU_SW, NT_data;
byte Rom_Type;
public Mapper168(NES parent) : base(parent) { }
public override bool IsStateSave()
{
return true;
}
public override void Reset()
{
reg5000 = 0;
reg5200 = 0;
reg5300 = 0;
PPU_SW = 0;
NT_data = 0;
nes.ppu.SetExtLatchMode(true);
SetPROM_16K_Bank(4, 0);
SetPROM_16K_Bank(6, 0);
Rom_Type = 0;
uint crc = nes.rom.GetPROM_CRC();
if (crc == 0x0A9808AE) //[Subor] Karaoke (C)
{
Rom_Type = 1;
SetPROM_32K_Bank(0);
nes.SetVideoMode(2 != 0);
}
if (crc == 0x12D61CE8) //[Subor] Subor V11.0 (C)
{
Rom_Type = 2;
}
}
public override byte ReadLow(ushort addr)
{
if (addr == 0x5300) return 0x8F; //返回0x8F跳过真人语音发声有关的程序段
return base.ReadLow(addr);
}
public override void WriteLow(ushort addr, byte data)
{
if (addr == 0x5000)
{
reg5000 = data;
SetBank_CPU();
}
else if (addr == 0x5200)
{
reg5200 = (byte)(data & 0x7);
SetBank_CPU();
}
else if (addr == 0x5300)
{
reg5300 = data;
}
else if (addr >= 0x6000)
{
CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data;
}
}
public override void Write(ushort addr, byte data)
{
if (Rom_Type == 1)
{ //[Subor] Karaoke (C)
SetPROM_32K_Bank(data & 0x1F);
if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
else SetVRAM_Mirror(VRAM_VMIRROR);
if ((data & 0xC0) != 0) PPU_SW = 1;
else PPU_SW = 0;
}
}
void SetBank_CPU()
{
if (reg5200 < 4) SetPROM_16K_Bank(4, reg5000);
else SetPROM_32K_Bank(reg5000);
switch (reg5200)
{
case 0:
SetVRAM_Mirror(VRAM_VMIRROR);
PPU_SW = 0;
break;
case 2:
SetVRAM_Mirror(VRAM_VMIRROR);
PPU_SW = 1;
break;
case 1:
case 3:
SetVRAM_Mirror(VRAM_HMIRROR);
PPU_SW = 0;
break;
case 5:
if (reg5000 == 4 && Rom_Type == 2)
{ //Special for [Subor] Subor V11.0 (C) - Tank (坦克大战)
nes.ppu.SetExtLatchMode(false);
SetVRAM_Mirror(VRAM_HMIRROR);
}
break;
}
}
public override void PPU_Latch(ushort addr)
{
if ((addr & 0xF000) == 0x2000)
{
NT_data = (byte)((addr >> 8) & 0x03);
}
}
public override void PPU_ExtLatch(ushort ntbladr, ref byte chr_l, ref byte chr_h, ref byte attr)
{
INT loopy_v = nes.ppu.GetPPUADDR();
INT loopy_y = nes.ppu.GetTILEY();
INT tileofs = (PPUREG[0] & PPU.PPU_BGTBL_BIT) << 8;
INT attradr = 0x23C0 + (loopy_v & 0x0C00) + ((loopy_v & 0x0380) >> 4);
INT attrsft = (ntbladr & 0x0040) >> 4;
ArrayRef<byte> pNTBL = PPU_MEM_BANK[ntbladr >> 10];
INT ntbl_x = ntbladr & 0x001F;
INT tileadr, ntb;
ntb = (ntbladr >> 10) & 3;
if (ntb == 2)
tileofs |= 0x1000;
else if (ntb != 0 && PPU_SW != 0)
tileofs |= 0x1000;
else
tileofs |= 0x0000;
attradr &= 0x3FF;
attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2);
tileadr = tileofs + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y;
chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF];
chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8];
}
public override void SaveState(byte[] p)
{
p[0] = reg5000;
p[1] = reg5200;
}
public override void LoadState(byte[] p)
{
reg5000 = p[0];
reg5200 = p[1];
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f468229e616f1cd41aba98252208fbf7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,244 @@
//////////////////////////////////////////////////////////////////////////
// Mapper173 Subor //
//////////////////////////////////////////////////////////////////////////
using Codice.CM.Client.Differences;
using VirtualNes.Core.Debug;
using static VirtualNes.MMU;
using BYTE = System.Byte;
using INT = System.Int32;
namespace VirtualNes.Core
{
public class Mapper173 : Mapper
{
BYTE[] reg = new BYTE[10];
INT irq_counter, irq_latch;
BYTE irq_enable, irq_repeat;
BYTE irq_occur;
public Mapper173(NES parent) : base(parent) { }
public override bool IsStateSave()
{
return true;
}
public override void Reset()
{
// nes.ppu.SetExtLatchMode( TRUE );
for (INT i = 0; i < 11; i++) reg[i] = 0x00;
irq_enable = irq_repeat = 0;
irq_counter = irq_latch = 0;
irq_occur = 0;
SetPROM_32K_Bank(0);
nes.SetVideoMode(2 != 0);
}
public override byte ExRead(ushort addr)
{
Debuger.Log($"ExRead - addr= {addr}\n");
return 0x00;
switch (addr)
{
case 0x4026:
//
break;
case 0x4033:
//D7:
//D6:
//D5:
//D4:
//D3:
//D2:
//D1:
//D0:
//
break;
case 0x4204: //FDC主状态寄存器(STATUS)
//
break;
case 0x4205: //FDC数据寄存器(DATA)(读???)
//
break;
}
}
public override void ExWrite(ushort addr, byte data)
{
Debuger.Log($"ExWrite - addr= {addr} ; dat= {data}\n");
switch (addr)
{
case 0x4020:
reg[0] = data;
break;
case 0x4022:
reg[1] = data;
break;
case 0x4023:
reg[2] = data;
break;
case 0x4026:
reg[3] = data;
break;
case 0x4031:
reg[4] = data;
break;
case 0x4032:
reg[5] = data;
irq_repeat = (byte)(data & 0x01);
irq_enable = (byte)(data & 0x02);
irq_occur = 0;
if (irq_enable!= null)
{
irq_counter = irq_latch;
}
else
{
nes.cpu.ClrIRQ(CPU.IRQ_MAPPER);
}
break;
case 0x4034:
reg[6] = data;
irq_latch = (irq_latch & 0xFF00) | data;
break;
case 0x4035:
reg[7] = data;
irq_latch = (irq_latch & 0x00FF) | ((ushort)data << 8);
break;
case 0x4040:
SetPROM_4K_Bank(0x8000, data & 0x7F);
break;
case 0x4041:
SetPROM_4K_Bank(0x9000, data & 0x7F);
break;
case 0x4042:
SetPROM_4K_Bank(0xa000, data & 0x7F);
break;
case 0x4043:
SetPROM_4K_Bank(0xb000, data & 0x7F);
break;
case 0x4044:
SetPROM_4K_Bank(0xc000, data & 0x7F);
break;
case 0x4045:
SetPROM_4K_Bank(0xd000, data & 0x7F);
break;
case 0x4046:
SetPROM_4K_Bank(0xe000, data & 0x7F);
break;
case 0x4047:
SetPROM_4K_Bank(0xf000, data & 0x7F);
break;
case 0x4205: //FDC数据寄存器(DATA)(写???)
//
break;
}
}
public override byte ReadLow(ushort addr)
{
// DEBUGOUT( "ReadLow - addr= %04x\n", addr );
return CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
}
public override void WriteLow(ushort addr, byte data)
{
// DEBUGOUT( "WriteLow - addr= %04x ; dat= %03x\n", addr, data );
CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data;
}
public override void Write(ushort addr, byte data)
{
// DEBUGOUT( "Write - addr= %04x ; dat= %03x\n", addr, data );
}
public override void HSync(int scanline)
{
// if( (scanline >= 0 && scanline <= 239) ) {
// if( nes.ppu.IsDispON() ) {
// if( irq_enable ) {
// irq_enable = 0;
/// nes.cpu.SetIRQ( IRQ_MAPPER );
// }
// }
// }
}
public override void Clock(int cycles)
{
if (irq_enable!= 0)
{
irq_counter -= cycles;
if (irq_counter <= 0)
{
//// irq_counter &= 0xFFFF;
irq_counter += irq_latch;
if (irq_occur == 0)
{
irq_occur = 0xFF;
if (irq_repeat == 0)
{
irq_enable = 0;
}
nes.cpu.SetIRQ(CPU.IRQ_MAPPER);
}
}
}
}
public override void PPU_Latch(ushort addr)
{
//
}
public override void PPU_ExtLatch(ushort ntbladr, ref byte chr_l, ref byte chr_h, ref byte attr)
{
INT loopy_v = nes.ppu.GetPPUADDR();
INT loopy_y = nes.ppu.GetTILEY();
INT tileofs = (PPUREG[0] & PPU.PPU_BGTBL_BIT) << 8;
INT attradr = 0x23C0 + (loopy_v & 0x0C00) + ((loopy_v & 0x0380) >> 4);
INT attrsft = (ntbladr & 0x0040) >> 4;
ArrayRef<byte> pNTBL = PPU_MEM_BANK[ntbladr >> 10];
INT ntbl_x = ntbladr & 0x001F;
INT tileadr, ntb;
ntb = (ntbladr >> 10) & 3;
if (ntb == 2)
tileofs |= 0x1000;
// else if(ntb && PPU_SW)
tileofs |= 0x1000;
// else
tileofs |= 0x0000;
attradr &= 0x3FF;
attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2);
tileadr = tileofs + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y;
chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF];
chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8];
}
public override void SaveState(byte[] p)
{
//
}
public override void LoadState(byte[] p)
{
//
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c98a3c4204201144eb348e0d2f1f7076
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -60,6 +60,16 @@ namespace VirtualNes.Core
}
}
public override void LoadState(byte[] p)
{
//
}
public override void SaveState(byte[] p)
{
//
}
}

View File

@ -1,4 +1,8 @@
namespace VirtualNes.Core
using VirtualNes.Core.Debug;
using static VirtualNes.MMU;
using BYTE = System.Byte;
using INT = System.Int32;
namespace VirtualNes.Core
{
public class _Mapper : Mapper
{