| @@ -42,6 +42,7 @@ static const char *error_descs[] = { | |||
| [ED_SYM_DUPE_LABELS] = "duplicate definitions for label", | |||
| [ED_SYM_NO_LABEL] = "undefined reference to label", | |||
| [ED_SYM_IS_REGISTER] = "labels cannot share names with registers", | |||
| [ED_PS_OP_TOO_LONG] = "opcode mnemonic is too long (2-4 characters)", | |||
| [ED_PS_OP_TOO_SHORT] = "opcode mnemonic is too short (2-4 characters)", | |||
| @@ -49,7 +50,8 @@ static const char *error_descs[] = { | |||
| [ED_PS_OP_UNKNOWN] = "unknown opcode mnemonic", | |||
| [ED_PS_TOO_FEW_ARGS] = "too few arguments for opcode", | |||
| [ED_PS_TOO_MANY_ARGS] = "too many arguments for opcode", | |||
| // [ED_PS_ARG1_TYPE] = "invalid type for first argument", | |||
| // [ED_PS_ARG1_NEED_REG] = "invalid type for first argument (needs register)", | |||
| // [ED_PS_ARG1_BAD_REG] = "first argument should be a register", | |||
| }; | |||
| /* Internal structs */ | |||
| @@ -41,6 +41,7 @@ typedef enum { | |||
| ED_SYM_DUPE_LABELS, | |||
| ED_SYM_NO_LABEL, | |||
| ED_SYM_IS_REGISTER, | |||
| ED_PS_OP_TOO_LONG, | |||
| ED_PS_OP_TOO_SHORT, | |||
| @@ -0,0 +1,59 @@ | |||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||
| #pragma once | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| typedef enum { | |||
| AT_REGISTER, | |||
| AT_IMMEDIATE, | |||
| AT_INDIRECT, | |||
| AT_INDEXED, | |||
| AT_LABEL, | |||
| AT_CONDITION | |||
| } ASMArgType; | |||
| typedef enum { | |||
| REG_A, REG_F, REG_B, REG_C, REG_D, REG_E, REG_H, REG_L, REG_I, REG_R, | |||
| REG_AF, REG_BC, REG_DE, REG_HL, REG_IX, REG_IY, | |||
| REG_PC, REG_SP, | |||
| REG_AF_, REG_IXH, REG_IXL, REG_IYH, REG_IYL | |||
| } ASMArgRegister; | |||
| typedef struct { | |||
| uint16_t value; | |||
| bool is_negative; | |||
| } ASMArgImmediate; | |||
| typedef struct { | |||
| bool is_reg; | |||
| ASMArgRegister reg; | |||
| ASMArgImmediate imm; | |||
| } ASMArgIndirect; | |||
| typedef struct { | |||
| ASMArgRegister reg; | |||
| ASMArgImmediate imm; | |||
| } ASMArgIndexed; | |||
| typedef char* ASMArgLabel; | |||
| typedef enum { | |||
| COND_NZ, COND_N, COND_NC, COND_C, COND_PO, COND_PE, COND_P, COND_M | |||
| } ASMArgCondition; | |||
| typedef union { | |||
| ASMArgRegister reg; | |||
| ASMArgImmediate imm; | |||
| ASMArgIndirect indirect; | |||
| ASMArgIndexed index; | |||
| ASMArgLabel label; | |||
| ASMArgCondition cond; | |||
| } ASMArgData; | |||
| typedef struct { | |||
| ASMArgType type; | |||
| ASMArgData data; | |||
| } ASMInstArg; | |||
| @@ -29,11 +29,11 @@ | |||
| #define JOIN_(a, b, c, d) ((uint32_t) ((a << 24) + (b << 16) + (c << 8) + d)) | |||
| #define DISPATCH_(s, z) ( \ | |||
| z == 2 ? JOIN_(s[0], s[1], 0x00, 0x00) : \ | |||
| z == 3 ? JOIN_(s[0], s[1], s[2], 0x00) : \ | |||
| JOIN_(s[0], s[1], s[2], s[3])) \ | |||
| (z) == 2 ? JOIN_(s[0], s[1], 0x00, 0x00) : \ | |||
| (z) == 3 ? JOIN_(s[0], s[1], s[2], 0x00) : \ | |||
| JOIN_(s[0], s[1], s[2], s[3])) \ | |||
| #define MAKE_CMP_(s) DISPATCH_(s, (sizeof(s) / sizeof(char) - 1)) | |||
| #define MAKE_CMP_(s) DISPATCH_(s, sizeof(s) / sizeof(char) - 1) | |||
| #define HANDLE(m) if (key == MAKE_CMP_(#m)) return parse_inst_##m; | |||
| @@ -27,9 +27,6 @@ | |||
| */ | |||
| bool parse_bool(bool *result, const char *arg, ssize_t size) | |||
| { | |||
| if (size <= 0 || size > 5) | |||
| return false; | |||
| switch (size) { | |||
| case 1: // 0, 1 | |||
| if (*arg == '0' || *arg == '1') | |||
| @@ -176,6 +173,63 @@ bool parse_bytes(uint8_t **result, size_t *length, const char *arg, ssize_t size | |||
| } | |||
| /* | |||
| Read in a register argument and store it in *result. | |||
| */ | |||
| bool parse_register(ASMArgRegister *result, const char *arg, ssize_t size) | |||
| { | |||
| if (size < 1 || size > 3) | |||
| return false; | |||
| #define LCASE(c) ((c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c) | |||
| char buf[3] = {'\0'}; | |||
| switch (size) { | |||
| case 3: buf[2] = LCASE(arg[2]); | |||
| case 2: buf[1] = LCASE(arg[1]); | |||
| case 1: buf[0] = LCASE(arg[0]); | |||
| } | |||
| #undef LCASE | |||
| switch (size) { | |||
| case 1: | |||
| switch (buf[0]) { | |||
| case 'a': return (*result = REG_A), true; | |||
| case 'f': return (*result = REG_F), true; | |||
| case 'b': return (*result = REG_B), true; | |||
| case 'c': return (*result = REG_C), true; | |||
| case 'd': return (*result = REG_D), true; | |||
| case 'e': return (*result = REG_E), true; | |||
| case 'h': return (*result = REG_H), true; | |||
| case 'l': return (*result = REG_L), true; | |||
| case 'i': return (*result = REG_I), true; | |||
| case 'r': return (*result = REG_R), true; | |||
| } | |||
| return false; | |||
| case 2: | |||
| switch ((buf[0] << 8) + buf[1]) { | |||
| case 0x6166: return (*result = REG_AF), true; | |||
| case 0x6263: return (*result = REG_BC), true; | |||
| case 0x6465: return (*result = REG_DE), true; | |||
| case 0x686c: return (*result = REG_HL), true; | |||
| case 0x6978: return (*result = REG_IX), true; | |||
| case 0x6979: return (*result = REG_IY), true; | |||
| case 0x7063: return (*result = REG_PC), true; | |||
| case 0x7370: return (*result = REG_SP), true; | |||
| } | |||
| return false; | |||
| case 3: | |||
| switch ((buf[0] << 16) + (buf[1] << 8) + buf[2]) { | |||
| case 0x616627: return (*result = REG_AF_), true; | |||
| case 0x697868: return (*result = REG_IXH), true; | |||
| case 0x69786c: return (*result = REG_IXL), true; | |||
| case 0x697968: return (*result = REG_IYH), true; | |||
| case 0x69796c: return (*result = REG_IYL), true; | |||
| } | |||
| return false; | |||
| } | |||
| return false; | |||
| } | |||
| /* | |||
| Read in a boolean argument from the given line and store it in *result. | |||
| */ | |||
| DIRECTIVE_PARSE_FUNC(bool, bool) | |||
| @@ -6,6 +6,7 @@ | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| #include "inst_args.h" | |||
| #include "state.h" | |||
| #define dparse__Bool dparse_bool | |||
| @@ -16,6 +17,7 @@ bool parse_bool(bool*, const char*, ssize_t); | |||
| bool parse_uint32_t(uint32_t*, const char*, ssize_t); | |||
| bool parse_string(char**, size_t*, const char*, ssize_t); | |||
| bool parse_bytes(uint8_t**, size_t*, const char*, ssize_t); | |||
| bool parse_register(ASMArgRegister*, const char*, ssize_t); | |||
| bool dparse_bool(bool*, const ASMLine*, const char*); | |||
| bool dparse_uint32_t(uint32_t*, const ASMLine*, const char*); | |||
| @@ -94,6 +94,19 @@ static inline bool is_valid_label_char(char c, bool first) | |||
| } | |||
| /* | |||
| Functions similar memcpy, but lowercases the characters along the way. | |||
| */ | |||
| static void memcpy_lc(char *restrict dst, const char *restrict src, size_t n) | |||
| { | |||
| while (n-- > 0) { | |||
| char c = *(src++); | |||
| if (c >= 'A' && c <= 'Z') | |||
| c += 'a' - 'A'; | |||
| *(dst++) = c; | |||
| } | |||
| } | |||
| /* | |||
| Preprocess a single source line for labels. | |||
| Return the index of first non-whitespace non-label character. *head_ptr is | |||
| @@ -125,7 +138,7 @@ static size_t read_labels( | |||
| if (!line->data) | |||
| OUT_OF_MEMORY() | |||
| strncpy(line->data, source + start, i - start + 1); | |||
| memcpy_lc(line->data, source + start, i - start + 1); | |||
| line->length = i - start + 1; | |||
| line->is_label = true; | |||
| @@ -7,6 +7,7 @@ | |||
| #include "tokenizer.h" | |||
| #include "directives.h" | |||
| #include "instructions.h" | |||
| #include "inst_args.h" | |||
| #include "parse_util.h" | |||
| #include "../logging.h" | |||
| #include "../mmu.h" | |||
| @@ -55,12 +56,16 @@ static inline int8_t default_bank_slot(uint8_t bank) | |||
| /* | |||
| Add a given line, representing a label, to the symbol table. | |||
| Return NULL on success and an ErrorInfo object on failure (in the case of | |||
| duplicate labels). | |||
| Return NULL on success and an ErrorInfo object on failure (e.g. in the case | |||
| of duplicate labels). | |||
| */ | |||
| static ErrorInfo* add_label_to_table( | |||
| ASMSymbolTable *symtable, const ASMLine *line, size_t offset, int8_t slot) | |||
| { | |||
| ASMArgRegister reg; | |||
| if (parse_register(®, line->data, line->length - 1)) | |||
| return error_info_create(line, ET_SYMBOL, ED_SYM_IS_REGISTER); | |||
| char *symbol = strndup(line->data, line->length - 1); | |||
| if (!symbol) | |||
| OUT_OF_MEMORY() | |||
| @@ -69,6 +74,7 @@ static ErrorInfo* add_label_to_table( | |||
| if (current) { | |||
| ErrorInfo *ei = error_info_create(line, ET_SYMBOL, ED_SYM_DUPE_LABELS); | |||
| error_info_append(ei, current->line); | |||
| free(symbol); | |||
| return ei; | |||
| } | |||
| @@ -249,7 +255,7 @@ static ErrorInfo* parse_instruction( | |||
| uint8_t *bytes; | |||
| size_t arglen = line->length - i, length; | |||
| char *argstart = arglen > 0 ? line->data + i : NULL, *symbol; | |||
| char *argstart = arglen > 0 ? line->data + i : NULL, *symbol = NULL; | |||
| ASMInstParser parser = get_inst_parser(mnemonic); | |||
| if (!parser) | |||