/* 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 . */ using System; using System.Collections.Generic; using System.Text; namespace Contralto.CPU.Nova { /// /// Quick and dirty disassembler for Nova instructions, so we can /// see what the emulator task is executing more clearly. /// public static class NovaDisassembler { static NovaDisassembler() { _altoIOTable = new Dictionary(); _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"); } /// /// Disassembles the specified instruction /// /// /// 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(); } /// /// Holds a map from opcode to Alto I/O mnemonics /// private static Dictionary _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, } } }