using BizHawk.Common; using System; namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { // Emulates the M6532 RIOT Chip public class M6532 { public A7800Hawk Core { get; set; } public byte _ddRa = 0x00; public byte _ddRb = 0x00; public byte _outputA = 0x00; public byte _outputB = 0x00; public TimerData Timer; public M6532() { // arbitrary value to start with. Timer.Value = 0x73; Timer.PrescalerShift = 10; Timer.PrescalerCount = 1 << Timer.PrescalerShift; } public byte ReadMemory(ushort addr, bool peek) { if ((addr & 0x0200) == 0) // If not register select, read Ram { //return _core.Ram[(ushort)(addr & 0x007f)]; return 0; } var registerAddr = (ushort)(addr & 0x0007); if (registerAddr == 0x00) { Core._islag = false; // Read Output reg A // Combine readings from player 1 and player 2 // actually depends on setting in SWCHCNTA (aka DDRa) byte temp = (byte)(Core.p1_state | Core.p2_state); temp = (byte)(temp & ~_ddRa); temp = (byte)(temp + (_outputA & _ddRa)); return temp; } if (registerAddr == 0x01) { // Read DDRA return _ddRa; } if (registerAddr == 0x02) { Core._islag = false; // Read Output reg B byte temp = Core.con_state; temp = (byte)(temp & ~_ddRb); temp = (byte)(temp + (_outputB & _ddRb)); return temp; } if (registerAddr == 0x03) // Read DDRB { return _ddRb; } if ((registerAddr & 0x5) == 0x4) { // Bit 0x0080 contains interrupt enable/disable Timer.InterruptEnabled = (addr & 0x0080) != 0; // The interrupt flag will be reset whenever the Timer is access by a read or a write // However, the reading of the timer at the same time the interrupt occurs will not reset the interrupt flag // (M6532 Datasheet) if (!(Timer.PrescalerCount == 0 && Timer.Value == 0)) { Timer.InterruptFlag = false; } return Timer.Value; } // TODO: fix this to match real behaviour // This is an undocumented instruction whose behaviour is more dynamic then indicated here if ((registerAddr & 0x5) == 0x5) { // Read interrupt flag if (Timer.InterruptFlag) // Timer.InterruptEnabled && ) { return 0xC0; } return 0x00; } return 0x3A; } public void WriteMemory(ushort addr, byte value) { if ((addr & 0x0200) == 0) // If the RS bit is not set, this is a ram write { //_core.Ram[(ushort)(addr & 0x007f)] = value; } else { // If bit 0x0010 is set, and bit 0x0004 is set, this is a timer write if ((addr & 0x0014) == 0x0014) { var registerAddr = (ushort)(addr & 0x0007); // Bit 0x0080 contains interrupt enable/disable Timer.InterruptEnabled = (addr & 0x0080) != 0; // The interrupt flag will be reset whenever the Timer is access by a read or a write // (M6532 datasheet) if (registerAddr == 0x04) { // Write to Timer/1 Timer.PrescalerShift = 0; Timer.Value = value; Timer.PrescalerCount = 0; // 1 << Timer.PrescalerShift; Timer.InterruptFlag = false; } else if (registerAddr == 0x05) { // Write to Timer/8 Timer.PrescalerShift = 3; Timer.Value = value; Timer.PrescalerCount = 0; // 1 << Timer.PrescalerShift; Timer.InterruptFlag = false; } else if (registerAddr == 0x06) { // Write to Timer/64 Timer.PrescalerShift = 6; Timer.Value = value; Timer.PrescalerCount = 0; // 1 << Timer.PrescalerShift; Timer.InterruptFlag = false; } else if (registerAddr == 0x07) { // Write to Timer/1024 Timer.PrescalerShift = 10; Timer.Value = value; Timer.PrescalerCount = 0; // 1 << Timer.PrescalerShift; Timer.InterruptFlag = false; } } // If bit 0x0004 is not set, bit 0x0010 is ignored and // these are register writes else if ((addr & 0x0004) == 0) { var registerAddr = (ushort)(addr & 0x0007); if (registerAddr == 0x00) { // Write Output reg A _outputA = value; } else if (registerAddr == 0x01) { // Write DDRA _ddRa = value; } else if (registerAddr == 0x02) { // Write Output reg B _outputB = value; } else if (registerAddr == 0x03) { // Write DDRB _ddRb = value; } } } } public void Reset() { // arbitrary value to start with. Timer.Value = 0x73; Timer.PrescalerShift = 10; Timer.PrescalerCount = 1 << Timer.PrescalerShift; _ddRa = 0x00; _ddRb = 0x00; _outputA = 0x00; _outputB = 0x00; } public void SyncState(Serializer ser) { ser.BeginSection("M6532"); ser.Sync("ddra", ref _ddRa); ser.Sync("ddrb", ref _ddRb); ser.Sync("OutputA", ref _outputA); ser.Sync("OutputB", ref _outputB); Timer.SyncState(ser); ser.EndSection(); } public struct TimerData { public int PrescalerCount; public byte PrescalerShift; public byte Value; public bool InterruptEnabled; public bool InterruptFlag; public void Tick() { if (PrescalerCount == 0) { Value--; PrescalerCount = 1 << PrescalerShift; } PrescalerCount--; if (PrescalerCount == 0) { if (Value == 0) { InterruptFlag = true; PrescalerShift = 0; } } } public void SyncState(Serializer ser) { ser.Sync("prescalerCount", ref PrescalerCount); ser.Sync("prescalerShift", ref PrescalerShift); ser.Sync("value", ref Value); ser.Sync("interruptEnabled", ref InterruptEnabled); ser.Sync("interruptFlag", ref InterruptFlag); } } } }