diff options
-rw-r--r-- | asmmon.h | 48 | ||||
-rw-r--r-- | assemble.c | 257 | ||||
-rw-r--r-- | enums.h | 1 | ||||
-rw-r--r-- | lexer.c | 735 |
4 files changed, 695 insertions, 346 deletions
@@ -9,6 +9,7 @@ typedef struct ln line; typedef struct sym symbol; typedef struct fix fixup; typedef struct inst instruction; +typedef struct expr expr; struct tok { @@ -27,6 +28,7 @@ struct tok { /* Token value(s). */ union { + expr *expr; symbol *sym; char *str; uint8_t byte ; @@ -74,6 +76,19 @@ struct inst { uint8_t op; /* Base value used to get the actual opcode. */ }; +struct expr { + int type; /* Expression type. */ + + expr *left; /* Left side of expression. */ + expr *right; /* Right side of expression. */ + + /* Expression value. */ + union { + uint64_t val; + symbol *sym; + } value; +}; + extern char lexeme[]; extern char *string[]; @@ -89,7 +104,6 @@ extern symbol *locals; extern symbol *last_loc; extern fixup *fixups; extern fixup *last_fix; -extern tmp_symtab *tmp_sym_table; extern uint8_t lex_type; @@ -173,7 +187,7 @@ enum pre_token { PTOK_OTHER }; -enum expr { +enum expression { EXPR_PLUS, EXPR_MINUS, EXPR_LOW, @@ -182,6 +196,12 @@ enum expr { EXPR_LSHFT, EXPR_RSHFT, EXPR_MUL, + EXPR_HEX, + EXPR_DEC, + EXPR_BIN, + EXPR_CHAR, + EXPR_SYM, + EXPR_REG, EXPR_NONE }; @@ -690,6 +710,25 @@ static const char *set_cc[8] = { "VC" }; +static const char *reg_name[16] = { + [REG_A ] = "A", + [REG_B ] = "B", + [REG_X ] = "X", + [REG_Y ] = "Y", + [REG_E ] = "E", + [REG_C ] = "C", + [REG_D ] = "D", + [REG_S ] = "S", + [REG_F ] = "F", + [REG_SP ] = "SP", + [REG_BP ] = "BP", + [REG_R11] = "R11", + [REG_R12] = "R12", + [REG_R13] = "R13", + [REG_R14] = "R14", + [REG_R15] = "R15" +}; + static const char *instdesc[OPNUM] = { [ADC] = "ADd accumulator, with operand, Carry if needed.", [AND] = "Bitwise AND accumulator, with operand.", @@ -805,11 +844,10 @@ extern uint8_t isfixup; extern line *find_line(uint32_t ln, uint8_t dbg); extern uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg); - -extern uint64_t get_val(token *t, uint64_t addr, uint8_t size, uint8_t end_expr, uint8_t stop_comma, uint8_t dbg); +extern uint64_t get_val(expr *tree, uint64_t addr, uint8_t size, int depth, uint8_t dbg); extern token *skip_expr(token *t, uint8_t end_expr, uint8_t stop_comma, uint8_t dbg); extern uint64_t parse_tokens(token *tm, line **l, bytecount *bc, uint8_t isasm, uint64_t address, uint8_t dbg); -extern token *make_token(uint8_t id, uint8_t type, uint8_t space, uint8_t tab, uint64_t value, char *str, symbol *s); +extern token *make_token(uint8_t id, uint8_t type, uint8_t space, uint8_t tab, uint64_t value, char *str, symbol *s, expr *e); extern void assemble(line *ln, bytecount *bc, uint8_t dbg); extern void fix_symtree(line *l); extern void cleanup(); @@ -113,98 +113,50 @@ static void write_value(uint64_t value, uint64_t address, uint8_t size) { } } -uint64_t get_val(token *t, uint64_t addr, uint8_t size, uint8_t end_expr, uint8_t stop_comma, uint8_t dbg) { +uint64_t get_val(expr *tree, uint64_t addr, uint8_t size, int depth, uint8_t dbg) { uint64_t value = 0; - uint64_t tmp_val = 0; - uint8_t type = EXPR_NONE; - uint8_t isstart = 1; - int done = 0; - do { - if (t->id == TOK_EXPR) { - type = t->type; - t = t->next; - } - if (stop_comma && t->subtype == TOK_CSV) { - done = 1; - } - if (stop_comma && t->id == TOK_REG) { - break; - } - switch (t->id) { - case TOK_HEX: - case TOK_DEC: - case TOK_BIN: - case TOK_CHAR: tmp_val = t->qword; t = t->next; break; - case TOK_SYM: - case TOK_LABEL: - for (; t->sym && t->sym->isstruct && t->next && t->next->id == TOK_SYM; t = t->next); - tmp_val = (t->sym) ? t->sym->val : addr; - t = t->next; - break; - default: tmp_val = 0; - } - if (end_expr != 0xFF && type == end_expr) { - break; - } - switch (type) { - case EXPR_PLUS : (isstart) ? (value = tmp_val) : (value += tmp_val); break; - case EXPR_MINUS: (isstart) ? (value = -tmp_val) : (value -= tmp_val); break; - case EXPR_OR : value |= tmp_val; break; - case EXPR_LSHFT: value <<= tmp_val; break; - case EXPR_RSHFT: value >>= tmp_val; break; - case EXPR_LOW : - value = tmp_val; - switch (size) { - default: - case 2 : value &= 0xFFFFFFFF; break; - case 1 : value &= 0x0000FFFF; break; - case 0 : value &= 0x000000FF; break; - } - break; - case EXPR_HIGH : - value = tmp_val; - switch (size) { - default: - case 2 : value >>= 0x20; break; - case 1 : value >>= 0x10; break; - case 0 : value >>= 0x08; break; - } - break; - case EXPR_NONE : value = tmp_val; break; - } - isstart = 0; - if (dbg) { - printf("get_val(): Value: $%"PRIX64", Expression type: $%X, Expression Value: $%"PRIX64".\n", value, type, tmp_val); - } - } while (!done && t && t->id == TOK_EXPR && isexpr(t->type, dbg)); - return value; -} + uint64_t lvalue = 0; + uint64_t rvalue = 0; + uint64_t expr_val = 0; + int type; -token *skip_expr(token *t, uint8_t end_expr, uint8_t stop_comma, uint8_t dbg) { - int done = 0; - int is_reg = 0; - do { - is_reg = (t && t->next && t->next->id == TOK_REG); - t = (t->id == TOK_EXPR && !is_reg) ? t->next : t; - if (is_reg || (stop_comma && (t->subtype == TOK_CSV))) { - done = 1; - } - switch (t->id) { - case TOK_HEX : - case TOK_DEC : - case TOK_BIN : - case TOK_CHAR : - case TOK_SYM : - case TOK_LABEL: t = t->next; break; - } - if (end_expr != 0xFF && t->id == TOK_EXPR && t->type == end_expr) { - break; - } - } while (!done && t && t->id == TOK_EXPR && isexpr(t->type, dbg)); - return t; -} + int is_start = (!depth && tree->left && tree->right == NULL); + + if (tree->left) { + lvalue = get_val(tree->left, addr, size, depth+1, dbg); + } + + if (tree->right) { + rvalue = get_val(tree->right, addr, size, depth+1, dbg); + } + type = tree->type; + expr_val = tree->value.val; + switch (type) { + case EXPR_HEX: + case EXPR_DEC: + case EXPR_BIN: + case EXPR_CHAR: value = expr_val; break; + case EXPR_SYM: value = (tree->value.sym) ? tree->value.sym->val : addr; break; + case EXPR_PLUS: value = lvalue + rvalue; break; + case EXPR_MINUS: (is_start) ? (value = -lvalue) : (value = lvalue - rvalue); break; + case EXPR_OR: value = lvalue | rvalue; break; + case EXPR_LSHFT: value = lvalue << rvalue; break; + case EXPR_RSHFT: value = lvalue >> rvalue; break; + case EXPR_LOW: + case EXPR_HIGH: + value = lvalue; + switch (size) { + default: + case 2 : (type == EXPR_LOW) ? (value &= 0xFFFFFFFF) : (value >>= 0x20); break; + case 1 : (type == EXPR_LOW) ? (value &= 0x0000FFFF) : (value >>= 0x10); break; + case 0 : (type == EXPR_LOW) ? (value &= 0x000000FF) : (value >>= 0x08); break; + } + break; + } + return value; +} uint8_t get_directivesize(uint8_t type, uint8_t dbg) { switch (type) { @@ -262,7 +214,7 @@ uint16_t handle_struct(line **ln, uint64_t address, uint16_t offset, uint8_t dbg case DIR_QWORD : member_size = 8; break; case DIR_UNION : case DIR_STRUCT: member_size = handle_struct(&l, address, offset, dbg); break; - case DIR_RES : member_size = get_val(t, address, 3, 0xFF, 0, dbg); t = skip_expr(t, 0xFF, 0, dbg); break; + case DIR_RES : t = t->next; member_size = get_val(t->expr, address, 3, 0, dbg); break; } if (member && t->type != DIR_UNION && t->type != DIR_STRUCT) { member->val = offset; @@ -299,13 +251,8 @@ uint64_t handle_directive(token *t, bytecount *bc, uint8_t isasm, uint64_t addre for (; t; t = t->next) { tmp = 0; switch (t->id) { - case TOK_HEX: - case TOK_DEC: - case TOK_BIN: - case TOK_CHAR: - case TOK_SYM: - case TOK_LABEL: - val.u64 = get_val(t, tmpaddr, get_directivesize(type, dbg), 0xFF, 0, dbg); + case TOK_EXPR: + val.u64 = get_val(t->expr, tmpaddr, get_directivesize(type, dbg), 0, dbg); switch (type) { case DIR_QWORD: tmp = 8; break; case DIR_DWORD: tmp = 4; break; @@ -315,9 +262,7 @@ uint64_t handle_directive(token *t, bytecount *bc, uint8_t isasm, uint64_t addre write_value(val.u64, tmpaddr, tmp-1); tmpaddr += tmp; bc->datasize += tmp; - if (t->next && t->next->id == TOK_EXPR && isexpr(t->next->type, dbg)) { - t = skip_expr(t, 0xFF, 0, dbg); - } + break; case TOK_STRING: if (type == DIR_BYTE) { @@ -363,6 +308,9 @@ static uint8_t write_inst(uint8_t prefix, uint8_t ext_prefix, uint8_t opcode, op uint8_t op_ins_size[2]; union reg ins; union reg op_ins[2]; + ins.u64 = 0; + op_ins[0].u64 = 0; + op_ins[1].u64 = 0; memset(op_ins_size, 0, sizeof(op_ins_size)); memset(op_ins, 0, sizeof(op_ins_size)); if (prefix & 3) { @@ -451,6 +399,35 @@ static uint8_t write_inst(uint8_t prefix, uint8_t ext_prefix, uint8_t opcode, op return inst_size; } +int is_value(expr *e, expr **found) { + if (e == NULL) { + return 0; + } + switch (e->type) { + case EXPR_HEX : + case EXPR_DEC : + case EXPR_BIN : + case EXPR_CHAR: + case EXPR_SYM : + if (found) { + *found = e; + } + return 1; + default: + if (e->left) { + return is_value(e->left, found); + } + if (e->right) { + return is_value(e->right, found); + } + break; + } + if (found) { + *found = e; + } + return 0; +} + token *get_operands(token *t, operand *op, uint64_t address, uint8_t rs, uint8_t dbg) { uint8_t op_type; uint8_t op_inst; @@ -478,6 +455,16 @@ token *get_operands(token *t, operand *op, uint64_t address, uint8_t rs, uint8_t printf("t: %p, t->id: $%X, t->id: %s, t->subtype: $%X, t->subtype: %s\n", tmp, tmp->id, (tmp->id <= TOK_MEMBER) ? lex_tok[tmp->id] : "TOK_NONE", tmp->subtype, (tmp->subtype == TOK_IND || tmp->subtype == TOK_CSV) ? lex_tok[tmp->subtype] : "TOK_NONE"); }*/ + if (t) { + switch (t->id) { + case TOK_OPCODE: + case TOK_EXTOP : + case TOK_ORTHO : t = (t->next) ? t->next : t; break; + } + } + + int isvalue = 0; + for (; t && i < 2; t = t->next) { reg = (old_i != i) ? 0 : reg; got_value = (old_i != i) ? 0 : got_value; @@ -485,26 +472,27 @@ token *get_operands(token *t, operand *op, uint64_t address, uint8_t rs, uint8_t brack_done = (t->id == TOK_REG) ? 2 : 1; } switch (t->id) { - case TOK_HEX : - case TOK_DEC : - case TOK_BIN : - case TOK_SYM : - case TOK_CHAR : - case TOK_LABEL: + case TOK_EXPR: + isvalue = is_value(t->expr, NULL); if (!got_value) { - expr_type = (expr_type == 0xFF && t->next && t->next->id == TOK_EXPR) ? t->next->type : expr_type; - switch (expr_type) { - default : stop_comma = 1; break; - case EXPR_MUL : stop_comma = 0; break; + expr *e = t->expr; + if (isvalue) { + if (expr_type == 0xFF && e) { + expr_type = (e->right) ? e->right->type : e->type; + } + stop_comma = (expr_type != EXPR_MUL); + value = get_val(e, address, (rs != 0xFF) ? rs : 0, 0, dbg); + } else { + break; } is_sib = (!stop_comma && op[i].type && op[i].id == MEM_IND); - value = get_val(t, address, (rs != 0xFF) ? rs : 0, (!stop_comma) ? expr_type : 0xFF, stop_comma, dbg); - op[i].value = (!is_sib) ? value : op[i].value; got_value = 1; } else { - op[i].value = (!is_sib) ? value : op[i].value; - got_value = 0; + if (!isvalue) { + break; + } } + op[i].value = (!is_sib) ? value : op[i].value; if ((op[i].type == 1 && op[i].id == MEM_RIND) || (!op[i].type)) { op[i].is_ind = (op[i].type == 1 && op[i].id == MEM_RIND); op[i].type = 1; @@ -528,11 +516,6 @@ token *get_operands(token *t, operand *op, uint64_t address, uint8_t rs, uint8_t op[i].id = MEM_ZMR; } } - if (got_value && !is_comma) { - if (t && t->subtype != TOK_CSV) { - t = skip_expr(t, (!stop_comma) ? expr_type : 0xFF, stop_comma, dbg); - } - } i += is_comma; if (old_i != i) { got_value = 0; @@ -561,21 +544,6 @@ token *get_operands(token *t, operand *op, uint64_t address, uint8_t rs, uint8_t is_comma = (is_comma >= 2) ? 0 : is_comma; i += is_comma; break; - case TOK_EXPR: - expr_type = t->type; - switch (expr_type) { - default : stop_comma = 1; break; - case EXPR_MUL : stop_comma = 0; break; - } - if (!got_value) { - if (t->next && t->next->id != TOK_REG) { - value = get_val(t, address, (rs != 0xFF) ? rs : 0, (!stop_comma) ? expr_type : 0xFF, stop_comma, dbg); - got_value = 1; - } - } else { - got_value = 0; - } - break; case TOK_CC: op[0].cc = t->byte; i = 3; @@ -1017,8 +985,8 @@ uint64_t parse_tokens(token *t, line **l, bytecount *bc, uint8_t isasm, uint64_t switch (t->type) { case DIR_STRUCT: case DIR_UNION : handle_struct(l, address, 0, dbg); break; - case DIR_RES: t = t->next; address += get_val(t, address, 3, 0xFF, 0, dbg); break; - case DIR_ORG: t = t->next; address = get_val(t, address, 3, 0xFF, 0, dbg); break; + case DIR_RES: t = t->next; address += get_val(t->expr, address, 3, 0, dbg); break; + case DIR_ORG: t = t->next; address = get_val(t->expr, address, 3, 0, dbg); break; case DIR_BYTE: case DIR_WORD: case DIR_DWORD: @@ -1034,7 +1002,7 @@ uint64_t parse_tokens(token *t, line **l, bytecount *bc, uint8_t isasm, uint64_t return address; } -token *make_token(uint8_t id, uint8_t type, uint8_t space, uint8_t tab, uint64_t value, char *str, symbol *s) { +token *make_token(uint8_t id, uint8_t type, uint8_t space, uint8_t tab, uint64_t value, char *str, symbol *s, expr *e) { token *new_tok = malloc(sizeof(token)); (last_tok) ? (last_tok->next = new_tok) : (tokens = new_tok); @@ -1052,6 +1020,8 @@ token *make_token(uint8_t id, uint8_t type, uint8_t space, uint8_t tab, uint64_t if (s) { new_tok->sym = s; + } else if (e) { + new_tok->expr = e; } else if (str[0]) { new_tok->str = str; } else { @@ -1286,17 +1256,6 @@ static void free_fixups(fixup *f) { } } -static inline void free_tmp_symtab(tmp_symtab *st) { - tmp_symtab *tmp; - if (st != NULL) { - tmp = st; - st = st->next; - free(tmp); - tmp = NULL; - free_tmp_symtab(st); - } -} - uint64_t get_tokmem(token *t) { uint64_t i = 0; for (; t; t = t->next, i++); @@ -1327,10 +1286,6 @@ void cleanup() { free_fixups(fixups); fixups = NULL; } - if (tmp_sym_table) { - free_tmp_symtab(tmp_sym_table); - tmp_sym_table = NULL; - } while (i < stridx || i < comidx) { if (i < stridx && string[i]) { free(string[i]); @@ -38,6 +38,7 @@ enum ortho_reg { REG_R13, REG_R14, REG_R15, + REG_PC, }; enum ortho_mem { @@ -68,11 +68,11 @@ int add_symbol(symbol *sym, const char *name, symbol **root, symbol **lsym, symb /*return 0;*/ } } - for (; s != NULL && s->next != NULL; s = s->next) { - if (dbg) { - printf("s: %p, s->next: %p, s->prev: %p\n", s, s->next, s->prev); - } + for (; s != NULL && s->next != NULL; s = s->next) { + if (dbg) { + printf("s: %p, s->next: %p, s->prev: %p\n", s, s->next, s->prev); } + } if ((is_new_scope && *lsym) || *lloc || *csym) { /* if (is_new_scope) { @@ -462,6 +462,29 @@ fixup *find_fixup(fixup *root, const char *name, int depth, uint8_t dbg) { return NULL; } +void find_expr_sym(expr *root, const char *name, symbol *sym, int depth, uint8_t dbg) { + size_t name_len = strlen(name); + if (root) { + if (root->left) { + find_expr_sym(root->left, name, sym, depth, dbg); + } + if (root->right) { + find_expr_sym(root->right, name, sym, depth, dbg); + } + if (root->type == EXPR_SYM) { + if (root->value.sym) { + symbol *s = root->value.sym; + size_t sym_name_len = strlen(s->name); + if (name_len == sym_name_len && name[0] == s->name[0] && !strcmp(name, s->name)) { + if (s->depth == depth && !s->def) { + root->value.sym = sym; + } + } + } + } + } +} + void resolve_symbol_names(line *l, const char *name, symbol *sym, int depth, uint8_t dbg) { size_t name_len = strlen(name); for (token *t = l->tok; t; t = t->next) { @@ -478,6 +501,7 @@ void resolve_symbol_names(line *l, const char *name, symbol *sym, int depth, uin } } break; + case TOK_EXPR: find_expr_sym(t->expr, name, sym, depth, dbg); break; } } if (l->next) { @@ -512,6 +536,416 @@ void new_symbol(token *t, const char *name, uint64_t value, int depth, uint8_t d } } +char *parse_escape(char *s, char *code) { + char dummy; + int count; + char *end; + int base = 0; + unsigned int value; + + if (*s++ != '\\') { + #if 0 + ierror(0); /* Start of escape sequence not found. */ + #endif + } + if (code == NULL) { + code = &dummy; + } + #if 0 + if (!esc_sequences) { + *code = '\\'; + return s; + } + #endif + + switch (*s) { + case 'a' : *code = '\a'; return s+1; + case 'b' : *code = '\b'; return s+1; + case 'f' : *code = '\f'; return s+1; + case 'n' : *code = '\n'; return s+1; + case 'r' : *code = '\r'; return s+1; + case 't' : *code = '\t'; return s+1; + case 'v' : *code = '\v'; return s+1; + case '\\': *code = '\\'; return s+1; + case '\"': *code = '\"'; return s+1; + case '\'': *code = '\''; return s+1; + case 'e' : *code = '\x1B'; return s+1; + case '$' : case 'x' : case 'X' : base = 16; s++; /* Falls Through. */ + case '%' : base = (!base) ? 2 : base; s += (!base); /* Falls Through. */ + case '0' : case '1' : case '2' : case '3' : case '4' : + case '5' : case '6' : case '7' : case '8' : case '9' : + base = (!base) ? 8 : base; + value = strtoull(s, &end, base); + *code = value; + return end; + default : + #if 0 + general_error(35, *s); /* No valid escape sequence was found. */ + #endif + return s; + } +} + +uint64_t parse_quote(char **s, char delm, int get_value, uint8_t dbg) { + uint64_t value = 0; + uint8_t *tmp_val = (uint8_t *)&value; + char *str = *s+1; + + for (int i = 0; *str; i++) { + char c; + /* Are we at the start of an escape character? */ + if (*str == '\\') { + str = parse_escape(str, &c); + } else { + c = *str++; + if (c == delm) { + if (*str == delm) { + /* Allow for multiple repeated + * instances of delm to be treated + * as a single instance of delm. + */ + str++; + } else { + break; + } + } + } + if (get_value && i < sizeof(uint64_t)) { + tmp_val[i] = c; + } + } + + *s = str; + + return value; +} + +expr *make_expr(int type, uint64_t value, symbol *sym, uint8_t dbg) { + expr *new = malloc(sizeof(expr)); + + new->type = type; + new->left = NULL; + new->right = NULL; + + if (sym) { + new->value.sym = sym; + } else { + new->value.val = value; + } + + return new; +} + +int is_reg(const char *str) { + size_t len = strlen(str); + switch (len) { + case 1: + switch(*str) { + case 'a': case 'A': return REG_A; + case 'b': case 'B': return REG_B; + case 'x': case 'X': return REG_X; + case 'y': case 'Y': return REG_Y; + case 'e': case 'E': return REG_E; + case 'c': case 'C': return REG_C; + case 'd': case 'D': return REG_D; + case 's': case 'S': return REG_S; + case 'f': case 'F': return REG_F; + } + break; + case 2: + if (str[1] == 'p' || str[1] == 'P') { + if ((*str == 's' || *str == 'S') || (*str == 'b' || *str == 'B')) { + return ((*str == 's' || *str == 'S')) ? REG_SP : REG_BP; + } + } else if (*str == 'p' || *str == 'P') { + return (str[1] == 'c' || str[1] == 'C') ? REG_PC : -1; + } + break; + case 3: + if (*str == 'r' || *str == 'R') { + int regnum = strtoul(str+1, NULL, 10); + if (regnum >= REG_R11 && regnum <= REG_R15) { + return regnum; + } + } + break; + } + return -1; +} + +#define SKIP_WHITESPACE(str, dbg) \ + /* Skip over any whitespace. */ \ + for (; isdelm(*str, dbg) & 0x10; str++) + + +expr *get_primary_expr(char **line, uint64_t address, int *found_reg, char stop, uint8_t dbg) { + char *str = *line; + char *tmp; + char *scope_name; + symbol *s = NULL; + + uint64_t value; + + int i = 0; + int base; + int type = EXPR_NONE; + int depth = 0; + int scope_depth = 0; + + uint8_t ptok = get_ptok(*str, dbg); + ptok = (is_altok(ptok, dbg)) ? PTOK_ALPHA : ptok; + + if (*str != stop) { + switch (ptok) { + case PTOK_DOLLAR: + case PTOK_PERCENT: + case PTOK_NUMBER: + switch (ptok) { + case PTOK_DOLLAR : base = 16; type = EXPR_HEX; str++; break; + case PTOK_PERCENT: base = 2; type = EXPR_BIN; str++; break; + case PTOK_NUMBER : base = 10; type = EXPR_DEC; /****/ break; + } + + /* Get the number of digits, and + * find the end of the number. + */ + for (; isxdigit(str[i]) && !(isdelm(str[i], dbg) & 0x03); i++); + + tmp = malloc(i+1); + + memcpy(tmp, str, i); + tmp[i] = '\0'; + + value = strtoull(tmp, NULL, base); + break; + case PTOK_SQUOTE: + type = EXPR_CHAR; + value = parse_quote(&str, *str, 1, dbg); + break; + case PTOK_AT: + /* Increment the depth count, by the + * number of '@' signs before the + * symbol name. + */ + for (depth = 0; *str == '@'; str++, depth++); + /* Falls through. */ + case PTOK_ALPHA: + /* Find the end of the symbol name. + * Also increment the depth count every + * time a '.' is found in the symbol name. + */ + for (scope_depth = 0; !isdelm2(str[i], dbg) || str[i] == '.'; scope_depth += (str[i++] == '.')); + tmp = malloc(i); + + memcpy(tmp, str, i); + tmp[i] = '\0'; + + if (is_reg(tmp) >= 0) { + *found_reg = 1; + return NULL; + } else { + scope_name = (!scope_depth) ? mk_scope_name(cur_sym, depth, tmp, dbg) : tmp; + s = get_sym(scope_name, address, NULL, (scope_depth) ? scope_depth : depth, 1, dbg); + isfixup += (s == NULL); + type = EXPR_SYM; + } + break; + } + + str += i; + + SKIP_WHITESPACE(str, dbg); + + if (ptok == PTOK_SCOLON || ptok == PTOK_COMMA || *str == stop) { + *found_reg = 1; + } + + *line = str; + + return make_expr(type, value, s, dbg); + } + return NULL; +} + +expr *get_unary_expr(char **line, uint64_t address, int *found_reg, char stop, uint8_t dbg) { + expr *new = NULL; + char *str = *line; + + if (*str != stop && !(*found_reg)) { + if (*str == '+' || *str == '-' || *str == '<' || *str == '>') { + uint8_t ptok = get_ptok(*str++, dbg); + SKIP_WHITESPACE(str, dbg); + int type; + switch (ptok) { + case PTOK_PLUS : type = EXPR_PLUS ; break; + case PTOK_MINUS: type = EXPR_MINUS; break; + case PTOK_GT : type = EXPR_LOW ; break; + case PTOK_LT : type = EXPR_HIGH ; break; + } + new = make_expr(type, 0, NULL, dbg); + new->left = get_primary_expr(&str, address, found_reg, stop, dbg); + } else { + return get_primary_expr(line, address, found_reg, stop, dbg); + } + } + + *line = str; + return new; +} + +expr *get_shift_expr(char **line, uint64_t address, int *found_reg, char stop, uint8_t dbg) { + expr *left = get_unary_expr(line, address, found_reg, stop, dbg); + expr *new = NULL; + char *str = *line; + + SKIP_WHITESPACE(str, dbg); + + for (; (*str == '<' || *str == '>') && (str[1] == *str);) { + uint8_t ptok = get_ptok(*str, dbg); + + str += 2; + SKIP_WHITESPACE(str, dbg); + + int type; + switch (ptok) { + case PTOK_GT: type = EXPR_RSHFT; break; + case PTOK_LT: type = EXPR_LSHFT; break; + } + + new = make_expr(type, 0, NULL, dbg); + + SKIP_WHITESPACE(str, dbg); + + new->left = left; + new->right = get_unary_expr(&str, address, found_reg, stop, dbg); + left = new; + + if (*str == stop || *found_reg) { + break; + } + } + + *line = str; + return left; +} + +expr *get_or_expr(char **line, uint64_t address, int *found_reg, char stop, uint8_t dbg) { + expr *left = get_shift_expr(line, address, found_reg, stop, dbg); + expr *new = NULL; + char *str = *line; + + SKIP_WHITESPACE(str, dbg); + + for (; *str == '|' && str[1] != '|';) { + str++; + SKIP_WHITESPACE(str, dbg); + + new = make_expr(EXPR_OR, 0, NULL, dbg); + + SKIP_WHITESPACE(str, dbg); + + new->left = left; + new->right = get_shift_expr(&str, address, found_reg, stop, dbg); + left = new; + + if (*str == stop || *found_reg) { + break; + } + } + + *line = str; + return left; +} + +expr *get_additive_expr(char **line, uint64_t address, int *found_reg, char stop, uint8_t dbg) { + expr *left = get_or_expr(line, address, found_reg, stop, dbg); + expr *new = NULL; + char *str = *line; + SKIP_WHITESPACE(str, dbg); + + for (; (*str == '+' && str[1] != '+') || (*str == '-' && str[1] != '-');) { + uint8_t ptok = get_ptok(*str++, dbg); + + SKIP_WHITESPACE(str, dbg); + + int type; + switch (ptok) { + case PTOK_PLUS : type = EXPR_PLUS ; break; + case PTOK_MINUS: type = EXPR_MINUS; break; + } + + new = make_expr(type, 0, NULL, dbg); + + SKIP_WHITESPACE(str, dbg); + + new->left = left; + new->right = get_or_expr(&str, address, found_reg, stop, dbg); + left = new; + + if (*str == stop || *found_reg) { + break; + } + } + + *line = str; + return left; +} + +#undef SKIP_WHITESPACE + +int get_expr_type(char *str, int *found_reg, char stop, uint8_t dbg) { + /* Skip over any whitespace. */ + for (; isdelm(*str, dbg) & 0x10; str++); + + if (*str != stop) { + uint8_t ptok = get_ptok(*str, dbg); + + int i = 0; + char *tmp; + + ptok = (is_altok(ptok, dbg)) ? PTOK_ALPHA : ptok; + + switch (ptok) { + case PTOK_PLUS : return EXPR_PLUS ; + case PTOK_MINUS: return EXPR_MINUS; + case PTOK_PIPE : return EXPR_OR ; + case PTOK_GT : return (get_ptok(*(++str), dbg) == PTOK_GT) ? (EXPR_RSHFT) : (EXPR_LOW) ; + case PTOK_LT : return (get_ptok(*(++str), dbg) == PTOK_LT) ? (EXPR_LSHFT) : (EXPR_HIGH); + case PTOK_DOLLAR : return EXPR_HEX ; + case PTOK_PERCENT: return EXPR_BIN ; + case PTOK_NUMBER : return EXPR_DEC ; + case PTOK_SQUOTE : return EXPR_CHAR; + case PTOK_AT: + for (; *str == '@'; str++); + /* Falls through. */ + case PTOK_ALPHA: + /* Find the end of the symbol name. */ + for (; !isdelm2(str[i], dbg) || str[i] == '.'; i++); + tmp = malloc(i); + + memcpy(tmp, str, i); + tmp[i] = '\0'; + + if (is_reg(tmp) < 0) { + return EXPR_SYM; + } + break; + } + } + *found_reg = 1; + return EXPR_NONE; +} + +expr *parse_expr(char **line, uint64_t address, int *found_reg, char stop, uint8_t dbg) { + char *str = *line; + int dummy = 0; + + found_reg = (found_reg == NULL) ? &dummy : found_reg; + expr *tree = get_additive_expr(&str, address, found_reg, stop, dbg); + *line = str; + return tree; +} + uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { char sym[0x100]; uint16_t i = 0; @@ -550,6 +984,8 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { uint8_t fall = 0; uint8_t done = 0; + char delm = ','; + line *l = NULL; token *st = NULL; @@ -586,70 +1022,19 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { while (isdelm(str[i], dbg) != 1) { uint8_t offset = 0; base = 0; - space = 0; - tab = 0; - while (isdelm(str[i+j], dbg) == 16) { - tab += str[i+j] == '\t'; - space += str[i+j] == ' '; - j++; - } j = 0; + for (tab = 0, space = 0; isdelm(str[i], dbg) == 16; tab += (str[i] == '\t'), space += (str[i] == ' '), i++); if (dbg) { printf("lex(): tab: %u, space: %u\n", tab, space); } - if (isdelm(str[i], dbg) == 16) { - for (; isdelm(str[i], dbg) == 16; i++); - } uint8_t ptok = get_ptok(str[i], dbg); if (is_altok(ptok, dbg)) { - offset++; - if (((ptok == PTOK_S || ptok == PTOK_B) && toupper(str[i+1]) == 'P') || (ptok == PTOK_P && toupper(str[i+1]) == 'C')) { - offset++; - } - int is_alpha = 0; - switch (get_ptok(str[i+offset], dbg)) { - case PTOK_B : - case PTOK_E : - case PTOK_X : - case PTOK_Y : - case PTOK_S : - case PTOK_P : - case PTOK_A : - case PTOK_C : - case PTOK_D : - case PTOK_F : - case PTOK_R : - case PTOK_ALPHA : ptok = PTOK_ALPHA; is_alpha = 1; break; - case PTOK_NUMBER: - if (ptok == PTOK_R) { - char reg_num[3]; - int isnum; - for (isnum = 0; isdigit(str[i+offset]) && !(isdelm(str[i+offset], dbg) & 0x03) && isnum < 2; offset++, isnum++) { - reg_num[isnum] = str[i+offset]; - } - reg_num[isnum] = '\0'; - if (isnum == 2) { - int regnum = strtoul(reg_num, NULL, 10); - if (regnum < 11 || regnum > 15) { - ptok = PTOK_ALPHA; - is_alpha = 1; - } - } else { - ptok = PTOK_ALPHA; - is_alpha = 1; - } - } else { - ptok = PTOK_ALPHA; - is_alpha = 1; - } - break; - } - if (ptok == PTOK_P && toupper(str[i+1]) != 'C') { - ptok = PTOK_ALPHA; - is_alpha = 1; - } - - ptok = (!is_inst && !is_alpha) ? PTOK_ALPHA : ptok; + for (; !isdelm2(str[i+j], dbg) || (is_inst && str[i+j] == '.'); j++); + memcpy(lexeme, &str[i], j); + lexeme[j] = '\0'; + j = 0; + ptok = (!is_inst || is_reg(lexeme) < 0) ? PTOK_ALPHA : ptok; + memset(lexeme, 0, strlen(lexeme)+1); } switch (ptok) { @@ -685,7 +1070,7 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { i -= j; } else { l->count++; - t = make_token(lex_type, k, space, tab, 0, "", NULL); + t = make_token(lex_type, k, space, tab, 0, "", NULL, NULL); } } else { for (k = 0; !(isdelm(lexeme[k], dbg) & 17); k++) { @@ -709,7 +1094,7 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { break; } l->count++; - t = make_token(lex_type, rs, space, tab, 0, "", NULL); + t = make_token(lex_type, rs, space, tab, 0, "", NULL, NULL); if (t) { lt = t; t = t->next; @@ -719,13 +1104,17 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { } break; case PTOK_DQUOTE: - i++; - for (; isdelm(str[i+j], dbg) != 4 || isesc; j++) { - isesc = (str[i+j] == '\\' && str[i+(j-1)] != '\\'); - } - memcpy(lexeme, str+i, j); - lexeme[j] = '\0'; - i += j; + do { + char *tmp = (str + i); + int get_value = (ptok == PTOK_SQUOTE); + value = parse_quote(&tmp, str[i], get_value, dbg); + tmp--; + i++; + j = tmp - (str + i); + memcpy(lexeme, str+i, j); + lexeme[j] = '\0'; + i += j; + } while (0); strid = get_string(lexeme, dbg); if (strid == 0xFFFF) { strid = stridx; @@ -743,70 +1132,35 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { } lex_type = TOK_STRING; l->count++; - t = make_token(lex_type, 0, space, tab, 0, string[strid], NULL); + t = make_token(lex_type, 0, space, tab, 0, string[strid], NULL, NULL); break; + case PTOK_SQUOTE: case PTOK_DOLLAR: case PTOK_PERCENT: case PTOK_NUMBER: - value = 0; - switch (ptok) { - case PTOK_DOLLAR : base = 16; lex_type = TOK_HEX; i++; break; - case PTOK_PERCENT: base = 2; lex_type = TOK_BIN; i++; break; - case PTOK_NUMBER : base = 10; lex_type = TOK_DEC; /**/ break; - } - for (; isxdigit(str[i+j]) && !(isdelm(str[i+j], dbg) & 0x03); j++); - memcpy(lexeme, str+i, j); - lexeme[j] = '\0'; - i += j; - value = strtoull(lexeme, NULL, base); - if (lt->id == TOK_SYM) { - new_symbol(lt, sym, value, depth, dbg); - depth = 0; - if (dbg) { - printf("lex(): isfixup: %u\n", isfixup); - } - } l->count++; - t = make_token(lex_type, 0, space, tab, value, "", NULL); - t->digits = (lt->id != TOK_SYM) ? j : 0; - break; - case PTOK_SQUOTE: - i++; - k = 0; - j = 0; - while (isdelm(str[i], dbg) != 8 || isesc) { - isesc = (str[i] == '\\' && str[i-1] != '\\'); - lexeme[j++] = str[i++]; - } - isesc = 0; - lexeme[j] = '\0'; - for (j = 0; lexeme[k] != '\0' && j < 7; k++) { - switch (lexeme[k]) { - case '\\': - switch (lexeme[++k]) { - case 'n' : ch.u8[j++] = '\n'; break; - case 'r' : ch.u8[j++] = '\r'; break; - case 't' : ch.u8[j++] = '\t'; break; - case 'b' : ch.u8[j++] = '\b'; break; - case '\'': ch.u8[j++] = '\''; break; - case '\"': ch.u8[j++] = '\"'; break; - case '\\': ch.u8[j++] = '\\'; break; - } - break; - default: ch.u8[j++] = lexeme[k]; - } - } - lex_type = TOK_CHAR; - l->count++; - t = make_token(lex_type, 0, space, tab, ch.u64, "", NULL); + do { + lex_type = TOK_EXPR; + memset(lexeme, 0, strlen(lexeme)+1); + char *tmp = &str[i]; + expr *e = parse_expr(&tmp, address, NULL, delm, dbg); + t = make_token(lex_type, 0, space, tab, 0, "", NULL, e); + j = tmp - &str[i]; + memcpy(lexeme, &str[i], j); + j = 0; + i = tmp - str; + t->subtype = (t->subtype == 0xFF && lex_subtype != 0xFF) ? lex_subtype : t->subtype; + lex_subtype = 0xFF; + } while (0); break; case PTOK_LBRACK: case PTOK_HASH : lex_type = TOK_MEM; value = (ptok == PTOK_LBRACK) ? MEM_IND : MEM_IMM; l->count++; - t = make_token(lex_type, value, space, tab, 0, "", NULL); + t = make_token(lex_type, value, space, tab, 0, "", NULL, NULL); lex_type = (ptok == PTOK_LBRACK) ? TOK_IND : TOK_IMM; + delm = (ptok == PTOK_LBRACK) ? ')' : delm; t->subtype = (t->subtype == 0xFF && lex_subtype != 0xFF) ? lex_subtype : t->subtype; if (lex_subtype != 0xFF) { lex_subtype = 0xFF; @@ -820,32 +1174,37 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { case PTOK_LT: case PTOK_PIPE: lex_type = TOK_EXPR; - switch (ptok) { - case PTOK_PLUS : value = EXPR_PLUS ; break; - case PTOK_MINUS: value = EXPR_MINUS; break; - case PTOK_PIPE : value = EXPR_OR ; break; - case PTOK_GT : value = (get_ptok(str[i+1], dbg) == PTOK_GT) ? (EXPR_RSHFT) : (EXPR_LOW) ; break; - case PTOK_LT : value = (get_ptok(str[i+1], dbg) == PTOK_LT) ? (EXPR_LSHFT) : (EXPR_HIGH); break; - } l->count++; - t = make_token(lex_type, value, space, tab, 0, "", NULL); memset(lexeme, 0, strlen(lexeme)+1); - lexeme[j++] = str[i]; - if (value == EXPR_LSHFT || value == EXPR_RSHFT) { - lexeme[j++] = str[++i]; - } + do { + char *tmp = &str[i]; + expr *e = parse_expr(&tmp, address, NULL, delm, dbg); + t = make_token(lex_type, 0, space, tab, 0, "", NULL, e); + j = tmp - &str[i]; + memcpy(lexeme, &str[i], j); + j = 0; + i = tmp - str; + t->subtype = (t->subtype == 0xFF && lex_subtype != 0xFF) ? lex_subtype : t->subtype; + lex_subtype = 0xFF; + } while (0); break; case PTOK_EQU: i++; lex_type = TOK_SYM; memset(lexeme, 0, strlen(lexeme)+1); lexeme[j] = str[i]; + if (lt) { + lt->id = lex_type; + lt->type = depth; + } + new_symbol(lt, sym, address, depth, dbg); (t) ? (t->subspace = space) : (lt->subspace = space); (t) ? (t->subtab = tab) : (lt->subtab = tab); break; case PTOK_RBRACK: i++; lex_type = TOK_IND; + delm = (delm == ')') ? ',' : delm; lexeme[j] = ')'; lexeme[j+1] = '\0'; lexeme[j+2] = '\0'; @@ -888,27 +1247,12 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { lexeme[j+1] = (ptok == PTOK_R || ((ptok == PTOK_S || ptok == PTOK_B) && get_ptok(str[i], dbg) == PTOK_P)) ? str[i++] : '\0'; lexeme[j+2] = (ptok == PTOK_R) ? str[i++] : '\0'; lexeme[j+3] = '\0'; + lex_type = TOK_REG; - switch (ptok) { - case PTOK_A: value = REG_A; break; - case PTOK_X: value = REG_X; break; - case PTOK_Y: value = REG_Y; break; - case PTOK_E: value = REG_E; break; - case PTOK_C: value = REG_C; break; - case PTOK_D: value = REG_D; break; - case PTOK_S: - case PTOK_B: - if (get_ptok(lexeme[j+1], dbg) == PTOK_P) { - value = (ptok == PTOK_S) ? REG_SP : REG_BP; - } else { - value = (ptok == PTOK_S) ? REG_S : REG_B; - } - break; - case PTOK_F: value = REG_F; break; - case PTOK_R: value = strtoull(lexeme+j+1, NULL, 10); break; - } + value = is_reg(lexeme); + l->count++; - t = make_token(lex_type, value, space, tab, 0, "", NULL); + t = make_token(lex_type, value, space, tab, 0, "", NULL, NULL); t->subtype = (t->subtype == 0xFF && lex_subtype != 0xFF) ? lex_subtype : t->subtype; lex_subtype = 0xFF; break; @@ -919,11 +1263,12 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { of = 2; lex_type = TOK_OF; l->count++; - t = make_token(lex_type, of, space, tab, 0, "", NULL); + t = make_token(lex_type, of, space, tab, 0, "", NULL, NULL); break; case PTOK_AT: memset(lexeme, 0, strlen(lexeme)+1); for (char *tmp = str+i; *tmp++ == '@'; depth++); + i += depth; lexeme[j] = '@'; lex_type = TOK_LOCAL; if (lt || t) { @@ -977,20 +1322,36 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { lex_type = TOK_COMMENT; l->count++; if (j) { - t = make_token(lex_type, 0, space, tab, 0, comment[comid], NULL); + t = make_token(lex_type, 0, space, tab, 0, comment[comid], NULL, NULL); } else { - t = make_token(lex_type, 0, space, tab, 0, "" , NULL); + t = make_token(lex_type, 0, space, tab, 0, "" , NULL, NULL); } break; case PTOK_ALPHA: + /* Get the length of the token. */ for (; !isdelm2(str[i+j], dbg) || (is_inst && str[i+j] == '.'); j++); memcpy(lexeme, str+i, j); lexeme[j] = '\0'; i += j; isch = 0; isop = 0; - if (j > 1 && j <= 3 && str[i] != ':' && !is_struct) { + /* We need to figure out if we're allowed to + * search for a valid instruction name. + * + * We're only allowed to so, if: + * + * 1. The previous token wasn't a directive. + * 2. There wasn't an instruction before us. + * 3. The length of the token is at, or above + * the length of the shortest instruction. + * 4. The length of the token is at, or below + * the length of the longest instruction. + * 5. The character after the token isn't a + * label delimiter. + * 6. We're not within a struct/union block. + */ + if (!(lt && lt->id == TOK_DIR) && !is_inst && j > 1 && j <= 3 && str[i] != ':' && !is_struct) { for (k = 0; k < OPNUM; k++) { int find_ext = (k < EXT_OPNUM); int find_ortho = (k < ORTHO_OPNUM); @@ -1010,7 +1371,7 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { isop = 1; is_inst = 1; l->count++; - t = make_token(lex_type, 0xFF, space, tab, k, "", NULL); + t = make_token(lex_type, 0xFF, space, tab, k, "", NULL, NULL); break; } } @@ -1024,7 +1385,7 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { if (!strcasecmp(lexeme, set_cc[k])) { lex_type = TOK_CC; l->count++; - t = make_token(lex_type, 0xFF, space, tab, k, "", NULL); + t = make_token(lex_type, 0xFF, space, tab, k, "", NULL, NULL); } } } @@ -1035,28 +1396,31 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { if (ret == PTOK_COLON || ret == PTOK_EQU) { depth = (lex_type == TOK_LOCAL); } - lex_type = TOK_SYM; + int is_expr = (!is_struct && str[i+spaces] != ':' && str[i+spaces] != '='); l->count++; - t = make_token(lex_type, depth, space, tab, 0, "", NULL); memcpy(sym, lexeme, j+1); - if (dbg) { - printf("lex(): spaces: %u\n", spaces); + if (is_expr) { + i -= j + (depth); + lex_type = TOK_EXPR; + memset(lexeme, 0, strlen(lexeme)+1); + char *tmp = &str[i]; + expr *e = parse_expr(&tmp, address, NULL, delm, dbg); + j = tmp - &str[i]; + memcpy(lexeme, &str[i], j); + /*i += j;*/ + i = tmp - str; + t = make_token(lex_type, 0, space, tab, 0, "", NULL, e); + t->subtype = (t->subtype == 0xFF && lex_subtype != 0xFF) ? lex_subtype : t->subtype; + lex_subtype = 0xFF; + } else { + memcpy(sym, lexeme, j+1); + lex_type = TOK_SYM; + t = make_token(lex_type, depth, space, tab, 0, "", NULL, NULL); } + if (is_struct) { create_struct(cur_sym, l, t, lt, sym, dbg); depth = 0; - } else if ((str[i+spaces] != ':' && str[i+spaces] != '=')) { - symbol *s; - int scope_depth; - char *tmp = lexeme; - for (scope_depth = 0; *tmp; scope_depth += (*tmp++ == '.')); - char *scope_name = (!scope_depth) ? mk_scope_name(cur_sym, depth, lexeme, dbg) : lexeme; - t->sym = get_sym(scope_name, address, t, (scope_depth) ? scope_depth : depth, 1, dbg); - isfixup += (t && t->sym == NULL); - depth = 0; - if (dbg) { - printf("lex(): isfixup: %u\n", isfixup); - } } /*if (!is_struct && t && t->sym && t->sym->isstruct) { tmp_sym = t->sym; @@ -1074,7 +1438,7 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { j = 0; if ((lex_type == TOK_OPCODE || lex_type == TOK_EXTOP) && !isop) { j = 0; - } else if (lex_type == TOK_EXPR || (lex_type != TOK_MEMBER && !isdelm2(str[i], dbg))) { + } else if (lex_type != TOK_EXPR && lex_type != TOK_LOCAL && lex_type != TOK_MEMBER && !isdelm2(str[i], dbg)) { i++; } switch (lex_type) { @@ -1093,22 +1457,13 @@ uint64_t lex(char *str, uint64_t address, uint16_t bline, uint8_t dbg) { } } if (i) { - l->tok = tokens; + l->tok = tokens; token *tok = tokens; - if (tok->id == TOK_SYM && tok->next) { + if ((tok->id == TOK_SYM || tok->id == TOK_LABEL) && tok->next) { symbol *s = tok->sym; for (; tok; tok = tok->next) { - switch (tok->id) { - case TOK_HEX : - case TOK_BIN : - case TOK_DEC : - case TOK_CHAR: - case TOK_EXPR: - s->val = get_val(tok, address, 3, 0xFF, 0, dbg); - if (tok->next) { - tok = skip_expr(tok, 0xFF, 0, dbg); - } - break; + if (tok->id == TOK_EXPR) { + s->val = get_val(tok->expr, address, 3, 0, dbg); } } } |