From a4d6c3efbfc4d647db3e036b64c82c549c2ba5d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Henrik=20Sk=C3=A5rstedt?= Date: Sun, 1 Nov 2015 15:08:38 -0800 Subject: [PATCH] Cycle timing listing for 65816 - XREF blocked linking with same name label - REPT also works as a symbols for repeat counter to reduce need for extra counting labels. - "<<" got interpreted as a right shift - Added LONG keyword to declare 32 bit values --- README.md | 21 +++++-- x65.cpp | 164 +++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 139 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index d16939a..0c1d802 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,7 @@ Directives are assembler commands that control the code generation but that does * [**EVAL**](#eval) Log an expression during assembly. * [**BYTES**](#bytes) Insert comma separated bytes at this address (same as **BYTE** or **DC.B**) * [**WORDS**](#words) Insert comma separated 16 bit values at this address (same as **WORD** or **DC.W**) +* [**LONG**](#long) Insert comma separated 32 bit values at this address * [**TEXT**](#text) Insert text at this address * [**INCLUDE**](#include) Include another source file and assemble at this address * [**INCBIN**](#incbin) Include a binary file at this address @@ -311,6 +312,10 @@ RandomBytes: Adds comma separated 16 bit values similar to how **BYTES** work. **word** or **dc.w** are also recognized. +**LONGS** + +Adds comma separated 32 bit values similar to how **WORDS** work. + **TEXT** Copies the string in quotes on the same line. The plan is to do a petscii conversion step. Use the modifier 'petscii' or 'petscii_shifted' to convert alphabetic characters to range. @@ -493,11 +498,9 @@ Example: ``` columns = 40 rows = 25 -screen_col = $400 -height_buf = $1000 rept columns { - screen_addr = screen_col - ldx height_buf + screen_addr = $400 + rept ; rept is the repeat counter + ldx $1000+rept dest = screen_addr remainder = 3 rept (rows+remainder)/4 { @@ -514,8 +517,6 @@ rept columns { dest = dest + 4*40 } } - screen_col = screen_col+1 - height_buf = height_buf+1 } ``` @@ -527,6 +528,9 @@ Note that if the -endm command line option is used (macros are not defined with .ENDREPEAT ``` +The symbol 'REPT' is the current repeat count within a REPT (0 outside of repeat blocks). + + **INCDIR** Adds a folder to search for INCLUDE, INCBIN, etc. files in @@ -821,6 +825,11 @@ Fish food! Assembler has all important features and switching to make a 6502 pro * irp (indefinite repeat) **FIXED** +* XREF prevented linking with same name symbol included from .x65 object causing a linker failure +* << was mistakenly interpreted as shift right +* REPT is also a value that can be used in expressions as a repeat counter +* LONG allows for adding 4 byte values like BYTE/WORD +* Cycle listing for 65816 - required more complex visualization due to more than 1 cycle extra * Enabled EXT and CYC directive for merlin, accumulating cycle timing within scope (Hierarchical cycle timing in list file) / CYC...CYC (6502 only for now) * First pass cycle timing in listing file for 6502 targets * Enums can have comments diff --git a/x65.cpp b/x65.cpp index 15358de..71bebe7 100644 --- a/x65.cpp +++ b/x65.cpp @@ -69,6 +69,7 @@ // minor variation of 65C02 #define NUM_WDC_65C02_SPECIFIC_OPS 18 + // To simplify some syntax disambiguation the preferred // ruleset can be specified on the command line. enum AsmSyntax { @@ -756,6 +757,37 @@ const char* aliases_65816[] = { static const int num_opcodes_65816 = sizeof(opcodes_65816) / sizeof(opcodes_65816[0]); +unsigned char timing_65816[] = { + 0x4e, 0x1c, 0x4e, 0x28, 0x3a, 0x26, 0x3a, 0x1c, 0x46, 0x24, 0x44, 0x48, 0x4c, 0x28, 0x5c, 0x2a, + 0x44, 0x1a, 0x1a, 0x2e, 0x3a, 0x18, 0x6c, 0x1c, 0x44, 0x28, 0x44, 0x44, 0x4c, 0x28, 0x5e, 0x2a, + 0x4c, 0x1c, 0x50, 0x28, 0x16, 0x26, 0x3a, 0x1c, 0x48, 0x24, 0x44, 0x4a, 0x28, 0x28, 0x4c, 0x2a, + 0x44, 0x1a, 0x1a, 0x2e, 0x18, 0x18, 0x3c, 0x1c, 0x44, 0x28, 0x44, 0x44, 0x28, 0x28, 0x4e, 0x2a, + 0x4c, 0x1c, 0x42, 0x28, 0x42, 0x16, 0x6a, 0x1c, 0x26, 0x24, 0x44, 0x46, 0x46, 0x28, 0x5c, 0x2a, + 0x44, 0x1a, 0x1a, 0x2e, 0x42, 0x18, 0x6c, 0x1c, 0x44, 0x28, 0x76, 0x44, 0x48, 0x28, 0x5e, 0x2a, + 0x4c, 0x1c, 0x4c, 0x28, 0x16, 0x26, 0x3a, 0x1c, 0x28, 0x24, 0x44, 0x4c, 0x4a, 0x28, 0x4c, 0x2a, + 0x44, 0x1a, 0x1a, 0x2e, 0x28, 0x18, 0x3c, 0x1c, 0x44, 0x28, 0x78, 0x44, 0x4c, 0x28, 0x4e, 0x2a, + 0x46, 0x1c, 0x48, 0x28, 0x86, 0x16, 0x86, 0x1c, 0x44, 0x24, 0x44, 0x46, 0x78, 0x28, 0x78, 0x2a, + 0x44, 0x1c, 0x1a, 0x2e, 0x88, 0x18, 0x88, 0x1c, 0x44, 0x2a, 0x44, 0x44, 0x28, 0x2a, 0x2a, 0x2a, + 0x74, 0x1c, 0x74, 0x28, 0x86, 0x16, 0x86, 0x1c, 0x44, 0x24, 0x44, 0x48, 0x78, 0x28, 0x78, 0x2a, + 0x44, 0x1a, 0x1a, 0x2e, 0x88, 0x18, 0x88, 0x1c, 0x44, 0x28, 0x44, 0x44, 0x78, 0x28, 0x78, 0x2a, + 0x74, 0x1c, 0x46, 0x28, 0x86, 0x16, 0x6a, 0x1c, 0x44, 0x24, 0x44, 0x26, 0x78, 0x28, 0x5c, 0x2a, + 0x44, 0x1a, 0x1a, 0x2e, 0x4c, 0x18, 0x6c, 0x1c, 0x44, 0x28, 0x76, 0x46, 0x4c, 0x28, 0x5e, 0x2a, + 0x74, 0x3c, 0x46, 0x48, 0x86, 0x36, 0x6a, 0x3c, 0x44, 0x44, 0x44, 0x46, 0x78, 0x48, 0x5c, 0x4a, + 0x44, 0x3a, 0x3a, 0x4e, 0x4a, 0x38, 0x6c, 0x3c, 0x44, 0x48, 0x78, 0x44, 0x50, 0x48, 0x5e, 0x4a +}; + +// m=0, i=0, dp!=0 +unsigned char timing_65816_plus[9][3] = { + { 0, 0, 0 }, // 6502 plus timing check bit 0 + { 1, 0, 1 }, // acc 16 bit + dp!=0 + { 1, 0, 0 }, // acc 16 bit + { 0, 0, 1 }, // dp != 0 + { 0, 0, 0 }, // no plus + { 2, 0, 0 }, // acc 16 bit yields 2+ + { 2, 0, 1 }, // acc 16 bit yields 2+ + dp!=0 + { 0, 1, 0 }, // idx 16 bit + { 0, 1, 1 } // idx 16 bit + dp!=0 +}; // 65C02 // http://6502.org/tutorials/65c02opcodes.html @@ -798,7 +830,7 @@ struct CPUDetails { { opcodes_6502, num_opcodes_6502, "6502ill", aliases_6502, timing_6502 }, { opcodes_65C02, num_opcodes_65C02 - NUM_WDC_65C02_SPECIFIC_OPS, "65C02", aliases_65C02, nullptr }, { opcodes_65C02, num_opcodes_65C02, "65C02WDC", aliases_65C02, nullptr }, - { opcodes_65816, num_opcodes_65816, "65816", aliases_65816, nullptr }, + { opcodes_65816, num_opcodes_65816, "65816", aliases_65816, timing_65816 }, }; static const int nCPUs = sizeof(aCPUs) / sizeof(aCPUs[0]); @@ -872,6 +904,7 @@ DirectiveName aDirectiveNames[] { { "BYTES", AD_BYTES }, { "WORD", AD_WORDS }, { "WORDS", AD_WORDS }, + { "LONG", AD_ADRL }, { "DC", AD_DC }, { "TEXT", AD_TEXT }, { "INCLUDE", AD_INCLUDE }, @@ -1295,6 +1328,7 @@ typedef struct { strref read_source; // current position/length in source file strref next_source; // next position/length in source file short repeat; // how many times to repeat this code segment + short repeat_total; // initial number of repeats for this code segment bool scoped_context; void restart() { read_source = code_segment; } bool complete() { repeat--; return repeat <= 0; } @@ -1318,6 +1352,7 @@ public: context.read_source = code_seg; context.next_source = code_seg; context.repeat = rept; + context.repeat_total = rept; context.scoped_context = false; stack.push_back(context); currContext = &stack[stack.size()-1]; @@ -2590,6 +2625,7 @@ EvalOperator Asm::RPNToken_Merlin(strref &expression, const struct EvalContext & if (ret == STATUS_OK) return EVOP_VAL; if (ret != STATUS_NOT_STRUCT) return EVOP_ERR; // partial struct } + if (!pLabel && label.same_str("rept")) { value = contextStack.curr().repeat_total - contextStack.curr().repeat; return EVOP_VAL; } if (!pLabel || !pLabel->evaluated) return EVOP_NRY; // this label could not be found (yet) value = pLabel->value; section = pLabel->section; return EVOP_VAL; } else @@ -2619,7 +2655,7 @@ EvalOperator Asm::RPNToken(strref &exp, const struct EvalContext &etx, EvalOpera if (prev_op == EVOP_VAL || prev_op == EVOP_RPR) { ++exp; if (exp[0] == '=') { ++exp; return EVOP_GTE; } return EVOP_GT; } ++exp; return EVOP_HIB; - case '<': if (exp.get_len() >= 2 && exp[1] == '<') { exp += 2; return EVOP_SHR; } + case '<': if (exp.get_len() >= 2 && exp[1] == '<') { exp += 2; return EVOP_SHL; } if (prev_op == EVOP_VAL || prev_op == EVOP_RPR) { ++exp; if (exp[0] == '=') { ++exp; return EVOP_LTE; } return EVOP_LT; } ++exp; return EVOP_LOB; @@ -2654,6 +2690,7 @@ EvalOperator Asm::RPNToken(strref &exp, const struct EvalContext &etx, EvalOpera if (ret == STATUS_OK) return EVOP_VAL; if (ret != STATUS_NOT_STRUCT) return EVOP_ERR; // partial struct } + if (!pLabel && label.same_str("rept")) { value = contextStack.curr().repeat_total - contextStack.curr().repeat; return EVOP_VAL; } if (!pLabel || !pLabel->evaluated) return EVOP_NRY; // this label could not be found (yet) value = pLabel->value; section = pLabel->section; return pLabel->reference ? EVOP_XRF : EVOP_VAL; } @@ -4945,6 +4982,45 @@ StatusCode Asm::BuildSegment() // Produce the assembler listing #define MAX_DEPTH_CYCLE_COUNTER 64 + +struct cycleCnt { + int base; + short plus, a16, x16, dp; + void clr() { base = 0; plus = a16 = x16 = dp = 0; } + void add(unsigned char c) { + if (c != 0xff) { + base += (c >> 1) & 7; + plus += c & 1; + if (c & 0xf0) { + int i = c >> 4; + if (i <= 8) { + a16 += timing_65816_plus[i][0]; + x16 += timing_65816_plus[i][1]; + dp += timing_65816_plus[i][2]; + } + } + } + } + int plus_acc() { + return plus + a16 + x16 + dp; + } + void combine(const struct cycleCnt &o) { + base += o.base; plus += o.plus; a16 += o.a16; x16 += o.x16; dp += o.dp; + } + bool complex() const { return a16 != 0 || x16 != 0 || dp != 0; } + static int get_base(unsigned char c) { + return (c & 0xf) >> 1; + } + static int sum_plus(unsigned char c) { + if (c == 0xff) + return 0; + int i = c >> 4; + if (i) + return i <= 8 ? (timing_65816_plus[i][0] + timing_65816_plus[i][1] + timing_65816_plus[i][2]) : 0; + return c & 1; + } +}; + bool Asm::List(strref filename) { FILE *f = stdout; @@ -4977,11 +5053,9 @@ bool Asm::List(strref filename) } } - short cycles[MAX_DEPTH_CYCLE_COUNTER][2]; + struct cycleCnt cycles[MAX_DEPTH_CYCLE_COUNTER]; short cycles_depth = 0; - - cycles[0][0] = 0; - cycles[0][1] = 0; + memset(cycles, 0, sizeof(cycles)); strref prev_src; int prev_offs = 0; @@ -5005,7 +5079,7 @@ bool Asm::List(strref filename) if (line_fix[pos] == '\t') line_fix.exchange(pos, 1, pos & 1 ? strref(" ") : strref(" ")); } - out.append_to(' ', aCPUs[cpu].timing ? 36 : 33); + out.append_to(' ', aCPUs[cpu].timing ? 40 : 33); out.append(line_fix.get_strref()); fprintf(f, STRREF_FMT "\n", STRREF_ARG(out)); out.clear(); @@ -5024,14 +5098,18 @@ bool Asm::List(strref filename) out.sprintf_append("%02x ", si->output[lst.address + b]); } if (lst.startClock() && cycles_depth%d", cycles_depth); + cycles_depth++; cycles[cycles_depth].clr(); + out.append_to(' ', 6); out.sprintf_append("c>%d", cycles_depth); } if (lst.stopClock()) { - out.append_to(' ', 25); out.sprintf_append("c<%d=%d+%d", cycles_depth, cycles[cycles_depth][0], cycles[cycles_depth][1]); + out.append_to(' ', 6); + if (cycles[cycles_depth].complex()) + out.sprintf_append("c<%d = %d + m%d + i%d + d%d", cycles_depth, cycles[cycles_depth].base, cycles[cycles_depth].a16, cycles[cycles_depth].x16, cycles[cycles_depth].dp); + else + out.sprintf_append("c<%d = %d + %d", cycles_depth, cycles[cycles_depth].base, cycles[cycles_depth].plus_acc()); if (cycles_depth) { - cycles_depth--; cycles[cycles_depth][0] += cycles[cycles_depth + 1][0]; - cycles[cycles_depth][1] += cycles[cycles_depth + 1][1]; + cycles_depth--; + cycles[cycles_depth].combine(cycles[cycles_depth + 1]); } } if (lst.size && lst.wasMnemonic()) { @@ -5064,15 +5142,19 @@ bool Asm::List(strref filename) else out.sprintf_append(fmt, opcode_table[op].instr, buf[1]); if (aCPUs[cpu].timing) { - cycles[cycles_depth][0] += aCPUs[cpu].timing[*buf] / 2; - cycles[cycles_depth][1] += aCPUs[cpu].timing[*buf] & 1; + cycles[cycles_depth].add(aCPUs[cpu].timing[*buf]); out.append_to(' ', 33); - out.sprintf_append("%x%s", aCPUs[cpu].timing[*buf] / 2, (aCPUs[cpu].timing[*buf] & 1) ? "+" : ""); + if (cycleCnt::sum_plus(aCPUs[cpu].timing[*buf])==1) + out.sprintf_append("%d+", cycleCnt::get_base(aCPUs[cpu].timing[*buf])); + else if (cycleCnt::sum_plus(aCPUs[cpu].timing[*buf])) + out.sprintf_append("%d+%d", cycleCnt::get_base(aCPUs[cpu].timing[*buf]), cycleCnt::sum_plus(aCPUs[cpu].timing[*buf])); + else + out.sprintf_append("%d", cycleCnt::get_base(aCPUs[cpu].timing[*buf])); } } } - out.append_to(' ', aCPUs[cpu].timing ? 36 : 33); + out.append_to(' ', aCPUs[cpu].timing ? 40 : 33); strref line = lst.code.get_skipped(lst.line_offs).get_line(); line.clip_trailing_whitespace(); strown<128> line_fix(line); @@ -5354,7 +5436,7 @@ StatusCode Asm::WriteObjectFile(strref filename) (si->IsDummySection() ? (1 << ObjFileSection::OFS_DUMMY) : 0) | (si->IsMergedSection() ? (1 << ObjFileSection::OFS_MERGED) : 0) | (si->address_assigned ? (1 << ObjFileSection::OFS_FIXED) : 0); - if (si->pRelocs && si->pRelocs->size()) { + if (si->pRelocs && si->pRelocs->size() && aRelocs) { for (relocList::iterator ri = si->pRelocs->begin(); ri!=si->pRelocs->end(); ++ri) { struct ObjFileReloc &r = aRelocs[reloc++]; r.base_value = ri->base_value; @@ -5552,32 +5634,34 @@ StatusCode Asm::ReadObjectFile(strref filename) struct ObjFileLabel &l = aLabels[li]; strref name = l.name.offs >= 0 ? strref(str_pool + l.name.offs) : strref(); Label *lbl = GetLabel(name); - if (!lbl) { - short f = l.flags; - int external = f & ObjFileLabel::OFL_XDEF; - if (external == ObjFileLabel::OFL_XDEF) + short f = l.flags; + int external = f & ObjFileLabel::OFL_XDEF; + if (external == ObjFileLabel::OFL_XDEF) { + if (!lbl) lbl = AddLabel(name.fnv1a()); // insert shared label - else { // insert protected label - while ((file_index + external) >= (int)externals.size()) { - if (externals.size() == externals.capacity()) - externals.reserve(externals.size() + 32); - externals.push_back(ExtLabels()); - } - unsigned int hash = name.fnv1a(); - unsigned int index = FindLabelIndex(hash, externals[file_index].labels.getKeys(), externals[file_index].labels.count()); - externals[file_index].labels.insert(index, hash); - lbl = externals[file_index].labels.getValues() + index; + else if (!lbl->reference) + continue; + } else { // insert protected label + while ((file_index + external) >= (int)externals.size()) { + if (externals.size() == externals.capacity()) + externals.reserve(externals.size() + 32); + externals.push_back(ExtLabels()); } - lbl->label_name = name; - lbl->pool_name.clear(); - lbl->value = l.value; - lbl->evaluated = !!(f & ObjFileLabel::OFL_EVAL); - lbl->constant = !!(f & ObjFileLabel::OFL_CNST); - lbl->pc_relative = !!(f & ObjFileLabel::OFL_ADDR); - lbl->external = external == ObjFileLabel::OFL_XDEF; - lbl->section = l.section >= 0 ? aSctRmp[l.section] : l.section; - lbl->mapIndex = l.mapIndex >= 0 ? (l.mapIndex + (int)map.size()) : -1; + unsigned int hash = name.fnv1a(); + unsigned int index = FindLabelIndex(hash, externals[file_index].labels.getKeys(), externals[file_index].labels.count()); + externals[file_index].labels.insert(index, hash); + lbl = externals[file_index].labels.getValues() + index; } + lbl->label_name = name; + lbl->pool_name.clear(); + lbl->value = l.value; + lbl->evaluated = !!(f & ObjFileLabel::OFL_EVAL); + lbl->constant = !!(f & ObjFileLabel::OFL_CNST); + lbl->pc_relative = !!(f & ObjFileLabel::OFL_ADDR); + lbl->external = external == ObjFileLabel::OFL_XDEF; + lbl->section = l.section >= 0 ? aSctRmp[l.section] : l.section; + lbl->mapIndex = l.mapIndex >= 0 ? (l.mapIndex + (int)map.size()) : -1; + lbl->reference = false; } if (file_index==(int)externals.size())