mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 17:50:35 +00:00
zero division checks and more optimizations
This commit is contained in:
parent
6573368a69
commit
31c6186245
@ -102,7 +102,7 @@ class PlyParser:
|
|||||||
if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST:
|
if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST:
|
||||||
raise ParseError("cannot modify a constant", target.sourceref)
|
raise ParseError("cannot modify a constant", target.sourceref)
|
||||||
elif isinstance(node, AugAssignment):
|
elif isinstance(node, AugAssignment):
|
||||||
# the assignment target must be a variable
|
# the assignment target must not be a constant
|
||||||
if isinstance(node.left, SymbolName):
|
if isinstance(node.left, SymbolName):
|
||||||
symdef = node.my_scope().lookup(node.left.name)
|
symdef = node.my_scope().lookup(node.left.name)
|
||||||
if isinstance(symdef, VarDef):
|
if isinstance(symdef, VarDef):
|
||||||
@ -111,6 +111,10 @@ class PlyParser:
|
|||||||
elif symdef.datatype not in {DataType.BYTE, DataType.WORD, DataType.FLOAT}:
|
elif symdef.datatype not in {DataType.BYTE, DataType.WORD, DataType.FLOAT}:
|
||||||
raise ParseError("cannot modify that datatype ({:s}) in this way"
|
raise ParseError("cannot modify that datatype ({:s}) in this way"
|
||||||
.format(symdef.datatype.name.lower()), node.sourceref)
|
.format(symdef.datatype.name.lower()), node.sourceref)
|
||||||
|
# check for divide by (constant) zero
|
||||||
|
if node.operator in ("/=", "//="):
|
||||||
|
if isinstance(node.right, LiteralValue) and node.right.value == 0:
|
||||||
|
raise ParseError("division by zero", node.right.sourceref)
|
||||||
previous_stmt = node
|
previous_stmt = node
|
||||||
|
|
||||||
def check_subroutine_arguments(self, call: SubCall, subdef: Subroutine) -> None:
|
def check_subroutine_arguments(self, call: SubCall, subdef: Subroutine) -> None:
|
||||||
|
@ -46,6 +46,9 @@ class DataType(enum.Enum):
|
|||||||
def isnumeric(self) -> bool:
|
def isnumeric(self) -> bool:
|
||||||
return self.value in (1, 2, 3)
|
return self.value in (1, 2, 3)
|
||||||
|
|
||||||
|
def isinteger(self) -> bool:
|
||||||
|
return self.value in (1, 2)
|
||||||
|
|
||||||
def isarray(self) -> bool:
|
def isarray(self) -> bool:
|
||||||
return self.value in (4, 5, 6)
|
return self.value in (4, 5, 6)
|
||||||
|
|
||||||
|
@ -6,18 +6,320 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from ..plyparse import AstNode, Scope, VarDef, Dereference, Register, TargetRegisters,\
|
from ..plyparse import Scope, Assignment, AugAssignment, Register, LiteralValue, SymbolName, VarDef
|
||||||
LiteralValue, Assignment, AugAssignment
|
from . import CodeError, preserving_registers, to_hex
|
||||||
|
from ..datatypes import REGISTER_BYTES, REGISTER_WORDS, VarType, DataType
|
||||||
|
from ..compile import Zeropage
|
||||||
|
|
||||||
|
|
||||||
def generate_assignment(out: Callable, stmt: Assignment) -> None:
|
def generate_assignment(out: Callable, stmt: Assignment, scope: Scope) -> None:
|
||||||
assert stmt.right is not None
|
pass
|
||||||
|
out("\v\t\t\t; " + stmt.lineref)
|
||||||
|
out("\v; @todo assignment")
|
||||||
|
# @todo assignment
|
||||||
|
|
||||||
|
|
||||||
|
def generate_aug_assignment(out: Callable, stmt: AugAssignment, scope: Scope) -> None:
|
||||||
|
# for instance: value += 3
|
||||||
|
# left: Register, SymbolName, or Dereference. right: Expression/LiteralValue
|
||||||
|
out("\v\t\t\t; " + stmt.lineref)
|
||||||
|
out("\v; @todo aug-assignment")
|
||||||
|
lvalue = stmt.left
|
||||||
rvalue = stmt.right
|
rvalue = stmt.right
|
||||||
if isinstance(stmt.right, LiteralValue):
|
if isinstance(lvalue, Register):
|
||||||
rvalue = stmt.right.value
|
if isinstance(rvalue, LiteralValue):
|
||||||
# @todo
|
if type(rvalue.value) is int:
|
||||||
|
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, rvalue.value, "", scope)
|
||||||
|
else:
|
||||||
|
raise CodeError("integer literal or variable required for now", rvalue) # XXX
|
||||||
|
elif isinstance(rvalue, SymbolName):
|
||||||
|
symdef = scope.lookup(rvalue.name)
|
||||||
|
if isinstance(symdef, VarDef) and symdef.datatype.isinteger():
|
||||||
|
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, 0, symdef.name, scope)
|
||||||
|
else:
|
||||||
|
raise CodeError("integer literal or variable required for now", rvalue) # XXX
|
||||||
|
elif isinstance(rvalue, Register):
|
||||||
|
_generate_aug_reg_reg(out, lvalue, stmt.operator, rvalue, scope)
|
||||||
|
else:
|
||||||
|
# @todo Register += symbolname / dereference , _generate_aug_reg_mem?
|
||||||
|
raise CodeError("invalid rvalue for augmented assignment on register", rvalue)
|
||||||
|
else:
|
||||||
|
raise CodeError("augmented assignment only implemented for registers for now", stmt.sourceref) # XXX
|
||||||
|
|
||||||
|
|
||||||
def generate_aug_assignment(out: Callable, stmt: AugAssignment) -> None:
|
def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None:
|
||||||
assert stmt.right is not None
|
r_str = rname or to_hex(rvalue)
|
||||||
pass # @todo
|
if operator == "+=":
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vclc")
|
||||||
|
out("\vadc #" + r_str)
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\vclc")
|
||||||
|
out("\vadc #" + r_str)
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\vclc")
|
||||||
|
out("\vadc #" + r_str)
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word
|
||||||
|
elif operator == "-=":
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vsec")
|
||||||
|
out("\vsbc #" + r_str)
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\vsec")
|
||||||
|
out("\vsbc #" + r_str)
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\vsec")
|
||||||
|
out("\vsbc #" + r_str)
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word
|
||||||
|
elif operator == "&=":
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vand #" + r_str)
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\vand #" + r_str)
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\vand #" + r_str)
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
|
||||||
|
elif operator == "|=":
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vora #" + r_str)
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\vora #" + r_str)
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\vora #" + r_str)
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
|
||||||
|
elif operator == "^=":
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\veor #" + r_str)
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\veor #" + r_str)
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\veor #" + r_str)
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word
|
||||||
|
elif operator == ">>=":
|
||||||
|
if rvalue.value > 0:
|
||||||
|
def shifts_A(times: int) -> None:
|
||||||
|
if times >= 8:
|
||||||
|
out("\vlda #0")
|
||||||
|
else:
|
||||||
|
for _ in range(min(8, times)):
|
||||||
|
out("\vlsr a")
|
||||||
|
if lvalue.register == "A":
|
||||||
|
shifts_A(rvalue.value)
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
shifts_A(rvalue.value)
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
shifts_A(rvalue.value)
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word
|
||||||
|
elif operator == "<<=":
|
||||||
|
if rvalue.value > 0:
|
||||||
|
def shifts_A(times: int) -> None:
|
||||||
|
if times >= 8:
|
||||||
|
out("\vlda #0")
|
||||||
|
else:
|
||||||
|
for _ in range(min(8, times)):
|
||||||
|
out("\vasl a")
|
||||||
|
if lvalue.register == "A":
|
||||||
|
shifts_A(rvalue.value)
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
shifts_A(rvalue.value)
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
shifts_A(rvalue.value)
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo <<=.word
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue: Register, scope: Scope) -> None:
|
||||||
|
if operator == "+=":
|
||||||
|
if rvalue.register not in REGISTER_BYTES:
|
||||||
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo +=.word
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
out("\vclc")
|
||||||
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\vclc")
|
||||||
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\vclc")
|
||||||
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo +=.word
|
||||||
|
elif operator == "-=":
|
||||||
|
if rvalue.register not in REGISTER_BYTES:
|
||||||
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo -=.word
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
out("\vsec")
|
||||||
|
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\vsec")
|
||||||
|
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\vsec")
|
||||||
|
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo -=.word
|
||||||
|
elif operator == "&=":
|
||||||
|
if rvalue.register not in REGISTER_BYTES:
|
||||||
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo &=.word
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo &=.word
|
||||||
|
elif operator == "|=":
|
||||||
|
if rvalue.register not in REGISTER_BYTES:
|
||||||
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo |=.word
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo |=.word
|
||||||
|
elif operator == "^=":
|
||||||
|
if rvalue.register not in REGISTER_BYTES:
|
||||||
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo ^=.word
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo ^=.word
|
||||||
|
elif operator == ">>=":
|
||||||
|
if rvalue.register not in REGISTER_BYTES:
|
||||||
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo >>=.word
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo >>=.word
|
||||||
|
elif operator == "<<=":
|
||||||
|
if rvalue.register not in REGISTER_BYTES:
|
||||||
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo <<=.word
|
||||||
|
if lvalue.register == "A":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
elif lvalue.register == "X":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtxa")
|
||||||
|
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtax")
|
||||||
|
elif lvalue.register == "Y":
|
||||||
|
out("\vst{:s} {:s}".format(rvalue.register.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
||||||
|
with preserving_registers({'A'}, scope, out):
|
||||||
|
out("\vtya")
|
||||||
|
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
||||||
|
out("\vtay")
|
||||||
|
else:
|
||||||
|
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo <<=.word
|
||||||
|
@ -211,9 +211,9 @@ class AssemblyGenerator:
|
|||||||
elif isinstance(stmt, SubCall):
|
elif isinstance(stmt, SubCall):
|
||||||
generate_subcall(out, stmt)
|
generate_subcall(out, stmt)
|
||||||
elif isinstance(stmt, Assignment):
|
elif isinstance(stmt, Assignment):
|
||||||
generate_assignment(out, stmt)
|
generate_assignment(out, stmt, scope)
|
||||||
elif isinstance(stmt, AugAssignment):
|
elif isinstance(stmt, AugAssignment):
|
||||||
generate_aug_assignment(out, stmt)
|
generate_aug_assignment(out, stmt, scope)
|
||||||
elif isinstance(stmt, Directive):
|
elif isinstance(stmt, Directive):
|
||||||
if stmt.name == "breakpoint":
|
if stmt.name == "breakpoint":
|
||||||
# put a marker in the source so that we can generate a list of breakpoints later
|
# put a marker in the source so that we can generate a list of breakpoints later
|
||||||
|
@ -1,203 +1,9 @@
|
|||||||
# old deprecated code
|
# old deprecated code, in the process of moving this to the new emit/... modules
|
||||||
|
|
||||||
class CodeGenerator:
|
class CodeGenerator:
|
||||||
BREAKPOINT_COMMENT_SIGNATURE = "~~~BREAKPOINT~~~"
|
BREAKPOINT_COMMENT_SIGNATURE = "~~~BREAKPOINT~~~"
|
||||||
BREAKPOINT_COMMENT_DETECTOR = r".(?P<address>\w+)\s+ea\s+nop\s+;\s+{:s}.*".format(BREAKPOINT_COMMENT_SIGNATURE)
|
BREAKPOINT_COMMENT_DETECTOR = r".(?P<address>\w+)\s+ea\s+nop\s+;\s+{:s}.*".format(BREAKPOINT_COMMENT_SIGNATURE)
|
||||||
|
|
||||||
def generate_incr_or_decr(self, stmt: Union[InplaceIncrStmt, InplaceDecrStmt]) -> None:
|
|
||||||
assert stmt.value.constant
|
|
||||||
assert (stmt.value.value is None and stmt.value.name) or stmt.value.value > 0
|
|
||||||
if stmt.what.datatype != DataType.FLOAT and not stmt.value.name and stmt.value.value > 0xff:
|
|
||||||
raise CodeError("only supports integer incr/decr by up to 255 for now") # XXX
|
|
||||||
is_incr = isinstance(stmt, InplaceIncrStmt)
|
|
||||||
howmuch = stmt.value.value
|
|
||||||
value_str = stmt.value.name or str(howmuch)
|
|
||||||
if isinstance(stmt.what, RegisterValue):
|
|
||||||
reg = stmt.what.register
|
|
||||||
# note: these operations below are all checked to be ok
|
|
||||||
if is_incr:
|
|
||||||
if reg == 'A':
|
|
||||||
# a += 1..255
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc #" + value_str)
|
|
||||||
elif reg in REGISTER_BYTES:
|
|
||||||
if howmuch == 1:
|
|
||||||
# x/y += 1
|
|
||||||
self.p("\t\tin{:s}".format(reg.lower()))
|
|
||||||
else:
|
|
||||||
# x/y += 2..255
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\tt{:s}a".format(reg.lower()))
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc #" + value_str)
|
|
||||||
self.p("\t\tta{:s}".format(reg.lower()))
|
|
||||||
elif reg == "AX":
|
|
||||||
# AX += 1..255
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc #" + value_str)
|
|
||||||
self.p("\t\tbcc +")
|
|
||||||
self.p("\t\tinx")
|
|
||||||
self.p("+")
|
|
||||||
elif reg == "AY":
|
|
||||||
# AY += 1..255
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc # " + value_str)
|
|
||||||
self.p("\t\tbcc +")
|
|
||||||
self.p("\t\tiny")
|
|
||||||
self.p("+")
|
|
||||||
elif reg == "XY":
|
|
||||||
if howmuch == 1:
|
|
||||||
# XY += 1
|
|
||||||
self.p("\t\tinx")
|
|
||||||
self.p("\t\tbne +")
|
|
||||||
self.p("\t\tiny")
|
|
||||||
self.p("+")
|
|
||||||
else:
|
|
||||||
# XY += 2..255
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc #" + value_str)
|
|
||||||
self.p("\t\ttax")
|
|
||||||
self.p("\t\tbcc +")
|
|
||||||
self.p("\t\tiny")
|
|
||||||
self.p("+")
|
|
||||||
else:
|
|
||||||
raise CodeError("invalid incr register: " + reg)
|
|
||||||
else:
|
|
||||||
if reg == 'A':
|
|
||||||
# a -= 1..255
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc #" + value_str)
|
|
||||||
elif reg in REGISTER_BYTES:
|
|
||||||
if howmuch == 1:
|
|
||||||
# x/y -= 1
|
|
||||||
self.p("\t\tde{:s}".format(reg.lower()))
|
|
||||||
else:
|
|
||||||
# x/y -= 2..255
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\tt{:s}a".format(reg.lower()))
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc #" + value_str)
|
|
||||||
self.p("\t\tta{:s}".format(reg.lower()))
|
|
||||||
elif reg == "AX":
|
|
||||||
# AX -= 1..255
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc #" + value_str)
|
|
||||||
self.p("\t\tbcs +")
|
|
||||||
self.p("\t\tdex")
|
|
||||||
self.p("+")
|
|
||||||
elif reg == "AY":
|
|
||||||
# AY -= 1..255
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc #" + value_str)
|
|
||||||
self.p("\t\tbcs +")
|
|
||||||
self.p("\t\tdey")
|
|
||||||
self.p("+")
|
|
||||||
elif reg == "XY":
|
|
||||||
if howmuch == 1:
|
|
||||||
# XY -= 1
|
|
||||||
self.p("\t\tcpx #0")
|
|
||||||
self.p("\t\tbne +")
|
|
||||||
self.p("\t\tdey")
|
|
||||||
self.p("+\t\tdex")
|
|
||||||
else:
|
|
||||||
# XY -= 2..255
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc #" + value_str)
|
|
||||||
self.p("\t\ttax")
|
|
||||||
self.p("\t\tbcs +")
|
|
||||||
self.p("\t\tdey")
|
|
||||||
self.p("+")
|
|
||||||
else:
|
|
||||||
raise CodeError("invalid decr register: " + reg)
|
|
||||||
elif isinstance(stmt.what, (MemMappedValue, IndirectValue)):
|
|
||||||
what = stmt.what
|
|
||||||
if isinstance(what, IndirectValue):
|
|
||||||
if isinstance(what.value, IntegerValue):
|
|
||||||
what_str = what.value.name or Parser.to_hex(what.value.value)
|
|
||||||
else:
|
|
||||||
raise CodeError("invalid incr indirect type", what.value)
|
|
||||||
else:
|
|
||||||
what_str = what.name or Parser.to_hex(what.address)
|
|
||||||
if what.datatype == DataType.BYTE:
|
|
||||||
if howmuch == 1:
|
|
||||||
self.p("\t\t{:s} {:s}".format("inc" if is_incr else "dec", what_str))
|
|
||||||
else:
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\tlda " + what_str)
|
|
||||||
if is_incr:
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc #" + value_str)
|
|
||||||
else:
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc #" + value_str)
|
|
||||||
self.p("\t\tsta " + what_str)
|
|
||||||
elif what.datatype == DataType.WORD:
|
|
||||||
if howmuch == 1:
|
|
||||||
# mem.word +=/-= 1
|
|
||||||
if is_incr:
|
|
||||||
self.p("\t\tinc " + what_str)
|
|
||||||
self.p("\t\tbne +")
|
|
||||||
self.p("\t\tinc {:s}+1".format(what_str))
|
|
||||||
self.p("+")
|
|
||||||
else:
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\tlda " + what_str)
|
|
||||||
self.p("\t\tbne +")
|
|
||||||
self.p("\t\tdec {:s}+1".format(what_str))
|
|
||||||
self.p("+\t\tdec " + what_str)
|
|
||||||
else:
|
|
||||||
# mem.word +=/-= 2..255
|
|
||||||
if is_incr:
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tlda " + what_str)
|
|
||||||
self.p("\t\tadc #" + value_str)
|
|
||||||
self.p("\t\tsta " + what_str)
|
|
||||||
self.p("\t\tbcc +")
|
|
||||||
self.p("\t\tinc {:s}+1".format(what_str))
|
|
||||||
self.p("+")
|
|
||||||
else:
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tlda " + what_str)
|
|
||||||
self.p("\t\tsbc #" + value_str)
|
|
||||||
self.p("\t\tsta " + what_str)
|
|
||||||
self.p("\t\tbcs +")
|
|
||||||
self.p("\t\tdec {:s}+1".format(what_str))
|
|
||||||
self.p("+")
|
|
||||||
elif what.datatype == DataType.FLOAT:
|
|
||||||
if howmuch == 1.0:
|
|
||||||
# special case for +/-1
|
|
||||||
with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True):
|
|
||||||
self.p("\t\tldx #<" + what_str)
|
|
||||||
self.p("\t\tldy #>" + what_str)
|
|
||||||
if is_incr:
|
|
||||||
self.p("\t\tjsr c64flt.float_add_one")
|
|
||||||
else:
|
|
||||||
self.p("\t\tjsr c64flt.float_sub_one")
|
|
||||||
elif stmt.value.name:
|
|
||||||
with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True):
|
|
||||||
self.p("\t\tlda #<" + stmt.value.name)
|
|
||||||
self.p("\t\tsta c64.SCRATCH_ZPWORD1")
|
|
||||||
self.p("\t\tlda #>" + stmt.value.name)
|
|
||||||
self.p("\t\tsta c64.SCRATCH_ZPWORD1+1")
|
|
||||||
self.p("\t\tldx #<" + what_str)
|
|
||||||
self.p("\t\tldy #>" + what_str)
|
|
||||||
if is_incr:
|
|
||||||
self.p("\t\tjsr c64flt.float_add_SW1_to_XY")
|
|
||||||
else:
|
|
||||||
self.p("\t\tjsr c64flt.float_sub_SW1_from_XY")
|
|
||||||
else:
|
|
||||||
raise CodeError("incr/decr missing float constant definition")
|
|
||||||
else:
|
|
||||||
raise CodeError("cannot in/decrement memory of type " + str(what.datatype), howmuch)
|
|
||||||
else:
|
|
||||||
raise CodeError("cannot in/decrement " + str(stmt.what))
|
|
||||||
|
|
||||||
def generate_call(self, stmt: CallStmt) -> None:
|
def generate_call(self, stmt: CallStmt) -> None:
|
||||||
self.p("\t\t\t\t\t; " + stmt.lineref)
|
self.p("\t\t\t\t\t; " + stmt.lineref)
|
||||||
if stmt.condition:
|
if stmt.condition:
|
||||||
@ -623,22 +429,7 @@ class CodeGenerator:
|
|||||||
branch_emitter(targetstr, False, False)
|
branch_emitter(targetstr, False, False)
|
||||||
generate_result_assignments()
|
generate_result_assignments()
|
||||||
|
|
||||||
def generate_augmented_assignment(self, stmt: AugmentedAssignmentStmt) -> None:
|
|
||||||
# for instance: value += 3
|
|
||||||
lvalue = stmt.leftvalues[0]
|
|
||||||
rvalue = stmt.right
|
|
||||||
self.p("\t\t\t\t\t; " + stmt.lineref)
|
|
||||||
if isinstance(lvalue, RegisterValue):
|
|
||||||
if isinstance(rvalue, IntegerValue):
|
|
||||||
self._generate_aug_reg_int(lvalue, stmt.operator, rvalue)
|
|
||||||
elif isinstance(rvalue, RegisterValue):
|
|
||||||
self._generate_aug_reg_reg(lvalue, stmt.operator, rvalue)
|
|
||||||
elif isinstance(rvalue, MemMappedValue):
|
|
||||||
self._generate_aug_reg_mem(lvalue, stmt.operator, rvalue)
|
|
||||||
else:
|
|
||||||
raise CodeError("invalid rvalue for augmented assignment on register", str(rvalue))
|
|
||||||
else:
|
|
||||||
raise CodeError("augmented assignment only implemented for registers for now", str(rvalue)) # XXX
|
|
||||||
|
|
||||||
def _generate_aug_reg_mem(self, lvalue: RegisterValue, operator: str, rvalue: MemMappedValue) -> None:
|
def _generate_aug_reg_mem(self, lvalue: RegisterValue, operator: str, rvalue: MemMappedValue) -> None:
|
||||||
r_str = rvalue.name or Parser.to_hex(rvalue.address)
|
r_str = rvalue.name or Parser.to_hex(rvalue.address)
|
||||||
@ -760,281 +551,7 @@ class CodeGenerator:
|
|||||||
else:
|
else:
|
||||||
raise CodeError("unsupported lvalue register for shift", str(lvalue)) # @todo >>=.word
|
raise CodeError("unsupported lvalue register for shift", str(lvalue)) # @todo >>=.word
|
||||||
|
|
||||||
def _generate_aug_reg_int(self, lvalue: RegisterValue, operator: str, rvalue: IntegerValue) -> None:
|
|
||||||
r_str = rvalue.name or Parser.to_hex(rvalue.value)
|
|
||||||
if operator == "+=":
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc #" + r_str)
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc #" + r_str)
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc #" + r_str)
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word
|
|
||||||
elif operator == "-=":
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc #" + r_str)
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc #" + r_str)
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc #" + r_str)
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word
|
|
||||||
elif operator == "&=":
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tand #" + r_str)
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tand #" + r_str)
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\tand #" + r_str)
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
|
|
||||||
elif operator == "|=":
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tora #" + r_str)
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tora #" + r_str)
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\tora #" + r_str)
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
|
|
||||||
elif operator == "^=":
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\teor #" + r_str)
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\teor #" + r_str)
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\teor #" + r_str)
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word
|
|
||||||
elif operator == ">>=":
|
|
||||||
if rvalue.value > 0:
|
|
||||||
def shifts_A(times: int) -> None:
|
|
||||||
if times >= 8:
|
|
||||||
self.p("\t\tlda #0")
|
|
||||||
else:
|
|
||||||
for _ in range(min(8, times)):
|
|
||||||
self.p("\t\tlsr a")
|
|
||||||
if lvalue.register == "A":
|
|
||||||
shifts_A(rvalue.value)
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
shifts_A(rvalue.value)
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
shifts_A(rvalue.value)
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word
|
|
||||||
elif operator == "<<=":
|
|
||||||
if rvalue.value > 0:
|
|
||||||
def shifts_A(times: int) -> None:
|
|
||||||
if times >= 8:
|
|
||||||
self.p("\t\tlda #0")
|
|
||||||
else:
|
|
||||||
for _ in range(min(8, times)):
|
|
||||||
self.p("\t\tasl a")
|
|
||||||
if lvalue.register == "A":
|
|
||||||
shifts_A(rvalue.value)
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
shifts_A(rvalue.value)
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
shifts_A(rvalue.value)
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo <<=.word
|
|
||||||
|
|
||||||
def _generate_aug_reg_reg(self, lvalue: RegisterValue, operator: str, rvalue: RegisterValue) -> None:
|
|
||||||
if operator == "+=":
|
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo +=.word
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\tclc")
|
|
||||||
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo +=.word
|
|
||||||
elif operator == "-=":
|
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo -=.word
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\tsec")
|
|
||||||
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo -=.word
|
|
||||||
elif operator == "&=":
|
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo &=.word
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo &=.word
|
|
||||||
elif operator == "|=":
|
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo |=.word
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo |=.word
|
|
||||||
elif operator == "^=":
|
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo ^=.word
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo ^=.word
|
|
||||||
elif operator == ">>=":
|
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo >>=.word
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo >>=.word
|
|
||||||
elif operator == "<<=":
|
|
||||||
if rvalue.register not in REGISTER_BYTES:
|
|
||||||
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo <<=.word
|
|
||||||
if lvalue.register == "A":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
elif lvalue.register == "X":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttxa")
|
|
||||||
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttax")
|
|
||||||
elif lvalue.register == "Y":
|
|
||||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
|
||||||
with self.preserving_registers({'A'}):
|
|
||||||
self.p("\t\ttya")
|
|
||||||
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
|
||||||
self.p("\t\ttay")
|
|
||||||
else:
|
|
||||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo <<=.word
|
|
||||||
|
|
||||||
def generate_assignment(self, stmt: AssignmentStmt) -> None:
|
def generate_assignment(self, stmt: AssignmentStmt) -> None:
|
||||||
def unwrap_indirect(iv: IndirectValue) -> MemMappedValue:
|
def unwrap_indirect(iv: IndirectValue) -> MemMappedValue:
|
||||||
@ -1256,58 +773,6 @@ class CodeGenerator:
|
|||||||
else:
|
else:
|
||||||
raise CodeError("invalid register " + lv.register)
|
raise CodeError("invalid register " + lv.register)
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def preserving_registers(self, registers: Set[str], loads_a_within: bool=False, always_preserve: bool=False):
|
|
||||||
# this sometimes clobbers a ZP scratch register and is therefore NOT safe to use in interrupts
|
|
||||||
# see http://6502.org/tutorials/register_preservation.html
|
|
||||||
if not self.cur_block.preserve_registers and not always_preserve:
|
|
||||||
yield
|
|
||||||
return
|
|
||||||
if registers == {'A'}:
|
|
||||||
self.p("\t\tpha")
|
|
||||||
yield
|
|
||||||
self.p("\t\tpla")
|
|
||||||
elif registers:
|
|
||||||
if not loads_a_within:
|
|
||||||
self.p("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2))
|
|
||||||
if 'A' in registers:
|
|
||||||
self.p("\t\tpha")
|
|
||||||
if 'X' in registers:
|
|
||||||
self.p("\t\ttxa\n\t\tpha")
|
|
||||||
if 'Y' in registers:
|
|
||||||
self.p("\t\ttya\n\t\tpha")
|
|
||||||
if not loads_a_within:
|
|
||||||
self.p("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2))
|
|
||||||
yield
|
|
||||||
if 'X' in registers and 'Y' in registers:
|
|
||||||
if 'A' not in registers:
|
|
||||||
self.p("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2))
|
|
||||||
self.p("\t\tpla\n\t\ttay")
|
|
||||||
self.p("\t\tpla\n\t\ttax")
|
|
||||||
self.p("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2))
|
|
||||||
else:
|
|
||||||
self.p("\t\tpla\n\t\ttay")
|
|
||||||
self.p("\t\tpla\n\t\ttax")
|
|
||||||
else:
|
|
||||||
if 'Y' in registers:
|
|
||||||
if 'A' not in registers:
|
|
||||||
self.p("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2))
|
|
||||||
self.p("\t\tpla\n\t\ttay")
|
|
||||||
self.p("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2))
|
|
||||||
else:
|
|
||||||
self.p("\t\tpla\n\t\ttay")
|
|
||||||
if 'X' in registers:
|
|
||||||
if 'A' not in registers:
|
|
||||||
self.p("\t\tsta ${:02x}".format(Zeropage.SCRATCH_B2))
|
|
||||||
self.p("\t\tpla\n\t\ttax")
|
|
||||||
self.p("\t\tlda ${:02x}".format(Zeropage.SCRATCH_B2))
|
|
||||||
else:
|
|
||||||
self.p("\t\tpla\n\t\ttax")
|
|
||||||
if 'A' in registers:
|
|
||||||
self.p("\t\tpla")
|
|
||||||
else:
|
|
||||||
yield
|
|
||||||
|
|
||||||
def generate_assign_integer_to_mem(self, lv: MemMappedValue, rvalue: IntegerValue) -> None:
|
def generate_assign_integer_to_mem(self, lv: MemMappedValue, rvalue: IntegerValue) -> None:
|
||||||
if lv.name:
|
if lv.name:
|
||||||
symblock, sym = self.cur_block.lookup(lv.name)
|
symblock, sym = self.cur_block.lookup(lv.name)
|
||||||
|
@ -43,15 +43,26 @@ class Optimizer:
|
|||||||
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
|
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
|
||||||
if isinstance(assignment, AugAssignment):
|
if isinstance(assignment, AugAssignment):
|
||||||
if isinstance(assignment.right, LiteralValue) and isinstance(assignment.right.value, (int, float)):
|
if isinstance(assignment.right, LiteralValue) and isinstance(assignment.right.value, (int, float)):
|
||||||
if assignment.right.value == 0 and assignment.operator in ("+=", "-=", "|=", "<<=", ">>=", "^="):
|
if assignment.right.value == 0:
|
||||||
|
if assignment.operator in ("+=", "-=", "|=", "<<=", ">>=", "^="):
|
||||||
self.num_warnings += 1
|
self.num_warnings += 1
|
||||||
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
|
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
|
||||||
assignment.my_scope().remove_node(assignment)
|
assignment.my_scope().remove_node(assignment)
|
||||||
|
elif assignment.operator == "*=":
|
||||||
|
self.num_warnings += 1
|
||||||
|
print_warning("{}: statement replaced by = 0".format(assignment.sourceref))
|
||||||
|
new_assignment = self._make_new_assignment(assignment, 0)
|
||||||
|
assignment.my_scope().replace_node(assignment, new_assignment)
|
||||||
|
elif assignment.operator == "**=":
|
||||||
|
self.num_warnings += 1
|
||||||
|
print_warning("{}: statement replaced by = 1".format(assignment.sourceref))
|
||||||
|
new_assignment = self._make_new_assignment(assignment, 1)
|
||||||
|
assignment.my_scope().replace_node(assignment, new_assignment)
|
||||||
if assignment.right.value >= 8 and assignment.operator in ("<<=", ">>="):
|
if assignment.right.value >= 8 and assignment.operator in ("<<=", ">>="):
|
||||||
print("{}: shifting result is always zero".format(assignment.sourceref))
|
print("{}: shifting result is always zero".format(assignment.sourceref))
|
||||||
new_stmt = Assignment(sourceref=assignment.sourceref)
|
new_stmt = Assignment(sourceref=assignment.sourceref)
|
||||||
new_stmt.nodes.append(AssignmentTargets(nodes=[assignment.left], sourceref=assignment.sourceref))
|
new_stmt.nodes.append(AssignmentTargets(nodes=[assignment.left], sourceref=assignment.sourceref))
|
||||||
new_stmt.nodes.append(0)
|
new_stmt.nodes.append(0) # XXX literalvalue?
|
||||||
assignment.my_scope().replace_node(assignment, new_stmt)
|
assignment.my_scope().replace_node(assignment, new_stmt)
|
||||||
if assignment.operator in ("+=", "-=") and 0 < assignment.right.value < 256:
|
if assignment.operator in ("+=", "-=") and 0 < assignment.right.value < 256:
|
||||||
howmuch = assignment.right
|
howmuch = assignment.right
|
||||||
@ -62,6 +73,22 @@ class Optimizer:
|
|||||||
howmuch=howmuch.value, sourceref=assignment.sourceref)
|
howmuch=howmuch.value, sourceref=assignment.sourceref)
|
||||||
new_stmt.target = assignment.left
|
new_stmt.target = assignment.left
|
||||||
assignment.my_scope().replace_node(assignment, new_stmt)
|
assignment.my_scope().replace_node(assignment, new_stmt)
|
||||||
|
if assignment.operator in ("/=", "//=", "*=") and assignment.right.value == 1:
|
||||||
|
self.num_warnings += 1
|
||||||
|
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
|
||||||
|
assignment.my_scope().remove_node(assignment)
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
|
def _make_new_assignment(self, old_aug_assignment: AugAssignment, constantvalue: int) -> Assignment:
|
||||||
|
new_assignment = Assignment(sourceref=old_aug_assignment.sourceref)
|
||||||
|
new_assignment.parent = old_aug_assignment.parent
|
||||||
|
left = AssignmentTargets(nodes=[old_aug_assignment.left], sourceref=old_aug_assignment.sourceref)
|
||||||
|
left.parent = new_assignment
|
||||||
|
new_assignment.nodes.append(left)
|
||||||
|
value = LiteralValue(value=constantvalue, sourceref=old_aug_assignment.sourceref)
|
||||||
|
value.parent = new_assignment
|
||||||
|
new_assignment.nodes.append(value)
|
||||||
|
return new_assignment
|
||||||
|
|
||||||
def combine_assignments_into_multi(self) -> None:
|
def combine_assignments_into_multi(self) -> None:
|
||||||
# fold multiple consecutive assignments with the same rvalue into one multi-assignment
|
# fold multiple consecutive assignments with the same rvalue into one multi-assignment
|
||||||
|
@ -86,7 +86,7 @@ t_BITOR = r"\|"
|
|||||||
t_BITXOR = r"\^"
|
t_BITXOR = r"\^"
|
||||||
t_BITINVERT = r"~"
|
t_BITINVERT = r"~"
|
||||||
t_IS = r"="
|
t_IS = r"="
|
||||||
t_AUGASSIGN = r"\+=|-=|/=|\*=|<<=|>>=|&=|\|=|\^="
|
t_AUGASSIGN = r"\+=|-=|/=|//=|\*=|\*\*=|<<=|>>=|&=|\|=|\^="
|
||||||
t_DECR = r"--"
|
t_DECR = r"--"
|
||||||
t_INCR = r"\+\+"
|
t_INCR = r"\+\+"
|
||||||
t_EQUALS = r"=="
|
t_EQUALS = r"=="
|
||||||
|
@ -482,6 +482,8 @@ class Expression(AstNode):
|
|||||||
estr = "{} {} {}".format(repr(self.left.value), self.operator, repr(self.right.value))
|
estr = "{} {} {}".format(repr(self.left.value), self.operator, repr(self.right.value))
|
||||||
try:
|
try:
|
||||||
return LiteralValue(value=eval(estr, {}, {}), sourceref=sourceref) # type: ignore # safe because of checks above
|
return LiteralValue(value=eval(estr, {}, {}), sourceref=sourceref) # type: ignore # safe because of checks above
|
||||||
|
except ZeroDivisionError:
|
||||||
|
raise ParseError("division by zero", sourceref)
|
||||||
except Exception as x:
|
except Exception as x:
|
||||||
raise ExpressionEvaluationError("expression error: " + str(x), self.sourceref) from None
|
raise ExpressionEvaluationError("expression error: " + str(x), self.sourceref) from None
|
||||||
|
|
||||||
|
12
todo.ill
12
todo.ill
@ -38,8 +38,8 @@ start:
|
|||||||
v3t++
|
v3t++
|
||||||
v3t+=1
|
v3t+=1
|
||||||
v3t+=1 ; @todo? (optimize) join with previous
|
v3t+=1 ; @todo? (optimize) join with previous
|
||||||
v3t+=0
|
v3t+=0 ; is removed.
|
||||||
v3t+=1
|
v3t+=1 ; @todo? (optimize) join with previous
|
||||||
v3t+=1 ; @todo? (optimize) join with previous
|
v3t+=1 ; @todo? (optimize) join with previous
|
||||||
v3t=2.23424 ; @todo store as constant float with generated name, replace value node
|
v3t=2.23424 ; @todo store as constant float with generated name, replace value node
|
||||||
v3t=2.23411 ; @todo store as constant float with generated name, replace value node
|
v3t=2.23411 ; @todo store as constant float with generated name, replace value node
|
||||||
@ -49,7 +49,13 @@ start:
|
|||||||
; v3t+=2.23411 ; @todo store as constant float with generated name, replace value node
|
; v3t+=2.23411 ; @todo store as constant float with generated name, replace value node
|
||||||
; v3t+=2.23411 ; @todo store as constant float with generated name, replace value node
|
; v3t+=2.23411 ; @todo store as constant float with generated name, replace value node
|
||||||
v3t=2.23424 * v3t ; @todo store as constant float with generated name, replace value node
|
v3t=2.23424 * v3t ; @todo store as constant float with generated name, replace value node
|
||||||
v3t*=2.23424 * v3t ; @todo store as constant float with generated name, replace value node
|
XY*=2
|
||||||
|
XY*=3
|
||||||
|
XY=XY/0 ; @todo zerodiv (during expression to code generation)
|
||||||
|
XY=XY//0 ; @todo zerodiv (during expression to code generation)
|
||||||
|
XY*=2.23424 ; @todo store as constant float with generated name, replace value node
|
||||||
|
XY*=2.23424 * v3t ; @todo store as constant float with generated name, replace value node
|
||||||
|
;v3t*=2.23424 * v3t ; @todo store as constant float with generated name, replace value node
|
||||||
; A++
|
; A++
|
||||||
; X--
|
; X--
|
||||||
; A+=1
|
; A+=1
|
||||||
|
Loading…
Reference in New Issue
Block a user