////////////////////////////////////////////////////////////////////////// // Mapper019 Namcot 106 // ////////////////////////////////////////////////////////////////////////// using System; using static VirtualNes.Core.CPU; using static VirtualNes.MMU; using BYTE = System.Byte; namespace VirtualNes.Core { public class Mapper019 : Mapper { BYTE patch; BYTE exsound_enable; BYTE[] reg = new byte[3]; BYTE[] exram = new byte[128]; BYTE irq_enable; ushort irq_counter; public Mapper019(NES parent) : base(parent) { } public override void Reset() { patch = 0; reg[0] = reg[1] = reg[2] = 0; MemoryUtility.ZEROMEMORY(exram, exram.Length); irq_enable = 0; irq_counter = 0; SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); if (VROM_1K_SIZE >= 8) { SetVROM_8K_Bank(VROM_8K_SIZE - 1); } exsound_enable = 0xFF; uint crc = nes.rom.GetPROM_CRC(); if (crc == 0xb62a7b71) { // Family Circuit '91(J) patch = 1; } if (crc == 0x02738c68) { // Wagan Land 2(J) patch = 3; } if (crc == 0x14942c06) { // Wagan Land 3(J) patch = 2; } if (crc == 0x968dcf09) { // Final Lap(J) nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER); } if (crc == 0x3deac303) { // Rolling Thunder(J) nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); } if (crc == 0xb1b9e187) { // For Kaijuu Monogatari(J) nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); } if (crc == 0x6901346e) { // For Sangokushi 2 - Haou no Tairiku(J) nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); } // if( crc == 0xdd454208 ) { // Hydlide 3(J) // nes.SetRenderMethod( NES::PRE_ALL_RENDER ); // } if (crc == 0xaf15338f // For Mindseeker(J) || crc == 0xb1b9e187 // For Kaijuu Monogatari(J) || crc == 0x96533999 // Dokuganryuu Masamune(J) // || crc == 0x2b825ce1 // Namco Classic(J) // || crc == 0x9a2b0641 // Namco Classic 2(J) || crc == 0x3296ff7a // Battle Fleet(J) || crc == 0xdd454208) { // Hydlide 3(J) exsound_enable = 0; } if (crc == 0x429fd177) { // Famista '90(J) exsound_enable = 0; } if (exsound_enable != 0) { nes.apu.SelectExSound(0x10); } } //BYTE Mapper019::ReadLow(WORD addr) public override byte ReadLow(ushort addr) { BYTE data = 0; switch (addr & 0xF800) { case 0x4800: if (addr == 0x4800) { if (exsound_enable != 0) { nes.apu.ExRead(addr); data = exram[reg[2] & 0x7F]; } else { data = WRAM[reg[2] & 0x7F]; } if ((reg[2] & 0x80) != 0) reg[2] = (byte)((reg[2] + 1) | 0x80); return data; } break; case 0x5000: return (byte)((BYTE)irq_counter & 0x00FF); case 0x5800: return (BYTE)((irq_counter >> 8) & 0x7F); case 0x6000: case 0x6800: case 0x7000: case 0x7800: return base.ReadLow(addr); } return (BYTE)(addr >> 8); } //void Mapper019::WriteLow(WORD addr, BYTE data) public override void WriteLow(ushort addr, byte data) { switch (addr & 0xF800) { case 0x4800: if (addr == 0x4800) { if (exsound_enable != 0) { nes.apu.ExWrite(addr, data); exram[reg[2] & 0x7F] = data; } else { WRAM[reg[2] & 0x7F] = data; } if ((reg[2] & 0x80) != 0) reg[2] = (byte)((reg[2] + 1) | 0x80); } break; case 0x5000: irq_counter = (byte)((irq_counter & 0xFF00) | (ushort)data); // if( irq_enable ) { // irq_counter++; // } nes.cpu.ClrIRQ(IRQ_MAPPER); break; case 0x5800: irq_counter = (byte)((irq_counter & 0x00FF) | ((ushort)(data & 0x7F) << 8)); irq_enable = (byte)(data & 0x80); // if( irq_enable ) { // irq_counter++; // } // if( !irq_enable ) { // nes.cpu.ClrIRQ( IRQ_MAPPER ); // } nes.cpu.ClrIRQ(IRQ_MAPPER); break; case 0x6000: case 0x6800: case 0x7000: case 0x7800: base.WriteLow(addr, data); break; } } //void Mapper019::Write(WORD addr, BYTE data) public override void Write(ushort addr, byte data) { //if( addr >= 0xC000 ) { //DEBUGOUT( "W %04X %02X L:%3d\n", addr, data, nes.GetScanline() ); //} switch (addr & 0xF800) { case 0x8000: if ((data < 0xE0) || (reg[0] != 0)) { SetVROM_1K_Bank(0, data); } else { SetCRAM_1K_Bank(0, data & 0x1F); } break; case 0x8800: if ((data < 0xE0) || (reg[0] != 0)) { SetVROM_1K_Bank(1, data); } else { SetCRAM_1K_Bank(1, data & 0x1F); } break; case 0x9000: if ((data < 0xE0) || (reg[0] != 0)) { SetVROM_1K_Bank(2, data); } else { SetCRAM_1K_Bank(2, data & 0x1F); } break; case 0x9800: if ((data < 0xE0) || (reg[0] != 0)) { SetVROM_1K_Bank(3, data); } else { SetCRAM_1K_Bank(3, data & 0x1F); } break; case 0xA000: if ((data < 0xE0) || (reg[1] != 0)) { SetVROM_1K_Bank(4, data); } else { SetCRAM_1K_Bank(4, data & 0x1F); } break; case 0xA800: if ((data < 0xE0) || (reg[1] != 0)) { SetVROM_1K_Bank(5, data); } else { SetCRAM_1K_Bank(5, data & 0x1F); } break; case 0xB000: if ((data < 0xE0) || (reg[1] != 0)) { SetVROM_1K_Bank(6, data); } else { SetCRAM_1K_Bank(6, data & 0x1F); } break; case 0xB800: if ((data < 0xE0) || (reg[1] != 0)) { SetVROM_1K_Bank(7, data); } else { SetCRAM_1K_Bank(7, data & 0x1F); } break; case 0xC000: if (patch == 0) { if (data <= 0xDF) { SetVROM_1K_Bank(8, data); } else { SetVRAM_1K_Bank(8, data & 0x01); } } break; case 0xC800: if (patch == 0) { if (data <= 0xDF) { SetVROM_1K_Bank(9, data); } else { SetVRAM_1K_Bank(9, data & 0x01); } } break; case 0xD000: if (patch == 0) { if (data <= 0xDF) { SetVROM_1K_Bank(10, data); } else { SetVRAM_1K_Bank(10, data & 0x01); } } break; case 0xD800: if (patch == 0) { if (data <= 0xDF) { SetVROM_1K_Bank(11, data); } else { SetVRAM_1K_Bank(11, data & 0x01); } } break; case 0xE000: SetPROM_8K_Bank(4, data & 0x3F); if (patch == 2) { if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_VMIRROR); else SetVRAM_Mirror(VRAM_MIRROR4L); } if (patch == 3) { if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_HMIRROR); else SetVRAM_Mirror(VRAM_VMIRROR); } break; case 0xE800: reg[0] = (byte)(data & 0x40); reg[1] = (byte)(data & 0x80); SetPROM_8K_Bank(5, data & 0x3F); break; case 0xF000: SetPROM_8K_Bank(6, data & 0x3F); break; case 0xF800: if (addr == 0xF800) { if (exsound_enable != 0) { nes.apu.ExWrite(addr, data); } reg[2] = data; } break; } } //void Mapper019::Clock(INT cycles) public override void Clock(int cycles) { if (irq_enable != 0) { irq_counter = (ushort)(irq_counter + cycles); if (irq_counter >= 0x7FFF) { // irq_counter = 0x7FFF; // nes.cpu.IRQ_NotPending(); irq_enable = 0; // irq_counter &= 0x7FFF; irq_counter = 0x7FFF; nes.cpu.SetIRQ(IRQ_MAPPER); } } } //void Mapper019::SaveState(LPBYTE p) public override void SaveState(byte[] p) { p[0] = reg[0]; p[1] = reg[1]; p[2] = reg[2]; p[3] = irq_enable; //*(WORD*)&p[4] = irq_counter; BitConverter.GetBytes(irq_counter).CopyTo(p, 4); //::memcpy(&p[8], exram, sizeof(exram)); Array.Copy(exram, p, exram.Length); } //void Mapper019::LoadState(LPBYTE p) public override void LoadState(byte[] p) { reg[0] = p[0]; reg[1] = p[1]; reg[2] = p[2]; irq_enable = p[3]; //irq_counter = *(WORD*)&p[4]; irq_counter = BitConverter.ToUInt16(p, 4); //::memcpy(exram, &p[8], sizeof(exram)); Array.Copy(p, exram, exram.Length); } public override bool IsStateSave() { return true; } } }