[C64] Disk: 1541 drive saveram now works properly when rewind is disabled
[C64] Disk: Use raw track capacity values per the G64 file format specification, should fix some disk loaders that are expecting data not to be so sparse (Spindle demos in D64 format particularly) [C64] Disk: fix 1541 drive address decodes [C64] VIA: 6522 core facelift, shift register and use of low-order timer latches implemented, should fix some disk loaders ("Sprite B*****e 2" demo plays, yes they called it that, and it's not what it sounds like) [C64] CIA: fix PB6/PB7 outputs when enabled on CRA/CRB
This commit is contained in:
parent
4a48d945c7
commit
0bb940ea7a
|
@ -46,13 +46,13 @@
|
|||
switch (addr)
|
||||
{
|
||||
case 0x0:
|
||||
return _port.ReadPra(_pra, _ddra, _prb, _ddrb);
|
||||
return _port.ReadPra(_praOut, _ddraOut, _prbOut, _ddrbOut);
|
||||
case 0x1:
|
||||
return _port.ReadPrb(_pra, _ddra, _prb, _ddrb);
|
||||
return _port.ReadPrb(_praOut, _ddraOut, _prbOut, _ddrbOut);
|
||||
case 0x2:
|
||||
return _ddra;
|
||||
return _ddraOut;
|
||||
case 0x3:
|
||||
return _ddrb;
|
||||
return _ddrbOut;
|
||||
case 0x4:
|
||||
return _ta & 0xFF;
|
||||
case 0x5:
|
||||
|
@ -177,7 +177,7 @@
|
|||
// Toggle output begins high when timer starts.
|
||||
if ((_cra & 0x05) == 0x05 && (oldCra & 0x01) == 0)
|
||||
{
|
||||
_prb |= 0x40;
|
||||
_taPrb6Out = 0x40;
|
||||
}
|
||||
break;
|
||||
case 0xF:
|
||||
|
@ -187,7 +187,7 @@
|
|||
// Toggle output begins high when timer starts.
|
||||
if ((_crb & 0x05) == 0x05 && (oldCrb & 0x01) == 0)
|
||||
{
|
||||
_prb |= 0x80;
|
||||
_tbPrb7Out = 0x80;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -251,6 +251,7 @@
|
|||
_newCra = val;
|
||||
_taCntPhi2 = (val & 0x20) == 0;
|
||||
_taCntCnt = (val & 0x20) == 0x20;
|
||||
_taPrb6OutEnable = (val & 0x2) != 0;
|
||||
break;
|
||||
case 0xF:
|
||||
_hasNewCrb = true;
|
||||
|
@ -259,20 +260,17 @@
|
|||
_tbCntCnt = (val & 0x60) == 0x20;
|
||||
_tbCntTa = (val & 0x60) == 0x40;
|
||||
_tbCntTaCnt = (val & 0x60) == 0x60;
|
||||
_tbPrb7OutEnable = (val & 0x2) != 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public int DdrA => _ddra;
|
||||
public int DdrA => _ddraOut;
|
||||
|
||||
public int DdrB => _ddrb;
|
||||
public int DdrB => _ddrbOut;
|
||||
|
||||
public int PrA => _pra;
|
||||
public int PrA => _praOut;
|
||||
|
||||
public int PrB => _prb;
|
||||
|
||||
public int EffectivePrA => _pra | ~_ddra;
|
||||
|
||||
public int EffectivePrB => _prb | ~_ddrb;
|
||||
public int PrB => _prbOut;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,9 +37,13 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
public bool DelayedInterrupts = true;
|
||||
|
||||
private int _pra;
|
||||
private int _praOut;
|
||||
private int _prb;
|
||||
private int _prbOut;
|
||||
private int _ddra;
|
||||
private int _ddraOut;
|
||||
private int _ddrb;
|
||||
private int _ddrbOut;
|
||||
private int _ta;
|
||||
private int _tb;
|
||||
private int _latcha;
|
||||
|
@ -83,6 +87,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
private bool _lastCnt;
|
||||
private bool _thisCnt;
|
||||
private bool _tbCntTaCnt;
|
||||
private int _taPrb6Out;
|
||||
private bool _taPrb6OutEnable;
|
||||
private int _tbPrb7Out;
|
||||
private bool _tbPrb7OutEnable;
|
||||
|
||||
private readonly IPort _port;
|
||||
private int _todlo;
|
||||
|
@ -141,6 +149,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
_taState = TimerState.Stop;
|
||||
_tbState = TimerState.Stop;
|
||||
_lastCnt = true;
|
||||
_taPrb6Out = 0; // datasheet: set low on /RES
|
||||
_tbPrb7Out = 0; // datasheet: set low on /RES
|
||||
_taPrb6OutEnable = false;
|
||||
_tbPrb7OutEnable = false;
|
||||
_praOut = 0;
|
||||
_prbOut = 0;
|
||||
}
|
||||
|
||||
public void ExecutePhase()
|
||||
|
@ -162,17 +176,16 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
|
||||
if (_taPrb6NegativeNextCycle)
|
||||
{
|
||||
_prb &= 0xBF;
|
||||
_taPrb6Out = 0x00;
|
||||
_taPrb6NegativeNextCycle = false;
|
||||
}
|
||||
|
||||
if (_tbPrb7NegativeNextCycle)
|
||||
{
|
||||
_prb &= 0x7F;
|
||||
_tbPrb7Out = 0x00;
|
||||
_tbPrb7NegativeNextCycle = false;
|
||||
}
|
||||
|
||||
|
||||
switch (_taState)
|
||||
{
|
||||
case TimerState.WaitThenCount:
|
||||
|
@ -271,11 +284,23 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
}
|
||||
_flagLatch = _flagInput;
|
||||
|
||||
if ((_cra & 0x02) != 0)
|
||||
_ddra |= 0x40;
|
||||
if ((_crb & 0x02) != 0)
|
||||
_ddrb |= 0x80;
|
||||
_praOut = _pra;
|
||||
_prbOut = _prb;
|
||||
_ddraOut = _ddra;
|
||||
_ddrbOut = _ddrb;
|
||||
|
||||
if (_taPrb6OutEnable)
|
||||
{
|
||||
_prbOut = (_prbOut & ~0x40) | _taPrb6Out;
|
||||
_ddrbOut |= 0x40;
|
||||
}
|
||||
|
||||
if (_tbPrb7OutEnable)
|
||||
{
|
||||
_prbOut = (_prbOut & ~0x80) | _tbPrb7Out;
|
||||
_ddrbOut |= 0x80;
|
||||
}
|
||||
|
||||
_lastCnt = _thisCnt;
|
||||
}
|
||||
|
||||
|
@ -321,14 +346,13 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
{
|
||||
if ((_cra & 0x04) != 0)
|
||||
{
|
||||
_taPrb6Out = 0x40;
|
||||
_taPrb6NegativeNextCycle = true;
|
||||
_prb |= 0x40;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prb ^= 0x40;
|
||||
_taPrb6Out ^= 0x40;
|
||||
}
|
||||
_ddrb |= 0x40;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -435,11 +459,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
if ((_crb & 0x04) != 0)
|
||||
{
|
||||
_tbPrb7NegativeNextCycle = true;
|
||||
_prb |= 0x80;
|
||||
_tbPrb7Out = 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prb ^= 0x80;
|
||||
_tbPrb7Out ^= 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -562,6 +586,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
ser.Sync(nameof(_thisCnt), ref _thisCnt);
|
||||
ser.Sync(nameof(_tbCntTaCnt), ref _tbCntTaCnt);
|
||||
ser.Sync(nameof(_todCounter), ref _todCounter);
|
||||
ser.Sync(nameof(_taPrb6Out), ref _taPrb6Out);
|
||||
ser.Sync(nameof(_taPrb6OutEnable), ref _taPrb6OutEnable);
|
||||
ser.Sync(nameof(_tbPrb7Out), ref _tbPrb7Out);
|
||||
ser.Sync(nameof(_tbPrb7OutEnable), ref _tbPrb7OutEnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,32 +18,32 @@
|
|||
switch (addr)
|
||||
{
|
||||
case 0x0:
|
||||
if (_pcrCb2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE && _pcrCb2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE)
|
||||
_ifr &= 0xE7;
|
||||
if (_acrPbLatchEnable)
|
||||
return _pbLatch;
|
||||
_ifr &= ~IRQ_CB1;
|
||||
if ((_pcr & PCR_CB2_ACK) == 0)
|
||||
{
|
||||
_ifr &= ~IRQ_CB2;
|
||||
}
|
||||
_cb2Handshake = true;
|
||||
_cb2Pulse = false;
|
||||
break;
|
||||
case 0x1:
|
||||
if (_pcrCa2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE && _pcrCa2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE)
|
||||
_ifr &= 0xFC;
|
||||
if (_acrPaLatchEnable)
|
||||
return _paLatch;
|
||||
_ifr &= ~IRQ_CA1;
|
||||
if ((_pcr & PCR_CA2_ACK) == 0)
|
||||
{
|
||||
_ifr &= ~IRQ_CA2;
|
||||
}
|
||||
_ca2Handshake = true;
|
||||
_ca2Pulse = false;
|
||||
break;
|
||||
case 0x4:
|
||||
_ifr &= 0xBF;
|
||||
_ifr &= ~IRQ_T1;
|
||||
break;
|
||||
case 0x8:
|
||||
_ifr &= 0xDF;
|
||||
_ifr &= ~IRQ_T2;
|
||||
break;
|
||||
case 0xA:
|
||||
_ifr &= 0xFB;
|
||||
_srCount = 8;
|
||||
break;
|
||||
case 0xF:
|
||||
if (_acrPaLatchEnable)
|
||||
{
|
||||
return _paLatch;
|
||||
}
|
||||
_srAccessed = true;
|
||||
_ifr &= ~IRQ_SR;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -55,10 +55,10 @@
|
|||
switch (addr)
|
||||
{
|
||||
case 0x0:
|
||||
return _port.ReadPrb(_prb, _ddrb);
|
||||
return (PrB & DdrB) | (_irb & ~DdrB);
|
||||
case 0x1:
|
||||
case 0xF:
|
||||
return _port.ReadExternalPra();
|
||||
return _ira;
|
||||
case 0x2:
|
||||
return _ddrb;
|
||||
case 0x3:
|
||||
|
@ -82,9 +82,9 @@
|
|||
case 0xC:
|
||||
return _pcr;
|
||||
case 0xD:
|
||||
return _ifr;
|
||||
return (_ifr & 0x7F) | (_irq ? 0x80 : 0x00);
|
||||
case 0xE:
|
||||
return _ier | 0x80;
|
||||
return _ier;
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
|
@ -96,17 +96,19 @@
|
|||
switch (addr)
|
||||
{
|
||||
case 0x0:
|
||||
if (_pcrCb2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE && _pcrCb2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE)
|
||||
_ifr &= 0xE7;
|
||||
if (_pcrCb2Control == PCR_CONTROL_PULSE_OUTPUT)
|
||||
_handshakeCb2NextClock = true;
|
||||
_ifr &= ~IRQ_CB1;
|
||||
if ((_pcr & PCR_CB2_ACK) == 0)
|
||||
{
|
||||
_ifr &= ~IRQ_CB2;
|
||||
}
|
||||
WriteRegister(addr, val);
|
||||
break;
|
||||
case 0x1:
|
||||
if (_pcrCa2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_NEGATIVE_EDGE && _pcrCa2Control != PCR_CONTROL_INDEPENDENT_INTERRUPT_INPUT_POSITIVE_EDGE)
|
||||
_ifr &= 0xFC;
|
||||
if (_pcrCa2Control == PCR_CONTROL_PULSE_OUTPUT)
|
||||
_handshakeCa2NextClock = true;
|
||||
_ifr &= ~IRQ_CA1;
|
||||
if ((_pcr & PCR_CA2_ACK) == 0)
|
||||
{
|
||||
_ifr &= ~IRQ_CA2;
|
||||
}
|
||||
WriteRegister(addr, val);
|
||||
break;
|
||||
case 0x4:
|
||||
|
@ -115,43 +117,41 @@
|
|||
break;
|
||||
case 0x5:
|
||||
_t1L = (_t1L & 0xFF) | ((val & 0xFF) << 8);
|
||||
_ifr &= 0xBF;
|
||||
_t1C = _t1L;
|
||||
_t1CLoaded = true;
|
||||
_t1Delayed = 1;
|
||||
_resetPb7NextClock = _acrT1Control == ACR_T1_CONTROL_INTERRUPT_ON_LOAD_AND_PULSE_PB7;
|
||||
_ifr &= ~IRQ_T1;
|
||||
_t1Reload = false;
|
||||
_t1Out = false;
|
||||
_t1IrqAllowed = true;
|
||||
break;
|
||||
case 0x7:
|
||||
_t1L = (_t1L & 0xFF) | ((val & 0xFF) << 8);
|
||||
_ifr &= 0xBF;
|
||||
_ifr &= ~IRQ_T1;
|
||||
break;
|
||||
case 0x8:
|
||||
_t2L = (_t2L & 0xFF00) | (val & 0xFF);
|
||||
break;
|
||||
case 0x9:
|
||||
_t2L = (_t2L & 0xFF) | ((val & 0xFF) << 8);
|
||||
_ifr &= 0xDF;
|
||||
if (_acrT2Control == ACR_T2_CONTROL_TIMED)
|
||||
{
|
||||
_t2C = _t2L;
|
||||
_t2CLoaded = true;
|
||||
}
|
||||
|
||||
_t2Delayed = 1;
|
||||
_ifr &= ~IRQ_T2;
|
||||
_t2IrqAllowed = true;
|
||||
break;
|
||||
case 0xA:
|
||||
_ifr &= 0xFB;
|
||||
_srCount = 8;
|
||||
_srAccessed = true;
|
||||
_srWritten = true;
|
||||
WriteRegister(addr, val);
|
||||
break;
|
||||
case 0xD:
|
||||
_ifr &= ~val;
|
||||
_ifr &= ~(val & 0x7F);
|
||||
break;
|
||||
case 0xE:
|
||||
if ((val & 0x80) != 0)
|
||||
_ier |= val & 0x7F;
|
||||
if ((val & IRQ_BIT) != 0)
|
||||
{
|
||||
_ier |= val & IRQ_MASK;
|
||||
}
|
||||
else
|
||||
_ier &= ~val;
|
||||
{
|
||||
_ier &= ~(val & IRQ_MASK);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WriteRegister(addr, val);
|
||||
|
@ -161,15 +161,14 @@
|
|||
|
||||
private void WriteRegister(int addr, int val)
|
||||
{
|
||||
addr &= 0xF;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x0:
|
||||
_prb = val & 0xFF;
|
||||
_orb = val & 0xFF;
|
||||
break;
|
||||
case 0x1:
|
||||
case 0xF:
|
||||
_pra = val & 0xFF;
|
||||
_ora = val & 0xFF;
|
||||
break;
|
||||
case 0x2:
|
||||
_ddrb = val & 0xFF;
|
||||
|
@ -200,21 +199,12 @@
|
|||
break;
|
||||
case 0xB:
|
||||
_acr = val & 0xFF;
|
||||
_acrPaLatchEnable = (val & 0x01) != 0;
|
||||
_acrPbLatchEnable = (val & 0x02) != 0;
|
||||
_acrSrControl = (val & 0x1C);
|
||||
_acrT2Control = (val & 0x20);
|
||||
_acrT1Control = (val & 0xC0);
|
||||
break;
|
||||
case 0xC:
|
||||
_pcr = val & 0xFF;
|
||||
_pcrCa1IntControl = _pcr & 0x01;
|
||||
_pcrCa2Control = _pcr & 0x0E;
|
||||
_pcrCb1IntControl = (_pcr & 0x10) >> 4;
|
||||
_pcrCb2Control = (_pcr & 0xE0) >> 4;
|
||||
break;
|
||||
case 0xD:
|
||||
_ifr = val & 0xFF;
|
||||
_ifr = val & 0x7F;
|
||||
break;
|
||||
case 0xE:
|
||||
_ier = val & 0xFF;
|
||||
|
@ -224,18 +214,12 @@
|
|||
|
||||
public int DdrA => _ddra;
|
||||
|
||||
public int DdrB => _ddrb;
|
||||
public int DdrB => _ddrb | (_acr & ACR_T1_PB7_OUT);
|
||||
|
||||
public int PrA => _pra;
|
||||
public int PrA => _ora;
|
||||
|
||||
public int PrB => _prb;
|
||||
|
||||
public int EffectivePrA => _pra | ~_ddra;
|
||||
|
||||
public int EffectivePrB => _prb | ~_ddrb;
|
||||
|
||||
public int ActualPrA => _acrPaLatchEnable ? _paLatch : _port.ReadPra(_pra, _ddra);
|
||||
|
||||
public int ActualPrB => _acrPbLatchEnable ? _pbLatch : _port.ReadPrb(_prb, _ddrb);
|
||||
public int PrB => (_acr & ACR_T1_PB7_OUT) != 0
|
||||
? (_orb & 0x7F) | (_t1Out ? 0x80 : 0x00)
|
||||
: _orb;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,87 +4,104 @@ 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;
|
||||
private const int ACR_T1_CONTROL_INTERRUPT_ON_LOAD_AND_PULSE_PB7 = 0x80;
|
||||
private const int PB6_MASK = 1 << 6;
|
||||
private const int PB6_TAP = 0b11 << 6;
|
||||
private const int PB6_NEGATIVE_EDGE = 0b10 << 6;
|
||||
|
||||
private int _pra;
|
||||
private const int IRQ_CA2 = 1 << 0;
|
||||
private const int IRQ_CA1 = 1 << 1;
|
||||
private const int IRQ_SR = 1 << 2;
|
||||
private const int IRQ_CB2 = 1 << 3;
|
||||
private const int IRQ_CB1 = 1 << 4;
|
||||
private const int IRQ_T2 = 1 << 5;
|
||||
private const int IRQ_T1 = 1 << 6;
|
||||
private const int IRQ_MASK = (1 << 7) - 1;
|
||||
private const int IRQ_BIT = 1 << 7;
|
||||
|
||||
private const int ACR_LATCH_PA = 1 << 0;
|
||||
private const int ACR_LATCH_PB = 1 << 1;
|
||||
private const int ACR_SR = 0b111 << 2;
|
||||
private const int ACR_SR_CLOCK = 0b011 << 2;
|
||||
private const int ACR_SR_USE_T2 = 0b101 << 2;
|
||||
private const int ACR_SR_OUT = 0b100 << 2;
|
||||
private const int ACR_SR_CLOCK_T2 = 0b001 << 2;
|
||||
private const int ACR_SR_CLOCK_PHI2 = 0b010 << 2;
|
||||
private const int ACR_SR_CLOCK_EXT = 0b011 << 2;
|
||||
private const int ACR_T2_COUNT_PB6 = 1 << 5;
|
||||
private const int ACR_T1_FREERUN = 1 << 6;
|
||||
private const int ACR_T1_PB7_OUT = 1 << 7;
|
||||
|
||||
private const int PCR_CA1_POLARITY = 1 << 0;
|
||||
private const int PCR_CA2_MODE = 0b011 << 1;
|
||||
private const int PCR_CA2_ACK = 0b001 << 1;
|
||||
private const int PCR_CA2_POLARITY = 0b010 << 1;
|
||||
private const int PCR_CA2_OUT = 0b100 << 1;
|
||||
private const int PCR_CA2_MODE_HANDSHAKE = 0;
|
||||
private const int PCR_CA2_MODE_PULSE = 0b001 << 1;
|
||||
private const int PCR_CA2_MODE_LOW = 0b010 << 1;
|
||||
private const int PCR_CB1_POLARITY = 1 << 4;
|
||||
private const int PCR_CB2_MODE = 0b011 << 5;
|
||||
private const int PCR_CB2_ACK = 0b001 << 5;
|
||||
private const int PCR_CB2_POLARITY = 0b010 << 5;
|
||||
private const int PCR_CB2_OUT = 0b100 << 5;
|
||||
private const int PCR_CB2_MODE_HANDSHAKE = 0;
|
||||
private const int PCR_CB2_MODE_PULSE = 0b001 << 5;
|
||||
private const int PCR_CB2_MODE_LOW = 0b010 << 5;
|
||||
|
||||
private const int EDGE_NEGATIVE = 0b10;
|
||||
private const int EDGE_POSITIVE = 0b01;
|
||||
private const int EDGE_MASK = 0b11;
|
||||
|
||||
private int _ora;
|
||||
private int _ddra;
|
||||
private int _prb;
|
||||
private int _orb;
|
||||
private int _ddrb;
|
||||
private int _t1C;
|
||||
private int _t1L;
|
||||
private int _t2C;
|
||||
private int _t2L;
|
||||
private bool _t1Out;
|
||||
private int _sr;
|
||||
private int _acr;
|
||||
private int _pcr;
|
||||
private int _ifr;
|
||||
private int _ier;
|
||||
private bool _irq;
|
||||
private readonly IPort _port;
|
||||
|
||||
private int _paLatch;
|
||||
private int _pbLatch;
|
||||
private int _ira;
|
||||
private int _irb;
|
||||
private int _shiftCount;
|
||||
|
||||
private int _pcrCa1IntControl;
|
||||
private int _pcrCa2Control;
|
||||
private int _pcrCb1IntControl;
|
||||
private int _pcrCb2Control;
|
||||
private bool _acrPaLatchEnable;
|
||||
private bool _acrPbLatchEnable;
|
||||
private int _acrSrControl;
|
||||
private int _acrT1Control;
|
||||
private int _acrT2Control;
|
||||
private int _srCount;
|
||||
private bool _ca2Handshake;
|
||||
private bool _cb2Handshake;
|
||||
private bool _ca2Pulse;
|
||||
private bool _cb2Pulse;
|
||||
|
||||
private bool _ca1L;
|
||||
private bool _ca2L;
|
||||
private bool _cb1L;
|
||||
private bool _cb2L;
|
||||
private bool _pb6L;
|
||||
private bool _ca2Out;
|
||||
private bool _cb1Out;
|
||||
private bool _cb2Out;
|
||||
private bool _shiftEnable;
|
||||
private bool _shiftDir;
|
||||
private int _shiftHist;
|
||||
private bool _srWritten;
|
||||
private bool _srAccessed;
|
||||
|
||||
private bool _resetCa2NextClock;
|
||||
private bool _resetCb2NextClock;
|
||||
private bool _resetPb7NextClock;
|
||||
private bool _setPb7NextClock;
|
||||
|
||||
private bool _handshakeCa2NextClock;
|
||||
private bool _handshakeCb2NextClock;
|
||||
|
||||
public bool Ca1;
|
||||
public bool Ca2;
|
||||
public bool Cb1;
|
||||
public bool Cb2;
|
||||
private bool _pb6;
|
||||
|
||||
private int _interruptNextClock;
|
||||
private bool _t1CLoaded;
|
||||
private bool _t2CLoaded;
|
||||
private int _t1Delayed;
|
||||
private int _t2Delayed;
|
||||
private int _nextIrq;
|
||||
private bool _t1Reload;
|
||||
private bool _t1IrqAllowed;
|
||||
private bool _t2IrqAllowed;
|
||||
private int _ca1Hist;
|
||||
private int _ca2Hist;
|
||||
private int _cb1Hist;
|
||||
private int _cb2Hist;
|
||||
private int _pb6Hist;
|
||||
|
||||
public Func<bool> ReadCa1 = () => true;
|
||||
public Func<bool> ReadCa2 = () => true;
|
||||
public Func<bool> ReadCb1 = () => true;
|
||||
public Func<bool> ReadCb2 = () => true;
|
||||
|
||||
public Via()
|
||||
{
|
||||
_port = new DisconnectedPort();
|
||||
|
@ -98,15 +115,35 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
public Via(Func<bool> readClock, Func<bool> readData, Func<bool> readAtn, int driveNumber)
|
||||
{
|
||||
_port = new IecPort(readClock, readData, readAtn, driveNumber);
|
||||
_ca1L = true;
|
||||
}
|
||||
|
||||
public bool Irq => (_ifr & 0x80) == 0;
|
||||
public bool Irq => _irq;
|
||||
|
||||
// A note on these outputs: since the output level on the pin
|
||||
// can reflect the input level on the pin, in order to avoid
|
||||
// an endless loop, the most recently buffered pin values are used
|
||||
// instead of calling the Read functions.
|
||||
|
||||
public bool Ca2 => Ca2IsOutput
|
||||
? _ca2Out
|
||||
: (_ca2Hist & 1) != 0;
|
||||
|
||||
public bool Cb1 => Cb1IsOutput
|
||||
? _cb1Out
|
||||
: (_cb1Hist & 1) != 0;
|
||||
|
||||
public bool Cb2 => Cb2IsOutput
|
||||
? _cb2Out
|
||||
: (_cb2Hist & 1) != 0;
|
||||
|
||||
public bool Ca2IsOutput => (_pcr & PCR_CA2_OUT) != 0;
|
||||
public bool Cb1IsOutput => (_acr & ACR_SR_CLOCK) != ACR_SR_CLOCK_EXT && (_acr & ACR_SR) != 0;
|
||||
public bool Cb2IsOutput => (_pcr & PCR_CB2_OUT) != 0;
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
_pra = 0;
|
||||
_prb = 0;
|
||||
_ora = 0;
|
||||
_orb = 0;
|
||||
_ddra = 0;
|
||||
_ddrb = 0;
|
||||
_t1C = 0xFFFF;
|
||||
|
@ -117,335 +154,357 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|||
_acr = 0;
|
||||
_pcr = 0;
|
||||
_ifr = 0;
|
||||
_irq = false;
|
||||
_ier = 0;
|
||||
_paLatch = 0;
|
||||
_pbLatch = 0;
|
||||
_pcrCa1IntControl = 0;
|
||||
_pcrCa2Control = 0;
|
||||
_pcrCb1IntControl = 0;
|
||||
_pcrCb2Control = 0;
|
||||
_acrPaLatchEnable = false;
|
||||
_acrPbLatchEnable = false;
|
||||
_acrSrControl = 0;
|
||||
_acrT1Control = 0;
|
||||
_acrT2Control = 0;
|
||||
_ca1L = true;
|
||||
_cb1L = true;
|
||||
Ca1 = true;
|
||||
Ca2 = true;
|
||||
Cb1 = true;
|
||||
Cb2 = true;
|
||||
_srCount = 0;
|
||||
|
||||
_pb6L = true;
|
||||
_pb6 = true;
|
||||
_resetCa2NextClock = false;
|
||||
_resetCb2NextClock = false;
|
||||
_handshakeCa2NextClock = false;
|
||||
_handshakeCb2NextClock = false;
|
||||
_interruptNextClock = 0;
|
||||
_t1CLoaded = false;
|
||||
_t2CLoaded = false;
|
||||
_resetPb7NextClock = false;
|
||||
_setPb7NextClock = false;
|
||||
_ira = 0;
|
||||
_irb = 0;
|
||||
_ca2Out = true;
|
||||
_cb1Out = true;
|
||||
_cb2Out = true;
|
||||
_shiftCount = 0;
|
||||
_nextIrq = 0;
|
||||
_t1Out = true;
|
||||
_ca2Handshake = true;
|
||||
_cb2Handshake = true;
|
||||
_ca2Pulse = true;
|
||||
_cb2Pulse = true;
|
||||
_t1IrqAllowed = false;
|
||||
_shiftHist = ~0;
|
||||
_ca1Hist = ~0;
|
||||
_ca2Hist = ~0;
|
||||
_cb1Hist = ~0;
|
||||
_cb2Hist = ~0;
|
||||
_pb6Hist = ~0;
|
||||
_t1Reload = false;
|
||||
_t2IrqAllowed = false;
|
||||
_irq = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute one full phase of VIA logic.
|
||||
/// </summary>
|
||||
public void ExecutePhase()
|
||||
{
|
||||
var shiftIn = false;
|
||||
// Generated interrupts are available externally on the following clock.
|
||||
var thisIrq = _nextIrq;
|
||||
_ifr |= _nextIrq & IRQ_MASK;
|
||||
_irq = (_ier & _ifr & IRQ_MASK) != 0;
|
||||
_nextIrq = 0;
|
||||
|
||||
// TODO: use this or delete
|
||||
////var shiftOut = false;
|
||||
// Buffer PB6 for both loading IRB and PB6 edge detection later.
|
||||
var pbIn = _port.ReadExternalPrb();
|
||||
|
||||
// Process delayed interrupts
|
||||
_ifr |= _interruptNextClock;
|
||||
_interruptNextClock = 0;
|
||||
|
||||
// Process 'pulse' and 'handshake' outputs on PB7, CA2 and CB2
|
||||
if (_resetCa2NextClock)
|
||||
// IRA is loaded when:
|
||||
// - PA latch is disabled
|
||||
// - a handshake is triggered by transition on CA1
|
||||
if ((_acr & ACR_LATCH_PA) == 0 ||
|
||||
(_ca2Handshake && (thisIrq & IRQ_CA1) != 0))
|
||||
{
|
||||
Ca2 = true;
|
||||
_resetCa2NextClock = false;
|
||||
}
|
||||
else if (_handshakeCa2NextClock)
|
||||
{
|
||||
Ca2 = false;
|
||||
_resetCa2NextClock = _pcrCa2Control == PCR_CONTROL_PULSE_OUTPUT;
|
||||
_handshakeCa2NextClock = false;
|
||||
_ira = _port.ReadExternalPra();
|
||||
}
|
||||
|
||||
if (_resetCb2NextClock)
|
||||
// IRB is loaded when:
|
||||
// - PB latch is disabled
|
||||
// - a handshake is triggered by transition on CB1
|
||||
if ((_acr & ACR_LATCH_PB) == 0 ||
|
||||
(_cb2Handshake && (thisIrq & IRQ_CB1) != 0))
|
||||
{
|
||||
Cb2 = true;
|
||||
_resetCb2NextClock = false;
|
||||
}
|
||||
else if (_handshakeCb2NextClock)
|
||||
{
|
||||
Cb2 = false;
|
||||
_resetCb2NextClock = _pcrCb2Control == PCR_CONTROL_PULSE_OUTPUT;
|
||||
_handshakeCb2NextClock = false;
|
||||
_irb = pbIn;
|
||||
}
|
||||
|
||||
if (_resetPb7NextClock)
|
||||
{
|
||||
_prb &= 0x7F;
|
||||
_resetPb7NextClock = false;
|
||||
}
|
||||
else if (_setPb7NextClock)
|
||||
{
|
||||
_prb |= 0x80;
|
||||
_setPb7NextClock = false;
|
||||
}
|
||||
// CA1, CA2, CB1, and CB2 are all buffered for edge detection each clock.
|
||||
// The state of each pin is shifted in at bit 0 each clock.
|
||||
_ca1Hist = (_ca1Hist << 1) | (ReadCa1() ? 1 : 0);
|
||||
_ca2Hist = (_ca2Hist << 1) | (ReadCa2() ? 1 : 0);
|
||||
_cb1Hist = (_cb1Hist << 1) | (ReadCb1() ? 1 : 0);
|
||||
_cb2Hist = (_cb2Hist << 1) | (ReadCb2() ? 1 : 0);
|
||||
|
||||
// Count timers
|
||||
if (_t1Delayed > 0)
|
||||
// Handshake on CA2 occurs after an edge transition on CA1.
|
||||
if ((thisIrq & IRQ_CA1) != 0)
|
||||
{
|
||||
_t1Delayed--;
|
||||
_ca2Handshake = false;
|
||||
}
|
||||
|
||||
// CA2 has one of four level sources.
|
||||
_ca2Out = (_pcr & PCR_CA2_MODE) switch
|
||||
{
|
||||
PCR_CA2_MODE_HANDSHAKE => _ca2Handshake,
|
||||
PCR_CA2_MODE_PULSE => _ca2Pulse,
|
||||
PCR_CA2_MODE_LOW => false,
|
||||
_ => true
|
||||
};
|
||||
|
||||
_ca2Pulse = true;
|
||||
|
||||
// Handshake on CB2 occurs after an edge transition on CB1.
|
||||
if ((thisIrq & IRQ_CB1) != 0)
|
||||
{
|
||||
_cb2Handshake = false;
|
||||
}
|
||||
|
||||
// CB2 has one of four level sources.
|
||||
_cb2Out = (_pcr & PCR_CB2_MODE) switch
|
||||
{
|
||||
PCR_CB2_MODE_HANDSHAKE => _cb2Handshake,
|
||||
PCR_CB2_MODE_PULSE => _cb2Pulse,
|
||||
PCR_CB2_MODE_LOW => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
_cb2Pulse = true;
|
||||
|
||||
// PB6 edge detection occurs on the external pin independently of
|
||||
// the ORB and DDRB registers.
|
||||
_pb6Hist = (_pb6Hist << 1) | (pbIn & PB6_MASK);
|
||||
|
||||
// Output of T1 is toggled when an underflow occurs and
|
||||
// the timer output is enabled on PB7. The datasheet says
|
||||
// this occurs 1.5 cycles after the timer reaches zero.
|
||||
if ((thisIrq & IRQ_T1) != 0 && (_acr & ACR_T1_PB7_OUT) != 0)
|
||||
{
|
||||
_t1Out = !_t1Out;
|
||||
}
|
||||
|
||||
// T1 will reload on the cycle following reaching zero.
|
||||
if (_t1Reload)
|
||||
{
|
||||
if (_t1IrqAllowed)
|
||||
{
|
||||
_nextIrq |= IRQ_T1;
|
||||
}
|
||||
|
||||
if ((_acr & ACR_T1_FREERUN) != 0)
|
||||
{
|
||||
// In free run mode, T1 is reloaded directly
|
||||
// from the latch when an underflow occurs.
|
||||
// Subsequent interrupts are permitted.
|
||||
_t1C = _t1L;
|
||||
}
|
||||
else
|
||||
{
|
||||
// In one-shot mode, T1 is not reloaded when
|
||||
// an underflow occurs but continues to count
|
||||
// down. Subsequent interrupts are not permitted
|
||||
// until the high order latch register is written.
|
||||
_t1IrqAllowed = false;
|
||||
}
|
||||
|
||||
_t1Reload = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_t1C--;
|
||||
// When the counter reaches zero, the following cycle
|
||||
// will initiate the reload sequence.
|
||||
if (_t1C == 0)
|
||||
{
|
||||
switch (_acrT1Control)
|
||||
{
|
||||
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7:
|
||||
_prb ^= 0x80;
|
||||
break;
|
||||
case ACR_T1_CONTROL_INTERRUPT_ON_LOAD_AND_PULSE_PB7:
|
||||
_prb |= 0x80;
|
||||
break;
|
||||
}
|
||||
_t1Reload = true;
|
||||
}
|
||||
else if (_t1C < 0)
|
||||
|
||||
_t1C = (_t1C - 1) & 0xFFFF;
|
||||
}
|
||||
|
||||
// T2 either counts negative transitions on PB6 or operates as a one-shot.
|
||||
// The pulse output of T2 can be used with shift register operations.
|
||||
var srT2 = false;
|
||||
if ((_acr & ACR_T2_COUNT_PB6) == 0 || (_pb6Hist & PB6_TAP) == PB6_NEGATIVE_EDGE)
|
||||
{
|
||||
// When the counter reaches zero, an interrupt is generated
|
||||
// if the counter has been loaded since the last underflow.
|
||||
if (_t2C == 0)
|
||||
{
|
||||
if (_t1CLoaded)
|
||||
if (_t2IrqAllowed)
|
||||
{
|
||||
_interruptNextClock |= 0x40;
|
||||
_t1CLoaded = false;
|
||||
_nextIrq |= IRQ_T2;
|
||||
_t2IrqAllowed = false;
|
||||
}
|
||||
|
||||
switch (_acrT1Control)
|
||||
{
|
||||
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS:
|
||||
case ACR_T1_CONTROL_CONTINUOUS_INTERRUPTS_AND_OUTPUT_ON_PB7:
|
||||
_t1C = _t1L;
|
||||
_t1CLoaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_t1C &= 0xFFFF;
|
||||
if ((_t2C & 0xFF) == 0 && (_acr & ACR_SR_USE_T2) != 0)
|
||||
{
|
||||
// When T2 is used to clock shift register operations,
|
||||
// only the low order counter is reloaded.
|
||||
srT2 = true;
|
||||
_t2C = ((_t2C - 1) & ~0xFF) | (_t2L & 0xFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When T2 is not used to clock shift register operations,
|
||||
// both the low and high orders of the counter are reloaded.
|
||||
_t2C = (_t2C - 1) & 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
if (_t2Delayed > 0)
|
||||
// Edge detection for CA1, CA2, CB1, CB2.
|
||||
if ((_ca1Hist & EDGE_MASK) == ((_pcr & PCR_CA1_POLARITY) != 0 ? EDGE_POSITIVE : EDGE_NEGATIVE))
|
||||
{
|
||||
_t2Delayed--;
|
||||
_nextIrq |= IRQ_CA1;
|
||||
}
|
||||
|
||||
if ((_ca2Hist & EDGE_MASK) == ((_pcr & PCR_CA2_POLARITY) != 0 ? EDGE_POSITIVE : EDGE_NEGATIVE))
|
||||
{
|
||||
_nextIrq |= IRQ_CA2;
|
||||
}
|
||||
|
||||
if ((_cb1Hist & EDGE_MASK) == ((_pcr & PCR_CB1_POLARITY) != 0 ? EDGE_POSITIVE : EDGE_NEGATIVE))
|
||||
{
|
||||
_nextIrq |= IRQ_CB1;
|
||||
}
|
||||
|
||||
if ((_cb2Hist & EDGE_MASK) == ((_pcr & PCR_CB2_POLARITY) != 0 ? EDGE_POSITIVE : EDGE_NEGATIVE))
|
||||
{
|
||||
_nextIrq |= IRQ_CB2;
|
||||
}
|
||||
|
||||
// This reflects the internal timing of shift register operations.
|
||||
// There are several methods to clock the shifter. "srClock" is the
|
||||
// state of the flip-flop output itself, whereas "srPulse" determines
|
||||
// whether an external source is supposed to clock the shifter.
|
||||
var srClock = true;
|
||||
var srPulse = false;
|
||||
switch (_acr & ACR_SR_CLOCK)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
srPulse = !_shiftEnable
|
||||
? (_acr & ACR_SR_OUT) == 0 && (_shiftHist & EDGE_MASK) == EDGE_POSITIVE
|
||||
: srT2;
|
||||
srClock = !_shiftEnable
|
||||
? (_cb1Hist & EDGE_MASK) == EDGE_POSITIVE
|
||||
: (_shiftHist & EDGE_MASK) == EDGE_POSITIVE;
|
||||
break;
|
||||
}
|
||||
case ACR_SR_CLOCK_T2:
|
||||
{
|
||||
srPulse = _shiftEnable && srT2;
|
||||
srClock = !_shiftEnable || (_shiftHist & 0b1) == 0;
|
||||
break;
|
||||
}
|
||||
case ACR_SR_CLOCK_PHI2:
|
||||
{
|
||||
srPulse = _shiftEnable;
|
||||
srClock = (_shiftHist & 0b1) == 0;
|
||||
break;
|
||||
}
|
||||
case ACR_SR_CLOCK_EXT:
|
||||
{
|
||||
srPulse = _shiftEnable && (_shiftHist & EDGE_MASK) == EDGE_POSITIVE;
|
||||
srClock = (_cb1Hist & 0b1) == 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
srClock = true;
|
||||
srPulse = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_shiftHist = (_shiftHist << 1) | (srClock ? 1 : 0);
|
||||
|
||||
if (_srWritten)
|
||||
{
|
||||
// The shift register is not modified on the same cycle
|
||||
// that it is written.
|
||||
_srWritten = false;
|
||||
}
|
||||
else if ((_acr & ACR_SR_OUT) != 0)
|
||||
{
|
||||
// Shift bits out.
|
||||
if ((_shiftHist & EDGE_MASK) == EDGE_NEGATIVE)
|
||||
{
|
||||
_sr = ((_sr << 1) & 0xFF) | ((_sr >> 7) & 0x80);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (_acrT2Control)
|
||||
// Shift bits in from CB2.
|
||||
if ((_shiftHist & EDGE_MASK) == EDGE_POSITIVE)
|
||||
{
|
||||
case ACR_T2_CONTROL_TIMED:
|
||||
_t2C--;
|
||||
if (_t2C < 0)
|
||||
{
|
||||
if (_t2CLoaded)
|
||||
{
|
||||
_interruptNextClock |= 0x20;
|
||||
_t2CLoaded = false;
|
||||
}
|
||||
_t2C = _t2L;
|
||||
}
|
||||
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;
|
||||
_sr = ((_sr << 1) & 0xFF) | ((_cb2Hist & 0b10) >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Process CA2
|
||||
switch (_pcrCa2Control)
|
||||
// In the event that a positive edge is sensed on the shift
|
||||
// register clock, and the shift register has not yet been
|
||||
// activated, an interrupt is generated.
|
||||
if (!_shiftEnable && (_shiftHist & EDGE_MASK) == EDGE_POSITIVE && (_acr & ACR_SR) != 0)
|
||||
{
|
||||
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;
|
||||
_nextIrq |= IRQ_SR;
|
||||
}
|
||||
|
||||
// Process CB2
|
||||
switch (_pcrCb2Control)
|
||||
if (!_shiftEnable && (_acr & ACR_SR) != 0)
|
||||
{
|
||||
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;
|
||||
if (_srAccessed)
|
||||
{
|
||||
_shiftCount = 7;
|
||||
_shiftEnable = true;
|
||||
}
|
||||
}
|
||||
|
||||
// interrupt generation
|
||||
|
||||
if (_acrSrControl == ACR_SR_CONTROL_DISABLED)
|
||||
{
|
||||
_ifr &= 0xFB;
|
||||
_srCount = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
As long as the CA1 interrupt flag is set, the data on the peripheral pins can change
|
||||
without affecting the data in the latches. This input latching can be used with any of the CA2
|
||||
input or output modes.
|
||||
It is important to note that on the PA port, the processor always reads the data on the
|
||||
peripheral pins (as reflected in the latches). For output pins, the processor still reads the
|
||||
latches. This may or may not reflect the data currently in the ORA. Proper system operation
|
||||
requires careful planning on the part of the system designer if input latching is combined
|
||||
with output pins on the peripheral ports.
|
||||
*/
|
||||
|
||||
if ((_pcrCa1IntControl is PCR_INT_CONTROL_POSITIVE_EDGE && Ca1 && !_ca1L)
|
||||
|| (_pcrCa1IntControl is PCR_INT_CONTROL_NEGATIVE_EDGE && !Ca1 && _ca1L))
|
||||
{
|
||||
if (_acrPaLatchEnable && (_ifr & 0x02) == 0)
|
||||
_paLatch = _port.ReadExternalPra();
|
||||
_ifr |= 0x02;
|
||||
}
|
||||
|
||||
/*
|
||||
Input latching on the PB port is controlled in the same manner as that described for the PA port.
|
||||
However, with the peripheral B port the input latch will store either the voltage on the pin or the contents
|
||||
of the Output Register (ORB) depending on whether the pin is programmed to act as an input or an
|
||||
output. As with the PA port, the processor always reads the input latches.
|
||||
*/
|
||||
|
||||
if ((_pcrCb1IntControl is PCR_INT_CONTROL_POSITIVE_EDGE && Cb1 && !_cb1L)
|
||||
|| (_pcrCb1IntControl is PCR_INT_CONTROL_NEGATIVE_EDGE && !Cb1 && _cb1L))
|
||||
{
|
||||
if (_acrPbLatchEnable && (_ifr & 0x10) == 0)
|
||||
_pbLatch = _port.ReadPrb(_prb, _ddrb);
|
||||
if (_acrSrControl == ACR_SR_CONTROL_DISABLED)
|
||||
shiftIn = true;
|
||||
_ifr |= 0x10;
|
||||
}
|
||||
|
||||
if (shiftIn)
|
||||
{
|
||||
_sr <<= 1;
|
||||
_sr |= Cb2 ? 1 : 0;
|
||||
}
|
||||
|
||||
if ((_ifr & _ier & 0x7F) != 0)
|
||||
_ifr |= 0x80;
|
||||
else
|
||||
_ifr &= 0x7F;
|
||||
{
|
||||
if ((_acr & ACR_SR_CLOCK) == 0)
|
||||
{
|
||||
_shiftEnable = (_acr & ACR_SR_OUT) != 0;
|
||||
}
|
||||
else if (srClock && srPulse)
|
||||
{
|
||||
if (_shiftCount == 0)
|
||||
{
|
||||
_shiftEnable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_shiftCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ca1L = Ca1;
|
||||
_ca2L = Ca2;
|
||||
_cb1L = Cb1;
|
||||
_cb2L = Cb2;
|
||||
_srAccessed = false;
|
||||
}
|
||||
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
ser.Sync("PortOutputA", ref _pra);
|
||||
ser.Sync("PortDirectionA", ref _ddra);
|
||||
ser.Sync("PortOutputB", ref _prb);
|
||||
ser.Sync("PortDirectionB", ref _ddrb);
|
||||
ser.Sync("Timer1Counter", ref _t1C);
|
||||
ser.Sync("Timer1Latch", ref _t1L);
|
||||
ser.Sync("Timer2Counter", ref _t2C);
|
||||
ser.Sync("Timer2Latch", ref _t2L);
|
||||
ser.Sync("ShiftRegister", ref _sr);
|
||||
ser.Sync("AuxiliaryControlRegister", ref _acr);
|
||||
ser.Sync("PeripheralControlRegister", ref _pcr);
|
||||
ser.Sync("InterruptFlagRegister", ref _ifr);
|
||||
ser.Sync("InterruptEnableRegister", ref _ier);
|
||||
ser.Sync(nameof(_ora), ref _ora);
|
||||
ser.Sync(nameof(_ddra), ref _ddra);
|
||||
ser.Sync(nameof(_orb), ref _orb);
|
||||
ser.Sync(nameof(_ddrb), ref _ddrb);
|
||||
ser.Sync(nameof(_t1C), ref _t1C);
|
||||
ser.Sync(nameof(_t1L), ref _t1L);
|
||||
ser.Sync(nameof(_t2C), ref _t2C);
|
||||
ser.Sync(nameof(_t2L), ref _t2L);
|
||||
ser.Sync(nameof(_sr), ref _sr);
|
||||
ser.Sync(nameof(_acr), ref _acr);
|
||||
ser.Sync(nameof(_pcr), ref _pcr);
|
||||
ser.Sync(nameof(_ifr), ref _ifr);
|
||||
ser.Sync(nameof(_ier), ref _ier);
|
||||
|
||||
ser.BeginSection("Port");
|
||||
_port.SyncState(ser);
|
||||
ser.EndSection();
|
||||
|
||||
ser.Sync("PortLatchA", ref _paLatch);
|
||||
ser.Sync("PortLatchB", ref _pbLatch);
|
||||
ser.Sync("CA1InterruptControl", ref _pcrCa1IntControl);
|
||||
ser.Sync("CA2Control", ref _pcrCa2Control);
|
||||
ser.Sync("CB1InterruptControl", ref _pcrCb1IntControl);
|
||||
ser.Sync("CB2Control", ref _pcrCb2Control);
|
||||
ser.Sync("PortLatchEnableA", ref _acrPaLatchEnable);
|
||||
ser.Sync("PortLatchEnableB", ref _acrPbLatchEnable);
|
||||
ser.Sync("ShiftRegisterControl", ref _acrSrControl);
|
||||
ser.Sync("Timer1Control", ref _acrT1Control);
|
||||
ser.Sync("Timer2Control", ref _acrT2Control);
|
||||
ser.Sync("PreviousCA1", ref _ca1L);
|
||||
ser.Sync("PreviousCA2", ref _ca2L);
|
||||
ser.Sync("PreviousCB1", ref _cb1L);
|
||||
ser.Sync("PreviousCB2", ref _cb2L);
|
||||
ser.Sync("PreviousPB6", ref _pb6L);
|
||||
ser.Sync("ResetCa2NextClock", ref _resetCa2NextClock);
|
||||
ser.Sync("ResetCb2NextClock", ref _resetCb2NextClock);
|
||||
ser.Sync("HandshakeCa2NextClock", ref _handshakeCa2NextClock);
|
||||
ser.Sync("HandshakeCb2NextClock", ref _handshakeCb2NextClock);
|
||||
ser.Sync("CA1", ref Ca1);
|
||||
ser.Sync("CA2", ref Ca2);
|
||||
ser.Sync("CB1", ref Cb1);
|
||||
ser.Sync("CB2", ref Cb2);
|
||||
ser.Sync("PB6", ref _pb6);
|
||||
ser.Sync("InterruptNextClock", ref _interruptNextClock);
|
||||
ser.Sync("T1Loaded", ref _t1CLoaded);
|
||||
ser.Sync("T2Loaded", ref _t2CLoaded);
|
||||
ser.Sync("T1Delayed", ref _t1Delayed);
|
||||
ser.Sync("T2Delayed", ref _t2Delayed);
|
||||
ser.Sync("ResetPb7NextClock", ref _resetPb7NextClock);
|
||||
ser.Sync("SetPb7NextClock", ref _setPb7NextClock);
|
||||
ser.Sync("ShiftRegisterCount", ref _srCount);
|
||||
ser.Sync(nameof(_ira), ref _ira);
|
||||
ser.Sync(nameof(_irb), ref _irb);
|
||||
ser.Sync(nameof(_ca1Hist), ref _ca1Hist);
|
||||
ser.Sync(nameof(_ca2Hist), ref _ca2Hist);
|
||||
ser.Sync(nameof(_cb1Hist), ref _cb1Hist);
|
||||
ser.Sync(nameof(_cb2Hist), ref _cb2Hist);
|
||||
ser.Sync(nameof(_pb6Hist), ref _pb6Hist);
|
||||
ser.Sync(nameof(_ca2Handshake), ref _ca2Handshake);
|
||||
ser.Sync(nameof(_cb2Handshake), ref _cb2Handshake);
|
||||
ser.Sync(nameof(_ca2Out), ref _ca2Out);
|
||||
ser.Sync(nameof(_cb1Out), ref _cb1Out);
|
||||
ser.Sync(nameof(_cb2Out), ref _cb2Out);
|
||||
ser.Sync(nameof(_nextIrq), ref _nextIrq);
|
||||
ser.Sync(nameof(_shiftCount), ref _shiftCount);
|
||||
ser.Sync(nameof(_t1IrqAllowed), ref _t1IrqAllowed);
|
||||
ser.Sync(nameof(_t1Out), ref _t1Out);
|
||||
ser.Sync(nameof(_shiftEnable), ref _shiftEnable);
|
||||
ser.Sync(nameof(_shiftDir), ref _shiftDir);
|
||||
ser.Sync(nameof(_shiftHist), ref _shiftHist);
|
||||
ser.Sync(nameof(_irq), ref _irq);
|
||||
ser.Sync(nameof(_t1Reload), ref _t1Reload);
|
||||
ser.Sync(nameof(_t2IrqAllowed), ref _t2IrqAllowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,21 +105,19 @@ public sealed class DiskTrack
|
|||
// in the 1541 disk drive. Outer tracks have more surface area, so a technique is used to read
|
||||
// bits at a higher rate.
|
||||
|
||||
var paddedLength = density switch
|
||||
{
|
||||
3 => Math.Max(bytes.Length, 7692),
|
||||
2 => Math.Max(bytes.Length, 7142),
|
||||
1 => Math.Max(bytes.Length, 6666),
|
||||
0 => Math.Max(bytes.Length, 6250),
|
||||
_ => bytes.Length
|
||||
};
|
||||
|
||||
// One extra byte is added at the end to break up tracks so that if the data is perfectly
|
||||
// Extra bits are added at the end to break up tracks so that if the data is perfectly
|
||||
// aligned in an unfortunate way, loaders don't seize up trying to find data. Some copy protections
|
||||
// will read the same track repeatedly to account for variations in drive mechanics, and this should get
|
||||
// the more temperamental ones to load eventually.
|
||||
|
||||
paddedLength++;
|
||||
var paddedLength = density switch
|
||||
{
|
||||
3 => Math.Max(bytes.Length, 7820),
|
||||
2 => Math.Max(bytes.Length, 7170),
|
||||
1 => Math.Max(bytes.Length, 6300),
|
||||
0 => Math.Max(bytes.Length, 6020),
|
||||
_ => bytes.Length
|
||||
};
|
||||
|
||||
// It is possible that there are more or fewer bits than the specification due to any number
|
||||
// of reasons (e.g. copy protection, tiny variations in motor speed) so we pad out with the "default"
|
||||
|
@ -128,34 +126,23 @@ public sealed class DiskTrack
|
|||
using var paddedBytesMem = MemoryPool<byte>.Shared.Rent(paddedLength);
|
||||
var paddedBytes = paddedBytesMem.Memory.Span.Slice(0, paddedLength);
|
||||
bytes.CopyTo(paddedBytes);
|
||||
paddedBytes.Slice(bytes.Length).Fill(0xAA);
|
||||
|
||||
var lengthBits = paddedLength * 8 - 7;
|
||||
var remainingBits = lengthBits;
|
||||
paddedBytes.Slice(bytes.Length).Fill(0x55);
|
||||
|
||||
long bitsDen = paddedLength * 8 + 3;
|
||||
const long bitsNum = FluxEntriesPerTrack * FluxBitsPerEntry;
|
||||
long bitsDen = lengthBits;
|
||||
|
||||
var bitIdx = 0;
|
||||
for (var i = 0; i < paddedLength; i++)
|
||||
{
|
||||
var byteData = paddedBytes[i];
|
||||
for (var j = 0; j < 8; j++)
|
||||
{
|
||||
var offset = fluxBitOffset + ((i * 8 + j) * bitsNum / bitsDen);
|
||||
var byteOffset = (int)(offset / FluxBitsPerEntry);
|
||||
var bitOffset = (int)(offset % FluxBitsPerEntry);
|
||||
_bits[byteOffset] |= (byteData >> 7) << bitOffset;
|
||||
var bit = fluxBitOffset + (bitIdx * bitsNum / bitsDen);
|
||||
bitIdx++;
|
||||
var entry = (int)(bit / FluxBitsPerEntry % FluxEntriesPerTrack);
|
||||
var entryBit = (int)(bit % FluxBitsPerEntry);
|
||||
_bits[entry] |= (byteData >> 7) << entryBit;
|
||||
byteData <<= 1;
|
||||
remainingBits--;
|
||||
if (remainingBits <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingBits <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
|||
if (_diskWriteEnabled && track.Write(_diskByteOffset, _diskOutputBits))
|
||||
{
|
||||
_dirtyDiskTracks[_getCurrentDiskNumber()][_trackNumber] = true;
|
||||
SaveRamModified = true;
|
||||
}
|
||||
|
||||
_diskByteOffset++;
|
||||
|
@ -144,7 +145,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
|||
_byteReady = false;
|
||||
if (_diskWriteBitsRemaining <= 0)
|
||||
{
|
||||
_diskWriteLatch = Via1.EffectivePrA;
|
||||
_diskWriteLatch = Via1.PrA;
|
||||
_diskWriteBitsRemaining = 8;
|
||||
_byteReady = Via1.Ca2;
|
||||
}
|
||||
|
@ -178,9 +179,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
|||
}
|
||||
|
||||
// negative transition activates SO pin on CPU
|
||||
_previousCa1 = Via1.Ca1;
|
||||
Via1.Ca1 = !_byteReady;
|
||||
if (_previousCa1 && !Via1.Ca1)
|
||||
_previousCa1 = _via1Ca1;
|
||||
_via1Ca1 = !_byteReady;
|
||||
if (_previousCa1 && !_via1Ca1)
|
||||
{
|
||||
// cycle 6 is roughly 400ns
|
||||
_overflowFlagDelaySr |= _diskCycle > 6 ? 4 : 2;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
private void ExecuteMotor()
|
||||
{
|
||||
_tempPrB1 = Via1.EffectivePrB;
|
||||
_tempPrB1 = Via1.PrB | ~Via1.DdrB;
|
||||
_tempStep = _tempPrB1 & 0x3;
|
||||
_diskDensity = (_tempPrB1 & 0x60) >> 5;
|
||||
_motorEnabled = (_tempPrB1 & 0x04) != 0;
|
||||
|
|
|
@ -33,6 +33,11 @@
|
|||
return !(inputData && outputData);
|
||||
}
|
||||
|
||||
private bool ViaReadByteReady()
|
||||
{
|
||||
return _via1Ca1;
|
||||
}
|
||||
|
||||
private bool ViaReadAtn()
|
||||
{
|
||||
var inputAtn = ReadMasterAtn();
|
||||
|
@ -114,43 +119,36 @@
|
|||
|
||||
public int Read(int addr)
|
||||
{
|
||||
switch (addr & 0xFC00)
|
||||
{
|
||||
case 0x1800:
|
||||
return Via0.Read(addr);
|
||||
case 0x1C00:
|
||||
return Via1.Read(addr);
|
||||
}
|
||||
|
||||
if ((addr & 0x8000) != 0)
|
||||
{
|
||||
return DriveRom.Read(addr & 0x3FFF);
|
||||
}
|
||||
|
||||
if ((addr & 0x1F00) < 0x800)
|
||||
switch (addr & 0x1C00)
|
||||
{
|
||||
return _ram[addr & 0x7FF];
|
||||
case < 0x800:
|
||||
return _ram[addr & 0x7FF];
|
||||
case 0x1800:
|
||||
return Via0.Read(addr);
|
||||
case 0x1C00:
|
||||
return Via1.Read(addr);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (addr >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
public void Write(int addr, int val)
|
||||
{
|
||||
switch (addr & 0xFC00)
|
||||
switch (addr & 0x1C00)
|
||||
{
|
||||
case < 0x800:
|
||||
_ram[addr & 0x7FF] = val & 0xFF;
|
||||
break;
|
||||
case 0x1800:
|
||||
Via0.Write(addr, val);
|
||||
break;
|
||||
case 0x1C00:
|
||||
Via1.Write(addr, val);
|
||||
break;
|
||||
default:
|
||||
if ((addr & 0x8000) == 0 && (addr & 0x1F00) < 0x800)
|
||||
{
|
||||
_ram[addr & 0x7FF] = val & 0xFF;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ public sealed partial class Drive1541 : ISaveRam
|
|||
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
SaveDeltas();
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
using var bw = new BinaryWriter(ms);
|
||||
bw.Write(_usedDiskTracks.Length);
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
|||
public Func<int> ReadIec = () => 0xFF;
|
||||
public Action DebuggerStep;
|
||||
public readonly Chip23128 DriveRom;
|
||||
private bool _via1Ca1;
|
||||
|
||||
private struct CpuLink : IMOS6502XLink
|
||||
{
|
||||
|
@ -61,7 +62,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
|||
|
||||
_ram = new int[0x800];
|
||||
Via0 = Chip6522.Create(ViaReadClock, ViaReadData, ViaReadAtn, 8);
|
||||
Via0.ReadCa1 = ViaReadAtn;
|
||||
Via1 = Chip6522.Create(ReadVia1PrA, ReadVia1PrB);
|
||||
Via1.ReadCa1 = ViaReadByteReady;
|
||||
|
||||
_cpuClockNum = clockNum;
|
||||
_driveCpuClockNum = clockDen * 16000000; // 16mhz
|
||||
|
@ -98,29 +101,30 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
|||
Via1.SyncState(ser);
|
||||
ser.EndSection();
|
||||
|
||||
ser.Sync("SystemCpuClockNumerator", ref _cpuClockNum);
|
||||
ser.Sync("SystemDriveCpuRatioDifference", ref _ratioDifference);
|
||||
ser.Sync("DriveLightOffTime", ref _driveLightOffTime);
|
||||
ser.Sync(nameof(_cpuClockNum), ref _cpuClockNum);
|
||||
ser.Sync(nameof(_ratioDifference), ref _ratioDifference);
|
||||
ser.Sync(nameof(_driveLightOffTime), ref _driveLightOffTime);
|
||||
|
||||
ser.Sync("DiskDensityCounter", ref _diskDensityCounter);
|
||||
ser.Sync("DiskSupplementaryCounter", ref _diskSupplementaryCounter);
|
||||
ser.Sync("DiskFluxReversalDetected", ref _diskFluxReversalDetected);
|
||||
ser.Sync("DiskBitsRemainingInDataEntry", ref _diskBitsLeft);
|
||||
ser.Sync("DiskDataEntryIndex", ref _diskByteOffset);
|
||||
ser.Sync("DiskDataEntry", ref _diskBits);
|
||||
ser.Sync("DiskCurrentCycle", ref _diskCycle);
|
||||
ser.Sync("DiskDensityConfig", ref _diskDensity);
|
||||
ser.Sync("PreviousCA1", ref _previousCa1);
|
||||
ser.Sync("CountsBeforeRandomTransition", ref _countsBeforeRandomTransition);
|
||||
ser.Sync("CurrentRNG", ref _rngCurrent);
|
||||
ser.Sync("Clocks", ref _clocks);
|
||||
ser.Sync("CpuClocks", ref _cpuClocks);
|
||||
ser.Sync("OverflowFlagDelayShiftRegister", ref _overflowFlagDelaySr);
|
||||
ser.Sync("DiskWriteBitsRemaining", ref _diskWriteBitsRemaining);
|
||||
ser.Sync("DiskWriteEnabled", ref _diskWriteEnabled);
|
||||
ser.Sync("DiskWriteLatch", ref _diskWriteLatch);
|
||||
ser.Sync("DiskOutputBits", ref _diskOutputBits);
|
||||
ser.Sync("DiskWriteProtected", ref _diskWriteProtected);
|
||||
ser.Sync(nameof(_diskDensityCounter), ref _diskDensityCounter);
|
||||
ser.Sync(nameof(_diskSupplementaryCounter), ref _diskSupplementaryCounter);
|
||||
ser.Sync(nameof(_diskFluxReversalDetected), ref _diskFluxReversalDetected);
|
||||
ser.Sync(nameof(_diskBitsLeft), ref _diskBitsLeft);
|
||||
ser.Sync(nameof(_diskByteOffset), ref _diskByteOffset);
|
||||
ser.Sync(nameof(_diskBits), ref _diskBits);
|
||||
ser.Sync(nameof(_diskCycle), ref _diskCycle);
|
||||
ser.Sync(nameof(_diskDensity), ref _diskDensity);
|
||||
ser.Sync(nameof(_previousCa1), ref _previousCa1);
|
||||
ser.Sync(nameof(_countsBeforeRandomTransition), ref _countsBeforeRandomTransition);
|
||||
ser.Sync(nameof(_rngCurrent), ref _rngCurrent);
|
||||
ser.Sync(nameof(_clocks), ref _clocks);
|
||||
ser.Sync(nameof(_cpuClocks), ref _cpuClocks);
|
||||
ser.Sync(nameof(_overflowFlagDelaySr), ref _overflowFlagDelaySr);
|
||||
ser.Sync(nameof(_diskWriteBitsRemaining), ref _diskWriteBitsRemaining);
|
||||
ser.Sync(nameof(_diskWriteEnabled), ref _diskWriteEnabled);
|
||||
ser.Sync(nameof(_diskWriteLatch), ref _diskWriteLatch);
|
||||
ser.Sync(nameof(_diskOutputBits), ref _diskOutputBits);
|
||||
ser.Sync(nameof(_diskWriteProtected), ref _diskWriteProtected);
|
||||
ser.Sync(nameof(_via1Ca1), ref _via1Ca1);
|
||||
|
||||
if (ser.IsReader)
|
||||
{
|
||||
|
@ -133,10 +137,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
|||
|
||||
for (var diskNumber = 0; diskNumber < _usedDiskTracks.Length; diskNumber++)
|
||||
{
|
||||
ser.Sync($"_usedDiskTracks{diskNumber}", ref _usedDiskTracks[diskNumber], useNull: false);
|
||||
ser.Sync($"{nameof(_usedDiskTracks)}{diskNumber}", ref _usedDiskTracks[diskNumber], useNull: false);
|
||||
for (var trackNumber = 0; trackNumber < 84; trackNumber++)
|
||||
{
|
||||
ser.Sync($"DiskDeltas{diskNumber},{trackNumber}", ref _diskDeltas[diskNumber][trackNumber], useNull: true);
|
||||
ser.Sync($"{nameof(_diskDeltas)}{diskNumber},{trackNumber}", ref _diskDeltas[diskNumber][trackNumber], useNull: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,7 +171,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
|||
|
||||
private void ExecuteSystem()
|
||||
{
|
||||
Via0.Ca1 = ViaReadAtn();
|
||||
Via0.ExecutePhase();
|
||||
Via1.ExecutePhase();
|
||||
|
||||
|
@ -179,7 +182,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
|
|||
|
||||
_overflowFlagDelaySr >>= 1;
|
||||
|
||||
_cpu.IRQ = !(Via0.Irq && Via1.Irq); // active low IRQ line
|
||||
_cpu.IRQ = Via0.Irq || Via1.Irq;
|
||||
_cpu.ExecuteOne();
|
||||
|
||||
if (_ledEnabled)
|
||||
|
|
Loading…
Reference in New Issue