From ade92aea515dfce3fe29daed33da05ce010263e5 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Fri, 15 May 2015 17:25:29 -0400 Subject: [PATCH] Starting work on new ASM instructions system. --- makefile | 3 + scripts/update_asm_instructions.py | 89 ++++ src/assembler/errors.c | 7 +- src/assembler/errors.h | 7 +- src/assembler/inst_args.h | 31 +- src/assembler/inst_support.c | 98 ---- src/assembler/inst_support.h | 155 ------- src/assembler/instructions.c | 895 +++++++------------------------------ src/assembler/instructions.inc.c | 733 ++++++++++++++++++++++++++++++ src/assembler/instructions.yml | 323 +++++++++++++ src/assembler/parse_util.c | 76 +++- src/assembler/parse_util.h | 2 +- src/assembler/tokenizer.c | 2 +- 13 files changed, 1384 insertions(+), 1037 deletions(-) create mode 100755 scripts/update_asm_instructions.py delete mode 100644 src/assembler/inst_support.c delete mode 100644 src/assembler/inst_support.h create mode 100644 src/assembler/instructions.inc.c create mode 100644 src/assembler/instructions.yml diff --git a/makefile b/makefile index 57346bc..35bf657 100644 --- a/makefile +++ b/makefile @@ -47,6 +47,9 @@ $(BUILD)/$(MODE)/%.o: %.c -include $(DEPS) +$(SOURCES)/assembler/instructions.inc.c: $(SOURCES)/assembler/instructions.yml + python scripts/update_asm_instructions.py + test: test-all test-z80 test-asm test-dasm test-all: diff --git a/scripts/update_asm_instructions.py b/scripts/update_asm_instructions.py new file mode 100755 index 0000000..d2c5afa --- /dev/null +++ b/scripts/update_asm_instructions.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2014-2015 Ben Kurtovic +# Released under the terms of the MIT License. See LICENSE for details. + +""" +This script generates 'src/assembler/instructions.inc.c' from +'src/assembler/instructions.yml'. It should be run automatically by make +when the latter is modified, but can also be run manually. +""" + +from __future__ import print_function + +import re +import time + +SOURCE = "src/assembler/instructions.yml" +DEST = "src/assembler/instructions.inc.c" + +ENCODING = "utf8" +TAB = " " * 4 + +try: + import yaml +except ImportError: + print("Error: PyYAML is required (https://pypi.python.org/pypi/PyYAML)\n" + "If you don't want to rebuild {0}, do:\n`make -t {0}`".format(DEST)) + exit(1) + +re_date = re.compile(r"^(\s*@AUTOGEN_DATE\s*)(.*?)$", re.M) +re_inst = re.compile( + r"(/\* @AUTOGEN_INST_BLOCK_START \*/\n*)(.*?)" + r"(\n*/\* @AUTOGEN_INST_BLOCK_END \*/)", re.S) +re_lookup = re.compile( + r"(/\* @AUTOGEN_LOOKUP_BLOCK_START \*/\n*)(.*?)" + r"(\n*/\* @AUTOGEN_LOOKUP_BLOCK_END \*/)", re.S) + +def build_inst(name, data): + """ + Convert data for an individual instruction into a C parse function. + """ + # TODO + return "INST_FUNC({0})\n{{\n}}".format(name) + +def build_inst_block(data): + """ + Return the instruction parser block, given instruction data. + """ + return "\n\n".join(build_inst(k, v) for k, v in sorted(data.items())) + +def build_lookup_block(data): + """ + Return the instruction lookup block, given instruction data. + """ + macro = TAB + "HANDLE({0})" + return "\n".join(macro.format(inst) for inst in sorted(data.keys())) + +def process(template, data): + """ + Return C code generated from a source template and instruction data. + """ + inst_block = build_inst_block(data) + lookup_block = build_lookup_block(data) + date = time.asctime(time.gmtime()) + + result = re_date.sub(r"\1{0} UTC".format(date), template) + result = re_inst.sub(r"\1{0}\3".format(inst_block), result) + result = re_lookup.sub(r"\1{0}\3".format(lookup_block), result) + return result + +def main(): + """ + Main script entry point. + """ + with open(SOURCE, "r") as fp: + text = fp.read().decode(ENCODING) + with open(DEST, "r") as fp: + template = fp.read().decode(ENCODING) + + data = yaml.load(text) + result = process(template, data) + + # with open(DEST, "w") as fp: + # fp.write(result.encode(ENCODING)) + print(result) # TODO: remove me! + +if __name__ == "__main__": + main() diff --git a/src/assembler/errors.c b/src/assembler/errors.c index 4a30f10..bc2094c 100644 --- a/src/assembler/errors.c +++ b/src/assembler/errors.c @@ -53,11 +53,8 @@ static const char *error_descs[] = { [ED_PS_TOO_FEW_ARGS] = "too few arguments for opcode", [ED_PS_TOO_MANY_ARGS] = "too many arguments for opcode", [ED_PS_ARG_SYNTAX] = "invalid syntax in argument(s)", - [ED_PS_ARG0_TYPE] = "invalid type for first argument", - [ED_PS_ARG0_BAD_REG] = "unsupported register as first argument", - [ED_PS_ARG1_TYPE] = "invalid type for second argument", - [ED_PS_ARG1_BAD_REG] = "unsupported register as second argument", - [ED_PS_ARG1_RANGE] = "second argument out of range" + [ED_PS_ARG_TYPE] = "ED_PS_ARG_TYPE", // TODO + [ED_PS_ARG_VALUE] = "ED_PS_ARG_VALUE" // TODO }; /* Internal structs */ diff --git a/src/assembler/errors.h b/src/assembler/errors.h index 6f5b8f3..cf7e1cf 100644 --- a/src/assembler/errors.h +++ b/src/assembler/errors.h @@ -52,11 +52,8 @@ typedef enum { ED_PS_TOO_FEW_ARGS, ED_PS_TOO_MANY_ARGS, ED_PS_ARG_SYNTAX, - ED_PS_ARG0_TYPE, - ED_PS_ARG0_BAD_REG, - ED_PS_ARG1_TYPE, - ED_PS_ARG1_BAD_REG, - ED_PS_ARG1_RANGE + ED_PS_ARG_TYPE, + ED_PS_ARG_VALUE } ASMErrorDesc; /* Structs */ diff --git a/src/assembler/inst_args.h b/src/assembler/inst_args.h index 29eaa77..910f11c 100644 --- a/src/assembler/inst_args.h +++ b/src/assembler/inst_args.h @@ -8,12 +8,14 @@ #define MAX_SYMBOL_SIZE 256 typedef enum { - AT_REGISTER, - AT_IMMEDIATE, - AT_INDIRECT, - AT_INDEXED, - AT_LABEL, - AT_CONDITION + AT_NONE = 0x00, + AT_OPTIONAL = 0x01, + AT_REGISTER = 0x02, + AT_IMMEDIATE = 0x04, + AT_INDIRECT = 0x08, + AT_INDEXED = 0x10, + AT_CONDITION = 0x20, + AT_PORT = 0x40 } ASMArgType; typedef enum { @@ -35,20 +37,17 @@ typedef enum { typedef struct { ASMArgImmType mask; + bool is_label; uint16_t uval; int16_t sval; + char label[MAX_SYMBOL_SIZE]; } ASMArgImmediate; typedef struct { - char text[MAX_SYMBOL_SIZE]; -} ASMArgLabel; - -typedef struct { ASMArgType type; union { ASMArgRegister reg; ASMArgImmediate imm; - ASMArgLabel label; } addr; } ASMArgIndirect; @@ -66,9 +65,17 @@ typedef struct { union { ASMArgRegister reg; ASMArgImmediate imm; + } port; +} ASMArgPort; + +typedef struct { + ASMArgType type; + union { + ASMArgRegister reg; + ASMArgImmediate imm; ASMArgIndirect indirect; ASMArgIndexed index; - ASMArgLabel label; ASMArgCondition cond; + ASMArgPort port; } data; } ASMInstArg; diff --git a/src/assembler/inst_support.c b/src/assembler/inst_support.c deleted file mode 100644 index 895f99d..0000000 --- a/src/assembler/inst_support.c +++ /dev/null @@ -1,98 +0,0 @@ -/* Copyright (C) 2014-2015 Ben Kurtovic - Released under the terms of the MIT License. See LICENSE for details. */ - -#include "inst_support.h" - -/* Macro used by parse_arg() */ - -#define TRY_PARSER(func, argtype, field) \ - if (argparse_##func(&arg->data.field, info)) { \ - arg->type = argtype; \ - return ED_NONE; \ - } - -/* - Fill an instruction's byte array with the given data. - - This internal function is only called for instructions longer than four - bytes (of which there is only one: the fake emulator debugging/testing - opcode with mnemonic "emu"), so it does not get used in normal situations. - - Return the value of the last byte inserted, for compatibility with the - INST_SETn_ family of macros. -*/ -uint8_t fill_bytes_variadic(uint8_t *bytes, size_t len, ...) -{ - va_list vargs; - va_start(vargs, len); - for (size_t i = 0; i < len; i++) - bytes[i] = va_arg(vargs, unsigned); - va_end(vargs); - return bytes[len - 1]; -} - -/* - Parse a single instruction argument into an ASMInstArg object. - - Return ED_NONE (0) on success or an error code on failure. -*/ -static ASMErrorDesc parse_arg( - ASMInstArg *arg, const char *str, size_t size, ASMDefineTable *deftable) -{ - ASMArgParseInfo info = {.arg = str, .size = size, .deftable = deftable}; - TRY_PARSER(register, AT_REGISTER, reg) - TRY_PARSER(immediate, AT_IMMEDIATE, imm) - TRY_PARSER(indirect, AT_INDIRECT, indirect) - TRY_PARSER(indexed, AT_INDEXED, index) - TRY_PARSER(condition, AT_CONDITION, cond) - TRY_PARSER(label, AT_LABEL, label) - return ED_PS_ARG_SYNTAX; -} - -/* - Parse an argument string into ASMInstArg objects. - - Return ED_NONE (0) on success or an error code on failure. -*/ -ASMErrorDesc parse_args( - ASMInstArg args[3], size_t *nargs, ASMArgParseInfo ap_info) -{ - ASMErrorDesc err; - ASMDefineTable *dt = ap_info.deftable; - const char *str = ap_info.arg; - size_t size = ap_info.size, start = 0, i = 0; - - while (i < size) { - char c = str[i]; - if (c == ',') { - if (i == start) - return ED_PS_ARG_SYNTAX; - if ((err = parse_arg(&args[*nargs], str + start, i - start, dt))) - return err; - (*nargs)++; - - i++; - if (i < size && str[i] == ' ') - i++; - start = i; - if (i == size) - return ED_PS_ARG_SYNTAX; - if (*nargs >= 3) - return ED_PS_TOO_MANY_ARGS; - } else { - if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || - c == ' ' || c == '+' || c == '-' || c == '(' || c == ')' || - c == '$' || c == '_' || c == '.') - i++; - else - return ED_PS_ARG_SYNTAX; - } - } - - if (i > start) { - if ((err = parse_arg(&args[*nargs], str + start, i - start, dt))) - return err; - (*nargs)++; - } - return ED_NONE; -} diff --git a/src/assembler/inst_support.h b/src/assembler/inst_support.h deleted file mode 100644 index ce282cd..0000000 --- a/src/assembler/inst_support.h +++ /dev/null @@ -1,155 +0,0 @@ -/* Copyright (C) 2014-2015 Ben Kurtovic - Released under the terms of the MIT License. See LICENSE for details. */ - -#pragma once - -#include -#include - -#include "errors.h" -#include "inst_args.h" -#include "parse_util.h" -#include "../util.h" - -/* Helper macros for get_inst_parser() */ - -#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])) \ - -#define MAKE_CMP_(s) DISPATCH_(s, sizeof(s) / sizeof(char) - 1) - -#define HANDLE(m) if (key == MAKE_CMP_(#m)) return parse_inst_##m; - -/* Internal helper macros */ - -#define INST_ALLOC_(len) \ - *length = len; \ - *bytes = cr_malloc(sizeof(uint8_t) * (len)); - -#define INST_SET_(b, val) ((*bytes)[b] = val) -#define INST_SET1_(b1) INST_SET_(0, b1) -#define INST_SET2_(b1, b2) INST_SET1_(b1), INST_SET_(1, b2) -#define INST_SET3_(b1, b2, b3) INST_SET2_(b1, b2), INST_SET_(2, b3) -#define INST_SET4_(b1, b2, b3, b4) INST_SET3_(b1, b2, b3), INST_SET_(3, b4) - -#define INST_DISPATCH_(a, b, c, d, target, ...) target - -#define INST_FILL_BYTES_(len, ...) \ - ((len > 4) ? fill_bytes_variadic(*bytes, len, __VA_ARGS__) : \ - INST_DISPATCH_(__VA_ARGS__, INST_SET4_, INST_SET3_, INST_SET2_, \ - INST_SET1_, __VA_ARGS__)(__VA_ARGS__)); - -#define INST_IX_PREFIX_ 0xDD -#define INST_IY_PREFIX_ 0xFD - -#define INST_PREFIX_(reg) \ - (((reg) == REG_IX || (reg) == REG_IXH || (reg) == REG_IXL) ? \ - INST_IX_PREFIX_ : INST_IY_PREFIX_) - -#define INST_RETURN_WITH_SYMBOL_(len, label, ...) { \ - *symbol = cr_strdup(label.text); \ - INST_ALLOC_(len) \ - INST_FILL_BYTES_(len - 2, __VA_ARGS__) \ - return ED_NONE; \ - } - -/* Essential/basic helper macros */ - -#define INST_FUNC(mnemonic) \ -static ASMErrorDesc parse_inst_##mnemonic( \ - uint8_t **bytes, size_t *length, char **symbol, ASMArgParseInfo ap_info) \ - -#define INST_ERROR(desc) return ED_PS_##desc; - -#define INST_TAKES_NO_ARGS \ - if (ap_info.arg) \ - INST_ERROR(TOO_MANY_ARGS) - -#define INST_TAKES_ARGS(lo, hi) \ - if (!ap_info.arg) \ - INST_ERROR(TOO_FEW_ARGS) \ - ASMInstArg args[3]; \ - size_t nargs = 0; \ - ASMErrorDesc err = parse_args(args, &nargs, ap_info); \ - if (err) \ - return err; \ - if (nargs < lo) \ - INST_ERROR(TOO_FEW_ARGS) \ - if (nargs > hi) \ - INST_ERROR(TOO_MANY_ARGS) - -#define INST_NARGS nargs -#define INST_TYPE(n) args[n].type -#define INST_REG(n) args[n].data.reg -#define INST_IMM(n) args[n].data.imm -#define INST_INDIRECT(n) args[n].data.indirect -#define INST_INDEX(n) args[n].data.index -#define INST_LABEL(n) args[n].data.label -#define INST_COND(n) args[n].data.cond - -#define INST_RETURN(len, ...) { \ - (void) symbol; \ - INST_ALLOC_(len) \ - INST_FILL_BYTES_(len, __VA_ARGS__) \ - return ED_NONE; \ - } - -/* Convenience macros */ - -#define INST_FORCE_TYPE(n, t) { \ - if (INST_TYPE(n) != t) \ - INST_ERROR(ARG##n##_TYPE) \ - } - -#define INST_CHECK_IMM(n, m) { \ - if (!(INST_IMM(n).mask & (m))) \ - INST_ERROR(ARG##n##_RANGE) \ - } - -#define INST_REG_ONLY(n, reg) { \ - if (INST_TYPE(n) != AT_REGISTER) \ - INST_ERROR(ARG##n##_TYPE) \ - if (INST_REG(n) != reg) \ - INST_ERROR(ARG##n##_BAD_REG) \ - } - -#define INST_INDIRECT_HL_ONLY(n) { \ - if (INST_INDIRECT(n).type != AT_REGISTER) \ - INST_ERROR(ARG##n##_TYPE) \ - if (INST_INDIRECT(n).addr.reg != REG_HL) \ - INST_ERROR(ARG##n##_BAD_REG) \ - } - -#define INST_INDEX_PREFIX(n) INST_PREFIX_(INST_INDEX(n).reg) - -#define INST_INDEX_BYTES(n, b) \ - INST_INDEX_PREFIX(n), b, INST_INDEX(n).offset - -#define INST_INDIRECT_IMM(n) \ - INST_INDIRECT(n).addr.imm.uval >> 8, \ - INST_INDIRECT(n).addr.imm.uval & 0xFF - -#define INST_RETURN_INDIRECT_LABEL(n, len, ...) \ - INST_RETURN_WITH_SYMBOL_(len, INST_INDIRECT(n).addr.label, __VA_ARGS__) - -#define INST_HANDLE_MAIN_8_BIT_REGS(base) \ - case REG_A: INST_RETURN(1, base + 7) \ - case REG_B: INST_RETURN(1, base) \ - case REG_C: INST_RETURN(1, base + 1) \ - case REG_D: INST_RETURN(1, base + 2) \ - case REG_E: INST_RETURN(1, base + 3) \ - case REG_H: INST_RETURN(1, base + 4) \ - case REG_L: INST_RETURN(1, base + 5) \ - case REG_IXH: INST_RETURN(2, INST_IX_PREFIX_, base + 4) \ - case REG_IXL: INST_RETURN(2, INST_IX_PREFIX_, base + 5) \ - case REG_IYH: INST_RETURN(2, INST_IY_PREFIX_, base + 4) \ - case REG_IYL: INST_RETURN(2, INST_IY_PREFIX_, base + 5) \ - -/* Internal functions */ - -uint8_t fill_bytes_variadic(uint8_t*, size_t, ...); -ASMErrorDesc parse_args(ASMInstArg args[3], size_t*, ASMArgParseInfo); diff --git a/src/assembler/instructions.c b/src/assembler/instructions.c index 652e210..4b6740e 100644 --- a/src/assembler/instructions.c +++ b/src/assembler/instructions.c @@ -1,705 +1,198 @@ /* Copyright (C) 2014-2015 Ben Kurtovic Released under the terms of the MIT License. See LICENSE for details. */ -#include "instructions.h" -#include "inst_support.h" - -/* Instruction parser functions */ - -INST_FUNC(adc) -{ - INST_TAKES_ARGS(2, 2) - INST_FORCE_TYPE(0, AT_REGISTER) - switch (INST_REG(0)) { - case REG_A: - switch (INST_TYPE(1)) { - case AT_REGISTER: - switch (INST_REG(1)) { - INST_HANDLE_MAIN_8_BIT_REGS(0x88) - default: INST_ERROR(ARG1_BAD_REG) - } - case AT_IMMEDIATE: - INST_CHECK_IMM(1, IMM_U8) - INST_RETURN(2, 0xCE, INST_IMM(1).uval) - case AT_INDIRECT: - INST_INDIRECT_HL_ONLY(1) - INST_RETURN(1, 0x8E) - case AT_INDEXED: - INST_RETURN(3, INST_INDEX_BYTES(1, 0x8E)) - default: - INST_ERROR(ARG1_TYPE) - } - case REG_HL: - INST_FORCE_TYPE(1, AT_REGISTER) - switch (INST_REG(1)) { - case REG_BC: INST_RETURN(2, 0xED, 0x4A) - case REG_DE: INST_RETURN(2, 0xED, 0x5A) - case REG_HL: INST_RETURN(2, 0xED, 0x6A) - case REG_SP: INST_RETURN(2, 0xED, 0x7A) - default: INST_ERROR(ARG1_BAD_REG) - } - default: - INST_ERROR(ARG0_TYPE) - } -} - -INST_FUNC(add) -{ - INST_TAKES_ARGS(2, 2) - INST_FORCE_TYPE(0, AT_REGISTER) - switch (INST_REG(0)) { - case REG_A: - switch (INST_TYPE(1)) { - case AT_REGISTER: - switch (INST_REG(1)) { - INST_HANDLE_MAIN_8_BIT_REGS(0x80) - default: INST_ERROR(ARG1_BAD_REG) - } - case AT_IMMEDIATE: - INST_CHECK_IMM(1, IMM_U8) - INST_RETURN(2, 0xC6, INST_IMM(1).uval) - case AT_INDIRECT: - INST_INDIRECT_HL_ONLY(1) - INST_RETURN(1, 0x86) - case AT_INDEXED: - INST_RETURN(3, INST_INDEX_BYTES(1, 0x86)) - default: - INST_ERROR(ARG1_TYPE) - } - case REG_HL: - INST_FORCE_TYPE(1, AT_REGISTER) - switch (INST_REG(1)) { - case REG_BC: INST_RETURN(1, 0x09) - case REG_DE: INST_RETURN(1, 0x19) - case REG_HL: INST_RETURN(1, 0x29) - case REG_SP: INST_RETURN(1, 0x39) - default: INST_ERROR(ARG1_BAD_REG) - } - case REG_IX: - case REG_IY: - INST_FORCE_TYPE(1, AT_REGISTER) - switch (INST_REG(1)) { - case REG_BC: INST_RETURN(2, INST_INDEX_PREFIX(1), 0x09) - case REG_DE: INST_RETURN(2, INST_INDEX_PREFIX(1), 0x19) - case REG_IX: - case REG_IY: - if (INST_REG(0) != INST_REG(1)) - INST_ERROR(ARG1_BAD_REG) - INST_RETURN(2, INST_INDEX_PREFIX(1), 0x29) - case REG_SP: INST_RETURN(2, INST_INDEX_PREFIX(1), 0x39) - default: INST_ERROR(ARG1_BAD_REG) - } - default: - INST_ERROR(ARG0_TYPE) - } -} - -INST_FUNC(and) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(bit) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +#include +#include -INST_FUNC(call) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +#include "instructions.h" +#include "inst_args.h" +#include "../util.h" -INST_FUNC(ccf) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0x3F) -} +/* Helper macros for get_inst_parser() and lookup_parser() */ -INST_FUNC(cp) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +#define JOIN(a, b, c, d) ((uint32_t) ((a << 24) + (b << 16) + (c << 8) + d)) -INST_FUNC(cpd) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xA9) -} +#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])) \ -INST_FUNC(cpdr) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xB9) -} +#define MAKE_CMP_(s) DISPATCH_(s, sizeof(s) / sizeof(char) - 1) -INST_FUNC(cpi) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xA1) -} +#define HANDLE(m) if (key == MAKE_CMP_(#m)) return parse_inst_##m; -INST_FUNC(cpir) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xB1) -} +/* Helper macro for parse_arg() */ -INST_FUNC(cpl) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0x2F) -} +#define TRY_PARSER(func, argtype, field) \ + if (argparse_##func(&arg->data.field, info)) { \ + arg->type = argtype; \ + return ED_NONE; \ + } -INST_FUNC(daa) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0x27) -} +/* Internal helper macros */ -INST_FUNC(dec) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +#define INST_ALLOC_(len) \ + *length = len; \ + *bytes = cr_malloc(sizeof(uint8_t) * (len)); -INST_FUNC(di) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0xF3) -} +#define INST_SET_(b, val) ((*bytes)[b] = val) +#define INST_SET1_(b1) INST_SET_(0, b1) +#define INST_SET2_(b1, b2) INST_SET1_(b1), INST_SET_(1, b2) +#define INST_SET3_(b1, b2, b3) INST_SET2_(b1, b2), INST_SET_(2, b3) +#define INST_SET4_(b1, b2, b3, b4) INST_SET3_(b1, b2, b3), INST_SET_(3, b4) -INST_FUNC(djnz) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +#define INST_DISPATCH_(a, b, c, d, target, ...) target -INST_FUNC(ei) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0xFB) -} +#define INST_FILL_BYTES_(len, ...) \ + ((len > 4) ? fill_bytes_variadic(*bytes, len, __VA_ARGS__) : \ + INST_DISPATCH_(__VA_ARGS__, INST_SET4_, INST_SET3_, INST_SET2_, \ + INST_SET1_, __VA_ARGS__)(__VA_ARGS__)); -INST_FUNC(ex) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +#define INST_IX_PREFIX 0xDD +#define INST_IY_PREFIX 0xFD -INST_FUNC(exx) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0xD9) -} +#define INST_PREFIX_(reg) \ + (((reg) == REG_IX || (reg) == REG_IXH || (reg) == REG_IXL) ? \ + INST_IX_PREFIX : INST_IY_PREFIX) -INST_FUNC(halt) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0x76) -} - -INST_FUNC(im) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(in) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(inc) -{ - INST_TAKES_ARGS(1, 1) - switch (INST_TYPE(0)) { - case AT_REGISTER: - switch (INST_REG(0)) { - case REG_A: INST_RETURN(1, 0x3C) - case REG_B: INST_RETURN(1, 0x04) - case REG_C: INST_RETURN(1, 0x0C) - case REG_D: INST_RETURN(1, 0x14) - case REG_E: INST_RETURN(1, 0x1C) - case REG_H: INST_RETURN(1, 0x24) - case REG_L: INST_RETURN(1, 0x2C) - case REG_BC: INST_RETURN(1, 0x03) - case REG_DE: INST_RETURN(1, 0x13) - case REG_HL: INST_RETURN(1, 0x23) - case REG_SP: INST_RETURN(1, 0x33) - case REG_IX: INST_RETURN(2, 0xDD, 0x23) - case REG_IY: INST_RETURN(2, 0xFD, 0x23) - case REG_IXH: INST_RETURN(2, 0xDD, 0x2C) - case REG_IXL: INST_RETURN(2, 0xFD, 0x2C) - case REG_IYH: INST_RETURN(2, 0xDD, 0x2C) - case REG_IYL: INST_RETURN(2, 0xFD, 0x2C) - default: INST_ERROR(ARG0_BAD_REG) - } - case AT_INDIRECT: - INST_INDIRECT_HL_ONLY(0) - INST_RETURN(1, 0x34) - case AT_INDEXED: - INST_RETURN(3, INST_INDEX_BYTES(0, 0x34)) - default: - INST_ERROR(ARG0_TYPE) +#define INST_RETURN_WITH_SYMBOL_(len, label, ...) { \ // TODO + *symbol = cr_strdup(label.text); \ + INST_ALLOC_(len) \ + INST_FILL_BYTES_(len - 2, __VA_ARGS__) \ + return ED_NONE; \ } -} - -INST_FUNC(ind) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xAA) -} - -INST_FUNC(indr) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xBA) -} - -INST_FUNC(ini) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xA2) -} - -INST_FUNC(inir) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xB2) -} -INST_FUNC(jp) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(jr) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(ld) -{ - uint8_t base; - INST_TAKES_ARGS(2, 2) - switch (INST_TYPE(0)) { - case AT_REGISTER: - switch (INST_REG(0)) { - case REG_A: - switch (INST_TYPE(1)) { - case AT_REGISTER: - switch (INST_REG(1)) { - INST_HANDLE_MAIN_8_BIT_REGS(0x78) - case REG_I: INST_RETURN(2, 0xED, 0x57) - case REG_R: INST_RETURN(2, 0xED, 0x5F) - default: INST_ERROR(ARG1_BAD_REG) - } - case AT_IMMEDIATE: - INST_CHECK_IMM(1, IMM_U8) - INST_RETURN(2, 0x3E, INST_IMM(1).uval) - case AT_INDIRECT: - switch (INST_INDIRECT(1).type) { - case AT_REGISTER: - switch (INST_INDIRECT(1).addr.reg) { - case REG_BC: INST_RETURN(1, 0x0A) - case REG_DE: INST_RETURN(1, 0x1A) - case REG_HL: INST_RETURN(1, 0x7E) - default: INST_ERROR(ARG0_BAD_REG) - } - case AT_IMMEDIATE: - INST_RETURN(3, 0x3A, INST_INDIRECT_IMM(1)) - case AT_LABEL: - INST_RETURN_INDIRECT_LABEL(1, 3, 0x3A) - default: - INST_ERROR(ARG1_TYPE) - } - case AT_INDEXED: - INST_RETURN(3, INST_INDEX_BYTES(1, 0x7E)) - default: - INST_ERROR(ARG1_TYPE) - } - case REG_B: - base = 0x00; - case REG_C: - base = 0x08; - case REG_D: - base = 0x10; - case REG_E: - base = 0x18; - switch (INST_TYPE(1)) { - case AT_REGISTER: - switch (INST_REG(1)) { - INST_HANDLE_MAIN_8_BIT_REGS(base + 0x40) - default: INST_ERROR(ARG1_BAD_REG) - } - case AT_IMMEDIATE: - INST_CHECK_IMM(1, IMM_U8) - INST_RETURN(2, base + 0x06, INST_IMM(1).uval) - case AT_INDIRECT: - INST_INDIRECT_HL_ONLY(1) - INST_RETURN(1, base + 0x46) - case AT_INDEXED: - INST_RETURN(3, INST_INDEX_BYTES(1, base + 0x46)) - default: - INST_ERROR(ARG1_TYPE) - } - case REG_H: // TODO (11 cases) - case REG_L: // TODO (11 cases) - case REG_I: - INST_REG_ONLY(1, REG_A) - INST_RETURN(2, 0xED, 0x47) - case REG_R: - INST_REG_ONLY(1, REG_A) - INST_RETURN(2, 0xED, 0x4F) - case REG_BC: // TODO ( 2 cases) - case REG_DE: // TODO ( 2 cases) - case REG_HL: // TODO ( 3 cases) - case REG_IX: // TODO ( 2 cases) - case REG_IY: // TODO ( 2 cases) - case REG_SP: // TODO ( 5 cases) - case REG_IXH: // TODO ( 8 cases) - case REG_IXL: // TODO ( 8 cases) - case REG_IYH: // TODO ( 8 cases) - case REG_IYL: // TODO ( 8 cases) - default: INST_ERROR(ARG0_BAD_REG) - } - case AT_INDIRECT: - switch (INST_INDIRECT(0).type) { - case AT_REGISTER: - switch (INST_INDIRECT(0).addr.reg) { - case REG_BC: // TODO (1 case ) - case REG_DE: // TODO (1 case ) - case REG_HL: // TODO (8 cases) - default: INST_ERROR(ARG0_BAD_REG) - } - case AT_IMMEDIATE: - // TODO (8 cases) - case AT_LABEL: - // TODO (same 8 cases) - default: - INST_ERROR(ARG0_TYPE) - } - case AT_INDEXED: - // TODO (16 cases) - default: - INST_ERROR(ARG0_TYPE) +/* Helper macros for instruction parsers */ + +#define INST_FUNC(mnemonic) \ +static ASMErrorDesc parse_inst_##mnemonic( \ + uint8_t **bytes, size_t *length, char **symbol, ASMArgParseInfo ap_info) \ + +#define INST_ERROR(desc) return ED_PS_##desc; + +#define INST_TAKES_NO_ARGS \ + if (ap_info.arg) \ + INST_ERROR(TOO_MANY_ARGS) + +#define INST_TAKES_ARGS(a0, a1, a2) \ + if (!ap_info.arg) \ + INST_ERROR(TOO_FEW_ARGS) \ + ASMInstArg args[3]; \ + size_t nargs = 0; \ + ASMArgType masks = {a0, a1, a2}; \ + ASMErrorDesc err = parse_args(args, &nargs, ap_info, masks); \ + if (err) \ + return err; \ + +#define INST_TYPE(n) args[n].type +#define INST_REG(n) args[n].data.reg +#define INST_IMM(n) args[n].data.imm +#define INST_INDIRECT(n) args[n].data.indirect +#define INST_INDEX(n) args[n].data.index +#define INST_COND(n) args[n].data.cond +#define INST_PORT(n) args[n].data.port + +#define INST_RETURN(len, ...) { \ + (void) symbol; \ + INST_ALLOC_(len) \ + INST_FILL_BYTES_(len, __VA_ARGS__) \ + return ED_NONE; \ } -} - -INST_FUNC(ldd) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xA8) -} - -INST_FUNC(lddr) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xB8) -} - -INST_FUNC(ldi) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xA0) -} - -INST_FUNC(ldir) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xB0) -} - -INST_FUNC(neg) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(nop) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0x00) -} - -INST_FUNC(or) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(otdr) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xBB) -} - -INST_FUNC(otir) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xB3) -} - -INST_FUNC(out) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(outd) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xAB) -} - -INST_FUNC(outi) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0xA3) -} - -INST_FUNC(pop) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(push) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(res) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(ret) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(reti) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0x4D) -} - -INST_FUNC(retn) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0x45) -} - -INST_FUNC(rl) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} -INST_FUNC(rla) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0x17) -} +#define INST_INDEX_PREFIX(n) INST_PREFIX_(INST_INDEX(n).reg) -INST_FUNC(rlc) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +#define INST_INDIRECT_IMM(n) \ // TODO + INST_INDIRECT(n).addr.imm.uval >> 8, \ + INST_INDIRECT(n).addr.imm.uval & 0xFF -INST_FUNC(rlca) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0x07) -} +/* ----------------------------- END WORK BLOCK ---------------------------- */ -INST_FUNC(rld) -{ - INST_TAKES_NO_ARGS - INST_RETURN(2, 0xED, 0x6F) -} - -INST_FUNC(rr) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(rra) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0x1F) -} - -INST_FUNC(rrc) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(rrca) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0x0F) -} - -INST_FUNC(rrd) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(rst) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(sbc) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} - -INST_FUNC(scf) -{ - INST_TAKES_NO_ARGS - INST_RETURN(1, 0x37) -} - -INST_FUNC(set) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +/* + Fill an instruction's byte array with the given data. -INST_FUNC(sl1) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} + This internal function is only called for instructions longer than four + bytes (of which there is only one: the fake emulator debugging/testing + opcode with mnemonic "emu"), so it does not get used in normal situations. -INST_FUNC(sla) + Return the value of the last byte inserted, for compatibility with the + INST_SETn_ family of macros. +*/ +static uint8_t fill_bytes_variadic(uint8_t *bytes, size_t len, ...) { - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) + va_list vargs; + va_start(vargs, len); + for (size_t i = 0; i < len; i++) + bytes[i] = va_arg(vargs, unsigned); + va_end(vargs); + return bytes[len - 1]; } -INST_FUNC(sll) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +/* + Parse a single instruction argument into an ASMInstArg object. -INST_FUNC(sls) + Return ED_NONE (0) on success or an error code on failure. +*/ +static ASMErrorDesc parse_arg( + ASMInstArg *arg, const char *str, size_t size, ASMDefineTable *deftable) { - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) + ASMArgParseInfo info = {.arg = str, .size = size, .deftable = deftable}; + TRY_PARSER(register, AT_REGISTER, reg) + TRY_PARSER(condition, AT_CONDITION, cond) + TRY_PARSER(indirect, AT_INDIRECT, indirect) + TRY_PARSER(indexed, AT_INDEXED, index) + TRY_PARSER(immediate, AT_IMMEDIATE, imm) + return ED_PS_ARG_SYNTAX; } -INST_FUNC(sra) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +/* + Parse an argument string into ASMInstArg objects. -INST_FUNC(srl) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} + Return ED_NONE (0) on success or an error code on failure. +*/ +static ASMErrorDesc parse_args( + ASMInstArg args[3], size_t *nargs, ASMArgParseInfo ap_info, ASMArgType masks[3]) // TODO +{ + ASMErrorDesc err; + ASMDefineTable *dt = ap_info.deftable; + const char *str = ap_info.arg; + size_t size = ap_info.size, start = 0, i = 0; + + while (i < size) { + char c = str[i]; + if (c == ',') { + if (i == start) + return ED_PS_ARG_SYNTAX; + if ((err = parse_arg(&args[*nargs], str + start, i - start, dt))) + return err; + (*nargs)++; + + i++; + if (i < size && str[i] == ' ') + i++; + start = i; + if (i == size) + return ED_PS_ARG_SYNTAX; + if (*nargs >= 3) + return ED_PS_TOO_MANY_ARGS; + } else { + if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || + c == ' ' || c == '+' || c == '-' || c == '(' || c == ')' || + c == '$' || c == '_' || c == '.') + i++; + else + return ED_PS_ARG_SYNTAX; + } + } -INST_FUNC(sub) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) + if (i > start) { + if ((err = parse_arg(&args[*nargs], str + start, i - start, dt))) + return err; + (*nargs)++; + } + return ED_NONE; } -INST_FUNC(xor) -{ - // TODO - INST_TAKES_NO_ARGS - INST_ERROR(ARG_SYNTAX) - INST_RETURN(1, 0xFF) -} +#include "instructions.inc.c" /* Return the relevant ASMInstParser function for a given mnemonic. @@ -710,78 +203,6 @@ ASMInstParser get_inst_parser(char mstr[MAX_MNEMONIC_SIZE]) { // Exploit the fact that we can store the entire mnemonic string as a // single 32-bit value to do fast lookups: - uint32_t key = (mstr[0] << 24) + (mstr[1] << 16) + (mstr[2] << 8) + mstr[3]; - - HANDLE(adc) - HANDLE(add) - HANDLE(and) - HANDLE(bit) - HANDLE(call) - HANDLE(ccf) - HANDLE(cp) - HANDLE(cpd) - HANDLE(cpdr) - HANDLE(cpi) - HANDLE(cpir) - HANDLE(cpl) - HANDLE(daa) - HANDLE(dec) - HANDLE(di) - HANDLE(djnz) - HANDLE(ei) - HANDLE(ex) - HANDLE(exx) - HANDLE(halt) - HANDLE(im) - HANDLE(in) - HANDLE(inc) - HANDLE(ind) - HANDLE(indr) - HANDLE(ini) - HANDLE(inir) - HANDLE(jp) - HANDLE(jr) - HANDLE(ld) - HANDLE(ldd) - HANDLE(lddr) - HANDLE(ldi) - HANDLE(ldir) - HANDLE(neg) - HANDLE(nop) - HANDLE(or) - HANDLE(otdr) - HANDLE(otir) - HANDLE(out) - HANDLE(outd) - HANDLE(outi) - HANDLE(pop) - HANDLE(push) - HANDLE(res) - HANDLE(ret) - HANDLE(reti) - HANDLE(retn) - HANDLE(rl) - HANDLE(rla) - HANDLE(rlc) - HANDLE(rlca) - HANDLE(rld) - HANDLE(rr) - HANDLE(rra) - HANDLE(rrc) - HANDLE(rrca) - HANDLE(rrd) - HANDLE(rst) - HANDLE(sbc) - HANDLE(scf) - HANDLE(set) - HANDLE(sl1) - HANDLE(sla) - HANDLE(sll) - HANDLE(sls) - HANDLE(sra) - HANDLE(srl) - HANDLE(sub) - HANDLE(xor) - - return NULL; + uint32_t key = JOIN(mstr[0], mstr[1], mstr[2], mstr[3]); + return lookup_parser(key); } diff --git a/src/assembler/instructions.inc.c b/src/assembler/instructions.inc.c new file mode 100644 index 0000000..34d8b69 --- /dev/null +++ b/src/assembler/instructions.inc.c @@ -0,0 +1,733 @@ +/* Copyright (C) 2014-2015 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +/* + This file is AUTO-GENERATED from 'instructions.yml'. + + `make` should trigger a rebuild when it is modified; if not, use: + `python scripts/update_asm_instructions.py`. + + @AUTOGEN_DATE Sun May 10 21:02:28 2015 +*/ + +/* @AUTOGEN_INST_BLOCK_START */ + +INST_FUNC(adc) +{ + INST_TAKES_ARGS( + AT_REGISTER, + AT_REGISTER|AT_IMMEDIATE|AT_INDEXED|AT_INDIRECT, + AT_NONE + ) + if (INST_TYPE(0) == AT_REGISTER && INST_TYPE(1) == AT_REGISTER) { + if (INST_REG(0) == REG_A && INST_REG(1) == REG_A) + INST_RETURN(1, 0x8F) + if (INST_REG(0) == REG_A && INST_REG(1) == REG_B) + INST_RETURN(1, 0x88) + // + if (INST_REG(0) == REG_A && INST_REG(1) == REG_H) + INST_RETURN(1, 0x8C) + if (INST_REG(0) == REG_A && INST_REG(1) == REG_IXH) + INST_RETURN(2, INST_IX_PREFIX, 0x8C) + // + INST_ERROR(ARG_VALUE) + } + if (INST_TYPE(0) == AT_REGISTER && INST_TYPE(1) == AT_IMMEDIATE) { + if (INST_REG(0) == REG_A && INST_IMM(1).mask & IMM_U8) + INST_RETURN(2, 0xCE, INST_IMM(1).uval) + INST_ERROR(ARG_VALUE) + } + if (INST_TYPE(0) == AT_REGISTER && INST_TYPE(1) == AT_INDEXED) { + if (INST_REG(0) == REG_A) + INST_RETURN(3, INST_INDEX_PREFIX(1), 0x8E, INST_INDEX(1).offset) + INST_ERROR(ARG_VALUE) + } + if (INST_TYPE(0) == AT_REGISTER && INST_TYPE(1) == AT_INDIRECT && + INST_INDIRECT(1).type == AT_REGISTER && + INST_INDIRECT(1).addr.reg == REG_HL) { + if (INST_REG(0) == REG_A) + INST_RETURN(1, 0x8E) + INST_ERROR(ARG_VALUE) + } + INST_ERROR(ARG_TYPE) +} + +INST_FUNC(add) +{ + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) + + // INST_TAKES_ARGS(2, 2) + // INST_FORCE_TYPE(0, AT_REGISTER) + // switch (INST_REG(0)) { + // case REG_A: + // switch (INST_TYPE(1)) { + // INST_CASE_REGS(1, INST_CASE_ALL_8_BIT_REGS(0x80)) + // INST_CASE_IMM_U8(1, 2, 0xC6) + // INST_CASE_INDIRECT_HL_IX_IY(1, 1, 0x86) + // default: INST_ERROR(ARG1_TYPE) + // } + // case REG_HL: + // INST_FORCE_TYPE(1, AT_REGISTER) + // switch (INST_REG(1)) { + // case REG_BC: INST_RETURN(1, 0x09) + // case REG_DE: INST_RETURN(1, 0x19) + // case REG_HL: INST_RETURN(1, 0x29) + // case REG_SP: INST_RETURN(1, 0x39) + // default: INST_ERROR(ARG1_BAD_REG) + // } + // case REG_IX: + // case REG_IY: + // INST_FORCE_TYPE(1, AT_REGISTER) + // switch (INST_REG(1)) { + // case REG_BC: INST_RETURN(2, INST_INDEX_PREFIX(1), 0x09) // TODO: wrong prefix + // case REG_DE: INST_RETURN(2, INST_INDEX_PREFIX(1), 0x19) + // case REG_IX: + // case REG_IY: + // if (INST_REG(0) != INST_REG(1)) + // INST_ERROR(ARG1_BAD_REG) + // INST_RETURN(2, INST_INDEX_PREFIX(1), 0x29) + // case REG_SP: INST_RETURN(2, INST_INDEX_PREFIX(1), 0x39) + // default: INST_ERROR(ARG1_BAD_REG) + // } + // default: + // INST_ERROR(ARG0_TYPE) + // } +} + +INST_FUNC(and) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(bit) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(call) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(ccf) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0x3F) +} + +INST_FUNC(cp) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(cpd) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xA9) +} + +INST_FUNC(cpdr) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xB9) +} + +INST_FUNC(cpi) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xA1) +} + +INST_FUNC(cpir) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xB1) +} + +INST_FUNC(cpl) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0x2F) +} + +INST_FUNC(daa) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0x27) +} + +INST_FUNC(dec) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(di) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0xF3) +} + +INST_FUNC(djnz) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(ei) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0xFB) +} + +INST_FUNC(ex) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(exx) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0xD9) +} + +INST_FUNC(halt) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0x76) +} + +INST_FUNC(im) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(in) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(inc) +{ + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) + + // INST_TAKES_ARGS(1, 1) + // switch (INST_TYPE(0)) { + // case AT_REGISTER: + // switch (INST_REG(0)) { + // case REG_A: INST_RETURN(1, 0x3C) + // case REG_B: INST_RETURN(1, 0x04) + // case REG_C: INST_RETURN(1, 0x0C) + // case REG_D: INST_RETURN(1, 0x14) + // case REG_E: INST_RETURN(1, 0x1C) + // case REG_H: INST_RETURN(1, 0x24) + // case REG_L: INST_RETURN(1, 0x2C) + // case REG_BC: INST_RETURN(1, 0x03) + // case REG_DE: INST_RETURN(1, 0x13) + // case REG_HL: INST_RETURN(1, 0x23) + // case REG_SP: INST_RETURN(1, 0x33) + // case REG_IX: INST_RETURN(2, 0xDD, 0x23) + // case REG_IY: INST_RETURN(2, 0xFD, 0x23) + // case REG_IXH: INST_RETURN(2, 0xDD, 0x2C) + // case REG_IXL: INST_RETURN(2, 0xFD, 0x2C) + // case REG_IYH: INST_RETURN(2, 0xDD, 0x2C) + // case REG_IYL: INST_RETURN(2, 0xFD, 0x2C) + // default: INST_ERROR(ARG0_BAD_REG) + // } + // INST_CASE_INDIRECT_HL_IX_IY(0, 1, 0x34) + // default: + // INST_ERROR(ARG0_TYPE) + // } +} + +INST_FUNC(ind) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xAA) +} + +INST_FUNC(indr) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xBA) +} + +INST_FUNC(ini) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xA2) +} + +INST_FUNC(inir) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xB2) +} + +INST_FUNC(jp) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(jr) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(ld) +{ + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) + + // INST_TAKES_ARGS(2, 2) + // switch (INST_TYPE(0)) { + // case AT_REGISTER: + // switch (INST_REG(0)) { + // case REG_A: + // switch (INST_TYPE(1)) { + // case AT_REGISTER: + // switch (INST_REG(1)) { + // INST_CASE_ALL_8_BIT_REGS(0x78) + // case REG_I: INST_RETURN(2, 0xED, 0x57) + // case REG_R: INST_RETURN(2, 0xED, 0x5F) + // default: INST_ERROR(ARG1_BAD_REG) + // } + // INST_CASE_IMM_U8(1, 2, 0x3E) + // case AT_INDIRECT: + // switch (INST_INDIRECT(1).type) { + // case AT_REGISTER: + // switch (INST_INDIRECT(1).addr.reg) { + // case REG_BC: INST_RETURN(1, 0x0A) + // case REG_DE: INST_RETURN(1, 0x1A) + // case REG_HL: INST_RETURN(1, 0x7E) + // default: INST_ERROR(ARG0_BAD_REG) + // } + // case AT_IMMEDIATE: + // INST_RETURN(3, 0x3A, INST_INDIRECT_IMM(1)) + // case AT_LABEL: + // INST_RETURN_INDIRECT_LABEL(1, 3, 0x3A) + // default: + // INST_ERROR(ARG1_TYPE) + // } + // case AT_INDEXED: + // INST_RETURN(3, INST_INDEX_BYTES(1, 0x7E)) + // default: + // INST_ERROR(ARG1_TYPE) + // } + // case REG_B: + // switch (INST_TYPE(1)) { + // INST_CASE_REGS(1, INST_CASE_ALL_8_BIT_REGS(0x40)) + // INST_CASE_IMM_U8(1, 2, 0x06) + // INST_CASE_INDIRECT_HL_IX_IY(1, 1, 0x46) + // default: INST_ERROR(ARG1_TYPE) + // } + // case REG_C: + // switch (INST_TYPE(1)) { + // INST_CASE_REGS(1, INST_CASE_ALL_8_BIT_REGS(0x48)) + // INST_CASE_IMM_U8(1, 2, 0x0E) + // INST_CASE_INDIRECT_HL_IX_IY(1, 1, 0x4E) + // default: INST_ERROR(ARG1_TYPE) + // } + // case REG_D: + // switch (INST_TYPE(1)) { + // INST_CASE_REGS(1, INST_CASE_ALL_8_BIT_REGS(0x50)) + // INST_CASE_IMM_U8(1, 2, 0x16) + // INST_CASE_INDIRECT_HL_IX_IY(1, 1, 0x56) + // default: INST_ERROR(ARG1_TYPE) + // } + // case REG_E: + // switch (INST_TYPE(1)) { + // INST_CASE_REGS(1, INST_CASE_ALL_8_BIT_REGS(0x58)) + // INST_CASE_IMM_U8(1, 2, 0x1E) + // INST_CASE_INDIRECT_HL_IX_IY(1, 1, 0x5E) + // default: INST_ERROR(ARG1_TYPE) + // } + // case REG_H: + // switch (INST_TYPE(1)) { + // INST_CASE_REGS(1, INST_CASE_MAIN_8_BIT_REGS(0x60)) + // INST_CASE_IMM_U8(1, 2, 0x26) + // INST_CASE_INDIRECT_HL_IX_IY(1, 1, 0x66) + // default: INST_ERROR(ARG1_TYPE) + // } + // case REG_L: + // switch (INST_TYPE(1)) { + // INST_CASE_REGS(1, INST_CASE_MAIN_8_BIT_REGS(0x68)) + // INST_CASE_IMM_U8(1, 2, 0x2E) + // INST_CASE_INDIRECT_HL_IX_IY(1, 1, 0x6E) + // default: INST_ERROR(ARG1_TYPE) + // } + // case REG_I: + // INST_REG_ONLY(1, REG_A) + // INST_RETURN(2, 0xED, 0x47) + // case REG_R: + // INST_REG_ONLY(1, REG_A) + // INST_RETURN(2, 0xED, 0x4F) + // case REG_BC: // TODO ( 2 cases) + // case REG_DE: // TODO ( 2 cases) + // case REG_HL: // TODO ( 3 cases) + // case REG_IX: // TODO ( 2 cases) + // case REG_IY: // TODO ( 2 cases) + // case REG_SP: // TODO ( 5 cases) + // case REG_IXH: // TODO ( 8 cases) + // case REG_IXL: // TODO ( 8 cases) + // case REG_IYH: // TODO ( 8 cases) + // case REG_IYL: // TODO ( 8 cases) + // default: INST_ERROR(ARG0_BAD_REG) + // } + // case AT_INDIRECT: + // switch (INST_INDIRECT(0).type) { + // case AT_REGISTER: + // switch (INST_INDIRECT(0).addr.reg) { + // case REG_BC: // TODO (1 case ) + // case REG_DE: // TODO (1 case ) + // case REG_HL: // TODO (8 cases) + // default: INST_ERROR(ARG0_BAD_REG) + // } + // case AT_IMMEDIATE: + // // TODO (8 cases) + // case AT_LABEL: + // // TODO (same 8 cases) + // default: + // INST_ERROR(ARG0_TYPE) + // } + // case AT_INDEXED: + // // TODO (16 cases) + // default: + // INST_ERROR(ARG0_TYPE) + // } +} + +INST_FUNC(ldd) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xA8) +} + +INST_FUNC(lddr) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xB8) +} + +INST_FUNC(ldi) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xA0) +} + +INST_FUNC(ldir) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xB0) +} + +INST_FUNC(neg) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0x44) +} + +INST_FUNC(nop) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0x00) +} + +INST_FUNC(or) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(otdr) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xBB) +} + +INST_FUNC(otir) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xB3) +} + +INST_FUNC(out) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(outd) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xAB) +} + +INST_FUNC(outi) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0xA3) +} + +INST_FUNC(pop) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(push) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(res) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(ret) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(reti) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0x4D) +} + +INST_FUNC(retn) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0x45) +} + +INST_FUNC(rl) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(rla) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0x17) +} + +INST_FUNC(rlc) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(rlca) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0x07) +} + +INST_FUNC(rld) +{ + INST_TAKES_NO_ARGS + INST_RETURN(2, 0xED, 0x6F) +} + +INST_FUNC(rr) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(rra) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0x1F) +} + +INST_FUNC(rrc) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(rrca) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0x0F) +} + +INST_FUNC(rrd) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(rst) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(sbc) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(scf) +{ + INST_TAKES_NO_ARGS + INST_RETURN(1, 0x37) +} + +INST_FUNC(set) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(sl1) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(sla) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(sll) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(sls) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(sra) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(srl) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(sub) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +INST_FUNC(xor) +{ + // TODO + INST_TAKES_NO_ARGS + INST_ERROR(ARG_SYNTAX) + INST_RETURN(1, 0xFF) +} + +/* @AUTOGEN_INST_BLOCK_END */ + +/* + Return the relevant ASMInstParser function for the given encoded mnemonic. +*/ +static ASMInstParser lookup_parser(uint32_t key) +{ +/* @AUTOGEN_LOOKUP_BLOCK_START */ + HANDLE(adc) + HANDLE(add) + HANDLE(and) +/* @AUTOGEN_LOOKUP_BLOCK_END */ + return NULL; +} diff --git a/src/assembler/instructions.yml b/src/assembler/instructions.yml new file mode 100644 index 0000000..0dd31d0 --- /dev/null +++ b/src/assembler/instructions.yml @@ -0,0 +1,323 @@ +# Copyright (C) 2014-2015 Ben Kurtovic +# Released under the terms of the MIT License. See LICENSE for details. + +# *** ASM Instruction Description File *** + +# This file is used to generate 'instructions.inc.c'. + +# `make` should trigger a rebuild when this file is modified; if not, use: +# `python scripts/update_asm_instructions.py`. + +--- + +adc: + args: yes + cases: + - type: [register, register] + cases: + - cond: [a, a] + return: [0x8F] + - cond: [a, b] + return: [0x88] + - cond: [a, c] + return: [0x89] + - cond: [a, d] + return: [0x8A] + - cond: [a, e] + return: [0x8B] + - cond: [a, h|ih] + return: [0x8C] + - cond: [a, l|il] + return: [0x8D] + - cond: [hl, bc] + return: [0xED, 0x4A] + - cond: [hl, de] + return: [0xED, 0x5A] + - cond: [hl, hl] + return: [0xED, 0x6A] + - cond: [hl, sp] + return: [0xED, 0x7A] + - type: [register, immediate] + cases: + - cond: [a, u8] + return: [0xCE, u8] + - type: [register, indexed] + cases: + - cond: [a, _] + return: [0x8E] + +add: + args: no + return: error + +and: + args: no + return: error + +bit: + args: no + return: error + +call: + args: no + return: error + +ccf: + args: no + return: error + +cp: + args: no + return: error + +cpd: + args: no + return: error + +cpdr: + args: no + return: error + +cpi: + args: no + return: error + +cpir: + args: no + return: error + +cpl: + args: no + return: error + +daa: + args: no + return: error + +dec: + args: no + return: error + +di: + args: no + return: error + +djnz: + args: no + return: error + +ei: + args: no + return: error + +ex: + args: no + return: error + +exx: + args: no + return: error + +halt: + args: no + return: error + +im: + args: no + return: error + +in: + args: no + return: error + +inc: + args: no + return: error + +ind: + args: no + return: error + +indr: + args: no + return: error + +ini: + args: no + return: [0xED, 0xA2] + +inir: + args: no + return: error + +jp: + args: no + return: error + +jr: + args: no + return: error + +ld: + args: no + return: error + +ldd: + args: no + return: error + +lddr: + args: no + return: error + +ldi: + args: no + return: error + +ldir: + args: no + return: error + +neg: + args: no + return: error + +nop: + args: no + return: error + +or: + args: no + return: error + +otdr: + args: no + return: error + +otir: + args: no + return: error + +out: + args: no + return: error + +outd: + args: no + return: error + +outi: + args: no + return: error + +pop: + args: no + return: error + +push: + args: no + return: error + +res: + args: no + return: error + +ret: + args: no + return: error + +reti: + args: no + return: error + +retn: + args: no + return: error + +rl: + args: no + return: error + +rla: + args: no + return: error + +rlc: + args: no + return: error + +rlca: + args: no + return: error + +rld: + args: no + return: error + +rr: + args: no + return: error + +rra: + args: no + return: error + +rrc: + args: no + return: error + +rrca: + args: no + return: error + +rrd: + args: no + return: error + +rst: + args: no + return: error + +sbc: + args: no + return: error + +scf: + args: no + return: error + +set: + args: no + return: error + +sl1: + args: no + return: error + +sla: + args: no + return: error + +sll: + args: no + return: error + +sls: + args: no + return: error + +sra: + args: no + return: error + +srl: + args: no + return: error + +sub: + args: no + return: error + +xor: + args: no + return: error diff --git a/src/assembler/parse_util.c b/src/assembler/parse_util.c index 2aa448a..9d5ff5a 100644 --- a/src/assembler/parse_util.c +++ b/src/assembler/parse_util.c @@ -319,6 +319,26 @@ bool argparse_condition(ASMArgCondition *result, ASMArgParseInfo ai) } /* + Read in a label immediate argument and store it in *result. +*/ +static bool argparse_imm_label(ASMArgImmediate *result, ASMArgParseInfo ai) +{ + if (ai.size >= MAX_SYMBOL_SIZE) + return false; + + for (const char *i = ai.arg; i < ai.arg + ai.size; i++) { + if (!is_valid_symbol_char(*i, i == ai.arg)) + return false; + } + + result->mask = IMM_U16; + result->is_label = true; + strncpy(result->label, ai.arg, ai.size); + result->label[ai.size] = '\0'; + return true; +} + +/* Read in an immediate argument and store it in *result. */ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) @@ -326,10 +346,11 @@ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) if (ai.size <= 0) return false; - bool negative = false; + bool negative = false, modifiers = false; ssize_t i = 0; while (ai.arg[i] == '-' || ai.arg[i] == '+' || ai.arg[i] == ' ') { + modifiers = true; if (ai.arg[i] == '-') negative = !negative; if (++i >= ai.size) @@ -341,9 +362,10 @@ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) const ASMDefine *define = asm_deftable_find(ai.deftable, ai.arg, ai.size); if (define) { if (negative) { + calculate_immediate_mask(result); + result->is_label = false; result->uval = define->value.uval; result->sval = -define->value.sval; - calculate_immediate_mask(result); } else { *result = define->value; } @@ -351,16 +373,23 @@ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) } uint32_t uval; - if (!parse_uint32_t(&uval, ai.arg, ai.size) || uval > UINT16_MAX) + if (!parse_uint32_t(&uval, ai.arg, ai.size)) { + if (!modifiers && argparse_imm_label(result, ai)) + return true; + return false; + } + + if (uval > UINT16_MAX) return false; int32_t sval = negative ? -uval : uval; if (sval < INT16_MIN) return false; + calculate_immediate_mask(result); + result->is_label = false; result->uval = uval; result->sval = sval; - calculate_immediate_mask(result); return true; } @@ -374,9 +403,9 @@ bool argparse_indirect(ASMArgIndirect *result, ASMArgParseInfo ai) ASMArgRegister reg; ASMArgImmediate imm; - ASMArgLabel label; if (argparse_register(®, ai)) { - if (reg == REG_BC || reg == REG_DE || reg == REG_HL) { + if (reg == REG_BC || reg == REG_DE || reg == REG_HL || + reg == REG_IX || reg == REG_IY) { result->type = AT_REGISTER; result->addr.reg = reg; return true; @@ -387,10 +416,6 @@ bool argparse_indirect(ASMArgIndirect *result, ASMArgParseInfo ai) result->addr.imm = imm; return true; } - } else if (argparse_label(&label, ai)) { - result->type = AT_LABEL; - result->addr.label = label; - return true; } return false; } @@ -433,24 +458,29 @@ bool argparse_indexed(ASMArgIndexed *result, ASMArgParseInfo ai) } /* - Read in a label argument and store it in *result. + Read in a port argument and store it in *result. */ -bool argparse_label(ASMArgLabel *result, ASMArgParseInfo ai) +bool argparse_port(ASMArgPort*, ASMArgParseInfo) { - if (ai.size <= 0 || ai.size >= MAX_SYMBOL_SIZE) + if (ai.size < 3 || !adjust_for_indirection(&ai)) return false; - for (const char *i = ai.arg; i < ai.arg + ai.size; i++) { - if (!is_valid_symbol_char(*i, i == ai.arg)) - return false; + ASMArgRegister reg; + ASMArgImmediate imm; + if (argparse_register(®, ai)) { + if (reg == REG_C) { + result->type = AT_REGISTER; + result->addr.reg = reg; + return true; + } + } else if (argparse_immediate(&imm, ai)) { + if (imm.mask & IMM_U8) { + result->type = AT_IMMEDIATE; + result->addr.imm = imm; + return true; + } } - - if (asm_deftable_find(ai.deftable, ai.arg, ai.size)) - return false; - - strncpy(result->text, ai.arg, ai.size); - result->text[ai.size] = '\0'; - return true; + return false; } /* diff --git a/src/assembler/parse_util.h b/src/assembler/parse_util.h index ab2003c..8d5c199 100644 --- a/src/assembler/parse_util.h +++ b/src/assembler/parse_util.h @@ -33,7 +33,7 @@ bool argparse_condition(ASMArgCondition*, ASMArgParseInfo); bool argparse_immediate(ASMArgImmediate*, ASMArgParseInfo); bool argparse_indirect(ASMArgIndirect*, ASMArgParseInfo); bool argparse_indexed(ASMArgIndexed*, ASMArgParseInfo); -bool argparse_label(ASMArgLabel*, ASMArgParseInfo); +bool argparse_port(ASMArgPort*, ASMArgParseInfo); /* Preprocessor directive parsers */ bool dparse_bool(bool*, const ASMLine*, const char*); diff --git a/src/assembler/tokenizer.c b/src/assembler/tokenizer.c index beb9808..896f822 100644 --- a/src/assembler/tokenizer.c +++ b/src/assembler/tokenizer.c @@ -155,7 +155,7 @@ static ErrorInfo* handle_define_directive( ASMArgImmediate imm; ASMArgParseInfo info = { .arg = line->data + i, .size = line->length - i, .deftable = deftab}; - if (!argparse_immediate(&imm, info)) + if (!argparse_immediate(&imm, info) || imm->is_label) return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG); ASMDefine *define = cr_malloc(sizeof(ASMDefine));