first part of goto codegen

This commit is contained in:
Irmen de Jong 2018-02-16 16:15:48 +01:00
parent 191978c8fb
commit a171bb998d
6 changed files with 144 additions and 106 deletions

View File

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

View File

@ -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 <target>
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

View File

@ -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

View File

@ -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.

View File

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

View File

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