mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
codegen vars
This commit is contained in:
parent
29060f3373
commit
534bf2f252
@ -55,7 +55,7 @@ class PlyParser:
|
|||||||
if block.name == "ZP":
|
if block.name == "ZP":
|
||||||
if zeropage:
|
if zeropage:
|
||||||
# merge other ZP block into first ZP block
|
# merge other ZP block into first ZP block
|
||||||
for node in block.scope.nodes:
|
for node in block.nodes:
|
||||||
if isinstance(node, Directive):
|
if isinstance(node, Directive):
|
||||||
zeropage.scope.add_node(node, 0)
|
zeropage.scope.add_node(node, 0)
|
||||||
elif isinstance(node, VarDef):
|
elif isinstance(node, VarDef):
|
||||||
|
@ -49,6 +49,12 @@ class DataType(enum.Enum):
|
|||||||
|
|
||||||
STRING_DATATYPES = {DataType.STRING, DataType.STRING_P, DataType.STRING_S, DataType.STRING_PS}
|
STRING_DATATYPES = {DataType.STRING, DataType.STRING_P, DataType.STRING_S, DataType.STRING_PS}
|
||||||
|
|
||||||
|
REGISTER_SYMBOLS = {"A", "X", "Y", "AX", "AY", "XY", "SC", "SI"}
|
||||||
|
REGISTER_SYMBOLS_RETURNVALUES = REGISTER_SYMBOLS | {"SZ"}
|
||||||
|
REGISTER_BYTES = {"A", "X", "Y"}
|
||||||
|
REGISTER_SBITS = {"SC", "SI", "SZ"}
|
||||||
|
REGISTER_WORDS = {"AX", "AY", "XY"}
|
||||||
|
|
||||||
# 5-byte cbm MFLPT format limitations:
|
# 5-byte cbm MFLPT format limitations:
|
||||||
FLOAT_MAX_POSITIVE = 1.7014118345e+38
|
FLOAT_MAX_POSITIVE = 1.7014118345e+38
|
||||||
FLOAT_MAX_NEGATIVE = -1.7014118345e+38
|
FLOAT_MAX_NEGATIVE = -1.7014118345e+38
|
||||||
@ -106,11 +112,14 @@ def coerce_value(datatype: DataType, value: PrimitiveType, sourceref: SourceRef=
|
|||||||
# if the value is out of bounds, raise an overflow exception
|
# if the value is out of bounds, raise an overflow exception
|
||||||
if isinstance(value, (int, float)):
|
if isinstance(value, (int, float)):
|
||||||
if datatype == DataType.BYTE and not (0 <= value <= 0xff): # type: ignore
|
if datatype == DataType.BYTE and not (0 <= value <= 0xff): # type: ignore
|
||||||
raise OverflowError("value out of range for byte")
|
raise OverflowError("value out of range for byte: " + str(value))
|
||||||
if datatype == DataType.WORD and not (0 <= value <= 0xffff): # type: ignore
|
if datatype == DataType.WORD and not (0 <= value <= 0xffff): # type: ignore
|
||||||
raise OverflowError("value out of range for word")
|
raise OverflowError("value out of range for word: " + str(value))
|
||||||
if datatype == DataType.FLOAT and not (FLOAT_MAX_NEGATIVE <= value <= FLOAT_MAX_POSITIVE): # type: ignore
|
if datatype == DataType.FLOAT and not (FLOAT_MAX_NEGATIVE <= value <= FLOAT_MAX_POSITIVE): # type: ignore
|
||||||
raise OverflowError("value out of range for float")
|
raise OverflowError("value out of range for float: " + str(value))
|
||||||
|
if datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||||
|
if not isinstance(value, (int, float)):
|
||||||
|
raise TypeError("cannot assign '{:s}' to {:s}".format(type(value).__name__, datatype.name.lower()))
|
||||||
if datatype in (DataType.BYTE, DataType.BYTEARRAY, DataType.MATRIX) and isinstance(value, str):
|
if datatype in (DataType.BYTE, DataType.BYTEARRAY, DataType.MATRIX) and isinstance(value, str):
|
||||||
if len(value) == 1:
|
if len(value) == 1:
|
||||||
return True, char_to_bytevalue(value)
|
return True, char_to_bytevalue(value)
|
||||||
|
@ -8,10 +8,11 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import datetime
|
import datetime
|
||||||
import itertools
|
from collections import defaultdict
|
||||||
from typing import Union, TextIO, List, Tuple, Iterator
|
from typing import Dict, TextIO, List, Any
|
||||||
from .plylex import print_bold
|
from .plylex import print_bold
|
||||||
from .plyparse import Module, ProgramFormat, Block, Directive, VarDef, Label, Subroutine, AstNode, ZpOptions
|
from .plyparse import Module, ProgramFormat, Block, Directive, VarDef, Label, Subroutine, AstNode, ZpOptions, \
|
||||||
|
InlineAssembly, Return, Register, LiteralValue
|
||||||
from .datatypes import VarType, DataType, to_hex, mflpt5_to_float, to_mflpt5, STRING_DATATYPES
|
from .datatypes import VarType, DataType, to_hex, mflpt5_to_float, to_mflpt5, STRING_DATATYPES
|
||||||
|
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ class AssemblyGenerator:
|
|||||||
def _generate(self) -> None:
|
def _generate(self) -> None:
|
||||||
self.sanitycheck()
|
self.sanitycheck()
|
||||||
self.header()
|
self.header()
|
||||||
self.initialize_variables()
|
self.init_vars_and_start()
|
||||||
self.blocks()
|
self.blocks()
|
||||||
self.footer()
|
self.footer()
|
||||||
|
|
||||||
@ -98,83 +99,46 @@ class AssemblyGenerator:
|
|||||||
self.p("; ---- raw assembler program ----")
|
self.p("; ---- raw assembler program ----")
|
||||||
self.p("* = " + to_hex(self.module.address) + "\n")
|
self.p("* = " + to_hex(self.module.address) + "\n")
|
||||||
|
|
||||||
def initialize_variables(self) -> None:
|
def init_vars_and_start(self) -> None:
|
||||||
if self.module.zp_options == ZpOptions.CLOBBER_RESTORE:
|
if self.module.zp_options == ZpOptions.CLOBBER_RESTORE:
|
||||||
self.p("\vjsr il65_lib_zp.save_zeropage")
|
self.p("\vjsr il65_lib_zp.save_zeropage")
|
||||||
zp_float_bytes = {}
|
self.p("\v; initialize all blocks (reset vars)")
|
||||||
# Only the vars from the ZeroPage need to be initialized here,
|
if self.module.zeropage():
|
||||||
# the vars in all other blocks are just defined and pre-filled there.
|
self.p("\vjsr ZP._il65_init_block")
|
||||||
zpblock = self.module.zeropage()
|
for block in self.module.nodes:
|
||||||
if zpblock:
|
if isinstance(block, Block) and block.name != "ZP":
|
||||||
vars_to_init = [v for v in zpblock.scope.filter_nodes(VarDef)
|
self.p("\vjsr {}._il65_init_block".format(block.name))
|
||||||
if v.vartype == VarType.VAR and v.vartype in (DataType.BYTE, DataType.WORD, DataType.FLOAT)]
|
self.p("\v; call user code")
|
||||||
# @todo optimize sort order (sort on value first, then type, then blockname, then address/name)
|
|
||||||
# (str(self.value) or "", self.blockname, self.name or "", self.address or 0, self.seq_nr)
|
|
||||||
prev_value = 0 # type: Union[str, int, float]
|
|
||||||
if vars_to_init:
|
|
||||||
self.p("; init zp vars")
|
|
||||||
self.p("\vlda #0\n\vldx #0")
|
|
||||||
for variable in vars_to_init:
|
|
||||||
vname = zpblock.label + '.' + variable.name
|
|
||||||
vvalue = variable.value
|
|
||||||
if variable.type == DataType.BYTE:
|
|
||||||
if vvalue != prev_value:
|
|
||||||
self.p("\vlda #${:02x}".format(vvalue))
|
|
||||||
prev_value = vvalue
|
|
||||||
self.p("\vsta {:s}".format(vname))
|
|
||||||
elif variable.type == DataType.WORD:
|
|
||||||
if vvalue != prev_value:
|
|
||||||
self.p("\vlda #<${:04x}".format(vvalue))
|
|
||||||
self.p("\vldx #>${:04x}".format(vvalue))
|
|
||||||
prev_value = vvalue
|
|
||||||
self.p("\vsta {:s}".format(vname))
|
|
||||||
self.p("\vstx {:s}+1".format(vname))
|
|
||||||
elif variable.type == DataType.FLOAT:
|
|
||||||
bytes = self.to_mflpt5(vvalue) # type: ignore
|
|
||||||
zp_float_bytes[variable.name] = (vname, bytes, vvalue)
|
|
||||||
if zp_float_bytes:
|
|
||||||
self.p("\vldx #4")
|
|
||||||
self.p("-")
|
|
||||||
for varname, (vname, b, fv) in zp_float_bytes.items():
|
|
||||||
self.p("\vlda _float_bytes_{:s},x".format(varname))
|
|
||||||
self.p("\vsta {:s},x".format(vname))
|
|
||||||
self.p("\vdex")
|
|
||||||
self.p("\vbpl -")
|
|
||||||
self.p("; end init zp vars")
|
|
||||||
else:
|
|
||||||
self.p("\v; there are no zp vars to initialize")
|
|
||||||
else:
|
|
||||||
self.p("\v; there is no zp block to initialize")
|
|
||||||
# @todo all block vars should be (re)initialized here as well!
|
|
||||||
if self.module.zp_options == ZpOptions.CLOBBER_RESTORE:
|
if self.module.zp_options == ZpOptions.CLOBBER_RESTORE:
|
||||||
self.p("\vjsr {:s}.start\v; call user code".format(self.module.main().label))
|
self.p("\vjsr {:s}.start".format(self.module.main().label))
|
||||||
self.p("\vcld")
|
self.p("\vcld")
|
||||||
self.p("\vjmp il65_lib_zp.restore_zeropage")
|
self.p("\vjmp il65_lib_zp.restore_zeropage")
|
||||||
else:
|
else:
|
||||||
self.p("\vjmp {:s}.start\v; call user code".format(self.module.main().label))
|
self.p("\vjmp {:s}.start".format(self.module.main().label))
|
||||||
self.p("")
|
self.p("")
|
||||||
for varname, (vname, bytes, fpvalue) in zp_float_bytes.items():
|
|
||||||
self.p("_float_bytes_{:s}\v.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}\t; {}".format(varname, *bytes, fpvalue))
|
|
||||||
self.p("\n")
|
|
||||||
|
|
||||||
def blocks(self) -> None:
|
def blocks(self) -> None:
|
||||||
zpblock = self.module.zeropage()
|
zpblock = self.module.zeropage()
|
||||||
if zpblock:
|
if zpblock:
|
||||||
# if there's a Zeropage block, it always goes first
|
# if there's a Zeropage block, it always goes first
|
||||||
self.cur_block = zpblock # type: ignore
|
self.cur_block = zpblock # type: ignore
|
||||||
self.p("\n; ---- zero page block: '{:s}' ----\t\t; src l. {:d}\n".format(zpblock.sourceref.file, zpblock.sourceref.line))
|
self.p("\n; ---- zero page block: '{:s}' ----".format(zpblock.name))
|
||||||
|
self.p("; file: '{:s}' src l. {:d}\n".format(zpblock.sourceref.file, zpblock.sourceref.line))
|
||||||
self.p("{:s}\t.proc\n".format(zpblock.label))
|
self.p("{:s}\t.proc\n".format(zpblock.label))
|
||||||
|
self.generate_block_init(zpblock)
|
||||||
self.generate_block_vars(zpblock)
|
self.generate_block_vars(zpblock)
|
||||||
self.p("\v.pend\n")
|
self.p("\v.pend\n")
|
||||||
for block in sorted(self.module.scope.filter_nodes(Block), key=lambda b: b.address or 0):
|
for block in sorted(self.module.scope.filter_nodes(Block), key=lambda b: b.address or 0):
|
||||||
if block.name == "ZP":
|
if block.name == "ZP":
|
||||||
continue # already processed
|
continue # already processed
|
||||||
self.cur_block = block
|
self.cur_block = block
|
||||||
self.p("\n; ---- next block: '{:s}' ----\t\t; src l. {:d}\n".format(block.sourceref.file, block.sourceref.line))
|
self.p("\n; ---- block: '{:s}' ----".format(block.name))
|
||||||
|
self.p("; file: '{:s}' src l. {:d}\n".format(block.sourceref.file, block.sourceref.line))
|
||||||
if block.address:
|
if block.address:
|
||||||
self.p(".cerror * > ${0:04x}, 'block address overlaps by ', *-${0:04x},' bytes'".format(block.address))
|
self.p(".cerror * > ${0:04x}, 'block address overlaps by ', *-${0:04x},' bytes'".format(block.address))
|
||||||
self.p("* = ${:04x}".format(block.address))
|
self.p("* = ${:04x}".format(block.address))
|
||||||
self.p("{:s}\t.proc\n".format(block.label))
|
self.p("{:s}\t.proc\n".format(block.label))
|
||||||
|
self.generate_block_init(block)
|
||||||
self.generate_block_vars(block)
|
self.generate_block_vars(block)
|
||||||
subroutines = list(sub for sub in block.scope.filter_nodes(Subroutine) if sub.address is not None)
|
subroutines = list(sub for sub in block.scope.filter_nodes(Subroutine) if sub.address is not None)
|
||||||
if subroutines:
|
if subroutines:
|
||||||
@ -185,14 +149,12 @@ class AssemblyGenerator:
|
|||||||
self.p("\v{:s} = {:s}".format(subdef.name, to_hex(subdef.address)))
|
self.p("\v{:s} = {:s}".format(subdef.name, to_hex(subdef.address)))
|
||||||
self.p("; end external subroutines\n")
|
self.p("; end external subroutines\n")
|
||||||
for stmt in block.scope.nodes:
|
for stmt in block.scope.nodes:
|
||||||
if isinstance(stmt, Directive):
|
if isinstance(stmt, (VarDef, Directive)):
|
||||||
continue # should have been handled already
|
continue # should have been handled already
|
||||||
self.generate_statement(stmt)
|
self.generate_statement(stmt)
|
||||||
if block.name == "main" and isinstance(stmt, Label) and stmt.name == "start":
|
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
|
# make sure the main.start routine clears the decimal and carry flags as first steps
|
||||||
self.p("\vcld\t\t\t; clear decimal flag")
|
self.p("\vcld\n\vclc\n\vclv")
|
||||||
self.p("\vclc\t\t\t; clear carry flag")
|
|
||||||
self.p("\vclv\t\t\t; clear overflow flag")
|
|
||||||
subroutines = list(sub for sub in block.scope.filter_nodes(Subroutine) if sub.address is None)
|
subroutines = list(sub for sub in block.scope.filter_nodes(Subroutine) if sub.address is None)
|
||||||
if subroutines:
|
if subroutines:
|
||||||
# these are subroutines that are defined by a scope/code
|
# these are subroutines that are defined by a scope/code
|
||||||
@ -208,7 +170,7 @@ class AssemblyGenerator:
|
|||||||
cur_block = self.cur_block
|
cur_block = self.cur_block
|
||||||
self.cur_block = subdef.scope
|
self.cur_block = subdef.scope
|
||||||
for stmt in subdef.scope.nodes:
|
for stmt in subdef.scope.nodes:
|
||||||
if isinstance(stmt, Directive):
|
if isinstance(stmt, (VarDef, Directive)):
|
||||||
continue # should have been handled already
|
continue # should have been handled already
|
||||||
self.generate_statement(stmt)
|
self.generate_statement(stmt)
|
||||||
self.cur_block = cur_block
|
self.cur_block = cur_block
|
||||||
@ -249,99 +211,172 @@ class AssemblyGenerator:
|
|||||||
result += '", {:d}, "'.format(ord(char))
|
result += '", {:d}, "'.format(ord(char))
|
||||||
return result + '"'
|
return result + '"'
|
||||||
|
|
||||||
def generate_block_vars(self, block: Block) -> None:
|
def generate_block_init(self, block: Block) -> None:
|
||||||
# @todo block vars should be re-initialized when the program is run again, and not depend on statically prefilled data!
|
# generate the block initializer
|
||||||
vars_by_vartype = itertools.groupby(block.scope.filter_nodes(VarDef), lambda c: c.vartype)
|
# @todo add a block initializer subroutine that can contain custom reset/init code? (static initializers)
|
||||||
variable_definitions = sorted(vars_by_vartype, key=lambda gt: gt[0]) # type: List[Tuple[VarType, Iterator[VarDef]]]
|
self.p("_il65_init_block\v; (re)set vars to initial values")
|
||||||
for vartype, varnodes in variable_definitions:
|
# @todo optimize init order (sort on value first to avoid needless register loads, etc)
|
||||||
if vartype == VarType.CONST:
|
self.p("\vlda #0\n\vldx #0")
|
||||||
self.p("; constants")
|
float_inits = {}
|
||||||
for vardef in varnodes:
|
string_inits = []
|
||||||
if vardef.datatype == DataType.FLOAT:
|
prev_value = 0
|
||||||
self.p("\t\t{:s} = {}".format(vardef.name, vardef.value))
|
for variable in [vd for vd in block.scope.filter_nodes(VarDef) if vd.vartype == VarType.VAR]:
|
||||||
elif vardef.datatype in (DataType.BYTE, DataType.WORD):
|
vname = variable.name
|
||||||
self.p("\t\t{:s} = {:s}".format(vardef.name, to_hex(vardef.value)))
|
vvalue = variable.value
|
||||||
elif vardef.datatype in STRING_DATATYPES:
|
if variable.datatype == DataType.BYTE:
|
||||||
# a const string is just a string variable in the generated assembly
|
if vvalue != prev_value:
|
||||||
self._generate_string_var(vardef)
|
self.p("\vlda #${:02x}".format(vvalue))
|
||||||
else:
|
prev_value = vvalue
|
||||||
raise CodeError("invalid const type", vardef)
|
self.p("\vsta {:s}".format(vname))
|
||||||
elif vartype == VarType.MEMORY:
|
elif variable.datatype == DataType.WORD:
|
||||||
self.p("; memory mapped variables")
|
if vvalue != prev_value:
|
||||||
for vardef in varnodes:
|
self.p("\vlda #<${:04x}".format(vvalue))
|
||||||
# create a definition for variables at a specific place in memory (memory-mapped)
|
self.p("\vldx #>${:04x}".format(vvalue))
|
||||||
if vardef.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
prev_value = vvalue
|
||||||
assert vardef.size == [1]
|
self.p("\vsta {:s}".format(vname))
|
||||||
self.p("\t\t{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value), vardef.datatype.name.lower()))
|
self.p("\vstx {:s}+1".format(vname))
|
||||||
elif vardef.datatype == DataType.BYTEARRAY:
|
elif variable.datatype == DataType.FLOAT:
|
||||||
assert len(vardef.size) == 1
|
fpbytes = to_mflpt5(vvalue) # type: ignore
|
||||||
self.p("\t\t{:s} = {:s}\t; array of {:d} bytes".format(vardef.name, to_hex(vardef.value), vardef.size[0]))
|
float_inits[variable.name] = (vname, fpbytes, vvalue)
|
||||||
elif vardef.datatype == DataType.WORDARRAY:
|
elif variable.datatype in STRING_DATATYPES:
|
||||||
assert len(vardef.size) == 1
|
string_inits.append(variable)
|
||||||
self.p("\t\t{:s} = {:s}\t; array of {:d} words".format(vardef.name, to_hex(vardef.value), vardef.size[0]))
|
else:
|
||||||
elif vardef.datatype == DataType.MATRIX:
|
raise CodeError("weird var datatype", variable.datatype)
|
||||||
assert len(vardef.size) == 2
|
if float_inits:
|
||||||
self.p("\t\t{:s} = {:s}\t; matrix {:d} by {:d} = {:d} bytes"
|
self.p("\vldx #4")
|
||||||
.format(vardef.name, to_hex(vardef.value), vardef.size[0], vardef.size[1], vardef.size[0]*vardef.size[1]))
|
self.p("-")
|
||||||
else:
|
for varname, (vname, b, fv) in sorted(float_inits.items()):
|
||||||
raise CodeError("invalid var type")
|
self.p("\vlda _init_float_{:s},x".format(varname))
|
||||||
elif vartype == VarType.VAR:
|
self.p("\vsta {:s},x".format(vname))
|
||||||
self.p("; normal variables")
|
self.p("\vdex")
|
||||||
for vardef in varnodes:
|
self.p("\vbpl -")
|
||||||
# create a definition for a variable that takes up space and will be initialized at startup
|
if string_inits:
|
||||||
if vardef.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
pass # @todo init string block (1 memcopy)
|
||||||
assert vardef.size == [1]
|
self.p("\vrts\n")
|
||||||
if vardef.datatype == DataType.BYTE:
|
for varname, (vname, fpbytes, fpvalue) in sorted(float_inits.items()):
|
||||||
self.p("{:s}\t\t.byte {:s}".format(vardef.name, to_hex(int(vardef.value or 0))))
|
self.p("_init_float_{:s}\t\t.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}\t; {}".format(varname, *fpbytes, fpvalue))
|
||||||
elif vardef.datatype == DataType.WORD:
|
if string_inits:
|
||||||
self.p("{:s}\t\t.word {:s}".format(vardef.name, to_hex(int(vardef.value or 0))))
|
self.p("_init_strings_start")
|
||||||
elif vardef.datatype == DataType.FLOAT:
|
for svar in sorted(string_inits, key=lambda v: v.name):
|
||||||
self.p("{:s}\t\t.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}{:s}"
|
self._generate_string_var(svar, init=True)
|
||||||
.format(vardef.name, *to_mflpt5(float(vardef.value or 0.0))))
|
self.p("_init_strings_size = * - _init_strings_start")
|
||||||
else:
|
self.p("")
|
||||||
raise CodeError("weird datatype")
|
|
||||||
elif vardef.datatype in (DataType.BYTEARRAY, DataType.WORDARRAY):
|
|
||||||
assert len(vardef.size) == 1
|
|
||||||
if vardef.datatype == DataType.BYTEARRAY:
|
|
||||||
self.p("{:s}\t\t.fill {:d}, ${:02x}".format(vardef.name, vardef.size[0], vardef.value or 0))
|
|
||||||
elif vardef.datatype == DataType.WORDARRAY:
|
|
||||||
f_hi, f_lo = divmod(vardef.value or 0, 256) # type: ignore
|
|
||||||
self.p("{:s}\t\t.fill {:d}, [${:02x}, ${:02x}]\t; {:d} words of ${:04x}"
|
|
||||||
.format(vardef.name, vardef.size[0] * 2, f_lo, f_hi, vardef.size[0], vardef.value or 0))
|
|
||||||
else:
|
|
||||||
raise CodeError("invalid datatype", vardef.datatype)
|
|
||||||
elif vardef.datatype == DataType.MATRIX:
|
|
||||||
assert len(vardef.size) == 2
|
|
||||||
self.p("{:s}\t\t.fill {:d}, ${:02x}\t\t; matrix {:d}*{:d} bytes"
|
|
||||||
.format(vardef.name, vardef.size[0] * vardef.size[1], vardef.value or 0, vardef.size[0], vardef.size[1]))
|
|
||||||
elif vardef.datatype in STRING_DATATYPES:
|
|
||||||
self._generate_string_var(vardef)
|
|
||||||
else:
|
|
||||||
raise CodeError("unknown variable type " + str(vardef.datatype))
|
|
||||||
|
|
||||||
def _generate_string_var(self, vardef: VarDef) -> None:
|
def generate_block_vars(self, block: Block) -> None:
|
||||||
|
# Generate the block variable storage.
|
||||||
|
# The memory bytes of the allocated variables is set to zero (so it compresses very well),
|
||||||
|
# their actual starting values are set by the block init code.
|
||||||
|
vars_by_vartype = defaultdict(list) # type: Dict[VarType, List[VarDef]]
|
||||||
|
for vardef in block.scope.filter_nodes(VarDef):
|
||||||
|
vars_by_vartype[vardef.vartype].append(vardef)
|
||||||
|
self.p("; constants")
|
||||||
|
for vardef in vars_by_vartype.get(VarType.CONST, []):
|
||||||
|
if vardef.datatype == DataType.FLOAT:
|
||||||
|
self.p("\v{:s} = {}".format(vardef.name, vardef.value))
|
||||||
|
elif vardef.datatype in (DataType.BYTE, DataType.WORD):
|
||||||
|
self.p("\v{:s} = {:s}".format(vardef.name, to_hex(vardef.value)))
|
||||||
|
elif vardef.datatype in STRING_DATATYPES:
|
||||||
|
# a const string is just a string variable in the generated assembly
|
||||||
|
self._generate_string_var(vardef)
|
||||||
|
else:
|
||||||
|
raise CodeError("invalid const type", vardef)
|
||||||
|
self.p("; memory mapped variables")
|
||||||
|
for vardef in vars_by_vartype.get(VarType.MEMORY, []):
|
||||||
|
# create a definition for variables at a specific place in memory (memory-mapped)
|
||||||
|
if vardef.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||||
|
assert vardef.size == [1]
|
||||||
|
self.p("\v{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value), vardef.datatype.name.lower()))
|
||||||
|
elif vardef.datatype == DataType.BYTEARRAY:
|
||||||
|
assert len(vardef.size) == 1
|
||||||
|
self.p("\v{:s} = {:s}\t; array of {:d} bytes".format(vardef.name, to_hex(vardef.value), vardef.size[0]))
|
||||||
|
elif vardef.datatype == DataType.WORDARRAY:
|
||||||
|
assert len(vardef.size) == 1
|
||||||
|
self.p("\v{:s} = {:s}\t; array of {:d} words".format(vardef.name, to_hex(vardef.value), vardef.size[0]))
|
||||||
|
elif vardef.datatype == DataType.MATRIX:
|
||||||
|
assert len(vardef.size) == 2
|
||||||
|
self.p("\v{:s} = {:s}\t; matrix of {:d} by {:d} = {:d} bytes"
|
||||||
|
.format(vardef.name, to_hex(vardef.value), vardef.size[0], vardef.size[1], vardef.size[0]*vardef.size[1]))
|
||||||
|
else:
|
||||||
|
raise CodeError("invalid var type")
|
||||||
|
self.p("; normal variables - initial values will be set by init code")
|
||||||
|
string_vars = []
|
||||||
|
for vardef in vars_by_vartype.get(VarType.VAR, []):
|
||||||
|
# create a definition for a variable that takes up empty space and will be initialized at startup
|
||||||
|
if vardef.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||||
|
assert vardef.size == [1]
|
||||||
|
if vardef.datatype == DataType.BYTE:
|
||||||
|
self.p("{:s}\v.byte ?".format(vardef.name))
|
||||||
|
elif vardef.datatype == DataType.WORD:
|
||||||
|
self.p("{:s}\v.word ?".format(vardef.name))
|
||||||
|
elif vardef.datatype == DataType.FLOAT:
|
||||||
|
self.p("{:s}\v.fill 5\t\t; float".format(vardef.name))
|
||||||
|
else:
|
||||||
|
raise CodeError("weird datatype")
|
||||||
|
elif vardef.datatype in (DataType.BYTEARRAY, DataType.WORDARRAY):
|
||||||
|
assert len(vardef.size) == 1
|
||||||
|
if vardef.datatype == DataType.BYTEARRAY:
|
||||||
|
self.p("{:s}\v.fill {:d}\t\t; bytearray".format(vardef.name, vardef.size[0]))
|
||||||
|
elif vardef.datatype == DataType.WORDARRAY:
|
||||||
|
self.p("{:s}\v.fill {:d}*2\t\t; wordarray".format(vardef.name, vardef.size[0]))
|
||||||
|
else:
|
||||||
|
raise CodeError("invalid datatype", vardef.datatype)
|
||||||
|
elif vardef.datatype == DataType.MATRIX:
|
||||||
|
assert len(vardef.size) == 2
|
||||||
|
self.p("{:s}\v.fill {:d}\t\t; matrix {:d}*{:d} bytes"
|
||||||
|
.format(vardef.name, vardef.size[0] * vardef.size[1], vardef.size[0], vardef.size[1]))
|
||||||
|
elif vardef.datatype in STRING_DATATYPES:
|
||||||
|
string_vars.append(vardef)
|
||||||
|
else:
|
||||||
|
raise CodeError("unknown variable type " + str(vardef.datatype))
|
||||||
|
if string_vars:
|
||||||
|
self.p("il65_string_vars_start")
|
||||||
|
for sv in sorted(string_vars): # must be the same order as in the init routine!!!
|
||||||
|
self.p("{:s}\v.fill {:d}+1\t\t; {}".format(sv.name, len(sv.value), sv.datatype.name.lower()))
|
||||||
|
self.p("")
|
||||||
|
|
||||||
|
def _generate_string_var(self, vardef: VarDef, init: bool=False) -> None:
|
||||||
|
prefix = "_init_str_" if init else ""
|
||||||
if vardef.datatype == DataType.STRING:
|
if vardef.datatype == DataType.STRING:
|
||||||
# 0-terminated string
|
# 0-terminated string
|
||||||
self.p("{:s}\n\v.null {:s}".format(vardef.name, self.output_string(str(vardef.value))))
|
self.p("{:s}{:s}\n\v.null {:s}".format(prefix, vardef.name, self.output_string(str(vardef.value))))
|
||||||
elif vardef.datatype == DataType.STRING_P:
|
elif vardef.datatype == DataType.STRING_P:
|
||||||
# pascal string
|
# pascal string
|
||||||
self.p("{:s}\n\v.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value))))
|
self.p("{:s}{:s}\n\v.ptext {:s}".format(prefix, vardef.name, self.output_string(str(vardef.value))))
|
||||||
elif vardef.datatype == DataType.STRING_S:
|
elif vardef.datatype == DataType.STRING_S:
|
||||||
# 0-terminated string in screencode encoding
|
# 0-terminated string in screencode encoding
|
||||||
self.p(".enc 'screen'")
|
self.p(".enc 'screen'")
|
||||||
self.p("{:s}\n\v.null {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
|
self.p("{:s}{:s}\n\v.null {:s}".format(prefix, vardef.name, self.output_string(str(vardef.value), True)))
|
||||||
self.p(".enc 'none'")
|
self.p(".enc 'none'")
|
||||||
elif vardef.datatype == DataType.STRING_PS:
|
elif vardef.datatype == DataType.STRING_PS:
|
||||||
# 0-terminated pascal string in screencode encoding
|
# 0-terminated pascal string in screencode encoding
|
||||||
self.p(".enc 'screen'")
|
self.p(".enc 'screen'")
|
||||||
self.p("{:s}\n\v.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
|
self.p("{:s}{:s}n\v.ptext {:s}".format(prefix, vardef.name, self.output_string(str(vardef.value), True)))
|
||||||
self.p(".enc 'none'")
|
self.p(".enc 'none'")
|
||||||
|
|
||||||
def generate_statement(self, stmt: AstNode) -> None:
|
def generate_statement(self, stmt: AstNode) -> None:
|
||||||
if isinstance(stmt, Label):
|
if isinstance(stmt, Label):
|
||||||
self.p("\n{:s}\v\t\t; {:s}".format(stmt.name, stmt.lineref))
|
self.p("\n{:s}\v\t\t; {:s}".format(stmt.name, stmt.lineref))
|
||||||
self.p("\vrts")
|
elif isinstance(stmt, Return):
|
||||||
# @todo rest of the statement nodes
|
if stmt.value_A:
|
||||||
|
self.generate_assignment(Register(name="A", sourceref=stmt.sourceref), '=', stmt.value_A) # type: ignore
|
||||||
|
if stmt.value_X:
|
||||||
|
self.generate_assignment(Register(name="X", sourceref=stmt.sourceref), '=', stmt.value_X) # type: ignore
|
||||||
|
if stmt.value_Y:
|
||||||
|
self.generate_assignment(Register(name="Y", sourceref=stmt.sourceref), '=', stmt.value_Y) # type: ignore
|
||||||
|
self.p("\vrts")
|
||||||
|
elif isinstance(stmt, InlineAssembly):
|
||||||
|
self.p("\n\v; inline asm, " + stmt.lineref)
|
||||||
|
self.p(stmt.assembly)
|
||||||
|
self.p("\v; end inline asm, " + stmt.lineref + "\n")
|
||||||
|
else:
|
||||||
|
self.p("\vrts; " + str(stmt)) # @todo rest of the statement nodes
|
||||||
|
|
||||||
|
def generate_assignment(self, lvalue: AstNode, operator: str, rvalue: Any) -> None:
|
||||||
|
assert rvalue is not None
|
||||||
|
if isinstance(rvalue, LiteralValue):
|
||||||
|
rvalue = rvalue.value
|
||||||
|
print("ASSIGN", lvalue, lvalue.datatype, operator, rvalue)
|
||||||
|
# @todo
|
||||||
|
|
||||||
|
|
||||||
class Assembler64Tass:
|
class Assembler64Tass:
|
||||||
|
@ -62,7 +62,6 @@ zp_backup .fill 256, 0
|
|||||||
|
|
||||||
|
|
||||||
%asm {
|
%asm {
|
||||||
|
|
||||||
; ---- jmp (indirect) routines for register pairs containing the indirect address
|
; ---- jmp (indirect) routines for register pairs containing the indirect address
|
||||||
jsr_indirect_nozpuse_AX
|
jsr_indirect_nozpuse_AX
|
||||||
sta jsr_indirect_tmp
|
sta jsr_indirect_tmp
|
||||||
@ -93,5 +92,48 @@ jsr_indirect_XY
|
|||||||
sty SCRATCH_ZP2
|
sty SCRATCH_ZP2
|
||||||
jmp (SCRATCH_ZP1)
|
jmp (SCRATCH_ZP1)
|
||||||
|
|
||||||
|
|
||||||
|
; copy memory UP from (SCRATCH_ZPWORD1) to (SCRATCH_ZPWORD2) of length X/Y (16-bit, X=lo, Y=hi)
|
||||||
|
; clobbers register A,X,Y
|
||||||
|
memcopy16_up
|
||||||
|
source = SCRATCH_ZPWORD1
|
||||||
|
dest = SCRATCH_ZPWORD2
|
||||||
|
length = SCRATCH_ZP1 ; (and SCRATCH_ZP2)
|
||||||
|
|
||||||
|
stx length
|
||||||
|
sty length+1
|
||||||
|
|
||||||
|
ldx length ; move low byte of length into X
|
||||||
|
bne + ; jump to start if X > 0
|
||||||
|
dec length ; subtract 1 from length
|
||||||
|
+ ldy #0 ; set Y to 0
|
||||||
|
- lda (source),y ; set A to whatever (source) points to offset by Y
|
||||||
|
sta (dest),y ; move A to location pointed to by (dest) offset by Y
|
||||||
|
iny ; increment Y
|
||||||
|
bne + ; if Y<>0 then (rolled over) then still moving bytes
|
||||||
|
inc source+1 ; increment hi byte of source
|
||||||
|
inc dest+1 ; increment hi byte of dest
|
||||||
|
+ dex ; decrement X (lo byte counter)
|
||||||
|
bne - ; if X<>0 then move another byte
|
||||||
|
dec length ; weve moved 255 bytes, dec length
|
||||||
|
bpl - ; if length is still positive go back and move more
|
||||||
|
rts ; done
|
||||||
|
|
||||||
|
|
||||||
|
; copy memory UP from (SCRATCH_ZPWORD1) to (AY) with length X (0 = 256)
|
||||||
|
; destination must not overlap, or be before start, then overlap is possible.
|
||||||
|
; clobbers A, X, Y
|
||||||
|
|
||||||
|
memcopy
|
||||||
|
sta SCRATCH_ZPWORD2
|
||||||
|
sty SCRATCH_ZPWORD2+1
|
||||||
|
ldy #0
|
||||||
|
- lda (SCRATCH_ZPWORD1),y
|
||||||
|
sta (SCRATCH_ZPWORD2),y
|
||||||
|
iny
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
rts
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,8 @@ class Optimizer:
|
|||||||
if not usages and parent.name + '.' + sub.name not in never_remove:
|
if not usages and parent.name + '.' + sub.name not in never_remove:
|
||||||
parent.scope.remove_node(sub)
|
parent.scope.remove_node(sub)
|
||||||
num_discarded += 1
|
num_discarded += 1
|
||||||
print("discarded {:d} unused subroutines".format(num_discarded))
|
if num_discarded:
|
||||||
|
print("discarded {:d} unused subroutines".format(num_discarded))
|
||||||
|
|
||||||
def optimize_compare_with_zero(self):
|
def optimize_compare_with_zero(self):
|
||||||
# a conditional goto that compares a value with zero will be simplified
|
# a conditional goto that compares a value with zero will be simplified
|
||||||
|
@ -14,7 +14,7 @@ from typing import Union, Generator, Tuple, List, Optional, Dict, Any, Iterable
|
|||||||
import attr
|
import attr
|
||||||
from ply.yacc import yacc
|
from ply.yacc import yacc
|
||||||
from .plylex import SourceRef, tokens, lexer, find_tok_column
|
from .plylex import SourceRef, tokens, lexer, find_tok_column
|
||||||
from .datatypes import DataType, VarType, coerce_value
|
from .datatypes import DataType, VarType, coerce_value, REGISTER_SYMBOLS, REGISTER_BYTES, REGISTER_WORDS
|
||||||
|
|
||||||
|
|
||||||
class ProgramFormat(enum.Enum):
|
class ProgramFormat(enum.Enum):
|
||||||
@ -261,7 +261,14 @@ class Label(AstNode):
|
|||||||
|
|
||||||
@attr.s(cmp=False, repr=False)
|
@attr.s(cmp=False, repr=False)
|
||||||
class Register(AstNode):
|
class Register(AstNode):
|
||||||
name = attr.ib(type=str)
|
name = attr.ib(type=str, validator=attr.validators.in_(REGISTER_SYMBOLS))
|
||||||
|
datatype = attr.ib(type=DataType, init=False)
|
||||||
|
|
||||||
|
def __attrs_post_init__(self):
|
||||||
|
if self.name in REGISTER_BYTES:
|
||||||
|
self.datatype = DataType.BYTE
|
||||||
|
elif self.name in REGISTER_WORDS:
|
||||||
|
self.datatype = DataType.WORD
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return hash(self.name)
|
return hash(self.name)
|
||||||
@ -337,11 +344,26 @@ class Return(AstNode):
|
|||||||
|
|
||||||
def process_expressions(self, scope: Scope) -> None:
|
def process_expressions(self, scope: Scope) -> None:
|
||||||
if self.value_A is not None:
|
if self.value_A is not None:
|
||||||
self.value_A = process_expression(self.value_A, scope, self.value_A.sourceref)
|
self.value_A = process_expression(self.value_A, scope, self.sourceref)
|
||||||
|
if isinstance(self.value_A, (int, float, str, bool)):
|
||||||
|
try:
|
||||||
|
_, self.value_A = coerce_value(DataType.BYTE, self.value_A, self.sourceref)
|
||||||
|
except (OverflowError, TypeError) as x:
|
||||||
|
raise ParseError("first value (A): " + str(x), self.sourceref) from None
|
||||||
if self.value_X is not None:
|
if self.value_X is not None:
|
||||||
self.value_X = process_expression(self.value_X, scope, self.value_X.sourceref)
|
self.value_X = process_expression(self.value_X, scope, self.sourceref)
|
||||||
|
if isinstance(self.value_X, (int, float, str, bool)):
|
||||||
|
try:
|
||||||
|
_, self.value_X = coerce_value(DataType.BYTE, self.value_X, self.sourceref)
|
||||||
|
except (OverflowError, TypeError) as x:
|
||||||
|
raise ParseError("second value (X): " + str(x), self.sourceref) from None
|
||||||
if self.value_Y is not None:
|
if self.value_Y is not None:
|
||||||
self.value_Y = process_expression(self.value_Y, scope, self.value_Y.sourceref)
|
self.value_Y = process_expression(self.value_Y, scope, self.sourceref)
|
||||||
|
if isinstance(self.value_Y, (int, float, str, bool)):
|
||||||
|
try:
|
||||||
|
_, self.value_Y = coerce_value(DataType.BYTE, self.value_Y, self.sourceref)
|
||||||
|
except (OverflowError, TypeError) as x:
|
||||||
|
raise ParseError("third value (Y): " + str(x), self.sourceref) from None
|
||||||
|
|
||||||
|
|
||||||
@attr.s(cmp=False, repr=False)
|
@attr.s(cmp=False, repr=False)
|
||||||
@ -384,6 +406,9 @@ class VarDef(AstNode):
|
|||||||
assert self.size is None
|
assert self.size is None
|
||||||
self.size = self.datatype.dimensions or [1]
|
self.size = self.datatype.dimensions or [1]
|
||||||
self.datatype = self.datatype.to_enum()
|
self.datatype = self.datatype.to_enum()
|
||||||
|
if self.vartype == VarType.CONST and self.value is None:
|
||||||
|
raise ParseError("constant value assignment is missing",
|
||||||
|
attr.evolve(self.sourceref, column=self.sourceref.column+len(self.name)))
|
||||||
# if the value is an expression, mark it as a *constant* expression here
|
# if the value is an expression, mark it as a *constant* expression here
|
||||||
if isinstance(self.value, Expression):
|
if isinstance(self.value, Expression):
|
||||||
self.value.processed_must_be_constant = True
|
self.value.processed_must_be_constant = True
|
||||||
@ -397,7 +422,7 @@ class VarDef(AstNode):
|
|||||||
if self.vartype in (VarType.CONST, VarType.VAR):
|
if self.vartype in (VarType.CONST, VarType.VAR):
|
||||||
try:
|
try:
|
||||||
_, self.value = coerce_value(self.datatype, self.value, self.sourceref)
|
_, self.value = coerce_value(self.datatype, self.value, self.sourceref)
|
||||||
except OverflowError as x:
|
except (TypeError, OverflowError) as x:
|
||||||
raise ParseError(str(x), self.sourceref) from None
|
raise ParseError(str(x), self.sourceref) from None
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,6 +96,10 @@ def test_coerce_value():
|
|||||||
assert datatypes.coerce_value(datatypes.DataType.FLOAT, 123.45) == (False, 123.45)
|
assert datatypes.coerce_value(datatypes.DataType.FLOAT, 123.45) == (False, 123.45)
|
||||||
assert datatypes.coerce_value(datatypes.DataType.BYTE, 5.678) == (True, 5)
|
assert datatypes.coerce_value(datatypes.DataType.BYTE, 5.678) == (True, 5)
|
||||||
assert datatypes.coerce_value(datatypes.DataType.WORD, 5.678) == (True, 5)
|
assert datatypes.coerce_value(datatypes.DataType.WORD, 5.678) == (True, 5)
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.STRING, "string") == (False, "string")
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.STRING_P, "string") == (False, "string")
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.STRING_S, "string") == (False, "string")
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.STRING_PS, "string") == (False, "string")
|
||||||
with pytest.raises(OverflowError):
|
with pytest.raises(OverflowError):
|
||||||
datatypes.coerce_value(datatypes.DataType.BYTE, -1)
|
datatypes.coerce_value(datatypes.DataType.BYTE, -1)
|
||||||
with pytest.raises(OverflowError):
|
with pytest.raises(OverflowError):
|
||||||
@ -112,3 +116,9 @@ def test_coerce_value():
|
|||||||
datatypes.coerce_value(datatypes.DataType.FLOAT, -1.7014118346e+38)
|
datatypes.coerce_value(datatypes.DataType.FLOAT, -1.7014118346e+38)
|
||||||
with pytest.raises(OverflowError):
|
with pytest.raises(OverflowError):
|
||||||
datatypes.coerce_value(datatypes.DataType.FLOAT, 1.7014118347e+38)
|
datatypes.coerce_value(datatypes.DataType.FLOAT, 1.7014118347e+38)
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.BYTE, "string")
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.WORD, "string")
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.FLOAT, "string")
|
||||||
|
231
todo.ill
231
todo.ill
@ -1,226 +1,29 @@
|
|||||||
%output basic
|
%output basic
|
||||||
%saveregisters
|
|
||||||
%import c64lib
|
|
||||||
%import mathlib
|
|
||||||
|
|
||||||
|
~ ZP {
|
||||||
|
var zp1_1
|
||||||
|
var zp1_2
|
||||||
|
var zp1_3
|
||||||
|
var zp1_4
|
||||||
|
const zpc1_1
|
||||||
|
const zpc1_2
|
||||||
|
}
|
||||||
|
|
||||||
|
~ ZP {
|
||||||
|
var zp2_1
|
||||||
|
var zp2_2
|
||||||
|
var zp2_3
|
||||||
|
var zp2_4
|
||||||
|
const zpc2_1
|
||||||
|
const zpc2_2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
~ main {
|
~ main {
|
||||||
|
|
||||||
%saveregisters true
|
|
||||||
|
|
||||||
|
|
||||||
const num = 2 + max(2, 8, 3.44//3)
|
|
||||||
const pi_val = 22/7 - 2.23423
|
|
||||||
var var1 =2 + 9/4
|
|
||||||
var .word wvar2 = 2 + cos(23.2)
|
|
||||||
memory memvar = $d020
|
|
||||||
var .word test2b = &memvar
|
|
||||||
var test3 = var1
|
|
||||||
|
|
||||||
|
|
||||||
start:
|
start:
|
||||||
|
|
||||||
wvar1 = 2+foo()+emptysub2
|
|
||||||
|
|
||||||
A=math.randbyte()
|
|
||||||
A += c64.RASTER
|
|
||||||
A-=c64.TIME_LO
|
|
||||||
X,A=math.divmod_bytes(A, 99)
|
|
||||||
A=B=C=foo()
|
|
||||||
c64scr.print_byte_decimal(A)
|
|
||||||
c64.CHROUT('\n')
|
|
||||||
return
|
|
||||||
|
|
||||||
screen = border = cursor = X = Y = A = X = Y = A = border = cursor = border = cursor = 66 ; multi-assign!
|
|
||||||
X = Y = A = X = Y = A = X = Y = A = X = Y = AX = Y = A = X = AY = XY =A = 123 ; multi-assign!
|
|
||||||
XY = XY
|
|
||||||
A= A
|
|
||||||
A=X=Y=A
|
|
||||||
|
|
||||||
|
|
||||||
rndloop:
|
|
||||||
XY = math.randword()
|
|
||||||
%asm {
|
|
||||||
txa
|
|
||||||
sta $0400,y
|
|
||||||
tya
|
|
||||||
sta $0500,x
|
|
||||||
}
|
|
||||||
[wvar1] = 81 ; @todo implement pointers like this
|
|
||||||
goto rndloop
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
A = math.randbyte()
|
|
||||||
Y=A
|
|
||||||
%asm {
|
|
||||||
txa
|
|
||||||
sta $0400,y
|
|
||||||
}
|
|
||||||
X--
|
|
||||||
if_ne goto rndloop
|
|
||||||
%asm {
|
|
||||||
iny
|
|
||||||
sty math.randbyte._seed
|
|
||||||
}
|
|
||||||
goto rndloop
|
|
||||||
return
|
|
||||||
|
|
||||||
goto start
|
|
||||||
|
|
||||||
X <<= var1
|
|
||||||
X >>= var1
|
|
||||||
|
|
||||||
var1 ++
|
|
||||||
var1 += num
|
|
||||||
X++
|
|
||||||
X+=num
|
|
||||||
X+=0
|
|
||||||
X-=0
|
|
||||||
X <<= Y
|
|
||||||
A <<= X
|
|
||||||
Y <<= A
|
|
||||||
X <<= 0
|
|
||||||
X <<= 33333
|
|
||||||
X >>= 33333
|
|
||||||
X <<= 2
|
|
||||||
X <<= 7
|
|
||||||
X <<= 8
|
|
||||||
X <<= 22
|
|
||||||
X >>= 0
|
|
||||||
X >>= 1
|
|
||||||
X >>= 2
|
|
||||||
X >>= 7
|
|
||||||
X >>= 8
|
|
||||||
X >>= 22
|
|
||||||
XY = 1
|
|
||||||
AX = 2
|
|
||||||
SC =1
|
|
||||||
|
|
||||||
var QW
|
|
||||||
QW =3
|
|
||||||
;XY <<= 0
|
|
||||||
;XY <<= 1
|
|
||||||
;XY <<= 2
|
|
||||||
|
|
||||||
|
|
||||||
%asm {
|
|
||||||
ldy #200
|
|
||||||
- lda #81
|
|
||||||
sta c64.Screen+39-40,y
|
|
||||||
sta c64.Screen+39+4*40,y
|
|
||||||
sta c64.Screen+39+9*40,y
|
|
||||||
sta c64.Screen+39+14*40,y
|
|
||||||
sta c64.Screen+39+19*40,y
|
|
||||||
lda #83
|
|
||||||
sta c64.Screen-40,y
|
|
||||||
sta c64.Screen+4*40,y
|
|
||||||
sta c64.Screen+9*40,y
|
|
||||||
sta c64.Screen+14*40,y
|
|
||||||
sta c64.Screen+19*40,y
|
|
||||||
lda #1
|
|
||||||
sta c64.Colors+39-40,y
|
|
||||||
sta c64.Colors+39+4*40,y
|
|
||||||
sta c64.Colors+39+9*40,y
|
|
||||||
sta c64.Colors+39+14*40,y
|
|
||||||
sta c64.Colors+39+19*40,y
|
|
||||||
lda #5
|
|
||||||
sta c64.Colors-40,y
|
|
||||||
sta c64.Colors+4*40,y
|
|
||||||
sta c64.Colors+9*40,y
|
|
||||||
sta c64.Colors+14*40,y
|
|
||||||
sta c64.Colors+19*40,y
|
|
||||||
tya
|
|
||||||
sec
|
|
||||||
sbc #40
|
|
||||||
tay
|
|
||||||
bne -
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
loop :
|
|
||||||
A=c64.GETIN()
|
|
||||||
if_not goto loop
|
|
||||||
c64scr.scroll_right_full(1)
|
|
||||||
goto loop
|
|
||||||
c64.CHROUT(A)
|
|
||||||
goto loop
|
|
||||||
|
|
||||||
;c64scr.print_byte_hex(0, A)
|
|
||||||
;c64.CHROUT(' ')
|
|
||||||
;goto loop
|
|
||||||
|
|
||||||
;return
|
|
||||||
|
|
||||||
A = $11
|
|
||||||
A = $11
|
|
||||||
A = $11
|
|
||||||
X = $11
|
|
||||||
Y = $11
|
|
||||||
X = $11
|
|
||||||
Y = $11
|
|
||||||
X = $22
|
|
||||||
Y = $33
|
|
||||||
|
|
||||||
c64scr.clear_screen (81, 5)
|
|
||||||
c64scr.clear_screen (81, 5)
|
|
||||||
c64scr.clear_screen (81, 5)
|
|
||||||
c64scr.clear_screen (81, 5)
|
|
||||||
c64scr.clear_screen ! (81, 5)
|
|
||||||
c64scr.clear_screen ! (81, 5)
|
|
||||||
c64scr.clear_screen !(81, 5)
|
|
||||||
c64scr.clear_screen !(81, 5)
|
|
||||||
c64scr.clear_screen !A (81, 5)
|
|
||||||
c64scr.clear_screen !X (81, 5)
|
|
||||||
c64scr.clear_screen !Y (81, 5)
|
|
||||||
c64scr.clear_screen !XY (81, 5)
|
|
||||||
c64scr.clear_screen !AXY (81, 5)
|
|
||||||
|
|
||||||
c64scr.print_byte_hex(1,A)
|
|
||||||
c64.CHROUT(' ')
|
|
||||||
c64scr.print_byte_hex(1,X)
|
|
||||||
c64.CHROUT(' ')
|
|
||||||
c64scr.print_byte_hex(1,Y)
|
|
||||||
c64scr.print_word_decimal(1222)
|
|
||||||
c64.CHROUT('\n')
|
|
||||||
|
|
||||||
%breakpoint
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
sub sub1 () -> () {
|
|
||||||
|
|
||||||
%saveregisters no
|
|
||||||
%breakpoint
|
|
||||||
%breakpoint
|
|
||||||
%breakpoint
|
|
||||||
|
|
||||||
label:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub emptysub () -> () {
|
|
||||||
|
|
||||||
%saveregisters yes
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
~ zzzz {
|
|
||||||
|
|
||||||
%saveregisters
|
|
||||||
%breakpoint
|
|
||||||
|
|
||||||
return 999990 + (2*sin(1.0)) + foo(), 999990 -1, 999999
|
|
||||||
|
|
||||||
}
|
|
||||||
~ {
|
|
||||||
;sdfsdf
|
|
||||||
return 999, -1, 3.445
|
|
||||||
;sdfsdf
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user