diff --git a/util/disassembler.c b/util/disassembler.c index 896f34c..573db44 100644 --- a/util/disassembler.c +++ b/util/disassembler.c @@ -5,694 +5,832 @@ /********************************* 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 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 +#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 ********************************/ -/* Instruction descriptor */ -typedef struct { - char mnemonic[8]; /* Mnemonic, uppercase */ - uint8_t operandsLength; /* Number of operands */ - uint8_t operands[3]; /* Operand types */ -} OpDef; - -/* Instruction descriptors */ +/* Top-level opcodes */ 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 } } + { "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 } } }; -/* 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 */ +/* 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 } } }; -/* 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 */ +/* 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 } } }; -/* Invalid instruction descriptors */ -static const OpDef OPDEF_ILLEGAL = { "---", 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 ******************************/ -/* 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"; +/* 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; } - return "GT"; + + /* 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 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; +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; } - 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'; + /* 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 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; - } +/* 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++] = '-'; } - if (text == NULL) - sprintf(dest, "R%d", id); - else strcpy(dest, text); - if (config->programCase == VBU_LOWER) - dasmToLower(dest); + + /* 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); -/**************************** 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); + /* 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 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 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 */ -/* 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); -} + /* Prefix */ + if (config->immediateNotation == VBU_PREFIX_NUMBER) + dest[len++] = '#'; -/* Format a 5-bit sign-extended immediate operand */ -static void dasmOpImm5S(char *dest, VBU_DasmConfig*config, VBU_DasmLine *line){ - if (config->immediateNotation == VBU_NUMBER) - *dest++ = '#'; - sprintf(dest, "%d", SignExtend(line->code[0], 5)); -} - -/* Format a 5-bit zero-filled immediate operand */ -static void dasmOpImm5U(char *dest, VBU_DasmConfig*config, VBU_DasmLine *line){ - if (config->immediateNotation == VBU_NUMBER) - *dest++ = '#'; - 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 (config->immediateNotation == VBU_NUMBER) - *dest++ = '#'; - if (imm >= -256 && imm <= 256) { - sprintf(dest, "%d", imm); - return; + /* 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); } - if (imm < 0) { - *dest++ = '-'; - imm = -imm; + + /* 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; } - dasmToHex(dest, config, 0, imm); + + /* Magnitude is large: format as hexadecimal */ + return len + dasmHex(&dest[len], + config->hexNotation, config->hexCase, value, 4); } -/* 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]; - if (config->immediateNotation == VBU_NUMBER) - *dest++ = '#'; - dasmToHex(dest, config, 4, imm); +/* 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 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 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 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]); +static int dasmOpMemory(char *dest, VBU_DasmConfig *config, uint16_t *code) { + int32_t disp; /* Memory displacement */ + int len = 0; /* Length of resulting string */ - /* Displacement of zero */ + /* Parse operand */ + disp = (int16_t) code[1]; + + /* No-displacement notation */ if (disp == 0) { - *dest++ = '['; - dasmProgram(dest, config, reg1); - strcat(dest, "]"); - return; + dest[len++] = '['; + len += dasmOpProgram(&dest[len], config, code, OP_REG1); + dest[len++] = ']'; } - /* 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 notation */ + else if (config->memoryNotation == VBU_OUTSIDE) { - /* Outside */ - else { - if (disp < 0) { - *dest++ = '-'; - disp = -disp; + /* 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); } - if (disp <= 256) - sprintf(dest, "%d", disp); - else dasmToHex(dest, config, 0, disp); - dest += strlen(dest); - *dest++ = '['; - dasmProgram(dest, config, reg1); - strcat(dest, "]"); + + /* 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++] = ']'; } -} - -/* 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); + /* Inside notation */ else { - strcpy(dest, dasmCondition(config, cond)); - if (config->conditionCase == VBU_LOWER) - dasmToLower(dest); + + /* 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 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; - } +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; } - if (text == NULL) { - sprintf(dest, "%d", id); - return; - } - strcpy(dest, text); - if (config->systemCase == VBU_LOWER) - dasmToLower(dest); + + /* 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; -/***************************** Module Functions ******************************/ + /* Fetch first code halfword */ + code[0] = vbFetch(sim, address); + line->code[0] = code[0]; + line->code[1] = code[0] >> 8; -/* 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 */ + /* Select opcode definition */ + def = (OpDef *) &OPDEFS[code[0] >> 10 & 63]; + line->codeLength = def->codeLength; + mnemonic = def->mnemonic; - /* 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; + /* Fetch second code halfword if necessary */ + if (line->codeLength == 4) { + code[1] = vbFetch(sim, address + 2); + line->code[2] = code[1]; + line->code[3] = code[1] >> 8; + } else { + code[1] = 0; + line->code[2] = 0; + line->code[3] = 0; } - /* 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; -} + /* Two-step processing */ + switch (def->format) { -/* 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]; -} - -/* 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, config, line); break; - case DASM_IMM5U : dasmOpImm5U (dest, config, 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, uint32_t pc, - 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 */ - uint32_t x; /* Scratch and iterator */ - - /* Process non-text members */ - line = &(*lines)[index]; - line->address = *address; - line->codeLength = vbuCodeSize(sim, *address); - line->isPC = *address == pc; - for (x = 0; x < line->codeLength; x++) - line->code[x] = vbRead(sim, *address + x, VB_U8); - - /* Advance to the next instruction or PC, whichever is sooner */ - *address += pc != *address && pc - *address < line->codeLength ? - pc - *address : line->codeLength; - - /* 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); + /* 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; - case 0x1F: /* Bit string */ - x = line->code[0] & 31; - opdef = (OpDef *) (x < 16 ? &OPDEFS_BITSTRING[x] : &OPDEF_ILLEGAL); + + /* Bit string instructions */ + case FMT_BITSTRING: + x = code[0] & 31; + def = (OpDef *) &BITSTRINGS[x > 15 ? 4 : x]; + mnemonic = def->mnemonic; break; - case 0x20: case 0x21: case 0x22: case 0x23: /* BCOND */ - case 0x24: case 0x25: case 0x26: case 0x27: - opdef = dasmBCOND(config, line, &special); + + /* Floating-point and Nintendo instructions */ + case FMT_FLOATENDO: + x = code[1] >> 10 & 63; + def = (OpDef *) &FLOATENDOS[x > 12 ? 1 : x]; + mnemonic = def->mnemonic; break; - case 0x3E: /* Floating-point/Nintendo */ - x = line->code[3] >> 2 & 63; - opdef = (OpDef *) (x < 13 ? &OPDEFS_FLOATENDO[x] : &OPDEF_ILLEGAL); + + /* 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; - 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; + /* Single template for illegal opcodes */ + if (def->format == FMT_ILLEGAL) { + def = (OpDef *) &ILLEGAL; + mnemonic = def->mnemonic; } - /* 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; + line->text.operandsLength = def->operandsLength; + for (mnemonicLen = 0; mnemonic[mnemonicLen]; mnemonicLen++); + textLen = 9 /* Address */ + line->codeLength * 3 + mnemonicLen + 1; - /* Address */ - strcpy(dest, tAddress); - line->text.address = dest; - dest += 9; + /* 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]; - /* Code */ + /* 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++) { - strcpy(dest, tCode[x]); - line->text.code[x] = dest; - dest += 3; + 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; } - /* 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; + return line->codeLength; } /***************************** Library Functions *****************************/ -/* Disassemble from a simulation */ +/* Disassemble instructions 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 ? 10 : line + 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 = vbuCodeSize(sim, addr); - if (address - addr < size) - break; - - /* Advance to the next instruction or PC, whichever is sooner */ - addr += addr != pc && 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 += vbuCodeSize(sim, addr); + 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 */ - size = length * sizeof (VBU_DasmLine); - lines = VBU_REALLOC(NULL, size); - offset = size; - if (lines == NULL) - goto catch; + pc = vbGetProgramCounter(sim); + start = (address - (line + 10) * 4) & 0xFFFFFFFE; - /* Process output lines */ - for (x = 0; x < length; x++) { - if (dasmLine(sim, &addr, pc, config, &lines, &size, &offset, x)) - goto catch; + /* 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; } - return VBU_REALLOC(lines, offset); - /* Exception handler */ - catch: - circle = VBU_REALLOC(circle, 0); - lines = VBU_REALLOC(lines , 0); - return NULL; + /* 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; } diff --git a/util/vbu.c b/util/vbu.c index f26740e..51be0bd 100644 --- a/util/vbu.c +++ b/util/vbu.c @@ -46,14 +46,21 @@ VBUAPI int vbuCodeSize(VB *sim, uint32_t address) { } /* Initialize disassembler options with default settings */ -VBUAPI VBU_DasmConfig* vbuDasmInit(VBU_DasmConfig *config) { +VBUAPI VBU_DasmConfig* vbuDasmDefaults(VBU_DasmConfig *config) { + + /* Error checking */ + if (config == NULL) + return NULL; + + /* Supply default configuration */ config->bcondNotation = VBU_JOINED; + config->branchNotation = VBU_ABSOLUTE; 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->hexNotation = VBU_PREFIX_0X; config->immediateNotation = VBU_NONE; config->memoryNotation = VBU_OUTSIDE; config->mnemonicCase = VBU_UPPER; @@ -69,6 +76,17 @@ VBUAPI VBU_DasmConfig* vbuDasmInit(VBU_DasmConfig *config) { /* Disassemble from a simulation */ VBUAPI VBU_DasmLine* vbuDisassemble(VB *sim, uint32_t address, VBU_DasmConfig *config, unsigned length, int line) { + VBU_DasmConfig defs; /* Default settings */ + + /* Error checking */ + if (sim == NULL || length == 0) + return NULL; + + /* Supply default configuration */ + if (config == NULL) + config = vbuDasmDefaults(&defs); + + /* Disassemble instructions */ return dasmDisassemble(sim, address, config, length, line); } diff --git a/util/vbu.h b/util/vbu.h index 9cbe667..4a99074 100644 --- a/util/vbu.h +++ b/util/vbu.h @@ -17,25 +17,27 @@ extern "C" { /********************************* Constants *********************************/ /* Disassembler options */ -#define VBU_0X 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_NONE 0 -#define VBU_NUMBER 1 -#define VBU_NUMBERS 0 -#define VBU_OUTSIDE 0 -#define VBU_SPLIT 1 -#define VBU_UPPER 0 -#define VBU_Z 1 +#define VBU_ABSOLUTE 0 +#define VBU_C 1 +#define VBU_DEST_FIRST 1 +#define VBU_DEST_LAST 0 +#define VBU_E 0 +#define VBU_INSIDE 1 +#define VBU_JOINED 0 +#define VBU_L 0 +#define VBU_LOWER 1 +#define VBU_NAMES 1 +#define VBU_NONE 0 +#define VBU_NUMBERS 0 +#define VBU_OUTSIDE 0 +#define VBU_PREFIX_0X 0 +#define VBU_PREFIX_DOLLAR 1 +#define VBU_PREFIX_NUMBER 1 +#define VBU_RELATIVE 1 +#define VBU_SPLIT 1 +#define VBU_SUFFIX_H 2 +#define VBU_UPPER 0 +#define VBU_Z 1 @@ -44,13 +46,14 @@ extern "C" { /* 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 immediateNotation; /* NONE, NUMBER */ + uint8_t hexNotation; /* PREFIX_0X, SUFFIX_H, PREFIX_DOLLAR */ + uint8_t immediateNotation; /* NONE, SUFFIX_NUMBER */ uint8_t memoryNotation; /* OUTSIDE, INSIDE */ uint8_t mnemonicCase; /* UPPER, LOWER */ uint8_t operandOrder; /* DEST_LAST, DEST_FIRST */ @@ -66,7 +69,7 @@ typedef struct { /* Parsed */ uint32_t address; /* Memory address */ - uint16_t code[4]; /* Instruction bytes */ + uint8_t code[4]; /* Instruction bytes */ uint8_t codeLength; /* Number of items in code */ uint8_t isPC; /* Set if PC is on this line */ @@ -85,10 +88,10 @@ typedef struct { /******************************* API Commands ********************************/ -VBUAPI int vbuCodeSize (VB *sim, uint32_t address); -VBUAPI VBU_DasmConfig* vbuDasmInit (VBU_DasmConfig *config); -VBUAPI VBU_DasmLine* vbuDisassemble(VB *sim, uint32_t address, VBU_DasmConfig *config, unsigned length, int line); -VBUAPI void* vbuFromISX (void *bytes, size_t length, size_t *romLength); +VBUAPI int vbuCodeSize (VB *sim, uint32_t address); +VBUAPI VBU_DasmConfig* vbuDasmDefaults(VBU_DasmConfig *config); +VBUAPI VBU_DasmLine* vbuDisassemble (VB *sim, uint32_t address, VBU_DasmConfig *config, unsigned length, int line); +VBUAPI void* vbuFromISX (void *bytes, size_t length, size_t *romLength);