mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 01:29:28 +00:00
incrdecr codegen
This commit is contained in:
parent
a560982b7e
commit
d18876ee70
@ -5,12 +5,12 @@ This is the assembly code generator (from the parse tree)
|
||||
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
"""
|
||||
|
||||
|
||||
import contextlib
|
||||
import math
|
||||
import attr
|
||||
from typing import Set, Callable
|
||||
from ..datatypes import FLOAT_MAX_POSITIVE, FLOAT_MAX_NEGATIVE
|
||||
from ..plyparse import Scope
|
||||
from ..plyparse import Scope, AstNode
|
||||
from ..compile import Zeropage
|
||||
|
||||
|
||||
@ -18,6 +18,14 @@ class CodeError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@attr.s(repr=False, cmp=False)
|
||||
class Context:
|
||||
out = attr.ib(type=Callable)
|
||||
stmt = attr.ib(type=AstNode)
|
||||
scope = attr.ib(type=Scope)
|
||||
floats_enabled = attr.ib(type=bool)
|
||||
|
||||
|
||||
def to_hex(number: int) -> str:
|
||||
# 0..15 -> "0".."15"
|
||||
# 16..255 -> "$10".."$ff"
|
||||
|
@ -7,20 +7,24 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
from typing import Callable
|
||||
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 . import CodeError, preserving_registers, to_hex, Context
|
||||
from ..datatypes import REGISTER_BYTES, VarType
|
||||
from ..compile import Zeropage
|
||||
|
||||
|
||||
def generate_assignment(out: Callable, stmt: Assignment, scope: Scope) -> None:
|
||||
out("\v\t\t\t; " + stmt.lineref)
|
||||
out("\v; @todo assignment")
|
||||
def generate_assignment(ctx: Context) -> None:
|
||||
assert isinstance(ctx.stmt, Assignment)
|
||||
ctx.out("\v\t\t\t; " + ctx.stmt.lineref)
|
||||
ctx.out("\v; @todo assignment")
|
||||
# @todo assignment
|
||||
|
||||
|
||||
def generate_aug_assignment(out: Callable, stmt: AugAssignment, scope: Scope) -> None:
|
||||
def generate_aug_assignment(ctx: Context) -> None:
|
||||
# for instance: value += 3 (value = 0-255 for now)
|
||||
# left: Register, SymbolName, or Dereference. right: Expression/LiteralValue
|
||||
out = ctx.out
|
||||
stmt = ctx.stmt
|
||||
assert isinstance(stmt, AugAssignment)
|
||||
out("\v\t\t\t; " + stmt.lineref)
|
||||
lvalue = stmt.left
|
||||
rvalue = stmt.right
|
||||
@ -28,23 +32,23 @@ def generate_aug_assignment(out: Callable, stmt: AugAssignment, scope: Scope) ->
|
||||
if isinstance(rvalue, LiteralValue):
|
||||
if type(rvalue.value) is int:
|
||||
if 0 <= rvalue.value <= 255:
|
||||
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, rvalue.value, "", scope)
|
||||
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, rvalue.value, "", ctx.scope)
|
||||
else:
|
||||
raise CodeError("aug. assignment value must be 0..255", rvalue)
|
||||
else:
|
||||
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX
|
||||
elif isinstance(rvalue, SymbolName):
|
||||
symdef = scope.lookup(rvalue.name)
|
||||
symdef = ctx.scope.lookup(rvalue.name)
|
||||
if isinstance(symdef, VarDef) and symdef.vartype == VarType.CONST and symdef.datatype.isinteger():
|
||||
if 0 <= symdef.value.const_value() <= 255: # type: ignore
|
||||
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, 0, symdef.name, scope)
|
||||
_generate_aug_reg_constant_int(out, lvalue, stmt.operator, 0, symdef.name, ctx.scope)
|
||||
else:
|
||||
raise CodeError("aug. assignment value must be 0..255", rvalue)
|
||||
else:
|
||||
raise CodeError("constant integer literal or variable required for now", rvalue) # XXX
|
||||
elif isinstance(rvalue, Register):
|
||||
# @todo check value range (single register; 0-255) @todo support combined registers
|
||||
_generate_aug_reg_reg(out, lvalue, stmt.operator, rvalue, scope)
|
||||
_generate_aug_reg_reg(out, lvalue, stmt.operator, rvalue, ctx.scope)
|
||||
else:
|
||||
# @todo Register += symbolname / dereference , _generate_aug_reg_mem?
|
||||
raise CodeError("invalid rvalue for aug. assignment on register", rvalue)
|
||||
|
@ -5,13 +5,17 @@ This is the code generator for gotos and subroutine calls.
|
||||
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
"""
|
||||
|
||||
from typing import Callable
|
||||
from ..plyparse import Goto, SubCall
|
||||
from . import Context
|
||||
|
||||
|
||||
def generate_goto(out: Callable, stmt: Goto) -> None:
|
||||
def generate_goto(ctx: Context) -> None:
|
||||
stmt = ctx.stmt
|
||||
assert isinstance(stmt, Goto)
|
||||
pass # @todo
|
||||
|
||||
|
||||
def generate_subcall(out: Callable, stmt: SubCall) -> None:
|
||||
def generate_subcall(ctx: Context) -> None:
|
||||
stmt = ctx.stmt
|
||||
assert isinstance(stmt, SubCall)
|
||||
pass # @todo
|
||||
|
@ -11,7 +11,7 @@ from typing import TextIO, Callable, no_type_check
|
||||
from ..plylex import print_bold
|
||||
from ..plyparse import Module, Scope, ProgramFormat, Block, Directive, VarDef, Label, Subroutine, AstNode, ZpOptions, \
|
||||
InlineAssembly, Return, Register, Goto, SubCall, Assignment, AugAssignment, IncrDecr, AssignmentTargets
|
||||
from . import CodeError, to_hex, to_mflpt5
|
||||
from . import CodeError, to_hex, to_mflpt5, Context
|
||||
from .variables import generate_block_init, generate_block_vars
|
||||
from .assignment import generate_assignment, generate_aug_assignment
|
||||
from .calls import generate_goto, generate_subcall
|
||||
@ -131,6 +131,7 @@ class AssemblyGenerator:
|
||||
# there's no code in the zero page block.
|
||||
out("\v.pend\n")
|
||||
for block in sorted(self.module.all_nodes(Block), key=lambda b: b.address or 0):
|
||||
ctx = Context(out=out, stmt=None, scope=block.scope, floats_enabled=self.floats_enabled)
|
||||
if block.name == "ZP":
|
||||
continue # already processed
|
||||
self.cur_block = block
|
||||
@ -153,7 +154,8 @@ class AssemblyGenerator:
|
||||
for stmt in block.scope.nodes:
|
||||
if isinstance(stmt, (VarDef, Subroutine)):
|
||||
continue # should have been handled already or will be later
|
||||
self.generate_statement(out, stmt, block.scope)
|
||||
ctx.stmt = stmt
|
||||
self.generate_statement(ctx)
|
||||
if block.name == "main" and isinstance(stmt, Label) and stmt.name == "start":
|
||||
# make sure the main.start routine clears the decimal and carry flags as first steps
|
||||
out("\vcld\n\vclc\n\vclv")
|
||||
@ -170,8 +172,8 @@ class AssemblyGenerator:
|
||||
out("\v; params: {}\n\v; returns: {} clobbers: {}".format(params or "-", returns or "-", clobbers or "-"))
|
||||
cur_block = self.cur_block
|
||||
self.cur_block = subdef.scope
|
||||
for stmt in subdef.scope.nodes:
|
||||
self.generate_statement(out, stmt, subdef.scope)
|
||||
for ctx.stmt in subdef.scope.nodes:
|
||||
self.generate_statement(ctx)
|
||||
self.cur_block = cur_block
|
||||
out("")
|
||||
out("; -- end block subroutines")
|
||||
@ -185,48 +187,52 @@ class AssemblyGenerator:
|
||||
out("\n\v.pend\n")
|
||||
|
||||
@no_type_check
|
||||
def generate_statement(self, out: Callable, stmt: AstNode, scope: Scope) -> None:
|
||||
def generate_statement(self, ctx: Context) -> None:
|
||||
stmt = ctx.stmt
|
||||
if isinstance(stmt, Label):
|
||||
out("\n{:s}\v\t\t; {:s}".format(stmt.name, stmt.lineref))
|
||||
ctx.out("\n{:s}\v\t\t; {:s}".format(stmt.name, stmt.lineref))
|
||||
elif isinstance(stmt, Return):
|
||||
if stmt.value_A:
|
||||
reg = Register(name="A", sourceref=stmt.sourceref)
|
||||
assignment = Assignment(sourceref=stmt.sourceref)
|
||||
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
||||
assignment.nodes.append(stmt.value_A)
|
||||
generate_assignment(out, assignment, scope)
|
||||
ctx.stmt = assignment
|
||||
generate_assignment(ctx)
|
||||
if stmt.value_X:
|
||||
reg = Register(name="X", sourceref=stmt.sourceref)
|
||||
assignment = Assignment(sourceref=stmt.sourceref)
|
||||
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
||||
assignment.nodes.append(stmt.value_X)
|
||||
generate_assignment(out, assignment, scope)
|
||||
ctx.stmt = assignment
|
||||
generate_assignment(ctx)
|
||||
if stmt.value_Y:
|
||||
reg = Register(name="Y", sourceref=stmt.sourceref)
|
||||
assignment = Assignment(sourceref=stmt.sourceref)
|
||||
assignment.nodes.append(AssignmentTargets(nodes=[reg], sourceref=stmt.sourceref))
|
||||
assignment.nodes.append(stmt.value_Y)
|
||||
generate_assignment(out, assignment, scope)
|
||||
out("\vrts")
|
||||
ctx.stmt = assignment
|
||||
generate_assignment(ctx)
|
||||
ctx.out("\vrts")
|
||||
elif isinstance(stmt, InlineAssembly):
|
||||
out("\n\v; inline asm, " + stmt.lineref)
|
||||
out(stmt.assembly)
|
||||
out("\v; end inline asm, " + stmt.lineref + "\n")
|
||||
ctx.out("\n\v; inline asm, " + stmt.lineref)
|
||||
ctx.out(stmt.assembly)
|
||||
ctx.out("\v; end inline asm, " + stmt.lineref + "\n")
|
||||
elif isinstance(stmt, IncrDecr):
|
||||
generate_incrdecr(out, stmt, scope, self.floats_enabled)
|
||||
generate_incrdecr(ctx)
|
||||
elif isinstance(stmt, Goto):
|
||||
generate_goto(out, stmt)
|
||||
generate_goto(ctx)
|
||||
elif isinstance(stmt, SubCall):
|
||||
generate_subcall(out, stmt)
|
||||
generate_subcall(ctx)
|
||||
elif isinstance(stmt, Assignment):
|
||||
generate_assignment(out, stmt, scope)
|
||||
generate_assignment(ctx)
|
||||
elif isinstance(stmt, AugAssignment):
|
||||
generate_aug_assignment(out, stmt, scope)
|
||||
generate_aug_assignment(ctx)
|
||||
elif isinstance(stmt, Directive):
|
||||
if stmt.name == "breakpoint":
|
||||
# put a marker in the source so that we can generate a list of breakpoints later
|
||||
# this label is later extracted from the label dump file to turn it into a breakpoint instruction
|
||||
out("_il65_breakpoint_{:d}".format(id(stmt)))
|
||||
ctx.out("_il65_breakpoint_{:d}".format(id(stmt)))
|
||||
# other directives are ignored here
|
||||
else:
|
||||
raise NotImplementedError("statement", stmt)
|
||||
|
@ -7,13 +7,16 @@ 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 typing import Callable
|
||||
from ..plyparse import Scope, VarType, VarDef, Register, IncrDecr, SymbolName, Dereference, LiteralValue, datatype_of
|
||||
from ..plyparse import VarType, VarDef, Register, IncrDecr, SymbolName, Dereference, LiteralValue, datatype_of
|
||||
from ..datatypes import DataType, REGISTER_BYTES
|
||||
from . import CodeError, preserving_registers, to_hex
|
||||
from . import CodeError, preserving_registers, to_hex, Context
|
||||
|
||||
|
||||
def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope, floats_enabled: bool) -> None:
|
||||
def generate_incrdecr(ctx: Context) -> None:
|
||||
out = ctx.out
|
||||
stmt = ctx.stmt
|
||||
scope = ctx.scope
|
||||
assert isinstance(stmt, IncrDecr)
|
||||
assert isinstance(stmt.howmuch, (int, float)) and stmt.howmuch >= 0
|
||||
assert stmt.operator in ("++", "--")
|
||||
if stmt.howmuch == 0:
|
||||
@ -185,7 +188,7 @@ def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope, floats_enable
|
||||
out("\vdec {:s}+1".format(what_str))
|
||||
out("+")
|
||||
elif target.datatype == DataType.FLOAT:
|
||||
if not floats_enabled:
|
||||
if not ctx.floats_enabled:
|
||||
raise CodeError("floating point numbers not enabled via option")
|
||||
if stmt.howmuch == 1.0:
|
||||
# special case for +/-1
|
||||
@ -224,7 +227,7 @@ def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope, floats_enable
|
||||
what = target.operand.name
|
||||
if stmt.howmuch == 1:
|
||||
if target.datatype == DataType.FLOAT:
|
||||
if not floats_enabled:
|
||||
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)
|
||||
@ -236,19 +239,13 @@ def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope, floats_enable
|
||||
else:
|
||||
with preserving_registers({'A', 'Y'}, scope, out, loads_a_within=True):
|
||||
out("\vlda " + what)
|
||||
out("\vsta c64.SCRATCH_ZPWORD1")
|
||||
out("\vlda {:s}+1".format(what))
|
||||
out("\vsta c64.SCRATCH_ZPWORD1+1")
|
||||
out("\vldy {:s}+1".format(what))
|
||||
if target.datatype == DataType.BYTE:
|
||||
if stmt.operator == "++":
|
||||
out("\vjsr il65_lib.incr_deref_byte")
|
||||
else:
|
||||
out("\vjsr il65_lib.decr_deref_byte")
|
||||
out("\vclc" if stmt.operator == "++" else "\vsec")
|
||||
out("\vjsr il65_lib.incrdecr_deref_byte_reg_AY")
|
||||
elif target.datatype == DataType.WORD:
|
||||
if stmt.operator == "++":
|
||||
out("\vjsr il65_lib.incr_deref_word")
|
||||
else:
|
||||
out("\vjsr il65_lib.decr_deref_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:
|
||||
@ -258,10 +255,7 @@ def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope, floats_enable
|
||||
raise CodeError("can't dereference just a single register, need combined register", target)
|
||||
reg = target.operand.name
|
||||
if stmt.howmuch == 1:
|
||||
if stmt.operator == "++":
|
||||
out("\vclc")
|
||||
else:
|
||||
out("\vsec")
|
||||
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)
|
||||
|
15
todo.ill
15
todo.ill
@ -31,17 +31,16 @@ start:
|
||||
[$55 .float] ++
|
||||
[screenm .float]--
|
||||
|
||||
;[border] ++
|
||||
;[border] ++
|
||||
;[border] --
|
||||
[border] ++
|
||||
[border] ++
|
||||
[border] --
|
||||
[$d020] ++
|
||||
[$d020] ++
|
||||
[$d020] --
|
||||
;[border2] ++ ;@todo suport incr/decr on deref var WORD
|
||||
;[border2] ++ ;@todo suport incr/decr on deref var WORD
|
||||
;[border2] -- ;@todo suport incr/decr on deref var WORD
|
||||
;[XY] ++
|
||||
;[XY] --
|
||||
[border2 .word] ++
|
||||
[border2 .float] ++
|
||||
[border2] --
|
||||
[XY] ++
|
||||
|
||||
return 44.123
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user