using BizHawk.Common; namespace BizHawk.Emulation.Cores.Atari.Atari2600 { public partial class TIA { private struct PlayerData { public MissileData Missile; public byte Grp; public byte Dgrp; public byte Color; public byte HPosCnt; public byte ScanCnt; public bool ScanCntInit; public byte HM; public bool Reflect; public bool Delay; public byte Nusiz; public byte Collisions; // Resp commands do not trigger start signals for main copies. We need to model this private int _startSignal; private int _signalReached; public bool Tick() { var result = false; if (ScanCnt < 8) { if (!ScanCntInit) { // Make the mask to check the graphic byte playerMask = (byte)(1 << (8 - 1 - ScanCnt)); // Reflect it if needed if (Reflect) { playerMask = (byte)ReverseBits(playerMask, 8); } // Check the graphic (depending on delay) if (!Delay) { if ((Grp & playerMask) != 0) { result = true; } } else { if ((Dgrp & playerMask) != 0) { result = true; } } // Reset missile, if desired if (ScanCnt == 0x04 && HPosCnt <= 16 && Missile.ResetToPlayer) { Missile.HPosCnt = 0; } } // Increment the Player Graphics Scan Counter // This counter advances once per clock for single sized players, // once every 2 clocks for double sized players (Nusiz == 0x05), // and once every 4 clocks for quad sizes players (Nusize == 0x07) // The ticks for starting and advancing this counter are tied to the div4 clocking phase. // The first tick for single sized players happens immediately. // The first tick for double and quad sized players is delayed one clock cycle, // and then happen every 2 or 4 clocks if ((Nusiz & 0x07) == 0x05) { if ((HPosCnt + 3) % 2 == 0) { if (ScanCntInit) { ScanCntInit = false; ScanCnt = 0; } else { ScanCnt++; } } } else if ((Nusiz & 0x07) == 0x07) { if ((HPosCnt + 3) % 4 == 0) { if (ScanCntInit) { ScanCntInit = false; ScanCnt = 0; } else { ScanCnt++; } } } else { ScanCntInit = false; ScanCnt++; } } // At counter position 0 we should initalize the scan counter. // Note that for double and quad sized players that the scan counter is not started immediately. if (_startSignal == 160) { ScanCnt = 0; _startSignal++; if ((Nusiz & 0x07) == 0x05) { ScanCntInit = true; } else if ((Nusiz & 0x07) == 0x07) { ScanCntInit = true; } else { ScanCntInit = false; } } if (_startSignal == 16 && ((Nusiz & 0x07) == 0x01 || ((Nusiz & 0x07) == 0x03))) { ScanCnt = 0; _startSignal++; } if (_startSignal == 32 && ((Nusiz & 0x07) == 0x02 || ((Nusiz & 0x07) == 0x03) || ((Nusiz & 0x07) == 0x06))) { ScanCnt = 0; _startSignal++; } if (_startSignal == 64 && ((Nusiz & 0x07) == 0x04 || ((Nusiz & 0x07) == 0x06))) { ScanCnt = 0; _startSignal++; } // Increment the counter HPosCnt++; // Counter loops at 160 HPosCnt %= 160; // our goal here is to send a start signal 4 clocks before drawing begins. This properly emulates // drawing on a real TIA if (HPosCnt == 156 || HPosCnt == 12 || HPosCnt == 28 || HPosCnt == 60) { _startSignal = HPosCnt - 1; _signalReached = HPosCnt + 5; } if (_startSignal < _signalReached) { _startSignal++; } return result; } public void SyncState(Serializer ser) { Missile.SyncState(ser); ser.Sync("grp", ref Grp); ser.Sync("dgrp", ref Dgrp); ser.Sync("color", ref Color); ser.Sync("hPosCnt", ref HPosCnt); ser.Sync("scanCnt", ref ScanCnt); ser.Sync("scanCntInit", ref ScanCntInit); ser.Sync("HM", ref HM); ser.Sync("reflect", ref Reflect); ser.Sync("delay", ref Delay); ser.Sync("nusiz", ref Nusiz); ser.Sync("collisions", ref Collisions); ser.Sync("start_signal", ref _startSignal); ser.Sync("signal_reached", ref _signalReached); } } } }