using System; using System.Collections.Generic; using System.IO; using System.Linq; using BizHawk.Common; using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge { public abstract class CartridgeDevice : IDriveLight { public Func ReadOpenBus; public static CartridgeDevice Load(byte[] crtFile) { using (var mem = new MemoryStream(crtFile)) { var reader = new BinaryReader(mem); if (new string(reader.ReadChars(16)) != "C64 CARTRIDGE ") { return null; } var chipAddress = new List(); var chipBank = new List(); var chipData = new List(); var chipType = new List(); var headerLength = ReadCRTInt(reader); var version = ReadCRTShort(reader); var mapper = ReadCRTShort(reader); var exrom = reader.ReadByte() != 0; var game = reader.ReadByte() != 0; // reserved reader.ReadBytes(6); // cartridge name reader.ReadBytes(0x20); // skip extra header bytes if (headerLength > 0x40) { reader.ReadBytes(headerLength - 0x40); } // read chips while (reader.PeekChar() >= 0) { if (new string(reader.ReadChars(4)) != "CHIP") { break; } var chipLength = ReadCRTInt(reader); chipType.Add(ReadCRTShort(reader)); chipBank.Add(ReadCRTShort(reader)); chipAddress.Add(ReadCRTShort(reader)); var chipDataLength = ReadCRTShort(reader); chipData.Add(reader.ReadBytes(chipDataLength).Select(x => (int)x).ToArray()); chipLength -= chipDataLength + 0x10; if (chipLength > 0) { reader.ReadBytes(chipLength); } } if (chipData.Count <= 0) { return null; } CartridgeDevice result; switch (mapper) { case 0x0000: // Standard Cartridge result = new Mapper0000(chipAddress, chipData, game, exrom); break; case 0x0001: // Action Replay (4.2 and up) result = new Mapper0001(chipAddress, chipBank, chipData); break; case 0x0005: // Ocean result = new Mapper0005(chipAddress, chipBank, chipData); break; case 0x0007: // Fun Play result = new Mapper0007(chipData, game, exrom); break; case 0x0008: // SuperGame result = new Mapper0008(chipData); break; case 0x000A: // Epyx FastLoad result = new Mapper000A(chipData); break; case 0x000B: // Westermann Learning result = new Mapper000B(chipAddress, chipData); break; case 0x000F: // C64 Game System / System 3 result = new Mapper000F(chipAddress, chipBank, chipData); break; case 0x0011: // Dinamic result = new Mapper0011(chipAddress, chipBank, chipData); break; case 0x0012: // Zaxxon / Super Zaxxon result = new Mapper0012(chipAddress, chipBank, chipData); break; case 0x0013: // Domark result = new Mapper0013(chipAddress, chipBank, chipData); break; case 0x0020: // EasyFlash result = new Mapper0020(chipAddress, chipBank, chipData); break; case 0x002B: // Prophet 64 result = new Mapper002B(chipAddress, chipBank, chipData); break; default: throw new Exception("This cartridge file uses an unrecognized mapper: " + mapper); } result.HardReset(); return result; } } private static int ReadCRTShort(BinaryReader reader) { return (reader.ReadByte() << 8) | reader.ReadByte(); } private static int ReadCRTInt(BinaryReader reader) { return (reader.ReadByte() << 24) | (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte(); } protected bool pinExRom; protected bool pinGame; protected bool pinIRQ; protected bool pinNMI; protected bool pinReset; protected bool validCartridge; public virtual void ExecutePhase() { } public bool ExRom => pinExRom; public bool Game => pinGame; public virtual void HardReset() { pinIRQ = true; pinNMI = true; pinReset = true; } public bool IRQ => pinIRQ; public bool NMI => pinNMI; public virtual int Peek8000(int addr) { return ReadOpenBus(); } public virtual int PeekA000(int addr) { return ReadOpenBus(); } public virtual int PeekDE00(int addr) { return ReadOpenBus(); } public virtual int PeekDF00(int addr) { return ReadOpenBus(); } public virtual void Poke8000(int addr, int val) { } public virtual void PokeA000(int addr, int val) { } public virtual void PokeDE00(int addr, int val) { } public virtual void PokeDF00(int addr, int val) { } public virtual int Read8000(int addr) { return ReadOpenBus(); } public virtual int ReadA000(int addr) { return ReadOpenBus(); } public virtual int ReadDE00(int addr) { return ReadOpenBus(); } public virtual int ReadDF00(int addr) { return ReadOpenBus(); } public bool Reset => pinReset; protected abstract void SyncStateInternal(Serializer ser); public virtual void SyncState(Serializer ser) { ser.Sync(nameof(pinExRom), ref pinExRom); ser.Sync(nameof(pinGame), ref pinGame); ser.Sync(nameof(pinIRQ), ref pinIRQ); ser.Sync(nameof(pinNMI), ref pinNMI); ser.Sync(nameof(pinReset), ref pinReset); ser.Sync(nameof(_driveLightEnabled), ref _driveLightEnabled); ser.Sync(nameof(_driveLightOn), ref _driveLightOn); } public bool Valid => validCartridge; public virtual void Write8000(int addr, int val) { } public virtual void WriteA000(int addr, int val) { } public virtual void WriteDE00(int addr, int val) { } public virtual void WriteDF00(int addr, int val) { } private bool _driveLightEnabled; private bool _driveLightOn; public bool DriveLightEnabled { get { return _driveLightEnabled; } protected set { _driveLightEnabled = value; } } public bool DriveLightOn { get { return _driveLightOn; } protected set { _driveLightOn = value; } } } }