| @@ -47,6 +47,9 @@ $(BUILD)/$(MODE)/%.o: %.c | |||||
| -include $(DEPS) | -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: test-all test-z80 test-asm test-dasm | ||||
| test-all: | test-all: | ||||
| @@ -0,0 +1,89 @@ | |||||
| #!/usr/bin/env python | |||||
| # -*- coding: utf-8 -*- | |||||
| # Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| # 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() | |||||
| @@ -53,11 +53,8 @@ static const char *error_descs[] = { | |||||
| [ED_PS_TOO_FEW_ARGS] = "too few arguments for opcode", | [ED_PS_TOO_FEW_ARGS] = "too few arguments for opcode", | ||||
| [ED_PS_TOO_MANY_ARGS] = "too many arguments for opcode", | [ED_PS_TOO_MANY_ARGS] = "too many arguments for opcode", | ||||
| [ED_PS_ARG_SYNTAX] = "invalid syntax in argument(s)", | [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 */ | /* Internal structs */ | ||||
| @@ -52,11 +52,8 @@ typedef enum { | |||||
| ED_PS_TOO_FEW_ARGS, | ED_PS_TOO_FEW_ARGS, | ||||
| ED_PS_TOO_MANY_ARGS, | ED_PS_TOO_MANY_ARGS, | ||||
| ED_PS_ARG_SYNTAX, | 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; | } ASMErrorDesc; | ||||
| /* Structs */ | /* Structs */ | ||||
| @@ -8,12 +8,14 @@ | |||||
| #define MAX_SYMBOL_SIZE 256 | #define MAX_SYMBOL_SIZE 256 | ||||
| typedef enum { | 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; | } ASMArgType; | ||||
| typedef enum { | typedef enum { | ||||
| @@ -35,20 +37,17 @@ typedef enum { | |||||
| typedef struct { | typedef struct { | ||||
| ASMArgImmType mask; | ASMArgImmType mask; | ||||
| bool is_label; | |||||
| uint16_t uval; | uint16_t uval; | ||||
| int16_t sval; | int16_t sval; | ||||
| char label[MAX_SYMBOL_SIZE]; | |||||
| } ASMArgImmediate; | } ASMArgImmediate; | ||||
| typedef struct { | typedef struct { | ||||
| char text[MAX_SYMBOL_SIZE]; | |||||
| } ASMArgLabel; | |||||
| typedef struct { | |||||
| ASMArgType type; | ASMArgType type; | ||||
| union { | union { | ||||
| ASMArgRegister reg; | ASMArgRegister reg; | ||||
| ASMArgImmediate imm; | ASMArgImmediate imm; | ||||
| ASMArgLabel label; | |||||
| } addr; | } addr; | ||||
| } ASMArgIndirect; | } ASMArgIndirect; | ||||
| @@ -66,9 +65,17 @@ typedef struct { | |||||
| union { | union { | ||||
| ASMArgRegister reg; | ASMArgRegister reg; | ||||
| ASMArgImmediate imm; | ASMArgImmediate imm; | ||||
| } port; | |||||
| } ASMArgPort; | |||||
| typedef struct { | |||||
| ASMArgType type; | |||||
| union { | |||||
| ASMArgRegister reg; | |||||
| ASMArgImmediate imm; | |||||
| ASMArgIndirect indirect; | ASMArgIndirect indirect; | ||||
| ASMArgIndexed index; | ASMArgIndexed index; | ||||
| ASMArgLabel label; | |||||
| ASMArgCondition cond; | ASMArgCondition cond; | ||||
| ASMArgPort port; | |||||
| } data; | } data; | ||||
| } ASMInstArg; | } ASMInstArg; | ||||
| @@ -1,98 +0,0 @@ | |||||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| 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; | |||||
| } | |||||
| @@ -1,155 +0,0 @@ | |||||
| /* 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 <stdarg.h> | |||||
| #include <stdlib.h> | |||||
| #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); | |||||
| @@ -1,705 +1,198 @@ | |||||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | ||||
| Released under the terms of the MIT License. See LICENSE for details. */ | 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 <stdarg.h> | |||||
| #include <stdlib.h> | |||||
| 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. | 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 | // Exploit the fact that we can store the entire mnemonic string as a | ||||
| // single 32-bit value to do fast lookups: | // 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); | |||||
| } | } | ||||
| @@ -0,0 +1,733 @@ | |||||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| 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; | |||||
| } | |||||
| @@ -0,0 +1,323 @@ | |||||
| # Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| # 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 | |||||
| @@ -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. | Read in an immediate argument and store it in *result. | ||||
| */ | */ | ||||
| bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) | bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) | ||||
| @@ -326,10 +346,11 @@ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) | |||||
| if (ai.size <= 0) | if (ai.size <= 0) | ||||
| return false; | return false; | ||||
| bool negative = false; | |||||
| bool negative = false, modifiers = false; | |||||
| ssize_t i = 0; | ssize_t i = 0; | ||||
| while (ai.arg[i] == '-' || ai.arg[i] == '+' || ai.arg[i] == ' ') { | while (ai.arg[i] == '-' || ai.arg[i] == '+' || ai.arg[i] == ' ') { | ||||
| modifiers = true; | |||||
| if (ai.arg[i] == '-') | if (ai.arg[i] == '-') | ||||
| negative = !negative; | negative = !negative; | ||||
| if (++i >= ai.size) | 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); | const ASMDefine *define = asm_deftable_find(ai.deftable, ai.arg, ai.size); | ||||
| if (define) { | if (define) { | ||||
| if (negative) { | if (negative) { | ||||
| calculate_immediate_mask(result); | |||||
| result->is_label = false; | |||||
| result->uval = define->value.uval; | result->uval = define->value.uval; | ||||
| result->sval = -define->value.sval; | result->sval = -define->value.sval; | ||||
| calculate_immediate_mask(result); | |||||
| } else { | } else { | ||||
| *result = define->value; | *result = define->value; | ||||
| } | } | ||||
| @@ -351,16 +373,23 @@ bool argparse_immediate(ASMArgImmediate *result, ASMArgParseInfo ai) | |||||
| } | } | ||||
| uint32_t uval; | 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; | return false; | ||||
| int32_t sval = negative ? -uval : uval; | int32_t sval = negative ? -uval : uval; | ||||
| if (sval < INT16_MIN) | if (sval < INT16_MIN) | ||||
| return false; | return false; | ||||
| calculate_immediate_mask(result); | |||||
| result->is_label = false; | |||||
| result->uval = uval; | result->uval = uval; | ||||
| result->sval = sval; | result->sval = sval; | ||||
| calculate_immediate_mask(result); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -374,9 +403,9 @@ bool argparse_indirect(ASMArgIndirect *result, ASMArgParseInfo ai) | |||||
| ASMArgRegister reg; | ASMArgRegister reg; | ||||
| ASMArgImmediate imm; | ASMArgImmediate imm; | ||||
| ASMArgLabel label; | |||||
| if (argparse_register(®, ai)) { | 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->type = AT_REGISTER; | ||||
| result->addr.reg = reg; | result->addr.reg = reg; | ||||
| return true; | return true; | ||||
| @@ -387,10 +416,6 @@ bool argparse_indirect(ASMArgIndirect *result, ASMArgParseInfo ai) | |||||
| result->addr.imm = imm; | result->addr.imm = imm; | ||||
| return true; | return true; | ||||
| } | } | ||||
| } else if (argparse_label(&label, ai)) { | |||||
| result->type = AT_LABEL; | |||||
| result->addr.label = label; | |||||
| return true; | |||||
| } | } | ||||
| return false; | 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; | 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; | |||||
| } | } | ||||
| /* | /* | ||||
| @@ -33,7 +33,7 @@ bool argparse_condition(ASMArgCondition*, ASMArgParseInfo); | |||||
| bool argparse_immediate(ASMArgImmediate*, ASMArgParseInfo); | bool argparse_immediate(ASMArgImmediate*, ASMArgParseInfo); | ||||
| bool argparse_indirect(ASMArgIndirect*, ASMArgParseInfo); | bool argparse_indirect(ASMArgIndirect*, ASMArgParseInfo); | ||||
| bool argparse_indexed(ASMArgIndexed*, ASMArgParseInfo); | bool argparse_indexed(ASMArgIndexed*, ASMArgParseInfo); | ||||
| bool argparse_label(ASMArgLabel*, ASMArgParseInfo); | |||||
| bool argparse_port(ASMArgPort*, ASMArgParseInfo); | |||||
| /* Preprocessor directive parsers */ | /* Preprocessor directive parsers */ | ||||
| bool dparse_bool(bool*, const ASMLine*, const char*); | bool dparse_bool(bool*, const ASMLine*, const char*); | ||||
| @@ -155,7 +155,7 @@ static ErrorInfo* handle_define_directive( | |||||
| ASMArgImmediate imm; | ASMArgImmediate imm; | ||||
| ASMArgParseInfo info = { | ASMArgParseInfo info = { | ||||
| .arg = line->data + i, .size = line->length - i, .deftable = deftab}; | .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); | return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG); | ||||
| ASMDefine *define = cr_malloc(sizeof(ASMDefine)); | ASMDefine *define = cr_malloc(sizeof(ASMDefine)); | ||||