From f5a84fd4d0feca5a4dcceda1d1c64e280e072437 Mon Sep 17 00:00:00 2001 From: Guy Perfect Date: Wed, 30 Oct 2024 09:48:56 -0500 Subject: [PATCH] Implement disassembler --- makefile | 10 +- util/disassembler.c | 691 ++++++++++++++++++++++++++++++++++++++++++++ util/vbu.c | 65 +++++ util/vbu.h | 97 +++++++ 4 files changed, 861 insertions(+), 2 deletions(-) create mode 100644 util/disassembler.c create mode 100644 util/vbu.c create mode 100644 util/vbu.h diff --git a/makefile b/makefile index e9b61d1..d7f86ba 100644 --- a/makefile +++ b/makefile @@ -29,7 +29,7 @@ core: -D VB_DIRECT_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \ -D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \ -D VB_DIRECT_WRITE=testWrite -D VB_DIRECT_SAMPLES=testSamples \ - -D VB_DIRECT_READ=testRead + -D VB_DIRECT_READ=testRead -D VB_DIRECT_LINK=testLink # Clang generic @emcc core/vb.c -I core -c -o /dev/null -O3 \ -Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing @@ -40,7 +40,13 @@ core: -D VB_DIRECT_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \ -D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \ -D VB_DIRECT_WRITE=testWrite -D VB_DIRECT_SAMPLES=testSamples \ - -D VB_DIRECT_READ=testRead + -D VB_DIRECT_READ=testRead -D VB_DIRECT_LINK=testLink + +.PHONY: util +util: +# GCC generic + @gcc util/vbu.c -I core -I util -c -o /dev/null -O3 \ + -Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing .PHONY: wasm wasm: diff --git a/util/disassembler.c b/util/disassembler.c new file mode 100644 index 0000000..ce0c60f --- /dev/null +++ b/util/disassembler.c @@ -0,0 +1,691 @@ +/* This file is included into vb.c and cannot be compiled on its own. */ +#ifdef VBUAPI + + + +/********************************* Constants *********************************/ + +/* Operand types */ +#define DASM_BCOND 0 +#define DASM_DISP9 1 +#define DASM_DISP26 2 +#define DASM_IMM5S 3 +#define DASM_IMM5U 4 +#define DASM_IMM16S 5 +#define DASM_IMM16U 6 +#define DASM_JMP 7 +#define DASM_MEMORY 8 +#define DASM_REG1 9 +#define DASM_REG2 10 +#define DASM_SETF 11 +#define DASM_SYSTEM 12 + + + +/******************************** Lookup Data ********************************/ + +/* Instruction descriptor */ +typedef struct { + char mnemonic[8]; /* Mnemonic, uppercase */ + uint8_t operandsLength; /* Number of operands */ + uint8_t operands[3]; /* Operand types */ +} OpDef; + +/* Instruction descriptors */ +static const OpDef OPDEFS[] = { + { "MOV" , 2, { DASM_REG1, DASM_REG2 } }, /* 000000 */ + { "ADD" , 2, { DASM_REG1, DASM_REG2 } }, + { "SUB" , 2, { DASM_REG1, DASM_REG2 } }, + { "CMP" , 2, { DASM_REG1, DASM_REG2 } }, + { "SHL" , 2, { DASM_REG1, DASM_REG2 } }, + { "SHR" , 2, { DASM_REG1, DASM_REG2 } }, + { "JMP" , 1, { DASM_JMP } }, + { "SAR" , 2, { DASM_REG1, DASM_REG2 } }, + { "MUL" , 2, { DASM_REG1, DASM_REG2 } }, + { "DIV" , 2, { DASM_REG1, DASM_REG2 } }, + { "MULU" , 2, { DASM_REG1, DASM_REG2 } }, + { "DIVU" , 2, { DASM_REG1, DASM_REG2 } }, + { "OR" , 2, { DASM_REG1, DASM_REG2 } }, + { "AND" , 2, { DASM_REG1, DASM_REG2 } }, + { "XOR" , 2, { DASM_REG1, DASM_REG2 } }, + { "NOT" , 1, { DASM_REG1 } }, + + { "MOV" , 2, { DASM_IMM5S, DASM_REG2 } }, /* 010000 */ + { "ADD" , 2, { DASM_IMM5S, DASM_REG2 } }, + { "" , 0, { 0 } }, /* SETF */ + { "CMP" , 2, { DASM_IMM5S, DASM_REG2 } }, + { "SHL" , 2, { DASM_IMM5U, DASM_REG2 } }, + { "SHR" , 2, { DASM_IMM5U, DASM_REG2 } }, + { "CLI" , 0, { 0 } }, + { "SAR" , 2, { DASM_IMM5U, DASM_REG2 } }, + { "TRAP" , 1, { DASM_IMM5U } }, + { "RETI" , 0, { 0 } }, + { "HALT" , 0, { 0 } }, + { "---" , 0, { 0 } }, + { "LDSR" , 2, { DASM_REG2, DASM_SYSTEM } }, + { "STSR" , 2, { DASM_SYSTEM, DASM_REG2 } }, + { "SEI" , 0, { 0 } }, + { "" , 0, { 0 } }, /* Bit string */ + + { "" , 0, { 0 } }, /* BCOND */ /* 100000 */ + { "" , 0, { 0 } }, /* BCOND */ + { "" , 0, { 0 } }, /* BCOND */ + { "" , 0, { 0 } }, /* BCOND */ + { "" , 0, { 0 } }, /* BCOND */ + { "" , 0, { 0 } }, /* BCOND */ + { "" , 0, { 0 } }, /* BCOND */ + { "" , 0, { 0 } }, /* BCOND */ + { "MOVEA", 3, { DASM_IMM16U, DASM_REG1, DASM_REG2 } }, + { "ADDI" , 3, { DASM_IMM16S, DASM_REG1, DASM_REG2 } }, + { "JR" , 1, { DASM_DISP26 } }, + { "JAL" , 1, { DASM_DISP26 } }, + { "ORI" , 3, { DASM_IMM16U, DASM_REG1, DASM_REG2 } }, + { "ANDI" , 3, { DASM_IMM16U, DASM_REG1, DASM_REG2 } }, + { "XORI" , 3, { DASM_IMM16U, DASM_REG1, DASM_REG2 } }, + { "MOVHI", 3, { DASM_IMM16U, DASM_REG1, DASM_REG2 } }, + + { "LD.B" , 2, { DASM_MEMORY, DASM_REG2 } }, /* 110000 */ + { "LD.H" , 2, { DASM_MEMORY, DASM_REG2 } }, + { "---" , 0, { 0 } }, + { "LD.W" , 2, { DASM_MEMORY, DASM_REG2 } }, + { "ST.B" , 2, { DASM_REG2, DASM_MEMORY } }, + { "ST.H" , 2, { DASM_REG2, DASM_MEMORY } }, + { "---" , 0, { 0 } }, + { "ST.W" , 2, { DASM_REG2, DASM_MEMORY } }, + { "IN.B" , 2, { DASM_MEMORY, DASM_REG2 } }, + { "IN.H" , 2, { DASM_MEMORY, DASM_REG2 } }, + { "CAXI" , 0, { DASM_MEMORY, DASM_REG2 } }, + { "IN.W" , 2, { DASM_MEMORY, DASM_REG2 } }, + { "OUT.B", 2, { DASM_REG2, DASM_MEMORY } }, + { "OUT.H", 2, { DASM_REG2, DASM_MEMORY } }, + { "" , 0, { 0 } }, /* Floating-point/Nintendo */ + { "OUT.W", 2, { DASM_REG2, DASM_MEMORY } } +}; + +/* Bit string instruction descriptors */ +static const OpDef OPDEFS_BITSTRING[] = { + { "SCH0BSU", 0, { 0 } }, /* 00000 */ + { "SCH0BSD", 0, { 0 } }, + { "SCH1BSU", 0, { 0 } }, + { "SCH1BSD", 0, { 0 } }, + { "---" , 0, { 0 } }, + { "---" , 0, { 0 } }, + { "---" , 0, { 0 } }, + { "---" , 0, { 0 } }, + { "ORBSU" , 0, { 0 } }, + { "ANDBSU" , 0, { 0 } }, + { "XORBSU" , 0, { 0 } }, + { "MOVBSU" , 0, { 0 } }, + { "ORNBSU" , 0, { 0 } }, + { "ANDNBSU", 0, { 0 } }, + { "XORNBSU", 0, { 0 } }, + { "NOTBSU" , 0, { 0 } } + /* +16 invalid opcodes */ +}; + +/* Floating-point/Nintendo instruction descriptors */ +static const OpDef OPDEFS_FLOATENDO[] = { + { "CMPF.S" , 2, { DASM_REG1, DASM_REG2 } }, + { "---" , 0, { 0 } }, + { "CVT.WS" , 2, { DASM_REG1, DASM_REG2 } }, + { "CVT.SW" , 2, { DASM_REG1, DASM_REG2 } }, + { "ADDF.S" , 2, { DASM_REG1, DASM_REG2 } }, + { "SUBF.S" , 2, { DASM_REG1, DASM_REG2 } }, + { "MULF.S" , 2, { DASM_REG1, DASM_REG2 } }, + { "DIVF.S" , 2, { DASM_REG1, DASM_REG2 } }, + { "XB" , 1, { DASM_REG2 } }, + { "XH" , 1, { DASM_REG2 } }, + { "REV" , 2, { DASM_REG1, DASM_REG2 } }, + { "TRNC.SW", 2, { DASM_REG1, DASM_REG2 } }, + { "MPYHW" , 2, { DASM_REG1, DASM_REG2 } }, + /* +51 invalid opcodes */ +}; + +/* Invalid instruction descriptors */ +static const OpDef OPDEF_ILLEGAL = { "---", 0, { 0 } }; + + + +/***************************** Module Functions ******************************/ + +/* Select the display text for a condition */ +static char* dasmCondition(VBU_DasmConfig *config, int cond) { + switch (cond) { + case 0: return "V" ; + case 1: return config->conditionCL == VBU_C ? "C" : "L"; + case 2: return config->conditionEZ == VBU_E ? "E" : "Z"; + case 3: return "NH"; + case 4: return "N" ; + case 5: return "T" ; + case 6: return "LT"; + case 7: return "LE"; + case 8: return "NV"; + case 9: return config->conditionCL == VBU_C ? "NC" : "NL"; + case 10: return config->conditionEZ == VBU_E ? "NE" : "NZ"; + case 11: return "H" ; + case 12: return "P" ; + case 13: return "F" ; + case 14: return "GE"; + } + return "GT"; +} + +/* Format a number as hexadecimal */ +static void dasmToHex(char *dest, VBU_DasmConfig *config, + int minDigits, int32_t value) { + char format[9]; + char *prefix = ""; + char *suffix = ""; + switch (config->hexNotation) { + case VBU_0X : prefix = "0x"; break; + case VBU_DOLLAR: prefix = "$" ; break; + case VBU_H : suffix = "h" ; break; + } + sprintf(format, "%%s%%0%d%s%%s", minDigits, + config->hexCase == VBU_LOWER ? "x" : "X"); + sprintf(dest, format, prefix, value, suffix); + if (config->hexNotation != VBU_H || *dest <= '9') + return; + *dest++ = '0'; + sprintf(dest, format, prefix, value, suffix); +} + +/* Convert a string to lowercase */ +static void dasmToLower(char *str) { + for (; *str; str++) { + if (*str >= 'A' && *str <= 'Z') + *str += 'a' - 'A'; + } +} + +/* Format a program register */ +static void dasmProgram(char *dest, VBU_DasmConfig *config, int id) { + char *text = NULL; + if (config->programNotation != VBU_NUMBERS) { + switch (id) { + case 2: text = "HP"; break; + case 3: text = "SP"; break; + case 4: text = "GP"; break; + case 5: text = "TP"; break; + case 31: text = "LP"; break; + } + } + if (text == NULL) + sprintf(dest, "R%d", id); + else strcpy(dest, text); + if (config->programCase == VBU_LOWER) + dasmToLower(dest); +} + + + +/**************************** Operand Formatters *****************************/ + +/* Format the condition operand of a split BCOND instruction */ +static void dasmOpBCOND(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + int cond = line->code[1] >> 2 & 15; + if (config->conditionNotation == VBU_NUMBERS) + sprintf(dest, "%d", cond); + else { + strcpy(dest, dasmCondition(config, cond)); + if (config->conditionCase == VBU_LOWER) + dasmToLower(dest); + } +} + +/* Format a 9-bit displacement operand */ +static void dasmOpDisp9(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + int32_t disp = SignExtend((int32_t) line->code[1] << 8 | line->code[0], 9); + sprintf(dest, config->hexCase == VBU_LOWER ? + "%08x" : "%08X", line->address + disp); +} + +/* Format a 26-bit displacement operand */ +static void dasmOpDisp26(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + int32_t disp = SignExtend( + (int32_t) line->code[1] << 24 | + (int32_t) line->code[0] << 16 | + (int32_t) line->code[3] << 8 | + line->code[2] + , 26); + sprintf(dest, config->hexCase == VBU_LOWER ? + "%08x" : "%08X", line->address + disp); +} + +/* Format a 5-bit sign-extended immediate operand */ +static void dasmOpImm5S(char *dest, VBU_DasmLine *line) { + sprintf(dest, "%d", SignExtend(line->code[0], 5)); +} + +/* Format a 5-bit zero-filled immediate operand */ +static void dasmOpImm5U(char *dest, VBU_DasmLine *line) { + sprintf(dest, "%d", line->code[0] & 31); +} + +/* Format a 16-bit sign-extended immediate operand */ +static void dasmOpImm16S(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + int32_t imm = (int16_t) ((int16_t) line->code[3] << 8 | line->code[2]); + if (imm >= -256 && imm <= 256) { + sprintf(dest, "%d", imm); + return; + } + if (imm < 0) { + *dest++ = '-'; + imm = -imm; + } + dasmToHex(dest, config, 0, imm); +} + +/* Format a 16-bit zero-filled immediate operand */ +static void dasmOpImm16U(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + uint16_t imm = (uint16_t) line->code[3] << 8 | line->code[2]; + dasmToHex(dest, config, 4, imm); +} + +/* Format the operand of a JMP instruction */ +static void dasmOpJMP(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + *dest++ = '['; + dasmProgram(dest, config, line->code[0] & 31); + strcat(dest, "]"); +} + +/* Format a memory operand */ +static void dasmOpMemory(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + int reg1 = line->code[0] & 31; + int32_t disp = (int16_t) ((int16_t) line->code[3] << 8 | line->code[2]); + + /* Displacement of zero */ + if (disp == 0) { + *dest++ = '['; + dasmProgram(dest, config, reg1); + strcat(dest, "]"); + return; + } + + /* Inside */ + if (config->memoryNotation == VBU_INSIDE) { + *dest++ = '['; + dasmProgram(dest, config, reg1); + dest += strlen(dest); + if (disp < 0) { + strcpy(dest, " - "); + disp = -disp; + } else strcpy(dest, " + "); + dest += 3; + if (disp <= 256) + sprintf(dest, "%d", disp); + else dasmToHex(dest, config, 0, disp); + strcat(dest, "]"); + } + + /* Outside */ + else { + if (disp < 0) { + *dest++ = '-'; + disp = -disp; + } + if (disp <= 256) + sprintf(dest, "%d", disp); + else dasmToHex(dest, config, 0, disp); + dest += strlen(dest); + *dest++ = '['; + dasmProgram(dest, config, reg1); + strcat(dest, "]"); + } + +} + +/* Format a source register operand */ +static void dasmOpReg1(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + dasmProgram(dest, config, line->code[0] & 31); +} + +/* Format a destination register operand */ +static void dasmOpReg2(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + dasmProgram(dest, config, + ((uint16_t) line->code[1] << 8 | line->code[0]) >> 5 & 31); +} + +/* Format the condition operand of a split SETF instruction */ +static void dasmOpSETF(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + int cond = line->code[0] & 15; + if (config->conditionNotation == VBU_NUMBERS) + sprintf(dest, "%d", cond); + else { + strcpy(dest, dasmCondition(config, cond)); + if (config->conditionCase == VBU_LOWER) + dasmToLower(dest); + } +} + +/* Format a system register operand */ +static void dasmOpSystem(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { + int id = line->code[0] & 31; + char *text = NULL; + if (config->systemNotation != VBU_NUMBERS) { + switch (id) { + case 0: text = "EIPC" ; break; + case 1: text = "EIPSW"; break; + case 2: text = "FEPC" ; break; + case 3: text = "FEPSW"; break; + case 4: text = "ECR" ; break; + case 5: text = "PSW" ; break; + case 6: text = "PIR" ; break; + case 7: text = "TKCW" ; break; + case 24: text = "CHCW" ; break; + case 25: text = "ADTRE"; break; + } + } + if (text == NULL) { + sprintf(dest, "%d", id); + return; + } + strcpy(dest, text); + if (config->programCase == VBU_LOWER) + dasmToLower(dest); +} + + + +/***************************** Module Functions ******************************/ + +/* Prepare an OpDef for a BCOND instruction */ +static OpDef* dasmBCOND(VBU_DasmConfig *config, + VBU_DasmLine *line, OpDef *opdef) { + int cond; /* Condition code */ + char *text; /* Mnemonic text */ + + /* Split notation */ + if (config->bcondNotation == VBU_SPLIT) { + strcpy(opdef->mnemonic, "BCOND"); + opdef->operandsLength = 2; + opdef->operands[0] = DASM_BCOND; + opdef->operands[1] = DASM_DISP9; + return opdef; + } + + /* Joined notation */ + cond = line->code[1] >> 1 & 15; + switch (cond) { + case 0: text = "BV" ; break; + case 1: text = config->conditionCL == VBU_C ? "BC" : "BL"; break; + case 2: text = config->conditionEZ == VBU_E ? "BE" : "BZ"; break; + case 3: text = "BNH"; break; + case 4: text = "BN" ; break; + case 5: text = "BR" ; break; + case 6: text = "BLT"; break; + case 7: text = "BLE"; break; + case 8: text = "BNV"; break; + case 9: text = config->conditionCL == VBU_C ? "BNC" : "BNL"; break; + case 10: text = config->conditionEZ == VBU_E ? "BNE" : "BNZ"; break; + case 11: text = "BH" ; break; + case 12: text = "BP" ; break; + case 13: text = "NOP"; break; + case 14: text = "BGE"; break; + default: text = "BGT"; + } + strcpy(opdef->mnemonic, text); + opdef->operandsLength = cond != 13; + opdef->operands[0] = DASM_DISP9; + return opdef; +} + +/* Ensure an output buffer matches the required size */ +static VBU_DasmLine* dasmGrow( + VBU_DasmLine **lines, size_t *size, size_t target, unsigned index) { + if (*size < target) { + while (*size < target) + *size *= 2; + *lines = VBU_REALLOC(*lines, *size); + } + return *lines == NULL ? NULL : &(*lines)[index]; +} + +/* Determine the size of an instruction */ +static uint32_t dasmInstSize(VB *sim, uint32_t address) { + unsigned opcode = vbRead(sim, address, VB_U16) >> 10 & 63; + return opcode < 0x20 || opcode == 0x32 || opcode == 0x36 ? 2 : 4; +} + +/* Format an operand */ +static void dasmOperand(char *dest, VBU_DasmConfig *config, + VBU_DasmLine *line, uint8_t type) { + switch (type) { + case DASM_BCOND : dasmOpBCOND (dest, config, line); break; + case DASM_DISP9 : dasmOpDisp9 (dest, config, line); break; + case DASM_DISP26: dasmOpDisp26(dest, config, line); break; + case DASM_IMM5S : dasmOpImm5S (dest, line); break; + case DASM_IMM5U : dasmOpImm5U (dest, line); break; + case DASM_IMM16S: dasmOpImm16S(dest, config, line); break; + case DASM_IMM16U: dasmOpImm16U(dest, config, line); break; + case DASM_JMP : dasmOpJMP (dest, config, line); break; + case DASM_MEMORY: dasmOpMemory(dest, config, line); break; + case DASM_REG1 : dasmOpReg1 (dest, config, line); break; + case DASM_REG2 : dasmOpReg2 (dest, config, line); break; + case DASM_SETF : dasmOpSETF (dest, config, line); break; + case DASM_SYSTEM: dasmOpSystem(dest, config, line); break; + } +} + +/* Prepare an OpDef for a SETF instruction */ +static OpDef* dasmSETF(VBU_DasmConfig *config, + VBU_DasmLine *line, OpDef *opdef) { + + /* Split notation */ + if (config->bcondNotation == VBU_SPLIT) { + strcpy(opdef->mnemonic, "SETF"); + opdef->operandsLength = 2; + opdef->operands[0] = DASM_SETF; + opdef->operands[1] = DASM_REG2; + return opdef; + } + + /* Joined notation */ + sprintf(opdef->mnemonic, "SETF%s", + dasmCondition(config, line->code[1] >> 1 & 15)); + opdef->operandsLength = 1; + opdef->operands[0] = DASM_REG2; + return opdef; +} + +/* Disassemble one instruction */ +static int dasmLine(VB *sim, uint32_t *address, VBU_DasmConfig *config, + VBU_DasmLine **lines, size_t *size, size_t *offset, unsigned index) { + char *dest; /* Text output pointer */ + char *format; /* Hexadecimal format string */ + VBU_DasmLine *line; /* Output line */ + size_t more; /* Number of bytes to output */ + OpDef *opdef; /* Instruction descriptor */ + OpDef special; /* Special-case descriptor */ + char tAddress[9]; /* Address display text */ + char tCode[4][3]; /* Code display text */ + char tMnemonic[8]; /* Mnemonic display text */ + char tOperands[3][15]; /* Operands display text */ + unsigned x; /* Scratch and iterator */ + + /* Process non-text members */ + line = &(*lines)[index]; + line->address = *address; + line->codeLength = dasmInstSize(sim, *address); + line->isPC = vbGetProgramCounter(sim) - *address < line->codeLength; + for (x = 0; x < line->codeLength; x++, (*address)++) + line->code[x] = vbRead(sim, *address, VB_U8); + + /* Do not process text members */ + if (config == NULL) { + line->text.address = NULL; + line->text.mnemonic = NULL; + line->text.operandsLength = 0; + for (x = 0; x < line->codeLength; x++) + line->text.code[x] = NULL; + return 0; + } + + /* Select instruction descriptor */ + x = line->code[1] >> 2 & 63; + switch (x) { + case 0x12: /* SETF */ + opdef = dasmSETF(config, line, &special); + break; + case 0x1F: /* Bit string */ + x = line->code[0] & 31; + opdef = (OpDef *) (x < 16 ? &OPDEFS_BITSTRING[x] : &OPDEF_ILLEGAL); + break; + case 0x20: case 0x21: case 0x22: case 0x23: /* BCOND */ + case 0x24: case 0x25: case 0x26: case 0x27: + opdef = dasmBCOND(config, line, &special); + break; + case 0x3E: /* Floating-point/Nintendo */ + x = line->code[3] >> 2 & 63; + opdef = (OpDef *) (x < 13 ? &OPDEFS_FLOATENDO[x] : &OPDEF_ILLEGAL); + break; + default: opdef = (OpDef *) &OPDEFS[x]; /* Other */ + } + + /* Address */ + format = config->hexCase == VBU_LOWER ? "%08x" : "%08X"; + sprintf(tAddress, format, line->address); + more = 9; + + /* Code */ + format = config->hexCase == VBU_LOWER ? "%02x" : "%02X"; + for (x = 0; x < line->codeLength; x++) { + sprintf(tCode[x], format, (int) line->code[x]); + more += 3; + } + + /* Mnemonic */ + strcpy(tMnemonic, opdef->mnemonic); + if (config->mnemonicCase == VBU_LOWER) + dasmToLower(tMnemonic); + more += strlen(tMnemonic) + 1; + + /* Operands */ + line->text.operandsLength = opdef->operandsLength; + for (x = 0; x < opdef->operandsLength; x++) { + dasmOperand(tOperands[x], config, line, opdef->operands[x]); + more += strlen(tOperands[x]) + 1; + } + + /* Grow the text buffer */ + line = dasmGrow(lines, size, *offset + more, index); + if (line == NULL) + return 1; + + /* Working variables */ + dest = &((char *) *lines)[*offset]; + *offset += more; + + /* Address */ + strcpy(dest, tAddress); + line->text.address = dest; + dest += 9; + + /* Code */ + for (x = 0; x < line->codeLength; x++) { + strcpy(dest, tCode[x]); + line->text.code[x] = dest; + dest += 3; + } + + /* Mnemonic */ + strcpy(dest, tMnemonic); + line->text.mnemonic = dest; + dest += strlen(dest) + 1; + + /* Operands */ + for (x = 0; x < opdef->operandsLength; x++) { + strcpy(dest, tOperands[ + config->operandOrder == VBU_DEST_FIRST ? + opdef->operandsLength - 1 - x : + x + ]); + line->text.operands[x] = dest; + dest += strlen(dest) + 1; + } + + return 0; +} + + + +/***************************** Library Functions *****************************/ + +/* Disassemble from a simulation */ +static VBU_DasmLine* dasmDisassemble(VB *sim, uint32_t address, + VBU_DasmConfig *config, unsigned length, int line) { + uint32_t addr; /* Working address */ + uint32_t *circle = NULL; /* Circular address buffer */ + VBU_DasmLine *lines = NULL; /* Output line buffer */ + size_t offset; /* Offset into lines buffer */ + uint32_t pc; /* Program counter */ + size_t size; /* Number of bytes in lines */ + unsigned x; /* Iterator */ + + /* Nothing to disassemble */ + if (length == 0) + return NULL; + + /* Establish a circular buffer */ + if (line > 0) { + circle = VBU_REALLOC(NULL, (size_t) (line + 1) * sizeof (uint32_t)); + if (circle == NULL) + goto catch; + } + + /* Begin decoding from at least 10 lines before first/reference */ + addr = (address & 0xFFFFFFFE) + + (int32_t) ((line < 0 ? -line : 0) - 10) * 4; + + /* Locate the address of the line containing the reference address */ + pc = vbGetProgramCounter(sim); + for (x = 0;;) { + + /* Track the address in the circular buffer */ + if (line > 0) { + circle[x] = addr; + if (++x > (size_t) line) + x = 0; + } + + /* Check if the instruction contains the reference address */ + size = dasmInstSize(sim, addr); + if (address - addr < size) + break; + addr += pc - addr < size ? pc - addr : size; + } + + /* Address of first line is in the circular buffer */ + if (line > 0) { + addr = circle[x]; + circle = VBU_REALLOC(circle, 0); + } + + /* Keep decoding until the first line of output */ + else for (; line < 0; line++) + addr += dasmInstSize(sim, addr); + + /* Working variables */ + size = length * sizeof (VBU_DasmLine); + lines = VBU_REALLOC(NULL, size); + offset = size; + if (lines == NULL) + goto catch; + + /* Process output lines */ + for (x = 0; x < length; x++) { + if (dasmLine(sim, &addr, config, &lines, &size, &offset, x)) + goto catch; + } + return lines; + + /* Exception handler */ + catch: + circle = VBU_REALLOC(circle, 0); + lines = VBU_REALLOC(lines , 0); + return NULL; +} + + + +#endif /* VBUAPI */ diff --git a/util/vbu.c b/util/vbu.c new file mode 100644 index 0000000..8abbd1d --- /dev/null +++ b/util/vbu.c @@ -0,0 +1,65 @@ +#ifndef VBUAPI +#define VBUAPI +#endif +#include +#include +#include + +/* Memory management */ +#ifndef VBU_REALLOC + #include + #define VBU_REALLOC realloc +#else + extern void* VBU_REALLOC(void *, size_t); +#endif + + + +/***************************** Library Functions *****************************/ + +/* Sign-extend an integer of variable width */ +static int32_t SignExtend(int32_t value, int32_t bits) { + #ifndef VB_SIGNED_PROPAGATE + value &= ~((uint32_t) 0xFFFFFFFF << bits); + bits = (int32_t) 1 << (bits - (int32_t) 1); + return (value ^ bits) - bits; + #else + return value << (32 - bits) >> (32 - bits); + #endif +} + + + +/******************************** Sub-Modules ********************************/ + +#include "disassembler.c" + + + +/******************************* API Commands ********************************/ + +/* Initialize disassembler options with default settings */ +VBUAPI VBU_DasmConfig* vbuDasmInit(VBU_DasmConfig *config) { + config->bcondNotation = VBU_JOINED; + config->conditionCase = VBU_LOWER; + config->conditionCL = VBU_L; + config->conditionEZ = VBU_Z; + config->conditionNotation = VBU_NAMES; + config->hexCase = VBU_UPPER; + config->hexNotation = VBU_0X; + config->memoryNotation = VBU_OUTSIDE; + config->mnemonicCase = VBU_UPPER; + config->operandOrder = VBU_DEST_LAST; + config->programCase = VBU_LOWER; + config->programNotation = VBU_NAMES; + config->setfNotation = VBU_SPLIT; + config->systemCase = VBU_LOWER; + config->systemNotation = VBU_NAMES; + return config; +} + +/* Disassemble from a simulation */ +VBUAPI VBU_DasmLine* vbuDisassemble(VB *sim, uint32_t address, + VBU_DasmConfig *config, unsigned length, int line) { + return dasmDisassemble(sim, address, config, length, line); +} diff --git a/util/vbu.h b/util/vbu.h new file mode 100644 index 0000000..ab80d5e --- /dev/null +++ b/util/vbu.h @@ -0,0 +1,97 @@ +#ifndef VBU_H_ +#define VBU_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef VBUAPI +#define VBUAPI extern +#endif + +/* Header includes */ +#include + + + +/********************************* Constants *********************************/ + +/* Disassembler options */ +#define VBU_0X 0 +#define VBU_ABSOLUTE 0 +#define VBU_C 1 +#define VBU_DEST_FIRST 1 +#define VBU_DEST_LAST 0 +#define VBU_DOLLAR 1 +#define VBU_E 0 +#define VBU_H 2 +#define VBU_INSIDE 1 +#define VBU_JOINED 0 +#define VBU_L 0 +#define VBU_LOWER 1 +#define VBU_NAMES 1 +#define VBU_NUMBERS 0 +#define VBU_OUTSIDE 0 +#define VBU_RELATIVE 1 +#define VBU_SPLIT 1 +#define VBU_UPPER 0 +#define VBU_Z 1 + + + +/*********************************** Types ***********************************/ + +/* Disassembler text options */ +typedef struct { /* Defaults listed first */ + uint8_t bcondNotation; /* JOINED, SPLIT */ + uint8_t branchNotation; /* ABSOLUTE, RELATIVE */ + uint8_t conditionCase; /* LOWER, UPPER */ + uint8_t conditionCL; /* L, C */ + uint8_t conditionEZ; /* Z, E */ + uint8_t conditionNotation; /* NAMES, NUMBERS */ + uint8_t hexCase; /* UPPER, LOWER */ + uint8_t hexNotation; /* 0X, H, DOLLAR */ + uint8_t memoryNotation; /* OUTSIDE, INSIDE */ + uint8_t mnemonicCase; /* UPPER, LOWER */ + uint8_t operandOrder; /* DEST_LAST, DEST_FIRST */ + uint8_t programCase; /* LOWER, UPPER */ + uint8_t programNotation; /* NAMES, NUMBERS */ + uint8_t setfNotation; /* SPLIT, JOINED */ + uint8_t systemCase; /* LOWER, UPPER */ + uint8_t systemNotation; /* NAMES, NUMBERS */ +} VBU_DasmConfig; + +/* Disassembler output line */ +typedef struct { + + /* Parsed */ + uint32_t address; /* Memory address */ + uint16_t code[4]; /* Instruction bytes */ + uint8_t codeLength; /* Number of items in code */ + uint8_t isPC; /* Set if PC is on this line */ + + /* Display text */ + struct { + char *address; /* Memory address */ + char *code[4]; /* Instruction bytes */ + char *mnemonic; /* Instruction mnemonic */ + char *operands[3]; /* Instruction operands */ + uint8_t operandsLength; /* Number of items in operands */ + } text; + +} VBU_DasmLine; + + + +/******************************* API Commands ********************************/ + +VBUAPI VBU_DasmConfig* vbuDasmInit (VBU_DasmConfig *config); +VBUAPI VBU_DasmLine* vbuDisassemble(VB *sim, uint32_t address, VBU_DasmConfig *config, unsigned length, int line); + + + +#ifdef __cplusplus +} +#endif + +#endif /* VBU_H_ */