mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 01:29:28 +00:00
register preserve is no longer the default
This commit is contained in:
parent
5bfca554a4
commit
8a99844140
@ -19,7 +19,7 @@ which aims to provide many conveniences over raw assembly code (even when using
|
||||
- automatic variable allocations, automatic string variables and string sharing
|
||||
- automatic type conversions
|
||||
- floating point operations
|
||||
- automatically preserving and restoring CPU registers state, when calling routines that otherwise would clobber these
|
||||
- optional automatic preserving and restoring CPU registers state, when calling routines that otherwise would clobber these
|
||||
- abstracting away low level aspects such as zero page handling, program startup, explicit memory addresses
|
||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||
|
@ -27,12 +27,13 @@ class _AstNode:
|
||||
class Block(_AstNode):
|
||||
_unnamed_block_labels = {} # type: Dict[Block, str]
|
||||
|
||||
def __init__(self, name: str, sourceref: SourceRef, parent_scope: SymbolTable) -> None:
|
||||
def __init__(self, name: str, sourceref: SourceRef, parent_scope: SymbolTable, preserve_registers: bool=False) -> None:
|
||||
super().__init__(sourceref)
|
||||
self.address = 0
|
||||
self.name = name
|
||||
self.statements = [] # type: List[_AstNode]
|
||||
self.symbols = SymbolTable(name, parent_scope, self)
|
||||
self.preserve_registers = preserve_registers
|
||||
|
||||
@property
|
||||
def ignore(self) -> bool:
|
||||
@ -484,7 +485,7 @@ class CallStmt(_AstNode):
|
||||
def __init__(self, sourceref: SourceRef, target: Optional[Value] = None, *,
|
||||
address: Optional[int] = None, arguments: List[Tuple[str, Any]] = None,
|
||||
outputs: List[Tuple[str, Value]] = None, is_goto: bool = False,
|
||||
preserve_regs: bool = True, condition: IfCondition = None) -> None:
|
||||
preserve_regs: Set[str] = None, condition: IfCondition = None) -> None:
|
||||
if not is_goto:
|
||||
assert condition is None
|
||||
super().__init__(sourceref)
|
||||
|
571
il65/codegen.py
571
il65/codegen.py
@ -417,12 +417,11 @@ class CodeGenerator:
|
||||
self.p("\t\tin{:s}".format(reg.lower()))
|
||||
else:
|
||||
# x/y += 2..255
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\tt{:s}a".format(reg.lower()))
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + value_str)
|
||||
self.p("\t\tta{:s}".format(reg.lower()))
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\tt{:s}a".format(reg.lower()))
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + value_str)
|
||||
self.p("\t\tta{:s}".format(reg.lower()))
|
||||
elif reg == "AX":
|
||||
# AX += 1..255
|
||||
self.p("\t\tclc")
|
||||
@ -446,14 +445,14 @@ class CodeGenerator:
|
||||
self.p("+")
|
||||
else:
|
||||
# XY += 2..255
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + value_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tbcc +")
|
||||
self.p("\t\tiny")
|
||||
self.p("+\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + value_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tbcc +")
|
||||
self.p("\t\tiny")
|
||||
self.p("+")
|
||||
else:
|
||||
raise CodeError("invalid incr register: " + reg)
|
||||
else:
|
||||
@ -467,12 +466,11 @@ class CodeGenerator:
|
||||
self.p("\t\tde{:s}".format(reg.lower()))
|
||||
else:
|
||||
# x/y -= 2..255
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\tt{:s}a".format(reg.lower()))
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + value_str)
|
||||
self.p("\t\tta{:s}".format(reg.lower()))
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\tt{:s}a".format(reg.lower()))
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + value_str)
|
||||
self.p("\t\tta{:s}".format(reg.lower()))
|
||||
elif reg == "AX":
|
||||
# AX -= 1..255
|
||||
self.p("\t\tsec")
|
||||
@ -496,14 +494,14 @@ class CodeGenerator:
|
||||
self.p("+\t\tdex")
|
||||
else:
|
||||
# XY -= 2..255
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + value_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tbcs +")
|
||||
self.p("\t\tdey")
|
||||
self.p("+\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + value_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tbcs +")
|
||||
self.p("\t\tdey")
|
||||
self.p("+")
|
||||
else:
|
||||
raise CodeError("invalid decr register: " + reg)
|
||||
elif isinstance(stmt.what, (MemMappedValue, IndirectValue)):
|
||||
@ -519,16 +517,15 @@ class CodeGenerator:
|
||||
if howmuch == 1:
|
||||
self.p("\t\t{:s} {:s}".format("inc" if is_incr else "dec", what_str))
|
||||
else:
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\tlda " + what_str)
|
||||
if is_incr:
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + value_str)
|
||||
else:
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + value_str)
|
||||
self.p("\t\tsta " + what_str)
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\tlda " + what_str)
|
||||
if is_incr:
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + value_str)
|
||||
else:
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + value_str)
|
||||
self.p("\t\tsta " + what_str)
|
||||
elif what.datatype == DataType.WORD:
|
||||
if howmuch == 1:
|
||||
# mem.word +=/-= 1
|
||||
@ -538,32 +535,31 @@ class CodeGenerator:
|
||||
self.p("\t\tinc {:s}+1".format(what_str))
|
||||
self.p("+")
|
||||
else:
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\tlda " + what_str)
|
||||
self.p("\t\tbne +")
|
||||
self.p("\t\tdec {:s}+1".format(what_str))
|
||||
self.p("+\t\tdec " + what_str)
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\tlda " + what_str)
|
||||
self.p("\t\tbne +")
|
||||
self.p("\t\tdec {:s}+1".format(what_str))
|
||||
self.p("+\t\tdec " + what_str)
|
||||
else:
|
||||
# mem.word +=/-= 2..255
|
||||
if is_incr:
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tlda " + what_str)
|
||||
self.p("\t\tadc #" + value_str)
|
||||
self.p("\t\tsta " + what_str)
|
||||
self.p("\t\tbcc +")
|
||||
self.p("\t\tinc {:s}+1".format(what_str))
|
||||
self.p("+\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tlda " + what_str)
|
||||
self.p("\t\tadc #" + value_str)
|
||||
self.p("\t\tsta " + what_str)
|
||||
self.p("\t\tbcc +")
|
||||
self.p("\t\tinc {:s}+1".format(what_str))
|
||||
self.p("+")
|
||||
else:
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tlda " + what_str)
|
||||
self.p("\t\tsbc #" + value_str)
|
||||
self.p("\t\tsta " + what_str)
|
||||
self.p("\t\tbcs +")
|
||||
self.p("\t\tdec {:s}+1".format(what_str))
|
||||
self.p("+\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tlda " + what_str)
|
||||
self.p("\t\tsbc #" + value_str)
|
||||
self.p("\t\tsta " + what_str)
|
||||
self.p("\t\tbcs +")
|
||||
self.p("\t\tdec {:s}+1".format(what_str))
|
||||
self.p("+")
|
||||
elif what.datatype == DataType.FLOAT:
|
||||
if howmuch == 1.0:
|
||||
# special case for +/-1
|
||||
@ -914,15 +910,17 @@ class CodeGenerator:
|
||||
return True
|
||||
return False
|
||||
|
||||
def unclobber_result_registers(registers: Set[str], output_assignments: List[AssignmentStmt]) -> None:
|
||||
def unclobber_result_registers(registers: Set[str], output_assignments: List[AssignmentStmt]) -> Set[str]:
|
||||
result = registers.copy()
|
||||
for a in output_assignments:
|
||||
for lv in a.leftvalues:
|
||||
if isinstance(lv, RegisterValue):
|
||||
if len(lv.register) == 1:
|
||||
registers.discard(lv.register)
|
||||
result.discard(lv.register)
|
||||
else:
|
||||
for r in lv.register:
|
||||
registers.discard(r)
|
||||
result.discard(r)
|
||||
return result
|
||||
|
||||
if stmt.target.name:
|
||||
symblock, targetdef = self.cur_block.lookup(stmt.target.name)
|
||||
@ -941,10 +939,10 @@ class CodeGenerator:
|
||||
return
|
||||
clobbered = set() # type: Set[str]
|
||||
if targetdef.clobbered_registers:
|
||||
if stmt.preserve_regs:
|
||||
clobbered = targetdef.clobbered_registers
|
||||
unclobber_result_registers(clobbered, stmt.desugared_output_assignments)
|
||||
with self.preserving_registers(clobbered, loads_a_within=params_load_a()):
|
||||
if stmt.preserve_regs is not None:
|
||||
clobbered = targetdef.clobbered_registers & stmt.preserve_regs
|
||||
clobbered = unclobber_result_registers(clobbered, stmt.desugared_output_assignments)
|
||||
with self.preserving_registers(clobbered, loads_a_within=params_load_a(), always_preserve=stmt.preserve_regs is not None):
|
||||
generate_param_assignments()
|
||||
branch_emitter(targetstr, False, False)
|
||||
generate_result_assignments()
|
||||
@ -974,14 +972,14 @@ class CodeGenerator:
|
||||
# no result assignments because it's a goto
|
||||
else:
|
||||
# indirect call to subroutine
|
||||
preserve_regs = {'A', 'X', 'Y'} if stmt.preserve_regs else set()
|
||||
unclobber_result_registers(preserve_regs, stmt.desugared_output_assignments)
|
||||
with self.preserving_registers(preserve_regs, loads_a_within=params_load_a()):
|
||||
preserve_regs = unclobber_result_registers(stmt.preserve_regs or set(), stmt.desugared_output_assignments)
|
||||
with self.preserving_registers(preserve_regs, loads_a_within=params_load_a(),
|
||||
always_preserve=stmt.preserve_regs is not None):
|
||||
generate_param_assignments()
|
||||
if targetstr in REGISTER_WORDS:
|
||||
print("warning: {}: indirect register pair call is quite inefficient, use a jump table in memory instead?"
|
||||
.format(stmt.sourceref))
|
||||
if stmt.preserve_regs:
|
||||
if stmt.preserve_regs is not None:
|
||||
# cannot use zp scratch because it may be used by the register backup. This is very inefficient code!
|
||||
self.p("\t\tjsr il65_lib.jsr_indirect_nozpuse_"+targetstr)
|
||||
|
||||
@ -1009,9 +1007,9 @@ class CodeGenerator:
|
||||
branch_emitter(targetstr, True, False)
|
||||
# no result assignments because it's a goto
|
||||
else:
|
||||
preserve_regs = {'A', 'X', 'Y'} if stmt.preserve_regs else set()
|
||||
unclobber_result_registers(preserve_regs, stmt.desugared_output_assignments)
|
||||
with self.preserving_registers(preserve_regs, loads_a_within=params_load_a()):
|
||||
preserve_regs = unclobber_result_registers(stmt.preserve_regs or set(), stmt.desugared_output_assignments)
|
||||
with self.preserving_registers(preserve_regs, loads_a_within=params_load_a(),
|
||||
always_preserve=stmt.preserve_regs is not None):
|
||||
generate_param_assignments()
|
||||
branch_emitter(targetstr, False, False)
|
||||
generate_result_assignments()
|
||||
@ -1040,19 +1038,17 @@ class CodeGenerator:
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc " + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc " + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc " + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc " + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc " + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word
|
||||
elif operator == "-=":
|
||||
@ -1060,70 +1056,62 @@ class CodeGenerator:
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc " + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc " + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc " + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc " + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc " + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word
|
||||
elif operator == "&=":
|
||||
if lvalue.register == "A":
|
||||
self.p("\t\tand " + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tand " + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tand " + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tand " + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tand " + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
|
||||
elif operator == "|=":
|
||||
if lvalue.register == "A":
|
||||
self.p("\t\tora " + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tora " + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tora " + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tora " + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tora " + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
|
||||
elif operator == "^=":
|
||||
if lvalue.register == "A":
|
||||
self.p("\t\teor " + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\teor " + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\teor " + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\teor " + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\teor " + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word
|
||||
elif operator == ">>=":
|
||||
@ -1133,17 +1121,15 @@ class CodeGenerator:
|
||||
if lvalue.register == "A":
|
||||
self.p("\t\tlsr " + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tlsr " + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tlsr " + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tlsr " + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tlsr " + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for shift", str(lvalue)) # @todo >>=.word
|
||||
elif operator == "<<=":
|
||||
@ -1153,17 +1139,15 @@ class CodeGenerator:
|
||||
if lvalue.register == "A":
|
||||
self.p("\t\tasl " + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tasl " + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tasl " + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tasl " + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tasl " + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for shift", str(lvalue)) # @todo >>=.word
|
||||
|
||||
@ -1174,19 +1158,17 @@ class CodeGenerator:
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc #" + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo +=.word
|
||||
elif operator == "-=":
|
||||
@ -1194,70 +1176,62 @@ class CodeGenerator:
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc #" + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo -=.word
|
||||
elif operator == "&=":
|
||||
if lvalue.register == "A":
|
||||
self.p("\t\tand #" + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tand #" + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tand #" + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tand #" + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tand #" + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo &=.word
|
||||
elif operator == "|=":
|
||||
if lvalue.register == "A":
|
||||
self.p("\t\tora #" + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tora #" + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tora #" + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tora #" + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tora #" + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo |=.word
|
||||
elif operator == "^=":
|
||||
if lvalue.register == "A":
|
||||
self.p("\t\teor #" + r_str)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\teor #" + r_str)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\teor #" + r_str)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\teor #" + r_str)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\teor #" + r_str)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo ^=.word
|
||||
elif operator == ">>=":
|
||||
@ -1271,17 +1245,15 @@ class CodeGenerator:
|
||||
if lvalue.register == "A":
|
||||
shifts_A(rvalue.value)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
shifts_A(rvalue.value)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
shifts_A(rvalue.value)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
shifts_A(rvalue.value)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
shifts_A(rvalue.value)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo >>=.word
|
||||
elif operator == "<<=":
|
||||
@ -1295,17 +1267,15 @@ class CodeGenerator:
|
||||
if lvalue.register == "A":
|
||||
shifts_A(rvalue.value)
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
shifts_A(rvalue.value)
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
shifts_A(rvalue.value)
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
shifts_A(rvalue.value)
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
shifts_A(rvalue.value)
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported register for aug assign", str(lvalue)) # @todo <<=.word
|
||||
|
||||
@ -1319,20 +1289,18 @@ class CodeGenerator:
|
||||
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tclc")
|
||||
self.p("\t\tadc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo +=.word
|
||||
elif operator == "-=":
|
||||
@ -1344,20 +1312,18 @@ class CodeGenerator:
|
||||
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tsec")
|
||||
self.p("\t\tsbc " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo -=.word
|
||||
elif operator == "&=":
|
||||
@ -1368,18 +1334,16 @@ class CodeGenerator:
|
||||
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tand " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo &=.word
|
||||
elif operator == "|=":
|
||||
@ -1390,18 +1354,16 @@ class CodeGenerator:
|
||||
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tora " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo |=.word
|
||||
elif operator == "^=":
|
||||
@ -1412,18 +1374,16 @@ class CodeGenerator:
|
||||
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\teor " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo ^=.word
|
||||
elif operator == ">>=":
|
||||
@ -1434,18 +1394,16 @@ class CodeGenerator:
|
||||
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tlsr " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo >>=.word
|
||||
elif operator == "<<=":
|
||||
@ -1456,18 +1414,16 @@ class CodeGenerator:
|
||||
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
elif lvalue.register == "X":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttxa")
|
||||
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttax")
|
||||
elif lvalue.register == "Y":
|
||||
self.p("\t\tst{:s} {:s}".format(rvalue.register.lower(), Parser.to_hex(Zeropage.SCRATCH_B1)))
|
||||
self.p("\t\tpha")
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\ttya")
|
||||
self.p("\t\tasl " + Parser.to_hex(Zeropage.SCRATCH_B1))
|
||||
self.p("\t\ttay")
|
||||
else:
|
||||
raise CodeError("unsupported lvalue register for aug assign", str(lvalue)) # @todo <<=.word
|
||||
|
||||
@ -1556,14 +1512,14 @@ class CodeGenerator:
|
||||
floatvalue = float(rvalue.value)
|
||||
mflpt = self.to_mflpt5(floatvalue)
|
||||
target = mmv.name or Parser.to_hex(mmv.address)
|
||||
self.p("\t\tpha\t\t\t; {:s} = {}".format(target, rvalue.name or floatvalue))
|
||||
a_reg_value = None
|
||||
for i, byte in enumerate(mflpt):
|
||||
if byte != a_reg_value:
|
||||
self.p("\t\tlda #${:02x}".format(byte))
|
||||
a_reg_value = byte
|
||||
self.p("\t\tsta {:s}+{:d}".format(target, i))
|
||||
self.p("\t\tpla")
|
||||
with self.preserving_registers({'A'}):
|
||||
self.p("\t\t\t\t\t; {:s} = {}".format(target, rvalue.name or floatvalue))
|
||||
a_reg_value = None
|
||||
for i, byte in enumerate(mflpt):
|
||||
if byte != a_reg_value:
|
||||
self.p("\t\tlda #${:02x}".format(byte))
|
||||
a_reg_value = byte
|
||||
self.p("\t\tsta {:s}+{:d}".format(target, i))
|
||||
|
||||
def generate_assign_reg_to_memory(self, lv: MemMappedValue, r_register: str) -> None:
|
||||
# Memory = Register
|
||||
@ -1692,9 +1648,12 @@ class CodeGenerator:
|
||||
raise CodeError("invalid register " + lv.register)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def preserving_registers(self, registers: Set[str], loads_a_within: bool=False):
|
||||
# this clobbers a ZP scratch register and is therefore NOT safe to use in interrupts
|
||||
def preserving_registers(self, registers: Set[str], loads_a_within: bool=False, always_preserve: bool=False):
|
||||
# this sometimes clobbers a ZP scratch register and is therefore NOT safe to use in interrupts
|
||||
# see http://6502.org/tutorials/register_preservation.html
|
||||
if not self.cur_block.preserve_registers and not always_preserve:
|
||||
yield
|
||||
return
|
||||
if registers == {'A'}:
|
||||
self.p("\t\tpha")
|
||||
yield
|
||||
|
233
il65/parse.py
233
il65/parse.py
@ -39,6 +39,7 @@ class ParseResult:
|
||||
self.blocks = [] # type: List[Block]
|
||||
self.subroutine_usage = defaultdict(set) # type: Dict[Tuple[str, str], Set[str]]
|
||||
self.zeropage = Zeropage()
|
||||
self.preserve_registers = False
|
||||
|
||||
def all_blocks(self) -> Generator[Block, None, None]:
|
||||
for block in self.blocks:
|
||||
@ -176,7 +177,7 @@ class Parser:
|
||||
break
|
||||
|
||||
def _parse_1(self) -> None:
|
||||
self.cur_block = Block("<header>", self.sourceref, self.root_scope)
|
||||
self.cur_block = Block("<header>", self.sourceref, self.root_scope, self.result.preserve_registers)
|
||||
self.result.add_block(self.cur_block)
|
||||
self.parse_header()
|
||||
if not self.parsing_import:
|
||||
@ -188,7 +189,7 @@ class Parser:
|
||||
block = self.parse_block()
|
||||
if block:
|
||||
self.result.add_block(block)
|
||||
elif next_line.startswith(("import ", "import\t")):
|
||||
elif next_line.startswith(("%import ", "%import\t")):
|
||||
self.parse_import()
|
||||
else:
|
||||
break
|
||||
@ -402,76 +403,91 @@ class Parser:
|
||||
self.result.format = ProgramFormat.RAW
|
||||
output_specified = False
|
||||
zp_specified = False
|
||||
preserve_specified = False
|
||||
while True:
|
||||
self._parse_comments()
|
||||
line = self.next_line()
|
||||
if line.startswith(("output ", "output\t")):
|
||||
if output_specified:
|
||||
raise self.PError("can only specify output options once")
|
||||
output_specified = True
|
||||
_, _, optionstr = line.partition(" ")
|
||||
options = set(optionstr.replace(' ', '').split(','))
|
||||
self.result.with_sys = False
|
||||
self.result.format = ProgramFormat.RAW
|
||||
if "raw" in options:
|
||||
options.remove("raw")
|
||||
if "prg" in options:
|
||||
options.remove("prg")
|
||||
self.result.format = ProgramFormat.PRG
|
||||
if "basic" in options:
|
||||
options.remove("basic")
|
||||
if self.result.format == ProgramFormat.PRG:
|
||||
self.result.with_sys = True
|
||||
else:
|
||||
raise self.PError("can only use basic output option with prg, not raw")
|
||||
if options:
|
||||
raise self.PError("invalid output option(s): " + str(options))
|
||||
elif line.startswith(("zp ", "zp\t")):
|
||||
if zp_specified:
|
||||
raise self.PError("can only specify ZP options once")
|
||||
zp_specified = True
|
||||
_, _, optionstr = line.partition(" ")
|
||||
options = set(optionstr.replace(' ', '').split(','))
|
||||
self.result.clobberzp = False
|
||||
self.result.restorezp = False
|
||||
if "clobber" in options:
|
||||
options.remove("clobber")
|
||||
self.result.clobberzp = True
|
||||
if "restore" in options:
|
||||
options.remove("restore")
|
||||
if self.result.clobberzp:
|
||||
self.result.restorezp = True
|
||||
else:
|
||||
raise self.PError("can only use restore zp option if clobber zp is used as well")
|
||||
if options:
|
||||
raise self.PError("invalid zp option(s): " + str(options))
|
||||
elif line.startswith("address"):
|
||||
if self.result.start_address:
|
||||
raise self.PError("multiple occurrences of 'address'")
|
||||
_, _, arg = line.partition(" ")
|
||||
try:
|
||||
self.result.start_address = parse_expr_as_int(arg, None, None, self.sourceref)
|
||||
except ParseError:
|
||||
raise self.PError("invalid address")
|
||||
if self.result.format == ProgramFormat.PRG and self.result.with_sys and self.result.start_address != 0x0801:
|
||||
raise self.PError("cannot use non-default 'address' when output format includes basic SYS program")
|
||||
else:
|
||||
# header parsing finished!
|
||||
self.prev_line()
|
||||
if not self.result.start_address:
|
||||
# set the proper default start address
|
||||
if self.result.format == ProgramFormat.PRG:
|
||||
self.result.start_address = 0x0801 # normal C-64 basic program start address
|
||||
elif self.result.format == ProgramFormat.RAW:
|
||||
self.result.start_address = 0xc000 # default start for raw assembly
|
||||
if self.result.format == ProgramFormat.PRG and self.result.with_sys and self.result.start_address != 0x0801:
|
||||
raise self.PError("cannot use non-default 'address' when output format includes basic SYS program")
|
||||
return
|
||||
if line.startswith('%'):
|
||||
directive = line.split(maxsplit=1)[0][1:]
|
||||
if directive == "output":
|
||||
if output_specified:
|
||||
raise self.PError("can only specify output options once")
|
||||
output_specified = True
|
||||
_, _, optionstr = line.partition(" ")
|
||||
options = set(optionstr.replace(' ', '').split(','))
|
||||
self.result.with_sys = False
|
||||
self.result.format = ProgramFormat.RAW
|
||||
if "raw" in options:
|
||||
options.remove("raw")
|
||||
if "prg" in options:
|
||||
options.remove("prg")
|
||||
self.result.format = ProgramFormat.PRG
|
||||
if "basic" in options:
|
||||
options.remove("basic")
|
||||
if self.result.format == ProgramFormat.PRG:
|
||||
self.result.with_sys = True
|
||||
else:
|
||||
raise self.PError("can only use basic output option with prg, not raw")
|
||||
if options:
|
||||
raise self.PError("invalid output option(s): " + str(options))
|
||||
continue
|
||||
elif directive == "zp":
|
||||
if zp_specified:
|
||||
raise self.PError("can only specify ZP options once")
|
||||
zp_specified = True
|
||||
_, _, optionstr = line.partition(" ")
|
||||
options = set(optionstr.replace(' ', '').split(','))
|
||||
self.result.clobberzp = False
|
||||
self.result.restorezp = False
|
||||
if "clobber" in options:
|
||||
options.remove("clobber")
|
||||
self.result.clobberzp = True
|
||||
if "restore" in options:
|
||||
options.remove("restore")
|
||||
if self.result.clobberzp:
|
||||
self.result.restorezp = True
|
||||
else:
|
||||
raise self.PError("can only use restore zp option if clobber zp is used as well")
|
||||
if options:
|
||||
raise self.PError("invalid zp option(s): " + str(options))
|
||||
continue
|
||||
elif directive == "address":
|
||||
if self.result.start_address:
|
||||
raise self.PError("multiple occurrences of 'address'")
|
||||
_, _, arg = line.partition(" ")
|
||||
try:
|
||||
self.result.start_address = parse_expr_as_int(arg, None, None, self.sourceref)
|
||||
except ParseError:
|
||||
raise self.PError("invalid address")
|
||||
if self.result.format == ProgramFormat.PRG and self.result.with_sys and self.result.start_address != 0x0801:
|
||||
raise self.PError("cannot use non-default 'address' when output format includes basic SYS program")
|
||||
continue
|
||||
elif directive == "preserve_registers":
|
||||
if preserve_specified:
|
||||
raise self.PError("can only specify preserve_registers option once")
|
||||
preserve_specified = True
|
||||
_, _, optionstr = line.partition(" ")
|
||||
self.result.preserve_registers = optionstr in ("", "true", "yes")
|
||||
continue
|
||||
elif directive == "import":
|
||||
break # the first import directive actually is not part of the header anymore
|
||||
else:
|
||||
raise self.PError("invalid directive")
|
||||
break # no more directives, header parsing finished!
|
||||
self.prev_line()
|
||||
if not self.result.start_address:
|
||||
# set the proper default start address
|
||||
if self.result.format == ProgramFormat.PRG:
|
||||
self.result.start_address = 0x0801 # normal C-64 basic program start address
|
||||
elif self.result.format == ProgramFormat.RAW:
|
||||
self.result.start_address = 0xc000 # default start for raw assembly
|
||||
if self.result.format == ProgramFormat.PRG and self.result.with_sys and self.result.start_address != 0x0801:
|
||||
raise self.PError("cannot use non-default 'address' when output format includes basic SYS program")
|
||||
|
||||
def parse_import(self) -> None:
|
||||
line = self.next_line()
|
||||
line = line.lstrip()
|
||||
if not line.startswith(("import ", "import\t")):
|
||||
if not line.startswith(("%import ", "%import\t")):
|
||||
raise self.PError("expected import")
|
||||
try:
|
||||
_, arg = line.split(maxsplit=1)
|
||||
@ -528,7 +544,7 @@ class Parser:
|
||||
raise self.PError("expected '~' (block)")
|
||||
block_args = line[1:].split()
|
||||
arg = ""
|
||||
self.cur_block = Block("", self.sourceref.copy(), self.root_scope)
|
||||
self.cur_block = Block("", self.sourceref.copy(), self.root_scope, self.result.preserve_registers)
|
||||
is_zp_block = False
|
||||
while block_args:
|
||||
arg = block_args.pop(0)
|
||||
@ -542,7 +558,7 @@ class Parser:
|
||||
raise self.PError("duplicate block name '{:s}', original definition at {}".format(arg, orig.sourceref))
|
||||
self.cur_block = orig # zero page block occurrences are merged
|
||||
else:
|
||||
self.cur_block = Block(arg, self.sourceref.copy(), self.root_scope)
|
||||
self.cur_block = Block(arg, self.sourceref.copy(), self.root_scope, self.result.preserve_registers)
|
||||
try:
|
||||
self.root_scope.define_scope(self.cur_block.symbols, self.cur_block.sourceref)
|
||||
except SymbolError as x:
|
||||
@ -605,14 +621,34 @@ class Parser:
|
||||
line = self.next_line()
|
||||
unstripped_line = line
|
||||
line = line.strip()
|
||||
if line == "}":
|
||||
if line.startswith('%'):
|
||||
directive, _, optionstr = line.partition(" ")
|
||||
directive = directive[1:]
|
||||
self.cur_block.preserve_registers = optionstr in ("", "true", "yes")
|
||||
if directive in ("asminclude", "asmbinary"):
|
||||
if is_zp_block:
|
||||
raise self.PError("ZP block cannot contain assembler directives")
|
||||
self.cur_block.statements.append(self.parse_asminclude(line))
|
||||
elif directive == "asm":
|
||||
if is_zp_block:
|
||||
raise self.PError("ZP block cannot contain code statements")
|
||||
self.prev_line()
|
||||
self.cur_block.statements.append(self.parse_asm())
|
||||
elif directive == "breakpoint":
|
||||
self.cur_block.statements.append(BreakpointStmt(self.sourceref))
|
||||
self.print_warning("breakpoint defined")
|
||||
elif directive == "preserve_registers":
|
||||
self.result.preserve_registers = optionstr in ("", "true", "yes")
|
||||
else:
|
||||
raise self.PError("invalid directive")
|
||||
elif line == "}":
|
||||
if is_zp_block and any(b.name == "ZP" for b in self.result.blocks):
|
||||
return False, None # we already have the ZP block
|
||||
if self.cur_block.ignore:
|
||||
self.print_warning("ignoring block without name and address", self.cur_block.sourceref)
|
||||
return False, None
|
||||
return False, self.cur_block
|
||||
if line.startswith(("var ", "var\t")):
|
||||
elif line.startswith(("var ", "var\t")):
|
||||
self.parse_var_def(line)
|
||||
elif line.startswith(("const ", "const\t")):
|
||||
self.parse_const_def(line)
|
||||
@ -622,19 +658,9 @@ class Parser:
|
||||
if is_zp_block:
|
||||
raise self.PError("ZP block cannot contain subroutines")
|
||||
self.parse_subroutine_def(line)
|
||||
elif line.startswith(("asminclude ", "asminclude\t", "asmbinary ", "asmbinary\t")):
|
||||
if is_zp_block:
|
||||
raise self.PError("ZP block cannot contain assembler directives")
|
||||
self.cur_block.statements.append(self.parse_asminclude(line))
|
||||
elif line.startswith(("asm ", "asm\t")):
|
||||
if is_zp_block:
|
||||
raise self.PError("ZP block cannot contain code statements")
|
||||
self.prev_line()
|
||||
self.cur_block.statements.append(self.parse_asm())
|
||||
elif line == "breakpoint":
|
||||
self.cur_block.statements.append(BreakpointStmt(self.sourceref))
|
||||
self.print_warning("breakpoint defined")
|
||||
elif unstripped_line.startswith((" ", "\t")):
|
||||
if line.endswith("{"):
|
||||
raise self.PError("invalid statement")
|
||||
if is_zp_block:
|
||||
raise self.PError("ZP block cannot contain code statements")
|
||||
self.cur_block.statements.append(self.parse_statement(line))
|
||||
@ -714,7 +740,8 @@ class Parser:
|
||||
if code_decl:
|
||||
address = None
|
||||
# parse the subroutine code lines (until the closing '}')
|
||||
subroutine_block = Block(self.cur_block.name + "." + name, self.sourceref, self.cur_block.symbols)
|
||||
subroutine_block = Block(self.cur_block.name + "." + name, self.sourceref, self.cur_block.symbols,
|
||||
self.cur_block.preserve_registers)
|
||||
current_block = self.cur_block
|
||||
self.cur_block = subroutine_block
|
||||
while True:
|
||||
@ -727,10 +754,12 @@ class Parser:
|
||||
break
|
||||
if line.startswith(("sub ", "sub\t")):
|
||||
raise self.PError("cannot nest subroutines")
|
||||
elif line.startswith(("asm ", "asm\t")):
|
||||
elif line.startswith(("%asm ", "%asm\t")):
|
||||
self.prev_line()
|
||||
subroutine_block.statements.append(self.parse_asm())
|
||||
elif unstripped_line.startswith((" ", "\t")):
|
||||
if line.endswith("{"):
|
||||
raise self.PError("invalid statement")
|
||||
subroutine_block.statements.append(self.parse_statement(line))
|
||||
elif line:
|
||||
self.parse_label(line)
|
||||
@ -792,25 +821,31 @@ class Parser:
|
||||
# conditional goto
|
||||
groups = match.groupdict()
|
||||
subname = groups["subname"]
|
||||
if '!' in subname:
|
||||
raise self.PError("goto is always without register preservation, should not have exclamation mark")
|
||||
if groups["if"] == "if" and not groups["cond"]:
|
||||
raise self.PError("need explicit if status when a condition is not present")
|
||||
condition = self.parse_if_condition(groups["if"], groups["cond"])
|
||||
return self.parse_call_or_goto(subname, groups["arguments"], None, False, True, condition=condition)
|
||||
return self.parse_call_or_goto(subname, groups["arguments"], None, None, True, condition=condition)
|
||||
match = re.fullmatch(r"goto\s+(?P<subname>[\S]+?)\s*(\((?P<arguments>.*)\))?\s*", line)
|
||||
if match:
|
||||
# goto
|
||||
groups = match.groupdict()
|
||||
subname = groups["subname"]
|
||||
if '!' in subname:
|
||||
raise self.PError("goto is always without register preservation, should not have exclamation mark")
|
||||
return self.parse_call_or_goto(subname, groups["arguments"], None, False, True)
|
||||
match = re.fullmatch(r"(?P<outputs>[^\(]*\s*=)?\s*(?P<subname>[\S]+?)\s*(?P<fcall>[!]?)\s*(\((?P<arguments>.*)\))?\s*", line)
|
||||
return self.parse_call_or_goto(subname, groups["arguments"], None, None, True)
|
||||
match = re.fullmatch(r"(?P<outputs>[^\(]*\s*=)?\s*(?P<subname>[\S]+?)\s*(?P<preserve>!\s*[A-Z]*)?\s*(\((?P<arguments>.*)\))?\s*", line)
|
||||
if match:
|
||||
# subroutine call (not a goto) with possible output param assignment
|
||||
groups = match.groupdict()
|
||||
preserve = not bool(groups["fcall"])
|
||||
preserve = None
|
||||
preserve_str = groups["preserve"]
|
||||
if preserve_str and preserve_str.startswith('!'):
|
||||
preserve_str = preserve_str.replace(' ', '')
|
||||
if preserve_str == "!":
|
||||
preserve = {'A', 'X', 'Y'}
|
||||
else:
|
||||
preserve = set(preserve_str[1:])
|
||||
for r in preserve:
|
||||
if r not in REGISTER_BYTES:
|
||||
raise self.PError("invalid register in call preservation list")
|
||||
subname = groups["subname"]
|
||||
arguments = groups["arguments"]
|
||||
outputs = groups["outputs"] or ""
|
||||
@ -843,7 +878,7 @@ class Parser:
|
||||
raise self.PError("invalid statement")
|
||||
|
||||
def parse_call_or_goto(self, targetstr: str, argumentstr: str, outputstr: str,
|
||||
preserve_regs=True, is_goto=False, condition: IfCondition=None) -> CallStmt:
|
||||
preserve_regs: Set[str]=None, is_goto: bool=False, condition: IfCondition=None) -> CallStmt:
|
||||
if not is_goto:
|
||||
assert condition is None
|
||||
argumentstr = argumentstr.strip() if argumentstr else ""
|
||||
@ -1008,8 +1043,8 @@ class Parser:
|
||||
def parse_asm(self) -> InlineAsm:
|
||||
line = self.next_line()
|
||||
aline = line.split()
|
||||
if not len(aline) == 2 or aline[0] != "asm" or aline[1] != "{":
|
||||
raise self.PError("invalid asm start")
|
||||
if not len(aline) == 2 or aline[0] != "%asm" or aline[1] != "{":
|
||||
raise self.PError("invalid asm directive")
|
||||
asmlines = [] # type: List[str]
|
||||
while True:
|
||||
line = self.next_line()
|
||||
@ -1036,7 +1071,7 @@ class Parser:
|
||||
def parse_asminclude(self, line: str) -> InlineAsm:
|
||||
aline = line.split()
|
||||
if len(aline) < 2:
|
||||
raise self.PError("invalid asminclude or asmbinary statement")
|
||||
raise self.PError("invalid asminclude or asmbinary directive")
|
||||
filename = aline[1]
|
||||
if not filename.startswith('"') or not filename.endswith('"'):
|
||||
raise self.PError("filename must be between quotes")
|
||||
@ -1049,14 +1084,14 @@ class Parser:
|
||||
raise self.PError("included file not found")
|
||||
print("copying included file to output location:", filename)
|
||||
shutil.copy(filename_in_sourcedir, filename_in_output_location)
|
||||
if aline[0] == "asminclude":
|
||||
if aline[0] == "%asminclude":
|
||||
if len(aline) == 3:
|
||||
scopename = aline[2]
|
||||
lines = ['{:s}\t.binclude "{:s}"'.format(scopename, filename)]
|
||||
else:
|
||||
raise self.PError("invalid asminclude statement")
|
||||
raise self.PError("invalid asminclude directive")
|
||||
return InlineAsm(lines, self.sourceref)
|
||||
elif aline[0] == "asmbinary":
|
||||
elif aline[0] == "%asmbinary":
|
||||
if len(aline) == 4:
|
||||
offset = parse_expr_as_int(aline[2], None, None, self.sourceref)
|
||||
length = parse_expr_as_int(aline[3], None, None, self.sourceref)
|
||||
@ -1067,7 +1102,7 @@ class Parser:
|
||||
elif len(aline) == 2:
|
||||
lines = ['\t.binary "{:s}"'.format(filename)]
|
||||
else:
|
||||
raise self.PError("invalid asmbinary statement")
|
||||
raise self.PError("invalid asmbinary directive")
|
||||
return InlineAsm(lines, self.sourceref)
|
||||
else:
|
||||
raise self.PError("invalid statement")
|
||||
|
@ -234,7 +234,7 @@ sub init_system () -> (?) {
|
||||
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
||||
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
||||
; Also a different color scheme is chosen to identify ourselves a little.
|
||||
asm {
|
||||
%asm {
|
||||
sei
|
||||
cld
|
||||
lda #%00101111
|
||||
@ -269,7 +269,7 @@ sub init_system () -> (?) {
|
||||
|
||||
sub FREADS32 () -> (?) {
|
||||
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
|
||||
asm {
|
||||
%asm {
|
||||
lda $62
|
||||
eor #$ff
|
||||
asl a
|
||||
@ -281,7 +281,7 @@ sub FREADS32 () -> (?) {
|
||||
|
||||
sub FREADUS32 () -> (?) {
|
||||
; ---- fac1 = uint32 from $62-$65 big endian (MSB FIRST)
|
||||
asm {
|
||||
%asm {
|
||||
sec
|
||||
lda #0
|
||||
ldx #$a0
|
||||
@ -292,7 +292,7 @@ sub FREADUS32 () -> (?) {
|
||||
sub FREADS24AXY (lo: A, mid: X, hi: Y) -> (?) {
|
||||
; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes)
|
||||
; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead.
|
||||
asm {
|
||||
%asm {
|
||||
sty $62
|
||||
stx $63
|
||||
sta $64
|
||||
@ -308,7 +308,7 @@ sub FREADS24AXY (lo: A, mid: X, hi: Y) -> (?) {
|
||||
|
||||
sub GIVUAYF (uword: AY) -> (?) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
asm {
|
||||
%asm {
|
||||
sty $62
|
||||
sta $63
|
||||
ldx #$90
|
||||
@ -319,7 +319,7 @@ sub GIVUAYF (uword: AY) -> (?) {
|
||||
|
||||
sub GIVAYFAY (sword: AY) -> (?) {
|
||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||
asm {
|
||||
%asm {
|
||||
sta c64.SCRATCH_ZP1
|
||||
tya
|
||||
ldy c64.SCRATCH_ZP1
|
||||
@ -329,7 +329,7 @@ sub GIVAYFAY (sword: AY) -> (?) {
|
||||
|
||||
sub FTOSWRDAY () -> (AY, X?) {
|
||||
; ---- fac1 to signed word in A/Y
|
||||
asm {
|
||||
%asm {
|
||||
jsr c64.FTOSWORDYA ; note the inverse Y/A order
|
||||
sta c64.SCRATCH_ZP1
|
||||
tya
|
||||
@ -340,7 +340,7 @@ sub FTOSWRDAY () -> (AY, X?) {
|
||||
|
||||
sub GETADRAY () -> (AY, X?) {
|
||||
; ---- fac1 to unsigned word in A/Y
|
||||
asm {
|
||||
%asm {
|
||||
jsr c64.GETADR ; this uses the inverse order, Y/A
|
||||
sta c64.SCRATCH_ZP1
|
||||
tya
|
||||
@ -353,7 +353,7 @@ sub GETADRAY () -> (AY, X?) {
|
||||
sub copy_mflt (source: XY) -> (A?, Y?) {
|
||||
; ---- copy a 5 byte MFLT floating point variable to another place
|
||||
; input: X/Y = source address, c64.SCRATCH_ZPWORD1 = destination address
|
||||
asm {
|
||||
%asm {
|
||||
stx c64.SCRATCH_ZP1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
ldy #0
|
||||
@ -378,7 +378,7 @@ sub copy_mflt (source: XY) -> (A?, Y?) {
|
||||
|
||||
sub float_add_one (mflt: XY) -> (?) {
|
||||
; ---- add 1 to the MFLT pointed to by X/Y. Clobbers A, X, Y
|
||||
asm {
|
||||
%asm {
|
||||
stx c64.SCRATCH_ZP1
|
||||
sty c64.SCRATCH_ZP2
|
||||
txa
|
||||
@ -394,7 +394,7 @@ sub float_add_one (mflt: XY) -> (?) {
|
||||
|
||||
sub float_sub_one (mflt: XY) -> (?) {
|
||||
; ---- subtract 1 from the MFLT pointed to by X/Y. Clobbers A, X, Y
|
||||
asm {
|
||||
%asm {
|
||||
stx c64.SCRATCH_ZP1
|
||||
sty c64.SCRATCH_ZP2
|
||||
lda #<c64.FL_FONE
|
||||
@ -411,7 +411,7 @@ sub float_sub_one (mflt: XY) -> (?) {
|
||||
|
||||
sub float_add_SW1_to_XY (mflt: XY) -> (?) {
|
||||
; ---- add MFLT pointed to by SCRATCH_ZPWORD1 to the MFLT pointed to by X/Y. Clobbers A, X, Y
|
||||
asm {
|
||||
%asm {
|
||||
stx c64.SCRATCH_ZP1
|
||||
sty c64.SCRATCH_ZP2
|
||||
txa
|
||||
@ -427,7 +427,7 @@ sub float_add_SW1_to_XY (mflt: XY) -> (?) {
|
||||
|
||||
sub float_sub_SW1_from_XY (mflt: XY) -> (?) {
|
||||
; ---- subtract MFLT pointed to by SCRATCH_ZPWORD1 from the MFLT pointed to by X/Y. Clobbers A, X, Y
|
||||
asm {
|
||||
%asm {
|
||||
stx c64.SCRATCH_ZP1
|
||||
sty c64.SCRATCH_ZP2
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
@ -454,7 +454,7 @@ sub clear_screen (char:A, color: Y) -> () {
|
||||
; ---- clear the character screen with the given fill character and character color.
|
||||
; (assumes screen is at $0400, could be altered in the future with self-modifying code)
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
sta _loop + 1 ; self-modifying
|
||||
stx c64.SCRATCH_ZP1
|
||||
ldx #0
|
||||
@ -483,7 +483,7 @@ sub scroll_left_full (alsocolors: SC) -> (A?, X?, Y?) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data should be scrolled too
|
||||
asm {
|
||||
%asm {
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
|
||||
@ -542,7 +542,7 @@ sub scroll_right_full (alsocolors: SC) -> (A?, X?) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data should be scrolled too
|
||||
asm {
|
||||
%asm {
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
|
||||
@ -593,7 +593,7 @@ sub scroll_up_full (alsocolors: SC) -> (A?, X?) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data should be scrolled too
|
||||
asm {
|
||||
%asm {
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
|
||||
@ -644,7 +644,7 @@ sub scroll_down_full (alsocolors: SC) -> (A?, X?) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data should be scrolled too
|
||||
asm {
|
||||
%asm {
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
|
||||
@ -693,7 +693,7 @@ _scroll_screen
|
||||
|
||||
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 {
|
||||
%asm {
|
||||
ldy #$2f
|
||||
ldx #$3a
|
||||
sec
|
||||
@ -710,7 +710,7 @@ sub byte2decimal (ubyte: A) -> (Y, X, 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 {
|
||||
%asm {
|
||||
pha
|
||||
and #$0f
|
||||
tax
|
||||
@ -733,7 +733,7 @@ hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as
|
||||
var .text word2hex_output = "1234" ; 0-terminated, to make printing easier
|
||||
sub word2hex (word: XY) -> (?) {
|
||||
; ---- convert 16 bit word in X/Y into 4-character hexadecimal string into memory 'word2hex_output'
|
||||
asm {
|
||||
%asm {
|
||||
stx c64.SCRATCH_ZP2
|
||||
tya
|
||||
jsr byte2hex
|
||||
@ -757,7 +757,7 @@ sub word2bcd (word: XY) -> (A?, X?) {
|
||||
; into a BCD value that is being doubled on each iteration. As all the
|
||||
; arithmetic is being done in BCD the result is a binary to decimal
|
||||
; conversion.
|
||||
asm {
|
||||
%asm {
|
||||
stx c64.SCRATCH_ZP1
|
||||
sty c64.SCRATCH_ZP2
|
||||
sed ; switch to decimal mode
|
||||
@ -789,7 +789,7 @@ sub word2bcd (word: XY) -> (A?, X?) {
|
||||
var .array(5) word2decimal_output
|
||||
sub word2decimal (word: XY) -> (?) {
|
||||
; ---- convert 16 bit word in X/Y into decimal string into memory 'word2decimal_output'
|
||||
asm {
|
||||
%asm {
|
||||
jsr word2bcd
|
||||
lda word2bcd_bcdbuff+2
|
||||
clc
|
||||
@ -827,7 +827,7 @@ sub print_string (address: XY) -> (A?, Y?) {
|
||||
; note: the IL65 compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to c64.CHROUT of that single char.
|
||||
asm {
|
||||
%asm {
|
||||
stx c64.SCRATCH_ZP1
|
||||
sty c64.SCRATCH_ZP2
|
||||
ldy #0
|
||||
@ -843,7 +843,7 @@ sub print_string (address: XY) -> (A?, Y?) {
|
||||
|
||||
sub print_pstring (address: XY) -> (A?, X?, Y) {
|
||||
; ---- print pstring (length as first byte) from X/Y, returns str len in Y
|
||||
asm {
|
||||
%asm {
|
||||
stx c64.SCRATCH_ZP1
|
||||
sty c64.SCRATCH_ZP2
|
||||
ldy #0
|
||||
@ -863,7 +863,7 @@ sub print_pstring (address: XY) -> (A?, X?, Y) {
|
||||
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 {
|
||||
%asm {
|
||||
tsx
|
||||
lda $102,x
|
||||
tay ; put high byte in y
|
||||
@ -891,7 +891,7 @@ sub print_pimmediate () -> () {
|
||||
|
||||
sub print_byte_decimal0 (ubyte: A) -> (?) {
|
||||
; ---- print the byte in A in decimal form, with left padding 0s (3 positions total)
|
||||
asm {
|
||||
%asm {
|
||||
jsr byte2decimal
|
||||
pha
|
||||
tya
|
||||
@ -906,7 +906,7 @@ sub print_byte_decimal0 (ubyte: A) -> (?) {
|
||||
|
||||
sub print_byte_decimal (ubyte: A) -> (?) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
asm {
|
||||
%asm {
|
||||
jsr byte2decimal
|
||||
pha
|
||||
cpy #'0'
|
||||
@ -927,7 +927,7 @@ _print_tens txa
|
||||
|
||||
sub print_byte_hex (prefix: SC, ubyte: A) -> (?) {
|
||||
; ---- print the byte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
asm {
|
||||
%asm {
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
@ -945,7 +945,7 @@ sub print_byte_hex (prefix: SC, ubyte: A) -> (?) {
|
||||
sub print_word_hex (prefix: SC, word: XY) -> (?) {
|
||||
; ---- print the (unsigned) word in X/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
asm {
|
||||
%asm {
|
||||
stx c64.SCRATCH_ZP1
|
||||
tya
|
||||
jsr print_byte_hex
|
||||
@ -958,7 +958,7 @@ sub print_word_hex (prefix: SC, word: XY) -> (?) {
|
||||
|
||||
sub print_word_decimal0 (word: XY) -> (?) {
|
||||
; ---- print the (unsigned) word in X/Y in decimal form, with left padding 0s (5 positions total)
|
||||
asm {
|
||||
%asm {
|
||||
jsr word2decimal
|
||||
lda word2decimal_output
|
||||
jsr c64.CHROUT
|
||||
@ -976,7 +976,7 @@ sub print_word_decimal0 (word: XY) -> (?) {
|
||||
|
||||
sub print_word_decimal (word: XY) -> (A?, X? Y?) {
|
||||
; ---- print the word in X/Y in decimal form, without left padding 0s
|
||||
asm {
|
||||
%asm {
|
||||
jsr word2decimal
|
||||
ldy #0
|
||||
lda word2decimal_output
|
||||
@ -1011,7 +1011,7 @@ sub input_chars (buffer: AX) -> (A?, Y) {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard.
|
||||
; It assumes the keyboard is selected as I/O channel!!
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
sta c64.SCRATCH_ZP1
|
||||
stx c64.SCRATCH_ZP2
|
||||
ldy #0 ; char counter = 0
|
||||
@ -1043,7 +1043,7 @@ sub input_chars (buffer: AX) -> (A?, Y) {
|
||||
; ; - moving data to higher addresses works even if areas overlap
|
||||
; ; - moving data to lower addresses only works if areas do not overlap
|
||||
; ; @todo fix this
|
||||
; asm {
|
||||
; %asm {
|
||||
; lda #<src_start
|
||||
; ldx #>src_start
|
||||
; sta $5f
|
||||
|
@ -9,7 +9,7 @@
|
||||
~ il65_lib_zp {
|
||||
; note: separate block so the 64tass assembler can remove this when no zp restore is required
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
|
||||
; ---- store the Zeropage in a backup area
|
||||
save_zeropage
|
||||
@ -62,7 +62,7 @@ zp_backup .fill 256, 0
|
||||
memory .word SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
|
||||
; ---- jmp (indirect) routines for register pairs containing the indirect address
|
||||
jsr_indirect_nozpuse_AX
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
sub multiply_bytes (byte1: X, byte2: Y) -> (A, X?) {
|
||||
; ---- multiply 2 bytes, result as byte in A (signed or unsigned)
|
||||
asm {
|
||||
%asm {
|
||||
stx SCRATCH_ZP1
|
||||
sty SCRATCH_ZP2
|
||||
ldx #8
|
||||
@ -40,7 +40,7 @@ sub multiply_bytes (byte1: X, byte2: Y) -> (A, X?) {
|
||||
|
||||
sub multiply_bytes_16 (byte1: X, byte2: Y) -> (A?, XY) {
|
||||
; ---- multiply 2 bytes, result as word in X/Y (unsigned)
|
||||
asm {
|
||||
%asm {
|
||||
lda #0
|
||||
_m_with_add stx SCRATCH_ZP1
|
||||
sty SCRATCH_ZP2
|
||||
@ -62,7 +62,7 @@ _m_with_add stx SCRATCH_ZP1
|
||||
|
||||
sub multiply_bytes_addA_16 (byte1: X, byte2: Y, add: A) -> (A?, XY) {
|
||||
; ---- multiply 2 bytes and add A, result as word in X/Y (unsigned)
|
||||
asm {
|
||||
%asm {
|
||||
jmp multiply_bytes_16._m_with_add
|
||||
}
|
||||
}
|
||||
@ -73,7 +73,7 @@ sub multiply_words (number: XY) -> (?) {
|
||||
; input: X/Y = first 16-bit number, SCRATCH_ZPWORD1 in ZP = second 16-bit number
|
||||
; output: multiply_words_product 32-bits product, LSB order (low-to-high)
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
stx SCRATCH_ZPWORD2
|
||||
sty SCRATCH_ZPWORD2+1
|
||||
|
||||
@ -105,7 +105,7 @@ mult16 lda #$00
|
||||
sub divmod_bytes (number: X, divisor: Y) -> (X, A) {
|
||||
; ---- divide X by Y, result quotient in X, remainder in A (unsigned)
|
||||
; division by zero will result in quotient = 255 and remainder = original number
|
||||
asm {
|
||||
%asm {
|
||||
stx SCRATCH_ZP1
|
||||
sty SCRATCH_ZP2
|
||||
|
||||
@ -131,7 +131,7 @@ sub divmod_words (divisor: XY) -> (A?, XY) {
|
||||
; output: SCRATCH_ZPWORD1 in ZP: 16 bit result, X/Y: 16 bit remainder
|
||||
; division by zero will result in quotient = 65535 and remainder = divident
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
remainder = SCRATCH_ZP1
|
||||
|
||||
stx SCRATCH_ZPWORD2
|
||||
|
178
reference.md
178
reference.md
@ -19,7 +19,7 @@ which aims to provide many conveniences over raw assembly code (even when using
|
||||
- automatic variable allocations, automatic string variables and string sharing
|
||||
- automatic type conversions
|
||||
- floating point operations
|
||||
- automatically preserving and restoring CPU registers state, when calling routines that otherwise would clobber these
|
||||
- optional automatic preserving and restoring CPU registers state, when calling routines that otherwise would clobber these
|
||||
- abstracting away low level aspects such as zero page handling, program startup, explicit memory addresses
|
||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||
@ -39,7 +39,7 @@ You need [Python 3.5](https://www.python.org/downloads/) or newer to run IL65 it
|
||||
IL65 is mainly targeted at the Commodore-64 machine, but should be mostly system independent.
|
||||
|
||||
|
||||
MEMORY MODEL
|
||||
Memory Model
|
||||
------------
|
||||
|
||||
Most of the 64 kilobyte address space can be accessed by your program.
|
||||
@ -123,7 +123,7 @@ and if blocks require it themselves you can't combine various modules anymore on
|
||||
|
||||
|
||||
|
||||
DATA TYPES
|
||||
Data Types
|
||||
----------
|
||||
|
||||
IL65 supports the following data types:
|
||||
@ -168,11 +168,14 @@ unless it is part of a subroutine call statement. For an indirect goto call, the
|
||||
(``jmp`` indirect) and an indirect subroutine call (``jsr`` indirect) is synthesized using a couple of instructions.
|
||||
|
||||
|
||||
PROGRAM STRUCTURE
|
||||
Program Structure
|
||||
-----------------
|
||||
|
||||
In IL65 every line in the source file can only contain *one* statement or declaration.
|
||||
In IL65 every line in the source file can only contain *one* statement or definitons.
|
||||
Compilation is done on *one* main source code file, but other files can be imported.
|
||||
A source file can start with global *directives* (starting with ``%``) and continues
|
||||
with imports and block definitions.
|
||||
|
||||
|
||||
### Comments
|
||||
|
||||
@ -181,44 +184,69 @@ If the comment is the only thing on the line, it is copied into the resulting as
|
||||
This makes it easier to understand and relate the generated code.
|
||||
|
||||
|
||||
### Output Modes
|
||||
### Output Mode
|
||||
|
||||
The default format of the generated program is a "raw" binary where code starts at ``$c000``.
|
||||
You can generate other types of programs as well, by telling IL65 via an output mode statement
|
||||
You can generate other types of programs as well, by telling IL65 via an output mode directive
|
||||
at the beginning of your program:
|
||||
|
||||
| mode declaration | meaning |
|
||||
|----------------------|------------------------------------------------------------------------------------|
|
||||
| ``output raw`` | no load address bytes |
|
||||
| ``output prg`` | include the first two load address bytes, (default is ``$0801``), no BASIC program |
|
||||
| ``output prg,basic`` | as 'prg', but include a BASIC start program with SYS call, default code start is immediately after the BASIC program at ``$081d``, or after that. |
|
||||
| | |
|
||||
| ``address $0801`` | override program start address (default is set to ``$c000`` for raw mode and ``$0801`` for C-64 prg mode). Cannot be used if output mode is ``prg,basic`` because BASIC programs always have to start at ``$0801``. |
|
||||
| mode directive | meaning |
|
||||
|-----------------------|------------------------------------------------------------------------------------|
|
||||
| ``%output raw`` | no load address bytes |
|
||||
| ``%output prg`` | include the first two load address bytes, (default is ``$0801``), no BASIC program |
|
||||
| ``%output prg,basic`` | as 'prg', but include a BASIC start program with SYS call, default code start is immediately after the BASIC program at ``$081d``, or after that. |
|
||||
| | |
|
||||
| ``%address $0801`` | override program start address (default is set to ``$c000`` for raw mode and ``$0801`` for C-64 prg mode). Cannot be used if output mode is ``prg,basic`` because BASIC programs always have to start at ``$0801``. |
|
||||
|
||||
|
||||
### ZeroPage Options
|
||||
|
||||
You can tell the compiler how to treat the *zero page*. Normally it is considered a 'no-go' area
|
||||
except for the frew free locations mentioned under "Memory Model".
|
||||
However you can specify some options globally in your program to change this behavior:
|
||||
However you can specify some options globally in your program by using the zp directive
|
||||
to change this behavior:
|
||||
|
||||
- ``zp clobber``
|
||||
- ``%zp clobber``
|
||||
Use the whole zeropage for variables. It is not possible to exit your program
|
||||
correctly back to BASIC, other than resetting the machine.
|
||||
It does make your program smaller and faster because many more variables can
|
||||
be stored in the ZP, which is more efficient.
|
||||
- ``zp clobber, restore``
|
||||
- ``%zp clobber, restore``
|
||||
Use the whole zeropage, but make a backup copy of the original values at program start.
|
||||
When your program exits, the original ZP is restored (except for the software jiffy clock
|
||||
in ``$a0 - $a2``) and you drop back to the BASIC prompt.
|
||||
Not that the default IRQ routine is *still enabled* when your program is entered!
|
||||
See the paragraph on the zero page for more info about this.
|
||||
|
||||
If you use ``zp clobber``, you can no longer use most BASIC or KERNAL routines,
|
||||
If you use ``%zp clobber``, you can no longer use most BASIC or KERNAL routines,
|
||||
because these depend on most of the locations in the ZP. This includes the floating-point
|
||||
logic and several utility routines that do I/O, such as ``print_string``.
|
||||
|
||||
|
||||
### Importing, Including and Binary-Including other files or raw assembly code
|
||||
|
||||
```%import "filename[.ill]"```
|
||||
Must be used *after* any global option directives, and outside of a block,
|
||||
but otherwise can be placed anywhere in the program.
|
||||
Reads and compiles the named IL65 file and stores the resulting definitions
|
||||
as if they were a part of your current program.
|
||||
|
||||
```%asminclude "filename.txt", scopelabel```
|
||||
This directive can only be used inside a block.
|
||||
The assembler will include the file as raw assembly source text at this point, il65 will not process this at all.
|
||||
The scopelabel will be used as a prefix to access the labels from the included source code,
|
||||
otherwise you would risk symbol redefinitions or duplications.
|
||||
|
||||
```%asmbinary "filename.bin" [, <offset>[, <length>]]```
|
||||
This directive can only be used inside a block.
|
||||
The assembler will include the file as binary bytes at this point, il65 will not process this at all.
|
||||
The optional offset and length can be used to select a particular piece of the file.
|
||||
|
||||
```%asm {``` [raw assembly code lines] ``}``
|
||||
This directive includes raw unparsed assembly code at that exact spot in the program.
|
||||
The ``%asm {`` and ``}`` start and end markers each have to be on their own unique line.
|
||||
|
||||
|
||||
### Program Entry Point
|
||||
|
||||
Every program has to have one entry point where code execution begins.
|
||||
@ -229,40 +257,29 @@ Blocks and other details are described below.
|
||||
|
||||
### Blocks
|
||||
|
||||
~ blockname [address] {
|
||||
statements
|
||||
}
|
||||
``~ blockname [address] {``
|
||||
|
||||
The blockname "ZP" is reserved and always means the ZeroPage. Its start address is always set to $04,
|
||||
[directives...]
|
||||
[statements...]
|
||||
|
||||
``}``
|
||||
|
||||
Blocks form the separate pieces of code and data of your program. They are combined and
|
||||
arranged to a single output program. No code or data can occur outside a block.
|
||||
A block is also a *scope* in your program so the symbols in the block don't clash with
|
||||
symbols of the same name defined elsewhere. You can refer to the symbols in a particular block
|
||||
by using a *dotted name*: ``blockname.symbolname``.
|
||||
|
||||
Block names must be unique in your entire program,
|
||||
except "ZP": the contents of every block with that name are merged into one.
|
||||
This block name refers to the zero page. Its start address is always set to $04,
|
||||
because $00/$01 are used by the hardware and $02/$03 are reserved as general purpose scratch registers.
|
||||
|
||||
Block names cannot occur more than once, EXCEPT 'ZP' where the contents of every occurrence of it are merged.
|
||||
Block address must be >= $0200 (because $00-$fff is the ZP and $100-$200 is the cpu stack)
|
||||
|
||||
You can omit the blockname but then you can only refer to the contents of the block via its absolute address,
|
||||
which is required in this case. If you omit both, the block is ignored altogether (and a warning is displayed).
|
||||
which is required in this case. If you omit *both*, the block is ignored altogether (and a warning is displayed).
|
||||
This is a way to quickly "comment out" a piece of code that is unfinshed or may contain errors that you
|
||||
want to work on later, because the contents of the ignored block are not syntactically parsed.
|
||||
|
||||
|
||||
### Importing, Including and Binary-Including Files
|
||||
|
||||
import "filename[.ill]"
|
||||
Must be used *after* any global option statements such as ``output``,
|
||||
and can only be used outside of a block. But can otherwise occur between any blocks.
|
||||
Reads everything from the named IL65 file at this point and compile it as a normal part of the program.
|
||||
|
||||
asminclude "filename.txt", scopelabel
|
||||
Can only be used in a block.
|
||||
The assembler will include the file as asm source text at this point, il65 will not process this at all.
|
||||
The scopelabel will be used as a prefix to access the labels from the included source code,
|
||||
otherwise you would risk symbol redefinitions or duplications.
|
||||
|
||||
asmbinary "filename.bin" [, <offset>[, <length>]]
|
||||
Can only be used in a block.
|
||||
The assembler will include the file as binary bytes at this point, il65 will not process this at all.
|
||||
The optional offset and length can be used to select a particular piece of the file.
|
||||
|
||||
want to work on later, because the contents of the ignored block are not fully parsed.
|
||||
|
||||
|
||||
### Assignments
|
||||
@ -282,7 +299,6 @@ A special assignment is the *augmented assignment* where the value is modified i
|
||||
Several assignment operators are available: ``+=``, ``-=``, ``&=``, ``|=``, ``^=``, ``<<=``, ``>>=``
|
||||
|
||||
|
||||
|
||||
### Expressions
|
||||
|
||||
In most places where a number or other value is expected, you can use just the number, or a full constant expression.
|
||||
@ -294,7 +310,6 @@ all builtin functions (max, avg, min, sum etc). They can also reference idendifi
|
||||
if this makes sense.
|
||||
|
||||
|
||||
|
||||
### Subroutine Definition
|
||||
|
||||
Subroutines are parts of the code that can be repeatedly invoked using a subroutine call from elsewhere.
|
||||
@ -335,25 +350,24 @@ but instead assign a memory address to it:
|
||||
### Subroutine Calling
|
||||
|
||||
You call a subroutine like this:
|
||||
subroutinename_or_address [!] ( [arguments...] )
|
||||
|
||||
``subroutinename_or_address (`` [arguments...] ``)``
|
||||
|
||||
or:
|
||||
|
||||
``subroutinename_or_address !``[register(s)] ``( [arguments...] )``
|
||||
|
||||
If the subroutine returns one or more values as results, you must use an assignment statement
|
||||
to store those values somewhere:
|
||||
outputvar1, outputvar2 = subroutine ( arg1, arg2, arg3 )
|
||||
|
||||
``outputvar1, outputvar2 = subroutine ( arg1, arg2, arg3 )``
|
||||
|
||||
The output variables must occur in the correct sequence of return registers as specified
|
||||
in the subroutine's definiton. It is possible to not specify any of them but the compiler
|
||||
will issue a warning then if the result values of a subroutine call are discarded.
|
||||
Even if the subroutine returns something in a register that already is the correct one
|
||||
you want to keep, you'll have to explicitly assign the return value to that register.
|
||||
If you omit it, no return value is stored at all (well, unless you call the subroutine without
|
||||
register preserving, see the next paragraph.)
|
||||
|
||||
|
||||
Normally, the registers are preserved when calling the subroutine and restored on return (except
|
||||
the ones that explictly take result values from the call).
|
||||
If you add a '!' after the name, no register preserving is done and the call essentially
|
||||
is just a single JSR instruction.
|
||||
Arguments should match the subroutine definition. You are allowed to omit the parameter names.
|
||||
If no definition is available (because you're directly calling memory or a label or something else),
|
||||
you can freely add arguments (but in this case they all have to be named).
|
||||
@ -362,8 +376,40 @@ To jump to a subroutine (without returning), prefix the subroutine call with the
|
||||
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.
|
||||
|
||||
**Register preserving calls:** use the ``!`` followed by a combination of A, X and Y (or followed
|
||||
by nothing, which is the same as AXY) to tell the compiler you want to preserve the origial
|
||||
value of the given registers after the subroutine call. Otherwise, the subroutine may just
|
||||
as well clobber all three registers. Preserving the original values does result in some
|
||||
stack manipulation code to be inserted for every call like this, which can be quite slow.
|
||||
|
||||
|
||||
#### Calling Convention
|
||||
|
||||
Subroutine arguments and results are passed via registers (and sometimes implicitly
|
||||
via certain memory locations).
|
||||
@todo support call non-register args (variable parameter passing)
|
||||
|
||||
In IL65 the "caller saves" principle applies to registers used in a subroutine.
|
||||
This means the code that calls a subroutine or performs some function that clobber certain
|
||||
registers (A, X or Y), is responsible for storing and restoring the original values if
|
||||
that is required.
|
||||
|
||||
*You should assume that the 3 hardware registers A, X and Y are volatile and their contents
|
||||
cannot be depended upon, unless you make sure otherwise*.
|
||||
|
||||
Normally, the registers are NOT preserved when calling a subroutine or when a certian
|
||||
operations are performed. Most calls will be simply a few instructions to load the
|
||||
values in the registers and then a JSR or JMP.
|
||||
|
||||
By using the ``%preserve_registers`` directive (globally or in a block) you can tell the
|
||||
compiler to preserve all registers. This does generate a lot of extra code that puts
|
||||
original values on the stack and gets them off the stack again once the subroutine is done.
|
||||
In this case however you don't have to worry about A, X and Y losing their original values
|
||||
and you can essentially treat them as three local variables instead of scratch data.
|
||||
|
||||
You can also use a ``!`` on a single subroutine call to preserve register values, instead of
|
||||
setting this behavior for the entire block or even program. See the paragraph on Subroutine Calling
|
||||
for more info.
|
||||
|
||||
|
||||
### Conditional Execution Flow
|
||||
@ -394,7 +440,7 @@ if you run into this type of assembler error.
|
||||
Debugging (with Vice)
|
||||
---------------------
|
||||
|
||||
The ``breakpoint`` statement is a special statement that instructs the compiler to put
|
||||
The ``%breakpoint`` directive instructs the compiler to put
|
||||
a *breakpoint* at that position in the code. It's a logical breakpoint instead of a physical
|
||||
BRK instruction because that will usually halt the machine altogether instead of breaking execution.
|
||||
Instead, a NOP instruction is generated and in a special output file the list of breakpoints is written.
|
||||
@ -409,7 +455,8 @@ so if your program runs and it hits a breakpoint, Vice will halt execution and d
|
||||
|
||||
|
||||
|
||||
TODOS
|
||||
|
||||
@Todo
|
||||
-----
|
||||
|
||||
### IF_XX:
|
||||
@ -549,12 +596,6 @@ these should call (or emit inline) optimized pieces of assembly code, so they ru
|
||||
|
||||
|
||||
|
||||
### Register Preservation Block
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### Bitmap Definition (for Sprites and Characters)
|
||||
|
||||
to define CHARACTERS (8x8 monochrome or 4x8 multicolor = 8 bytes)
|
||||
@ -565,7 +606,9 @@ and SPRITES (24x21 monochrome or 12x21 multicolor = 63 bytes)
|
||||
|
||||
### More Datatypes
|
||||
|
||||
@todo 24 and 32 bits integers, unsigned and signed?
|
||||
@todo pointers/addresses? (as opposed to normal WORDs)
|
||||
@todo signed integers (byte and word)?
|
||||
|
||||
|
||||
### Some support for simple arithmetic
|
||||
|
||||
@ -573,6 +616,3 @@ and SPRITES (24x21 monochrome or 12x21 multicolor = 63 bytes)
|
||||
A = X * Y
|
||||
A /= Y
|
||||
A = Y / Y
|
||||
|
||||
@todo multiplication routines (8*8 -> 16, 8*16 -> 16, 16*16->16 (or 32?))
|
||||
@todo division routines
|
||||
|
@ -59,7 +59,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -75,6 +75,15 @@ bar
|
||||
sub4! ("hello", other=42)
|
||||
sub4! (string="hello", other = 42)
|
||||
sub4! (string="hello, there", other = 42)
|
||||
|
||||
sub3 (81)
|
||||
sub3 !(81)
|
||||
sub3 !A (81)
|
||||
sub3 !X (81)
|
||||
sub3 !Y (81)
|
||||
sub3 !XY (81)
|
||||
sub3 !AXY (81)
|
||||
|
||||
bar!()
|
||||
[XY] ! ()
|
||||
[var1] !()
|
||||
@ -84,7 +93,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -110,7 +119,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
@ -1,6 +1,6 @@
|
||||
output prg,basic
|
||||
%output prg,basic
|
||||
|
||||
import "c64lib"
|
||||
%import "c64lib"
|
||||
|
||||
~ main {
|
||||
var .word value
|
||||
|
@ -1,10 +1,10 @@
|
||||
; var definitions and immediate primitive data type tests
|
||||
|
||||
output raw
|
||||
zp clobber
|
||||
%output raw
|
||||
%zp clobber
|
||||
|
||||
|
||||
import "c64lib"
|
||||
%import "c64lib"
|
||||
|
||||
~ ZP {
|
||||
; ZeroPage block definition:
|
||||
|
@ -1,8 +1,8 @@
|
||||
; floating point tests
|
||||
|
||||
output prg, basic
|
||||
%output prg, basic
|
||||
|
||||
import "c64lib"
|
||||
%import "c64lib"
|
||||
|
||||
~ main_testing {
|
||||
start
|
||||
|
@ -1,14 +1,15 @@
|
||||
output prg,basic
|
||||
%output prg,basic
|
||||
|
||||
import "c64lib"
|
||||
%import "c64lib"
|
||||
|
||||
~ main {
|
||||
var .text name = "?"*80
|
||||
var .word orig_irq
|
||||
|
||||
start
|
||||
c64.init_system()
|
||||
|
||||
XY = c64.CINV
|
||||
orig_irq = c64.CINV
|
||||
SI = 1
|
||||
c64.CINV = #irq_handler
|
||||
SI = 0
|
||||
@ -19,27 +20,28 @@ start
|
||||
c64.CHROUT('\n')
|
||||
|
||||
blop
|
||||
breakpoint ; yeah!
|
||||
%breakpoint ; yeah!
|
||||
|
||||
c64scr.print_string("thank you, mr or mrs: ")
|
||||
c64scr.print_string(name)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
SI = 1
|
||||
c64.CINV = XY
|
||||
c64.CINV = orig_irq
|
||||
SI = 0
|
||||
|
||||
return
|
||||
|
||||
|
||||
irq_handler
|
||||
asm {
|
||||
lda $cb
|
||||
cmp #$40
|
||||
%asm {
|
||||
lda c64.SFDX
|
||||
cmp #$40 ; nothing pressed?
|
||||
beq +
|
||||
inc c64.EXTCOL
|
||||
inc c64.EXTCOL ; otherwise change color
|
||||
+ jmp c64.IRQDFRT
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
; var definitions and immediate primitive data type tests
|
||||
|
||||
output prg, basic
|
||||
zp clobber
|
||||
%output prg, basic
|
||||
%zp clobber
|
||||
|
||||
import "c64lib"
|
||||
%import "c64lib"
|
||||
|
||||
~ main {
|
||||
start
|
||||
@ -343,7 +343,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -368,7 +368,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -394,7 +394,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -696,7 +696,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -721,7 +721,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -747,7 +747,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -1049,7 +1049,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -1074,7 +1074,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -1100,7 +1100,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -1402,7 +1402,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -1427,7 +1427,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -1453,7 +1453,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -1755,7 +1755,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -1780,7 +1780,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -1806,7 +1806,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -2108,7 +2108,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -2133,7 +2133,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -2159,7 +2159,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -2461,7 +2461,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -2486,7 +2486,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -2512,7 +2512,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -2814,7 +2814,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -2839,7 +2839,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -2865,7 +2865,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -3167,7 +3167,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -3192,7 +3192,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -3218,7 +3218,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -3520,7 +3520,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -3545,7 +3545,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -3571,7 +3571,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -3873,7 +3873,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -3898,7 +3898,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -3924,7 +3924,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -4226,7 +4226,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -4251,7 +4251,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -4277,7 +4277,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -4579,7 +4579,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -4604,7 +4604,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -4630,7 +4630,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -4932,7 +4932,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -4957,7 +4957,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -4983,7 +4983,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -5285,7 +5285,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -5310,7 +5310,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -5336,7 +5336,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -5638,7 +5638,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -5663,7 +5663,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -5689,7 +5689,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -5991,7 +5991,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -6016,7 +6016,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -6042,7 +6042,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -6344,7 +6344,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -6369,7 +6369,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -6395,7 +6395,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -6697,7 +6697,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -6722,7 +6722,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -6748,7 +6748,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -7050,7 +7050,7 @@ bar
|
||||
goto $c2
|
||||
goto $c2()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -7075,7 +7075,7 @@ bar
|
||||
$c000!()
|
||||
$c2!()
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@ -7101,7 +7101,7 @@ bar
|
||||
$c2()
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
@ -1,8 +1,5 @@
|
||||
output prg,basic
|
||||
;reg_preserve off ; @todo global option off/on default off?
|
||||
|
||||
|
||||
import "c64lib"
|
||||
%output prg,basic
|
||||
%import "c64lib"
|
||||
|
||||
|
||||
~ main {
|
||||
|
@ -4,8 +4,8 @@
|
||||
; line 3 comment
|
||||
|
||||
|
||||
output basic , prg ; create a c-64 program with basic SYS call to launch it
|
||||
zp restore , clobber ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
||||
%output basic , prg ; create a c-64 program with basic SYS call to launch it
|
||||
%zp restore , clobber ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
||||
|
||||
|
||||
~main $0a00
|
||||
@ -88,7 +88,7 @@ sub memsub () -> () = $fff2
|
||||
|
||||
sub customsub (Y)->() {
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
lda #99
|
||||
@ -120,7 +120,7 @@ somelabel1222
|
||||
|
||||
~ block3 {
|
||||
somelabel1
|
||||
asm {
|
||||
%asm {
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
@ -1,71 +1,24 @@
|
||||
; source IL file
|
||||
; these are comments
|
||||
%output prg
|
||||
%address $c000
|
||||
|
||||
output prg, basic ; create a c-64 program with basic SYS call to launch it
|
||||
zp clobber,restore ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
||||
|
||||
~main
|
||||
{
|
||||
memory borderb1 = $d020
|
||||
memory .byte borderb2 = $d020
|
||||
memory .word screencolors = $d020
|
||||
memory .array($10) screenblock1 = $0400 ; 10 bytes
|
||||
memory .array($0100) screenblock2 = $0500 ; 256 bytes
|
||||
memory .array($0234) screenblock3 = $2000 ; some weird size
|
||||
memory .wordarray($20) screenblock4 = $0600 ; 32 words
|
||||
memory .matrix(40,25) charscreen = $0400
|
||||
var .array($10) var1 = $aa
|
||||
var .wordarray($10) var2 = $9988
|
||||
var .array($10) var3 = $11
|
||||
var var4
|
||||
var .matrix(40,25) vcharscreen ; init with 0
|
||||
var .matrix(40,25) vcharscreen2 = 111 ; init with 111
|
||||
|
||||
start
|
||||
; set some colors
|
||||
Y = 0
|
||||
borderb1 = Y
|
||||
X=Y=A = 0
|
||||
X = 1
|
||||
screencolors = X
|
||||
borderb1 = 0
|
||||
borderb2 = 1
|
||||
screencolors = $0
|
||||
screencolors = $0207
|
||||
|
||||
; fill block of memory with a single value (byte or word)
|
||||
;screenblock1,* = A
|
||||
;screenblock1,* = X
|
||||
;screenblock1,* = Y
|
||||
;screenblock1,* = 2
|
||||
;screenblock1,* = $aaa
|
||||
;screenblock2,* = A
|
||||
;screenblock2,* = X
|
||||
;screenblock2,* = Y
|
||||
;screenblock2,* = 2
|
||||
;screenblock2,* = $aaa
|
||||
;screenblock3,* = A
|
||||
;screenblock3,* = X
|
||||
;screenblock3,* = Y
|
||||
;screenblock3,* = 2
|
||||
;screenblock3,* = $aaa
|
||||
;screenblock4,* = A
|
||||
;screenblock4,* = X
|
||||
;screenblock4,* = Y
|
||||
;screenblock4,* = 2
|
||||
;screenblock4,* = $aaa
|
||||
|
||||
; swap bytes
|
||||
;A = var1[0]
|
||||
;X = var1[1]
|
||||
;var1[0] = X
|
||||
;var1[1] = A
|
||||
|
||||
; swap words
|
||||
;A = var2[0] ; must crash, cannot put word in register
|
||||
;X = var2[1] ;crash
|
||||
;var2[0] = X ; ok, 00-padded
|
||||
;var2[1] = A ; ok, 00-padded
|
||||
|
||||
return
|
||||
~ test {
|
||||
var .byte localvar = 33
|
||||
return
|
||||
}
|
||||
|
||||
~ main {
|
||||
start
|
||||
A=0
|
||||
[$d020]=A
|
||||
X=false
|
||||
Y=true
|
||||
return
|
||||
|
||||
;included_assembly
|
||||
%asminclude "included.source" test_include
|
||||
|
||||
;included_binary
|
||||
%asmbinary "included.binary"
|
||||
%asmbinary "included.binary" $40
|
||||
%asmbinary "included.binary" $40 $200
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
|
||||
output prg,basic ; create a c-64 program with basic SYS call to launch it
|
||||
%output prg,basic ; create a c-64 program with basic SYS call to launch it
|
||||
|
||||
|
||||
import "c64lib" ; searched in several locations and with .ill file extension added
|
||||
%import "c64lib" ; searched in several locations and with .ill file extension added
|
||||
|
||||
~ main
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
output prg,basic ; create a c-64 program with basic SYS to() launch it
|
||||
%output prg,basic ; create a c-64 program with basic SYS to() launch it
|
||||
|
||||
import "c64lib.ill"
|
||||
%import "c64lib.ill"
|
||||
|
||||
~ main
|
||||
{
|
||||
@ -9,21 +9,21 @@ output prg,basic ; create a c-64 program with basic SYS to() launch it
|
||||
const .word BORDER = $d020
|
||||
|
||||
start
|
||||
c64scr.print_pimmediate ! () ; this prints the pstring immediately following it
|
||||
asm {
|
||||
c64scr.print_pimmediate() ; this prints the pstring immediately following it
|
||||
%asm {
|
||||
.ptext "hello-pimmediate!{cr}"
|
||||
}
|
||||
|
||||
c64scr.print_byte_decimal0 ! (19)
|
||||
c64.CHROUT ! (13)
|
||||
c64scr.print_byte_decimal ! (19)
|
||||
c64.CHROUT ! (13)
|
||||
c64scr.print_byte_decimal0 (19)
|
||||
c64.CHROUT (13)
|
||||
c64scr.print_byte_decimal (19)
|
||||
c64.CHROUT (13)
|
||||
|
||||
|
||||
c64scr.print_word_decimal0 ! ($0102)
|
||||
c64.CHROUT ! (13)
|
||||
c64scr.print_word_decimal ! ($0102)
|
||||
c64.CHROUT ! (13)
|
||||
c64scr.print_word_decimal0 ($0102)
|
||||
c64.CHROUT (13)
|
||||
c64scr.print_word_decimal ($0102)
|
||||
c64.CHROUT (13)
|
||||
return
|
||||
|
||||
start2
|
||||
|
@ -1,24 +0,0 @@
|
||||
output prg
|
||||
address $c000
|
||||
|
||||
~ test {
|
||||
var .byte localvar = 33
|
||||
return
|
||||
}
|
||||
|
||||
~ main {
|
||||
start
|
||||
A=0
|
||||
[$d020]=A
|
||||
X=false
|
||||
Y=true
|
||||
return
|
||||
|
||||
;included_assembly
|
||||
asminclude "included.source" test_include
|
||||
|
||||
;included_binary
|
||||
asmbinary "included.binary"
|
||||
asmbinary "included.binary" $40
|
||||
asmbinary "included.binary" $40 $200
|
||||
}
|
23
todo.ill
23
todo.ill
@ -1,10 +1,12 @@
|
||||
output prg,basic
|
||||
%output prg,basic
|
||||
%preserve_registers no
|
||||
|
||||
;reg_preserve off ; @todo global option off/on default off? NOT AN OPTION -> change the default to OFF
|
||||
|
||||
import "c64lib"
|
||||
%import "c64lib"
|
||||
|
||||
~ main {
|
||||
%preserve_registers false
|
||||
|
||||
|
||||
const num = 2
|
||||
var var1 =2
|
||||
var .word wvar1 = 2
|
||||
@ -41,7 +43,7 @@ start
|
||||
;XY <<= 2
|
||||
|
||||
|
||||
asm {
|
||||
%asm {
|
||||
ldy #200
|
||||
- lda #81
|
||||
sta c64.Screen+39-40,y
|
||||
@ -92,10 +94,13 @@ loop
|
||||
X = $22
|
||||
Y = $33
|
||||
|
||||
c64scr.clear_screen !(81, 5) ; @todo new syntax to specify registers to save (! = all three A,X,Y)
|
||||
;c64scr.clear_screen !A (81, 5) ; @todo new syntax to specify registers to save (only A)
|
||||
;c64scr.clear_screen !AX (81, 5)
|
||||
;c64scr.clear_screen !AXY (81, 5)
|
||||
c64scr.clear_screen (81, 5)
|
||||
c64scr.clear_screen !(81, 5)
|
||||
c64scr.clear_screen !A (81, 5)
|
||||
c64scr.clear_screen !X (81, 5)
|
||||
c64scr.clear_screen !Y (81, 5)
|
||||
c64scr.clear_screen !XY (81, 5)
|
||||
c64scr.clear_screen !AXY (81, 5)
|
||||
|
||||
c64scr.print_byte_hex(1,A)
|
||||
c64.CHROUT(' ')
|
||||
|
Loading…
Reference in New Issue
Block a user