diff --git a/il65/compile.py b/il65/compile.py index 2d4f6d8b9..0a4e216e9 100644 --- a/il65/compile.py +++ b/il65/compile.py @@ -16,7 +16,7 @@ from .plyparse import parse_file, ParseError, Module, Directive, Block, Subrouti SymbolName, Dereference, AddressOf from .plylex import SourceRef, print_bold from .optimize import optimize -from .datatypes import DataType, VarType, STRING_DATATYPES +from .datatypes import DataType, VarType class CompileError(Exception): @@ -42,7 +42,6 @@ class PlyParser: # these shall only be done on the main module after all imports have been done: self.apply_directive_options(module) self.determine_subroutine_usage(module) - # XXX merge zero page from imported modules??? do we still have to do that? self.allocate_zeropage_vars(module) except ParseError as x: self.handle_parse_error(x) @@ -85,6 +84,8 @@ class PlyParser: return zeropage = Zeropage(module.zp_options) for vardef in zpnode.scope.filter_nodes(VarDef): + if vardef.datatype.isstring(): + raise ParseError("cannot put strings in the zeropage", vardef.sourceref) try: if vardef.vartype == VarType.VAR: vardef.zp_address = zeropage.allocate(vardef) @@ -293,7 +294,7 @@ class PlyParser: if len(splits) == 2: for match in re.finditer(r"(?P[a-zA-Z_$][a-zA-Z0-9_\.]+)", splits[1]): name = match.group("symbol") - if name[0] == '$' or "." not in name: + if name[0] == '$': continue try: symbol = parent_scope[name] @@ -301,8 +302,11 @@ class PlyParser: pass else: if isinstance(symbol, Subroutine): - namespace, name = name.rsplit(".", maxsplit=2) - usages[(namespace, name)].add(str(asmnode.sourceref)) + if symbol.scope: + namespace = symbol.scope.parent_scope.name + else: + namespace, name = name.rsplit(".", maxsplit=2) + usages[(namespace, symbol.name)].add(str(asmnode.sourceref)) def check_directives(self, module: Module) -> None: for node, parent in module.all_scopes(): @@ -460,7 +464,7 @@ class Zeropage: elif vardef.datatype == DataType.MATRIX: print_bold("warning: {}: allocating a large datatype in zeropage".format(vardef.sourceref)) size = vardef.size[0] * vardef.size[1] - elif vardef.datatype in STRING_DATATYPES: + elif vardef.datatype.isstring(): print_bold("warning: {}: allocating a large datatype in zeropage".format(vardef.sourceref)) size = vardef.size[0] else: diff --git a/il65/datatypes.py b/il65/datatypes.py index 611088d9f..50ae93abe 100644 --- a/il65/datatypes.py +++ b/il65/datatypes.py @@ -46,9 +46,19 @@ class DataType(enum.Enum): return self.value < other.value return NotImplemented + def isnumeric(self) -> bool: + return self.value in (1, 2, 3) + + def isarray(self) -> bool: + return self.value in (4, 5, 6) + + def isstring(self) -> bool: + return self.value in (7, 8, 9, 10) + STRING_DATATYPES = {DataType.STRING, DataType.STRING_P, DataType.STRING_S, DataType.STRING_PS} + REGISTER_SYMBOLS = {"A", "X", "Y", "AX", "AY", "XY", "SC", "SI"} REGISTER_SYMBOLS_RETURNVALUES = REGISTER_SYMBOLS | {"SZ"} REGISTER_BYTES = {"A", "X", "Y"} diff --git a/il65/generateasm.py b/il65/generateasm.py index b8386ac1c..900e43bc2 100644 --- a/il65/generateasm.py +++ b/il65/generateasm.py @@ -223,40 +223,55 @@ class AssemblyGenerator: # @todo add a block initializer subroutine that can contain custom reset/init code? (static initializer) def _memset(varname: str, value: int, size: int) -> None: - value = value or 0 - self.p("\vlda #<" + varname) - self.p("\vsta il65_lib.SCRATCH_ZPWORD1") - self.p("\vlda #>" + varname) - self.p("\vsta il65_lib.SCRATCH_ZPWORD1+1") - self.p("\vlda #" + to_hex(value)) - self.p("\vldx #" + to_hex(size)) - self.p("\vjsr il65_lib.memset") + if size > 6: + self.p("\vlda #<" + varname) + self.p("\vsta il65_lib.SCRATCH_ZPWORD1") + self.p("\vlda #>" + varname) + self.p("\vsta il65_lib.SCRATCH_ZPWORD1+1") + self.p("\vlda #" + to_hex(value)) + self.p("\vldx #<" + to_hex(size)) + self.p("\vldy #>" + to_hex(size)) + self.p("\vjsr il65_lib.memset") + else: + self.p("\vlda #" + to_hex(value)) + for i in range(size): + self.p("\vsta {:s}+{:d}".format(varname, i)) def _memsetw(varname: str, value: int, size: int) -> None: - value = value or 0 - self.p("\vlda #<" + varname) - self.p("\vsta il65_lib.SCRATCH_ZPWORD1") - self.p("\vlda #>" + varname) - self.p("\vsta il65_lib.SCRATCH_ZPWORD1+1") - self.p("\vlda #<" + to_hex(value)) - self.p("\vldy #>" + to_hex(value)) - self.p("\vldx #" + to_hex(size)) - self.p("\vjsr il65_lib.memsetw") + if size > 4: + self.p("\vlda #<" + varname) + self.p("\vsta il65_lib.SCRATCH_ZPWORD1") + self.p("\vlda #>" + varname) + self.p("\vsta il65_lib.SCRATCH_ZPWORD1+1") + self.p("\vlda #<" + to_hex(size)) + self.p("\vsta il65_lib.SCRATCH_ZPWORD2") + self.p("\vlda #>" + to_hex(size)) + self.p("\vsta il65_lib.SCRATCH_ZPWORD2+1") + self.p("\vlda #<" + to_hex(value)) + self.p("\vldx #>" + to_hex(value)) + self.p("\vjsr il65_lib.memsetw") + else: + self.p("\vlda #<" + to_hex(value)) + self.p("\vldy #>" + to_hex(value)) + for i in range(size): + self.p("\vsta {:s}+{:d}".format(varname, i*2)) + self.p("\vsty {:s}+{:d}".format(varname, i*2+1)) self.p("_il65_init_block\v; (re)set vars to initial values") float_inits = {} - string_inits = [] # type: List[VarDef] prev_value_a, prev_value_x = None, None vars_by_datatype = defaultdict(list) # type: Dict[DataType, List[VarDef]] for vardef in block.scope.filter_nodes(VarDef): if vardef.vartype == VarType.VAR: vars_by_datatype[vardef.datatype].append(vardef) for bytevar in sorted(vars_by_datatype[DataType.BYTE], key=lambda vd: vd.value): + assert isinstance(bytevar.value, int) if bytevar.value != prev_value_a: self.p("\vlda #${:02x}".format(bytevar.value)) prev_value_a = bytevar.value self.p("\vsta {:s}".format(bytevar.name)) for wordvar in sorted(vars_by_datatype[DataType.WORD], key=lambda vd: vd.value): + assert isinstance(wordvar.value, int) v_hi, v_lo = divmod(wordvar.value, 256) if v_hi != prev_value_a: self.p("\vlda #${:02x}".format(v_hi)) @@ -267,15 +282,18 @@ class AssemblyGenerator: self.p("\vsta {:s}".format(wordvar.name)) self.p("\vstx {:s}+1".format(wordvar.name)) for floatvar in vars_by_datatype[DataType.FLOAT]: + assert isinstance(floatvar.value, (int, float)) fpbytes = to_mflpt5(floatvar.value) # type: ignore float_inits[floatvar.name] = (floatvar.name, fpbytes, floatvar.value) for arrayvar in vars_by_datatype[DataType.BYTEARRAY]: + assert isinstance(arrayvar.value, int) _memset(arrayvar.name, arrayvar.value, arrayvar.size[0]) for arrayvar in vars_by_datatype[DataType.WORDARRAY]: + assert isinstance(arrayvar.value, int) _memsetw(arrayvar.name, arrayvar.value, arrayvar.size[0]) for arrayvar in vars_by_datatype[DataType.MATRIX]: + assert isinstance(arrayvar.value, int) _memset(arrayvar.name, arrayvar.value, arrayvar.size[0] * arrayvar.size[1]) - # @todo string datatype inits with 1 memcopy if float_inits: self.p("\vldx #4") self.p("-") @@ -287,11 +305,12 @@ class AssemblyGenerator: self.p("\vrts\n") for varname, (vname, fpbytes, fpvalue) in sorted(float_inits.items()): self.p("_init_float_{:s}\t\t.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}\t; {}".format(varname, *fpbytes, fpvalue)) - if string_inits: - self.p("_init_strings_start") - for svar in sorted(string_inits, key=lambda v: v.name): - self._generate_string_var(svar, init=True) - self.p("_init_strings_size = * - _init_strings_start") + all_string_vars = [] + for svtype in STRING_DATATYPES: + all_string_vars.extend(vars_by_datatype[svtype]) + for strvar in all_string_vars: + # string vars are considered to be a constant, and are statically initialized. + self._generate_string_var(strvar) self.p("") def _numeric_value_str(self, value: Any, as_hex: bool=False) -> str: @@ -320,7 +339,7 @@ class AssemblyGenerator: self.p("\v{:s} = {}".format(vardef.name, self._numeric_value_str(vardef.value))) elif vardef.datatype in (DataType.BYTE, DataType.WORD): self.p("\v{:s} = {:s}".format(vardef.name, self._numeric_value_str(vardef.value, True))) - elif vardef.datatype in STRING_DATATYPES: + elif vardef.datatype.isstring(): # a const string is just a string variable in the generated assembly self._generate_string_var(vardef) else: @@ -328,7 +347,7 @@ class AssemblyGenerator: self.p("; memory mapped variables") for vardef in vars_by_vartype.get(VarType.MEMORY, []): # create a definition for variables at a specific place in memory (memory-mapped) - if vardef.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT): + if vardef.datatype.isnumeric(): assert vardef.size == [1] self.p("\v{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value), vardef.datatype.name.lower())) elif vardef.datatype == DataType.BYTEARRAY: @@ -353,7 +372,9 @@ class AssemblyGenerator: # zeropage uses the zp_address we've allocated, instead of allocating memory here for vardef in vars_by_vartype.get(VarType.VAR, []): assert vardef.zp_address is not None - if vardef.datatype in (DataType.WORDARRAY, DataType.BYTEARRAY, DataType.MATRIX): + if vardef.datatype.isstring(): + raise CodeError("cannot put strings in the zeropage", vardef.sourceref) + if vardef.datatype.isarray(): size_str = "size " + str(vardef.size) else: size_str = "" @@ -363,7 +384,7 @@ class AssemblyGenerator: # create definitions for the variables that takes up empty space and will be initialized at startup string_vars = [] for vardef in vars_by_vartype.get(VarType.VAR, []): - if vardef.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT): + if vardef.datatype.isnumeric(): assert vardef.size == [1] if vardef.datatype == DataType.BYTE: self.p("{:s}\v.byte ?".format(vardef.name)) @@ -385,33 +406,29 @@ class AssemblyGenerator: assert len(vardef.size) == 2 self.p("{:s}\v.fill {:d}\t\t; matrix {:d}*{:d} bytes" .format(vardef.name, vardef.size[0] * vardef.size[1], vardef.size[0], vardef.size[1])) - elif vardef.datatype in STRING_DATATYPES: + elif vardef.datatype.isstring(): string_vars.append(vardef) else: raise CodeError("unknown variable type " + str(vardef.datatype)) - if string_vars: - self.p("il65_string_vars_start") - for svar in sorted(string_vars, key=lambda v: v.name): # must be the same order as in the init routine!!! - self.p("{:s}\v.fill {:d}+1\t\t; {}".format(svar.name, len(svar.value), svar.datatype.name.lower())) + # string vars are considered to be a constant, and are not re-initialized. self.p("") - def _generate_string_var(self, vardef: VarDef, init: bool=False) -> None: - prefix = "_init_str_" if init else "" + def _generate_string_var(self, vardef: VarDef) -> None: if vardef.datatype == DataType.STRING: # 0-terminated string - self.p("{:s}{:s}\n\v.null {:s}".format(prefix, vardef.name, self.output_string(str(vardef.value)))) + self.p("{:s}\n\v.null {:s}".format(vardef.name, self.output_string(str(vardef.value)))) elif vardef.datatype == DataType.STRING_P: # pascal string - self.p("{:s}{:s}\n\v.ptext {:s}".format(prefix, vardef.name, self.output_string(str(vardef.value)))) + self.p("{:s}\n\v.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value)))) elif vardef.datatype == DataType.STRING_S: # 0-terminated string in screencode encoding self.p(".enc 'screen'") - self.p("{:s}{:s}\n\v.null {:s}".format(prefix, vardef.name, self.output_string(str(vardef.value), True))) + self.p("{:s}\n\v.null {:s}".format(vardef.name, self.output_string(str(vardef.value), True))) self.p(".enc 'none'") elif vardef.datatype == DataType.STRING_PS: # 0-terminated pascal string in screencode encoding self.p(".enc 'screen'") - self.p("{:s}{:s}n\v.ptext {:s}".format(prefix, vardef.name, self.output_string(str(vardef.value), True))) + self.p("{:s}n\v.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value), True))) self.p(".enc 'none'") def generate_statement(self, stmt: AstNode) -> None: diff --git a/il65/lib/c64lib.ill b/il65/lib/c64lib.ill index 7fea24007..6ade9999b 100644 --- a/il65/lib/c64lib.ill +++ b/il65/lib/c64lib.ill @@ -1035,7 +1035,7 @@ sub input_chars (buffer: AX) -> (A?, Y) { ;sub memcopy_basic () -> (?) { -; ; ---- copy a memory block by using a BASIC ROM routine @todo fix code +; ; ---- copy a memory block by using a BASIC ROM routine ; ; it calls a function from the basic interpreter, so: ; ; - BASIC ROM must be banked in ; ; - the source block must be readable (so no RAM hidden under BASIC, Kernal, or I/O) @@ -1043,7 +1043,6 @@ sub input_chars (buffer: AX) -> (A?, Y) { ; ; higher addresses are copied first, so: ; ; - 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 { ; lda #src_start @@ -1061,7 +1060,7 @@ sub input_chars (buffer: AX) -> (A?, Y) { ; } ;} -; macro version of the above memcopy_basic routine: @todo macro support? +; macro version of the above memcopy_basic routine: ; MACRO PARAMS src_start, src_end, target_start ; lda #src_start diff --git a/il65/plyparse.py b/il65/plyparse.py index 0abcc577e..71f196fd2 100644 --- a/il65/plyparse.py +++ b/il65/plyparse.py @@ -431,7 +431,7 @@ class VarDef(AstNode): assert self.size is None self.size = self.datatype.dimensions or [1] self.datatype = self.datatype.to_enum() - if self.datatype in {DataType.BYTEARRAY, DataType.WORDARRAY, DataType.MATRIX} and sum(self.size) in (0, 1): + if self.datatype.isarray() and sum(self.size) in (0, 1): print("warning: {}: array/matrix with size 1, use normal byte/word instead for efficiency".format(self.sourceref)) if self.vartype == VarType.CONST and self.value is None: raise ParseError("constant value assignment is missing", @@ -439,7 +439,7 @@ class VarDef(AstNode): # if the value is an expression, mark it as a *constant* expression here if isinstance(self.value, AstNode): self.value.processed_expr_must_be_constant = True - elif self.value is None and self.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT): + elif self.value is None and self.datatype.isnumeric(): self.value = 0 # if it's a matrix with interleave, it must be memory mapped if self.datatype == DataType.MATRIX and len(self.size) == 3: @@ -688,9 +688,9 @@ def process_constant_expression(expr: Any, sourceref: SourceRef, symbolscope: Sc else: raise ExpressionEvaluationError("can only use math- or builtin function", expr.sourceref) elif isinstance(target, Dereference): # '[...](1,2,3)' - raise NotImplementedError("dereferenced call") # XXX + raise ExpressionEvaluationError("dereferenced value call is not a constant value", expr.sourceref) elif isinstance(target, int): # '64738()' - raise NotImplementedError("immediate address call") # XXX + raise ExpressionEvaluationError("immediate address call is not a constant value", expr.sourceref) else: raise NotImplementedError("weird call target", target) else: diff --git a/reference.md b/reference.md index c099b7cc9..1bdd5efab 100644 --- a/reference.md +++ b/reference.md @@ -247,13 +247,18 @@ logic and several utility routines that do I/O, such as ``print_string``. The ``%asm {`` and ``}`` start and end markers each have to be on their own unique line. -### Program Entry Point +### Program Start and Entry Point Every program has to have one entry point where code execution begins. The compiler looks for the ``start`` label in the ``main`` block for this. For proper program termination, this block has to end with a ``return`` statement (or a ``goto`` call). Blocks and other details are described below. +The initial values of your variables will be restored automatically when the program is (re)started, +*except for string variables*. It is assumed they are unchanged by your program. +If you do modify them in-place, you should take care yourself that they work as +expected when the program is restarted. + ### Blocks diff --git a/tests/test_core.py b/tests/test_core.py index 09bb623c9..f0dc912ae 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,6 +6,14 @@ from il65.plylex import SourceRef def test_datatypes(): assert all(isinstance(s, datatypes.DataType) for s in datatypes.STRING_DATATYPES) + assert all(s.isstring() for s in datatypes.STRING_DATATYPES) + assert not any(s.isarray() or s.isnumeric() for s in datatypes.STRING_DATATYPES) + assert datatypes.DataType.WORDARRAY.isarray() + assert not datatypes.DataType.WORDARRAY.isnumeric() + assert not datatypes.DataType.WORDARRAY.isstring() + assert not datatypes.DataType.WORD.isarray() + assert datatypes.DataType.WORD.isnumeric() + assert not datatypes.DataType.WORD.isstring() def test_sourceref(): diff --git a/testsource/calls.ill b/testsource/calls.ill index ff5002aa6..ab6cb811d 100644 --- a/testsource/calls.ill +++ b/testsource/calls.ill @@ -34,7 +34,6 @@ bar: goto $c000 ; ---- goto sub1 - ;goto sub2 (1 ) ; @todo error, must be return sub2(1) -> optimized in 'tail call' return sub2 ( ) return sub2 () return sub2 (1 ) @@ -47,13 +46,12 @@ bar: goto $c000 return sub4 (string="hello", other = 42) return bar () goto [AX] - ; goto [AX()] % ; @todo error, must be return() goto [var1] goto [mem1] ; comment goto $c000 goto 64738 - 64738(1,2) ; @todo jsr $64738 ?? - return 9999() ; @todo jsr 9999 ?? + 64738(1,2) ; @todo should be jsr $64738 + return 9999() ; @todo should be jmp 9999 ? return [AX]() return [var1] () ; comment return [mem1] () @@ -63,13 +61,12 @@ bar: goto $c000 return $c2() goto [$c2.word] return 33 - return [$c2.word] ; @todo this as rvalue - ; [ $c2.word (4) ] ;@ todo parse precedence + return [$c2.word] return [$c2.word] (4) return [$c2.word] (4) return [$c2.word] (4) return [$c2.word] (4) - ; return [$c2dd.word] ( ) ;@ todo parse precedence + return [$c2dd.word] ( ) goto [$c2dd.word] %asm { @@ -128,9 +125,8 @@ bar: goto $c000 [AX]() [var1] ( ) [mem1] () - A= [$c2.word(4)] - ;A= [$c2.word() ] ; @todo Precedence? - ;A= [$c2dd.word() ] ; @todo Precedence? + A= [$c2.word] + A= [$c2dd.word ] $c000() $c2() diff --git a/testsource/dtypes.ill b/testsource/dtypes.ill index 7dab077f6..5eca7fadf 100644 --- a/testsource/dtypes.ill +++ b/testsource/dtypes.ill @@ -78,7 +78,7 @@ var .stext stext = 'screencodes-null' var .pstext pstext = "screencodes-pascal" - var .matrix( 2, 400 ) uninitmatrix + var .matrix( 2, 128 ) uninitmatrix var .matrix(10, 20) initmatrix1 = $12 var .matrix(10, 20) initmatrix1b = true var .matrix(10, 20) initmatrix1c = '@' @@ -305,7 +305,7 @@ start: AY = "text-immediate" ; reuses existing AX = "another" AX = "" - ;*$c000.word = "another" ; reuse @ todo precedence? - ;*$c100.word = "text-immediate" ; reuse @ todo precedence? - ;*$c200.word = "" ; reuse @ todo precedence? + [$c000.word] = "another" ; must reuse string + [$c100.word] = "text-immediate" ; must reuse string + [$c200.word] = "" ; must reuse string } diff --git a/testsource/numbergame.ill b/testsource/numbergame.ill index 686c46753..b482486ac 100644 --- a/testsource/numbergame.ill +++ b/testsource/numbergame.ill @@ -13,8 +13,9 @@ start: c64.init_system() A = c64.VMCSB - A |= 2 ; @todo c64.VMCSB |= 2 + A |= 2 c64.VMCSB = A + c64.VMCSB |= 2 ; @todo when this works it replaces the three lines above ; greeting c64scr.print_string("Enter your name: ") @@ -58,7 +59,8 @@ ask_guess: [$22.word] = guess c64.FREADSTR(A) AY = c64flt.GETADRAY() - A -= secretnumber ; @todo condition so we can do if guess > secretnumber.... + A -= secretnumber ; @todo condition so we can do if guess > secretnumber.... # @todo "removed statement that has no effect" is WRONG!! + A -= secretnumber ; # @todo "removed statement that has no effect" is WRONG!! if_zero goto correct_guess if_gt goto too_high c64scr.print_string("That is too ") @@ -87,9 +89,12 @@ game_over: return sub goodbye ()->() { - ;var x ; @todo vars in sub - ;memory y = $c000 ; @todo vars in sub - ;const q = 22 ; @todo const in sub + var xxxxxx ; @todo vars in sub + memory y = $c000 ; @todo vars in sub + const q = 22 ; @todo const in sub + + xxxxxx = q *4 + xxxxxx = qqqqq *44 ;@todo symbol error c64scr.print_string("\nThanks for playing. Bye!\n") return diff --git a/todo.ill b/todo.ill index 6207da32e..e72223eb8 100644 --- a/todo.ill +++ b/todo.ill @@ -1,67 +1,47 @@ %output basic -%zp clobber,restore - -~ ZP { - var zp1_1 = 200 - var zp1_2 = 200 - var .word zp1_3 = $ff99 - var .word zp1_4 = $ee88 - const zpc1_1 = 55 - const .word zpc1_2 = 2333.54566 - const .float zpc1_3 = 6.54566 -} - -~ ZP { - var zp2_1 = 100 - var zp2_2 = 100 - var .word zp2_3 = $55aa - var .word zp2_4 = $66bb - var .word zp2_5 = $66bc - var .word zp2_6 = $66bd - var .word zp2_7 = $66be - var .word zp2_8 = $67be - var .word zp2_9 = $68be - var .word zp2_10 = $69be - var .word zp2_11 = $69be - var .array(4) array1 - var .wordarray(4) warray1 - var .matrix(3,3) matrix1 - var .text string = "bye" - const .text zpc2_1 = "hello" - const zpc2_2 = 0 -} +%import c64lib ~ main { - var .float float1 = 3.14 - var .float float2 = 3.14 - var .float float3 = 3.14 - var .float float4 = 3.14 - var .float float5 = 3.14 - var .array(10) array1 - var .wordarray(10) warray1 - var .matrix(4,4) matrix1 - var b1 = 10 - var b2 = 20 - var b3 = 10 - var b4 = 20 - var b5 = 10 - var b6 = 20 - var b7 = 10 - var b8 = 30 - var b9 = 30 - var b10 = 40 - var b11 = 40 - var b12 = 30 - var b13 = 40 - var b14 = 0 - var b15 = 0 - var b16 = 0 + var zp1_1 = 200 + var zp1_2 = 200 + var .text zp_s1 = "hello\n" + var .ptext zp_s2 = "goodbye\n" + var .stext zp_s3 = "welcome\n" + var .pstext zp_s4 = "go away\n" + const .text ctext = "constant\n" + var .array(20) arr1 = $ea + var .wordarray(20) arr2 = $ea start: + %asm { - return 1.22, 46 + lda zp1_1 + jsr c64scr.print_byte_decimal0 + inc zp1_1 + lda zp1_1 + jsr c64scr.print_byte_decimal0 + inc zp1_1 + lda zp1_1 + jsr c64scr.print_byte_decimal0 + inc zp1_1 + lda zp1_1 + jsr c64scr.print_byte_decimal0 + inc zp1_1 + + ;ldx #zp_s1 + ;jsr c64scr.print_string + ;ldx #zp_s2 + ;jsr c64scr.print_pstring + + ;ldx #ctext + ;jsr c64scr.print_string + } + return }