changes to call

This commit is contained in:
Irmen de Jong 2017-12-23 14:36:23 +01:00
parent fbb4ba4bd8
commit 37f049ee54
11 changed files with 316 additions and 253 deletions

View File

@ -110,13 +110,6 @@ def parse_expr_as_primitive(text: str, context: Optional[SymbolTable], ppcontext
raise src.to_error("int or float or string expected, not " + type(result).__name__) raise src.to_error("int or float or string expected, not " + type(result).__name__)
def parse_statement(text: str, sourceref: SourceRef) -> int: # @todo in progress...
src = SourceLine(text, sourceref)
text = src.preprocess()
node = ast.parse(text, sourceref.file, mode="single")
return node
class EvaluatingTransformer(ast.NodeTransformer): class EvaluatingTransformer(ast.NodeTransformer):
def __init__(self, src: SourceLine, context: SymbolTable, ppcontext: SymbolTable) -> None: def __init__(self, src: SourceLine, context: SymbolTable, ppcontext: SymbolTable) -> None:
super().__init__() super().__init__()

View File

@ -14,7 +14,7 @@ import contextlib
from functools import partial from functools import partial
from typing import TextIO, Set, Union from typing import TextIO, Set, Union
from .parse import ProgramFormat, ParseResult, Parser from .parse import ProgramFormat, ParseResult, Parser
from .symbols import Zeropage, DataType, VariableDef, SubroutineDef, \ from .symbols import Zeropage, DataType, ConstantDef, VariableDef, SubroutineDef, \
STRING_DATATYPES, REGISTER_WORDS, FLOAT_MAX_NEGATIVE, FLOAT_MAX_POSITIVE STRING_DATATYPES, REGISTER_WORDS, FLOAT_MAX_NEGATIVE, FLOAT_MAX_POSITIVE
@ -246,10 +246,11 @@ class CodeGenerator:
for constdef in consts: for constdef in consts:
if constdef.type == DataType.FLOAT: if constdef.type == DataType.FLOAT:
self.p("\t\t{:s} = {}".format(constdef.name, constdef.value)) self.p("\t\t{:s} = {}".format(constdef.name, constdef.value))
elif constdef.type in STRING_DATATYPES:
print("warning: {}: const string not defined in output yet".format(constdef.sourceref)) # XXX
elif constdef.type in (DataType.BYTE, DataType.WORD): elif constdef.type in (DataType.BYTE, DataType.WORD):
self.p("\t\t{:s} = {:s}".format(constdef.name, Parser.to_hex(constdef.value))) # type: ignore self.p("\t\t{:s} = {:s}".format(constdef.name, Parser.to_hex(constdef.value))) # type: ignore
elif constdef.type in STRING_DATATYPES:
# a const string is just a string variable in the generated assembly
self._generate_string_var(constdef)
else: else:
raise CodeError("invalid const type", constdef) raise CodeError("invalid const type", constdef)
mem_vars = [vi for vi in block.symbols.iter_variables() if not vi.allocate and not vi.register] mem_vars = [vi for vi in block.symbols.iter_variables() if not vi.allocate and not vi.register]
@ -306,25 +307,29 @@ class CodeGenerator:
vardef.matrixsize[0] * vardef.matrixsize[1], vardef.matrixsize[0] * vardef.matrixsize[1],
vardef.value or 0, vardef.value or 0,
vardef.matrixsize[0], vardef.matrixsize[1])) vardef.matrixsize[0], vardef.matrixsize[1]))
elif vardef.type == DataType.STRING: elif vardef.type in STRING_DATATYPES:
# 0-terminated string self._generate_string_var(vardef)
self.p("{:s}\n\t\t.null {:s}".format(vardef.name, self.output_string(str(vardef.value))))
elif vardef.type == DataType.STRING_P:
# pascal string
self.p("{:s}\n\t\t.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value))))
elif vardef.type == DataType.STRING_S:
# 0-terminated string in screencode encoding
self.p(".enc 'screen'")
self.p("{:s}\n\t\t.null {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
self.p(".enc 'none'")
elif vardef.type == DataType.STRING_PS:
# 0-terminated pascal string in screencode encoding
self.p(".enc 'screen'")
self.p("{:s}\n\t\t.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
self.p(".enc 'none'")
else: else:
raise CodeError("unknown variable type " + str(vardef.type)) raise CodeError("unknown variable type " + str(vardef.type))
def _generate_string_var(self, vardef: Union[ConstantDef, VariableDef]) -> None:
if vardef.type == DataType.STRING:
# 0-terminated string
self.p("{:s}\n\t\t.null {:s}".format(vardef.name, self.output_string(str(vardef.value))))
elif vardef.type == DataType.STRING_P:
# pascal string
self.p("{:s}\n\t\t.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value))))
elif vardef.type == DataType.STRING_S:
# 0-terminated string in screencode encoding
self.p(".enc 'screen'")
self.p("{:s}\n\t\t.null {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
self.p(".enc 'none'")
elif vardef.type == DataType.STRING_PS:
# 0-terminated pascal string in screencode encoding
self.p(".enc 'screen'")
self.p("{:s}\n\t\t.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
self.p(".enc 'none'")
def generate_statement(self, stmt: ParseResult._AstNode) -> None: def generate_statement(self, stmt: ParseResult._AstNode) -> None:
if isinstance(stmt, ParseResult.ReturnStmt): if isinstance(stmt, ParseResult.ReturnStmt):
if stmt.a: if stmt.a:
@ -566,30 +571,31 @@ class CodeGenerator:
else: else:
raise CodeError("invalid assignment target (4)", str(stmt)) raise CodeError("invalid assignment target (4)", str(stmt))
elif isinstance(rvalue, ParseResult.FloatValue): elif isinstance(rvalue, ParseResult.FloatValue):
mflpt = self.to_mflpt5(rvalue.value)
for lv in stmt.leftvalues: for lv in stmt.leftvalues:
if isinstance(lv, ParseResult.MemMappedValue) and lv.datatype == DataType.FLOAT: if isinstance(lv, ParseResult.MemMappedValue) and lv.datatype == DataType.FLOAT:
self.generate_store_immediate_float(lv, rvalue.value, mflpt) self.generate_assign_float_to_mem(lv, rvalue)
elif isinstance(lv, ParseResult.IndirectValue): elif isinstance(lv, ParseResult.IndirectValue):
lv = unwrap_indirect(lv) lv = unwrap_indirect(lv)
assert lv.datatype == DataType.FLOAT assert lv.datatype == DataType.FLOAT
self.generate_store_immediate_float(lv, rvalue.value, mflpt) self.generate_assign_float_to_mem(lv, rvalue)
else: else:
raise CodeError("cannot assign float to ", str(lv)) raise CodeError("cannot assign float to ", str(lv))
else: else:
raise CodeError("invalid assignment value type", str(stmt)) raise CodeError("invalid assignment value type", str(stmt))
def generate_store_immediate_float(self, mmv: ParseResult.MemMappedValue, floatvalue: float, def generate_assign_float_to_mem(self, mmv: ParseResult.MemMappedValue,
mflpt: bytearray, emit_pha: bool=True) -> None: rvalue: Union[ParseResult.FloatValue, ParseResult.IntegerValue], save_reg: bool=True) -> None:
floatvalue = float(rvalue.value)
mflpt = self.to_mflpt5(floatvalue)
target = mmv.name or Parser.to_hex(mmv.address) target = mmv.name or Parser.to_hex(mmv.address)
if emit_pha: if save_reg:
self.p("\t\tpha\t\t\t; {:s} = {}".format(target, floatvalue)) self.p("\t\tpha\t\t\t; {:s} = {}".format(target, rvalue.name or floatvalue))
else: else:
self.p("\t\t\t\t\t; {:s} = {}".format(target, floatvalue)) self.p("\t\t\t\t\t; {:s} = {}".format(target, rvalue.name or floatvalue))
for num in range(5): for num in range(5):
self.p("\t\tlda #${:02x}".format(mflpt[num])) self.p("\t\tlda #${:02x}".format(mflpt[num]))
self.p("\t\tsta {:s}+{:d}".format(target, num)) self.p("\t\tsta {:s}+{:d}".format(target, num))
if emit_pha: if save_reg:
self.p("\t\tpla") self.p("\t\tpla")
def generate_assign_reg_to_memory(self, lv: ParseResult.MemMappedValue, r_register: str) -> None: def generate_assign_reg_to_memory(self, lv: ParseResult.MemMappedValue, r_register: str) -> None:
@ -732,8 +738,7 @@ class CodeGenerator:
elif lvdatatype == DataType.FLOAT: elif lvdatatype == DataType.FLOAT:
if rvalue.value is not None and not DataType.FLOAT.assignable_from_value(rvalue.value): if rvalue.value is not None and not DataType.FLOAT.assignable_from_value(rvalue.value):
raise ValueError("value cannot be assigned to a float") raise ValueError("value cannot be assigned to a float")
floatvalue = float(rvalue.value) self.generate_assign_float_to_mem(lv, rvalue, False)
self.generate_store_immediate_float(lv, floatvalue, self.to_mflpt5(floatvalue), False)
else: else:
raise TypeError("invalid lvalue type " + str(lvdatatype)) raise TypeError("invalid lvalue type " + str(lvdatatype))
@ -744,15 +749,20 @@ class CodeGenerator:
raise CodeError("can only assign a byte to a register") raise CodeError("can only assign a byte to a register")
self.p("\t\tld{:s} {:s}".format(l_register.lower(), r_str)) self.p("\t\tld{:s} {:s}".format(l_register.lower(), r_str))
else: else:
if rvalue.datatype != DataType.WORD: if rvalue.datatype == DataType.BYTE:
raise CodeError("can only assign a word to a register pair") self.p("\t\tld{:s} {:s}".format(l_register[0].lower(), r_str))
raise NotImplementedError("some mmap type assignment") # @todo other mmapped types self.p("\t\tld{:s} #0".format(l_register[1].lower()))
elif rvalue.datatype == DataType.WORD:
self.p("\t\tld{:s} {:s}".format(l_register[0].lower(), r_str))
self.p("\t\tld{:s} {:s}+1".format(l_register[1].lower(), r_str))
else:
raise CodeError("can only assign a byte or word to a register pair")
def generate_assign_mem_to_mem(self, lv: ParseResult.MemMappedValue, rvalue: ParseResult.MemMappedValue) -> None: def generate_assign_mem_to_mem(self, lv: ParseResult.MemMappedValue, rvalue: ParseResult.MemMappedValue) -> None:
r_str = rvalue.name if rvalue.name else "${:x}".format(rvalue.address) r_str = rvalue.name if rvalue.name else "${:x}".format(rvalue.address)
if lv.datatype == DataType.BYTE: if lv.datatype == DataType.BYTE:
if rvalue.datatype != DataType.BYTE: if rvalue.datatype != DataType.BYTE:
raise CodeError("can only assign a byte to a byte") raise CodeError("can only assign a byte to a byte", str(rvalue))
with self.preserving_registers({'A'}): with self.preserving_registers({'A'}):
self.p("\t\tlda " + r_str) self.p("\t\tlda " + r_str)
self.p("\t\tsta " + (lv.name or Parser.to_hex(lv.address))) self.p("\t\tsta " + (lv.name or Parser.to_hex(lv.address)))
@ -760,9 +770,9 @@ class CodeGenerator:
if rvalue.datatype == DataType.BYTE: if rvalue.datatype == DataType.BYTE:
with self.preserving_registers({'A'}): with self.preserving_registers({'A'}):
l_str = lv.name or Parser.to_hex(lv.address) l_str = lv.name or Parser.to_hex(lv.address)
self.p("\t\tlda #0")
self.p("\t\tsta " + l_str)
self.p("\t\tlda " + r_str) self.p("\t\tlda " + r_str)
self.p("\t\tsta " + l_str)
self.p("\t\tlda #0")
self.p("\t\tsta {:s}+1".format(l_str)) self.p("\t\tsta {:s}+1".format(l_str))
elif rvalue.datatype == DataType.WORD: elif rvalue.datatype == DataType.WORD:
with self.preserving_registers({'A'}): with self.preserving_registers({'A'}):
@ -772,10 +782,10 @@ class CodeGenerator:
self.p("\t\tlda {:s}+1".format(r_str)) self.p("\t\tlda {:s}+1".format(r_str))
self.p("\t\tsta {:s}+1".format(l_str)) self.p("\t\tsta {:s}+1".format(l_str))
else: else:
raise CodeError("can only assign a byte or word to a word") raise CodeError("can only assign a byte or word to a word", str(rvalue))
else: else:
raise CodeError("can only assign to a memory mapped byte or word value for now " raise CodeError("can only assign memory to a memory mapped byte or word value for now "
"(if you need other types, can't you use a var?)") "(if you need other types, can't you use a var?)", str(rvalue))
def generate_assign_char_to_memory(self, lv: ParseResult.MemMappedValue, char_str: str) -> None: def generate_assign_char_to_memory(self, lv: ParseResult.MemMappedValue, char_str: str) -> None:
# Memory = Character # Memory = Character

View File

@ -92,8 +92,7 @@ class ParseResult:
class IndirectValue(Value): class IndirectValue(Value):
def __init__(self, value: 'ParseResult.Value', type_modifier: DataType) -> None: def __init__(self, value: 'ParseResult.Value', type_modifier: DataType) -> None:
if not type_modifier: assert type_modifier
type_modifier = value.datatype
super().__init__(type_modifier, value.name, False) super().__init__(type_modifier, value.name, False)
self.value = value self.value = value
@ -103,7 +102,19 @@ class ParseResult:
def assignable_from(self, other: 'ParseResult.Value') -> Tuple[bool, str]: def assignable_from(self, other: 'ParseResult.Value') -> Tuple[bool, str]:
if self.constant: if self.constant:
return False, "cannot assign to a constant" return False, "cannot assign to a constant"
if other.datatype in {DataType.BYTE, DataType.WORD, DataType.FLOAT} | STRING_DATATYPES: if self.datatype == DataType.BYTE:
if other.datatype == DataType.BYTE:
return True, ""
if self.datatype == DataType.WORD:
if other.datatype in {DataType.BYTE, DataType.WORD} | STRING_DATATYPES:
return True, ""
if self.datatype == DataType.FLOAT:
if other.datatype in {DataType.BYTE, DataType.WORD, DataType.FLOAT}:
return True, ""
if isinstance(other, (ParseResult.IntegerValue, ParseResult.FloatValue, ParseResult.StringValue)):
rangefault = check_value_in_range(self.datatype, "", 1, other.value)
if rangefault:
return False, rangefault
return True, "" return True, ""
return False, "incompatible value for indirect assignment (need byte, word, float or string)" return False, "incompatible value for indirect assignment (need byte, word, float or string)"
@ -209,10 +220,15 @@ class ParseResult:
def assignable_from(self, other: 'ParseResult.Value') -> Tuple[bool, str]: def assignable_from(self, other: 'ParseResult.Value') -> Tuple[bool, str]:
if isinstance(other, ParseResult.IndirectValue): if isinstance(other, ParseResult.IndirectValue):
other = other.value if self.datatype == DataType.BYTE:
if other.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT): if other.datatype == DataType.BYTE:
return True, "" return True, ""
return False, "incompatible value for register assignment" return False, "(unsigned) byte required"
if self.datatype == DataType.WORD:
if other.datatype in (DataType.BYTE, DataType.WORD):
return True, ""
return False, "(unsigned) byte required"
return False, "incompatible indirect value for register assignment"
if self.register == "SC": if self.register == "SC":
if isinstance(other, ParseResult.IntegerValue) and other.value in (0, 1): if isinstance(other, ParseResult.IntegerValue) and other.value in (0, 1):
return True, "" return True, ""
@ -223,7 +239,16 @@ class ParseResult:
return False, "register size mismatch" return False, "register size mismatch"
if isinstance(other, ParseResult.StringValue) and self.register in REGISTER_BYTES: if isinstance(other, ParseResult.StringValue) and self.register in REGISTER_BYTES:
return False, "string address requires 16 bits combined register" return False, "string address requires 16 bits combined register"
if isinstance(other, (ParseResult.IntegerValue, ParseResult.FloatValue)): if isinstance(other, ParseResult.IntegerValue):
if other.value is not None:
range_error = check_value_in_range(self.datatype, self.register, 1, other.value)
if range_error:
return False, range_error
return True, ""
if self.datatype == DataType.WORD:
return True, ""
return False, "cannot assign address to single register"
if isinstance(other, ParseResult.FloatValue):
range_error = check_value_in_range(self.datatype, self.register, 1, other.value) range_error = check_value_in_range(self.datatype, self.register, 1, other.value)
if range_error: if range_error:
return False, range_error return False, range_error
@ -369,7 +394,7 @@ class ParseResult:
return [self] return [self]
statements = [] # type: List[ParseResult._AstNode] statements = [] # type: List[ParseResult._AstNode]
for name, value in self.arguments: for name, value in self.arguments:
assert name is not None, "call argument should have a parameter name assigned" assert name is not None, "all call arguments should have a name or be matched on a named parameter"
assignment = parser.parse_assignment("{:s}={:s}".format(name, value)) assignment = parser.parse_assignment("{:s}={:s}".format(name, value))
assignment.lineno = self.lineno assignment.lineno = self.lineno
statements.append(assignment) statements.append(assignment)
@ -419,16 +444,18 @@ class Parser:
with open(filename, "rU") as source: with open(filename, "rU") as source:
sourcelines = source.readlines() sourcelines = source.readlines()
# store all lines that aren't empty # store all lines that aren't empty
# comments are kept (end-of-line comments are put on a separate line) # comments are kept (end-of-line comments are stripped though)
lines = [] lines = []
for num, line in enumerate(sourcelines, start=1): for num, line in enumerate(sourcelines, start=1):
line2 = line.strip() line = line.rstrip()
if line2: if line.lstrip().startswith(';'):
line, sep, comment = line.partition(";") lines.append((num, line.lstrip()))
if comment: else:
lines.append((num, "; " + comment.strip())) line2, sep, comment = line.rpartition(';')
if line.rstrip(): if sep:
lines.append((num, line.rstrip())) line = line2.rstrip()
if line:
lines.append((num, line))
return lines return lines
def parse(self) -> Optional[ParseResult]: def parse(self) -> Optional[ParseResult]:
@ -759,21 +786,21 @@ class Parser:
self.print_warning("warning: {}: Ignoring block without name and address.".format(self.cur_block.sourceref)) self.print_warning("warning: {}: Ignoring block without name and address.".format(self.cur_block.sourceref))
return None return None
return self.cur_block return self.cur_block
if line.startswith("var"): if line.startswith(("var ", "var\t")):
self.parse_var_def(line) self.parse_var_def(line)
elif line.startswith("const"): elif line.startswith(("const ", "const\t")):
self.parse_const_def(line) self.parse_const_def(line)
elif line.startswith("memory"): elif line.startswith(("memory ", "memory\t")):
self.parse_memory_def(line, is_zp_block) self.parse_memory_def(line, is_zp_block)
elif line.startswith("subx"): elif line.startswith(("subx ", "subx\t")):
if is_zp_block: if is_zp_block:
raise self.PError("ZP block cannot contain subroutines") raise self.PError("ZP block cannot contain subroutines")
self.parse_subx_def(line) self.parse_subx_def(line)
elif line.startswith(("asminclude", "asmbinary")): elif line.startswith(("asminclude ", "asminclude\t", "asmbinary ", "asmbinary\t")):
if is_zp_block: if is_zp_block:
raise self.PError("ZP block cannot contain assembler directives") raise self.PError("ZP block cannot contain assembler directives")
self.cur_block.statements.append(self.parse_asminclude(line)) self.cur_block.statements.append(self.parse_asminclude(line))
elif line.startswith("asm"): elif line.startswith(("asm ", "asm\t")):
if is_zp_block: if is_zp_block:
raise self.PError("ZP block cannot contain code statements") raise self.PError("ZP block cannot contain code statements")
self.prev_line() self.prev_line()
@ -896,15 +923,18 @@ class Parser:
return varname, datatype, length, matrix_dimensions, valuetext return varname, datatype, length, matrix_dimensions, valuetext
def parse_statement(self, line: str) -> ParseResult._AstNode: def parse_statement(self, line: str) -> ParseResult._AstNode:
# check if we have a subroutine call using () syntax match = re.match(r"(?P<goto>goto\s+)?(?P<subname>[\S]+?)\s*(?P<fcall>[!]?)\s*(\((?P<arguments>.*)\))?\s*$", line)
match = re.match(r"^(?P<subname>[\w\.]+)\s*(?P<fcall>[!]?)\s*\((?P<params>.*)\)\s*$", line)
if match: if match:
# subroutine or goto call
is_goto = bool(match.group("goto"))
preserve = not bool(match.group("fcall"))
subname = match.group("subname") subname = match.group("subname")
fcall = "f" if match.group("fcall") else "" arguments = match.group("arguments")
param_str = match.group("params") if is_goto:
# turn this into "[f]call subname parameters" so it will be parsed below return self.parse_call_or_goto(subname, arguments, preserve, True)
line = "{:s}call {:s} {:s}".format(fcall, subname, param_str) elif arguments or match.group(4):
if line.startswith("return"): return self.parse_call_or_goto(subname, arguments, preserve, False)
if line == "return" or line.startswith(("return ", "return\t")):
return self.parse_return(line) return self.parse_return(line)
elif line.endswith(("++", "--")): elif line.endswith(("++", "--")):
incr = line.endswith("++") incr = line.endswith("++")
@ -912,12 +942,6 @@ class Parser:
if isinstance(what, ParseResult.IntegerValue): if isinstance(what, ParseResult.IntegerValue):
raise self.PError("cannot in/decrement a constant value") raise self.PError("cannot in/decrement a constant value")
return ParseResult.IncrDecrStmt(what, 1 if incr else -1) return ParseResult.IncrDecrStmt(what, 1 if incr else -1)
elif line.startswith("call"):
return self.parse_call_or_go(line, "call")
elif line.startswith("fcall"):
return self.parse_call_or_go(line, "fcall")
elif line.startswith("go"):
return self.parse_call_or_go(line, "go")
else: else:
# perhaps it is an assignment statment # perhaps it is an assignment statment
lhs, sep, rhs = line.partition("=") lhs, sep, rhs = line.partition("=")
@ -925,13 +949,10 @@ class Parser:
return self.parse_assignment(line) return self.parse_assignment(line)
raise self.PError("invalid statement") raise self.PError("invalid statement")
def parse_call_or_go(self, line: str, what: str) -> ParseResult.CallStmt: def parse_call_or_goto(self, targetstr: str, argumentstr: str, preserve_regs=True, is_goto=False) -> ParseResult.CallStmt:
args = line.split(maxsplit=2) argumentstr = argumentstr.strip() if argumentstr else ""
arguments = None arguments = None
if len(args) == 2: if argumentstr:
targetstr, argumentstr, = args[1], ""
elif len(args) == 3:
targetstr, argumentstr = args[1], args[2]
arguments = [] arguments = []
for part in argumentstr.split(','): for part in argumentstr.split(','):
pname, sep, pvalue = part.partition('=') pname, sep, pvalue = part.partition('=')
@ -941,8 +962,6 @@ class Parser:
arguments.append((pname, pvalue)) arguments.append((pname, pvalue))
else: else:
arguments.append((None, pname)) arguments.append((None, pname))
else:
raise self.PError("invalid call/go arguments")
target = None # type: ParseResult.Value target = None # type: ParseResult.Value
if targetstr[0] == '[' and targetstr[-1] == ']': if targetstr[0] == '[' and targetstr[-1] == ']':
# indirect call to address in register pair or memory location # indirect call to address in register pair or memory location
@ -961,9 +980,9 @@ class Parser:
_, symbol = self.cur_block.lookup(targetstr) _, symbol = self.cur_block.lookup(targetstr)
if isinstance(symbol, SubroutineDef): if isinstance(symbol, SubroutineDef):
# verify subroutine arguments # verify subroutine arguments
if arguments is not None and len(arguments) != len(symbol.parameters): if len(arguments or []) != len(symbol.parameters):
raise self.PError("invalid number of arguments ({:d}, expected {:d})" raise self.PError("invalid number of arguments ({:d}, expected {:d})"
.format(len(arguments), len(symbol.parameters))) .format(len(arguments or []), len(symbol.parameters)))
args_with_pnames = [] args_with_pnames = []
for i, (argname, value) in enumerate(arguments or []): for i, (argname, value) in enumerate(arguments or []):
pname, preg = symbol.parameters[i] pname, preg = symbol.parameters[i]
@ -974,15 +993,18 @@ class Parser:
argname = preg argname = preg
args_with_pnames.append((argname, value)) args_with_pnames.append((argname, value))
arguments = args_with_pnames arguments = args_with_pnames
else:
if arguments:
raise self.PError("call cannot take any arguments here, use a subroutine for that")
if arguments:
# verify that all arguments have gotten a name
if any(not a[0] for a in arguments):
raise self.PError("all call arguments should have a name or be matched on a named parameter")
if isinstance(target, (type(None), ParseResult.Value)): if isinstance(target, (type(None), ParseResult.Value)):
if what == "go": if is_goto:
return ParseResult.CallStmt(self.sourceref.line, target, address=address, is_goto=True) return ParseResult.CallStmt(self.sourceref.line, target, address=address, arguments=arguments, is_goto=True)
elif what == "call":
return ParseResult.CallStmt(self.sourceref.line, target, address=address, arguments=arguments)
elif what == "fcall":
return ParseResult.CallStmt(self.sourceref.line, target, address=address, arguments=arguments, preserve_regs=False)
else: else:
raise ValueError("invalid what") return ParseResult.CallStmt(self.sourceref.line, target, address=address, arguments=arguments, preserve_regs=preserve_regs)
else: else:
raise TypeError("target should be a Value", target) raise TypeError("target should be a Value", target)
@ -1085,12 +1107,15 @@ class Parser:
else: else:
raise self.PError("invalid statement") raise self.PError("invalid statement")
def parse_expression(self, text: str) -> ParseResult.Value: def parse_expression(self, text: str, is_indirect=False) -> ParseResult.Value:
# parse an expression into whatever it is (primitive value, register, memory, register, etc) # parse an expression into whatever it is (primitive value, register, memory, register, etc)
# @todo only numeric expressions supported for now
text = text.strip() text = text.strip()
if not text: if not text:
raise self.PError("value expected") raise self.PError("value expected")
if text[0] == '#': if text[0] == '#':
if is_indirect:
raise self.PError("using the address-of something in an indirect value makes no sense")
# take the pointer (memory address) from the thing that follows this # take the pointer (memory address) from the thing that follows this
expression = self.parse_expression(text[1:]) expression = self.parse_expression(text[1:])
if isinstance(expression, ParseResult.StringValue): if isinstance(expression, ParseResult.StringValue):
@ -1175,11 +1200,16 @@ class Parser:
if typestr in ("byte", "word", "float"): if typestr in ("byte", "word", "float"):
type_modifier, type_len, _ = self.get_datatype(sep + typestr) type_modifier, type_len, _ = self.get_datatype(sep + typestr)
indirect = indirect2 indirect = indirect2
expr = self.parse_expression(indirect) expr = self.parse_expression(indirect, True)
if not isinstance(expr, (ParseResult.IntegerValue, ParseResult.MemMappedValue, ParseResult.RegisterValue)): if not isinstance(expr, (ParseResult.IntegerValue, ParseResult.MemMappedValue, ParseResult.RegisterValue)):
raise self.PError("only integers, memmapped vars, and registers can be used in an indirect value") raise self.PError("only integers, memmapped vars, and registers can be used in an indirect value")
if type_modifier is None:
if isinstance(expr, (ParseResult.RegisterValue, ParseResult.MemMappedValue)):
type_modifier = expr.datatype
else:
type_modifier = DataType.BYTE
if isinstance(expr, ParseResult.IntegerValue): if isinstance(expr, ParseResult.IntegerValue):
if type_modifier not in (None, DataType.BYTE, DataType.WORD, DataType.FLOAT): if type_modifier not in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
raise self.PError("invalid type modifier for the value's datatype") raise self.PError("invalid type modifier for the value's datatype")
elif isinstance(expr, ParseResult.MemMappedValue): elif isinstance(expr, ParseResult.MemMappedValue):
if type_modifier and expr.datatype != type_modifier: if type_modifier and expr.datatype != type_modifier:

View File

@ -262,7 +262,7 @@ class SymbolTable:
return self, getattr(math, nameparts[0]) return self, getattr(math, nameparts[0])
elif nameparts[0] in BUILTIN_SYMBOLS: elif nameparts[0] in BUILTIN_SYMBOLS:
return self, getattr(builtins, nameparts[0]) return self, getattr(builtins, nameparts[0])
raise SymbolError("undefined symbol '{:s}'".format(nameparts[0])) raise SymbolError("undefined symbol '{:s}'".format(nameparts[0])) from None
# start from toplevel namespace: # start from toplevel namespace:
scope = self scope = self
while scope.parent: while scope.parent:
@ -272,7 +272,7 @@ class SymbolTable:
scope = scope.symbols[namepart] # type: ignore scope = scope.symbols[namepart] # type: ignore
assert scope.name == namepart assert scope.name == namepart
except LookupError: except LookupError:
raise SymbolError("undefined block '{:s}'".format(namepart)) raise SymbolError("undefined block '{:s}'".format(namepart)) from None
if isinstance(scope, SymbolTable): if isinstance(scope, SymbolTable):
return scope.lookup(nameparts[-1]) return scope.lookup(nameparts[-1])
else: else:
@ -445,11 +445,11 @@ class SymbolTable:
class EvalSymbolDict(dict): class EvalSymbolDict(dict):
def __init__(self, symboltable: SymbolTable, ppsymbols: SymbolTable, constants: bool=True) -> None: def __init__(self, symboltable: SymbolTable, ppsymbols: SymbolTable, constant: bool=True) -> None:
super().__init__() super().__init__()
self._symboltable = symboltable self._symboltable = symboltable
self._constants = constants
self._ppsymbols = ppsymbols self._ppsymbols = ppsymbols
self._is_constant = constant
def __getattr__(self, name): def __getattr__(self, name):
return self.__getitem__(name) return self.__getitem__(name)
@ -471,17 +471,17 @@ class EvalSymbolDict(dict):
if self._ppsymbols: if self._ppsymbols:
return self._ppsymbols.as_eval_dict(None)[name] return self._ppsymbols.as_eval_dict(None)[name]
raise SymbolError("undefined symbol '{:s}'".format(name)) from None raise SymbolError("undefined symbol '{:s}'".format(name)) from None
if self._constants: if self._is_constant:
if isinstance(symbol, ConstantDef): if isinstance(symbol, ConstantDef):
return symbol.value return symbol.value
elif isinstance(symbol, VariableDef): elif isinstance(symbol, VariableDef):
return symbol.value raise SymbolError("can't reference a variable inside a (constant) expression")
elif inspect.isbuiltin(symbol): elif inspect.isbuiltin(symbol):
return symbol return symbol
elif isinstance(symbol, SymbolTable): elif isinstance(symbol, SymbolTable):
return symbol.as_eval_dict(self._ppsymbols) return symbol.as_eval_dict(self._ppsymbols)
elif isinstance(symbol, LabelDef): elif isinstance(symbol, (LabelDef, SubroutineDef)):
raise SymbolError("can't reference a label here") raise SymbolError("can't reference a label or subroutine inside a (constant) expression")
else: else:
raise SymbolError("invalid symbol type referenced " + repr(symbol)) raise SymbolError("invalid symbol type referenced " + repr(symbol))
else: else:

View File

@ -158,7 +158,8 @@ The syntax "[address]" means: the contents of the memory at address.
By default, if not otherwise known, a single byte is assumed. You can add the ".byte" or ".word" or ".float" suffix By default, if not otherwise known, a single byte is assumed. You can add the ".byte" or ".word" or ".float" suffix
to make it clear what data type the address points to. to make it clear what data type the address points to.
Everything after a semicolon ';' is a comment and is ignored, however the comment is copied into the resulting assembly source code. Everything after a semicolon ';' is a comment and is ignored, however the comment (if it is the only thing
on the line) is copied into the resulting assembly source code.
FLOW CONTROL FLOW CONTROL
@ -349,28 +350,20 @@ ISOLATION (register preservation when calling subroutines): @todo isolation
SUBROUTINE CALLS SUBROUTINE CALLS
---------------- ----------------
CALL and FCALL: You call a subroutine like this:
They are just inserting a call to the specified location or subroutine. subroutinename[!] ( [arguments...] )
[F]CALL: calls subroutine and continue afterwards ('gosub'):
[f]call <subroutine> / <label> / <address> / `[`indirect-pointer`]` [arguments...]
A 'call' preserves all registers when doing the procedure call and restores them afterwards. Normally, the registers are preserved when calling the subroutine and restored on return.
'fcall' (fast call) doesn't preserve registers, so generates code that is a lot faster. If you add a '!' after the name, no register preserving is done and the call essentially
It's basically one jmp or jsr instruction. It can clobber register values because of this. is just a single JSR instruction.
If you provide arguments (not required) these will be matched to the subroutine's parameters. Arguments should match the subroutine definition. You are allowed to omit the parameter names.
If you don't provide arguments, it is assumed you have prepared the correct registers etc yourself. If no definition is available (because you're directly calling memory or a label or something else),
you can freely add arguments (but in this case they all have to be named).
To jump to a subroutine (without returning), prefix the subroutine call with the word 'goto'.
Unlike gotos in other languages, here it take arguments as well, because it
essentially is the same as calling a subroutine and only doing something different when it's finished.
The following contemporary syntax to call a subroutine is also available:
subroutine `(` [arguments...] `)`
subroutine! `(` [arguments...] `)`
These are understood as: "call subroutine arguments" and "fcall subroutine arguments" respectively.
You can only call a subroutine or label this way. This syntax cannot be used
to call a memory address or variable, you have to use the call statement for that.
GO:
'go' continues execution with the specified routine or address and doesn't retuurn (it is a 'goto'):
go <subroutine> / <label> / <address> / [indirect-pointer]
@todo support call non-register args (variable parameter passing) @todo support call non-register args (variable parameter passing)

View File

@ -16,19 +16,27 @@
const .text constt = "derp" const .text constt = "derp"
subx sub1 () -> (X?) = $ffdd subx sub1 () -> (X?) = $ffdd
subx sub2 (A) -> (Y?) = $eecc
bar bar
go sub1 goto sub1
go bar goto sub2 (1 )
go [AX] goto bar ()
go [var1] goto [AX]
go [mem1] goto [AX] ()
go [#mem1] ; @todo error: indirection of address-of makes no sense, should have outputted jmp mem1 instead of jmp(mem1) goto [var1]
go [$c2.word] goto [var1] () ; comment
go [$c2dd.word] goto [mem1] ; comment
go $c000 goto [mem1] ()
go $c2 goto [$c2.word]
goto [$c2.word] ()
goto [$c2dd.word]
goto [$c2dd.word] ( )
goto $c000
goto $c000 ( )
goto $c2
goto $c2()
asm { asm {
nop nop
@ -37,16 +45,16 @@ bar
nop nop
} }
fcall sub1 sub1!()
fcall bar sub2!(11)
fcall [XY] bar!()
fcall [var1] [XY] ! ()
fcall [mem1] [var1] !()
fcall [#mem1] ; @todo error: indirection of address-of makes no sense [mem1]!()
fcall [$c2.word] [$c2.word]!()
fcall [$c2dd.word] [$c2dd.word]!()
fcall $c000 $c000!()
fcall $c2 $c2!()
asm { asm {
nop nop
@ -55,16 +63,16 @@ bar
nop nop
} }
call sub1 sub1()
call bar sub2(11)
call [AX] bar ()
call [var1] [AX]()
call [mem1] [var1] ( )
call [#mem1] ; @todo error: indirection of address-of makes no sense [mem1] ()
call [$c2.word] [$c2.word]()
call [$c2dd.word] [$c2dd.word]()
call $c000 $c000()
call $c2 $c2()
asm { asm {
@ -74,16 +82,15 @@ bar
nop nop
} }
call constw constw()
call sub1 sub1()
call main.start main.start()
} }
~ main { ~ main {
start start
call foo.bar foo.bar()
return return
} }

View File

@ -45,7 +45,6 @@ clobberzp
var .word initword1b = true var .word initword1b = true
var .word initword2 = false var .word initword2 = false
var .word initword3 = 9876.554321 var .word initword3 = 9876.554321
var .word initword4 = initword3
var .word initword5 = 20 var .word initword5 = 20
var .float uninitfloat var .float uninitfloat
var .float initfloat1 = 0 var .float initfloat1 = 0
@ -76,8 +75,7 @@ clobberzp
; memory-mapped variables ; memory-mapped variables
memory membyte1 = $cf01 memory membyte1 = $cf01
memory .byte membyte2 = $c2 memory .byte membyte2 = $c222
memory .byte membyte3 = initbyte2
memory .word memword1 = $cf03 memory .word memword1 = $cf03
memory .float memfloat = $cf04 memory .float memfloat = $cf04
memory .array(10 ) membytes = $cf05 memory .array(10 ) membytes = $cf05
@ -109,7 +107,6 @@ clobberzp
var .word vmemaddr4 = #membytes var .word vmemaddr4 = #membytes
var .word vmemaddr5 = #memwords var .word vmemaddr5 = #memwords
var .word vmemaddr6 = #memmatrix var .word vmemaddr6 = #memmatrix
var .word vmemaddr7 = vmemaddr1
var .word vmemaddr8 = 100*sin(cbyte1) var .word vmemaddr8 = 100*sin(cbyte1)
var .word vmemaddr9 = cword2+$5432 var .word vmemaddr9 = cword2+$5432
var .word vmemaddr10 = cfloat2b var .word vmemaddr10 = cfloat2b
@ -144,17 +141,16 @@ start
A = false A = false
A = 255 A = 255
A = X A = X
A = [$99]
A = [$c020.byte]
A = [$c020] A = [$c020]
A = [#membyte2] A = cbyte3
A = membyte2 A = membyte2
A = [membyte2] ; @todo error, invalid rvalue, use membyte without indirect? A = uninitbyte1
A = [membyte2.byte] ; @todo error, " ;A = [membyte2] ; @todo error, invalid rvalue, use membyte without indirect?
A = expr_byte1b ; @todo ok ;A = [membyte2.byte] ; @todo error, "
;A = #expr_byte1b ; @todo cannot assign address to byte, correct error
;A = cbyte3 ; @todo fix assignment to lda #cybte3
;A = [cbyte3] ; @todo error invalid rvalue ;A = [cbyte3] ; @todo error invalid rvalue
A = initbytea0 ;A = [initbytea0] ; @todo error, invalid rvalue, use initbytea0 without indirect?
A = [initbytea0] ; @todo error, invalid rvalue, use initbytea0 without indirect?
XY = 0 XY = 0
@ -165,43 +161,67 @@ start
XY = true XY = true
XY = false XY = false
XY = text XY = text
XY = cbyte3
XY = cword2
XY = uninitbyte1
XY = "text-immediate" XY = "text-immediate"
AY = "text-immediate" AY = "text-immediate"
AX = #"text-immediate" ; equivalent to simply assigning the string directly AX = #"text-immediate" ; equivalent to simply assigning the string directly
AX = # "text-immediate" ; equivalent to simply assigning the string directly AX = # "text-immediate" ; equivalent to simply assigning the string directly
AX = ctext3
AX = "" AX = ""
AX = XY AX = XY
AX = Y AX = Y
;XY = [membyte2] ; @todo ok pad XY = [membyte2] ; @todo indirection error?
;XY = [membyte2.byte] ; @todo ok pad XY = [membyte2.byte] ; @todo indirection error?
;XY = membyte2 ; @todo ok pad XY = membyte2
;XY = #membyte2 ; @todo ok XY = #membyte2
;XY = [memword1] ; @todo ok XY = [memword1] ; @todo indirection error?
;XY = [memword1.byte] ; @todo ok pad XY = [memword1.word] ; @todo indirection error?
;XY = [memword1.word] ; @todo ok XY = memword1
XY = sin ; @todo ok XY = sin
XY = #sin
[$c000] = A
[$c000] = 255 [$c000] = 255
[$c000] = '@' [$c000] = '@'
[$c000] = 1.2345
[$c000] = true [$c000] = true
[$c000] = false [$c000] = false
[$c000] = cbyte3
[$c000] = uninitbyte1
[$c000] = [membyte2] ; @todo indirection error?
[$c000] = [membyte2.byte] ; @todo indirection error?
[$c000] = membyte2
[$c000.word] = A
[$c000.word] = AX
[$c000.word] = cbyte3
[$c000.word] = cword2
[$c000.word] = ctext3
[$c000.word] = 65535 [$c000.word] = 65535
[$c000.word] = 456.66
[$c000.word] = "text" [$c000.word] = "text"
[$c000.word] = "" [$c000.word] = ""
[$c000.word] = uninitbyte1
[$c000.word] = [membyte2] ; @todo indirection error?
[$c000.word] = [membyte2.byte] ; @todo indirection error?
[$c000.word] = membyte2
[$c000.word] = #membyte2
[$c000.word] = [memword1] ; @todo indirection error?
[$c000.word] = [memword1.word] ; @todo indirection error?
[$c000.word] = memword1
[$c000.float] = 65535 [$c000.float] = 65535
[$c000.float] = 456.66 [$c000.float] = 456.66
[$c000.float] = 1.70141183e+38 [$c000.float] = 1.70141183e+38
[$c000.word] = AX [$c000.float] = cbyte3
[$c000.float] = cword2
[$c001] = [$c002] [$c001] = [$c002]
[$c111.word] = [$c222] [$c111.word] = [$c222]
[$c112.word] = [$c223.byte] [$c112.word] = [$c223.byte]
[$c222.word] = [$c333.word] [$c222.word] = [$c333.word]
[$c333] = sin ; @todo error byte required [$c333.word] = sin
[$c333.word] = sin ; @todo ok [$c333.word] = #sin
SC = 0 SC = 0
@ -215,36 +235,59 @@ start
initbyte1 = 1.234 initbyte1 = 1.234
initbyte1 = '@' initbyte1 = '@'
initbyte1 = A initbyte1 = A
initbyte1 = cbyte3
uninitword = 99 uninitword = 99
uninitword = 5.6778 uninitword = 5.6778
uninitword = "test" uninitword = "test"
uninitword = '@' uninitword = '@'
uninitword = A uninitword = A
uninitword = XY uninitword = XY
uninitword = ctext3
initword1 = cbyte3
initword1 = cword2
initfloat1 = 99 initfloat1 = 99
initfloat1 = 9.8765 initfloat1 = 9.8765
initfloat1 = '@' initfloat1 = '@'
initfloat1 = cbyte3
initfloat1 = cword2
uninitfloat = 99 uninitfloat = 99
uninitfloat = 9.8765 uninitfloat = 9.8765
uninitfloat = '@' uninitfloat = '@'
;uninitfloat = A ; @todo support this initword1 = sin
; uninitfloat = XY ; @todo support this initword1 = #sin
initword1 = sin ; @todo ok
membyte1 = A
membyte1 = cbyte3
memword1 = A
memword1 = AX
memword1 = cbyte3
memword1 = cword2
memword1 = ctext3
membyte1 = 22 membyte1 = 22
memword1 = 2233 memword1 = 2233
memfloat = 3.4567 memfloat = 3.4567
;[membyte1] = 33 ; @todo error, invalid lvalue, use without [] ;[membyte1] = 33 ; @todo error, invalid lvalue, use without []
[memword1] = 4444 ;[memword1] = 4444 ; @todo error ^
;[memword1] = [AX] ; @todo error, only address allowed in [] ;[memword1] = [AX] ; @todo error, only address allowed in []
[memfloat] = 5.5566 ;[memfloat] = 5.5566 ; @todo error ^
memword1 = sin ; @todo ok memword1 = sin
memword1 = #sin
membyte1 = A membyte1 = A
memword1 = A memword1 = A
memword1 = XY memword1 = XY
;memfloat = A ; @todo support this memfloat = cbyte3
;memfloat = XY ; @todo support this memfloat = cword2
; @todo float assignments that require ROM functions:
; memfloat = Y
; memfloat = XY
; uninitfloat = Y
; uninitfloat = XY
; initfloat2 = Y
; initfloat2 = XY
return return
} }

View File

@ -53,6 +53,7 @@ start
start start
; assign some float values to the memory
AY = #flt_pi AY = #flt_pi
[some_address] = # flt_pi [some_address] = # flt_pi
[some_address] = # flt_pi [some_address] = # flt_pi
@ -65,6 +66,8 @@ start
[some_address.word] = #flt_pi [some_address.word] = #flt_pi
[$c000.word] = # flt_pi [$c000.word] = # flt_pi
; print some floating points from source and compare them with ROM
c64.MOVFM!(#flt_pi) c64.MOVFM!(#flt_pi)
c64.FPRINTLN!() c64.FPRINTLN!()
c64.MOVFM!(#c64.FL_PIVAL) c64.MOVFM!(#c64.FL_PIVAL)

View File

@ -74,8 +74,8 @@ _loop block2.zpw1 ++
Y-- Y--
[$d020]-- [$d020]--
[block2.zpw2] = 99 [block2.zpw2] = 99
call fidget.subroutine fidget.subroutine()
go _loop goto _loop
return 155,2,%00000101 ; will end up in A, X, Y return 155,2,%00000101 ; will end up in A, X, Y
} }
@ -102,8 +102,8 @@ somelabel1
nop nop
} }
go somelabel1 goto somelabel1
go block2.somelabel1222 goto block2.somelabel1222
A=X=Y=A=X=Y=A=X=Y=99 A=X=Y=A=X=Y=A=X=Y=99
[$d020]=[$d021]=[$d020]=[$d021]=55 [$d020]=[$d021]=[$d020]=[$d021]=55

View File

@ -28,7 +28,7 @@ import "c64lib" ; searched in several locations and with .ill file
var .text hello2 = "@@\f\b\n\r\t@@" var .text hello2 = "@@\f\b\n\r\t@@"
start start
call global2.make_screen_black global2.make_screen_black()
A='?' A='?'
[$d020] = '?' [$d020] = '?'
@ -46,9 +46,7 @@ start
X=A X=A
A='\xf2' A='\xf2'
X=A X=A
A='A' c64.CHROUT('A')
call c64.CHROUT ;(A)
call c64.CHROUT ;(char=66)
A='\f' A='\f'
X=A X=A
A='\b' A='\b'
@ -58,18 +56,13 @@ start
A='\r' A='\r'
X=A X=A
A='\t' A='\t'
X=A
call c64.CHROUT ;(foo=A)
A='0' A='0'
call c64.CHROUT ;('0') c64.CHROUT('0')
A='1' c64.CHROUT('1')
call c64.CHROUT ;(49) c64.CHROUT('2')
A='2'
call c64.CHROUT
XY = hello XY = hello
call c64util.print_string c64util.print_string()
A='!' goto c64.CHROUT('!')
go c64.CHROUT
return return

View File

@ -1,4 +1,4 @@
output prg,sys ; create a c-64 program with basic SYS call to launch it output prg,sys ; create a c-64 program with basic SYS to() launch it
import "c64lib.ill" import "c64lib.ill"
@ -9,67 +9,58 @@ output prg,sys ; create a c-64 program with basic SYS call to launch it
const .word BORDER = $d020 const .word BORDER = $d020
start start
fcall c64util.print_pimmediate ; this prints the pstring immediately following it c64util.print_pimmediate ! () ; this prints the pstring immediately following it
asm { asm {
.ptext "hello-pimmediate!{cr}" .ptext "hello-pimmediate!{cr}"
} }
A = 19 A = 19
fcall c64util.print_byte_decimal0 c64util.print_byte_decimal0 ! ()
A = 13 c64.CHROUT ! (13)
fcall c64.CHROUT
A = 19 A = 19
fcall c64util.print_byte_decimal c64util.print_byte_decimal ! ()
A = 13 c64.CHROUT ! (13)
fcall c64.CHROUT
X = $01 X = $01
Y = $02 Y = $02
fcall c64util.print_word_decimal0 c64util.print_word_decimal0 ! ()
A = 13 c64.CHROUT ! (13)
fcall c64.CHROUT
X = $01 X = $01
Y = $02 Y = $02
fcall c64util.print_word_decimal c64util.print_word_decimal ! ()
A = 13 c64.CHROUT ! (13)
fcall c64.CHROUT
return return
start2 start2
call global2.make_screen_black global2.make_screen_black()
call c64.CLEARSCR c64.CLEARSCR()
XY = greeting XY = greeting
call c64util.print_string c64util.print_string()
XY = p_greeting XY = p_greeting
call c64util.print_pstring c64util.print_pstring()
A = 0 A = 0
call c64util.print_byte_decimal c64util.print_byte_decimal()
A = 0 A = 0
call c64util.print_byte_hex c64util.print_byte_hex()
c64.CHROUT(13)
c64util.print_byte_decimal()
A = 13 A = 13
call c64.CHROUT c64util.print_byte_hex()
call c64util.print_byte_decimal c64.CHROUT(13)
A = 13
call c64util.print_byte_hex
A = 13
call c64.CHROUT
A = 255 A = 255
call c64util.print_byte_decimal c64util.print_byte_decimal()
A = 254 A = 254
call c64util.print_byte_hex c64util.print_byte_hex()
A = 129 A = 129
call c64util.print_byte_hex c64util.print_byte_hex()
A = 13 c64.CHROUT(13)
call c64.CHROUT
A = 13 c64.CHROUT(13)
call c64.CHROUT
X = 1 X = 1
Y = 0 Y = 0
call c64util.print_word_decimal c64util.print_word_decimal()
A = 13 c64.CHROUT(13)
call c64.CHROUT
return return