diff --git a/README.md b/README.md
index 4bd87ef..29f531e 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ To keep up with this trend x65 is adding the following features to the mix:
* [Directives](#directives) support both with and without leading period.
* Labels don't need to end with colon, but they can.
* No indentation required for instructions, meaning that labels can't be mnemonics, macros or directives.
-* Conditional assembly with #if/#ifdef/#else etc.
+* Conditional assembly with if/ifdef/else etc.
* As far as achievable, support the syntax of other 6502 assemblers (Merlin syntax now requires command line argument, -endm adds support for sources using macro/endmacro and repeat/endrepeat combos rather than scoeps).
In summary, if you are familiar with any 6502 assembler syntax you should feel at home with x65. If you're familiar with C programming expressions you should be familiar with '{', '}' scoping and complex expressions.
@@ -152,7 +152,7 @@ Directives are assembler commands that control the code generation but that does
* [**LABEL**](#label) Decorative directive to assign an expression to a label
* [**INCSYM**](#incsym) Include a symbol file with an optional set of wanted symbols.
* [**POOL**](#pool) Add a label pool for temporary address labels
-* [**#IF / #ELSE / #IFDEF / #ELIF / #ENDIF**](#conditional) Conditional assembly
+* [**IF / ELSE / IFDEF / ELIF / ENDIF**](#conditional) Conditional assembly
* [**STRUCT**](#struct) Hierarchical data structures (dot separated sub structures)
* [**REPT**](#rept) Repeat a scoped block of code a number of times.
* [**INCDIR**](#incdir) Add a directory to look for binary and text include files in.
@@ -432,7 +432,15 @@ Function_Name: {
rts
```
-**#IF / #ELSE / #IFDEF / #ELIF / #ENDIF**
+The following extensions are recognized:
+
+* [pool name] var (no extension is one byte)
+* [pool name] var.w (2 bytes)
+* [pool name] var.d (2 bytes)
+* [pool name] var.t (3 bytes)
+* [pool name] var.l (4 bytes)
+
+**IF / ELSE / IFDEF / ELIF / ENDIF**
Conditional code parsing is very similar to C directive conditional compilation.
@@ -441,11 +449,11 @@ Example:
```
DEBUG = 1
-#if DEBUG
+if DEBUG
lda #2
-#else
+else
lda #0
-#endif
+endif
```
**STRUCT**
@@ -825,6 +833,8 @@ Fish food! Assembler has all important features and switching to make a 6502 pro
* irp (indefinite repeat)
**FIXED**
+* % evaluates to the current end of scope instead of whatever scope ends first
+* rept crash fix if not resolved until assembly completed
* rept and symbol reference with forward reference label was not taking section into account
* Link append sections target confusion cleared up (caused crash/link errors/freeze)
* XREF prevented linking with same name symbol included from .x65 object causing a linker failure
diff --git a/x65.cpp b/x65.cpp
index e01089c..590ef77 100644
--- a/x65.cpp
+++ b/x65.cpp
@@ -1252,6 +1252,7 @@ typedef struct {
int target; // offset into output buffer
int address; // current pc
int scope; // scope pc
+ int scope_depth; // relevant for scope end
short section; // which section to apply to.
short rept; // value of rept
int file_ref; // -1 if current or xdef'd otherwise index of file for label
@@ -1314,11 +1315,14 @@ struct EvalContext {
int pc; // current address at point of eval
int scope_pc; // current scope open at point of eval
int scope_end_pc; // late scope closure after eval
+ int scope_depth; // scope depth for eval (must match current for scope_end_pc to eval)
int relative_section; // return can be relative to this section
int file_ref; // can access private label from this file or -1
- EvalContext(int _pc, int _scope, int _close, int _sect) :
- pc(_pc), scope_pc(_scope), scope_end_pc(_close), relative_section(_sect),
- file_ref(-1) {}
+ int rept_cnt; // current repeat counter
+ EvalContext() {}
+ EvalContext(int _pc, int _scope, int _close, int _sect, int _rept_cnt) :
+ pc(_pc), scope_pc(_scope), scope_end_pc(_close), scope_depth(-1),
+ relative_section(_sect), file_ref(-1), rept_cnt(_rept_cnt) {}
};
// Source context is current file (include file, etc.) or current macro.
@@ -1343,6 +1347,7 @@ private:
public:
ContextStack() : currContext(nullptr) { stack.reserve(32); }
SourceContext& curr() { return *currContext; }
+ const SourceContext& curr() const { return *currContext; }
void push(strref src_name, strref src_file, strref code_seg, int rept = 1) {
if (currContext)
currContext->read_source = currContext->next_source;
@@ -1480,6 +1485,8 @@ public:
EvalOperator RPNToken(strref &expression, const struct EvalContext &etx,
EvalOperator prev_op, short §ion, int &value);
StatusCode EvalExpression(strref expression, const struct EvalContext &etx, int &result);
+ void SetEvalCtxDefaults(struct EvalContext &etx);
+ int ReptCnt() const;
// Access labels
Label* GetLabel(strref label);
@@ -2425,8 +2432,8 @@ StatusCode Asm::BuildEnum(strref name, strref declaration)
pEnum->size = 0; // enums are 0 sized
int value = 0;
- struct EvalContext etx(CurrSection().GetPC(),
- scope_address[scope_depth], -1, -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
while (strref line = declaration.line()) {
line = line.before_or_full(',');
@@ -2573,6 +2580,23 @@ StatusCode Asm::EvalStruct(strref name, int &value)
//
+
+int Asm::ReptCnt() const
+{
+ return contextStack.curr().repeat_total - contextStack.curr().repeat;
+}
+
+void Asm::SetEvalCtxDefaults(struct EvalContext &etx)
+{
+ etx.pc = CurrSection().GetPC(); // current address at point of eval
+ etx.scope_pc = scope_address[scope_depth]; // current scope open at point of eval
+ etx.scope_end_pc = -1; // late scope closure after eval
+ etx.scope_depth = scope_depth; // scope depth for eval (must match current for scope_end_pc to eval)
+ etx.relative_section = -1; // return can be relative to this section
+ etx.file_ref = -1; // can access private label from this file or -1
+ etx.rept_cnt = ReptCnt(); // current repeat counter
+}
+
// Get a single token from a merlin expression
EvalOperator Asm::RPNToken_Merlin(strref &expression, const struct EvalContext &etx, EvalOperator prev_op, short §ion, int &value)
{
@@ -2594,7 +2618,7 @@ EvalOperator Asm::RPNToken_Merlin(strref &expression, const struct EvalContext &
case '%': // % means both binary and scope closure, disambiguate!
if (expression[1]=='0' || expression[1]=='1') {
++expression; value = expression.abinarytoui_skip(); return EVOP_VAL; }
- if (etx.scope_end_pc<0) return EVOP_NRY;
+ if (etx.scope_end_pc<0 || scope_depth != etx.scope_depth) return EVOP_NRY;
++expression; value = etx.scope_end_pc; section = CurrSection().IsRelativeSection() ? SectionId() : -1; return EVOP_VAL;
case '|':
case '.': ++expression; return EVOP_OR; // MERLIN: . is or, | is not used
@@ -2626,7 +2650,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 && label.same_str("rept")) { value = etx.rept_cnt; 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
@@ -2662,7 +2686,7 @@ EvalOperator Asm::RPNToken(strref &exp, const struct EvalContext &etx, EvalOpera
++exp; return EVOP_LOB;
case '%': // % means both binary and scope closure, disambiguate!
if (exp[1] == '0' || exp[1] == '1') { ++exp; value = exp.abinarytoui_skip(); return EVOP_VAL; }
- if (etx.scope_end_pc<0) return EVOP_NRY;
+ if (etx.scope_end_pc<0 || scope_depth != etx.scope_depth) return EVOP_NRY;
++exp; value = etx.scope_end_pc; section = CurrSection().IsRelativeSection() ? SectionId() : -1; return EVOP_VAL;
case '|': ++exp; return EVOP_OR;
case '^': if (prev_op == EVOP_VAL || prev_op == EVOP_RPR) { ++exp; return EVOP_EOR; }
@@ -2691,7 +2715,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 && label.same_str("rept")) { value = etx.rept_cnt; 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;
}
@@ -2947,6 +2971,7 @@ void Asm::AddLateEval(int target, int pc, int scope_pc, strref expression, strre
LateEval le;
le.address = pc;
le.scope = scope_pc;
+ le.scope_depth = scope_depth;
le.target = target;
le.section = (int)(&CurrSection() - &allSections[0]);
le.rept = contextStack.curr().repeat_total - contextStack.curr().repeat;
@@ -2964,6 +2989,7 @@ void Asm::AddLateEval(strref label, int pc, int scope_pc, strref expression, Lat
LateEval le;
le.address = pc;
le.scope = scope_pc;
+ le.scope_depth = scope_depth;
le.target = 0;
le.label = label;
le.section = (int)(&CurrSection() - &allSections[0]);
@@ -3011,12 +3037,10 @@ StatusCode Asm::CheckLateEval(strref added_label, int scope_end, bool print_miss
}
if (check) {
struct EvalContext etx(i->address, i->scope, scope_end,
- i->type == LateEval::LET_BRANCH ? SectionId() : -1);
- SourceContext &ctx = contextStack.curr();
+ i->type == LateEval::LET_BRANCH ? SectionId() : -1, i->rept);
+ etx.scope_depth = i->scope_depth;
etx.file_ref = i->file_ref;
- int r = ctx.repeat, rt = ctx.repeat_total; ctx.repeat = 0; ctx.repeat_total = i->rept;
StatusCode ret = EvalExpression(i->expression, etx, value);
- ctx.repeat = r; ctx.repeat_total = rt;
if (ret == STATUS_OK || ret==STATUS_RELATIVE_SECTION) {
// Check if target section merged with another section
int trg = i->target;
@@ -3287,7 +3311,8 @@ StatusCode Asm::AddLabelPool(strref name, strref args)
int ranges = 0;
int num32 = 0;
unsigned short aRng[256];
- struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1, -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
while (strref arg = args.split_token_trim(',')) {
strref start = arg[0]=='(' ? arg.scoped_block_skip() : arg.split_token_trim('-');
int addr0 = 0, addr1 = 0;
@@ -3332,8 +3357,12 @@ StatusCode Asm::AssignPoolLabel(LabelPool &pool, strref label)
strref type = label;
label = type.split_token('.');
int bytes = 1;
- if (strref::tolower(type[0])=='w')
- bytes = 2;
+ switch (strref::tolower(type.get_first())) {
+ case 'l': bytes = 4; break;
+ case 't': bytes = 3; break;
+ case 'd':
+ case 'w': bytes = 2; break;
+ }
if (GetLabel(label))
return ERROR_POOL_LABEL_ALREADY_DEFINED;
unsigned int addr;
@@ -3441,7 +3470,8 @@ StatusCode Asm::AssignLabel(strref label, strref line, bool make_constant)
{
line.trim_whitespace();
int val = 0;
- struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1, -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
StatusCode status = EvalExpression(line, etx, val);
if (status != STATUS_NOT_READY && status != STATUS_OK)
return status;
@@ -3629,7 +3659,8 @@ void Asm::ConditionalElse() {
StatusCode Asm::EvalStatement(strref line, bool &result)
{
int equ = line.find('=');
- struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1, -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
if (equ >= 0) {
// (EXP) == (EXP)
strref left = line.get_clipped(equ);
@@ -3709,7 +3740,8 @@ StatusCode Asm::Directive_Rept(strref line, strref source_file)
}
expression.trim_whitespace();
int count;
- struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1, -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
if (STATUS_OK != EvalExpression(expression, etx, count))
return ERROR_REPT_COUNT_EXPRESSION;
strref recur;
@@ -3823,7 +3855,8 @@ StatusCode Asm::Directive_Import(strref line)
++param;
param.skip_whitespace();
if (param) {
- struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1, -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
EvalExpression(param.split_token_trim(','), etx, skip);
if (param)
EvalExpression(param, etx, len);
@@ -3880,7 +3913,8 @@ StatusCode Asm::Directive_ORG(strref line)
line.next_word_ws();
line.skip_whitespace();
- struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1, -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
StatusCode error = EvalExpression(line, etx, addr);
if (error != STATUS_OK)
return (error == STATUS_NOT_READY || error == STATUS_XREF_DEPENDENT) ?
@@ -3905,7 +3939,8 @@ StatusCode Asm::Directive_LOAD(strref line)
if (line[0]=='=' || keyword_equ.is_prefix_word(line))
line.next_word_ws();
- struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1, -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
StatusCode error = EvalExpression(line, etx, addr);
if (error != STATUS_OK)
return (error == STATUS_NOT_READY || error == STATUS_XREF_DEPENDENT) ?
@@ -3976,7 +4011,8 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
if (dir!=AD_IF && dir!=AD_IFDEF && dir!=AD_ELSE && dir!=AD_ELIF && dir!=AD_ELSE && dir!=AD_ENDIF)
return STATUS_OK;
}
- struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1, -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
switch (dir) {
case AD_CPU:
for (int c = 0; c < nCPUs; c++) {
@@ -4547,8 +4583,10 @@ StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
Reloc::Type target_section_type = Reloc::NONE;
bool evalLater = false;
if (expression) {
- struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1,
- !!(validModes & AMM_BRANCH) ? SectionId() : -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
+ if (validModes & (AMM_BRANCH | AMM_BRANCH_L))
+ etx.relative_section = SectionId();
error = EvalExpression(expression, etx, value);
if (error == STATUS_NOT_READY || error == STATUS_XREF_DEPENDENT) {
evalLater = true;
@@ -4716,7 +4754,9 @@ StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
target_section_type == Reloc::HI_BYTE ? Reloc::HI_BYTE : Reloc::LO_BYTE);
}
AddByte(value);
- struct EvalContext etx(CurrSection().GetPC()-2, scope_address[scope_depth], -1, -1);
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
+ etx.pc = CurrSection().GetPC()-2;
line.split_token_trim(',');
error = EvalExpression(line, etx, value);
if (error==STATUS_NOT_READY || error == STATUS_XREF_DEPENDENT)
@@ -4746,7 +4786,10 @@ StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
target_section_type == Reloc::HI_BYTE ? Reloc::HI_BYTE : Reloc::LO_BYTE);
}
AddByte(value);
- struct EvalContext etx(CurrSection().GetPC()-2, scope_address[scope_depth], -1, SectionId());
+ struct EvalContext etx;
+ SetEvalCtxDefaults(etx);
+ etx.pc = CurrSection().GetPC()-2;
+ etx.relative_section = SectionId();
error = EvalExpression(line, etx, value);
if (error==STATUS_NOT_READY || error == STATUS_XREF_DEPENDENT)
AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], line, source_file, LateEval::LET_BRANCH);