AxibugEmuOnline/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper001.cs
2024-11-13 18:42:30 +08:00

410 lines
14 KiB
C#

//////////////////////////////////////////////////////////////////////////
// Mapper001 Nintendo MMC1 //
//////////////////////////////////////////////////////////////////////////
using static VirtualNes.MMU;
using BYTE = System.Byte;
using INT = System.Int32;
namespace VirtualNes.Core
{
public class Mapper001 : Mapper
{
ushort last_addr;
BYTE patch;
BYTE wram_patch;
BYTE wram_bank;
BYTE wram_count;
BYTE[] reg = new byte[4];
BYTE shift, regbuf;
public Mapper001(NES parent) : base(parent) { }
public override void Reset()
{
reg[0] = 0x0C; // D3=1,D2=1
reg[1] = reg[2] = reg[3] = 0;
shift = regbuf = 0;
patch = 0;
wram_patch = 0;
if (PROM_16K_SIZE < 32)
{
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
}
else
{
// For 512K/1M byte Cartridge
SetPROM_16K_Bank(4, 0);
SetPROM_16K_Bank(6, 16 - 1);
patch = 1;
}
if (VROM_8K_SIZE != 0)
{
// SetVROM_8K_Bank( 0 );
}
uint crc = nes.rom.GetPROM_CRC();
if (crc == 0xb8e16bd0)
{ // Snow Bros.(J)
patch = 2;
}
// if( crc == 0x9b565541 ) { // Triathron, The(J)
// nes.SetFrameIRQmode( FALSE );
// }
if (crc == 0xc96c6f04)
{ // Venus Senki(J)
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
}
// if( crc == 0x5e3f7004 ) { // Softball Tengoku(J)
// }
if (crc == 0x4d2edf70)
{ // Night Rider(J)
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
}
if (crc == 0xcd2a73f0)
{ // Pirates!(U)
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
patch = 2;
}
// if( crc == 0x09efe54b ) { // Majaventure - Mahjong Senki(J)
// nes.SetFrameIRQmode( FALSE );
// }
if (crc == 0x11469ce3)
{ // Viva! Las Vegas(J)
}
if (crc == 0xd878ebf5)
{ // Ninja Ryukenden(J)
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
}
// if( crc == 0x7bd7b849 ) { // Nekketsu Koukou - Dodgeball Bu(J)
// }
if (crc == 0x466efdc2)
{ // Final Fantasy(J)
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
nes.ppu.SetExtMonoMode(true);
}
if (crc == 0xc9556b36)
{ // Final Fantasy I&II(J)
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
nes.ppu.SetExtMonoMode(true);
nes.SetSAVERAM_SIZE(16 * 1024);
wram_patch = 2;
}
if (crc == 0x717e1169)
{ // Cosmic Wars(J)
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
}
if (crc == 0xC05D2034)
{ // Snake's Revenge(U)
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
}
if (crc == 0xb8747abf // Best Play - Pro Yakyuu Special(J)
|| crc == 0x29449ba9 // Nobunaga no Yabou - Zenkoku Ban(J)
|| crc == 0x2b11e0b0 // Nobunaga no Yabou - Zenkoku Ban(J)(alt)
|| crc == 0x4642dda6 // Nobunaga's Ambition(U)
|| crc == 0xfb69743a // Aoki Ookami to Shiroki Mejika - Genghis Khan(J)
|| crc == 0x2225c20f // Genghis Khan(U)
|| crc == 0xabbf7217 // Sangokushi(J)
)
{
nes.SetSAVERAM_SIZE(16 * 1024);
wram_patch = 1;
wram_bank = 0;
wram_count = 0;
}
}
//void Mapper001::Write(WORD addr, BYTE data)
public override void Write(ushort addr, byte data)
{
// DEBUGOUT( "MMC1 %04X=%02X\n", addr&0xFFFF,data&0xFF );
if (wram_patch == 1 && addr == 0xBFFF)
{
wram_count++;
wram_bank += (byte)(data & 0x01);
if (wram_count == 5)
{
if (wram_bank != 0)
{
SetPROM_Bank(3, new ArrayRef<byte>(WRAM, 0x2000), BANKTYPE_RAM);
}
else
{
SetPROM_Bank(3, new ArrayRef<byte>(WRAM, 0x0000), BANKTYPE_RAM);
}
wram_bank = wram_count = 0;
}
}
if (patch != 1)
{
if ((addr & 0x6000) != (last_addr & 0x6000))
{
shift = regbuf = 0;
}
last_addr = addr;
}
if ((data & 0x80) != 0)
{
shift = regbuf = 0;
// reg[0] = 0x0C; // D3=1,D2=1
reg[0] |= 0x0C; // D3=1,D2=1 残りはリセットされない
return;
}
if ((data & 0x01) != 0) regbuf |= (byte)(1 << shift);
if (++shift < 5)
return;
addr = (ushort)((addr & 0x7FFF) >> 13);
reg[addr] = regbuf;
// DEBUGOUT( "MMC1 %d=%02X\n", addr&0xFFFF,regbuf&0xFF );
regbuf = 0;
shift = 0;
if (patch != 1)
{
// For Normal Cartridge
switch (addr)
{
case 0:
if ((reg[0] & 0x02) != 0)
{
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
else SetVRAM_Mirror(VRAM_VMIRROR);
}
else
{
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
else SetVRAM_Mirror(VRAM_MIRROR4L);
}
break;
case 1:
// Register #1
if (VROM_1K_SIZE != 0)
{
if ((reg[0] & 0x10) != 0)
{
// CHR 4K bank lower($0000-$0FFF)
SetVROM_4K_Bank(0, reg[1]);
// CHR 4K bank higher($1000-$1FFF)
SetVROM_4K_Bank(4, reg[2]);
}
else
{
// CHR 8K bank($0000-$1FFF)
SetVROM_8K_Bank(reg[1] >> 1);
}
}
else
{
// For Romancia
if ((reg[0] & 0x10) != 0)
{
SetCRAM_4K_Bank(0, reg[1]);
}
}
break;
case 2:
// Register #2
if (VROM_1K_SIZE != 0)
{
if ((reg[0] & 0x10) != 0)
{
// CHR 4K bank lower($0000-$0FFF)
SetVROM_4K_Bank(0, reg[1]);
// CHR 4K bank higher($1000-$1FFF)
SetVROM_4K_Bank(4, reg[2]);
}
else
{
// CHR 8K bank($0000-$1FFF)
SetVROM_8K_Bank(reg[1] >> 1);
}
}
else
{
// For Romancia
if ((reg[0] & 0x10) != 0)
{
SetCRAM_4K_Bank(4, reg[2]);
}
}
break;
case 3:
if (!((reg[0] & 0x08) != 0))
{
// PRG 32K bank ($8000-$FFFF)
SetPROM_32K_Bank(reg[3] >> 1);
}
else
{
if ((reg[0] & 0x04) != 0)
{
// PRG 16K bank ($8000-$BFFF)
SetPROM_16K_Bank(4, reg[3]);
SetPROM_16K_Bank(6, PROM_16K_SIZE - 1);
}
else
{
// PRG 16K bank ($C000-$FFFF)
SetPROM_16K_Bank(6, reg[3]);
SetPROM_16K_Bank(4, 0);
}
}
break;
}
}
else
{
// For 512K/1M byte Cartridge
INT PROM_BASE = 0;
if (PROM_16K_SIZE >= 32)
{
PROM_BASE = reg[1] & 0x10;
}
// For FinalFantasy I&II
if (wram_patch == 2)
{
if (((reg[1] & 0x18) == 0))
{
SetPROM_Bank(3, new ArrayRef<byte>(WRAM, 0x0000), BANKTYPE_RAM);
}
else
{
SetPROM_Bank(3, new ArrayRef<byte>(WRAM, 0x2000), BANKTYPE_RAM);
}
}
// Register #0
if (addr == 0)
{
if ((reg[0] & 0x02) != 0)
{
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
else SetVRAM_Mirror(VRAM_VMIRROR);
}
else
{
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
else SetVRAM_Mirror(VRAM_MIRROR4L);
}
}
// Register #1
if (VROM_1K_SIZE != 0)
{
if ((reg[0] & 0x10) != 0)
{
// CHR 4K bank lower($0000-$0FFF)
SetVROM_4K_Bank(0, reg[1]);
}
else
{
// CHR 8K bank($0000-$1FFF)
SetVROM_8K_Bank(reg[1] >> 1);
}
}
else
{
// For Romancia
if ((reg[0] & 0x10) != 0)
{
SetCRAM_4K_Bank(0, reg[1]);
}
}
// Register #2
if (VROM_1K_SIZE != 0)
{
if ((reg[0] & 0x10) != 0)
{
// CHR 4K bank higher($1000-$1FFF)
SetVROM_4K_Bank(4, reg[2]);
}
}
else
{
// For Romancia
if ((reg[0] & 0x10) != 0)
{
SetCRAM_4K_Bank(4, reg[2]);
}
}
// Register #3
if (((reg[0] & 0x08) == 0))
{
// PRG 32K bank ($8000-$FFFF)
SetPROM_32K_Bank((reg[3] & (0xF + PROM_BASE)) >> 1);
}
else
{
if ((reg[0] & 0x04) != 0)
{
// PRG 16K bank ($8000-$BFFF)
SetPROM_16K_Bank(4, PROM_BASE + (reg[3] & 0x0F));
if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(6, PROM_BASE + 16 - 1);
}
else
{
// PRG 16K bank ($C000-$FFFF)
SetPROM_16K_Bank(6, PROM_BASE + (reg[3] & 0x0F));
if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(4, PROM_BASE);
}
}
}
}
//void Mapper001::SaveState(LPBYTE p)
public override void SaveState(byte[] p)
{
p[0] = reg[0];
p[1] = reg[1];
p[2] = reg[2];
p[3] = reg[3];
p[4] = shift;
p[5] = regbuf;
p[6] = wram_bank;
p[7] = wram_count;
}
//void Mapper001::LoadState(LPBYTE p)
public override void LoadState(byte[] p)
{
reg[0] = p[0];
reg[1] = p[1];
reg[2] = p[2];
reg[3] = p[3];
shift = p[4];
regbuf = p[5];
wram_bank = p[6];
wram_count = p[7];
}
public override bool IsStateSave()
{
return true;
}
}
}