# HG changeset patch # User William Astle # Date 1436841316 21600 # Node ID 71f507f404f1327f97d4b28c02d7b2aefd59bbe6 # Parent 8e25147c2aa871c6ec940849d6aece17c20771cc Add "testmode" pragma Add a pragma to allow testing more easily. Thanks to Erik G for the patch. diff -r 8e25147c2aa8 -r 71f507f404f1 lwasm/lwasm.c --- a/lwasm/lwasm.c Mon Jul 13 20:31:56 2015 -0600 +++ b/lwasm/lwasm.c Mon Jul 13 20:35:16 2015 -0600 @@ -27,6 +27,7 @@ #include #include #include +#include #include "lwasm.h" #include "instab.h" @@ -269,16 +270,98 @@ } } -void lwasm_register_error_real(asmstate_t *as, line_t *l, lwasm_errorcode_t err, const char *msg) +/* keeping this as a separate error output for stability in unit test scripts */ +void lwasm_error_testmode(line_t *cl, const char* msg, int fatal) +{ + cl -> as -> testmode_errorcount++; + fprintf(stderr, "line %d: %s : %s\n", cl->lineno, msg, cl->ltext); + if (fatal == 1) lw_error("aborting\n"); +} + +/* parse unit test input data from comment field */ +void lwasm_parse_testmode_comment(line_t *l, lwasm_testflags_t *flags, lwasm_errorcode_t *err, int *len, char **buf) +{ + *flags = 0; + + if (!l) + return; + + char* s = strstr(l -> ltext, ";."); + if (s == NULL) return; + + char* t = strstr(s, ":"); + if (t == NULL) + { + /* parse: ;.8E0FCE (emitted code) */ + + if (buf == NULL) return; + + int i; + *flags = TF_EMIT; + + s = s + 2; /* skip ;. prefix */ + t = s; + while (*t > 32) t++; + + if ((t - s) & 1) + { + lwasm_error_testmode(l, "bad test data (wrong length of hex chars)", 1); + return; + } + + *len = (t - s) / 2; + + t = lw_alloc(*len); + *buf = t; + + for (i = 0; i < *len; i++) + { + int val; + sscanf(s, "%2x", &val); + *t++ = (char) val; + s += 2; + } + } + else + { + /* parse: ;.E:1000 or ;.E:7 (warnings or errors) */ + *flags = TF_ERROR; + + char ch = toupper(*(t - 1)); + if (ch != 'E') lwasm_error_testmode(l, "bad test data (expected E: flag)", 1); + sscanf(t + 1, "%d", (int*) err); + } +} + +void lwasm_register_error_real(asmstate_t *as, line_t *l, lwasm_errorcode_t error_code, const char *msg) { lwasm_error_t *e; if (!l) return; + if (CURPRAGMA(l, PRAGMA_TESTMODE)) + { + lwasm_testflags_t flags; + lwasm_errorcode_t testmode_error_code; + lwasm_parse_testmode_comment(l, &flags, &testmode_error_code, NULL, NULL); + if (flags == TF_ERROR) + { + l -> len = 0; /* null out bogus line */ + l -> insn = -1; + l -> err_testmode = error_code; + if (testmode_error_code == error_code) return; /* expected error: ignore and keep assembling */ + + char buf[128]; + sprintf(buf, "wrong error code (%d)", error_code); + lwasm_error_testmode(l, buf, 0); + return; + } + } + e = lw_alloc(sizeof(lwasm_error_t)); - if (err >= 1000) + if (error_code >= 1000) { e->next = l->warn; l->warn = e; @@ -291,6 +374,7 @@ as->errorcount++; } + e -> code = error_code; e -> charpos = -1; e -> mess = lw_strdup(msg); @@ -984,11 +1068,11 @@ continue; for (e = cl -> err; e; e = e -> next) { - fprintf(stderr, "ERROR: %s\n", e -> mess); + fprintf(stderr, "ERROR: %s (%d)\n", e -> mess, e -> code); } for (e = cl -> warn; e; e = e -> next) { - fprintf(stderr, "WARNING: %s\n", e -> mess); + fprintf(stderr, "WARNING: %s (%d)\n", e -> mess, e -> code); } fprintf(stderr, "%s:%05d %s\n\n", cl -> linespec, cl -> lineno, cl -> ltext); } diff -r 8e25147c2aa8 -r 71f507f404f1 lwasm/lwasm.h --- a/lwasm/lwasm.h Mon Jul 13 20:31:56 2015 -0600 +++ b/lwasm/lwasm.h Mon Jul 13 20:35:16 2015 -0600 @@ -77,22 +77,23 @@ enum lwasm_pragmas_e { - PRAGMA_NONE = 0, // no pragmas in effect - PRAGMA_DOLLARNOTLOCAL = 0x0001, // dollar sign does not make a symbol local - PRAGMA_NOINDEX0TONONE = 0x0002, // do not change implicit 0,R to ,R - PRAGMA_UNDEFEXTERN = 0x0004, // undefined symbols are considered to be external - PRAGMA_CESCAPES = 0x0008, // allow C style escapes in fcc, fcs, fcn, etc. - PRAGMA_IMPORTUNDEFEXPORT = 0x0010, // imports symbol if undefined upon export - PRAGMA_PCASPCR = 0x0020, // treats ,PC as ,PCR instead of constant offset - PRAGMA_SHADOW = 0x0040, // allow macros to shadow builtin operations - PRAGMA_NOLIST = 0x0080, // don't show line in listing - PRAGMA_AUTOBRANCHLENGTH = 0x0100, // automatically select proper length for relative branches - PRAGMA_EXPORT = 0x0200, // export symbols by default, unless local - PRAGMA_SYMBOLNOCASE = 0x400, // symbols defined under this pragma are matched case insensitively - PRAGMA_CONDUNDEFZERO = 0x800, // treat undefined symbols as zero in conditionals during pass 1 - PRAGMA_6800COMPAT = 0x1000, // enable 6800 compatibility opcodes - PRAGMA_FORWARDREFMAX = 0x2000, // force incomplete references on pass 1 to maximum mode - PRAGMA_6809 = 0x4000 // 6809/6309 assembly mode + PRAGMA_NONE = 0, // no pragmas in effect + PRAGMA_DOLLARNOTLOCAL = 1 << 0, // dollar sign does not make a symbol local + PRAGMA_NOINDEX0TONONE = 1 << 1, // do not change implicit 0,R to ,R + PRAGMA_UNDEFEXTERN = 1 << 2, // undefined symbols are considered to be external + PRAGMA_CESCAPES = 1 << 3, // allow C style escapes in fcc, fcs, fcn, etc. + PRAGMA_IMPORTUNDEFEXPORT = 1 << 4, // imports symbol if undefined upon export + PRAGMA_PCASPCR = 1 << 5, // treats ,PC as ,PCR instead of constant offset + PRAGMA_SHADOW = 1 << 6, // allow macros to shadow builtin operations + PRAGMA_NOLIST = 1 << 7, // don't show line in listing + PRAGMA_AUTOBRANCHLENGTH = 1 << 8, // automatically select proper length for relative branches + PRAGMA_EXPORT = 1 << 9, // export symbols by default, unless local + PRAGMA_SYMBOLNOCASE = 1 << 10, // symbols defined under this pragma are matched case insensitively + PRAGMA_CONDUNDEFZERO = 1 << 11, // treat undefined symbols as zero in conditionals during pass 1 + PRAGMA_6800COMPAT = 1 << 12, // enable 6800 compatibility opcodes + PRAGMA_FORWARDREFMAX = 1 << 13, // force incomplete references on pass 1 to maximum mode + PRAGMA_6809 = 1 << 14, // 6809/6309 assembly mode + PRAGMA_TESTMODE = 1 << 15 // enable test mode (for internal unit testing) }; enum @@ -124,71 +125,77 @@ sectiontab_t *next; }; +typedef enum +{ + TF_EMIT = 1, + TF_ERROR = 2 +} lwasm_testflags_t; + typedef enum { - E_6309_INVALID, - E_6809_INVALID, - E_ALIGNMENT_INVALID, - E_BITNUMBER_UNRESOLVED, - E_BITNUMBER_INVALID, - E_BYTE_OVERFLOW, - E_CONDITION_P1, - E_DIRECTIVE_OS9_ONLY, - E_DIV0, - E_EXEC_ADDRESS, - E_FILL_INVALID, - E_IMMEDIATE_INVALID, - E_IMMEDIATE_UNRESOLVED, - E_EXPRESSION_BAD, - E_EXPRESSION_NOT_CONST, - E_EXPRESSION_NOT_RESOLVED, - E_FILE_OPEN, - E_FILENAME_MISSING, - E_INSTRUCTION_FAILED, - E_INSTRUCTION_SECTION, - E_LINE_ADDRESS, - E_LINED_ADDRESS, - E_MACRO_DUPE, - E_MACRO_ENDM, - E_MACRO_NONAME, - E_MACRO_RECURSE, - E_MODULE_IN, - E_MODULE_NOTIN, - E_NEGATIVE_BLOCKSIZE, - E_NEGATIVE_RESERVATION, - E_NW_8, - E_OPCODE_BAD, - E_OPERAND_BAD, - E_OBJTARGET_ONLY, - E_PADDING_BAD, - E_PRAGMA_UNRECOGNIZED, - E_REGISTER_BAD, - E_SECTION_END, - E_SECTION_EXTDEP, - E_SECTION_FLAG, - E_SECTION_NAME, - E_SECTION_TARGET, - E_SETDP_INVALID, - E_SETDP_NOT_CONST, - E_STRING_BAD, - E_STRUCT_DUPE, - E_STRUCT_NONAME, - E_STRUCT_NOSYMBOL, - E_STRUCT_RECURSE, - E_SYMBOL_BAD, - E_SYMBOL_DUPE, - E_SYMBOL_MISSING, - E_SYMBOL_UNDEFINED, - E_SYMBOL_UNDEFINED_EXPORT, - E_UNKNOWN_OPERATION, - E_USER_SPECIFIED, + E_6309_INVALID = 1, + E_6809_INVALID = 2, + E_ALIGNMENT_INVALID = 3, + E_BITNUMBER_UNRESOLVED = 4, + E_BITNUMBER_INVALID = 5, + E_BYTE_OVERFLOW = 6, + E_CONDITION_P1 = 7, + E_DIRECTIVE_OS9_ONLY = 8, + E_DIV0 = 9, + E_EXEC_ADDRESS = 10, + E_FILL_INVALID = 11, + E_IMMEDIATE_INVALID = 12, + E_IMMEDIATE_UNRESOLVED = 13, + E_EXPRESSION_BAD = 14, + E_EXPRESSION_NOT_CONST = 15, + E_EXPRESSION_NOT_RESOLVED = 16, + E_FILE_OPEN = 17, + E_FILENAME_MISSING = 18, + E_INSTRUCTION_FAILED = 19, + E_INSTRUCTION_SECTION = 20, + E_LINE_ADDRESS = 21, + E_LINED_ADDRESS = 22, + E_MACRO_DUPE = 23, + E_MACRO_ENDM = 24, + E_MACRO_NONAME = 25, + E_MACRO_RECURSE = 26, + E_MODULE_IN = 27, + E_MODULE_NOTIN = 28, + E_NEGATIVE_BLOCKSIZE = 29, + E_NEGATIVE_RESERVATION = 30, + E_NW_8 = 31, + E_OPCODE_BAD = 32, + E_OPERAND_BAD = 33, + E_OBJTARGET_ONLY = 34, + E_PADDING_BAD = 35, + E_PRAGMA_UNRECOGNIZED = 36, + E_REGISTER_BAD = 37, + E_SECTION_END = 38, + E_SECTION_EXTDEP = 39, + E_SECTION_FLAG = 40, + E_SECTION_NAME = 41, + E_SECTION_TARGET = 42, + E_SETDP_INVALID = 43, + E_SETDP_NOT_CONST = 44, + E_STRING_BAD = 45, + E_STRUCT_DUPE = 46, + E_STRUCT_NONAME = 47, + E_STRUCT_NOSYMBOL = 48, + E_STRUCT_RECURSE = 49, + E_SYMBOL_BAD = 50, + E_SYMBOL_DUPE = 51, + E_SYMBOL_MISSING = 52, + E_SYMBOL_UNDEFINED = 53, + E_SYMBOL_UNDEFINED_EXPORT = 54, + E_UNKNOWN_OPERATION = 55, + E_USER_SPECIFIED = 56, /* warnings must be 1000 or greater */ - W_DUPLICATE_SECTION = 1000, - W_ENDSTRUCT_WITHOUT = 1001, - W_NOT_SUPPORTED = 1002, - W_USER_SPECIFIED = 1003 + W_DUPLICATE_SECTION = 1000, + W_ENDSTRUCT_WITHOUT = 1001, + W_NOT_SUPPORTED = 1002, + W_USER_SPECIFIED = 1003 } lwasm_errorcode_t; typedef struct lwasm_error_s lwasm_error_t; @@ -242,6 +249,7 @@ int dpval; // direct page value lwasm_error_t *err; // list of errors lwasm_error_t *warn; // list of errors + lwasm_errorcode_t err_testmode; // error code in testmode line_t *prev; // previous line line_t *next; // next line int inmod; // inside a module? @@ -340,6 +348,7 @@ int pragmas; // pragmas currently in effect int errorcount; // number of errors encountered int warningcount; // number of warnings issued + int testmode_errorcount; // number of errors in testmode int inmacro; // are we in a macro? int instruct; // are w in a structure? int skipcond; // skipping a condition? @@ -390,6 +399,9 @@ struct symtabe *register_symbol(asmstate_t *as, line_t *cl, char *sym, lw_expr_t value, int flags); struct symtabe *lookup_symbol(asmstate_t *as, line_t *cl, char *sym); +void lwasm_parse_testmode_comment(line_t *cl, lwasm_testflags_t *flags, lwasm_errorcode_t *err, int *len, char **buf); +void lwasm_error_testmode(line_t *cl, const char* msg, int fatal); + void lwasm_register_error(asmstate_t *as, line_t *cl, lwasm_errorcode_t err); void lwasm_register_error2(asmstate_t *as, line_t *cl, lwasm_errorcode_t err, const char* fmt, ...); diff -r 8e25147c2aa8 -r 71f507f404f1 lwasm/main.c --- a/lwasm/main.c Mon Jul 13 20:31:56 2015 -0600 +++ b/lwasm/main.c Mon Jul 13 20:35:16 2015 -0600 @@ -361,5 +361,8 @@ } do_list(&asmstate); do_map(&asmstate); + + if (asmstate.testmode_errorcount > 0) exit(1); + exit(0); } diff -r 8e25147c2aa8 -r 71f507f404f1 lwasm/pass1.c --- a/lwasm/pass1.c Mon Jul 13 20:31:56 2015 -0600 +++ b/lwasm/pass1.c Mon Jul 13 20:35:16 2015 -0600 @@ -263,6 +263,14 @@ } if (*tok) { + if (CURPRAGMA(cl, PRAGMA_TESTMODE)) + { + /* in test mode, terminate the line here so we don't affect the parsers */ + /* (cl -> ltext retains the full, unmodified string) */ + char *t = strstr(p1, ";."); + if (t) *t = 0; + } + // look up operation code lw_free(sym); sym = lw_strndup(tok, p1 - tok); diff -r 8e25147c2aa8 -r 71f507f404f1 lwasm/pass7.c --- a/lwasm/pass7.c Mon Jul 13 20:31:56 2015 -0600 +++ b/lwasm/pass7.c Mon Jul 13 20:35:16 2015 -0600 @@ -46,6 +46,30 @@ { (instab[cl -> insn].emit)(as, cl); } + + if (CURPRAGMA(cl, PRAGMA_TESTMODE)) + { + char* buf; + int len; + lwasm_testflags_t flags; + lwasm_errorcode_t err; + + lwasm_parse_testmode_comment(cl, &flags, &err, &len, &buf); + + if (flags == TF_ERROR && cl -> err_testmode == 0) + { + char s[128]; + sprintf(s, "expected %d but assembled OK", err); + lwasm_error_testmode(cl, s, 0); + } + + if (flags == TF_EMIT) + { + if (cl -> len != len) lwasm_error_testmode(cl, "incorrect assembly (wrong length)", 0); + if (memcmp(buf, cl -> output, len) != 0) lwasm_error_testmode(cl, "incorrect assembly", 0); + lw_free(buf); + } + } } } } diff -r 8e25147c2aa8 -r 71f507f404f1 lwasm/pragma.c --- a/lwasm/pragma.c Mon Jul 13 20:31:56 2015 -0600 +++ b/lwasm/pragma.c Mon Jul 13 20:35:16 2015 -0600 @@ -63,6 +63,7 @@ { "6809", "6309", PRAGMA_6809 }, { "6800compat", "no6800compat", PRAGMA_6800COMPAT }, { "forwardrefmax", "noforwardrefmax", PRAGMA_FORWARDREFMAX }, + { "testmode", "notestmode", PRAGMA_TESTMODE }, { 0, 0, 0} };