Browse Source

Split off some of instructions into inst_support; fix z80 errors.

master
Ben Kurtovic 9 years ago
parent
commit
2e2e5d6216
4 changed files with 230 additions and 214 deletions
  1. +98
    -0
      src/assembler/inst_support.c
  2. +129
    -0
      src/assembler/inst_support.h
  3. +1
    -212
      src/assembler/instructions.c
  4. +2
    -2
      src/z80.c

+ 98
- 0
src/assembler/inst_support.c View File

@@ -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;
}

+ 129
- 0
src/assembler/inst_support.h View File

@@ -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
- 212
src/assembler/instructions.c View File

@@ -1,219 +1,8 @@
/* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
Released under the terms of the MIT License. See LICENSE for details. */

#include <stdarg.h>
#include <stdlib.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 */



+ 2
- 2
src/z80.c View File

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



Loading…
Cancel
Save