diff --git a/README.md b/README.md index e31b9df..efcf28c 100644 --- a/README.md +++ b/README.md @@ -643,10 +643,39 @@ Abort assembly if any of the expressions is false. Seed the random number generator used by the RND() function. If this is not used, the random number generator is seeded based on the current time and so each build of a program using `RND()` will be different. + `ASM ` Assemble the supplied assembly language string. For example `ASM "LDA #&41"`. + +`DEFINE ` + +Evaluates `` as the name of a variable to define with value ``. The `` expressison must be a string, for example `DEFINE "addr" &70`. It is not possible to re-assign variables. + +This must be used instead of `=` when the variable name needs to be generated dynamically such as from macro arguments, for example: + +``` + MACRO VIA name, base_addr + DEFINE name, addr + DEFINE name + "_IOR_B", base_addr + DEFINE name + "_IOR_A", base_addr + 1 + DEFINE name + "_DDR_B", base_addr + 2 + DEFINE name + "_DDR_A", base_addr + 3 + ; ... + ; ... + ENDMACRO + + VIA SYSVIA, &FE40 + VIA USERVIA, &FE60 +``` + + +`ASSIGN ` + +Does the same thing as `DEFINE ` except that it can be used to change the value of a variable. + + ## 7. TIPS AND TRICKS BeebAsm's approach of treating memory as a canvas which can be written to, saved, and rewritten if desired makes it very easy to create certain types of applications. diff --git a/src/commands.cpp b/src/commands.cpp index c53a8a2..18ed00f 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -82,7 +82,9 @@ const LineParser::Token LineParser::m_gaTokenTable[] = { "ERROR", &LineParser::HandleError, 0 }, { "COPYBLOCK", &LineParser::HandleCopyBlock, 0 }, { "RANDOMIZE", &LineParser::HandleRandomize, 0 }, - { "ASM", &LineParser::HandleAsm, 0 } + { "ASM", &LineParser::HandleAsm, 0 }, + { "DEFINE", &LineParser::HandleDefine, 0 }, + { "ASSIGN", &LineParser::HandleAssign, 0 } }; @@ -1876,3 +1878,74 @@ void LineParser::HandleAsm() parser.HandleAssembler(instruction); } + +/*************************************************************************************************/ +/** + LineParser::HandleDefineAssignCommon() + LineParser::HandleDefine() + LineParser::HandleAssign() +*/ +/*************************************************************************************************/ +void LineParser::HandleDefineAssignCommon( bool bAllowRedefinition ) +{ + string name = EvaluateExpressionAsString(); + + if ( name.empty() || ! ( isalpha( name[0] ) || name[0] == '_' ) ) + { + throw AsmException_SyntaxError_InvalidSymbolName( m_line, m_column ); + } + for (std::string::size_type i = 1, e = name.size(); i < e; ++i) + { + if ( ! ( ( isalpha( name[ i ] ) || + isdigit( name[ i ] ) || + name[ i ] == '_' || + name[ i ] == '%' || + name[ i ] == '$' ) && + name[ i - 1 ] != '%' && + name[ i - 1 ] != '$' ) ) + { + throw AsmException_SyntaxError_InvalidSymbolName( m_line, m_column ); + } + } + + if ( ! ( AdvanceAndCheckEndOfStatement() && m_line[ m_column ] == ',' ) ) + { + throw AsmException_SyntaxError_MissingComma( m_line, m_column ); + } + + ++m_column; + if ( ! AdvanceAndCheckEndOfStatement() ) + { + throw AsmException_SyntaxError_MissingValue( m_line, m_column ); + } + + Value value = EvaluateExpression(); + + if ( AdvanceAndCheckEndOfStatement() ) + { + throw AsmException_SyntaxError_InvalidCharacter( m_line, m_column ); + } + + if ( GlobalData::Instance().IsFirstPass() ) + { + if ( SymbolTable::Instance().IsSymbolDefined( name ) ) + { + if ( !bAllowRedefinition ) + { + throw AsmException_SyntaxError_LabelAlreadyDefined( m_line, m_column ); + } + SymbolTable::Instance().RemoveSymbol( name ); + } + SymbolTable::Instance().AddSymbol( name, value ); + } +} + +void LineParser::HandleDefine() +{ + HandleDefineAssignCommon( false ); +} + +void LineParser::HandleAssign() +{ + HandleDefineAssignCommon( true ); +} diff --git a/src/lineparser.h b/src/lineparser.h index 294bd13..fb393ae 100644 --- a/src/lineparser.h +++ b/src/lineparser.h @@ -164,6 +164,9 @@ class LineParser void HandleCopyBlock(); void HandleRandomize(); void HandleAsm(); + void HandleDefineAssignCommon( bool bAllowRedefinition ); + void HandleDefine(); + void HandleAssign(); // expression evaluating methods diff --git a/test/3-directives/assign/define-variable.6502 b/test/3-directives/assign/define-variable.6502 new file mode 100644 index 0000000..994a324 --- /dev/null +++ b/test/3-directives/assign/define-variable.6502 @@ -0,0 +1,23 @@ +\ Assign examples + +ASSIGN "v1",27183 +ASSERT v1 = 27183 + +ASSIGN "v2",v1+1 +ASSERT v2 = 27184 + +prefix = "v" +ASSIGN prefix + "3", "A string" +ASSERT v3 = "A string" + +ASSIGN "_a",1 +ASSERT _a = 1 + +ASSIGN "a_",2 +ASSERT a_ = 2 + +ASSIGN "a$","str" +ASSERT a$ = "str" + +ASSIGN "a%",3 +ASSERT a% = 3 diff --git a/test/3-directives/assign/name-contains-bang.fail.6502 b/test/3-directives/assign/name-contains-bang.fail.6502 new file mode 100644 index 0000000..4c9e313 --- /dev/null +++ b/test/3-directives/assign/name-contains-bang.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name cannot contain general punctuation +ASSIGN "a!b",13 diff --git a/test/3-directives/assign/name-contains-bang.fail.gold.txt b/test/3-directives/assign/name-contains-bang.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/assign/name-contains-bang.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/assign/name-contains-dollar.fail.6502 b/test/3-directives/assign/name-contains-dollar.fail.6502 new file mode 100644 index 0000000..76c95e9 --- /dev/null +++ b/test/3-directives/assign/name-contains-dollar.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name cannot contain a dollar +ASSIGN "a$b",13 diff --git a/test/3-directives/assign/name-contains-dollar.fail.gold.txt b/test/3-directives/assign/name-contains-dollar.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/assign/name-contains-dollar.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/assign/name-contains-percent.fail.6502 b/test/3-directives/assign/name-contains-percent.fail.6502 new file mode 100644 index 0000000..c9f4bc1 --- /dev/null +++ b/test/3-directives/assign/name-contains-percent.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name cannot contain a percent +ASSIGN "a%b",13 diff --git a/test/3-directives/assign/name-contains-percent.fail.gold.txt b/test/3-directives/assign/name-contains-percent.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/assign/name-contains-percent.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/assign/name-empty.fail.6502 b/test/3-directives/assign/name-empty.fail.6502 new file mode 100644 index 0000000..0912df3 --- /dev/null +++ b/test/3-directives/assign/name-empty.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name can't be empty +ASSIGN "",13 diff --git a/test/3-directives/assign/name-empty.fail.gold.txt b/test/3-directives/assign/name-empty.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/assign/name-empty.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/assign/name-not-string.fail.6502 b/test/3-directives/assign/name-not-string.fail.6502 new file mode 100644 index 0000000..9efed1d --- /dev/null +++ b/test/3-directives/assign/name-not-string.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name must be a string. +ASSIGN 1,13 diff --git a/test/3-directives/assign/name-not-string.fail.gold.txt b/test/3-directives/assign/name-not-string.fail.gold.txt new file mode 100644 index 0000000..ea47f95 --- /dev/null +++ b/test/3-directives/assign/name-not-string.fail.gold.txt @@ -0,0 +1 @@ +error: Type mismatch diff --git a/test/3-directives/assign/name-starts-with-dollar.fail.6502 b/test/3-directives/assign/name-starts-with-dollar.fail.6502 new file mode 100644 index 0000000..bec0b26 --- /dev/null +++ b/test/3-directives/assign/name-starts-with-dollar.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name can't start with $ +ASSIGN "$a",13 diff --git a/test/3-directives/assign/name-starts-with-dollar.fail.gold.txt b/test/3-directives/assign/name-starts-with-dollar.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/assign/name-starts-with-dollar.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/assign/name-starts-with-number.fail.6502 b/test/3-directives/assign/name-starts-with-number.fail.6502 new file mode 100644 index 0000000..1160940 --- /dev/null +++ b/test/3-directives/assign/name-starts-with-number.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name can't start with a number +ASSIGN "1a",13 diff --git a/test/3-directives/assign/name-starts-with-number.fail.gold.txt b/test/3-directives/assign/name-starts-with-number.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/assign/name-starts-with-number.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/assign/name-starts-with-percent.fail.6502 b/test/3-directives/assign/name-starts-with-percent.fail.6502 new file mode 100644 index 0000000..5cdf207 --- /dev/null +++ b/test/3-directives/assign/name-starts-with-percent.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name can't start with % +ASSIGN "%a",13 diff --git a/test/3-directives/assign/name-starts-with-percent.fail.gold.txt b/test/3-directives/assign/name-starts-with-percent.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/assign/name-starts-with-percent.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/assign/redefine.6502 b/test/3-directives/assign/redefine.6502 new file mode 100644 index 0000000..d0c9e50 --- /dev/null +++ b/test/3-directives/assign/redefine.6502 @@ -0,0 +1,4 @@ +\ Can reassign variables +a = 12 +ASSIGN "a",13 +ASSERT a = 13 diff --git a/test/3-directives/define/define-variable.6502 b/test/3-directives/define/define-variable.6502 new file mode 100644 index 0000000..56b6fbe --- /dev/null +++ b/test/3-directives/define/define-variable.6502 @@ -0,0 +1,23 @@ +\ Define examples + +DEFINE "v1",27183 +ASSERT v1 = 27183 + +DEFINE "v2",v1+1 +ASSERT v2 = 27184 + +prefix = "v" +DEFINE prefix + "3", "A string" +ASSERT v3 = "A string" + +DEFINE "_a",1 +ASSERT _a = 1 + +DEFINE "a_",2 +ASSERT a_ = 2 + +DEFINE "a$","str" +ASSERT a$ = "str" + +DEFINE "a%",3 +ASSERT a% = 3 diff --git a/test/3-directives/define/name-contains-bang.fail.6502 b/test/3-directives/define/name-contains-bang.fail.6502 new file mode 100644 index 0000000..85317ec --- /dev/null +++ b/test/3-directives/define/name-contains-bang.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name cannot contain general punctuation +DEFINE "a!b",13 diff --git a/test/3-directives/define/name-contains-bang.fail.gold.txt b/test/3-directives/define/name-contains-bang.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/define/name-contains-bang.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/define/name-contains-dollar.fail.6502 b/test/3-directives/define/name-contains-dollar.fail.6502 new file mode 100644 index 0000000..4ae562d --- /dev/null +++ b/test/3-directives/define/name-contains-dollar.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name cannot contain a dollar +DEFINE "a$b",13 diff --git a/test/3-directives/define/name-contains-dollar.fail.gold.txt b/test/3-directives/define/name-contains-dollar.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/define/name-contains-dollar.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/define/name-contains-percent.fail.6502 b/test/3-directives/define/name-contains-percent.fail.6502 new file mode 100644 index 0000000..69cc843 --- /dev/null +++ b/test/3-directives/define/name-contains-percent.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name cannot contain a percent +DEFINE "a%b",13 diff --git a/test/3-directives/define/name-contains-percent.fail.gold.txt b/test/3-directives/define/name-contains-percent.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/define/name-contains-percent.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/define/name-empty.fail.6502 b/test/3-directives/define/name-empty.fail.6502 new file mode 100644 index 0000000..caa8bfb --- /dev/null +++ b/test/3-directives/define/name-empty.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name can't be empty +DEFINE "",13 diff --git a/test/3-directives/define/name-empty.fail.gold.txt b/test/3-directives/define/name-empty.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/define/name-empty.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/define/name-not-string.fail.6502 b/test/3-directives/define/name-not-string.fail.6502 new file mode 100644 index 0000000..5c6c7c0 --- /dev/null +++ b/test/3-directives/define/name-not-string.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name must be a string. +DEFINE 1,13 diff --git a/test/3-directives/define/name-not-string.fail.gold.txt b/test/3-directives/define/name-not-string.fail.gold.txt new file mode 100644 index 0000000..ea47f95 --- /dev/null +++ b/test/3-directives/define/name-not-string.fail.gold.txt @@ -0,0 +1 @@ +error: Type mismatch diff --git a/test/3-directives/define/name-starts-with-dollar.fail.6502 b/test/3-directives/define/name-starts-with-dollar.fail.6502 new file mode 100644 index 0000000..7486666 --- /dev/null +++ b/test/3-directives/define/name-starts-with-dollar.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name can't start with $ +DEFINE "$a",13 diff --git a/test/3-directives/define/name-starts-with-dollar.fail.gold.txt b/test/3-directives/define/name-starts-with-dollar.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/define/name-starts-with-dollar.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/define/name-starts-with-number.fail.6502 b/test/3-directives/define/name-starts-with-number.fail.6502 new file mode 100644 index 0000000..00758f0 --- /dev/null +++ b/test/3-directives/define/name-starts-with-number.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name can't start with a number +DEFINE "1a",13 diff --git a/test/3-directives/define/name-starts-with-number.fail.gold.txt b/test/3-directives/define/name-starts-with-number.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/define/name-starts-with-number.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/define/name-starts-with-percent.fail.6502 b/test/3-directives/define/name-starts-with-percent.fail.6502 new file mode 100644 index 0000000..f918606 --- /dev/null +++ b/test/3-directives/define/name-starts-with-percent.fail.6502 @@ -0,0 +1,2 @@ +\ Variable name can't start with % +DEFINE "%a",13 diff --git a/test/3-directives/define/name-starts-with-percent.fail.gold.txt b/test/3-directives/define/name-starts-with-percent.fail.gold.txt new file mode 100644 index 0000000..b1187eb --- /dev/null +++ b/test/3-directives/define/name-starts-with-percent.fail.gold.txt @@ -0,0 +1 @@ +error: Invalid symbol name; must start with a letter diff --git a/test/3-directives/define/redefine.fail.6502 b/test/3-directives/define/redefine.fail.6502 new file mode 100644 index 0000000..91750ef --- /dev/null +++ b/test/3-directives/define/redefine.fail.6502 @@ -0,0 +1,3 @@ +\ Can't redefine variables +a = 12 +DEFINE "a",13 diff --git a/test/3-directives/define/redefine.fail.gold.txt b/test/3-directives/define/redefine.fail.gold.txt new file mode 100644 index 0000000..0f21b65 --- /dev/null +++ b/test/3-directives/define/redefine.fail.gold.txt @@ -0,0 +1 @@ +error: Symbol already defined diff --git a/test/testrunner.py b/test/testrunner.py index 8ec3ef9..f4876c7 100644 --- a/test/testrunner.py +++ b/test/testrunner.py @@ -123,7 +123,7 @@ def run_test(beebasm, path, file_names, file_name): # Duplicate captured data on stdout sys.stdout.write(capture) with open(gold_txt, 'r') as gold_file: - gold = gold_file.read() + gold = gold_file.read().strip() print('Comparing beebasm output to', gold_txt, end = '') if capture.find(gold) != -1: print(' succeeded') @@ -199,4 +199,3 @@ def parse_args(): except TestFailure as e: print("FAILURE: " + e.args[0], file = original_stdout) sys.exit(1) -