1
0
mirror of https://github.com/ksherlock/x65.git synced 2024-06-12 07:29:28 +00:00

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
This commit is contained in:
Carl-Henrik Skårstedt 2015-12-16 22:19:25 -08:00
parent 219323d911
commit cac8f1e9cb
5 changed files with 463 additions and 9 deletions

Binary file not shown.

Binary file not shown.

View File

@ -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)

158
disassembler/c64kernal.lbl Normal file
View File

@ -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

View File

@ -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<last_user; ++i) {
if (refs[i].data==DT_PTRS || refs[i].data==DT_PTRS_DATA) {
int num = refs[i].size ? (refs[i].size/2) : ((refs[i+1].address - refs[i].address)/2);
if (refs[i].address>=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<num; l++) {
int a = p[0] + ((unsigned short)p[1]<<8);
@ -1676,7 +1678,7 @@ void GetReferences(unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, i
// after a separator if there is no jmp, jsr, brl begin data block
if (!was_data) {
if (op == 0x60 || op == 0x40 || op == 0x6b ||
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
bool exit_instr = false;
@ -1824,6 +1826,76 @@ void GetReferences(unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, i
++k;
}
bytes = bytes_orig;
addr = addr_orig;
mem = mem_orig;
curr_label = 0;
was_data = true;
// disassemble code sections again with all the labels filtered out to
// re-evaluate separators
while (bytes && curr_label<(int)refs.size()) {
while (curr_label<(int)refs.size() && addr>=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<RefLink> &links = *k->pRefs;
bool definitely_code = false;
for (std::vector<RefLink>::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_ref> call_vector;
typedef std::vector<seg_call> 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<pLookup[curr_ref].num_calls_in_seg; j++) {
int next_ref = pCalls[pLookup[curr_ref].first_call_in_seg + j].seg_trg;
if ((visited[next_ref>>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<pLookup[curr_ref].num_calls_in_seg; j++) {
int next_ref = pCalls[pLookup[curr_ref].first_call_in_seg + j].seg_trg;
if ((visited[next_ref>>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<int> 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<RefLink> *pLinks = refs[i].pRefs) {
for (std::vector<RefLink>::iterator l = pLinks->begin(); l!=pLinks->end(); ++l) {
if (l->instr_addr>=addr0 && l->instr_addr<addr1)
continue;
if ((l->type == RT_JSR || l->type==RT_JUMP || (branches && (l->type == RT_BRANCH || l->type==RT_BRA_L))) &&
l->instr_addr>=start && l->instr_addr<end) {
struct call_ref c;
c.address = l->instr_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<refs[j+1].address)) {
c.seg_addr = jg;
break;
}
}
if (c.seg_trg != c.seg_addr)
calls.push_back(c);
}
}
}
}
prevType = (DataType)refs[i].data;
}
if (refs.size())
qsort(&calls[0], calls.size(), sizeof(calls[0]), _sortCalls);
struct seg_call sg0 = { -1, -1, 0 };
for (int i = 0; i<(int)refs.size(); i++) {
sg0.address = refs[i].address;
call_counts.push_back(0);
lookup.push_back(sg0);
}
int seg = 0;
int call = 0;
for (int i = 0; i<(int)calls.size(); i++) {
if (calls[i].seg_addr>=0)
call_counts[calls[i].seg_addr]++;
if (seg<calls[i].seg_addr) {
seg = calls[i].seg_addr;
lookup[seg].first_call_in_seg = i;
}
lookup[seg].num_calls_in_seg = i-lookup[seg].first_call_in_seg+1;
}
if (lookup.size()) {
int call8s = ((int)lookup.size() + 7)/8;
char *visited = (char*)malloc(call8s);
char *included = (char*)malloc(call8s);
int *subcalls = (int*)malloc(2 * lookup.size() * sizeof(int));
memset(included, 0, call8s);
memset(subcalls, 0, lookup.size() * sizeof(int) * 2);
// count number of calls
for (int i = 0; i<(int)lookup.size(); i++) {
memset(visited, 0, call8s);
subcalls[i*2] = i;
subcalls[i*2+1] = refs[i].data==DT_CODE ? countCalls(i, &calls[0], (int)calls.size(), &lookup[0], (int)lookup.size(), visited) : 0;
}
qsort(subcalls, lookup.size(), sizeof(int)*2, _sortInclusiveCalls);
fprintf(f, ";\n; FUNCTION CALLING GRAPH\n;\n");
char prefix[512];
strcpy(prefix, "; ");
for (int i = 0; i<(int)lookup.size(); i++) {
if (subcalls[i*2+1]) {
int n = subcalls[i*2];
if ((included[n>>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);
}