diff --git a/README.md b/README.md index 2ba5fe7..cefd534 100644 --- a/README.md +++ b/README.md @@ -103,8 +103,9 @@ 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. **ORG** @@ -260,7 +261,7 @@ Function_Name: { rts ``` -**#IF/#ELSE/#IFDEF/#ELIF/#ENDIF** +**#IF / #ELSE / #IFDEF / #ELIF / #ENDIF** Conditional code parsing is very similar to C directive conditional compilation. @@ -317,6 +318,40 @@ EVAL(28): "Mixed.things.thing_two.pointer" = $6 EVAL(29): "Mixed.things.thing_one.count" = $2 ``` +**REPT** + +Repeat a scoped block of code a number of times. The syntax is rept \ { \ }. + +Example: + +``` +columns = 40 +rows = 25 +screen_col = $400 +height_buf = $1000 +rept columns { + screen_addr = screen_col + ldx height_buf + dest = screen_addr + remainder = 3 + rept (rows+remainder)/4 { + stx dest + dest = dest + 4*40 + } + rept 3 { + inx + remainder = remainder-1 + screen_addr = screen_addr + 40 + dest = screen_addr + rept (rows+remainder)/4 { + stx dest + dest = dest + 4*40 + } + } + screen_col = screen_col+1 + height_buf = height_buf+1 +} +``` ## Expression syntax @@ -330,6 +365,13 @@ Expressions contain values, such as labels or raw numbers and operators includin * $: Preceeds hexadecimal value * %: If immediately followed by '0' or '1' this is a binary value and not scope closure address +Example: + +``` +lda #(((>SCREEN_MATRIX)&$3c)*4)+8 +sta $d018 +``` + ## Macros A macro can be defined by the using the directive macro and includes the line within the following scope: @@ -405,9 +447,10 @@ Currently the assembler is in the first public revision and while features are t **TODO** * Macro parameters should replace only whole words instead of any substring * Add 'import' directive as a catch-all include/incbin/etc. alternative -* rept / irp macro helpers (repeat, indefinite repeat) +* irp (indefinite repeat) **FIXED** +* [REPT](#rept) * fixed a flaw in expressions that ignored the next operator after raw hex values if no whitespace * expressions now handles high byte/low byte (\>, \<) as RPN tokens and not special cased. * structs @@ -420,9 +463,10 @@ Currently the assembler is in the first public revision and while features are t * TEXT directive converts ascii to petscii (respect uppercase or lowercase petscii) (simplistic) Revisions: -* 2015-10-04 Added STRUCT directive, sorted functions by grouping a bit more, bug fixes +* 2015-10-04 Added [REPT](#rept) directive +* 2015-10-04 Added [STRUCT](#struct) directive, sorted functions by grouping a bit more, bug fixes * 2015-10-02 Cleanup hid an error (#else without #if), exit with nonzero if error was encountered -* 2015-10-02 General cleanup, wrapping conditional assembly in functions -* 2015-10-01 Added Label Pools and conditional assembly +* 2015-10-02 General cleanup, wrapping [conditional assembly](#conditional) in functions +* 2015-10-01 Added [Label Pools](#pool) and conditional assembly * 2015-09-29 Moved Asm6502 out of Struse Samples. * 2015-09-28 First commit diff --git a/asm6502.cpp b/asm6502.cpp index d80d3c1..267ce73 100644 --- a/asm6502.cpp +++ b/asm6502.cpp @@ -84,6 +84,7 @@ enum StatusCode { ERROR_STRUCT_ALREADY_DEFINED, ERROR_REFERENCED_STRUCT_NOT_FOUND, ERROR_BAD_TYPE_FOR_DECLARE_CONSTANT, + ERROR_REPT_COUNT_EXPRESSION, ERROR_STOP_PROCESSING_ON_HIGHER, // errors greater than this will stop execution @@ -98,6 +99,7 @@ enum StatusCode { ERROR_ELSE_WITHOUT_IF, ERROR_STRUCT_CANT_BE_ASSEMBLED, ERROR_UNTERMINATED_CONDITION, + ERROR_REPT_MISSING_SCOPE, STATUSCODE_COUNT }; @@ -129,9 +131,10 @@ const char *aStatusStrings[STATUSCODE_COUNT] = { "Struct already defined", "Referenced struct not found", "Declare constant type not recognized (dc.?)", - + "rept count expression could not be evaluated", + "Errors after this point will stop execution", - + "Target address must evaluate immediately for this operation", "Scoping is too deep", "Unbalanced scope closure", @@ -143,6 +146,7 @@ const char *aStatusStrings[STATUSCODE_COUNT] = { "#else or #elif outside conditional block", "Struct can not be assembled as is", "Conditional assembly (#if/#ifdef) was not terminated in file or macro", + "rept is missing a scope ('{ ... }')", }; // Assembler directives @@ -168,6 +172,7 @@ enum AssemblerDirective { AD_ELIF, // #ELIF: Otherwise conditional assembly follows AD_ENDIF, // #ENDIF: End a block of #IF/#IFDEF AD_STRUCT, // STRUCT: Declare a set of labels offset from a base address + AD_REPT, // REPT: Repeat the assembly of the bracketed code a number of times }; // Operators are either instructions or directives @@ -465,15 +470,6 @@ typedef struct { strref source_file; // entire source file (req. for line #) } Macro; -// Source context is current file (include file, etc.) or current macro. -typedef struct { - strref source_name; // source file name (error output) - strref source_file; // entire source file (req. for line #) - strref code_segment; // the segment of the file for this context - strref read_source; // current position/length in source file - strref next_source; // next position/length in source file -} SourceContext; - // All local labels are removed when a global label is defined but some when a scope ends typedef struct { strref label; @@ -508,6 +504,18 @@ typedef struct { unsigned short size; } LabelStruct; +// Source context is current file (include file, etc.) or current macro. +typedef struct { + strref source_name; // source file name (error output) + strref source_file; // entire source file (req. for line #) + strref code_segment; // the segment of the file for this context + strref read_source; // current position/length in source file + strref next_source; // next position/length in source file + int repeat; // how many times to repeat this code segment + void restart() { read_source = code_segment; } + bool complete() { repeat--; return repeat <= 0; } +} SourceContext; + // Context stack is a stack of currently processing text class ContextStack { private: @@ -516,7 +524,7 @@ private: public: ContextStack() : currContext(nullptr) { stack.reserve(32); } SourceContext& curr() { return *currContext; } - void push(strref src_name, strref src_file, strref code_seg) { + void push(strref src_name, strref src_file, strref code_seg, int rept=1) { if (currContext) currContext->read_source = currContext->next_source; SourceContext context; @@ -525,6 +533,7 @@ public: context.code_segment = code_seg; context.read_source = code_seg; context.next_source = code_seg; + context.repeat = rept; stack.push_back(context); currContext = &stack[stack.size()-1]; } @@ -1724,6 +1733,7 @@ DirectiveName aDirectiveNames[] { { "#ELIF", AD_ELIF }, { "#ENDIF", AD_ENDIF }, { "STRUCT", AD_STRUCT }, + { "REPT", AD_REPT }, }; static const int nDirectiveNames = sizeof(aDirectiveNames) / sizeof(aDirectiveNames[0]); @@ -2021,13 +2031,36 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc error = ERROR_STRUCT_CANT_BE_ASSEMBLED; break; } - + case AD_REPT: { + SourceContext &ctx = contextStack.curr(); + strref read_source = ctx.read_source; + if (read_source.is_substr(line.get())) { + read_source.skip(strl_t(line.get() - read_source.get())); + int block = read_source.find('{'); + if (block<0) { + error = ERROR_REPT_MISSING_SCOPE; + break; + } + strref expression = read_source.get_substr(0, block); + read_source += block; + read_source.skip_whitespace(); + expression.trim_whitespace(); + int count; + if (STATUS_OK != EvalExpression(expression, address, + scope_address[scope_depth], -1, count)) { + error = ERROR_REPT_COUNT_EXPRESSION; + break; + } + strref recur = read_source.scoped_block_skip(); + ctx.next_source = read_source; + contextStack.push(ctx.source_name, ctx.source_file, recur, count); + } + break; + } } return error; } - - int sortHashLookup(const void *A, const void *B) { const OP_ID *_A = (const OP_ID*)A; const OP_ID *_B = (const OP_ID*)B; @@ -2472,7 +2505,10 @@ void Asm::Assemble(strref source, strref filename) scope_address[scope_depth] = address; while (contextStack.has_work()) { error = BuildSegment(pInstr, numInstructions); - contextStack.pop(); + if (contextStack.curr().complete()) + contextStack.pop(); + else + contextStack.curr().restart(); } if (error == STATUS_OK) { error = CheckLateEval();