@@ -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 | |||
@@ -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): | |||
""" | |||
@@ -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 | |||
@@ -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 |