C64: Implement more CIA features and CIA/VIA defaults.

This commit is contained in:
SaxxonPike 2019-07-04 00:31:48 -05:00
parent 32d59e8514
commit 2dd80eb0f4
7 changed files with 420 additions and 424 deletions

View File

@ -28,7 +28,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
private int Cia1_ReadPortB() private int Cia1_ReadPortB()
{ {
return 0xFF; // Ordinarily these are connected to the userport.
return 0x00;
} }
private int Cpu_ReadPort() private int Cpu_ReadPort()
@ -111,16 +112,19 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
private bool SerPort_ReadAtnOut() private bool SerPort_ReadAtnOut()
{ {
// inverted PA3 (input NOT pulled up)
return !((Cia1.DdrA & 0x08) != 0 && (Cia1.PrA & 0x08) != 0); return !((Cia1.DdrA & 0x08) != 0 && (Cia1.PrA & 0x08) != 0);
} }
private bool SerPort_ReadClockOut() private bool SerPort_ReadClockOut()
{ {
// inverted PA4 (input NOT pulled up)
return !((Cia1.DdrA & 0x10) != 0 && (Cia1.PrA & 0x10) != 0); return !((Cia1.DdrA & 0x10) != 0 && (Cia1.PrA & 0x10) != 0);
} }
private bool SerPort_ReadDataOut() private bool SerPort_ReadDataOut()
{ {
// inverted PA5 (input NOT pulled up)
return !((Cia1.DdrA & 0x20) != 0 && (Cia1.PrA & 0x20) != 0); return !((Cia1.DdrA & 0x20) != 0 && (Cia1.PrA & 0x20) != 0);
} }

View File

@ -9,27 +9,27 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
// * A low RES pin is emulated via HardReset(). // * A low RES pin is emulated via HardReset().
public static class Chip6526 public static class Chip6526
{ {
public static Cia Create(C64.CiaType type, Func<int> readIec) public static Cia Create(C64.CiaType type, Func<int> readIec, Func<int> readUserPort)
{ {
switch (type) switch (type)
{ {
case C64.CiaType.Ntsc: case C64.CiaType.Ntsc:
return new Cia(14318181, 14 * 60, readIec) return new Cia(14318181, 14 * 60, readIec, readUserPort)
{ {
DelayedInterrupts = true DelayedInterrupts = true
}; };
case C64.CiaType.NtscRevA: case C64.CiaType.NtscRevA:
return new Cia(14318181, 14 * 60, readIec) return new Cia(14318181, 14 * 60, readIec, readUserPort)
{ {
DelayedInterrupts = false DelayedInterrupts = false
}; };
case C64.CiaType.Pal: case C64.CiaType.Pal:
return new Cia(17734472, 18 * 50, readIec) return new Cia(17734472, 18 * 50, readIec, readUserPort)
{ {
DelayedInterrupts = true DelayedInterrupts = true
}; };
case C64.CiaType.PalRevA: case C64.CiaType.PalRevA:
return new Cia(17734472, 18 * 50, readIec) return new Cia(17734472, 18 * 50, readIec, readUserPort)
{ {
DelayedInterrupts = false DelayedInterrupts = false
}; };

View File

@ -131,10 +131,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private sealed class IecPort : IPort private sealed class IecPort : IPort
{ {
private readonly Func<int> _readIec; private readonly Func<int> _readIec;
private readonly Func<int> _readUserPort;
public IecPort(Func<int> readIec) public IecPort(Func<int> readIec, Func<int> readUserPort)
{ {
_readIec = readIec; _readIec = readIec;
_readUserPort = readUserPort;
} }
public int ReadPra(int pra, int ddra, int prb, int ddrb) public int ReadPra(int pra, int ddra, int prb, int ddrb)
@ -144,7 +146,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
public int ReadPrb(int pra, int ddra, int prb, int ddrb) public int ReadPrb(int pra, int ddra, int prb, int ddrb)
{ {
return (prb | ~ddrb) & 0xFF; return (prb | ~ddrb) | (~ddrb & _readUserPort());
} }
} }
} }

View File

@ -252,15 +252,16 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
case 0xE: case 0xE:
_hasNewCra = true; _hasNewCra = true;
_newCra = val; _newCra = val;
_taCntPhi2 = ((val & 0x20) == 0); _taCntPhi2 = (val & 0x20) == 0;
_taCntCnt = ((val & 0x20) == 0x20); _taCntCnt = (val & 0x20) == 0x20;
break; break;
case 0xF: case 0xF:
_hasNewCrb = true; _hasNewCrb = true;
_newCrb = val; _newCrb = val;
_tbCntPhi2 = ((val & 0x60) == 0); _tbCntPhi2 = (val & 0x60) == 0;
_tbCntTa = ((val & 0x40) == 0x40); _tbCntCnt = (val & 0x60) == 0x20;
_tbCntCnt = ((val & 0x20) == 0x20); _tbCntTa = (val & 0x60) == 0x40;
_tbCntTaCnt = (val & 0x60) == 0x60;
break; break;
} }
} }

View File

@ -33,6 +33,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
} }
public Func<bool> ReadFlag = () => true; public Func<bool> ReadFlag = () => true;
public Func<bool> ReadCnt = () => true;
public Func<bool> ReadSp = () => true;
public bool DelayedInterrupts = true; public bool DelayedInterrupts = true;
private int _pra; private int _pra;
@ -79,6 +81,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
private bool _flagLatch; private bool _flagLatch;
private bool _flagInput; private bool _flagInput;
private bool _taUnderflow; private bool _taUnderflow;
private bool _lastCnt;
private bool _thisCnt;
private bool _tbCntTaCnt;
private readonly IPort _port; private readonly IPort _port;
private int _todlo; private int _todlo;
@ -98,438 +103,417 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_port = new JoystickKeyboardPort(joysticks, keyboard); _port = new JoystickKeyboardPort(joysticks, keyboard);
} }
public Cia(int todNum, int todDen, Func<int> readIec) : this(todNum, todDen) public Cia(int todNum, int todDen, Func<int> readIec, Func<int> readUserPort) : this(todNum, todDen)
{ {
_port = new IecPort(readIec); _port = new IecPort(readIec, readUserPort);
} }
public void HardReset() public void HardReset()
{ {
_pra = 0; _pra = 0;
_prb = 0; _prb = 0;
_ddra = 0; _ddra = 0;
_ddrb = 0; _ddrb = 0;
_ta = 0xFFFF; _ta = 0xFFFF;
_tb = 0xFFFF; _tb = 0xFFFF;
_latcha = 1; _latcha = 1;
_latchb = 1; _latchb = 1;
_tod10Ths = 0; _tod10Ths = 0;
_todSec = 0; _todSec = 0;
_todMin = 0; _todMin = 0;
_todHr = 0; _todHr = 0;
_alm10Ths = 0; _alm10Ths = 0;
_almSec = 0; _almSec = 0;
_almMin = 0; _almMin = 0;
_almHr = 0; _almHr = 0;
_sdr = 0; _sdr = 0;
_icr = 0; _icr = 0;
_cra = 0; _cra = 0;
_crb = 0; _crb = 0;
_intMask = 0; _intMask = 0;
_todLatch = false; _todLatch = false;
_taCntPhi2 = false; _taCntPhi2 = false;
_taCntCnt = false; _taCntCnt = false;
_tbCntPhi2 = false; _tbCntPhi2 = false;
_tbCntTa = false; _tbCntTa = false;
_tbCntCnt = false; _tbCntCnt = false;
_taIrqNextCycle = false; _taIrqNextCycle = false;
_tbIrqNextCycle = false; _tbIrqNextCycle = false;
_taState = TimerState.Stop; _taState = TimerState.Stop;
_tbState = TimerState.Stop; _tbState = TimerState.Stop;
} _lastCnt = true;
}
private void CheckIrqs() private void CheckIrqs()
{ {
if (_taIrqNextCycle) if (_taIrqNextCycle)
{ {
_taIrqNextCycle = false; _taIrqNextCycle = false;
TriggerInterrupt(1); TriggerInterrupt(1);
} }
if (_tbIrqNextCycle)
{
_tbIrqNextCycle = false;
TriggerInterrupt(2);
}
}
if (_tbIrqNextCycle) public void ExecutePhase()
{ {
_tbIrqNextCycle = false; _thisCnt = ReadCnt();
TriggerInterrupt(2); _taUnderflow = false;
} if (DelayedInterrupts)
} {
CheckIrqs();
}
public void ExecutePhase() if (_taPrb6NegativeNextCycle)
{ {
_taUnderflow = false; _prb &= 0xBF;
if (DelayedInterrupts) _taPrb6NegativeNextCycle = false;
{ }
CheckIrqs(); if (_tbPrb7NegativeNextCycle)
} {
_prb &= 0x7F;
_tbPrb7NegativeNextCycle = false;
}
if (_taPrb6NegativeNextCycle)
{
_prb &= 0xBF;
_taPrb6NegativeNextCycle = false;
}
if (_tbPrb7NegativeNextCycle) switch (_taState)
{ {
_prb &= 0x7F; case TimerState.WaitThenCount:
_tbPrb7NegativeNextCycle = false; _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 (_taState) switch (_tbState)
{ {
case TimerState.WaitThenCount: case TimerState.WaitThenCount:
_taState = TimerState.Count; _tbState = TimerState.Count;
Ta_Idle(); Tb_Idle();
break; break;
case TimerState.Stop: case TimerState.Stop:
Ta_Idle(); Tb_Idle();
break; break;
case TimerState.LoadThenStop: case TimerState.LoadThenStop:
_taState = TimerState.Stop; _tbState = TimerState.Stop;
_ta = _latcha; _tb = _latchb;
Ta_Idle(); Tb_Idle();
break; break;
case TimerState.LoadThenCount: case TimerState.LoadThenCount:
_taState = TimerState.Count; _tbState = TimerState.Count;
_ta = _latcha; _tb = _latchb;
Ta_Idle(); Tb_Idle();
break; break;
case TimerState.LoadThenWaitThenCount: case TimerState.LoadThenWaitThenCount:
_taState = TimerState.WaitThenCount; _tbState = TimerState.WaitThenCount;
if (_ta == 1) if (_tb == 1)
{ {
Ta_Interrupt(); Tb_Interrupt();
_taUnderflow = true; }
} else
else {
{ _tb = _latchb;
_ta = _latcha; }
} Tb_Idle();
break;
case TimerState.Count:
Tb_Count();
break;
case TimerState.CountThenStop:
_tbState = TimerState.Stop;
Tb_Count();
break;
}
Ta_Idle(); CountTod();
break;
case TimerState.Count:
Ta_Count();
break;
case TimerState.CountThenStop:
_taState = TimerState.Stop;
Ta_Count();
break;
}
switch (_tbState) if (!_todLatch)
{ {
case TimerState.WaitThenCount: _latch10Ths = _tod10Ths;
_tbState = TimerState.Count; _latchSec = _todSec;
Tb_Idle(); _latchMin = _todMin;
break; _latchHr = _todHr;
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(); _flagInput = ReadFlag();
break; if (!_flagInput && _flagLatch)
case TimerState.Count: {
Tb_Count(); TriggerInterrupt(16);
break; }
case TimerState.CountThenStop: _flagLatch = _flagInput;
_tbState = TimerState.Stop;
Tb_Count();
break;
}
CountTod(); if (!DelayedInterrupts)
{
CheckIrqs();
}
if (!_todLatch) if ((_cra & 0x02) != 0)
{ _ddra |= 0x40;
_latch10Ths = _tod10Ths; if ((_crb & 0x02) != 0)
_latchSec = _todSec; _ddrb |= 0x80;
_latchMin = _todMin;
_latchHr = _todHr;
}
_flagInput = ReadFlag(); _lastCnt = _thisCnt;
if (!_flagInput && _flagLatch) }
{
TriggerInterrupt(16);
}
_flagLatch = _flagInput; private void Ta_Count()
{
if (_taCntPhi2 || (_taCntCnt && !_lastCnt && _thisCnt))
{
if (_ta <= 0 || --_ta == 0)
{
if (_taState != TimerState.Stop)
{
Ta_Interrupt();
}
_taUnderflow = true;
}
}
Ta_Idle();
}
if (!DelayedInterrupts) private void Ta_Interrupt()
{ {
CheckIrqs(); _ta = _latcha;
} _taIrqNextCycle = true;
_icr |= 1;
if ((_cra & 0x02) != 0) if ((_cra & 0x08) != 0)
{ {
_ddra |= 0x40; _cra &= 0xFE;
} _newCra &= 0xFE;
_taState = TimerState.LoadThenStop;
}
else
{
_taState = TimerState.LoadThenCount;
}
if ((_crb & 0x02) != 0) if ((_cra & 0x02) != 0)
{ {
_ddrb |= 0x80; if ((_cra & 0x04) != 0)
} {
} _taPrb6NegativeNextCycle = true;
_prb |= 0x40;
}
else
{
_prb ^= 0x40;
}
_ddrb |= 0x40;
}
}
private void Ta_Count() private void Ta_Idle()
{ {
if (_taCntPhi2) if (_hasNewCra)
{ {
if (_ta <= 0 || --_ta == 0) switch (_taState)
{ {
if (_taState != TimerState.Stop) case TimerState.Stop:
{ case TimerState.LoadThenStop:
Ta_Interrupt(); 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;
}
}
_taUnderflow = true; 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();
}
Ta_Idle(); private void Tb_Interrupt()
} {
_tb = _latchb;
_tbIrqNextCycle = true;
_icr |= 2;
private void Ta_Interrupt() if ((_crb & 0x08) != 0)
{ {
_ta = _latcha; _crb &= 0xFE;
_taIrqNextCycle = true; _newCrb &= 0xFE;
_icr |= 1; _tbState = TimerState.LoadThenStop;
}
else
{
_tbState = TimerState.LoadThenCount;
}
if ((_cra & 0x08) != 0) if ((_crb & 0x02) != 0)
{ {
_cra &= 0xFE; if ((_crb & 0x04) != 0)
_newCra &= 0xFE; {
_taState = TimerState.LoadThenStop; _tbPrb7NegativeNextCycle = true;
} _prb |= 0x80;
else }
{ else
_taState = TimerState.LoadThenCount; {
} _prb ^= 0x80;
}
}
}
if ((_cra & 0x02) != 0) private void Tb_Idle()
{ {
if ((_cra & 0x04) != 0) if (_hasNewCrb)
{ {
_taPrb6NegativeNextCycle = true; switch (_tbState)
_prb |= 0x40; {
} case TimerState.Stop:
else case TimerState.LoadThenStop:
{ if ((_newCrb & 0x01) != 0)
_prb ^= 0x40; {
} _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;
}
}
_ddrb |= 0x40; private void TriggerInterrupt(int bit)
} {
} _icr |= bit;
if ((_intMask & bit) == 0) return;
private void Ta_Idle() _icr |= 0x80;
{ }
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))
{
if (_tb <= 0 || --_tb == 0)
{
if (_tbState != TimerState.Stop)
{
Tb_Interrupt();
}
}
}
Tb_Idle();
}
private void Tb_Interrupt()
{
_tb = _latchb;
_tbIrqNextCycle = true;
_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) public void SyncState(Serializer ser)
{ {
@ -578,6 +562,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
ser.Sync(nameof(_flagLatch), ref _flagLatch); ser.Sync(nameof(_flagLatch), ref _flagLatch);
ser.Sync(nameof(_todCounter), ref _todCounter); ser.Sync(nameof(_todCounter), ref _todCounter);
ser.Sync(nameof(_lastCnt), ref _lastCnt);
ser.Sync(nameof(_thisCnt), ref _thisCnt);
} }
} }
} }

View File

@ -110,11 +110,11 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
_prb = 0; _prb = 0;
_ddra = 0; _ddra = 0;
_ddrb = 0; _ddrb = 0;
_t1C = 0; _t1C = 0xFFFF;
_t1L = 0; _t1L = 0xFFFF;
_t2C = 0; _t2C = 0xFFFF;
_t2L = 0; _t2L = 0xFFFF;
_sr = 0; _sr = 0xFF;
_acr = 0; _acr = 0;
_pcr = 0; _pcr = 0;
_ifr = 0; _ifr = 0;

View File

@ -163,8 +163,11 @@
public override bool ReadDeviceData() public override bool ReadDeviceData()
{ {
// PB1 (input not pulled up)
var viaOutputData = (Via0.DdrB & 0x02) != 0 && (Via0.PrB & 0x02) != 0; var viaOutputData = (Via0.DdrB & 0x02) != 0 && (Via0.PrB & 0x02) != 0;
// inverted from c64, input, not pulled up to PB7/CA1
var viaInputAtn = ViaReadAtn(); var viaInputAtn = ViaReadAtn();
// PB4 (input not pulled up)
var viaOutputAtna = (Via0.DdrB & 0x10) != 0 && (Via0.PrB & 0x10) != 0; var viaOutputAtna = (Via0.DdrB & 0x10) != 0 && (Via0.PrB & 0x10) != 0;
return !(viaOutputAtna ^ viaInputAtn) && !viaOutputData; return !(viaOutputAtna ^ viaInputAtn) && !viaOutputData;