@@ -0,0 +1,98 @@ | |||||
/* 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; | |||||
} |
@@ -0,0 +1,129 @@ | |||||
/* 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 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); |
@@ -1,219 +1,8 @@ | |||||
/* 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 <stdarg.h> | |||||
#include <stdlib.h> | |||||
#include "instructions.h" | #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 */ | /* Instruction parser functions */ | ||||
@@ -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_BC: return (z80->regfile.b << 8) + z80->regfile.c; | ||||
case REG_DE: return (z80->regfile.d << 8) + z80->regfile.e; | case REG_DE: return (z80->regfile.d << 8) + z80->regfile.e; | ||||
case REG_HL: return (z80->regfile.h << 8) + z80->regfile.l; | 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_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_DE: z80->regfile.d = value >> 8; z80->regfile.e = value; break; | ||||
case REG_HL: z80->regfile.h = value >> 8; z80->regfile.l = 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) | |||||
} | } | ||||
} | } | ||||