@@ -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) | |||