////////////////////////////////////////////////////////////////////////// // 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 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]; } } }