Implement disassembler
This commit is contained in:
		
							parent
							
								
									9cc01bb6f7
								
							
						
					
					
						commit
						f5a84fd4d0
					
				
							
								
								
									
										10
									
								
								makefile
								
								
								
								
							
							
						
						
									
										10
									
								
								makefile
								
								
								
								
							| 
						 | 
					@ -29,7 +29,7 @@ core:
 | 
				
			||||||
        -D VB_DIRECT_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \
 | 
					        -D VB_DIRECT_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \
 | 
				
			||||||
        -D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \
 | 
					        -D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \
 | 
				
			||||||
        -D VB_DIRECT_WRITE=testWrite -D VB_DIRECT_SAMPLES=testSamples \
 | 
					        -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
 | 
					#	Clang generic
 | 
				
			||||||
	@emcc core/vb.c -I core -c -o /dev/null -O3 \
 | 
						@emcc core/vb.c -I core -c -o /dev/null -O3 \
 | 
				
			||||||
        -Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing
 | 
					        -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_EXCEPTION=textException -D VB_DIRECT_EXECUTE=textExecute \
 | 
				
			||||||
        -D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \
 | 
					        -D VB_DIRECT_FETCH=testFetch -D VB_DIRECT_FRAME=testFrame \
 | 
				
			||||||
        -D VB_DIRECT_WRITE=testWrite -D VB_DIRECT_SAMPLES=testSamples \
 | 
					        -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
 | 
					.PHONY: wasm
 | 
				
			||||||
wasm:
 | 
					wasm:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 */
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,65 @@
 | 
				
			||||||
 | 
					#ifndef VBUAPI
 | 
				
			||||||
 | 
					#define VBUAPI
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <vbu.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Memory management */
 | 
				
			||||||
 | 
					#ifndef VBU_REALLOC
 | 
				
			||||||
 | 
					    #include <stdlib.h>
 | 
				
			||||||
 | 
					    #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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,97 @@
 | 
				
			||||||
 | 
					#ifndef VBU_H_
 | 
				
			||||||
 | 
					#define VBU_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef VBUAPI
 | 
				
			||||||
 | 
					#define VBUAPI extern
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Header includes */
 | 
				
			||||||
 | 
					#include <vb.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/********************************* 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_ */
 | 
				
			||||||
		Loading…
	
		Reference in New Issue