diff --git a/BizHawk.Emulation/Computers/Commodore64/1541.cs b/BizHawk.Emulation/Computers/Commodore64/1541.cs index deb70f9214..eaac83a9d7 100644 --- a/BizHawk.Emulation/Computers/Commodore64/1541.cs +++ b/BizHawk.Emulation/Computers/Commodore64/1541.cs @@ -14,6 +14,15 @@ namespace BizHawk.Emulation.Computers.Commodore64 // two 6522 VIA chips, mapped at 1800 (communication to C64) and 1C00 (drive mechanics) // drive ROM, mapped C000-FFFF + // default 1800: + // 07 00 1A FF 05 00 FF FF + // 04 00 00 00 00 00 00 00 + // default 1C00: + // 90 00 00 00 05 00 FF FF + // 04 00 00 00 00 00 80 00 + + private Cia cia; + public MOS6502X cpu; public int cyclesPerRevolution; public int cyclesPerSecond; @@ -21,14 +30,17 @@ namespace BizHawk.Emulation.Computers.Commodore64 public byte[] ram; public byte[] rom; public double rpm; + public ChipSignals signal; public Via via0; public Via via1; - public Drive1541(byte[] driveRom, Region driveRegion) + public Drive1541(byte[] driveRom, Region driveRegion, Cia ciaInterface) { rom = new byte[driveRom.Length]; Array.Copy(driveRom, rom, driveRom.Length); + cia = ciaInterface; + switch (driveRegion) { case Region.NTSC: @@ -38,6 +50,7 @@ namespace BizHawk.Emulation.Computers.Commodore64 cyclesPerSecond = 14318181 / 18; break; } + HardReset(); } @@ -58,6 +71,24 @@ namespace BizHawk.Emulation.Computers.Commodore64 via0 = new Via(); via1 = new Via(); SetRPM(300.0); + + // attach VIA/CIA + + // set VIA values + via0.Poke(0x0, 0x07); + via0.Poke(0x2, 0x1A); + via0.Poke(0x3, 0xFF); + via0.Poke(0x4, 0x05); + via0.Poke(0x6, 0xFF); + via0.Poke(0x7, 0xFF); + via0.Poke(0x8, 0x04); + via0.Poke(0xE, 0x80); + via1.Poke(0x0, 0x90); + via1.Poke(0x4, 0x05); + via1.Poke(0x6, 0xFF); + via1.Poke(0x7, 0xFF); + via1.Poke(0x8, 0x04); + via1.Poke(0xE, 0x80); } public void Insert(Disk newDisk) @@ -72,11 +103,11 @@ namespace BizHawk.Emulation.Computers.Commodore64 { return ram[addr]; } - else if (addr >= 0x1800 && addr < 0x1810) + else if (addr >= 0x1800 && addr < 0x1C00) { return via0.Peek(addr); } - else if (addr >= 0x1C00 && addr < 0x1C10) + else if (addr >= 0x1C00 && addr < 0x2000) { return via1.Peek(addr); } @@ -89,7 +120,10 @@ namespace BizHawk.Emulation.Computers.Commodore64 public void PerformCycle() { + cpu.IRQ = via0.IRQ | via1.IRQ; cpu.ExecuteOne(); + via0.PerformCycle(); + via1.PerformCycle(); } public void Poke(int addr, byte val) @@ -99,11 +133,11 @@ namespace BizHawk.Emulation.Computers.Commodore64 { ram[addr] = val; } - else if (addr >= 0x1800 && addr < 0x1810) + else if (addr >= 0x1800 && addr < 0x1C00) { via0.Poke(addr, val); } - else if (addr >= 0x1C00 && addr < 0x1C10) + else if (addr >= 0x1C00 && addr < 0x2000) { via1.Poke(addr, val); } @@ -115,11 +149,11 @@ namespace BizHawk.Emulation.Computers.Commodore64 { return ram[addr]; } - else if (addr >= 0x1800 && addr < 0x1810) + else if (addr >= 0x1800 && addr < 0x1C00) { return via0.Read(addr); } - else if (addr >= 0x1C00 && addr < 0x1C10) + else if (addr >= 0x1C00 && addr < 0x2000) { return via1.Read(addr); } @@ -142,11 +176,11 @@ namespace BizHawk.Emulation.Computers.Commodore64 { ram[addr] = val; } - else if (addr >= 0x1800 && addr < 0x1810) + else if (addr >= 0x1800 && addr < 0x1C00) { via0.Write(addr, val); } - else if (addr >= 0x1C00 && addr < 0x1C10) + else if (addr >= 0x1C00 && addr < 0x2000) { via1.Write(addr, val); } diff --git a/BizHawk.Emulation/Computers/Commodore64/C64.core.cs b/BizHawk.Emulation/Computers/Commodore64/C64.core.cs index 601cdb1c3d..c02eab68a8 100644 --- a/BizHawk.Emulation/Computers/Commodore64/C64.core.cs +++ b/BizHawk.Emulation/Computers/Commodore64/C64.core.cs @@ -63,8 +63,8 @@ namespace BizHawk.Emulation.Computers.Commodore64 cpu.DummyReadMemory = PeekMemory; // initialize cia timers - cia0 = new Cia(signal, Region.NTSC); - cia1 = new Cia(signal, Region.NTSC); + cia0 = new Cia(Region.NTSC); + cia1 = new Cia(Region.NTSC); // initialize vic signal = new ChipSignals(); @@ -97,11 +97,11 @@ namespace BizHawk.Emulation.Computers.Commodore64 switch (extension.ToUpper()) { case @".G64": - diskDrive = new Drive1541(File.ReadAllBytes(Path.Combine(romPath, @"dos1541")), Region.NTSC); + diskDrive = new Drive1541(File.ReadAllBytes(Path.Combine(romPath, @"dos1541")), Region.NTSC, cia1); diskDrive.Insert(G64.Read(inputFile)); break; case @".D64": - diskDrive = new Drive1541(File.ReadAllBytes(Path.Combine(romPath, @"dos1541")), Region.NTSC); + diskDrive = new Drive1541(File.ReadAllBytes(Path.Combine(romPath, @"dos1541")), Region.NTSC, cia1); diskDrive.Insert(D64.Read(inputFile)); break; case @".PRG": diff --git a/BizHawk.Emulation/Computers/Commodore64/Cia.cs b/BizHawk.Emulation/Computers/Commodore64/Cia.cs index d4beecae76..1d804e8a82 100644 --- a/BizHawk.Emulation/Computers/Commodore64/Cia.cs +++ b/BizHawk.Emulation/Computers/Commodore64/Cia.cs @@ -51,12 +51,9 @@ namespace BizHawk.Emulation.Computers.Commodore64 public DataPortBus[] ports; private DataPortConnector[] connectors; - private ChipSignals signal; - public CiaRegs(ChipSignals newSignal) + public CiaRegs() { - signal = newSignal; - // power on state TLATCH[0] = 0xFFFF; TLATCH[1] = 0xFFFF; @@ -251,7 +248,6 @@ namespace BizHawk.Emulation.Computers.Commodore64 public bool lastCNT; public byte[] outputBitMask; private CiaRegs regs; - public ChipSignals signal; public int todCounter; public int todFrequency; public bool[] underflow; @@ -259,9 +255,8 @@ namespace BizHawk.Emulation.Computers.Commodore64 public Func ReadSerial; public Action WriteSerial; - public Cia(ChipSignals newSignal, Region newRegion) + public Cia(Region newRegion) { - signal = newSignal; ReadSerial = ReadSerialDummy; WriteSerial = WriteSerialDummy; switch (newRegion) @@ -350,7 +345,7 @@ namespace BizHawk.Emulation.Computers.Commodore64 public void HardReset() { outputBitMask = new byte[] { 0x40, 0x80 }; - regs = new CiaRegs(signal); + regs = new CiaRegs(); underflow = new bool[2]; todCounter = todFrequency; } diff --git a/BizHawk.Emulation/Computers/Commodore64/DataPort.cs b/BizHawk.Emulation/Computers/Commodore64/DataPort.cs index d972939a14..f94f3db324 100644 --- a/BizHawk.Emulation/Computers/Commodore64/DataPort.cs +++ b/BizHawk.Emulation/Computers/Commodore64/DataPort.cs @@ -7,15 +7,23 @@ namespace BizHawk.Emulation.Computers.Commodore64 { public class DataPortBus { - private DataPortConnector[] connectors; - private bool[] connected = new bool[2]; - private byte[] direction = new byte[2]; - private byte[] latch = new byte[2]; - private List servingHooks = new List(); - private List writeHooks = new List(); + protected bool[] connected = new bool[2]; + protected DataPortConnector[] connectors; + protected byte[] direction = new byte[2]; + protected DataPortConverter[] inputConverters; + protected byte[] latch = new byte[2]; + protected DataPortConverter[] outputConverters; + protected List servingHooks = new List(); + protected List writeHooks = new List(); public DataPortBus() { + inputConverters = new DataPortConverter[2]; + inputConverters[0] = new DataPortConverter(); + inputConverters[1] = new DataPortConverter(); + outputConverters = new DataPortConverter[2]; + outputConverters[0] = new DataPortConverter(); + outputConverters[1] = new DataPortConverter(); connectors = new DataPortConnector[2]; connectors[0] = new DataPortConnector(ReadData0, ReadDirection0, ReadRemoteData0, WriteData0, WriteDirection0); connectors[1] = new DataPortConnector(ReadData1, ReadDirection1, ReadRemoteData1, WriteData1, WriteDirection1); @@ -27,13 +35,37 @@ namespace BizHawk.Emulation.Computers.Commodore64 latch[1] = 0x00; } + public void AttachInputConverter(DataPortConnector connector, DataPortConverter converter) + { + if (connector.Equals(connectors[0])) + { + inputConverters[0] = converter; + } + else if (connector.Equals(connectors[1])) + { + inputConverters[1] = converter; + } + } + + public void AttachOutputConverter(DataPortConnector connector, DataPortConverter converter) + { + if (connector.Equals(connectors[0])) + { + outputConverters[0] = converter; + } + else if (connector.Equals(connectors[1])) + { + outputConverters[1] = converter; + } + } + public void AttachWriteHook(Action act) { writeHooks.Add(act); servingHooks.Add(0); } - private void ClearHooks() + protected void ClearHooks() { int count = servingHooks.Count; for (int i = 0; i < count; i++) @@ -57,6 +89,21 @@ namespace BizHawk.Emulation.Computers.Commodore64 throw new Exception("Two connections to this bus have already been established.."); } + public void Connect(DataPortConnector connection) + { + if (!connected[0]) + { + connected[0] = true; + connectors[0] = connection; + } + else if (!connected[1]) + { + connected[1] = true; + connectors[1] = connection; + } + throw new Exception("Two connections to this bus have already been established.."); + } + public void Disconnect(DataPortConnector connector) { if (connector.Equals(connectors[0])) @@ -73,7 +120,7 @@ namespace BizHawk.Emulation.Computers.Commodore64 } } - private void ExecuteWriteHooks() + protected void ExecuteWriteHooks() { int count = servingHooks.Count; for (int i = 0; i < count; i++) @@ -91,63 +138,71 @@ namespace BizHawk.Emulation.Computers.Commodore64 ClearHooks(); } - private byte ReadData0() + protected virtual byte ReadData0() { + byte result; if (connected[1]) - return (byte)((~direction[0] & latch[1]) | (direction[0] & latch[0])); + result = (byte)((~direction[0] & latch[1]) | (direction[0] & latch[0])); else - return latch[0]; + result = latch[0]; + return inputConverters[0].Convert(result, latch[1]); } - private byte ReadData1() + protected virtual byte ReadData1() { + byte result; if (connected[0]) - return (byte)((~direction[1] & latch[0]) | (direction[1] & latch[1])); + result = (byte)((~direction[1] & latch[0]) | (direction[1] & latch[1])); else - return latch[1]; + result = latch[1]; + return inputConverters[1].Convert(result, latch[0]); } - private byte ReadDirection0() + protected virtual byte ReadDirection0() { return direction[0]; } - private byte ReadDirection1() + protected virtual byte ReadDirection1() { return direction[1]; } - private byte ReadRemoteData0() + protected virtual byte ReadRemoteData0() { return latch[1]; } - private byte ReadRemoteData1() + protected virtual byte ReadRemoteData1() { return latch[0]; } - private void WriteData0(byte val) + protected virtual void WriteData0(byte val) { - latch[0] &= (byte)~direction[0]; - latch[0] |= (byte)(val & direction[0]); + byte result = latch[0]; + result &= (byte)~direction[0]; + result |= (byte)(val & direction[0]); + latch[0] = outputConverters[0].Convert(result, latch[1]); ExecuteWriteHooks(); } - private void WriteData1(byte val) + protected virtual void WriteData1(byte val) { - latch[1] &= (byte)~direction[1]; - latch[1] |= (byte)(val & direction[1]); + byte result = latch[1]; + result &= (byte)~direction[1]; + result |= (byte)(val & direction[1]); + latch[1] = outputConverters[1].Convert(result, latch[0]); ExecuteWriteHooks(); } - private void WriteDirection0(byte val) + protected virtual void WriteDirection0(byte val) { direction[0] = val; ExecuteWriteHooks(); } - private void WriteDirection1(byte val) + protected virtual void WriteDirection1(byte val) { direction[1] = val; ExecuteWriteHooks(); @@ -204,6 +259,11 @@ namespace BizHawk.Emulation.Computers.Commodore64 } } + public DataPortListener Listener() + { + return new DataPortListener(ReadData, ReadDirection); + } + public byte RemoteData { get @@ -211,10 +271,14 @@ namespace BizHawk.Emulation.Computers.Commodore64 return ReadRemoteData(); } } + } - public DataPortListener Listener() + public class DataPortConverter + { + public virtual byte Convert(byte input, byte remote) { - return new DataPortListener(ReadData, ReadDirection); + // the base converter transfers the values directly + return input; } } diff --git a/BizHawk.Emulation/Computers/Commodore64/SerialCable.cs b/BizHawk.Emulation/Computers/Commodore64/SerialCable.cs index 2a2cbac83f..01488bb752 100644 --- a/BizHawk.Emulation/Computers/Commodore64/SerialCable.cs +++ b/BizHawk.Emulation/Computers/Commodore64/SerialCable.cs @@ -5,60 +5,31 @@ using System.Text; namespace BizHawk.Emulation.Computers.Commodore64 { - // adapter for converting CIA output to VIA - // inherits DataPortConnector so the conversion is invisible - // to both devices - - struct SerialCableData + public class DataPortSerialInputConverter : DataPortConverter { - public bool ATNIN; - public bool ATNOUT; - public bool CLOCKIN; - public bool CLOCKOUT; - public bool DATAIN; - public bool DATAOUT; - } - - class SerialCable : DataPortConnector - { - private DataPortConnector connector; - - public SerialCable(DataPortConnector baseConnector) : base(baseConnector) + public override byte Convert(byte local, byte remote) { - connector = baseConnector; - } - - new public byte Data - { - get - { - return base.Data; - } - set - { - base.Data = value; - } - } - - new public byte Direction - { - get - { - return base.Direction; - } - set - { - base.Direction = value; - } - } - - new public byte RemoteData - { - get - { - return base.RemoteData; - } + int via = (local & 0x70); + via |= ((remote & 0x08) == 0) ? 0x80 : 0x00; //CIA ATNOUT - VIA ATNIN + via |= ((remote & 0x10) == 0) ? 0x04 : 0x00; //CIA CLKOUT - VIA CLKIN + via |= ((remote & 0x20) == 0) ? 0x01 : 0x00; //CIA DATOUT - VIA DATIN + via |= ((remote & 0x40) == 0) ? 0x08 : 0x00; //CIA CLKIN - VIA CLKOUT + via |= ((remote & 0x80) == 0) ? 0x02 : 0x00; //CIA DATIN - VIA DATOUT + return (byte)via; } } + public class DataPortSerialOutputConverter : DataPortConverter + { + public override byte Convert(byte local, byte remote) + { + int cia = (remote & 0x7); + cia |= ((local & 0x01) == 0) ? 0x20 : 0x00; //VIA DATIN - CIA DATOUT + cia |= ((local & 0x02) == 0) ? 0x80 : 0x00; //VIA DATOUT - CIA DATIN + cia |= ((local & 0x04) == 0) ? 0x10 : 0x00; //VIA CLKIN - CIA CLKOUT + cia |= ((local & 0x08) == 0) ? 0x40 : 0x00; //VIA CLKOUT - CIA CLKIN + cia |= ((local & 0x80) == 0) ? 0x08 : 0x00; //VIA DATIN - CIA DATOUT + return (byte)cia; + } + } } diff --git a/BizHawk.Emulation/Computers/Commodore64/Via.cs b/BizHawk.Emulation/Computers/Commodore64/Via.cs index f4eed897ab..b629b72aa0 100644 --- a/BizHawk.Emulation/Computers/Commodore64/Via.cs +++ b/BizHawk.Emulation/Computers/Commodore64/Via.cs @@ -11,28 +11,204 @@ namespace BizHawk.Emulation.Computers.Commodore64 public class ViaRegs { - public int ACR; - public int IER; - public int IFR; - public int PCR; + public int[] CACONTROL = new int[2]; + public int[] CBCONTROL = new int[2]; + public bool[] EICA = new bool[2]; + public bool[] EICB = new bool[2]; + public bool EISR; + public bool[] EIT = new bool[2]; + public bool[] ICA = new bool[2]; + public bool[] ICB = new bool[2]; + public bool IRQ; + public bool ISR; + public bool[] IT = new bool[2]; + public bool PALE; + public bool PBLE; public int SR; + public int SRCONTROL; public int[] TC = new int[2]; + public int[] TCONTROL = new int[2]; public int[] TL = new int[2]; + public DataPortBus[] ports; + + private DataPortConnector[] connectors; + public ViaRegs() { // power on state + ports = new DataPortBus[2]; + ports[0] = new DataPortBus(); + ports[1] = new DataPortBus(); + connectors = new DataPortConnector[2]; + connectors[0] = ports[0].Connect(); + connectors[1] = ports[1].Connect(); + connectors[0].Data = 0xFF; + connectors[1].Data = 0xFF; + connectors[0].Direction = 0xFF; + connectors[1].Direction = 0xFF; } public byte this[int addr] { get { - return 0xFF; + int result = 0xFF; + + addr &= 0xF; + switch (addr) + { + case 0x0: // port B data + result = connectors[1].Data; + break; + case 0x1: // port A data + case 0xF: // port A data without handshake + result = connectors[0].Data; + break; + case 0x2: // port B direction + result = connectors[1].Direction; + break; + case 0x3: // port A direction + result = connectors[0].Direction; + break; + case 0x4: // timer 0 lo + result = TC[0] & 0xFF; + break; + case 0x5: // timer 0 hi + result = (TC[0] & 0xFF00) >> 8; + break; + case 0x6: // timer 0 latch lo + result = TL[0] & 0xFF; + break; + case 0x7: // timer 0 latch hi + result = (TL[0] & 0xFF00) >> 8; + break; + case 0x8: // timer 1 lo + result = TC[1] & 0xFF; + break; + case 0x9: // timer 1 hi + result = (TC[1] & 0xFF00) >> 8; + break; + case 0xA: // shift register + result = SR; + break; + case 0xB: // peripheral control register + result = (CACONTROL[0] & 0x01); + result |= (CACONTROL[1] & 0x07) << 1; + result |= (CBCONTROL[0] & 0x01) << 4; + result |= (CBCONTROL[1] & 0x07) << 5; + break; + case 0xC: // auxilary control register + result = (PALE) ? 0x01 : 0x00; + result |= (PBLE) ? 0x02 : 0x00; + result |= (SRCONTROL & 0x7) << 2; + result |= (TCONTROL[0] & 0x1) << 5; + result |= (TCONTROL[1] & 0x3) << 6; + break; + case 0xD: // interrupt status register + result = ICA[1] ? 0x01 : 0x00; + result |= ICA[0] ? 0x02 : 0x00; + result |= ISR ? 0x04 : 0x00; + result |= ICB[1] ? 0x08 : 0x00; + result |= ICB[0] ? 0x10 : 0x00; + result |= IT[1] ? 0x20 : 0x00; + result |= IT[0] ? 0x40 : 0x00; + result |= IRQ ? 0x80 : 0x00; + break; + case 0xE: // interrupt control register + result = EICA[1] ? 0x01 : 0x00; + result |= EICA[0] ? 0x02 : 0x00; + result |= EISR ? 0x04 : 0x00; + result |= EICB[1] ? 0x08 : 0x00; + result |= EICB[0] ? 0x10 : 0x00; + result |= EIT[1] ? 0x20 : 0x00; + result |= EIT[0] ? 0x40 : 0x00; + result |= 0x80; // TODO: check if this is needed + break; + } + + return (byte)result; } set { - // set register + byte val = value; + addr &= 0xF; + + switch (addr) + { + case 0x0: // port B data + connectors[1].Data = val; + break; + case 0x1: // port A data + case 0xF: // port A data without handshake + connectors[0].Data = val; + break; + case 0x2: // port B direction + connectors[1].Direction = val; + break; + case 0x3: // port A direction + connectors[0].Direction = val; + break; + case 0x4: // timer 0 lo + TC[0] &= 0xFF00; + TC[0] |= val; + break; + case 0x5: // timer 0 hi + TC[0] &= 0x00FF; + TC[0] |= (int)val << 8; + break; + case 0x6: // timer 0 latch lo + TL[0] &= 0xFF00; + TL[0] |= val; + break; + case 0x7: // timer 0 latch hi + TL[0] &= 0x00FF; + TL[0] |= (int)val << 8; + break; + case 0x8: // timer 1 lo + TC[1] &= 0xFF00; + TC[1] |= val; + break; + case 0x9: // timer 1 hi + TC[1] &= 0x00FF; + TC[1] |= (int)val << 8; + break; + case 0xA: // shift register + SR = val; + break; + case 0xB: // peripheral control register + CACONTROL[0] = (val & 0x1); + CACONTROL[1] = ((val >> 1) & 0x7); + CBCONTROL[0] = ((val >> 4) & 0x1); + CBCONTROL[1] = ((val >> 5) & 0x7); + break; + case 0xC: // auxilary control register + PALE = ((val & 0x01) != 0); + PBLE = ((val & 0x02) != 0); + SRCONTROL = (val >> 2) & 0x7; + TCONTROL[0] = (val >> 5) & 0x1; + TCONTROL[1] = (val >> 6) & 0x3; + break; + case 0xD: // interrupt status register + ICA[1] = ((val & 0x01) != 0); + ICA[0] = ((val & 0x02) != 0); + ISR = ((val & 0x04) != 0); + ICB[1] = ((val & 0x08) != 0); + ICB[0] = ((val & 0x10) != 0); + IT[1] = ((val & 0x20) != 0); + IT[0] = ((val & 0x40) != 0); + IRQ = ((val & 0x80) != 0); + break; + case 0xE: // interrupt control register + EICA[1] = ((val & 0x01) != 0); + EICA[0] = ((val & 0x02) != 0); + EISR = ((val & 0x04) != 0); + EICB[1] = ((val & 0x08) != 0); + EICB[0] = ((val & 0x10) != 0); + EIT[1] = ((val & 0x20) != 0); + EIT[0] = ((val & 0x40) != 0); + break; + } } } } @@ -68,24 +244,73 @@ namespace BizHawk.Emulation.Computers.Commodore64 regs = new ViaRegs(); } + public bool IRQ + { + get + { + return regs.IRQ; + } + } + public byte Peek(int addr) { addr &= 0xF; - return 0; + return regs[addr]; + } + + public void PerformCycle() + { + // do stuff + UpdateInterrupts(); } public void Poke(int addr, byte val) { addr &= 0xF; + regs[addr] = val; } public byte Read(ushort addr) { - return 0x00; + byte result; + + addr &= 0xF; + switch (addr) + { + case 0xD: + // reading this clears it + result = regs[addr]; + regs[addr] = 0x00; + break; + default: + result = regs[addr]; + break; + } + + return result; + } + + private void UpdateInterrupts() + { + regs.IRQ = + (regs.ICA[0] & regs.EICA[0]) | + (regs.ICA[1] & regs.EICA[1]) | + (regs.ICB[0] & regs.EICB[0]) | + (regs.ICB[1] & regs.EICB[1]) | + (regs.IT[0] & regs.EIT[0]) | + (regs.IT[1] & regs.EIT[1]) | + (regs.ISR & regs.EISR); } public void Write(ushort addr, byte val) { + addr &= 0xF; + switch (addr) + { + default: + regs[addr] = val; + break; + } } } }