Mercurial > repos > blastem
diff upddis.c @ 2717:04007ac9ee3b
Add upd78k2 disassembler
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Wed, 16 Jul 2025 07:36:01 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upddis.c Wed Jul 16 07:36:01 2025 -0700 @@ -0,0 +1,356 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include "upd78k2_dis.h" +#include "util.h" + +int headless; +void render_errorbox(char *title, char *message) {} +void render_warnbox(char *title, char *message) {} +void render_infobox(char *title, char *message) {} + +typedef struct { + uint16_t address_off; + uint16_t address_end; + uint8_t *buffer; +} rom_def; + +uint8_t fetch(uint16_t address, void *data) +{ + rom_def *rom = data; + if (address >= rom->address_off && address < rom->address_end) { + return rom->buffer[(address - rom->address_off)]; + } + return 0; +} + +void print_label_def(char *key, tern_val val, uint8_t valtype, void *data) +{ + rom_def *rom = data; + label_def *label = val.ptrval; + uint32_t address = label->full_address & 0xFFFF; + if (address >= rom->address_off && address < rom->address_end) { + return; + } + if (!label->referenced) { + return; + } + if (label->num_labels) { + for (int i = 0; i < label->num_labels; i++) + { + printf("%s equ 0%XH\n", label->labels[i], label->full_address); + } + } else { + printf("ADR_%X equ 0%XH\n", label->full_address, label->full_address); + } +} + +void format_data(disasm_context *context, rom_def *rom, uint8_t labels, uint16_t start_address, uint16_t end_address) +{ + char label_buf[256]; + for (uint16_t address = start_address; address < end_address;) + { + if (labels) { + uint16_t end = address + 8; + if (end > end_address) { + end = end_address; + } + uint16_t start = address; + if (!(address & 1) && address < 0x80) { + for (; address < end; address += 2) { + uint16_t value = fetch(address, rom); + value |= fetch(address + 1, rom) << 8; + format_label(label_buf, value, context); + if (address == start) { + printf("\tdw %s", label_buf); + } else { + printf(", %s", label_buf); + } + } + } else { + if (address < 0x80) { + end = address + 1; + } + for (; address < end; address++) { + uint8_t value = fetch(address, rom); + if (address == start) { + printf("\tdb 0%02XH", value); + } else { + printf(", 0%02XH", value); + } + } + } + address = end; + puts(""); + } else { + uint8_t value = fetch(address, rom); + printf("%X: %X\n", address, value); + address++; + } + } +} + +int main(int argc, char ** argv) +{ + long filesize; + uint8_t *filebuf = NULL; + char disbuf[1024]; + + uint8_t labels = 0, addr = 0, only = 0, reset = 0; + disasm_context *context = create_upd78k2_disasm(); + + uint32_t address_off = 0, address_end; + 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 '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) { + defer_disasm(context, address); + if (*end == '=') { + add_label(context, strip_ws(end+1), address); + } else { + reference(context, address); + } + } + } + } + fclose(address_log); + } + } else { + char *end; + uint32_t address = strtol(argv[opt], &end, 16); + defer_disasm(context, address); + if (*end == '=') { + add_label(context, end+1, address); + } else { + reference(context, address); + } + } + } + if (labels) { + add_upd7823x_labels(context); + } + 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]; + uint8_t has_manual_defs = !!context->deferred; + filebuf = malloc(filesize); + if (fread(filebuf, 1, filesize, f) != filesize) + { + fprintf(stderr, "Failure while reading file %s\n", argv[1]); + } + address_end = address_off + filesize; + if (address_end > 0xFB00) { + //correct for uPD78237 and some other members of the uPD78K/II family + //but others allow external ROM up to higher addresses + address_end = 0xFB00; + } + if (!address_off && filesize >= 0x40) { + char *vector_names[] = { + "reset", + "nmi", + NULL, + "intp0", + "intp1", + "intp2", + "intp3", + "intp4_intc30", + "intp5_intad", + "intc20_intp6", + "intc00", + "intc01", + "intc10", + "intc11", + "intc21", + NULL, + "intser", + "intsr", + "intst", + "intcsi", + [0x3E/2] = "brkex" + }; + for (int i = 0; i < sizeof(vector_names)/sizeof(*vector_names); i++) + { + if (vector_names[i]) { + uint16_t address = filebuf[i * 2] | (filebuf[i * 2 + 1] << 8); + if (address < address_end) { + defer_disasm(context, address); + add_label(context, vector_names[i], address); + } + } + } + } + rom_def rom = { + .address_off = address_off, + .address_end = address_end, + .buffer = filebuf + }; + uint16_t address, tmp_addr; + uint8_t valid_address; + while(context->deferred) { + do { + valid_address = 0; + address = context->deferred->address; + if (!is_visited(context, address)) { + address &= context->address_mask; + if (address < address_end && address >= address_off) { + valid_address = 1; + address = context->deferred->address; + } + } + deferred_addr *tmpd = context->deferred; + context->deferred = context->deferred->next; + free(tmpd); + } while(context->deferred && !valid_address); + if (!valid_address) { + break; + } + for(;;) { + if ((address & context->address_mask) > address_end || address < address_off) { + break; + } + visit(context, address); + upd_address_ref ref; + address = upd78k2_disasm(disbuf, &ref, address, fetch, &rom, NULL); + if (!strcmp(disbuf, "invalid") || startswith(disbuf, "ret")) { + break; + } + switch(ref.ref_type) + { + case UPD_REF_NONE: + if (startswith(disbuf, "br ")) { + //unconditional branch to register + goto loop_end; + } + break; + case UPD_REF_OP: + reference(context, ref.address); + break; + case UPD_REF_2OP: + reference(context, ref.address); + reference(context, ref.address2); + break; + case UPD_REF_BRANCH: + reference(context, ref.address); + if (ref.address <= address) { + defer_disasm(context, ref.address); + goto loop_end; + } else { + address = ref.address; + } + break; + case UPD_REF_COND_BRANCH: + case UPD_REF_CALL: + reference(context, ref.address); + defer_disasm(context, ref.address); + break; + case UPD_REF_OP_BRANCH: + reference(context, ref.address); + reference(context, ref.address2); + defer_disasm(context, ref.address2); + break; + case UPD_REF_CALL_TABLE: + reference(context, ref.address); + if (ref.address >= address_off && ref.address < address_end - 1) { + uint16_t table_address = ref.address; + ref.address = fetch(table_address, &rom); + ref.address |= fetch(table_address + 1, &rom) << 8; + reference(context, ref.address); + defer_disasm(context, ref.address); + } + break; + } + } +loop_end: + } + if (labels) { + tern_foreach(context->labels, print_label_def, &rom); + puts(""); + } + uint16_t data_start = 0xFFFF; + for (address = address_off; address < address_end;) { + if (labels) { + label_def *label = find_label(context, address); + if (label) { + if (data_start < address) { + format_data(context, &rom, labels, data_start, address); + data_start = 0xFFFF; + } + if (label->num_labels) { + for (int i = 0; i < label->num_labels; i++) + { + printf("%s:\n", label->labels[i]); + } + } else { + printf("ADR_%X:\n", label->full_address); + } + } + } + if (is_visited(context, address)) { + if (data_start < address) { + format_data(context, &rom, labels, data_start, address); + data_start = 0xFFFF; + } + uint16_t next = upd78k2_disasm(disbuf, NULL, address, fetch, &rom, labels ? context : NULL); + if (labels) { + if (addr) { + printf("\t%s\t;%X\n", disbuf, address); + } else { + printf("\t%s\n", disbuf); + } + } else { + printf("%X: %s\n", address, disbuf); + } + address = next; + } else { + if (data_start > address) { + data_start = address; + } + address++; + } + } + if (data_start < address) { + format_data(context, &rom, labels, data_start, address); + } + return 0; +} \ No newline at end of file