using BizHawk.Common; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Computers.Commodore64.Cartridge; using BizHawk.Emulation.Cores.Computers.Commodore64.Cassette; using BizHawk.Emulation.Cores.Computers.Commodore64.MOS; using BizHawk.Emulation.Cores.Computers.Commodore64.Serial; using BizHawk.Emulation.Cores.Computers.Commodore64.User; namespace BizHawk.Emulation.Cores.Computers.Commodore64 { /// /// Contains the onboard chipset and glue. /// public sealed partial class Motherboard { // chips public readonly Chip23128 BasicRom; public readonly Chip23128 CharRom; public readonly Cia Cia0; public readonly Cia Cia1; public readonly Chip2114 ColorRam; public readonly Chip6510 Cpu; public readonly Chip23128 KernalRom; public readonly Chip90611401 Pla; public readonly Chip4864 Ram; public readonly Sid Sid; public readonly Vic Vic; // ports public readonly CartridgePort CartPort; public readonly CassettePort Cassette; public IController Controller = NullController.Instance; public readonly SerialPort Serial; public readonly TapeDrive TapeDrive; public readonly UserPort User; // devices public readonly Drive1541 DiskDrive; // state public bool InputRead; public bool Irq; public bool Nmi; private readonly C64 _c64; public Motherboard(C64 c64, C64.VicType initRegion, C64.BorderType borderType, C64.SidType sidType, C64.TapeDriveType tapeDriveType, C64.DiskDriveType diskDriveType) { // note: roms need to be added on their own externally _c64 = c64; int clockNum, clockDen; switch (initRegion) { case C64.VicType.Pal: clockNum = 17734475; clockDen = 18; break; case C64.VicType.Ntsc: clockNum = 14318181; clockDen = 14; break; case C64.VicType.NtscOld: clockNum = 11250000; clockDen = 11; break; case C64.VicType.Drean: clockNum = 14328225; clockDen = 14; break; default: throw new System.Exception(); } CartPort = new CartridgePort(); Cassette = new CassettePort(); ColorRam = new Chip2114(); Cpu = new Chip6510(); Pla = new Chip90611401(); Ram = new Chip4864(); Serial = new SerialPort(); switch (sidType) { case C64.SidType.OldR2: Sid = Chip6581R2.Create(44100, clockNum, clockDen); break; case C64.SidType.OldR3: Sid = Chip6581R3.Create(44100, clockNum, clockDen); break; case C64.SidType.OldR4AR: Sid = Chip6581R4AR.Create(44100, clockNum, clockDen); break; case C64.SidType.NewR5: Sid = Chip8580R5.Create(44100, clockNum, clockDen); break; } switch (initRegion) { case C64.VicType.Ntsc: Vic = Chip6567R8.Create(borderType); Cia0 = Chip6526.CreateCia0(C64.CiaType.Ntsc, Input_ReadKeyboard, Input_ReadJoysticks); Cia1 = Chip6526.CreateCia1(C64.CiaType.Ntsc, Cia1_ReadPortA, () => 0xFF); break; case C64.VicType.Pal: Vic = Chip6569.Create(borderType); Cia0 = Chip6526.CreateCia0(C64.CiaType.Pal, Input_ReadKeyboard, Input_ReadJoysticks); Cia1 = Chip6526.CreateCia1(C64.CiaType.Pal, Cia1_ReadPortA, () => 0xFF); break; case C64.VicType.NtscOld: Vic = Chip6567R56A.Create(borderType); Cia0 = Chip6526.CreateCia0(C64.CiaType.NtscRevA, Input_ReadKeyboard, Input_ReadJoysticks); Cia1 = Chip6526.CreateCia1(C64.CiaType.NtscRevA, Cia1_ReadPortA, () => 0xFF); break; case C64.VicType.Drean: Vic = Chip6572.Create(borderType); Cia0 = Chip6526.CreateCia0(C64.CiaType.Pal, Input_ReadKeyboard, Input_ReadJoysticks); Cia1 = Chip6526.CreateCia1(C64.CiaType.Pal, Cia1_ReadPortA, () => 0xFF); break; } User = new UserPort(); ClockNumerator = clockNum; ClockDenominator = clockDen; // Initialize disk drive switch (diskDriveType) { case C64.DiskDriveType.Commodore1541: case C64.DiskDriveType.Commodore1541II: DiskDrive = new Drive1541(ClockNumerator, ClockDenominator); Serial.Connect(DiskDrive); break; } // Initialize tape drive switch (tapeDriveType) { case C64.TapeDriveType.Commodore1530: TapeDrive = new TapeDrive(); Cassette.Connect(TapeDrive); break; } BasicRom = new Chip23128(); CharRom = new Chip23128(); KernalRom = new Chip23128(); if (Cpu != null) Cpu.DebuggerStep = Execute; if (DiskDrive != null) DiskDrive.DebuggerStep = Execute; } public int ClockNumerator { get; } public int ClockDenominator { get; } // ----------------------------------------- public void Execute() { _vicBank = (0x3 - ((Cia1.PrA | ~Cia1.DdrA) & 0x3)) << 14; Vic.ExecutePhase1(); CartPort.ExecutePhase(); Cassette.ExecutePhase(); Serial.ExecutePhase(); Sid.ExecutePhase(); Cia0.ExecutePhase(); Cia1.ExecutePhase(); Cpu.ExecutePhase(); Vic.ExecutePhase2(); } public void Flush() { Sid.Flush(false); } // ----------------------------------------- public void HardReset() { _lastReadVicAddress = 0x3FFF; _lastReadVicData = 0xFF; InputRead = false; Cia0.HardReset(); Cia1.HardReset(); ColorRam.HardReset(); Ram.HardReset(); Serial.HardReset(); Sid.HardReset(); Vic.HardReset(); User.HardReset(); Cassette.HardReset(); Serial.HardReset(); Cpu.HardReset(); CartPort.HardReset(); } public void SoftReset() { // equivalent to a hard reset EXCEPT cpu, color ram, memory _lastReadVicAddress = 0x3FFF; _lastReadVicData = 0xFF; InputRead = false; Cia0.HardReset(); Cia1.HardReset(); Serial.HardReset(); Sid.HardReset(); Vic.HardReset(); User.HardReset(); Cassette.HardReset(); Serial.HardReset(); Cpu.SoftReset(); CartPort.HardReset(); } public void Init() { CartPort.ReadOpenBus = ReadOpenBus; Cassette.ReadDataOutput = CassPort_ReadDataOutput; Cassette.ReadMotor = CassPort_ReadMotor; Cia0.ReadFlag = Cassette.ReadDataInputBuffer; Cpu.PeekMemory = Pla.Peek; Cpu.PokeMemory = Pla.Poke; Cpu.ReadAec = Vic.ReadAec; Cpu.ReadIrq = Glue_ReadIRQ; Cpu.ReadNmi = Glue_ReadNMI; Cpu.ReadPort = Cpu_ReadPort; Cpu.ReadRdy = Vic.ReadBa; Cpu.ReadMemory = Pla.Read; Cpu.WriteMemory = Pla.Write; Cpu.WriteMemoryPort = Cpu_WriteMemoryPort; Cpu.ReadBus = ReadOpenBus; Pla.PeekBasicRom = BasicRom.Peek; Pla.PeekCartridgeHi = CartPort.PeekHiRom; Pla.PeekCartridgeLo = CartPort.PeekLoRom; Pla.PeekCharRom = CharRom.Peek; Pla.PeekCia0 = Cia0.Peek; Pla.PeekCia1 = Cia1.Peek; Pla.PeekColorRam = ColorRam.Peek; Pla.PeekExpansionHi = CartPort.PeekHiExp; Pla.PeekExpansionLo = CartPort.PeekLoExp; Pla.PeekKernalRom = KernalRom.Peek; Pla.PeekMemory = Ram.Peek; Pla.PeekSid = Sid.Peek; Pla.PeekVic = Vic.Peek; Pla.PokeCartridgeHi = CartPort.PokeHiRom; Pla.PokeCartridgeLo = CartPort.PokeLoRom; Pla.PokeCia0 = Cia0.Poke; Pla.PokeCia1 = Cia1.Poke; Pla.PokeColorRam = ColorRam.Poke; Pla.PokeExpansionHi = CartPort.PokeHiExp; Pla.PokeExpansionLo = CartPort.PokeLoExp; Pla.PokeMemory = Ram.Poke; Pla.PokeSid = Sid.Poke; Pla.PokeVic = Vic.Poke; Pla.ReadBasicRom = BasicRom.Read; Pla.ReadCartridgeHi = CartPort.ReadHiRom; Pla.ReadCartridgeLo = CartPort.ReadLoRom; Pla.ReadCharen = Pla_ReadCharen; Pla.ReadCharRom = CharRom.Read; Pla.ReadCia0 = Pla_ReadCia0; Pla.ReadCia1 = Cia1.Read; Pla.ReadColorRam = Pla_ReadColorRam; Pla.ReadExpansionHi = Pla_ReadExpansion1; Pla.ReadExpansionLo = Pla_ReadExpansion0; Pla.ReadExRom = CartPort.ReadExRom; Pla.ReadGame = CartPort.ReadGame; Pla.ReadHiRam = Pla_ReadHiRam; Pla.ReadKernalRom = KernalRom.Read; Pla.ReadLoRam = Pla_ReadLoRam; Pla.ReadMemory = Ram.Read; Pla.ReadSid = Sid.Read; Pla.ReadVic = Vic.Read; Pla.WriteCartridgeHi = CartPort.WriteHiRom; Pla.WriteCartridgeLo = CartPort.WriteLoRom; Pla.WriteCia0 = Cia0.Write; Pla.WriteCia1 = Cia1.Write; Pla.WriteColorRam = ColorRam.Write; Pla.WriteExpansionHi = CartPort.WriteHiExp; Pla.WriteExpansionLo = CartPort.WriteLoExp; Pla.WriteMemory = Ram.Write; Pla.WriteSid = Sid.Write; Pla.WriteVic = Vic.Write; Serial.ReadMasterAtn = SerPort_ReadAtnOut; Serial.ReadMasterClk = SerPort_ReadClockOut; Serial.ReadMasterData = SerPort_ReadDataOut; Sid.ReadPotX = Sid_ReadPotX; Sid.ReadPotY = Sid_ReadPotY; Vic.ReadMemory = Vic_ReadMemory; Vic.ReadColorRam = ColorRam.Read; } public void SyncState(Serializer ser) { ser.BeginSection(nameof(Cia0)); Cia0.SyncState(ser); ser.EndSection(); ser.BeginSection(nameof(Cia1)); Cia1.SyncState(ser); ser.EndSection(); ser.BeginSection(nameof(ColorRam)); ColorRam.SyncState(ser); ser.EndSection(); ser.BeginSection(nameof(Cpu)); Cpu.SyncState(ser); ser.EndSection(); ser.BeginSection(nameof(Pla)); Pla.SyncState(ser); ser.EndSection(); ser.BeginSection(nameof(Ram)); Ram.SyncState(ser); ser.EndSection(); ser.BeginSection(nameof(Sid)); Sid.SyncState(ser); ser.EndSection(); ser.BeginSection(nameof(Vic)); Vic.SyncState(ser); ser.EndSection(); if (CartPort.IsConnected) { ser.BeginSection(nameof(CartPort)); CartPort.SyncState(ser); ser.EndSection(); } ser.BeginSection(nameof(Cassette)); Cassette.SyncState(ser); ser.EndSection(); ser.BeginSection(nameof(Serial)); Serial.SyncState(ser); ser.EndSection(); if (TapeDrive != null) // TODO: a tape object is already in a nested class, is it the same reference? do we need this? { ser.BeginSection(nameof(TapeDrive)); TapeDrive.SyncState(ser); ser.EndSection(); } ser.BeginSection(nameof(User)); User.SyncState(ser); ser.EndSection(); if (DiskDrive != null) // TODO: a disk object is already in a nested class, is it the same reference? do we need this? { ser.BeginSection(nameof(DiskDrive)); DiskDrive.SyncState(ser); ser.EndSection(); } ser.Sync(nameof(InputRead), ref InputRead); ser.Sync(nameof(Irq), ref Irq); ser.Sync(nameof(Nmi), ref Nmi); ser.Sync(nameof(_lastReadVicAddress), ref _lastReadVicAddress); ser.Sync(nameof(_lastReadVicData), ref _lastReadVicData); ser.Sync(nameof(_vicBank), ref _vicBank); ser.Sync(nameof(_joystickPressed), ref _joystickPressed, useNull: false); ser.Sync(nameof(_keyboardPressed), ref _keyboardPressed, useNull: false); ser.Sync(nameof(_restorePressed), ref _restorePressed); } } }