using System; using BizHawk.Common; namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS { public sealed partial class Cia { /* Commodore CIA 6526 core. Many thanks to: - 6502.org for hosting the 6526 datasheet http://archive.6502.org/datasheets/mos_6526_cia.pdf - Christian Bauer for information on the delayed interrupt mechanism on the 6526 http://frodo.cebix.net/ */ // operation of the interrupt at the serial data port occurs in 2 instances: // 1. Being in output mode and having a complete transfer as defined by clocking of timer A // 2. Being in input mode and receiving 8 clocks from the /CNT pin // This is TODO private enum TimerState { Stop = 0, WaitThenCount = 1, LoadThenStop = 2, LoadThenCount = 3, LoadThenWaitThenCount = 4, Count = 5, CountThenStop = 6 } public Func ReadFlag = () => true; public Func ReadCnt = () => true; public Func ReadSp = () => true; public bool DelayedInterrupts = true; private int _pra; private int _prb; private int _ddra; private int _ddrb; private int _ta; private int _tb; private int _latcha; private int _latchb; private int _tod10Ths; private int _todSec; private int _todMin; private int _todHr; private int _latch10Ths; private int _latchSec; private int _latchMin; private int _latchHr; private int _alm10Ths; private int _almSec; private int _almMin; private int _almHr; private int _sdr; private int _icr; private int _cra; private int _crb; private int _intMask; private bool _todLatch; private bool _taCntPhi2; private bool _taCntCnt; private bool _tbCntPhi2; private bool _tbCntTa; private bool _tbCntCnt; private bool _taIrqNextCycle; private bool _taPrb6NegativeNextCycle; private bool _tbIrqNextCycle; private bool _tbPrb7NegativeNextCycle; private bool _hasNewCra; private bool _hasNewCrb; private TimerState _taState; private TimerState _tbState; private int _newCra; private int _newCrb; private bool _flagLatch; private bool _flagInput; private bool _taUnderflow; private bool _lastCnt; private bool _thisCnt; private bool _tbCntTaCnt; private readonly IPort _port; private int _todlo; private int _todhi; private readonly int _todNum; private readonly int _todDen; private int _todCounter; private Cia(int todNum, int todDen) { _todNum = todNum; _todDen = todDen; } public Cia(int todNum, int todDen, Func keyboard, Func joysticks) : this(todNum, todDen) { _port = new JoystickKeyboardPort(joysticks, keyboard); } public Cia(int todNum, int todDen, Func readIec, Func readUserPort) : this(todNum, todDen) { _port = new IecPort(readIec, readUserPort); } public void HardReset() { _pra = 0; _prb = 0; _ddra = 0; _ddrb = 0; _ta = 0xFFFF; _tb = 0xFFFF; _latcha = 1; _latchb = 1; _tod10Ths = 0; _todSec = 0; _todMin = 0; _todHr = 0; _alm10Ths = 0; _almSec = 0; _almMin = 0; _almHr = 0; _sdr = 0; _icr = 0; _cra = 0; _crb = 0; _intMask = 0; _todLatch = false; _taCntPhi2 = false; _taCntCnt = false; _tbCntPhi2 = false; _tbCntTa = false; _tbCntCnt = false; _taIrqNextCycle = false; _tbIrqNextCycle = false; _taState = TimerState.Stop; _tbState = TimerState.Stop; _lastCnt = true; } public void ExecutePhase() { _thisCnt = ReadCnt(); _taUnderflow = false; if (_taIrqNextCycle) { _taIrqNextCycle = false; TriggerInterrupt(1); } if (_tbIrqNextCycle) { _tbIrqNextCycle = false; TriggerInterrupt(2); } if (_taPrb6NegativeNextCycle) { _prb &= 0xBF; _taPrb6NegativeNextCycle = false; } if (_tbPrb7NegativeNextCycle) { _prb &= 0x7F; _tbPrb7NegativeNextCycle = false; } switch (_taState) { case TimerState.WaitThenCount: _taState = TimerState.Count; Ta_Idle(); break; case TimerState.Stop: Ta_Idle(); break; case TimerState.LoadThenStop: _taState = TimerState.Stop; _ta = _latcha; Ta_Idle(); break; case TimerState.LoadThenCount: _taState = TimerState.Count; _ta = _latcha; Ta_Idle(); break; case TimerState.LoadThenWaitThenCount: _taState = TimerState.WaitThenCount; if (_ta == 1) { Ta_Interrupt(); _taUnderflow = true; } else { _ta = _latcha; } Ta_Idle(); break; case TimerState.Count: Ta_Count(); break; case TimerState.CountThenStop: _taState = TimerState.Stop; Ta_Count(); break; } switch (_tbState) { case TimerState.WaitThenCount: _tbState = TimerState.Count; Tb_Idle(); break; case TimerState.Stop: Tb_Idle(); break; case TimerState.LoadThenStop: _tbState = TimerState.Stop; _tb = _latchb; Tb_Idle(); break; case TimerState.LoadThenCount: _tbState = TimerState.Count; _tb = _latchb; Tb_Idle(); break; case TimerState.LoadThenWaitThenCount: _tbState = TimerState.WaitThenCount; if (_tb == 1) { Tb_Interrupt(); } else { _tb = _latchb; } Tb_Idle(); break; case TimerState.Count: Tb_Count(); break; case TimerState.CountThenStop: _tbState = TimerState.Stop; Tb_Count(); break; } CountTod(); if (!_todLatch) { _latch10Ths = _tod10Ths; _latchSec = _todSec; _latchMin = _todMin; _latchHr = _todHr; } _flagInput = ReadFlag(); if (!_flagInput && _flagLatch) { TriggerInterrupt(16); } _flagLatch = _flagInput; if ((_cra & 0x02) != 0) _ddra |= 0x40; if ((_crb & 0x02) != 0) _ddrb |= 0x80; _lastCnt = _thisCnt; } private void Ta_Count() { if (_taCntPhi2 || (_taCntCnt && !_lastCnt && _thisCnt)) { if (_ta <= 0 || --_ta == 0) { if (_taState != TimerState.Stop) { Ta_Interrupt(); } _taUnderflow = true; } } Ta_Idle(); } private void Ta_Interrupt() { _ta = _latcha; if (DelayedInterrupts) _taIrqNextCycle = true; else TriggerInterrupt(1); _icr |= 1; if ((_cra & 0x08) != 0) { _cra &= 0xFE; _newCra &= 0xFE; _taState = TimerState.LoadThenStop; } else { _taState = TimerState.LoadThenCount; } if ((_cra & 0x02) != 0) { if ((_cra & 0x04) != 0) { _taPrb6NegativeNextCycle = true; _prb |= 0x40; } else { _prb ^= 0x40; } _ddrb |= 0x40; } } private void Ta_Idle() { if (_hasNewCra) { switch (_taState) { case TimerState.Stop: case TimerState.LoadThenStop: if ((_newCra & 0x01) != 0) { _taState = (_newCra & 0x10) != 0 ? TimerState.LoadThenWaitThenCount : TimerState.WaitThenCount; } else { if ((_newCra & 0x10) != 0) { _taState = TimerState.LoadThenStop; } } break; case TimerState.Count: if ((_newCra & 0x01) != 0) { if ((_newCra & 0x10) != 0) { _taState = TimerState.LoadThenWaitThenCount; } } else { _taState = (_newCra & 0x10) != 0 ? TimerState.LoadThenStop : TimerState.CountThenStop; } break; case TimerState.LoadThenCount: case TimerState.WaitThenCount: if ((_newCra & 0x01) != 0) { if ((_newCra & 0x08) != 0) { _newCra &= 0xFE; _taState = TimerState.Stop; } else if ((_newCra & 0x10) != 0) { _taState = TimerState.LoadThenWaitThenCount; } } else { _taState = TimerState.Stop; } break; } _cra = _newCra & 0xEF; _hasNewCra = false; } } private void Tb_Count() { if (_tbCntPhi2 || (_tbCntTa && _taUnderflow) || (_tbCntTaCnt && _taUnderflow && _thisCnt) || (_tbCntCnt && !_lastCnt && _thisCnt)) { if (_tb <= 0 || --_tb == 0) { if (_tbState != TimerState.Stop) { Tb_Interrupt(); } } } Tb_Idle(); } private void Tb_Interrupt() { _tb = _latchb; if (DelayedInterrupts) _tbIrqNextCycle = true; else TriggerInterrupt(2); _icr |= 2; if ((_crb & 0x08) != 0) { _crb &= 0xFE; _newCrb &= 0xFE; _tbState = TimerState.LoadThenStop; } else { _tbState = TimerState.LoadThenCount; } if ((_crb & 0x02) != 0) { if ((_crb & 0x04) != 0) { _tbPrb7NegativeNextCycle = true; _prb |= 0x80; } else { _prb ^= 0x80; } } } private void Tb_Idle() { if (_hasNewCrb) { switch (_tbState) { case TimerState.Stop: case TimerState.LoadThenStop: if ((_newCrb & 0x01) != 0) { _tbState = (_newCrb & 0x10) != 0 ? TimerState.LoadThenWaitThenCount : TimerState.WaitThenCount; } else { if ((_newCrb & 0x10) != 0) { _tbState = TimerState.LoadThenStop; } } break; case TimerState.Count: if ((_newCrb & 0x01) != 0) { if ((_newCrb & 0x10) != 0) { _tbState = TimerState.LoadThenWaitThenCount; } } else { _tbState = (_newCrb & 0x10) != 0 ? TimerState.LoadThenStop : TimerState.CountThenStop; } break; case TimerState.LoadThenCount: case TimerState.WaitThenCount: if ((_newCrb & 0x01) != 0) { if ((_newCrb & 0x08) != 0) { _newCrb &= 0xFE; _tbState = TimerState.Stop; } else if ((_newCrb & 0x10) != 0) { _tbState = TimerState.LoadThenWaitThenCount; } } else { _tbState = TimerState.Stop; } break; } _crb = _newCrb & 0xEF; _hasNewCrb = false; } } private void TriggerInterrupt(int bit) { _icr |= bit; if ((_intMask & bit) == 0) return; _icr |= 0x80; } public void SyncState(Serializer ser) { ser.Sync(nameof(DelayedInterrupts), ref DelayedInterrupts); ser.Sync(nameof(_pra), ref _pra); ser.Sync(nameof(_prb), ref _prb); ser.Sync(nameof(_ddra), ref _ddra); ser.Sync(nameof(_ddrb), ref _ddrb); ser.Sync(nameof(_ta), ref _ta); ser.Sync(nameof(_tb), ref _tb); ser.Sync(nameof(_latcha), ref _latcha); ser.Sync(nameof(_latchb), ref _latchb); ser.Sync(nameof(_tod10Ths), ref _tod10Ths); ser.Sync(nameof(_todSec), ref _todSec); ser.Sync(nameof(_todMin), ref _todMin); ser.Sync(nameof(_todHr), ref _todHr); ser.Sync(nameof(_latch10Ths), ref _latch10Ths); ser.Sync(nameof(_latchSec), ref _latchSec); ser.Sync(nameof(_latchMin), ref _latchMin); ser.Sync(nameof(_latchHr), ref _latchHr); ser.Sync(nameof(_alm10Ths), ref _alm10Ths); ser.Sync(nameof(_almSec), ref _almSec); ser.Sync(nameof(_almMin), ref _almMin); ser.Sync(nameof(_almHr), ref _almHr); ser.Sync(nameof(_sdr), ref _sdr); ser.Sync(nameof(_icr), ref _icr); ser.Sync(nameof(_cra), ref _cra); ser.Sync(nameof(_crb), ref _crb); ser.Sync(nameof(_intMask), ref _intMask); ser.Sync(nameof(_todLatch), ref _todLatch); ser.Sync(nameof(_taCntPhi2), ref _taCntPhi2); ser.Sync(nameof(_taCntCnt), ref _taCntCnt); ser.Sync(nameof(_tbCntPhi2), ref _tbCntPhi2); ser.Sync(nameof(_tbCntTa), ref _tbCntTa); ser.Sync(nameof(_tbCntCnt), ref _tbCntCnt); ser.Sync(nameof(_taIrqNextCycle), ref _taIrqNextCycle); ser.Sync(nameof(_taPrb6NegativeNextCycle), ref _taPrb6NegativeNextCycle); ser.Sync(nameof(_tbIrqNextCycle), ref _tbIrqNextCycle); ser.Sync(nameof(_tbPrb7NegativeNextCycle), ref _tbPrb7NegativeNextCycle); ser.Sync(nameof(_hasNewCra), ref _hasNewCra); ser.Sync(nameof(_hasNewCrb), ref _hasNewCrb); ser.SyncEnum(nameof(_taState), ref _taState); ser.SyncEnum(nameof(_tbState), ref _tbState); ser.Sync(nameof(_newCra), ref _newCra); ser.Sync(nameof(_newCrb), ref _newCrb); ser.Sync(nameof(_flagLatch), ref _flagLatch); ser.Sync(nameof(_todCounter), ref _todCounter); ser.Sync(nameof(_lastCnt), ref _lastCnt); ser.Sync(nameof(_thisCnt), ref _thisCnt); } } }