1
0
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:
Carl-Henrik Skårstedt 2015-11-01 15:08:38 -08:00
parent c50ae5027d
commit a4d6c3efbf
2 changed files with 139 additions and 46 deletions

View File

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

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