Improved assembler error reporting + stricter number validation
h3rald h3rald@h3rald.com
Fri, 06 Feb 2026 15:13:41 +0100
5 files changed,
68 insertions(+),
18 deletions(-)
M
examples/d6.xyw
→
examples/d6.xyw
@@ -1,7 +1,7 @@
.include "lib/macros.xyw" ; Get random number and calculate modulo 6 -system.random LDB $6 MOD +system.random LDB $06 MOD ; Print result+1 and new line $31 ADD PUTC
M
examples/factorial16.xyw
→
examples/factorial16.xyw
@@ -1,7 +1,7 @@
.include "lib/macros.xyw" ; Main program: calculate and print factorial of 7 -$00 $07 fact JSRw +$0007 fact JSRw PUTDw JSRw NL end JMPw@@ -14,14 +14,14 @@ POPxyw
.label fact.loop ; Need to work with 16-bit comparisons ; but JCN only works with 8-bit values, so remove high byte first - PSHyw $00 $00 EQUw LBYTE fact.end0 JCNw - PSHyw $00 $01 EQUw LBYTE fact.end1 JCNw + PSHyw $0000 EQUw LBYTE fact.end0 JCNw + PSHyw $0001 EQUw LBYTE fact.end1 JCNw ; Decrement y and multiply DECyw PSHxw PSHyw MULxw POPxw fact.loop JMPw .label fact.end0 ; Edge case: factorial of 0 is 1 - $1 + $0001 CLRxyw RTS .label fact.end1
M
examples/factorial8.xyw
→
examples/factorial8.xyw
@@ -1,7 +1,7 @@
.include "lib/macros.xyw" ; Main program: calculate and print factorial of 5 -$5 fact JSRw +$05 fact JSRw PUTD JSRw NL end JMPw@@ -12,14 +12,14 @@ .label fact
; Save argument to both registers POPxy .label fact.loop - PSHy $0 EQU fact.end0 JCNw - PSHy $1 EQU fact.end1 JCNw + PSHy $00 EQU fact.end0 JCNw + PSHy $01 EQU fact.end1 JCNw ; Decrement y and multiply DECy PSHx PSHy MULx POPx fact.loop JMPw .label fact.end0 ; Edge case: factorial of 0 is 1 - $1 + $01 CLRxy RTS .label fact.end1
M
examples/on-error.xyw
→
examples/on-error.xyw
@@ -4,18 +4,18 @@ ; register on error handler
on_error system.on_error STW ; main program -$10 $0 DIV +$10 $00 DIV end JMPw ; on error handler .label on_error - system.error LDB $1 EQU on_error.div-by-zero.print JCNw + system.error LDB $01 EQU on_error.div-by-zero.print JCNw unknown-error PUTS JSRw - $0 system.error STB + $00 system.error STB RTS .label on_error.div-by-zero.print div-by-zero-error PUTS JSRw - $3 system.error STB + $03 system.error STB RTS .include "lib/PUTS.xyw"
M
xywasm.c
→
xywasm.c
@@ -8,8 +8,8 @@ #define MAX_SYMBOLS 0x500
#define MAX_REFS 0x1000 #define error(msg) fprintf(stderr, "Error - %s [%s]\n", msg, ctx->file) -#define token_error(msg) fprintf(stderr, "Error - %s: %s [%s - Ln %d, Col %d]\n", token, msg, ctx->file, ctx->line, ctx->column) -#define symbol_error(symbol, msg) fprintf(stderr, "Error - %s: %s [%s - Ln %d, Col %d]\n", symbol, msg, ctx->file, ctx->line, ctx->column) +#define token_error(msg) fprintf(stderr, "Error - %s: %s [%s - Ln %d, Col %d]\n", token, msg, ctx->file, token_line, token_column) +#define symbol_error(symbol, msg) fprintf(stderr, "Error - %s: %s [%s - Ln %d, Col %d]\n", symbol, msg, ctx->file, token_line, token_column) #define PSH 0x01@@ -67,6 +67,8 @@ static char token[TOKEN_MAX_LENGTH];
static unsigned int token_length = 0; static xyw_token_type token_type = TOKEN_TYPE_UNKNOWN; static xyw_word token_value = 0; +static int token_line = 1; +static int token_column = 1; // Tracks whether we are inside a directive or not static unsigned int directive = 0;@@ -321,8 +323,16 @@ static int process_number(xyw_context *ctx)
{ xyw_word value = 0; unsigned int i = 0; + int is_word = 0; // Track if this is a word-sized number (based on digit count) if (token[0] == '$') { + unsigned int hex_digits = token_length - 1; + if (hex_digits != 2 && hex_digits != 4) + { + token_error("Hex numbers must have exactly 2 or 4 digits"); + return -1; + } + is_word = (hex_digits == 4); for (i = 1; i < token_length; i++) { char c = token[i];@@ -345,10 +355,17 @@ token_error("Invalid hex digit");
return -1; } } - xyw_dbg("Hex number:\t%s\t(%04X)\n", token, value); + xyw_dbg("Hex number:\t%s\t(%04X) [%s]\n", token, value, is_word ? "word" : "byte"); } else if (token[0] == '%') { + unsigned int bin_digits = token_length - 1; + if (bin_digits != 8) + { + token_error("Binary numbers must have exactly 8 digits"); + return -1; + } + is_word = 0; // Binary numbers are always bytes (8 bits) for (i = 1; i < token_length; i++) { char c = token[i];@@ -363,7 +380,7 @@ token_error("Invalid binary digit");
return -1; } } - xyw_dbg("Binary number:\t%s\t(%04X)\n", token, value); + xyw_dbg("Binary number:\t%s\t(%04X) [byte]\n", token, value); } else {@@ -374,7 +391,18 @@ token_type = TOKEN_TYPE_NUMBER;
token_value = value; if (!directive) { - push(value); + // Use digit count to determine byte vs word, not value + if (is_word) + { + append(PSH | XYW_MODE_W); + XYW_POKEW(&data[ptr], value); + ptr += 2; + } + else + { + append(PSH); + data[ptr++] = (xyw_byte)value; + } } return 0; }@@ -452,10 +480,16 @@ xyw_symbol *sym = &symbols[i];
if (sym->type == SYMBOL_TYPE_MACRO) { xyw_dbg("Macro:\t\t%s -> %s\n", token, sym->contents); + // Save context position before macro expansion + int saved_line = ctx->line; + int saved_column = ctx->column; ctx->sp = sym->contents; assemble_string(ctx); ctx->src = SRC_FILE; ctx->sp = NULL; + // Restore context position after macro expansion + ctx->line = saved_line; + ctx->column = saved_column; } else if (sym->type == SYMBOL_TYPE_LABEL) {@@ -719,6 +753,7 @@ {
char c; int in_string = 0; int in_comment = 0; + int token_started = 0; token_length = 0; token[0] = '\0'; while (1)@@ -773,6 +808,12 @@ // Not storing comment context in token for now
} else if (c == '"') { + if (!token_started) + { + token_line = ctx->line; + token_column = ctx->column; + token_started = 1; + } ctx->column++; if (token_length > STRING_MAX_LENGTH) {@@ -866,6 +907,9 @@ token[token_length] = '\0';
return TOKEN_END_COMMENT; } in_comment = 1; + token_line = ctx->line; + token_column = ctx->column; + token_started = 1; ctx->column++; // Start of comment token token[token_length++] = (char)c;@@ -883,6 +927,12 @@ }
// Non-whitespace, non-control characters else if (c > 0x20 && c != 0x7f) { + if (!token_started) + { + token_line = ctx->line; + token_column = ctx->column; + token_started = 1; + } ctx->column++; if (token_length > TOKEN_MAX_LENGTH) {