BizHawk/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs

363 lines
10 KiB
C#

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
{
/// <summary>
/// Contains the onboard chipset and glue.
/// </summary>
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;
public readonly SerialPort Serial;
public readonly TapeDrive TapeDrive;
public readonly UserPort User;
// devices
public readonly Drive1541 DiskDrive;
// state
//public int address;
public int Bus;
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.Create(C64.CiaType.Ntsc, Input_ReadKeyboard, Input_ReadJoysticks);
Cia1 = Chip6526.Create(C64.CiaType.Ntsc, Cia1_ReadPortA);
break;
case C64.VicType.Pal:
Vic = Chip6569.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.Pal, Input_ReadKeyboard, Input_ReadJoysticks);
Cia1 = Chip6526.Create(C64.CiaType.Pal, Cia1_ReadPortA);
break;
case C64.VicType.NtscOld:
Vic = Chip6567R56A.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.NtscRevA, Input_ReadKeyboard, Input_ReadJoysticks);
Cia1 = Chip6526.Create(C64.CiaType.NtscRevA, Cia1_ReadPortA);
break;
case C64.VicType.Drean:
Vic = Chip6572.Create(borderType);
Cia0 = Chip6526.Create(C64.CiaType.Pal, Input_ReadKeyboard, Input_ReadJoysticks);
Cia1 = Chip6526.Create(C64.CiaType.Pal, Cia1_ReadPortA);
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();
}
public int ClockNumerator { get; private set; }
public int ClockDenominator { get; private set; }
// -----------------------------------------
public void Execute()
{
_vicBank = (0x3 - ((Cia1.PrA | ~Cia1.DdrA) & 0x3)) << 14;
Vic.ExecutePhase();
CartPort.ExecutePhase();
Cassette.ExecutePhase();
Serial.ExecutePhase();
Sid.ExecutePhase();
Cia0.ExecutePhase();
Cia1.ExecutePhase();
Cpu.ExecutePhase();
}
public void Flush()
{
Sid.Flush();
}
// -----------------------------------------
public void HardReset()
{
Bus = 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 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;
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.ReadAec = Vic.ReadAec;
Pla.ReadBa = Vic.ReadBa;
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("Cia0");
Cia0.SyncState(ser);
ser.EndSection();
ser.BeginSection("Cia1");
Cia1.SyncState(ser);
ser.EndSection();
ser.BeginSection("ColorRam");
ColorRam.SyncState(ser);
ser.EndSection();
ser.BeginSection("Cpu");
Cpu.SyncState(ser);
ser.EndSection();
ser.BeginSection("Pla");
Pla.SyncState(ser);
ser.EndSection();
ser.BeginSection("Ram");
Ram.SyncState(ser);
ser.EndSection();
ser.BeginSection("Sid");
Sid.SyncState(ser);
ser.EndSection();
ser.BeginSection("Vic");
Vic.SyncState(ser);
ser.EndSection();
if (CartPort.IsConnected)
{
ser.BeginSection("CartPort");
CartPort.SyncState(ser);
ser.EndSection();
}
ser.BeginSection("Cassette");
Cassette.SyncState(ser);
ser.EndSection();
ser.BeginSection("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("TapeDrive");
TapeDrive.SyncState(ser);
ser.EndSection();
}
ser.BeginSection("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("DiskDrive");
DiskDrive.SyncState(ser);
ser.EndSection();
}
ser.Sync("Bus", ref Bus);
ser.Sync("InputRead", ref InputRead);
ser.Sync("Irq", ref Irq);
ser.Sync("Nmi", ref Nmi);
ser.Sync("_lastReadVicAddress", ref _lastReadVicAddress);
ser.Sync("_lastReadVicData", ref _lastReadVicData);
ser.Sync("_vicBank", ref _vicBank);
ser.Sync("_joystickPressed", ref _joystickPressed, useNull: false);
ser.Sync("_keyboardPressed", ref _keyboardPressed, useNull: false);
ser.Sync("_restorePressed", ref _restorePressed);
}
}
}