From c4921552ba827218b0b0271588ebd013ad322a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Henrik=20Sk=C3=A5rstedt?= Date: Wed, 16 Dec 2015 22:19:25 -0800 Subject: [PATCH] Disassembler calling graph - graph command line option adds a calling graph to the disassembly file - graph+bra includes branches in the calling graph - re-evaluating separating code/data segments after the segments have been culled. - Added a c64kernal.lbl file that includes kernal calls --- disassembler/README.MD | 27 +++- disassembler/c64kernal.lbl | 158 ++++++++++++++++++++ disassembler/x65dsasm.cpp | 287 +++++++++++++++++++++++++++++++++++-- 3 files changed, 463 insertions(+), 9 deletions(-) create mode 100644 disassembler/c64kernal.lbl diff --git a/disassembler/README.MD b/disassembler/README.MD index 795b5f3..e9bdc0b 100644 --- a/disassembler/README.MD +++ b/disassembler/README.MD @@ -23,9 +23,14 @@ x65dsasm binary disasm.txt [$skip[-$end]] [addr=$xxxx] [cpu=6502/65C02/65816] * src: export near assemblable source with guesstimated data blocks * mx: set the mx flags which control accumulator and index register size * labels: import labels from a file (each line: label=$xxxx [code]/[data] comment) +* graph: prefix the file with a calling graph +* graph+bra: include branches in the calling graph (increases size significantly) ### Updates +* graph option exports a calling graph before the disassembly +* re-evaluation of separating segments after final cleanup +* c64kernal.lbl file defining c64 hw + kernal functions in the range of $e000-$fffa * a78.lbl file defining Atari 7800 hardware addresses * Switched the instruction info around to more easily determine read-only instructions * Various improvements distinguishing between code and data @@ -72,11 +77,31 @@ VIC_Sprite0_x = $d000 set sprite 0 x position VIC_Sprite0_y = $d001 set sprite 0 y position VIC_Sprite1_x = $d002 set sprite 1 x position VIC_Sprite1_y = $d003 set sprite 1 y position -``` +`` + +The simple way to work with a labels file is to copy an existing hardware file and add labels to it. Sample output: ``` +; +; FUNCTION CALLING GRAPH +; +; Code_140 ($d4af) [-] +; Code_21 ($c5a3) [jmp] +; Code_143 ($d56e) [jsr] +; Code_109 ($cec3) [jsr] +; +; DISASSEMBLY +; +; Referenced from ResetVector + $0 (subroutine, $fffc) +; Referenced from IntVector + $0 (subroutine, $fffe) +Reset: ; $c000 + cld + sei +; Referenced from Reset / .l_1 + $3 (branch, $c002) +.l_1: ; $c002 + ; -------------------------------- ; ; Referenced from Label_14 + $7 (subroutine) diff --git a/disassembler/c64kernal.lbl b/disassembler/c64kernal.lbl new file mode 100644 index 0000000..f2d783e --- /dev/null +++ b/disassembler/c64kernal.lbl @@ -0,0 +1,158 @@ +6510_Port_Data_Direction = 0 data +6510_Port_Data = 1 data +C64_System_Interrupt = $314-$316 data +C64_System_NMI = $318-$31a data +Vic_Sprite_Pos = $d000-$d011 data +Vic_Screen_Ctrl = $d011 data +Vic_Raster_Line = $d012 data +Vic_Light_Pen = $d013-$d015 data +Vic_Sprite_Enable = $d015 data +Vic_Sprite_Ctrl = $d016 data +Vic_Sprite_Height_Dbl = $d017 data +Vic_Memory_Setup = $d018 data +Vic_Int_Status = $d019-$d01b data +Vic_Sprite_Pri = $d01b data +Vic_Sprite_MultiCol = $d01C data +Vic_Sprite_Width_Dbl = $d01d data +Vic_Sprite_Sprite_Coll = $d01e data +Vic_Sprite_Background_Coll = $d01f data +Vic_Border_Color = $d020 data +Vic_Back_Color = $d021-$d025 data +Vic_Sprite_Color_Extra = $d025-$d027 data +Vic_Sprite_Color = $d027-$d02f data +Vic = $d02f-$d400 data +SID_Voice1_Freq = $d400-$d402 data +SID_Voice1_Pulse = $d402-$d404 data +SID_Voice1_Ctrl = $d404 data +SID_Voice1_AttackDecay = $d405 data +SID_Voice1_SustainRelease = $d406 data +SID_Voice2_Freq = $d407-$d409 data +SID_Voice2_Pulse = $d409-$d40b data +SID_Voice2_Ctrl = $d40b data +SID_Voice2_AttackDecay = $d40c data +SID_Voice2_SustainRelease = $d40d data +SID_Voice3_Freq = $d40e-$d410 data +SID_Voice3_Pulse = $d410-$d412 data +SID_Voice3_Ctrl = $d412 data +SID_Voice3_AttackDecay = $d413 data +SID_Voice3_SustainRelease = $d414 data +SID_Filter_Cutoff = $d415-$d417 data +SID_Filter_Ctrl = $d417 data +SID_Volume_Filter_Mode = $d418 data +SID_Paddles = $d419-$d41b data +SID_Voice3_Wave_Out = $d41b data +SID_Voice3_ADSR_Out = $d41c data +SID = $D41d-$D800 data +ColorRam = $D800-$DC00 data +CIA1_PortA_KBD_Joy2 = $dc00 data +CIA1_PortB_KBD_Joy1 = $dc01 data +CIA1_PortA_Data_Dir = $dc02 data +CIA1_PortB_Data_Dir = $dc03 data +CIA1_TimerA = $dc04-$dc06 data +CIA1_TimerB = $dc06-$dc08 data +CIA1_TimeOfDay = $dc08-$dc0c data +CIA1_Serial_Shift = $dc0c data +CIA1_Interrupt_Ctrl_Status = $dc0d data +CIA1_TimerA_Ctrl = $dc0e data +CIA1_TimerB_Ctrl = $dc0f data +CIA1 = $DC10-$DD00 data +CIA2_PortA_Serial = $dd00 data +CIA2_PortB_RS232 = $dd01 data +CIA2_PortA_Data_Dir = $dd02 data +CIA2_PortB_Data_Dir = $dd03 data +CIA2_TimerA = $dd04-$dd06 data +CIA2_TimerB = $dd06-$dd08 data +CIA2_TimeOfDay = $dd08-$dd0c data +CIA2_Serial_Shift = $dd0c data +CIA2_Interrupt_Ctrl_Status = $dd0d data +CIA2_TimerA_Ctrl = $dd0e data +CIA2_TimerB_Ctrl = $dd0f data +CIA2 = $dd10-$de00 data +IO_Area1 = $de00-$df00 data +IO_Area2 = $df00-$e000 data + +; from sta.c64.org + +KRN_SCINIT_JMP = $FF81 code Initialize VIC; restore default input/output to keyboard/screen; clear screen; set PAL/NTSC switch and interrupt timer. +KRN_SCINIT = $FF5B code Initialize VIC; restore default input/output to keyboard/screen; clear screen; set PAL/NTSC switch and interrupt timer. +KRN_IOINIT_JMP = $FF84 code Initialize CIA's, SID volume; setup memory configuration; set and start interrupt timer. +KRN_IOINIT= $FDA3 code Initialize CIA's, SID volume; setup memory configuration; set and start interrupt timer. +KRN_RAMTAS_JMP = $FF87 code Clear memory addresses $0002-$0101 and $0200-$03FF; run memory test and set start and end address of BASIC work area accordingly; set screen memory to $0400 and datasette buffer to $033C. +KRN_RAMTAS = FD50 code Clear memory addresses $0002-$0101 and $0200-$03FF; run memory test and set start and end address of BASIC work area accordingly; set screen memory to $0400 and datasette buffer to $033C. +KRN_RESTOR_JMP = $FF8A code Fill vector table at memory addresses $0314-$0333 with default values. +KRN_RESTOR = $FD15 code Fill vector table at memory addresses $0314-$0333 with default values. +KRN_VECTOR_JMP = $FF8D code Copy vector table at memory addresses $0314-$0333 from or into user table. +KRN_VECTOR = $FD1A code Copy vector table at memory addresses $0314-$0333 from or into user table. +KRN_SETMSG_JMP = $FF90 code Set system error display switch at memory address $009D. +KRN_SETMSG = $FE18 code Set system error display switch at memory address $009D. +KRN_LSTNSA_JMP = $FF93 code Send LISTEN secondary address to serial bus. (Must call LISTEN beforehands.) +KRN_LSTNSA = $EDB9. code Send LISTEN secondary address to serial bus. (Must call LISTEN beforehands.) +KRN_TALKSA_JMP = $FF96 code Send TALK secondary address to serial bus. (Must call TALK beforehands.) +KRN_TALKSA = $EDC7 code Send TALK secondary address to serial bus. (Must call TALK beforehands.) +KRN_MEMBOT_JMP = $FF99 code Save or restore start address of BASIC work area. +KRN_MEMBOT = $FE25 code Save or restore start address of BASIC work area. +KRN_MEMTOP_JMP = $FF9C code Save or restore end address of BASIC work area. +KRN_MEMTOP = $FE34 code Save or restore end address of BASIC work area. +KRN_SCNKEY_JMP = $FF9F code Query keyboard; put current matrix code into memory address $00CB, current status of shift keys into memory address $028D and PETSCII code into keyboard buffer. +KRN_SCNKEY = $EA87 code Query keyboard; put current matrix code into memory address $00CB, current status of shift keys into memory address $028D and PETSCII code into keyboard buffer. +KRN_SETTMO_JMP = $FFA2 code Unknown. (Set serial bus timeout.) +KRN_SETTMO = $FE21 code Unknown. (Set serial bus timeout.) +KRN_IECIN_JMP = $FFA5 code Read byte from serial bus. (Must call TALK and TALKSA beforehands.) +KRN_IECIN = $EE13 code Read byte from serial bus. (Must call TALK and TALKSA beforehands.) +KRN_IECOUT_JMP = $FFA8 code Write byte to serial bus. (Must call LISTEN and LSTNSA beforehands.) +KRN_IECOUT = $EDDD code Write byte to serial bus. (Must call LISTEN and LSTNSA beforehands.) +KRN_UNTALK_JMP = $FFAB code Send UNTALK command to serial bus. +KRN_UNTALK = $EDEF code Send UNTALK command to serial bus. +KRN_UNLSTN_JMP = $FFAE code Send UNLISTEN command to serial bus. +KRN_UNLSTN = $EDFE code Send UNLISTEN command to serial bus. +KRN_LISTEN_JMP = $FFB1 code Send LISTEN command to serial bus. +KRN_LISTEN = $ED0C code Send LISTEN command to serial bus. +KRN_TALK_JMP = $FFB4 code Send TALK command to serial bus. +KRN_TALK = $ED09 code Send TALK command to serial bus. +KRN_READST_JMP = $FFB7 code Fetch status of current input/output device, value of ST variable. (For RS232, status is cleared.) +KRN_READST = $FE07 code Fetch status of current input/output device, value of ST variable. (For RS232, status is cleared.) +KRN_SETLFS_JMP = $FFBA code Set file parameters. +KRN_SETLFS = $FE00 code Set file parameters. +KRN_SETNAM_JMP = $FFBD code Set file name parameters. +KRN_SETNAM = $FDF9 code Set file name parameters. +KRN_OPEN_JMP = $FFC0 code Open file. (Must call SETLFS and SETNAM beforehands.) +KRN_OPEN = $F34A code Open file. (Must call SETLFS and SETNAM beforehands.) +KRN_CLOSE_JMP = $FFC3 code Close file. +KRN_CLOSE = $F291 code Close file. +KRN_CHKIN_JMP = $FFC6 code Define file as default input. (Must call OPEN beforehands.) +KRN_CHKIN = $F20E code Define file as default input. (Must call OPEN beforehands.) +KRN_CHKOUT_JMP = $FFC9 code Define file as default output. (Must call OPEN beforehands.) +KRN_CHKOUT = $F250 code Define file as default output. (Must call OPEN beforehands.) +KRN_CLRCHN_JMP = $FFCC code Close default input/output files (for serial bus, send UNTALK and/or UNLISTEN); restore default input/output to keyboard/screen. +KRN_CLRCHN = $F333 code Close default input/output files (for serial bus, send UNTALK and/or UNLISTEN); restore default input/output to keyboard/screen. +KRN_CHRIN_JMP = $FFCF code Read byte from default input (for keyboard, read a line from the screen). (If not keyboard, must call OPEN and CHKIN beforehands.) +KRN_CHRIN = $F157 code Read byte from default input (for keyboard, read a line from the screen). (If not keyboard, must call OPEN and CHKIN beforehands.) +KRN_CHROUT_JMP = $FFD2 code Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.) +KRN_CHROUT = $F1CA code Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.) +KRN_LOAD_JMP = $FFD5 code Load or verify file. (Must call SETLFS and SETNAM beforehands.) +KRN_LOAD = $F49E code Load or verify file. (Must call SETLFS and SETNAM beforehands.) +KRN_SAVE_JMP = $FFD8 code Save file. (Must call SETLFS and SETNAM beforehands.) +KRN_SAVE = $F5DD code Save file. (Must call SETLFS and SETNAM beforehands.) +KRN_SETTIM_JMP = $FFDB code Set Time of Day, at memory address $00A0-$00A2. +KRN_SETTIM = $F6E4 code Set Time of Day, at memory address $00A0-$00A2. +KRN_RDTIM_JMP = $FFDE code read Time of Day, at memory address $00A0-$00A2. +KRN_RDTIM = $F6DD code read Time of Day, at memory address $00A0-$00A2. +KRN_STOP_JMP = $FFE1 code Query Stop key indicator, at memory address $0091; if pressed, call CLRCHN and clear keyboard buffer. +KRN_STOP = $F6ED code Query Stop key indicator, at memory address $0091; if pressed, call CLRCHN and clear keyboard buffer. +KRN_GETIN_JMP = $FFE4 code Read byte from default input. (If not keyboard, must call OPEN and CHKIN beforehands.) +KRN_GETIN = $F13E code Read byte from default input. (If not keyboard, must call OPEN and CHKIN beforehands.) +KRN_CLALL_JMP = $FFE7 code Clear file table; call CLRCHN. +KRN_CLALL = $F32F code Clear file table; call CLRCHN. +KRN_UDTIM_JMP = $FFEA code Update Time of Day, at memory address $00A0-$00A2, and Stop key indicator, at memory address $0091. +KRN_UDTIM = $F69B code Update Time of Day, at memory address $00A0-$00A2, and Stop key indicator, at memory address $0091. +KRN_SCREEN_JMP = $FFED code Fetch number of screen rows and columns. +KRN_SCREEN = $E505 code Fetch number of screen rows and columns. +KRN_PLOT_JMP = $FFF0 code Save or restore cursor position. +KRN_PLOT = $E50A code Save or restore cursor position. +KRN_IOBASE_JMP = $FFF3 code Fetch CIA #1 base address. +KRN_IOBASE = $E500 code Fetch CIA #1 base address. + +NMIVector = $fffa-$fffc pointers vector to NMI interrupt +ResetVector = $fffc-$fffe pointers vector to reset address +IntVector = $fffe-$10000 pointers vector to interrupt address + diff --git a/disassembler/x65dsasm.cpp b/disassembler/x65dsasm.cpp index 031b9c1..2eedead 100644 --- a/disassembler/x65dsasm.cpp +++ b/disassembler/x65dsasm.cpp @@ -1316,6 +1316,8 @@ void GetReferences(unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, i int end_addr = addr + (int)bytes; while (strref lab_line = labels.line()) { + if (lab_line.get_first()==';') + continue; strref name = lab_line.split_token_trim('='); if (lab_line.get_first()=='$') ++lab_line; @@ -1379,7 +1381,7 @@ void GetReferences(unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, i for (int i = 0; i=addr && (refs[i].address+refs[i].size)<=(addr+bytes)) { + if (refs[i].address>=addr && (refs[i].address+refs[i].size)<=(addr+(int)bytes)) { unsigned char *p = mem + refs[i].address - addr; for (int l = 0; l=refs[curr_label].address) { + was_data = refs[curr_label].data != DT_CODE; + curr_label++; + } + if (curr_label==refs.size()) + break; + if (was_data || refs[curr_label].separator) { + int skip = refs[curr_label].address-addr; + if (skip>(int)bytes) + break; + bytes -= skip; + addr += skip; + mem += skip; + } else { + unsigned char op = *mem++; + int curr = addr; + bytes--; + addr++; + if (opcodes == a65816_ops) { + if (op == 0xe2) { // sep + if ((*mem)&0x20) acc_16 = false; + if ((*mem)&0x10) ind_16 = false; + } else if (op == 0xc2) { // rep + if ((*mem)&0x20) acc_16 = true; + if ((*mem)&0x10) ind_16 = true; + } + } + + bool not_valid = opcodes[op].mnemonic==mnm_inv || (!ill_wdc && opcodes[op].mnemonic>=mnm_wdc_and_illegal_instructions); + int arg_size = not_valid ? 0 : opcodes[op].arg_size;; + int mode = not_valid ? AM_NON : opcodes[op].addrMode; + switch (mode) { + case AM_IMM_DBL_A: + arg_size = acc_16 ? 2 : 1; + break; + case AM_IMM_DBL_I: + arg_size = ind_16 ? 2 : 1; + break; + } + + addr += arg_size; + mem += arg_size; + if (arg_size > (int)bytes) + break; + bytes -= arg_size; + + if (op == 0x00 || op == 0x60 || op == 0x40 || op == 0x6b || + ((op == 0x4c || op == 0x6c) && (prev_op!=0x4c && prev_op!=0x6c)) || + op == 0x6c || op == 0x7c || op == 0x5c || op == 0xdc) { // rts, rti, rtl or jmp + refs[curr_label].separator = true; + int skip = refs[curr_label+1].address-addr; + if (skip>(int)bytes) + break; + bytes -= skip; + addr += skip; + mem += skip; + } + } + } + + // re-check for code references to code being mislabeled bool adjusted_code_ref = true; while (adjusted_code_ref) { @@ -1859,8 +1931,9 @@ void GetReferences(unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, i } } k = refs.begin(); + bool was_code = false; while (k!=refs.end()) { - if (k->data == DT_CODE && !k->label) { + if (k->data == DT_CODE && !k->label && (!was_code || k->separator)) { std::vector &links = *k->pRefs; bool definitely_code = false; for (std::vector::iterator l = links.begin(); l!=links.end(); ++l) { @@ -1873,6 +1946,7 @@ void GetReferences(unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, i if (!definitely_code) k->data = DT_DATA; } + was_code = k->data == DT_CODE; ++k; } } @@ -1900,8 +1974,195 @@ bool IsReadOnlyInstruction(MNM_Base op_base) return false; } +struct call_ref { + int address; + int target; + int seg_addr; + int seg_trg; + bool recursed; + RefType type; +}; + +struct seg_call { + int first_call_in_seg; + int num_calls_in_seg; + int address; +}; + + +typedef std::vector call_vector; +typedef std::vector seg_lookup; + +static int _sortCalls(const void *A, const void *B) +{ + return ((const struct call_ref*)A)->address - ((const struct call_ref*)B)->address; +} + +static int _sortInclusiveCalls(const void *A, const void *B) +{ + return ((const int*)B)[1] - ((const int*)A)[1]; +} + +static int countCalls(const int curr_ref, const call_ref *pCalls, const int num_calls, const seg_call *pLookup, const int num_lookup, char *visited) +{ + visited[curr_ref>>3] |= 1<<(curr_ref&7); + int ret = 1; + for (int j = 0; j>3] & 1<<(next_ref&7))==0) + ret += countCalls(next_ref, pCalls, num_calls, pLookup, num_lookup, visited); + } + return ret; +} + +static strown<256> _lblName; + +static const char *aRefStr[] = { + "-", //RT_NONE, + "branch", //RT_BRANCH, // bne, etc. + "long branch", //RT_BRA_L, // brl + "jmp", //RT_JUMP, // jmp + "jsr", //RT_JSR, // jsr + "data", //RT_DATA, // lda $... + "zp", //RT_ZP, // using a zero page / direct page instruction +}; + +static void printCalls(const int curr_ref, const call_ref *pCalls, const int num_calls, const seg_call *pLookup, const int num_lookup, char *visited, char *included, char *prefix, RefType rtype, FILE *f) +{ + RefAddr &ra = refs[curr_ref]; + if (ra.label) + _lblName.copy(ra.label); + else + _lblName.sprintf("%s_%d", ra.local ? ".l" : (ra.data==DT_CODE ? "Code" : + (ra.address>=0 && ra.address<0x100 ? "zp" : "Data")), ra.number); + + fprintf(f, "%s" STRREF_FMT " ($%x) [%s]%s\n", prefix, STRREF_ARG(_lblName), pLookup[curr_ref].address, aRefStr[rtype], (visited[curr_ref>>3] & 1<<(curr_ref&7)) ? " ...":""); + if (visited[curr_ref>>3] & 1<<(curr_ref&7)) + return; + visited[curr_ref>>3] |= 1<<(curr_ref&7); + included[curr_ref>>3] |= 1<<(curr_ref&7); + strcpy(prefix+strlen(prefix), " "); + for (int j = 0; j>3] & 1<<(next_ref&7)) == 0) { + RefType next_type = pCalls[pLookup[curr_ref].first_call_in_seg + j].type; + printCalls(next_ref, pCalls, num_calls, pLookup, num_lookup, visited, included, prefix, next_type, f); + } + } + prefix[strlen(prefix)-2] = 0; +} + + + +void CallGraph(int start, int end, bool branches, FILE *f) +{ + call_vector calls; + calls.reserve(refs.size() * 5); + std::vector call_counts; + call_counts.reserve(refs.size()); + seg_lookup lookup; + lookup.reserve(refs.size()); + + + int g = 0; + DataType prevType = DT_DATA; + for (int i = 0; i<(int)refs.size(); ++i) { + if (refs[i].data == DT_CODE) { + if (!refs[i].local || prevType != DT_CODE) + g = i; + int addr0 = refs[g].address; + int addr1 = (i+1)<(int)refs.size() ? refs[i+1].address : end; + if (std::vector *pLinks = refs[i].pRefs) { + for (std::vector::iterator l = pLinks->begin(); l!=pLinks->end(); ++l) { + if (l->instr_addr>=addr0 && l->instr_addrtype == RT_JSR || l->type==RT_JUMP || (branches && (l->type == RT_BRANCH || l->type==RT_BRA_L))) && + l->instr_addr>=start && l->instr_addrinstr_addr; + c.target = refs[i].address; + c.seg_trg = g; + c.seg_addr = -1; + c.type = l->type; + int jg = 0; + for (int j = 0; j<(int)refs.size(); ++j) { + if (!j || !refs[j].local) + jg = j; + if (c.address>=refs[j].address && ((j+1)==refs.size() || c.address=0) + call_counts[calls[i].seg_addr]++; + if (seg>3]&(1<<(n&7))) == 0 && refs[n].data==DT_CODE && !refs[n].local) { + memset(visited, 0, call8s); + printCalls(n, &calls[0], (int)calls.size(), &lookup[0], (int)lookup.size(), visited, included, prefix, RT_NONE, f); + } + } + } + fprintf(f, ";\n; DISASSEMBLY\n;\n"); + free(subcalls); + free(visited); + free(included); + } +} + static const char spacing[] = " "; -void Disassemble(strref filename, unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, int addr, bool ill_wdc, const dismnm *opcodes, bool src, int init_data, strref labels) +void Disassemble(strref filename, unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, int addr, bool ill_wdc, bool graph, bool graph_branches, const dismnm *opcodes, bool src, int init_data, strref labels) { const char *spc = src ? "" : spacing; @@ -1929,6 +2190,9 @@ void Disassemble(strref filename, unsigned char *mem, size_t bytes, bool acc_16, int reseperate = -1; + if (graph) + CallGraph(addr, addr+(int)bytes, graph_branches, f); + while (bytes) { // Determine if current address is referenced from somewhere while (curr_label_index<(int)refs.size() && addr >= refs[curr_label_index].address) { @@ -2024,7 +2288,7 @@ void Disassemble(strref filename, unsigned char *mem, size_t bytes, bool acc_16, out.sprintf_append(" ; $%04x " STRREF_FMT "\n", a, STRREF_ARG(refs[lbl].comment)); } else { RefAddr &ra = refs[lbl]; - out.sprintf_append("%s%s_%d: ; $%04x" STRREF_FMT "\n", spc, + out.sprintf_append("%s%s_%d ; $%04x" STRREF_FMT "\n", spc, ra.local ? ".l" : (ra.data==DT_CODE ? "Code" : (ra.address>=0 && ra.address<0x100 ? "zp" : "Data")), ra.number, a, STRREF_ARG(ra.comment)); } @@ -2105,7 +2369,7 @@ void Disassemble(strref filename, unsigned char *mem, size_t bytes, bool acc_16, int reference = -1; separator = false; - if (op == 0x60 || op == 0x40 || op == 0x6b || + if (op == 0x00 || op == 0x60 || op == 0x40 || op == 0x6b || ((op == 0x4c || op == 0x6c) && mem[arg_size] != 0x4c && mem[arg_size] != 0x6c) || op == 0x6c || op == 0x7c || op == 0x5c || op == 0xdc) { // rts, rti, rtl or jmp separator = true; @@ -2310,6 +2574,8 @@ int main(int argc, char **argv) bool src = false; bool prg = false; bool ill_wdc = false; + bool graph = false; + bool graph_branches = false; const dismnm *opcodes = a6502_ops; strref labels; @@ -2332,7 +2598,12 @@ int main(int argc, char **argv) src = true; else if (var.same_str("prg")) prg = true; - else if (!arg) { + else if (var.same_str("graph")) + graph = true; + else if (var.same_str("graph+bra")) { + graph = true; + graph_branches = true; + } else if (!arg) { if (!bin) bin = argv[i]; else if (!out) @@ -2393,7 +2664,7 @@ int main(int argc, char **argv) size_t bytes = size - skip; if (end && bytes > size_t(end - skip)) bytes = size_t(end - skip); - Disassemble(out, mem + skip, bytes, acc_16, ind_16, addr, ill_wdc, opcodes, src, data, labels); + Disassemble(out, mem + skip, bytes, acc_16, ind_16, addr, ill_wdc, graph, graph_branches, opcodes, src, data, labels); } free(mem); }