diff --git a/README.adoc b/README.adoc index 195931c..8ab3575 100644 --- a/README.adoc +++ b/README.adoc @@ -15,13 +15,13 @@ See this http://apone.org/bass/part1.html[TUTORIAL] for a tutorial / introductio === Example -[source,asm] +[source,ca65] ---- VRAM_LO = $9f00 VRAM_HI = $9f01 !macro SetVRAM(a) { - .lo = a&0xff; - .hi = a>>8; + .lo = a; lda #.lo sta VRAM_LO !if .lo != .hi { lda #.hi } @@ -48,6 +48,30 @@ $ lda start,x text: !byte "Text to copy", 0 end: + + ; --- Generate sine table with and without LUA + + !define mysin(x, amp, size) { + (sin(x * Math.Pi * 2 / size) * 0.5 + 0.5) * amp + } +sin: + !fill 100 { mysin(i, 255, 100) } + +%{ -- LUA code +function make_sin(amp, size) + res = {} + for i=0,size-1,1 do + res[i+1] = (math.sin(i * math.pi * 2/ size) * 0.5 + 0.5) * amp + end + return res +end +}% + +sin2: + !fill make_sin(255, 100) + + !assert make_sin(5, 5)[1] == round(mysin(1, 5,5)) + !assert make_sin(5, 5)[3] == round(mysin(3, 5,5)) ---- See http://apone.org/bass/example.asm.html[example.asm] for a full example. diff --git a/README.md b/README.md deleted file mode 100644 index 26856fc..0000000 --- a/README.md +++ /dev/null @@ -1,147 +0,0 @@ - -## The Bass 6502 Assembler - -* ACME like syntax -* Aims to be on par with Kickassembler in automation/scriptability, - but with less complexity -* Unit tests through internal emulator -* Open and easily extendable source - -See this [TUTORIAL](http://apone.org/bass/part1.html) for a tutorial / introduction. - -### Invoking - -`bass -i -Dname=val ...` - -### Example - -```asm - VRAM_LO = $9f00 - VRAM_HI = $9f01 - !macro SetVRAM(a) { - .lo = a&0xff; - .hi = a>>8; - lda #.lo - sta VRAM_LO - !if .lo != .hi { lda #.hi } - sta VRAM_HI - } - - !section "main", $1000 -start: - - ; Clear 1K - ldx #0 -$ !rept 4 { sta $1000+i*256,x } - dex - bne - - - SetVRAM(0x1234) - ldx #end - text -$ lda start,x - sta $1000,x - dex - bne - - rts - -text: - !byte "Text to copy", 0 -end: -``` - -See [example.asm](http://apone.org/bass/example.asm.html) for a full example. - -### Labels - -Labels either end with ':' or have to start at the first column. - -A label starting with a dot ('.') is a _local_ label and treated -as _lastLabel.label_, where _lastLabel_ is the closest previous -_non local_ label. - -A label can be also called '$'. - -Referencing '+' will mean the closest _following_ '$' label. -Referencing '-' will mean the closest _previous_ '$' label. - -Repeated '-' or '+' signs means to skip further. - - -### Unit Tests - -Any statements inside a `!test` block is executed inside an internal -emulator. - -Before running the test code, all sections are placed in the right -places in memory. - -The test code is then assembled into the "test" section. - -Results are saved in tests. - -The assembled changes are then rolled back. - - -### Basic Operation in Detail - -The source is parsed top to bottom. Included files are inserted -at the point of inclusion. - -Symbols are defined through labels, symbol assignments or indirectly -through meta commands such as "!section" or "!test". - -An assignment may not change an existing symbol. - -If an expression references an undefined symbol, it is assumed to -be zero and flagged as "undefined". - -If an existing symbol gets assigned a new value, it is also flagged -as "undefined", since we need to make another pass to make sure all -it's previous references are resolved to the correct value. -This happens when code size changes, as subsequent labels are moved -to new locations. - -When all source code is parsed, the undefined list is checked; - - * If it is empty we are done. - * If all entries now also exist in the symbol table, we clear - the undefined list and make another pass. - * Otherwise we have an error, and report the undefined symbols. - -A _branch_ instruction to an undefined or incorrect label may -temporarily be too long. This error must be postponed until all -parsing is done. - -Macros use normal symbols as arguments. These symbols are only set -during macro evaluation. If a macro argument "shadows" a global -symbol a warning is issue. - -Macros affect the symbol table, so you can set symbols from macros. -If you don't want to pollute the symbol table, used "."-symbols, they -will be local to the macro. - -The symbol table supports the following types: - * Number (double) - * String (`std::string_view`) - * Byte Array (`std::vector`) - -Only numbers and strings can be assigned directly. - -Arrays are returned from functions, such as `bytes(elems...)` which is the basic -way of creating an array. - -### Limitations of the parser - -Currently the parser evaluates everything while parsing. This means -that it is not possible to _parse_ an expression without _evaluating_ it. - -To support delayed (macros) or skipped (if) evaluation, the parser -recognizes blocks ( `'{' anything '}'` ) and saves the contents without -evaluating it. - -If we rewrite the parser to create an AST for delayed evaluation, more -advanced constructs would be possible. - - - - diff --git a/src/functions.cpp b/src/functions.cpp index da1fd35..7a7448d 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -40,6 +40,7 @@ void initFunctions(Assembler& a) a.registerFunction("len", [](std::vector const& v) { return v.size(); }); a.registerFunction("round", [](double a) { return std::round(a); }); + a.registerFunction("trunc", [](double a) { return std::trunc(a); }); a.registerFunction("compare", [](std::vector const& v0, diff --git a/src/meta.cpp b/src/meta.cpp index 10ccdd9..bbaabb6 100644 --- a/src/meta.cpp +++ b/src/meta.cpp @@ -95,6 +95,21 @@ void initMeta(Assembler& assem) resetTranslate(); + static auto is_space = [](char const c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + }; + + static auto strip_space = [](std::string_view sv) -> std::string_view { + while (is_space(sv.front())) { + sv.remove_prefix(1); + } + while (is_space(sv.back())) { + sv.remove_suffix(1); + } + return sv; + }; + + assem.registerMeta("test", [&](auto const& text, auto const& blocks) { auto args = assem.evaluateList(text); std::string testName; @@ -128,7 +143,8 @@ void initMeta(Assembler& assem) assem.registerMeta("define", [&](auto const& text, auto const& blocks) { Check(blocks.size() == 1, "Expected block"); auto def = assem.evaluateDefinition(text); - assem.addDefine(def.name, def.args, blocks[0].line, blocks[0].contents); + auto contents = strip_space(blocks[0].contents); + assem.addDefine(def.name, def.args, blocks[0].line, contents); }); assem.registerMeta("ascii", [&](auto const&, auto const&) { @@ -233,9 +249,10 @@ void initMeta(Assembler& assem) uint8_t d = (*vec)[i]; syms.erase("i"); syms.set("i", any_num(i)); - for (auto const& b : blocks) { + for (Assembler::Block const& b : blocks) { + auto contents = strip_space(b.contents); assem.getSymbols().at("v") = d; - auto res = assem.evaluateExpression(b.contents, b.line); + auto res = assem.evaluateExpression(contents, b.line); d = number(res); } mach.writeByte(d); @@ -247,8 +264,9 @@ void initMeta(Assembler& assem) syms.erase("i"); syms.set("i", any_num(i)); for (auto const& b : blocks) { + auto contents = strip_space(b.contents); assem.getSymbols().at("v") = d; - auto res = assem.evaluateExpression(b.contents, b.line); + auto res = assem.evaluateExpression(contents, b.line); d = number(res); } mach.writeByte(char_translate.at(d)); @@ -261,8 +279,9 @@ void initMeta(Assembler& assem) syms.set("i", any_num(i)); uint8_t d = 0; for (auto const& b : blocks) { + auto contents = strip_space(b.contents); assem.getSymbols().at("v") = d; - auto res = assem.evaluateExpression(b.contents, b.line); + auto res = assem.evaluateExpression(contents, b.line); d = number(res); } mach.writeByte(d); diff --git a/src/script.cpp b/src/script.cpp index 03b0770..a18d934 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -105,9 +105,11 @@ std::any Scripting::to_any(sol::object const& obj) } if (isVec) { auto index = key.as(); - if(index > 0) { - vec.resize(index); - vec[index-1] = val.as(); + if (index >= StartIndex) { + if (vec.size() < index + 1 - StartIndex) { + vec.resize(index + 1 - StartIndex); + } + vec[index-StartIndex] = val.as(); } } else { auto s = key.as(); @@ -132,7 +134,7 @@ sol::object Scripting::to_object(std::any const& a) // TODO: Can we sol make this 'value' conversion? // return sol::make_object(lua, *av); sol::table t = lua.create_table(); - size_t i = 1; + size_t i = StartIndex; for (auto v : *av) { t[i++] = v; } diff --git a/src/script.h b/src/script.h index 2ead7a7..4d4ecc0 100644 --- a/src/script.h +++ b/src/script.h @@ -34,6 +34,8 @@ class Scripting sol::object to_object(std::any const& a); std::any to_any(sol::object const& obj); + static constexpr int StartIndex = 1; + private: std::unique_ptr luap; sol::state& lua;