mirror of
https://github.com/ksherlock/x65.git
synced 2024-12-30 17:30:58 +00:00
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
This commit is contained in:
parent
c50ae5027d
commit
a4d6c3efbf
21
README.md
21
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.
|
||||
|
||||
<a name="long">**LONGS**
|
||||
|
||||
Adds comma separated 32 bit values similar to how **WORDS** work.
|
||||
|
||||
<a name="text">**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).
|
||||
|
||||
|
||||
<a name="incdir">**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
|
||||
|
164
x65.cpp
164
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<MAX_DEPTH_CYCLE_COUNTER) {
|
||||
cycles_depth++; cycles[cycles_depth][0] = 0; cycles[cycles_depth][1] = 0;
|
||||
out.append_to(' ', 25); out.sprintf_append("c>%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())
|
||||
|
Loading…
Reference in New Issue
Block a user