Browse Source

Replace step with reg/cond, which are smarter; replace 'cond' with 'if'.

master
Ben Kurtovic 9 years ago
parent
commit
e12b7ee559
3 changed files with 353 additions and 319 deletions
  1. +75
    -41
      scripts/update_asm_instructions.py
  2. +1
    -1
      src/assembler/instructions.inc.c
  3. +277
    -277
      src/assembler/instructions.yml

+ 75
- 41
scripts/update_asm_instructions.py View File

@@ -36,6 +36,12 @@ re_lookup = re.compile(
r"(/\* @AUTOGEN_LOOKUP_BLOCK_START \*/\n*)(.*?)"
r"(\n*/\* @AUTOGEN_LOOKUP_BLOCK_END \*/)", re.S)

def _rindex(L, val):
"""
Return the index of the last occurence of val in L.
"""
return len(L) - L[::-1].index(val) - 1

def _atoi(value):
"""
Try to convert a string to an integer, supporting decimal and hexadecimal.
@@ -57,6 +63,22 @@ def _call_args(call, func):
"""
return call[len(func) + 1:-1].strip()

def _parse_step_args(call, func):
"""
Parse arguments to a step function (e.g. reg() or cond()).
"""
args = _call_args(call, func)
if " " in args:
return map(_atoi, args.split(" "))
else:
return _atoi(args), 1

class ASMInstError(Exception):
"""
Base class for all errors while trying to generate the instructions file.
"""


class Instruction(object):
"""
Represent a single ASM instruction mnemonic.
@@ -72,13 +94,32 @@ class Instruction(object):
PSEUDO_TYPES = {
"indirect_hl_or_indexed": ["AT_INDIRECT", "AT_INDEXED"]
}
REGISTER_OFFSETS = {
"a": 7,
"b": 0,
"c": 1,
"d": 2,
"e": 3,
"h": 4,
"ixh": 4,
"iyh": 4,
"l": 5,
"ixl": 5,
"iyl": 5,

"bc": 0,
"de": 1,
"hl": 2,
"ix": 2,
"iy": 2,
"sp": 3
}
CONDITION_ORDER = ["nz", "z", "nc", "c", "po", "pe", "p", "m"]

def __init__(self, name, data):
self._name = name
self._data = data

self._has_optional_args = False
self._step_state = {}

def _get_arg_parse_mask(self, num):
"""
@@ -159,13 +200,13 @@ class Instruction(object):
return "INST_INDIRECT({0}).type == AT_IMMEDIATE".format(num)

err = "Unknown condition for indirect argument: {0}"
return RuntimeError(err.format(cond))
return ASMInstError(err.format(cond))

def _build_indexed_check(self, num, cond):
"""
Return an expression to check for a particular indexed value.
"""
raise RuntimeError("The indexed arg type does not support conditions")
raise ASMInstError("The indexed arg type does not support conditions")

def _build_condition_check(self, num, cond):
"""
@@ -183,7 +224,7 @@ class Instruction(object):
return "INST_PORT({0}).type == AT_IMMEDIATE".format(num)

err = "Unknown condition for port argument: {0}"
return RuntimeError(err.format(cond))
return ASMInstError(err.format(cond))

_SUBCASE_LOOKUP_TABLE = {
"register": _build_register_check,
@@ -212,7 +253,7 @@ class Instruction(object):
merged = [choice for s in splits for choice in s]
if len(merged) != len(set(merged)):
msg = "Repeated conditions for {0}: {1}"
raise RuntimeError(msg.format(typ, cond))
raise ASMInstError(msg.format(typ, cond))
return merged
if typ == "register":
if cond == "i":
@@ -228,32 +269,20 @@ class Instruction(object):

if any(1 < len(cond) < num for cond in splits):
msg = "Invalid condition permutations: {0}"
raise RuntimeError(msg.format(conds))
raise ASMInstError(msg.format(conds))

choices = [cond * num if len(cond) == 1 else cond for cond in splits]
return zip(*choices)

def _step(self, argdata):
"""
Evaluate a step function call into a single byte.
"""
args = _call_args(argdata, "step")
if " " in args:
base, stride = map(_atoi, args.split(" "))
else:
base, stride = _atoi(args), 1

if base not in self._step_state:
self._step_state[base] = 0

byte = base + self._step_state[base] * stride
self._step_state[base] += 1
return byte

def _adapt_return(self, types, conds, ret):
"""
Return a modified byte list to accomodate for prefixes and immediates.
"""
def handle_reg_func(call):
base, stride = _parse_step_args(call, "reg")
index = _rindex(types, "register")
return base + self.REGISTER_OFFSETS[conds[index]] * stride

ret = ret[:]
for i, byte in enumerate(ret):
if not isinstance(byte, int):
@@ -268,7 +297,7 @@ class Instruction(object):

elif byte == "u16":
if i < len(ret) - 1:
raise RuntimeError("U16 return byte must be last")
raise ASMInstError("U16 return byte must be last")
try:
index = types.index("immediate")
imm = "INST_IMM({0})".format(index)
@@ -276,7 +305,7 @@ class Instruction(object):
indir = types.index("indirect")
if not conds[indir].startswith("imm"):
msg = "Passing non-immediate indirect as immediate"
raise RuntimeError(msg)
raise ASMInstError(msg)
imm = "INST_INDIRECT({0}).addr.imm".format(indir)
ret[i] = "INST_IMM_U16_B1({0})".format(imm)
ret.append("INST_IMM_U16_B2({0})".format(imm))
@@ -289,17 +318,23 @@ class Instruction(object):
elif _is_call(byte, "bit"):
index = types.index("immediate")
base = _call_args(byte, "bit")
if _is_call(base, "step"):
base = self._step(base)
if _is_call(base, "reg"):
base = handle_reg_func(base)
ret[i] = "0x{0:02X} + 8 * INST_IMM({1}).uval".format(
_atoi(base), index)

elif _is_call(byte, "step"):
ret[i] = self._step(byte)
elif _is_call(byte, "reg"):
ret[i] = handle_reg_func(byte)

elif _is_call(byte, "cond"):
base, stride = _parse_step_args(byte, "cond")
index = types.index("condition")
offset = self.CONDITION_ORDER.index(conds[index])
ret[i] = base + offset * stride

else:
msg = "Unsupported return byte: {0}"
raise RuntimeError(msg.format(byte))
raise ASMInstError(msg.format(byte))

for i, cond in enumerate(conds):
if types[i] == "register" and cond[0] == "i":
@@ -343,7 +378,7 @@ class Instruction(object):

return indirect + indexed

raise RuntimeError("Unknown pseudo-type: {0}".format(pseudo))
raise ASMInstError("Unknown pseudo-type: {0}".format(pseudo))

def _handle_case(self, case):
"""
@@ -361,9 +396,8 @@ class Instruction(object):
cond = self._build_case_type_check(ctype)
lines.append(TAB + "if ({0}) {{".format(cond))

self._step_state = {}
subcases = [(perm, sub["return"]) for sub in case["cases"]
for perm in self._iter_permutations(ctype, sub["cond"])]
for perm in self._iter_permutations(ctype, sub["if"])]
for cond, ret in subcases:
check = self._build_subcase_check(ctype, cond)
ret = self._adapt_return(ctype, cond, ret)
@@ -401,32 +435,32 @@ class Instruction(object):
lines.append(TAB + "INST_ERROR(ARG_TYPE)")
else:
msg = "Missing return or case block for {0} instruction"
raise RuntimeError(msg.format(self._name))
raise ASMInstError(msg.format(self._name))

contents = "\n".join(lines)
return "INST_FUNC({0})\n{{\n{1}\n}}".format(self._name, contents)


def build_inst_block(data):
def _build_inst_block(data):
"""
Return the instruction parser block, given instruction data.
"""
return "\n\n".join(
Instruction(k, v).render() for k, v in sorted(data.items()))

def build_lookup_block(data):
def _build_lookup_block(data):
"""
Return the instruction lookup block, given instruction data.
"""
macro = TAB + "HANDLE({0})"
return "\n".join(macro.format(inst) for inst in sorted(data.keys()))

def process(template, data):
def _process(template, data):
"""
Return C code generated from a source template and instruction data.
"""
inst_block = build_inst_block(data)
lookup_block = build_lookup_block(data)
inst_block = _build_inst_block(data)
lookup_block = _build_lookup_block(data)
date = time.asctime(time.gmtime())

result = re_date.sub(r"\1{0} UTC".format(date), template)
@@ -444,7 +478,7 @@ def main():
template = fp.read().decode(ENCODING)

data = yaml.load(text)
result = process(template, data)
result = _process(template, data)

with open(DEST, "w") as fp:
fp.write(result.encode(ENCODING))


+ 1
- 1
src/assembler/instructions.inc.c View File

@@ -7,7 +7,7 @@
`make` should trigger a rebuild when it is modified; if not, use:
`python scripts/update_asm_instructions.py`.

@AUTOGEN_DATE Thu May 21 23:52:57 2015 UTC
@AUTOGEN_DATE Fri May 22 00:38:38 2015 UTC
*/

/* @AUTOGEN_INST_BLOCK_START */


+ 277
- 277
src/assembler/instructions.yml
File diff suppressed because it is too large
View File


Loading…
Cancel
Save