/* * Miniasm * * Copyright (c) 2010 Benjamin Moody * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include "miniasm.h" #ifdef __GNUC__ # define UNUSED __attribute__((unused)) # define PRINTF_ARG(a,b) __attribute__((format(printf,a,b))) #else # define UNUSED # define PRINTF_ARG(a,b) #endif #define HASH_MODULUS 16381 #define MAX_PASSES 32 #define MAX_SUBEXPS 3 enum { BYTE = 1, SBYTE, REL, WORD, BIT, REG, PREF }; enum { PARSE_NORMAL = 1, PARSE_ARGUMENT = 2, PARSE_INDEX = 3 }; enum { GENERAL_ERROR, ERROR, WARNING, MESSAGE }; typedef unsigned char byte; typedef unsigned short word; typedef signed long dword; typedef struct _iforminfo { const char* pattern; dword opcode; int length; byte args[2 * MAX_SUBEXPS]; } iforminfo; typedef struct _instinfo { const char* name; const iforminfo* forms; } instinfo; typedef struct _dirinfo { const char* name; unsigned need_label : 1; unsigned need_arg : 2; void (*func)(asctx*, char*, char*); } dirinfo; typedef struct _symbol { char* name; struct _symbol* next; dword value; int pass; int firstpass; unsigned set : 1; unsigned equated : 1; unsigned exported : 1; } symbol; typedef struct _filecache { char* filename; int nlines; int nlines_a; char** lines; struct _filecache* next; } filecache; typedef struct _macrodef { char* name; int nparams; int vaparam; char** param_names; char** lines; int nlines; int pass; struct _macrodef* next; } macrodef; typedef struct _includeinfo { asfile* file; char* filename; int linenum; int clinenum; int conditional_skip; int conditional_run; macrodef* defining_macro; filecache* cache; struct _includeinfo* next; } includeinfo; struct _asctx { void* user_data; unsigned long flags; includeinfo* incstack; int passnum; int again; int final; int nerrors; int nwarnings; int liston; int ignoreundef; byte* data; dword start; dword end; dword end_a; dword data_write_addr; dword data_write_count; dword pc; dword rpc; byte charvalues[256]; symbol* symtab[HASH_MODULUS]; filecache* fctab[HASH_MODULUS]; macrodef* macrotab[HASH_MODULUS]; }; /**************** Character types ****************/ /* All non-ASCII characters are considered word constituents */ #define IS_WORD_START(c) (((c) & 0x80) \ || ((c) >= 'A' && (c) <= 'Z') \ || ((c) >= 'a' && (c) <= 'z') \ || (c) == '_' \ || (c) == '@' \ || (c) == '.') #define IS_DIGIT(c) ((c) >= '0' && (c) <= '9') #define IS_XDIGIT(c) (IS_DIGIT(c) \ || ((c) >= 'A' && (c) <= 'F') \ || ((c) >= 'a' && (c) <= 'f')) #define IS_WORD(c) (IS_WORD_START(c) || IS_DIGIT(c)) #define IS_COMMENT(c) ((c) == ';') #define IS_SPACE(c) ((c) == ' ' || (c) == '\t' \ || (c) == '\n' || (c) == '\v' \ || (c) == '\f' || (c) == '\r') #define TO_LOWER(c) (((c) >= 'A' && (c) <= 'Z') \ ? (c) + 'a' - 'A' \ : (c)) static const char* get_word_end(const char* start) { const char* p = start; while (*p) { if (IS_SPACE(*p)) { start = p; p++; while (IS_SPACE(*p)) p++; if (p[0] != '#' || p[1] != '#') return start; p += 2; while (IS_SPACE(*p)) p++; if (!IS_WORD(*p)) return start; } else if (p[0] == '#' && p[1] == '#') p++; else if (!IS_WORD(*p)) return p; p++; } return p; } static char* collapse_word(char* word) { char *p, *q; q = p = word; while (*p) { if (IS_WORD(*p)) { *q = *p; q++; } p++; } *q = 0; return word; } /**************** Memory allocation ****************/ #define xnew(ttt, nnn) ((ttt*) xrealloc(ctx, 0, (nnn) * sizeof(ttt))) #define xrenew(ttt, ppp, nnn) ((ttt*) xrealloc(ctx, (ppp), (nnn) * sizeof(ttt))) #define xfree(ppp) xrealloc(0, (ppp), 0) static void* xrealloc(asctx* ctx, void* p, unsigned long sz) { if (sz) { if (p) p = realloc(p, sz); else p = malloc(sz); if (!p) { char buf[256]; sprintf(buf, "Error: Out of memory (need %lu bytes)\n", sz); miniasm_write_message(buf, ctx->user_data); abort(); } } else { if (p) free(p); p = 0; } return p; } #define xstrdup(sss) xstrndup(ctx, (sss), strlen((sss))) static char* xstrndup(asctx* ctx, const char* s, int len) { int i; char* p = xnew(char, len + 1); for (i = 0; i < len; i++) p[i] = s[i]; p[len] = 0; return p; } static char* chomp(char* p) { int i; for (i = 0; p[i]; i++) ; while (i > 0 && IS_SPACE(p[i-1])) { p[i-1] = 0; i--; } return p; } /**************** Error reporting ****************/ static void PRINTF_ARG(3, 4) message(asctx* ctx, int type, const char* fmt, ...) { va_list ap; char* s; unsigned len = 255; s = xnew(char, len + 1); s[len] = 0; /* This is a bit of a nasty hack to work around the various incompatible versions of snprintf. We ignore snprintf's return value, and instead, simply double the size of the buffer until we're sure the output string fits. */ while (1) { if (type == GENERAL_ERROR) { ctx->nerrors++; snprintf(s, len, "Error: "); } else if (type == ERROR) { ctx->nerrors++; snprintf(s, len, "%s:%d: error: ", ctx->incstack->filename, ctx->incstack->linenum); } else if (type == WARNING) { ctx->nwarnings++; snprintf(s, len, "%s:%d: warning: ", ctx->incstack->filename, ctx->incstack->linenum); } else snprintf(s, len, "%s:%d: ", ctx->incstack->filename, ctx->incstack->linenum); if (strlen(s) < len - 1) { va_start(ap, fmt); vsnprintf(s + strlen(s), len - strlen(s), fmt, ap); va_end(ap); } if (strlen(s) < len - 1) { miniasm_write_message(s, ctx->user_data); xfree(s); return; } len = (len * 2) + 1; s = xrenew(char, s, len + 1); s[len] = 0; } } /**************** Symbol table ****************/ static symbol* get_symbol_n(asctx* ctx, const char* name, int n) { unsigned int hash = 0; int i; symbol* s; for (i = 0; i < n; i++) hash = (hash * 53) + name[i]; hash %= HASH_MODULUS; for (s = ctx->symtab[hash]; s; s = s->next) if (!strncmp(s->name, name, n) && s->name[n] == 0) return s; s = xnew(symbol, 1); s->name = xstrndup(ctx, name, n); s->set = 0; s->equated = 0; s->exported = 0; s->value = 0; s->pass = -1; s->firstpass = ctx->passnum; s->next = ctx->symtab[hash]; ctx->symtab[hash] = s; return s; } static symbol* get_symbol(asctx* ctx, const char* name) { return get_symbol_n(ctx, name, strlen(name)); } static void set_equate(asctx* ctx, symbol* sym, dword value) { if (sym->set && !sym->equated) { message(ctx, ERROR, "Label '%s' redefined with EQU after SET", sym->name); } else if (sym->pass == ctx->passnum) { message(ctx, ERROR, "Label '%s' already defined", sym->name); } if (!sym->set || sym->value != value) ctx->again = 1; sym->set = 1; sym->equated = 1; sym->pass = ctx->passnum; sym->value = value; } static void set_variable_equate(asctx* ctx, symbol* sym, dword value) { if (sym->set && sym->equated) { message(ctx, ERROR, "Label '%s' redefined with SET after EQU", sym->name); } sym->set = 1; sym->equated = 0; sym->pass = ctx->passnum; sym->value = value; } static int get_equate_defined(asctx* ctx, symbol* sym) { return (sym->set && (sym->equated || sym->pass == ctx->passnum)); } static int get_equate_value(asctx* ctx, symbol* sym, dword* value) { if (!get_equate_defined(ctx, sym)) { if (!ctx->ignoreundef) { message(ctx, ERROR, "Undefined label '%s'", sym->name); *value = 0; return 1; } else { *value = 0; return 0; } } *value = sym->value; return 0; } /**************** File cache ****************/ static filecache* cache_find(asctx* ctx, const char* filename) { unsigned int hash = 0; filecache* c; int i; for (i = 0; filename[i]; i++) hash = (hash * 53) + filename[i]; hash %= HASH_MODULUS; for (c = ctx->fctab[hash]; c; c = c->next) if (!strcmp(c->filename, filename)) return c; return NULL; } static filecache* cache_read(asctx* ctx, const char* filename, asfile* f) { unsigned int hash = 0; filecache* c; char* line; int i; for (i = 0; filename[i]; i++) hash = (hash * 53) + filename[i]; hash %= HASH_MODULUS; for (c = ctx->fctab[hash]; c; c = c->next) if (!strcmp(c->filename, filename)) break; if (!c) { c = xnew(filecache, 1); c->filename = xstrdup(filename); c->nlines = 0; c->nlines_a = 64; c->lines = xnew(char*, c->nlines_a); c->next = ctx->fctab[hash]; ctx->fctab[hash] = c; } while ((line = miniasm_gets(f, ctx->user_data))) { if (c->nlines >= c->nlines_a) { c->nlines_a *= 2; c->lines = xrenew(char*, c->lines, c->nlines_a); } c->lines[c->nlines] = line; c->nlines++; } return c; } static void cache_free(filecache* c) { int i; for (i = 0; i < c->nlines; i++) miniasm_gets_free(c->lines[i]); xfree(c->lines); xfree(c->filename); xfree(c); } /**************** Macro definitions ****************/ static macrodef* lookup_macro(asctx* ctx, const char* name) { unsigned int hash = 0; macrodef* m; int i; for (i = 0; name[i]; i++) hash = (hash * 53) + name[i]; hash %= HASH_MODULUS; for (m = ctx->macrotab[hash]; m; m = m->next) if (!strcmp(m->name, name)) return m; return NULL; } static macrodef* define_macro(asctx* ctx, const char* name) { unsigned int hash = 0; macrodef* m; int i; for (i = 0; name[i]; i++) hash = (hash * 53) + name[i]; hash %= HASH_MODULUS; m = xnew(macrodef, 1); m->name = xstrdup(name); m->nparams = 0; m->vaparam = 0; m->param_names = NULL; m->lines = NULL; m->nlines = 0; m->pass = ctx->passnum; m->next = ctx->macrotab[hash]; ctx->macrotab[hash] = m; return m; } static void macro_free(macrodef* mac) { int i; for (i = 0; i < mac->nparams; i++) xfree(mac->param_names[i]); xfree(mac->param_names); for (i = 0; i < mac->nlines; i++) xfree(mac->lines[i]); xfree(mac->lines); xfree(mac->name); xfree(mac); } /**************** Inclusion stack ****************/ static int push_file(asctx* ctx, const char* filename, int local) { includeinfo* inc = xnew(includeinfo, 1); asfile* parent; if (ctx->incstack) parent = ctx->incstack->file; else parent = NULL; inc->file = NULL; inc->cache = cache_find(ctx, filename); if (!inc->cache) { inc->file = miniasm_open(filename, parent, local, ctx->user_data); if (!inc->file && !inc->cache) { xfree(inc); return 1; } if (ctx->flags & MINIASM_CACHE_ALL_FILES || miniasm_rewind(inc->file, ctx->user_data)) { inc->cache = cache_read(ctx, filename, inc->file); miniasm_close(inc->file, ctx->user_data); inc->file = NULL; } } inc->filename = xstrdup(filename); inc->linenum = inc->clinenum = 0; inc->conditional_run = inc->conditional_skip = 0; inc->defining_macro = NULL; inc->next = ctx->incstack; ctx->incstack = inc; return 0; } static void pop_file(asctx* ctx) { includeinfo* inc; if (ctx->incstack->file) miniasm_close(ctx->incstack->file, ctx->user_data); xfree(ctx->incstack->filename); inc = ctx->incstack; ctx->incstack = ctx->incstack->next; xfree(inc); } /**************** Pattern matching ****************/ static const char *skip_quoted(const char* s) { char q; if (*s == '"' || *s == '\'') { q = *s; s++; while (*s && *s != q) { if (*s == '\\' && s[1]) s++; s++; } } if (*s) s++; return s; } static int pattern_match(asctx* ctx, const char* pattern, const char* string, char** subexps) { const char* p; char* q; char x, y; int i, n; for (i = 0; i < MAX_SUBEXPS; i++) subexps[i] = NULL; i = 0; if (!string) { if (*pattern) return 0; else return 1; } while (*pattern) { while (IS_SPACE(*string)) string++; if (*pattern == '*') { if (!pattern[1]) { q = xstrdup(string); string = ""; } else { p = string; while (*string && *string != pattern[1]) { if (*string == '(') { n = 1; string++; while (*string && n > 0) { if (*string == ')') n--; else if (*string == '(') n++; string = skip_quoted(string); } } else string = skip_quoted(string); } if (*string != pattern[1]) return 0; q = xstrndup(ctx, p, string-p); } subexps[i] = q; i++; } else if (*pattern == '!' || *pattern == '?' || *pattern == '#') { x = TO_LOWER(*string); if (x == 'a') p = "7"; else if (x == 'b') p = "0"; else if (x == 'c') p = "1"; else if (x == 'd') p = "2"; else if (x == 'e') p = "3"; else if (x == 'h' && *pattern != '#') p = "4"; else if (x == 'l' && *pattern != '#') p = "5"; else if (x == '(' && *pattern == '!') { do string++; while (IS_SPACE(*string)); if (TO_LOWER(*string) != 'h') return 0; string++; if (TO_LOWER(*string) != 'l') return 0; do string++; while (IS_SPACE(*string)); if (*string != ')') return 0; p = "6"; } else return 0; string++; q = xstrdup(p); subexps[i] = q; i++; } else if (*pattern == '$' || *pattern == '@') { if (!(x = TO_LOWER(*string))) return 0; string++; if (!(y = TO_LOWER(*string))) return 0; string++; if (x == 'b' && y == 'c') p = "0"; else if (x == 'd' && y == 'e') p = "2"; else if (x == 'h' && y == 'l') p = "4"; else if (x == 's' && y == 'p' && *pattern == '$') p = "6"; else if (x == 'a' && y == 'f' && *pattern == '@') p = "6"; else return 0; q = xstrdup(p); subexps[i] = q; i++; } else if (*pattern == '&') { if (TO_LOWER(*string) != 'i') return 0; string++; x = TO_LOWER(*string); string++; if (x == 'x') p = "$DD"; else if (x == 'y') p = "$FD"; else return 0; q = xstrdup(p); subexps[i] = q; i++; } else if (*pattern == TO_LOWER(*string)) string++; else return 0; pattern++; } while (IS_SPACE(*string)) string++; if (*string) return 0; return 1; } /**************** Expression parsing ****************/ static dword binop_lshift(dword a, dword b) { return a << b; } static dword binop_rshift(dword a, dword b) { return a >> b; } static dword binop_lequal(dword a, dword b) { return (a <= b ? 1 : 0); } static dword binop_gequal(dword a, dword b) { return (a >= b ? 1 : 0); } static dword binop_equal(dword a, dword b) { return (a == b ? 1 : 0); } static dword binop_nequal(dword a, dword b) { return (a != b ? 1 : 0); } static dword binop_logand(dword a, dword b) { return (a && b ? 1 : 0); } static dword binop_logor(dword a, dword b) { return (a || b ? 1 : 0); } static dword binop_mult(dword a, dword b) { return a * b; } static dword binop_div(dword a, dword b) { return a / b; } static dword binop_mod(dword a, dword b) { return a % b; } static dword binop_add(dword a, dword b) { return a + b; } static dword binop_sub(dword a, dword b) { return a - b; } static dword binop_less(dword a, dword b) { return (a < b ? 1 : 0); } static dword binop_greater(dword a, dword b) { return (a > b ? 1 : 0); } static dword binop_and(dword a, dword b) { return a & b; } static dword binop_xor(dword a, dword b) { return a ^ b; } static dword binop_or(dword a, dword b) { return a | b; } static dword unop_not(dword a) { return (a ? 0 : 1); } static dword unop_cpl(dword a) { return ~a; } static dword unop_plus(dword a) { return a; } static dword unop_minus(dword a) { return -a; } /* Tokens (note this list is in the same order as the array below) */ enum { T_END = 0, T_LPAREN, T_RPAREN, T_LSHIFT, /* << */ T_RSHIFT, /* >> */ T_LEQUAL, /* <= */ T_GEQUAL, /* >= */ T_EQUAL2, /* == */ T_NOTEQUAL, /* != */ T_LOGAND, /* && */ T_LOGOR, /* || */ T_NOT, /* ! */ T_COMPLEMENT, /* ~ */ T_MULTIPLY, /* * */ T_DIVIDE, /* / */ T_MODULUS, /* % */ T_PLUS, /* + */ T_MINUS, /* - */ T_LESS, /* < */ T_GREATER, /* > */ T_EQUAL, /* = */ T_BITAND, /* & */ T_BITXOR, /* ^ */ T_BITOR, /* | */ T_CONSTANT, T_ERROR, NUM_TOKENS }; static const struct _token { char str[2]; int prec; const char* desc; dword (*unop)(dword); dword (*binop)(dword, dword); } tokens[NUM_TOKENS] = {{"", 0, "end of expression", NULL, NULL}, {"(", 0, "'('", NULL, NULL}, {")", 0, "')'", NULL, NULL}, /* two-character tokens come first so they'll be recognized */ {"<<", -3, "'<<'", NULL, &binop_lshift}, {">>", -3, "'>>'", NULL, &binop_rshift}, {"<=", -4, "'<='", NULL, &binop_lequal}, {">=", -4, "'>='", NULL, &binop_gequal}, {"==", -5, "'=='", NULL, &binop_equal}, {"!=", -5, "'!='", NULL, &binop_nequal}, {"&&", -9, "'&&'", NULL, &binop_logand}, {"||", -10, "'||'", NULL, &binop_logor}, /* now one-character tokens */ {"!", 0, "'!'", &unop_not, NULL}, {"~", 0, "'~'", &unop_cpl, NULL}, {"*", -1, "'*'", NULL, &binop_mult}, {"/", -1, "'/'", NULL, &binop_div}, {"%", -1, "'%'", NULL, &binop_mod}, {"+", -2, "'+'", &unop_plus, &binop_add}, {"-", -2, "'-'", &unop_minus, &binop_sub}, {"<", -4, "'<'", NULL, &binop_less}, {">", -4, "'>'", NULL, &binop_greater}, {"=", -5, "'='", NULL, &binop_equal}, {"&", -6, "'&'", NULL, &binop_and}, {"^", -7, "'^'", NULL, &binop_xor}, {"|", -8, "'|'", NULL, &binop_or}, {"", 0, "constant", 0, 0}, {"", 0, "invalid token", 0, 0}}; #define MIN_PRECEDENCE -10 static dword parse_number(asctx* ctx, const char* s, int len, dword* value, int base, const char* basestr) { char* p; *value = strtol(s, &p, base); if (p != s + len) { message(ctx, ERROR, "Invalid %s digit '%c'", basestr, *p); return 1; } return 0; } #define parse_hex(ccc, sss, lll, vvv) \ parse_number((ccc), (sss), (lll), (vvv), 16, "hexadecimal") #define parse_dec(ccc, sss, lll, vvv) \ parse_number((ccc), (sss), (lll), (vvv), 10, "decimal") #define parse_oct(ccc, sss, lll, vvv) \ parse_number((ccc), (sss), (lll), (vvv), 8, "octal") #define parse_bin(ccc, sss, lll, vvv) \ parse_number((ccc), (sss), (lll), (vvv), 2, "binary") static int parse_num_token(asctx* ctx, const char* p, dword* value) { const char* q = p; while (q[0] && q[1]) q++; if (*q == 'h' || *q == 'H') return parse_hex(ctx, p, q - p, value); else if (*q == 'o' || *q == 'O') return parse_oct(ctx, p, q - p, value); else if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) return parse_hex(ctx, p + 2, q - p - 1, value); else if (*q == 'd' || *q == 'D') return parse_dec(ctx, p, q - p, value); else if (*q == 'b' || *q == 'B') return parse_bin(ctx, p, q - p, value); else if (p[0] == '0' && (p[1] == 'b' || p[1] == 'B')) return parse_bin(ctx, p + 2, q - p - 1, value); else return parse_dec(ctx, p, q - p + 1, value); } static int parse_char(asctx* ctx, const char** p, dword* value, int* raw) { char c; char* q; if ((*p)[0] == '\\' && (*p)[1]) { (*p)++; switch (**p) { case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 't': c = '\t'; break; case 'n': c = '\n'; break; case 'v': c = '\v'; break; case 'f': c = '\f'; break; case 'r': c = '\r'; break; case 'x': c = strtol((*p) + 1, &q, 16); *p = q - 1; break; case ('0'): case '1': case '2': case ('3'): c = strtol(*p, &q, 8); *p = q - 1; break; default: c = **p; break; } (*p)++; } else { c = **p; (*p)++; } if (raw) *raw = c; if (!value) return 0; else { *value = ctx->charvalues[(int) (unsigned char) c]; if (c == 0 || *value) { return 0; } else { if (ctx->final) message(ctx, ERROR, "Invalid character '%c'", c); return 1; } } } static int parse_token(asctx* ctx, const char** p, dword* value) { int i; const char* q; char* str; symbol* sym; while (IS_SPACE(**p)) (*p)++; if (!**p) return 0; if (**p != '%' || ((*p)[1] != '0' && (*p)[1] != '1')) { for (i = 1; tokens[i].str[0]; i++) { if (tokens[i].str[0] == (*p)[0] && (tokens[i].str[1] == 0 || tokens[i].str[1] == (*p)[1])) { (*p)++; if (tokens[i].str[1]) (*p)++; *value = 0; return i; } } } if (IS_WORD_START(**p)) { q = get_word_end(*p); str = xstrndup(ctx, *p, q - *p); collapse_word(str); sym = get_symbol(ctx, str); xfree(str); if (get_equate_value(ctx, sym, value)) return T_ERROR; } else if (IS_DIGIT(**p)) { q = get_word_end(*p); str = xstrndup(ctx, *p, q - *p); collapse_word(str); if (parse_num_token(ctx, str, value)) { xfree(str); return T_ERROR; } xfree(str); } else if (**p == '$') { (*p)++; q = *p; while (IS_XDIGIT(*q)) q++; if (q == *p) { if (*q == '$') { q++; *value = ctx->pc; } else *value = ctx->rpc; } else if (parse_hex(ctx, *p, q - *p, value)) return T_ERROR; } else if (**p == '%') { (*p)++; q = *p; while (IS_DIGIT(*q)) q++; if (parse_bin(ctx, *p, q - *p, value)) return T_ERROR; } else if (**p == '\'') { (*p)++; q = *p; if (parse_char(ctx, &q, value, NULL)) return T_ERROR; if (*q != '\'') { message(ctx, ERROR, "Incomplete character constant"); return T_ERROR; } q++; } else { message(ctx, ERROR, "Invalid character '%c' in expression", **p); while (**p) (*p)++; *value = 0; return T_ERROR; } *p = q; return T_CONSTANT; } /* Expression grammar: EXPR[p] -> EXPR[p+1] | EXPR[p] BINARY-OP[p] EXPR[p+1] EXPR[0] -> UNARY-EXPR UNARY-EXPR -> UNARY-OP UNARY-EXPR | '(' EXPR ')' | CONSTANT */ static int parse_EXPR(asctx* ctx, const char** p, dword* value, int* topparen, int prec); static int parse_UNARYEXPR(asctx* ctx, const char** p, dword* value, int* topparen) { int token = parse_token(ctx, p, value); dword zz; switch (token) { case T_ERROR: return 1; case T_CONSTANT: if (topparen) *topparen = 0; return 0; case T_LPAREN: if (parse_EXPR(ctx, p, value, NULL, MIN_PRECEDENCE)) return 1; token = parse_token(ctx, p, &zz); if (token != T_RPAREN) { message(ctx, ERROR, "Unexpected %s (expecting ')')", tokens[token].desc); return 1; } if (topparen) *topparen = 1; return 0; default: if (tokens[token].unop) { if (parse_UNARYEXPR(ctx, p, value, NULL)) return 1; *value = (*tokens[token].unop)(*value); if (topparen) *topparen = 0; return 0; } else { message(ctx, ERROR, "Unexpected %s (expecting '(', value, or unary operator)", tokens[token].desc); return 1; } } } static int parse_EXPR(asctx* ctx, const char** p, dword* value, int* topparen, int prec) { dword valueb; const char* p2; int token; if (!prec) return parse_UNARYEXPR(ctx, p, value, topparen); if (parse_EXPR(ctx, p, value, topparen, prec+1)) return 1; while (1) { p2 = *p; token = parse_token(ctx, &p2, &valueb); if (token == T_ERROR) return 1; else if (!token || token == T_RPAREN) return 0; if (topparen) *topparen = 0; if (!tokens[token].binop) { message(ctx, ERROR, "Unexpected %s (expecting binary operator)", tokens[token].desc); return 1; } else if (tokens[token].prec == prec) { *p = p2; if (parse_EXPR(ctx, p, &valueb, NULL, prec+1)) return 1; *value = (*tokens[token].binop)(*value, valueb); } else return 0; } } static dword parse_exp(asctx* ctx, const char* exp, int mode) { dword value, valueb; int token; int topparen; if (mode == PARSE_INDEX && !*exp) return 0; else if (parse_EXPR(ctx, &exp, &value, &topparen, MIN_PRECEDENCE)) return 0; if (mode == PARSE_ARGUMENT && topparen) message(ctx, WARNING, "Extraneous parentheses in argument expression"); token = parse_token(ctx, &exp, &valueb); if (!token) return value; else if (token != T_ERROR) message(ctx, ERROR, "Unexpected %s at end of expression", tokens[token].desc); return 0; } /**************** Output ****************/ static dword format_arg(asctx* ctx, dword opcode, const char* exp, int argtype, int argshift) { dword min, max; dword value; dword arg; if (!ctx->final) return opcode; value = parse_exp(ctx, exp, (argtype == SBYTE ? PARSE_INDEX : PARSE_ARGUMENT)); switch (argtype) { case BYTE: min = -128; max = 255; arg = 0xff; break; case SBYTE: min = -128; max = 127; arg = 0xff; break; case REL: value -= ctx->rpc + 2; min = -128; max = 127; arg = 0xff; break; case WORD: min = -32768; max = 65535; arg = 0xffff; break; case BIT: case REG: min = 0; max = 7; arg = 7; break; case PREF: min = 0xdd; max = 0xfd; arg = 0xff; break; default: message(ctx, ERROR, "Internal error: bad argument type"); min = 0; max = 0; arg = 0; break; } if (value < min || value > max) message(ctx, ERROR, "Value %lXh exceeds range of argument", value); arg &= value; arg <<= argshift; if (arg & opcode) message(ctx, ERROR, "Invalid value %lXh", value); return (opcode | arg); } static void output_byte(asctx* ctx, dword addr, int value) { byte* ndp; if (!ctx->final) return; if (ctx->flags & MINIASM_MEM_PAGED) addr = ((addr & 0x3fff) | ((addr & 0xff0000) >> 2)); if (ctx->start == ctx->end) ctx->start = ctx->end = ctx->end_a = addr; if (addr < ctx->start) { ndp = xnew(byte, ctx->end_a - addr); memset(ndp, 0xff, ctx->start - addr); memcpy(ndp + ctx->start - addr, ctx->data, ctx->end - ctx->start); ctx->start = addr; } else if (addr >= ctx->end) { if (addr >= ctx->end_a) { ctx->end_a = addr + 1024; ctx->data = xrenew(byte, ctx->data, ctx->end_a - ctx->start); } while (ctx->end < addr) { ctx->data[ctx->end-ctx->start] = 0xff; ctx->end++; } ctx->end = addr + 1; } ctx->data[addr-ctx->start] = value; } static void output_code_byte(asctx* ctx, int value) { if (ctx->data_write_addr != ctx->pc - ctx->data_write_count) { ctx->data_write_addr = ctx->pc; ctx->data_write_count = 0; } output_byte(ctx, ctx->pc, value); ctx->pc++; ctx->rpc++; if (ctx->final) ctx->data_write_count++; } /**************** Instructions ****************/ /* Pattern wildcard characters: * normal argument expression ! register b, c, d, e, h, l, (hl), a ? register b, c, d, e, h, l, a # register b, c, d, e, a $ register bc, de, hl, sp @ register bc, de, hl, af & register ix, iy '*' may be used with argument types BYTE, SBYTE, REL, WORD, and BIT; the argument value is simply the value of the expression. '!', '?', and '#' must be used with argument type REG; the argument value is the register code, 0-7. These are usually used with shift values of 0, 3, 8 or 11. '$' and '@' must be used with argument type REG; the argument value is the register code, 0, 2, 4 or 6. These are used with shift values of 3 or 11. '&' must be used with argument type PREF; the argument value is the prefix code, DDh or FDh. These are used with shift value 0. */ static const iforminfo adc_forms[] = {{"a,!", 0x88, 1, { REG, 0, 0, 0, 0, 0}}, {"a,&h", 0x8c00, 2, { PREF, 0, 0, 0, 0, 0}}, {"a,&l", 0x8d00, 2, { PREF, 0, 0, 0, 0, 0}}, {"a,(&*)", 0x8e00, 3, { PREF, 0, SBYTE, 16, 0, 0}}, {"a,*", 0xce, 2, { BYTE, 8, 0, 0, 0, 0}}, {"hl,$", 0x4aed, 2, { REG, 11, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo add_forms[] = {{"a,!", 0x80, 1, { REG, 0, 0, 0, 0, 0}}, {"a,&h", 0x8400, 2, { PREF, 0, 0, 0, 0, 0}}, {"a,&l" , 0x8500, 2, { PREF, 0, 0, 0, 0, 0}}, {"a,(&*)", 0x8600, 3, { PREF, 0, SBYTE, 16, 0, 0}}, {"a,*", 0xc6, 2, { BYTE, 8, 0, 0, 0, 0}}, {"hl,$", 0x09, 1, { REG, 3, 0, 0, 0, 0}}, {"&,$", 0x0900, 2, { PREF, 0, REG, 11, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo and_forms[] = {{"!", 0xa0, 1, { REG, 0, 0, 0, 0, 0}}, {"&h", 0xa400, 2, { PREF, 0, 0, 0, 0, 0}}, {"&l", 0xa500, 2, { PREF, 0, 0, 0, 0, 0}}, {"(&*)", 0xa600, 3, { PREF, 0, SBYTE, 16, 0, 0}}, {"*", 0xe6, 2, { BYTE, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo bit_forms[] = {{"*,!", 0x40cb, 2, { BIT, 11, REG, 8, 0, 0}}, {"*,(&*)", 0x4600cb00, 4, { BIT, 27, PREF, 0, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo call_forms[] = {{"nz,*", 0xc4, 3, { WORD, 8, 0, 0, 0, 0}}, {"z,*", 0xcc, 3, { WORD, 8, 0, 0, 0, 0}}, {"nc,*", 0xd4, 3, { WORD, 8, 0, 0, 0, 0}}, {"c,*", 0xdc, 3, { WORD, 8, 0, 0, 0, 0}}, {"po,*", 0xe4, 3, { WORD, 8, 0, 0, 0, 0}}, {"pe,*", 0xec, 3, { WORD, 8, 0, 0, 0, 0}}, {"p,*", 0xf4, 3, { WORD, 8, 0, 0, 0, 0}}, {"m,*", 0xfc, 3, { WORD, 8, 0, 0, 0, 0}}, {"*", 0xcd, 3, { WORD, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo ccf_forms[] = {{"", 0x3f, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo cp_forms[] = {{"!", 0xb8, 1, { REG, 0, 0, 0, 0, 0}}, {"&h", 0xbc00, 2, { PREF, 0, 0, 0, 0, 0}}, {"&l", 0xbd00, 2, { PREF, 0, 0, 0, 0, 0}}, {"(&*)", 0xbe00, 3, { PREF, 0, SBYTE, 16, 0, 0}}, {"*", 0xfe, 2, { BYTE, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo cpd_forms[] = {{"", 0xa9ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo cpdr_forms[] = {{"", 0xb9ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo cpi_forms[] = {{"", 0xa1ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo cpir_forms[] = {{"", 0xb1ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo cpl_forms[] = {{"", 0x2f, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo daa_forms[] = {{"", 0x27, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo dec_forms[] = {{"!", 0x05, 1, { REG, 3, 0, 0, 0, 0}}, {"&h", 0x2500, 2, { PREF, 0, 0, 0, 0, 0}}, {"&l", 0x2d00, 2, { PREF, 0, 0, 0, 0, 0}}, {"(&*)", 0x3500, 3, { PREF, 0, SBYTE, 16, 0, 0}}, {"$", 0x0b, 1, { REG, 3, 0, 0, 0, 0}}, {"&", 0x2b00, 2, { PREF, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo di_forms[] = {{"", 0xf3, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo djnz_forms[] = {{"*", 0x10, 2, { REL, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo ei_forms[] = {{"", 0xfb, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo ex_forms[] = {{"af,af'", 0x08, 1, { 0, 0, 0, 0, 0, 0}}, {"(sp),hl", 0xe3, 1, { 0, 0, 0, 0, 0, 0}}, {"(sp),&", 0xe300, 2, { PREF, 0, 0, 0, 0, 0}}, {"de,hl", 0xeb, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo exx_forms[] = {{"", 0xd9, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo halt_forms[] = {{"", 0x76, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo im_forms[] = {{"0", 0x46ed, 2, { 0, 0, 0, 0, 0, 0}}, {"1", 0x56ed, 2, { 0, 0, 0, 0, 0, 0}}, {"2", 0x5eed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo in_forms[] = {{"?,(c)", 0x40ed, 2, { REG, 11, 0, 0, 0, 0}}, {"(c)", 0x70ed, 2, { 0, 0, 0, 0, 0, 0}}, {"f,(c)", 0x70ed, 2, { 0, 0, 0, 0, 0, 0}}, {"a,(*)", 0xdb, 2, { BYTE, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo inc_forms[] = {{"!", 0x04, 1, { REG, 3, 0, 0, 0, 0}}, {"&h", 0x2400, 2, { PREF, 0, 0, 0, 0, 0}}, {"&l", 0x2c00, 2, { PREF, 0, 0, 0, 0, 0}}, {"(&*)", 0x3400, 3, { PREF, 0, SBYTE, 16, 0, 0}}, {"$", 0x03, 1, { REG, 3, 0, 0, 0, 0}}, {"&", 0x2300, 2, { PREF, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo ind_forms[] = {{"", 0xaaed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo indr_forms[] = {{"", 0xbaed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo ini_forms[] = {{"", 0xa2ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo inir_forms[] = {{"", 0xb2ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo jp_forms[] = {{"nz,*", 0xc2, 3, { WORD, 8, 0, 0, 0, 0}}, {"z,*", 0xca, 3, { WORD, 8, 0, 0, 0, 0}}, {"nc,*", 0xd2, 3, { WORD, 8, 0, 0, 0, 0}}, {"c,*", 0xda, 3, { WORD, 8, 0, 0, 0, 0}}, {"po,*", 0xe2, 3, { WORD, 8, 0, 0, 0, 0}}, {"pe,*", 0xea, 3, { WORD, 8, 0, 0, 0, 0}}, {"p,*", 0xf2, 3, { WORD, 8, 0, 0, 0, 0}}, {"m,*", 0xfa, 3, { WORD, 8, 0, 0, 0, 0}}, {"(hl)", 0xe9, 1, { 0, 0, 0, 0, 0, 0}}, {"(&)", 0xe900, 2, { PREF, 0, 0, 0, 0, 0}}, {"*", 0xc3, 3, { WORD, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo jr_forms[] = {{"nz,*", 0x20, 2, { REL, 8, 0, 0, 0, 0}}, {"z,*", 0x28, 2, { REL, 8, 0, 0, 0, 0}}, {"nc,*", 0x30, 2, { REL, 8, 0, 0, 0, 0}}, {"c,*", 0x38, 2, { REL, 8, 0, 0, 0, 0}}, {"*", 0x18, 2, { REL, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo ld_forms[] = {{"?,!", 0x40, 1, { REG, 3, REG, 0, 0, 0}}, {"(hl),?", 0x70, 1, { REG, 0, 0, 0, 0, 0}}, {"#,&h", 0x4400, 2, { REG, 11, PREF, 0, 0, 0}}, {"#,&l", 0x4500, 2, { REG, 11, PREF, 0, 0, 0}}, {"?,(&*)", 0x4600, 3, { REG, 11, PREF, 0, SBYTE, 16}}, {"&h,#", 0x6000, 2, { PREF, 0, REG, 8, 0, 0}}, {"&l,#", 0x6800, 2, { PREF, 0, REG, 8, 0, 0}}, {"(&*),?", 0x7000, 3, { PREF, 0, SBYTE, 16, REG, 8}}, {"ixh,ixh", 0x64dd, 2, { 0, 0, 0, 0, 0, 0}}, {"ixh,ixl", 0x65dd, 2, { 0, 0, 0, 0, 0, 0}}, {"ixl,ixh", 0x6cdd, 2, { 0, 0, 0, 0, 0, 0}}, {"ixl,ixl", 0x6ddd, 2, { 0, 0, 0, 0, 0, 0}}, {"iyh,iyh", 0x64fd, 2, { 0, 0, 0, 0, 0, 0}}, {"iyh,iyl", 0x65fd, 2, { 0, 0, 0, 0, 0, 0}}, {"iyl,iyh", 0x6cfd, 2, { 0, 0, 0, 0, 0, 0}}, {"iyl,iyl", 0x6dfd, 2, { 0, 0, 0, 0, 0, 0}}, {"a,i", 0x57ed, 2, { 0, 0, 0, 0, 0, 0}}, {"a,r", 0x5fed, 2, { 0, 0, 0, 0, 0, 0}}, {"a,(bc)", 0x0a, 1, { 0, 0, 0, 0, 0, 0}}, {"a,(de)", 0x1a, 1, { 0, 0, 0, 0, 0, 0}}, {"i,a", 0x47ed, 2, { 0, 0, 0, 0, 0, 0}}, {"r,a", 0x4fed, 2, { 0, 0, 0, 0, 0, 0}}, {"(bc),a", 0x02, 1, { 0, 0, 0, 0, 0, 0}}, {"(de),a", 0x12, 1, { 0, 0, 0, 0, 0, 0}}, {"sp,hl", 0xf9, 1, { 0, 0, 0, 0, 0, 0}}, {"sp,&", 0xf900, 2, { PREF, 0, 0, 0, 0, 0}}, {"a,(*)", 0x3a, 3, { WORD, 8, 0, 0, 0, 0}}, {"hl,(*)", 0x2a, 3, { WORD, 8, 0, 0, 0, 0}}, {"&,(*)", 0x2a00, 4, { PREF, 0, WORD, 16, 0, 0}}, {"$,(*)", 0x4bed, 4, { REG, 11, WORD, 16, 0, 0}}, {"(*),a", 0x32, 3, { WORD, 8, 0, 0, 0, 0}}, {"(*),hl", 0x22, 3, { WORD, 8, 0, 0, 0, 0}}, {"(*),&", 0x2200, 4, { WORD, 16, PREF, 0, 0, 0}}, {"(*),$", 0x43ed, 4, { WORD, 16, REG, 11, 0, 0}}, {"!,*", 0x06, 2, { REG, 3, BYTE, 8, 0, 0}}, {"&h,*", 0x2600, 3, { PREF, 0, BYTE, 16, 0, 0}}, {"&l,*", 0x2e00, 3, { PREF, 0, BYTE, 16, 0, 0}}, {"(&*),*", 0x3600, 4, { PREF, 0, SBYTE, 16, BYTE, 24}}, {"$,*", 0x01, 3, { REG, 3, WORD, 8, 0, 0}}, {"&,*", 0x2100, 4, { PREF, 0, WORD, 16, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo ldd_forms[] = {{"", 0xa8ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo lddr_forms[] = {{"", 0xb8ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo ldi_forms[] = {{"", 0xa0ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo ldir_forms[] = {{"", 0xb0ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo neg_forms[] = {{"", 0x44ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo nop_forms[] = {{"", 0x00, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo or_forms[] = {{"!", 0xb0, 1, { REG, 0, 0, 0, 0, 0}}, {"&h", 0xb400, 2, { PREF, 0, 0, 0, 0, 0}}, {"&l", 0xb500, 2, { PREF, 0, 0, 0, 0, 0}}, {"(&*)", 0xb600, 3, { PREF, 0, SBYTE, 16, 0, 0}}, {"*", 0xf6, 2, { BYTE, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo otdr_forms[] = {{"", 0xbbed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo otir_forms[] = {{"", 0xb3ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo out_forms[] = {{"(c),?", 0x41ed, 2, { REG, 11, 0, 0, 0, 0}}, {"(c),0", 0x71ed, 2, { 0, 0, 0, 0, 0, 0}}, {"(*),a", 0xd3, 2, { BYTE, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo outd_forms[] = {{"", 0xabed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo outi_forms[] = {{"", 0xa3ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo pop_forms[] = {{"@", 0xc1, 1, { REG, 3, 0, 0, 0, 0}}, {"&", 0xe100, 2, { PREF, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo push_forms[] = {{"@", 0xc5, 1, { REG, 3, 0, 0, 0, 0}}, {"&", 0xe500, 2, { PREF, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo res_forms[] = {{"*,!", 0x80cb, 2, { BIT, 11, REG, 8, 0, 0}}, {"*,(&*)", 0x8600cb00, 4, { BIT, 27, PREF, 0, SBYTE, 16}}, {"*,?,(ix*)", 0x8000cbdd, 4, { BIT, 27, REG, 24, SBYTE, 16}}, {"*,?,(iy*)", 0x8000cbfd, 4, { BIT, 27, REG, 24, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo ret_forms[] = {{"", 0xc9, 1, { 0, 0, 0, 0, 0, 0}}, {"nz", 0xc0, 1, { 0, 0, 0, 0, 0, 0}}, {"z", 0xc8, 1, { 0, 0, 0, 0, 0, 0}}, {"nc", 0xd0, 1, { 0, 0, 0, 0, 0, 0}}, {"c", 0xd8, 1, { 0, 0, 0, 0, 0, 0}}, {"po", 0xe0, 1, { 0, 0, 0, 0, 0, 0}}, {"pe", 0xe8, 1, { 0, 0, 0, 0, 0, 0}}, {"p", 0xf0, 1, { 0, 0, 0, 0, 0, 0}}, {"m", 0xf8, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo reti_forms[] = {{"", 0x4ded, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo retn_forms[] = {{"", 0x45ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rl_forms[] = {{"!", 0x10cb, 2, { REG, 8, 0, 0, 0, 0}}, {"(&*)", 0x1600cb00, 4, { PREF, 0, SBYTE, 16, 0, 0}}, {"?,(&*)", 0x1000cb00, 4, { REG, 24, PREF, 0, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rla_forms[] = {{"", 0x17, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rlc_forms[] = {{"!", 0x00cb, 2, { REG, 8, 0, 0, 0, 0}}, {"(&*)", 0x0600cb00, 4, { PREF, 0, SBYTE, 16, 0, 0}}, {"?,(&*)", 0x0000cb00, 4, { REG, 24, PREF, 0, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rlca_forms[] = {{"", 0x07, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rld_forms[] = {{"", 0x6fed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rr_forms[] = {{"!", 0x18cb, 2, { REG, 8, 0, 0, 0, 0}}, {"(&*)", 0x1e00cb00, 4, { PREF, 0, SBYTE, 16, 0, 0}}, {"?,(&*)", 0x1800cb00, 4, { REG, 24, PREF, 0, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rra_forms[] = {{"", 0x1f, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rrc_forms[] = {{"!", 0x08cb, 2, { REG, 8, 0, 0, 0, 0}}, {"(&*)", 0x0e00cb00, 4, { PREF, 0, SBYTE, 16, 0, 0}}, {"?,(&*)", 0x0800cb00, 4, { REG, 24, PREF, 0, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rrca_forms[] = {{"", 0x0f, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rrd_forms[] = {{"", 0x67ed, 2, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo rst_forms[] = {{"*", 0xc7, 1, { BYTE, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo sbc_forms[] = {{"a,!", 0x98, 1, { REG, 0, 0, 0, 0, 0}}, {"a,&h", 0x9c00, 2, { PREF, 0, 0, 0, 0, 0}}, {"a,&l", 0x9d00, 2, { PREF, 0, 0, 0, 0, 0}}, {"a,(&*)", 0x9e00, 3, { PREF, 0, SBYTE, 16, 0, 0}}, {"a,*", 0xde, 2, { BYTE, 8, 0, 0, 0, 0}}, {"hl,$", 0x42ed, 2, { REG, 11, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo scf_forms[] = {{"", 0x37, 1, { 0, 0, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo set_forms[] = {{"*,!", 0xc0cb, 2, { BIT, 11, REG, 8, 0, 0}}, {"*,(&*)", 0xc600cb00, 4, { BIT, 27, PREF, 0, SBYTE, 16}}, {"*,?,(ix*)", 0xc000cbdd, 4, { BIT, 27, REG, 24, SBYTE, 16}}, {"*,?,(iy*)", 0xc000cbfd, 4, { BIT, 27, REG, 24, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo sla_forms[] = {{"!", 0x20cb, 2, { REG, 8, 0, 0, 0, 0}}, {"(&*)", 0x2600cb00, 4, { PREF, 0, SBYTE, 16, 0, 0}}, {"?,(&*)", 0x2000cb00, 4, { REG, 24, PREF, 0, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo slia_forms[] = {{"!", 0x30cb, 2, { REG, 8, 0, 0, 0, 0}}, {"(&*)", 0x3600cb00, 4, { PREF, 0, SBYTE, 16, 0, 0}}, {"?,(&*)", 0x3000cb00, 4, { REG, 24, PREF, 0, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo sra_forms[] = {{"!", 0x28cb, 2, { REG, 8, 0, 0, 0, 0}}, {"(&*)", 0x2e00cb00, 4, { PREF, 0, SBYTE, 16, 0, 0}}, {"?,(&*)", 0x2800cb00, 4, { REG, 24, PREF, 0, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo srl_forms[] = {{"!", 0x38cb, 2, { REG, 8, 0, 0, 0, 0}}, {"(&*)", 0x3e00cb00, 4, { PREF, 0, SBYTE, 16, 0, 0}}, {"?,(&*)", 0x3800cb00, 4, { REG, 24, PREF, 0, SBYTE, 16}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo sub_forms[] = {{"!", 0x90, 1, { REG, 0, 0, 0, 0, 0}}, {"&h", 0x9400, 2, { PREF, 0, 0, 0, 0, 0}}, {"&l", 0x9500, 2, { PREF, 0, 0, 0, 0, 0}}, {"(&*)", 0x9600, 3, { PREF, 0, SBYTE, 16, 0, 0}}, {"*", 0xd6, 2, { BYTE, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const iforminfo xor_forms[] = {{"!", 0xa8, 1, { REG, 0, 0, 0, 0, 0}}, {"&h", 0xac00, 2, { PREF, 0, 0, 0, 0, 0}}, {"&l", 0xad00, 2, { PREF, 0, 0, 0, 0, 0}}, {"(&*)", 0xae00, 3, { PREF, 0, SBYTE, 16, 0, 0}}, {"*", 0xee, 2, { BYTE, 8, 0, 0, 0, 0}}, {0,0,0,{0,0,0,0,0,0}}}; static const instinfo instructions[] = {{"adc", adc_forms}, {"add", add_forms}, {"and", and_forms}, {"bit", bit_forms}, {"call", call_forms}, {"ccf", ccf_forms}, {"cp", cp_forms}, {"cpd", cpd_forms}, {"cpdr", cpdr_forms}, {"cpi", cpi_forms}, {"cpir", cpir_forms}, {"cpl", cpl_forms}, {"daa", daa_forms}, {"dec", dec_forms}, {"di", di_forms}, {"djnz", djnz_forms}, {"ei", ei_forms}, {"ex", ex_forms}, {"exx", exx_forms}, {"halt", halt_forms}, {"im", im_forms}, {"in", in_forms}, {"inc", inc_forms}, {"ind", ind_forms}, {"indr", indr_forms}, {"ini", ini_forms}, {"inir", inir_forms}, {"jp", jp_forms}, {"jr", jr_forms}, {"ld", ld_forms}, {"ldd", ldd_forms}, {"lddr", lddr_forms}, {"ldi", ldi_forms}, {"ldir", ldir_forms}, {"neg", neg_forms}, {"nop", nop_forms}, {"or", or_forms}, {"otdr", otdr_forms}, {"otir", otir_forms}, {"out", out_forms}, {"outd", outd_forms}, {"outi", outi_forms}, {"pop", pop_forms}, {"push", push_forms}, {"res", res_forms}, {"ret", ret_forms}, {"reti", reti_forms}, {"retn", retn_forms}, {"rl", rl_forms}, {"rla", rla_forms}, {"rlc", rlc_forms}, {"rlca", rlca_forms}, {"rld", rld_forms}, {"rr", rr_forms}, {"rra", rra_forms}, {"rrc", rrc_forms}, {"rrca", rrca_forms}, {"rrd", rrd_forms}, {"rst", rst_forms}, {"sbc", sbc_forms}, {"scf", scf_forms}, {"set", set_forms}, {"sl1", slia_forms}, {"sla", sla_forms}, {"slia", slia_forms}, {"sll", slia_forms}, {"sra", sra_forms}, {"srl", srl_forms}, {"sub", sub_forms}, {"xor", xor_forms}}; #define N_INSTRUCTIONS (sizeof(instructions) / sizeof(instinfo)) /**************** Directives ****************/ static void assemble_align(asctx* ctx, char* label UNUSED, char* arg) { dword value = parse_exp(ctx, arg, PARSE_NORMAL); if (ctx->rpc % value == 0) return; value -= ctx->rpc % value; ctx->rpc += value; ctx->pc += value; } static void assemble_ascii(asctx* ctx, char* label UNUSED, char* arg) { const char* p; dword v; if (*arg != '"') { message(ctx, ERROR, "Invalid argument"); return; } p = arg + 1; while (*p && *p != '"') { if (!parse_char(ctx, &p, &v, NULL)) output_code_byte(ctx, v); } if (*p != '"') message(ctx, ERROR, "Invalid argument"); else { p++; while (IS_SPACE(*p)) p++; if (*p) message(ctx, ERROR, "Invalid argument"); } } static void assemble_ascis(asctx* ctx, char* label UNUSED, char* arg) { const char* p; dword v, prev = 256; if (*arg != '"') { message(ctx, ERROR, "Invalid argument"); return; } p = arg + 1; while (*p && *p != '"') { if (!parse_char(ctx, &p, &v, NULL)) { if (prev != 256) output_code_byte(ctx, prev & 0x7f); prev = v; } } if (prev != 256) output_code_byte(ctx, prev | 0x80); if (*p != '"') message(ctx, ERROR, "Invalid argument"); else { p++; while (IS_SPACE(*p)) p++; if (*p) message(ctx, ERROR, "Invalid argument"); } } static void assemble_clearchars(asctx *ctx, char* label UNUSED, char* arg UNUSED) { int i; for (i = 0; i < 256; i++) ctx->charvalues[i] = 0; } static void assemble_db(asctx* ctx, char* label UNUSED, char* arg) { char *subexps[MAX_SUBEXPS]; dword value; char* a = NULL; while (pattern_match(ctx, "*,*", (a ? a : arg), subexps)) { if (subexps[0][0] == '"') assemble_ascii(ctx, NULL, subexps[0]); else { value = format_arg(ctx, 0, subexps[0], BYTE, 0); output_code_byte(ctx, value & 0xff); } xfree(subexps[0]); xfree(a); a = subexps[1]; } if (a) arg = a; if (arg[0] == '"') assemble_ascii(ctx, NULL, arg); else { value = format_arg(ctx, 0, arg, BYTE, 0); output_code_byte(ctx, value & 0xff); } xfree(a); } static void assemble_defchar(asctx* ctx, char* label UNUSED, char* arg) { char *subexps[MAX_SUBEXPS]; const char *p; int c; dword value; if (!pattern_match(ctx, "*,*", arg, subexps)) { message(ctx, ERROR, "Not enough arguments"); } else if (subexps[0][0] != '"' && subexps[0][0] != '\'') { message(ctx, ERROR, "Invalid character constant"); } else { p = subexps[0] + 1; if (!parse_char(ctx, &p, NULL, &c)) { if (*p != subexps[0][0]) { message(ctx, ERROR, "Invalid character constant"); } else { value = parse_exp(ctx, subexps[1], PARSE_NORMAL); ctx->charvalues[(int) (unsigned char) c] = value; } } } xfree(subexps[0]); xfree(subexps[1]); } static void assemble_dl(asctx* ctx, char* label UNUSED, char* arg) { char *subexps[MAX_SUBEXPS]; dword value; char* a = NULL; while (pattern_match(ctx, "*,*", (a ? a : arg), subexps)) { if (ctx->final) value = parse_exp(ctx, subexps[0], PARSE_NORMAL); else value = 0; output_code_byte(ctx, value & 0xff); output_code_byte(ctx, (value >> 8) & 0xff); output_code_byte(ctx, (value >> 16) & 0xff); output_code_byte(ctx, (value >> 24) & 0xff); xfree(subexps[0]); xfree(a); a = subexps[1]; } if (a) arg = a; if (ctx->final) value = parse_exp(ctx, arg, PARSE_NORMAL); else value = 0; output_code_byte(ctx, value & 0xff); output_code_byte(ctx, (value >> 8) & 0xff); output_code_byte(ctx, (value >> 16) & 0xff); output_code_byte(ctx, (value >> 24) & 0xff); xfree(a); } static void assemble_ds(asctx* ctx, char* label UNUSED, char* arg) { dword value = parse_exp(ctx, arg, PARSE_NORMAL); ctx->rpc += value; ctx->pc += value; } static void assemble_dsw(asctx* ctx, char* label UNUSED, char* arg) { dword value = 2 * parse_exp(ctx, arg, PARSE_NORMAL); ctx->rpc += value; ctx->pc += value; } static void assemble_dw(asctx* ctx, char* label UNUSED, char* arg) { char *subexps[MAX_SUBEXPS]; dword value; char* a = NULL; while (pattern_match(ctx, "*,*", (a ? a : arg), subexps)) { value = format_arg(ctx, 0, subexps[0], WORD, 0); output_code_byte(ctx, value & 0xff); output_code_byte(ctx, (value >> 8) & 0xff); xfree(subexps[0]); xfree(a); a = subexps[1]; } if (a) arg = a; value = format_arg(ctx, 0, arg, WORD, 0); output_code_byte(ctx, value & 0xff); output_code_byte(ctx, (value >> 8) & 0xff); xfree(a); } static void assemble_else(asctx* ctx, char* label UNUSED, char* arg UNUSED) { if (!ctx->incstack->conditional_run) message(ctx, ERROR, "ELSE without preceding IF"); else { ctx->incstack->conditional_run--; ctx->incstack->conditional_skip++; } } static void assemble_endif(asctx* ctx, char* label UNUSED, char* arg UNUSED) { if (!ctx->incstack->conditional_run) message(ctx, ERROR, "ENDIF without preceding IF"); else ctx->incstack->conditional_run--; } static void assemble_endm(asctx* ctx, char* label UNUSED, char* arg UNUSED) { message(ctx, ERROR, "ENDM without preceding MACRO"); } static void assemble_equ(asctx* ctx, char* label, char* arg) { dword value = parse_exp(ctx, arg, PARSE_NORMAL); symbol* sym = get_symbol(ctx, label); set_equate(ctx, sym, value); } static void assemble_error(asctx* ctx, char* label UNUSED, char* arg) { if (*arg == '"') arg++; if (arg[strlen(arg)-1] == '"') arg[strlen(arg)-1] = 0; message(ctx, ERROR, "%s", arg); } static void assemble_export(asctx* ctx, char* label UNUSED, char* arg) { char* subexps[MAX_SUBEXPS]; char* a = NULL; symbol* sym; while (pattern_match(ctx, "*,*", (a ? a : arg), subexps)) { sym = get_symbol(ctx, collapse_word(subexps[0])); sym->exported = 1; xfree(subexps[0]); xfree(a); a = subexps[1]; } sym = get_symbol(ctx, collapse_word(a ? a : arg)); sym->exported = 1; xfree(a); } static void assemble_if(asctx* ctx, char* label UNUSED, char* arg) { int prevignore = ctx->ignoreundef; ctx->ignoreundef = 1; if (parse_exp(ctx, arg, PARSE_NORMAL)) ctx->incstack->conditional_run++; else ctx->incstack->conditional_skip++; ctx->ignoreundef = prevignore; } static void assemble_ifdef(asctx* ctx, char* label UNUSED, char* arg) { symbol* sym; collapse_word(arg); sym = get_symbol(ctx, arg); if (!get_equate_defined(ctx, sym)) ctx->incstack->conditional_skip++; else ctx->incstack->conditional_run++; } static void assemble_ifndef(asctx* ctx, char* label UNUSED, char* arg) { symbol* sym; collapse_word(arg); sym = get_symbol(ctx, arg); if (!get_equate_defined(ctx, sym)) ctx->incstack->conditional_run++; else ctx->incstack->conditional_skip++; } static void assemble_incbin(asctx* ctx, char* label UNUSED, char* arg) { long int n; asfile* parent; int local; if (*arg == '"') { arg++; local = 1; } else if (*arg == '<') { arg++; local = 0; } else { message(ctx, ERROR, "Invalid argument"); return; } if (arg[strlen(arg)-1] == (local ? '"' : '>')) arg[strlen(arg)-1] = 0; else { message(ctx, ERROR, "Invalid argument"); return; } if (ctx->incstack) parent = ctx->incstack->file; else parent = NULL; if (miniasm_get_bin_size(arg, parent, local, &n, ctx->user_data)) message(ctx, ERROR, "Cannot read file '%s'", arg); else { ctx->pc += n; ctx->rpc += n; } if (ctx->final) { output_byte(ctx, ctx->pc - n, 0xff); output_byte(ctx, ctx->pc - 1, 0xff); if (miniasm_read_bin(arg, parent, local, &ctx->data[ctx->pc - n - ctx->start], n, ctx->user_data)) message(ctx, ERROR, "Cannot read file '%s'", arg); } } static void assemble_include(asctx* ctx, char* label UNUSED, char* arg) { int local; if (*arg == '"') { arg++; local = 1; } else if (*arg == '<') { arg++; local = 0; } else { message(ctx, ERROR, "Invalid argument"); return; } if (arg[strlen(arg)-1] == (local ? '"' : '>')) arg[strlen(arg)-1] = 0; else { message(ctx, ERROR, "Invalid argument"); return; } if (push_file(ctx, arg, local)) message(ctx, ERROR, "Cannot read file '%s'", arg); } static void assemble_list(asctx* ctx, char* label UNUSED, char* arg UNUSED) { ctx->liston = 1; } static void assemble_macro(asctx* ctx, char* label, char* arg) { char* name; macrodef* mac; char *subexps[MAX_SUBEXPS]; char* a = NULL; int i; name = xnew(char, strlen(label)+1); for (i = 0; label[i]; i++) name[i] = TO_LOWER(label[i]); name[i] = 0; mac = lookup_macro(ctx, name); if (mac) { if (mac->pass == ctx->passnum) message(ctx, ERROR, "Macro '%s' already defined", name); mac->pass = ctx->passnum; } else { mac = define_macro(ctx, name); } xfree(name); for (i = 0; i < mac->nparams; i++) xfree(mac->param_names[i]); mac->nparams = 0; for (i = 0; i < mac->nlines; i++) xfree(mac->lines[i]); mac->nlines = 0; if (arg) { while (pattern_match(ctx, "*,*", (a ? a : arg), subexps)) { mac->nparams++; mac->param_names = xrenew(char*, mac->param_names, mac->nparams); mac->param_names[mac->nparams-1] = subexps[0]; chomp(mac->param_names[mac->nparams-1]); xfree(a); a = subexps[1]; } xfree(subexps[0]); xfree(subexps[1]); if (a) arg = a; while (IS_SPACE(*arg)) arg++; if (*arg) { mac->nparams++; mac->param_names = xrenew(char*, mac->param_names, mac->nparams); mac->param_names[mac->nparams-1] = xstrdup(arg); chomp(mac->param_names[mac->nparams-1]); i = strlen(mac->param_names[mac->nparams-1]); if (i > 3 && !strcmp(&mac->param_names[mac->nparams-1][i-3], "...")) { mac->param_names[mac->nparams-1][i-3] = 0; mac->vaparam = 1; } else { mac->vaparam = 0; } } xfree(a); } ctx->incstack->defining_macro = mac; } static void assemble_nolist(asctx* ctx, char* label UNUSED, char* arg UNUSED) { ctx->liston = 0; } static void assemble_org(asctx* ctx, char* label UNUSED, char* arg) { ctx->rpc = ctx->pc = parse_exp(ctx, arg, PARSE_NORMAL); } static void assemble_rorg(asctx* ctx, char* label UNUSED, char* arg) { ctx->rpc = parse_exp(ctx, arg, PARSE_NORMAL); } static void assemble_set(asctx* ctx, char* label, char* arg) { dword value = parse_exp(ctx, arg, PARSE_NORMAL); symbol* sym = get_symbol(ctx, label); set_variable_equate(ctx, sym, value); } static void assemble_unset(asctx* ctx, char* label, char* arg UNUSED) { symbol* sym = get_symbol(ctx, label); if (sym->equated) { message(ctx, ERROR, "Cannot UNSET label which has not been SET"); return; } sym->set = 0; } static void assemble_warning(asctx* ctx, char* label UNUSED, char* arg) { if (!ctx->final) return; if (*arg == '"') arg++; if (arg[strlen(arg)-1] == '"') arg[strlen(arg)-1] = 0; message(ctx, WARNING, "%s", arg); } static const dirinfo directives[] = { {"align", 0, 1, &assemble_align}, {"ascii", 0, 1, &assemble_ascii}, {"ascis", 0, 1, &assemble_ascis}, {"block", 0, 1, &assemble_ds}, {"byte", 0, 1, &assemble_db}, {"clearchars", 0, 0, &assemble_clearchars}, {"db", 0, 1, &assemble_db}, {"dc.b", 0, 1, &assemble_db}, {"dc.w", 0, 1, &assemble_dw}, {"defb", 0, 1, &assemble_db}, {"defchar", 0, 1, &assemble_defchar}, {"dl", 0, 1, &assemble_dl}, {"ds", 0, 1, &assemble_ds}, {"ds.b", 0, 1, &assemble_ds}, {"ds.w", 0, 1, &assemble_dsw}, {"dw", 0, 1, &assemble_dw}, {"else", 0, 0, &assemble_else}, {"endif", 0, 0, &assemble_endif}, {"endm", 0, 0, &assemble_endm}, {"equ", 1, 1, &assemble_equ}, {"error", 0, 1, &assemble_error}, {"export", 0, 1, &assemble_export}, {"if", 0, 1, &assemble_if}, {"ifdef", 0, 1, &assemble_ifdef}, {"ifndef", 0, 1, &assemble_ifndef}, {"incbin", 0, 1, &assemble_incbin}, {"include", 0, 1, &assemble_include}, {"list", 0, 0, &assemble_list}, {"macro", 1, 2, &assemble_macro}, {"nolist", 0, 0, &assemble_nolist}, {"org", 0, 1, &assemble_org}, {"rorg", 0, 1, &assemble_rorg}, {"set", 1, 1, &assemble_set}, {"unset", 1, 0, &assemble_unset}, {"warning", 0, 1, &assemble_warning}, {"word", 0, 1, &assemble_dw}}; #define N_DIRECTIVES (sizeof(directives) / sizeof(dirinfo)) /**************** Assembler Core ****************/ static int split_line(asctx* ctx, const char* line, char** label, char** inst, char** arg) { const char* p = line; int i; *label = *inst = *arg = NULL; if (!*p || IS_COMMENT(*p)) return 0; else if (IS_WORD_START(*p)) { p = get_word_end(p); *label = xstrndup(ctx, line, p - line); collapse_word(*label); if (!*p || IS_COMMENT(*p)) return 0; if (*p == ':') p++; else if (!IS_SPACE(*p)) return 1; } else if (!IS_SPACE(*p)) return 1; while (IS_SPACE(*p)) p++; if (!*p || IS_COMMENT(*p)) return 0; if (!IS_WORD_START(*p)) return 1; line = p; p = get_word_end(p); *inst = xstrndup(ctx, line, p - line); collapse_word(*inst); for (i = 0; (*inst)[i]; i++) (*inst)[i] = TO_LOWER((*inst)[i]); while (IS_SPACE(*p)) p++; line = p; while (*p && !IS_COMMENT(*p)) { p = skip_quoted(p); } while (p != line && IS_SPACE(p[-1])) p--; if (p != line) *arg = xstrndup(ctx, line, p - line); return 0; } static int cmp_instinfo(const void* a, const void* b) { const instinfo* ia = a; const instinfo* ib = b; return strcmp(ia->name, ib->name); } static int cmp_dirinfo(const void* a, const void* b) { const dirinfo* ia = a; const dirinfo* ib = b; return strcmp(ia->name, ib->name); } static void assemble_line(asctx* ctx, const char* line) { char *label, *inst, *arg; symbol* sym; instinfo itemp; instinfo* ii; macrodef* mac; dirinfo dtemp; dirinfo* di; char* subexps[MAX_SUBEXPS]; char** params; const char* macline; char* maclinea; char* p; dword opcode; int f, i, j, k, n, l; /* Check for conditionals */ if (ctx->incstack->conditional_skip) { if (!split_line(ctx, line, &label, &inst, &arg)) { if (inst) { if (!strcmp(inst, "if") || !strcmp(inst, ".if") || !strcmp(inst, "ifdef") || !strcmp(inst, ".ifdef") || !strcmp(inst, "ifndef") || !strcmp(inst, ".ifndef")) ctx->incstack->conditional_skip++; else if (!strcmp(inst, "endif") || !strcmp(inst, ".endif")) ctx->incstack->conditional_skip--; else if ((!strcmp(inst, "else") || !strcmp(inst, ".else")) && ctx->incstack->conditional_skip == 1) { ctx->incstack->conditional_skip--; ctx->incstack->conditional_run++; } } xfree(label); xfree(inst); xfree(arg); } return; } /* Check if we are defining a macro */ if (ctx->incstack->defining_macro) { if (!split_line(ctx, line, &label, &inst, &arg)) { if (inst) { if (!strcmp(inst, "endm") || !strcmp(inst, ".endm")) { ctx->incstack->defining_macro = NULL; xfree(label); xfree(inst); xfree(arg); return; } } xfree(label); xfree(inst); xfree(arg); } mac = ctx->incstack->defining_macro; mac->nlines++; mac->lines = xrenew(char*, mac->lines, mac->nlines); mac->lines[mac->nlines-1] = xstrdup(line); return; } /* Parse the line */ if (split_line(ctx, line, &label, &inst, &arg)) message(ctx, ERROR, "Syntax error"); else { if (!inst) { if (label) { /* No instruction, just a label */ sym = get_symbol(ctx, label); set_equate(ctx, sym, ctx->rpc); } } else { /* CPU instruction */ itemp.name = inst; ii = bsearch(&itemp, instructions, N_INSTRUCTIONS, sizeof(instinfo), &cmp_instinfo); if (ii) { if (label) { sym = get_symbol(ctx, label); set_equate(ctx, sym, ctx->rpc); } for (f = 0; ii->forms[f].pattern; f++) { if (pattern_match(ctx, ii->forms[f].pattern, arg, subexps)) { if (ctx->final) { opcode = ii->forms[f].opcode; for (i = 0; i < MAX_SUBEXPS; i++) { if (subexps[i]) { opcode = format_arg(ctx, opcode, subexps[i], ii->forms[f].args[2*i], ii->forms[f].args[2*i+1]); xfree(subexps[i]); } } for (i = 0; i < ii->forms[f].length; i++) { output_code_byte(ctx, opcode & 0xff); opcode >>= 8; } } else { for (i = 0; i < MAX_SUBEXPS; i++) xfree(subexps[i]); ctx->rpc += ii->forms[f].length; ctx->pc += ii->forms[f].length; } break; } else for (i = 0; i < MAX_SUBEXPS; i++) xfree(subexps[i]); } if (!ii->forms[f].pattern) message(ctx, ERROR, "Unknown argument form '%s %s'", inst, arg); } else if ((mac = lookup_macro(ctx, inst))) { /* Macro */ if (label) { sym = get_symbol(ctx, label); set_equate(ctx, sym, ctx->rpc); } params = xnew(char*, mac->nparams); for (i = 0; i < mac->nparams - 1; i++) { if (!arg || !pattern_match(ctx, "*,*", arg, subexps)) { message(ctx, ERROR, "Not enough arguments to macro '%s'", inst); xfree(subexps[0]); xfree(subexps[1]); while (i > 0) xfree(params[--i]); xfree(params); xfree(label); xfree(inst); xfree(arg); return; } xfree(arg); params[i] = subexps[0]; arg = subexps[1]; } if (mac->nparams > 0) { if (mac->vaparam) { params[i] = arg; arg = NULL; } else { if (pattern_match(ctx, "*,*", arg, subexps)) { message(ctx, ERROR, "Too many arguments to macro '%s'", inst); params[i] = subexps[0]; xfree(subexps[1]); } else { params[i] = arg; arg = NULL; xfree(subexps[0]); xfree(subexps[1]); } } } else if (arg) { message(ctx, ERROR, "Too many arguments to macro '%s'", inst); } for (i = 0; i < mac->nlines; i++) { macline = mac->lines[i]; maclinea = NULL; for (j = 0; macline[j]; j++) { if (IS_WORD_START(macline[j]) && (j == 0 || !IS_WORD(macline[j-1]))) { for (k = 0; k < mac->nparams; k++) { n = strlen(mac->param_names[k]); if (!strncmp(&macline[j], mac->param_names[k], n) && !IS_WORD(macline[j+n])) { /* substitute parameter text */ p = xnew(char, (strlen(macline) + strlen(params[k]) - n + 1)); if (j) strncpy(p, macline, j); strcpy(&p[j], params[k]); l = strlen(p); strcat(p, &macline[j+n]); xfree(maclinea); macline = maclinea = p; j = l - 1; break; } } } } assemble_line(ctx, macline); xfree(maclinea); } for (i = 0; i < mac->nparams; i++) xfree(params[i]); xfree(params); } else { /* Directive */ if (*inst == '.') dtemp.name = inst+1; else dtemp.name = inst; di = bsearch(&dtemp, directives, N_DIRECTIVES, sizeof(dirinfo), &cmp_dirinfo); if (label && (!di || !di->need_label)) { sym = get_symbol(ctx, label); set_equate(ctx, sym, ctx->rpc); } if (di) { if (di->need_label && !label) message(ctx, ERROR, "Directive '%s' requires label", inst); else if (di->need_arg == 1 && !arg) message(ctx, ERROR, "Directive '%s' requires argument", inst); else { if (!di->need_arg && arg) message(ctx, ERROR, "Extraneous argument to '%s'", inst); (*di->func)(ctx, label, arg); } } else message(ctx, ERROR, "Unknown instruction '%s'", inst); } } } xfree(label); xfree(inst); xfree(arg); } static void run_pass(asctx* ctx) { char *line; int fromcache; int curlinenum; int i; ctx->pc = 0; ctx->rpc = 0; ctx->again = 0; ctx->liston = 1; for (i = 0; i < 256; i++) ctx->charvalues[i] = i; miniasm_begin_listing(ctx->passnum, ctx->final, ctx->user_data); while (ctx->incstack) { while (1) { if (!ctx->incstack->file) { if (ctx->incstack->clinenum < ctx->incstack->cache->nlines) line = ctx->incstack->cache->lines[ctx->incstack->clinenum]; else line = NULL; ctx->incstack->clinenum++; fromcache = 1; } else { line = miniasm_gets(ctx->incstack->file, ctx->user_data); fromcache = 0; } curlinenum = ++ctx->incstack->linenum; if (!line) break; ctx->data_write_addr = ctx->pc; ctx->data_write_count = 0; assemble_line(ctx, line); if (ctx->liston && (ctx->flags & MINIASM_WRITE_LISTING)) { miniasm_write_listing(ctx->incstack->file, curlinenum, line, ctx->data_write_addr, ctx->data_write_count, (ctx->data ? ctx->data + ctx->data_write_addr - ctx->start : NULL), ctx->user_data); } if (!fromcache && line) miniasm_gets_free(line); } if (ctx->incstack->conditional_run || ctx->incstack->conditional_skip) message(ctx, ERROR, "Missing ENDIF at end of file"); if (ctx->incstack->defining_macro) message(ctx, ERROR, "Missing ENDM at end of file"); pop_file(ctx); } ctx->passnum++; } /**************** API ****************/ void miniasm_define(asctx* ctx, const char* name, long int value) { symbol* sym = get_symbol(ctx, name); sym->set = 1; sym->equated = 1; sym->value = value; } int miniasm_get_symbol(asctx* ctx, const char* name, long int* value) { symbol* sym = get_symbol(ctx, name); if (!get_equate_defined(ctx, sym)) return 0; if (value) *value = sym->value; return 1; } asctx* miniasm_init(unsigned long flags, void* data) { asctx* ctx; int i; ctx = malloc(sizeof(asctx)); if (!ctx) return NULL; ctx->user_data = data; ctx->flags = flags; ctx->incstack = NULL; ctx->passnum = ctx->again = ctx->nerrors = ctx->nwarnings = ctx->final = 0; ctx->data = NULL; ctx->start = ctx->end = ctx->end_a = 0; ctx->pc = ctx->rpc = 0; for (i = 0; i < HASH_MODULUS; i++) { ctx->symtab[i] = NULL; ctx->fctab[i] = NULL; ctx->macrotab[i] = NULL; } miniasm_define(ctx, "_MINIASM", 2); miniasm_define(ctx, "_Z80", 1); return ctx; } void miniasm_exit(asctx* ctx) { int i; symbol* s; filecache* c; macrodef* m; for (i = 0; i < HASH_MODULUS; i++) { while (ctx->symtab[i]) { s = ctx->symtab[i]; xfree(s->name); ctx->symtab[i] = s->next; xfree(s); } while (ctx->fctab[i]) { c = ctx->fctab[i]; ctx->fctab[i] = c->next; cache_free(c); } while (ctx->macrotab[i]) { m = ctx->macrotab[i]; ctx->macrotab[i] = m->next; macro_free(m); } } xfree(ctx->data); xfree(ctx); } int miniasm_assemble(asctx* ctx, const char* filename, const unsigned char** bufp, long int* startp, long int* endp, int* npassesp, int* nerrorsp, int* nwarningsp) { int i; symbol* s; ctx->passnum = 0; ctx->nerrors = 0; xfree(ctx->data); ctx->data = NULL; ctx->start = ctx->end = ctx->end_a = 0; ctx->passnum = ctx->again = ctx->nerrors = ctx->nwarnings = ctx->final = 0; ctx->ignoreundef = 1; if (bufp) *bufp = NULL; if (startp) *startp = 0; if (endp) *endp = 0; if (npassesp) *npassesp = 0; if (nerrorsp) *nerrorsp = 0; if (nwarningsp) *nwarningsp = 0; while (1) { if (ctx->passnum > MAX_PASSES) { message(ctx, GENERAL_ERROR, "Maximum number of passes exceeded"); break; } if (push_file(ctx, filename, 1)) { message(ctx, GENERAL_ERROR, "Cannot read file '%s'", filename); break; } run_pass(ctx); if (!ctx->again) break; if (ctx->nerrors) break; } if (ctx->nerrors) { if (npassesp) *npassesp = ctx->passnum; if (nerrorsp) *nerrorsp = ctx->nerrors; if (nwarningsp) *nwarningsp = ctx->nwarnings; return ctx->nerrors; } ctx->final = 1; ctx->ignoreundef = 0; if (push_file(ctx, filename, 1)) message(ctx, GENERAL_ERROR, "Cannot read file '%s'", filename); else run_pass(ctx); if (ctx->flags & MINIASM_WRITE_SYMBOLS) { for (i = 0; i < HASH_MODULUS; i++) { for (s = ctx->symtab[i]; s; s = s->next) { if (get_equate_defined(ctx, s)) { miniasm_write_symbol(s->name, s->value, s->exported, ctx->user_data); } } } } if (bufp) *bufp = ctx->data; if (startp) *startp = ctx->start; if (endp) *endp = ctx->end; if (npassesp) *npassesp = ctx->passnum; if (nerrorsp) *nerrorsp = ctx->nerrors; if (nwarningsp) *nwarningsp = ctx->nwarnings; return ctx->nerrors; }