From bf2e281751071b16f6d311abcfe8c45701c58c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Henrik=20Sk=C3=A5rstedt?= Date: Tue, 17 Nov 2015 23:47:35 -0800 Subject: [PATCH] Resolved OMF Directpage+stack seg, Disassembler "smarter" - Using a section named DirectPage_Stack to adjust same named segment in OMF file - Resolving all zero page sections to fixed addresses before exporting OMF - Disassembler does more shenanigans to output more assemblable code - Automatically determine the code address for c64 prg files --- README.md | 7 ++ disassembler/README.MD | 4 +- disassembler/x65dsasm.cpp | 199 +++++++++++++++++++++++++++++++------- struse.h | 5 +- x65.cpp | 55 +++++++++-- 5 files changed, 222 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index a1d95bf..0c29cf2 100644 --- a/README.md +++ b/README.md @@ -254,6 +254,12 @@ Additional section directive styles include: For creating relocatable files (OMF) certain sections can not be fixed address. +Special sections for Apple II GS executables: + +Sections named DirectPage_Stack and of a BSS type (default) determine the size of the direct page + stack for the executable. If multiple sections match this rule the size will be the sum of all the sections with this name. + +Zeropage sections will be linked to a fixed address (default at the highest direct page addresses) prior to exporting the relocatable code. Zeropage sections in x65 is intended to allocate ranges of the zero page / direct page which is a bit confusing with OMF that has the concept of the direct page + stack segment. + **XDEF** Used in files assembled to object files to share a label globally. All labels that are not xdef'd are still processed but protected so that other objects can use the same label name without colliding. **XDEF ** must be specified before the label is defined, such as at the top of the file. @@ -945,6 +951,7 @@ Primarily tested with personal archive of sources written for Kick assmebler, DA * irp (indefinite repeat) **FIXED** +* Resolved the DirectPage_Stack section vs. Zeropage section for Apple II GS/OS executables. * OMF export for Apple II GS/OS executables * More DASM directives supported (ERR, DV, DS.B, DS.W, DS.L) * Removed the concept of linking by merging sections and instead keeping the sections separate and individually assigned memory addresses so they won't overlap. diff --git a/disassembler/README.MD b/disassembler/README.MD index d347628..7f27250 100644 --- a/disassembler/README.MD +++ b/disassembler/README.MD @@ -9,14 +9,16 @@ Typical command line ([*] = optional): **Usage** ``` -x65dsasm binary disasm.txt [$skip[-$end]] [addr=$xxxx] [cpu=6502/65C02/65816] [mx=0-3] [src] +x65dsasm binary disasm.txt [$skip[-$end]] [addr=$xxxx] [cpu=6502/65C02/65816] [mx=0-3] [src] [prg] ``` * binary: file which contains some 65xx series instructions * disasm.txt: output file (default is stdout) * $skip-$end: first byte offset to disassemble to last byte offset to disassemble * addr: disassemble as if loaded at addr +* prg: file is a c64 program file starting with the load address * cpu: set which cpu to disassemble for (default is 6502) +* src: export near assemblable source with guesstimated data blocks * mx: set the mx flags which control accumulator and index register size diff --git a/disassembler/x65dsasm.cpp b/disassembler/x65dsasm.cpp index e33a7e0..94b7b85 100644 --- a/disassembler/x65dsasm.cpp +++ b/disassembler/x65dsasm.cpp @@ -940,6 +940,7 @@ struct dismnm a65816_ops[256] = { enum RefType { RT_NONE, RT_BRANCH, // bne, etc. + RT_BRA_L, // brl RT_JUMP, // jmp RT_JSR, // jsr RT_DATA, // lda $... @@ -947,7 +948,7 @@ enum RefType { RT_COUNT }; -const char *aRefNames[RT_COUNT] = { "???", "branch", "jump", "subroutine", "data" }; +const char *aRefNames[RT_COUNT] = { "???", "branch", "long branch", "jump", "subroutine", "data" }; struct RefLink { int instr_addr; @@ -955,16 +956,14 @@ struct RefLink { }; struct RefAddr { - int address; // address + int address:30; // address + int data:2; // 1 if data, 0 if code std::vector *pRefs; // what is referencing this address - RefAddr() : address(-1), pRefs(nullptr) {} - RefAddr(int addr) : address(addr), pRefs(nullptr) {} + RefAddr() : address(-1), data(0), pRefs(nullptr) {} + RefAddr(int addr) : address(addr), data(0), pRefs(nullptr) {} }; -static const strref _jsr("jsr"); -static const strref _jmp("jmp"); - std::vector refs; static int _sortRefs(const void *A, const void *B) @@ -979,11 +978,24 @@ void GetReferences(unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, i refs.push_back(RefAddr(start_addr)); refs[0].pRefs = new std::vector(); + unsigned char *mem_orig = mem; + size_t bytes_orig = bytes; + int addr_orig = addr; + while (bytes) { 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; + } + } int arg_size = opcodes[op].arg_size;; int mode = opcodes[op].addrMode; @@ -1004,12 +1016,12 @@ void GetReferences(unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, i type = RT_BRANCH; } else if (mode == AM_BRANCH_L) { reference = curr + 2 + (short)(unsigned short)mem[0] + ((unsigned short)mem[1]<<8); - type = RT_BRANCH; + type = RT_BRA_L; } else if (mode == AM_ABS || mode == AM_ABS_Y || mode == AM_ABS_X || mode == AM_REL || mode == AM_REL_X || mode == AM_REL_L) { reference = (unsigned short)mem[0] + ((unsigned short)mem[1]<<8); - if (_jsr.same_str(opcodes[op].name)) + if (op == 0x20 || op == 0xfc || op == 0x22) // jsr opcodes type = RT_JSR; - else if (_jmp.same_str(opcodes[op].name)) + else if (op == 0x4c || op == 0x6c || op == 0x7c || op == 0x5c || op == 0xdc) // jmp opcodes type = RT_JUMP; else type = RT_DATA; @@ -1040,8 +1052,115 @@ void GetReferences(unsigned char *mem, size_t bytes, bool acc_16, bool ind_16, i break; bytes -= arg_size; } + + // sort the order of the labels by address if (refs.size()) qsort(&refs[0], refs.size(), sizeof(RefAddr), _sortRefs); + + // validate the label addresses + mem = mem_orig; + bytes = bytes_orig; + addr = addr_orig; + int curr_label = 0; + int prev_addr = -1; + while (bytes && curr_label refs[curr_label].address && prev_addr>=0) { + refs[curr_label].address = prev_addr; + curr_label++; + } + prev_addr = curr; + } + + // mark segments as code or data + mem = mem_orig; + bytes = bytes_orig; + addr = addr_orig; + bool separator = false; + bool was_data = false; + curr_label = 0; + while (bytes && curr_labelsize() && (*ref.pRefs)[0].type == RT_DATA) { + was_data = true; + for (int j = 1; was_data && jsize(); j++) + was_data = (*ref.pRefs)[0].type == RT_DATA; + } else + was_data = false; + refs[curr_label].data = was_data; + curr_label++; + } + + separator = false; + if (op == 0x60 || op == 0x40 || op == 0x68 || op == 0x4c || op == 0x6c || op == 0x7c || op == 0x5c || op == 0xdc) { // rts, rti, rtl or jmp + separator = true; + for (size_t i = 0; i &pRefs = *refs[i].pRefs; + if (refs[i].address<=curr) { + for (size_t j = 0; jcurr) { + separator = false; + break; + } + } + } else { + for (size_t j = 0; j out; while (bytes) { - // update label index? - while (curr_label_index < (int)(refs.size()-1) && addr >= refs[curr_label_index+1].address) { - curr_label_index++; - struct RefAddr &ref = refs[curr_label_index]; - if ((values_data || separator) && ref.pRefs && ref.pRefs->size() && (*ref.pRefs)[0].type == RT_DATA) { - values_data = true; - for (int j = 1; values_data && jsize(); j++) { - values_data = (*ref.pRefs)[0].type == RT_DATA; - } - } else - values_data = false; - } // Determine if current address is referenced from somewhere - if (addr == refs[curr_label_index].address) { + while (curr_label_indexsize(); ++j) { @@ -1106,21 +1213,23 @@ void Disassemble(strref filename, unsigned char *mem, size_t bytes, bool acc_16, fputs(out.c_str(), f); } } - out.sprintf("%sLabel_%d:\n", spc, curr_label_index); + out.sprintf("%sLabel_%d: ; $%04x\n", spc, curr_label_index, addr); fputs(out.c_str(), f); + is_data = !!refs[curr_label_index].data; + curr_label_index++; } - if (src && values_data) { + if (src && is_data) { out.clear(); int left = end_addr - addr; - if (curr_label_index < (int)(refs.size()-2)) - left = refs[curr_label_index+1].address - addr; + if (curr_label_index < (int)refs.size()) + left = refs[curr_label_index].address - addr; for (int i = 0; i &pRefs = *refs[i].pRefs; @@ -1204,11 +1312,15 @@ void Disassemble(strref filename, unsigned char *mem, size_t bytes, bool acc_16, reference = (int)mem[0] | ((int)mem[1])<<8; strown<64> lblname; - if (reference>=0) { + if (reference>=start_addr && reference<=end_addr) { for (size_t i = 0; i= refs[i].address) { + if (i==(refs.size()-1) || reference refs[i].address) + lblname.sprintf_append(" + $%x", reference - refs[i].address); + break; + } } } } @@ -1279,6 +1391,8 @@ void Disassemble(strref filename, unsigned char *mem, size_t bytes, bool acc_16, } if (!src && lblname) out.sprintf_append(" ; %s", lblname.c_str()); + else if (src && lblname) + out.sprintf_append(" ; $%4x", reference); mem += arg_size; out.append('\n'); @@ -1286,7 +1400,7 @@ void Disassemble(strref filename, unsigned char *mem, size_t bytes, bool acc_16, if (separator) { fputs("\n", f); fputs(spc, f); - fputs("; -------------------------------- ;\n\n", f); + fprintf(f, "; ------------- $%04x ------------- ;\n\n", addr); } } } @@ -1312,6 +1426,7 @@ int main(int argc, char **argv) bool acc_16 = true; bool ind_16 = true; bool src = false; + bool prg = false; const dismnm *opcodes = a6502_ops; @@ -1331,6 +1446,8 @@ int main(int argc, char **argv) strref var = arg.split_token('='); if (var.same_str("src")) src = true; + else if (var.same_str("prg")) + prg = true; else if (!arg) { if (!bin) bin = argv[i]; @@ -1367,6 +1484,12 @@ int main(int argc, char **argv) if (unsigned char *mem = (unsigned char*)malloc(size)) { fread(mem, size, 1, f); fclose(f); + if (prg) { + addr = mem[0] + ((int)mem[1]<<8); + skip += 2; + if (end) + end += 2; + } if (size > (size_t)skip && (end == 0 || end > skip)) { size_t bytes = size - skip; if (end && bytes > size_t(end - skip)) @@ -1381,7 +1504,9 @@ int main(int argc, char **argv) " * disasm.txt: output file (default is stdout)\n" " * $skip-$end: first byte offset to disassemble to last byte offset to disassemble\n" " * addr: disassemble as if loaded at addr\n" + " * prg: file is a c64 program file starting with the load address\n" " * cpu: set which cpu to disassemble for (default is 6502)\n" + " * src: export near assemblable source with guesstimated data blocks\n" " * mx: set the mx flags which control accumulator and index register size\n"); } return 0; diff --git a/struse.h b/struse.h index 2b83339..136e816 100644 --- a/struse.h +++ b/struse.h @@ -858,8 +858,9 @@ public: int sprintf_at(strl_t pos, const char *format, ...) { va_list args; va_start(args, format); int l = vsnprintf_s(charstr()+pos, cap()-pos, _TRUNCATE, format, args); if (l+pos>len()) set_len(l+pos); va_end(args); return l; } - int sprintf_append(const char *format, ...) { va_list args; va_start(args, format); - int l = vsnprintf_s(end(), cap()-len(), _TRUNCATE, format, args); va_end(args); add_len_int(l); return l; } + int sprintf_append(const char *format, ...) { va_list args; va_start(args, format); int l = 0; + if (len()= 0) type = ST_CODE; else if (name.find("data") >= 0) type = ST_DATA; - else if (name.find("bss") >= 0) type = ST_BSS; + else if (name.find("bss" >= 0 || name.same_str("directpage_stack"))) type = ST_BSS; else if (name.find("zp") >= 0 || name.find("zeropage") >= 0 || name.find("direct") >= 0) type = ST_ZEROPAGE; else type = ST_CODE; @@ -6370,14 +6370,33 @@ StatusCode Asm::WriteA2GS_OMF(strref filename, bool full_collapse) // collapse all section together that share the same name StatusCode status = MergeSectionsByName(first_section); + if (status != STATUS_OK) + return status; + + // Zero page section for x65 implies addresses, OMF direct-page/stack seg implies size of direct page + stack + // so resolve the zero page sections first + status = LinkZP(); + if (status != STATUS_OK) + return status; // full collapse means that all sections gets merged into one // code+data section and one bss section which will be appended - if (full_collapse) + if (full_collapse) { status = MergeAllSections(first_section); + if (status != STATUS_OK) + return status; + } - if (status != STATUS_OK) - return status; + // determine if there is a direct page stack + int DP_Stack_Size = 0; // 0 => default size (don't include) + for (std::vector
::iterator s = allSections.begin(); s != allSections.end(); ++s) { + if (s->type == ST_ZEROPAGE) + s->type = ST_REMOVED; + if (s->type == ST_BSS && s->name.same_str("directpage_stack")) { + DP_Stack_Size += s->addr_size(); + s->type = ST_REMOVED; + } + } std::vector SegNum; // order of valid segments std::vector SegLookup; // inverse of SegNum @@ -6419,13 +6438,14 @@ StatusCode Asm::WriteA2GS_OMF(strref filename, bool full_collapse) hdr.BankSize[2] = 1; // 64k banks _writeNBytes(hdr.DispNameOffset, 2, sizeof(hdr)); // start of file name (10 chars) + strref fileBase = export_base_name; + char segfile[10]; + memset(segfile, ' ', 10); + memcpy(segfile, fileBase.get(), fileBase.get_len() > 10 ? 10 : fileBase.get_len()); + for (std::vector::iterator i = SegNum.begin(); i != SegNum.end(); ++i) { Section &s = allSections[*i]; strref segName = s.name ? s.name : (s.type == ST_CODE ? strref("CODE") : strref("DATA")); - strref fileBase = export_base_name; - char segfile[10]; - memset(segfile, ' ', 10); - memcpy(segfile, fileBase.get(), fileBase.get_len() > 10 ? 10 : fileBase.get_len()); // support zero bytes at end of block int num_zeroes_at_end = s.addr_size() - s.size(); @@ -6528,6 +6548,25 @@ StatusCode Asm::WriteA2GS_OMF(strref filename, bool full_collapse) if (instruction_offs > 5) fwrite(instructions + 5, instruction_offs - 5, 1, f); // reloc instructions } + // if there is a size of the direct page & stack, write it + if (DP_Stack_Size) { + strref segName("DPStack"); + char lenSegName = segName.get_len(); + _writeNBytes(hdr.SegNum, 2, (int)SegNum.size()+1); + _writeNBytes(hdr.Kind, 2, 0x12); + _writeNBytes(hdr.DispDataOffset, 2, sizeof(hdr) + 10 + 1 + segName.get_len()); + _writeNBytes(hdr.Length, 4, DP_Stack_Size); + _writeNBytes(hdr.ResSpc, 4, DP_Stack_Size); + _writeNBytes(hdr.Align, 4, 256); + int segSize = sizeof(hdr) + 10 + 1 + segName.get_len() + 1; + _writeNBytes(hdr.SegTotal, 4, segSize); + instructions[0] = 0; + fwrite(&hdr, sizeof(hdr), 1, f); + fwrite(segfile, 10, 1, f); + fwrite(&lenSegName, 1, 1, f); + fwrite(segName.get(), segName.get_len(), 1, f); + fwrite(instructions, 1, 1, f); // end instruction + } free(instructions); fclose(f); return STATUS_OK;