mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
subroutines and assignment changes
This commit is contained in:
parent
63aa3cae8c
commit
4025d44b74
@ -8,7 +8,7 @@ License: GNU GPL 3.0, see LICENSE
|
||||
|
||||
import ast
|
||||
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):
|
||||
@ -191,10 +191,3 @@ class ExpressionTransformer(EvaluatingTransformer):
|
||||
else:
|
||||
raise self.error("invalid MatMult/Pointer node in AST")
|
||||
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)
|
||||
|
@ -634,7 +634,31 @@ class CodeGenerator:
|
||||
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))
|
||||
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:
|
||||
raise CodeError("invalid lvalue type", lv.datatype)
|
||||
|
||||
@ -803,7 +827,7 @@ class CodeGenerator:
|
||||
else:
|
||||
raise CodeError("can only assign a byte or word to a word", str(rvalue))
|
||||
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))
|
||||
|
||||
def generate_assign_char_to_memory(self, lv: ParseResult.MemMappedValue, char_str: str) -> None:
|
||||
|
@ -99,6 +99,20 @@ class ParseResult:
|
||||
def __str__(self):
|
||||
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]:
|
||||
if self.constant:
|
||||
return False, "cannot assign to a constant"
|
||||
@ -151,7 +165,7 @@ class ParseResult:
|
||||
elif self is other:
|
||||
return True
|
||||
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):
|
||||
return "<IntegerValue {} name={}>".format(self.value, self.name)
|
||||
@ -173,7 +187,7 @@ class ParseResult:
|
||||
elif self is other:
|
||||
return True
|
||||
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):
|
||||
return "<FloatValue {} name={}>".format(self.value, self.name)
|
||||
@ -184,7 +198,7 @@ class ParseResult:
|
||||
self.value = value
|
||||
|
||||
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:
|
||||
if not isinstance(other, ParseResult.StringValue):
|
||||
@ -192,7 +206,7 @@ class ParseResult:
|
||||
elif self is other:
|
||||
return True
|
||||
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):
|
||||
return "<StringValue {!r:s} name={} constant={}>".format(self.value, self.name, self.constant)
|
||||
@ -213,7 +227,7 @@ class ParseResult:
|
||||
elif self is other:
|
||||
return True
|
||||
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):
|
||||
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
|
||||
|
||||
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:
|
||||
if not isinstance(other, ParseResult.MemMappedValue):
|
||||
@ -279,8 +293,8 @@ class ParseResult:
|
||||
elif self is other:
|
||||
return True
|
||||
else:
|
||||
return other.datatype == self.datatype and other.address == self.address and \
|
||||
other.length == self.length and other.name == self.name
|
||||
return (other.datatype, other.address, other.length, other.name, other.constant) ==\
|
||||
(self.datatype, self.address, self.length, self.name, self.constant)
|
||||
|
||||
def __str__(self):
|
||||
addr = "" if self.address is None else "${:04x}".format(self.address)
|
||||
@ -365,6 +379,10 @@ class ParseResult:
|
||||
self.right.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):
|
||||
def __init__(self, a: 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'")
|
||||
|
||||
def _parse_2(self) -> None:
|
||||
# parsing pass 2
|
||||
# parsing pass 2 (not done during preprocessing!)
|
||||
self.cur_block = None
|
||||
self.sourceref.line = -1
|
||||
self.sourceref.column = 0
|
||||
for block in self.result.blocks:
|
||||
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
|
||||
for index, stmt in enumerate(list(block.statements)):
|
||||
if isinstance(stmt, ParseResult.CallStmt):
|
||||
@ -719,7 +749,6 @@ class Parser:
|
||||
# first line contains block header "~ [name] [addr]" followed by a '{'
|
||||
self._parse_comments()
|
||||
line = self.next_line()
|
||||
block_def_lineno = self.sourceref.line
|
||||
line = line.lstrip()
|
||||
if not line.startswith("~"):
|
||||
raise self.PError("expected '~' (block)")
|
||||
@ -792,7 +821,7 @@ class Parser:
|
||||
self.parse_const_def(line)
|
||||
elif line.startswith(("memory ", "memory\t")):
|
||||
self.parse_memory_def(line, is_zp_block)
|
||||
elif line.startswith(("subx ", "subx\t")):
|
||||
elif line.startswith(("sub ", "sub\t")):
|
||||
if is_zp_block:
|
||||
raise self.PError("ZP block cannot contain subroutines")
|
||||
self.parse_subx_def(line)
|
||||
@ -814,7 +843,7 @@ class Parser:
|
||||
raise self.PError("ZP block cannot contain code labels")
|
||||
self.parse_label(line)
|
||||
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:
|
||||
label_line = line.split(maxsplit=1)
|
||||
@ -855,13 +884,13 @@ class Parser:
|
||||
raise self.PError(str(x)) from x
|
||||
|
||||
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"\s*->\s*"
|
||||
r"\((?P<results>[\w\s?,]*)\)\s*"
|
||||
r"(?P<decltype>\s+=\s+(?P<address>\S*)|{)\s*$", line)
|
||||
if not match:
|
||||
raise self.PError("invalid subx declaration")
|
||||
raise self.PError("invalid sub declaration")
|
||||
code_decl = match.group("decltype") == "{"
|
||||
name, parameterlist, resultlist, address_str = \
|
||||
match.group("name"), match.group("parameters"), match.group("results"), match.group("address")
|
||||
@ -889,7 +918,7 @@ class Parser:
|
||||
if line == "}":
|
||||
# subroutine end
|
||||
break
|
||||
if line.startswith(("subx ", "subx\t")):
|
||||
if line.startswith(("sub ", "sub\t")):
|
||||
raise self.PError("cannot nest subroutines")
|
||||
elif line.startswith(("asm ", "asm\t")):
|
||||
self.prev_line()
|
||||
@ -899,7 +928,7 @@ class Parser:
|
||||
elif line:
|
||||
self.parse_label(line)
|
||||
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.sourceref = subroutine_block.sourceref
|
||||
else:
|
||||
@ -993,7 +1022,7 @@ class Parser:
|
||||
target = None # type: ParseResult.Value
|
||||
if targetstr[0] == '[' and targetstr[-1] == ']':
|
||||
# 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:
|
||||
raise self.PError("invalid call target (should contain 16-bit)")
|
||||
else:
|
||||
@ -1005,7 +1034,7 @@ class Parser:
|
||||
and not isinstance(target.value, (ParseResult.IntegerValue, ParseResult.RegisterValue, ParseResult.MemMappedValue)):
|
||||
raise self.PError("cannot call that type of indirect symbol")
|
||||
address = target.address if isinstance(target, ParseResult.MemMappedValue) else None
|
||||
_, symbol = self.cur_block.lookup(targetstr)
|
||||
_, symbol = self.lookup(targetstr)
|
||||
if isinstance(symbol, SubroutineDef):
|
||||
# verify subroutine arguments
|
||||
if len(arguments or []) != len(symbol.parameters):
|
||||
@ -1050,7 +1079,7 @@ class Parser:
|
||||
rhs = parts.pop()
|
||||
l_values = [self.parse_expression(part) for part in parts]
|
||||
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)
|
||||
for lv in l_values:
|
||||
assignable, reason = lv.assignable_from(r_value)
|
||||
@ -1220,7 +1249,7 @@ class Parser:
|
||||
else:
|
||||
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()
|
||||
indirect2, sep, typestr = indirect.rpartition('.')
|
||||
type_modifier = None
|
||||
@ -1240,8 +1269,11 @@ class Parser:
|
||||
if type_modifier not in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||
raise self.PError("invalid type modifier for the value's datatype")
|
||||
elif isinstance(expr, ParseResult.MemMappedValue):
|
||||
if allow_mmapped_for_call:
|
||||
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)
|
||||
|
||||
def is_identifier(self, name: str) -> bool:
|
||||
@ -1367,10 +1399,10 @@ class Optimizer:
|
||||
if len(lvalues) != len(stmt.leftvalues):
|
||||
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)
|
||||
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):
|
||||
num = 0
|
||||
for char in value.register:
|
||||
@ -1378,18 +1410,11 @@ def value_sortkey(value: ParseResult.Value) -> int:
|
||||
num += ord(char)
|
||||
return num
|
||||
elif isinstance(value, ParseResult.MemMappedValue):
|
||||
if value.address is None:
|
||||
return 99999999
|
||||
if value.address < 0x100:
|
||||
return 10000 + value.address
|
||||
else:
|
||||
return 20000 + value.address
|
||||
else:
|
||||
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))
|
||||
|
@ -405,6 +405,7 @@ class SymbolTable:
|
||||
self.eval_dict = 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():
|
||||
if isinstance(thing, SymbolTable):
|
||||
try:
|
||||
@ -428,7 +429,7 @@ class SymbolTable:
|
||||
def print_symbols(symbols: 'SymbolTable', level: int) -> None:
|
||||
indent = '\t' * level
|
||||
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):
|
||||
print_symbols(s, level + 1)
|
||||
elif isinstance(s, SubroutineDef):
|
||||
|
215
lib/c64lib.ill
215
lib/c64lib.ill
@ -93,68 +93,68 @@ output raw
|
||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||
|
||||
; checked functions below:
|
||||
subx 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
|
||||
subx 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
|
||||
subx MOVFA () -> (A?, X?) = $bbfc ; copy fac2 to fac1
|
||||
subx MOVAF () -> (A?, X?) = $bc0c ; copy fac1 to fac2 (rounded)
|
||||
subx 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
|
||||
subx FTOSWORDYA () -> (Y, A, X?) = $b1aa ; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
||||
sub MOVFM (mflpt: AY) -> (A?, Y?) = $bba2 ; load mflpt value from memory in A/Y into fac1
|
||||
sub FREADMEM () -> (A?, Y?) = $bba6 ; load mflpt value from memory in $22/$23 into fac1
|
||||
sub CONUPK (mflpt: AY) -> (A?, Y?) = $ba8c ; load mflpt value from memory in A/Y into fac2
|
||||
sub FAREADMEM () -> (A?, Y?) = $ba90 ; load mflpt value from memory in $22/$23 into fac2
|
||||
sub MOVFA () -> (A?, X?) = $bbfc ; copy fac2 to fac1
|
||||
sub MOVAF () -> (A?, X?) = $bc0c ; copy fac1 to fac2 (rounded)
|
||||
sub MOVEF () -> (A?, X?) = $bc0f ; copy fac1 to fac2
|
||||
sub FTOMEMXY (mflpt: XY) -> (A?, Y?) = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt
|
||||
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)
|
||||
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)
|
||||
subx 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)
|
||||
subx GIVAYF (lo: Y, hi: A) -> (A?, X?, Y?) = $b391 ; signed word in Y/A -> float in fac1
|
||||
sub QINT () -> (A?, X?, Y?) = $bc9b ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
||||
sub AYINT () -> (A?, X?, Y?) = $b1bf ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||
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)
|
||||
; 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.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)
|
||||
subx 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
|
||||
subx 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)
|
||||
subx FOUT () -> (AY, X?) = $bddd ; fac1 -> string, address returned in AY ($0100)
|
||||
sub FREADUY (ubyte: Y) -> (A?, X?, Y?) = $b3a2 ; 8 bit unsigned Y -> float in fac1
|
||||
sub FREADSA (sbyte: A) -> (A?, X?, Y?) = $bc3c ; 8 bit signed A -> float in fac1
|
||||
sub FREADSTR (len: A) -> (A?, X?, Y?) = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length
|
||||
sub FPRINTLN () -> (A?, X?, Y?) = $aabc ; print string of fac1, on one line (= with newline)
|
||||
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
|
||||
subx MUL10 () -> (A?, X?, Y?) = $bae2 ; fac1 *= 10
|
||||
subx 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 FADDH () -> (A?, X?, Y?) = $b849 ; fac1 += 0.5, for rounding- call this before INT
|
||||
sub MUL10 () -> (A?, X?, Y?) = $bae2 ; fac1 *= 10
|
||||
sub DIV10 () -> (A?, X?, Y?) = $bafe ; fac1 /= 10 , CAUTION: result is always positive!
|
||||
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
|
||||
subx 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
|
||||
subx FSUB (mflpt: AY) -> (A?, X?, Y?) = $b850 ; fac1 = mflpt from A/Y - fac1
|
||||
subx FMULTT () -> (A?, X?, Y?) = $ba2b ; fac1 *= fac2
|
||||
subx 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
|
||||
subx FDIV (mflpt: AY) -> (A?, X?, Y?) = $bb0f ; fac1 = mflpt in A/Y / fac1
|
||||
subx FPWRT () -> (A?, X?, Y?) = $bf7b ; fac1 = fac2 ** fac1
|
||||
subx FPWR (mflpt: AY) -> (A?, X?, Y?) = $bf78 ; fac1 = fac2 ** mflpt from A/Y
|
||||
sub FADDT () -> (A?, X?, Y?) = $b86a ; fac1 += fac2
|
||||
sub FADD (mflpt: AY) -> (A?, X?, Y?) = $b867 ; fac1 += mflpt value from A/Y
|
||||
sub FSUBT () -> (A?, X?, Y?) = $b853 ; fac1 = fac2-fac1 mind the order of the operands
|
||||
sub FSUB (mflpt: AY) -> (A?, X?, Y?) = $b850 ; fac1 = mflpt from A/Y - fac1
|
||||
sub FMULTT () -> (A?, X?, Y?) = $ba2b ; fac1 *= fac2
|
||||
sub FMULT (mflpt: AY) -> (A?, X?, Y?) = $ba28 ; fac1 *= mflpt value from A/Y
|
||||
sub FDIVT () -> (A?, X?, Y?) = $bb12 ; fac1 = fac2/fac1 mind the order of the operands
|
||||
sub FDIV (mflpt: AY) -> (A?, X?, Y?) = $bb0f ; fac1 = mflpt in A/Y / fac1
|
||||
sub FPWRT () -> (A?, X?, Y?) = $bf7b ; fac1 = fac2 ** fac1
|
||||
sub FPWR (mflpt: AY) -> (A?, X?, Y?) = $bf78 ; fac1 = fac2 ** mflpt from A/Y
|
||||
|
||||
subx NOTOP () -> (A?, X?, Y?) = $aed4 ; fac1 = NOT(fac1)
|
||||
subx 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)
|
||||
subx 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
|
||||
subx ABS () -> () = $bc58 ; fac1 = ABS(fac1)
|
||||
subx SQR () -> (A?, X?, Y?) = $bf71 ; fac1 = SQRT(fac1)
|
||||
subx EXP () -> (A?, X?, Y?) = $bfed ; fac1 = EXP(fac1) (e ** fac1)
|
||||
subx NEGOP () -> (A?) = $bfb4 ; switch the sign of fac1
|
||||
subx RND () -> (A?, X?, Y?) = $e097 ; fac1 = RND()
|
||||
subx COS () -> (A?, X?, Y?) = $e264 ; fac1 = COS(fac1)
|
||||
subx SIN () -> (A?, X?, Y?) = $e26b ; fac1 = SIN(fac1)
|
||||
subx TAN () -> (A?, X?, Y?) = $e2b4 ; fac1 = TAN(fac1)
|
||||
subx ATN () -> (A?, X?, Y?) = $e30e ; fac1 = ATN(fac1)
|
||||
sub NOTOP () -> (A?, X?, Y?) = $aed4 ; fac1 = NOT(fac1)
|
||||
sub INT () -> (A?, X?, Y?) = $bccc ; INT() truncates, use FADDH first to round instead of trunc
|
||||
sub LOG () -> (A?, X?, Y?) = $b9ea ; fac1 = LN(fac1) (natural log)
|
||||
sub SGN () -> (A?, X?, Y?) = $bc39 ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||
sub SIGN () -> (A) = $bc2b ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||
sub ABS () -> () = $bc58 ; fac1 = ABS(fac1)
|
||||
sub SQR () -> (A?, X?, Y?) = $bf71 ; fac1 = SQRT(fac1)
|
||||
sub EXP () -> (A?, X?, Y?) = $bfed ; fac1 = EXP(fac1) (e ** fac1)
|
||||
sub NEGOP () -> (A?) = $bfb4 ; switch the sign of fac1
|
||||
sub RND () -> (A?, X?, Y?) = $e097 ; fac1 = RND()
|
||||
sub COS () -> (A?, X?, Y?) = $e264 ; fac1 = COS(fac1)
|
||||
sub SIN () -> (A?, X?, Y?) = $e26b ; fac1 = SIN(fac1)
|
||||
sub TAN () -> (A?, X?, Y?) = $e2b4 ; fac1 = TAN(fac1)
|
||||
sub ATN () -> (A?, X?, Y?) = $e30e ; fac1 = ATN(fac1)
|
||||
|
||||
|
||||
; ---- C64 basic routines ----
|
||||
|
||||
subx CLEARSCR () -> (A?, X?, Y?) = $E544 ; clear the screen
|
||||
subx HOMECRSR () -> (A?, X?, Y?) = $E566 ; cursor to top left of screen
|
||||
sub CLEARSCR () -> (A?, X?, Y?) = $E544 ; clear the screen
|
||||
sub HOMECRSR () -> (A?, X?, Y?) = $E566 ; cursor to top left of screen
|
||||
|
||||
|
||||
; ---- end of C64 basic routines ----
|
||||
@ -163,45 +163,45 @@ subx HOMECRSR () -> (A?, X?, Y?) = $E566 ; cursor to top left of screen
|
||||
|
||||
; ---- C64 kernal routines ----
|
||||
|
||||
subx CINT () -> (A?, X?, Y?) = $FF81 ; (alias: SCINIT) initialize screen editor and video chip
|
||||
subx IOINIT () -> (A?, X?) = $FF84 ; initialize I/O devices
|
||||
subx RAMTAS () -> (A?, X?, Y?) = $FF87 ; initialize RAM, tape buffer, screen
|
||||
subx RESTOR () -> () = $FF8A ; restore default I/O vectors
|
||||
subx VECTOR (dir: SC, userptr: XY) -> (A?, Y?) = $FF8D ; read/set I/O vector table
|
||||
subx SETMSG (value: A) -> () = $FF90 ; set Kernal message control flag
|
||||
subx 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
|
||||
subx 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
|
||||
subx SCNKEY () -> (A?, X?, Y?) = $FF9F ; scan the keyboard
|
||||
subx SETTMO (timeout: A) -> () = $FFA2 ; set time-out flag for IEEE bus
|
||||
subx ACPTR () -> (A) = $FFA5 ; (alias: IECIN) input byte from serial bus
|
||||
subx CIOUT (byte: A) -> () = $FFA8 ; (alias: IECOUT) output byte to serial bus
|
||||
subx UNTLK () -> (A?) = $FFAB ; command serial bus device to UNTALK
|
||||
subx UNLSN () -> (A?) = $FFAE ; command serial bus device to UNLISTEN
|
||||
subx LISTEN (device: A) -> (A?) = $FFB1 ; command serial bus device to LISTEN
|
||||
subx TALK (device: A) -> (A?) = $FFB4 ; command serial bus device to TALK
|
||||
subx READST () -> (A) = $FFB7 ; read I/O status word
|
||||
subx SETLFS (logical: A, device: X, address: Y) -> () = $FFBA ; set logical file parameters
|
||||
subx SETNAM (namelen: A, filename: XY) -> () = $FFBD ; set filename parameters
|
||||
subx 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
|
||||
subx 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
|
||||
subx CLRCHN () -> (A?, X?) = $FFCC ; (via 802 ($322)) restore default devices
|
||||
subx CHRIN () -> (A, Y?) = $FFCF ; (via 804 ($324)) input a character
|
||||
subx 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
|
||||
subx 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
|
||||
subx RDTIM () -> (A, X, Y) = $FFDE ; read the software clock
|
||||
subx 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
|
||||
subx CLALL () -> (A?, X?) = $FFE7 ; (via 812 ($32C)) close all files
|
||||
subx UDTIM () -> (A?, X?) = $FFEA ; update the software clock
|
||||
subx 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
|
||||
subx IOBASE () -> (X, Y) = $FFF3 ; read base address of I/O devices
|
||||
sub CINT () -> (A?, X?, Y?) = $FF81 ; (alias: SCINIT) initialize screen editor and video chip
|
||||
sub IOINIT () -> (A?, X?) = $FF84 ; initialize I/O devices
|
||||
sub RAMTAS () -> (A?, X?, Y?) = $FF87 ; initialize RAM, tape buffer, screen
|
||||
sub RESTOR () -> () = $FF8A ; restore default I/O vectors
|
||||
sub VECTOR (dir: SC, userptr: XY) -> (A?, Y?) = $FF8D ; read/set I/O vector table
|
||||
sub SETMSG (value: A) -> () = $FF90 ; set Kernal message control flag
|
||||
sub SECOND (address: A) -> (A?) = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
sub TKSA (address: A) -> (A?) = $FF96 ; (alias: TALKSA) send secondary address after TALK
|
||||
sub MEMTOP (dir: SC, address: XY) -> (XY) = $FF99 ; read/set top of memory pointer
|
||||
sub MEMBOT (dir: SC, address: XY) -> (XY) = $FF9C ; read/set bottom of memory pointer
|
||||
sub SCNKEY () -> (A?, X?, Y?) = $FF9F ; scan the keyboard
|
||||
sub SETTMO (timeout: A) -> () = $FFA2 ; set time-out flag for IEEE bus
|
||||
sub ACPTR () -> (A) = $FFA5 ; (alias: IECIN) input byte from serial bus
|
||||
sub CIOUT (byte: A) -> () = $FFA8 ; (alias: IECOUT) output byte to serial bus
|
||||
sub UNTLK () -> (A?) = $FFAB ; command serial bus device to UNTALK
|
||||
sub UNLSN () -> (A?) = $FFAE ; command serial bus device to UNLISTEN
|
||||
sub LISTEN (device: A) -> (A?) = $FFB1 ; command serial bus device to LISTEN
|
||||
sub TALK (device: A) -> (A?) = $FFB4 ; command serial bus device to TALK
|
||||
sub READST () -> (A) = $FFB7 ; read I/O status word
|
||||
sub SETLFS (logical: A, device: X, address: Y) -> () = $FFBA ; set logical file parameters
|
||||
sub SETNAM (namelen: A, filename: XY) -> () = $FFBD ; set filename parameters
|
||||
sub OPEN () -> (A?, X?, Y?) = $FFC0 ; (via 794 ($31A)) open a logical file
|
||||
sub CLOSE (logical: A) -> (A?, X?, Y?) = $FFC3 ; (via 796 ($31C)) close a logical file
|
||||
sub CHKIN (logical: X) -> (A?, X?) = $FFC6 ; (via 798 ($31E)) define an input channel
|
||||
sub CHKOUT (logical: X) -> (A?, X?) = $FFC9 ; (via 800 ($320)) define an output channel
|
||||
sub CLRCHN () -> (A?, X?) = $FFCC ; (via 802 ($322)) restore default devices
|
||||
sub CHRIN () -> (A, Y?) = $FFCF ; (via 804 ($324)) input a character
|
||||
sub CHROUT (char: A) -> () = $FFD2 ; (via 806 ($326)) output a character
|
||||
sub LOAD (verify: A, address: XY) -> (SC, A, X, Y) = $FFD5 ; (via 816 ($330)) load from device
|
||||
sub SAVE (zp_startaddr: A, endaddr: XY) -> (SC, A) = $FFD8 ; (via 818 ($332)) save to a device
|
||||
sub SETTIM (low: A, middle: X, high: Y) -> () = $FFDB ; set the software clock
|
||||
sub RDTIM () -> (A, X, Y) = $FFDE ; read the software clock
|
||||
sub STOP () -> (SZ, SC, A?, X?) = $FFE1 ; (via 808 ($328)) check the STOP key
|
||||
sub GETIN () -> (A, X?, Y?) = $FFE4 ; (via 810 ($32A)) get a character
|
||||
sub CLALL () -> (A?, X?) = $FFE7 ; (via 812 ($32C)) close all files
|
||||
sub UDTIM () -> (A?, X?) = $FFEA ; update the software clock
|
||||
sub SCREEN () -> (X, Y) = $FFED ; read number of screen rows and columns
|
||||
sub PLOT (dir: SC, col: X, row: Y) -> (X, Y) = $FFF0 ; read/set position of cursor on screen
|
||||
sub IOBASE () -> (X, Y) = $FFF3 ; read base address of I/O devices
|
||||
|
||||
; ---- end of C64 kernal routines ----
|
||||
|
||||
@ -211,11 +211,10 @@ subx IOBASE () -> (X, Y) = $FFF3 ; read base addres
|
||||
|
||||
}
|
||||
|
||||
|
||||
~ c64util {
|
||||
|
||||
; @todo use user-defined subroutines here to have param definitions
|
||||
|
||||
subx FREADS32 () -> (A?, X?, Y?) {
|
||||
sub FREADS32 () -> (A?, X?, Y?) {
|
||||
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
|
||||
asm {
|
||||
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)
|
||||
asm {
|
||||
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)
|
||||
; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead.
|
||||
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
|
||||
asm {
|
||||
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
|
||||
asm {
|
||||
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
|
||||
asm {
|
||||
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
|
||||
asm {
|
||||
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
|
||||
asm {
|
||||
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
|
||||
asm {
|
||||
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
|
||||
; note that the clobbered registers (A,X,Y) are not listed ON PURPOSE
|
||||
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)
|
||||
asm {
|
||||
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)
|
||||
asm {
|
||||
pha
|
||||
@ -399,7 +398,7 @@ hex_digits .text "0123456789abcdef" ; can probably be reused for other st
|
||||
|
||||
|
||||
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
|
||||
;
|
||||
; 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
|
||||
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'
|
||||
asm {
|
||||
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)
|
||||
asm {
|
||||
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
|
||||
asm {
|
||||
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
|
||||
asm {
|
||||
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)
|
||||
asm {
|
||||
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
|
||||
asm {
|
||||
jsr word2decimal
|
||||
|
@ -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,
|
||||
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
|
||||
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
|
||||
on the line) is copied into the resulting assembly source code.
|
||||
@ -316,40 +319,44 @@ MEMORY BLOCK OPERATIONS:
|
||||
|
||||
- strings: identical operations as on lists.
|
||||
|
||||
|
||||
these call (or emit inline) optimized pieces of assembly code, so they run as fast as possible
|
||||
these should call (or emit inline) optimized pieces of assembly code, so they run as fast as possible
|
||||
|
||||
|
||||
|
||||
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_results = sequence of <register> names that specify 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
|
||||
proc_parameters = comma separated list of "<parametername>:<register>" pairs specifying the input parameters
|
||||
proc_results = comma separated list of <register> names specifying in which register(s) the output is returned.
|
||||
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.
|
||||
|
||||
example: "subx CLOSE (logical: A) -> (A?, X?, Y?) $FFC3"
|
||||
This is not immediately useful for your own code, but the compiler needs this information to
|
||||
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.
|
||||
nopreserve [regs] { .... } removes register preservation on all statements in the block that would otherwise have it.
|
||||
sub <identifier> ([proc_parameters]) -> ([proc_results]) = <address>
|
||||
|
||||
|
||||
@todo document user defined subroutines
|
||||
example: "sub CLOSE (logical: A) -> (A?, X?, Y?) = $FFC3"
|
||||
|
||||
|
||||
SUBROUTINE CALLS
|
||||
----------------
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
||||
|
||||
@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:
|
||||
|
@ -15,8 +15,8 @@
|
||||
const .float constf = 3.4556677
|
||||
const .text constt = "derp"
|
||||
|
||||
subx sub1 () -> (X?) = $ffdd
|
||||
subx sub2 (A) -> (Y?) = $eecc
|
||||
sub sub1 () -> (X?) = $ffdd
|
||||
sub sub2 (A) -> (Y?) = $eecc
|
||||
|
||||
|
||||
bar
|
||||
|
@ -88,7 +88,7 @@ clobberzp
|
||||
const .byte cbyte2 = 1
|
||||
const .byte cbyte3 = '@'
|
||||
const .byte cbyte4 = true
|
||||
const .word cbyte6 = false
|
||||
const .word cword1 = false
|
||||
const .word cword2 = $1234
|
||||
const .word cword5 = 9876.5432
|
||||
const cfloat1 = 1.2345
|
||||
@ -134,6 +134,10 @@ start
|
||||
|
||||
; --- immediate primitive value assignments ----
|
||||
|
||||
A = [$99]
|
||||
A = [$aabb]
|
||||
A = $99
|
||||
A = [cbyte3]
|
||||
A = 0
|
||||
A = '@'
|
||||
A = 1.2345
|
||||
@ -147,10 +151,6 @@ start
|
||||
A = cbyte3
|
||||
A = membyte2
|
||||
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
|
||||
@ -162,7 +162,8 @@ start
|
||||
XY = false
|
||||
XY = text
|
||||
XY = cbyte3
|
||||
XY = cword2
|
||||
XY = [cbyte3]
|
||||
XY = [cword2]
|
||||
XY = uninitbyte1
|
||||
XY = "text-immediate"
|
||||
AY = "text-immediate"
|
||||
@ -172,12 +173,8 @@ start
|
||||
AX = ""
|
||||
AX = XY
|
||||
AX = Y
|
||||
XY = [membyte2] ; @todo indirection error?
|
||||
XY = [membyte2.byte] ; @todo indirection error?
|
||||
XY = membyte2
|
||||
XY = #membyte2
|
||||
XY = [memword1] ; @todo indirection error?
|
||||
XY = [memword1.word] ; @todo indirection error?
|
||||
XY = memword1
|
||||
XY = sin
|
||||
XY = #sin
|
||||
@ -190,9 +187,9 @@ start
|
||||
[$c000] = false
|
||||
[$c000] = cbyte3
|
||||
[$c000] = uninitbyte1
|
||||
[$c000] = [membyte2] ; @todo indirection error?
|
||||
[$c000] = [membyte2.byte] ; @todo indirection error?
|
||||
[$c000] = membyte2
|
||||
[$c000] = cbyte2
|
||||
[$c000] = [cword2]
|
||||
|
||||
[$c000.word] = A
|
||||
[$c000.word] = AX
|
||||
@ -203,12 +200,9 @@ start
|
||||
[$c000.word] = "text"
|
||||
[$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] = [cword2]
|
||||
[$c000.word] = memword1
|
||||
[$c000.float] = 65535
|
||||
[$c000.float] = 456.66
|
||||
@ -268,10 +262,6 @@ start
|
||||
membyte1 = 22
|
||||
memword1 = 2233
|
||||
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
|
||||
|
||||
@ -281,13 +271,17 @@ start
|
||||
memfloat = cbyte3
|
||||
memfloat = cword2
|
||||
|
||||
; @todo float assignments that require ROM functions:
|
||||
; @todo float assignments that require ROM functions or shims:
|
||||
; memfloat = Y
|
||||
; memfloat = XY
|
||||
; uninitfloat = Y
|
||||
; uninitfloat = XY
|
||||
; initfloat2 = Y
|
||||
; initfloat2 = XY
|
||||
; initfloat2 = initbyte2
|
||||
; initfloat2 = initword2
|
||||
; initfloat1 = uninitfloat
|
||||
; initfloat1 = initfloat2
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -48,22 +48,19 @@ start
|
||||
var .float flt_half_pi = 1.5707963267948966
|
||||
var .float flt_double_pi = 6.283185307179586
|
||||
var .float flt_point25 = .25
|
||||
var .float my_float
|
||||
memory .word some_address = $ccdd
|
||||
memory .byte some_addressb = $ccee
|
||||
var .byte bytevar = $cc
|
||||
var .word wordvar = $cdef
|
||||
|
||||
|
||||
|
||||
start
|
||||
; assign some float values to the memory
|
||||
AY = #flt_pi
|
||||
[some_address] = # flt_pi
|
||||
[some_address] = # flt_pi
|
||||
[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
|
||||
some_address = # flt_pi
|
||||
some_address = 4123.2342342222
|
||||
[$c000.word] = # flt_pi
|
||||
|
||||
; print some floating points from source and compare them with ROM
|
||||
@ -139,5 +136,4 @@ start
|
||||
c64.FPRINTLN!()
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ _loop block2.zpw1 ++
|
||||
X --
|
||||
Y--
|
||||
[$d020]--
|
||||
[block2.zpw2] = 99
|
||||
block2.zpw2 = 99
|
||||
fidget.subroutine()
|
||||
goto _loop
|
||||
return 155,2,%00000101 ; will end up in A, X, Y
|
||||
@ -85,9 +85,9 @@ _loop block2.zpw1 ++
|
||||
|
||||
return
|
||||
|
||||
subx memsub () -> () = $fff2
|
||||
sub memsub () -> () = $fff2
|
||||
|
||||
subx customsub (Y)->() {
|
||||
sub customsub (Y)->() {
|
||||
|
||||
asm {
|
||||
nop
|
||||
|
Loading…
Reference in New Issue
Block a user