From a535295929ca66f5b0a20ec73f56e8d3c09de345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Henrik=20Sk=C3=A5rstedt?= Date: Fri, 10 Jan 2020 14:20:00 -0800 Subject: [PATCH] Implementing Eval Functions, complimented by IFCONST, IFBLANK and IFNBLANK --- test/ca65directive.s | 24 ++++++++- x65.cpp | 126 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 138 insertions(+), 12 deletions(-) diff --git a/test/ca65directive.s b/test/ca65directive.s index 6a388da..5b4f0ad 100644 --- a/test/ca65directive.s +++ b/test/ca65directive.s @@ -6,12 +6,32 @@ dc.b rept .ENDR +eval Checking defined function, Should be 0: .defined(test_stack) test_stack = 0 +eval Checking referenced function, Should be 0: .referenced(test_stack) +eval Checking defined function, Should be 1: .defined(test_stack) PUSH test_stack +eval Checking referenced function, Should be 1: .referenced(test_stack) test_stack = 10 -eval test_stack +eval Push Before Pull: test_stack PULL test_stack -eval test_stack +eval Pull original: test_stack + +eval Checking symbol is not const (0): .const(test_stack) +const ConstAddress = $1000 +eval Checking symbol is const (0): .const(ConstAddress) + +eval This should be blank (1): .blank() +eval This should be blank (1): .blank({}) +eval This should be not be blank (0): .blank({monkeys}) + +.ifconst test_stack +eval Checking ifconst with non-const symbol, should not print: +.endif + +.ifconst ConstAddress +eval Checking ifconst with const symbol, this should print: +.endif zp_len_lo = $a7 zp_len_hi = $a8 diff --git a/x65.cpp b/x65.cpp index b0b4df3..0d8343a 100644 --- a/x65.cpp +++ b/x65.cpp @@ -279,6 +279,9 @@ enum AssemblerDirective { AD_IF, // #IF: Conditional assembly follows based on expression AD_IFDEF, // #IFDEF: Conditional assembly follows based on label defined or not AD_IFNDEF, // #IFNDEF: Conditional assembly inverted from IFDEF + AD_IFCONST, // #IFCONST: Conditional assembly follows based on label being const + AD_IFBLANK, // #IFBLANK: Conditional assembly follows based on rest of line empty + AD_IFNBLANK, // #IFNBLANK: Conditional assembly follows based on rest of line not empty AD_ELSE, // #ELSE: Otherwise assembly AD_ELIF, // #ELIF: Otherwise conditional assembly follows AD_ENDIF, // #ENDIF: End a block of #IF/#IFDEF @@ -317,6 +320,7 @@ enum AssemblerDirective { // evaluation functions enum EvalFuncs { EF_DEFINED, // DEFINED(label) 1 if label is defined + EF_REFERENCED, // REFERENCED(label) 1 if label has been referenced in this file EF_BLANK, // BLANK() 1 if the contents within the parenthesis is empty EF_CONST, // CONST(label) 1 if label is a const label EF_SIN, // SIN(index, period, amplitude) @@ -1005,6 +1009,9 @@ DirectiveName aDirectiveNames[] { { "IF", AD_IF }, { "IFDEF", AD_IFDEF }, { "IFNDEF", AD_IFNDEF }, + { "IFCONST", AD_IFCONST }, + { "IFBLANK", AD_IFBLANK }, // #IFBLANK: Conditional assembly follows based on rest of line empty + { "IFNBLANK", AD_IFNBLANK }, // #IFDEF: Conditional assembly follows based on rest of line not empty { "ELSE", AD_ELSE }, { "ELIF", AD_ELIF }, { "ENDIF", AD_ENDIF }, @@ -1072,10 +1079,12 @@ struct EvalFuncNames { }; EvalFuncNames aEvalFunctions[] = { - { "DEFINED", EF_DEFINED }, // DEFINED(label) 1 if label is defined - { "BLANK", EF_BLANK }, // BLANK() 1 if the contents within the parenthesis is empty - { "CONST", EF_CONST }, // CONST(label) 1 if label is a const label - { "SIN", EF_SIN }, // SIN(index, period, amplitude) + { "DEFINED", EF_DEFINED }, // DEFINED(label) 1 if label is defined + { "DEF", EF_DEFINED }, // DEFINED(label) 1 if label is defined + { "REFERENCED", EF_REFERENCED }, // REFERENCED(label) 1 if label has been referenced in this file + { "BLANK", EF_BLANK }, // BLANK() 1 if the contents within the parenthesis is empty + { "CONST", EF_CONST }, // CONST(label) 1 if label is a const label + { "TRIGSIN", EF_SIN }, // TRIGSIN(index, period, amplitude) }; static const int nDirectiveNames = sizeof(aDirectiveNames) / sizeof(aDirectiveNames[0]); @@ -1554,6 +1563,7 @@ public: bool constant; // the value of this label can not change bool external; // this label is globally accessible bool reference; // this label is accessed from external and can't be used for evaluation locally + bool referenced; // this label has been found via GetLabel and can be assumed to be referenced for some purpose } Label; @@ -1840,6 +1850,9 @@ public: StatusCode EvalStruct(strref name, int &value); StatusCode BuildEnum(strref name, strref declaration); + // Check if function is a valid function and if so evaluate the expression + bool EvalFunction(strref function, strref &expression, int &value); + // Calculate a value based on an expression. EvalOperator RPNToken_Merlin(strref &expression, const struct EvalContext &etx, EvalOperator prev_op, int16_t §ion, int &value); @@ -1851,7 +1864,7 @@ public: int ReptCnt() const; // Access labels - Label* GetLabel(strref label); + Label * GetLabel(strref label, bool reference_check = false); Label* GetLabel(strref label, int file_ref); Label* AddLabel(uint32_t hash); bool MatchXDEF(strref label); @@ -3436,6 +3449,56 @@ StatusCode Asm::EvalStruct(strref name, int &value) { return STATUS_OK; } +// +// +// EVAL FUNCTIONS +// +// + +bool Asm::EvalFunction(strref function, strref& expression, int &value) +{ + // all eval functions take a parenthesis with arguments + if (expression.get_first() != '(') { return false; } + + strref expRet = expression; + strref params = expRet.scoped_block_comment_skip(); + params.trim_whitespace(); + if (function.get_first() == '.') { ++function; } + for (int i = 0; i < nEvalFuncs; ++i) { + if (function.same_str(aEvalFunctions[i].name)) { + switch (aEvalFunctions[i].function) { + case EF_DEFINED: + expression = expRet; + value = GetLabel(params, true) != nullptr ? 1 : 0; + return true; + case EF_REFERENCED: + expression = expRet; + if (Label* label = GetLabel(params, true)) { value = label->referenced; return true; } + return false; + case EF_BLANK: + expression = expRet; + if (params.get_first() == '{') { params = params.scoped_block_comment_skip(); } + params.trim_whitespace(); + value = params.is_empty(); + return true; + case EF_CONST: + expression = expRet; + if (Label* label = GetLabel(params, true)) { + return label->constant; + } + return false; + case EF_SIN: + expression = expRet; + value = 0; // TODO: implement trigsin + return true; + } + return false; + } + } + return false; +} + + // // // EXPRESSIONS AND LATE EVALUATION @@ -3577,6 +3640,7 @@ EvalOperator Asm::RPNToken(strref &exp, const struct EvalContext &etx, EvalOpera } if (!pLabel && label.same_str("rept")) { value = etx.rept_cnt; return EVOP_VAL; } if (!pLabel) { if (StringSymbol *pStr = GetString(label)) { subexp = pStr->get(); return EVOP_EXP; } } + if (!pLabel) { if (EvalFunction(label, exp, value)) { return EVOP_VAL; } } if (!pLabel || !pLabel->evaluated) return EVOP_NRY; // this label could not be found (yet) value = pLabel->value; section = int16_t(pLabel->section); return pLabel->reference ? EVOP_XRF : EVOP_VAL; } @@ -4118,12 +4182,14 @@ StatusCode Asm::CheckLateEval(strref added_label, int scope_end, bool print_miss // // Get a label record if it exists -Label *Asm::GetLabel(strref label) { +Label *Asm::GetLabel(strref label, bool reference_check) { uint32_t label_hash = label.fnv1a(); uint32_t index = FindLabelIndex(label_hash, labels.getKeys(), labels.count()); while (index < labels.count() && label_hash == labels.getKey(index)) { if (label.same_str(labels.getValue(index).label_name)) { - return labels.getValues()+index; + Label *label = labels.getValues() + index; + if (!reference_check) { label->referenced = true; } + return label; } index++; } @@ -4138,7 +4204,9 @@ Label *Asm::GetLabel(strref label, int file_ref) { uint32_t index = FindLabelIndex(label_hash, labs.labels.getKeys(), labs.labels.count()); while (index < labs.labels.count() && label_hash == labs.labels.getKey(index)) { if (label.same_str(labs.labels.getValue(index).label_name)) { - return labs.labels.getValues()+index; + Label *label = labs.labels.getValues()+index; + label->referenced = true; + return label; } index++; } @@ -4332,6 +4400,7 @@ StatusCode Asm::AssignPoolLabel(LabelPool &pool, strref label) { pLabel->constant = true; pLabel->external = false; pLabel->reference = false; + pLabel->referenced = false; bool local = false; if (label[ 0 ] == '.' || label[ 0 ] == '@' || label[ 0 ] == '!' || label[ 0 ] == ':' || label.get_last() == '$') { @@ -4388,7 +4457,7 @@ StatusCode Asm::AssignLabel(strref label, strref expression, bool make_constant) if (pLabel->constant && pLabel->evaluated && val!=pLabel->value) { return (status==STATUS_NOT_READY) ? STATUS_OK : ERROR_MODIFYING_CONST_LABEL; } - } else { pLabel = AddLabel(label.fnv1a()); } + } else { pLabel = AddLabel(label.fnv1a()); pLabel->referenced = false; } pLabel->label_name = label; pLabel->pool_name.clear(); @@ -4420,6 +4489,7 @@ StatusCode Asm::AddressLabel(strref label) bool constLabel = false; if (!pLabel) { pLabel = AddLabel(label.fnv1a()); + pLabel->referenced = false; // if this label already exists but is changed then it may already have been referenced } else if (pLabel->constant && pLabel->value!=CurrSection().GetPC()) { return ERROR_MODIFYING_CONST_LABEL; } else { constLabel = pLabel->constant; } @@ -5114,6 +5184,7 @@ StatusCode Asm::Directive_XREF(strref label) pLabelXREF->external = true; pLabelXREF->constant = false; pLabelXREF->reference = true; + pLabelXREF->referenced = false; // referenced is only within the current object file } return STATUS_OK; } @@ -5549,6 +5620,41 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc } break; + case AD_IFCONST: + if (NewConditional()) { // Start new conditional block + CheckConditionalDepth(); // Check if nesting + // ifdef doesn't need to evaluate the value, just determine if it exists or not + strref label_name = line.split_range_trim(label_end_char_range); + if (Label* label = GetLabel(label_name, etx.file_ref)) { + if (label->constant) { ConsumeConditional(); } + else { SetConditional(); } + } + else { SetConditional(); } + } + break; + + case AD_IFBLANK: + if (NewConditional()) { // Start new conditional block + CheckConditionalDepth(); // Check if nesting + line.trim_whitespace(); + if (line.is_empty()) + ConsumeConditional(); + else + SetConditional(); + } + break; + + case AD_IFNBLANK: + if (NewConditional()) { // Start new conditional block + CheckConditionalDepth(); // Check if nesting + line.trim_whitespace(); + if (!line.is_empty()) + ConsumeConditional(); + else + SetConditional(); + } + break; + case AD_ELSE: if (ConditionalAsm()) { if (ConditionalConsumed()) @@ -7215,7 +7321,7 @@ StatusCode Asm::ReadObjectFile(strref filename, int link_to_section) int16_t f = (int16_t)l.flags; int external = f & ObjFileLabel::OFL_XDEF; if (external == ObjFileLabel::OFL_XDEF) { - if (!lbl) { lbl = AddLabel(name.fnv1a()); } // insert shared label + if (!lbl) { lbl = AddLabel(name.fnv1a()); lbl->referenced = false; } // insert shared label else if (!lbl->reference) { continue; } } else { // insert protected label while ((file_index + external) >= (int)externals.size()) {