diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs index d32eaba17f..fafc6b24a5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs @@ -6,26 +6,26 @@ using System.Numerics; namespace BizHawk.Emulation.Cores.Atari.Atari2600 { - // Emulates the TIA - public partial class TIA : IVideoProvider, ISoundProvider - { + // Emulates the TIA + public partial class TIA : IVideoProvider, ISoundProvider + { - #region palette + #region palette - const int BackColor = unchecked((int)0xff000000); + const int BackColor = unchecked((int)0xff000000); - static TIA() - { - // add alpha to palette entries - for (int i = 0; i < PALPalette.Length; i++) - PALPalette[i] |= unchecked((int)0xff000000); - for (int i = 0; i < NTSCPalette.Length; i++) - NTSCPalette[i] |= unchecked((int)0xff000000); - } + static TIA() + { + // add alpha to palette entries + for (int i = 0; i < PALPalette.Length; i++) + PALPalette[i] |= unchecked((int)0xff000000); + for (int i = 0; i < NTSCPalette.Length; i++) + NTSCPalette[i] |= unchecked((int)0xff000000); + } - private static readonly int[] PALPalette = - { - 0x000000, 0x000000, 0x2b2b2b, 0x2b2b2b, + private static readonly int[] PALPalette = + { + 0x000000, 0x000000, 0x2b2b2b, 0x2b2b2b, 0x525252, 0x525252, 0x767676, 0x767676, 0x979797, 0x979797, 0xb6b6b6, 0xb6b6b6, 0xd2d2d2, 0xd2d2d2, 0xececec, 0xececec, @@ -104,175 +104,170 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 0x525252, 0x525252, 0x767676, 0x767676, 0x979797, 0x979797, 0xb6b6b6, 0xb6b6b6, 0xd2d2d2, 0xd2d2d2, 0xececec, 0xececec - }; + }; - private static readonly int[] NTSCPalette = - { - 0x000000, 0, 0x4a4a4a, 0, 0x6f6f6f, 0, 0x8e8e8e, 0, - 0xaaaaaa, 0, 0xc0c0c0, 0, 0xd6d6d6, 0, 0xececec, 0, - 0x484800, 0, 0x69690f, 0, 0x86861d, 0, 0xa2a22a, 0, - 0xbbbb35, 0, 0xd2d240, 0, 0xe8e84a, 0, 0xfcfc54, 0, - 0x7c2c00, 0, 0x904811, 0, 0xa26221, 0, 0xb47a30, 0, - 0xc3903d, 0, 0xd2a44a, 0, 0xdfb755, 0, 0xecc860, 0, - 0x901c00, 0, 0xa33915, 0, 0xb55328, 0, 0xc66c3a, 0, - 0xd5824a, 0, 0xe39759, 0, 0xf0aa67, 0, 0xfcbc74, 0, - 0x940000, 0, 0xa71a1a, 0, 0xb83232, 0, 0xc84848, 0, - 0xd65c5c, 0, 0xe46f6f, 0, 0xf08080, 0, 0xfc9090, 0, - 0x840064, 0, 0x97197a, 0, 0xa8308f, 0, 0xb846a2, 0, - 0xc659b3, 0, 0xd46cc3, 0, 0xe07cd2, 0, 0xec8ce0, 0, - 0x500084, 0, 0x68199a, 0, 0x7d30ad, 0, 0x9246c0, 0, - 0xa459d0, 0, 0xb56ce0, 0, 0xc57cee, 0, 0xd48cfc, 0, - 0x140090, 0, 0x331aa3, 0, 0x4e32b5, 0, 0x6848c6, 0, - 0x7f5cd5, 0, 0x956fe3, 0, 0xa980f0, 0, 0xbc90fc, 0, - 0x000094, 0, 0x181aa7, 0, 0x2d32b8, 0, 0x4248c8, 0, - 0x545cd6, 0, 0x656fe4, 0, 0x7580f0, 0, 0x8490fc, 0, - 0x001c88, 0, 0x183b9d, 0, 0x2d57b0, 0, 0x4272c2, 0, - 0x548ad2, 0, 0x65a0e1, 0, 0x75b5ef, 0, 0x84c8fc, 0, - 0x003064, 0, 0x185080, 0, 0x2d6d98, 0, 0x4288b0, 0, - 0x54a0c5, 0, 0x65b7d9, 0, 0x75cceb, 0, 0x84e0fc, 0, - 0x004030, 0, 0x18624e, 0, 0x2d8169, 0, 0x429e82, 0, - 0x54b899, 0, 0x65d1ae, 0, 0x75e7c2, 0, 0x84fcd4, 0, - 0x004400, 0, 0x1a661a, 0, 0x328432, 0, 0x48a048, 0, - 0x5cba5c, 0, 0x6fd26f, 0, 0x80e880, 0, 0x90fc90, 0, - 0x143c00, 0, 0x355f18, 0, 0x527e2d, 0, 0x6e9c42, 0, - 0x87b754, 0, 0x9ed065, 0, 0xb4e775, 0, 0xc8fc84, 0, - 0x303800, 0, 0x505916, 0, 0x6d762b, 0, 0x88923e, 0, - 0xa0ab4f, 0, 0xb7c25f, 0, 0xccd86e, 0, 0xe0ec7c, 0, - 0x482c00, 0, 0x694d14, 0, 0x866a26, 0, 0xa28638, 0, - 0xbb9f47, 0, 0xd2b656, 0, 0xe8cc63, 0, 0xfce070, 0 - }; + private static readonly int[] NTSCPalette = + { + 0x000000, 0, 0x4a4a4a, 0, 0x6f6f6f, 0, 0x8e8e8e, 0, + 0xaaaaaa, 0, 0xc0c0c0, 0, 0xd6d6d6, 0, 0xececec, 0, + 0x484800, 0, 0x69690f, 0, 0x86861d, 0, 0xa2a22a, 0, + 0xbbbb35, 0, 0xd2d240, 0, 0xe8e84a, 0, 0xfcfc54, 0, + 0x7c2c00, 0, 0x904811, 0, 0xa26221, 0, 0xb47a30, 0, + 0xc3903d, 0, 0xd2a44a, 0, 0xdfb755, 0, 0xecc860, 0, + 0x901c00, 0, 0xa33915, 0, 0xb55328, 0, 0xc66c3a, 0, + 0xd5824a, 0, 0xe39759, 0, 0xf0aa67, 0, 0xfcbc74, 0, + 0x940000, 0, 0xa71a1a, 0, 0xb83232, 0, 0xc84848, 0, + 0xd65c5c, 0, 0xe46f6f, 0, 0xf08080, 0, 0xfc9090, 0, + 0x840064, 0, 0x97197a, 0, 0xa8308f, 0, 0xb846a2, 0, + 0xc659b3, 0, 0xd46cc3, 0, 0xe07cd2, 0, 0xec8ce0, 0, + 0x500084, 0, 0x68199a, 0, 0x7d30ad, 0, 0x9246c0, 0, + 0xa459d0, 0, 0xb56ce0, 0, 0xc57cee, 0, 0xd48cfc, 0, + 0x140090, 0, 0x331aa3, 0, 0x4e32b5, 0, 0x6848c6, 0, + 0x7f5cd5, 0, 0x956fe3, 0, 0xa980f0, 0, 0xbc90fc, 0, + 0x000094, 0, 0x181aa7, 0, 0x2d32b8, 0, 0x4248c8, 0, + 0x545cd6, 0, 0x656fe4, 0, 0x7580f0, 0, 0x8490fc, 0, + 0x001c88, 0, 0x183b9d, 0, 0x2d57b0, 0, 0x4272c2, 0, + 0x548ad2, 0, 0x65a0e1, 0, 0x75b5ef, 0, 0x84c8fc, 0, + 0x003064, 0, 0x185080, 0, 0x2d6d98, 0, 0x4288b0, 0, + 0x54a0c5, 0, 0x65b7d9, 0, 0x75cceb, 0, 0x84e0fc, 0, + 0x004030, 0, 0x18624e, 0, 0x2d8169, 0, 0x429e82, 0, + 0x54b899, 0, 0x65d1ae, 0, 0x75e7c2, 0, 0x84fcd4, 0, + 0x004400, 0, 0x1a661a, 0, 0x328432, 0, 0x48a048, 0, + 0x5cba5c, 0, 0x6fd26f, 0, 0x80e880, 0, 0x90fc90, 0, + 0x143c00, 0, 0x355f18, 0, 0x527e2d, 0, 0x6e9c42, 0, + 0x87b754, 0, 0x9ed065, 0, 0xb4e775, 0, 0xc8fc84, 0, + 0x303800, 0, 0x505916, 0, 0x6d762b, 0, 0x88923e, 0, + 0xa0ab4f, 0, 0xb7c25f, 0, 0xccd86e, 0, 0xe0ec7c, 0, + 0x482c00, 0, 0x694d14, 0, 0x866a26, 0, 0xa28638, 0, + 0xbb9f47, 0, 0xd2b656, 0, 0xe8cc63, 0, 0xfce070, 0 + }; - private static readonly int[] SECAMPalette = - { - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + private static readonly int[] SECAMPalette = + { + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - 0x000000,0x000000,0x2121FF,0x2121FF, - 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, - 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, - 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, - }; + 0x000000,0x000000,0x2121FF,0x2121FF, + 0xF03C79,0xF03C79,0xFF50FF,0xFF50FF, + 0x7FFF00,0x7FFF00,0x7FFFFF,0x7FFFFF, + 0xFFFF3F,0xFFFF3F,0xffffff,0xffffff, + }; - #endregion + #endregion - // in all cases, the TIA has 228 clocks per scanline - // the NTSC TIA has a clock rate of 3579575hz - // the PAL/SECAM TIA has a clock rate of 3546894hz + // in all cases, the TIA has 228 clocks per scanline + // the NTSC TIA has a clock rate of 3579575hz + // the PAL/SECAM TIA has a clock rate of 3546894hz - private bool _pal; + private bool _pal; - public int NominalNumScanlines - { - get - { - return _pal ? 312 : 262; - } - } + public int NominalNumScanlines + { + get + { + return _pal ? 312 : 262; + } + } - public void GetFrameRate(out int num, out int den) - { - // TODO when sound timing is made exact: - // NTSC refclock is actually 315 / 88 mhz + public void GetFrameRate(out int num, out int den) + { + // TODO when sound timing is made exact: + // NTSC refclock is actually 315 / 88 mhz + //3546895 - int clockrate = _pal ? 3546895 : 3579545; - int clocksperframe = 228 * NominalNumScanlines; - int gcd = (int)BigInteger.GreatestCommonDivisor(clockrate, clocksperframe); - num = clockrate / gcd; - den = clocksperframe / gcd; - } + int clockrate = _pal ? 3546895 : 3579545; + int clocksperframe = 228 * NominalNumScanlines; + int gcd = (int)BigInteger.GreatestCommonDivisor(clockrate, clocksperframe); + num = clockrate / gcd; + den = clocksperframe / gcd; + } - private const int ScreenWidth = 160; - private const int MaxScreenHeight = 312; + private const int ScreenWidth = 160; + private const int MaxScreenHeight = 312; - private const byte CXP0 = 0x01; - private const byte CXP1 = 0x02; - private const byte CXM0 = 0x04; - private const byte CXM1 = 0x08; - private const byte CXPF = 0x10; - private const byte CXBL = 0x20; + private const byte CXP0 = 0x01; + private const byte CXP1 = 0x02; + private const byte CXM0 = 0x04; + private const byte CXM1 = 0x08; + private const byte CXPF = 0x10; + private const byte CXBL = 0x20; - private readonly Atari2600 _core; - private int[] _scanlinebuffer = new int[ScreenWidth * MaxScreenHeight]; + private readonly Atari2600 _core; + private int[] _scanlinebuffer = new int[ScreenWidth * MaxScreenHeight]; - private int[] _palette; - - private int test_count_p0; - private int test_count_p1; - private int test_count_m0; - private int test_count_m1; - private int test_count_b; + private int[] _palette; private byte pf0_update = 0; private byte pf1_update = 0; @@ -294,6 +289,16 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 private bool enam1_val = false; private bool enamb_val = false; + private bool p0_stuff = false; + private bool p1_stuff = false; + private bool m0_stuff = false; + private bool m1_stuff = false; + private bool b_stuff = false; + + private int HMP0_delay = 0; + private byte HMP0_val = 0; + private int HMP1_delay = 0; + private byte HMP1_val = 0; private int prg0_delay = 0; private int prg1_delay = 0; @@ -302,134 +307,134 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 private bool do_ticks = false; private byte _hsyncCnt; - private int _capChargeStart; - private bool _capCharging; - private bool _vblankEnabled; - private bool _vsyncEnabled; - private int _CurrentScanLine; - private int _audioClocks; // not savestated + private int _capChargeStart; + private bool _capCharging; + private bool _vblankEnabled; + private bool _vsyncEnabled; + private int _CurrentScanLine; + private int _audioClocks; // not savestated - private PlayerData _player0; - private PlayerData _player1; - private PlayfieldData _playField; - private HMoveData _hmove; - private BallData _ball; + private PlayerData _player0; + private PlayerData _player1; + private PlayfieldData _playField; + private HMoveData _hmove; + private BallData _ball; - public Audio[] AUD = { new Audio(), new Audio() }; + public Audio[] AUD = { new Audio(), new Audio() }; - public TIA(Atari2600 core, bool pal, bool secam) - { - _core = core; - _player0.ScanCnt = 8; - _player1.ScanCnt = 8; - _pal = pal; - SetSECAM(secam); - } + public TIA(Atari2600 core, bool pal, bool secam) + { + _core = core; + _player0.ScanCnt = 8; + _player1.ScanCnt = 8; + _pal = pal; + SetSECAM(secam); + } - public void SetSECAM(bool secam) - { - _palette = _pal ? secam ? SECAMPalette : PALPalette : NTSCPalette; - } + public void SetSECAM(bool secam) + { + _palette = _pal ? secam ? SECAMPalette : PALPalette : NTSCPalette; + } - public int CurrentScanLine - { - get { return _CurrentScanLine; } - } + public int CurrentScanLine + { + get { return _CurrentScanLine; } + } - public bool IsVBlank - { - get { return _vblankEnabled; } - } + public bool IsVBlank + { + get { return _vblankEnabled; } + } - public bool IsVSync - { - get { return _vsyncEnabled; } - } + public bool IsVSync + { + get { return _vsyncEnabled; } + } - /// - /// a count of lines emulated; incremented by the TIA but not used by it - /// - public int LineCount { get; set; } + /// + /// a count of lines emulated; incremented by the TIA but not used by it + /// + public int LineCount { get; set; } - /// - /// called at the end of a video frame. used internally - /// - public Action FrameEndCallBack { get; set; } + /// + /// called at the end of a video frame. used internally + /// + public Action FrameEndCallBack { get; set; } - public int MaxVolume { get; set; } + public int MaxVolume { get; set; } - public int VirtualWidth - { - // TODO: PAL? - get - { - if (_pal) - { - return 320; - } + public int VirtualWidth + { + // TODO: PAL? + get + { + if (_pal) + { + return 320; + } - return 275; // 275 comes from NTSC specs and the actual pixel clock of a 2600 TIA - } - } + return 275; // 275 comes from NTSC specs and the actual pixel clock of a 2600 TIA + } + } - public int VirtualHeight - { - get { return BufferHeight; } - } + public int VirtualHeight + { + get { return BufferHeight; } + } - public int BufferWidth - { - get { return ScreenWidth; } - } + public int BufferWidth + { + get { return ScreenWidth; } + } - public int BufferHeight - { - get - { - if (_pal) - return _core.Settings.PALBottomLine - _core.Settings.PALTopLine; - else - return _core.Settings.NTSCBottomLine - _core.Settings.NTSCTopLine; - } - } + public int BufferHeight + { + get + { + if (_pal) + return _core.Settings.PALBottomLine - _core.Settings.PALTopLine; + else + return _core.Settings.NTSCBottomLine - _core.Settings.NTSCTopLine; + } + } - public int BackgroundColor - { - get { return _core.Settings.BackgroundColor.ToArgb(); } - } + public int BackgroundColor + { + get { return _core.Settings.BackgroundColor.ToArgb(); } + } - public int[] GetVideoBuffer() - { - return FrameBuffer; - } + public int[] GetVideoBuffer() + { + return FrameBuffer; + } - public void Reset() - { - _hsyncCnt = 0; - _capChargeStart = 0; - _capCharging = false; - _vblankEnabled = false; - _vsyncEnabled = false; - _CurrentScanLine = 0; - _audioClocks = 0; + public void Reset() + { + _hsyncCnt = 0; + _capChargeStart = 0; + _capCharging = false; + _vblankEnabled = false; + _vsyncEnabled = false; + _CurrentScanLine = 0; + _audioClocks = 0; - _player0 = new PlayerData(); - _player1 = new PlayerData(); - _playField = new PlayfieldData(); - _hmove = new HMoveData(); - _ball = new BallData(); + _player0 = new PlayerData(); + _player1 = new PlayerData(); + _playField = new PlayfieldData(); + _hmove = new HMoveData(); + _ball = new BallData(); - _player0.ScanCnt = 8; - _player1.ScanCnt = 8; - } + _player0.ScanCnt = 8; + _player1.ScanCnt = 8; + } - // Execute TIA cycles - public void Execute(int cycles) - { - // Still ignoring cycles... + // Execute TIA cycles + public void Execute(int cycles) + { + // Still ignoring cycles... //delay latch to new playfield register - if (pf0_updater==true) + if (pf0_updater == true) { pf0_delay_clock++; if (pf0_delay_clock > pf0_max_delay) @@ -458,15 +463,15 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 } //delay latch to missile enable - if (enam0_delay>0) + if (enam0_delay > 0) { enam0_delay++; - if (enam0_delay==3) + if (enam0_delay == 3) { enam0_delay = 0; _player0.Missile.Enabled = enam0_val; } - + } if (enam1_delay > 0) @@ -520,21 +525,42 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 } + // HMP write delay + if (HMP0_delay > 0) + { + HMP0_delay++; + if (HMP0_delay == 4) + { + HMP0_delay = 0; + _player0.HM = HMP0_val; + } + } + + if (HMP1_delay > 0) + { + HMP1_delay++; + if (HMP1_delay == 3) + { + HMP1_delay = 0; + _player1.HM = HMP1_val; + } + } + // Reset the RDY flag when we reach hblank if (_hsyncCnt <= 0) - { - _core.Cpu.RDY = true; - } + { + _core.Cpu.RDY = true; + } - // Assume we're on the left side of the screen for now - var rightSide = false; + // Assume we're on the left side of the screen for now + var rightSide = false; // ---- Things that happen only in the drawing section ---- // TODO: Remove this magic number (17). It depends on the HMOVE if ((_hsyncCnt) >= (_hmove.LateHBlankReset ? 76 : 68)) { do_ticks = false; - + // TODO: Remove this magic number if ((_hsyncCnt / 4) >= 37) { @@ -655,7 +681,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 pixelColor = !rightSide ? _palette[_player0.Color] : _palette[_player1.Color]; } - if (_playField.Priority && (collisions & CXPF) != 0 && _core.Settings.ShowPlayfield) + if (_playField.Priority && (collisions & CXPF) != 0 && _core.Settings.ShowPlayfield) { pixelColor = _palette[_playField.PfColor]; @@ -681,7 +707,8 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 throw new Exception(); // TODO _scanlinebuffer[_CurrentScanLine * ScreenWidth + x] = pixelColor; } - } else + } + else { do_ticks = true; } @@ -708,421 +735,434 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 - // ---- Things that happen every time ---- + - // Handle HMOVE - if (_hmove.HMoveEnabled) + // Handle HMOVE + if (_hmove.HMoveEnabled) + { + + if (_hmove.DecCntEnabled) { - // On the first time, set the latches and counters - if (_hmove.HMoveJustStarted) + + + // Actually do stuff only evey 4 pulses + if (_hmove.HMoveCnt == 0) { - - - _hmove.Player0Latch = true; - _hmove.Player0Cnt = 0; - test_count_p0 = 0; - test_count_p1 = 0; - test_count_m0 = 0; - test_count_m1 = 0; - test_count_b = 0; - - - _hmove.Missile0Latch = true; - _hmove.Missile0Cnt = 0; - - _hmove.Player1Latch = true; - _hmove.Player1Cnt = 0; - - _hmove.Missile1Latch = true; - _hmove.Missile1Cnt = 0; - - _hmove.BallLatch = true; - _hmove.BallCnt = 0; - - _hmove.HMoveCnt = 0; - - _hmove.HMoveJustStarted = false; - _hmove.LateHBlankReset = true; - _hmove.DecCntEnabled = false; - - } - - if (_hmove.DecCntEnabled) - { - - - // Actually do stuff only evey 4 pulses - if (_hmove.HMoveCnt == 0) + // If the latch is still set + if (_hmove.Player0Latch) { - // If the latch is still set - if (_hmove.Player0Latch) + // If the move counter still has a bit in common with the HM register + if (((15 - _hmove.Player0Cnt) ^ ((_player0.HM & 0x07) | ((~(_player0.HM & 0x08)) & 0x08))) != 0x0F) { - // If the move counter still has a bit in common with the HM register - if (((15 - _hmove.Player0Cnt) ^ ((_player0.HM & 0x07) | ((~(_player0.HM & 0x08)) & 0x08))) != 0x0F) - { - // "Clock-Stuffing" - if (do_ticks==true) - { - _player0.Tick(); - } - - - // Increase by 1, max of 15 - - test_count_p0++; - if (test_count_p0 < 16) - { - _hmove.Player0Cnt++; - } - else - { - _hmove.Player0Cnt = 0; - } - - //_hmove.Player0Cnt %= 16; - } - else - { - _hmove.Player0Latch = false; - } + p0_stuff = true; } - - if (_hmove.Missile0Latch) + else { - - // If the move counter still has a bit in common with the HM register - if (((15 - _hmove.Missile0Cnt) ^ ((_player0.Missile.Hm & 0x07) | ((~(_player0.Missile.Hm & 0x08)) & 0x08))) != 0x0F) - { - // "Clock-Stuffing" - if (do_ticks == true) - { - _player0.Missile.Tick(); - } - // Increase by 1, max of 15 - - - test_count_m0++; - if (test_count_m0 < 16) - { - _hmove.Missile0Cnt++; - } - else - { - _hmove.Missile0Cnt = 0; - } - //_hmove.Missile0Cnt %= 16; - } - else - { - _hmove.Missile0Latch = false; - - } - } - - if (_hmove.Player1Latch) - { - // If the move counter still has a bit in common with the HM register - if (((15 - _hmove.Player1Cnt) ^ ((_player1.HM & 0x07) | ((~(_player1.HM & 0x08)) & 0x08))) != 0x0F) - { - // "Clock-Stuffing" - if (do_ticks == true) - { - _player1.Tick(); - } - // Increase by 1, max of 15 - test_count_p1++; - if (test_count_p1 < 16) - { - _hmove.Player1Cnt++; - } - else - { - _hmove.Player1Cnt = 0; - } - //_hmove.Player1Cnt %= 16; - } - else - { - _hmove.Player1Latch = false; - } - } - - if (_hmove.Missile1Latch) - { - // If the move counter still has a bit in common with the HM register - if (((15 - _hmove.Missile1Cnt) ^ ((_player1.Missile.Hm & 0x07) | ((~(_player1.Missile.Hm & 0x08)) & 0x08))) != 0x0F) - { - // "Clock-Stuffing" - if (do_ticks == true) - { - _player1.Missile.Tick(); - } - // Increase by 1, max of 15 - test_count_m1++; - if (test_count_m1 < 16) - { - _hmove.Missile1Cnt++; - } - else - { - _hmove.Missile1Cnt = 0; - } - // _hmove.Missile1Cnt %= 16; - } - else - { - _hmove.Missile1Latch = false; - } - } - - if (_hmove.BallLatch) - { - // If the move counter still has a bit in common with the HM register - if (((15 - _hmove.BallCnt) ^ ((_ball.HM & 0x07) | ((~(_ball.HM & 0x08)) & 0x08))) != 0x0F) - { - // "Clock-Stuffing" - if (do_ticks == true) - { - _ball.Tick(); - } - // Increase by 1, max of 15 - test_count_b++; - if (test_count_b < 16) - { - _hmove.BallCnt++; - } - else - { - _hmove.BallCnt = 0; - } - //_hmove.BallCnt %= 16; - } - else - { - _hmove.BallLatch = false; - } - } - - if (!_hmove.Player0Latch && !_hmove.Player1Latch && !_hmove.BallLatch && !_hmove.Missile0Latch && !_hmove.Missile1Latch) - { - _hmove.HMoveEnabled = false; - _hmove.DecCntEnabled = false; - _hmove.HMoveDelayCnt = 0; + _hmove.Player0Latch = false; } } - _hmove.HMoveCnt++; - _hmove.HMoveCnt %= 4; - } - - if (_hmove.HMoveDelayCnt < 5) - { - _hmove.HMoveDelayCnt++; + if (_hmove.Missile0Latch) + { + + // If the move counter still has a bit in common with the HM register + if (((15 - _hmove.Missile0Cnt) ^ ((_player0.Missile.Hm & 0x07) | ((~(_player0.Missile.Hm & 0x08)) & 0x08))) != 0x0F) + { + m0_stuff = true; + } + else + { + _hmove.Missile0Latch = false; + + } + } + + if (_hmove.Player1Latch) + { + // If the move counter still has a bit in common with the HM register + if (((15 - _hmove.Player1Cnt) ^ ((_player1.HM & 0x07) | ((~(_player1.HM & 0x08)) & 0x08))) != 0x0F) + { + p1_stuff = true; + } + else + { + _hmove.Player1Latch = false; + } + } + + if (_hmove.Missile1Latch) + { + // If the move counter still has a bit in common with the HM register + if (((15 - _hmove.Missile1Cnt) ^ ((_player1.Missile.Hm & 0x07) | ((~(_player1.Missile.Hm & 0x08)) & 0x08))) != 0x0F) + { + m1_stuff = true; + } + else + { + _hmove.Missile1Latch = false; + } + } + + if (_hmove.BallLatch) + { + // If the move counter still has a bit in common with the HM register + if (((15 - _hmove.BallCnt) ^ ((_ball.HM & 0x07) | ((~(_ball.HM & 0x08)) & 0x08))) != 0x0F) + { + b_stuff = true; + } + else + { + _hmove.BallLatch = false; + } + } + + if (!_hmove.Player0Latch && !_hmove.Player1Latch && !_hmove.BallLatch && !_hmove.Missile0Latch && !_hmove.Missile1Latch) + { + _hmove.HMoveEnabled = false; + _hmove.DecCntEnabled = false; + _hmove.HMoveDelayCnt = 0; + } } - if (_hmove.HMoveDelayCnt == 5) + _hmove.HMoveCnt++; + _hmove.HMoveCnt %= 4; + + if (p0_stuff==true && _hsyncCnt%4==0) { - _hmove.HMoveDelayCnt++; - _hmove.HMoveCnt = 0; - _hmove.DecCntEnabled = true; + p0_stuff = false; + // "Clock-Stuffing" + if (do_ticks == true) + { + _player0.Tick(); + } + + + // Increase by 1, max of 15 + + _hmove.test_count_p0++; + if (_hmove.test_count_p0 < 16) + { + _hmove.Player0Cnt++; + } + else + { + _hmove.Player0Cnt = 0; + } + } + if (p1_stuff == true && _hsyncCnt % 4 == 0) + { + p1_stuff = false; + // "Clock-Stuffing" + if (do_ticks == true) + { + _player1.Tick(); + } + // Increase by 1, max of 15 + _hmove.test_count_p1++; + if (_hmove.test_count_p1 < 16) + { + _hmove.Player1Cnt++; + } + else + { + _hmove.Player1Cnt = 0; + } + + } + if (m0_stuff == true && _hsyncCnt % 4 == 0) + { + m0_stuff = false; + // "Clock-Stuffing" + if (do_ticks == true) + { + _player0.Missile.Tick(); + } + // Increase by 1, max of 15 + + + _hmove.test_count_m0++; + if (_hmove.test_count_m0 < 16) + { + _hmove.Missile0Cnt++; + } + else + { + _hmove.Missile0Cnt = 0; + } + } + if (m1_stuff == true && _hsyncCnt % 4 == 0) + { + m1_stuff = false; + // "Clock-Stuffing" + if (do_ticks == true) + { + _player1.Missile.Tick(); + } + // Increase by 1, max of 15 + _hmove.test_count_m1++; + if (_hmove.test_count_m1 < 16) + { + _hmove.Missile1Cnt++; + } + else + { + _hmove.Missile1Cnt = 0; + } + } + if (b_stuff == true && _hsyncCnt % 4 == 0) + { + b_stuff = false; + // "Clock-Stuffing" + if (do_ticks == true) + { + _ball.Tick(); + } + // Increase by 1, max of 15 + _hmove.test_count_b++; + if (_hmove.test_count_b < 16) + { + _hmove.BallCnt++; + } + else + { + _hmove.BallCnt = 0; + } } } + + if (_hmove.HMoveDelayCnt < 5) + { + _hmove.HMoveDelayCnt++; + } + + if (_hmove.HMoveDelayCnt == 5) + { + _hmove.HMoveDelayCnt++; + _hmove.HMoveCnt = 0; + _hmove.DecCntEnabled = true; + + _hmove.test_count_p0 = 0; + _hmove.test_count_p1 = 0; + _hmove.test_count_m0 = 0; + _hmove.test_count_m1 = 0; + _hmove.test_count_b = 0; + + _hmove.Player0Latch = true; + _hmove.Player0Cnt = 0; + + _hmove.Missile0Latch = true; + _hmove.Missile0Cnt = 0; + + _hmove.Player1Latch = true; + _hmove.Player1Cnt = 0; + + _hmove.Missile1Latch = true; + _hmove.Missile1Cnt = 0; + + _hmove.BallLatch = true; + _hmove.BallCnt = 0; + + _hmove.LateHBlankReset = true; + } + } + - // Increment the hsync counter - _hsyncCnt++; - _hsyncCnt %= 228; - // End of the line? Add it to the buffer! - if (_hsyncCnt == 0) - { - _hmove.LateHBlankReset = false; - _CurrentScanLine++; - LineCount++; - _audioClocks += 2; // TODO: increment this at the appropriate places twice per line - } - } + // Increment the hsync counter + _hsyncCnt++; + _hsyncCnt %= 228; - public int[] FrameBuffer = new int[ScreenWidth * MaxScreenHeight]; + // End of the line? Add it to the buffer! + if (_hsyncCnt == 0) + { + _hmove.LateHBlankReset = false; + _CurrentScanLine++; + LineCount++; + _audioClocks += 2; // TODO: increment this at the appropriate places twice per line + } + } - void OutputFrame(int validlines) - { - int topLine = _pal ? _core.Settings.PALTopLine : _core.Settings.NTSCTopLine; - int bottomLine = _pal ? _core.Settings.PALBottomLine : _core.Settings.NTSCBottomLine; + public int[] FrameBuffer = new int[ScreenWidth * MaxScreenHeight]; - // if vsync occured unexpectedly early, black out the remainer - for (; validlines < bottomLine; validlines++) - { - for (int i = 0; i < 160; i++) - _scanlinebuffer[validlines * 160 + i] = BackColor; - } + void OutputFrame(int validlines) + { + int topLine = _pal ? _core.Settings.PALTopLine : _core.Settings.NTSCTopLine; + int bottomLine = _pal ? _core.Settings.PALBottomLine : _core.Settings.NTSCBottomLine; - int srcbytes = sizeof(int) * ScreenWidth * topLine; - int count = bottomLine - topLine; // no +1, as the bottom line number is not inclusive - count *= sizeof(int) * ScreenWidth; + // if vsync occured unexpectedly early, black out the remainer + for (; validlines < bottomLine; validlines++) + { + for (int i = 0; i < 160; i++) + _scanlinebuffer[validlines * 160 + i] = BackColor; + } - Buffer.BlockCopy(_scanlinebuffer, srcbytes, FrameBuffer, 0, count); - } + int srcbytes = sizeof(int) * ScreenWidth * topLine; + int count = bottomLine - topLine; // no +1, as the bottom line number is not inclusive + count *= sizeof(int) * ScreenWidth; - public byte ReadMemory(ushort addr, bool peek) - { - var maskedAddr = (ushort)(addr & 0x000F); - if (maskedAddr == 0x00) // CXM0P - { - return (byte)((((_player0.Missile.Collisions & CXP1) != 0) ? 0x80 : 0x00) | (((_player0.Missile.Collisions & CXP0) != 0) ? 0x40 : 0x00)); - } + Buffer.BlockCopy(_scanlinebuffer, srcbytes, FrameBuffer, 0, count); + } - if (maskedAddr == 0x01) // CXM1P - { - return (byte)((((_player1.Missile.Collisions & CXP0) != 0) ? 0x80 : 0x00) | (((_player1.Missile.Collisions & CXP1) != 0) ? 0x40 : 0x00)); - } + public byte ReadMemory(ushort addr, bool peek) + { + var maskedAddr = (ushort)(addr & 0x000F); + if (maskedAddr == 0x00) // CXM0P + { + return (byte)((((_player0.Missile.Collisions & CXP1) != 0) ? 0x80 : 0x00) | (((_player0.Missile.Collisions & CXP0) != 0) ? 0x40 : 0x00)); + } - if (maskedAddr == 0x02) // CXP0FB - { - return (byte)((((_player0.Collisions & CXPF) != 0) ? 0x80 : 0x00) | (((_player0.Collisions & CXBL) != 0) ? 0x40 : 0x00)); - } + if (maskedAddr == 0x01) // CXM1P + { + return (byte)((((_player1.Missile.Collisions & CXP0) != 0) ? 0x80 : 0x00) | (((_player1.Missile.Collisions & CXP1) != 0) ? 0x40 : 0x00)); + } - if (maskedAddr == 0x03) // CXP1FB - { - return (byte)((((_player1.Collisions & CXPF) != 0) ? 0x80 : 0x00) | (((_player1.Collisions & CXBL) != 0) ? 0x40 : 0x00)); - } + if (maskedAddr == 0x02) // CXP0FB + { + return (byte)((((_player0.Collisions & CXPF) != 0) ? 0x80 : 0x00) | (((_player0.Collisions & CXBL) != 0) ? 0x40 : 0x00)); + } - if (maskedAddr == 0x04) // CXM0FB - { - return (byte)((((_player0.Missile.Collisions & CXPF) != 0) ? 0x80 : 0x00) | (((_player0.Missile.Collisions & CXBL) != 0) ? 0x40 : 0x00)); - } + if (maskedAddr == 0x03) // CXP1FB + { + return (byte)((((_player1.Collisions & CXPF) != 0) ? 0x80 : 0x00) | (((_player1.Collisions & CXBL) != 0) ? 0x40 : 0x00)); + } - if (maskedAddr == 0x05) // CXM1FB - { - return (byte)((((_player1.Missile.Collisions & CXPF) != 0) ? 0x80 : 0x00) | (((_player1.Missile.Collisions & CXBL) != 0) ? 0x40 : 0x00)); - } + if (maskedAddr == 0x04) // CXM0FB + { + return (byte)((((_player0.Missile.Collisions & CXPF) != 0) ? 0x80 : 0x00) | (((_player0.Missile.Collisions & CXBL) != 0) ? 0x40 : 0x00)); + } - if (maskedAddr == 0x06) // CXBLPF - { - return (byte)(((_ball.Collisions & CXPF) != 0) ? 0x80 : 0x00); - } + if (maskedAddr == 0x05) // CXM1FB + { + return (byte)((((_player1.Missile.Collisions & CXPF) != 0) ? 0x80 : 0x00) | (((_player1.Missile.Collisions & CXBL) != 0) ? 0x40 : 0x00)); + } - if (maskedAddr == 0x07) // CXPPMM - { - return (byte)((((_player0.Collisions & CXP1) != 0) ? 0x80 : 0x00) | (((_player0.Missile.Collisions & CXM1) != 0) ? 0x40 : 0x00)); - } + if (maskedAddr == 0x06) // CXBLPF + { + return (byte)(((_ball.Collisions & CXPF) != 0) ? 0x80 : 0x00); + } - if (maskedAddr == 0x08) // INPT0 - { - // Changing the hard coded value will change the paddle position. The range seems to be roughly 0-56000 according to values from stella - // 6105 roughly centers the paddle in Breakout - if (_capCharging && _core.Cpu.TotalExecutedCycles - _capChargeStart >= 6105) - { - return 0x80; - } + if (maskedAddr == 0x07) // CXPPMM + { + return (byte)((((_player0.Collisions & CXP1) != 0) ? 0x80 : 0x00) | (((_player0.Missile.Collisions & CXM1) != 0) ? 0x40 : 0x00)); + } - return 0x00; - } + if (maskedAddr == 0x08) // INPT0 + { + // Changing the hard coded value will change the paddle position. The range seems to be roughly 0-56000 according to values from stella + // 6105 roughly centers the paddle in Breakout + if (_capCharging && _core.Cpu.TotalExecutedCycles - _capChargeStart >= 6105) + { + return 0x80; + } - if (maskedAddr == 0x0C) // INPT4 - { - return (byte)((_core.ReadControls1(peek) & 0x08) != 0 ? 0x80 : 0x00); - } + return 0x00; + } - if (maskedAddr == 0x0D) // INPT5 - { - return (byte)((_core.ReadControls2(peek) & 0x08) != 0 ? 0x80 : 0x00); - } + if (maskedAddr == 0x0C) // INPT4 + { + return (byte)((_core.ReadControls1(peek) & 0x08) != 0 ? 0x80 : 0x00); + } - return 0x00; - } + if (maskedAddr == 0x0D) // INPT5 + { + return (byte)((_core.ReadControls2(peek) & 0x08) != 0 ? 0x80 : 0x00); + } - public void WriteMemory(ushort addr, byte value) - { - var maskedAddr = (ushort)(addr & 0x3f); + return 0x00; + } - if (maskedAddr == 0x00) // VSYNC - { - if ((value & 0x02) != 0) - { - // Frame is complete, output to buffer - _vsyncEnabled = true; - } - else if (_vsyncEnabled) - { - // When VSYNC is disabled, this will be the first line of the new frame + public void WriteMemory(ushort addr, byte value) + { + var maskedAddr = (ushort)(addr & 0x3f); - // write to frame buffer - OutputFrame(_CurrentScanLine); + if (maskedAddr == 0x00) // VSYNC + { + if ((value & 0x02) != 0) + { + // Frame is complete, output to buffer + _vsyncEnabled = true; + } + else if (_vsyncEnabled) + { + // When VSYNC is disabled, this will be the first line of the new frame - if (FrameEndCallBack != null) - FrameEndCallBack(_CurrentScanLine); + // write to frame buffer + OutputFrame(_CurrentScanLine); - // Clear all from last frame - _CurrentScanLine = 0; + if (FrameEndCallBack != null) + FrameEndCallBack(_CurrentScanLine); - // Frame is done - _vsyncEnabled = false; + // Clear all from last frame + _CurrentScanLine = 0; - // Do not reset hsync, since we're on the first line of the new frame - // hsyncCnt = 0; - } - } - else if (maskedAddr == 0x01) // VBLANK - { - _vblankEnabled = (value & 0x02) != 0; - _capCharging = (value & 0x80) == 0; - if ((value & 0x80) == 0) - { - _capChargeStart = _core.Cpu.TotalExecutedCycles; - } - } - else if (maskedAddr == 0x02) // WSYNC - { - // Halt the CPU until we reach hblank - _core.Cpu.RDY = false; - } - else if (maskedAddr == 0x04) // NUSIZ0 - { - _player0.Nusiz = (byte)(value & 0x37); - _player0.Missile.Size = (byte)((value & 0x30) >> 4); - _player0.Missile.Number = (byte)(value & 0x07); - } - else if (maskedAddr == 0x05) // NUSIZ1 - { - _player1.Nusiz = (byte)(value & 0x37); - _player1.Missile.Size = (byte)((value & 0x30) >> 4); - _player1.Missile.Number = (byte)(value & 0x07); - } - else if (maskedAddr == 0x06) // COLUP0 - { - _player0.Color = (byte)(value & 0xFE); - } - else if (maskedAddr == 0x07) // COLUP1 - { - _player1.Color = (byte)(value & 0xFE); - } - else if (maskedAddr == 0x08) // COLUPF - { + // Frame is done + _vsyncEnabled = false; + + // Do not reset hsync, since we're on the first line of the new frame + // hsyncCnt = 0; + } + } + else if (maskedAddr == 0x01) // VBLANK + { + _vblankEnabled = (value & 0x02) != 0; + _capCharging = (value & 0x80) == 0; + if ((value & 0x80) == 0) + { + _capChargeStart = _core.Cpu.TotalExecutedCycles; + } + } + else if (maskedAddr == 0x02) // WSYNC + { + // Halt the CPU until we reach hblank + _core.Cpu.RDY = false; + } + else if (maskedAddr == 0x04) // NUSIZ0 + { + _player0.Nusiz = (byte)(value & 0x37); + _player0.Missile.Size = (byte)((value & 0x30) >> 4); + _player0.Missile.Number = (byte)(value & 0x07); + } + else if (maskedAddr == 0x05) // NUSIZ1 + { + _player1.Nusiz = (byte)(value & 0x37); + _player1.Missile.Size = (byte)((value & 0x30) >> 4); + _player1.Missile.Number = (byte)(value & 0x07); + } + else if (maskedAddr == 0x06) // COLUP0 + { + _player0.Color = (byte)(value & 0xFE); + } + else if (maskedAddr == 0x07) // COLUP1 + { + _player1.Color = (byte)(value & 0xFE); + } + else if (maskedAddr == 0x08) // COLUPF + { _playField.PfColor = (byte)(value & 0xFE); - } - else if (maskedAddr == 0x09) // COLUBK - { - _playField.BkColor = (byte)(value & 0xFE); - } - else if (maskedAddr == 0x0A) // CTRLPF - { - _playField.Reflect = (value & 0x01) != 0; + } + else if (maskedAddr == 0x09) // COLUBK + { + _playField.BkColor = (byte)(value & 0xFE); + } + else if (maskedAddr == 0x0A) // CTRLPF + { + _playField.Reflect = (value & 0x01) != 0; _playField.Score = (value & 0x02) != 0; - _playField.Priority = (value & 0x04) != 0; + _playField.Priority = (value & 0x04) != 0; - _ball.Size = (byte)((value & 0x30) >> 4); - } - else if (maskedAddr == 0x0B) // REFP0 - { - _player0.Reflect = (value & 0x08) != 0; - } - else if (maskedAddr == 0x0C) // REFP1 - { - _player1.Reflect = (value & 0x08) != 0; - } - else if (maskedAddr == 0x0D) // PF0 - { + _ball.Size = (byte)((value & 0x30) >> 4); + } + else if (maskedAddr == 0x0B) // REFP0 + { + _player0.Reflect = (value & 0x08) != 0; + } + else if (maskedAddr == 0x0C) // REFP1 + { + _player1.Reflect = (value & 0x08) != 0; + } + else if (maskedAddr == 0x0D) // PF0 + { pf0_update = value; pf0_updater = true; pf0_delay_clock = 0; @@ -1145,8 +1185,8 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 //_playField.Grp = (uint)((_playField.Grp & 0x0FFFF) + ((ReverseBits(value, 8) & 0x0F) << 16)); } - else if (maskedAddr == 0x0E) // PF1 - { + else if (maskedAddr == 0x0E) // PF1 + { pf1_update = value; pf1_updater = true; pf1_delay_clock = 0; @@ -1168,8 +1208,8 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 } //_playField.Grp = (uint)((_playField.Grp & 0xF00FF) + (value << 8)); } - else if (maskedAddr == 0x0F) // PF2 - { + else if (maskedAddr == 0x0F) // PF2 + { pf2_update = value; pf2_updater = true; pf2_delay_clock = 0; @@ -1191,12 +1231,12 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 } //_playField.Grp = (uint)((_playField.Grp & 0xFFF00) + ReverseBits(value, 8)); } - else if (maskedAddr == 0x10) // RESP0 - { + else if (maskedAddr == 0x10) // RESP0 + { // Resp depends on HMOVE if (!_hmove.LateHBlankReset) { - if (_hsyncCnt < 69) + if (_hsyncCnt < 68) { _player0.HPosCnt = 0; _player0.ResetCnt = 2; @@ -1221,21 +1261,23 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 } } } - else if (maskedAddr == 0x11) // RESP1 - { - // RESP depends on HMOVE + else if (maskedAddr == 0x11) // RESP1 + { + // RESP depends on HMOVE if (!_hmove.LateHBlankReset) { - if (_hsyncCnt < 69) + if (_hsyncCnt < 68) { _player1.HPosCnt = 0; _player1.ResetCnt = 2; _player1.Reset = true; - } else + } + else { _player1.ResetCnt = 0; } - } else + } + else { if (_hsyncCnt < 76) { @@ -1248,21 +1290,22 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 _player1.ResetCnt = 0; } } - - } - else if (maskedAddr == 0x12) // RESM0 - { + + } + else if (maskedAddr == 0x12) // RESM0 + { if (!_hmove.LateHBlankReset) { _player0.Missile.HPosCnt = (byte)(_hsyncCnt < 68 ? 160 - 2 : 160 - 4); - } else + } + else { _player0.Missile.HPosCnt = (byte)(_hsyncCnt < 76 ? 160 - 2 : 160 - 4); } } - else if (maskedAddr == 0x13) // RESM1 - { + else if (maskedAddr == 0x13) // RESM1 + { if (!_hmove.LateHBlankReset) { _player1.Missile.HPosCnt = (byte)(_hsyncCnt < 68 ? 160 - 2 : 160 - 4); @@ -1272,8 +1315,8 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 _player1.Missile.HPosCnt = (byte)(_hsyncCnt < 76 ? 160 - 2 : 160 - 4); } } - else if (maskedAddr == 0x14) // RESBL - { + else if (maskedAddr == 0x14) // RESBL + { if (!_hmove.LateHBlankReset) { _ball.HPosCnt = (byte)(_hsyncCnt < 68 ? 160 - 2 : 160 - 4); @@ -1282,254 +1325,255 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 { _ball.HPosCnt = (byte)(_hsyncCnt < 76 ? 160 - 3 : 160 - 4); } - - } - else if (maskedAddr == 0x15) // AUDC0 - { - WriteAudio(0, AudioRegister.AUDC, (byte)(value & 15)); - } - else if (maskedAddr == 0x16) // AUDC1 - { - WriteAudio(1, AudioRegister.AUDC, (byte)(value & 15)); - } - else if (maskedAddr == 0x17) // AUDF0 - { - WriteAudio(0, AudioRegister.AUDF, (byte)((value & 31) + 1)); - } - else if (maskedAddr == 0x18) // AUDF1 - { - WriteAudio(1, AudioRegister.AUDF, (byte)((value & 31) + 1)); - } - else if (maskedAddr == 0x19) // AUDV0 - { - WriteAudio(0, AudioRegister.AUDV, (byte)(value & 15)); - } - else if (maskedAddr == 0x1A) // AUDV1 - { - WriteAudio(1, AudioRegister.AUDV, (byte)(value & 15)); - } - else if (maskedAddr == 0x1B) // GRP0 - { + + } + else if (maskedAddr == 0x15) // AUDC0 + { + WriteAudio(0, AudioRegister.AUDC, (byte)(value & 15)); + } + else if (maskedAddr == 0x16) // AUDC1 + { + WriteAudio(1, AudioRegister.AUDC, (byte)(value & 15)); + } + else if (maskedAddr == 0x17) // AUDF0 + { + WriteAudio(0, AudioRegister.AUDF, (byte)((value & 31) + 1)); + } + else if (maskedAddr == 0x18) // AUDF1 + { + WriteAudio(1, AudioRegister.AUDF, (byte)((value & 31) + 1)); + } + else if (maskedAddr == 0x19) // AUDV0 + { + WriteAudio(0, AudioRegister.AUDV, (byte)(value & 15)); + } + else if (maskedAddr == 0x1A) // AUDV1 + { + WriteAudio(1, AudioRegister.AUDV, (byte)(value & 15)); + } + else if (maskedAddr == 0x1B) // GRP0 + { prg0_val = value; prg0_delay = 1; - - } - else if (maskedAddr == 0x1C) // GRP1 - { + + } + else if (maskedAddr == 0x1C) // GRP1 + { prg1_val = value; prg1_delay = 1; - - } - else if (maskedAddr == 0x1D) // ENAM0 - { + + } + else if (maskedAddr == 0x1D) // ENAM0 + { enam0_val = (value & 0x02) != 0; enam0_delay = 1; } - else if (maskedAddr == 0x1E) // ENAM1 - { + else if (maskedAddr == 0x1E) // ENAM1 + { enam1_val = (value & 0x02) != 0; enam1_delay = 1; } - else if (maskedAddr == 0x1F) // ENABL - { + else if (maskedAddr == 0x1F) // ENABL + { enamb_val = (value & 0x02) != 0; enamb_delay = 1; - } - else if (maskedAddr == 0x20) // HMP0 - { - _player0.HM = (byte)((value & 0xF0) >> 4); - } - else if (maskedAddr == 0x21) // HMP1 - { - _player1.HM = (byte)((value & 0xF0) >> 4); - } - else if (maskedAddr == 0x22) // HMM0 - { - _player0.Missile.Hm = (byte)((value & 0xF0) >> 4); - } - else if (maskedAddr == 0x23) // HMM1 - { - _player1.Missile.Hm = (byte)((value & 0xF0) >> 4); - } - else if (maskedAddr == 0x24) // HMBL - { - _ball.HM = (byte)((value & 0xF0) >> 4); - } - else if (maskedAddr == 0x25) // VDELP0 - { - _player0.Delay = (value & 0x01) != 0; - } - else if (maskedAddr == 0x26) // VDELP1 - { - _player1.Delay = (value & 0x01) != 0; - } - else if (maskedAddr == 0x27) // VDELBL - { - _ball.Delay = (value & 0x01) != 0; - } - else if (maskedAddr == 0x28) // RESMP0 - { - _player0.Missile.ResetToPlayer = (value & 0x02) != 0; - } - else if (maskedAddr == 0x29) // RESMP1 - { - _player1.Missile.ResetToPlayer = (value & 0x02) != 0; - } - else if (maskedAddr == 0x2A) // HMOVE - { - _hmove.HMoveEnabled = true; - _hmove.HMoveJustStarted = true; - _hmove.HMoveDelayCnt = 0; - } - else if (maskedAddr == 0x2B) // HMCLR - { - _player0.HM = 0; - _player0.Missile.Hm = 0; - _player1.HM = 0; - _player1.Missile.Hm = 0; - _ball.HM = 0; - } - else if (maskedAddr == 0x2C) // CXCLR - { - _player0.Collisions = 0; - _player0.Missile.Collisions = 0; - _player1.Collisions = 0; - _player1.Missile.Collisions = 0; - _ball.Collisions = 0; - } - } + } + else if (maskedAddr == 0x20) // HMP0 + { + HMP0_val = (byte)((value & 0xF0) >> 4); + HMP0_delay = 1; + } + else if (maskedAddr == 0x21) // HMP1 + { + HMP1_val = (byte)((value & 0xF0) >> 4); + HMP1_delay = 1; + } + else if (maskedAddr == 0x22) // HMM0 + { + _player0.Missile.Hm = (byte)((value & 0xF0) >> 4); + } + else if (maskedAddr == 0x23) // HMM1 + { + _player1.Missile.Hm = (byte)((value & 0xF0) >> 4); + } + else if (maskedAddr == 0x24) // HMBL + { + _ball.HM = (byte)((value & 0xF0) >> 4); + } + else if (maskedAddr == 0x25) // VDELP0 + { + _player0.Delay = (value & 0x01) != 0; + } + else if (maskedAddr == 0x26) // VDELP1 + { + _player1.Delay = (value & 0x01) != 0; + } + else if (maskedAddr == 0x27) // VDELBL + { + _ball.Delay = (value & 0x01) != 0; + } + else if (maskedAddr == 0x28) // RESMP0 + { + _player0.Missile.ResetToPlayer = (value & 0x02) != 0; + } + else if (maskedAddr == 0x29) // RESMP1 + { + _player1.Missile.ResetToPlayer = (value & 0x02) != 0; + } + else if (maskedAddr == 0x2A) // HMOVE + { + _hmove.HMoveEnabled = true; + _hmove.HMoveDelayCnt = 0; + } + else if (maskedAddr == 0x2B) // HMCLR + { + _player0.HM = 0; + _player0.Missile.Hm = 0; + _player1.HM = 0; + _player1.Missile.Hm = 0; + _ball.HM = 0; + } + else if (maskedAddr == 0x2C) // CXCLR + { + _player0.Collisions = 0; + _player0.Missile.Collisions = 0; + _player1.Collisions = 0; + _player1.Missile.Collisions = 0; + _ball.Collisions = 0; + } + } - private static int ReverseBits(int value, int bits) - { - int result = 0; - for (int i = 0; i < bits; i++) - { - result = (result << 1) | ((value >> i) & 0x01); - } + private static int ReverseBits(int value, int bits) + { + int result = 0; + for (int i = 0; i < bits; i++) + { + result = (result << 1) | ((value >> i) & 0x01); + } - return result; - } + return result; + } - #region Audio bits + #region Audio bits - private enum AudioRegister : byte { AUDC, AUDF, AUDV } - private struct QueuedCommand - { - public int Time; - public byte Channel; - public AudioRegister Register; - public byte Value; - } + private enum AudioRegister : byte { AUDC, AUDF, AUDV } + private struct QueuedCommand + { + public int Time; + public byte Channel; + public AudioRegister Register; + public byte Value; + } - private int frameStartCycles, frameEndCycles; - private Queue commands = new Queue(4096); + private int frameStartCycles, frameEndCycles; + private Queue commands = new Queue(4096); - public void BeginAudioFrame() - { - frameStartCycles = _core.Cpu.TotalExecutedCycles; - } + public void BeginAudioFrame() + { + frameStartCycles = _core.Cpu.TotalExecutedCycles; + } - public void CompleteAudioFrame() - { - frameEndCycles = _core.Cpu.TotalExecutedCycles; - } + public void CompleteAudioFrame() + { + frameEndCycles = _core.Cpu.TotalExecutedCycles; + } - private void WriteAudio(byte channel, AudioRegister register, byte value) - { - commands.Enqueue(new QueuedCommand { Channel = channel, Register = register, Value = value, Time = _core.Cpu.TotalExecutedCycles - frameStartCycles }); - } + private void WriteAudio(byte channel, AudioRegister register, byte value) + { + commands.Enqueue(new QueuedCommand { Channel = channel, Register = register, Value = value, Time = _core.Cpu.TotalExecutedCycles - frameStartCycles }); + } - private void ApplyAudioCommand(QueuedCommand cmd) - { - switch (cmd.Register) - { - case AudioRegister.AUDC: - AUD[cmd.Channel].AUDC = cmd.Value; - break; - case AudioRegister.AUDF: - AUD[cmd.Channel].AUDF = cmd.Value; - break; - case AudioRegister.AUDV: - AUD[cmd.Channel].AUDV = cmd.Value; - break; - } - } + private void ApplyAudioCommand(QueuedCommand cmd) + { + switch (cmd.Register) + { + case AudioRegister.AUDC: + AUD[cmd.Channel].AUDC = cmd.Value; + break; + case AudioRegister.AUDF: + AUD[cmd.Channel].AUDF = cmd.Value; + break; + case AudioRegister.AUDV: + AUD[cmd.Channel].AUDV = cmd.Value; + break; + } + } - // TODO: more accurate would be to have audio.Cycle() occur at - // the explicit exact times in the scanline, instead of just approximately spaced - public void GetSamples(short[] samples) - { - var samples31khz = new short[_audioClocks]; // mono + // TODO: more accurate would be to have audio.Cycle() occur at + // the explicit exact times in the scanline, instead of just approximately spaced + public void GetSamples(short[] samples) + { + var samples31khz = new short[_audioClocks]; // mono - int elapsedCycles = frameEndCycles - frameStartCycles; - if (elapsedCycles == 0) - { - elapsedCycles = 1; // better than diving by zero - } + int elapsedCycles = frameEndCycles - frameStartCycles; + if (elapsedCycles == 0) + { + elapsedCycles = 1; // better than diving by zero + } - int start = 0; - while (commands.Count > 0) - { - var cmd = commands.Dequeue(); - int pos = (cmd.Time * samples31khz.Length) / elapsedCycles; - pos = Math.Min(pos, samples31khz.Length); // sometimes the cpu timestamp of the write is > frameEndCycles - GetSamplesImmediate(samples31khz, start, pos - start); - start = pos; - ApplyAudioCommand(cmd); - } + int start = 0; + while (commands.Count > 0) + { + var cmd = commands.Dequeue(); + int pos = (cmd.Time * samples31khz.Length) / elapsedCycles; + pos = Math.Min(pos, samples31khz.Length); // sometimes the cpu timestamp of the write is > frameEndCycles + GetSamplesImmediate(samples31khz, start, pos - start); + start = pos; + ApplyAudioCommand(cmd); + } - GetSamplesImmediate(samples31khz, start, samples31khz.Length - start); + GetSamplesImmediate(samples31khz, start, samples31khz.Length - start); - // convert from 31khz to 44khz - for (var i = 0; i < samples.Length / 2; i++) - { - samples[i * 2] = samples31khz[(int)(((double)samples31khz.Length / (double)(samples.Length / 2)) * i)]; - samples[(i * 2) + 1] = samples[i * 2]; - } + // convert from 31khz to 44khz + for (var i = 0; i < samples.Length / 2; i++) + { + samples[i * 2] = samples31khz[(int)(((double)samples31khz.Length / (double)(samples.Length / 2)) * i)]; + samples[(i * 2) + 1] = samples[i * 2]; + } - _audioClocks = 0; - } + _audioClocks = 0; + } - public void GetSamplesImmediate(short[] samples, int start, int len) - { - for (var i = start; i < start + len; i++) - { - samples[i] += AUD[0].Cycle(); - samples[i] += AUD[1].Cycle(); - } - } + public void GetSamplesImmediate(short[] samples, int start, int len) + { + for (var i = start; i < start + len; i++) + { + samples[i] += AUD[0].Cycle(); + samples[i] += AUD[1].Cycle(); + } + } - public void DiscardSamples() - { - commands.Clear(); - _audioClocks = 0; - } + public void DiscardSamples() + { + commands.Clear(); + _audioClocks = 0; + } - #endregion + #endregion - public void SyncState(Serializer ser) - { - ser.BeginSection("TIA"); - _ball.SyncState(ser); - _hmove.SyncState(ser); - ser.Sync("hsyncCnt", ref _hsyncCnt); - // some of these things weren't in the state because they weren't needed if - // states were always taken at frame boundaries - ser.Sync("capChargeStart", ref _capChargeStart); - ser.Sync("capCharging", ref _capCharging); - ser.Sync("vblankEnabled", ref _vblankEnabled); - ser.Sync("vsyncEnabled", ref _vsyncEnabled); - ser.Sync("CurrentScanLine", ref _CurrentScanLine); - ser.Sync("scanlinebuffer", ref _scanlinebuffer, false); + public void SyncState(Serializer ser) + { + ser.BeginSection("TIA"); + _ball.SyncState(ser); + _hmove.SyncState(ser); + ser.Sync("hsyncCnt", ref _hsyncCnt); + // some of these things weren't in the state because they weren't needed if + // states were always taken at frame boundaries + ser.Sync("capChargeStart", ref _capChargeStart); + ser.Sync("capCharging", ref _capCharging); + ser.Sync("vblankEnabled", ref _vblankEnabled); + ser.Sync("vsyncEnabled", ref _vsyncEnabled); + ser.Sync("CurrentScanLine", ref _CurrentScanLine); + ser.Sync("scanlinebuffer", ref _scanlinebuffer, false); - ser.BeginSection("Player0"); - _player0.SyncState(ser); - ser.EndSection(); - ser.BeginSection("Player1"); - _player1.SyncState(ser); - ser.EndSection(); - _playField.SyncState(ser); - ser.EndSection(); - } - } + ser.BeginSection("Player0"); + _player0.SyncState(ser); + ser.EndSection(); + ser.BeginSection("Player1"); + _player1.SyncState(ser); + ser.EndSection(); + _playField.SyncState(ser); + ser.EndSection(); + } + } } diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/Tia.HMoveData.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/Tia.HMoveData.cs index 2b29cec0cf..48a6ec8244 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/Tia.HMoveData.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/Tia.HMoveData.cs @@ -7,7 +7,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 private struct HMoveData { public bool HMoveEnabled; - public bool HMoveJustStarted; public bool LateHBlankReset; public bool DecCntEnabled; @@ -20,7 +19,13 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 public byte HMoveDelayCnt; public byte HMoveCnt; - public byte Player0Cnt; + public int test_count_p0; + public int test_count_p1; + public int test_count_m0; + public int test_count_m1; + public int test_count_b; + + public byte Player0Cnt; public byte Player1Cnt; public byte Missile0Cnt; public byte Missile1Cnt; @@ -30,7 +35,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 { ser.BeginSection("HMove"); ser.Sync("hmoveEnabled", ref HMoveEnabled); - ser.Sync("hmoveJustStarted", ref HMoveJustStarted); ser.Sync("lateHBlankReset", ref LateHBlankReset); ser.Sync("decCntEnabled", ref DecCntEnabled); ser.Sync("player0Latch", ref Player0Latch); @@ -44,8 +48,13 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 ser.Sync("player1Cnt", ref Player1Cnt); ser.Sync("missile0Cnt", ref Missile0Cnt); ser.Sync("missile1Cnt", ref Missile1Cnt); - ser.Sync("ballCnt", ref BallCnt); - ser.EndSection(); + ser.Sync("Test_count_p0", ref test_count_p0); + ser.Sync("Test_count_p1", ref test_count_p1); + ser.Sync("Test_count_m0", ref test_count_m0); + ser.Sync("Test_count_m1", ref test_count_m1); + ser.Sync("Test_count_b", ref test_count_b); + ser.Sync("ballCnt", ref BallCnt); + ser.EndSection(); } } }