mirror of
https://github.com/ksherlock/x65.git
synced 2024-12-29 10:30:32 +00:00
Bug fixes
- REPT caused a crash if expression was resolved after assembly completed - Fix expression % as a label at end of scope if another scope ended before the current scope - Removed # from conditionals since that doesn't fit with any assembler style - Added .l (long) and .t (triple) to label pool sizes, also supporting .d as double which is the same size but different meaning than .w (word)
This commit is contained in:
parent
95e22ae7a2
commit
9f935a8e91
22
README.md
22
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
|
||||
```
|
||||
|
||||
<a name="conditional">**#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)
|
||||
|
||||
<a name="conditional">**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
|
||||
```
|
||||
|
||||
<a name="struct">**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
|
||||
|
97
x65.cpp
97
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);
|
||||
|
Loading…
Reference in New Issue
Block a user