diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Cia.Registers.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Cia.Registers.cs index 8d435bfdf2..697cfbe7a5 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Cia.Registers.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Cia.Registers.cs @@ -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; } } diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Cia.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Cia.cs index 22bf8856ef..eb90f6dd2c 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Cia.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Cia.cs @@ -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); } } } diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Via.Registers.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Via.Registers.cs index a5b122724b..c8bfa5d14c 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Via.Registers.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Via.Registers.cs @@ -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; } } diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Via.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Via.cs index f278f377e3..a40b86fd91 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Via.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Via.cs @@ -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 ReadCa1 = () => true; + public Func ReadCa2 = () => true; + public Func ReadCb1 = () => true; + public Func ReadCb2 = () => true; + public Via() { _port = new DisconnectedPort(); @@ -98,15 +115,35 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS public Via(Func readClock, Func readData, Func 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; } + /// + /// Execute one full phase of VIA logic. + /// 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); } } } diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Media/DiskTrack.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Media/DiskTrack.cs index b6a6f11c0b..afdb39d584 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Media/DiskTrack.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Media/DiskTrack.cs @@ -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.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; } } diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.FluxTransitions.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.FluxTransitions.cs index 837d85afcb..672ff10019 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.FluxTransitions.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.FluxTransitions.cs @@ -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; diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.Motor.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.Motor.cs index 5247824497..3463af7047 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.Motor.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.Motor.cs @@ -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; diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.Registers.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.Registers.cs index 44259d3899..08c9e6ab89 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.Registers.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.Registers.cs @@ -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; } } diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.SaveRam.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.SaveRam.cs index 214b9fb074..d06f4d5397 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.SaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.SaveRam.cs @@ -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); diff --git a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs index 70ceb86a8f..9ac79e9db5 100644 --- a/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs +++ b/src/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs @@ -28,6 +28,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial public Func 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)