////////////////////////////////////////////////////////////////////////// // 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(WRAM, 0x2000), BANKTYPE_RAM); } else { SetPROM_Bank(3, new ArrayRef(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(WRAM, 0x0000), BANKTYPE_RAM); } else { SetPROM_Bank(3, new ArrayRef(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; } } }