mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +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:
|
||||
raise ParseError("cannot modify a constant", target.sourceref)
|
||||
elif isinstance(node, AugAssignment):
|
||||
# the assignment target must be a variable
|
||||
# the assignment target must not be a constant
|
||||
if isinstance(node.left, SymbolName):
|
||||
symdef = node.my_scope().lookup(node.left.name)
|
||||
if isinstance(symdef, VarDef):
|
||||
@ -111,6 +111,10 @@ class PlyParser:
|
||||
elif symdef.datatype not in {DataType.BYTE, DataType.WORD, DataType.FLOAT}:
|
||||
raise ParseError("cannot modify that datatype ({:s}) in this way"
|
||||
.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
|
||||
|
||||
def check_subroutine_arguments(self, call: SubCall, subdef: Subroutine) -> None:
|
||||
|
@ -46,6 +46,9 @@ class DataType(enum.Enum):
|
||||
def isnumeric(self) -> bool:
|
||||
return self.value in (1, 2, 3)
|
||||
|
||||
def isinteger(self) -> bool:
|
||||
return self.value in (1, 2)
|
||||
|
||||
def isarray(self) -> bool:
|
||||
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 ..plyparse import AstNode, Scope, VarDef, Dereference, Register, TargetRegisters,\
|
||||
LiteralValue, Assignment, AugAssignment
|
||||
from ..plyparse import Scope, Assignment, AugAssignment, Register, LiteralValue, SymbolName, VarDef
|
||||
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:
|
||||
assert stmt.right is not None
|
||||
def generate_assignment(out: Callable, stmt: Assignment, scope: Scope) -> 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
|
||||
if isinstance(stmt.right, LiteralValue):
|
||||
rvalue = stmt.right.value
|
||||
# @todo
|
||||
if isinstance(lvalue, Register):
|
||||
if isinstance(rvalue, LiteralValue):
|
||||
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:
|
||||
assert stmt.right is not None
|
||||
pass # @todo
|
||||
def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None:
|
||||
r_str = rname or to_hex(rvalue)
|
||||
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):
|
||||
generate_subcall(out, stmt)
|
||||
elif isinstance(stmt, Assignment):
|
||||
generate_assignment(out, stmt)
|
||||
generate_assignment(out, stmt, scope)
|
||||
elif isinstance(stmt, AugAssignment):
|
||||
generate_aug_assignment(out, stmt)
|
||||
generate_aug_assignment(out, stmt, scope)
|
||||
elif isinstance(stmt, Directive):
|
||||
if stmt.name == "breakpoint":
|
||||
# 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:
|
||||
BREAKPOINT_COMMENT_SIGNATURE = "~~~BREAKPOINT~~~"
|
||||
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:
|
||||
self.p("\t\t\t\t\t; " + stmt.lineref)
|
||||
if stmt.condition:
|
||||
@ -623,22 +429,7 @@ class CodeGenerator:
|
||||
branch_emitter(targetstr, False, False)
|
||||
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:
|
||||
r_str = rvalue.name or Parser.to_hex(rvalue.address)
|
||||
@ -760,281 +551,7 @@ class CodeGenerator:
|
||||
else:
|
||||
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 unwrap_indirect(iv: IndirectValue) -> MemMappedValue:
|
||||
@ -1256,58 +773,6 @@ class CodeGenerator:
|
||||
else:
|
||||
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:
|
||||
if 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))
|
||||
if isinstance(assignment, AugAssignment):
|
||||
if isinstance(assignment.right, LiteralValue) and isinstance(assignment.right.value, (int, float)):
|
||||
if assignment.right.value == 0 and assignment.operator in ("+=", "-=", "|=", "<<=", ">>=", "^="):
|
||||
self.num_warnings += 1
|
||||
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
|
||||
assignment.my_scope().remove_node(assignment)
|
||||
if assignment.right.value == 0:
|
||||
if assignment.operator in ("+=", "-=", "|=", "<<=", ">>=", "^="):
|
||||
self.num_warnings += 1
|
||||
print_warning("{}: removed statement that has no effect".format(assignment.sourceref))
|
||||
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 ("<<=", ">>="):
|
||||
print("{}: shifting result is always zero".format(assignment.sourceref))
|
||||
new_stmt = Assignment(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)
|
||||
if assignment.operator in ("+=", "-=") and 0 < assignment.right.value < 256:
|
||||
howmuch = assignment.right
|
||||
@ -62,6 +73,22 @@ class Optimizer:
|
||||
howmuch=howmuch.value, sourceref=assignment.sourceref)
|
||||
new_stmt.target = assignment.left
|
||||
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:
|
||||
# fold multiple consecutive assignments with the same rvalue into one multi-assignment
|
||||
|
@ -86,7 +86,7 @@ t_BITOR = r"\|"
|
||||
t_BITXOR = r"\^"
|
||||
t_BITINVERT = r"~"
|
||||
t_IS = r"="
|
||||
t_AUGASSIGN = r"\+=|-=|/=|\*=|<<=|>>=|&=|\|=|\^="
|
||||
t_AUGASSIGN = r"\+=|-=|/=|//=|\*=|\*\*=|<<=|>>=|&=|\|=|\^="
|
||||
t_DECR = r"--"
|
||||
t_INCR = r"\+\+"
|
||||
t_EQUALS = r"=="
|
||||
|
@ -482,6 +482,8 @@ class Expression(AstNode):
|
||||
estr = "{} {} {}".format(repr(self.left.value), self.operator, repr(self.right.value))
|
||||
try:
|
||||
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:
|
||||
raise ExpressionEvaluationError("expression error: " + str(x), self.sourceref) from None
|
||||
|
||||
|
12
todo.ill
12
todo.ill
@ -38,8 +38,8 @@ start:
|
||||
v3t++
|
||||
v3t+=1
|
||||
v3t+=1 ; @todo? (optimize) join with previous
|
||||
v3t+=0
|
||||
v3t+=1
|
||||
v3t+=0 ; is removed.
|
||||
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.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.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++
|
||||
; X--
|
||||
; A+=1
|
||||
|
Loading…
Reference in New Issue
Block a user