prog8/il65/emit/incrdecr.py
2018-02-21 00:50:09 +01:00

267 lines
12 KiB
Python

"""
Programming Language for 6502/6510 microprocessors, codename 'Sick'
This is the code generator for the in-place incr and decr instructions.
Incrementing or decrementing variables by a small byte value 0..255
is quite frequent and this generates assembly code tweaked for this case.
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
"""
from ..plyparse import VarDef, Register, IncrDecr, SymbolName, Dereference, LiteralValue
from ..datatypes import VarType, DataType, REGISTER_BYTES
from . import CodeError, preserving_registers, to_hex, Context, scoped_name
def generate_incrdecr(ctx: Context) -> None:
out = ctx.out
stmt = ctx.stmt
scope = ctx.scope
assert isinstance(stmt, IncrDecr)
if stmt.howmuch == 0:
return
if not 0 <= stmt.howmuch <= 255:
raise CodeError("incr/decr value must be 0..255 - other values should have been converted into an AugAssignment")
target = stmt.target # one of Register/SymbolName/Dereference, or a VarDef
if isinstance(target, SymbolName):
symdef = scope.lookup(target.name)
if isinstance(symdef, VarDef):
target = symdef # type: ignore
else:
raise CodeError("cannot incr/decr this", symdef)
howmuch_str = str(stmt.howmuch)
if isinstance(target, Register):
reg = target.name
# note: these operations below are all checked to be ok
if stmt.operator == "++":
if reg == 'A':
# a += 1..255
out("\vclc")
out("\vadc #" + howmuch_str)
elif reg in REGISTER_BYTES:
if stmt.howmuch == 1:
# x/y += 1
out("\vin{:s}".format(reg.lower()))
else:
# x/y += 2..255
with preserving_registers({'A'}, scope, out):
out("\vt{:s}a".format(reg.lower()))
out("\vclc")
out("\vadc #" + howmuch_str)
out("\vta{:s}".format(reg.lower()))
elif reg == "AX":
# AX += 1..255
out("\vclc")
out("\vadc #" + howmuch_str)
out("\vbcc +")
out("\vinx")
out("+")
elif reg == "AY":
# AY += 1..255
out("\vclc")
out("\vadc # " + howmuch_str)
out("\vbcc +")
out("\viny")
out("+")
elif reg == "XY":
if stmt.howmuch == 1:
# XY += 1
out("\vinx")
out("\vbne +")
out("\viny")
out("+")
else:
# XY += 2..255
with preserving_registers({'A'}, scope, out):
out("\vtxa")
out("\vclc")
out("\vadc #" + howmuch_str)
out("\vtax")
out("\vbcc +")
out("\viny")
out("+")
else:
raise CodeError("invalid incr register: " + reg)
else:
if reg == 'A':
# a -= 1..255
out("\vsec")
out("\vsbc #" + howmuch_str)
elif reg in REGISTER_BYTES:
if stmt.howmuch == 1:
# x/y -= 1
out("\vde{:s}".format(reg.lower()))
else:
# x/y -= 2..255
with preserving_registers({'A'}, scope, out):
out("\vt{:s}a".format(reg.lower()))
out("\vsec")
out("\vsbc #" + howmuch_str)
out("\vta{:s}".format(reg.lower()))
elif reg == "AX":
# AX -= 1..255
out("\vsec")
out("\vsbc #" + howmuch_str)
out("\vbcs +")
out("\vdex")
out("+")
elif reg == "AY":
# AY -= 1..255
out("\vsec")
out("\vsbc #" + howmuch_str)
out("\vbcs +")
out("\vdey")
out("+")
elif reg == "XY":
if stmt.howmuch == 1:
# XY -= 1
out("\vcpx #0")
out("\vbne +")
out("\vdey")
out("+\t\tdex")
else:
# XY -= 2..255
with preserving_registers({'A'}, scope, out):
out("\vtxa")
out("\vsec")
out("\vsbc #" + howmuch_str)
out("\vtax")
out("\vbcs +")
out("\vdey")
out("+")
else:
raise CodeError("invalid decr register: " + reg)
elif isinstance(target, VarDef):
if target.vartype == VarType.CONST:
raise CodeError("cannot modify a constant", target)
what_str = scoped_name(target, scope)
if target.datatype == DataType.BYTE:
if stmt.howmuch == 1:
out("\v{:s} {:s}".format("inc" if stmt.operator == "++" else "dec", what_str))
else:
with preserving_registers({'A'}, scope, out):
out("\vlda " + what_str)
if stmt.operator == "++":
out("\vclc")
out("\vadc #" + howmuch_str)
else:
out("\vsec")
out("\vsbc #" + howmuch_str)
out("\vsta " + what_str)
elif target.datatype == DataType.WORD:
if stmt.howmuch == 1:
# mem.word +=/-= 1
if stmt.operator == "++":
out("\vinc " + what_str)
out("\vbne +")
out("\vinc {:s}+1".format(what_str))
out("+")
else:
with preserving_registers({'A'}, scope, out):
out("\vlda " + what_str)
out("\vbne +")
out("\vdec {:s}+1".format(what_str))
out("+\t\tdec " + what_str)
else:
# mem.word +=/-= 2..255
if stmt.operator == "++":
with preserving_registers({'A'}, scope, out):
out("\vclc")
out("\vlda " + what_str)
out("\vadc #" + howmuch_str)
out("\vsta " + what_str)
out("\vbcc +")
out("\vinc {:s}+1".format(what_str))
out("+")
else:
with preserving_registers({'A'}, scope, out):
out("\vsec")
out("\vlda " + what_str)
out("\vsbc #" + howmuch_str)
out("\vsta " + what_str)
out("\vbcs +")
out("\vdec {:s}+1".format(what_str))
out("+")
elif target.datatype == DataType.FLOAT:
if not ctx.floats_enabled:
raise CodeError("floating point numbers not enabled via option")
if stmt.howmuch == 1.0:
# special case for +/-1
with preserving_registers({'A', 'X', 'Y'}, scope, out, loads_a_within=True):
out("\vldx #<" + what_str)
out("\vldy #>" + what_str)
if stmt.operator == "++":
out("\vjsr c64flt.float_add_one")
else:
out("\vjsr c64flt.float_sub_one")
elif stmt.howmuch != 0:
float_name = scope.define_float_constant(stmt.howmuch)
with preserving_registers({'A', 'X', 'Y'}, scope, out, loads_a_within=True):
out("\vlda #<" + float_name)
out("\vsta c64.SCRATCH_ZPWORD1")
out("\vlda #>" + float_name)
out("\vsta c64.SCRATCH_ZPWORD1+1")
out("\vldx #<" + what_str)
out("\vldy #>" + what_str)
if stmt.operator == "++":
out("\vjsr c64flt.float_add_SW1_to_XY")
else:
out("\vjsr c64flt.float_sub_SW1_from_XY")
else:
raise CodeError("cannot in/decrement memory of type " + str(target.datatype), stmt.howmuch)
elif isinstance(target, Dereference):
if isinstance(target.operand, (LiteralValue, SymbolName)):
if isinstance(target.operand, LiteralValue):
what = to_hex(target.operand.value)
else:
symdef = target.my_scope().lookup(target.operand.name)
if isinstance(symdef, VarDef) and symdef.vartype == VarType.MEMORY:
what = to_hex(symdef.value.value) # type: ignore
else:
what = target.operand.name
if stmt.howmuch == 1:
if target.datatype == DataType.FLOAT:
if not ctx.floats_enabled:
raise CodeError("floating point numbers not enabled via option")
with preserving_registers({'A', 'X', 'Y'}, scope, out, loads_a_within=True):
out("\vldx " + what)
out("\vldy {:s}+1".format(what))
if stmt.operator == "++":
out("\vjsr c64flt.float_add_one")
else:
out("\vjsr c64flt.float_sub_one")
else:
with preserving_registers({'A', 'Y'}, scope, out, loads_a_within=True):
out("\vlda " + what)
out("\vldy {:s}+1".format(what))
if target.datatype == DataType.BYTE:
out("\vclc" if stmt.operator == "++" else "\vsec")
out("\vjsr il65_lib.incrdecr_deref_byte_reg_AY")
elif target.datatype == DataType.WORD:
out("\vclc" if stmt.operator == "++" else "\vsec")
out("\vjsr il65_lib.incrdecr_deref_word_reg_AY")
else:
raise CodeError("cannot inc/decrement dereferenced literal of type " + str(target.datatype), stmt)
else:
raise CodeError("can't inc/dec this by something else as 1 right now", stmt) # XXX
elif isinstance(target.operand, Register):
assert target.operand.datatype == DataType.WORD
reg = target.operand.name
if stmt.howmuch == 1:
out("\vclc" if stmt.operator == "++" else "\vsec")
if target.datatype == DataType.BYTE:
with preserving_registers({'A', 'Y'}, scope, out, loads_a_within=True):
out("\vjsr il65_lib.incrdecr_deref_byte_reg_" + reg)
else:
with preserving_registers({'A', 'Y'}, scope, out, loads_a_within=True):
out("\vjsr il65_lib.incrdecr_deref_word_reg_" + reg)
else:
raise CodeError("can't inc/dec this by something else as 1 right now", stmt) # XXX
else:
raise TypeError("invalid dereference operand type", target)
else:
raise CodeError("cannot inc/decrement", target) # @todo support more