diff --git a/.gitignore b/.gitignore index 145d8a0..ee2e9b3 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ *.obj.enc *.user *.db* +*.recipe test/results/* \ No newline at end of file diff --git a/README.md b/README.md index 29a954c..d9d1797 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ Primarily tested with personal archive of sources written for Kick assmebler, DA * irp (indefinite repeat) **FIXED** +* Source Debug output file including linkable object files, C64Debugger format * Adding MERGE directive, Label Pools rewrite, TEXT data can be indexed from a string symbol * Label Pools were destroyed after each scope so they did not work in include files which defeated their purpose. Label pools are now persistent through scopes. * Labels reserved from label pools now distinguish between global and local. Use [.!@$] as a prefix to reserve a local label from a label pool (previously always local) diff --git a/obj/x64Releasedump_x65/dump_x65.vcxproj.FileListAbsolute.txt b/obj/x64Releasedump_x65/dump_x65.vcxproj.FileListAbsolute.txt new file mode 100644 index 0000000..e69de29 diff --git a/obj/x64Releasex65/x65.vcxproj.FileListAbsolute.txt b/obj/x64Releasex65/x65.vcxproj.FileListAbsolute.txt new file mode 100644 index 0000000..e69de29 diff --git a/sln/dump_x65/dump_x65.vcxproj b/sln/dump_x65/dump_x65.vcxproj index 996b984..433d851 100644 --- a/sln/dump_x65/dump_x65.vcxproj +++ b/sln/dump_x65/dump_x65.vcxproj @@ -23,32 +23,32 @@ {57EFF4A4-7BF2-43F0-AD62-A79092DA67D1} Win32Proj dump_x65 - 10.0.15063.0 + 10.0 Application true - v141 + v142 NotSet Application false - v141 + v142 true NotSet Application true - v141 + v142 NotSet Application false - v141 + v142 true NotSet diff --git a/sln/x65.vcxproj b/sln/x65.vcxproj index 5568169..8ab13a3 100644 --- a/sln/x65.vcxproj +++ b/sln/x65.vcxproj @@ -23,32 +23,32 @@ {2823019A-A423-4A40-BB9C-5CE242019BD0} Win32Proj x65 - 10.0.15063.0 + 10.0 Application true - v141 + v142 NotSet Application false - v141 + v142 true NotSet Application true - v141 + v142 NotSet Application false - v141 + v142 true NotSet diff --git a/x65.cpp b/x65.cpp index cdabb0c..de97ecb 100644 --- a/x65.cpp +++ b/x65.cpp @@ -47,6 +47,7 @@ // Command line arguments static const strref cmdarg_listing("lst"); // -lst / -lst=(file.lst) : generate disassembly text from result(file or stdout) +static const strref cmdarg_srcdebug("srcdbg"); // -srcdbg : generate debug, -srcdbg=(file) : save debug file static const strref cmdarg_tass_listing("tsl"); // -tsl=(file) : generate listing file in TASS style static const strref cmdarg_tass_labels("tl"); // -tl=(file) : generate labels in TASS style static const strref cmdarg_allinstr("opcodes"); // -opcodes / -opcodes=(file.s) : dump all available opcodes(file or stdout) @@ -1133,6 +1134,13 @@ uint32_t FindLabelIndex(uint32_t hash, uint32_t *table, uint32_t count) return count; } +char* StringCopy(strref str) +{ + char* buf = (char*)calloc(1, (size_t)str.get_len() + 1); + if (buf && str.get_len()) { memcpy(buf, str.get(), str.get_len()); } + return buf; +} + // @@ -1431,7 +1439,7 @@ struct SourceDebugEntry { int size; int source_file_offset; // can be converted into line/column while linking }; -typedef std::vector SourceDebug; +typedef std::vector SourceDebug; enum SectionType : int8_t { // enum order indicates fixed address linking priority @@ -1513,6 +1521,8 @@ typedef struct Section { pRelocs = nullptr; if (pListing) delete pListing; pListing = nullptr; + if (pSrcDbg) delete pSrcDbg; + pSrcDbg = nullptr; } void Cleanup() { if (output) free(output); reset(); } @@ -1536,11 +1546,11 @@ typedef struct Section { void AddReloc(int base, int offset, int section, int8_t bytes, int8_t shift); Section() : pRelocs(nullptr), pListing(nullptr) { reset(); } - Section(strref _name, int _address) : pRelocs(nullptr), pListing(nullptr) { + Section(strref _name, int _address) : pRelocs(nullptr), pListing(nullptr), pSrcDbg(nullptr) { reset(); name = _name; start_address = load_address = address = _address; address_assigned = true; } - Section(strref _name) : pRelocs(nullptr), pListing(nullptr) { + Section(strref _name) : pRelocs(nullptr), pListing(nullptr), pSrcDbg(nullptr) { reset(); name = _name; start_address = load_address = address = 0; address_assigned = false; } @@ -1819,6 +1829,7 @@ public: int8_t cycle_counter_level; // merlin toggles the cycle counter rather than hierarchically evals bool error_encountered; // if any error encountered, don't export binary bool list_assembly; // generate assembler listing + bool src_debug; // generate source debug info bool end_macro_directive; // whether to use { } or macro / endmacro for macro scope bool import_means_xref; @@ -1835,6 +1846,9 @@ public: // Mimic TASS listing bool ListTassStyle( strref filename ); + // Export C64Debugger dbg xml file + bool SourceDebugExport(strref filename); + // Generate source for all valid instructions and addressing modes for current CPU bool AllOpcodes(strref filename); @@ -2034,9 +2048,7 @@ void SymbolStackTable::PushSymbol(StringSymbol* string) ValueOrString val; val.string = nullptr; if (string->string_value) { - val.string = (char*)malloc(string->string_value.get_len() + 1); - memcpy(val.string, string->string_value.get(), string->string_value.get_len()); - val.string[string->string_value.get_len()] = 0; + val.string = StringCopy(string->string_value.get_strref()); } (*ppStack)->push_back(val); } @@ -2143,8 +2155,12 @@ void Asm::Cleanup() { free(str.string_value.charstr()); } strings.clear(); - for (std::vector::iterator exti = externals.begin(); exti !=externals.end(); ++exti) + for (std::vector::iterator exti = externals.begin(); exti != externals.end(); ++exti) { exti->labels.clear(); + } + for (std::vector::iterator src = source_files.begin(); src != source_files.end(); ++src) { + free(*src); + } externals.clear(); // this section is relocatable but is assigned address $1000 if exporting without directives SetSection(strref("default,code")); @@ -2159,6 +2175,7 @@ void Asm::Cleanup() { directive_scope_depth = 0; error_encountered = false; list_assembly = false; + src_debug = false; end_macro_directive = false; import_means_xref = false; accumulator_16bit = false; // default 65816 8 bit immediate mode @@ -2921,6 +2938,20 @@ StatusCode Asm::MergeSections(int section_id, int section_merge) { delete m.pListing; m.pListing = nullptr; } + // go through source debug + if (m.pSrcDbg) { + if (!s.pSrcDbg) { s.pSrcDbg = new SourceDebug; } + if (s.pSrcDbg->capacity() < (m.pSrcDbg->size() + s.pSrcDbg->size())) { + s.pSrcDbg->reserve((m.pSrcDbg->size() + s.pSrcDbg->size())); + } + for (SourceDebug::iterator i = m.pSrcDbg->begin(); i != m.pSrcDbg->end(); ++i) { + SourceDebugEntry l = *i; + l.address += addr_start; + s.pSrcDbg->push_back(l); + } + delete m.pSrcDbg; + m.pSrcDbg = nullptr; + } m.type = ST_REMOVED; return STATUS_OK; } @@ -6707,24 +6738,43 @@ StatusCode Asm::BuildLine(strref line) { } } // update listing - if (error == STATUS_OK && list_assembly) { + if (error == STATUS_OK) { if (SectionId() == start_section) { Section &curr = CurrSection(); - if (!curr.pListing) { curr.pListing = new Listing; } - if (curr.pListing && curr.pListing->size()==curr.pListing->capacity()) { - curr.pListing->reserve(curr.pListing->size()+256); - } - if (((list_flags&(ListLine::KEYWORD|ListLine::CYCLES_START|ListLine::CYCLES_STOP)) || + if (list_assembly) { + if (!curr.pListing) { curr.pListing = new Listing; } + if (((list_flags & (ListLine::KEYWORD | ListLine::CYCLES_START | ListLine::CYCLES_STOP)) || (curr.address != start_address && curr.size())) && !curr.IsDummySection()) { - struct ListLine lst; - lst.address = start_address - curr.start_address; - lst.size = curr.address - start_address; - lst.code = contextStack.curr().source_file; - lst.column = (uint16_t)(data_line - code_line.get()); - lst.source_name = contextStack.curr().source_name; - lst.line_offs = int(code_line.get() - lst.code.get()); - lst.flags = list_flags; - curr.pListing->push_back(lst); + struct ListLine lst; + lst.address = start_address - curr.start_address; + lst.size = curr.address - start_address; + lst.code = contextStack.curr().source_file; + lst.column = (uint16_t)(data_line - code_line.get()); + lst.source_name = contextStack.curr().source_name; + lst.line_offs = int(code_line.get() - lst.code.get()); + lst.flags = list_flags; + curr.pListing->push_back(lst); + } + } + if (src_debug) { + if (!curr.pSrcDbg) { curr.pSrcDbg = new SourceDebug; } + if (curr.address != start_address && curr.size()) { + SourceDebugEntry entry; + entry.address = start_address - curr.start_address; + entry.size = curr.address - start_address; + + size_t sf = 0, nsf = source_files.size(); + strref src = contextStack.curr().source_name; + for (; sf < nsf; ++sf) { + if (src.same_str_case(source_files[sf])) { break; } + } + if (sf == nsf) { + source_files.push_back(StringCopy(src)); + } + entry.source_file_index = (int)sf; + entry.source_file_offset = (int)(data_line - contextStack.curr().source_file.get()); + curr.pSrcDbg->push_back(entry); + } } } } @@ -6888,6 +6938,95 @@ bool Asm::ListTassStyle( strref filename ) { return true; } +bool Asm::SourceDebugExport(strref filename) { + FILE* f = stdout; + bool opened = false; + if (filename) { + f = fopen(strown<512>(filename).c_str(), "w"); + if (!f) { return false; } + opened = true; + } else { + return false; + } + + std::vector source_code; source_code.reserve(source_files.size()); + + fprintf(f, "\n\t\n"); + for (size_t i = 0, n = source_files.size(); i < n; ++i) { + fprintf(f, "\t\t%d,%s\n", (int)i+1, source_files[i]); + + size_t size = 0; + char* src = LoadText(source_files[i], size); + source_code.push_back(strovl(src, (strl_t)size, (strl_t)size)); + } + fprintf(f, "\t\n\n"); + + for (size_t i = 0, n = allSections.size(); i < n; ++i) { + Section& s = allSections[i]; + if (s.pSrcDbg && s.pSrcDbg->size()) { + fprintf(f, "\t\n\t\t\n", + STRREF_ARG(s.name)); + for (size_t d = 0, nd = s.pSrcDbg->size(); d < nd; ++d) { + SourceDebugEntry& e = s.pSrcDbg->at(d); + int line = 0, col0 = 0, col1 = 0; + if ((e.source_file_index) < source_code.size()) { + strref src = source_code[e.source_file_index].get_strref(); + if (src.get_len() > strl_t(e.source_file_offset)) { + line = strref(src.get(), e.source_file_offset).count_lines(); + strl_t offs = e.source_file_offset; + while (src.get_at(offs) != 0x0a && src.get_at(offs) != 0x0d && offs) { --offs; ++col0; } + col1 = col0; + offs = e.source_file_offset; + while (src.get_at(offs) != 0x0a && src.get_at(offs) != 0x0d && offs\n\t\n\n"); + } + } + + fprintf(f, "\t\n"); + for (MapSymbolArray::iterator i = map.begin(); i != map.end(); ++i) { + if (i->name.same_str("debugbreak")) { continue; } + uint32_t value = (uint32_t)i->value; + strref sectName; + if (size_t(i->section) < allSections.size()) { + value += allSections[i->section].start_address; + sectName = allSections[i->section].name; + } + fprintf(f, "\t\t" STRREF_FMT ",$%04x," STRREF_FMT ",0,0,0,0,0\n", STRREF_ARG(sectName), value, STRREF_ARG(i->name)); + } + fprintf(f, "\t\n\n"); + + fprintf(f, "\t\n"); + for (MapSymbolArray::iterator i = map.begin(); i != map.end(); ++i) { + uint32_t value = (uint32_t)i->value; + strref sectName; + if (size_t(i->section) < allSections.size()) { + value += allSections[i->section].start_address; + sectName = allSections[i->section].name; + } + if (i->name.same_str("debugbreak")) { + fprintf(f, "\t\t" STRREF_FMT ",$%04x,\n", STRREF_ARG(sectName), value); + } + } + fprintf(f, "\t\n\n"); + + fprintf(f, "\t\n"); + fprintf(f, "\t\n\n"); + + fprintf(f, "\n"); + fclose(f); + + for (size_t i = 0, n = source_code.size(); i < n; ++i) { + if (source_code[i].get()) { free(source_code[i].charstr()); } + } + return true; +} + bool Asm::List(strref filename) { FILE *f = stdout; bool opened = false; @@ -7205,6 +7344,7 @@ struct ObjFileHeader { int16_t late_evals; int16_t map_symbols; uint32_t stringdata; + uint32_t srcdebug; int bindata; }; @@ -7224,7 +7364,7 @@ struct ObjFileSection { int end_address; // address size int output_size; // assembled binary size int align_address; - int list_count; // how many addresses included in debugger listing + int srcdebug_count; // how many addresses included in debugger listing int16_t next_group; // next section of group int16_t first_group; // first section of group int16_t relocs; @@ -7279,10 +7419,11 @@ struct ObjFileMapSymbol { // this struct is follwed by numSources x ObjFileStr struct ObjFileSourceList { uint32_t numSources; + ObjFileStr sourceFile[1]; }; // after that one long array of all sections worth of source references -struct ObjFileSrcRef { +struct ObjFileSrcDbg { uint16_t addr; // relative to section uint16_t src_idx; uint16_t size; @@ -7360,6 +7501,7 @@ StatusCode Asm::WriteObjectFile(strref filename) { hdr.late_evals = (int16_t)lateEval.size(); hdr.map_symbols = (int16_t)map.size(); hdr.stringdata = 0; + hdr.srcdebug = 0; // labels don't include XREF labels hdr.labels = 0; @@ -7380,7 +7522,7 @@ StatusCode Asm::WriteObjectFile(strref filename) { char *stringPool = nullptr; uint32_t stringPoolCap = 0; pairArray stringArray; - stringArray.reserve(hdr.labels * 2 + hdr.sections + hdr.late_evals*2); + stringArray.reserve(hdr.labels * 2 + hdr.sections + hdr.late_evals*2 + (uint32_t)source_files.size()); struct ObjFileSection *aSects = hdr.sections ? (struct ObjFileSection*)calloc(hdr.sections, sizeof(struct ObjFileSection)) : nullptr; struct ObjFileReloc *aRelocs = hdr.relocs ? (struct ObjFileReloc*)calloc(hdr.relocs, sizeof(struct ObjFileReloc)) : nullptr; @@ -7398,11 +7540,11 @@ StatusCode Asm::WriteObjectFile(strref filename) { } sect = 0; + uint32_t srcDbgEntries = 0; // write out sections and relocs if (hdr.sections) { for (std::vector
::iterator si = allSections.begin(); si!=allSections.end(); ++si) { - if (si->type == ST_REMOVED) - continue; + if (si->type == ST_REMOVED) { continue; } struct ObjFileSection &s = aSects[sect++]; s.name.offs = _AddStrPool(si->name, &stringArray, &stringPool, hdr.stringdata, stringPoolCap); s.exp_app.offs = _AddStrPool(si->export_append, &stringArray, &stringPool, hdr.stringdata, stringPoolCap); @@ -7411,6 +7553,8 @@ StatusCode Asm::WriteObjectFile(strref filename) { s.next_group = si->next_group >= 0 ? aRemapSects[si->next_group] : -1; s.first_group = si->first_group >= 0 ? aRemapSects[si->first_group] : -1; s.relocs = si->pRelocs ? (int16_t)(si->pRelocs->size()) : 0; + s.srcdebug_count = si->pSrcDbg ? (int)si->pSrcDbg->size() : 0; + srcDbgEntries += s.srcdebug_count; s.start_address = si->start_address; s.end_address = si->address; s.type = si->type; @@ -7432,6 +7576,25 @@ StatusCode Asm::WriteObjectFile(strref filename) { } hdr.sections = (int16_t)sect; + struct ObjFileSrcDbg* srcLines = srcDbgEntries ? (ObjFileSrcDbg*)calloc(1, sizeof(ObjFileSrcDbg) * srcDbgEntries) : nullptr; + if (srcLines) { + size_t srcDbgIdx = 0; + for (std::vector
::iterator si = allSections.begin(); si != allSections.end(); ++si) { + if (si->type == ST_REMOVED) { continue; } + if (si->pSrcDbg && si->pSrcDbg->size()) { + for (size_t src = 0, nsrc = si->pSrcDbg->size(); src < nsrc; ++src) { + SourceDebugEntry& entry = si->pSrcDbg->at(src); + ObjFileSrcDbg& ref = srcLines[srcDbgIdx++]; + ref.addr = entry.address; + ref.src_idx = entry.source_file_index; + ref.size = entry.size; + ref.file_offs = entry.source_file_offset; + } + } + } + assert(srcDbgIdx == srcDbgEntries); + } + // write out labels if (hdr.labels) { for (uint32_t li = 0; linumSources = (uint32_t)source_files.size(); + for (size_t src = 0, nsrc = source_files.size(); src < nsrc; ++src) { + sourceList->sourceFile[src].offs = _AddStrPool(strref(source_files[src]), &stringArray, &stringPool, hdr.stringdata, stringPoolCap); + } + } + + hdr.srcdebug = srcDbgEntries; + // write out the file fwrite(&hdr, sizeof(hdr), 1, f); fwrite(aSects, sizeof(aSects[0]), sect, f); @@ -7511,6 +7685,12 @@ StatusCode Asm::WriteObjectFile(strref filename) { fwrite(si->output, si->size(), 1, f); } } + if (sourceList) { + fwrite(sourceList, sizeof(ObjFileSourceList) + sizeof(ObjFileStr) * (source_files.size() - 1), 1, f); + fwrite(srcLines, sizeof(ObjFileSrcDbg) * srcDbgEntries, 1, f); + } + + // done with I/O fclose(f); if (aRemapSects) { free(aRemapSects); } @@ -7539,6 +7719,14 @@ StatusCode Asm::ReadObjectFile(strref filename, int link_to_section) hdr.relocs * sizeof(struct ObjFileReloc) + hdr.labels * sizeof(struct ObjFileLabel) + hdr.late_evals * sizeof(struct ObjFileLateEval) + hdr.map_symbols * sizeof(struct ObjFileMapSymbol) + hdr.stringdata + hdr.bindata; + const struct ObjFileSourceList* pSrcDbgInfo = hdr.srcdebug ? (struct ObjFileSourceList*)(data + sum) : nullptr; + const struct ObjFileSrcDbg* pSrcDbgEntry = pSrcDbgInfo ? (const struct ObjFileSrcDbg*)(pSrcDbgInfo->sourceFile + pSrcDbgInfo->numSources) : nullptr; + + if (pSrcDbgInfo) { + sum += sizeof(ObjFileSourceList) + (pSrcDbgInfo->numSources - 1) * sizeof(ObjFileStr); + sum += sizeof(ObjFileSrcDbg) * hdr.srcdebug; + } + if (hdr.id == 0x7836 && sum == size) { struct ObjFileSection *aSect = (struct ObjFileSection*)(&hdr + 1); struct ObjFileReloc *aReloc = (struct ObjFileReloc*)(aSect + hdr.sections); @@ -7552,6 +7740,23 @@ StatusCode Asm::ReadObjectFile(strref filename, int link_to_section) memcpy(str_pool, str_orig, hdr.stringdata); loadedData.push_back(str_pool); + // source files + std::vector source_file_remap; + if (pSrcDbgInfo) { + source_file_remap.reserve(pSrcDbgInfo->numSources); + for (size_t s = 0, n = pSrcDbgInfo->numSources; s < n; ++s) { + strref source_file(str_pool + pSrcDbgInfo->sourceFile[s].offs); + size_t i = 0, sz = source_files.size(); + for (; i < sz; ++i) { + if (source_file.same_str(source_files[i])) { break; } + } + if (i == sz) { + source_files.push_back(StringCopy(source_file)); + } + source_file_remap.push_back(i); + } + } + int prevSection = SectionId(); int16_t *aSctRmp = (int16_t*)malloc(hdr.sections * sizeof(int16_t)); int last_linked_section = link_to_section; @@ -7596,6 +7801,19 @@ StatusCode Asm::ReadObjectFile(strref filename, int link_to_section) s.first_group = allSections[last_linked_section].first_group >=0 ? allSections[last_linked_section].first_group : last_linked_section; last_linked_section = SectionId(); } + // add source debug entries from object file + if (aSect[si].srcdebug_count) { + if (!s.pSrcDbg) { s.pSrcDbg = new SourceDebug; } + for (int sd = 0, nsd = aSect[si].srcdebug_count; sd < nsd; ++sd) { + SourceDebugEntry entry; + entry.address = pSrcDbgEntry->addr; + entry.size = pSrcDbgEntry->size; + entry.source_file_index = (int)source_file_remap[pSrcDbgEntry->src_idx]; + entry.source_file_offset = pSrcDbgEntry->file_offs; + s.pSrcDbg->push_back(entry); + ++pSrcDbgEntry; + } + } } aSctRmp[si] = (int16_t)allSections.size()-1; } @@ -7978,6 +8196,7 @@ int main(int argc, char **argv) { const char *sym_file = nullptr, *vs_file = nullptr, *cmdarg_tass_labels_file = nullptr; strref list_file, allinstr_file; strref tass_list_file; + strref srcdebug_file; for (int a = 1; a