using System; namespace BizHawk.Emulation.Computers.Commodore64.MOS { // emulates the PLA // which handles all bank switching public class MOSPLA { // ------------------------------------ public Func PeekBasicRom; public Func PeekCartridgeLo; public Func PeekCartridgeHi; public Func PeekCharRom; public Func PeekCia0; public Func PeekCia1; public Func PeekColorRam; public Func PeekExpansionLo; public Func PeekExpansionHi; public Func PeekKernalRom; public Func PeekMemory; public Func PeekSid; public Func PeekVic; public Action PokeCartridgeLo; public Action PokeCartridgeHi; public Action PokeCia0; public Action PokeCia1; public Action PokeColorRam; public Action PokeExpansionLo; public Action PokeExpansionHi; public Action PokeMemory; public Action PokeSid; public Action PokeVic; public Func ReadBasicRom; public Func ReadCartridgeLo; public Func ReadCartridgeHi; public Func ReadCharen; public Func ReadCharRom; public Func ReadCia0; public Func ReadCia1; public Func ReadColorRam; public Func ReadExpansionLo; public Func ReadExpansionHi; public Func ReadExRom; public Func ReadGame; public Func ReadHiRam; public Func ReadKernalRom; public Func ReadLoRam; public Func ReadMemory; public Func ReadSid; public Func ReadVic; public Action WriteCartridgeLo; public Action WriteCartridgeHi; public Action WriteCia0; public Action WriteCia1; public Action WriteColorRam; public Action WriteExpansionLo; public Action WriteExpansionHi; public Action WriteMemory; public Action WriteSid; public Action WriteVic; // ------------------------------------ private enum PLABank { None, RAM, BasicROM, KernalROM, CharROM, IO, CartridgeLo, CartridgeHi, Vic, Sid, ColorRam, Cia0, Cia1, Expansion0, Expansion1 } private struct PLACpuMap { public PLABank layout1000; public PLABank layout8000; public PLABank layoutA000; public PLABank layoutC000; public PLABank layoutD000; public PLABank layoutE000; } // ------------------------------------ private PLACpuMap map; private bool pinCharenLast; private bool pinExRomLast; private bool pinGameLast; private bool pinHiRamLast; private bool pinLoRamLast; public MOSPLA() { } public void HardReset() { UpdateMap(); } // ------------------------------------ public void ExecutePhase1() { UpdatePins(); } public void ExecutePhase2() { UpdatePins(); } public void UpdatePins() { if ((ReadExRom() != pinExRomLast) || (ReadGame() != pinGameLast) || (ReadLoRam() != pinLoRamLast) || (ReadHiRam() != pinHiRamLast) || (ReadCharen() != pinCharenLast)) { UpdateMap(); } } // ------------------------------------ private void UpdateMap() { bool pinGame = ReadGame(); bool pinExRom = ReadExRom(); bool pinCharen = ReadCharen(); bool pinHiRam = ReadHiRam(); bool pinLoRam = ReadLoRam(); if (pinCharen && pinHiRam && pinLoRam && pinGame && pinExRom) { // 11111 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.BasicROM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.IO; map.layoutE000 = PLABank.KernalROM; } else if (!pinCharen && pinHiRam && pinLoRam && pinGame && pinExRom) { // 01111 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.BasicROM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.CharROM; map.layoutE000 = PLABank.KernalROM; } else if (pinCharen && !pinHiRam && pinLoRam && pinGame) { // 1011X map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.RAM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.IO; map.layoutE000 = PLABank.RAM; } else if (pinCharen && !pinHiRam && pinLoRam && !pinGame && !pinExRom) { // 10100 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.RAM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.IO; map.layoutE000 = PLABank.RAM; } else if (!pinCharen && !pinHiRam && pinLoRam && pinGame) { // 0011X map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.RAM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.CharROM; map.layoutE000 = PLABank.RAM; } else if (!pinCharen && !pinHiRam && pinLoRam && !pinGame && !pinExRom) { // 00100 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.RAM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.RAM; map.layoutE000 = PLABank.RAM; } else if (!pinHiRam && !pinLoRam && pinGame) { // X001X map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.RAM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.RAM; map.layoutE000 = PLABank.RAM; } else if (pinCharen && pinHiRam && !pinLoRam && pinGame) { // 1101X map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.RAM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.IO; map.layoutE000 = PLABank.KernalROM; } else if (pinCharen && !pinHiRam && !pinLoRam && !pinExRom) { // 100X0 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.RAM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.IO; map.layoutE000 = PLABank.KernalROM; } else if (!pinCharen && pinHiRam && !pinLoRam && pinGame) { // 0101X map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.RAM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.CharROM; map.layoutE000 = PLABank.KernalROM; } else if (!pinCharen && !pinHiRam && !pinLoRam && !pinExRom) { // 000X0 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.RAM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.CharROM; map.layoutE000 = PLABank.KernalROM; } else if (pinCharen && pinHiRam && pinLoRam && pinGame && !pinExRom) { // 11110 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.CartridgeLo; map.layoutA000 = PLABank.BasicROM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.IO; map.layoutE000 = PLABank.KernalROM; } else if (!pinCharen && pinHiRam && pinLoRam && pinGame && !pinExRom) { // 01110 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.CartridgeLo; map.layoutA000 = PLABank.BasicROM; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.CharROM; map.layoutE000 = PLABank.KernalROM; } else if (pinCharen && pinHiRam && !pinLoRam && !pinGame && !pinExRom) { // 11000 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.CartridgeHi; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.IO; map.layoutE000 = PLABank.KernalROM; } else if (!pinCharen && pinHiRam && !pinLoRam && !pinGame && !pinExRom) { // 01000 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.RAM; map.layoutA000 = PLABank.CartridgeHi; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.CharROM; map.layoutE000 = PLABank.KernalROM; } else if (pinCharen && pinHiRam && pinLoRam && !pinGame && !pinExRom) { // 11100 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.CartridgeLo; map.layoutA000 = PLABank.CartridgeHi; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.IO; map.layoutE000 = PLABank.KernalROM; } else if (!pinCharen && pinHiRam && pinLoRam && !pinGame && !pinExRom) { // 01100 map.layout1000 = PLABank.RAM; map.layout8000 = PLABank.CartridgeLo; map.layoutA000 = PLABank.CartridgeHi; map.layoutC000 = PLABank.RAM; map.layoutD000 = PLABank.CharROM; map.layoutE000 = PLABank.KernalROM; } else if (!pinGame && pinExRom) { // XXX01 (ultimax) map.layout1000 = PLABank.None; map.layout8000 = PLABank.CartridgeLo; map.layoutA000 = PLABank.None; map.layoutC000 = PLABank.None; map.layoutD000 = PLABank.IO; map.layoutE000 = PLABank.CartridgeHi; } else { throw new Exception("Memory configuration missing from PLA, fix this!"); } pinExRomLast = pinExRom; pinGameLast = pinGame; pinLoRamLast = pinLoRam; pinHiRamLast = pinHiRam; pinCharenLast = pinCharen; } // ------------------------------------ private PLABank Bank(ushort addr) { if (addr < 0x1000) return PLABank.RAM; else if (addr >= 0x1000 && addr < 0x8000) return map.layout1000; else if (addr >= 0x8000 && addr < 0xA000) return map.layout8000; else if (addr >= 0xA000 && addr < 0xC000) return map.layoutA000; else if (addr >= 0xC000 && addr < 0xD000) return map.layoutC000; else if (addr >= 0xD000 && addr < 0xE000) { if (map.layoutD000 == PLABank.IO) { if (addr >= 0xD000 && addr < 0xD400) return PLABank.Vic; else if (addr >= 0xD400 && addr < 0xD800) return PLABank.Sid; else if (addr >= 0xD800 && addr < 0xDC00) return PLABank.ColorRam; else if (addr >= 0xDC00 && addr < 0xDD00) return PLABank.Cia0; else if (addr >= 0xDD00 && addr < 0xDE00) return PLABank.Cia1; else if (addr >= 0xDE00 && addr < 0xDF00) return PLABank.Expansion0; else return PLABank.Expansion1; } else { return map.layoutD000; } } else { return map.layoutE000; } } public byte Peek(int addr) { switch (Bank((ushort)(addr & 0xFFFF))) { case PLABank.BasicROM: return PeekBasicRom(addr); case PLABank.CartridgeHi: return PeekCartridgeHi(addr); case PLABank.CartridgeLo: return PeekCartridgeLo(addr); case PLABank.CharROM: return PeekCharRom(addr); case PLABank.Cia0: return PeekCia0(addr); case PLABank.Cia1: return PeekCia1(addr); case PLABank.ColorRam: return PeekColorRam(addr); case PLABank.Expansion0: return PeekExpansionLo(addr); case PLABank.Expansion1: return PeekExpansionHi(addr); case PLABank.KernalROM: return PeekKernalRom(addr); case PLABank.None: return 0xFF; case PLABank.RAM: return PeekMemory(addr); case PLABank.Sid: return PeekSid(addr); case PLABank.Vic: return PeekVic(addr); } return 0xFF; } public void Poke(int addr, byte val) { switch (Bank((ushort)(addr & 0xFFFF))) { case PLABank.BasicROM: break; case PLABank.CartridgeHi: PokeCartridgeHi(addr, val); break; case PLABank.CartridgeLo: PokeCartridgeLo(addr, val); break; case PLABank.CharROM: break; case PLABank.Cia0: PokeCia0(addr, val); break; case PLABank.Cia1: PokeCia1(addr, val); break; case PLABank.ColorRam: PokeColorRam(addr, val); break; case PLABank.Expansion0: PokeExpansionLo(addr, val); break; case PLABank.Expansion1: PokeExpansionHi(addr, val); break; case PLABank.KernalROM: break; case PLABank.None: break; case PLABank.RAM: PokeMemory(addr, val); break; case PLABank.Sid: PokeSid(addr, val); break; case PLABank.Vic: PokeVic(addr, val); break; } } public byte Read(ushort addr) { switch (Bank(addr)) { case PLABank.BasicROM: return ReadBasicRom(addr); case PLABank.CartridgeHi: return ReadCartridgeHi(addr); case PLABank.CartridgeLo: return ReadCartridgeLo(addr); case PLABank.CharROM: return ReadCharRom(addr); case PLABank.Cia0: return ReadCia0(addr); case PLABank.Cia1: return ReadCia1(addr); case PLABank.ColorRam: return ReadColorRam(addr); case PLABank.Expansion0: return ReadExpansionLo(addr); case PLABank.Expansion1: return ReadExpansionHi(addr); case PLABank.KernalROM: return ReadKernalRom(addr); case PLABank.RAM: return ReadMemory(addr); case PLABank.Sid: return ReadSid(addr); case PLABank.Vic: return ReadVic(addr); } return 0xFF; } public void SyncState(Serializer ser) { ser.Sync("pinCharenLast", ref pinCharenLast); ser.Sync("pinExRomLast", ref pinExRomLast); ser.Sync("pinGameLast", ref pinGameLast); ser.Sync("pinHiRamLast", ref pinHiRamLast); ser.Sync("pinLoRamLast", ref pinLoRamLast); if (ser.IsReader) UpdateMap(); } public void Write(ushort addr, byte val) { switch (Bank(addr)) { case PLABank.BasicROM: break; case PLABank.CartridgeHi: WriteCartridgeHi(addr, val); break; case PLABank.CartridgeLo: WriteCartridgeLo(addr, val); break; case PLABank.CharROM: break; case PLABank.Cia0: WriteCia0(addr, val); break; case PLABank.Cia1: WriteCia1(addr, val); break; case PLABank.ColorRam: WriteColorRam(addr, val); break; case PLABank.Expansion0: WriteExpansionLo(addr, val); break; case PLABank.Expansion1: WriteExpansionHi(addr, val); break; case PLABank.KernalROM: break; case PLABank.None: break; case PLABank.RAM: // RAM is written through anyway, don't do it here break; case PLABank.Sid: WriteSid(addr, val); break; case PLABank.Vic: WriteVic(addr, val); break; } WriteMemory(addr, val); } } }