/* 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] = vbFetch(sim, address); 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] = 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; } /* 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 */