This commit is contained in:
Irmen de Jong 2018-02-04 22:00:08 +01:00
parent d18876ee70
commit 938c541cc2
5 changed files with 175 additions and 169 deletions

View File

@ -6,9 +6,9 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
""" """
from typing import Callable from typing import Callable
from ..plyparse import Scope, Assignment, AugAssignment, Register, LiteralValue, SymbolName, VarDef from ..plyparse import Scope, Assignment, AugAssignment, Register, LiteralValue, SymbolName, VarDef, Dereference
from . import CodeError, preserving_registers, to_hex, Context from . import CodeError, preserving_registers, to_hex, Context
from ..datatypes import REGISTER_BYTES, VarType from ..datatypes import REGISTER_BYTES, VarType, DataType
from ..compile import Zeropage from ..compile import Zeropage
@ -32,115 +32,188 @@ def generate_aug_assignment(ctx: Context) -> None:
if isinstance(rvalue, LiteralValue): if isinstance(rvalue, LiteralValue):
if type(rvalue.value) is int: if type(rvalue.value) is int:
if 0 <= rvalue.value <= 255: if 0 <= rvalue.value <= 255:
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, rvalue.value, "", ctx.scope) if stmt.operator not in ("<<=", ">>=") or rvalue.value != 0:
_generate_aug_reg_int(out, lvalue, stmt.operator, rvalue.value, "", ctx.scope)
else: else:
raise CodeError("aug. assignment value must be 0..255", rvalue) raise CodeError("aug. assignment value must be 0..255", rvalue)
else: else:
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX raise CodeError("constant integer literal or variable required for now", rvalue) # XXX
elif isinstance(rvalue, SymbolName): elif isinstance(rvalue, SymbolName):
symdef = ctx.scope.lookup(rvalue.name) symdef = ctx.scope.lookup(rvalue.name)
if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST and symdef.datatype.isinteger(): if isinstance(symdef, VarDef):
if 0 <= symdef.value.const_value() <= 255: # type: ignore if symdef.vartype == VarType.CONST:
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, 0, symdef.name, ctx.scope) if symdef.datatype.isinteger() and 0 <= symdef.value.const_value() <= 255: # type: ignore
_generate_aug_reg_int(out, lvalue, stmt.operator, symdef.value.const_value(), "", ctx.scope) # type: ignore
else: else:
raise CodeError("aug. assignment value must be 0..255", rvalue) raise CodeError("aug. assignment value must be integer 0..255", rvalue)
elif symdef.datatype == DataType.BYTE:
_generate_aug_reg_int(out, lvalue, stmt.operator, 0, symdef.name, ctx.scope)
else: else:
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX raise CodeError("variable must be of type byte for now", rvalue) # XXX
else:
raise CodeError("can only use variable name as symbol for aug assign rvalue", rvalue)
elif isinstance(rvalue, Register): elif isinstance(rvalue, Register):
# @todo check value range (single register; 0-255) @todo support combined registers 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)
_generate_aug_reg_reg(out, lvalue, stmt.operator, rvalue, ctx.scope) _generate_aug_reg_reg(out, lvalue, stmt.operator, rvalue, ctx.scope)
elif isinstance(rvalue, Dereference):
if isinstance(rvalue.operand, (LiteralValue, SymbolName)):
if isinstance(rvalue.operand, LiteralValue):
what = to_hex(rvalue.operand.value)
else: else:
# @todo Register += symbolname / dereference , _generate_aug_reg_mem? symdef = rvalue.my_scope().lookup(rvalue.operand.name)
raise CodeError("invalid rvalue for aug. assignment on register", rvalue) 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
_generate_aug_reg_reg(out, lvalue, stmt.operator, a_reg, ctx.scope)
out("\vst{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower()))
out("\vpla\n\vtay\n\vpla") # restore A, Y from stack
out("\vld{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower()))
elif isinstance(rvalue.operand, Register):
assert rvalue.operand.datatype == DataType.WORD
if rvalue.datatype != DataType.BYTE:
raise CodeError("aug. assignment value must be a byte, 0..255", rvalue)
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
_generate_aug_reg_reg(out, lvalue, stmt.operator, a_reg, ctx.scope)
if lvalue.name != 'X':
out("\vst{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower()))
out("\vpla\n\vtay\n\vpla") # restore A, Y from stack
if lvalue.name != 'X':
out("\vld{:s} il65_lib.SCRATCH_ZP1".format(lvalue.name.lower()))
else:
raise CodeError("invalid dereference operand type", rvalue)
else:
raise CodeError("invalid rvalue type", rvalue)
else: else:
raise CodeError("aug. assignment only implemented for registers for now", stmt.sourceref) # XXX raise CodeError("aug. assignment only implemented for registers for now", stmt.sourceref) # XXX
def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None: def _generate_aug_reg_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None:
r_str = rname or to_hex(rvalue) if rname:
right_str = rname
else:
# an immediate value is provided in rvalue
right_str = "#" + to_hex(rvalue)
if operator == "+=": if operator == "+=":
if lvalue.name == "A": if lvalue.name == "A":
out("\vclc") out("\vclc")
out("\vadc #" + r_str) out("\vadc " + right_str)
elif lvalue.name == "X": elif lvalue.name == "X":
with preserving_registers({'A'}, scope, out): with preserving_registers({'A'}, scope, out):
out("\vtxa") out("\vtxa")
out("\vclc") out("\vclc")
out("\vadc #" + r_str) out("\vadc " + right_str)
out("\vtax") out("\vtax")
elif lvalue.name == "Y": elif lvalue.name == "Y":
with preserving_registers({'A'}, scope, out): with preserving_registers({'A'}, scope, out):
out("\vtya") out("\vtya")
out("\vclc") out("\vclc")
out("\vadc #" + r_str) out("\vadc " + right_str)
out("\vtay") out("\vtay")
else: else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word
elif operator == "-=": elif operator == "-=":
if lvalue.name == "A": if lvalue.name == "A":
out("\vsec") out("\vsec")
out("\vsbc #" + r_str) out("\vsbc " + right_str)
elif lvalue.name == "X": elif lvalue.name == "X":
with preserving_registers({'A'}, scope, out): with preserving_registers({'A'}, scope, out):
out("\vtxa") out("\vtxa")
out("\vsec") out("\vsec")
out("\vsbc #" + r_str) out("\vsbc " + right_str)
out("\vtax") out("\vtax")
elif lvalue.name == "Y": elif lvalue.name == "Y":
with preserving_registers({'A'}, scope, out): with preserving_registers({'A'}, scope, out):
out("\vtya") out("\vtya")
out("\vsec") out("\vsec")
out("\vsbc #" + r_str) out("\vsbc " + right_str)
out("\vtay") out("\vtay")
else: else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word
elif operator == "&=": elif operator == "&=":
if lvalue.name == "A": if lvalue.name == "A":
out("\vand #" + r_str) out("\vand " + right_str)
elif lvalue.name == "X": elif lvalue.name == "X":
with preserving_registers({'A'}, scope, out): with preserving_registers({'A'}, scope, out):
out("\vtxa") out("\vtxa")
out("\vand #" + r_str) out("\vand " + right_str)
out("\vtax") out("\vtax")
elif lvalue.name == "Y": elif lvalue.name == "Y":
with preserving_registers({'A'}, scope, out): with preserving_registers({'A'}, scope, out):
out("\vtya") out("\vtya")
out("\vand #" + r_str) out("\vand " + right_str)
out("\vtay") out("\vtay")
else: else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
elif operator == "|=": elif operator == "|=":
if lvalue.name == "A": if lvalue.name == "A":
out("\vora #" + r_str) out("\vora " + right_str)
elif lvalue.name == "X": elif lvalue.name == "X":
with preserving_registers({'A'}, scope, out): with preserving_registers({'A'}, scope, out):
out("\vtxa") out("\vtxa")
out("\vora #" + r_str) out("\vora " + right_str)
out("\vtax") out("\vtax")
elif lvalue.name == "Y": elif lvalue.name == "Y":
with preserving_registers({'A'}, scope, out): with preserving_registers({'A'}, scope, out):
out("\vtya") out("\vtya")
out("\vora #" + r_str) out("\vora " + right_str)
out("\vtay") out("\vtay")
else: else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
elif operator == "^=": elif operator == "^=":
if lvalue.name == "A": if lvalue.name == "A":
out("\veor #" + r_str) out("\veor " + right_str)
elif lvalue.name == "X": elif lvalue.name == "X":
with preserving_registers({'A'}, scope, out): with preserving_registers({'A'}, scope, out):
out("\vtxa") out("\vtxa")
out("\veor #" + r_str) out("\veor " + right_str)
out("\vtax") out("\vtax")
elif lvalue.name == "Y": elif lvalue.name == "Y":
with preserving_registers({'A'}, scope, out): with preserving_registers({'A'}, scope, out):
out("\vtya") out("\vtya")
out("\veor #" + r_str) out("\veor " + right_str)
out("\vtay") out("\vtay")
else: else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word
elif operator == ">>=": elif operator == ">>=":
if rvalue > 0: if rname:
assert lvalue.name in REGISTER_BYTES, "only single registers for now" # @todo >>=.word
if lvalue.name == "A":
preserve_regs = {'X'}
elif lvalue.name == "X":
preserve_regs = {'A'}
out("\vtxa")
elif lvalue.name == "Y":
preserve_regs = {'A', 'X'}
out("\vtya")
with preserving_registers(preserve_regs, scope, out):
out("\vldx " + rname)
out("\vjsr il65_lib.lsr_A_by_X")
# out("\vbeq +")
# out("-\vlsr a")
# out("\vdex")
# out("\vbne -")
# put A back into target register
if lvalue.name == "X":
out("\vtax")
if lvalue.name == "Y":
out("\vtay")
else:
def shifts_A(times: int) -> None: def shifts_A(times: int) -> None:
if times >= 8: if times >= 8:
out("\vlda #0") out("\vlda #0")
@ -162,7 +235,29 @@ def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: st
else: else:
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word
elif operator == "<<=": elif operator == "<<=":
if rvalue > 0: if rname:
assert lvalue.name in REGISTER_BYTES, "only single registers for now" # @todo <<=.word
if lvalue.name == "A":
preserve_regs = {'X'}
elif lvalue.name == "X":
preserve_regs = {'A'}
out("\vtxa")
elif lvalue.name == "Y":
preserve_regs = {'A', 'X'}
out("\vtya")
with preserving_registers(preserve_regs, scope, out):
out("\vldx " + rname)
out("\vjsr il65_lib.asl_A_by_X")
# out("\vbeq +")
# out("-\vasl a")
# out("\vdex")
# out("\vbne -")
# put A back into target register
if lvalue.name == "X":
out("\vtax")
elif lvalue.name == "Y":
out("\vtay")
else:
def shifts_A(times: int) -> None: def shifts_A(times: int) -> None:
if times >= 8: if times >= 8:
out("\vlda #0") out("\vlda #0")

View File

@ -251,8 +251,7 @@ def generate_incrdecr(ctx: Context) -> None:
else: else:
raise CodeError("can't inc/dec this by something else as 1 right now", stmt) # XXX raise CodeError("can't inc/dec this by something else as 1 right now", stmt) # XXX
elif isinstance(target.operand, Register): elif isinstance(target.operand, Register):
if target.operand.datatype == DataType.BYTE: assert target.operand.datatype == DataType.WORD
raise CodeError("can't dereference just a single register, need combined register", target)
reg = target.operand.name reg = target.operand.name
if stmt.howmuch == 1: if stmt.howmuch == 1:
out("\vclc" if stmt.operator == "++" else "\vsec") out("\vclc" if stmt.operator == "++" else "\vsec")
@ -265,7 +264,7 @@ def generate_incrdecr(ctx: Context) -> None:
else: else:
raise CodeError("can't inc/dec this by something else as 1 right now", stmt) # XXX raise CodeError("can't inc/dec this by something else as 1 right now", stmt) # XXX
else: else:
raise TypeError("invalid dereference target type", target) raise TypeError("invalid dereference operand type", target)
else: else:
raise CodeError("cannot inc/decrement", target) # @todo support more raise CodeError("cannot inc/decrement", target) # @todo support more

View File

@ -186,15 +186,15 @@ incrdecr_deref_byte_reg_XY
incr_deref_byte incr_deref_byte
ldy #0 ldy #0
lda (c64.SCRATCH_ZPWORD1), y lda (SCRATCH_ZPWORD1), y
adc #1 ; carry's cleared already adc #1 ; carry's cleared already
sta (c64.SCRATCH_ZPWORD1), y sta (SCRATCH_ZPWORD1), y
rts rts
decr_deref_byte decr_deref_byte
ldy #0 ldy #0
lda (c64.SCRATCH_ZPWORD1), y lda (SCRATCH_ZPWORD1), y
sbc #1 ; carry's set already sbc #1 ; carry's set already
sta (c64.SCRATCH_ZPWORD1), y sta (SCRATCH_ZPWORD1), y
rts rts
; increments/decrements a word referenced by indirect register pair by 1 ; increments/decrements a word referenced by indirect register pair by 1
@ -216,32 +216,61 @@ incrdecr_deref_word_reg_XY
incr_deref_word incr_deref_word
ldy #0 ldy #0
lda (c64.SCRATCH_ZPWORD1), y lda (SCRATCH_ZPWORD1), y
adc #1 ; carry's cleared already adc #1 ; carry's cleared already
sta (c64.SCRATCH_ZPWORD1), y sta (SCRATCH_ZPWORD1), y
bcc + bcc +
iny iny
lda (c64.SCRATCH_ZPWORD1), y lda (SCRATCH_ZPWORD1), y
adc #0 ; carry is set adc #0 ; carry is set
sta (c64.SCRATCH_ZPWORD1), y sta (SCRATCH_ZPWORD1), y
+ rts + rts
decr_deref_word decr_deref_word
ldy #0 ldy #0
lda (c64.SCRATCH_ZPWORD1), y lda (SCRATCH_ZPWORD1), y
bne + bne +
pha pha
iny iny
lda (c64.SCRATCH_ZPWORD1), y lda (SCRATCH_ZPWORD1), y
sbc #1 ; carry's set already sbc #1 ; carry's set already
sta (c64.SCRATCH_ZPWORD1), y sta (SCRATCH_ZPWORD1), y
dey dey
pla pla
+ sec + sec
sbc #1 sbc #1
sta (c64.SCRATCH_ZPWORD1), y sta (SCRATCH_ZPWORD1), y
rts rts
; shift bits in A right by X positions
lsr_A_by_X
cpx #8
bcc _shift
lda #0 ; x >=8, result always 0
rts
_shift cpx #0
beq +
- lsr a
dex
bne -
+ rts
; shift bits in A left by X positions
asl_A_by_X
cpx #8
bcc _shift
lda #0 ; x >=8, result always 0
rts
_shift cpx #0
beq +
- asl a
dex
bne -
+ rts
} }
%noreturn %noreturn

View File

@ -1,5 +1,6 @@
# old deprecated code, in the process of moving this to the new emit/... modules # 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)
@ -429,130 +430,6 @@ class CodeGenerator:
branch_emitter(targetstr, False, False) branch_emitter(targetstr, False, False)
generate_result_assignments() generate_result_assignments()
def _generate_aug_reg_mem(self, lvalue: RegisterValue, operator: str, rvalue: MemMappedValue) -> None:
r_str = rvalue.name or Parser.to_hex(rvalue.address)
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.datatype != DataType.BYTE:
raise CodeError("can only shift by a byte value", str(rvalue))
r_str = rvalue.name or Parser.to_hex(rvalue.address)
if lvalue.register == "A":
self.p("\t\tlsr " + r_str)
elif lvalue.register == "X":
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tlsr " + r_str)
self.p("\t\ttax")
elif lvalue.register == "Y":
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tlsr " + r_str)
self.p("\t\ttay")
else:
raise CodeError("unsupported lvalue register for shift", str(lvalue)) # @todo >>=.word
elif operator == "<<=":
if rvalue.datatype != DataType.BYTE:
raise CodeError("can only shift by a byte value", str(rvalue))
r_str = rvalue.name or Parser.to_hex(rvalue.address)
if lvalue.register == "A":
self.p("\t\tasl " + r_str)
elif lvalue.register == "X":
with self.preserving_registers({'A'}):
self.p("\t\ttxa")
self.p("\t\tasl " + r_str)
self.p("\t\ttax")
elif lvalue.register == "Y":
with self.preserving_registers({'A'}):
self.p("\t\ttya")
self.p("\t\tasl " + r_str)
self.p("\t\ttay")
else:
raise CodeError("unsupported lvalue register for shift", 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:
if isinstance(iv.value, MemMappedValue): if isinstance(iv.value, MemMappedValue):

View File

@ -37,6 +37,12 @@
start: start:
%breakpoint abc,def %breakpoint abc,def
;X += border
;XY += border
A += [c2f]
AY += [c2f]
AY += [XY]
XY+=255 XY+=255
XY+=254 XY+=254
XY+=253 XY+=253