using System; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// /// The abstract class that all emulated models will inherit from /// * Memory * /// public abstract partial class SpectrumBase { #region Memory Fields & Properties /// /// ROM Banks /// public byte[] ROM0 = new byte[0x4000]; public byte[] ROM1 = new byte[0x4000]; public byte[] ROM2 = new byte[0x4000]; public byte[] ROM3 = new byte[0x4000]; /// /// RAM Banks /// public byte[] RAM0 = new byte[0x4000]; // Bank 0 public byte[] RAM1 = new byte[0x4000]; // Bank 1 public byte[] RAM2 = new byte[0x4000]; // Bank 2 public byte[] RAM3 = new byte[0x4000]; // Bank 3 public byte[] RAM4 = new byte[0x4000]; // Bank 4 public byte[] RAM5 = new byte[0x4000]; // Bank 5 public byte[] RAM6 = new byte[0x4000]; // Bank 6 public byte[] RAM7 = new byte[0x4000]; // Bank 7 /// /// Signs that the shadow screen is now displaying /// Note: normal screen memory in RAM5 is not altered, the ULA just outputs Screen1 instead (RAM7) /// public bool SHADOWPaged; /// /// Index of the current RAM page /// /// 128k, +2/2a and +3 only /// public int RAMPaged; /// /// Signs that all paging is disabled /// If this is TRUE, then 128k and above machines need a hard reset before paging is allowed again /// public bool PagingDisabled; /// /// Index of the currently paged ROM /// 128k, +2/2a and +3 only /// protected int ROMPaged; public virtual int _ROMpaged { get { return ROMPaged; } set { ROMPaged = value; } } /* * +3/+2A only */ /// /// High bit of the ROM selection (in normal paging mode) /// public bool ROMhigh = false; /// /// Low bit of the ROM selection (in normal paging mode) /// public bool ROMlow = false; /// /// Signs that the +2a/+3 special paging mode is activated /// public bool SpecialPagingMode; /// /// Index of the current special paging mode (0-3) /// public int PagingConfiguration; /// /// The last byte that was read after contended cycles /// public byte LastContendedReadByte; #endregion #region Memory Related Methods /// /// Simulates reading from the bus /// Paging should be handled here /// /// /// public abstract byte ReadBus(ushort addr); /// /// Pushes a value onto the data bus that should be valid as long as the interrupt is true /// /// /// public virtual byte PushBus() { return 0xFF; } /// /// Simulates writing to the bus /// Paging should be handled here /// /// /// public virtual void WriteBus(ushort addr, byte value) { throw new NotImplementedException("Must be overriden"); } /// /// Reads a byte of data from a specified memory address /// (with memory contention if appropriate) /// /// /// public abstract byte ReadMemory(ushort addr); /// /// Returns the ROM/RAM enum that relates to this particular memory read operation /// /// /// public abstract ZXSpectrum.CDLResult ReadCDL(ushort addr); /// /// Writes a byte of data to a specified memory address /// (with memory contention if appropriate) /// /// /// public abstract void WriteMemory(ushort addr, byte value); /// /// Sets up the ROM /// /// public abstract void InitROM(RomData romData); /// /// ULA reads the memory at the specified address /// (No memory contention) /// /// /// public virtual byte FetchScreenMemory(ushort addr) { var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000)); //var value = ReadBus(addr); return value; } /// /// Checks whether supplied address is in a potentially contended bank /// /// public abstract bool IsContended(ushort addr); /// /// Returns TRUE if there is a contended bank paged in /// /// public abstract bool ContendedBankPaged(); #endregion #region Helper Methods /// /// Detects whether this is a 48k machine (or a 128k in 48k mode) /// /// public virtual bool IsIn48kMode() { if (this.GetType() == typeof(ZX48) || this.GetType() == typeof(ZX16) || PagingDisabled) { return true; } else return false; } /// /// Monitors ROM access /// Used to auto start/stop the tape device when appropriate /// /// public virtual void TestForTapeTraps(int addr) { if (TapeDevice.TapeIsPlaying) { // THE 'ERROR' RESTART if (addr == 8) { //TapeDevice?.AutoStopTape(); return; } // THE 'ED-ERROR' SUBROUTINE if (addr == 4223) { //TapeDevice?.AutoStopTape(); return; } // THE 'ERROR-2' ROUTINE if (addr == 83) { //TapeDevice?.AutoStopTape(); return; } // THE 'MASKABLE INTERRUPT' ROUTINE // This is sometimes used when the tape is to be stopped // if (addr == 56) { //TapeDevice.MaskableInterruptCount++; //if (TapeDevice.MaskableInterruptCount > 50) //{ //TapeDevice.MaskableInterruptCount = 0; //TapeDevice.AutoStopTape(); //} //TapeDevice?.AutoStopTape(); return; } } else { // THE 'LD-BYTES' SUBROUTINE if (addr == 1366) { //TapeDevice?.AutoStartTape(); return; } // THE 'LD-EDGE-2' AND 'LD-EDGE-1' SUBROUTINES if (addr == 1507) { //TapeDevice?.AutoStartTape(); return; } } } #endregion } }