diff --git a/il65/astparse.py b/il65/astparse.py index 1cc1548d7..45734165d 100644 --- a/il65/astparse.py +++ b/il65/astparse.py @@ -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("", None, None) - symbols.define_variable("derp", SourceRef("", 1), DataType.BYTE, address=2345) - result = parse_expr_as_primitive("2+#derp", symbols, None, SourceRef("", 1)) - print("EXPRESSION RESULT:", result) diff --git a/il65/codegen.py b/il65/codegen.py index a7c847103..3937d9c7b 100644 --- a/il65/codegen.py +++ b/il65/codegen.py @@ -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: diff --git a/il65/parse.py b/il65/parse.py index ead2d4cdc..45cf605bd 100644 --- a/il65/parse.py +++ b/il65/parse.py @@ -99,6 +99,20 @@ class ParseResult: def __str__(self): return "".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 "".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 "".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 "".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 "".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\w+)\s+" + match = re.match(r"^sub\s+(?P\w+)\s+" r"\((?P[\w\s:,]*)\)" r"\s*->\s*" r"\((?P[\w\s?,]*)\)\s*" r"(?P\s+=\s+(?P
\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 type_modifier and expr.datatype != type_modifier: - raise self.PError("invalid type modifier for the value's datatype, must be " + expr.datatype.name) + 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)) diff --git a/il65/symbols.py b/il65/symbols.py index 0fa0b6c8f..fa3c0b270 100644 --- a/il65/symbols.py +++ b/il65/symbols.py @@ -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): diff --git a/lib/c64lib.ill b/lib/c64lib.ill index 47cd1bf2f..9fb6b5305 100644 --- a/lib/c64lib.ill +++ b/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 diff --git a/reference.txt b/reference.txt index 927406a16..6de20b102 100644 --- a/reference.txt +++ b/reference.txt @@ -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 ([proc_parameters]) -> ([proc_results])
+ sub ([proc_parameters]) -> ([proc_results]) { + ... statements ... + } -proc_parameters = sequence of ":" pairs that specify what the input parameters are -proc_results = sequence of 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 - 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" + proc_parameters = comma separated list of ":" pairs specifying the input parameters + proc_results = comma separated list of 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. + 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 ([proc_parameters]) -> ([proc_results]) =
- -@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: diff --git a/testsource/calls.ill b/testsource/calls.ill index e379149e1..d5e7baa54 100644 --- a/testsource/calls.ill +++ b/testsource/calls.ill @@ -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 diff --git a/testsource/dtypes.ill b/testsource/dtypes.ill index 4908e7597..735b6f481 100644 --- a/testsource/dtypes.ill +++ b/testsource/dtypes.ill @@ -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 } diff --git a/testsource/floats.ill b/testsource/floats.ill index 0cb63e066..2db5298ee 100644 --- a/testsource/floats.ill +++ b/testsource/floats.ill @@ -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 - } diff --git a/testsource/source1.ill b/testsource/source1.ill index f3bdf29df..b2bd18d05 100644 --- a/testsource/source1.ill +++ b/testsource/source1.ill @@ -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