diff --git a/makefile b/makefile index 35bf657..b6763ab 100644 --- a/makefile +++ b/makefile @@ -12,6 +12,7 @@ CFLAGS = $(shell sdl2-config --cflags) LIBS = $(shell sdl2-config --libs) MKDIR = mkdir -p RM = rm -rf +ASM_UP = scripts/update_asm_instructions.py MODE = release BNRY = $(PROGRAM) @@ -47,8 +48,9 @@ $(BUILD)/$(MODE)/%.o: %.c -include $(DEPS) -$(SOURCES)/assembler/instructions.inc.c: $(SOURCES)/assembler/instructions.yml - python scripts/update_asm_instructions.py +ASM_INST = $(SOURCES)/assembler/instructions +$(ASM_INST).inc.c: $(ASM_INST).yml $(ASM_UP) + python $(ASM_UP) test: test-all test-z80 test-asm test-dasm diff --git a/scripts/update_asm_instructions.py b/scripts/update_asm_instructions.py index 6625b89..762c8bc 100755 --- a/scripts/update_asm_instructions.py +++ b/scripts/update_asm_instructions.py @@ -12,6 +12,7 @@ when the latter is modified, but can also be run manually. from __future__ import print_function +from itertools import product import re import time @@ -44,10 +45,13 @@ class Instruction(object): "register": "AT_REGISTER", "immediate": "AT_IMMEDIATE", "indirect": "AT_INDIRECT", - "indexed": "AT_INDEXED|AT_INDIRECT", + "indexed": "AT_INDEXED", "condition": "AT_CONDITION", "port": "AT_PORT" } + ARG_EXTRA = { + "indexed": ["AT_INDIRECT"] + } def __init__(self, name, data): self._name = name @@ -61,7 +65,10 @@ class Instruction(object): optional = False for case in self._data["cases"]: if num < len(case["type"]): - types.add(self.ARG_TYPES[case["type"][num]]) + atype = case["type"][num] + types.add(self.ARG_TYPES[atype]) + if atype in self.ARG_EXTRA: + types.update(self.ARG_EXTRA[atype]) else: optional = True @@ -71,22 +78,123 @@ class Instruction(object): types.add("AT_OPTIONAL") return "|".join(types) - def _handle_return(self, arg, indent=1): + def _handle_return(self, ret, indent=1): """ Return code to handle an instruction return statement. """ - tabs = TAB * indent - if arg == "error": - return tabs + "INST_ERROR(ARG_SYNTAX)" - else: - data = ", ".join("0x%02X" % byte for byte in arg) - return tabs + "INST_RETURN({0}, {1})".format(len(arg), data) + data = ", ".join("0x%02X" % byte if isinstance(byte, int) else byte + for byte in ret) + return TAB * indent + "INST_RETURN({0}, {1})".format(len(ret), data) + + def _build_case_type_check(self, args): + """ + Return the test part of an if statement for an instruction case. + """ + conds = ["INST_TYPE({0}) == {1}".format(i, self.ARG_TYPES[cond]) + for i, cond in enumerate(args)] + return "INST_NARGS == {0} && {1}".format(len(args), " && ".join(conds)) + + def _build_register_check(self, num, cond): + """ + Return an expression to check for a particular register value. + """ + return "INST_REG({0}) == REG_{1}".format(num, cond.upper()) + + def _build_immediate_check(self, num, cond): + """ + Return an expression to check for a particular immediate value. + """ + return "INST_IMM({0}).mask & IMM_{1}".format(num, cond.upper()) + + def _build_indirect_check(self, num, cond): + """ + Return an expression to check for a particular indirect value. + """ + # TODO + return cond + + def _build_indexed_check(self, num, cond): + """ + Return an expression to check for a particular indexed value. + """ + # TODO + return cond + + def _build_condition_check(self, num, cond): + """ + Return an expression to check for a particular condition value. + """ + return "INST_COND({0}) == COND_{1}".format(num, cond.upper()) + + def _build_port_check(self, num, cond): + """ + Return an expression to check for a particular port value. + """ + # TODO + return cond + + _SUBCASE_LOOKUP_TABLE = { + "register": _build_register_check, + "immediate": _build_immediate_check, + "indirect": _build_indirect_check, + "indexed": _build_indexed_check, + "condition": _build_condition_check, + "port": _build_port_check + } + + def _build_subcase_check(self, types, conds): + """ + Return the test part of an if statement for an instruction subcase. + """ + return " && ".join(self._SUBCASE_LOOKUP_TABLE[types[i]](self, i, cond) + for i, cond in enumerate(conds) if cond != "_") + + def _iter_permutations(self, types, conds): + """ + Iterate over all permutations of the given subcase conditions. + """ + def split(typ, cond): + if "|" in cond: + sets = [split(typ, c) for c in cond.split("|")] + return {choice for s in sets for choice in s} + if typ == "register" and cond == "ih": + return {"ixh", "iyh"} + if typ == "register" and cond == "il": + return {"ixl", "iyl"} + return {cond} + + return product(*(split(types[i], cond) + for i, cond in enumerate(conds))) + + def _adapt_return(self, types, conds, ret): + """ + Return a modified byte list to accomodate for prefixes and immediates. + """ + for i, cond in enumerate(conds): + if types[i] == "register" and cond.startswith("ix"): + ret = ["INST_IX_PREFIX"] + ret + elif types[i] == "register" and cond.startswith("iy"): + ret = ["INST_IY_PREFIX"] + ret + return ret def _handle_case(self, case): """ - TODO + Return code to handle an instruction case. """ - return [TAB + "// " + str(case)] + lines = [] + cond = self._build_case_type_check(case["type"]) + lines.append(TAB + "if ({0}) {{".format(cond)) + + for subcase in case["cases"]: + for perm in self._iter_permutations(case["type"], subcase["cond"]): + cond = self._build_subcase_check(case["type"], perm) + ret = self._adapt_return(case["type"], perm, subcase["return"]) + lines.append(TAB * 2 + "if ({0})".format(cond)) + lines.append(self._handle_return(ret, 3)) + + lines.append(TAB * 2 + "INST_ERROR(ARG_VALUE)") + lines.append(TAB + "}") + return lines def render(self): """ diff --git a/src/assembler/instructions.c b/src/assembler/instructions.c index 4b6740e..121ecce 100644 --- a/src/assembler/instructions.c +++ b/src/assembler/instructions.c @@ -84,6 +84,7 @@ static ASMErrorDesc parse_inst_##mnemonic( \ if (err) \ return err; \ +#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 diff --git a/src/assembler/instructions.yml b/src/assembler/instructions.yml index 0dd31d0..d596130 100644 --- a/src/assembler/instructions.yml +++ b/src/assembler/instructions.yml @@ -46,278 +46,278 @@ adc: - cond: [a, _] return: [0x8E] -add: - args: no - return: error +# add: +# args: no +# return: error -and: - args: no - return: error +# and: +# args: no +# return: error -bit: - args: no - return: error +# bit: +# args: no +# return: error -call: - args: no - return: error +# call: +# args: no +# return: error -ccf: - args: no - return: error +# ccf: +# args: no +# return: error -cp: - args: no - return: error +# cp: +# args: no +# return: error -cpd: - args: no - return: error +# cpd: +# args: no +# return: error -cpdr: - args: no - return: error +# cpdr: +# args: no +# return: error -cpi: - args: no - return: error +# cpi: +# args: no +# return: error -cpir: - args: no - return: error +# cpir: +# args: no +# return: error -cpl: - args: no - return: error +# cpl: +# args: no +# return: error -daa: - args: no - return: error +# daa: +# args: no +# return: error -dec: - args: no - return: error +# dec: +# args: no +# return: error -di: - args: no - return: error +# di: +# args: no +# return: error -djnz: - args: no - return: error +# djnz: +# args: no +# return: error -ei: - args: no - return: error +# ei: +# args: no +# return: error -ex: - args: no - return: error +# ex: +# args: no +# return: error -exx: - args: no - return: error +# exx: +# args: no +# return: error -halt: - args: no - return: error +# halt: +# args: no +# return: error -im: - args: no - return: error +# im: +# args: no +# return: error -in: - args: no - return: error +# in: +# args: no +# return: error -inc: - args: no - return: error +# inc: +# args: no +# return: error -ind: - args: no - return: error +# ind: +# args: no +# return: error -indr: - args: no - return: error +# indr: +# args: no +# return: error ini: args: no return: [0xED, 0xA2] -inir: - args: no - return: error +# inir: +# args: no +# return: error -jp: - args: no - return: error +# jp: +# args: no +# return: error -jr: - args: no - return: error +# jr: +# args: no +# return: error -ld: - args: no - return: error +# ld: +# args: no +# return: error -ldd: - args: no - return: error +# ldd: +# args: no +# return: error -lddr: - args: no - return: error +# lddr: +# args: no +# return: error -ldi: - args: no - return: error +# ldi: +# args: no +# return: error -ldir: - args: no - return: error +# ldir: +# args: no +# return: error -neg: - args: no - return: error +# neg: +# args: no +# return: error -nop: - args: no - return: error +# nop: +# args: no +# return: error -or: - args: no - return: error +# or: +# args: no +# return: error -otdr: - args: no - return: error +# otdr: +# args: no +# return: error -otir: - args: no - return: error +# otir: +# args: no +# return: error -out: - args: no - return: error +# out: +# args: no +# return: error -outd: - args: no - return: error +# outd: +# args: no +# return: error -outi: - args: no - return: error +# outi: +# args: no +# return: error -pop: - args: no - return: error +# pop: +# args: no +# return: error -push: - args: no - return: error +# push: +# args: no +# return: error -res: - args: no - return: error +# res: +# args: no +# return: error -ret: - args: no - return: error +# ret: +# args: no +# return: error -reti: - args: no - return: error +# reti: +# args: no +# return: error -retn: - args: no - return: error +# retn: +# args: no +# return: error -rl: - args: no - return: error +# rl: +# args: no +# return: error -rla: - args: no - return: error +# rla: +# args: no +# return: error -rlc: - args: no - return: error +# rlc: +# args: no +# return: error -rlca: - args: no - return: error +# rlca: +# args: no +# return: error -rld: - args: no - return: error +# rld: +# args: no +# return: error -rr: - args: no - return: error +# rr: +# args: no +# return: error -rra: - args: no - return: error +# rra: +# args: no +# return: error -rrc: - args: no - return: error +# rrc: +# args: no +# return: error -rrca: - args: no - return: error +# rrca: +# args: no +# return: error -rrd: - args: no - return: error +# rrd: +# args: no +# return: error -rst: - args: no - return: error +# rst: +# args: no +# return: error -sbc: - args: no - return: error +# sbc: +# args: no +# return: error -scf: - args: no - return: error +# scf: +# args: no +# return: error -set: - args: no - return: error +# set: +# args: no +# return: error -sl1: - args: no - return: error +# sl1: +# args: no +# return: error -sla: - args: no - return: error +# sla: +# args: no +# return: error -sll: - args: no - return: error +# sll: +# args: no +# return: error -sls: - args: no - return: error +# sls: +# args: no +# return: error -sra: - args: no - return: error +# sra: +# args: no +# return: error -srl: - args: no - return: error +# srl: +# args: no +# return: error -sub: - args: no - return: error +# sub: +# args: no +# return: error -xor: - args: no - return: error +# xor: +# args: no +# return: error