# HG changeset patch # User Michael Pavone # Date 1477881768 25200 # Node ID 4987fddd42a0a7fd4c513eddbdc87654caac24a9 # Parent f338c950fcef17a84acd39b3e196a3e5cc86d135 Initial stab at jaguar disassemler diff -r f338c950fcef -r 4987fddd42a0 Makefile --- a/Makefile Sun Oct 30 19:42:30 2016 -0700 +++ b/Makefile Sun Oct 30 19:42:48 2016 -0700 @@ -165,6 +165,9 @@ dis$(EXE) : dis.o 68kinst.o tern.o vos_program_module.o $(CC) -o $@ $^ + +jagdis : jagdis.o jagcpu.o tern.o + $(CC) -o $@ $^ zdis$(EXE) : zdis.o z80inst.o $(CC) -o $@ $^ diff -r f338c950fcef -r 4987fddd42a0 jagcpu.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jagcpu.c Sun Oct 30 19:42:48 2016 -0700 @@ -0,0 +1,231 @@ +#include +#include +#include "jagcpu.h" + +char *gpu_mnemonics[] = { + "add", + "addc", + "addq", + "addqt", + "sub", + "subc", + "subq", + "subqt", + "neg", + "and", + "or", + "xor", + "not", + "btst", + "bset", + "bclr", + "mult", + "imult", + "imultn", + "resmac", + "imacn", + "div", + "abs", + "sh", + "shlq", + "shrq", + "sha", + "sharq", + "ror", + "rorq", + "cmp", + "cmpq", + "sat8", + "sat16", + "move", + "moveq", + "moveta", + "movefa", + "movei", + "loadb", + "loadw", + "load", + "loadp", + "load", //r14 relative + "load", //r15 relative + "storeb", + "storew", + "store", + "storep", + "store", //r14 relative + "store", //r15 relative + "move", //PC + "jump", + "jr", + "mmult", + "mtoi", + "normi", + "nop", + "load", //r14 indexed + "load", //r15 indexed + "store", //r14 indexed + "store", //r15 indexed + "sat24", + "pack", + "unpack" +}; + +char *dsp_mnemonics[DSP_ADDQMOD+1] = { + [DSP_SUBQMOD] = "subqmod", + [DSP_SAT16S] = "sat16s", + [DSP_SAT32S] = "sat32s", + [DSP_MIRROR] = "mirror", + [DSP_ADDQMOD] = "addqmod" +}; + +void init_dsp_mnemonic_table() +{ + static uint8_t init_done; + if (init_done) { + return; + } + for (int i = 0; i < DSP_ADDQMOD; i++) + { + if (!dsp_mnemonics[i]) { + dsp_mnemonics[i] = gpu_mnemonics[i]; + } + } + init_done = 1; +} + +uint16_t jag_opcode(uint16_t inst, uint8_t is_gpu) +{ + uint16_t opcode = inst >> 11; + if (is_gpu && opcode == GPU_PACK && (inst & 0x20)) { + return GPU_UNPACK; + } + return opcode; +} + +uint16_t jag_reg2(uint16_t inst) +{ + return inst & 0x1F; +} + +uint16_t jag_reg1(uint16_t inst) +{ + return inst >> 5 & 0x1F; +} + +//moveq and bit instructions should just use jag_reg1 instead +uint32_t jag_quick(uint16_t inst) +{ + uint32_t val = inst >> 5 & 0x1F; + return val ? val : 32; +} + +uint8_t is_quick_1_32_opcode(uint16_t opcode, uint8_t is_gpu) +{ + return opcode == JAG_ADDQ + || opcode == JAG_ADDQT + || opcode == JAG_SUBQ + || opcode == JAG_SUBQT + || opcode == JAG_SHLQ + || opcode == JAG_SHRQ + || opcode == JAG_SHARQ + || opcode == JAG_RORQ + || (!is_gpu && ( + opcode == DSP_SUBQMOD + || opcode == DSP_ADDQMOD + )); +} + +uint8_t is_quick_0_31_opcode(uint16_t opcode) +{ + return opcode == JAG_MOVEQ + || opcode == JAG_BTST + || opcode == JAG_BSET + || opcode == JAG_BCLR; +} + +char * jag_cc_names[] = { + "t", + "ne", + "eq", + "f", + "cc", + "hi", + "eq_cc", + "f", + "cs", + "ne_cs", + "eq_cs", + "f", + "f", + "f", + "f", + "f" + "t_alt", + "ne_alt", + "eq_alt", + "f", + "pl", + "ne_pl", + "eq_pl", + "f", + "mi", + "ne_mi", + "eq_mi" +}; + +char * jag_cc(uint16_t inst) +{ + uint16_t ccnum = jag_reg2(inst); + if (ccnum >= sizeof(jag_cc_names)/sizeof(*jag_cc_names)) { + return jag_cc_names[3]; + } + return jag_cc_names[ccnum]; +} + +uint32_t jag_jr_dest(uint16_t inst, uint32_t address) +{ + uint32_t rel = jag_reg1(inst); + if (rel & 0x10) { + rel |= 0xFFFFFFE0; + } + return address + 2 + rel; +} + +int jag_cpu_disasm(uint16_t **stream, uint32_t address, char *dst, uint8_t is_gpu, uint8_t labels) +{ + uint16_t inst = **stream; + *stream++; + uint16_t opcode = jag_opcode(inst, is_gpu); + char **mnemonics; + if (is_gpu) { + mnemonics = gpu_mnemonics; + } else { + init_dsp_mnemonic_table(); + mnemonics = dsp_mnemonics; + } + switch (opcode) + { + case JAG_MOVEI: { + uint32_t immed = **stream; + *stream++; + immed |= **stream << 16; + *stream++; + return sprintf("%s $%X, r%d", mnemonics[opcode], immed, jag_reg2(inst)); + } + case JAG_JR: + return sprintf( + labels ? "%s %s, ADR_%X" : "%s %s, $W%X", + mnemonics[opcode], jag_cc(inst), jag_jr_dest(inst, address) + ); + case JAG_JUMP: + return sprintf("%s %s, (r%d)", mnemonics[opcode], jag_cc(inst), jag_reg1(inst)); + default: + if (is_quick_1_32_opcode(opcode, is_gpu)) { + return sprintf("%s %d, r%d", mnemonics[opcode], jag_quick(inst), jag_reg2(inst)); + } else if (is_quick_0_31_opcode(opcode)) { + return sprintf("%s %d, r%d", mnemonics[opcode], jag_reg1(inst), jag_reg2(inst)); + } else { + return sprintf("%s r%d, r%d", mnemonics[opcode], jag_reg1(inst), jag_reg2(inst)); + } + } +} diff -r f338c950fcef -r 4987fddd42a0 jagdis.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jagdis.c Sun Oct 30 19:42:48 2016 -0700 @@ -0,0 +1,339 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#include +#include +#include +#include +#include +#include "tern.h" +#include "util.h" +#include "jagcpu.h" + +uint8_t visited[(16*1024*1024)/16]; +uint16_t label[(16*1024*1024)/8]; + +void fatal_error(char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + exit(1); +} + + +void visit(uint32_t address) +{ + address &= 0xFFFFFF; + visited[address/16] |= 1 << ((address / 2) % 8); +} + +void reference(uint32_t address) +{ + address &= 0xFFFFFF; + //printf("referenced: %X\n", address); + label[address/16] |= 1 << (address % 16); +} + +uint8_t is_visited(uint32_t address) +{ + address &= 0xFFFFFF; + return visited[address/16] & (1 << ((address / 2) % 8)); +} + +uint16_t is_label(uint32_t address) +{ + address &= 0xFFFFFF; + return label[address/16] & (1 << (address % 16)); +} + +typedef struct { + uint32_t num_labels; + uint32_t storage; + char *labels[]; +} label_names; + +tern_node * add_label(tern_node * head, char * name, uint32_t address) +{ + char key[MAX_INT_KEY_SIZE]; + address &= 0xFFFFFF; + reference(address); + tern_int_key(address, key); + label_names * names = tern_find_ptr(head, key); + if (names) + { + if (names->num_labels == names->storage) + { + names->storage = names->storage + (names->storage >> 1); + names = realloc(names, sizeof(label_names) + names->storage * sizeof(char *)); + } + } else { + names = malloc(sizeof(label_names) + 4 * sizeof(char *)); + names->num_labels = 0; + names->storage = 4; + head = tern_insert_ptr(head, key, names); + } + names->labels[names->num_labels++] = strdup(name); + return head; +} + +typedef struct deferred { + uint32_t address; + struct deferred *next; +} deferred; + +deferred * defer(uint32_t address, deferred * next) +{ + if (is_visited(address) || address & 1) { + return next; + } + //printf("deferring %X\n", address); + deferred * d = malloc(sizeof(deferred)); + d->address = address; + d->next = next; + return d; +} + +void check_reference(uint16_t inst, uint32_t address, uint8_t is_gpu) +{ + if (jag_opcode(inst, is_gpu) == JAG_JR) { + reference(jag_jr_dest(inst, address)); + } +} + +char * strip_ws(char * text) +{ + while (*text && (!isprint(*text) || isblank(*text))) + { + text++; + } + char * ret = text; + text = ret + strlen(ret) - 1; + while (text > ret && (!isprint(*text) || isblank(*text))) + { + *text = 0; + text--; + } + return ret; +} + +int main(int argc, char ** argv) +{ + long filesize; + unsigned short *filebuf; + char disbuf[1024]; + unsigned short * cur; + deferred *def = NULL, *tmpd; + uint32_t start = 0xFFFFFFFF; + + uint8_t labels = 0, addr = 0, only = 0, vos = 0, reset = 0; + tern_node * named_labels = NULL; + + uint32_t address_off = 0xFFFFFFFF, address_end; + uint8_t is_gpu = 1; + for(uint8_t opt = 2; opt < argc; ++opt) { + if (argv[opt][0] == '-') { + FILE * address_log; + switch (argv[opt][1]) + { + case 'l': + labels = 1; + break; + case 'a': + addr = 1; + break; + case 'o': + only = 1; + break; + case 'r': + reset = 1; + break; + case 's': + opt++; + if (opt >= argc) { + fputs("-s must be followed by an offset\n", stderr); + exit(1); + } + address_off = strtol(argv[opt], NULL, 0); + break; + case 'p': + opt++; + if (opt >= argc) { + fputs("-p must be followed by a starting PC value\n", stderr); + exit(1); + } + start = strtol(argv[opt], NULL, 0); + break; + case 'd': + is_gpu = 0; + break; + case 'f': + opt++; + if (opt >= argc) { + fputs("-f must be followed by a filename\n", stderr); + exit(1); + } + address_log = fopen(argv[opt], "r"); + if (!address_log) { + fprintf(stderr, "Failed to open %s for reading\n", argv[opt]); + exit(1); + } + while (fgets(disbuf, sizeof(disbuf), address_log)) { + if (disbuf[0]) { + char *end; + uint32_t address = strtol(disbuf, &end, 16); + if (address) { + def = defer(address, def); + reference(address); + if (*end == '=') { + named_labels = add_label(named_labels, strip_ws(end+1), address); + } + } + } + } + } + } else { + char *end; + uint32_t address = strtol(argv[opt], &end, 16); + def = defer(address, def); + reference(address); + if (*end == '=') { + named_labels = add_label(named_labels, end+1, address); + } + } + } + if (address_off == 0xFFFFFFFF) { + address_off = is_gpu ? 0xF03000 : 0xF1B000; + } + if (start == 0xFFFFFFFF) { + start = address_off; + } + FILE * f = fopen(argv[1], "rb"); + fseek(f, 0, SEEK_END); + filesize = ftell(f); + fseek(f, 0, SEEK_SET); + + char int_key[MAX_INT_KEY_SIZE]; + address_end = address_off + filesize; + filebuf = malloc(filesize); + if (fread(filebuf, 2, filesize/2, f) != filesize/2) + { + fprintf(stderr, "Failure while reading file %s\n", argv[1]); + } + fclose(f); + for(cur = filebuf; cur - filebuf < (filesize/2); ++cur) + { + *cur = (*cur >> 8) | (*cur << 8); + } + named_labels = add_label(named_labels, "start", start); + if (!def || !only) { + def = defer(start, def); + } + + uint16_t *encoded, *next; + uint32_t size, tmp_addr; + uint32_t address; + while(def) { + do { + encoded = NULL; + address = def->address; + if (!is_visited(address)) { + encoded = filebuf + (address - address_off)/2; + } + tmpd = def; + def = def->next; + free(tmpd); + } while(def && encoded == NULL); + if (!encoded) { + break; + } + for(;;) { + if (address > address_end || address < address_off) { + break; + } + visit(address); + uint16_t inst = *encoded; + uint32_t inst_address = address; + check_reference(inst, address, is_gpu); + uint16_t opcode = jag_opcode(inst, is_gpu); + if (opcode == JAG_MOVEI) { + address += 6; + encoded += 3; + } else { + address += 2; + encoded++; + } + + + if (opcode == JAG_JR || opcode == JAG_JUMP) { + if (!(jag_reg2(inst) & 0xF)) { + //unconditional jump + if (opcode == JAG_JR) { + address = jag_jr_dest(inst, inst_address); + reference(address); + if (is_visited(address)) { + break; + } + } else { + break; + } + } else if (opcode == JAG_JR) { + uint32_t dest = jag_jr_dest(inst, inst_address); + reference(dest); + def = defer(dest, def); + } + } + } + } + if (labels) { + for (address = 0; address < address_off; address++) { + if (is_label(address)) { + printf("ADR_%X equ $%X\n", address, address); + } + } + for (address = filesize; address < (16*1024*1024); address++) { + char key[MAX_INT_KEY_SIZE]; + tern_int_key(address, key); + label_names *names = tern_find_ptr(named_labels, key); + if (names) { + for (int i = 0; i < names->num_labels; i++) + { + printf("%s equ $%X\n", names->labels[i], address); + } + } else if (is_label(address)) { + printf("ADR_%X equ $%X\n", address, address); + } + } + puts(""); + } + for (address = address_off; address < address_end; address+=2) { + if (is_visited(address)) { + encoded = filebuf + (address-address_off)/2; + jag_cpu_disasm(&encoded, address, disbuf, is_gpu, labels); + if (labels) { + char keybuf[MAX_INT_KEY_SIZE]; + label_names * names = tern_find_ptr(named_labels, tern_int_key(address, keybuf)); + if (names) + { + for (int i = 0; i < names->num_labels; i++) + { + printf("%s:\n", names->labels[i]); + } + } else if (is_label(address)) { + printf("ADR_%X:\n", address); + } + if (addr) { + printf("\t%s\t;%X\n", disbuf, address); + } else { + printf("\t%s\n", disbuf); + } + } else { + printf("%X: %s\n", address, disbuf); + } + } + } + return 0; +}