1
0
mirror of https://github.com/livingcomputermuseum/ContrAlto.git synced 2026-01-23 10:57:55 +00:00

373 lines
12 KiB
C#

/*
This file is part of ContrAlto.
ContrAlto is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ContrAlto is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with ContrAlto. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace Contralto.CPU.Nova
{
/// <summary>
/// Quick and dirty disassembler for Nova instructions, so we can
/// see what the emulator task is executing more clearly.
/// </summary>
public static class NovaDisassembler
{
static NovaDisassembler()
{
_altoIOTable = new Dictionary<ushort, string>();
_altoIOTable.Add(0x6210, "MUL");
_altoIOTable.Add(0x6211, "DIV");
_altoIOTable.Add(0x6203, "RCLK");
_altoIOTable.Add(0x6204, "SIO");
_altoIOTable.Add(0x6205, "BLT");
_altoIOTable.Add(0x6206, "BLKS");
_altoIOTable.Add(0x6207, "SIT");
_altoIOTable.Add(0x6208, "JMPRAM");
_altoIOTable.Add(0x6209, "RDRAM");
_altoIOTable.Add(0x620a, "WRTRAM");
_altoIOTable.Add(0x620c, "VERSION");
_altoIOTable.Add(0x620d, "DREAD");
_altoIOTable.Add(0x620e, "DWRITE");
_altoIOTable.Add(0x620f, "DEXCH");
_altoIOTable.Add(0x6212, "DIAGNOSE1");
_altoIOTable.Add(0x6213, "DIAGNOSE2");
_altoIOTable.Add(0x6214, "BITBLT");
_altoIOTable.Add(0x6215, "XMLDA");
_altoIOTable.Add(0x6216, "XMSTA");
_altoIOTable.Add(0x6200, "DIR");
_altoIOTable.Add(0x6201, "EIR");
_altoIOTable.Add(0x6202, "BRI");
_altoIOTable.Add(0x620b, "DIRS");
}
/// <summary>
/// Disassembles the specified instruction
/// </summary>
/// <param name="instructionWord"></param>
/// <returns></returns>
public static string DisassembleInstruction(ushort address, ushort instructionWord)
{
string disassembly = null;
switch ((InstructionClass)(instructionWord & 0xe000))
{
case InstructionClass.MEM:
disassembly = DisassembleMem(address, instructionWord);
break;
case InstructionClass.LDA:
case InstructionClass.STA:
disassembly = DisassembleLoadStore(address, instructionWord);
break;
case InstructionClass.AltoSpecific1:
case InstructionClass.AltoSpecific2:
disassembly = DisassembleAltoSpecific(address, instructionWord);
break;
default:
// None of the above, must be ALC
disassembly = DisassembleALC(instructionWord);
break;
}
return disassembly;
}
private static string DisassembleMem(ushort address, ushort instructionWord)
{
StringBuilder d = new StringBuilder();
// Function
MemFunction func = (MemFunction)(instructionWord & 0x1800);
// Indirect bit
bool indirect = (instructionWord & 0x400) != 0;
// Indexing mode
MemIndex index = (MemIndex)(instructionWord & 0x300);
// Displacement
int disp = (instructionWord & 0xff);
switch (index)
{
case MemIndex.PageZero:
d.AppendFormat("{0}{1} {2}",
func,
indirect ? "@" : String.Empty,
Conversion.ToOctal(disp));
break;
case MemIndex.PCRelative:
d.AppendFormat("{0}{1} .+{2} ;({3})",
func,
indirect ? "@" : String.Empty,
Conversion.ToOctal((sbyte)disp),
Conversion.ToOctal((sbyte)disp + address));
break;
case MemIndex.AC2Relative:
d.AppendFormat("{0}{1} AC2+{2}",
func,
indirect ? "@" : String.Empty,
Conversion.ToOctal((sbyte)disp));
break;
case MemIndex.AC3Relative:
d.AppendFormat("{0}{1} AC3+{2}",
func,
indirect ? "@" : String.Empty,
Conversion.ToOctal((sbyte)disp));
break;
default:
throw new InvalidOperationException("unexpected index type.");
}
return d.ToString();
}
private static string DisassembleLoadStore(ushort address, ushort instructionWord)
{
StringBuilder d = new StringBuilder();
// Accumulator
int ac = (instructionWord & 0x1800) >> 11;
// Indirect bit
bool indirect = (instructionWord & 0x400) != 0;
// Indexing mode
MemIndex index = (MemIndex)(instructionWord & 0x300);
// Displacement
int disp = (instructionWord & 0xff);
// instruction (LDA or STA)
string inst = (InstructionClass)(instructionWord & 0x6000) == InstructionClass.LDA ? "LDA" : "STA";
switch(index)
{
case MemIndex.PageZero:
d.AppendFormat("{0}{1} {2},{3}",
inst,
indirect ? "@" : String.Empty,
ac,
Conversion.ToOctal(disp));
break;
case MemIndex.PCRelative:
d.AppendFormat("{0}{1} {2},.+{3} ;({4})",
inst,
indirect ? "@" : String.Empty,
ac,
Conversion.ToOctal((sbyte)disp),
Conversion.ToOctal((sbyte)disp + address));
break;
case MemIndex.AC2Relative:
d.AppendFormat("{0}{1} {2},AC2+{3}",
inst,
indirect ? "@" : String.Empty,
ac,
Conversion.ToOctal((sbyte)disp));
break;
case MemIndex.AC3Relative:
d.AppendFormat("{0}{1} {2},AC3+{3}",
inst,
indirect ? "@" : String.Empty,
ac,
Conversion.ToOctal((sbyte)disp));
break;
default:
throw new InvalidOperationException("unexpected index type.");
}
return d.ToString();
}
private static string DisassembleAltoSpecific(ushort address, ushort instructionWord)
{
StringBuilder d = new StringBuilder();
//
// Check for Alto-specific instructions that do not use the DISP field
//
if (_altoIOTable.ContainsKey(instructionWord))
{
d.Append(_altoIOTable[instructionWord]);
}
else
{
//
// Check for Alto-specific instructions using the DISP field
int topBits = (instructionWord & 0xff00);
switch (topBits)
{
case 0x6000:
d.AppendFormat("CYCLE {0}", Conversion.ToOctal(instructionWord & 0xf));
break;
case 0x6900:
d.AppendFormat("JSRII {0} ;({1})",
Conversion.ToOctal((sbyte)(instructionWord & 0xff)),
Conversion.ToOctal(address + (sbyte)((instructionWord & 0xff))));
break;
case 0x6a00:
d.AppendFormat("JSRIS {0}", Conversion.ToOctal((sbyte)(instructionWord & 0xff)));
break;
case 0x6e00:
d.AppendFormat("CONVERT {0}", Conversion.ToOctal((sbyte)(instructionWord & 0xff)));
break;
default:
// Unimplemented, treated as a TRAP to either ROM or RAM.
d.Append("TRAP");
break;
}
}
return d.ToString();
}
private static string DisassembleALC(ushort instructionWord)
{
StringBuilder d = new StringBuilder();
// Grab source/dest accumulators
int srcAC = (instructionWord & 0x6000) >> 13;
int dstAC = (instructionWord & 0x1800) >> 11;
// Function
ALCFunctions func = (ALCFunctions)(instructionWord & 0x700);
// Shift
ALCShift shift = (ALCShift)(instructionWord & 0xc0);
// Carry
ALCCarry carry = (ALCCarry)(instructionWord & 0x30);
// No load
bool noLoad = ((instructionWord & 0x8) != 0);
// Skip
ALCSkip skip = (ALCSkip)(instructionWord & 0x7);
// initial format (minus skip):
// FUNC[shift][carry][noload] src, dest
d.AppendFormat(
"{0}{1}{2}{3} {4},{5}",
func,
shift == ALCShift.None ? String.Empty : shift.ToString(),
carry == ALCCarry.None ? String.Empty : carry.ToString(),
noLoad ? "#" : String.Empty,
srcAC,
dstAC);
// If a skip is specified, tack it on
if (skip != ALCSkip.None)
{
d.AppendFormat(",{0}", skip);
}
return d.ToString();
}
/// <summary>
/// Holds a map from opcode to Alto I/O mnemonics
/// </summary>
private static Dictionary<ushort, string> _altoIOTable;
private enum InstructionClass
{
MEM = 0x0000,
LDA = 0x2000,
STA = 0x4000,
AltoSpecific1 = 0x6000,
AltoSpecific2 = 0x7000,
}
private enum ALCFunctions
{
COM = 0x000,
NEG = 0x100,
MOV = 0x200,
INC = 0x300,
ADC = 0x400,
SUB = 0x500,
ADD = 0x600,
AND = 0x700,
}
private enum ALCShift
{
None = 0x00,
L = 0x40,
R = 0x80,
S = 0xc0,
}
private enum ALCCarry
{
None = 0x00,
Z = 0x10,
O = 0x20,
C = 0x30,
}
private enum ALCSkip
{
None = 0x0,
SKP = 0x1,
SZC = 0x2,
SNC = 0x3,
SZR = 0x4,
SNR = 0x5,
SEZ = 0x6,
SBN = 0x7,
}
private enum MemFunction
{
JMP = 0x0000,
JSR = 0x0800,
ISZ = 0x1000,
DSZ = 0x1800,
}
private enum MemIndex
{
PageZero = 0x000,
PCRelative = 0x100,
AC2Relative = 0x200,
AC3Relative = 0x300,
}
}
}