From a171bb998d8c956b3cea6ce7b1a863228a34f84d Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 16 Feb 2018 16:15:48 +0100 Subject: [PATCH] first part of goto codegen --- il65/emit/assignment.py | 4 +- il65/emit/calls.py | 137 ++++++++++++++++++++++++++++++++++++-- il65/lib/il65lib.ill | 1 + il65/oldstuff/codegen.py | 99 --------------------------- il65/plyparse.py | 7 +- testsource/numbergame.ill | 2 +- 6 files changed, 144 insertions(+), 106 deletions(-) diff --git a/il65/emit/assignment.py b/il65/emit/assignment.py index 9634d1922..af1ddb1ad 100644 --- a/il65/emit/assignment.py +++ b/il65/emit/assignment.py @@ -121,8 +121,10 @@ def generate_aug_assignment(ctx: Context) -> None: raise CodeError("invalid dereference operand type", rvalue) else: raise CodeError("invalid rvalue type", rvalue) + elif isinstance(lvalue, SymbolName): + raise NotImplementedError("symbolname augassign", lvalue) # XXX else: - raise CodeError("aug. assignment only implemented for registers for now", stmt.sourceref) # XXX + raise CodeError("aug. assignment only implemented for registers and symbols for now", lvalue, stmt.sourceref) # XXX def _generate_aug_reg_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None: diff --git a/il65/emit/calls.py b/il65/emit/calls.py index 4f9529a37..b0ade54f7 100644 --- a/il65/emit/calls.py +++ b/il65/emit/calls.py @@ -5,17 +5,146 @@ This is the code generator for gotos and subroutine calls. Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 """ -from ..plyparse import Goto, SubCall -from . import Context +from ..plyparse import Goto, SubCall, LiteralValue, SymbolName, Dereference, Register +from . import Context, CodeError, to_hex def generate_goto(ctx: Context) -> None: stmt = ctx.stmt assert isinstance(stmt, Goto) - pass # @todo + ctx.out("\v\t\t\t; " + ctx.stmt.lineref) + if stmt.condition: + if stmt.if_stmt: + _gen_goto_special_if_cond(ctx, stmt) + else: + _gen_goto_cond(ctx, stmt) + else: + if stmt.if_stmt: + _gen_goto_special_if(ctx, stmt) + else: + _gen_goto_unconditional(ctx, stmt) + + +def _gen_goto_special_if(ctx: Context, stmt: Goto) -> None: + # a special if, but no conditional expression + if isinstance(stmt.target, Dereference): + # dereference is symbol, literal, or register (pair) + if isinstance(stmt.target.operand, LiteralValue): + targetstr = to_hex(stmt.target.operand.value) + elif isinstance(stmt.target.operand, SymbolName): + targetstr = stmt.target.operand.name + else: + # register pair + ctx.out("\vst{:s} il65_lib.SCRATCH_ZPWORD1".format(stmt.target.operand.name[0])) + ctx.out("\vst{:s} il65_lib.SCRATCH_ZPWORD1+1".format(stmt.target.operand.name[1])) + targetstr = "il65_lib.SCRATCH_ZPWORD1" + if stmt.if_cond == "true": + ctx.out("\vbeq +") + ctx.out("\vjmp ({:s})".format(targetstr)) + ctx.out("+") + elif stmt.if_cond in ("not", "zero"): + ctx.out("\vbne +") + ctx.out("\vjmp ({:s})".format(targetstr)) + ctx.out("+") + elif stmt.if_cond in ("cc", "cs", "vc", "vs", "eq", "ne"): + if stmt.if_cond == "cc": + ctx.out("\vbcs +") + elif stmt.if_cond == "cs": + ctx.out("\vbcc +") + elif stmt.if_cond == "vc": + ctx.out("\vbvs +") + elif stmt.if_cond == "vs": + ctx.out("\vbvc +") + elif stmt.if_cond == "eq": + ctx.out("\vbne +") + elif stmt.if_cond == "ne": + ctx.out("\vbeq +") + ctx.out("\vjmp ({:s})".format(targetstr)) + ctx.out("+") + elif stmt.if_cond == "lt": + ctx.out("\vbcs +") + ctx.out("\vjmp ({:s})".format(targetstr)) + ctx.out("+") + elif stmt.if_cond == "gt": + ctx.out("\vbcc +") + ctx.out("\vbeq +") + ctx.out("\vjmp ({:s})".format(targetstr)) + ctx.out("+") + elif stmt.if_cond == "ge": + ctx.out("\vbcc +") + ctx.out("\vjmp ({:s})".format(targetstr)) + ctx.out("+") + elif stmt.if_cond == "le": + ctx.out("\vbeq +") + ctx.out("\vbcs ++") + ctx.out("+\t\tjmp ({:s})".format(targetstr)) + ctx.out("+") + else: + raise CodeError("invalid if status " + stmt.if_cond) + else: + if isinstance(stmt.target, LiteralValue) and type(stmt.target.value) is int: + targetstr = to_hex(stmt.target.value) + elif isinstance(stmt.target, SymbolName): + targetstr = stmt.target.name + else: + raise CodeError("invalid goto target type", stmt) + if stmt.if_cond == "true": + ctx.out("\vbne " + targetstr) + elif stmt.if_cond in ("not", "zero"): + ctx.out("\vbeq " + targetstr) + elif stmt.if_cond in ("cc", "cs", "vc", "vs", "eq", "ne"): + ctx.out("\vb{:s} {:s}".format(stmt.if_cond, targetstr)) + elif stmt.if_cond == "pos": + ctx.out("\vbpl " + targetstr) + elif stmt.if_cond == "neg": + ctx.out("\vbmi " + targetstr) + elif stmt.if_cond == "lt": + ctx.out("\vbcc " + targetstr) + elif stmt.if_cond == "gt": + ctx.out("\vbeq +") + ctx.out("\vbcs " + targetstr) + ctx.out("+") + elif stmt.if_cond == "ge": + ctx.out("\vbcs " + targetstr) + elif stmt.if_cond == "le": + ctx.out("\vbcc " + targetstr) + ctx.out("\vbeq " + targetstr) + else: + raise CodeError("invalid if status " + stmt.if_cond) + + +def _gen_goto_unconditional(ctx: Context, stmt: Goto) -> None: + # unconditional jump to + if isinstance(stmt.target, LiteralValue) and type(stmt.target.value) is int: + ctx.out("\vjmp " + to_hex(stmt.target.value)) + elif isinstance(stmt.target, SymbolName): + ctx.out("\vjmp " + stmt.target.name) + elif isinstance(stmt.target, Dereference): + # dereference is symbol, literal, or register (pair) + if isinstance(stmt.target.operand, LiteralValue): + ctx.out("\vjmp ({:s})".format(to_hex(stmt.target.operand.value))) + elif isinstance(stmt.target.operand, SymbolName): + ctx.out("\vjmp ({:s})".format(stmt.target.operand.name)) + else: + # register pair + ctx.out("\vst{:s} il65_lib.SCRATCH_ZPWORD1".format(stmt.target.operand.name[0])) + ctx.out("\vst{:s} il65_lib.SCRATCH_ZPWORD1+1".format(stmt.target.operand.name[1])) + ctx.out("\vjmp (il65_lib.SCRATCH_ZPWORD1)") + else: + raise CodeError("invalid goto target type", stmt) + + +def _gen_goto_special_if_cond(ctx: Context, stmt: Goto) -> None: + pass # @todo special if WITH conditional expression + + +def _gen_goto_cond(ctx: Context, stmt: Goto) -> None: + pass # @todo regular if WITH conditional expression def generate_subcall(ctx: Context) -> None: stmt = ctx.stmt assert isinstance(stmt, SubCall) - pass # @todo + ctx.out("\v\t\t\t; " + ctx.stmt.lineref) + ctx.out("\v; @todo sub call: {}".format(ctx.stmt.target)) + # @todo subcall diff --git a/il65/lib/il65lib.ill b/il65/lib/il65lib.ill index 242368a8a..ba0d0de2c 100644 --- a/il65/lib/il65lib.ill +++ b/il65/lib/il65lib.ill @@ -15,6 +15,7 @@ %asm { ; ---- jmp (indirect) routines for register pairs containing the indirect address +; @todo still needed?? jsr_indirect_nozpuse_AX sta jsr_indirect_tmp stx jsr_indirect_tmp+1 diff --git a/il65/oldstuff/codegen.py b/il65/oldstuff/codegen.py index 7404e42c3..0518255ff 100644 --- a/il65/oldstuff/codegen.py +++ b/il65/oldstuff/codegen.py @@ -2,105 +2,6 @@ class CodeGenerator: - def generate_call(self, stmt: CallStmt) -> None: - self.p("\t\t\t\t\t; " + stmt.lineref) - if stmt.condition: - assert stmt.is_goto - if stmt.condition.lvalue: - if stmt.condition.comparison_op: - self._generate_goto_conditional_comparison(stmt) - else: - self._generate_goto_conditional_truthvalue(stmt) - else: - self._generate_goto_conditional_if(stmt) - else: - # unconditional goto or subroutine call. - def branch_emitter(targetstr: str, is_goto: bool, target_indirect: bool) -> None: - if is_goto: - if target_indirect: - self.p("\t\tjmp ({:s})".format(targetstr)) - else: - self.p("\t\tjmp {:s}".format(targetstr)) - else: - assert not target_indirect - self.p("\t\tjsr " + targetstr) - self._generate_call_or_goto(stmt, branch_emitter) - - def _generate_goto_conditional_if(self, stmt): - # a goto with just an if-condition, no condition expression - def branch_emitter(targetstr: str, is_goto: bool, target_indirect: bool) -> None: - assert is_goto and not stmt.condition.comparison_op - ifs = stmt.condition.ifstatus - if target_indirect: - if ifs == "true": - self.p("\t\tbeq +") - self.p("\t\tjmp ({:s})".format(targetstr)) - self.p("+") - elif ifs in ("not", "zero"): - self.p("\t\tbne +") - self.p("\t\tjmp ({:s})".format(targetstr)) - self.p("+") - elif ifs in ("cc", "cs", "vc", "vs", "eq", "ne"): - if ifs == "cc": - self.p("\t\tbcs +") - elif ifs == "cs": - self.p("\t\tbcc +") - elif ifs == "vc": - self.p("\t\tbvs +") - elif ifs == "vs": - self.p("\t\tbvc +") - elif ifs == "eq": - self.p("\t\tbne +") - elif ifs == "ne": - self.p("\t\tbeq +") - self.p("\t\tjmp ({:s})".format(targetstr)) - self.p("+") - elif ifs == "lt": - self.p("\t\tbcs +") - self.p("\t\tjmp ({:s})".format(targetstr)) - self.p("+") - elif ifs == "gt": - self.p("\t\tbcc +") - self.p("\t\tbeq +") - self.p("\t\tjmp ({:s})".format(targetstr)) - self.p("+") - elif ifs == "ge": - self.p("\t\tbcc +") - self.p("\t\tjmp ({:s})".format(targetstr)) - self.p("+") - elif ifs == "le": - self.p("\t\tbeq +") - self.p("\t\tbcs ++") - self.p("+\t\tjmp ({:s})".format(targetstr)) - self.p("+") - else: - raise CodeError("invalid if status " + ifs) - else: - if ifs == "true": - self.p("\t\tbne " + targetstr) - elif ifs in ("not", "zero"): - self.p("\t\tbeq " + targetstr) - elif ifs in ("cc", "cs", "vc", "vs", "eq", "ne"): - self.p("\t\tb{:s} {:s}".format(ifs, targetstr)) - elif ifs == "pos": - self.p("\t\tbpl " + targetstr) - elif ifs == "neg": - self.p("\t\tbmi " + targetstr) - elif ifs == "lt": - self.p("\t\tbcc " + targetstr) - elif ifs == "gt": - self.p("\t\tbeq +") - self.p("\t\tbcs " + targetstr) - self.p("+") - elif ifs == "ge": - self.p("\t\tbcs " + targetstr) - elif ifs == "le": - self.p("\t\tbcc " + targetstr) - self.p("\t\tbeq " + targetstr) - else: - raise CodeError("invalid if status " + ifs) - self._generate_call_or_goto(stmt, branch_emitter) - def _generate_goto_conditional_truthvalue(self, stmt: CallStmt) -> None: # the condition is just the 'truth value' of the single value, # this is translated into assembly by comparing the argument to zero. diff --git a/il65/plyparse.py b/il65/plyparse.py index e5a100e02..7eabc62d9 100644 --- a/il65/plyparse.py +++ b/il65/plyparse.py @@ -508,7 +508,7 @@ class SymbolName(Expression): @attr.s(cmp=False) class Dereference(Expression): - # one subnode: operand (SymbolName, integer LiteralValue or Register) + # one subnode: operand (SymbolName, integer LiteralValue or Register (pair) ) datatype = attr.ib() size = attr.ib(type=int, default=None) @@ -691,6 +691,7 @@ class ExpressionWithOperator(Expression): @attr.s(cmp=False, repr=False) class Goto(AstNode): # one or two subnodes: target (SymbolName, integer LiteralValue, or Dereference) and optionally: condition (Expression) + # also the if_stmt itself can be a form of a condition (if_gt, ...) if_stmt = attr.ib(default=None) @property @@ -701,6 +702,10 @@ class Goto(AstNode): def condition(self) -> Optional[Expression]: return self.nodes[1] if len(self.nodes) == 2 else None # type: ignore + @property + def if_cond(self) -> str: + return self.if_stmt[3:] if self.if_stmt else "" + @attr.s(cmp=False, slots=True, repr=False) class CallArgument(AstNode): diff --git a/testsource/numbergame.ill b/testsource/numbergame.ill index 138abedf9..593b6243d 100644 --- a/testsource/numbergame.ill +++ b/testsource/numbergame.ill @@ -15,7 +15,7 @@ start: A = c64.VMCSB A |= 2 c64.VMCSB = A - c64.VMCSB |= 2 ; @todo when this works it replaces the three lines above + ;c64.VMCSB |= 2 ; @todo when this works it replaces the three lines above ; greeting c64scr.print_string("Enter your name: ")