commodore64: VIA timer chip registers added. Also, writes to mirrored registers now work correctly on all chips

This commit is contained in:
saxxonpike 2012-11-16 06:14:32 +00:00
parent 8196caf731
commit 5b701a58e9
6 changed files with 397 additions and 108 deletions

View File

@ -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);
}

View File

@ -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":

View File

@ -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<bool> ReadSerial;
public Action<bool> 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;
}

View File

@ -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<int> servingHooks = new List<int>();
private List<Action> writeHooks = new List<Action>();
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<int> servingHooks = new List<int>();
protected List<Action> writeHooks = new List<Action>();
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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}
}