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();