380 lines
13 KiB
Java
380 lines
13 KiB
Java
|
package app;
|
||
|
|
||
|
// Project imports
|
||
|
import vue.*;
|
||
|
|
||
|
// Instruction disassembler
|
||
|
class Disassembler {
|
||
|
private Disassembler() { } // Cannot be instantiated
|
||
|
static { setDefaults(); } // Initialize to default settings
|
||
|
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
// Global Settings //
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
static boolean bcondCombine; // Merge Bcond condition into mnemonic
|
||
|
static boolean bcondNames; // Use symbolic condition names for Bcond
|
||
|
static boolean condCaps; // Uppercase condition mnemonics
|
||
|
static boolean destLast; // Destination register last
|
||
|
static boolean dispDest; // Resolve jump destination addresses
|
||
|
static boolean dispInside; // Load/store displacement inside brackets
|
||
|
static boolean hexCaps; // Upercase hexadecimal
|
||
|
static int hexMode; // Hexadecimal decoration
|
||
|
static boolean immNumber; // Use number sign with immediate values
|
||
|
static boolean jmpBrackets; // Square brackets around JMP
|
||
|
static boolean jumpAddress; // Use jump destination addresses
|
||
|
static boolean lower; // Use L instead of C in conditions
|
||
|
static boolean mnemonicCaps; // Uppercase mnemonics
|
||
|
static boolean programCaps; // Uppercase program register names
|
||
|
static boolean programNames; // Use symbolic program register names
|
||
|
static boolean setfCombine; // Merge SETF condition into mnemonic
|
||
|
static boolean setfNames; // Use symbolic condition names for SETF
|
||
|
static boolean systemCaps; // Uppercase system register names
|
||
|
static boolean systemNames; // Use symbolic system register names
|
||
|
static boolean zero; // Use Z instead of E in conditions
|
||
|
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
// Constants //
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Hexadecimal decorations
|
||
|
static final int DOLLAR = 0;
|
||
|
static final int H = 1;
|
||
|
static final int ZEROX = 2;
|
||
|
|
||
|
// Condition mneonics
|
||
|
static final String[] CONDITIONS = {
|
||
|
"V" , "C" , "E" , "NH", "N", "T", "LT", "LE",
|
||
|
"NV", "NC", "NE", "H" , "P", "F", "GE", "GT"
|
||
|
};
|
||
|
|
||
|
// Mnemonics
|
||
|
static final String[] MNEMONICS = {
|
||
|
"---" , "ADD" , "ADD" , "ADDF.S", "ADDI" , "AND" ,
|
||
|
"ANDBSU" , "ANDI" , "ANDNBSU", "Bcond" , "CAXI" , "CLI" ,
|
||
|
"CMP" , "CMP" , "CMPF.S" , "CVT.SW", "CVT.WS" , "DIV" ,
|
||
|
"DIVF.S" , "DIVU" , "HALT" , "IN.B" , "IN.H" , "IN.W" ,
|
||
|
"JAL" , "JMP" , "JR" , "LD.B" , "LD.H" , "LD.W" ,
|
||
|
"LDSR" , "MOV" , "MOV" , "MOVBSU", "MOVEA" , "MOVHI" ,
|
||
|
"MPYHW" , "MUL" , "MULF.S" , "MULU" , "NOT" , "NOTBSU" ,
|
||
|
"OR" , "ORBSU" , "ORI" , "ORNBSU", "OUT.B" , "OUT.H" ,
|
||
|
"OUT.W" , "RETI" , "REV" , "SAR" , "SAR" , "SCH0BSD",
|
||
|
"SCH0BSU", "SCH1BSD", "SCH1BSU", "SEI" , "SETF" , "SHL" ,
|
||
|
"SHL" , "SHR" , "SHR" , "ST.B" , "ST.H" , "ST.W" ,
|
||
|
"STSR" , "SUB" , "SUBF.S" , "TRAP" , "TRNC.SW", "XB" ,
|
||
|
"XH" , "XOR" , "XORBSU" , "XORI" , "XORNBSU"
|
||
|
};
|
||
|
|
||
|
// Program register names
|
||
|
private static final String[] PROGRAMS = {
|
||
|
null, null, "hp", "sp", "gp", "tp", null, null,
|
||
|
null, null, null, null, null, null, null, null,
|
||
|
null, null, null, null, null, null, null, null,
|
||
|
null, null, null, null, null, null, null, "lp"
|
||
|
};
|
||
|
|
||
|
// System register names
|
||
|
private static final String[] SYSTEMS = {
|
||
|
"EIPC", "EIPSW", "FEPC", "FEPSW", "ECR", "PSW", "PIR", "TKCW",
|
||
|
null , null , null , null , null , null , null , null ,
|
||
|
null , null , null , null , null , null , null , null ,
|
||
|
"CHCW", "ADTRE", null , null , null , null , null , null
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
// Classes //
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// One row of output
|
||
|
static class Row {
|
||
|
String address; // Bus address
|
||
|
String bytes; // Encoded bytes in bus order
|
||
|
String mnemonic; // Instruction mnemonic
|
||
|
String operands; // Instruction operands, if any
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
// Static Methods //
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Reset all settings to their default values
|
||
|
static void setDefaults() {
|
||
|
bcondCombine = true;
|
||
|
bcondNames = true;
|
||
|
condCaps = true;
|
||
|
destLast = true;
|
||
|
dispDest = true;
|
||
|
dispInside = false;
|
||
|
hexCaps = true;
|
||
|
hexMode = ZEROX;
|
||
|
immNumber = false;
|
||
|
jmpBrackets = true;
|
||
|
jumpAddress = true;
|
||
|
lower = true;
|
||
|
mnemonicCaps = true;
|
||
|
programCaps = false;
|
||
|
programNames = true;
|
||
|
setfCombine = false;
|
||
|
setfNames = true;
|
||
|
systemCaps = true;
|
||
|
systemNames = true;
|
||
|
zero = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
// Package Methods //
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Represent an instruction as text fields
|
||
|
static void disassemble(int address, Instruction inst, Row row) {
|
||
|
|
||
|
// Address
|
||
|
String x = hexCaps ? "X" : "x";
|
||
|
row.address = String.format("%08" + x, address);
|
||
|
|
||
|
// Bytes
|
||
|
x = "%02" + x;
|
||
|
row.bytes = inst.size == 2 ?
|
||
|
String.format(x + " " + x,
|
||
|
inst.bits >> 16 & 0xFF,
|
||
|
inst.bits >> 24 & 0xFF
|
||
|
) : String.format(x + " " + x + " " + x + " " + x,
|
||
|
inst.bits >> 16 & 0xFF,
|
||
|
inst.bits >> 24 & 0xFF,
|
||
|
inst.bits & 0xFF,
|
||
|
inst.bits >> 8 & 0xFF
|
||
|
)
|
||
|
;
|
||
|
|
||
|
// Bcond mnemonic
|
||
|
if (inst.id == VUE.BCOND && bcondCombine) {
|
||
|
row.mnemonic = "B" + CONDITIONS[inst.cond];
|
||
|
if (lower && (inst.cond & 7) == 1)
|
||
|
row.mnemonic = inst.cond == 1 ? "BL" : "BNL";
|
||
|
if (zero && (inst.cond & 7) == 2)
|
||
|
row.mnemonic = inst.cond == 2 ? "BZ" : "BNZ";
|
||
|
if ( (inst.cond & 7) == 5)
|
||
|
row.mnemonic = inst.cond == 5 ? "BR" : "NOP";
|
||
|
}
|
||
|
|
||
|
// SETF mnemonic
|
||
|
else if (inst.id == VUE.SETF && setfCombine)
|
||
|
row.mnemonic = "SETF" + CONDITIONS[inst.imm];
|
||
|
|
||
|
// All other mnemonics
|
||
|
else row.mnemonic = MNEMONICS[inst.id];
|
||
|
|
||
|
// Adjust mnemonic case
|
||
|
if (!mnemonicCaps)
|
||
|
row.mnemonic = row.mnemonic.toLowerCase();
|
||
|
|
||
|
// Operands by format
|
||
|
row.operands = null;
|
||
|
if (inst.id != VUE.ILLEGAL) switch (inst.format) {
|
||
|
case 1: // Fallthrough
|
||
|
case 7: row.operands = formatI_VII(inst ); break;
|
||
|
case 2: row.operands = formatII (inst ); break;
|
||
|
case 3: row.operands = formatIII (inst, address); break;
|
||
|
case 4: row.operands = destination(inst, address); break;
|
||
|
case 5: row.operands = formatV (inst ); break;
|
||
|
case 6: row.operands = formatVI (inst ); break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
// Private Methods //
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Format the destination of a branch or jump
|
||
|
private static String destination(Instruction inst, int address) {
|
||
|
return !dispDest ? toHex(inst.disp, 1, immNumber) :
|
||
|
String.format("%08" + (hexCaps ? "X" : "x"), address + inst.disp);
|
||
|
}
|
||
|
|
||
|
// Operands for Format I and Format VII
|
||
|
private static String formatI_VII(Instruction inst) {
|
||
|
String reg1 = program(inst.reg1);
|
||
|
String reg2 = program(inst.reg2);
|
||
|
|
||
|
// One-operand instructions
|
||
|
switch (inst.id) {
|
||
|
case VUE.JMP:
|
||
|
return String.format(jmpBrackets ? "[%s]" : "%s", reg1);
|
||
|
case VUE.XB: // Fallthrough
|
||
|
case VUE.XH:
|
||
|
return reg2;
|
||
|
}
|
||
|
|
||
|
// Generic
|
||
|
return String.format("%s, %s",
|
||
|
destLast ? reg1 : reg2,
|
||
|
destLast ? reg2 : reg1
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Operands for Format II
|
||
|
private static String formatII(Instruction inst) {
|
||
|
|
||
|
// Bit string
|
||
|
if (inst.opcode == 0b011111)
|
||
|
return null;
|
||
|
|
||
|
// Zero- or one-operand instructions
|
||
|
switch (inst.id) {
|
||
|
|
||
|
// Zero-operand
|
||
|
case VUE.CLI : // Fallthrough
|
||
|
case VUE.HALT: // Fallthrough
|
||
|
case VUE.RETI: // Fallthrough
|
||
|
case VUE.SEI :
|
||
|
return null;
|
||
|
|
||
|
// One-operand
|
||
|
case VUE.TRAP:
|
||
|
return Integer.toString(inst.imm);
|
||
|
}
|
||
|
|
||
|
// Combined SETF
|
||
|
String reg2 = program(inst.reg2);
|
||
|
if (inst.id == VUE.SETF && setfCombine)
|
||
|
return reg2;
|
||
|
|
||
|
// Two-operand instructions
|
||
|
String imm = String.format("%s%d", immNumber ? "#" : "", inst.imm);
|
||
|
switch (inst.id) {
|
||
|
case VUE.LDSR: // Fallthrough
|
||
|
case VUE.STSR:
|
||
|
if (!systemNames || SYSTEMS[inst.imm] == null)
|
||
|
break;
|
||
|
imm = SYSTEMS[inst.imm];
|
||
|
if (!systemCaps)
|
||
|
imm = imm.toLowerCase();
|
||
|
break;
|
||
|
case VUE.SETF:
|
||
|
if (!setfNames)
|
||
|
break;
|
||
|
imm = CONDITIONS[inst.imm];
|
||
|
if (!condCaps)
|
||
|
imm = imm.toLowerCase();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Generic
|
||
|
return String.format("%s, %s",
|
||
|
destLast ? imm : reg2,
|
||
|
destLast ? reg2 : imm
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Operands for Format III
|
||
|
private static String formatIII(Instruction inst, int address) {
|
||
|
|
||
|
// NOP
|
||
|
if (bcondCombine && inst.cond == 13)
|
||
|
return null;
|
||
|
|
||
|
// Format destination
|
||
|
String dest = destination(inst, address);
|
||
|
|
||
|
// One-operand notation
|
||
|
if (bcondCombine)
|
||
|
return dest;
|
||
|
|
||
|
// Two-operand notation
|
||
|
String cond = bcondNames ?
|
||
|
CONDITIONS[inst.cond] : Integer.toString(inst.cond);
|
||
|
if (bcondNames && !condCaps)
|
||
|
cond = cond.toLowerCase();
|
||
|
return String.format("%s, %s", cond, dest);
|
||
|
}
|
||
|
|
||
|
// Operands for Format V
|
||
|
private static String formatV(Instruction inst) {
|
||
|
String reg1 = program(inst.reg1);
|
||
|
String reg2 = program(inst.reg2);
|
||
|
String imm = toHex(
|
||
|
inst.id == VUE.MOVEA ? inst.imm & 0xFFFF : inst.imm,
|
||
|
inst.id == VUE.ADDI ? 1 : 4,
|
||
|
immNumber
|
||
|
);
|
||
|
return String.format("%s, %s, %s",
|
||
|
destLast ? imm : reg2,
|
||
|
reg1,
|
||
|
destLast ? reg2 : imm
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Operands for Format VI
|
||
|
private static String formatVI(Instruction inst) {
|
||
|
String src = program(inst.reg1);
|
||
|
String dest = program(inst.reg2);
|
||
|
|
||
|
// Data operand
|
||
|
src = inst.disp == 0 ? String.format("[%s]", src) :
|
||
|
dispInside ? String.format(
|
||
|
"[%s %s %s]",
|
||
|
src,
|
||
|
inst.disp < 0 ? "-" : "+",
|
||
|
toHex(Math.abs(inst.disp), 1, immNumber)
|
||
|
) : String.format(
|
||
|
"%s[%s]",
|
||
|
toHex(inst.disp, 1, immNumber),
|
||
|
src
|
||
|
)
|
||
|
;
|
||
|
|
||
|
// Write instruction
|
||
|
switch (inst.id) {
|
||
|
case VUE.OUT_B: // Fallthrough
|
||
|
case VUE.OUT_H: // Fallthrough
|
||
|
case VUE.OUT_W: // Fallthrough
|
||
|
case VUE.ST_B : // Fallthrough
|
||
|
case VUE.ST_H : // Fallthrough
|
||
|
case VUE.ST_W :
|
||
|
String temp = src;
|
||
|
src = dest;
|
||
|
dest = temp;
|
||
|
}
|
||
|
|
||
|
// Format operands
|
||
|
return String.format("%s, %s",
|
||
|
destLast ? src : dest,
|
||
|
destLast ? dest : src
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Format a program register
|
||
|
private static String program(int index) {
|
||
|
String ret = programNames ? PROGRAMS[index] : null;
|
||
|
if (ret == null)
|
||
|
ret = "r" + index;
|
||
|
return programCaps ? ret.toUpperCase() : ret;
|
||
|
}
|
||
|
|
||
|
// Represent an immediate value as hexadecimal
|
||
|
private static String toHex(int value, int digits, boolean number) {
|
||
|
return String.format(
|
||
|
"%s%s%s%0" + digits + (hexCaps ? "X" : "x") + "%s",
|
||
|
number ? "#" : "",
|
||
|
value < 0 ? "-" : "",
|
||
|
hexMode == DOLLAR ? "$" : hexMode == ZEROX ? "0x" : "",
|
||
|
value < 0 ? -value : value,
|
||
|
hexMode == H ? "h" : ""
|
||
|
);
|
||
|
}
|
||
|
|
||
|
}
|