2018-01-14 14:18:50 +00:00
|
|
|
"""
|
|
|
|
Programming Language for 6502/6510 microprocessors, codename 'Sick'
|
|
|
|
This is the code generator for assignment statements.
|
|
|
|
|
|
|
|
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
|
|
"""
|
|
|
|
|
|
|
|
from typing import Callable
|
2018-02-04 21:00:08 +00:00
|
|
|
from ..plyparse import Scope, Assignment, AugAssignment, Register, LiteralValue, SymbolName, VarDef, Dereference
|
2018-02-03 00:53:07 +00:00
|
|
|
from . import CodeError, preserving_registers, to_hex, Context
|
2018-02-04 21:00:08 +00:00
|
|
|
from ..datatypes import REGISTER_BYTES, VarType, DataType
|
2018-01-23 23:41:50 +00:00
|
|
|
from ..compile import Zeropage
|
2018-01-14 14:18:50 +00:00
|
|
|
|
|
|
|
|
2018-02-03 00:53:07 +00:00
|
|
|
def generate_assignment(ctx: Context) -> None:
|
|
|
|
assert isinstance(ctx.stmt, Assignment)
|
2018-02-08 20:56:43 +00:00
|
|
|
assert not isinstance(ctx.stmt.right, Assignment), "assignment should have been flattened"
|
2018-02-03 00:53:07 +00:00
|
|
|
ctx.out("\v\t\t\t; " + ctx.stmt.lineref)
|
2018-02-18 17:19:51 +00:00
|
|
|
ctx.out("\v; @todo assignment: {} = {}".format(ctx.stmt.left, ctx.stmt.right))
|
2018-01-23 23:41:50 +00:00
|
|
|
# @todo assignment
|
|
|
|
|
|
|
|
|
2018-02-03 00:53:07 +00:00
|
|
|
def generate_aug_assignment(ctx: Context) -> None:
|
2018-02-20 23:50:09 +00:00
|
|
|
# for instance: value += 33
|
|
|
|
# (note that with += and -=, values 0..255 usually occur as the more efficient incrdecr statements instead)
|
2018-01-23 23:41:50 +00:00
|
|
|
# left: Register, SymbolName, or Dereference. right: Expression/LiteralValue
|
2018-02-03 00:53:07 +00:00
|
|
|
out = ctx.out
|
|
|
|
stmt = ctx.stmt
|
|
|
|
assert isinstance(stmt, AugAssignment)
|
2018-01-23 23:41:50 +00:00
|
|
|
out("\v\t\t\t; " + stmt.lineref)
|
|
|
|
lvalue = stmt.left
|
2018-01-14 14:18:50 +00:00
|
|
|
rvalue = stmt.right
|
2018-01-23 23:41:50 +00:00
|
|
|
if isinstance(lvalue, Register):
|
|
|
|
if isinstance(rvalue, LiteralValue):
|
|
|
|
if type(rvalue.value) is int:
|
2018-02-22 00:21:29 +00:00
|
|
|
assert rvalue.value >= 0, "augassign value can't be < 0"
|
|
|
|
_generate_aug_reg_int(out, lvalue, stmt.operator, rvalue.value, "", ctx.scope)
|
2018-01-23 23:41:50 +00:00
|
|
|
else:
|
2018-01-27 00:07:00 +00:00
|
|
|
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX
|
2018-01-23 23:41:50 +00:00
|
|
|
elif isinstance(rvalue, SymbolName):
|
2018-02-03 00:53:07 +00:00
|
|
|
symdef = ctx.scope.lookup(rvalue.name)
|
2018-02-04 21:00:08 +00:00
|
|
|
if isinstance(symdef, VarDef):
|
|
|
|
if symdef.vartype == VarType.CONST:
|
2018-02-22 00:21:29 +00:00
|
|
|
if symdef.datatype.isinteger():
|
|
|
|
assert symdef.value.const_value() >= 0, "augassign value can't be <0" # type: ignore
|
2018-02-04 21:00:08 +00:00
|
|
|
_generate_aug_reg_int(out, lvalue, stmt.operator, symdef.value.const_value(), "", ctx.scope) # type: ignore
|
|
|
|
else:
|
2018-02-22 00:21:29 +00:00
|
|
|
raise CodeError("aug. assignment value must be integer", rvalue)
|
2018-02-04 21:00:08 +00:00
|
|
|
elif symdef.datatype == DataType.BYTE:
|
|
|
|
_generate_aug_reg_int(out, lvalue, stmt.operator, 0, symdef.name, ctx.scope)
|
2018-01-28 20:58:16 +00:00
|
|
|
else:
|
2018-02-04 21:00:08 +00:00
|
|
|
raise CodeError("variable must be of type byte for now", rvalue) # XXX
|
2018-01-23 23:41:50 +00:00
|
|
|
else:
|
2018-02-04 21:00:08 +00:00
|
|
|
raise CodeError("can only use variable name as symbol for aug assign rvalue", rvalue)
|
2018-01-23 23:41:50 +00:00
|
|
|
elif isinstance(rvalue, Register):
|
2018-02-04 21:00:08 +00:00
|
|
|
if lvalue.datatype == DataType.BYTE and rvalue.datatype == DataType.WORD:
|
|
|
|
raise CodeError("cannot assign a combined 16-bit register to a single 8-bit register", rvalue)
|
2018-02-03 00:53:07 +00:00
|
|
|
_generate_aug_reg_reg(out, lvalue, stmt.operator, rvalue, ctx.scope)
|
2018-02-04 21:00:08 +00:00
|
|
|
elif isinstance(rvalue, Dereference):
|
2018-02-08 22:35:50 +00:00
|
|
|
print("warning: {}: using indirect/dereferece is very costly".format(rvalue.sourceref))
|
|
|
|
if rvalue.datatype != DataType.BYTE:
|
2018-02-22 00:21:29 +00:00
|
|
|
raise CodeError("aug. assignment value must be a byte for now", rvalue)
|
2018-02-04 21:00:08 +00:00
|
|
|
if isinstance(rvalue.operand, (LiteralValue, SymbolName)):
|
|
|
|
if isinstance(rvalue.operand, LiteralValue):
|
|
|
|
what = to_hex(rvalue.operand.value)
|
|
|
|
else:
|
|
|
|
symdef = rvalue.my_scope().lookup(rvalue.operand.name)
|
|
|
|
if isinstance(symdef, VarDef) and symdef.vartype == VarType.MEMORY:
|
|
|
|
what = to_hex(symdef.value.value) # type: ignore
|
|
|
|
else:
|
|
|
|
what = rvalue.operand.name
|
|
|
|
out("\vpha\n\vtya\n\vpha") # save A, Y on stack
|
|
|
|
out("\vlda " + what)
|
|
|
|
out("\vsta il65_lib.SCRATCH_ZPWORD1")
|
|
|
|
out("\vlda {:s}+1".format(what))
|
|
|
|
out("\vsta il65_lib.SCRATCH_ZPWORD1+1")
|
|
|
|
out("\vldy #0")
|
|
|
|
out("\vlda (il65_lib.SCRATCH_ZPWORD1), y")
|
|
|
|
a_reg = Register(name="A", sourceref=stmt.sourceref) # type: ignore
|
2018-02-08 22:35:50 +00:00
|
|
|
if 'A' in lvalue.name:
|
|
|
|
raise CodeError("can't yet use register A in this aug assign lhs", lvalue.sourceref) # @todo
|
2018-02-04 21:00:08 +00:00
|
|
|
_generate_aug_reg_reg(out, lvalue, stmt.operator, a_reg, ctx.scope)
|
2018-02-08 22:35:50 +00:00
|
|
|
if lvalue.name in REGISTER_BYTES:
|
|
|
|
out("\vst{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower()))
|
|
|
|
else:
|
|
|
|
out("\vst{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name[0].lower()))
|
|
|
|
out("\vst{:s} il65_lib.SCRATCH_ZP2".format(lvalue.name[1].lower()))
|
2018-02-04 21:00:08 +00:00
|
|
|
out("\vpla\n\vtay\n\vpla") # restore A, Y from stack
|
2018-02-08 22:35:50 +00:00
|
|
|
if lvalue.name in REGISTER_BYTES:
|
|
|
|
out("\vld{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower()))
|
|
|
|
else:
|
|
|
|
out("\vld{:s} il65_lib.SCRATCH_ZP2".format(lvalue.name[0].lower()))
|
|
|
|
out("\vld{:s} il65_lib.SCRATCH_ZP2".format(lvalue.name[1].lower()))
|
2018-02-04 21:00:08 +00:00
|
|
|
elif isinstance(rvalue.operand, Register):
|
|
|
|
assert rvalue.operand.datatype == DataType.WORD
|
|
|
|
reg = rvalue.operand.name
|
|
|
|
out("\vst{:s} il65_lib.SCRATCH_ZPWORD1".format(reg[0].lower()))
|
|
|
|
out("\vst{:s} il65_lib.SCRATCH_ZPWORD1+1".format(reg[1].lower()))
|
|
|
|
out("\vpha\n\vtya\n\vpha") # save A, Y on stack
|
|
|
|
out("\vldy #0")
|
|
|
|
out("\vlda (il65_lib.SCRATCH_ZPWORD1), y")
|
|
|
|
a_reg = Register(name="A", sourceref=stmt.sourceref) # type: ignore
|
2018-02-08 22:35:50 +00:00
|
|
|
if 'A' in lvalue.name:
|
|
|
|
raise CodeError("can't yet use register A in this aug assign lhs", lvalue.sourceref) # @todo
|
2018-02-04 21:00:08 +00:00
|
|
|
_generate_aug_reg_reg(out, lvalue, stmt.operator, a_reg, ctx.scope)
|
|
|
|
if lvalue.name != 'X':
|
2018-02-08 22:35:50 +00:00
|
|
|
if lvalue.name in REGISTER_BYTES:
|
|
|
|
out("\vst{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower()))
|
|
|
|
else:
|
|
|
|
out("\vst{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name[0].lower()))
|
|
|
|
out("\vst{:s} il65_lib.SCRATCH_ZP2".format(lvalue.name[1].lower()))
|
2018-02-04 21:00:08 +00:00
|
|
|
out("\vpla\n\vtay\n\vpla") # restore A, Y from stack
|
|
|
|
if lvalue.name != 'X':
|
2018-02-08 22:35:50 +00:00
|
|
|
if lvalue.name in REGISTER_BYTES:
|
|
|
|
out("\vld{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower()))
|
|
|
|
else:
|
|
|
|
out("\vld{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name[0].lower()))
|
|
|
|
out("\vld{:s} il65_lib.SCRATCH_ZP2".format(lvalue.name[1].lower()))
|
2018-02-04 21:00:08 +00:00
|
|
|
else:
|
|
|
|
raise CodeError("invalid dereference operand type", rvalue)
|
2018-01-23 23:41:50 +00:00
|
|
|
else:
|
2018-02-04 21:00:08 +00:00
|
|
|
raise CodeError("invalid rvalue type", rvalue)
|
2018-02-16 15:15:48 +00:00
|
|
|
elif isinstance(lvalue, SymbolName):
|
|
|
|
raise NotImplementedError("symbolname augassign", lvalue) # XXX
|
2018-01-23 23:41:50 +00:00
|
|
|
else:
|
2018-02-16 15:15:48 +00:00
|
|
|
raise CodeError("aug. assignment only implemented for registers and symbols for now", lvalue, stmt.sourceref) # XXX
|
2018-01-23 23:41:50 +00:00
|
|
|
|
|
|
|
|
2018-02-04 21:00:08 +00:00
|
|
|
def _generate_aug_reg_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None:
|
|
|
|
if rname:
|
|
|
|
right_str = rname
|
|
|
|
else:
|
|
|
|
# an immediate value is provided in rvalue
|
2018-02-08 22:35:50 +00:00
|
|
|
right_str = "#" + str(rvalue)
|
2018-01-23 23:41:50 +00:00
|
|
|
if operator == "+=":
|
2018-02-22 00:21:29 +00:00
|
|
|
assert 0 <= rvalue <= 255, "+= value should be 0..255 for now at " + str(lvalue.sourceref) # @todo
|
|
|
|
if rvalue > 0:
|
|
|
|
_gen_aug_plus_reg_int(lvalue, out, right_str, scope)
|
2018-01-23 23:41:50 +00:00
|
|
|
elif operator == "-=":
|
2018-02-22 00:21:29 +00:00
|
|
|
assert 0 <= rvalue <= 255, "-= value should be 0..255 for now at " + str(lvalue.sourceref) # @todo
|
|
|
|
if rvalue > 0:
|
|
|
|
_gen_aug_minus_reg_int(lvalue, out, right_str, scope)
|
2018-01-23 23:41:50 +00:00
|
|
|
elif operator == "&=":
|
2018-02-22 00:21:29 +00:00
|
|
|
assert 0 <= rvalue <= 255, "&= value should be 0..255 for now at " + str(lvalue.sourceref) # @todo
|
|
|
|
if rvalue == 0:
|
|
|
|
# output '=0'
|
|
|
|
assignment = Assignment(sourceref=lvalue.sourceref) # type: ignore
|
|
|
|
assignment.nodes.append(lvalue)
|
|
|
|
assignment.nodes.append(LiteralValue(value=0, sourceref=lvalue.sourceref)) # type: ignore
|
|
|
|
ctx = Context(out=out, stmt=assignment, scope=scope, floats_enabled=False) # type: ignore
|
|
|
|
generate_assignment(ctx)
|
|
|
|
else:
|
|
|
|
_gen_aug_and_reg_int(lvalue, out, right_str, scope)
|
2018-01-23 23:41:50 +00:00
|
|
|
elif operator == "|=":
|
2018-02-22 00:21:29 +00:00
|
|
|
assert 0 <= rvalue <= 255, "|= value should be 0..255 for now at " + str(lvalue.sourceref) # @todo
|
|
|
|
if rvalue > 0:
|
|
|
|
_gen_aug_or_reg_int(lvalue, out, right_str, scope)
|
2018-01-23 23:41:50 +00:00
|
|
|
elif operator == "^=":
|
2018-02-22 00:21:29 +00:00
|
|
|
assert 0 <= rvalue <= 255, "^= value should be 0..255 for now at " + str(lvalue.sourceref) # @todo
|
|
|
|
if rvalue > 0:
|
|
|
|
_gen_aug_xor_reg_int(lvalue, out, right_str, scope)
|
2018-01-23 23:41:50 +00:00
|
|
|
elif operator == ">>=":
|
2018-02-08 20:56:43 +00:00
|
|
|
_gen_aug_shiftright_reg_int(lvalue, out, rname, rvalue, scope)
|
2018-01-23 23:41:50 +00:00
|
|
|
elif operator == "<<=":
|
2018-02-08 20:56:43 +00:00
|
|
|
_gen_aug_shiftleft_reg_int(lvalue, out, rname, rvalue, scope)
|
|
|
|
else:
|
2018-02-11 14:11:14 +00:00
|
|
|
raise ValueError("invalid operator: " + operator, str(lvalue.sourceref)) # @todo implement more operators such as *=, /=
|
2018-01-14 14:18:50 +00:00
|
|
|
|
|
|
|
|
2018-01-23 23:41:50 +00:00
|
|
|
def _generate_aug_reg_reg(out: Callable, lvalue: Register, operator: str, rvalue: Register, scope: Scope) -> None:
|
|
|
|
if operator == "+=":
|
2018-02-08 20:56:43 +00:00
|
|
|
_gen_aug_plus_reg_reg(lvalue, out, rvalue, scope)
|
2018-01-23 23:41:50 +00:00
|
|
|
elif operator == "-=":
|
2018-02-08 20:56:43 +00:00
|
|
|
_gen_aug_minus_reg_reg(lvalue, out, rvalue, scope)
|
2018-01-23 23:41:50 +00:00
|
|
|
elif operator == "&=":
|
2018-02-08 20:56:43 +00:00
|
|
|
_gen_aug_and_reg_reg(lvalue, out, rvalue, scope)
|
2018-01-23 23:41:50 +00:00
|
|
|
elif operator == "|=":
|
2018-02-08 20:56:43 +00:00
|
|
|
_gen_aug_or_reg_reg(lvalue, out, rvalue, scope)
|
|
|
|
elif operator == "^=":
|
|
|
|
_gen_aug_xor_reg_reg(lvalue, out, rvalue, scope)
|
|
|
|
elif operator == ">>=":
|
|
|
|
_gen_aug_shiftright_reg_reg(lvalue, out, rvalue, scope)
|
|
|
|
elif operator == "<<=":
|
|
|
|
_gen_aug_shiftleft_reg_reg(lvalue, out, rvalue, scope)
|
|
|
|
else:
|
2018-02-11 14:11:14 +00:00
|
|
|
raise ValueError("invalid operator: " + operator, str(lvalue.sourceref)) # @todo implement more operators such as *=, /=
|
2018-02-08 20:56:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_shiftleft_reg_int(lvalue: Register, out: Callable, rname: str, rvalue: int, scope: Scope) -> None:
|
|
|
|
if rname:
|
|
|
|
assert lvalue.name in REGISTER_BYTES, "only single registers for now" # @todo <<=.word
|
2018-01-28 20:58:16 +00:00
|
|
|
if lvalue.name == "A":
|
2018-02-08 20:56:43 +00:00
|
|
|
preserve_regs = {'X'}
|
2018-01-28 20:58:16 +00:00
|
|
|
elif lvalue.name == "X":
|
2018-02-08 20:56:43 +00:00
|
|
|
preserve_regs = {'A'}
|
|
|
|
out("\vtxa")
|
2018-01-28 20:58:16 +00:00
|
|
|
elif lvalue.name == "Y":
|
2018-02-08 20:56:43 +00:00
|
|
|
preserve_regs = {'A', 'X'}
|
|
|
|
out("\vtya")
|
|
|
|
with preserving_registers(preserve_regs, scope, out):
|
|
|
|
out("\vldx " + rname)
|
|
|
|
out("\vjsr il65_lib.asl_A_by_X")
|
|
|
|
# put A back into target register
|
|
|
|
if lvalue.name == "X":
|
|
|
|
out("\vtax")
|
|
|
|
elif lvalue.name == "Y":
|
2018-01-23 23:41:50 +00:00
|
|
|
out("\vtay")
|
2018-02-08 20:56:43 +00:00
|
|
|
else:
|
|
|
|
def shifts_A(times: int) -> None:
|
|
|
|
if times >= 8:
|
|
|
|
out("\vlda #0")
|
|
|
|
else:
|
|
|
|
for _ in range(min(8, times)):
|
|
|
|
out("\vasl a")
|
|
|
|
|
2018-01-28 20:58:16 +00:00
|
|
|
if lvalue.name == "A":
|
2018-02-08 20:56:43 +00:00
|
|
|
shifts_A(rvalue)
|
2018-01-28 20:58:16 +00:00
|
|
|
elif lvalue.name == "X":
|
2018-01-23 23:41:50 +00:00
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
2018-02-08 20:56:43 +00:00
|
|
|
shifts_A(rvalue)
|
2018-01-23 23:41:50 +00:00
|
|
|
out("\vtax")
|
2018-01-28 20:58:16 +00:00
|
|
|
elif lvalue.name == "Y":
|
2018-01-23 23:41:50 +00:00
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtya")
|
2018-02-08 20:56:43 +00:00
|
|
|
shifts_A(rvalue)
|
2018-01-23 23:41:50 +00:00
|
|
|
out("\vtay")
|
|
|
|
else:
|
2018-02-08 20:56:43 +00:00
|
|
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo <<=.word
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_shiftright_reg_int(lvalue: Register, out: Callable, rname: str, rvalue: int, scope: Scope) -> None:
|
|
|
|
if rname:
|
|
|
|
assert lvalue.name in REGISTER_BYTES, "only single registers for now" # @todo >>=.word
|
2018-01-28 20:58:16 +00:00
|
|
|
if lvalue.name == "A":
|
2018-02-08 20:56:43 +00:00
|
|
|
preserve_regs = {'X'}
|
2018-01-28 20:58:16 +00:00
|
|
|
elif lvalue.name == "X":
|
2018-02-08 20:56:43 +00:00
|
|
|
preserve_regs = {'A'}
|
|
|
|
out("\vtxa")
|
2018-01-28 20:58:16 +00:00
|
|
|
elif lvalue.name == "Y":
|
2018-02-08 20:56:43 +00:00
|
|
|
preserve_regs = {'A', 'X'}
|
|
|
|
out("\vtya")
|
|
|
|
with preserving_registers(preserve_regs, scope, out):
|
|
|
|
out("\vldx " + rname)
|
|
|
|
out("\vjsr il65_lib.lsr_A_by_X")
|
|
|
|
# put A back into target register
|
|
|
|
if lvalue.name == "X":
|
|
|
|
out("\vtax")
|
|
|
|
if lvalue.name == "Y":
|
2018-01-23 23:41:50 +00:00
|
|
|
out("\vtay")
|
2018-02-08 20:56:43 +00:00
|
|
|
else:
|
|
|
|
def shifts_A(times: int) -> None:
|
|
|
|
if times >= 8:
|
|
|
|
out("\vlda #0")
|
|
|
|
else:
|
|
|
|
for _ in range(min(8, times)):
|
|
|
|
out("\vlsr a")
|
|
|
|
|
2018-01-28 20:58:16 +00:00
|
|
|
if lvalue.name == "A":
|
2018-02-08 20:56:43 +00:00
|
|
|
shifts_A(rvalue)
|
2018-01-28 20:58:16 +00:00
|
|
|
elif lvalue.name == "X":
|
2018-01-23 23:41:50 +00:00
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
2018-02-08 20:56:43 +00:00
|
|
|
shifts_A(rvalue)
|
2018-01-23 23:41:50 +00:00
|
|
|
out("\vtax")
|
2018-01-28 20:58:16 +00:00
|
|
|
elif lvalue.name == "Y":
|
2018-01-23 23:41:50 +00:00
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtya")
|
2018-02-08 20:56:43 +00:00
|
|
|
shifts_A(rvalue)
|
2018-01-23 23:41:50 +00:00
|
|
|
out("\vtay")
|
|
|
|
else:
|
2018-02-08 20:56:43 +00:00
|
|
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_xor_reg_int(lvalue: Register, out: Callable, right_str: str, scope: Scope) -> None:
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\veor " + right_str)
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
|
|
|
out("\veor " + right_str)
|
|
|
|
out("\vtax")
|
|
|
|
elif lvalue.name == "Y":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtya")
|
|
|
|
out("\veor " + right_str)
|
|
|
|
out("\vtay")
|
|
|
|
else:
|
|
|
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_or_reg_int(lvalue: Register, out: Callable, right_str: str, scope: Scope) -> None:
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vora " + right_str)
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
|
|
|
out("\vora " + right_str)
|
|
|
|
out("\vtax")
|
|
|
|
elif lvalue.name == "Y":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtya")
|
|
|
|
out("\vora " + right_str)
|
|
|
|
out("\vtay")
|
|
|
|
else:
|
|
|
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_and_reg_int(lvalue: Register, out: Callable, right_str: str, scope: Scope) -> None:
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vand " + right_str)
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
|
|
|
out("\vand " + right_str)
|
|
|
|
out("\vtax")
|
|
|
|
elif lvalue.name == "Y":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtya")
|
|
|
|
out("\vand " + right_str)
|
|
|
|
out("\vtay")
|
|
|
|
else:
|
|
|
|
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_minus_reg_int(lvalue: Register, out: Callable, right_str: str, scope: Scope) -> None:
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vsec")
|
|
|
|
out("\vsbc " + right_str)
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
|
|
|
out("\vsec")
|
|
|
|
out("\vsbc " + right_str)
|
|
|
|
out("\vtax")
|
|
|
|
elif lvalue.name == "Y":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtya")
|
|
|
|
out("\vsec")
|
|
|
|
out("\vsbc " + right_str)
|
|
|
|
out("\vtay")
|
2018-02-08 22:35:50 +00:00
|
|
|
elif lvalue.name == "AX":
|
|
|
|
out("\vsec")
|
|
|
|
out("\vsbc " + right_str)
|
|
|
|
out("\vbcs +")
|
|
|
|
out("\vdex")
|
|
|
|
out("+")
|
|
|
|
elif lvalue.name == "AY":
|
|
|
|
out("\vsec")
|
|
|
|
out("\vsbc " + right_str)
|
|
|
|
out("\vbcs +")
|
|
|
|
out("\vdey")
|
|
|
|
out("+")
|
|
|
|
elif lvalue.name == "XY":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
|
|
|
out("\vsec")
|
|
|
|
out("\vsbc " + right_str)
|
|
|
|
out("\vtax")
|
|
|
|
out("\vbcs +")
|
|
|
|
out("\vdey")
|
|
|
|
out("+")
|
2018-02-08 20:56:43 +00:00
|
|
|
else:
|
2018-02-08 22:35:50 +00:00
|
|
|
raise ValueError("invalid register", str(lvalue))
|
2018-02-08 20:56:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_plus_reg_int(lvalue: Register, out: Callable, right_str: str, scope: Scope) -> None:
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + right_str)
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + right_str)
|
|
|
|
out("\vtax")
|
|
|
|
elif lvalue.name == "Y":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtya")
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + right_str)
|
|
|
|
out("\vtay")
|
2018-02-08 22:35:50 +00:00
|
|
|
elif lvalue.name == "AX":
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + right_str)
|
|
|
|
out("\vbcc +")
|
|
|
|
out("\vinx")
|
|
|
|
out("+")
|
|
|
|
elif lvalue.name == "AY":
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + right_str)
|
|
|
|
out("\vbcc +")
|
|
|
|
out("\viny")
|
|
|
|
out("+")
|
|
|
|
elif lvalue.name == "XY":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + right_str)
|
|
|
|
out("\vtax")
|
|
|
|
out("\vbcc +")
|
|
|
|
out("\viny")
|
|
|
|
out("+")
|
2018-02-08 20:56:43 +00:00
|
|
|
else:
|
2018-02-08 22:35:50 +00:00
|
|
|
raise ValueError("invalid register", str(lvalue))
|
2018-02-08 20:56:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_shiftleft_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None:
|
|
|
|
if rvalue.name not in REGISTER_BYTES:
|
|
|
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo <<=.word
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
|
|
|
out("\vasl " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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.name == "Y":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_shiftright_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None:
|
|
|
|
if rvalue.name not in REGISTER_BYTES:
|
|
|
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo >>=.word
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
|
|
|
out("\vlsr " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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.name == "Y":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_xor_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None:
|
|
|
|
if rvalue.name not in REGISTER_BYTES:
|
|
|
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo ^=.word
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
|
|
|
out("\veor " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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.name == "Y":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_or_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None:
|
|
|
|
if rvalue.name not in REGISTER_BYTES:
|
|
|
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo |=.word
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
|
|
|
out("\vora " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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.name == "Y":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_and_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None:
|
|
|
|
if rvalue.name not in REGISTER_BYTES:
|
|
|
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo &=.word
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
|
|
|
out("\vand " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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.name == "Y":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_minus_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None:
|
|
|
|
if rvalue.name not in REGISTER_BYTES:
|
|
|
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo -=.word
|
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
|
|
|
out("\vsec")
|
|
|
|
out("\vsbc " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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.name == "Y":
|
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.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
|
|
|
|
|
|
|
|
|
|
|
|
def _gen_aug_plus_reg_reg(lvalue: Register, out: Callable, rvalue: Register, scope: Scope) -> None:
|
|
|
|
if rvalue.name not in REGISTER_BYTES:
|
|
|
|
raise CodeError("unsupported rvalue register for aug assign", str(rvalue)) # @todo +=.word
|
2018-02-08 22:35:50 +00:00
|
|
|
out("\vst{:s} {:s}".format(rvalue.name.lower(), to_hex(Zeropage.SCRATCH_B1)))
|
2018-02-08 20:56:43 +00:00
|
|
|
if lvalue.name == "A":
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
elif lvalue.name == "X":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
out("\vtax")
|
|
|
|
elif lvalue.name == "Y":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtya")
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
out("\vtay")
|
2018-02-08 22:35:50 +00:00
|
|
|
elif lvalue.name == "AX":
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
out("\vbcc +")
|
|
|
|
out("\vinx")
|
|
|
|
out("+")
|
|
|
|
elif lvalue.name == "AY":
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
out("\vbcc +")
|
|
|
|
out("\viny")
|
|
|
|
out("+")
|
|
|
|
elif lvalue.name == "XY":
|
|
|
|
with preserving_registers({'A'}, scope, out):
|
|
|
|
out("\vtxa")
|
|
|
|
out("\vclc")
|
|
|
|
out("\vadc " + to_hex(Zeropage.SCRATCH_B1))
|
|
|
|
out("\vtax")
|
|
|
|
out("\vbcc +")
|
|
|
|
out("\viny")
|
|
|
|
out("+")
|
2018-02-08 20:56:43 +00:00
|
|
|
else:
|
2018-02-08 22:35:50 +00:00
|
|
|
raise ValueError("invalid register", str(lvalue))
|