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