Mercurial > repos > blastem
view 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 source
#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; }