pvbemu/src/desktop/app/Disassembler.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" : ""
);
}
}