changeset 1093:4987fddd42a0

Initial stab at jaguar disassemler
author Michael Pavone <pavone@retrodev.com>
date Sun, 30 Oct 2016 19:42:48 -0700
parents f338c950fcef
children 1dba006bad47
files Makefile jagcpu.c jagdis.c
diffstat 3 files changed, 573 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- 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 $@ $^
--- /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 <stdint.h>
+#include <stdio.h>
+#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));
+		}
+	}
+}
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#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;
+}