C64: Implement more of the VIA timers.
This commit is contained in:
parent
c4c12ff357
commit
64ded912df
|
@ -156,7 +156,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
|
|||
|
||||
public void Execute()
|
||||
{
|
||||
_vicBank = (0x3 - (Cia1.EffectivePrA & 0x3)) << 14;
|
||||
_vicBank = (0x3 - ((Cia1.PrA | ~Cia1.DdrA) & 0x3)) << 14;
|
||||
|
||||
Vic.ExecutePhase();
|
||||
Cassette.ExecutePhase();
|
||||
|
|
|
@ -10,7 +10,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
|
|||
private int _lastReadVicAddress = 0x3FFF;
|
||||
private int _lastReadVicData = 0xFF;
|
||||
private int _vicBank = 0xC000;
|
||||
private int _tempCia1Cra;
|
||||
|
||||
private bool CassPort_ReadDataOutput()
|
||||
{
|
||||
|
|
|
@ -102,10 +102,18 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
{
|
||||
case 0x0:
|
||||
_ifr &= 0xE7;
|
||||
if (_pcrCb2Control == PCR_CONTROL_HANDSHAKE_OUTPUT || _pcrCb2Control == PCR_CONTROL_PULSE_OUTPUT)
|
||||
{
|
||||
_handshakeCb2NextClock = true;
|
||||
}
|
||||
WriteRegister(addr, val);
|
||||
break;
|
||||
case 0x1:
|
||||
_ifr &= 0xFC;
|
||||
if (_pcrCa2Control == PCR_CONTROL_HANDSHAKE_OUTPUT || _pcrCa2Control == PCR_CONTROL_PULSE_OUTPUT)
|
||||
{
|
||||
_handshakeCa2NextClock = true;
|
||||
}
|
||||
WriteRegister(addr, val);
|
||||
break;
|
||||
case 0x4:
|
||||
|
|
|
@ -9,70 +9,118 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
{
|
||||
public sealed partial class Via
|
||||
{
|
||||
private const int PCR_INT_CONTROL_NEGATIVE_EDGE = 0x00;
|
||||
private const int PCR_INT_CONTROL_POSITIVE_EDGE = 0x01;
|
||||
private const int PCR_CONTROL_INPUT_NEGATIVE_ACTIVE_EDGE = 0x00;
|
||||
private const int PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE = 0x02;
|
||||
private const int PCR_CONTROL_INPUT_POSITIVE_ACTIVE_EDGE = 0x04;
|
||||
private const int PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE = 0x06;
|
||||
private const int PCR_CONTROL_HANDSHAKE_OUTPUT = 0x08;
|
||||
private const int PCR_CONTROL_PULSE_OUTPUT = 0x0A;
|
||||
private const int PCR_CONTROL_LOW_OUTPUT = 0x0C;
|
||||
private const int PCR_CONTROL_HIGH_OUTPUT = 0x0E;
|
||||
private const int ACR_SR_CONTROL_DISABLED = 0x00;
|
||||
private const int ACR_SR_CONTROL_SHIFT_IN_T2_ONCE = 0x04;
|
||||
private const int ACR_SR_CONTROL_SHIFT_IN_PHI2 = 0x08;
|
||||
private const int ACR_SR_CONTROL_SHIFT_IN_CLOCK = 0x0C;
|
||||
private const int ACR_SR_CONTROL_SHIFT_OUT_T2 = 0x10;
|
||||
private const int ACR_SR_CONTROL_SHIFT_OUT_T2_ONCE = 0x14;
|
||||
private const int ACR_SR_CONTROL_SHIFT_OUT_PHI2 = 0x18;
|
||||
private const int ACR_SR_CONTROL_SHIFT_OUT_CLOCK = 0x1C;
|
||||
private const int ACR_T2_CONTROL_TIMED = 0x00;
|
||||
private const int ACR_T2_CONTROL_COUNT_ON_PB6 = 0x20;
|
||||
private const int ACR_T1_CONTROL_INTERRUPT_ON_LOAD = 0x00;
|
||||
private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS = 0x40;
|
||||
private const int ACR_T1_CONTROL_INTERRUPT_ON_LOAD_AND_ONESHOT_PB7 = 0x80;
|
||||
private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7 = 0xC0;
|
||||
[SaveState.DoNotSave] private const int PCR_INT_CONTROL_NEGATIVE_EDGE = 0x00;
|
||||
[SaveState.DoNotSave] private const int PCR_INT_CONTROL_POSITIVE_EDGE = 0x01;
|
||||
[SaveState.DoNotSave] private const int PCR_CONTROL_INPUT_NEGATIVE_ACTIVE_EDGE = 0x00;
|
||||
[SaveState.DoNotSave] private const int PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE = 0x02;
|
||||
[SaveState.DoNotSave] private const int PCR_CONTROL_INPUT_POSITIVE_ACTIVE_EDGE = 0x04;
|
||||
[SaveState.DoNotSave] private const int PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE = 0x06;
|
||||
[SaveState.DoNotSave] private const int PCR_CONTROL_HANDSHAKE_OUTPUT = 0x08;
|
||||
[SaveState.DoNotSave] private const int PCR_CONTROL_PULSE_OUTPUT = 0x0A;
|
||||
[SaveState.DoNotSave] private const int PCR_CONTROL_LOW_OUTPUT = 0x0C;
|
||||
[SaveState.DoNotSave] private const int PCR_CONTROL_HIGH_OUTPUT = 0x0E;
|
||||
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_DISABLED = 0x00;
|
||||
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_IN_T2_ONCE = 0x04;
|
||||
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_IN_PHI2 = 0x08;
|
||||
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_IN_CLOCK = 0x0C;
|
||||
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_OUT_T2 = 0x10;
|
||||
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_OUT_T2_ONCE = 0x14;
|
||||
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_OUT_PHI2 = 0x18;
|
||||
[SaveState.DoNotSave] private const int ACR_SR_CONTROL_SHIFT_OUT_CLOCK = 0x1C;
|
||||
[SaveState.DoNotSave] private const int ACR_T2_CONTROL_TIMED = 0x00;
|
||||
[SaveState.DoNotSave] private const int ACR_T2_CONTROL_COUNT_ON_PB6 = 0x20;
|
||||
[SaveState.DoNotSave] private const int ACR_T1_CONTROL_INTERRUPT_ON_LOAD = 0x00;
|
||||
[SaveState.DoNotSave] private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS = 0x40;
|
||||
[SaveState.DoNotSave] private const int ACR_T1_CONTROL_INTERRUPT_ON_LOAD_AND_ONESHOT_PB7 = 0x80;
|
||||
[SaveState.DoNotSave] private const int ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7 = 0xC0;
|
||||
|
||||
[SaveState.SaveWithName("PortOutputA")]
|
||||
private int _pra;
|
||||
[SaveState.SaveWithName("PortDirectionA")]
|
||||
private int _ddra;
|
||||
[SaveState.SaveWithName("PortOutputB")]
|
||||
private int _prb;
|
||||
[SaveState.SaveWithName("PortDirectionB")]
|
||||
private int _ddrb;
|
||||
[SaveState.SaveWithName("Timer1Counter")]
|
||||
private int _t1C;
|
||||
[SaveState.SaveWithName("Timer1Latch")]
|
||||
private int _t1L;
|
||||
[SaveState.SaveWithName("Timer2Counter")]
|
||||
private int _t2C;
|
||||
[SaveState.SaveWithName("Timer2Latch")]
|
||||
private int _t2L;
|
||||
[SaveState.SaveWithName("ShiftRegister")]
|
||||
private int _sr;
|
||||
[SaveState.SaveWithName("AuxiliaryControlRegister")]
|
||||
private int _acr;
|
||||
[SaveState.SaveWithName("PeripheralControlRegister")]
|
||||
private int _pcr;
|
||||
[SaveState.SaveWithName("InterruptFlagRegister")]
|
||||
private int _ifr;
|
||||
[SaveState.SaveWithName("InterruptEnableRegister")]
|
||||
private int _ier;
|
||||
[SaveState.SaveWithName("Port")]
|
||||
private readonly Port _port;
|
||||
|
||||
[SaveState.SaveWithName("PortLatchA")]
|
||||
private int _paLatch;
|
||||
[SaveState.SaveWithName("PortLatchB")]
|
||||
private int _pbLatch;
|
||||
|
||||
[SaveState.SaveWithName("CA1InterruptControl")]
|
||||
private int _pcrCa1IntControl;
|
||||
[SaveState.SaveWithName("CA2Control")]
|
||||
private int _pcrCa2Control;
|
||||
[SaveState.SaveWithName("CB1InterruptControl")]
|
||||
private int _pcrCb1IntControl;
|
||||
[SaveState.SaveWithName("CB2Control")]
|
||||
private int _pcrCb2Control;
|
||||
[SaveState.SaveWithName("PortLatchEnableA")]
|
||||
private bool _acrPaLatchEnable;
|
||||
[SaveState.SaveWithName("PortLatchEnableB")]
|
||||
private bool _acrPbLatchEnable;
|
||||
[SaveState.SaveWithName("ShiftRegisterControl")]
|
||||
private int _acrSrControl;
|
||||
[SaveState.SaveWithName("Timer1Control")]
|
||||
private int _acrT1Control;
|
||||
[SaveState.SaveWithName("Timer2Control")]
|
||||
private int _acrT2Control;
|
||||
|
||||
[SaveState.SaveWithName("PreviousCA1")]
|
||||
private bool _ca1L;
|
||||
[SaveState.SaveWithName("PreviousCA2")]
|
||||
private bool _ca2L;
|
||||
[SaveState.SaveWithName("PreviousCB1")]
|
||||
private bool _cb1L;
|
||||
[SaveState.SaveWithName("PreviousCB2")]
|
||||
private bool _cb2L;
|
||||
[SaveState.SaveWithName("PreviousPB6")]
|
||||
private bool _pb6L;
|
||||
|
||||
[SaveState.SaveWithName("ResetCa2NextClock")]
|
||||
private bool _resetCa2NextClock;
|
||||
[SaveState.SaveWithName("ResetCb2NextClock")]
|
||||
private bool _resetCb2NextClock;
|
||||
|
||||
[SaveState.SaveWithName("HandshakeCa2NextClock")]
|
||||
private bool _handshakeCa2NextClock;
|
||||
[SaveState.SaveWithName("HandshakeCb2NextClock")]
|
||||
private bool _handshakeCb2NextClock;
|
||||
|
||||
[SaveState.SaveWithName("ShiftRegisterCounter")]
|
||||
private int _shiftCount;
|
||||
|
||||
[SaveState.SaveWithName("CA1")]
|
||||
public bool Ca1;
|
||||
[SaveState.SaveWithName("CA2")]
|
||||
public bool Ca2;
|
||||
[SaveState.SaveWithName("CB1")]
|
||||
public bool Cb1;
|
||||
[SaveState.SaveWithName("CB2")]
|
||||
public bool Cb2;
|
||||
[SaveState.SaveWithName("PB6")]
|
||||
private bool _pb6;
|
||||
|
||||
public Via()
|
||||
{
|
||||
|
@ -128,78 +176,157 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
Ca2 = false;
|
||||
Cb1 = false;
|
||||
Cb2 = false;
|
||||
}
|
||||
|
||||
private bool ProcessC2(bool c2, int control)
|
||||
{
|
||||
switch (control)
|
||||
{
|
||||
case PCR_CONTROL_INPUT_NEGATIVE_ACTIVE_EDGE:
|
||||
return c2;
|
||||
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE:
|
||||
return c2;
|
||||
case PCR_CONTROL_INPUT_POSITIVE_ACTIVE_EDGE:
|
||||
return c2;
|
||||
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE:
|
||||
return c2;
|
||||
case PCR_CONTROL_HANDSHAKE_OUTPUT:
|
||||
return c2;
|
||||
case PCR_CONTROL_PULSE_OUTPUT:
|
||||
return c2;
|
||||
case PCR_CONTROL_LOW_OUTPUT:
|
||||
return false;
|
||||
case PCR_CONTROL_HIGH_OUTPUT:
|
||||
return true;
|
||||
}
|
||||
return c2;
|
||||
_pb6L = false;
|
||||
_pb6 = false;
|
||||
_resetCa2NextClock = false;
|
||||
_resetCb2NextClock = false;
|
||||
_handshakeCa2NextClock = false;
|
||||
_handshakeCb2NextClock = false;
|
||||
}
|
||||
|
||||
public void ExecutePhase()
|
||||
{
|
||||
// Process 'pulse' and 'handshake' outputs on CA2 and CB2
|
||||
|
||||
if (_resetCa2NextClock)
|
||||
{
|
||||
Ca2 = true;
|
||||
_resetCa2NextClock = false;
|
||||
}
|
||||
else if (_handshakeCa2NextClock)
|
||||
{
|
||||
Ca2 = false;
|
||||
_resetCa2NextClock = _pcrCa2Control == PCR_CONTROL_PULSE_OUTPUT;
|
||||
_handshakeCa2NextClock = false;
|
||||
}
|
||||
|
||||
if (_resetCb2NextClock)
|
||||
{
|
||||
Cb2 = true;
|
||||
_resetCb2NextClock = false;
|
||||
}
|
||||
else if (_handshakeCb2NextClock)
|
||||
{
|
||||
Cb2 = false;
|
||||
_resetCb2NextClock = _pcrCb2Control == PCR_CONTROL_PULSE_OUTPUT;
|
||||
_handshakeCb2NextClock = false;
|
||||
}
|
||||
|
||||
// Count timers
|
||||
|
||||
_t1C--;
|
||||
if (_t1C < 0)
|
||||
{
|
||||
if (_acrT1Control == ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS ||
|
||||
_acrT1Control == ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7)
|
||||
switch (_acrT1Control)
|
||||
{
|
||||
_t1C = _t1L;
|
||||
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS:
|
||||
_t1C = _t1L;
|
||||
break;
|
||||
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7:
|
||||
_t1C = _t1L;
|
||||
_prb ^= 0x80;
|
||||
break;
|
||||
default:
|
||||
_t1C = 0xFFFF;
|
||||
break;
|
||||
}
|
||||
_ifr |= 0x40;
|
||||
}
|
||||
|
||||
if (_acrT2Control == ACR_T2_CONTROL_TIMED)
|
||||
switch (_acrT2Control)
|
||||
{
|
||||
_t2C--;
|
||||
if (_t2C < 0)
|
||||
{
|
||||
_ifr |= 0x20;
|
||||
_t2C = _t2L;
|
||||
}
|
||||
case ACR_T2_CONTROL_TIMED:
|
||||
_t2C--;
|
||||
if (_t2C < 0)
|
||||
{
|
||||
_ifr |= 0x20;
|
||||
_t2C = 0xFFFF;
|
||||
}
|
||||
break;
|
||||
case ACR_T2_CONTROL_COUNT_ON_PB6:
|
||||
_pb6L = _pb6;
|
||||
_pb6 = (_port.ReadExternalPrb() & 0x40) != 0;
|
||||
if (!_pb6 && _pb6L)
|
||||
{
|
||||
_t2C--;
|
||||
if (_t2C < 0)
|
||||
{
|
||||
_ifr |= 0x20;
|
||||
_t2C = 0xFFFF;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Ca2 = ProcessC2(Ca2, _pcrCa2Control);
|
||||
Cb2 = ProcessC2(Cb2, _pcrCb2Control);
|
||||
// Process CA2
|
||||
|
||||
// unknown behavior
|
||||
|
||||
if (_acrT1Control != ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS &&
|
||||
_acrT1Control != ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7)
|
||||
switch (_pcrCa2Control)
|
||||
{
|
||||
// unknown ACR T1 control
|
||||
case PCR_CONTROL_INPUT_NEGATIVE_ACTIVE_EDGE:
|
||||
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE:
|
||||
if (_ca2L && !Ca2)
|
||||
_ifr |= 0x01;
|
||||
break;
|
||||
case PCR_CONTROL_INPUT_POSITIVE_ACTIVE_EDGE:
|
||||
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE:
|
||||
if (!_ca2L && Ca2)
|
||||
_ifr |= 0x01;
|
||||
break;
|
||||
case PCR_CONTROL_HANDSHAKE_OUTPUT:
|
||||
if (_ca1L && !Ca1)
|
||||
{
|
||||
Ca2 = true;
|
||||
_ifr |= 0x01;
|
||||
}
|
||||
break;
|
||||
case PCR_CONTROL_PULSE_OUTPUT:
|
||||
break;
|
||||
case PCR_CONTROL_LOW_OUTPUT:
|
||||
Ca2 = false;
|
||||
break;
|
||||
case PCR_CONTROL_HIGH_OUTPUT:
|
||||
Ca2 = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_acrT2Control != ACR_T2_CONTROL_TIMED)
|
||||
{
|
||||
// unknown ACR T2 control
|
||||
}
|
||||
// Process CB2
|
||||
|
||||
switch (_pcrCb2Control)
|
||||
{
|
||||
case PCR_CONTROL_INPUT_NEGATIVE_ACTIVE_EDGE:
|
||||
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE:
|
||||
if (_cb2L && !Cb2)
|
||||
_ifr |= 0x08;
|
||||
break;
|
||||
case PCR_CONTROL_INPUT_POSITIVE_ACTIVE_EDGE:
|
||||
case PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE:
|
||||
if (!_cb2L && Cb2)
|
||||
_ifr |= 0x08;
|
||||
break;
|
||||
case PCR_CONTROL_HANDSHAKE_OUTPUT:
|
||||
if (_cb1L && !Cb1)
|
||||
{
|
||||
Cb2 = true;
|
||||
_ifr |= 0x08;
|
||||
}
|
||||
break;
|
||||
case PCR_CONTROL_PULSE_OUTPUT:
|
||||
break;
|
||||
case PCR_CONTROL_LOW_OUTPUT:
|
||||
Cb2 = false;
|
||||
break;
|
||||
case PCR_CONTROL_HIGH_OUTPUT:
|
||||
Cb2 = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// interrupt generation
|
||||
|
||||
if ((_pcrCb1IntControl == PCR_INT_CONTROL_POSITIVE_EDGE && Cb1 && !_cb1L) ||
|
||||
(_pcrCb1IntControl == PCR_INT_CONTROL_NEGATIVE_EDGE && !Cb1 && _cb1L))
|
||||
{
|
||||
_ifr |= 0x01;
|
||||
_ifr |= 0x10;
|
||||
if (_acrPbLatchEnable)
|
||||
{
|
||||
_pbLatch = _port.ReadExternalPrb();
|
||||
|
|
Loading…
Reference in New Issue