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 c58ef00..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 @@ -314,6 +317,15 @@ enum AssemblerDirective { AD_ERROR, }; +// 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) +}; + // Operators are either instructions or directives enum OperationType { OT_NONE, @@ -997,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 }, @@ -1058,8 +1073,23 @@ DirectiveName aDirectiveNamesMerlin[] { { "CYC", AD_CYC }, // MERLIN: Start and stop cycle counter }; +struct EvalFuncNames { + const char* name; + EvalFuncs function; +}; + +EvalFuncNames aEvalFunctions[] = { + { "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]); static const int nDirectiveNamesMerlin = sizeof(aDirectiveNamesMerlin) / sizeof(aDirectiveNamesMerlin[0]); +static const int nEvalFuncs = sizeof(aEvalFunctions) / sizeof(aEvalFunctions[0]); // Binary search over an array of unsigned integers, may contain multiple instances of same key uint32_t FindLabelIndex(uint32_t hash, uint32_t *table, uint32_t count) @@ -1533,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; @@ -1819,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); @@ -1830,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); @@ -1974,7 +2008,7 @@ StatusCode SymbolStackTable::PullSymbol(StringSymbol* string) } else { if (string->string_value.empty() || string->string_value.cap() < (strlen(str) + 1)) { if (string->string_value.charstr()) { free(string->string_value.charstr()); } - string->string_value.set_overlay((char*)malloc(strlen(str) + 1), strlen(str) + 1); + string->string_value.set_overlay((char*)malloc(strlen(str) + 1), (strl_t)strlen(str) + 1); } string->string_value.copy(str); free(str); @@ -3415,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 @@ -3556,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; } @@ -4097,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++; } @@ -4117,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++; } @@ -4311,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() == '$') { @@ -4367,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(); @@ -4399,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; } @@ -5093,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; } @@ -5528,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()) @@ -7194,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()) {