Files
shrooms-vb-core/util/disassembler.c
T

839 lines
29 KiB
C

/* This file is included into vbu.c and cannot be compiled on its own. */
#ifdef VBUAPI
/********************************* Constants *********************************/
/* Assembly text formats */
#define FMT_BITSTRING 0
#define FMT_BCOND 1
#define FMT_FLOATENDO 2
#define FMT_SETF 3
#define FMT_ILLEGAL 4
#define FMT_NORMAL 5
/* Operand types */
#define OP_COND 0
#define OP_DISP9 1
#define OP_DISP26 2
#define OP_IMM5S 3
#define OP_IMM5U 4
#define OP_IMM16S 5
#define OP_IMM16U 6
#define OP_JMP 7
#define OP_MEM 8
#define OP_REG1 9
#define OP_REG2 10
#define OP_SYS 11
/*********************************** Types ***********************************/
/* Disassembler output */
typedef struct {
char *buffer; /* Managed memory buffer */
size_t capacity; /* Size of allocated buffer */
size_t length; /* Bytes currently used in buffer */
} DasmOutput;
/* Opcode definition */
typedef struct {
char *mnemonic; /* Display text of instruction's mnemonic */
uint8_t codeLength; /* Number of bytes in instruction code */
uint8_t format; /* Display text format */
uint8_t operandsLength; /* Number of operands in display format */
uint8_t operands[3]; /* Types of display text operands */
} OpDef;
/******************************** Lookup Data ********************************/
/* Top-level opcodes */
static const OpDef OPDEFS[] = {
{ "MOV" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "ADD" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "SUB" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "CMP" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "SHL" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "SHR" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "JMP" , 2, FMT_NORMAL , 1, { OP_JMP , 0 , 0 } },
{ "SAR" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "MUL" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "DIV" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "MULU" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "DIVU" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "OR" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "AND" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "XOR" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "NOT" , 2, FMT_NORMAL , 2, { OP_REG1 , OP_REG2, 0 } },
{ "MOV" , 2, FMT_NORMAL , 2, { OP_IMM5S , OP_REG2, 0 } },
{ "ADD" , 2, FMT_NORMAL , 2, { OP_IMM5S , OP_REG2, 0 } },
{ "SETF" , 2, FMT_SETF , 0, { 0 , 0 , 0 } },
{ "CMP" , 2, FMT_NORMAL , 2, { OP_IMM5S , OP_REG2, 0 } },
{ "SHL" , 2, FMT_NORMAL , 2, { OP_IMM5U , OP_REG2, 0 } },
{ "SHR" , 2, FMT_NORMAL , 2, { OP_IMM5U , OP_REG2, 0 } },
{ "CLI" , 2, FMT_NORMAL , 0, { 0 , 0 , 0 } },
{ "SAR" , 2, FMT_NORMAL , 2, { OP_IMM5U , OP_REG2, 0 } },
{ "TRAP" , 2, FMT_NORMAL , 1, { OP_IMM5U , 0 , 0 } },
{ "RETI" , 2, FMT_NORMAL , 0, { 0 , 0 , 0 } },
{ "HALT" , 2, FMT_NORMAL , 0, { 0 , 0 , 0 } },
{ NULL , 2, FMT_ILLEGAL , 0, { 0 , 0 , 0 } },
{ "LDSR" , 2, FMT_NORMAL , 2, { OP_REG2 , OP_SYS , 0 } },
{ "STSR" , 2, FMT_NORMAL , 2, { OP_SYS , OP_REG2, 0 } },
{ "SEI" , 2, FMT_NORMAL , 0, { 0 , 0 , 0 } },
{ NULL , 2, FMT_BITSTRING, 0, { 0 , 0 , 0 } },
{ NULL , 2, FMT_BCOND , 0, { 0 , 0 , 0 } },
{ NULL , 2, FMT_BCOND , 0, { 0 , 0 , 0 } },
{ NULL , 2, FMT_BCOND , 0, { 0 , 0 , 0 } },
{ NULL , 2, FMT_BCOND , 0, { 0 , 0 , 0 } },
{ NULL , 2, FMT_BCOND , 0, { 0 , 0 , 0 } },
{ NULL , 2, FMT_BCOND , 0, { 0 , 0 , 0 } },
{ NULL , 2, FMT_BCOND , 0, { 0 , 0 , 0 } },
{ NULL , 2, FMT_BCOND , 0, { 0 , 0 , 0 } },
{ "MOVEA", 4, FMT_NORMAL , 3, { OP_IMM16U, OP_REG1, OP_REG2 } },
{ "ADDI" , 4, FMT_NORMAL , 3, { OP_IMM16S, OP_REG1, OP_REG2 } },
{ "JR" , 4, FMT_NORMAL , 1, { OP_DISP26, 0 , 0 } },
{ "JAL" , 4, FMT_NORMAL , 1, { OP_DISP26, 0 , 0 } },
{ "ORI" , 4, FMT_NORMAL , 3, { OP_IMM16U, OP_REG1, OP_REG2 } },
{ "AND" , 4, FMT_NORMAL , 3, { OP_IMM16U, OP_REG1, OP_REG2 } },
{ "XOR" , 4, FMT_NORMAL , 3, { OP_IMM16U, OP_REG1, OP_REG2 } },
{ "MOVHI", 4, FMT_NORMAL , 3, { OP_IMM16U, OP_REG1, OP_REG2 } },
{ "LD.B" , 4, FMT_NORMAL , 2, { OP_MEM , OP_REG2, 0 } },
{ "LD.H" , 4, FMT_NORMAL , 2, { OP_MEM , OP_REG2, 0 } },
{ NULL , 2, FMT_ILLEGAL , 0, { 0 , 0 , 0 } },
{ "LD.W" , 4, FMT_NORMAL , 2, { OP_MEM , OP_REG2, 0 } },
{ "ST.B" , 4, FMT_NORMAL , 2, { OP_REG2 , OP_MEM , 0 } },
{ "ST.H" , 4, FMT_NORMAL , 2, { OP_REG2 , OP_MEM , 0 } },
{ NULL , 2, FMT_ILLEGAL , 0, { 0 , 0 , 0 } },
{ "ST.W" , 4, FMT_NORMAL , 2, { OP_REG2 , OP_MEM , 0 } },
{ "IN.B" , 4, FMT_NORMAL , 2, { OP_MEM , OP_REG2, 0 } },
{ "IN.H" , 4, FMT_NORMAL , 2, { OP_MEM , OP_REG2, 0 } },
{ "CAXI" , 2, FMT_NORMAL , 2, { OP_MEM , OP_REG2, 0 } },
{ "IN.W" , 4, FMT_NORMAL , 2, { OP_MEM , OP_REG2, 0 } },
{ "OUT.B", 4, FMT_NORMAL , 2, { OP_REG2 , OP_MEM , 0 } },
{ "OUT.H", 4, FMT_NORMAL , 2, { OP_REG2 , OP_MEM , 0 } },
{ NULL , 4, FMT_FLOATENDO, 0, { 0 , 0 , 0 } },
{ "OUT.W", 4, FMT_NORMAL , 2, { OP_REG2 , OP_MEM , 0 } }
};
/* Format VII opcodes */
static const OpDef FLOATENDOS[] = {
{ "CMPF.S" , 4, FMT_NORMAL , 2, { OP_REG1, OP_REG2, 0 } },
{ NULL , 4, FMT_ILLEGAL, 0, { 0 , 0 , 0 } },
{ "CVT.WS" , 4, FMT_NORMAL , 2, { OP_REG1, OP_REG2, 0 } },
{ "CVT.SW" , 4, FMT_NORMAL , 2, { OP_REG1, OP_REG2, 0 } },
{ "ADDF.S" , 4, FMT_NORMAL , 2, { OP_REG1, OP_REG2, 0 } },
{ "SUBF.S" , 4, FMT_NORMAL , 2, { OP_REG1, OP_REG2, 0 } },
{ "MULF.S" , 4, FMT_NORMAL , 2, { OP_REG1, OP_REG2, 0 } },
{ "DIVF.S" , 4, FMT_NORMAL , 2, { OP_REG1, OP_REG2, 0 } },
{ "XB" , 4, FMT_NORMAL , 1, { OP_REG2, 0 , 0 } },
{ "XH" , 4, FMT_NORMAL , 1, { OP_REG2, 0 , 0 } },
{ "REV" , 4, FMT_NORMAL , 1, { OP_REG1, OP_REG2, 0 } },
{ "TRNC.SW", 4, FMT_NORMAL , 1, { OP_REG1, OP_REG2, 0 } },
{ "MPYHW" , 4, FMT_NORMAL , 1, { OP_REG1, OP_REG2, 0 } }
};
/* Bit string mnemonics */
static const OpDef BITSTRINGS[] = {
{ "SCH0BSU", 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ "SCH0BSD", 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ "SCH1BSU", 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ "SCH1BSD", 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ NULL , 2, FMT_ILLEGAL, 0, { 0, 0, 0 } },
{ NULL , 2, FMT_ILLEGAL, 0, { 0, 0, 0 } },
{ NULL , 2, FMT_ILLEGAL, 0, { 0, 0, 0 } },
{ NULL , 2, FMT_ILLEGAL, 0, { 0, 0, 0 } },
{ "ORBSU" , 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ "ANDBSU" , 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ "XORBSU" , 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ "MOVBSU" , 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ "ORNBSU" , 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ "ANDNBSU", 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ "XORNBSU", 2, FMT_NORMAL , 0, { 0, 0, 0 } },
{ "NOTBSU" , 2, FMT_NORMAL , 0, { 0, 0, 0 } }
};
/* Special instructions */
static const OpDef
BCOND_J = { NULL , 2, FMT_NORMAL, 1, { OP_DISP9, 0 , 0 } },
BCOND_S = { "Bcond", 2, FMT_NORMAL, 2, { OP_COND , OP_DISP9, 0 } },
ILLEGAL = { "---" , 2, FMT_NORMAL, 0, { 0 , 0 , 0 } },
SETF_J = { NULL , 2, FMT_NORMAL, 1, { OP_REG2 , 0 , 0 } },
SETF_S = { "SETF" , 2, FMT_NORMAL, 2, { OP_COND , OP_REG2 , 0 } }
;
/***************************** Module Functions ******************************/
/* Format a number as hexadecimal */
static int dasmDec(char *dest, int32_t value) {
int digits; /* Number of digits in value */
int32_t divisor; /* Current power of 10 */
int len = 0; /* Length of resulting string */
int32_t x; /* Scratch */
/* Value is negative */
if (value < 0) {
dest[len++] = '-';
value = -value;
}
/* Count how many digits are needed to represent the number */
digits = 1;
divisor = 1;
for (x = value / 10; x > 0; x /= 10) {
digits++;
divisor *= 10;
}
/* Output each digit from highest to lowest */
for (x = 0; x < digits; x++) {
int digit = value / divisor;
dest[len++] = '0' + digit;
value -= digit * divisor;
divisor /= 10;
}
/* Return length of string */
dest[len] = 0;
return len;
}
/* Retrieve a joined condition-based mnemonic */
static char* dasmGetCondition(
VBU_DasmConfig *config, int id, int bcond, int setf) {
/* Joined Bcond mnemonics */
static const char *BCONDS[] = {
"BV" , "BL" , "BZ" , "BNH", "BN", "BR" , "BLT", "BLE",
"BNV", "BNL", "BNZ", "BH" , "BP", "NOP", "BGE", "BGT",
NULL , "BC" , "BE" , NULL , NULL, NULL , NULL , NULL ,
NULL , "BNC", "BNE", NULL , NULL, NULL , NULL , NULL
};
/* Split condition operand mnemonics */
static const char *CONDITIONS[] = {
"V" , "L" , "Z" , "NH", "N" , "T" , "LT", "LE",
"NV", "NL", "NZ", "H" , "P" , "F" , "GE", "GT",
NULL, "C" , "E" , NULL, NULL, NULL, NULL, NULL,
NULL, "NC", "NE", NULL, NULL, NULL, NULL, NULL
};
/* Joined SETF mnemonics */
static const char *SETFS[] = {
"SETFV" , "SETFL" , "SETFZ" , "SETFNH",
"SETFN" , "SETFT" , "SETFLT", "SETFLE",
"SETFNV", "SETFNL", "SETFNZ", "SETFH" ,
"SETFP" , "SETFF" , "SETFGE", "SETFGT",
NULL , "SETFC" , "SETFE" , NULL ,
NULL , NULL , NULL , NULL ,
NULL , "SETFNC", "SETFNE", NULL ,
NULL , NULL , NULL , NULL
};
/* Alternate mnemonic for C/L and E/Z */
if (
(config->conditionCL == VBU_C && (id & 7) == 1) ||
(config->conditionEZ == VBU_E && (id & 7) == 2)
) id += 16;
/* Select the mnemonic from the appropriate list */
return (char *) (bcond ? BCONDS : setf ? SETFS : CONDITIONS)[id];
}
/* Format a number as hexadecimal */
static int dasmHex(char *dest,int format,int caps,int32_t value,int digits) {
int len = 0; /* Length of resulting string */
int x; /* Iterator */
/* Hex digits */
static const char DIGITS[] = "0123456789ABCDEF";
/* Prefix */
switch (format) {
case VBU_PREFIX_0X:
dest[len++] = '0';
dest[len++] = 'x';
break;
case VBU_PREFIX_DOLLAR:
dest[len++] = '$';
break;
case VBU_SUFFIX_H:
if ((value >> ((digits - 1) << 2) & 15) >= 10)
dest[len++] = '0';
break;
}
/* Value */
for (x = 0; x < digits; x++) {
int digit = value >> ((digits - 1 - x) << 2) & 15;
char c = DIGITS[digit];
if (caps == VBU_LOWER && digit >= 10)
c += 'a' - 'A';
dest[len++] = c;
}
/* Suffix */
if (format == VBU_SUFFIX_H)
dest[len++] = 'h';
/* Return length of string */
dest[len] = 0;
return len;
}
/* Format a branch operand */
static int dasmOpBranch(char *dest, VBU_DasmConfig *config,
uint16_t *code, int type, uint32_t address) {
int32_t disp; /* Displacement amount */
int len = 0; /* Length of resulting string */
/* Parse the displacement operand */
disp = type == OP_DISP9 ?
(int32_t) (int8_t ) (code[0] >> 1) << 1 | (code[0] & 1) :
(int32_t) (int16_t) (code[0] << 6) << 10 | code[1]
;
/* Absolute target address formatting */
if (config->branchNotation == VBU_ABSOLUTE)
return dasmHex(dest, -1, config->hexCase, address + disp, 8);
/* Negative displacement */
if (disp < 0) {
disp = -disp;
dest[len++] = '-';
}
/* Positive displacement */
else dest[len++] = '+';
/* Format the displacement as decimal */
return len + dasmDec(&dest[len], disp);
}
/* Format a condition operand */
static int dasmOpCondition(char *dest, VBU_DasmConfig *config, uint16_t *code){
char *src; /* Source mnemonic */
int x; /* Iterator */
/* Retrieve the standalone condition mnemonic */
src = dasmGetCondition(config,
code[0] >> ((code[0] >> 15 & 1) ? 9 : 0) & 15, 0, 0);
/* Copy the characters to the output */
for (x = 0; src[x]; x++) {
char c = src[x];
if (config->conditionCase == VBU_LOWER && c >= 'A' && c <= 'Z')
c += 'a' - 'A';
dest[x] = c;
}
/* Return length of string */
dest[x] = 0;
return x;
}
/* Format an immediate operand */
static int dasmOpImmediate(
char *dest, VBU_DasmConfig *config, uint16_t *code, int type) {
int len = 0; /* Length of resulting string */
int32_t value; /* Immediate value */
/* Prefix */
if (config->immediateNotation == VBU_PREFIX_NUMBER)
dest[len++] = '#';
/* Select format type */
switch (type) {
case OP_IMM5S:
value = code[0] & 31;
return len + dasmDec(&dest[len], value > 15 ? value - 32 : value);
case OP_IMM5U:
return len + dasmDec(&dest[len], code[0] & 31);
case OP_IMM16U:
return len + dasmHex(&dest[len],
config->hexNotation, config->hexCase, code[1], 4);
}
/* Parse the value */
value = (int16_t) code[1];
/* Magnitude is small: format as decimal */
if (value >= -256 && value <= 256)
return len + dasmDec(&dest[len], value);
/* Value is negative */
if (value < 0) {
dest[len++] = '-';
value = -value;
}
/* Magnitude is large: format as hexadecimal */
return len + dasmHex(&dest[len],
config->hexNotation, config->hexCase, value, 4);
}
/* Format a program register operand */
static int dasmOpProgram(
char *dest, VBU_DasmConfig *config, uint16_t *code, int type) {
int len = 0; /* Length of resulting string */
char *src; /* Program register mnemonic */
int x; /* Scratch */
/* Program registers by name */
static const char *NAMES[] = {
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"
};
/* Program registers by number */
static const char *NUMBERS[] = {
"R0" , "R1" , "R2" , "R3" , "R4" , "R5" , "R6" , "R7" ,
"R8" , "R9" , "R10", "R11", "R12", "R13", "R14", "R15",
"R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23",
"R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31"
};
/* Select the program register mnemonic */
x = code[0] >> (type == OP_REG1 ? 0 : 5) & 31;
src = (char *) NUMBERS[x];
if (config->programNotation == VBU_NAMES && NAMES[x] != NULL)
src = (char *) NAMES[x];
/* Format the operand characters */
for (x = 0; src[x]; x++) {
char c = src[x];
if (config->programCase == VBU_LOWER && c >= 'A' && c <= 'Z')
c += 'a' - 'A';
dest[len++] = c;
}
/* Return length of string */
dest[len] = 0;
return len;
}
/* Format a JMP operand */
static int dasmOpJMP(char *dest, VBU_DasmConfig *config, uint16_t *code) {
int len = 0; /* Length of resulting string */
/* Format the operand characters */
dest[len++] = '[';
len += dasmOpProgram(&dest[len], config, code, OP_REG1);
dest[len++] = ']';
/* Return length of string */
dest[len] = 0;
return len;
}
/* Format a memory operand */
static int dasmOpMemory(char *dest, VBU_DasmConfig *config, uint16_t *code) {
int32_t disp; /* Memory displacement */
int len = 0; /* Length of resulting string */
/* Parse operand */
disp = (int16_t) code[1];
/* No-displacement notation */
if (disp == 0) {
dest[len++] = '[';
len += dasmOpProgram(&dest[len], config, code, OP_REG1);
dest[len++] = ']';
}
/* Outside notation */
else if (config->memoryNotation == VBU_OUTSIDE) {
/* Magnitude is large: format as hexadecimal */
if (disp < -256 || disp > 256) {
/* Displacement is negative */
if (disp < 0) {
dest[len++] = '-';
disp = -disp;
}
/* Format displacement */
len += dasmHex(&dest[len], config->hexNotation,
config->hexCase, disp, 4);
}
/* Magnitude is small: format as decimal */
else len += dasmDec(&dest[len], disp);
/* Format operand characters */
dest[len++] = '[';
len += dasmOpProgram(&dest[len], config, code, OP_REG1);
dest[len++] = ']';
}
/* Inside notation */
else {
/* Format operand characters */
dest[len++] = '[';
len += dasmOpProgram(&dest[len], config, code, OP_REG1);
dest[len++] = ' ';
/* Displacement is negative */
if (disp < 0) {
dest[len++] = '-';
disp = -disp;
}
/* Displacement is positive */
else dest[len++] = '+';
/* Format operand characters */
dest[len++] = ' ';
/* Magnitude is large: format as hexadecimal */
if (disp < -256 || disp > 256) {
len += dasmHex(&dest[len], config->hexNotation,
config->hexCase, disp, 4);
}
/* Magnitude is small: format as decimal */
else len += dasmDec(&dest[len], disp);
/* Format operand characters */
dest[len++] = ']';
}
/* Return length of string */
dest[len] = 0;
return len;
}
/* Format a system register operand */
static int dasmOpSystem(char *dest, VBU_DasmConfig *config, uint16_t *code) {
int len = 0; /* Length of resulting string */
char *src; /* System register mnemonic */
int x; /* Scratch */
/* System registers by name */
static const char *NAMES[] = {
"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
};
/* System registers by number */
static const char *NUMBERS[] = {
"0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" ,
"8" , "9" , "10", "11", "12", "13", "14", "15",
"16", "17", "18", "19", "20", "21", "22", "23",
"24", "25", "26", "27", "28", "29", "30", "31"
};
/* Select the system register mnemonic */
x = code[0] & 31;
src = (char *) NUMBERS[x];
if (config->systemNotation == VBU_NAMES && NAMES[x] != NULL)
src = (char *) NAMES[x];
/* Format the operand characters */
for (x = 0; src[x]; x++) {
char c = src[x];
if (config->systemCase == VBU_LOWER && c >= 'A' && c <= 'Z')
c += 'a' - 'A';
dest[len++] = c;
}
/* Return length of string */
dest[len] = 0;
return len;
}
/* Process one line of disassembly */
static uint32_t dasmLine(DasmOutput *ret, unsigned index,
VB *sim, uint32_t address, int isPC, VBU_DasmConfig *config) {
uint16_t code[2]; /* Instruction bits */
OpDef *def; /* Opcode definition */
VBU_DasmLine *line; /* Current output line */
char *mnemonic; /* Mnemonic display text */
int mnemonicLen; /* Length of mnemonic text */
char operands[3][16]; /* Operand display text */
int opLens[3]; /* Operand string lengths */
size_t textLen; /* Number of text bytes needed */
int x; /* Scratch */
/* Select the current line in the buffer */
line = &((VBU_DasmLine *) ret->buffer)[index];
line->address = address;
line->isPC = isPC;
/* Fetch first code halfword */
code[0] = vbRead(sim, address, VB_U16);
line->code[0] = code[0];
line->code[1] = code[0] >> 8;
/* Select opcode definition */
def = (OpDef *) &OPDEFS[code[0] >> 10 & 63];
line->codeLength = def->codeLength;
mnemonic = def->mnemonic;
/* Fetch second code halfword if necessary */
if (line->codeLength == 4) {
code[1] = vbRead(sim, address + 2, VB_U16);
line->code[2] = code[1];
line->code[3] = code[1] >> 8;
} else {
code[1] = 0;
line->code[2] = 0;
line->code[3] = 0;
}
/* Two-step processing */
switch (def->format) {
/* Bcond */
case FMT_BCOND:
if (config->bcondNotation == VBU_SPLIT) {
def = (OpDef *) &BCOND_S;
mnemonic = def->mnemonic;
} else {
x = code[0] >> 9 & 15;
def = (OpDef *) &BCOND_J;
mnemonic = dasmGetCondition(config, x, 1, 0);
}
break;
/* Bit string instructions */
case FMT_BITSTRING:
x = code[0] & 31;
def = (OpDef *) &BITSTRINGS[x > 15 ? 4 : x];
mnemonic = def->mnemonic;
break;
/* Floating-point and Nintendo instructions */
case FMT_FLOATENDO:
x = code[1] >> 10 & 63;
def = (OpDef *) &FLOATENDOS[x > 12 ? 1 : x];
mnemonic = def->mnemonic;
break;
/* SETF */
case FMT_SETF:
if (config->setfNotation == VBU_SPLIT) {
def = (OpDef *) &SETF_S;
mnemonic = def->mnemonic;
} else {
x = code[0] & 15;
def = (OpDef *) &SETF_J;
mnemonic = dasmGetCondition(config, x, 0, 1);
}
break;
}
/* Single template for illegal opcodes */
if (def->format == FMT_ILLEGAL) {
def = (OpDef *) &ILLEGAL;
mnemonic = def->mnemonic;
}
/* Working variables */
line->text.operandsLength = def->operandsLength;
for (mnemonicLen = 0; mnemonic[mnemonicLen]; mnemonicLen++);
textLen = 9 /* Address */ + line->codeLength * 3 + mnemonicLen + 1;
/* Process all operands */
for (x = 0; x < def->operandsLength; x++) {
int len = 0; /* Length of operand string */
int type = def->operands[x];
char *op = operands[x];
/* Select operand type */
switch (def->operands[x]) {
case OP_COND:
len = dasmOpCondition(op, config, code);
break;
case OP_DISP9: /* Fallthrough */
case OP_DISP26:
len = dasmOpBranch (op, config, code, type, address);
break;
case OP_IMM5U: /* Fallthrough */
case OP_IMM5S: /* Fallthrough */
case OP_IMM16U: /* Fallthrough */
case OP_IMM16S:
len = dasmOpImmediate(op, config, code, type);
break;
case OP_JMP:
len = dasmOpJMP (op, config, code);
break;
case OP_MEM:
len = dasmOpMemory (op, config, code);
break;
case OP_REG1: /* Fallthrough */
case OP_REG2:
len = dasmOpProgram (op, config, code, type);
break;
case OP_SYS:
len = dasmOpSystem (op, config, code);
break;
}
/* Incorporate text length */
opLens[x] = len;
textLen += len + 1;
}
/* Output buffer needs to grow */
if (ret->length + textLen > ret->capacity) {
ret->capacity *= 2;
ret->buffer = VBU_MEMMGR(ret->buffer, ret->capacity);
}
/* Output address */
line->text.address = (char *) ret->length;
ret->length += dasmHex(&ret->buffer[ret->length], -1,
config->hexCase, line->address, 8);
ret->buffer[ret->length++] = 0;
/* Output code */
for (x = 0; x < line->codeLength; x++) {
line->text.code[x] = (char *) ret->length;
ret->length += dasmHex(&ret->buffer[ret->length], -1,
config->hexCase, line->code[x], 2);
ret->buffer[ret->length++] = 0;
}
for (; x < 4; x++)
line->text.code[x] = NULL;
/* Output mnemonic */
line->text.mnemonic = (char *) ret->length;
for (x = 0; x < mnemonicLen; x++) {
char c = mnemonic[x];
if (config->mnemonicCase == VBU_LOWER && c >= 'A' && c <= 'Z')
c += 'a' - 'A';
ret->buffer[ret->length++] = c;
}
ret->buffer[ret->length++] = 0;
/* Output operands */
for (x = 0; x < line->text.operandsLength; x++) {
char *op = operands[x];
int len = opLens[x];
int y;
line->text.operands[x] = (char *) ret->length;
for (y = 0; y < len; y++)
ret->buffer[ret->length++] = op[y];
ret->buffer[ret->length++] = 0;
}
for (; x < 3; x++)
line->text.operands[x] = NULL;
/* Adjust operand order */
if (config->operandOrder==VBU_DEST_FIRST && line->text.operandsLength>=2) {
int l = line->text.operandsLength - 1;
char *t = line->text.operands[0];
line->text.operands[0] = line->text.operands[l];
line->text.operands[l] = t;
}
return line->codeLength;
}
/***************************** Library Functions *****************************/
/* Disassemble instructions from a simulation */
static VBU_DasmLine* dasmDisassemble(VB *sim, uint32_t address,
VBU_DasmConfig *config, unsigned length, int line) {
uint32_t cur; /* Current disassembly address */
uint32_t pc; /* Current simulation program counter */
uint32_t rel; /* Line relative to start to the first output line */
DasmOutput ret; /* Output buffer state */
uint32_t start; /* At least 10 lines before the first output line */
unsigned x, y; /* Iterators */
/* Working variables */
pc = vbGetProgramCounter(sim);
start = (address - (line + 10) * 4) & 0xFFFFFFFE;
/* Locate the instruction containing the first line of output */
for (cur = start, rel = 0;; rel++) {
uint32_t size = vbuCodeSize(sim, cur);
/* Cannot skip over PC or target address */
if (cur != pc && pc - cur < size)
size = pc - cur;
if (cur != address && address - cur < size)
size = address - cur;
/* This instruction contains the target address */
if (address - cur < size)
break;
/* Advance to the next instruction */
cur += size;
}
/* Seek to the first line of output */
for (cur = start; rel > 0; rel--) {
uint32_t size = vbuCodeSize(sim, cur);
/* Cannot skip over PC or target address */
if (cur != pc && pc - cur < size)
size = pc - cur;
if (cur != address && address - cur < size)
size = address - cur;
/* Advance to the next instruction */
cur += size;
}
/* Process all lines */
ret.length = length * sizeof (VBU_DasmLine);
ret.capacity = ret.length * 2;
ret.buffer = VBU_MEMMGR(NULL, ret.capacity);
for (x = 0; x < length; x++) {
uint32_t size;
/* Disassemble the instruction */
size = dasmLine(&ret, x, sim, cur, cur == pc, config);
/* Cannot skip over PC or target address */
if (cur != pc && pc - cur < size)
size = pc - cur;
if (cur != address && address - cur < size)
size = address - cur;
/* Advance to the next instruction */
cur += size;
}
/* Finalize the output buffer */
if (ret.capacity > ret.length)
ret.buffer = VBU_MEMMGR(ret.buffer, ret.length);
for (x = 0; x < length; x++) {
VBU_DasmLine *line = &((VBU_DasmLine *) ret.buffer)[x];
/* Convert text offsets into proper pointers */
line->text.address = &ret.buffer[(size_t) line->text.address ];
line->text.mnemonic = &ret.buffer[(size_t) line->text.mnemonic];
for (y = 0; y < line->codeLength; y++)
line->text.code[y] = &ret.buffer[(size_t) line->text.code[y]];
for (y = 0; y < line->text.operandsLength; y++) {
line->text.operands[y] =
&ret.buffer[(size_t) line->text.operands[y]];
}
}
return (VBU_DasmLine *) ret.buffer;
}
#endif /* VBUAPI */