From 2e2e5d6216988ca9b8e7d1a995edf143e2ee1fc6 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Sun, 10 May 2015 00:16:40 -0500 Subject: [PATCH] Split off some of instructions into inst_support; fix z80 errors. --- src/assembler/inst_support.c | 98 ++++++++++++++++++++ src/assembler/inst_support.h | 129 ++++++++++++++++++++++++++ src/assembler/instructions.c | 213 +------------------------------------------ src/z80.c | 4 +- 4 files changed, 230 insertions(+), 214 deletions(-) create mode 100644 src/assembler/inst_support.c create mode 100644 src/assembler/inst_support.h diff --git a/src/assembler/inst_support.c b/src/assembler/inst_support.c new file mode 100644 index 0000000..895f99d --- /dev/null +++ b/src/assembler/inst_support.c @@ -0,0 +1,98 @@ +/* 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 new file mode 100644 index 0000000..34bfcae --- /dev/null +++ b/src/assembler/inst_support.h @@ -0,0 +1,129 @@ +/* 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 for instruction parsers */ + +#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_PREFIX_(reg) \ + (((reg) == REG_IX || (reg) == REG_IXH || (reg) == REG_IXL) ? 0xDD : 0xFD) + +#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; \ + } + +/* 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(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_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_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_RETURN(len, ...) { \ + (void) symbol; \ + INST_ALLOC_(len) \ + INST_FILL_BYTES_(len, __VA_ARGS__) \ + return ED_NONE; \ + } + +#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__) + +/* 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 84abc96..b80c3f2 100644 --- a/src/assembler/instructions.c +++ b/src/assembler/instructions.c @@ -1,219 +1,8 @@ /* Copyright (C) 2014-2015 Ben Kurtovic Released under the terms of the MIT License. See LICENSE for details. */ -#include -#include - #include "instructions.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 for instruction parsers */ - -#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_PREFIX_(reg) \ - (((reg) == REG_IX || (reg) == REG_IXH || (reg) == REG_IXL) ? 0xDD : 0xFD) - -#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; \ - } - -/* 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(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_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_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_RETURN(len, ...) { \ - (void) symbol; \ - INST_ALLOC_(len) \ - INST_FILL_BYTES_(len, __VA_ARGS__) \ - return ED_NONE; \ - } - -#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__) - -/* - 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. -*/ -static 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) -{ -#define TRY_PARSER(func, argtype, field) \ - if (argparse_##func(&arg->data.field, info)) { \ - arg->type = argtype; \ - return ED_NONE; \ - } - - 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; - -#undef TRY_PARSER -} - -/* - Parse an argument string into ASMInstArg objects. - - 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) -{ - 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; -} +#include "inst_support.h" /* Instruction parser functions */ diff --git a/src/z80.c b/src/z80.c index 681f3c0..593d2fd 100644 --- a/src/z80.c +++ b/src/z80.c @@ -90,7 +90,7 @@ static inline uint16_t get_pair(Z80 *z80, uint8_t pair) case REG_BC: return (z80->regfile.b << 8) + z80->regfile.c; case REG_DE: return (z80->regfile.d << 8) + z80->regfile.e; case REG_HL: return (z80->regfile.h << 8) + z80->regfile.l; - default: FATAL("Invalid call: get_pair(z80, %u)", pair) + default: FATAL("invalid call: get_pair(z80, %u)", pair) } } @@ -104,7 +104,7 @@ static inline void set_pair(Z80 *z80, uint8_t pair, uint16_t value) case REG_BC: z80->regfile.b = value >> 8; z80->regfile.c = value; break; case REG_DE: z80->regfile.d = value >> 8; z80->regfile.e = value; break; case REG_HL: z80->regfile.h = value >> 8; z80->regfile.l = value; break; - default: FATAL("Invalid call: set_pair(z80, %u, 0x%04X)", pair, value) + default: FATAL("invalid call: set_pair(z80, %u, 0x%04X)", pair, value) } }