subroutines and assignment changes

This commit is contained in:
Irmen de Jong 2017-12-25 00:15:04 +01:00
parent 63aa3cae8c
commit 4025d44b74
10 changed files with 253 additions and 207 deletions

View File

@ -8,7 +8,7 @@ License: GNU GPL 3.0, see LICENSE
import ast import ast
from typing import Union, Optional from typing import Union, Optional
from .symbols import FLOAT_MAX_POSITIVE, FLOAT_MAX_NEGATIVE, SourceRef, SymbolTable, SymbolError, DataType, PrimitiveType from .symbols import FLOAT_MAX_POSITIVE, FLOAT_MAX_NEGATIVE, SourceRef, SymbolTable, SymbolError, PrimitiveType
class ParseError(Exception): class ParseError(Exception):
@ -191,10 +191,3 @@ class ExpressionTransformer(EvaluatingTransformer):
else: else:
raise self.error("invalid MatMult/Pointer node in AST") raise self.error("invalid MatMult/Pointer node in AST")
return node return node
if __name__ == "__main__":
symbols = SymbolTable("<root>", None, None)
symbols.define_variable("derp", SourceRef("<source>", 1), DataType.BYTE, address=2345)
result = parse_expr_as_primitive("2+#derp", symbols, None, SourceRef("<source>", 1))
print("EXPRESSION RESULT:", result)

View File

@ -634,7 +634,31 @@ class CodeGenerator:
self.p("\t\tst{:s} {}".format(r_register[0].lower(), lv_string)) self.p("\t\tst{:s} {}".format(r_register[0].lower(), lv_string))
self.p("\t\tst{:s} {}+1".format(r_register[1].lower(), lv_string)) self.p("\t\tst{:s} {}+1".format(r_register[1].lower(), lv_string))
elif lv.datatype == DataType.FLOAT: elif lv.datatype == DataType.FLOAT:
raise CodeError("assigning register to float not yet supported") # @todo support float=reg # assigning a register to a float requires ROM routines
if r_register in REGISTER_WORDS:
raise CodeError("cannot yet assign register pair to float", r_register) # XXX reg pair -> float
elif r_register in "AXY":
def do_rom_calls():
self.p("\t\tjsr c64.FREADUY") # ubyte Y -> fac1
self.p("\t\tldx #<" + lv_string)
self.p("\t\tldy #>" + lv_string)
self.p("\t\tjsr c64.FTOMEMXY") # fac1 -> memory XY
if r_register == "A":
with self.preserving_registers({'A', 'X', 'Y'}):
self.p("\t\ttay")
do_rom_calls()
elif r_register == "X":
with self.preserving_registers({'A', 'X', 'Y'}):
self.p("\t\ttxa")
self.p("\t\ttay")
do_rom_calls()
elif r_register == "Y":
with self.preserving_registers({'A', 'X', 'Y'}):
do_rom_calls()
else:
raise CodeError("invalid register to assign to float", r_register)
else: else:
raise CodeError("invalid lvalue type", lv.datatype) raise CodeError("invalid lvalue type", lv.datatype)
@ -803,7 +827,7 @@ class CodeGenerator:
else: else:
raise CodeError("can only assign a byte or word to a word", str(rvalue)) raise CodeError("can only assign a byte or word to a word", str(rvalue))
else: else:
raise CodeError("can only assign memory to a memory mapped byte or word value for now " raise CodeError("can only assign memory to a memory mapped value for now "
"(if you need other types, can't you use a var?)", str(rvalue)) "(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:

View File

@ -99,6 +99,20 @@ class ParseResult:
def __str__(self): def __str__(self):
return "<IndirectValue {} itype={} name={}>".format(self.value, self.datatype, self.name) return "<IndirectValue {} itype={} name={}>".format(self.value, self.datatype, self.name)
def __hash__(self):
return hash((self.datatype, self.name, self.value))
def __eq__(self, other: Any) -> bool:
if not isinstance(other, ParseResult.IndirectValue):
return NotImplemented
elif self is other:
return True
else:
vvo = getattr(other.value, "value", getattr(other.value, "address", None))
vvs = getattr(self.value, "value", getattr(self.value, "address", None))
return (other.datatype, other.name, other.value.name, other.value.datatype, other.value.constant, vvo) ==\
(self.datatype, self.name, self.value.name, self.value.datatype, self.value.constant, vvs)
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"
@ -151,7 +165,7 @@ class ParseResult:
elif self is other: elif self is other:
return True return True
else: else:
return other.datatype == self.datatype and other.value == self.value and other.name == self.name return (other.datatype, other.value, other.name) == (self.datatype, self.value, self.name)
def __str__(self): def __str__(self):
return "<IntegerValue {} name={}>".format(self.value, self.name) return "<IntegerValue {} name={}>".format(self.value, self.name)
@ -173,7 +187,7 @@ class ParseResult:
elif self is other: elif self is other:
return True return True
else: else:
return other.datatype == self.datatype and other.value == self.value and other.name == self.name return (other.datatype, other.value, other.name) == (self.datatype, self.value, self.name)
def __str__(self): def __str__(self):
return "<FloatValue {} name={}>".format(self.value, self.name) return "<FloatValue {} name={}>".format(self.value, self.name)
@ -184,7 +198,7 @@ class ParseResult:
self.value = value self.value = value
def __hash__(self): def __hash__(self):
return hash((self.datatype, self.value, self.name)) return hash((self.datatype, self.value, self.name, self.constant))
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
if not isinstance(other, ParseResult.StringValue): if not isinstance(other, ParseResult.StringValue):
@ -192,7 +206,7 @@ class ParseResult:
elif self is other: elif self is other:
return True return True
else: else:
return other.datatype == self.datatype and other.value == self.value and other.name == self.name return (other.datatype, other.value, other.name, other.constant) == (self.datatype, self.value, self.name, self.constant)
def __str__(self): def __str__(self):
return "<StringValue {!r:s} name={} constant={}>".format(self.value, self.name, self.constant) return "<StringValue {!r:s} name={} constant={}>".format(self.value, self.name, self.constant)
@ -213,7 +227,7 @@ class ParseResult:
elif self is other: elif self is other:
return True return True
else: else:
return other.datatype == self.datatype and other.register == self.register and other.name == self.name return (other.datatype, other.register, other.name) == (self.datatype, self.register, self.name)
def __str__(self): def __str__(self):
return "<RegisterValue {:s} type {:s} name={}>".format(self.register, self.datatype, self.name) return "<RegisterValue {:s} type {:s} name={}>".format(self.register, self.datatype, self.name)
@ -271,7 +285,7 @@ class ParseResult:
assert address is None or type(address) is int assert address is None or type(address) is int
def __hash__(self): def __hash__(self):
return hash((self.datatype, self.address, self.length, self.name)) return hash((self.datatype, self.address, self.length, self.name, self.constant))
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
if not isinstance(other, ParseResult.MemMappedValue): if not isinstance(other, ParseResult.MemMappedValue):
@ -279,8 +293,8 @@ class ParseResult:
elif self is other: elif self is other:
return True return True
else: else:
return other.datatype == self.datatype and other.address == self.address and \ return (other.datatype, other.address, other.length, other.name, other.constant) ==\
other.length == self.length and other.name == self.name (self.datatype, self.address, self.length, self.name, self.constant)
def __str__(self): def __str__(self):
addr = "" if self.address is None else "${:04x}".format(self.address) addr = "" if self.address is None else "${:04x}".format(self.address)
@ -365,6 +379,10 @@ class ParseResult:
self.right.name = stringvar_name self.right.name = stringvar_name
self._immediate_string_vars[self.right.value] = (cur_block.name, stringvar_name) self._immediate_string_vars[self.right.value] = (cur_block.name, stringvar_name)
def remove_identity_assigns(self) -> None:
remaining_leftvalues = [lv for lv in self.leftvalues if lv != self.right]
self.leftvalues = remaining_leftvalues
class ReturnStmt(_AstNode): class ReturnStmt(_AstNode):
def __init__(self, a: Optional['ParseResult.Value']=None, def __init__(self, a: Optional['ParseResult.Value']=None,
x: Optional['ParseResult.Value']=None, x: Optional['ParseResult.Value']=None,
@ -530,12 +548,24 @@ class Parser:
raise self.PError("A block named 'main' should be defined for the program's entry point 'start'") raise self.PError("A block named 'main' should be defined for the program's entry point 'start'")
def _parse_2(self) -> None: def _parse_2(self) -> None:
# parsing pass 2 # parsing pass 2 (not done during preprocessing!)
self.cur_block = None self.cur_block = None
self.sourceref.line = -1 self.sourceref.line = -1
self.sourceref.column = 0 self.sourceref.column = 0
for block in self.result.blocks: for block in self.result.blocks:
self.cur_block = block self.cur_block = block
# remove identity assignments
have_removed_stmts = False
for index, stmt in enumerate(list(block.statements)):
if isinstance(stmt, ParseResult.AssignmentStmt):
stmt.remove_identity_assigns()
if not stmt.leftvalues:
print("warning: {:s}:{:d}: removed identity assignment".format(self.sourceref.file, stmt.lineno))
have_removed_stmts = True
block.statements[index] = None
if have_removed_stmts:
# remove the Nones
block.statements = [s for s in block.statements if s is not None]
# create parameter loads for calls # create parameter loads for calls
for index, stmt in enumerate(list(block.statements)): for index, stmt in enumerate(list(block.statements)):
if isinstance(stmt, ParseResult.CallStmt): if isinstance(stmt, ParseResult.CallStmt):
@ -719,7 +749,6 @@ class Parser:
# first line contains block header "~ [name] [addr]" followed by a '{' # first line contains block header "~ [name] [addr]" followed by a '{'
self._parse_comments() self._parse_comments()
line = self.next_line() line = self.next_line()
block_def_lineno = self.sourceref.line
line = line.lstrip() line = line.lstrip()
if not line.startswith("~"): if not line.startswith("~"):
raise self.PError("expected '~' (block)") raise self.PError("expected '~' (block)")
@ -792,7 +821,7 @@ class Parser:
self.parse_const_def(line) self.parse_const_def(line)
elif line.startswith(("memory ", "memory\t")): 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 ", "subx\t")): elif line.startswith(("sub ", "sub\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)
@ -814,7 +843,7 @@ class Parser:
raise self.PError("ZP block cannot contain code labels") raise self.PError("ZP block cannot contain code labels")
self.parse_label(line) self.parse_label(line)
else: else:
raise self.PError("missing } to close block from line " + str(self.cur_block.sourceref.line)) raise self.PError("invalid statement in block")
def parse_label(self, line: str) -> None: def parse_label(self, line: str) -> None:
label_line = line.split(maxsplit=1) label_line = line.split(maxsplit=1)
@ -855,13 +884,13 @@ class Parser:
raise self.PError(str(x)) from x raise self.PError(str(x)) from x
def parse_subx_def(self, line: str) -> None: def parse_subx_def(self, line: str) -> None:
match = re.match(r"^subx\s+(?P<name>\w+)\s+" match = re.match(r"^sub\s+(?P<name>\w+)\s+"
r"\((?P<parameters>[\w\s:,]*)\)" r"\((?P<parameters>[\w\s:,]*)\)"
r"\s*->\s*" r"\s*->\s*"
r"\((?P<results>[\w\s?,]*)\)\s*" r"\((?P<results>[\w\s?,]*)\)\s*"
r"(?P<decltype>\s+=\s+(?P<address>\S*)|{)\s*$", line) r"(?P<decltype>\s+=\s+(?P<address>\S*)|{)\s*$", line)
if not match: if not match:
raise self.PError("invalid subx declaration") raise self.PError("invalid sub declaration")
code_decl = match.group("decltype") == "{" code_decl = match.group("decltype") == "{"
name, parameterlist, resultlist, address_str = \ name, parameterlist, resultlist, address_str = \
match.group("name"), match.group("parameters"), match.group("results"), match.group("address") match.group("name"), match.group("parameters"), match.group("results"), match.group("address")
@ -889,7 +918,7 @@ class Parser:
if line == "}": if line == "}":
# subroutine end # subroutine end
break break
if line.startswith(("subx ", "subx\t")): if line.startswith(("sub ", "sub\t")):
raise self.PError("cannot nest subroutines") raise self.PError("cannot nest subroutines")
elif line.startswith(("asm ", "asm\t")): elif line.startswith(("asm ", "asm\t")):
self.prev_line() self.prev_line()
@ -899,7 +928,7 @@ class Parser:
elif line: elif line:
self.parse_label(line) self.parse_label(line)
else: else:
raise self.PError("missing } to close subroutine from line " + str(subroutine_block.sourceref.line)) raise self.PError("invalid statement in subroutine")
self.cur_block = current_block self.cur_block = current_block
self.cur_block.sourceref = subroutine_block.sourceref self.cur_block.sourceref = subroutine_block.sourceref
else: else:
@ -993,7 +1022,7 @@ class Parser:
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
targetstr, target = self.parse_indirect_value(targetstr) targetstr, target = self.parse_indirect_value(targetstr, True)
if target.datatype != DataType.WORD: if target.datatype != DataType.WORD:
raise self.PError("invalid call target (should contain 16-bit)") raise self.PError("invalid call target (should contain 16-bit)")
else: else:
@ -1005,7 +1034,7 @@ class Parser:
and not isinstance(target.value, (ParseResult.IntegerValue, ParseResult.RegisterValue, ParseResult.MemMappedValue)): and not isinstance(target.value, (ParseResult.IntegerValue, ParseResult.RegisterValue, ParseResult.MemMappedValue)):
raise self.PError("cannot call that type of indirect symbol") raise self.PError("cannot call that type of indirect symbol")
address = target.address if isinstance(target, ParseResult.MemMappedValue) else None address = target.address if isinstance(target, ParseResult.MemMappedValue) else None
_, symbol = self.cur_block.lookup(targetstr) _, symbol = self.lookup(targetstr)
if isinstance(symbol, SubroutineDef): if isinstance(symbol, SubroutineDef):
# verify subroutine arguments # verify subroutine arguments
if len(arguments or []) != len(symbol.parameters): if len(arguments or []) != len(symbol.parameters):
@ -1050,7 +1079,7 @@ class Parser:
rhs = parts.pop() rhs = parts.pop()
l_values = [self.parse_expression(part) for part in parts] l_values = [self.parse_expression(part) for part in parts]
if any(isinstance(lv, ParseResult.IntegerValue) for lv in l_values): if any(isinstance(lv, ParseResult.IntegerValue) for lv in l_values):
raise self.PError("can't have a constant as assignment target, did you mean [name] instead?") raise self.PError("can't have a constant as assignment target, perhaps you wanted indirection [...] instead?")
r_value = self.parse_expression(rhs) r_value = self.parse_expression(rhs)
for lv in l_values: for lv in l_values:
assignable, reason = lv.assignable_from(r_value) assignable, reason = lv.assignable_from(r_value)
@ -1220,7 +1249,7 @@ class Parser:
else: else:
raise self.PError("invalid value '" + text + "'") raise self.PError("invalid value '" + text + "'")
def parse_indirect_value(self, text: str) -> Tuple[str, ParseResult.IndirectValue]: def parse_indirect_value(self, text: str, allow_mmapped_for_call: bool=False) -> Tuple[str, ParseResult.IndirectValue]:
indirect = text[1:-1].strip() indirect = text[1:-1].strip()
indirect2, sep, typestr = indirect.rpartition('.') indirect2, sep, typestr = indirect.rpartition('.')
type_modifier = None type_modifier = None
@ -1240,8 +1269,11 @@ class Parser:
if type_modifier not in (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 allow_mmapped_for_call:
raise self.PError("invalid type modifier for the value's datatype, must be " + expr.datatype.name) if type_modifier and expr.datatype != type_modifier:
raise self.PError("invalid type modifier for the value's datatype, must be " + expr.datatype.name)
else:
raise self.PError("use variable directly instead of using indirect addressing")
return indirect, ParseResult.IndirectValue(expr, type_modifier) return indirect, ParseResult.IndirectValue(expr, type_modifier)
def is_identifier(self, name: str) -> bool: def is_identifier(self, name: str) -> bool:
@ -1367,10 +1399,10 @@ class Optimizer:
if len(lvalues) != len(stmt.leftvalues): if len(lvalues) != len(stmt.leftvalues):
print("{:s}:{:d}: removed duplicate assignment targets".format(block.sourceref.file, stmt.lineno)) print("{:s}:{:d}: removed duplicate assignment targets".format(block.sourceref.file, stmt.lineno))
# change order: first registers, then zp addresses, then non-zp addresses, then the rest (if any) # change order: first registers, then zp addresses, then non-zp addresses, then the rest (if any)
stmt.leftvalues = list(sorted(lvalues, key=value_sortkey)) stmt.leftvalues = list(sorted(lvalues, key=_value_sortkey))
def value_sortkey(value: ParseResult.Value) -> int: def _value_sortkey(value: ParseResult.Value) -> int:
if isinstance(value, ParseResult.RegisterValue): if isinstance(value, ParseResult.RegisterValue):
num = 0 num = 0
for char in value.register: for char in value.register:
@ -1378,18 +1410,11 @@ def value_sortkey(value: ParseResult.Value) -> int:
num += ord(char) num += ord(char)
return num return num
elif isinstance(value, ParseResult.MemMappedValue): elif isinstance(value, ParseResult.MemMappedValue):
if value.address is None:
return 99999999
if value.address < 0x100: if value.address < 0x100:
return 10000 + value.address return 10000 + value.address
else: else:
return 20000 + value.address return 20000 + value.address
else: else:
return 99999999 return 99999999
if __name__ == "__main__":
p = Parser("readme.txt", outputdir="output")
p.cur_block = ParseResult.Block("test", SourceRef("testfile", 1), None)
p.parse_subx_def("subx SUBNAME (A, test2:XY, X) -> (A?, X) = $c000")
sub = list(p.cur_block.symbols.iter_subroutines())[0]
import pprint
pprint.pprint(vars(sub))

View File

@ -405,6 +405,7 @@ class SymbolTable:
self.eval_dict = None self.eval_dict = None
def merge_roots(self, other_root: 'SymbolTable') -> None: def merge_roots(self, other_root: 'SymbolTable') -> None:
assert self.parent is None and other_root.parent is None
for name, thing in other_root.symbols.items(): for name, thing in other_root.symbols.items():
if isinstance(thing, SymbolTable): if isinstance(thing, SymbolTable):
try: try:
@ -428,7 +429,7 @@ class SymbolTable:
def print_symbols(symbols: 'SymbolTable', level: int) -> None: def print_symbols(symbols: 'SymbolTable', level: int) -> None:
indent = '\t' * level indent = '\t' * level
print("\n" + indent + "BLOCK:", symbols.name) print("\n" + indent + "BLOCK:", symbols.name)
for name, s in sorted(symbols.symbols.items(), key=lambda x: getattr(x[1], "sourceref", ("", 0))): for name, s in sorted(symbols.symbols.items(), key=lambda x: str(getattr(x[1], "sourceref", ""))):
if isinstance(s, SymbolTable): if isinstance(s, SymbolTable):
print_symbols(s, level + 1) print_symbols(s, level + 1)
elif isinstance(s, SubroutineDef): elif isinstance(s, SubroutineDef):

View File

@ -93,68 +93,68 @@ output raw
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1. ; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
; checked functions below: ; checked functions below:
subx MOVFM (mflpt: AY) -> (A?, Y?) = $bba2 ; load mflpt value from memory in A/Y into fac1 sub MOVFM (mflpt: AY) -> (A?, Y?) = $bba2 ; load mflpt value from memory in A/Y into fac1
subx FREADMEM () -> (A?, Y?) = $bba6 ; load mflpt value from memory in $22/$23 into fac1 sub FREADMEM () -> (A?, Y?) = $bba6 ; load mflpt value from memory in $22/$23 into fac1
subx CONUPK (mflpt: AY) -> (A?, Y?) = $ba8c ; load mflpt value from memory in A/Y into fac2 sub CONUPK (mflpt: AY) -> (A?, Y?) = $ba8c ; load mflpt value from memory in A/Y into fac2
subx FAREADMEM () -> (A?, Y?) = $ba90 ; load mflpt value from memory in $22/$23 into fac2 sub FAREADMEM () -> (A?, Y?) = $ba90 ; load mflpt value from memory in $22/$23 into fac2
subx MOVFA () -> (A?, X?) = $bbfc ; copy fac2 to fac1 sub MOVFA () -> (A?, X?) = $bbfc ; copy fac2 to fac1
subx MOVAF () -> (A?, X?) = $bc0c ; copy fac1 to fac2 (rounded) sub MOVAF () -> (A?, X?) = $bc0c ; copy fac1 to fac2 (rounded)
subx MOVEF () -> (A?, X?) = $bc0f ; copy fac1 to fac2 sub MOVEF () -> (A?, X?) = $bc0f ; copy fac1 to fac2
subx FTOMEMXY (mflpt: XY) -> (A?, Y?) = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt sub FTOMEMXY (mflpt: XY) -> (A?, Y?) = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt
subx FTOSWORDYA () -> (Y, A, X?) = $b1aa ; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY) sub FTOSWORDYA () -> (Y, A, X?) = $b1aa ; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
; use c64util.FTOSWRDAY to get A/Y output (lo/hi switched to normal order) ; use c64util.FTOSWRDAY to get A/Y output (lo/hi switched to normal order)
subx GETADR () -> (Y, A, X?) = $b7f7 ; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) sub GETADR () -> (Y, A, X?) = $b7f7 ; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY)
; (result also in $14/15) use c64util.GETADRAY to get A/Y output (lo/hi switched to normal order) ; (result also in $14/15) use c64util.GETADRAY to get A/Y output (lo/hi switched to normal order)
subx QINT () -> (A?, X?, Y?) = $bc9b ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST. sub QINT () -> (A?, X?, Y?) = $bc9b ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
subx AYINT () -> (A?, X?, Y?) = $b1bf ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY) sub AYINT () -> (A?, X?, Y?) = $b1bf ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
subx GIVAYF (lo: Y, hi: A) -> (A?, X?, Y?) = $b391 ; signed word in Y/A -> float in fac1 sub GIVAYF (lo: Y, hi: A) -> (A?, X?, Y?) = $b391 ; signed word in Y/A -> float in fac1
; use c64util.GIVAYFAY to use A/Y input (lo/hi switched to normal order) ; use c64util.GIVAYFAY to use A/Y input (lo/hi switched to normal order)
; there is also c64util.GIVUAYF - unsigned word in A/Y (lo/hi) to fac1 ; there is also c64util.GIVUAYF - unsigned word in A/Y (lo/hi) to fac1
; there is also c64util.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST ; there is also c64util.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
; there is also c64util.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST ; there is also c64util.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
; there is also c64util.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes) ; there is also c64util.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
subx FREADUY (ubyte: Y) -> (A?, X?, Y?) = $b3a2 ; 8 bit unsigned Y -> float in fac1 sub FREADUY (ubyte: Y) -> (A?, X?, Y?) = $b3a2 ; 8 bit unsigned Y -> float in fac1
subx FREADSA (sbyte: A) -> (A?, X?, Y?) = $bc3c ; 8 bit signed A -> float in fac1 sub FREADSA (sbyte: A) -> (A?, X?, Y?) = $bc3c ; 8 bit signed A -> float in fac1
subx FREADSTR (len: A) -> (A?, X?, Y?) = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length sub FREADSTR (len: A) -> (A?, X?, Y?) = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length
subx FPRINTLN () -> (A?, X?, Y?) = $aabc ; print string of fac1, on one line (= with newline) sub FPRINTLN () -> (A?, X?, Y?) = $aabc ; print string of fac1, on one line (= with newline)
subx FOUT () -> (AY, X?) = $bddd ; fac1 -> string, address returned in AY ($0100) sub FOUT () -> (AY, X?) = $bddd ; fac1 -> string, address returned in AY ($0100)
subx FADDH () -> (A?, X?, Y?) = $b849 ; fac1 += 0.5, for rounding- call this before INT sub FADDH () -> (A?, X?, Y?) = $b849 ; fac1 += 0.5, for rounding- call this before INT
subx MUL10 () -> (A?, X?, Y?) = $bae2 ; fac1 *= 10 sub MUL10 () -> (A?, X?, Y?) = $bae2 ; fac1 *= 10
subx DIV10 () -> (A?, X?, Y?) = $bafe ; fac1 /= 10 , CAUTION: result is always positive! sub DIV10 () -> (A?, X?, Y?) = $bafe ; fac1 /= 10 , CAUTION: result is always positive!
subx FCOMP (mflpt: AY) -> (A, X?, Y?) = $bc5b ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than sub FCOMP (mflpt: AY) -> (A, X?, Y?) = $bc5b ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
subx FADDT () -> (A?, X?, Y?) = $b86a ; fac1 += fac2 sub FADDT () -> (A?, X?, Y?) = $b86a ; fac1 += fac2
subx FADD (mflpt: AY) -> (A?, X?, Y?) = $b867 ; fac1 += mflpt value from A/Y sub FADD (mflpt: AY) -> (A?, X?, Y?) = $b867 ; fac1 += mflpt value from A/Y
subx FSUBT () -> (A?, X?, Y?) = $b853 ; fac1 = fac2-fac1 mind the order of the operands sub FSUBT () -> (A?, X?, Y?) = $b853 ; fac1 = fac2-fac1 mind the order of the operands
subx FSUB (mflpt: AY) -> (A?, X?, Y?) = $b850 ; fac1 = mflpt from A/Y - fac1 sub FSUB (mflpt: AY) -> (A?, X?, Y?) = $b850 ; fac1 = mflpt from A/Y - fac1
subx FMULTT () -> (A?, X?, Y?) = $ba2b ; fac1 *= fac2 sub FMULTT () -> (A?, X?, Y?) = $ba2b ; fac1 *= fac2
subx FMULT (mflpt: AY) -> (A?, X?, Y?) = $ba28 ; fac1 *= mflpt value from A/Y sub FMULT (mflpt: AY) -> (A?, X?, Y?) = $ba28 ; fac1 *= mflpt value from A/Y
subx FDIVT () -> (A?, X?, Y?) = $bb12 ; fac1 = fac2/fac1 mind the order of the operands sub FDIVT () -> (A?, X?, Y?) = $bb12 ; fac1 = fac2/fac1 mind the order of the operands
subx FDIV (mflpt: AY) -> (A?, X?, Y?) = $bb0f ; fac1 = mflpt in A/Y / fac1 sub FDIV (mflpt: AY) -> (A?, X?, Y?) = $bb0f ; fac1 = mflpt in A/Y / fac1
subx FPWRT () -> (A?, X?, Y?) = $bf7b ; fac1 = fac2 ** fac1 sub FPWRT () -> (A?, X?, Y?) = $bf7b ; fac1 = fac2 ** fac1
subx FPWR (mflpt: AY) -> (A?, X?, Y?) = $bf78 ; fac1 = fac2 ** mflpt from A/Y sub FPWR (mflpt: AY) -> (A?, X?, Y?) = $bf78 ; fac1 = fac2 ** mflpt from A/Y
subx NOTOP () -> (A?, X?, Y?) = $aed4 ; fac1 = NOT(fac1) sub NOTOP () -> (A?, X?, Y?) = $aed4 ; fac1 = NOT(fac1)
subx INT () -> (A?, X?, Y?) = $bccc ; INT() truncates, use FADDH first to round instead of trunc sub INT () -> (A?, X?, Y?) = $bccc ; INT() truncates, use FADDH first to round instead of trunc
subx LOG () -> (A?, X?, Y?) = $b9ea ; fac1 = LN(fac1) (natural log) sub LOG () -> (A?, X?, Y?) = $b9ea ; fac1 = LN(fac1) (natural log)
subx SGN () -> (A?, X?, Y?) = $bc39 ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1) sub SGN () -> (A?, X?, Y?) = $bc39 ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
subx SIGN () -> (A) = $bc2b ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive sub SIGN () -> (A) = $bc2b ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
subx ABS () -> () = $bc58 ; fac1 = ABS(fac1) sub ABS () -> () = $bc58 ; fac1 = ABS(fac1)
subx SQR () -> (A?, X?, Y?) = $bf71 ; fac1 = SQRT(fac1) sub SQR () -> (A?, X?, Y?) = $bf71 ; fac1 = SQRT(fac1)
subx EXP () -> (A?, X?, Y?) = $bfed ; fac1 = EXP(fac1) (e ** fac1) sub EXP () -> (A?, X?, Y?) = $bfed ; fac1 = EXP(fac1) (e ** fac1)
subx NEGOP () -> (A?) = $bfb4 ; switch the sign of fac1 sub NEGOP () -> (A?) = $bfb4 ; switch the sign of fac1
subx RND () -> (A?, X?, Y?) = $e097 ; fac1 = RND() sub RND () -> (A?, X?, Y?) = $e097 ; fac1 = RND()
subx COS () -> (A?, X?, Y?) = $e264 ; fac1 = COS(fac1) sub COS () -> (A?, X?, Y?) = $e264 ; fac1 = COS(fac1)
subx SIN () -> (A?, X?, Y?) = $e26b ; fac1 = SIN(fac1) sub SIN () -> (A?, X?, Y?) = $e26b ; fac1 = SIN(fac1)
subx TAN () -> (A?, X?, Y?) = $e2b4 ; fac1 = TAN(fac1) sub TAN () -> (A?, X?, Y?) = $e2b4 ; fac1 = TAN(fac1)
subx ATN () -> (A?, X?, Y?) = $e30e ; fac1 = ATN(fac1) sub ATN () -> (A?, X?, Y?) = $e30e ; fac1 = ATN(fac1)
; ---- C64 basic routines ---- ; ---- C64 basic routines ----
subx CLEARSCR () -> (A?, X?, Y?) = $E544 ; clear the screen sub CLEARSCR () -> (A?, X?, Y?) = $E544 ; clear the screen
subx HOMECRSR () -> (A?, X?, Y?) = $E566 ; cursor to top left of screen sub HOMECRSR () -> (A?, X?, Y?) = $E566 ; cursor to top left of screen
; ---- end of C64 basic routines ---- ; ---- end of C64 basic routines ----
@ -163,45 +163,45 @@ subx HOMECRSR () -> (A?, X?, Y?) = $E566 ; cursor to top left of screen
; ---- C64 kernal routines ---- ; ---- C64 kernal routines ----
subx CINT () -> (A?, X?, Y?) = $FF81 ; (alias: SCINIT) initialize screen editor and video chip sub CINT () -> (A?, X?, Y?) = $FF81 ; (alias: SCINIT) initialize screen editor and video chip
subx IOINIT () -> (A?, X?) = $FF84 ; initialize I/O devices sub IOINIT () -> (A?, X?) = $FF84 ; initialize I/O devices
subx RAMTAS () -> (A?, X?, Y?) = $FF87 ; initialize RAM, tape buffer, screen sub RAMTAS () -> (A?, X?, Y?) = $FF87 ; initialize RAM, tape buffer, screen
subx RESTOR () -> () = $FF8A ; restore default I/O vectors sub RESTOR () -> () = $FF8A ; restore default I/O vectors
subx VECTOR (dir: SC, userptr: XY) -> (A?, Y?) = $FF8D ; read/set I/O vector table sub VECTOR (dir: SC, userptr: XY) -> (A?, Y?) = $FF8D ; read/set I/O vector table
subx SETMSG (value: A) -> () = $FF90 ; set Kernal message control flag sub SETMSG (value: A) -> () = $FF90 ; set Kernal message control flag
subx SECOND (address: A) -> (A?) = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN sub SECOND (address: A) -> (A?) = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN
subx TKSA (address: A) -> (A?) = $FF96 ; (alias: TALKSA) send secondary address after TALK sub TKSA (address: A) -> (A?) = $FF96 ; (alias: TALKSA) send secondary address after TALK
subx MEMTOP (dir: SC, address: XY) -> (XY) = $FF99 ; read/set top of memory pointer sub MEMTOP (dir: SC, address: XY) -> (XY) = $FF99 ; read/set top of memory pointer
subx MEMBOT (dir: SC, address: XY) -> (XY) = $FF9C ; read/set bottom of memory pointer sub MEMBOT (dir: SC, address: XY) -> (XY) = $FF9C ; read/set bottom of memory pointer
subx SCNKEY () -> (A?, X?, Y?) = $FF9F ; scan the keyboard sub SCNKEY () -> (A?, X?, Y?) = $FF9F ; scan the keyboard
subx SETTMO (timeout: A) -> () = $FFA2 ; set time-out flag for IEEE bus sub SETTMO (timeout: A) -> () = $FFA2 ; set time-out flag for IEEE bus
subx ACPTR () -> (A) = $FFA5 ; (alias: IECIN) input byte from serial bus sub ACPTR () -> (A) = $FFA5 ; (alias: IECIN) input byte from serial bus
subx CIOUT (byte: A) -> () = $FFA8 ; (alias: IECOUT) output byte to serial bus sub CIOUT (byte: A) -> () = $FFA8 ; (alias: IECOUT) output byte to serial bus
subx UNTLK () -> (A?) = $FFAB ; command serial bus device to UNTALK sub UNTLK () -> (A?) = $FFAB ; command serial bus device to UNTALK
subx UNLSN () -> (A?) = $FFAE ; command serial bus device to UNLISTEN sub UNLSN () -> (A?) = $FFAE ; command serial bus device to UNLISTEN
subx LISTEN (device: A) -> (A?) = $FFB1 ; command serial bus device to LISTEN sub LISTEN (device: A) -> (A?) = $FFB1 ; command serial bus device to LISTEN
subx TALK (device: A) -> (A?) = $FFB4 ; command serial bus device to TALK sub TALK (device: A) -> (A?) = $FFB4 ; command serial bus device to TALK
subx READST () -> (A) = $FFB7 ; read I/O status word sub READST () -> (A) = $FFB7 ; read I/O status word
subx SETLFS (logical: A, device: X, address: Y) -> () = $FFBA ; set logical file parameters sub SETLFS (logical: A, device: X, address: Y) -> () = $FFBA ; set logical file parameters
subx SETNAM (namelen: A, filename: XY) -> () = $FFBD ; set filename parameters sub SETNAM (namelen: A, filename: XY) -> () = $FFBD ; set filename parameters
subx OPEN () -> (A?, X?, Y?) = $FFC0 ; (via 794 ($31A)) open a logical file sub OPEN () -> (A?, X?, Y?) = $FFC0 ; (via 794 ($31A)) open a logical file
subx CLOSE (logical: A) -> (A?, X?, Y?) = $FFC3 ; (via 796 ($31C)) close a logical file sub CLOSE (logical: A) -> (A?, X?, Y?) = $FFC3 ; (via 796 ($31C)) close a logical file
subx CHKIN (logical: X) -> (A?, X?) = $FFC6 ; (via 798 ($31E)) define an input channel sub CHKIN (logical: X) -> (A?, X?) = $FFC6 ; (via 798 ($31E)) define an input channel
subx CHKOUT (logical: X) -> (A?, X?) = $FFC9 ; (via 800 ($320)) define an output channel sub CHKOUT (logical: X) -> (A?, X?) = $FFC9 ; (via 800 ($320)) define an output channel
subx CLRCHN () -> (A?, X?) = $FFCC ; (via 802 ($322)) restore default devices sub CLRCHN () -> (A?, X?) = $FFCC ; (via 802 ($322)) restore default devices
subx CHRIN () -> (A, Y?) = $FFCF ; (via 804 ($324)) input a character sub CHRIN () -> (A, Y?) = $FFCF ; (via 804 ($324)) input a character
subx CHROUT (char: A) -> () = $FFD2 ; (via 806 ($326)) output a character sub CHROUT (char: A) -> () = $FFD2 ; (via 806 ($326)) output a character
subx LOAD (verify: A, address: XY) -> (SC, A, X, Y) = $FFD5 ; (via 816 ($330)) load from device sub LOAD (verify: A, address: XY) -> (SC, A, X, Y) = $FFD5 ; (via 816 ($330)) load from device
subx SAVE (zp_startaddr: A, endaddr: XY) -> (SC, A) = $FFD8 ; (via 818 ($332)) save to a device sub SAVE (zp_startaddr: A, endaddr: XY) -> (SC, A) = $FFD8 ; (via 818 ($332)) save to a device
subx SETTIM (low: A, middle: X, high: Y) -> () = $FFDB ; set the software clock sub SETTIM (low: A, middle: X, high: Y) -> () = $FFDB ; set the software clock
subx RDTIM () -> (A, X, Y) = $FFDE ; read the software clock sub RDTIM () -> (A, X, Y) = $FFDE ; read the software clock
subx STOP () -> (SZ, SC, A?, X?) = $FFE1 ; (via 808 ($328)) check the STOP key sub STOP () -> (SZ, SC, A?, X?) = $FFE1 ; (via 808 ($328)) check the STOP key
subx GETIN () -> (A, X?, Y?) = $FFE4 ; (via 810 ($32A)) get a character sub GETIN () -> (A, X?, Y?) = $FFE4 ; (via 810 ($32A)) get a character
subx CLALL () -> (A?, X?) = $FFE7 ; (via 812 ($32C)) close all files sub CLALL () -> (A?, X?) = $FFE7 ; (via 812 ($32C)) close all files
subx UDTIM () -> (A?, X?) = $FFEA ; update the software clock sub UDTIM () -> (A?, X?) = $FFEA ; update the software clock
subx SCREEN () -> (X, Y) = $FFED ; read number of screen rows and columns sub SCREEN () -> (X, Y) = $FFED ; read number of screen rows and columns
subx PLOT (dir: SC, col: X, row: Y) -> (X, Y) = $FFF0 ; read/set position of cursor on screen sub PLOT (dir: SC, col: X, row: Y) -> (X, Y) = $FFF0 ; read/set position of cursor on screen
subx IOBASE () -> (X, Y) = $FFF3 ; read base address of I/O devices sub IOBASE () -> (X, Y) = $FFF3 ; read base address of I/O devices
; ---- end of C64 kernal routines ---- ; ---- end of C64 kernal routines ----
@ -211,11 +211,10 @@ subx IOBASE () -> (X, Y) = $FFF3 ; read base addres
} }
~ c64util { ~ c64util {
; @todo use user-defined subroutines here to have param definitions sub FREADS32 () -> (A?, X?, Y?) {
subx FREADS32 () -> (A?, X?, Y?) {
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST) ; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
asm { asm {
lda $62 lda $62
@ -227,7 +226,7 @@ subx FREADS32 () -> (A?, X?, Y?) {
} }
} }
subx FREADUS32 () -> (A?, X?, Y?) { sub FREADUS32 () -> (A?, X?, Y?) {
; ---- fac1 = uint32 from $62-$65 big endian (MSB FIRST) ; ---- fac1 = uint32 from $62-$65 big endian (MSB FIRST)
asm { asm {
sec sec
@ -237,7 +236,7 @@ subx FREADUS32 () -> (A?, X?, Y?) {
} }
} }
subx FREADS24AXY (lo: A, mid: X, hi: Y) -> (A?, X?, Y?) { sub FREADS24AXY (lo: A, mid: X, hi: Y) -> (A?, X?, Y?) {
; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes) ; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes)
; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead. ; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead.
asm { asm {
@ -254,7 +253,7 @@ subx FREADS24AXY (lo: A, mid: X, hi: Y) -> (A?, X?, Y?) {
} }
} }
subx GIVUAYF (uword: AY) -> (A?, X?, Y?) { sub GIVUAYF (uword: AY) -> (A?, X?, Y?) {
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1 ; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
asm { asm {
sty $62 sty $62
@ -265,7 +264,7 @@ subx GIVUAYF (uword: AY) -> (A?, X?, Y?) {
} }
} }
subx GIVAYFAY (sword: AY) -> (A?, X?, Y?) { sub GIVAYFAY (sword: AY) -> (A?, X?, Y?) {
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1 ; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
asm { asm {
sta c64.SCRATCH_ZP1 sta c64.SCRATCH_ZP1
@ -275,7 +274,7 @@ subx GIVAYFAY (sword: AY) -> (A?, X?, Y?) {
} }
} }
subx FTOSWRDAY () -> (A, Y, X?) { sub FTOSWRDAY () -> (A, Y, X?) {
; ---- fac1 to signed word in A/Y ; ---- fac1 to signed word in A/Y
asm { asm {
jsr c64.FTOSWORDYA ; note the inverse Y/A order jsr c64.FTOSWORDYA ; note the inverse Y/A order
@ -286,7 +285,7 @@ subx FTOSWRDAY () -> (A, Y, X?) {
} }
} }
subx GETADRAY () -> (A, Y, X?) { sub GETADRAY () -> (A, Y, X?) {
; ---- fac1 to unsigned word in A/Y ; ---- fac1 to unsigned word in A/Y
asm { asm {
jsr c64.GETADR ; this uses the inverse order, Y/A jsr c64.GETADR ; this uses the inverse order, Y/A
@ -297,7 +296,7 @@ subx GETADRAY () -> (A, Y, X?) {
} }
} }
subx print_string (address: XY) -> (A?, Y?) { sub print_string (address: XY) -> (A?, Y?) {
; ---- print null terminated string from X/Y ; ---- print null terminated string from X/Y
asm { asm {
stx c64.SCRATCH_ZP1 stx c64.SCRATCH_ZP1
@ -312,7 +311,7 @@ subx print_string (address: XY) -> (A?, Y?) {
} }
} }
subx print_pstring (address: XY) -> (A?, X?, Y) { sub print_pstring (address: XY) -> (A?, X?, Y) {
; ---- print pstring (length as first byte) from X/Y, returns str len in Y ; ---- print pstring (length as first byte) from X/Y, returns str len in Y
asm { asm {
stx c64.SCRATCH_ZP1 stx c64.SCRATCH_ZP1
@ -330,7 +329,7 @@ subx print_pstring (address: XY) -> (A?, X?, Y) {
} }
} }
subx print_pimmediate () -> () { sub print_pimmediate () -> () {
; ---- print pstring in memory immediately following the subroutine fast call instruction ; ---- print pstring in memory immediately following the subroutine fast call instruction
; note that the clobbered registers (A,X,Y) are not listed ON PURPOSE ; note that the clobbered registers (A,X,Y) are not listed ON PURPOSE
asm { asm {
@ -358,7 +357,7 @@ subx print_pimmediate () -> () {
} }
} }
subx byte2decimal (ubyte: A) -> (Y, X, A) { sub byte2decimal (ubyte: A) -> (Y, X, A) {
; ---- A to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A) ; ---- A to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A)
asm { asm {
ldy #$2f ldy #$2f
@ -375,7 +374,7 @@ subx byte2decimal (ubyte: A) -> (Y, X, A) {
} }
} }
subx byte2hex (ubyte: A) -> (X, Y, A?) { sub byte2hex (ubyte: A) -> (X, Y, A?) {
; ---- A to hex string in XY (first hex char in X, second hex char in Y) ; ---- A to hex string in XY (first hex char in X, second hex char in Y)
asm { asm {
pha pha
@ -399,7 +398,7 @@ hex_digits .text "0123456789abcdef" ; can probably be reused for other st
var .array(3) word2bcd_bcdbuff var .array(3) word2bcd_bcdbuff
subx word2bcd (address: XY) -> (A?, X?) { sub word2bcd (address: XY) -> (A?, X?) {
; Convert an 16 bit binary value to BCD ; Convert an 16 bit binary value to BCD
; ;
; This function converts a 16 bit binary value in X/Y into a 24 bit BCD. It ; This function converts a 16 bit binary value in X/Y into a 24 bit BCD. It
@ -437,7 +436,7 @@ subx word2bcd (address: XY) -> (A?, X?) {
var .array(5) word2decimal_output var .array(5) word2decimal_output
subx word2decimal (address: XY) -> (A?, X?, Y?) { sub word2decimal (address: XY) -> (A?, X?, Y?) {
; ---- convert 16 bit word in X/Y into decimal string into memory 'word2decimal_output' ; ---- convert 16 bit word in X/Y into decimal string into memory 'word2decimal_output'
asm { asm {
jsr word2bcd jsr word2bcd
@ -468,7 +467,7 @@ subx word2decimal (address: XY) -> (A?, X?, Y?) {
} }
} }
subx print_byte_decimal0 (ubyte: A) -> (A?, X?, Y?) { sub print_byte_decimal0 (ubyte: A) -> (A?, X?, Y?) {
; ---- print the byte in A in decimal form, with left padding 0s (3 positions total) ; ---- print the byte in A in decimal form, with left padding 0s (3 positions total)
asm { asm {
jsr byte2decimal jsr byte2decimal
@ -482,7 +481,7 @@ subx print_byte_decimal0 (ubyte: A) -> (A?, X?, Y?) {
} }
} }
subx print_byte_decimal (ubyte: A) -> (A?, X?, Y?) { sub print_byte_decimal (ubyte: A) -> (A?, X?, Y?) {
; ---- print the byte in A in decimal form, without left padding 0s ; ---- print the byte in A in decimal form, without left padding 0s
asm { asm {
jsr byte2decimal jsr byte2decimal
@ -500,7 +499,7 @@ subx print_byte_decimal (ubyte: A) -> (A?, X?, Y?) {
} }
} }
subx print_byte_hex (ubyte: A) -> (A?, X?, Y?) { sub print_byte_hex (ubyte: A) -> (A?, X?, Y?) {
; ---- print the byte in A in hex form ; ---- print the byte in A in hex form
asm { asm {
jsr byte2hex jsr byte2hex
@ -512,7 +511,7 @@ subx print_byte_hex (ubyte: A) -> (A?, X?, Y?) {
} }
subx print_word_decimal0 (address: XY) -> (A?, X?, Y?) { sub print_word_decimal0 (address: XY) -> (A?, X?, Y?) {
; ---- print the word in X/Y in decimal form, with left padding 0s (5 positions total) ; ---- print the word in X/Y in decimal form, with left padding 0s (5 positions total)
asm { asm {
jsr word2decimal jsr word2decimal
@ -530,7 +529,7 @@ subx print_word_decimal0 (address: XY) -> (A?, X?, Y?) {
} }
subx print_word_decimal (address: XY) -> (A?, X? Y?) { sub print_word_decimal (address: XY) -> (A?, X? Y?) {
; ---- print the word in X/Y in decimal form, without left padding 0s ; ---- print the word in X/Y in decimal form, without left padding 0s
asm { asm {
jsr word2decimal jsr word2decimal

View File

@ -154,9 +154,12 @@ Expressions can contain function calls to the math library (sin, cos, etc) and y
all builtin functions (max, avg, min, sum etc). They can also reference idendifiers defined elsewhere in your code, all builtin functions (max, avg, min, sum etc). They can also reference idendifiers defined elsewhere in your code,
if this makes sense. if this makes sense.
The syntax "[address]" means: the contents of the memory at address. The syntax "[address]" means: the contents of the memory at address, or "indirect addressing".
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.
This addressing mode is only supported for constant (integer) addresses and not for variable types,
unless it is part of a subroutine call statement. For an indirect goto call, the 6502 CPU has a special opcode
(JMP indirect) and an indirect subroutine call (JSR indirect) is synthesized using a couple of instructions.
Everything after a semicolon ';' is a comment and is ignored, however the comment (if it is the only thing 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. on the line) is copied into the resulting assembly source code.
@ -316,40 +319,44 @@ MEMORY BLOCK OPERATIONS:
- strings: identical operations as on lists. - strings: identical operations as on lists.
these should call (or emit inline) optimized pieces of assembly code, so they run as fast as possible
these call (or emit inline) optimized pieces of assembly code, so they run as fast as possible
SUBROUTINES DEFINITIONS SUBROUTINES DEFINITIONS
----------------------- -----------------------
External subroutines for instance defined in ROM, can be defined using the 'subx' statement. Subroutines are parts of the code that can be repeatedly invoked using a subroutine call from elsewhere.
Their definition, using the sub statement, includes the specification of the required input- and output parameters.
For now, only register based parameters are supported (A, X, Y and paired registers, and the carry status bit SC as a special).
The syntax is:
subx <identifier> ([proc_parameters]) -> ([proc_results]) <address> sub <identifier> ([proc_parameters]) -> ([proc_results]) {
... statements ...
}
proc_parameters = sequence of "<parametername>:<register>" pairs that specify what the input parameters are proc_parameters = comma separated list of "<parametername>:<register>" pairs specifying the input parameters
proc_results = sequence of <register> names that specify in which register(s) the output is returned proc_results = comma separated list of <register> names specifying in which register(s) the output is returned.
if the name ends with a '?', that means the register doesn't contain a real return value but If the register name ends with a '?', that means the register doesn't contain a real return value but
is clobbered in the process so the original value it had before calling the sub is no longer valid. is clobbered in the process so the original value it had before calling the sub is no longer valid.
This is not immediately useful for your own code, but the compiler needs this information to
example: "subx CLOSE (logical: A) -> (A?, X?, Y?) $FFC3" emit the correct assembly code to preserve the cpu registers if needed when the call is made.
REGISTER PRESERVATION BLOCK: @todo (no)preserve Subroutines that are pre-defined on a specific memory location (usually routines from ROM),
can also be defined using the 'sub' statement. But in this case you don't supply a block with statements,
but instead assign a memory address to it:
preserve [regs] { .... } adds register preservation around the containing code default = all 3 regs, or specify which. sub <identifier> ([proc_parameters]) -> ([proc_results]) = <address>
nopreserve [regs] { .... } removes register preservation on all statements in the block that would otherwise have it.
example: "sub CLOSE (logical: A) -> (A?, X?, Y?) = $FFC3"
@todo document user defined subroutines
SUBROUTINE CALLS SUBROUTINE CALLS
---------------- ----------------
You call a subroutine like this: You call a subroutine like this:
subroutinename[!] ( [arguments...] ) subroutinename_or_address [!] ( [arguments...] )
Normally, the registers are preserved when calling the subroutine and restored on return. Normally, the registers are preserved when calling the subroutine and restored on return.
If you add a '!' after the name, no register preserving is done and the call essentially If you add a '!' after the name, no register preserving is done and the call essentially
@ -363,9 +370,16 @@ 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. essentially is the same as calling a subroutine and only doing something different when it's finished.
@todo support call non-register args (variable parameter passing) @todo support call non-register args (variable parameter passing)
@todo support call return values (so that you can assign these to other variables, and allows the line to be a full expression) @todo support assigning call return values (so that you can assign these to other variables, and allows the subroutine call be an actual expression)
REGISTER PRESERVATION BLOCK: @todo (no)preserve
----------------------------
preserve [regs] { .... } adds register preservation around the containing code default = all 3 regs, or specify which.
nopreserve [regs] { .... } removes register preservation on all statements in the block that would otherwise have it.
@todo BITMAP DEFINITIONS: @todo BITMAP DEFINITIONS:

View File

@ -15,8 +15,8 @@
const .float constf = 3.4556677 const .float constf = 3.4556677
const .text constt = "derp" const .text constt = "derp"
subx sub1 () -> (X?) = $ffdd sub sub1 () -> (X?) = $ffdd
subx sub2 (A) -> (Y?) = $eecc sub sub2 (A) -> (Y?) = $eecc
bar bar

View File

@ -88,7 +88,7 @@ clobberzp
const .byte cbyte2 = 1 const .byte cbyte2 = 1
const .byte cbyte3 = '@' const .byte cbyte3 = '@'
const .byte cbyte4 = true const .byte cbyte4 = true
const .word cbyte6 = false const .word cword1 = false
const .word cword2 = $1234 const .word cword2 = $1234
const .word cword5 = 9876.5432 const .word cword5 = 9876.5432
const cfloat1 = 1.2345 const cfloat1 = 1.2345
@ -134,6 +134,10 @@ start
; --- immediate primitive value assignments ---- ; --- immediate primitive value assignments ----
A = [$99]
A = [$aabb]
A = $99
A = [cbyte3]
A = 0 A = 0
A = '@' A = '@'
A = 1.2345 A = 1.2345
@ -147,10 +151,6 @@ start
A = cbyte3 A = cbyte3
A = membyte2 A = membyte2
A = uninitbyte1 A = uninitbyte1
;A = [membyte2] ; @todo error, invalid rvalue, use membyte without indirect?
;A = [membyte2.byte] ; @todo error, "
;A = [cbyte3] ; @todo error invalid rvalue
;A = [initbytea0] ; @todo error, invalid rvalue, use initbytea0 without indirect?
XY = 0 XY = 0
@ -162,7 +162,8 @@ start
XY = false XY = false
XY = text XY = text
XY = cbyte3 XY = cbyte3
XY = cword2 XY = [cbyte3]
XY = [cword2]
XY = uninitbyte1 XY = uninitbyte1
XY = "text-immediate" XY = "text-immediate"
AY = "text-immediate" AY = "text-immediate"
@ -172,12 +173,8 @@ start
AX = "" AX = ""
AX = XY AX = XY
AX = Y AX = Y
XY = [membyte2] ; @todo indirection error?
XY = [membyte2.byte] ; @todo indirection error?
XY = membyte2 XY = membyte2
XY = #membyte2 XY = #membyte2
XY = [memword1] ; @todo indirection error?
XY = [memword1.word] ; @todo indirection error?
XY = memword1 XY = memword1
XY = sin XY = sin
XY = #sin XY = #sin
@ -190,9 +187,9 @@ start
[$c000] = false [$c000] = false
[$c000] = cbyte3 [$c000] = cbyte3
[$c000] = uninitbyte1 [$c000] = uninitbyte1
[$c000] = [membyte2] ; @todo indirection error?
[$c000] = [membyte2.byte] ; @todo indirection error?
[$c000] = membyte2 [$c000] = membyte2
[$c000] = cbyte2
[$c000] = [cword2]
[$c000.word] = A [$c000.word] = A
[$c000.word] = AX [$c000.word] = AX
@ -203,12 +200,9 @@ start
[$c000.word] = "text" [$c000.word] = "text"
[$c000.word] = "" [$c000.word] = ""
[$c000.word] = uninitbyte1 [$c000.word] = uninitbyte1
[$c000.word] = [membyte2] ; @todo indirection error?
[$c000.word] = [membyte2.byte] ; @todo indirection error?
[$c000.word] = membyte2 [$c000.word] = membyte2
[$c000.word] = #membyte2 [$c000.word] = #membyte2
[$c000.word] = [memword1] ; @todo indirection error? [$c000.word] = [cword2]
[$c000.word] = [memword1.word] ; @todo indirection error?
[$c000.word] = memword1 [$c000.word] = memword1
[$c000.float] = 65535 [$c000.float] = 65535
[$c000.float] = 456.66 [$c000.float] = 456.66
@ -268,10 +262,6 @@ start
membyte1 = 22 membyte1 = 22
memword1 = 2233 memword1 = 2233
memfloat = 3.4567 memfloat = 3.4567
;[membyte1] = 33 ; @todo error, invalid lvalue, use without []
;[memword1] = 4444 ; @todo error ^
;[memword1] = [AX] ; @todo error, only address allowed in []
;[memfloat] = 5.5566 ; @todo error ^
memword1 = sin memword1 = sin
memword1 = #sin memword1 = #sin
@ -281,13 +271,17 @@ start
memfloat = cbyte3 memfloat = cbyte3
memfloat = cword2 memfloat = cword2
; @todo float assignments that require ROM functions: ; @todo float assignments that require ROM functions or shims:
; memfloat = Y ; memfloat = Y
; memfloat = XY ; memfloat = XY
; uninitfloat = Y ; uninitfloat = Y
; uninitfloat = XY ; uninitfloat = XY
; initfloat2 = Y ; initfloat2 = Y
; initfloat2 = XY ; initfloat2 = XY
; initfloat2 = initbyte2
; initfloat2 = initword2
; initfloat1 = uninitfloat
; initfloat1 = initfloat2
return return
} }

View File

@ -48,22 +48,19 @@ start
var .float flt_half_pi = 1.5707963267948966 var .float flt_half_pi = 1.5707963267948966
var .float flt_double_pi = 6.283185307179586 var .float flt_double_pi = 6.283185307179586
var .float flt_point25 = .25 var .float flt_point25 = .25
var .float my_float
memory .word some_address = $ccdd memory .word some_address = $ccdd
memory .byte some_addressb = $ccee memory .byte some_addressb = $ccee
var .byte bytevar = $cc
var .word wordvar = $cdef
start start
; assign some float values to the memory ; 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 = 4123.2342342222
[some_address] = # flt_pi
[some_address] = 4123.2342342222
[some_address] = 4123.2342342222
[some_address] = 4123.2342342222
[some_address.word] = #flt_pi
[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 ; print some floating points from source and compare them with ROM
@ -139,5 +136,4 @@ start
c64.FPRINTLN!() c64.FPRINTLN!()
return return
} }

View File

@ -73,7 +73,7 @@ _loop block2.zpw1 ++
X -- X --
Y-- Y--
[$d020]-- [$d020]--
[block2.zpw2] = 99 block2.zpw2 = 99
fidget.subroutine() fidget.subroutine()
goto _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
@ -85,9 +85,9 @@ _loop block2.zpw1 ++
return return
subx memsub () -> () = $fff2 sub memsub () -> () = $fff2
subx customsub (Y)->() { sub customsub (Y)->() {
asm { asm {
nop nop