using Contralto.Logging; using System; using System.IO; namespace Contralto.CPU { public enum MicrocodeBank { ROM0 = 0, ROM1 = 1, RAM0 = 2, RAM1 = 3, RAM2 = 4 } struct RomFile { public RomFile(string filename, ushort addr, int bitPosition) { Filename = filename; StartingAddress = addr; BitPosition = bitPosition; } public string Filename; public ushort StartingAddress; public int BitPosition; } static class UCodeMemory { static UCodeMemory() { Init(); } public static void Reset() { Init(); } private static void Init() { // // Max 3 banks of microcode RAM _uCodeRam = new UInt32[1024 * 3]; LoadMicrocode(_uCodeRoms); // // Cache 5k of instructions: max 2K ROM, 3K RAM. _decodeCache = new MicroInstruction[1024 * 5]; // Precache ROM CacheMicrocodeROM(); // Precache (empty) RAM for(ushort i=0;i<_uCodeRam.Length;i++) { UpdateRAMCache(i); } // Start in ROM0 _microcodeBank = new MicrocodeBank[16]; _ramAddr = 0; _ramBank = 0; _ramSelect = true; _lowHalfsel = true; } public static void LoadBanksFromRMR(ushort rmr) { for(int i=0;i<16;i++) { _microcodeBank[i] = (rmr & (1 << i)) == 0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM0; } } /// /// Exposes the raw contents of the Microcode ROM /// public static UInt32[] UCodeROM { get { return _uCodeRom; } } /// /// Exposes the raw contents of the Microcode RAM /// public static UInt32[] UCodeRAM { get { return _uCodeRam; } } public static MicrocodeBank GetBank(TaskType task) { return _microcodeBank[(int)task]; } public static void LoadControlRAMAddress(ushort address) { _ramBank = (address & 0x3000) >> 12; _ramSelect = (address & 0x0800) == 0; _lowHalfsel = (address & 0x0400) == 0; _ramAddr = (address & 0x3ff); // Clip RAM bank into range, it's always 0 unless we have a 3K uCode RAM system switch (Configuration.SystemType) { case SystemType.OneKRom: case SystemType.TwoKRom: _ramBank = 0; break; case SystemType.ThreeKRam: if (_ramBank > 2) { _ramBank = 2; } break; } } /// /// Implements the SWMODE F1 logic; selects the proper uCode bank (from /// RAM or ROM) based on the supplied NEXT value. /// Technically this is only supported for the Emulator task. /// /// public static void SwitchMode(ushort nextAddress, TaskType task) { // Log.Write(Logging.LogComponent.Microcode, "SWMODE: Current Bank {0}", _microcodeBank[(int)task]); switch (Configuration.SystemType) { case SystemType.OneKRom: _microcodeBank[(int)task] = _microcodeBank[(int)task] == MicrocodeBank.ROM0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM0; break; case SystemType.TwoKRom: switch (_microcodeBank[(int)task]) { case MicrocodeBank.ROM0: _microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM1; break; case MicrocodeBank.ROM1: _microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.RAM0; break; case MicrocodeBank.RAM0: _microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.ROM1; break; } break; case SystemType.ThreeKRam: if ((nextAddress & 0x100) == 0) { switch(_microcodeBank[(int)task]) { case MicrocodeBank.ROM0: _microcodeBank[(int)task] = (nextAddress & 0x80) == 0 ? MicrocodeBank.RAM0 : MicrocodeBank.RAM2; break; case MicrocodeBank.RAM0: _microcodeBank[(int)task] = (nextAddress & 0x80) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.RAM2; break; case MicrocodeBank.RAM1: _microcodeBank[(int)task] = (nextAddress & 0x80) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.RAM2; break; case MicrocodeBank.RAM2: _microcodeBank[(int)task] = (nextAddress & 0x80) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.RAM1; break; } } else { switch (_microcodeBank[(int)task]) { case MicrocodeBank.ROM0: _microcodeBank[(int)task] = (nextAddress & 0x80) == 0 ? MicrocodeBank.RAM1 : MicrocodeBank.RAM0; break; case MicrocodeBank.RAM0: _microcodeBank[(int)task] = (nextAddress & 0x80) == 0 ? MicrocodeBank.RAM1 : MicrocodeBank.RAM1; break; case MicrocodeBank.RAM1: _microcodeBank[(int)task] = (nextAddress & 0x80) == 0 ? MicrocodeBank.RAM0 : MicrocodeBank.RAM0; break; case MicrocodeBank.RAM2: _microcodeBank[(int)task] = (nextAddress & 0x80) == 0 ? MicrocodeBank.RAM0 : MicrocodeBank.RAM0; break; } } break; } // Log.Write(Logging.LogComponent.Microcode, "SWMODE: New Bank {0} for Task {1}", _microcodeBank[(int)task], task); } public static ushort ReadRAM() { if (!_ramSelect) { throw new NotImplementedException("Read from microcode ROM not implemented."); } Log.Write(Logging.LogComponent.Microcode, "CRAM address for read: Bank {0}, RAM {1}, lowhalf {2} addr {3}", _ramBank, _ramSelect, _lowHalfsel, Conversion.ToOctal(_ramAddr)); UInt32 data = MapRAMWord(_uCodeRam[_ramAddr + (_ramBank * 1024)]); // Flip the necessary bits before returning them. // (See table in section 8.3 of HWRef.) ushort halfWord = (ushort)(_lowHalfsel ? data : (data >> 16)); Log.Write(Logging.LogComponent.Microcode, "CRAM data read: {0}-{1}", _lowHalfsel ? "low" : "high", Conversion.ToOctal(halfWord)); return halfWord; } public static void WriteRAM(ushort low, ushort high) { if (!_ramSelect) { // No-op, can't write to ROM. return; } /* Log.Write(Logging.LogComponent.Microcode, "CRAM address for write: Bank {0}, addr {1}", _ramBank, Conversion.ToOctal(_ramAddr)); Log.Write(Logging.LogComponent.Microcode, "CRAM write of low {0}, high {1}", Conversion.ToOctal(low), Conversion.ToOctal(high)); */ ushort address = (ushort)(_ramAddr + _ramBank * 1024); _uCodeRam[address] = MapRAMWord(((UInt32)(high) << 16) | low); UpdateRAMCache(address); } /// /// Retrieve the microinstruction for the given address using the currently /// selected memory bank. /// /// /// public static MicroInstruction GetInstruction(ushort address, TaskType task) { return _decodeCache[address + (int)_microcodeBank[(int)task] * 1024]; } private static void LoadMicrocode(RomFile[] romInfo) { _uCodeRom = new UInt32[2048]; foreach(RomFile file in romInfo) { // // Each file contains 1024 bytes, each byte containing one nybble in the low 4 bits. // using(FileStream fs = new FileStream(Path.Combine("ROM", file.Filename), FileMode.Open, FileAccess.Read)) { int length = (int)fs.Length; if (length != 1024) { throw new InvalidOperationException("ROM file should be 1024 bytes in length"); } byte[] data = new byte[fs.Length]; fs.Read(data, 0, (int)fs.Length); // OR in the data for(int i=0;i