| @@ -12,6 +12,7 @@ CFLAGS = $(shell sdl2-config --cflags) | |||||
| LIBS = $(shell sdl2-config --libs) | LIBS = $(shell sdl2-config --libs) | ||||
| MKDIR = mkdir -p | MKDIR = mkdir -p | ||||
| RM = rm -rf | RM = rm -rf | ||||
| ASM_UP = scripts/update_asm_instructions.py | |||||
| MODE = release | MODE = release | ||||
| BNRY = $(PROGRAM) | BNRY = $(PROGRAM) | ||||
| @@ -47,8 +48,9 @@ $(BUILD)/$(MODE)/%.o: %.c | |||||
| -include $(DEPS) | -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 | test: test-all test-z80 test-asm test-dasm | ||||
| @@ -12,6 +12,7 @@ when the latter is modified, but can also be run manually. | |||||
| from __future__ import print_function | from __future__ import print_function | ||||
| from itertools import product | |||||
| import re | import re | ||||
| import time | import time | ||||
| @@ -44,10 +45,13 @@ class Instruction(object): | |||||
| "register": "AT_REGISTER", | "register": "AT_REGISTER", | ||||
| "immediate": "AT_IMMEDIATE", | "immediate": "AT_IMMEDIATE", | ||||
| "indirect": "AT_INDIRECT", | "indirect": "AT_INDIRECT", | ||||
| "indexed": "AT_INDEXED|AT_INDIRECT", | |||||
| "indexed": "AT_INDEXED", | |||||
| "condition": "AT_CONDITION", | "condition": "AT_CONDITION", | ||||
| "port": "AT_PORT" | "port": "AT_PORT" | ||||
| } | } | ||||
| ARG_EXTRA = { | |||||
| "indexed": ["AT_INDIRECT"] | |||||
| } | |||||
| def __init__(self, name, data): | def __init__(self, name, data): | ||||
| self._name = name | self._name = name | ||||
| @@ -61,7 +65,10 @@ class Instruction(object): | |||||
| optional = False | optional = False | ||||
| for case in self._data["cases"]: | for case in self._data["cases"]: | ||||
| if num < len(case["type"]): | 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: | else: | ||||
| optional = True | optional = True | ||||
| @@ -71,22 +78,123 @@ class Instruction(object): | |||||
| types.add("AT_OPTIONAL") | types.add("AT_OPTIONAL") | ||||
| return "|".join(types) | 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. | 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): | 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): | def render(self): | ||||
| """ | """ | ||||
| @@ -84,6 +84,7 @@ static ASMErrorDesc parse_inst_##mnemonic( \ | |||||
| if (err) \ | if (err) \ | ||||
| return err; \ | return err; \ | ||||
| #define INST_NARGS nargs | |||||
| #define INST_TYPE(n) args[n].type | #define INST_TYPE(n) args[n].type | ||||
| #define INST_REG(n) args[n].data.reg | #define INST_REG(n) args[n].data.reg | ||||
| #define INST_IMM(n) args[n].data.imm | #define INST_IMM(n) args[n].data.imm | ||||
| @@ -46,278 +46,278 @@ adc: | |||||
| - cond: [a, _] | - cond: [a, _] | ||||
| return: [0x8E] | 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: | ini: | ||||
| args: no | args: no | ||||
| return: [0xED, 0xA2] | 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 | |||||