178 lines
4 KiB
Python
178 lines
4 KiB
Python
import sys
|
|
import pprint
|
|
import dataclasses
|
|
|
|
import pyparsing as pp
|
|
import click
|
|
|
|
pp.ParserElement.set_default_whitespace_chars(" \t")
|
|
|
|
newline = pp.Word("\r\n").suppress()
|
|
|
|
comment = pp.Group(pp.Literal("#") + ... + newline).suppress()
|
|
|
|
blank = newline
|
|
|
|
prologue = (comment | blank)[...] # (comment | blank)[...]
|
|
|
|
heading = pp.Literal("[").suppress() + ... + pp.Literal("]").suppress() + newline
|
|
|
|
statement = pp.Group(pp.Word(pp.alphanums + "(-") + ... + newline)
|
|
|
|
operand = pp.Word(pp.alphanums + "()#")
|
|
|
|
accumulator = pp.Literal("A")
|
|
immediate = pp.Literal("#d8")
|
|
zp = pp.Literal("a8")
|
|
x_ind_zp = pp.Literal("a8,X")
|
|
y_ind_zp = pp.Literal("a8,Y")
|
|
x_ind_zp_ind = pp.Literal("(a8,X)")
|
|
zp_ind_y_ind = pp.Literal("(a8),Y")
|
|
absolute = pp.Literal("a16")
|
|
x_ind_absolute = pp.Literal("a16,X")
|
|
y_ind_absolute = pp.Literal("a16,Y")
|
|
absolute_ind = pp.Literal("(a16)")
|
|
relative = pp.Literal("r8")
|
|
|
|
mode = (
|
|
accumulator
|
|
| immediate
|
|
| x_ind_zp
|
|
| y_ind_zp
|
|
| zp
|
|
| x_ind_zp_ind
|
|
| zp_ind_y_ind
|
|
| x_ind_absolute
|
|
| y_ind_absolute
|
|
| absolute
|
|
| absolute_ind
|
|
| relative
|
|
)
|
|
|
|
opcode = pp.Group(pp.common.hex_integer + pp.Word(pp.alphas) + pp.Opt(mode) + newline)
|
|
|
|
body = (comment | opcode | statement | blank)[...]
|
|
|
|
section = pp.Group(heading + body)
|
|
|
|
doc = prologue + section[...]
|
|
|
|
|
|
@dataclasses.dataclass
|
|
class Instruction:
|
|
opcode: str
|
|
operands: list[str]
|
|
bytes: list[int]
|
|
|
|
|
|
_RESERVED = ("and", "or", "xor")
|
|
|
|
|
|
def cname(name: str) -> str:
|
|
name = (
|
|
name.lower()
|
|
.replace("(", "")
|
|
.replace("#", "")
|
|
.replace(")", "i")
|
|
.replace(",", "_")
|
|
.replace("+", "_")
|
|
.replace("'", "p")
|
|
)
|
|
if name in _RESERVED:
|
|
return name + "_"
|
|
return name
|
|
|
|
|
|
def func_name(instruction: Instruction) -> str:
|
|
parts = [instruction.opcode] + instruction.operands
|
|
return cname("_".join(parts))
|
|
|
|
|
|
def remap_operand(operands: list[str]):
|
|
if not operands:
|
|
return ""
|
|
elif operands == ["A"]:
|
|
return "regs.a"
|
|
return f"{cname('_'.join(operands))}()"
|
|
|
|
|
|
def parse(opcodes: list):
|
|
for ops in opcodes:
|
|
top = doc.parse_file(ops, parse_all=True).as_list()
|
|
for section in top:
|
|
if section[0] == "opcodes":
|
|
for opcode in section[1:]:
|
|
if isinstance(opcode[0], int):
|
|
yield Instruction(
|
|
opcode=opcode[1],
|
|
operands=[opcode[2]] if len(opcode) > 2 else [],
|
|
bytes=[opcode[0]],
|
|
)
|
|
|
|
|
|
def goto_prefix(emit):
|
|
emit(
|
|
"""#pragma once
|
|
|
|
#include "cores/w65c02v1.h"
|
|
|
|
template<typename Board>
|
|
void W65C02v1<Board>::run() {
|
|
"""
|
|
)
|
|
|
|
|
|
def goto_dispatch(emit, top: list[Instruction]):
|
|
by_bytes = {}
|
|
for instruction in top:
|
|
by_bytes[instruction.bytes[0]] = instruction
|
|
emit(f"static const void* const dispatch[] = {{")
|
|
for i in range(256):
|
|
if i in by_bytes:
|
|
emit(f"&&_{func_name(by_bytes[i])},")
|
|
else:
|
|
emit(f"&&_illegal,")
|
|
emit("};")
|
|
|
|
|
|
def goto_suffix(emit):
|
|
emit(
|
|
"""
|
|
_illegal:
|
|
illegal();
|
|
}
|
|
"""
|
|
)
|
|
|
|
USE_NEXT = False
|
|
|
|
def goto_implement(emit, top):
|
|
goto_prefix(emit)
|
|
goto_dispatch(emit, top)
|
|
if USE_NEXT:
|
|
emit("_next:")
|
|
emit("goto *dispatch[fetch()];")
|
|
for instruction in top:
|
|
emit(
|
|
f"""_{func_name(instruction)}:
|
|
{cname(instruction.opcode)}({remap_operand(instruction.operands)});"""
|
|
)
|
|
if USE_NEXT:
|
|
emit("goto _next;")
|
|
else:
|
|
emit("goto *dispatch[fetch()];")
|
|
goto_suffix(emit)
|
|
|
|
|
|
@click.command()
|
|
@click.option("--opcodes", required=True, multiple=True, type=click.File(mode="r"))
|
|
@click.option("--out", required=True, type=click.File(mode="w", lazy=True, atomic=True))
|
|
def main(opcodes: list, out):
|
|
def emit(*args):
|
|
print(*args, file=out)
|
|
|
|
goto_implement(emit, list(parse(opcodes)))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|