Merge pull request #14 from catseye/develop-0.17
Develop 0.17
Chris Pressey authored 4 years ago
GitHub committed 4 years ago
0 | 0 | History of SixtyPical |
1 | 1 | ===================== |
2 | ||
3 | 0.17 | |
4 | ---- | |
5 | ||
6 | * `save X, Y, Z { }` now allowed as a shortcut for nested `save`s. | |
7 | * If the name in a location expression isn't found in the symbol | |
8 | table, a forward reference will _always_ be generated; and forward | |
9 | references in _all_ operations will be resolved after parsing. | |
10 | * As a consequence, trying to call or goto a non-routine-typed symbol | |
11 | is now an analysis error, not a syntax error. | |
12 | * Deprecated `routine foo ...` syntax has been removed. | |
13 | * Split TODO off into own file. | |
14 | * `sixtypical` no longer writes the compiled binary to standard | |
15 | output. The `--output` command-line argument should be given | |
16 | to get a compiled binary; otherwise only analysis is run. | |
17 | * Internal cleanups, including a hierarchy of `Outputters`. | |
18 | * All tests pass when `sixtypical` is run under Python 3.5.2. | |
2 | 19 | |
3 | 20 | 0.16 |
4 | 21 | ---- |
0 | 0 | SixtyPical |
1 | 1 | ========== |
2 | 2 | |
3 | _Version 0.16. Work-in-progress, everything is subject to change._ | |
3 | _Version 0.17. Work-in-progress, everything is subject to change._ | |
4 | 4 | |
5 | **SixtyPical** is a 6502-like programming language with advanced | |
6 | static analysis. | |
5 | **SixtyPical** is a low-level programming language with advanced | |
6 | static analysis. Many of its primitive instructions resemble | |
7 | those of the 6502 CPU — in fact it is intended to be compiled to | |
8 | 6502 machine code — but along with these instructions are | |
9 | constructs which ease structuring and analyzing the code. The | |
10 | language aims to fill this niche: | |
7 | 11 | |
8 | "6502-like" means that it has similar restrictions as programming | |
9 | in 6502 assembly (e.g. the programmer must choose the registers that | |
10 | values will be stored in) and is concomitantly easy for a compiler to | |
11 | translate it to 6502 machine language code. | |
12 | * You'd use assembly, but you don't want to spend hours | |
13 | debugging (say) a memory overrun that happened because of a | |
14 | ridiculous silly error. | |
15 | * You'd use C or some other "high-level" language, but you don't | |
16 | want the extra overhead added by the compiler to manage the | |
17 | stack and registers. | |
12 | 18 | |
13 | "Advanced static analysis" includes _abstract interpretation_, where we | |
14 | go through the program step by step, tracking not just the changes that | |
15 | happen during a _specific_ execution of the program, but _sets_ of changes | |
16 | that could _possibly_ happen in any run of the program. This lets us | |
17 | determine that certain things can never happen, which we can then formulate | |
18 | as safety checks. | |
19 | ||
20 | In practice, this means it catches things like | |
19 | SixtyPical gives the programmer a coding regimen on par with assembly | |
20 | language in terms of size and hands-on-ness, but also able to catch | |
21 | many ridiculous silly errors at compile time, such as | |
21 | 22 | |
22 | 23 | * you forgot to clear carry before adding something to the accumulator |
23 | * a subroutine that you call trashes a register you thought was preserved | |
24 | * a subroutine that you called trashes a register you thought it preserved | |
24 | 25 | * you tried to read or write a byte beyond the end of a byte array |
25 | 26 | * you tried to write the address of something that was not a routine, to |
26 | 27 | a jump vector |
27 | 28 | |
28 | and suchlike. It also provides some convenient operations based on | |
29 | Many of these checks are done with _abstract interpretation_, where we | |
30 | go through the program step by step, tracking not just the changes that | |
31 | happen during a _specific_ execution of the program, but _sets_ of changes | |
32 | that could _possibly_ happen in any run of the program. | |
33 | ||
34 | SixtyPical also provides some convenient operations based on | |
29 | 35 | machine-language programming idioms, such as |
30 | 36 | |
31 | 37 | * copying values from one register to another (via a third register when |
34 | 40 | * explicit tail calls |
35 | 41 | * indirect subroutine calls |
36 | 42 | |
37 | The reference implementation can analyze and compile SixtyPical programs to | |
38 | 6502 machine code. | |
43 | SixtyPical is defined by a specification document, a set of test cases, | |
44 | and a reference implementation written in Python 2. The reference | |
45 | implementation can analyze and compile SixtyPical programs to 6502 machine | |
46 | code, which can be run on several 6502-based 8-bit architectures: | |
47 | ||
48 | * Commodore 64 | |
49 | * Commodore VIC-20 | |
50 | * Atari 2600 VCS | |
51 | * Apple II | |
39 | 52 | |
40 | 53 | Quick Start |
41 | 54 | ----------- |
67 | 80 | * [Literate test suite for SixtyPical fallthru optimization](tests/SixtyPical%20Fallthru.md) |
68 | 81 | * [6502 Opcodes used/not used in SixtyPical](doc/6502%20Opcodes.md) |
69 | 82 | * [Output formats supported by `sixtypical`](doc/Output%20Formats.md) |
70 | ||
71 | TODO | |
72 | ---- | |
73 | ||
74 | ### `low` and `high` address operators | |
75 | ||
76 | To turn `word` type into `byte`. | |
77 | ||
78 | Trying to remember if we have a compelling case for this or now. The best I can think | |
79 | of is for implementing 16-bit `cmp` in an efficient way. Maybe we should see if we | |
80 | can get by with 16-bit `cmp` instead though. | |
81 | ||
82 | The problem is that once a byte is extracted, putting it back into a word is awkward. | |
83 | The address operators have to modify a destination in a special way. That is, when | |
84 | you say `st a, >word`, you are updating `word` to be `word & $ff | a << 8`, somelike. | |
85 | Is that consistent with `st`? Well, probably it is, but we have to explain it. | |
86 | It might make more sense, then, for it to be "part of the operation" instead of "part of | |
87 | the reference"; something like `st.hi x, word`; `st.lo y, word`. Dunno. | |
88 | ||
89 | ### Save multiple values in single block | |
90 | ||
91 | As a shortcut for the idiom | |
92 | ||
93 | save a { save var { | |
94 | ... | |
95 | } } | |
96 | ||
97 | allow | |
98 | ||
99 | save a, var { | |
100 | ... | |
101 | } | |
102 | ||
103 | ### Save values to other-than-the-stack | |
104 | ||
105 | Allow | |
106 | ||
107 | save a to temp_a { | |
108 | ... | |
109 | } | |
110 | ||
111 | Which uses some other storage location instead of the stack. A local static | |
112 | would be a good candidate for such. | |
113 | ||
114 | ### Make all symbols forward-referencable | |
115 | ||
116 | Basically, don't do symbol-table lookups when parsing, but do have a more formal | |
117 | "symbol resolution" (linking) phase right after parsing. | |
118 | ||
119 | ### Associate each pointer with the buffer it points into | |
120 | ||
121 | Check that the buffer being read or written to through pointer, appears in appropriate | |
122 | inputs or outputs set. | |
123 | ||
124 | In the analysis, when we obtain a pointer, we need to record, in contect, what buffer | |
125 | that pointer came from. | |
126 | ||
127 | When we write through that pointer, we need to set that buffer as written. | |
128 | ||
129 | When we read through the pointer, we need to check that the buffer is readable. | |
130 | ||
131 | ### Table overlays | |
132 | ||
133 | They are uninitialized, but the twist is, the address is a buffer that is | |
134 | an input to and/or output of the routine. So, they are defined (insofar | |
135 | as the buffer is defined.) | |
136 | ||
137 | They are therefore a "view" of a section of a buffer. | |
138 | ||
139 | This is slightly dangerous since it does permit aliases: the buffer and the | |
140 | table refer to the same memory. | |
141 | ||
142 | Although, if they are `static`, you could say, in the routine in which they | |
143 | are `static`, as soon as you've established one, you can no longer use the | |
144 | buffer; and the ones you establish must be disjoint. | |
145 | ||
146 | (That seems to be the most compelling case for restricting them to `static`.) | |
147 | ||
148 | An alternative would be `static` pointers, which are currently not possible because | |
149 | pointers must be zero-page, thus `@`, thus uninitialized. | |
150 | ||
151 | ### Question "consistent initialization" | |
152 | ||
153 | Question the value of the "consistent initialization" principle for `if` statement analysis. | |
154 | ||
155 | Part of this is the trashes at the end; I think what it should be is that the trashes | |
156 | after the `if` is the union of the trashes in each of the branches; this would obviate the | |
157 | need to `trash` values explicitly, but if you tried to access them afterwards, it would still | |
158 | error. | |
159 | ||
160 | ### Tail-call optimization | |
161 | ||
162 | More generally, define a block as having zero or one `goto`s at the end. (and `goto`s cannot | |
163 | appear elsewhere.) | |
164 | ||
165 | If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can. | |
166 | The constraints should iron out the same both ways. | |
167 | ||
168 | And - once we have this - why do we need `goto` to be in tail position, strictly? | |
169 | As long as the routine has consistent type context every place it exits, that should be fine. | |
170 | ||
171 | ### "Include" directives | |
172 | ||
173 | Search a searchlist of include paths. And use them to make libraries of routines. | |
174 | ||
175 | One such library routine might be an `interrupt routine` type for various architectures. | |
176 | Since "the supervisor" has stored values on the stack, we should be able to trash them | |
177 | with impunity, in such a routine. | |
83 | * [TODO](TODO.md) |
0 | TODO for SixtyPical | |
1 | =================== | |
2 | ||
3 | ### 16-bit `cmp` | |
4 | ||
5 | This is because we don't actually want `low` and `high` address operators | |
6 | that turn `word` type into `byte`. | |
7 | ||
8 | This is because this immediately makes things harder (that is, effectively | |
9 | impossible) to analyze. | |
10 | ||
11 | 16-bit `cmp` also benefits from some special differences between `cmp` | |
12 | and `sub` on 6502, so it would be nice to capture them. | |
13 | ||
14 | ### Save values to other-than-the-stack | |
15 | ||
16 | Allow | |
17 | ||
18 | save a to temp_a { | |
19 | ... | |
20 | } | |
21 | ||
22 | Which uses some other storage location instead of the stack. A local static | |
23 | would be a good candidate for such. | |
24 | ||
25 | ### Associate each pointer with the buffer it points into | |
26 | ||
27 | Check that the buffer being read or written to through pointer, appears in appropriate | |
28 | inputs or outputs set. | |
29 | ||
30 | In the analysis, when we obtain a pointer, we need to record, in context, what buffer | |
31 | that pointer came from. | |
32 | ||
33 | When we write through that pointer, we need to set that buffer as written. | |
34 | ||
35 | When we read through the pointer, we need to check that the buffer is readable. | |
36 | ||
37 | ### Table overlays | |
38 | ||
39 | They are uninitialized, but the twist is, the address is a buffer that is | |
40 | an input to and/or output of the routine. So, they are defined (insofar | |
41 | as the buffer is defined.) | |
42 | ||
43 | They are therefore a "view" of a section of a buffer. | |
44 | ||
45 | This is slightly dangerous since it does permit aliases: the buffer and the | |
46 | table refer to the same memory. | |
47 | ||
48 | Although, if they are `static`, you could say, in the routine in which they | |
49 | are `static`, as soon as you've established one, you can no longer use the | |
50 | buffer; and the ones you establish must be disjoint. | |
51 | ||
52 | (That seems to be the most compelling case for restricting them to `static`.) | |
53 | ||
54 | An alternative would be `static` pointers, which are currently not possible because | |
55 | pointers must be zero-page, thus `@`, thus uninitialized. | |
56 | ||
57 | ### Question "consistent initialization" | |
58 | ||
59 | Question the value of the "consistent initialization" principle for `if` statement analysis. | |
60 | ||
61 | Part of this is the trashes at the end; I think what it should be is that the trashes | |
62 | after the `if` is the union of the trashes in each of the branches; this would obviate the | |
63 | need to `trash` values explicitly, but if you tried to access them afterwards, it would still | |
64 | error. | |
65 | ||
66 | ### Tail-call optimization | |
67 | ||
68 | More generally, define a block as having zero or one `goto`s at the end. (and `goto`s cannot | |
69 | appear elsewhere.) | |
70 | ||
71 | If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can, | |
72 | if the block is in tail position. The constraints should iron out the same both ways. | |
73 | ||
74 | And - once we have this - why do we need `goto` to be in tail position, strictly? | |
75 | As long as the routine has consistent type context every place it exits, that should be fine. | |
76 | ||
77 | ### "Include" directives | |
78 | ||
79 | Search a searchlist of include paths. And use them to make libraries of routines. | |
80 | ||
81 | One such library routine might be an `interrupt routine` type for various architectures. | |
82 | Since "the supervisor" has stored values on the stack, we should be able to trash them | |
83 | with impunity, in such a routine. | |
84 | ||
85 | ### Line numbers in analysis error messages | |
86 | ||
87 | For analysis errors, there is a line number, but it's the line of the routine | |
88 | after the routine in which the analysis error occurred. Fix this. |
17 | 17 | import sys |
18 | 18 | import traceback |
19 | 19 | |
20 | from sixtypical.parser import Parser, ParsingContext | |
20 | from sixtypical.parser import Parser, ParsingContext, merge_programs | |
21 | 21 | from sixtypical.analyzer import Analyzer |
22 | from sixtypical.emitter import Emitter, Byte, Word | |
22 | from sixtypical.outputter import outputter_class_for | |
23 | 23 | from sixtypical.compiler import Compiler |
24 | ||
25 | ||
26 | def merge_programs(programs): | |
27 | """Assumes that the programs do not have any conflicts.""" | |
28 | ||
29 | from sixtypical.ast import Program | |
30 | ||
31 | full = Program(1, defns=[], routines=[]) | |
32 | for p in programs: | |
33 | full.defns.extend(p.defns) | |
34 | full.routines.extend(p.routines) | |
35 | ||
36 | return full | |
37 | 24 | |
38 | 25 | |
39 | 26 | def process_input_files(filenames, options): |
67 | 54 | return |
68 | 55 | if label: |
69 | 56 | sys.stdout.write("*** {}:\n".format(label)) |
70 | sys.stdout.write(json.dumps(data, indent=4, sort_keys=True)) | |
57 | sys.stdout.write(json.dumps(data, indent=4, sort_keys=True, separators=(',', ':'))) | |
71 | 58 | sys.stdout.write("\n") |
72 | 59 | |
73 | 60 | fa = FallthruAnalyzer(debug=options.debug) |
75 | 62 | compilation_roster = fa.serialize() |
76 | 63 | dump(compilation_roster) |
77 | 64 | |
78 | if options.analyze_only: | |
65 | if options.analyze_only or options.output is None: | |
79 | 66 | return |
80 | 67 | |
81 | fh = sys.stdout | |
82 | ||
83 | if options.output_format == 'raw': | |
84 | start_addr = 0x0000 | |
85 | prelude = [] | |
86 | elif options.output_format == 'prg': | |
87 | start_addr = 0xc000 | |
88 | prelude = [] | |
89 | elif options.output_format == 'c64-basic-prg': | |
90 | start_addr = 0x0801 | |
91 | prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32, | |
92 | 0x30, 0x36, 0x31, 0x00, 0x00, 0x00] | |
93 | elif options.output_format == 'vic20-basic-prg': | |
94 | start_addr = 0x1001 | |
95 | prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34, | |
96 | 0x31, 0x30, 0x39, 0x00, 0x00, 0x00] | |
97 | elif options.output_format == 'atari2600-cart': | |
98 | start_addr = 0xf000 | |
99 | prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9, | |
100 | 0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb] | |
101 | else: | |
102 | raise NotImplementedError("Unknown output format: {}".format(options.output_format)) | |
103 | ||
68 | start_addr = None | |
104 | 69 | if options.origin is not None: |
105 | 70 | if options.origin.startswith('0x'): |
106 | 71 | start_addr = int(options.origin, 16) |
107 | 72 | else: |
108 | 73 | start_addr = int(options.origin, 10) |
109 | 74 | |
110 | # If we are outputting a .PRG, we output the load address first. | |
111 | # We don't use the Emitter for this b/c not part of addr space. | |
112 | if options.output_format in ('prg', 'c64-basic-prg', 'vic20-basic-prg'): | |
113 | fh.write(Word(start_addr).serialize(0)) | |
114 | ||
115 | emitter = Emitter(start_addr) | |
116 | for byte in prelude: | |
117 | emitter.emit(Byte(byte)) | |
118 | compiler = Compiler(emitter) | |
119 | compiler.compile_program(program, compilation_roster=compilation_roster) | |
120 | ||
121 | # If we are outputting a cartridge with boot and BRK address | |
122 | # at the end, pad to ROM size minus 4 bytes, and emit addresses. | |
123 | if options.output_format == 'atari2600-cart': | |
124 | emitter.pad_to_size(4096 - 4) | |
125 | emitter.emit(Word(start_addr)) | |
126 | emitter.emit(Word(start_addr)) | |
127 | ||
128 | if options.debug: | |
129 | pprint(emitter.accum) | |
130 | else: | |
131 | emitter.serialize(fh) | |
75 | with open(options.output, 'wb') as fh: | |
76 | outputter = outputter_class_for(options.output_format)(fh, start_addr=start_addr) | |
77 | outputter.write_prelude() | |
78 | compiler = Compiler(outputter.emitter) | |
79 | compiler.compile_program(program, compilation_roster=compilation_roster) | |
80 | outputter.write_postlude() | |
81 | if options.debug: | |
82 | pprint(outputter.emitter) | |
83 | else: | |
84 | outputter.emitter.serialize_to(fh) | |
132 | 85 | |
133 | 86 | |
134 | 87 | if __name__ == '__main__': |
139 | 92 | help="The SixtyPical source files to compile." |
140 | 93 | ) |
141 | 94 | |
95 | argparser.add_argument( | |
96 | "--output", "-o", type=str, metavar='FILENAME', | |
97 | help="File to which generated 6502 code will be written." | |
98 | ) | |
142 | 99 | argparser.add_argument( |
143 | 100 | "--origin", type=str, default=None, |
144 | 101 | help="Location in memory where the `main` routine will be " |
7 | 7 | The file contains only the emitted bytes of the compiled SixtyPical |
8 | 8 | program. |
9 | 9 | |
10 | The default origin is $0000; it is not unlikely you will want to | |
11 | override this. | |
10 | The default origin is $0000; you will likely want to override this. | |
12 | 11 | |
13 | 12 | Note that the origin is not stored in the output file in this format; |
14 | 13 | that information must be recorded separately. |
19 | 18 | little-endian format. The remainder of the file is the emitted bytes |
20 | 19 | of the compiled SixtyPical program, starting at that origin. |
21 | 20 | |
22 | The default origin is $C000; it is likely you will want to | |
23 | override this. | |
21 | The default origin is $C000; you will likely want override this. | |
24 | 22 | |
25 | 23 | This format coincides with Commodore's PRG format for disk files, |
26 | 24 | thus its name. |
0 | byte ds_graphics @ $C050 | |
1 | byte ds_text @ $C051 | |
2 | byte ds_full @ $C052 | |
3 | byte ds_split @ $C053 | |
4 | byte ds_page1 @ $C054 | |
5 | byte ds_page2 @ $C055 | |
6 | byte ds_lores @ $C056 | |
7 | byte ds_hires @ $C057 | |
8 | ||
9 | define main routine | |
10 | inputs a | |
11 | outputs ds_lores, ds_page1, ds_split, ds_graphics | |
12 | trashes a, z, n | |
13 | { | |
14 | ld a, 0 | |
15 | st a, ds_lores | |
16 | st a, ds_page1 | |
17 | st a, ds_split | |
18 | st a, ds_graphics | |
19 | } |
0 | // Write ">AB>" to "standard output" | |
1 | ||
2 | define cout routine | |
3 | inputs a | |
4 | trashes a | |
5 | @ $FDED | |
6 | ||
7 | define main routine | |
8 | trashes a, z, n | |
9 | { | |
10 | ld a, 62 | |
11 | call cout | |
12 | ld a, 65 | |
13 | call cout | |
14 | ld a, 66 | |
15 | call cout | |
16 | ld a, 62 | |
17 | call cout | |
18 | } |
32 | 32 | typedef routine |
33 | 33 | inputs joy2, press_fire_msg, dispatch_game_state, |
34 | 34 | actor_pos, actor_delta, actor_logic, |
35 | player_died, | |
35 | 36 | screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 |
36 | 37 | outputs dispatch_game_state, |
37 | 38 | actor_pos, actor_delta, actor_logic, |
39 | player_died, | |
38 | 40 | screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 |
39 | 41 | trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic |
40 | 42 | game_state_routine |
44 | 46 | // |
45 | 47 | // Routines that conform to this type also follow this convention: |
46 | 48 | // |
47 | // Set carry if the player perished. Carry clear otherwise. | |
49 | // Set player_died to 1 if the player perished. Unchanged otherwise. | |
48 | 50 | // |
49 | 51 | |
50 | 52 | typedef routine |
51 | inputs pos, delta, joy2, screen | |
52 | outputs pos, delta, new_pos, screen, c | |
53 | trashes a, x, y, z, n, v, ptr | |
53 | inputs pos, delta, joy2, screen, player_died | |
54 | outputs pos, delta, new_pos, screen, player_died | |
55 | trashes a, x, y, z, n, v, c, ptr | |
54 | 56 | logic_routine |
55 | 57 | |
56 | 58 | // ---------------------------------------------------------------- |
86 | 88 | word table[256] actor_delta |
87 | 89 | word delta |
88 | 90 | |
91 | byte player_died | |
92 | ||
89 | 93 | vector logic_routine table[256] actor_logic |
90 | 94 | vector logic_routine dispatch_logic |
91 | 95 | |
116 | 120 | // Utility Routines |
117 | 121 | // ---------------------------------------------------------------- |
118 | 122 | |
119 | routine read_stick | |
123 | define read_stick routine | |
120 | 124 | inputs joy2 |
121 | 125 | outputs delta |
122 | 126 | trashes a, x, z, n |
181 | 185 | } |
182 | 186 | } |
183 | 187 | |
184 | routine clear_screen | |
188 | define clear_screen routine | |
185 | 189 | outputs screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 |
186 | 190 | trashes a, y, c, n, z |
187 | 191 | { |
204 | 208 | } until z |
205 | 209 | } |
206 | 210 | |
207 | routine calculate_new_position | |
211 | define calculate_new_position routine | |
208 | 212 | inputs pos, delta |
209 | 213 | outputs new_pos |
210 | 214 | trashes a, c, n, z, v |
238 | 242 | } |
239 | 243 | } |
240 | 244 | |
241 | routine init_game | |
245 | define init_game routine | |
242 | 246 | inputs actor_pos, actor_delta, actor_logic |
243 | outputs actor_pos, actor_delta, actor_logic | |
247 | outputs actor_pos, actor_delta, actor_logic, player_died | |
244 | 248 | trashes pos, a, y, z, n, c, v |
245 | 249 | { |
246 | 250 | ld y, 0 |
258 | 262 | } until z |
259 | 263 | |
260 | 264 | ld y, 0 |
261 | copy word 0, actor_pos + y | |
265 | copy word 40, actor_pos + y | |
262 | 266 | copy word 0, actor_delta + y |
263 | 267 | copy player_logic, actor_logic + y |
268 | ||
269 | st y, player_died | |
264 | 270 | } |
265 | 271 | |
266 | 272 | // ---------------------------------------------------------------- |
300 | 306 | st off, c |
301 | 307 | add ptr, pos |
302 | 308 | copy 81, [ptr] + y |
303 | ||
304 | st off, c | |
305 | 309 | } else { |
306 | st on, c | |
310 | ld a, 1 | |
311 | st a, player_died | |
307 | 312 | } |
308 | 313 | |
309 | 314 | // FIXME these trashes, strictly speaking, probably shouldn't be needed, |
313 | 318 | trash ptr |
314 | 319 | trash y |
315 | 320 | trash v |
316 | } else { | |
317 | st off, c | |
318 | 321 | } |
319 | 322 | } |
320 | 323 | |
350 | 353 | st off, c |
351 | 354 | add ptr, pos |
352 | 355 | copy 82, [ptr] + y |
353 | ||
354 | st off, c | |
355 | } else { | |
356 | st on, c | |
357 | 356 | } |
358 | 357 | |
359 | 358 | // FIXME these trashes, strictly speaking, probably shouldn't be needed, |
372 | 371 | copy $ffd8, delta |
373 | 372 | } |
374 | 373 | } |
375 | ||
376 | st off, c | |
377 | 374 | } |
378 | 375 | |
379 | 376 | // ---------------------------------------------------------------- |
407 | 404 | define game_state_play game_state_routine |
408 | 405 | { |
409 | 406 | ld x, 0 |
407 | st x, player_died | |
410 | 408 | for x up to 15 { |
411 | 409 | copy actor_pos + x, pos |
412 | 410 | copy actor_delta + x, delta |
419 | 417 | save x { |
420 | 418 | copy actor_logic + x, dispatch_logic |
421 | 419 | call dispatch_logic |
422 | ||
423 | if c { | |
424 | // Player died! Want no dead! | |
425 | call clear_screen | |
426 | copy game_state_game_over, dispatch_game_state | |
427 | } | |
428 | 420 | } |
429 | 421 | |
430 | 422 | copy pos, actor_pos + x |
431 | 423 | copy delta, actor_delta + x |
424 | } | |
425 | ||
426 | ld a, player_died | |
427 | if not z { | |
428 | // Player died! Want no dead! | |
429 | call clear_screen | |
430 | copy game_state_game_over, dispatch_game_state | |
432 | 431 | } |
433 | 432 | |
434 | 433 | goto save_cinv |
457 | 456 | goto dispatch_game_state |
458 | 457 | } |
459 | 458 | |
460 | routine main | |
459 | define main routine | |
461 | 460 | inputs cinv |
462 | 461 | outputs cinv, save_cinv, pos, dispatch_game_state, |
463 | 462 | screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 |
2 | 2 | // Define where the screen starts in memory: |
3 | 3 | byte table[256] screen @ 1024 |
4 | 4 | |
5 | routine main | |
5 | define main routine | |
6 | 6 | // These are the values that will be written to by this routine: |
7 | 7 | trashes a, x, z, n, screen |
8 | 8 | { |
23 | 23 | trashes z, n |
24 | 24 | save_cinv |
25 | 25 | |
26 | routine our_cinv | |
26 | define our_cinv routine | |
27 | 27 | inputs vic_border |
28 | 28 | outputs vic_border |
29 | 29 | trashes z, n |
32 | 32 | goto save_cinv |
33 | 33 | } |
34 | 34 | |
35 | routine main | |
35 | define main routine | |
36 | 36 | inputs cinv |
37 | 37 | outputs cinv, save_cinv |
38 | 38 | trashes a, n, z |
2 | 2 | |
3 | 3 | word delta |
4 | 4 | |
5 | routine read_stick | |
5 | define read_stick routine | |
6 | 6 | inputs joy2 |
7 | 7 | outputs delta |
8 | 8 | trashes a, x, z, n |
35 | 35 | } |
36 | 36 | } |
37 | 37 | |
38 | routine main | |
38 | define main routine | |
39 | 39 | inputs joy2 |
40 | 40 | outputs delta |
41 | 41 | trashes a, x, z, n, screen |
48 | 48 | // generating them as part of a SixtyPical program would not |
49 | 49 | // be practical. So we just jump to this location instead. |
50 | 50 | |
51 | routine pla_tay_pla_tax_pla_rti | |
51 | define pla_tay_pla_tax_pla_rti routine | |
52 | 52 | inputs a |
53 | 53 | trashes a |
54 | 54 | @ $EA81 |
0 | 0 | byte screen @ 1024 |
1 | 1 | |
2 | routine main | |
2 | define main routine | |
3 | 3 | trashes a, z, n, screen |
4 | 4 | { |
5 | 5 | ld a, 83 |
0 | 0 | word score |
1 | routine main | |
1 | define main routine | |
2 | 2 | inputs score |
3 | 3 | outputs score |
4 | 4 | trashes a, c, z, v, n |
1 | 1 | pointer ptr @ 254 |
2 | 2 | byte foo |
3 | 3 | |
4 | routine main | |
4 | define main routine | |
5 | 5 | inputs buf |
6 | 6 | outputs buf, y, foo |
7 | 7 | trashes a, z, n, ptr |
0 | routine chrout | |
0 | define chrout routine | |
1 | 1 | inputs a |
2 | 2 | trashes a |
3 | 3 | @ 65490 |
4 | 4 | |
5 | routine print | |
5 | define print routine | |
6 | 6 | trashes a, z, n |
7 | 7 | { |
8 | 8 | ld a, 65 |
9 | 9 | call chrout |
10 | 10 | } |
11 | 11 | |
12 | routine main | |
12 | define main routine | |
13 | 13 | trashes a, z, n |
14 | 14 | { |
15 | 15 | call print |
0 | routine chrout | |
0 | define chrout routine | |
1 | 1 | inputs a |
2 | 2 | trashes a |
3 | 3 | @ 65490 |
4 | 4 | |
5 | routine main | |
5 | define main routine | |
6 | 6 | trashes a, x, y, z, n, c, v |
7 | 7 | { |
8 | 8 | ld a, 0 |
0 | routine chrout | |
0 | define chrout routine | |
1 | 1 | inputs a |
2 | 2 | trashes a |
3 | 3 | @ 65490 |
4 | 4 | |
5 | routine main | |
5 | define main routine | |
6 | 6 | trashes a, x, y, z, n, c, v |
7 | 7 | { |
8 | 8 | ld a, 0 |
0 | 0 | byte bar |
1 | 1 | byte baz |
2 | 2 | |
3 | routine main | |
3 | define main routine | |
4 | 4 | inputs baz |
5 | 5 | outputs bar |
6 | 6 | trashes a, n, z |
0 | 0 | byte lives |
1 | 1 | |
2 | routine main | |
2 | define main routine | |
3 | 3 | inputs lives |
4 | 4 | outputs lives |
5 | trashes a, x | |
5 | trashes a, x, z, n, c, v | |
6 | 6 | { |
7 | 7 | ld a, 0 |
8 | 8 | st a, lives |
0 | routine chrout | |
0 | define chrout routine | |
1 | 1 | inputs a |
2 | 2 | trashes a |
3 | 3 | @ 65490 |
4 | 4 | |
5 | routine bar trashes a, z, n { | |
5 | define bar routine trashes a, z, n { | |
6 | 6 | ld a, 66 |
7 | 7 | call chrout |
8 | 8 | } |
9 | 9 | |
10 | routine main trashes a, z, n { | |
10 | define main routine trashes a, z, n { | |
11 | 11 | ld a, 65 |
12 | 12 | call chrout |
13 | 13 | goto bar |
0 | routine foo | |
0 | define main routine | |
1 | 1 | inputs a |
2 | 2 | outputs a |
3 | trashes z, n, c | |
3 | 4 | { |
4 | 5 | cmp a, 42 |
5 | 6 | if z { |
0 | routine chrout | |
0 | define chrout routine | |
1 | 1 | inputs a |
2 | 2 | trashes a |
3 | 3 | @ 65490 |
4 | 4 | |
5 | routine main | |
5 | define main routine | |
6 | 6 | trashes a, y, z, n, c |
7 | 7 | { |
8 | 8 | ld y, 65 |
0 | 0 | byte foo |
1 | 1 | |
2 | routine chrout | |
2 | define chrout routine | |
3 | 3 | inputs a |
4 | 4 | trashes a |
5 | 5 | @ 65490 |
6 | 6 | |
7 | routine print | |
7 | define print routine | |
8 | 8 | inputs foo |
9 | 9 | trashes a, z, n |
10 | 10 | { |
12 | 12 | call chrout |
13 | 13 | } |
14 | 14 | |
15 | routine main | |
15 | define main routine | |
16 | 16 | trashes a, y, z, n, foo |
17 | 17 | { |
18 | 18 | ld y, 65 |
0 | routine chrout | |
0 | define chrout routine | |
1 | 1 | inputs a |
2 | 2 | trashes a |
3 | 3 | @ 65490 |
4 | 4 | |
5 | routine main | |
5 | define main routine | |
6 | 6 | inputs a |
7 | 7 | trashes a, z, n |
8 | 8 | { |
0 | 0 | byte table[8] message : "WHAT?" |
1 | 1 | |
2 | routine main | |
2 | define main routine | |
3 | 3 | inputs message |
4 | 4 | outputs x, a, z, n |
5 | 5 | { |
0 | 0 | // This will not compile on its own, because there is no `main`. |
1 | 1 | // But this and `vector-main.60p` together will compile. |
2 | 2 | |
3 | routine chrout | |
3 | define chrout routine | |
4 | 4 | inputs a |
5 | 5 | trashes a |
6 | 6 | @ 65490 |
7 | 7 | |
8 | routine printa | |
8 | define printa routine | |
9 | 9 | trashes a, z, n |
10 | 10 | { |
11 | 11 | ld a, 65 |
12 | 12 | call chrout |
13 | 13 | } |
14 | 14 | |
15 | routine printb | |
15 | define printb routine | |
16 | 16 | trashes a, z, n |
17 | 17 | { |
18 | 18 | ld a, 66 |
11 | 11 | // call chrout |
12 | 12 | // } |
13 | 13 | |
14 | routine main | |
14 | define main routine | |
15 | 15 | trashes print, a, z, n |
16 | 16 | { |
17 | 17 | copy printa, print |
10 | 10 | trashes a, z, n) |
11 | 11 | table[32] vectors |
12 | 12 | |
13 | routine chrout | |
13 | define chrout routine | |
14 | 14 | inputs a |
15 | 15 | trashes a |
16 | 16 | @ 65490 |
17 | 17 | |
18 | routine printa | |
18 | define printa routine | |
19 | 19 | trashes a, z, n |
20 | 20 | { |
21 | 21 | ld a, 65 |
22 | 22 | call chrout |
23 | 23 | } |
24 | 24 | |
25 | routine printb | |
25 | define printb routine | |
26 | 26 | trashes a, z, n |
27 | 27 | { |
28 | 28 | ld a, 66 |
29 | 29 | call chrout |
30 | 30 | } |
31 | 31 | |
32 | routine main | |
32 | define main routine | |
33 | 33 | inputs vectors |
34 | 34 | outputs vectors |
35 | 35 | trashes print, a, x, z, n, c |
1 | 1 | trashes a, z, n |
2 | 2 | |
3 | 3 | |
4 | routine chrout | |
4 | define chrout routine | |
5 | 5 | inputs a |
6 | 6 | trashes a |
7 | 7 | @ 65490 |
8 | 8 | |
9 | routine printa | |
9 | define printa routine | |
10 | 10 | trashes a, z, n |
11 | 11 | { |
12 | 12 | ld a, 65 |
13 | 13 | call chrout |
14 | 14 | } |
15 | 15 | |
16 | routine printb | |
16 | define printb routine | |
17 | 17 | trashes a, z, n |
18 | 18 | { |
19 | 19 | ld a, 66 |
20 | 20 | call chrout |
21 | 21 | } |
22 | 22 | |
23 | routine main | |
23 | define main routine | |
24 | 24 | trashes print, a, z, n |
25 | 25 | { |
26 | 26 | copy printa, print |
0 | 0 | word one |
1 | 1 | word table[256] many |
2 | 2 | |
3 | routine main | |
3 | define main routine | |
4 | 4 | inputs one, many |
5 | 5 | outputs one, many |
6 | 6 | trashes a, x, y, n, z |
2 | 2 | // Define where the screen starts in memory: |
3 | 3 | byte table[256] screen @ 7680 |
4 | 4 | |
5 | routine main | |
5 | define main routine | |
6 | 6 | // These are the values that will be written to by this routine: |
7 | 7 | trashes a, x, z, n, screen |
8 | 8 | { |
0 | 0 | #!/bin/sh |
1 | 1 | |
2 | usage="Usage: loadngo.sh (c64|vic20|atari2600) [--dry-run] <source.60p>" | |
2 | usage="Usage: loadngo.sh (c64|vic20|atari2600|apple2) [--dry-run] <source.60p>" | |
3 | 3 | |
4 | 4 | arch="$1" |
5 | 5 | shift 1 |
20 | 20 | elif [ "X$arch" = "Xatari2600" ]; then |
21 | 21 | output_format='atari2600-cart' |
22 | 22 | emu='stella' |
23 | elif [ "X$arch" = "Xapple2" ]; then | |
24 | src="$1" | |
25 | out=/tmp/a-out.bin | |
26 | bin/sixtypical --traceback --origin=0x2000 --output-format=raw $src --output $out || exit 1 | |
27 | ls -la $out | |
28 | cp ~/scratchpad/linapple/res/Master.dsk sixtypical.dsk | |
29 | # TODO: replace HELLO with something that does like | |
30 | # BLOAD "PROG" | |
31 | # CALL 8192 | |
32 | # (not BRUN because it does not always return to BASIC afterwards not sure why) | |
33 | a2rm sixtypical.dsk PROG | |
34 | a2in B sixtypical.dsk PROG $out | |
35 | linapple -d1 sixtypical.dsk -autoboot | |
36 | rm -f $out sixtypical.dsk | |
37 | exit 0 | |
23 | 38 | else |
24 | 39 | echo $usage && exit 1 |
25 | 40 | fi |
37 | 52 | ### do it ### |
38 | 53 | |
39 | 54 | out=/tmp/a-out.prg |
40 | bin/sixtypical --traceback --output-format=$output_format $src > $out || exit 1 | |
55 | bin/sixtypical --traceback --output-format=$output_format $src --output $out || exit 1 | |
41 | 56 | ls -la $out |
42 | 57 | $emu $out |
43 | 58 | rm -f $out |
0 | 0 | # encoding: UTF-8 |
1 | 1 | |
2 | from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save | |
2 | from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save | |
3 | 3 | from sixtypical.model import ( |
4 | 4 | TYPE_BYTE, TYPE_WORD, |
5 | 5 | TableType, BufferType, PointerType, VectorType, RoutineType, |
371 | 371 | context = Context(self.routines, routine, type_.inputs, type_.outputs, type_.trashes) |
372 | 372 | |
373 | 373 | if self.debug: |
374 | print "at start of routine `{}`:".format(routine.name) | |
375 | print context | |
374 | print("at start of routine `{}`:".format(routine.name)) | |
375 | print(context) | |
376 | 376 | |
377 | 377 | self.analyze_block(routine.block, context) |
378 | 378 | trashed = set(context.each_touched()) - set(context.each_meaningful()) |
379 | 379 | |
380 | 380 | if self.debug: |
381 | print "at end of routine `{}`:".format(routine.name) | |
382 | print context | |
383 | print "trashed: ", LocationRef.format_set(trashed) | |
384 | print "outputs: ", LocationRef.format_set(type_.outputs) | |
381 | print("at end of routine `{}`:".format(routine.name)) | |
382 | print(context) | |
383 | print("trashed: ", LocationRef.format_set(trashed)) | |
384 | print("outputs: ", LocationRef.format_set(type_.outputs)) | |
385 | 385 | trashed_outputs = type_.outputs & trashed |
386 | 386 | if trashed_outputs: |
387 | print "TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs) | |
388 | print '' | |
389 | print '-' * 79 | |
390 | print '' | |
387 | print("TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs)) | |
388 | print('') | |
389 | print('-' * 79) | |
390 | print('') | |
391 | 391 | |
392 | 392 | # even if we goto another routine, we can't trash an output. |
393 | 393 | for ref in trashed: |
549 | 549 | context.invalidate_range(dest) |
550 | 550 | elif opcode == 'call': |
551 | 551 | type = instr.location.type |
552 | if not isinstance(type, (RoutineType, VectorType)): | |
553 | raise TypeMismatchError(instr, instr.location) | |
552 | 554 | if isinstance(type, VectorType): |
553 | 555 | type = type.of_type |
554 | 556 | for ref in type.inputs: |
770 | 772 | context.set_writeable(instr.dest) |
771 | 773 | |
772 | 774 | def analyze_save(self, instr, context): |
773 | if len(instr.locations) != 1: | |
774 | raise NotImplementedError("Only 1 location in save is supported right now") | |
775 | location = instr.locations[0] | |
776 | self.assert_type(TYPE_BYTE, location) | |
777 | ||
778 | baton = context.extract(location) | |
775 | batons = [] | |
776 | for location in instr.locations: | |
777 | self.assert_type(TYPE_BYTE, location) | |
778 | baton = context.extract(location) | |
779 | batons.append(baton) | |
780 | ||
779 | 781 | self.analyze_block(instr.block, context) |
780 | 782 | if context.encountered_gotos(): |
781 | 783 | raise IllegalJumpError(instr, instr) |
782 | context.re_introduce(baton) | |
783 | ||
784 | ||
785 | for location in reversed(instr.locations): | |
786 | baton = batons.pop() | |
787 | context.re_introduce(baton) | |
788 | ||
789 | # We do this check outside the loop, because A is only preserved | |
790 | # if it is the outermost thing being `save`d. | |
784 | 791 | if location == REG_A: |
785 | 792 | pass |
786 | 793 | else: |
36 | 36 | def all_children(self): |
37 | 37 | for attr in self.children_attrs: |
38 | 38 | for child in self.attrs[attr]: |
39 | if child is not None: | |
40 | yield child | |
41 | for subchild in child.all_children(): | |
42 | yield subchild | |
43 | for attr in self.child_attrs: | |
44 | child = self.attrs[attr] | |
45 | if child is not None: | |
39 | 46 | yield child |
40 | 47 | for subchild in child.all_children(): |
41 | 48 | yield subchild |
42 | for attr in self.child_attrs: | |
43 | child = self.attrs[attr] | |
44 | yield child | |
45 | for subchild in child.all_children(): | |
46 | yield subchild | |
47 | 49 | |
48 | 50 | |
49 | 51 | class Program(AST): |
0 | 0 | # encoding: UTF-8 |
1 | 1 | |
2 | from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save | |
2 | from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save | |
3 | 3 | from sixtypical.model import ( |
4 | 4 | ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef, |
5 | 5 | TYPE_BIT, TYPE_BYTE, TYPE_WORD, |
111 | 111 | routine_name = roster_row[-1] |
112 | 112 | self.compile_routine(self.routines[routine_name]) |
113 | 113 | |
114 | for location, label in self.trampolines.iteritems(): | |
114 | for location, label in self.trampolines.items(): | |
115 | 115 | self.emitter.resolve_label(label) |
116 | 116 | self.emitter.emit(JMP(Indirect(self.get_label(location.name)))) |
117 | 117 | self.emitter.emit(RTS()) |
617 | 617 | self.emitter.emit(CLI()) |
618 | 618 | |
619 | 619 | def compile_save(self, instr): |
620 | location = instr.locations[0] | |
621 | if location == REG_A: | |
622 | self.emitter.emit(PHA()) | |
623 | self.compile_block(instr.block) | |
624 | self.emitter.emit(PLA()) | |
625 | elif location == REG_X: | |
626 | self.emitter.emit(TXA()) | |
627 | self.emitter.emit(PHA()) | |
628 | self.compile_block(instr.block) | |
629 | self.emitter.emit(PLA()) | |
630 | self.emitter.emit(TAX()) | |
631 | elif location == REG_Y: | |
632 | self.emitter.emit(TYA()) | |
633 | self.emitter.emit(PHA()) | |
634 | self.compile_block(instr.block) | |
635 | self.emitter.emit(PLA()) | |
636 | self.emitter.emit(TAY()) | |
637 | else: | |
638 | src_label = self.get_label(location.name) | |
639 | self.emitter.emit(LDA(Absolute(src_label))) | |
640 | self.emitter.emit(PHA()) | |
641 | self.compile_block(instr.block) | |
642 | self.emitter.emit(PLA()) | |
643 | self.emitter.emit(STA(Absolute(src_label))) | |
620 | for location in instr.locations: | |
621 | if location == REG_A: | |
622 | self.emitter.emit(PHA()) | |
623 | elif location == REG_X: | |
624 | self.emitter.emit(TXA()) | |
625 | self.emitter.emit(PHA()) | |
626 | elif location == REG_Y: | |
627 | self.emitter.emit(TYA()) | |
628 | self.emitter.emit(PHA()) | |
629 | else: | |
630 | src_label = self.get_label(location.name) | |
631 | self.emitter.emit(LDA(Absolute(src_label))) | |
632 | self.emitter.emit(PHA()) | |
633 | ||
634 | self.compile_block(instr.block) | |
635 | ||
636 | for location in reversed(instr.locations): | |
637 | if location == REG_A: | |
638 | self.emitter.emit(PLA()) | |
639 | elif location == REG_X: | |
640 | self.emitter.emit(PLA()) | |
641 | self.emitter.emit(TAX()) | |
642 | elif location == REG_Y: | |
643 | self.emitter.emit(PLA()) | |
644 | self.emitter.emit(TAY()) | |
645 | else: | |
646 | src_label = self.get_label(location.name) | |
647 | self.emitter.emit(PLA()) | |
648 | self.emitter.emit(STA(Absolute(src_label))) |
7 | 7 | raise NotImplementedError |
8 | 8 | |
9 | 9 | def serialize(self, addr): |
10 | """Should return an array of unsigned bytes (integers from 0 to 255.) | |
11 | `addr` is the address the value is being serialized at; for most objects | |
12 | it makes no difference, but some objects (like relative branches) do care.""" | |
10 | 13 | raise NotImplementedError |
11 | 14 | |
12 | 15 | |
13 | 16 | class Byte(Emittable): |
14 | 17 | def __init__(self, value): |
15 | if isinstance(value, basestring): | |
18 | if isinstance(value, str): | |
16 | 19 | value = ord(value) |
17 | 20 | if value < -127 or value > 255: |
18 | 21 | raise IndexError(value) |
23 | 26 | def size(self): |
24 | 27 | return 1 |
25 | 28 | |
26 | def serialize(self, addr=None): | |
27 | return chr(self.value) | |
29 | def serialize(self, addr): | |
30 | return [self.value] | |
28 | 31 | |
29 | 32 | def __repr__(self): |
30 | 33 | return "%s(%r)" % (self.__class__.__name__, self.value) |
38 | 41 | def size(self): |
39 | 42 | return 2 |
40 | 43 | |
41 | def serialize(self, addr=None): | |
44 | def serialize(self, addr): | |
42 | 45 | word = self.value |
43 | 46 | low = word & 255 |
44 | 47 | high = (word >> 8) & 255 |
45 | return chr(low) + chr(high) | |
48 | return [low, high] | |
46 | 49 | |
47 | 50 | def __repr__(self): |
48 | 51 | return "%s(%r)" % (self.__class__.__name__, self.value) |
58 | 61 | def size(self): |
59 | 62 | return self._size |
60 | 63 | |
61 | def serialize(self, addr=None): | |
62 | buf = ''.join([emittable.serialize() for emittable in self.value]) | |
64 | def serialize(self, addr): | |
65 | buf = [] | |
66 | for emittable in self.value: | |
67 | buf.extend(emittable.serialize(addr)) # FIXME: addr + offset | |
63 | 68 | while len(buf) < self.size(): |
64 | buf += chr(0) | |
69 | buf.append(0) | |
65 | 70 | return buf |
66 | 71 | |
67 | 72 | def __repr__(self): |
83 | 88 | def size(self): |
84 | 89 | return 2 |
85 | 90 | |
86 | def serialize(self, addr=None, offset=0): | |
91 | def serialize(self, addr, offset=0): | |
87 | 92 | assert self.addr is not None, "unresolved label: %s" % self.name |
88 | return Word(self.addr + offset).serialize() | |
93 | return Word(self.addr + offset).serialize(addr) | |
89 | 94 | |
90 | 95 | def serialize_relative_to(self, addr): |
91 | 96 | assert self.addr is not None, "unresolved label: %s" % self.name |
92 | return Byte(self.addr - (addr + 2)).serialize() | |
93 | ||
94 | def serialize_as_zero_page(self, offset=0): | |
97 | return Byte(self.addr - (addr + 2)).serialize(addr) | |
98 | ||
99 | def serialize_as_zero_page(self, addr, offset=0): | |
95 | 100 | assert self.addr is not None, "unresolved label: %s" % self.name |
96 | return Byte(self.addr + offset).serialize() | |
101 | return Byte(self.addr + offset).serialize(addr) | |
97 | 102 | |
98 | 103 | def __repr__(self): |
99 | 104 | addr_s = ', addr=%r' % self.addr if self.addr is not None else '' |
110 | 115 | def size(self): |
111 | 116 | self.label.size() |
112 | 117 | |
113 | def serialize(self, addr=None): | |
114 | return self.label.serialize(offset=self.offset) | |
115 | ||
116 | def serialize_as_zero_page(self, offset=0): | |
117 | return self.label.serialize_as_zero_page(offset=self.offset) | |
118 | def serialize(self, addr): | |
119 | return self.label.serialize(addr, offset=self.offset) | |
120 | ||
121 | def serialize_as_zero_page(self, addr, offset=0): | |
122 | return self.label.serialize_as_zero_page(addr, offset=self.offset) | |
118 | 123 | |
119 | 124 | def __repr__(self): |
120 | 125 | return "%s(%r, %r)" % (self.__class__.__name__, self.label, self.offset) |
128 | 133 | def size(self): |
129 | 134 | return 1 |
130 | 135 | |
131 | def serialize(self, addr=None): | |
132 | return self.label.serialize()[0] | |
136 | def serialize(self, addr): | |
137 | return [self.label.serialize(addr)[0]] | |
133 | 138 | |
134 | 139 | def __repr__(self): |
135 | 140 | return "%s(%r)" % (self.__class__.__name__, self.label) |
143 | 148 | def size(self): |
144 | 149 | return 1 |
145 | 150 | |
146 | def serialize(self, addr=None): | |
147 | return self.label.serialize()[1] | |
151 | def serialize(self, addr): | |
152 | return [self.label.serialize(addr)[1]] | |
148 | 153 | |
149 | 154 | def __repr__(self): |
150 | 155 | return "%s(%r)" % (self.__class__.__name__, self.label) |
165 | 170 | self.accum.append(thing) |
166 | 171 | self.addr += thing.size() |
167 | 172 | |
168 | def serialize(self, stream): | |
173 | def serialize_to(self, stream): | |
174 | """`stream` should be a file opened in binary mode.""" | |
169 | 175 | addr = self.start_addr |
170 | 176 | for emittable in self.accum: |
171 | 177 | chunk = emittable.serialize(addr) |
172 | stream.write(chunk) | |
178 | stream.write(bytearray(chunk)) | |
173 | 179 | addr += len(chunk) |
174 | 180 | |
175 | 181 | def make_label(self, name=None): |
42 | 42 | |
43 | 43 | while pending_routines: |
44 | 44 | chains = [self.find_chain(k, pending_routines) for k in pending_routines.keys()] |
45 | chains.sort(key=len, reverse=True) | |
45 | chains.sort(key=lambda x: (len(x), str(x)), reverse=True) | |
46 | 46 | c = chains[0] |
47 | 47 | roster.append(c) |
48 | 48 | for k in c: |
7 | 7 | """Size of the operand for the mode (not including the opcode)""" |
8 | 8 | raise NotImplementedError |
9 | 9 | |
10 | def serialize(self, addr=None): | |
10 | def serialize(self, addr): | |
11 | 11 | raise NotImplementedError |
12 | 12 | |
13 | 13 | def __repr__(self): |
18 | 18 | def size(self): |
19 | 19 | return 0 |
20 | 20 | |
21 | def serialize(self, addr=None): | |
22 | return '' | |
21 | def serialize(self, addr): | |
22 | return [] | |
23 | 23 | |
24 | 24 | def __repr__(self): |
25 | 25 | return "%s()" % (self.__class__.__name__) |
33 | 33 | def size(self): |
34 | 34 | return 1 |
35 | 35 | |
36 | def serialize(self, addr=None): | |
37 | return self.value.serialize() | |
36 | def serialize(self, addr): | |
37 | return self.value.serialize(addr) | |
38 | 38 | |
39 | 39 | |
40 | 40 | class Absolute(AddressingMode): |
45 | 45 | def size(self): |
46 | 46 | return 2 |
47 | 47 | |
48 | def serialize(self, addr=None): | |
49 | return self.value.serialize() | |
48 | def serialize(self, addr): | |
49 | return self.value.serialize(addr) | |
50 | 50 | |
51 | 51 | |
52 | 52 | class AbsoluteX(Absolute): |
65 | 65 | def size(self): |
66 | 66 | return 1 |
67 | 67 | |
68 | def serialize(self, addr=None): | |
69 | return self.value.serialize_as_zero_page() | |
68 | def serialize(self, addr): | |
69 | return self.value.serialize_as_zero_page(addr) | |
70 | 70 | |
71 | 71 | |
72 | 72 | class Indirect(AddressingMode): |
77 | 77 | def size(self): |
78 | 78 | return 2 |
79 | 79 | |
80 | def serialize(self, addr=None): | |
81 | return self.value.serialize() | |
80 | def serialize(self, addr): | |
81 | return self.value.serialize(addr) | |
82 | 82 | |
83 | 83 | |
84 | 84 | class IndirectY(ZeroPage): |
93 | 93 | def size(self): |
94 | 94 | return 1 |
95 | 95 | |
96 | def serialize(self, addr=None): | |
96 | def serialize(self, addr): | |
97 | 97 | return self.value.serialize_relative_to(addr) |
98 | 98 | |
99 | 99 | |
107 | 107 | def size(self): |
108 | 108 | return 1 + self.operand.size() if self.operand else 0 |
109 | 109 | |
110 | def serialize(self, addr=None): | |
111 | return ( | |
112 | chr(self.opcodes[self.operand.__class__]) + | |
113 | self.operand.serialize(addr) | |
114 | ) | |
110 | def serialize(self, addr): | |
111 | return [self.opcodes[self.operand.__class__]] + self.operand.serialize(addr) | |
115 | 112 | |
116 | 113 | def __repr__(self): |
117 | 114 | return "%s(%r)" % (self.__class__.__name__, self.operand) |
17 | 17 | def __hash__(self): |
18 | 18 | return hash(self.name) |
19 | 19 | |
20 | def backpatch_constraint_labels(self, resolver): | |
21 | def resolve(w): | |
22 | if not isinstance(w, basestring): | |
23 | return w | |
24 | return resolver(w) | |
25 | if isinstance(self, TableType): | |
26 | self.of_type.backpatch_constraint_labels(resolver) | |
27 | elif isinstance(self, VectorType): | |
28 | self.of_type.backpatch_constraint_labels(resolver) | |
29 | elif isinstance(self, RoutineType): | |
30 | self.inputs = set([resolve(w) for w in self.inputs]) | |
31 | self.outputs = set([resolve(w) for w in self.outputs]) | |
32 | self.trashes = set([resolve(w) for w in self.trashes]) | |
33 | ||
34 | 20 | |
35 | 21 | TYPE_BIT = Type('bit', max_range=(0, 1)) |
36 | 22 | TYPE_BYTE = Type('byte', max_range=(0, 255)) |
40 | 26 | |
41 | 27 | class RoutineType(Type): |
42 | 28 | """This memory location contains the code for a routine.""" |
43 | def __init__(self, inputs=None, outputs=None, trashes=None): | |
29 | def __init__(self, inputs, outputs, trashes): | |
44 | 30 | self.name = 'routine' |
45 | self.inputs = inputs or set() | |
46 | self.outputs = outputs or set() | |
47 | self.trashes = trashes or set() | |
31 | self.inputs = inputs | |
32 | self.outputs = outputs | |
33 | self.trashes = trashes | |
48 | 34 | |
49 | 35 | def __repr__(self): |
50 | 36 | return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % ( |
171 | 157 | |
172 | 158 | @classmethod |
173 | 159 | def format_set(cls, location_refs): |
174 | return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs)]) | |
160 | return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs, key=lambda x: x.name)]) | |
175 | 161 | |
176 | 162 | |
177 | 163 | class IndirectRef(Ref): |
0 | """Executable file writer.""" | |
1 | ||
2 | from sixtypical.emitter import Emitter, Byte, Word | |
3 | ||
4 | ||
5 | class Outputter(object): | |
6 | def __init__(self, fh, start_addr=None): | |
7 | self.start_addr = self.__class__.start_addr | |
8 | if start_addr is not None: | |
9 | self.start_addr = start_addr | |
10 | self.prelude = self.__class__.prelude | |
11 | self.fh = fh | |
12 | self.emitter = Emitter(self.start_addr) | |
13 | ||
14 | def write_header(self): | |
15 | pass | |
16 | ||
17 | def write_prelude(self): | |
18 | self.write_header() | |
19 | for byte in self.prelude: | |
20 | self.emitter.emit(Byte(byte)) | |
21 | ||
22 | def write_postlude(self): | |
23 | pass | |
24 | ||
25 | ||
26 | class RawOutputter(Outputter): | |
27 | start_addr = 0x0000 | |
28 | prelude = [] | |
29 | ||
30 | ||
31 | class PrgOutputter(Outputter): | |
32 | start_addr = 0xc000 | |
33 | prelude = [] | |
34 | ||
35 | def write_header(self): | |
36 | # If we are outputting a .PRG, we output the load address first. | |
37 | # We don't use the Emitter for this b/c not part of addr space. | |
38 | self.fh.write(bytearray(Word(self.start_addr).serialize(0))) | |
39 | ||
40 | ||
41 | class C64BasicPrgOutputter(PrgOutputter): | |
42 | start_addr = 0x0801 | |
43 | prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32, | |
44 | 0x30, 0x36, 0x31, 0x00, 0x00, 0x00] | |
45 | ||
46 | ||
47 | class Vic20BasicPrgOutputter(PrgOutputter): | |
48 | start_addr = 0x1001 | |
49 | prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34, | |
50 | 0x31, 0x30, 0x39, 0x00, 0x00, 0x00] | |
51 | ||
52 | ||
53 | class Atari2600CartOutputter(Outputter): | |
54 | start_addr = 0xf000 | |
55 | prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9, | |
56 | 0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb] | |
57 | ||
58 | def write_postlude(self): | |
59 | # If we are outputting a cartridge with boot and BRK address | |
60 | # at the end, pad to ROM size minus 4 bytes, and emit addresses. | |
61 | self.emitter.pad_to_size(4096 - 4) | |
62 | self.emitter.emit(Word(self.start_addr)) | |
63 | self.emitter.emit(Word(self.start_addr)) | |
64 | ||
65 | ||
66 | def outputter_class_for(output_format): | |
67 | return { | |
68 | 'raw': RawOutputter, | |
69 | 'prg': PrgOutputter, | |
70 | 'c64-basic-prg': C64BasicPrgOutputter, | |
71 | 'vic20-basic-prg': Vic20BasicPrgOutputter, | |
72 | 'atari2600-cart': Atari2600CartOutputter, | |
73 | }[output_format] |
17 | 17 | return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model) |
18 | 18 | |
19 | 19 | |
20 | class ForwardReference(object): | |
21 | def __init__(self, name): | |
22 | self.name = name | |
23 | ||
24 | def __repr__(self): | |
25 | return "%s(%r)" % (self.__class__.__name__, self.name) | |
26 | ||
27 | ||
20 | 28 | class ParsingContext(object): |
21 | 29 | def __init__(self): |
22 | 30 | self.symbols = {} # token -> SymEntry |
32 | 40 | def __str__(self): |
33 | 41 | return "Symbols: {}\nStatics: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.statics, self.typedefs, self.consts) |
34 | 42 | |
35 | def lookup(self, name): | |
43 | def fetch(self, name): | |
36 | 44 | if name in self.statics: |
37 | 45 | return self.statics[name].model |
38 | 46 | if name in self.symbols: |
44 | 52 | def __init__(self, context, text, filename): |
45 | 53 | self.context = context |
46 | 54 | self.scanner = Scanner(text, filename) |
47 | self.backpatch_instrs = [] | |
48 | 55 | |
49 | 56 | def syntax_error(self, msg): |
50 | 57 | self.scanner.syntax_error(msg) |
51 | 58 | |
52 | 59 | def lookup(self, name): |
53 | model = self.context.lookup(name) | |
60 | model = self.context.fetch(name) | |
54 | 61 | if model is None: |
55 | 62 | self.syntax_error('Undefined symbol "{}"'.format(name)) |
56 | 63 | return model |
64 | ||
65 | def declare(self, name, symentry, static=False): | |
66 | if self.context.fetch(name): | |
67 | self.syntax_error('Symbol "%s" already declared' % name) | |
68 | if static: | |
69 | self.context.statics[name] = symentry | |
70 | else: | |
71 | self.context.symbols[name] = symentry | |
72 | ||
73 | def clear_statics(self): | |
74 | self.context.statics = {} | |
75 | ||
76 | # ---- symbol resolution | |
77 | ||
78 | def resolve_symbols(self, program): | |
79 | # This could stand to be better unified. | |
80 | ||
81 | def backpatch_constraint_labels(type_): | |
82 | def resolve(w): | |
83 | if not isinstance(w, ForwardReference): | |
84 | return w | |
85 | return self.lookup(w.name) | |
86 | if isinstance(type_, TableType): | |
87 | backpatch_constraint_labels(type_.of_type) | |
88 | elif isinstance(type_, VectorType): | |
89 | backpatch_constraint_labels(type_.of_type) | |
90 | elif isinstance(type_, RoutineType): | |
91 | type_.inputs = set([resolve(w) for w in type_.inputs]) | |
92 | type_.outputs = set([resolve(w) for w in type_.outputs]) | |
93 | type_.trashes = set([resolve(w) for w in type_.trashes]) | |
94 | ||
95 | for defn in program.defns: | |
96 | backpatch_constraint_labels(defn.location.type) | |
97 | for routine in program.routines: | |
98 | backpatch_constraint_labels(routine.location.type) | |
99 | ||
100 | def resolve_fwd_reference(obj, field): | |
101 | field_value = getattr(obj, field, None) | |
102 | if isinstance(field_value, ForwardReference): | |
103 | setattr(obj, field, self.lookup(field_value.name)) | |
104 | elif isinstance(field_value, IndexedRef): | |
105 | if isinstance(field_value.ref, ForwardReference): | |
106 | field_value.ref = self.lookup(field_value.ref.name) | |
107 | ||
108 | for node in program.all_children(): | |
109 | if isinstance(node, SingleOp): | |
110 | resolve_fwd_reference(node, 'location') | |
111 | resolve_fwd_reference(node, 'src') | |
112 | resolve_fwd_reference(node, 'dest') | |
57 | 113 | |
58 | 114 | # --- grammar productions |
59 | 115 | |
69 | 125 | typenames.extend(self.context.typedefs.keys()) |
70 | 126 | while self.scanner.on(*typenames): |
71 | 127 | defn = self.defn() |
72 | name = defn.name | |
73 | if self.context.lookup(name): | |
74 | self.syntax_error('Symbol "%s" already declared' % name) | |
75 | self.context.symbols[name] = SymEntry(defn, defn.location) | |
128 | self.declare(defn.name, SymEntry(defn, defn.location)) | |
76 | 129 | defns.append(defn) |
77 | while self.scanner.on('define', 'routine'): | |
78 | if self.scanner.consume('define'): | |
79 | name = self.scanner.token | |
80 | self.scanner.scan() | |
81 | routine = self.routine(name) | |
82 | else: | |
83 | routine = self.legacy_routine() | |
84 | name = routine.name | |
85 | if self.context.lookup(name): | |
86 | self.syntax_error('Symbol "%s" already declared' % name) | |
87 | self.context.symbols[name] = SymEntry(routine, routine.location) | |
130 | while self.scanner.consume('define'): | |
131 | name = self.scanner.token | |
132 | self.scanner.scan() | |
133 | routine = self.routine(name) | |
134 | self.declare(name, SymEntry(routine, routine.location)) | |
88 | 135 | routines.append(routine) |
89 | 136 | self.scanner.check_type('EOF') |
90 | 137 | |
91 | # now backpatch the executable types. | |
92 | #for type_name, type_ in self.context.typedefs.iteritems(): | |
93 | # type_.backpatch_constraint_labels(lambda w: self.lookup(w)) | |
94 | for defn in defns: | |
95 | defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w)) | |
96 | for routine in routines: | |
97 | routine.location.type.backpatch_constraint_labels(lambda w: self.lookup(w)) | |
98 | for instr in self.backpatch_instrs: | |
99 | if instr.opcode in ('call', 'goto'): | |
100 | name = instr.location | |
101 | model = self.lookup(name) | |
102 | if not isinstance(model.type, (RoutineType, VectorType)): | |
103 | self.syntax_error('Illegal call of non-executable "%s"' % name) | |
104 | instr.location = model | |
105 | if instr.opcode in ('copy',) and isinstance(instr.src, basestring): | |
106 | name = instr.src | |
107 | model = self.lookup(name) | |
108 | if not isinstance(model.type, (RoutineType, VectorType)): | |
109 | self.syntax_error('Illegal copy of non-executable "%s"' % name) | |
110 | instr.src = model | |
111 | ||
112 | return Program(self.scanner.line_number, defns=defns, routines=routines) | |
138 | program = Program(self.scanner.line_number, defns=defns, routines=routines) | |
139 | self.resolve_symbols(program) | |
140 | return program | |
113 | 141 | |
114 | 142 | def typedef(self): |
115 | 143 | self.scanner.expect('typedef') |
249 | 277 | outputs = set(self.labels()) |
250 | 278 | if self.scanner.consume('trashes'): |
251 | 279 | trashes = set(self.labels()) |
252 | return (inputs, outputs, trashes) | |
253 | ||
254 | def legacy_routine(self): | |
255 | self.scanner.expect('routine') | |
256 | name = self.scanner.token | |
257 | self.scanner.scan() | |
258 | (inputs, outputs, trashes) = self.constraints() | |
259 | type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes) | |
260 | if self.scanner.consume('@'): | |
261 | self.scanner.check_type('integer literal') | |
262 | block = None | |
263 | addr = int(self.scanner.token) | |
264 | self.scanner.scan() | |
265 | else: | |
266 | block = self.block() | |
267 | addr = None | |
268 | location = LocationRef(type_, name) | |
269 | return Routine( | |
270 | self.scanner.line_number, | |
271 | name=name, block=block, addr=addr, | |
272 | location=location | |
280 | return ( | |
281 | set([ForwardReference(n) for n in inputs]), | |
282 | set([ForwardReference(n) for n in outputs]), | |
283 | set([ForwardReference(n) for n in trashes]) | |
273 | 284 | ) |
274 | 285 | |
275 | 286 | def routine(self, name): |
285 | 296 | else: |
286 | 297 | statics = self.statics() |
287 | 298 | |
288 | self.context.statics = self.compose_statics_dict(statics) | |
299 | self.clear_statics() | |
300 | for defn in statics: | |
301 | self.declare(defn.name, SymEntry(defn, defn.location), static=True) | |
289 | 302 | block = self.block() |
290 | self.context.statics = {} | |
303 | self.clear_statics() | |
291 | 304 | |
292 | 305 | addr = None |
293 | 306 | location = LocationRef(type_, name) |
297 | 310 | location=location, statics=statics |
298 | 311 | ) |
299 | 312 | |
300 | def compose_statics_dict(self, statics): | |
301 | c = {} | |
302 | for defn in statics: | |
303 | name = defn.name | |
304 | if self.context.lookup(name): | |
305 | self.syntax_error('Symbol "%s" already declared' % name) | |
306 | c[name] = SymEntry(defn, defn.location) | |
307 | return c | |
308 | ||
309 | 313 | def labels(self): |
310 | 314 | accum = [] |
311 | 315 | accum.append(self.label()) |
327 | 331 | accum.append(self.locexpr()) |
328 | 332 | return accum |
329 | 333 | |
330 | def locexpr(self, forward=False): | |
334 | def locexpr(self): | |
331 | 335 | if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.context.consts or self.scanner.on_type('integer literal'): |
332 | 336 | return self.const() |
333 | elif forward: | |
337 | else: | |
334 | 338 | name = self.scanner.token |
335 | 339 | self.scanner.scan() |
336 | loc = self.context.lookup(name) | |
337 | if loc is not None: | |
340 | loc = self.context.fetch(name) | |
341 | if loc: | |
338 | 342 | return loc |
339 | 343 | else: |
340 | return name | |
341 | else: | |
342 | loc = self.lookup(self.scanner.token) | |
343 | self.scanner.scan() | |
344 | return loc | |
345 | ||
346 | def indlocexpr(self, forward=False): | |
344 | return ForwardReference(name) | |
345 | ||
346 | def indlocexpr(self): | |
347 | 347 | if self.scanner.consume('['): |
348 | 348 | loc = self.locexpr() |
349 | 349 | self.scanner.expect(']') |
354 | 354 | loc = self.locexpr() |
355 | 355 | return AddressRef(loc) |
356 | 356 | else: |
357 | return self.indexed_locexpr(forward=forward) | |
358 | ||
359 | def indexed_locexpr(self, forward=False): | |
360 | loc = self.locexpr(forward=forward) | |
361 | if not isinstance(loc, basestring): | |
357 | return self.indexed_locexpr() | |
358 | ||
359 | def indexed_locexpr(self): | |
360 | loc = self.locexpr() | |
361 | if not isinstance(loc, str): | |
362 | 362 | index = None |
363 | 363 | if self.scanner.consume('+'): |
364 | 364 | index = self.locexpr() |
452 | 452 | self.scanner.scan() |
453 | 453 | name = self.scanner.token |
454 | 454 | self.scanner.scan() |
455 | instr = SingleOp(self.scanner.line_number, opcode=opcode, location=name, dest=None, src=None) | |
456 | self.backpatch_instrs.append(instr) | |
455 | instr = SingleOp(self.scanner.line_number, opcode=opcode, location=ForwardReference(name), dest=None, src=None) | |
457 | 456 | return instr |
458 | 457 | elif self.scanner.token in ("copy",): |
459 | 458 | opcode = self.scanner.token |
460 | 459 | self.scanner.scan() |
461 | src = self.indlocexpr(forward=True) | |
460 | src = self.indlocexpr() | |
462 | 461 | self.scanner.expect(',') |
463 | 462 | dest = self.indlocexpr() |
464 | 463 | instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src) |
465 | self.backpatch_instrs.append(instr) | |
466 | 464 | return instr |
467 | 465 | elif self.scanner.consume("with"): |
468 | 466 | self.scanner.expect("interrupts") |
478 | 476 | return SingleOp(self.scanner.line_number, opcode='trash', src=None, dest=dest) |
479 | 477 | else: |
480 | 478 | self.syntax_error('bad opcode "%s"' % self.scanner.token) |
479 | ||
480 | ||
481 | # - - - - | |
482 | ||
483 | ||
484 | def merge_programs(programs): | |
485 | """Assumes that the programs do not have any conflicts.""" | |
486 | ||
487 | full = Program(1, defns=[], routines=[]) | |
488 | for p in programs: | |
489 | full.defns.extend(p.defns) | |
490 | full.routines.extend(p.routines) | |
491 | ||
492 | return full |
14 | 14 | |
15 | 15 | Routines must declare their inputs, outputs, and memory locations they trash. |
16 | 16 | |
17 | | routine up | |
17 | | define up routine | |
18 | 18 | | inputs a |
19 | 19 | | outputs a |
20 | 20 | | trashes c, z, v, n |
26 | 26 | |
27 | 27 | Routines may not declare a memory location to be both an output and trashed. |
28 | 28 | |
29 | | routine main | |
29 | | define main routine | |
30 | 30 | | outputs a |
31 | 31 | | trashes a |
32 | 32 | | { |
36 | 36 | |
37 | 37 | If a routine declares it outputs a location, that location should be initialized. |
38 | 38 | |
39 | | routine main | |
39 | | define main routine | |
40 | 40 | | outputs a, x, z, n |
41 | 41 | | { |
42 | 42 | | ld x, 0 |
43 | 43 | | } |
44 | 44 | ? UnmeaningfulOutputError: a |
45 | 45 | |
46 | | routine main | |
46 | | define main routine | |
47 | 47 | | inputs a |
48 | 48 | | outputs a |
49 | 49 | | { |
53 | 53 | If a routine declares it outputs a location, that location may or may not have |
54 | 54 | been initialized. Trashing is mainly a signal to the caller. |
55 | 55 | |
56 | | routine main | |
56 | | define main routine | |
57 | 57 | | trashes x, z, n |
58 | 58 | | { |
59 | 59 | | ld x, 0 |
60 | 60 | | } |
61 | 61 | = ok |
62 | 62 | |
63 | | routine main | |
63 | | define main routine | |
64 | 64 | | trashes x, z, n |
65 | 65 | | { |
66 | 66 | | } |
68 | 68 | |
69 | 69 | If a routine modifies a location, it needs to either output it or trash it. |
70 | 70 | |
71 | | routine main | |
71 | | define main routine | |
72 | 72 | | { |
73 | 73 | | ld x, 0 |
74 | 74 | | } |
75 | 75 | ? ForbiddenWriteError: x |
76 | 76 | |
77 | | routine main | |
77 | | define main routine | |
78 | 78 | | outputs x, z, n |
79 | 79 | | { |
80 | 80 | | ld x, 0 |
81 | 81 | | } |
82 | 82 | = ok |
83 | 83 | |
84 | | routine main | |
84 | | define main routine | |
85 | 85 | | trashes x, z, n |
86 | 86 | | { |
87 | 87 | | ld x, 0 |
90 | 90 | |
91 | 91 | This is true regardless of whether it's an input or not. |
92 | 92 | |
93 | | routine main | |
93 | | define main routine | |
94 | 94 | | inputs x |
95 | 95 | | { |
96 | 96 | | ld x, 0 |
97 | 97 | | } |
98 | 98 | ? ForbiddenWriteError: x |
99 | 99 | |
100 | | routine main | |
100 | | define main routine | |
101 | 101 | | inputs x |
102 | 102 | | outputs x, z, n |
103 | 103 | | { |
105 | 105 | | } |
106 | 106 | = ok |
107 | 107 | |
108 | | routine main | |
108 | | define main routine | |
109 | 109 | | inputs x |
110 | 110 | | trashes x, z, n |
111 | 111 | | { |
115 | 115 | |
116 | 116 | If a routine trashes a location, this must be declared. |
117 | 117 | |
118 | | routine foo | |
118 | | define foo routine | |
119 | 119 | | trashes x |
120 | 120 | | { |
121 | 121 | | trash x |
122 | 122 | | } |
123 | 123 | = ok |
124 | 124 | |
125 | | routine foo | |
125 | | define foo routine | |
126 | 126 | | { |
127 | 127 | | trash x |
128 | 128 | | } |
129 | 129 | ? ForbiddenWriteError: x |
130 | 130 | |
131 | | routine foo | |
131 | | define foo routine | |
132 | 132 | | outputs x |
133 | 133 | | { |
134 | 134 | | trash x |
137 | 137 | |
138 | 138 | If a routine causes a location to be trashed, this must be declared in the caller. |
139 | 139 | |
140 | | routine trash_x | |
140 | | define trash_x routine | |
141 | 141 | | trashes x, z, n |
142 | 142 | | { |
143 | 143 | | ld x, 0 |
144 | 144 | | } |
145 | 145 | | |
146 | | routine foo | |
146 | | define foo routine | |
147 | 147 | | trashes x, z, n |
148 | 148 | | { |
149 | 149 | | call trash_x |
150 | 150 | | } |
151 | 151 | = ok |
152 | 152 | |
153 | | routine trash_x | |
153 | | define trash_x routine | |
154 | 154 | | trashes x, z, n |
155 | 155 | | { |
156 | 156 | | ld x, 0 |
157 | 157 | | } |
158 | 158 | | |
159 | | routine foo | |
159 | | define foo routine | |
160 | 160 | | trashes z, n |
161 | 161 | | { |
162 | 162 | | call trash_x |
163 | 163 | | } |
164 | 164 | ? ForbiddenWriteError: x |
165 | 165 | |
166 | | routine trash_x | |
166 | | define trash_x routine | |
167 | 167 | | trashes x, z, n |
168 | 168 | | { |
169 | 169 | | ld x, 0 |
170 | 170 | | } |
171 | 171 | | |
172 | | routine foo | |
172 | | define foo routine | |
173 | 173 | | outputs x |
174 | 174 | | trashes z, n |
175 | 175 | | { |
184 | 184 | | word w1 @ 60001 |
185 | 185 | | word w2 : 2000 |
186 | 186 | | |
187 | | routine main | |
187 | | define main routine | |
188 | 188 | | inputs b1, w1 |
189 | 189 | | outputs b2, w2 |
190 | 190 | | trashes a, z, n |
195 | 195 | | } |
196 | 196 | = ok |
197 | 197 | |
198 | ### call ### | |
199 | ||
200 | You can't call a non-routine. | |
201 | ||
202 | | byte up | |
203 | | | |
204 | | define main routine outputs x, y trashes z, n { | |
205 | | ld x, 0 | |
206 | | ld y, 1 | |
207 | | call up | |
208 | | } | |
209 | ? TypeMismatchError: up | |
210 | ||
211 | | define main routine outputs x, y trashes z, n { | |
212 | | ld x, 0 | |
213 | | ld y, 1 | |
214 | | call x | |
215 | | } | |
216 | ? TypeMismatchError: x | |
217 | ||
218 | Nor can you goto a non-routine. | |
219 | ||
220 | | byte foo | |
221 | | | |
222 | | define main routine { | |
223 | | goto foo | |
224 | | } | |
225 | ? TypeMismatchError: foo | |
226 | ||
198 | 227 | ### ld ### |
199 | 228 | |
200 | 229 | Can't `ld` from a memory location that isn't initialized. |
201 | 230 | |
202 | | routine main | |
231 | | define main routine | |
203 | 232 | | inputs a, x |
204 | 233 | | trashes a, z, n |
205 | 234 | | { |
207 | 236 | | } |
208 | 237 | = ok |
209 | 238 | |
210 | | routine main | |
239 | | define main routine | |
211 | 240 | | inputs a |
212 | 241 | | trashes a |
213 | 242 | | { |
217 | 246 | |
218 | 247 | Can't `ld` to a memory location that doesn't appear in (outputs ∪ trashes). |
219 | 248 | |
220 | | routine main | |
249 | | define main routine | |
221 | 250 | | trashes a, z, n |
222 | 251 | | { |
223 | 252 | | ld a, 0 |
224 | 253 | | } |
225 | 254 | = ok |
226 | 255 | |
227 | | routine main | |
256 | | define main routine | |
228 | 257 | | outputs a |
229 | 258 | | trashes z, n |
230 | 259 | | { |
232 | 261 | | } |
233 | 262 | = ok |
234 | 263 | |
235 | | routine main | |
264 | | define main routine | |
236 | 265 | | outputs z, n |
237 | 266 | | trashes a |
238 | 267 | | { |
240 | 269 | | } |
241 | 270 | = ok |
242 | 271 | |
243 | | routine main | |
272 | | define main routine | |
244 | 273 | | trashes z, n |
245 | 274 | | { |
246 | 275 | | ld a, 0 |
247 | 276 | | } |
248 | 277 | ? ForbiddenWriteError: a |
249 | 278 | |
250 | | routine main | |
279 | | define main routine | |
251 | 280 | | trashes a, n |
252 | 281 | | { |
253 | 282 | | ld a, 0 |
258 | 287 | |
259 | 288 | | word foo |
260 | 289 | | |
261 | | routine main | |
290 | | define main routine | |
262 | 291 | | inputs foo |
263 | 292 | | trashes a, n, z |
264 | 293 | | { |
271 | 300 | Can't `st` from a memory location that isn't initialized. |
272 | 301 | |
273 | 302 | | byte lives |
274 | | routine main | |
303 | | define main routine | |
275 | 304 | | inputs x |
276 | 305 | | trashes lives |
277 | 306 | | { |
280 | 309 | = ok |
281 | 310 | |
282 | 311 | | byte lives |
283 | | routine main | |
312 | | define main routine | |
284 | 313 | | trashes x, lives |
285 | 314 | | { |
286 | 315 | | st x, lives |
290 | 319 | Can't `st` to a memory location that doesn't appear in (outputs ∪ trashes). |
291 | 320 | |
292 | 321 | | byte lives |
293 | | routine main | |
322 | | define main routine | |
294 | 323 | | trashes lives |
295 | 324 | | { |
296 | 325 | | st 0, lives |
298 | 327 | = ok |
299 | 328 | |
300 | 329 | | byte lives |
301 | | routine main | |
330 | | define main routine | |
302 | 331 | | outputs lives |
303 | 332 | | { |
304 | 333 | | st 0, lives |
306 | 335 | = ok |
307 | 336 | |
308 | 337 | | byte lives |
309 | | routine main | |
338 | | define main routine | |
310 | 339 | | inputs lives |
311 | 340 | | { |
312 | 341 | | st 0, lives |
317 | 346 | |
318 | 347 | | word foo |
319 | 348 | | |
320 | | routine main | |
349 | | define main routine | |
321 | 350 | | outputs foo |
322 | 351 | | trashes a, n, z |
323 | 352 | | { |
333 | 362 | | byte one |
334 | 363 | | byte table[256] many |
335 | 364 | | |
336 | | routine main | |
365 | | define main routine | |
337 | 366 | | outputs one |
338 | 367 | | trashes a, x, n, z |
339 | 368 | | { |
346 | 375 | | byte one |
347 | 376 | | byte table[256] many |
348 | 377 | | |
349 | | routine main | |
378 | | define main routine | |
350 | 379 | | outputs many |
351 | 380 | | trashes a, x, n, z |
352 | 381 | | { |
359 | 388 | | byte one |
360 | 389 | | byte table[256] many |
361 | 390 | | |
362 | | routine main | |
391 | | define main routine | |
363 | 392 | | outputs one |
364 | 393 | | trashes a, x, n, z |
365 | 394 | | { |
372 | 401 | | byte one |
373 | 402 | | byte table[256] many |
374 | 403 | | |
375 | | routine main | |
404 | | define main routine | |
376 | 405 | | outputs many |
377 | 406 | | trashes a, x, n, z |
378 | 407 | | { |
387 | 416 | | byte one |
388 | 417 | | byte table[256] many |
389 | 418 | | |
390 | | routine main | |
419 | | define main routine | |
391 | 420 | | outputs many |
392 | 421 | | trashes a, x, n, z |
393 | 422 | | { |
400 | 429 | |
401 | 430 | | byte one |
402 | 431 | | |
403 | | routine main | |
432 | | define main routine | |
404 | 433 | | outputs one |
405 | 434 | | trashes a, x, n, z |
406 | 435 | | { |
412 | 441 | |
413 | 442 | | byte one |
414 | 443 | | |
415 | | routine main | |
444 | | define main routine | |
416 | 445 | | outputs one |
417 | 446 | | trashes a, x, n, z |
418 | 447 | | { |
424 | 453 | |
425 | 454 | | byte table[256] many |
426 | 455 | | |
427 | | routine main | |
456 | | define main routine | |
428 | 457 | | outputs many |
429 | 458 | | trashes a, x, n, z |
430 | 459 | | { |
437 | 466 | |
438 | 467 | | byte table[256] many |
439 | 468 | | |
440 | | routine main | |
469 | | define main routine | |
441 | 470 | | outputs many |
442 | 471 | | trashes a, x, n, z |
443 | 472 | | { |
450 | 479 | |
451 | 480 | | byte table[256] many |
452 | 481 | | |
453 | | routine main | |
482 | | define main routine | |
454 | 483 | | inputs many |
455 | 484 | | outputs many |
456 | 485 | | trashes a, x, n, z |
464 | 493 | |
465 | 494 | | byte table[256] many |
466 | 495 | | |
467 | | routine main | |
496 | | define main routine | |
468 | 497 | | inputs many |
469 | 498 | | outputs many |
470 | 499 | | trashes a, x, n, z |
477 | 506 | |
478 | 507 | | byte table[256] many |
479 | 508 | | |
480 | | routine main | |
509 | | define main routine | |
481 | 510 | | inputs many |
482 | 511 | | outputs many |
483 | 512 | | trashes a, x, c, n, z, v |
495 | 524 | |
496 | 525 | | byte table[256] many |
497 | 526 | | |
498 | | routine main | |
527 | | define main routine | |
499 | 528 | | inputs many |
500 | 529 | | outputs many |
501 | 530 | | trashes a, x, c, n, z |
512 | 541 | |
513 | 542 | | byte table[256] many |
514 | 543 | | |
515 | | routine main | |
544 | | define main routine | |
516 | 545 | | inputs many |
517 | 546 | | outputs many |
518 | 547 | | trashes a, x, c, n, z |
532 | 561 | | word one |
533 | 562 | | word table[256] many |
534 | 563 | | |
535 | | routine main | |
564 | | define main routine | |
536 | 565 | | inputs one, many |
537 | 566 | | outputs one, many |
538 | 567 | | trashes a, x, n, z |
546 | 575 | | word one |
547 | 576 | | word table[256] many |
548 | 577 | | |
549 | | routine main | |
578 | | define main routine | |
550 | 579 | | inputs one, many |
551 | 580 | | outputs one, many |
552 | 581 | | trashes a, x, n, z |
559 | 588 | | word one |
560 | 589 | | word table[256] many |
561 | 590 | | |
562 | | routine main | |
591 | | define main routine | |
563 | 592 | | inputs one, many |
564 | 593 | | outputs one, many |
565 | 594 | | trashes a, x, n, z |
574 | 603 | |
575 | 604 | | word table[32] many |
576 | 605 | | |
577 | | routine main | |
606 | | define main routine | |
578 | 607 | | inputs many |
579 | 608 | | outputs many |
580 | 609 | | trashes a, x, n, z |
598 | 627 | |
599 | 628 | | byte table[32] many |
600 | 629 | | |
601 | | routine main | |
630 | | define main routine | |
602 | 631 | | inputs many |
603 | 632 | | outputs many |
604 | 633 | | trashes a, x, n, z |
611 | 640 | |
612 | 641 | | byte table[32] many |
613 | 642 | | |
614 | | routine main | |
643 | | define main routine | |
615 | 644 | | inputs many |
616 | 645 | | outputs many |
617 | 646 | | trashes a, x, n, z |
623 | 652 | |
624 | 653 | | byte table[32] many |
625 | 654 | | |
626 | | routine main | |
655 | | define main routine | |
627 | 656 | | inputs many |
628 | 657 | | outputs many |
629 | 658 | | trashes a, x, n, z |
639 | 668 | | word one: 77 |
640 | 669 | | word table[32] many |
641 | 670 | | |
642 | | routine main | |
671 | | define main routine | |
643 | 672 | | inputs many, one |
644 | 673 | | outputs many, one |
645 | 674 | | trashes a, x, n, z |
653 | 682 | | word one: 77 |
654 | 683 | | word table[32] many |
655 | 684 | | |
656 | | routine main | |
685 | | define main routine | |
657 | 686 | | inputs many, one |
658 | 687 | | outputs many, one |
659 | 688 | | trashes a, x, n, z |
666 | 695 | | word one: 77 |
667 | 696 | | word table[32] many |
668 | 697 | | |
669 | | routine main | |
698 | | define main routine | |
670 | 699 | | inputs many, one |
671 | 700 | | outputs many, one |
672 | 701 | | trashes a, x, n, z |
684 | 713 | | word one: 77 |
685 | 714 | | word table[32] many |
686 | 715 | | |
687 | | routine main | |
716 | | define main routine | |
688 | 717 | | inputs a, many, one |
689 | 718 | | outputs many, one |
690 | 719 | | trashes a, x, n, z |
701 | 730 | | word one: 77 |
702 | 731 | | word table[32] many |
703 | 732 | | |
704 | | routine main | |
733 | | define main routine | |
705 | 734 | | inputs a, many, one |
706 | 735 | | outputs many, one |
707 | 736 | | trashes a, x, n, z |
719 | 748 | | word one: 77 |
720 | 749 | | word table[32] many |
721 | 750 | | |
722 | | routine main | |
751 | | define main routine | |
723 | 752 | | inputs a, many, one |
724 | 753 | | outputs many, one |
725 | 754 | | trashes a, x, n, z |
736 | 765 | |
737 | 766 | Can't `add` from or to a memory location that isn't initialized. |
738 | 767 | |
739 | | routine main | |
768 | | define main routine | |
740 | 769 | | inputs a |
741 | 770 | | outputs a |
742 | 771 | | trashes c, z, v, n |
747 | 776 | = ok |
748 | 777 | |
749 | 778 | | byte lives |
750 | | routine main | |
779 | | define main routine | |
751 | 780 | | inputs a |
752 | 781 | | outputs a |
753 | 782 | | trashes c, z, v, n |
758 | 787 | ? UnmeaningfulReadError: lives |
759 | 788 | |
760 | 789 | | byte lives |
761 | | routine main | |
790 | | define main routine | |
762 | 791 | | inputs lives |
763 | 792 | | outputs a |
764 | 793 | | trashes c, z, v, n |
770 | 799 | |
771 | 800 | Can't `add` to a memory location that isn't writeable. |
772 | 801 | |
773 | | routine main | |
802 | | define main routine | |
774 | 803 | | inputs a |
775 | 804 | | trashes c |
776 | 805 | | { |
782 | 811 | You can `add` a word constant to a word memory location. |
783 | 812 | |
784 | 813 | | word score |
785 | | routine main | |
814 | | define main routine | |
786 | 815 | | inputs a, score |
787 | 816 | | outputs score |
788 | 817 | | trashes a, c, z, v, n |
795 | 824 | `add`ing a word constant to a word memory location trashes `a`. |
796 | 825 | |
797 | 826 | | word score |
798 | | routine main | |
827 | | define main routine | |
799 | 828 | | inputs a, score |
800 | 829 | | outputs score, a |
801 | 830 | | trashes c, z, v, n |
808 | 837 | To be sure, `add`ing a word constant to a word memory location trashes `a`. |
809 | 838 | |
810 | 839 | | word score |
811 | | routine main | |
840 | | define main routine | |
812 | 841 | | inputs score |
813 | 842 | | outputs score |
814 | 843 | | trashes c, z, v, n |
822 | 851 | |
823 | 852 | | word score |
824 | 853 | | word delta |
825 | | routine main | |
854 | | define main routine | |
826 | 855 | | inputs score, delta |
827 | 856 | | outputs score |
828 | 857 | | trashes a, c, z, v, n |
836 | 865 | |
837 | 866 | | word score |
838 | 867 | | word delta |
839 | | routine main | |
868 | | define main routine | |
840 | 869 | | inputs score, delta |
841 | 870 | | outputs score |
842 | 871 | | trashes c, z, v, n |
850 | 879 | |
851 | 880 | | pointer ptr |
852 | 881 | | word delta |
853 | | routine main | |
882 | | define main routine | |
854 | 883 | | inputs ptr, delta |
855 | 884 | | outputs ptr |
856 | 885 | | trashes a, c, z, v, n |
865 | 894 | |
866 | 895 | | pointer ptr |
867 | 896 | | word delta |
868 | | routine main | |
897 | | define main routine | |
869 | 898 | | inputs ptr, delta |
870 | 899 | | outputs ptr |
871 | 900 | | trashes c, z, v, n |
880 | 909 | |
881 | 910 | Can't `sub` from or to a memory location that isn't initialized. |
882 | 911 | |
883 | | routine main | |
912 | | define main routine | |
884 | 913 | | inputs a |
885 | 914 | | outputs a |
886 | 915 | | trashes c, z, v, n |
891 | 920 | = ok |
892 | 921 | |
893 | 922 | | byte lives |
894 | | routine main | |
923 | | define main routine | |
895 | 924 | | inputs a |
896 | 925 | | outputs a |
897 | 926 | | trashes c, z, v, n |
902 | 931 | ? UnmeaningfulReadError: lives |
903 | 932 | |
904 | 933 | | byte lives |
905 | | routine main | |
934 | | define main routine | |
906 | 935 | | inputs lives |
907 | 936 | | outputs a |
908 | 937 | | trashes c, z, v, n |
914 | 943 | |
915 | 944 | Can't `sub` to a memory location that isn't writeable. |
916 | 945 | |
917 | | routine main | |
946 | | define main routine | |
918 | 947 | | inputs a |
919 | 948 | | trashes c |
920 | 949 | | { |
926 | 955 | You can `sub` a word constant from a word memory location. |
927 | 956 | |
928 | 957 | | word score |
929 | | routine main | |
958 | | define main routine | |
930 | 959 | | inputs a, score |
931 | 960 | | outputs score |
932 | 961 | | trashes a, c, z, v, n |
939 | 968 | `sub`ing a word constant from a word memory location trashes `a`. |
940 | 969 | |
941 | 970 | | word score |
942 | | routine main | |
971 | | define main routine | |
943 | 972 | | inputs a, score |
944 | 973 | | outputs score, a |
945 | 974 | | trashes c, z, v, n |
953 | 982 | |
954 | 983 | | word score |
955 | 984 | | word delta |
956 | | routine main | |
985 | | define main routine | |
957 | 986 | | inputs score, delta |
958 | 987 | | outputs score |
959 | 988 | | trashes a, c, z, v, n |
967 | 996 | |
968 | 997 | | word score |
969 | 998 | | word delta |
970 | | routine main | |
999 | | define main routine | |
971 | 1000 | | inputs score, delta |
972 | 1001 | | outputs score |
973 | 1002 | | trashes c, z, v, n |
981 | 1010 | |
982 | 1011 | Location must be initialized and writeable. |
983 | 1012 | |
984 | | routine main | |
1013 | | define main routine | |
985 | 1014 | | outputs x |
986 | 1015 | | trashes z, n |
987 | 1016 | | { |
989 | 1018 | | } |
990 | 1019 | ? UnmeaningfulReadError: x |
991 | 1020 | |
992 | | routine main | |
1021 | | define main routine | |
993 | 1022 | | inputs x |
994 | 1023 | | trashes z, n |
995 | 1024 | | { |
997 | 1026 | | } |
998 | 1027 | ? ForbiddenWriteError: x |
999 | 1028 | |
1000 | | routine main | |
1029 | | define main routine | |
1001 | 1030 | | inputs x |
1002 | 1031 | | outputs x |
1003 | 1032 | | trashes z, n |
1010 | 1039 | |
1011 | 1040 | | word foo |
1012 | 1041 | | |
1013 | | routine main | |
1042 | | define main routine | |
1014 | 1043 | | inputs foo |
1015 | 1044 | | outputs foo |
1016 | 1045 | | trashes z, n |
1023 | 1052 | |
1024 | 1053 | Location must be initialized and writeable. |
1025 | 1054 | |
1026 | | routine main | |
1055 | | define main routine | |
1027 | 1056 | | outputs x |
1028 | 1057 | | trashes z, n |
1029 | 1058 | | { |
1031 | 1060 | | } |
1032 | 1061 | ? UnmeaningfulReadError: x |
1033 | 1062 | |
1034 | | routine main | |
1063 | | define main routine | |
1035 | 1064 | | inputs x |
1036 | 1065 | | trashes z, n |
1037 | 1066 | | { |
1039 | 1068 | | } |
1040 | 1069 | ? ForbiddenWriteError: x |
1041 | 1070 | |
1042 | | routine main | |
1071 | | define main routine | |
1043 | 1072 | | inputs x |
1044 | 1073 | | outputs x |
1045 | 1074 | | trashes z, n |
1052 | 1081 | |
1053 | 1082 | | word foo |
1054 | 1083 | | |
1055 | | routine main | |
1084 | | define main routine | |
1056 | 1085 | | inputs foo |
1057 | 1086 | | outputs foo |
1058 | 1087 | | trashes z, n |
1065 | 1094 | |
1066 | 1095 | Some rudimentary tests for `cmp`. |
1067 | 1096 | |
1068 | | routine main | |
1097 | | define main routine | |
1069 | 1098 | | inputs a |
1070 | 1099 | | trashes z, c, n |
1071 | 1100 | | { |
1073 | 1102 | | } |
1074 | 1103 | = ok |
1075 | 1104 | |
1076 | | routine main | |
1105 | | define main routine | |
1077 | 1106 | | inputs a |
1078 | 1107 | | trashes z, n |
1079 | 1108 | | { |
1081 | 1110 | | } |
1082 | 1111 | ? ForbiddenWriteError: c |
1083 | 1112 | |
1084 | | routine main | |
1113 | | define main routine | |
1085 | 1114 | | trashes z, c, n |
1086 | 1115 | | { |
1087 | 1116 | | cmp a, 4 |
1092 | 1121 | |
1093 | 1122 | Some rudimentary tests for `and`. |
1094 | 1123 | |
1095 | | routine main | |
1124 | | define main routine | |
1096 | 1125 | | inputs a |
1097 | 1126 | | outputs a, z, n |
1098 | 1127 | | { |
1100 | 1129 | | } |
1101 | 1130 | = ok |
1102 | 1131 | |
1103 | | routine main | |
1132 | | define main routine | |
1104 | 1133 | | inputs a |
1105 | 1134 | | trashes z, n |
1106 | 1135 | | { |
1108 | 1137 | | } |
1109 | 1138 | ? ForbiddenWriteError: a |
1110 | 1139 | |
1111 | | routine main | |
1140 | | define main routine | |
1112 | 1141 | | trashes z, n |
1113 | 1142 | | { |
1114 | 1143 | | and a, 4 |
1119 | 1148 | |
1120 | 1149 | Some rudimentary tests for `or`. |
1121 | 1150 | |
1122 | | routine main | |
1151 | | define main routine | |
1123 | 1152 | | inputs a |
1124 | 1153 | | outputs a, z, n |
1125 | 1154 | | { |
1127 | 1156 | | } |
1128 | 1157 | = ok |
1129 | 1158 | |
1130 | | routine main | |
1159 | | define main routine | |
1131 | 1160 | | inputs a |
1132 | 1161 | | trashes z, n |
1133 | 1162 | | { |
1135 | 1164 | | } |
1136 | 1165 | ? ForbiddenWriteError: a |
1137 | 1166 | |
1138 | | routine main | |
1167 | | define main routine | |
1139 | 1168 | | trashes z, n |
1140 | 1169 | | { |
1141 | 1170 | | or a, 4 |
1146 | 1175 | |
1147 | 1176 | Some rudimentary tests for `xor`. |
1148 | 1177 | |
1149 | | routine main | |
1178 | | define main routine | |
1150 | 1179 | | inputs a |
1151 | 1180 | | outputs a, z, n |
1152 | 1181 | | { |
1154 | 1183 | | } |
1155 | 1184 | = ok |
1156 | 1185 | |
1157 | | routine main | |
1186 | | define main routine | |
1158 | 1187 | | inputs a |
1159 | 1188 | | trashes z, n |
1160 | 1189 | | { |
1162 | 1191 | | } |
1163 | 1192 | ? ForbiddenWriteError: a |
1164 | 1193 | |
1165 | | routine main | |
1194 | | define main routine | |
1166 | 1195 | | trashes z, n |
1167 | 1196 | | { |
1168 | 1197 | | xor a, 4 |
1174 | 1203 | Some rudimentary tests for `shl`. |
1175 | 1204 | |
1176 | 1205 | | byte foo |
1177 | | routine main | |
1206 | | define main routine | |
1178 | 1207 | | inputs foo, a, c |
1179 | 1208 | | outputs foo, a, c, z, n |
1180 | 1209 | | { |
1183 | 1212 | | } |
1184 | 1213 | = ok |
1185 | 1214 | |
1186 | | routine main | |
1215 | | define main routine | |
1187 | 1216 | | inputs a, c |
1188 | 1217 | | outputs c, z, n |
1189 | 1218 | | { |
1191 | 1220 | | } |
1192 | 1221 | ? ForbiddenWriteError: a |
1193 | 1222 | |
1194 | | routine main | |
1223 | | define main routine | |
1195 | 1224 | | inputs a |
1196 | 1225 | | outputs a, c, z, n |
1197 | 1226 | | { |
1204 | 1233 | Some rudimentary tests for `shr`. |
1205 | 1234 | |
1206 | 1235 | | byte foo |
1207 | | routine main | |
1236 | | define main routine | |
1208 | 1237 | | inputs foo, a, c |
1209 | 1238 | | outputs foo, a, c, z, n |
1210 | 1239 | | { |
1213 | 1242 | | } |
1214 | 1243 | = ok |
1215 | 1244 | |
1216 | | routine main | |
1245 | | define main routine | |
1217 | 1246 | | inputs a, c |
1218 | 1247 | | outputs c, z, n |
1219 | 1248 | | { |
1221 | 1250 | | } |
1222 | 1251 | ? ForbiddenWriteError: a |
1223 | 1252 | |
1224 | | routine main | |
1253 | | define main routine | |
1225 | 1254 | | inputs a |
1226 | 1255 | | outputs a, c, z, n |
1227 | 1256 | | { |
1233 | 1262 | |
1234 | 1263 | Some rudimentary tests for `nop`. |
1235 | 1264 | |
1236 | | routine main | |
1265 | | define main routine | |
1237 | 1266 | | { |
1238 | 1267 | | nop |
1239 | 1268 | | } |
1246 | 1275 | |
1247 | 1276 | | byte lives |
1248 | 1277 | | |
1249 | | routine foo | |
1278 | | define foo routine | |
1250 | 1279 | | inputs x |
1251 | 1280 | | trashes lives |
1252 | 1281 | | { |
1253 | 1282 | | st x, lives |
1254 | 1283 | | } |
1255 | 1284 | | |
1256 | | routine main | |
1285 | | define main routine | |
1257 | 1286 | | { |
1258 | 1287 | | call foo |
1259 | 1288 | | } |
1263 | 1292 | |
1264 | 1293 | | byte lives |
1265 | 1294 | | |
1266 | | routine foo | |
1295 | | define foo routine | |
1267 | 1296 | | inputs x |
1268 | 1297 | | trashes lives |
1269 | 1298 | | { |
1270 | 1299 | | st x, lives |
1271 | 1300 | | } |
1272 | 1301 | | |
1273 | | routine main | |
1302 | | define main routine | |
1274 | 1303 | | outputs x, z, n |
1275 | 1304 | | { |
1276 | 1305 | | ld x, 0 |
1280 | 1309 | |
1281 | 1310 | | byte lives |
1282 | 1311 | | |
1283 | | routine foo | |
1312 | | define foo routine | |
1284 | 1313 | | inputs x |
1285 | 1314 | | trashes lives |
1286 | 1315 | | { |
1287 | 1316 | | st x, lives |
1288 | 1317 | | } |
1289 | 1318 | | |
1290 | | routine main | |
1319 | | define main routine | |
1291 | 1320 | | outputs x, z, n |
1292 | 1321 | | trashes lives |
1293 | 1322 | | { |
1300 | 1329 | |
1301 | 1330 | | byte lives |
1302 | 1331 | | |
1303 | | routine foo | |
1332 | | define foo routine | |
1304 | 1333 | | inputs x |
1305 | 1334 | | trashes lives |
1306 | 1335 | | { |
1307 | 1336 | | st x, lives |
1308 | 1337 | | } |
1309 | 1338 | | |
1310 | | routine main | |
1339 | | define main routine | |
1311 | 1340 | | outputs x, z, n, lives |
1312 | 1341 | | { |
1313 | 1342 | | ld x, 0 |
1319 | 1348 | |
1320 | 1349 | | byte lives |
1321 | 1350 | | |
1322 | | routine foo | |
1351 | | define foo routine | |
1323 | 1352 | | inputs x |
1324 | 1353 | | trashes lives |
1325 | 1354 | | { |
1326 | 1355 | | st x, lives |
1327 | 1356 | | } |
1328 | 1357 | | |
1329 | | routine main | |
1358 | | define main routine | |
1330 | 1359 | | outputs x, z, n, lives |
1331 | 1360 | | { |
1332 | 1361 | | ld x, 0 |
1338 | 1367 | If a routine declares outputs, they are initialized in the caller after |
1339 | 1368 | calling it. |
1340 | 1369 | |
1341 | | routine foo | |
1370 | | define foo routine | |
1342 | 1371 | | outputs x, z, n |
1343 | 1372 | | { |
1344 | 1373 | | ld x, 0 |
1345 | 1374 | | } |
1346 | 1375 | | |
1347 | | routine main | |
1376 | | define main routine | |
1348 | 1377 | | outputs a |
1349 | 1378 | | trashes x, z, n |
1350 | 1379 | | { |
1353 | 1382 | | } |
1354 | 1383 | = ok |
1355 | 1384 | |
1356 | | routine foo | |
1357 | | { | |
1358 | | } | |
1359 | | | |
1360 | | routine main | |
1385 | | define foo routine | |
1386 | | { | |
1387 | | } | |
1388 | | | |
1389 | | define main routine | |
1361 | 1390 | | outputs a |
1362 | 1391 | | trashes x |
1363 | 1392 | | { |
1369 | 1398 | If a routine trashes locations, they are uninitialized in the caller after |
1370 | 1399 | calling it. |
1371 | 1400 | |
1372 | | routine foo | |
1401 | | define foo routine | |
1373 | 1402 | | trashes x, z, n |
1374 | 1403 | | { |
1375 | 1404 | | ld x, 0 |
1376 | 1405 | | } |
1377 | 1406 | = ok |
1378 | 1407 | |
1379 | | routine foo | |
1408 | | define foo routine | |
1380 | 1409 | | trashes x, z, n |
1381 | 1410 | | { |
1382 | 1411 | | ld x, 0 |
1383 | 1412 | | } |
1384 | 1413 | | |
1385 | | routine main | |
1414 | | define main routine | |
1386 | 1415 | | outputs a |
1387 | 1416 | | trashes x, z, n |
1388 | 1417 | | { |
1394 | 1423 | Calling an extern is just the same as calling a defined routine with the |
1395 | 1424 | same constraints. |
1396 | 1425 | |
1397 | | routine chrout | |
1426 | | define chrout routine | |
1398 | 1427 | | inputs a |
1399 | 1428 | | trashes a |
1400 | 1429 | | @ 65490 |
1401 | 1430 | | |
1402 | | routine main | |
1431 | | define main routine | |
1403 | 1432 | | trashes a, z, n |
1404 | 1433 | | { |
1405 | 1434 | | ld a, 65 |
1407 | 1436 | | } |
1408 | 1437 | = ok |
1409 | 1438 | |
1410 | | routine chrout | |
1439 | | define chrout routine | |
1411 | 1440 | | inputs a |
1412 | 1441 | | trashes a |
1413 | 1442 | | @ 65490 |
1414 | 1443 | | |
1415 | | routine main | |
1444 | | define main routine | |
1416 | 1445 | | trashes a, z, n |
1417 | 1446 | | { |
1418 | 1447 | | call chrout |
1419 | 1448 | | } |
1420 | 1449 | ? UnmeaningfulReadError: a |
1421 | 1450 | |
1422 | | routine chrout | |
1451 | | define chrout routine | |
1423 | 1452 | | inputs a |
1424 | 1453 | | trashes a |
1425 | 1454 | | @ 65490 |
1426 | 1455 | | |
1427 | | routine main | |
1456 | | define main routine | |
1428 | 1457 | | trashes a, x, z, n |
1429 | 1458 | | { |
1430 | 1459 | | ld a, 65 |
1437 | 1466 | |
1438 | 1467 | Trash does nothing except indicate that we do not care about the value anymore. |
1439 | 1468 | |
1440 | | routine foo | |
1469 | | define foo routine | |
1441 | 1470 | | inputs a |
1442 | 1471 | | outputs x |
1443 | 1472 | | trashes a, z, n |
1448 | 1477 | | } |
1449 | 1478 | = ok |
1450 | 1479 | |
1451 | | routine foo | |
1480 | | define foo routine | |
1452 | 1481 | | inputs a |
1453 | 1482 | | outputs a, x |
1454 | 1483 | | trashes z, n |
1459 | 1488 | | } |
1460 | 1489 | ? UnmeaningfulOutputError: a |
1461 | 1490 | |
1462 | | routine foo | |
1491 | | define foo routine | |
1463 | 1492 | | inputs a |
1464 | 1493 | | outputs x |
1465 | 1494 | | trashes a, z, n |
1474 | 1503 | |
1475 | 1504 | Both blocks of an `if` are analyzed. |
1476 | 1505 | |
1477 | | routine foo | |
1506 | | define foo routine | |
1478 | 1507 | | inputs a |
1479 | 1508 | | outputs x |
1480 | 1509 | | trashes a, z, n, c |
1490 | 1519 | |
1491 | 1520 | If a location is initialized in one block, is must be initialized in the other as well. |
1492 | 1521 | |
1493 | | routine foo | |
1522 | | define foo routine | |
1494 | 1523 | | inputs a |
1495 | 1524 | | outputs x |
1496 | 1525 | | trashes a, z, n, c |
1504 | 1533 | | } |
1505 | 1534 | ? InconsistentInitializationError: x |
1506 | 1535 | |
1507 | | routine foo | |
1536 | | define foo routine | |
1508 | 1537 | | inputs a |
1509 | 1538 | | outputs x |
1510 | 1539 | | trashes a, z, n, c |
1518 | 1547 | | } |
1519 | 1548 | ? InconsistentInitializationError: x |
1520 | 1549 | |
1521 | | routine foo | |
1550 | | define foo routine | |
1522 | 1551 | | inputs a |
1523 | 1552 | | outputs x |
1524 | 1553 | | trashes a, z, n, c |
1537 | 1566 | input to the routine, and it is initialized in one branch, it need not |
1538 | 1567 | be initialized in the other. |
1539 | 1568 | |
1540 | | routine foo | |
1569 | | define foo routine | |
1541 | 1570 | | outputs x |
1542 | 1571 | | trashes a, z, n, c |
1543 | 1572 | | { |
1552 | 1581 | | } |
1553 | 1582 | = ok |
1554 | 1583 | |
1555 | | routine foo | |
1584 | | define foo routine | |
1556 | 1585 | | inputs x |
1557 | 1586 | | outputs x |
1558 | 1587 | | trashes a, z, n, c |
1569 | 1598 | |
1570 | 1599 | An `if` with a single block is analyzed as if it had an empty `else` block. |
1571 | 1600 | |
1572 | | routine foo | |
1601 | | define foo routine | |
1573 | 1602 | | inputs a |
1574 | 1603 | | outputs x |
1575 | 1604 | | trashes a, z, n, c |
1581 | 1610 | | } |
1582 | 1611 | ? InconsistentInitializationError: x |
1583 | 1612 | |
1584 | | routine foo | |
1613 | | define foo routine | |
1585 | 1614 | | inputs a |
1586 | 1615 | | outputs x |
1587 | 1616 | | trashes a, z, n, c |
1594 | 1623 | | } |
1595 | 1624 | = ok |
1596 | 1625 | |
1597 | | routine foo | |
1626 | | define foo routine | |
1598 | 1627 | | inputs a |
1599 | 1628 | | outputs x |
1600 | 1629 | | trashes a, z, n, c |
1611 | 1640 | trashes {`a`} and the other branch trashes {`b`} then the whole `if` statement |
1612 | 1641 | trashes {`a`, `b`}. |
1613 | 1642 | |
1614 | | routine foo | |
1643 | | define foo routine | |
1615 | 1644 | | inputs a, x, z |
1616 | 1645 | | trashes a, x |
1617 | 1646 | | { |
1623 | 1652 | | } |
1624 | 1653 | = ok |
1625 | 1654 | |
1626 | | routine foo | |
1655 | | define foo routine | |
1627 | 1656 | | inputs a, x, z |
1628 | 1657 | | trashes a |
1629 | 1658 | | { |
1635 | 1664 | | } |
1636 | 1665 | ? ForbiddenWriteError: x (in foo, line 10) |
1637 | 1666 | |
1638 | | routine foo | |
1667 | | define foo routine | |
1639 | 1668 | | inputs a, x, z |
1640 | 1669 | | trashes x |
1641 | 1670 | | { |
1651 | 1680 | |
1652 | 1681 | Repeat loop. |
1653 | 1682 | |
1654 | | routine main | |
1683 | | define main routine | |
1655 | 1684 | | outputs x, y, n, z, c |
1656 | 1685 | | { |
1657 | 1686 | | ld x, 0 |
1666 | 1695 | |
1667 | 1696 | You can initialize something inside the loop that was uninitialized outside. |
1668 | 1697 | |
1669 | | routine main | |
1698 | | define main routine | |
1670 | 1699 | | outputs x, y, n, z, c |
1671 | 1700 | | { |
1672 | 1701 | | ld x, 0 |
1681 | 1710 | But you can't UNinitialize something at the end of the loop that you need |
1682 | 1711 | initialized at the start. |
1683 | 1712 | |
1684 | | routine foo | |
1713 | | define foo routine | |
1685 | 1714 | | trashes y |
1686 | 1715 | | { |
1687 | 1716 | | } |
1688 | 1717 | | |
1689 | | routine main | |
1718 | | define main routine | |
1690 | 1719 | | outputs x, y, n, z, c |
1691 | 1720 | | { |
1692 | 1721 | | ld x, 0 |
1706 | 1735 | | word one : 0 |
1707 | 1736 | | word two : 0 |
1708 | 1737 | | |
1709 | | routine main | |
1738 | | define main routine | |
1710 | 1739 | | inputs one, two |
1711 | 1740 | | outputs two |
1712 | 1741 | | trashes a, z, n |
1719 | 1748 | |
1720 | 1749 | The body of `repeat forever` can be empty. |
1721 | 1750 | |
1722 | | routine main | |
1751 | | define main routine | |
1723 | 1752 | | { |
1724 | 1753 | | repeat { |
1725 | 1754 | | } forever |
1728 | 1757 | |
1729 | 1758 | While `repeat` is most often used with `z`, it can also be used with `n`. |
1730 | 1759 | |
1731 | | routine main | |
1760 | | define main routine | |
1732 | 1761 | | outputs y, n, z |
1733 | 1762 | | { |
1734 | 1763 | | ld y, 15 |
1742 | 1771 | |
1743 | 1772 | Basic "open-faced for" loop. We'll start with the "upto" variant. |
1744 | 1773 | |
1745 | In a "for" loop, we know the exact range the loop variable takes on. | |
1774 | #### upward-counting variant | |
1775 | ||
1776 | Even though we do not give the starting value in the "for" construct, | |
1777 | we know the exact range the loop variable takes on. | |
1746 | 1778 | |
1747 | 1779 | | byte table[16] tab |
1748 | 1780 | | |
1774 | 1806 | | } |
1775 | 1807 | | } |
1776 | 1808 | ? UnmeaningfulReadError |
1809 | ||
1810 | Because routines currently do not include range constraints, | |
1811 | the loop variable may not be useful as an input (the location | |
1812 | is assumed to have the maximum range.) | |
1813 | ||
1814 | | byte table[16] tab | |
1815 | | | |
1816 | | define foo routine | |
1817 | | inputs tab, x | |
1818 | | trashes a, x, c, z, v, n { | |
1819 | | for x up to 15 { | |
1820 | | ld a, 0 | |
1821 | | } | |
1822 | | } | |
1823 | ? RangeExceededError | |
1777 | 1824 | |
1778 | 1825 | You cannot modify the loop variable in a "for" loop. |
1779 | 1826 | |
1845 | 1892 | | } |
1846 | 1893 | ? RangeExceededError |
1847 | 1894 | |
1895 | You can initialize something inside the loop that was uninitialized outside. | |
1896 | ||
1897 | | define main routine | |
1898 | | outputs x, y, n, z | |
1899 | | trashes c | |
1900 | | { | |
1901 | | ld x, 0 | |
1902 | | for x up to 15 { | |
1903 | | ld y, 15 | |
1904 | | } | |
1905 | | } | |
1906 | = ok | |
1907 | ||
1908 | But you can't UNinitialize something at the end of the loop that you need | |
1909 | initialized at the start of that loop. | |
1910 | ||
1911 | | define foo routine | |
1912 | | trashes y | |
1913 | | { | |
1914 | | } | |
1915 | | | |
1916 | | define main routine | |
1917 | | outputs x, y, n, z | |
1918 | | trashes c | |
1919 | | { | |
1920 | | ld x, 0 | |
1921 | | ld y, 15 | |
1922 | | for x up to 15 { | |
1923 | | inc y | |
1924 | | call foo | |
1925 | | } | |
1926 | | } | |
1927 | ? UnmeaningfulReadError: y | |
1928 | ||
1929 | The "for" loop does not preserve the `z` or `n` registers. | |
1930 | ||
1931 | | define foo routine trashes x { | |
1932 | | ld x, 0 | |
1933 | | for x up to 15 { | |
1934 | | } | |
1935 | | } | |
1936 | ? ForbiddenWriteError | |
1937 | ||
1938 | But it does preserve the other registers, such as `c`. | |
1939 | ||
1940 | | define foo routine trashes x, z, n { | |
1941 | | ld x, 0 | |
1942 | | for x up to 15 { | |
1943 | | } | |
1944 | | } | |
1945 | = ok | |
1946 | ||
1947 | In fact it does not strictly trash `z` and `n`, as they are | |
1948 | always set to known values after the loop. TODO: document | |
1949 | what these known values are! | |
1950 | ||
1951 | | define foo routine outputs z, n trashes x { | |
1952 | | ld x, 0 | |
1953 | | for x up to 15 { | |
1954 | | } | |
1955 | | } | |
1956 | = ok | |
1957 | ||
1958 | #### downward-counting variant | |
1959 | ||
1848 | 1960 | In a "for" loop (downward-counting variant), we know the exact range the loop variable takes on. |
1849 | 1961 | |
1850 | 1962 | | byte table[16] tab |
1902 | 2014 | | } |
1903 | 2015 | ? RangeExceededError |
1904 | 2016 | |
1905 | You can initialize something inside the loop that was uninitialized outside. | |
1906 | ||
1907 | | routine main | |
1908 | | outputs x, y, n, z | |
1909 | | trashes c | |
1910 | | { | |
1911 | | ld x, 0 | |
1912 | | for x up to 15 { | |
1913 | | ld y, 15 | |
1914 | | } | |
1915 | | } | |
1916 | = ok | |
1917 | ||
1918 | But you can't UNinitialize something at the end of the loop that you need | |
1919 | initialized at the start of that loop. | |
1920 | ||
1921 | | routine foo | |
1922 | | trashes y | |
1923 | | { | |
1924 | | } | |
1925 | | | |
1926 | | routine main | |
1927 | | outputs x, y, n, z | |
1928 | | trashes c | |
1929 | | { | |
1930 | | ld x, 0 | |
1931 | | ld y, 15 | |
1932 | | for x up to 15 { | |
1933 | | inc y | |
1934 | | call foo | |
1935 | | } | |
1936 | | } | |
1937 | ? UnmeaningfulReadError: y | |
2017 | The "for" loop does not preserve the `z` or `n` registers. | |
2018 | ||
2019 | | define foo routine trashes x { | |
2020 | | ld x, 15 | |
2021 | | for x down to 0 { | |
2022 | | } | |
2023 | | } | |
2024 | ? ForbiddenWriteError | |
2025 | ||
2026 | But it does preserve the other registers, such as `c`. | |
2027 | ||
2028 | | define foo routine trashes x, z, n { | |
2029 | | ld x, 15 | |
2030 | | for x down to 0 { | |
2031 | | } | |
2032 | | } | |
2033 | = ok | |
2034 | ||
2035 | In fact it does not strictly trash `z` and `n`, as they are | |
2036 | always set to known values after the loop. TODO: document | |
2037 | what these known values are! | |
2038 | ||
2039 | | define foo routine outputs z, n trashes x { | |
2040 | | ld x, 15 | |
2041 | | for x down to 0 { | |
2042 | | } | |
2043 | | } | |
2044 | = ok | |
1938 | 2045 | |
1939 | 2046 | ### save ### |
1940 | 2047 | |
1941 | 2048 | Basic neutral test, where the `save` makes no difference. |
1942 | 2049 | |
1943 | | routine main | |
2050 | | define main routine | |
1944 | 2051 | | inputs a, x |
1945 | 2052 | | outputs a, x |
1946 | 2053 | | trashes z, n |
1955 | 2062 | |
1956 | 2063 | Saving any location (other than `a`) will trash `a`. |
1957 | 2064 | |
1958 | | routine main | |
2065 | | define main routine | |
1959 | 2066 | | inputs a, x |
1960 | 2067 | | outputs a, x |
1961 | 2068 | | trashes z, n |
1969 | 2076 | |
1970 | 2077 | Saving `a` does not trash anything. |
1971 | 2078 | |
1972 | | routine main | |
2079 | | define main routine | |
1973 | 2080 | | inputs a, x |
1974 | 2081 | | outputs a, x |
1975 | 2082 | | trashes z, n |
1985 | 2092 | A defined value that has been saved can be trashed inside the block. |
1986 | 2093 | It will continue to be defined outside the block. |
1987 | 2094 | |
1988 | | routine main | |
2095 | | define main routine | |
1989 | 2096 | | outputs x, y |
1990 | 2097 | | trashes a, z, n |
1991 | 2098 | | { |
2000 | 2107 | A trashed value that has been saved can be used inside the block. |
2001 | 2108 | It will continue to be trashed outside the block. |
2002 | 2109 | |
2003 | | routine main | |
2110 | (Note, both x and a are unmeaningful in this test.) | |
2111 | ||
2112 | | define main routine | |
2004 | 2113 | | inputs a |
2005 | 2114 | | outputs a, x |
2006 | 2115 | | trashes z, n |
2012 | 2121 | | ld x, 1 |
2013 | 2122 | | } |
2014 | 2123 | | } |
2015 | ? UnmeaningfulOutputError: x | |
2124 | ? UnmeaningfulOutputError | |
2016 | 2125 | |
2017 | 2126 | The known range of a value will be preserved outside the block as well. |
2018 | 2127 | |
2019 | 2128 | | word one: 77 |
2020 | 2129 | | word table[32] many |
2021 | 2130 | | |
2022 | | routine main | |
2131 | | define main routine | |
2023 | 2132 | | inputs a, many, one |
2024 | 2133 | | outputs many, one |
2025 | 2134 | | trashes a, x, n, z |
2037 | 2146 | | word one: 77 |
2038 | 2147 | | word table[32] many < |