prog8/il65/emit/incrdecr.py

213 lines
8.7 KiB
Python
Raw Normal View History

2018-01-14 14:18:50 +00:00
"""
Programming Language for 6502/6510 microprocessors, codename 'Sick'
This is the code generator for the in-place incr and decr instructions.
2018-01-14 23:20:36 +00:00
Incrementing or decrementing variables by 1 (or another constant amount)
is quite frequent and this generates assembly code tweaked for this case.
2018-01-14 14:18:50 +00:00
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
"""
from typing import Callable
2018-01-14 23:20:36 +00:00
from ..plyparse import Scope, VarType, VarDef, Register, IncrDecr, SymbolName, Dereference, datatype_of
2018-01-14 14:18:50 +00:00
from ..datatypes import DataType, REGISTER_BYTES
2018-01-14 23:20:36 +00:00
from . import CodeError, preserving_registers
2018-01-14 14:18:50 +00:00
def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope) -> None:
2018-01-14 14:18:50 +00:00
assert isinstance(stmt.howmuch, (int, float)) and stmt.howmuch >= 0
assert stmt.operator in ("++", "--")
target = stmt.target # one of Register/SymbolName/Dereference
if isinstance(target, SymbolName):
symdef = scope[target.name]
if isinstance(symdef, VarDef):
target = symdef
else:
raise CodeError("cannot incr/decr this", symdef)
2018-01-14 14:18:50 +00:00
if stmt.howmuch > 255:
if datatype_of(target, scope) != DataType.FLOAT:
raise CodeError("only supports integer incr/decr by up to 255 for now")
howmuch_str = str(stmt.howmuch)
if isinstance(target, Register):
reg = target.name
2018-01-14 14:18:50 +00:00
# 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)
2018-01-14 14:18:50 +00:00
elif reg in REGISTER_BYTES:
if stmt.howmuch == 1:
2018-01-14 14:18:50 +00:00
# x/y += 1
out("\vin{:s}".format(reg.lower()))
2018-01-14 14:18:50 +00:00
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()))
2018-01-14 14:18:50 +00:00
elif reg == "AX":
# AX += 1..255
out("\vclc")
out("\vadc #" + howmuch_str)
out("\vbcc +")
out("\vinx")
2018-01-14 14:18:50 +00:00
out("+")
elif reg == "AY":
# AY += 1..255
out("\vclc")
out("\vadc # " + howmuch_str)
out("\vbcc +")
out("\viny")
2018-01-14 14:18:50 +00:00
out("+")
elif reg == "XY":
if stmt.howmuch == 1:
2018-01-14 14:18:50 +00:00
# XY += 1
out("\vinx")
out("\vbne +")
out("\viny")
2018-01-14 14:18:50 +00:00
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")
2018-01-14 14:18:50 +00:00
out("+")
else:
raise CodeError("invalid incr register: " + reg)
else:
if reg == 'A':
# a -= 1..255
out("\vsec")
out("\vsbc #" + howmuch_str)
2018-01-14 14:18:50 +00:00
elif reg in REGISTER_BYTES:
if stmt.howmuch == 1:
2018-01-14 14:18:50 +00:00
# x/y -= 1
out("\vde{:s}".format(reg.lower()))
2018-01-14 14:18:50 +00:00
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()))
2018-01-14 14:18:50 +00:00
elif reg == "AX":
# AX -= 1..255
out("\vsec")
out("\vsbc #" + howmuch_str)
out("\vbcs +")
out("\vdex")
2018-01-14 14:18:50 +00:00
out("+")
elif reg == "AY":
# AY -= 1..255
out("\vsec")
out("\vsbc #" + howmuch_str)
out("\vbcs +")
out("\vdey")
2018-01-14 14:18:50 +00:00
out("+")
elif reg == "XY":
if stmt.howmuch == 1:
2018-01-14 14:18:50 +00:00
# XY -= 1
out("\vcpx #0")
out("\vbne +")
out("\vdey")
2018-01-14 14:18:50 +00:00
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")
2018-01-14 14:18:50 +00:00
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 = target.name
if target.datatype == DataType.BYTE:
if stmt.howmuch == 1:
out("\v{:s} {:s}".format("inc" if stmt.operator == "++" else "dec", what_str))
2018-01-14 14:18:50 +00:00
else:
with preserving_registers({'A'}, scope, out):
out("\vlda " + what_str)
2018-01-14 14:18:50 +00:00
if stmt.operator == "++":
out("\vclc")
out("\vadc #" + howmuch_str)
2018-01-14 14:18:50 +00:00
else:
out("\vsec")
out("\vsbc #" + howmuch_str)
out("\vsta " + what_str)
elif target.datatype == DataType.WORD:
if stmt.howmuch == 1:
2018-01-14 14:18:50 +00:00
# mem.word +=/-= 1
if stmt.operator == "++":
out("\vinc " + what_str)
out("\vbne +")
out("\vinc {:s}+1".format(what_str))
2018-01-14 14:18:50 +00:00
out("+")
else:
with preserving_registers({'A'}, scope, out):
out("\vlda " + what_str)
out("\vbne +")
out("\vdec {:s}+1".format(what_str))
2018-01-14 14:18:50 +00:00
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))
2018-01-14 14:18:50 +00:00
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))
2018-01-14 14:18:50 +00:00
out("+")
elif target.datatype == DataType.FLOAT:
if stmt.howmuch == 1.0:
2018-01-14 14:18:50 +00:00
# special case for +/-1
with preserving_registers({'A', 'X', 'Y'}, scope, out, loads_a_within=True):
out("\vldx #<" + what_str)
out("\vldy #>" + what_str)
2018-01-14 14:18:50 +00:00
if stmt.operator == "++":
out("\vjsr c64flt.float_add_one")
2018-01-14 14:18:50 +00:00
else:
out("\vjsr c64flt.float_sub_one")
2018-01-14 23:20:36 +00:00
elif NOTYETIMPLEMENTED: # XXX for the float += otherfloat cases
with preserving_registers({'A', 'X', 'Y'}, scope, out, loads_a_within=True):
out("\vlda #<" + stmt.value.name)
out("\vsta c64.SCRATCH_ZPWORD1")
out("\vlda #>" + stmt.value.name)
out("\vsta c64.SCRATCH_ZPWORD1+1")
out("\vldx #<" + what_str)
out("\vldy #>" + what_str)
2018-01-14 14:18:50 +00:00
if stmt.operator == "++":
out("\vjsr c64flt.float_add_SW1_to_XY")
2018-01-14 14:18:50 +00:00
else:
out("\vjsr c64flt.float_sub_SW1_from_XY")
2018-01-14 14:18:50 +00:00
else:
raise CodeError("incr/decr missing float constant definition")
else:
raise CodeError("cannot in/decrement memory of type " + str(target.datatype), stmt.howmuch)
2018-01-14 14:18:50 +00:00
else:
raise CodeError("cannot in/decrement", target) # @todo support more such as [dereference]++