Checkpoint import of changes for version 0.19.
Chris Pressey
3 years ago
0 | 0 | History of SixtyPical |
1 | 1 | ===================== |
2 | ||
3 | 0.19 | |
4 | ---- | |
5 | ||
6 | * A `table` may be defined with more than 256 entries, even | |
7 | though the conventional index syntax can only refer to the | |
8 | first 256 entries. | |
9 | * A `pointer` may point inside values of type `byte table`, | |
10 | allowing access to entries beyond the 256th. | |
11 | * `buffer` types have been eliminated from the language, | |
12 | as the above two improvements allow `byte table`s to | |
13 | do everything `buffer`s previously did. | |
14 | * When accessing a table with an index, a constant offset | |
15 | can also be given. | |
16 | * Accessing a `table` through a `pointer` must be done in | |
17 | the context of a `point ... into` block. This allows the | |
18 | analyzer to check *which* table is being modified. | |
19 | * Added `--dump-exit-contexts` option to `sixtypical`. | |
2 | 20 | |
3 | 21 | 0.18 |
4 | 22 | ---- |
11 | 11 | Which uses some other storage location instead of the stack. A local static |
12 | 12 | would be a good candidate for such. |
13 | 13 | |
14 | ### Associate each pointer with the buffer it points into | |
14 | ### Analyze `call` within blocks? | |
15 | 15 | |
16 | Check that the buffer being read or written to through pointer, appears in appropriate | |
17 | inputs or outputs set. | |
16 | What happens if you call another routine from inside a `with interrupts off` block? | |
18 | 17 | |
19 | In the analysis, when we obtain a pointer, we need to record, in context, what buffer | |
20 | that pointer came from. | |
18 | What happens if you call another routine from inside a `save` block? | |
21 | 19 | |
22 | When we write through that pointer, we need to set that buffer as written. | |
20 | What happens if you call another routine from inside a `point into` block? | |
23 | 21 | |
24 | When we read through the pointer, we need to check that the buffer is readable. | |
22 | What happens if you call another routine from inside a `for` block? | |
25 | 23 | |
26 | ### Table overlays | |
24 | Remember that any of these may have a `goto` ... and they may have a second | |
25 | instance of the same block (e.g. `with interrupts off` nested within | |
26 | `with interrupts off` shouldn't be allowed to turn them back on after the | |
27 | inner block has finished -- even if there is no `call`.) | |
27 | 28 | |
28 | They are uninitialized, but the twist is, the address is a buffer that is | |
29 | an input to and/or output of the routine. So, they are defined (insofar | |
30 | as the buffer is defined.) | |
29 | These holes need to be plugged. | |
31 | 30 | |
32 | They are therefore a "view" of a section of a buffer. | |
31 | ### Pointers associated globally with a table | |
33 | 32 | |
34 | This is slightly dangerous since it does permit aliases: the buffer and the | |
35 | table refer to the same memory. | |
33 | We have `point into` blocks, but we would also like to sometimes pass a pointer | |
34 | around to different routines, and have them all "know" what table it operates on. | |
36 | 35 | |
37 | Although, if they are `static`, you could say, in the routine in which they | |
38 | are `static`, as soon as you've established one, you can no longer use the | |
39 | buffer; and the ones you establish must be disjoint. | |
36 | We could associate every pointer variable with a specific table variable, in its | |
37 | declaration. This makes some things simple, and would allow us to know what table a | |
38 | pointer is supposed to point into, even if that pointer was passed into our routine. | |
40 | 39 | |
41 | (That seems to be the most compelling case for restricting them to `static`.) | |
40 | One drawback is that it would limit each pointer to be used only on one table. Since a | |
41 | pointer basically represents a zero-page location, and since those are a relatively scarce | |
42 | resource, we would prefer if a single pointer could be used to point into different tables | |
43 | at different times. | |
42 | 44 | |
43 | An alternative would be `static` pointers, which are currently not possible because | |
44 | pointers must be zero-page, thus `@`, thus uninitialized. | |
45 | These can co-exist with general, non-specific-table-linked `pointer` variables. | |
46 | ||
47 | ### Local non-statics | |
48 | ||
49 | Somewhat related to the above, it should be possible to declare a local storage | |
50 | location which is not static. | |
51 | ||
52 | In this case, it would be considered uninitialized each time the routine was | |
53 | entered. | |
54 | ||
55 | So, you do not have a guarantee that it has a valid value. But you are guaranteed | |
56 | that no other routine can read or modify it. | |
57 | ||
58 | It also enables a trick: if there are two routines A and B, and A never calls B | |
59 | (even indirectly), and B never calls A (even indirectly), then their locals can | |
60 | be allocated at the same space. | |
61 | ||
62 | A local could also be given an explicit address. In this case, two locals in | |
63 | different routines could be given the same address, and as long as the condition | |
64 | in the above paragraph holds, that's okay. (If it doesn't, the analyzer should | |
65 | detect it.) | |
66 | ||
67 | This would permit local pointers, which would be one way of addressing the | |
68 | "same pointer to different tables" problem. | |
69 | ||
70 | ### Copy byte to/from table | |
71 | ||
72 | Do we want a `copy bytevar, table + x` instruction? We don't currently have one. | |
73 | You have to `ld a`, `st a`. I think maybe we should have one. | |
45 | 74 | |
46 | 75 | ### Tail-call optimization |
47 | 76 |
11 | 11 | |
12 | 12 | # ----------------------------------------------------------------- # |
13 | 13 | |
14 | from argparse import ArgumentParser | |
14 | 15 | import codecs |
15 | from argparse import ArgumentParser | |
16 | import json | |
16 | 17 | from pprint import pprint |
17 | 18 | import sys |
18 | 19 | import traceback |
42 | 43 | program = merge_programs(programs) |
43 | 44 | |
44 | 45 | analyzer = Analyzer(debug=options.debug) |
45 | analyzer.analyze_program(program) | |
46 | ||
47 | try: | |
48 | analyzer.analyze_program(program) | |
49 | finally: | |
50 | if options.dump_exit_contexts: | |
51 | sys.stdout.write(json.dumps(analyzer.exit_contexts_map, indent=4, sort_keys=True, separators=(',', ':'))) | |
52 | sys.stdout.write("\n") | |
46 | 53 | |
47 | 54 | compilation_roster = None |
48 | 55 | if options.optimize_fallthru: |
49 | 56 | from sixtypical.fallthru import FallthruAnalyzer |
50 | 57 | |
51 | 58 | def dump(data, label=None): |
52 | import json | |
53 | 59 | if not options.dump_fallthru_info: |
54 | 60 | return |
55 | 61 | if label: |
114 | 120 | help="Only parse and analyze the program; do not compile it." |
115 | 121 | ) |
116 | 122 | argparser.add_argument( |
123 | "--dump-exit-contexts", | |
124 | action="store_true", | |
125 | help="Dump a map, in JSON, of the analysis context at each exit of each routine " | |
126 | "after analyzing the program." | |
127 | ) | |
128 | argparser.add_argument( | |
117 | 129 | "--optimize-fallthru", |
118 | 130 | action="store_true", |
119 | 131 | help="Reorder the routines in the program to maximize the number of tail calls " |
122 | 134 | argparser.add_argument( |
123 | 135 | "--dump-fallthru-info", |
124 | 136 | action="store_true", |
125 | help="Dump the fallthru map and ordering to stdout after analyzing the program." | |
137 | help="Dump the ordered fallthru map, in JSON, to stdout after analyzing the program." | |
126 | 138 | ) |
127 | 139 | argparser.add_argument( |
128 | 140 | "--parse-only", |
0 | 0 | SixtyPical |
1 | 1 | ========== |
2 | 2 | |
3 | This document describes the SixtyPical programming language version 0.15, | |
3 | This document describes the SixtyPical programming language version 0.19, | |
4 | 4 | both its static semantics (the capabilities and limits of the static |
5 | 5 | analyses it defines) and its runtime semantics (with reference to the |
6 | 6 | semantics of 6502 machine code.) |
11 | 11 | Refer to the bottom of this document for an EBNF grammar of the syntax of |
12 | 12 | the language. |
13 | 13 | |
14 | Types | |
15 | ----- | |
16 | ||
17 | There are five *primitive types* in SixtyPical: | |
14 | Data Model | |
15 | ---------- | |
16 | ||
17 | SixtyPical defines a data model where every value has some type | |
18 | information associated with it. The values include those that are | |
19 | directly manipulable by a SixtyPical program, but are not limited to them. | |
20 | Type information includes not only what kind of structure the data has, | |
21 | but other properties as well (sometimes called "type annotations".) | |
22 | ||
23 | ### Basic types ### | |
24 | ||
25 | SixtyPical defines a handful of basic types. There are three types that | |
26 | are "primitive" in that they are not parameterized in any way: | |
18 | 27 | |
19 | 28 | * bit (2 possible values) |
20 | 29 | * byte (256 possible values) |
21 | 30 | * word (65536 possible values) |
31 | ||
32 | Types can also be parameterized and constructed from other types | |
33 | (which is a kind of parameterization). One such type constructor is | |
34 | ||
35 | * pointer (16-bit address of a byte inside a byte table) | |
36 | * vector T (address of a value of type T; T must be a routine type) | |
37 | ||
38 | Values of the above-listed types are directly manipulable by a SixtyPical | |
39 | program. Other types describe values which can only be indirectly | |
40 | manipulated by a program: | |
41 | ||
22 | 42 | * routine (code stored somewhere in memory, read-only) |
23 | * pointer (address of a byte in a buffer) | |
24 | ||
25 | There are also three *type constructors*: | |
26 | ||
27 | * T table[N] (N entries, 1 ≤ N ≤ 256; each entry holds a value | |
28 | of type T, where T is `byte`, `word`, or `vector`) | |
29 | * buffer[N] (N entries; each entry is a byte; 1 ≤ N ≤ 65536) | |
30 | * vector T (address of a value of type T; T must be a routine type) | |
31 | ||
32 | ### User-defined ### | |
43 | * T table[N] (series of 1 ≤ N ≤ 65536 values of type T) | |
44 | ||
45 | There are some restrictions here; for example, a table may only | |
46 | consist of `byte`, `word`, or `vector` types. A pointer may only | |
47 | point to a byte inside a `table` of `byte` type. | |
48 | ||
49 | Each routine is associated with a rich set of type information, | |
50 | which is basically the types and statuses of memory locations that | |
51 | have been declared as being relevant to that routine. | |
52 | ||
53 | #### User-defined #### | |
33 | 54 | |
34 | 55 | A program may define its own types using the `typedef` feature. Typedefs |
35 | 56 | must occur before everything else in the program. A typedef takes a |
36 | 57 | type expression and an identifier which has not previously been used in |
37 | 58 | the program. It associates that identifer with that type. This is merely |
38 | a type alias; two types with different names will compare as equal. | |
39 | ||
40 | Memory locations | |
41 | ---------------- | |
59 | a type alias; if two types have identical structure but different names, | |
60 | they will compare as equal. | |
61 | ||
62 | ### Memory locations ### | |
42 | 63 | |
43 | 64 | A primary concept in SixtyPical is the *memory location*. At any given point |
44 | 65 | in time during execution, each memory location is either *uninitialized* or |
50 | 71 | There are four general kinds of memory location. The first three are |
51 | 72 | pre-defined and built-in. |
52 | 73 | |
53 | ### Registers ### | |
74 | #### Registers #### | |
54 | 75 | |
55 | 76 | Each of these hold a byte. They are initially uninitialized. |
56 | 77 | |
58 | 79 | x |
59 | 80 | y |
60 | 81 | |
61 | ### Flags ### | |
82 | #### Flags #### | |
62 | 83 | |
63 | 84 | Each of these hold a bit. They are initially uninitialized. |
64 | 85 | |
67 | 88 | v (overflow) |
68 | 89 | n (negative) |
69 | 90 | |
70 | ### Constants ### | |
91 | #### Constants #### | |
71 | 92 | |
72 | 93 | It may be strange to think of constants as memory locations, but keep in mind |
73 | 94 | that a memory location in SixtyPical need not map to a memory location in the |
96 | 117 | Note that if a word constant is between 256 and 65535, the leading `word` |
97 | 118 | token can be omitted. |
98 | 119 | |
99 | ### User-defined ### | |
120 | #### User-defined #### | |
100 | 121 | |
101 | 122 | There may be any number of user-defined memory locations. They are defined |
102 | 123 | by giving the type (which may be any type except `bit` and `routine`) and the |
136 | 157 | that literal integers in the code are always immediate values. (But this |
137 | 158 | may change at some point.) |
138 | 159 | |
139 | ### Buffers and Pointers ### | |
140 | ||
141 | Roughly speaking, a `buffer` is a table that can be longer than 256 bytes, | |
142 | and a `pointer` is an address within a buffer. | |
160 | ### Tables and Pointers ### | |
161 | ||
162 | A table is a collection of memory locations that can be indexed in a number | |
163 | of ways. | |
164 | ||
165 | The simplest way is to use another memory location as an index. There | |
166 | are restrictions on which memory locations can be used as indexes; | |
167 | only the `x` and `y` locations can be used this way. Since those can | |
168 | only hold a byte, this method, by itself, only allows access to the first | |
169 | 256 entries of the table. | |
170 | ||
171 | byte table[1024] tab | |
172 | ... | |
173 | ld a, tab + x | |
174 | st a, tab + y | |
175 | ||
176 | However, by combining indexing with a constant _offset_, entries beyond the | |
177 | 256th entry can be accessed. | |
178 | ||
179 | byte table[1024] tab | |
180 | ... | |
181 | ld a, tab + 512 + x | |
182 | st a, tab + 512 + y | |
183 | ||
184 | Even with an offset, the range of indexing still cannot exceed 256 entries. | |
185 | Accessing entries at an arbitrary address inside a table can be done with | |
186 | a `pointer`. Pointers can only be point inside `byte` tables. When a | |
187 | pointer is used, indexing with `x` or `y` will also take place. | |
143 | 188 | |
144 | 189 | A `pointer` is implemented as a zero-page memory location, and accessing the |
145 | buffer pointed to is implemented with "indirect indexed" addressing, as in | |
190 | table pointed to is implemented with "indirect indexed" addressing, as in | |
146 | 191 | |
147 | 192 | LDA ($02), Y |
148 | 193 | STA ($02), Y |
150 | 195 | There are extended instruction modes for using these types of memory location. |
151 | 196 | See `copy` below, but here is some illustrative example code: |
152 | 197 | |
153 | copy ^buf, ptr // this is the only way to initialize a pointer | |
154 | add ptr, 4 // ok, but only if it does not exceed buffer's size | |
155 | ld y, 0 // you must set this to something yourself | |
156 | copy [ptr] + y, byt // read memory through pointer, into byte | |
157 | copy 100, [ptr] + y // write memory through pointer (still trashes a) | |
158 | ||
159 | where `ptr` is a user-defined storage location of `pointer` type, and the | |
160 | `+ y` part is mandatory. | |
198 | point ptr into buf { // this is the only way to initialize a pointer | |
199 | add ptr, 4 // note, this is unchecked against table's size! | |
200 | ld y, 0 // you must set this to something yourself | |
201 | copy [ptr] + y, byt // read memory through pointer, into byte | |
202 | copy 100, [ptr] + y // write memory through pointer (still trashes a) | |
203 | } // after this block, ptr can no longer be used | |
204 | ||
205 | where `ptr` is a user-defined storage location of `pointer` type, `buf` | |
206 | is a `table` of `byte` type, and the `+ y` part is mandatory. | |
161 | 207 | |
162 | 208 | Routines |
163 | 209 | -------- |
299 | 345 | After execution, dest is considered initialized, and `z` and `n`, and |
300 | 346 | `a` are considered uninitialized. |
301 | 347 | |
302 | There are two extra modes that this instruction can be used in. The first is | |
303 | to load an address into a pointer: | |
304 | ||
305 | copy ^<src-memory-location>, <dest-memory-location> | |
306 | ||
307 | This copies the address of src into dest. In this case, src must be | |
308 | of type buffer, and dest must be of type pointer. src will not be | |
309 | considered a memory location that is read, since it is only its address | |
310 | that is being retrieved. | |
311 | ||
312 | The second is to read or write indirectly through a pointer. | |
348 | There is an extra mode that this instruction can be used in: | |
313 | 349 | |
314 | 350 | copy [<src-memory-location>] + y, <dest-memory-location> |
315 | 351 | copy <src-memory-location>, [<dest-memory-location>] + y |
349 | 385 | when the dest is `a`. |
350 | 386 | |
351 | 387 | NOTE: If dest is a pointer, the addition does not check if the result of |
352 | the pointer arithmetic continues to be valid (within a buffer) or not. | |
388 | the pointer arithmetic continues to be valid (within a table) or not. | |
353 | 389 | |
354 | 390 | ### inc ### |
355 | 391 | |
580 | 616 | Program ::= {ConstDefn | TypeDefn} {Defn} {Routine}. |
581 | 617 | ConstDefn::= "const" Ident<new> Const. |
582 | 618 | TypeDefn::= "typedef" Type Ident<new>. |
583 | Defn ::= Type Ident<new> [Constraints] (":" Const | "@" LitWord). | |
619 | Defn ::= Type Ident<new> (":" Const | "@" LitWord). | |
584 | 620 | Type ::= TypeTerm ["table" TypeSize]. |
585 | 621 | TypeExpr::= "byte" |
586 | 622 | | "word" |
587 | | "buffer" TypeSize | |
588 | 623 | | "pointer" |
589 | 624 | | "vector" TypeTerm |
590 | 625 | | "routine" Constraints |
593 | 628 | TypeSize::= "[" LitWord "]". |
594 | 629 | Constrnt::= ["inputs" LocExprs] ["outputs" LocExprs] ["trashes" LocExprs]. |
595 | 630 | Routine ::= "define" Ident<new> Type (Block | "@" LitWord). |
596 | | "routine" Ident<new> Constraints (Block | "@" LitWord) | |
597 | . | |
598 | 631 | LocExprs::= LocExpr {"," LocExpr}. |
599 | LocExpr ::= Register | Flag | Const | Ident. | |
632 | LocExpr ::= Register | Flag | Const | Ident [["+" Const] "+" Register]. | |
600 | 633 | Register::= "a" | "x" | "y". |
601 | 634 | Flag ::= "c" | "z" | "n" | "v". |
602 | 635 | Const ::= Literal | Ident<const>. |
20 | 20 | // and the end of their own routines, so the type needs to be compatible. |
21 | 21 | // (In a good sense, it is a continuation.) |
22 | 22 | // |
23 | // Further, | |
24 | // | |
25 | // It's very arguable that screen1/2/3/4 and colormap1/2/3/4 are not REALLY inputs. | |
26 | // They're only there to support the fact that game states sometimes clear the | |
27 | // screen, and sometimes don't. When they don't, they preserve the screen, and | |
28 | // currently the way to say "we preserve the screen" is to have it as both input | |
29 | // and output. There is probably a better way to do this, but it needs thought. | |
30 | // | |
31 | 23 | |
32 | 24 | typedef routine |
33 | 25 | inputs joy2, press_fire_msg, dispatch_game_state, |
34 | 26 | actor_pos, actor_delta, actor_logic, |
35 | 27 | player_died, |
36 | screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 | |
28 | screen, colormap | |
37 | 29 | outputs dispatch_game_state, |
38 | 30 | actor_pos, actor_delta, actor_logic, |
39 | 31 | player_died, |
40 | screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 | |
32 | screen, colormap | |
41 | 33 | trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic |
42 | 34 | game_state_routine |
43 | 35 | |
61 | 53 | |
62 | 54 | byte vic_border @ 53280 |
63 | 55 | byte vic_bg @ 53281 |
64 | ||
65 | byte table[256] screen1 @ 1024 | |
66 | byte table[256] screen2 @ 1274 | |
67 | byte table[256] screen3 @ 1524 | |
68 | byte table[256] screen4 @ 1774 | |
69 | ||
70 | byte table[256] colormap1 @ 55296 | |
71 | byte table[256] colormap2 @ 55546 | |
72 | byte table[256] colormap3 @ 55796 | |
73 | byte table[256] colormap4 @ 56046 | |
74 | ||
75 | buffer[2048] screen @ 1024 | |
56 | byte table[2048] screen @ 1024 | |
57 | byte table[2048] colormap @ 55296 | |
76 | 58 | byte joy2 @ $dc00 |
77 | 59 | |
78 | 60 | // ---------------------------------------------------------------- |
186 | 168 | } |
187 | 169 | |
188 | 170 | define clear_screen routine |
189 | outputs screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 | |
171 | outputs screen, colormap | |
190 | 172 | trashes a, y, c, n, z |
191 | 173 | { |
192 | 174 | ld y, 0 |
193 | 175 | repeat { |
194 | 176 | ld a, 1 |
195 | st a, colormap1 + y | |
196 | st a, colormap2 + y | |
197 | st a, colormap3 + y | |
198 | st a, colormap4 + y | |
177 | st a, colormap + y | |
178 | st a, colormap + 250 + y | |
179 | st a, colormap + 500 + y | |
180 | st a, colormap + 750 + y | |
199 | 181 | |
200 | 182 | ld a, 32 |
201 | st a, screen1 + y | |
202 | st a, screen2 + y | |
203 | st a, screen3 + y | |
204 | st a, screen4 + y | |
183 | st a, screen + y | |
184 | st a, screen + 250 + y | |
185 | st a, screen + 500 + y | |
186 | st a, screen + 750 + y | |
205 | 187 | |
206 | 188 | inc y |
207 | 189 | cmp y, 250 |
281 | 263 | call check_new_position_in_bounds |
282 | 264 | |
283 | 265 | if c { |
284 | copy ^screen, ptr | |
285 | st off, c | |
286 | add ptr, new_pos | |
287 | ld y, 0 | |
288 | ||
289 | // check collision. | |
290 | ld a, [ptr] + y | |
266 | point ptr into screen { | |
267 | st off, c | |
268 | add ptr, new_pos | |
269 | ld y, 0 | |
270 | // check collision. | |
271 | ld a, [ptr] + y | |
272 | } | |
273 | ||
291 | 274 | // if "collision" is with your own self, treat it as if it's blank space! |
292 | 275 | cmp a, 81 |
293 | 276 | if z { |
295 | 278 | } |
296 | 279 | cmp a, 32 |
297 | 280 | if z { |
298 | copy ^screen, ptr | |
299 | st off, c | |
300 | add ptr, pos | |
301 | copy 32, [ptr] + y | |
281 | point ptr into screen { | |
282 | st off, c | |
283 | add ptr, pos | |
284 | copy 32, [ptr] + y | |
285 | } | |
302 | 286 | |
303 | 287 | copy new_pos, pos |
304 | 288 | |
305 | copy ^screen, ptr | |
306 | st off, c | |
307 | add ptr, pos | |
308 | copy 81, [ptr] + y | |
289 | point ptr into screen { | |
290 | st off, c | |
291 | add ptr, pos | |
292 | copy 81, [ptr] + y | |
293 | } | |
309 | 294 | } else { |
310 | 295 | ld a, 1 |
311 | 296 | st a, player_died |
320 | 305 | call check_new_position_in_bounds |
321 | 306 | |
322 | 307 | if c { |
323 | copy ^screen, ptr | |
324 | st off, c | |
325 | add ptr, new_pos | |
326 | ld y, 0 | |
327 | ||
328 | // check collision. | |
329 | ld a, [ptr] + y | |
308 | point ptr into screen { | |
309 | st off, c | |
310 | add ptr, new_pos | |
311 | ld y, 0 | |
312 | // check collision. | |
313 | ld a, [ptr] + y | |
314 | } | |
330 | 315 | // if "collision" is with your own self, treat it as if it's blank space! |
331 | 316 | cmp a, 82 |
332 | 317 | if z { |
334 | 319 | } |
335 | 320 | cmp a, 32 |
336 | 321 | if z { |
337 | copy ^screen, ptr | |
338 | st off, c | |
339 | add ptr, pos | |
340 | copy 32, [ptr] + y | |
322 | point ptr into screen { | |
323 | st off, c | |
324 | add ptr, pos | |
325 | copy 32, [ptr] + y | |
326 | } | |
341 | 327 | |
342 | 328 | copy new_pos, pos |
343 | 329 | |
344 | copy ^screen, ptr | |
345 | st off, c | |
346 | add ptr, pos | |
347 | copy 82, [ptr] + y | |
330 | point ptr into screen { | |
331 | st off, c | |
332 | add ptr, pos | |
333 | copy 82, [ptr] + y | |
334 | } | |
348 | 335 | } |
349 | 336 | } else { |
350 | 337 | copy delta, compare_target |
371 | 358 | st on, c |
372 | 359 | sub a, 64 // yuck. oh well |
373 | 360 | |
374 | st a, screen1 + y | |
361 | st a, screen + y | |
375 | 362 | } |
376 | 363 | |
377 | 364 | st off, c |
443 | 430 | |
444 | 431 | define main routine |
445 | 432 | inputs cinv |
446 | outputs cinv, save_cinv, pos, dispatch_game_state, | |
447 | screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4 | |
433 | outputs cinv, save_cinv, pos, dispatch_game_state, screen, colormap | |
448 | 434 | trashes a, y, n, c, z, vic_border, vic_bg |
449 | 435 | { |
450 | 436 | ld a, 5 |
0 | 0 | // Include `support/${PLATFORM}.60p` before this source |
1 | 1 | // Should print Y |
2 | 2 | |
3 | buffer[2048] buf | |
3 | byte table[2048] buf | |
4 | 4 | pointer ptr @ 254 |
5 | 5 | byte foo |
6 | 6 |
0 | 0 | # encoding: UTF-8 |
1 | 1 | |
2 | from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save | |
2 | from sixtypical.ast import ( | |
3 | Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save, PointInto | |
4 | ) | |
3 | 5 | from sixtypical.model import ( |
4 | 6 | TYPE_BYTE, TYPE_WORD, |
5 | TableType, BufferType, PointerType, VectorType, RoutineType, | |
6 | ConstantRef, LocationRef, IndirectRef, IndexedRef, AddressRef, | |
7 | TableType, PointerType, VectorType, RoutineType, | |
8 | ConstantRef, LocationRef, IndirectRef, IndexedRef, | |
7 | 9 | REG_A, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C |
8 | 10 | ) |
9 | 11 | |
106 | 108 | unwriteable in certain contexts, such as `for` loops. |
107 | 109 | """ |
108 | 110 | def __init__(self, routines, routine, inputs, outputs, trashes): |
109 | self.routines = routines # Location -> AST node | |
110 | self.routine = routine | |
111 | self._touched = set() | |
112 | self._range = dict() | |
113 | self._writeable = set() | |
111 | self.routines = routines # LocationRef -> Routine (AST node) | |
112 | self.routine = routine # Routine (AST node) | |
113 | self._touched = set() # {LocationRef} | |
114 | self._range = dict() # LocationRef -> (Int, Int) | |
115 | self._writeable = set() # {LocationRef} | |
114 | 116 | self._terminated = False |
115 | 117 | self._gotos_encountered = set() |
118 | self._pointer_assoc = dict() | |
116 | 119 | |
117 | 120 | for ref in inputs: |
118 | 121 | if ref.is_constant(): |
136 | 139 | LocationRef.format_set(self._touched), LocationRef.format_set(self._range), LocationRef.format_set(self._writeable) |
137 | 140 | ) |
138 | 141 | |
142 | def to_json_data(self): | |
143 | return { | |
144 | 'touched': ','.join(sorted(loc.name for loc in self._touched)), | |
145 | 'range': dict((loc.name, '{}-{}'.format(rng[0], rng[1])) for (loc, rng) in self._range.items()), | |
146 | 'writeable': ','.join(sorted(loc.name for loc in self._writeable)), | |
147 | 'terminated': self._terminated, | |
148 | 'gotos_encountered': ','.join(sorted(loc.name for loc in self._gotos_encountered)), | |
149 | } | |
150 | ||
139 | 151 | def clone(self): |
140 | 152 | c = Context(self.routines, self.routine, [], [], []) |
141 | 153 | c._touched = set(self._touched) |
142 | 154 | c._range = dict(self._range) |
143 | 155 | c._writeable = set(self._writeable) |
156 | c._pointer_assoc = dict(self._pointer_assoc) | |
157 | c._gotos_encountered = set(self._gotos_encountered) | |
144 | 158 | return c |
145 | 159 | |
146 | 160 | def update_from(self, other): |
161 | """Replaces the information in this context, with the information from the other context. | |
162 | This is an overwriting action - it does not attempt to merge the contexts. | |
163 | ||
164 | We do not replace the gotos_encountered for technical reasons. (In `analyze_if`, | |
165 | we merge those sets afterwards; at the end of `analyze_routine`, they are not distinct in the | |
166 | set of contexts we are updating from, and we want to retain our own.)""" | |
147 | 167 | self.routines = other.routines |
148 | 168 | self.routine = other.routine |
149 | 169 | self._touched = set(other._touched) |
150 | 170 | self._range = dict(other._range) |
151 | 171 | self._writeable = set(other._writeable) |
152 | 172 | self._terminated = other._terminated |
153 | self._gotos_encounters = set(other._gotos_encountered) | |
173 | self._pointer_assoc = dict(other._pointer_assoc) | |
154 | 174 | |
155 | 175 | def each_meaningful(self): |
156 | 176 | for ref in self._range.keys(): |
196 | 216 | message += ' (%s)' % kwargs['message'] |
197 | 217 | raise exception_class(self.routine, message) |
198 | 218 | |
199 | def assert_in_range(self, inside, outside): | |
200 | # FIXME there's a bit of I'm-not-sure-the-best-way-to-do-this-ness, here... | |
219 | def assert_in_range(self, inside, outside, offset): | |
220 | """Given two locations, assert that the first location, offset by the given offset, | |
221 | is contained 'inside' the second location.""" | |
222 | assert isinstance(inside, LocationRef) | |
223 | assert isinstance(outside, LocationRef) | |
201 | 224 | |
202 | 225 | # inside should always be meaningful |
203 | 226 | inside_range = self._range[inside] |
207 | 230 | outside_range = self._range[outside] |
208 | 231 | else: |
209 | 232 | outside_range = outside.max_range() |
210 | if isinstance(outside.type, TableType): | |
211 | outside_range = (0, outside.type.size-1) | |
212 | ||
213 | if inside_range[0] < outside_range[0] or inside_range[1] > outside_range[1]: | |
233 | ||
234 | if (inside_range[0] + offset.value) < outside_range[0] or (inside_range[1] + offset.value) > outside_range[1]: | |
214 | 235 | raise RangeExceededError(self.routine, |
215 | "Possible range of {} {} exceeds acceptable range of {} {}".format( | |
216 | inside, inside_range, outside, outside_range | |
236 | "Possible range of {} {} (+{}) exceeds acceptable range of {} {}".format( | |
237 | inside, inside_range, offset, outside, outside_range | |
217 | 238 | ) |
218 | 239 | ) |
219 | 240 | |
310 | 331 | def has_terminated(self): |
311 | 332 | return self._terminated |
312 | 333 | |
313 | def assert_types_for_read_table(self, instr, src, dest, type_): | |
334 | def assert_types_for_read_table(self, instr, src, dest, type_, offset): | |
314 | 335 | if (not TableType.is_a_table_type(src.ref.type, type_)) or (not dest.type == type_): |
315 | 336 | raise TypeMismatchError(instr, '{} and {}'.format(src.ref.name, dest.name)) |
316 | 337 | self.assert_meaningful(src, src.index) |
317 | self.assert_in_range(src.index, src.ref) | |
318 | ||
319 | def assert_types_for_update_table(self, instr, dest, type_): | |
338 | self.assert_in_range(src.index, src.ref, offset) | |
339 | ||
340 | def assert_types_for_update_table(self, instr, dest, type_, offset): | |
320 | 341 | if not TableType.is_a_table_type(dest.ref.type, type_): |
321 | 342 | raise TypeMismatchError(instr, '{}'.format(dest.ref.name)) |
322 | 343 | self.assert_meaningful(dest.index) |
323 | self.assert_in_range(dest.index, dest.ref) | |
344 | self.assert_in_range(dest.index, dest.ref, offset) | |
324 | 345 | self.set_written(dest.ref) |
325 | 346 | |
326 | 347 | def extract(self, location): |
358 | 379 | elif location in self._writeable: |
359 | 380 | self._writeable.remove(location) |
360 | 381 | |
382 | def get_assoc(self, pointer): | |
383 | return self._pointer_assoc.get(pointer) | |
384 | ||
385 | def set_assoc(self, pointer, table): | |
386 | self._pointer_assoc[pointer] = table | |
387 | ||
361 | 388 | |
362 | 389 | class Analyzer(object): |
363 | 390 | |
365 | 392 | self.current_routine = None |
366 | 393 | self.routines = {} |
367 | 394 | self.debug = debug |
395 | self.exit_contexts_map = {} | |
368 | 396 | |
369 | 397 | def assert_type(self, type_, *locations): |
370 | 398 | for location in locations: |
397 | 425 | assert isinstance(routine, Routine) |
398 | 426 | if routine.block is None: |
399 | 427 | # it's an extern, that's fine |
400 | return | |
428 | return None | |
401 | 429 | |
402 | 430 | self.current_routine = routine |
403 | 431 | type_ = routine.location.type |
404 | 432 | context = Context(self.routines, routine, type_.inputs, type_.outputs, type_.trashes) |
405 | 433 | self.exit_contexts = [] |
406 | 434 | |
407 | if self.debug: | |
408 | print("at start of routine `{}`:".format(routine.name)) | |
409 | print(context) | |
410 | ||
411 | 435 | self.analyze_block(routine.block, context) |
412 | 436 | |
413 | 437 | trashed = set(context.each_touched()) - set(context.each_meaningful()) |
414 | 438 | |
415 | if self.debug: | |
416 | print("at end of routine `{}`:".format(routine.name)) | |
417 | print(context) | |
418 | print("trashed: ", LocationRef.format_set(trashed)) | |
419 | print("outputs: ", LocationRef.format_set(type_.outputs)) | |
420 | trashed_outputs = type_.outputs & trashed | |
421 | if trashed_outputs: | |
422 | print("TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs)) | |
423 | print('') | |
424 | print('-' * 79) | |
425 | print('') | |
439 | self.exit_contexts_map[routine.name] = { | |
440 | 'end_context': context.to_json_data(), | |
441 | 'exit_contexts': [e.to_json_data() for e in self.exit_contexts] | |
442 | } | |
426 | 443 | |
427 | 444 | if self.exit_contexts: |
428 | 445 | # check that they are all consistent |
437 | 454 | raise InconsistentExitError("Exit contexts are not consistent") |
438 | 455 | if set(ex.each_writeable()) != exit_writeable: |
439 | 456 | raise InconsistentExitError("Exit contexts are not consistent") |
457 | ||
458 | # We now set the main context to the (consistent) exit context | |
459 | # so that this routine is perceived as having the same effect | |
460 | # that any of the goto'ed routines have. | |
440 | 461 | context.update_from(exit_context) |
441 | 462 | |
442 | 463 | # these all apply whether we encountered goto(s) in this routine, or not...: |
479 | 500 | raise IllegalJumpError(instr, instr) |
480 | 501 | elif isinstance(instr, Save): |
481 | 502 | self.analyze_save(instr, context) |
503 | elif isinstance(instr, PointInto): | |
504 | self.analyze_point_into(instr, context) | |
482 | 505 | else: |
483 | 506 | raise NotImplementedError |
484 | 507 | |
493 | 516 | |
494 | 517 | if opcode == 'ld': |
495 | 518 | if isinstance(src, IndexedRef): |
496 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE) | |
519 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE, src.offset) | |
497 | 520 | elif isinstance(src, IndirectRef): |
498 | 521 | # copying this analysis from the matching branch in `copy`, below |
499 | 522 | if isinstance(src.ref.type, PointerType) and dest.type == TYPE_BYTE: |
500 | 523 | pass |
501 | 524 | else: |
502 | 525 | raise TypeMismatchError(instr, (src, dest)) |
526 | ||
527 | origin = context.get_assoc(src.ref) | |
528 | if not origin: | |
529 | raise UnmeaningfulReadError(instr, src.ref) | |
530 | context.assert_meaningful(origin) | |
531 | ||
503 | 532 | context.assert_meaningful(src.ref, REG_Y) |
504 | 533 | elif src.type != dest.type: |
505 | 534 | raise TypeMismatchError(instr, '{} and {}'.format(src.name, dest.name)) |
511 | 540 | if isinstance(dest, IndexedRef): |
512 | 541 | if src.type != TYPE_BYTE: |
513 | 542 | raise TypeMismatchError(instr, (src, dest)) |
514 | context.assert_types_for_update_table(instr, dest, TYPE_BYTE) | |
543 | context.assert_types_for_update_table(instr, dest, TYPE_BYTE, dest.offset) | |
515 | 544 | elif isinstance(dest, IndirectRef): |
516 | 545 | # copying this analysis from the matching branch in `copy`, below |
517 | 546 | if isinstance(dest.ref.type, PointerType) and src.type == TYPE_BYTE: |
518 | 547 | pass |
519 | 548 | else: |
520 | 549 | raise TypeMismatchError(instr, (src, dest)) |
550 | ||
521 | 551 | context.assert_meaningful(dest.ref, REG_Y) |
522 | context.set_written(dest.ref) | |
552 | ||
553 | target = context.get_assoc(dest.ref) | |
554 | if not target: | |
555 | raise ForbiddenWriteError(instr, dest.ref) | |
556 | context.set_touched(target) | |
557 | context.set_written(target) | |
558 | ||
523 | 559 | elif src.type != dest.type: |
524 | 560 | raise TypeMismatchError(instr, '{} and {}'.format(src, dest)) |
525 | 561 | else: |
529 | 565 | elif opcode == 'add': |
530 | 566 | context.assert_meaningful(src, dest, FLAG_C) |
531 | 567 | if isinstance(src, IndexedRef): |
532 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE) | |
568 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE, src.offset) | |
533 | 569 | elif src.type == TYPE_BYTE: |
534 | 570 | self.assert_type(TYPE_BYTE, src, dest) |
535 | 571 | if dest != REG_A: |
550 | 586 | elif opcode == 'sub': |
551 | 587 | context.assert_meaningful(src, dest, FLAG_C) |
552 | 588 | if isinstance(src, IndexedRef): |
553 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE) | |
589 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE, src.offset) | |
554 | 590 | elif src.type == TYPE_BYTE: |
555 | 591 | self.assert_type(TYPE_BYTE, src, dest) |
556 | 592 | if dest != REG_A: |
565 | 601 | elif opcode == 'cmp': |
566 | 602 | context.assert_meaningful(src, dest) |
567 | 603 | if isinstance(src, IndexedRef): |
568 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE) | |
604 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE, src.offset) | |
569 | 605 | elif src.type == TYPE_BYTE: |
570 | 606 | self.assert_type(TYPE_BYTE, src, dest) |
571 | 607 | else: |
575 | 611 | context.set_written(FLAG_Z, FLAG_N, FLAG_C) |
576 | 612 | elif opcode == 'and': |
577 | 613 | if isinstance(src, IndexedRef): |
578 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE) | |
614 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE, src.offset) | |
579 | 615 | else: |
580 | 616 | self.assert_type(TYPE_BYTE, src, dest) |
581 | 617 | context.assert_meaningful(src, dest) |
587 | 623 | context.set_top_of_range(dest, context.get_top_of_range(src)) |
588 | 624 | elif opcode in ('or', 'xor'): |
589 | 625 | if isinstance(src, IndexedRef): |
590 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE) | |
626 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE, src.offset) | |
591 | 627 | else: |
592 | 628 | self.assert_type(TYPE_BYTE, src, dest) |
593 | 629 | context.assert_meaningful(src, dest) |
596 | 632 | elif opcode in ('inc', 'dec'): |
597 | 633 | context.assert_meaningful(dest) |
598 | 634 | if isinstance(dest, IndexedRef): |
599 | context.assert_types_for_update_table(instr, dest, TYPE_BYTE) | |
635 | context.assert_types_for_update_table(instr, dest, TYPE_BYTE, dest.offset) | |
600 | 636 | context.set_written(dest.ref, FLAG_Z, FLAG_N) |
601 | 637 | #context.invalidate_range(dest) |
602 | 638 | else: |
619 | 655 | elif opcode in ('shl', 'shr'): |
620 | 656 | context.assert_meaningful(dest, FLAG_C) |
621 | 657 | if isinstance(dest, IndexedRef): |
622 | context.assert_types_for_update_table(instr, dest, TYPE_BYTE) | |
658 | context.assert_types_for_update_table(instr, dest, TYPE_BYTE, dest.offset) | |
623 | 659 | context.set_written(dest.ref, FLAG_Z, FLAG_N, FLAG_C) |
624 | 660 | #context.invalidate_range(dest) |
625 | 661 | else: |
646 | 682 | |
647 | 683 | # 1. check that their types are compatible |
648 | 684 | |
649 | if isinstance(src, AddressRef) and isinstance(dest, LocationRef): | |
650 | if isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType): | |
651 | pass | |
652 | else: | |
653 | raise TypeMismatchError(instr, (src, dest)) | |
654 | elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef): | |
685 | if isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef): | |
655 | 686 | if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType): |
656 | 687 | pass |
657 | 688 | else: |
678 | 709 | pass |
679 | 710 | else: |
680 | 711 | raise TypeMismatchError(instr, (src, dest)) |
681 | context.assert_in_range(dest.index, dest.ref) | |
712 | context.assert_in_range(dest.index, dest.ref, dest.offset) | |
682 | 713 | |
683 | 714 | elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef): |
684 | 715 | if TableType.is_a_table_type(src.ref.type, TYPE_WORD) and dest.type == TYPE_WORD: |
688 | 719 | pass |
689 | 720 | else: |
690 | 721 | raise TypeMismatchError(instr, (src, dest)) |
691 | context.assert_in_range(src.index, src.ref) | |
722 | context.assert_in_range(src.index, src.ref, src.offset) | |
692 | 723 | |
693 | 724 | elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef): |
694 | 725 | if src.type == dest.type: |
706 | 737 | |
707 | 738 | if isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef): |
708 | 739 | context.assert_meaningful(src, REG_Y) |
709 | # TODO this will need to be more sophisticated. it's the thing ref points to that is written, not ref itself. | |
710 | context.set_written(dest.ref) | |
740 | ||
741 | target = context.get_assoc(dest.ref) | |
742 | if not target: | |
743 | raise ForbiddenWriteError(instr, dest.ref) | |
744 | context.set_touched(target) | |
745 | context.set_written(target) | |
746 | ||
711 | 747 | elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef): |
712 | 748 | context.assert_meaningful(src.ref, REG_Y) |
713 | # TODO more sophisticated? | |
749 | ||
750 | origin = context.get_assoc(src.ref) | |
751 | if not origin: | |
752 | raise UnmeaningfulReadError(instr, src.ref) | |
753 | context.assert_meaningful(origin) | |
754 | ||
755 | context.set_touched(dest) | |
714 | 756 | context.set_written(dest) |
715 | 757 | elif isinstance(src, IndirectRef) and isinstance(dest, IndirectRef): |
716 | 758 | context.assert_meaningful(src.ref, REG_Y) |
717 | # TODO more sophisticated? | |
718 | context.set_written(dest.ref) | |
759 | ||
760 | origin = context.get_assoc(src.ref) | |
761 | if not origin: | |
762 | raise UnmeaningfulReadError(instr, src.ref) | |
763 | context.assert_meaningful(origin) | |
764 | ||
765 | target = context.get_assoc(dest.ref) | |
766 | if not target: | |
767 | raise ForbiddenWriteError(instr, dest.ref) | |
768 | context.set_touched(target) | |
769 | context.set_written(target) | |
770 | ||
719 | 771 | elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef): |
720 | 772 | context.assert_meaningful(src, dest.ref, dest.index) |
721 | 773 | context.set_written(dest.ref) |
904 | 956 | else: |
905 | 957 | context.set_touched(REG_A) |
906 | 958 | context.set_unmeaningful(REG_A) |
959 | ||
960 | def analyze_point_into(self, instr, context): | |
961 | if not isinstance(instr.pointer.type, PointerType): | |
962 | raise TypeMismatchError(instr, instr.pointer) | |
963 | if not TableType.is_a_table_type(instr.table.type, TYPE_BYTE): | |
964 | raise TypeMismatchError(instr, instr.table) | |
965 | ||
966 | # check that pointer is not yet associated with any table. | |
967 | ||
968 | if context.get_assoc(instr.pointer): | |
969 | raise ForbiddenWriteError(instr, instr.pointer) | |
970 | ||
971 | # associate pointer with table, mark it as meaningful. | |
972 | ||
973 | context.set_assoc(instr.pointer, instr.table) | |
974 | context.set_meaningful(instr.pointer) | |
975 | context.set_touched(instr.pointer) | |
976 | ||
977 | self.analyze_block(instr.block, context) | |
978 | if context.encountered_gotos(): | |
979 | raise IllegalJumpError(instr, instr) | |
980 | ||
981 | # unassociate pointer with table, mark as unmeaningful. | |
982 | ||
983 | context.set_assoc(instr.pointer, None) | |
984 | context.set_unmeaningful(instr.pointer) |
96 | 96 | class Save(Instr): |
97 | 97 | value_attrs = ('locations',) |
98 | 98 | child_attrs = ('block',) |
99 | ||
100 | ||
101 | class PointInto(Instr): | |
102 | value_attrs = ('pointer', 'table',) | |
103 | child_attrs = ('block',) |
0 | 0 | # encoding: UTF-8 |
1 | 1 | |
2 | from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save | |
2 | from sixtypical.ast import ( | |
3 | Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save, PointInto | |
4 | ) | |
3 | 5 | from sixtypical.model import ( |
4 | ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef, | |
6 | ConstantRef, LocationRef, IndexedRef, IndirectRef, | |
5 | 7 | TYPE_BIT, TYPE_BYTE, TYPE_WORD, |
6 | TableType, BufferType, PointerType, RoutineType, VectorType, | |
8 | TableType, PointerType, RoutineType, VectorType, | |
7 | 9 | REG_A, REG_X, REG_Y, FLAG_C |
8 | 10 | ) |
9 | 11 | from sixtypical.emitter import Byte, Word, Table, Label, Offset, LowAddressByte, HighAddressByte |
54 | 56 | length = 2 |
55 | 57 | elif isinstance(type_, TableType): |
56 | 58 | length = type_.size * (1 if type_.of_type == TYPE_BYTE else 2) |
57 | elif isinstance(type_, BufferType): | |
58 | length = type_.size | |
59 | 59 | if length is None: |
60 | 60 | raise NotImplementedError("Need size for type {}".format(type_)) |
61 | 61 | return length |
171 | 171 | return self.compile_with_interrupts_off(instr) |
172 | 172 | elif isinstance(instr, Save): |
173 | 173 | return self.compile_save(instr) |
174 | elif isinstance(instr, PointInto): | |
175 | return self.compile_point_into(instr) | |
174 | 176 | else: |
175 | 177 | raise NotImplementedError |
176 | 178 | |
189 | 191 | elif isinstance(src, ConstantRef): |
190 | 192 | self.emitter.emit(LDA(Immediate(Byte(src.value)))) |
191 | 193 | elif isinstance(src, IndexedRef) and src.index == REG_X: |
192 | self.emitter.emit(LDA(AbsoluteX(self.get_label(src.ref.name)))) | |
194 | self.emitter.emit(LDA(AbsoluteX(Offset(self.get_label(src.ref.name), src.offset.value)))) | |
193 | 195 | elif isinstance(src, IndexedRef) and src.index == REG_Y: |
194 | self.emitter.emit(LDA(AbsoluteY(self.get_label(src.ref.name)))) | |
196 | self.emitter.emit(LDA(AbsoluteY(Offset(self.get_label(src.ref.name), src.offset.value)))) | |
195 | 197 | elif isinstance(src, IndirectRef) and isinstance(src.ref.type, PointerType): |
196 | 198 | self.emitter.emit(LDA(IndirectY(self.get_label(src.ref.name)))) |
197 | 199 | else: |
202 | 204 | elif isinstance(src, ConstantRef): |
203 | 205 | self.emitter.emit(LDX(Immediate(Byte(src.value)))) |
204 | 206 | elif isinstance(src, IndexedRef) and src.index == REG_Y: |
205 | self.emitter.emit(LDX(AbsoluteY(self.get_label(src.ref.name)))) | |
207 | self.emitter.emit(LDX(AbsoluteY(Offset(self.get_label(src.ref.name), src.offset.value)))) | |
206 | 208 | else: |
207 | 209 | self.emitter.emit(LDX(self.absolute_or_zero_page(self.get_label(src.name)))) |
208 | 210 | elif dest == REG_Y: |
211 | 213 | elif isinstance(src, ConstantRef): |
212 | 214 | self.emitter.emit(LDY(Immediate(Byte(src.value)))) |
213 | 215 | elif isinstance(src, IndexedRef) and src.index == REG_X: |
214 | self.emitter.emit(LDY(AbsoluteX(self.get_label(src.ref.name)))) | |
216 | self.emitter.emit(LDY(AbsoluteX(Offset(self.get_label(src.ref.name), src.offset.value)))) | |
215 | 217 | else: |
216 | 218 | self.emitter.emit(LDY(self.absolute_or_zero_page(self.get_label(src.name)))) |
217 | 219 | else: |
233 | 235 | REG_X: AbsoluteX, |
234 | 236 | REG_Y: AbsoluteY, |
235 | 237 | }[dest.index] |
236 | operand = mode_cls(self.get_label(dest.ref.name)) | |
238 | operand = mode_cls(Offset(self.get_label(dest.ref.name), dest.offset.value)) | |
237 | 239 | elif isinstance(dest, IndirectRef) and isinstance(dest.ref.type, PointerType): |
238 | 240 | operand = IndirectY(self.get_label(dest.ref.name)) |
239 | 241 | else: |
249 | 251 | if isinstance(src, ConstantRef): |
250 | 252 | self.emitter.emit(ADC(Immediate(Byte(src.value)))) |
251 | 253 | elif isinstance(src, IndexedRef): |
252 | self.emitter.emit(ADC(self.addressing_mode_for_index(src.index)(self.get_label(src.ref.name)))) | |
254 | mode = self.addressing_mode_for_index(src.index) | |
255 | self.emitter.emit(ADC(mode(Offset(self.get_label(src.ref.name), src.offset.value)))) | |
253 | 256 | else: |
254 | 257 | self.emitter.emit(ADC(Absolute(self.get_label(src.name)))) |
255 | 258 | elif isinstance(dest, LocationRef) and src.type == TYPE_BYTE and dest.type == TYPE_BYTE: |
315 | 318 | if isinstance(src, ConstantRef): |
316 | 319 | self.emitter.emit(SBC(Immediate(Byte(src.value)))) |
317 | 320 | elif isinstance(src, IndexedRef): |
318 | self.emitter.emit(SBC(self.addressing_mode_for_index(src.index)(self.get_label(src.ref.name)))) | |
321 | mode = self.addressing_mode_for_index(src.index) | |
322 | self.emitter.emit(SBC(mode(Offset(self.get_label(src.ref.name), src.offset.value)))) | |
319 | 323 | else: |
320 | 324 | self.emitter.emit(SBC(Absolute(self.get_label(src.name)))) |
321 | 325 | elif isinstance(dest, LocationRef) and src.type == TYPE_BYTE and dest.type == TYPE_BYTE: |
366 | 370 | if isinstance(src, ConstantRef): |
367 | 371 | self.emitter.emit(cls(Immediate(Byte(src.value)))) |
368 | 372 | elif isinstance(src, IndexedRef): |
369 | self.emitter.emit(cls(self.addressing_mode_for_index(src.index)(self.get_label(src.ref.name)))) | |
373 | mode = self.addressing_mode_for_index(src.index) | |
374 | self.emitter.emit(cls(mode(Offset(self.get_label(src.ref.name), src.offset.value)))) | |
370 | 375 | else: |
371 | 376 | self.emitter.emit(cls(self.absolute_or_zero_page(self.get_label(src.name)))) |
372 | 377 | else: |
383 | 388 | if dest == REG_A: |
384 | 389 | self.emitter.emit(cls()) |
385 | 390 | elif isinstance(dest, IndexedRef): |
386 | self.emitter.emit(cls(self.addressing_mode_for_index(dest.index)(self.get_label(dest.ref.name)))) | |
391 | mode = self.addressing_mode_for_index(dest.index) | |
392 | self.emitter.emit(cls(mode(Offset(self.get_label(dest.ref.name), dest.offset.value)))) | |
387 | 393 | else: |
388 | 394 | self.emitter.emit(cls(self.absolute_or_zero_page(self.get_label(dest.name)))) |
389 | 395 | elif opcode == 'call': |
454 | 460 | self.emitter.emit(cls(Immediate(Byte(src.value)))) |
455 | 461 | elif isinstance(src, IndexedRef): |
456 | 462 | # FIXME might not work for some dest's (that is, cls's) |
457 | self.emitter.emit(cls(self.addressing_mode_for_index(src.index)(self.get_label(src.ref.name)))) | |
463 | mode = self.addressing_mode_for_index(src.index) | |
464 | self.emitter.emit(cls(mode(Offset(self.get_label(src.ref.name), src.offset.value)))) | |
458 | 465 | else: |
459 | 466 | self.emitter.emit(cls(Absolute(self.get_label(src.name)))) |
460 | 467 | |
465 | 472 | elif dest == REG_Y: |
466 | 473 | self.emitter.emit(INY()) |
467 | 474 | elif isinstance(dest, IndexedRef): |
468 | self.emitter.emit(INC(self.addressing_mode_for_index(dest.index)(self.get_label(dest.ref.name)))) | |
475 | mode = self.addressing_mode_for_index(dest.index) | |
476 | self.emitter.emit(INC(mode(Offset(self.get_label(dest.ref.name), dest.offset.value)))) | |
469 | 477 | else: |
470 | 478 | self.emitter.emit(INC(Absolute(self.get_label(dest.name)))) |
471 | 479 | |
476 | 484 | elif dest == REG_Y: |
477 | 485 | self.emitter.emit(DEY()) |
478 | 486 | elif isinstance(dest, IndexedRef): |
479 | self.emitter.emit(DEC(self.addressing_mode_for_index(dest.index)(self.get_label(dest.ref.name)))) | |
487 | mode = self.addressing_mode_for_index(dest.index) | |
488 | self.emitter.emit(DEC(mode(Offset(self.get_label(dest.ref.name), dest.offset.value)))) | |
480 | 489 | else: |
481 | 490 | self.emitter.emit(DEC(Absolute(self.get_label(dest.name)))) |
482 | 491 | |
504 | 513 | dest_label = self.get_label(dest.ref.name) |
505 | 514 | self.emitter.emit(LDA(IndirectY(src_label))) |
506 | 515 | self.emitter.emit(STA(IndirectY(dest_label))) |
507 | elif isinstance(src, AddressRef) and isinstance(dest, LocationRef) and isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType): | |
508 | ### copy ^buf, ptr | |
509 | src_label = self.get_label(src.ref.name) | |
510 | dest_label = self.get_label(dest.name) | |
511 | self.emitter.emit(LDA(Immediate(HighAddressByte(src_label)))) | |
512 | self.emitter.emit(STA(ZeroPage(dest_label))) | |
513 | self.emitter.emit(LDA(Immediate(LowAddressByte(src_label)))) | |
514 | self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1)))) | |
515 | 516 | elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef) and src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD): |
516 | 517 | ### copy w, wtab + y |
517 | 518 | src_label = self.get_label(src.name) |
518 | 519 | dest_label = self.get_label(dest.ref.name) |
520 | mode = self.addressing_mode_for_index(dest.index) | |
519 | 521 | self.emitter.emit(LDA(Absolute(src_label))) |
520 | self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label))) | |
522 | self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value)))) | |
521 | 523 | self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) |
522 | self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256)))) | |
524 | self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value + 256)))) | |
523 | 525 | elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef) and isinstance(src.type, VectorType) and isinstance(dest.ref.type, TableType) and isinstance(dest.ref.type.of_type, VectorType): |
524 | 526 | ### copy vec, vtab + y |
525 | 527 | # FIXME this is the exact same as above - can this be simplified? |
526 | 528 | src_label = self.get_label(src.name) |
527 | 529 | dest_label = self.get_label(dest.ref.name) |
530 | mode = self.addressing_mode_for_index(dest.index) | |
528 | 531 | self.emitter.emit(LDA(Absolute(src_label))) |
529 | self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label))) | |
532 | self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value)))) | |
530 | 533 | self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) |
531 | self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256)))) | |
534 | self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value + 256)))) | |
532 | 535 | elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef) and isinstance(src.type, RoutineType) and isinstance(dest.ref.type, TableType) and isinstance(dest.ref.type.of_type, VectorType): |
533 | 536 | ### copy routine, vtab + y |
534 | 537 | src_label = self.get_label(src.name) |
535 | 538 | dest_label = self.get_label(dest.ref.name) |
539 | mode = self.addressing_mode_for_index(dest.index) | |
536 | 540 | self.emitter.emit(LDA(Immediate(HighAddressByte(src_label)))) |
537 | self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label))) | |
541 | self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value)))) | |
538 | 542 | self.emitter.emit(LDA(Immediate(LowAddressByte(src_label)))) |
539 | self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256)))) | |
543 | self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value + 256)))) | |
540 | 544 | elif isinstance(src, ConstantRef) and isinstance(dest, IndexedRef) and src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD): |
541 | 545 | ### copy 9999, wtab + y |
542 | 546 | dest_label = self.get_label(dest.ref.name) |
547 | mode = self.addressing_mode_for_index(dest.index) | |
543 | 548 | self.emitter.emit(LDA(Immediate(Byte(src.low_byte())))) |
544 | self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label))) | |
549 | self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value)))) | |
545 | 550 | self.emitter.emit(LDA(Immediate(Byte(src.high_byte())))) |
546 | self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256)))) | |
551 | self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value + 256)))) | |
547 | 552 | elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef) and TableType.is_a_table_type(src.ref.type, TYPE_WORD) and dest.type == TYPE_WORD: |
548 | 553 | ### copy wtab + y, w |
549 | 554 | src_label = self.get_label(src.ref.name) |
550 | 555 | dest_label = self.get_label(dest.name) |
551 | self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(src_label))) | |
556 | mode = self.addressing_mode_for_index(src.index) | |
557 | self.emitter.emit(LDA(mode(Offset(src_label, src.offset.value)))) | |
552 | 558 | self.emitter.emit(STA(Absolute(dest_label))) |
553 | self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(Offset(src_label, 256)))) | |
559 | self.emitter.emit(LDA(mode(Offset(src_label, src.offset.value + 256)))) | |
554 | 560 | self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) |
555 | 561 | elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef) and isinstance(dest.type, VectorType) and isinstance(src.ref.type, TableType) and isinstance(src.ref.type.of_type, VectorType): |
556 | 562 | ### copy vtab + y, vec |
557 | 563 | # FIXME this is the exact same as above - can this be simplified? |
558 | 564 | src_label = self.get_label(src.ref.name) |
559 | 565 | dest_label = self.get_label(dest.name) |
560 | self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(src_label))) | |
566 | mode = self.addressing_mode_for_index(src.index) | |
567 | self.emitter.emit(LDA(mode(Offset(src_label, src.offset.value)))) | |
561 | 568 | self.emitter.emit(STA(Absolute(dest_label))) |
562 | self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(Offset(src_label, 256)))) | |
569 | self.emitter.emit(LDA(mode(Offset(src_label, src.offset.value + 256)))) | |
563 | 570 | self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) |
564 | 571 | elif src.type == TYPE_BYTE and dest.type == TYPE_BYTE and not isinstance(src, ConstantRef): |
565 | 572 | ### copy b1, b2 |
699 | 706 | src_label = self.get_label(location.name) |
700 | 707 | self.emitter.emit(PLA()) |
701 | 708 | self.emitter.emit(STA(Absolute(src_label))) |
709 | ||
710 | def compile_point_into(self, instr): | |
711 | src_label = self.get_label(instr.table.name) | |
712 | dest_label = self.get_label(instr.pointer.name) | |
713 | ||
714 | self.emitter.emit(LDA(Immediate(HighAddressByte(src_label)))) | |
715 | self.emitter.emit(STA(ZeroPage(dest_label))) | |
716 | self.emitter.emit(LDA(Immediate(LowAddressByte(src_label)))) | |
717 | self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1)))) | |
718 | ||
719 | self.compile_block(instr.block) |
8 | 8 | def __repr__(self): |
9 | 9 | return 'Type(%r)' % self.name |
10 | 10 | |
11 | def __str__(self): | |
12 | return self.name | |
13 | ||
14 | def __eq__(self, other): | |
15 | return isinstance(other, Type) and other.name == self.name | |
16 | ||
17 | def __hash__(self): | |
18 | return hash(self.name) | |
11 | def __eq__(self, other): | |
12 | return other.__class__ == self.__class__ and other.name == self.name | |
19 | 13 | |
20 | 14 | |
21 | 15 | TYPE_BIT = Type('bit', max_range=(0, 1)) |
23 | 17 | TYPE_WORD = Type('word', max_range=(0, 65535)) |
24 | 18 | |
25 | 19 | |
26 | ||
27 | 20 | class RoutineType(Type): |
28 | 21 | """This memory location contains the code for a routine.""" |
29 | 22 | def __init__(self, inputs, outputs, trashes): |
30 | self.name = 'routine' | |
31 | 23 | self.inputs = inputs |
32 | 24 | self.outputs = outputs |
33 | 25 | self.trashes = trashes |
34 | 26 | |
35 | 27 | def __repr__(self): |
36 | return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % ( | |
37 | self.__class__.__name__, self.name, self.inputs, self.outputs, self.trashes | |
28 | return '%s(inputs=%r, outputs=%r, trashes=%r)' % ( | |
29 | self.__class__.__name__, self.inputs, self.outputs, self.trashes | |
38 | 30 | ) |
39 | 31 | |
40 | 32 | def __eq__(self, other): |
41 | 33 | return isinstance(other, RoutineType) and ( |
42 | other.name == self.name and | |
43 | 34 | other.inputs == self.inputs and |
44 | 35 | other.outputs == self.outputs and |
45 | 36 | other.trashes == self.trashes |
46 | 37 | ) |
47 | ||
48 | def __hash__(self): | |
49 | return hash(self.name) ^ hash(self.inputs) ^ hash(self.outputs) ^ hash(self.trashes) | |
50 | 38 | |
51 | 39 | @classmethod |
52 | 40 | def executable_types_compatible(cls_, src, dest): |
69 | 57 | class VectorType(Type): |
70 | 58 | """This memory location contains the address of some other type (currently, only RoutineType).""" |
71 | 59 | def __init__(self, of_type): |
72 | self.name = 'vector' | |
73 | 60 | self.of_type = of_type |
74 | 61 | |
75 | 62 | def __repr__(self): |
78 | 65 | ) |
79 | 66 | |
80 | 67 | def __eq__(self, other): |
81 | return self.name == other.name and self.of_type == other.of_type | |
82 | ||
83 | def __hash__(self): | |
84 | return hash(self.name) ^ hash(self.of_type) | |
68 | return isinstance(other, VectorType) and self.of_type == other.of_type | |
85 | 69 | |
86 | 70 | |
87 | 71 | class TableType(Type): |
88 | 72 | def __init__(self, of_type, size): |
89 | 73 | self.of_type = of_type |
90 | 74 | self.size = size |
91 | self.name = '{} table[{}]'.format(self.of_type.name, self.size) | |
92 | 75 | |
93 | 76 | def __repr__(self): |
94 | 77 | return '%s(%r, %r)' % ( |
95 | 78 | self.__class__.__name__, self.of_type, self.size |
96 | 79 | ) |
80 | ||
81 | def __eq__(self, other): | |
82 | return isinstance(other, TableType) and self.of_type == other.of_type and self.size == other.size | |
83 | ||
84 | @property | |
85 | def max_range(self): | |
86 | return (0, self.size - 1) | |
97 | 87 | |
98 | 88 | @classmethod |
99 | 89 | def is_a_table_type(cls_, x, of_type): |
100 | 90 | return isinstance(x, TableType) and x.of_type == of_type |
101 | 91 | |
102 | 92 | |
103 | class BufferType(Type): | |
104 | def __init__(self, size): | |
105 | self.size = size | |
106 | self.name = 'buffer[%s]' % self.size | |
107 | ||
108 | ||
109 | 93 | class PointerType(Type): |
110 | 94 | def __init__(self): |
111 | 95 | self.name = 'pointer' |
96 | ||
97 | def __eq__(self, other): | |
98 | return other.__class__ == self.__class__ | |
112 | 99 | |
113 | 100 | |
114 | 101 | class Ref(object): |
138 | 125 | return equal |
139 | 126 | |
140 | 127 | def __hash__(self): |
141 | return hash(self.name + str(self.type)) | |
128 | return hash(self.name + repr(self.type)) | |
142 | 129 | |
143 | 130 | def __repr__(self): |
144 | 131 | return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.name) |
182 | 169 | |
183 | 170 | |
184 | 171 | class IndexedRef(Ref): |
185 | def __init__(self, ref, index): | |
172 | def __init__(self, ref, offset, index): | |
186 | 173 | self.ref = ref |
174 | self.offset = offset | |
187 | 175 | self.index = index |
188 | 176 | |
189 | 177 | def __eq__(self, other): |
190 | return isinstance(other, self.__class__) and self.ref == other.ref and self.index == other.index | |
191 | ||
192 | def __hash__(self): | |
193 | return hash(self.__class__.name) ^ hash(self.ref) ^ hash(self.index) | |
194 | ||
195 | def __repr__(self): | |
196 | return '%s(%r, %r)' % (self.__class__.__name__, self.ref, self.index) | |
178 | return isinstance(other, self.__class__) and self.ref == other.ref and self.offset == other.offset and self.index == other.index | |
179 | ||
180 | def __hash__(self): | |
181 | return hash(self.__class__.name) ^ hash(self.ref) ^ hash(self.offset) ^ hash(self.index) | |
182 | ||
183 | def __repr__(self): | |
184 | return '%s(%r, %r, %r)' % (self.__class__.__name__, self.ref, self.offset, self.index) | |
197 | 185 | |
198 | 186 | @property |
199 | 187 | def name(self): |
200 | return '{}+{}'.format(self.ref.name, self.index.name) | |
188 | return '{}+{}+{}'.format(self.ref.name, self.offset, self.index.name) | |
201 | 189 | |
202 | 190 | def is_constant(self): |
203 | 191 | return False |
204 | ||
205 | ||
206 | class AddressRef(Ref): | |
207 | def __init__(self, ref): | |
208 | self.ref = ref | |
209 | ||
210 | def __eq__(self, other): | |
211 | return self.ref == other.ref | |
212 | ||
213 | def __hash__(self): | |
214 | return hash(self.__class__.name) ^ hash(self.ref) | |
215 | ||
216 | def __repr__(self): | |
217 | return '%s(%r)' % (self.__class__.__name__, self.ref) | |
218 | ||
219 | @property | |
220 | def name(self): | |
221 | return '^{}'.format(self.ref.name) | |
222 | ||
223 | def is_constant(self): | |
224 | return True | |
225 | ||
226 | ||
227 | class PartRef(Ref): | |
228 | """For 'low byte of' location and 'high byte of' location modifiers. | |
229 | ||
230 | height=0 = low byte, height=1 = high byte. | |
231 | ||
232 | NOTE: Not actually used yet. Might require more thought before it's usable. | |
233 | """ | |
234 | def __init__(self, ref, height): | |
235 | assert isinstance(ref, Ref) | |
236 | assert ref.type == TYPE_WORD | |
237 | self.ref = ref | |
238 | self.height = height | |
239 | self.type = TYPE_BYTE | |
240 | ||
241 | def __eq__(self, other): | |
242 | return isinstance(other, PartRef) and ( | |
243 | other.height == self.height and other.ref == self.ref | |
244 | ) | |
245 | ||
246 | def __hash__(self): | |
247 | return hash(self.ref) ^ hash(self.height) ^ hash(self.type) | |
248 | ||
249 | def __repr__(self): | |
250 | return '%s(%r, %r)' % (self.__class__.__name__, self.ref, self.height) | |
251 | ||
252 | def is_constant(self): | |
253 | return self.ref.is_constant() | |
254 | 192 | |
255 | 193 | |
256 | 194 | class ConstantRef(Ref): |
295 | 233 | value -= 256 |
296 | 234 | return ConstantRef(self.type, value) |
297 | 235 | |
236 | @property | |
237 | def name(self): | |
238 | return 'constant({})'.format(self.value) | |
239 | ||
298 | 240 | |
299 | 241 | REG_A = LocationRef(TYPE_BYTE, 'a') |
300 | 242 | REG_X = LocationRef(TYPE_BYTE, 'x') |
0 | 0 | # encoding: UTF-8 |
1 | 1 | |
2 | from sixtypical.ast import Program, Defn, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save | |
2 | from sixtypical.ast import ( | |
3 | Program, Defn, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save, PointInto | |
4 | ) | |
3 | 5 | from sixtypical.model import ( |
4 | 6 | TYPE_BIT, TYPE_BYTE, TYPE_WORD, |
5 | RoutineType, VectorType, TableType, BufferType, PointerType, | |
6 | LocationRef, ConstantRef, IndirectRef, IndexedRef, AddressRef, | |
7 | RoutineType, VectorType, TableType, PointerType, | |
8 | LocationRef, ConstantRef, IndirectRef, IndexedRef, | |
7 | 9 | ) |
8 | 10 | from sixtypical.scanner import Scanner |
9 | 11 | |
80 | 82 | |
81 | 83 | def backpatch_constraint_labels(type_): |
82 | 84 | def resolve(w): |
83 | if not isinstance(w, ForwardReference): | |
84 | return w | |
85 | return self.lookup(w.name) | |
85 | if not isinstance(w, ForwardReference): | |
86 | return w | |
87 | return self.lookup(w.name) | |
86 | 88 | if isinstance(type_, TableType): |
87 | 89 | backpatch_constraint_labels(type_.of_type) |
88 | 90 | elif isinstance(type_, VectorType): |
121 | 123 | self.typedef() |
122 | 124 | if self.scanner.on('const'): |
123 | 125 | self.defn_const() |
124 | typenames = ['byte', 'word', 'table', 'vector', 'buffer', 'pointer'] # 'routine', | |
126 | typenames = ['byte', 'word', 'table', 'vector', 'pointer'] # 'routine', | |
125 | 127 | typenames.extend(self.context.typedefs.keys()) |
126 | 128 | while self.scanner.on(*typenames): |
127 | 129 | defn = self.defn() |
221 | 223 | |
222 | 224 | if self.scanner.consume('table'): |
223 | 225 | size = self.defn_size() |
224 | if size <= 0 or size > 256: | |
225 | self.syntax_error("Table size must be > 0 and <= 256") | |
226 | if size <= 0 or size > 65536: | |
227 | self.syntax_error("Table size must be > 0 and <= 65536") | |
226 | 228 | type_ = TableType(type_, size) |
227 | 229 | |
228 | 230 | return type_ |
247 | 249 | elif self.scanner.consume('routine'): |
248 | 250 | (inputs, outputs, trashes) = self.constraints() |
249 | 251 | type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes) |
250 | elif self.scanner.consume('buffer'): | |
251 | size = self.defn_size() | |
252 | type_ = BufferType(size) | |
253 | 252 | elif self.scanner.consume('pointer'): |
254 | 253 | type_ = PointerType() |
255 | 254 | else: |
350 | 349 | self.scanner.expect('+') |
351 | 350 | self.scanner.expect('y') |
352 | 351 | return IndirectRef(loc) |
353 | elif self.scanner.consume('^'): | |
354 | loc = self.locexpr() | |
355 | return AddressRef(loc) | |
356 | 352 | else: |
357 | 353 | return self.indexed_locexpr() |
358 | 354 | |
360 | 356 | loc = self.locexpr() |
361 | 357 | if not isinstance(loc, str): |
362 | 358 | index = None |
359 | offset = ConstantRef(TYPE_BYTE, 0) | |
363 | 360 | if self.scanner.consume('+'): |
361 | if self.scanner.token in self.context.consts or self.scanner.on_type('integer literal'): | |
362 | offset = self.const() | |
363 | self.scanner.expect('+') | |
364 | 364 | index = self.locexpr() |
365 | loc = IndexedRef(loc, index) | |
365 | loc = IndexedRef(loc, offset, index) | |
366 | 366 | return loc |
367 | 367 | |
368 | 368 | def statics(self): |
473 | 473 | locations = self.locexprs() |
474 | 474 | block = self.block() |
475 | 475 | return Save(self.scanner.line_number, locations=locations, block=block) |
476 | elif self.scanner.consume("point"): | |
477 | pointer = self.locexpr() | |
478 | self.scanner.expect("into") | |
479 | table = self.locexpr() | |
480 | block = self.block() | |
481 | return PointInto(self.scanner.line_number, pointer=pointer, table=table, block=block) | |
476 | 482 | elif self.scanner.consume("trash"): |
477 | 483 | dest = self.locexpr() |
478 | 484 | return SingleOp(self.scanner.line_number, opcode='trash', src=None, dest=dest) |
501 | 501 | | ld a, many + x |
502 | 502 | | } |
503 | 503 | ? UnmeaningfulReadError: x |
504 | ||
505 | Storing to a table, you may also include a constant offset. | |
506 | ||
507 | | byte one | |
508 | | byte table[256] many | |
509 | | | |
510 | | define main routine | |
511 | | outputs many | |
512 | | trashes a, x, n, z | |
513 | | { | |
514 | | ld x, 0 | |
515 | | ld a, 0 | |
516 | | st a, many + 100 + x | |
517 | | } | |
518 | = ok | |
519 | ||
520 | Reading from a table, you may also include a constant offset. | |
521 | ||
522 | | byte table[256] many | |
523 | | | |
524 | | define main routine | |
525 | | inputs many | |
526 | | outputs many | |
527 | | trashes a, x, n, z | |
528 | | { | |
529 | | ld x, 0 | |
530 | | ld a, many + 100 + x | |
531 | | } | |
532 | = ok | |
533 | ||
534 | Using a constant offset, you can read and write entries in | |
535 | the table beyond the 256th. | |
536 | ||
537 | | byte one | |
538 | | byte table[1024] many | |
539 | | | |
540 | | define main routine | |
541 | | inputs many | |
542 | | outputs many | |
543 | | trashes a, x, n, z | |
544 | | { | |
545 | | ld x, 0 | |
546 | | ld a, many + 999 + x | |
547 | | st a, many + 1000 + x | |
548 | | } | |
549 | = ok | |
504 | 550 | |
505 | 551 | There are other operations you can do on tables. (1/3) |
506 | 552 | |
613 | 659 | | } |
614 | 660 | = ok |
615 | 661 | |
662 | Copying to and from a word table with a constant offset. | |
663 | ||
664 | | word one | |
665 | | word table[256] many | |
666 | | | |
667 | | define main routine | |
668 | | inputs one, many | |
669 | | outputs one, many | |
670 | | trashes a, x, n, z | |
671 | | { | |
672 | | ld x, 0 | |
673 | | copy one, many + 100 + x | |
674 | | copy many + 100 + x, one | |
675 | | copy 9999, many + 1 + x | |
676 | | } | |
677 | = ok | |
678 | ||
616 | 679 | #### tables: range checking #### |
617 | 680 | |
618 | 681 | It is a static analysis error if it cannot be proven that a read or write |
619 | 682 | to a table falls within the defined size of that table. |
620 | 683 | |
621 | (If a table has 256 entries, then there is never a problem, because a byte | |
622 | cannot index any entry outside of 0..255.) | |
684 | If a table has 256 entries, then there is never a problem (so long as | |
685 | no constant offset is supplied), because a byte cannot index any entry | |
686 | outside of 0..255. | |
687 | ||
688 | But if the table has fewer than 256 entries, or if a constant offset is | |
689 | supplied, there is the possibility that the index will refer to an | |
690 | entry in the table which does not exist. | |
623 | 691 | |
624 | 692 | A SixtyPical implementation must be able to prove that the index is inside |
625 | 693 | the range of the table in various ways. The simplest is to show that a |
663 | 731 | | } |
664 | 732 | ? RangeExceededError |
665 | 733 | |
734 | Any constant offset is taken into account in this check. | |
735 | ||
736 | | byte table[32] many | |
737 | | | |
738 | | define main routine | |
739 | | inputs many | |
740 | | outputs many | |
741 | | trashes a, x, n, z | |
742 | | { | |
743 | | ld x, 31 | |
744 | | ld a, many + 1 + x | |
745 | | } | |
746 | ? RangeExceededError | |
747 | ||
748 | | byte table[32] many | |
749 | | | |
750 | | define main routine | |
751 | | inputs many | |
752 | | outputs many | |
753 | | trashes a, x, n, z | |
754 | | { | |
755 | | ld x, 31 | |
756 | | ld a, 0 | |
757 | | st a, many + 1 + x | |
758 | | } | |
759 | ? RangeExceededError | |
760 | ||
666 | 761 | This applies to `copy` as well. |
667 | 762 | |
668 | 763 | | word one: 77 |
702 | 797 | | { |
703 | 798 | | ld x, 32 |
704 | 799 | | copy one, many + x |
800 | | } | |
801 | ? RangeExceededError | |
802 | ||
803 | Any constant offset is taken into account in this check. | |
804 | ||
805 | | word one: 77 | |
806 | | word table[32] many | |
807 | | | |
808 | | define main routine | |
809 | | inputs many, one | |
810 | | outputs many, one | |
811 | | trashes a, x, n, z | |
812 | | { | |
813 | | ld x, 31 | |
814 | | copy many + 1 + x, one | |
815 | | } | |
816 | ? RangeExceededError | |
817 | ||
818 | | word one: 77 | |
819 | | word table[32] many | |
820 | | | |
821 | | define main routine | |
822 | | inputs many, one | |
823 | | outputs many, one | |
824 | | trashes a, x, n, z | |
825 | | { | |
826 | | ld x, 31 | |
827 | | copy one, many + 1 + x | |
705 | 828 | | } |
706 | 829 | ? RangeExceededError |
707 | 830 | |
725 | 848 | | } |
726 | 849 | = ok |
727 | 850 | |
728 | Test for "clipping", but not enough. | |
851 | Tests for "clipping", but not enough. | |
729 | 852 | |
730 | 853 | | word one: 77 |
731 | 854 | | word table[32] many |
739 | 862 | | ld x, a |
740 | 863 | | copy one, many + x |
741 | 864 | | copy many + x, one |
865 | | } | |
866 | ? RangeExceededError | |
867 | ||
868 | | word one: 77 | |
869 | | word table[32] many | |
870 | | | |
871 | | define main routine | |
872 | | inputs a, many, one | |
873 | | outputs many, one | |
874 | | trashes a, x, n, z | |
875 | | { | |
876 | | and a, 31 | |
877 | | ld x, a | |
878 | | copy one, many + 1 + x | |
879 | | copy many + 1 + x, one | |
742 | 880 | | } |
743 | 881 | ? RangeExceededError |
744 | 882 | |
2778 | 2916 | | } |
2779 | 2917 | ? TypeMismatchError |
2780 | 2918 | |
2781 | ### Buffers and pointers ### | |
2782 | ||
2783 | Note that `^buf` is a constant value, so it by itself does not require `buf` to be | |
2784 | listed in any input/output sets. | |
2785 | ||
2786 | However, if the code reads from it through a pointer, it *should* be in `inputs`. | |
2787 | ||
2788 | Likewise, if the code writes to it through a pointer, it *should* be in `outputs`. | |
2789 | ||
2790 | Of course, unless you write to *all* the bytes in a buffer, some of those bytes | |
2791 | might not be meaningful. So how meaningful is this check? | |
2792 | ||
2793 | This is an open problem. | |
2794 | ||
2795 | For now, convention says: if it is being read, list it in `inputs`, and if it is | |
2796 | being modified, list it in both `inputs` and `outputs`. | |
2797 | ||
2798 | Write literal through a pointer. | |
2799 | ||
2800 | | buffer[2048] buf | |
2919 | ### point ... into blocks ### | |
2920 | ||
2921 | Pointer must be a pointer type. | |
2922 | ||
2923 | | byte table[256] tab | |
2924 | | word ptr | |
2925 | | | |
2926 | | define main routine | |
2927 | | inputs tab | |
2928 | | outputs y, tab | |
2929 | | trashes a, z, n, ptr | |
2930 | | { | |
2931 | | ld y, 0 | |
2932 | | point ptr into tab { | |
2933 | | copy 123, [ptr] + y | |
2934 | | } | |
2935 | | } | |
2936 | ? TypeMismatchError | |
2937 | ||
2938 | Cannot write through pointer outside a `point ... into` block. | |
2939 | ||
2940 | | byte table[256] tab | |
2801 | 2941 | | pointer ptr |
2802 | 2942 | | |
2803 | 2943 | | define main routine |
2804 | | inputs buf | |
2805 | | outputs y, buf | |
2944 | | inputs tab, ptr | |
2945 | | outputs y, tab | |
2806 | 2946 | | trashes a, z, n, ptr |
2807 | 2947 | | { |
2808 | 2948 | | ld y, 0 |
2809 | | copy ^buf, ptr | |
2810 | 2949 | | copy 123, [ptr] + y |
2811 | 2950 | | } |
2812 | = ok | |
2813 | ||
2814 | It does use `y`. | |
2815 | ||
2816 | | buffer[2048] buf | |
2951 | ? ForbiddenWriteError | |
2952 | ||
2953 | | byte table[256] tab | |
2817 | 2954 | | pointer ptr |
2818 | 2955 | | |
2819 | 2956 | | define main routine |
2820 | | inputs buf | |
2821 | | outputs buf | |
2957 | | inputs tab | |
2958 | | outputs y, tab | |
2822 | 2959 | | trashes a, z, n, ptr |
2823 | 2960 | | { |
2824 | | copy ^buf, ptr | |
2961 | | ld y, 0 | |
2962 | | point ptr into tab { | |
2963 | | copy 123, [ptr] + y | |
2964 | | } | |
2825 | 2965 | | copy 123, [ptr] + y |
2826 | 2966 | | } |
2967 | ? ForbiddenWriteError | |
2968 | ||
2969 | Write literal through a pointer into a table. | |
2970 | ||
2971 | | byte table[256] tab | |
2972 | | pointer ptr | |
2973 | | | |
2974 | | define main routine | |
2975 | | inputs tab | |
2976 | | outputs y, tab | |
2977 | | trashes a, z, n, ptr | |
2978 | | { | |
2979 | | ld y, 0 | |
2980 | | point ptr into tab { | |
2981 | | copy 123, [ptr] + y | |
2982 | | } | |
2983 | | } | |
2984 | = ok | |
2985 | ||
2986 | Writing into a table via a pointer does use `y`. | |
2987 | ||
2988 | | byte table[256] tab | |
2989 | | pointer ptr | |
2990 | | | |
2991 | | define main routine | |
2992 | | inputs tab | |
2993 | | outputs tab | |
2994 | | trashes a, z, n, ptr | |
2995 | | { | |
2996 | | point ptr into tab { | |
2997 | | copy 123, [ptr] + y | |
2998 | | } | |
2999 | | } | |
2827 | 3000 | ? UnmeaningfulReadError |
2828 | 3001 | |
2829 | Write stored value through a pointer. | |
2830 | ||
2831 | | buffer[2048] buf | |
3002 | Write stored value through a pointer into a table. | |
3003 | ||
3004 | | byte table[256] tab | |
2832 | 3005 | | pointer ptr |
2833 | 3006 | | byte foo |
2834 | 3007 | | |
2835 | 3008 | | define main routine |
2836 | | inputs foo, buf | |
2837 | | outputs y, buf | |
3009 | | inputs foo, tab | |
3010 | | outputs y, tab | |
2838 | 3011 | | trashes a, z, n, ptr |
2839 | 3012 | | { |
2840 | 3013 | | ld y, 0 |
2841 | | copy ^buf, ptr | |
2842 | | copy foo, [ptr] + y | |
2843 | | } | |
2844 | = ok | |
2845 | ||
2846 | Read through a pointer. | |
2847 | ||
2848 | | buffer[2048] buf | |
3014 | | point ptr into tab { | |
3015 | | copy foo, [ptr] + y | |
3016 | | } | |
3017 | | } | |
3018 | = ok | |
3019 | ||
3020 | Read a table entry via a pointer. | |
3021 | ||
3022 | | byte table[256] tab | |
2849 | 3023 | | pointer ptr |
2850 | 3024 | | byte foo |
2851 | 3025 | | |
2852 | 3026 | | define main routine |
2853 | | inputs buf | |
3027 | | inputs tab | |
2854 | 3028 | | outputs foo |
2855 | 3029 | | trashes a, y, z, n, ptr |
2856 | 3030 | | { |
2857 | 3031 | | ld y, 0 |
2858 | | copy ^buf, ptr | |
3032 | | point ptr into tab { | |
2859 | 3033 | | copy [ptr] + y, foo |
2860 | | } | |
2861 | = ok | |
2862 | ||
2863 | Read and write through two pointers. | |
2864 | ||
2865 | | buffer[2048] buf | |
3034 | | } | |
3035 | | } | |
3036 | = ok | |
3037 | ||
3038 | Read and write through two pointers into a table. | |
3039 | ||
3040 | | byte table[256] tab | |
2866 | 3041 | | pointer ptra |
2867 | 3042 | | pointer ptrb |
2868 | 3043 | | |
2869 | 3044 | | define main routine |
2870 | | inputs buf | |
2871 | | outputs buf | |
3045 | | inputs tab | |
3046 | | outputs tab | |
2872 | 3047 | | trashes a, y, z, n, ptra, ptrb |
2873 | 3048 | | { |
2874 | 3049 | | ld y, 0 |
2875 | | copy ^buf, ptra | |
2876 | | copy ^buf, ptrb | |
2877 | | copy [ptra] + y, [ptrb] + y | |
2878 | | } | |
2879 | = ok | |
2880 | ||
2881 | Read through a pointer to the `a` register. Note that this is done with `ld`, | |
3050 | | point ptra into tab { | |
3051 | | point ptrb into tab { | |
3052 | | copy [ptra] + y, [ptrb] + y | |
3053 | | } | |
3054 | | } | |
3055 | | } | |
3056 | = ok | |
3057 | ||
3058 | Read through a pointer into a table, to the `a` register. Note that this is done with `ld`, | |
2882 | 3059 | not `copy`. |
2883 | 3060 | |
2884 | | buffer[2048] buf | |
3061 | | byte table[256] tab | |
2885 | 3062 | | pointer ptr |
2886 | 3063 | | byte foo |
2887 | 3064 | | |
2888 | 3065 | | define main routine |
2889 | | inputs buf | |
3066 | | inputs tab | |
2890 | 3067 | | outputs a |
2891 | 3068 | | trashes y, z, n, ptr |
2892 | 3069 | | { |
2893 | 3070 | | ld y, 0 |
2894 | | copy ^buf, ptr | |
2895 | | ld a, [ptr] + y | |
2896 | | } | |
2897 | = ok | |
2898 | ||
2899 | Write the `a` register through a pointer. Note that this is done with `st`, | |
3071 | | point ptr into tab { | |
3072 | | ld a, [ptr] + y | |
3073 | | } | |
3074 | | } | |
3075 | = ok | |
3076 | ||
3077 | Write the `a` register through a pointer into a table. Note that this is done with `st`, | |
2900 | 3078 | not `copy`. |
2901 | 3079 | |
2902 | | buffer[2048] buf | |
3080 | | byte table[256] tab | |
2903 | 3081 | | pointer ptr |
2904 | 3082 | | byte foo |
2905 | 3083 | | |
2906 | 3084 | | define main routine |
2907 | | inputs buf | |
2908 | | outputs buf | |
3085 | | inputs tab | |
3086 | | outputs tab | |
2909 | 3087 | | trashes a, y, z, n, ptr |
2910 | 3088 | | { |
2911 | 3089 | | ld y, 0 |
2912 | | copy ^buf, ptr | |
2913 | | ld a, 255 | |
2914 | | st a, [ptr] + y | |
3090 | | point ptr into tab { | |
3091 | | ld a, 255 | |
3092 | | st a, [ptr] + y | |
3093 | | } | |
3094 | | } | |
3095 | = ok | |
3096 | ||
3097 | Cannot get a pointer into a non-byte (for instance, word) table. | |
3098 | ||
3099 | | word table[256] tab | |
3100 | | pointer ptr | |
3101 | | byte foo | |
3102 | | | |
3103 | | define main routine | |
3104 | | inputs tab | |
3105 | | outputs foo | |
3106 | | trashes a, y, z, n, ptr | |
3107 | | { | |
3108 | | ld y, 0 | |
3109 | | point ptr into tab { | |
3110 | | copy [ptr] + y, foo | |
3111 | | } | |
3112 | | } | |
3113 | ? TypeMismatchError | |
3114 | ||
3115 | Cannot get a pointer into a non-byte (for instance, vector) table. | |
3116 | ||
3117 | | vector (routine trashes a, z, n) table[256] tab | |
3118 | | pointer ptr | |
3119 | | vector (routine trashes a, z, n) foo | |
3120 | | | |
3121 | | define main routine | |
3122 | | inputs tab | |
3123 | | outputs foo | |
3124 | | trashes a, y, z, n, ptr | |
3125 | | { | |
3126 | | ld y, 0 | |
3127 | | point ptr into tab { | |
3128 | | copy [ptr] + y, foo | |
3129 | | } | |
3130 | | } | |
3131 | ? TypeMismatchError | |
3132 | ||
3133 | `point into` by itself only requires `ptr` to be writeable. By itself, | |
3134 | it does not require `tab` to be readable or writeable. | |
3135 | ||
3136 | | byte table[256] tab | |
3137 | | pointer ptr | |
3138 | | | |
3139 | | define main routine | |
3140 | | trashes a, z, n, ptr | |
3141 | | { | |
3142 | | point ptr into tab { | |
3143 | | ld a, 0 | |
3144 | | } | |
3145 | | } | |
3146 | = ok | |
3147 | ||
3148 | | byte table[256] tab | |
3149 | | pointer ptr | |
3150 | | | |
3151 | | define main routine | |
3152 | | trashes a, z, n | |
3153 | | { | |
3154 | | point ptr into tab { | |
3155 | | ld a, 0 | |
3156 | | } | |
3157 | | } | |
3158 | ? ForbiddenWriteError | |
3159 | ||
3160 | After a `point into` block, the pointer is no longer meaningful and cannot | |
3161 | be considered an output of the routine. | |
3162 | ||
3163 | | byte table[256] tab | |
3164 | | pointer ptr | |
3165 | | | |
3166 | | define main routine | |
3167 | | inputs tab | |
3168 | | outputs y, tab, ptr | |
3169 | | trashes a, z, n | |
3170 | | { | |
3171 | | ld y, 0 | |
3172 | | point ptr into tab { | |
3173 | | copy 123, [ptr] + y | |
3174 | | } | |
3175 | | } | |
3176 | ? UnmeaningfulOutputError | |
3177 | ||
3178 | If code in a routine reads from a table through a pointer, the table must be in | |
3179 | the `inputs` of that routine. | |
3180 | ||
3181 | | byte table[256] tab | |
3182 | | pointer ptr | |
3183 | | byte foo | |
3184 | | | |
3185 | | define main routine | |
3186 | | outputs foo | |
3187 | | trashes a, y, z, n, ptr | |
3188 | | { | |
3189 | | ld y, 0 | |
3190 | | point ptr into tab { | |
3191 | | copy [ptr] + y, foo | |
3192 | | } | |
3193 | | } | |
3194 | ? UnmeaningfulReadError | |
3195 | ||
3196 | Likewise, if code in a routine writes into a table via a pointer, the table must | |
3197 | be in the `outputs` of that routine. | |
3198 | ||
3199 | | byte table[256] tab | |
3200 | | pointer ptr | |
3201 | | | |
3202 | | define main routine | |
3203 | | inputs tab | |
3204 | | trashes a, y, z, n, ptr | |
3205 | | { | |
3206 | | ld y, 0 | |
3207 | | point ptr into tab { | |
3208 | | copy 123, [ptr] + y | |
3209 | | } | |
3210 | | } | |
3211 | ? ForbiddenWriteError | |
3212 | ||
3213 | If code in a routine reads from a table through a pointer, the pointer *should* | |
3214 | remain inside the range of the table. This is currently not checked. | |
3215 | ||
3216 | | byte table[32] tab | |
3217 | | pointer ptr | |
3218 | | byte foo | |
3219 | | | |
3220 | | define main routine | |
3221 | | inputs tab | |
3222 | | outputs foo | |
3223 | | trashes a, y, c, z, n, v, ptr | |
3224 | | { | |
3225 | | ld y, 0 | |
3226 | | point ptr into tab { | |
3227 | | st off, c | |
3228 | | add ptr, word 100 | |
3229 | | copy [ptr] + y, foo | |
3230 | | } | |
3231 | | } | |
3232 | = ok | |
3233 | ||
3234 | Likewise, if code in a routine writes into a table through a pointer, the pointer | |
3235 | *should* remain inside the range of the table. This is currently not checked. | |
3236 | ||
3237 | | byte table[32] tab | |
3238 | | pointer ptr | |
3239 | | | |
3240 | | define main routine | |
3241 | | inputs tab | |
3242 | | outputs tab | |
3243 | | trashes a, y, c, z, n, v, ptr | |
3244 | | { | |
3245 | | ld y, 0 | |
3246 | | point ptr into tab { | |
3247 | | st off, c | |
3248 | | add ptr, word 100 | |
3249 | | copy 123, [ptr] + y | |
3250 | | } | |
2915 | 3251 | | } |
2916 | 3252 | = ok |
2917 | 3253 |
384 | 384 | = $081B DEC $081F,X |
385 | 385 | = $081E RTS |
386 | 386 | |
387 | Using a constant offset, you can read and write entries in | |
388 | the table beyond the 256th. | |
389 | ||
390 | | byte one | |
391 | | byte table[1024] many | |
392 | | | |
393 | | define main routine | |
394 | | inputs many | |
395 | | outputs many | |
396 | | trashes a, x, n, z | |
397 | | { | |
398 | | ld x, 0 | |
399 | | ld a, many + x | |
400 | | st a, many + x | |
401 | | ld a, many + 999 + x | |
402 | | st a, many + 1000 + x | |
403 | | } | |
404 | = $080D LDX #$00 | |
405 | = $080F LDA $081D,X | |
406 | = $0812 STA $081D,X | |
407 | = $0815 LDA $0C04,X | |
408 | = $0818 STA $0C05,X | |
409 | = $081B RTS | |
410 | ||
411 | Instructions on tables, with constant offsets. | |
412 | ||
413 | | byte table[256] many | |
414 | | | |
415 | | define main routine | |
416 | | inputs many | |
417 | | outputs many | |
418 | | trashes a, x, c, n, z, v | |
419 | | { | |
420 | | ld x, 0 | |
421 | | ld a, 0 | |
422 | | st off, c | |
423 | | add a, many + 1 + x | |
424 | | sub a, many + 2 + x | |
425 | | cmp a, many + 3 + x | |
426 | | and a, many + 4 + x | |
427 | | or a, many + 5 + x | |
428 | | xor a, many + 6 + x | |
429 | | shl many + 7 + x | |
430 | | shr many + 8 + x | |
431 | | inc many + 9 + x | |
432 | | dec many + 10 + x | |
433 | | } | |
434 | = $080D LDX #$00 | |
435 | = $080F LDA #$00 | |
436 | = $0811 CLC | |
437 | = $0812 ADC $0832,X | |
438 | = $0815 SBC $0833,X | |
439 | = $0818 CMP $0834,X | |
440 | = $081B AND $0835,X | |
441 | = $081E ORA $0836,X | |
442 | = $0821 EOR $0837,X | |
443 | = $0824 ROL $0838,X | |
444 | = $0827 ROR $0839,X | |
445 | = $082A INC $083A,X | |
446 | = $082D DEC $083B,X | |
447 | = $0830 RTS | |
448 | ||
387 | 449 | Compiling 16-bit `cmp`. |
388 | 450 | |
389 | 451 | | word za @ 60001 |
874 | 936 | = $0817 RTS |
875 | 937 | = $0818 INX |
876 | 938 | = $0819 RTS |
939 | ||
940 | Copy byte to byte table and back, with both `x` and `y` as indexes, | |
941 | plus constant offsets. | |
942 | ||
943 | | byte one | |
944 | | byte table[256] many | |
945 | | | |
946 | | define main routine | |
947 | | inputs one, many | |
948 | | outputs one, many | |
949 | | trashes a, x, y, n, z | |
950 | | { | |
951 | | ld x, 0 | |
952 | | ld y, 0 | |
953 | | ld a, 77 | |
954 | | st a, many + x | |
955 | | st a, many + y | |
956 | | st a, many + 1 + x | |
957 | | st a, many + 1 + y | |
958 | | ld a, many + x | |
959 | | ld a, many + y | |
960 | | ld a, many + 8 + x | |
961 | | ld a, many + 8 + y | |
962 | | } | |
963 | = $080D LDX #$00 | |
964 | = $080F LDY #$00 | |
965 | = $0811 LDA #$4D | |
966 | = $0813 STA $082D,X | |
967 | = $0816 STA $082D,Y | |
968 | = $0819 STA $082E,X | |
969 | = $081C STA $082E,Y | |
970 | = $081F LDA $082D,X | |
971 | = $0822 LDA $082D,Y | |
972 | = $0825 LDA $0835,X | |
973 | = $0828 LDA $0835,Y | |
974 | = $082B RTS | |
877 | 975 | |
878 | 976 | Copy word to word table and back, with both `x` and `y` as indexes. |
879 | 977 | |
917 | 1015 | = $0848 STA $084D |
918 | 1016 | = $084B RTS |
919 | 1017 | |
1018 | Copy word to word table and back, with constant offsets. | |
1019 | ||
1020 | | word one | |
1021 | | word table[256] many | |
1022 | | | |
1023 | | define main routine | |
1024 | | inputs one, many | |
1025 | | outputs one, many | |
1026 | | trashes a, x, y, n, z | |
1027 | | { | |
1028 | | ld x, 0 | |
1029 | | ld y, 0 | |
1030 | | copy 777, one | |
1031 | | copy one, many + 1 + x | |
1032 | | copy one, many + 2 + y | |
1033 | | copy many + 3 + x, one | |
1034 | | copy many + 4 + y, one | |
1035 | | } | |
1036 | = $080D LDX #$00 | |
1037 | = $080F LDY #$00 | |
1038 | = $0811 LDA #$09 | |
1039 | = $0813 STA $084C | |
1040 | = $0816 LDA #$03 | |
1041 | = $0818 STA $084D | |
1042 | = $081B LDA $084C | |
1043 | = $081E STA $084F,X | |
1044 | = $0821 LDA $084D | |
1045 | = $0824 STA $094F,X | |
1046 | = $0827 LDA $084C | |
1047 | = $082A STA $0850,Y | |
1048 | = $082D LDA $084D | |
1049 | = $0830 STA $0950,Y | |
1050 | = $0833 LDA $0851,X | |
1051 | = $0836 STA $084C | |
1052 | = $0839 LDA $0951,X | |
1053 | = $083C STA $084D | |
1054 | = $083F LDA $0852,Y | |
1055 | = $0842 STA $084C | |
1056 | = $0845 LDA $0952,Y | |
1057 | = $0848 STA $084D | |
1058 | = $084B RTS | |
1059 | ||
920 | 1060 | Indirect call. |
921 | 1061 | |
922 | 1062 | | vector routine |
1016 | 1156 | = $082F LDA $0848,X |
1017 | 1157 | = $0832 STA $0846 |
1018 | 1158 | = $0835 LDA $0948,X |
1159 | = $0838 STA $0847 | |
1160 | = $083B JSR $0842 | |
1161 | = $083E RTS | |
1162 | = $083F LDX #$C8 | |
1163 | = $0841 RTS | |
1164 | = $0842 JMP ($0846) | |
1165 | = $0845 RTS | |
1166 | ||
1167 | Copying to and from a vector table, with constant offsets. | |
1168 | ||
1169 | | vector routine | |
1170 | | outputs x | |
1171 | | trashes a, z, n | |
1172 | | one | |
1173 | | vector routine | |
1174 | | outputs x | |
1175 | | trashes a, z, n | |
1176 | | table[256] many | |
1177 | | | |
1178 | | define bar routine outputs x trashes a, z, n { | |
1179 | | ld x, 200 | |
1180 | | } | |
1181 | | | |
1182 | | define main routine | |
1183 | | inputs one, many | |
1184 | | outputs one, many | |
1185 | | trashes a, x, n, z | |
1186 | | { | |
1187 | | ld x, 0 | |
1188 | | copy bar, one | |
1189 | | copy bar, many + 1 + x | |
1190 | | copy one, many + 2 + x | |
1191 | | copy many + 3 + x, one | |
1192 | | call one | |
1193 | | } | |
1194 | = $080D LDX #$00 | |
1195 | = $080F LDA #$3F | |
1196 | = $0811 STA $0846 | |
1197 | = $0814 LDA #$08 | |
1198 | = $0816 STA $0847 | |
1199 | = $0819 LDA #$3F | |
1200 | = $081B STA $0849,X | |
1201 | = $081E LDA #$08 | |
1202 | = $0820 STA $0949,X | |
1203 | = $0823 LDA $0846 | |
1204 | = $0826 STA $084A,X | |
1205 | = $0829 LDA $0847 | |
1206 | = $082C STA $094A,X | |
1207 | = $082F LDA $084B,X | |
1208 | = $0832 STA $0846 | |
1209 | = $0835 LDA $094B,X | |
1019 | 1210 | = $0838 STA $0847 |
1020 | 1211 | = $083B JSR $0842 |
1021 | 1212 | = $083E RTS |
1206 | 1397 | = $081D STA $0822 |
1207 | 1398 | = $0820 RTS |
1208 | 1399 | |
1209 | ### Buffers and Pointers | |
1210 | ||
1211 | Load address into pointer. | |
1212 | ||
1213 | | buffer[2048] buf | |
1400 | ### Tables and Pointers | |
1401 | ||
1402 | Load address of table into pointer. | |
1403 | ||
1404 | | byte table[256] tab | |
1214 | 1405 | | pointer ptr @ 254 |
1215 | 1406 | | |
1216 | 1407 | | define main routine |
1217 | | inputs buf | |
1218 | | outputs buf, y | |
1408 | | inputs tab | |
1409 | | outputs tab, y | |
1219 | 1410 | | trashes a, z, n, ptr |
1220 | 1411 | | { |
1221 | 1412 | | ld y, 0 |
1222 | | copy ^buf, ptr | |
1413 | | point ptr into tab { | |
1414 | | } | |
1223 | 1415 | | } |
1224 | 1416 | = $080D LDY #$00 |
1225 | 1417 | = $080F LDA #$18 |
1230 | 1422 | |
1231 | 1423 | Write literal through a pointer. |
1232 | 1424 | |
1233 | | buffer[2048] buf | |
1425 | | byte table[256] tab | |
1234 | 1426 | | pointer ptr @ 254 |
1235 | 1427 | | |
1236 | 1428 | | define main routine |
1237 | | inputs buf | |
1238 | | outputs buf, y | |
1429 | | inputs tab | |
1430 | | outputs tab, y | |
1239 | 1431 | | trashes a, z, n, ptr |
1240 | 1432 | | { |
1241 | 1433 | | ld y, 0 |
1242 | | copy ^buf, ptr | |
1243 | | copy 123, [ptr] + y | |
1434 | | point ptr into tab { | |
1435 | | copy 123, [ptr] + y | |
1436 | | } | |
1244 | 1437 | | } |
1245 | 1438 | = $080D LDY #$00 |
1246 | 1439 | = $080F LDA #$1C |
1253 | 1446 | |
1254 | 1447 | Write stored value through a pointer. |
1255 | 1448 | |
1256 | | buffer[2048] buf | |
1449 | | byte table[256] tab | |
1257 | 1450 | | pointer ptr @ 254 |
1258 | 1451 | | byte foo |
1259 | 1452 | | |
1260 | 1453 | | define main routine |
1261 | | inputs foo, buf | |
1262 | | outputs y, buf | |
1454 | | inputs foo, tab | |
1455 | | outputs y, tab | |
1263 | 1456 | | trashes a, z, n, ptr |
1264 | 1457 | | { |
1265 | 1458 | | ld y, 0 |
1266 | | copy ^buf, ptr | |
1267 | | copy foo, [ptr] + y | |
1459 | | point ptr into tab { | |
1460 | | copy foo, [ptr] + y | |
1461 | | } | |
1268 | 1462 | | } |
1269 | 1463 | = $080D LDY #$00 |
1270 | 1464 | = $080F LDA #$1D |
1271 | 1465 | = $0811 STA $FE |
1272 | 1466 | = $0813 LDA #$08 |
1273 | 1467 | = $0815 STA $FF |
1274 | = $0817 LDA $101D | |
1468 | = $0817 LDA $091D | |
1275 | 1469 | = $081A STA ($FE),Y |
1276 | 1470 | = $081C RTS |
1277 | 1471 | |
1278 | 1472 | Read through a pointer, into a byte storage location, or the `a` register. |
1279 | 1473 | |
1280 | | buffer[2048] buf | |
1474 | | byte table[256] tab | |
1281 | 1475 | | pointer ptr @ 254 |
1282 | 1476 | | byte foo |
1283 | 1477 | | |
1284 | 1478 | | define main routine |
1285 | | inputs buf | |
1479 | | inputs tab | |
1286 | 1480 | | outputs y, foo |
1287 | 1481 | | trashes a, z, n, ptr |
1288 | 1482 | | { |
1289 | 1483 | | ld y, 0 |
1290 | | copy ^buf, ptr | |
1291 | | copy [ptr] + y, foo | |
1292 | | ld a, [ptr] + y | |
1484 | | point ptr into tab { | |
1485 | | copy [ptr] + y, foo | |
1486 | | ld a, [ptr] + y | |
1487 | | } | |
1293 | 1488 | | } |
1294 | 1489 | = $080D LDY #$00 |
1295 | 1490 | = $080F LDA #$1F |
1297 | 1492 | = $0813 LDA #$08 |
1298 | 1493 | = $0815 STA $FF |
1299 | 1494 | = $0817 LDA ($FE),Y |
1300 | = $0819 STA $101F | |
1495 | = $0819 STA $091F | |
1301 | 1496 | = $081C LDA ($FE),Y |
1302 | 1497 | = $081E RTS |
1303 | 1498 | |
1304 | 1499 | Read and write through two pointers. |
1305 | 1500 | |
1306 | | buffer[2048] buf | |
1501 | | byte table[256] tab | |
1307 | 1502 | | pointer ptra @ 252 |
1308 | 1503 | | pointer ptrb @ 254 |
1309 | 1504 | | |
1310 | 1505 | | define main routine |
1311 | | inputs buf | |
1312 | | outputs buf | |
1506 | | inputs tab | |
1507 | | outputs tab | |
1313 | 1508 | | trashes a, y, z, n, ptra, ptrb |
1314 | 1509 | | { |
1315 | 1510 | | ld y, 0 |
1316 | | copy ^buf, ptra | |
1317 | | copy ^buf, ptrb | |
1318 | | copy [ptra] + y, [ptrb] + y | |
1511 | | point ptra into tab { | |
1512 | | point ptrb into tab { | |
1513 | | copy [ptra] + y, [ptrb] + y | |
1514 | | } | |
1515 | | } | |
1319 | 1516 | | } |
1320 | 1517 | = $080D LDY #$00 |
1321 | 1518 | = $080F LDA #$24 |
1332 | 1529 | |
1333 | 1530 | Write the `a` register through a pointer. |
1334 | 1531 | |
1335 | | buffer[2048] buf | |
1532 | | byte table[256] tab | |
1336 | 1533 | | pointer ptr @ 254 |
1337 | 1534 | | byte foo |
1338 | 1535 | | |
1339 | 1536 | | define main routine |
1340 | | inputs buf | |
1341 | | outputs buf | |
1537 | | inputs tab | |
1538 | | outputs tab | |
1342 | 1539 | | trashes a, y, z, n, ptr |
1343 | 1540 | | { |
1344 | 1541 | | ld y, 0 |
1345 | | copy ^buf, ptr | |
1346 | | ld a, 255 | |
1347 | | st a, [ptr] + y | |
1542 | | point ptr into tab { | |
1543 | | ld a, 255 | |
1544 | | st a, [ptr] + y | |
1545 | | } | |
1348 | 1546 | | } |
1349 | 1547 | = $080D LDY #$00 |
1350 | 1548 | = $080F LDA #$1C |
1358 | 1556 | Add a word memory location, and a literal word, to a pointer, and then read through it. |
1359 | 1557 | Note that this is *not* range-checked. (Yet.) |
1360 | 1558 | |
1361 | | buffer[2048] buf | |
1559 | | byte table[256] tab | |
1362 | 1560 | | pointer ptr @ 254 |
1363 | 1561 | | byte foo |
1364 | 1562 | | word delta |
1365 | 1563 | | |
1366 | 1564 | | define main routine |
1367 | | inputs buf | |
1565 | | inputs tab | |
1368 | 1566 | | outputs y, foo, delta |
1369 | 1567 | | trashes a, c, v, z, n, ptr |
1370 | 1568 | | { |
1371 | 1569 | | copy 619, delta |
1372 | 1570 | | ld y, 0 |
1373 | 1571 | | st off, c |
1374 | | copy ^buf, ptr | |
1375 | | add ptr, delta | |
1376 | | add ptr, word 1 | |
1377 | | copy [ptr] + y, foo | |
1572 | | point ptr into tab { | |
1573 | | add ptr, delta | |
1574 | | add ptr, word 1 | |
1575 | | copy [ptr] + y, foo | |
1576 | | } | |
1378 | 1577 | | } |
1379 | 1578 | = $080D LDA #$6B |
1380 | = $080F STA $1043 | |
1579 | = $080F STA $0943 | |
1381 | 1580 | = $0812 LDA #$02 |
1382 | = $0814 STA $1044 | |
1581 | = $0814 STA $0944 | |
1383 | 1582 | = $0817 LDY #$00 |
1384 | 1583 | = $0819 CLC |
1385 | 1584 | = $081A LDA #$42 |
1387 | 1586 | = $081E LDA #$08 |
1388 | 1587 | = $0820 STA $FF |
1389 | 1588 | = $0822 LDA $FE |
1390 | = $0824 ADC $1043 | |
1589 | = $0824 ADC $0943 | |
1391 | 1590 | = $0827 STA $FE |
1392 | 1591 | = $0829 LDA $FF |
1393 | = $082B ADC $1044 | |
1592 | = $082B ADC $0944 | |
1394 | 1593 | = $082E STA $FF |
1395 | 1594 | = $0830 LDA $FE |
1396 | 1595 | = $0832 ADC #$01 |
1399 | 1598 | = $0838 ADC #$00 |
1400 | 1599 | = $083A STA $FF |
1401 | 1600 | = $083C LDA ($FE),Y |
1402 | = $083E STA $1042 | |
1601 | = $083E STA $0942 | |
1403 | 1602 | = $0841 RTS |
1404 | 1603 | |
1405 | 1604 | ### Trash |
162 | 162 | |
163 | 163 | Other blocks. |
164 | 164 | |
165 | | byte table[256] tab | |
166 | | pointer ptr | |
167 | | | |
165 | 168 | | define main routine trashes a, x, c, z, v { |
166 | 169 | | with interrupts off { |
167 | 170 | | save a, x, c { |
171 | 174 | | save a, x, c { |
172 | 175 | | ld a, 0 |
173 | 176 | | } |
177 | | point ptr into tab { | |
178 | | ld a, [ptr] + y | |
179 | | } | |
174 | 180 | | } |
175 | 181 | = ok |
176 | 182 | |
179 | 185 | | byte byt |
180 | 186 | | word wor |
181 | 187 | | vector routine trashes a vec |
182 | | buffer[2048] buf | |
188 | | byte table[2048] buf | |
183 | 189 | | pointer ptr |
184 | 190 | | |
185 | 191 | | define main routine { |
191 | 197 | | byte table[256] many |
192 | 198 | | word table[256] wmany |
193 | 199 | | vector (routine trashes a) table[256] vmany |
200 | | byte bval | |
201 | | word wval | |
194 | 202 | | |
195 | 203 | | define main routine { |
196 | 204 | | ld x, 0 |
206 | 214 | | shr many + x |
207 | 215 | | inc many + x |
208 | 216 | | dec many + x |
217 | | ld a, many + x | |
218 | | st a, many + x | |
219 | | copy wval, wmany + x | |
220 | | copy wmany + x, wval | |
221 | | } | |
222 | = ok | |
223 | ||
224 | Indexing with an offset in some tables. | |
225 | ||
226 | | byte table[256] many | |
227 | | word table[256] wmany | |
228 | | byte bval | |
229 | | word wval | |
230 | | | |
231 | | define main routine { | |
232 | | ld x, 0 | |
233 | | ld a, 0 | |
234 | | st off, c | |
235 | | add a, many + 100 + x | |
236 | | sub a, many + 100 + x | |
237 | | cmp a, many + 100 + x | |
238 | | and a, many + 100 + x | |
239 | | or a, many + 100 + x | |
240 | | xor a, many + 100 + x | |
241 | | shl many + 100 + x | |
242 | | shr many + 100 + x | |
243 | | inc many + 100 + x | |
244 | | dec many + 100 + x | |
245 | | ld a, many + 100 + x | |
246 | | st a, many + 100 + x | |
247 | | copy wval, wmany + 100 + x | |
248 | | copy wmany + 100 + x, wval | |
209 | 249 | | } |
210 | 250 | = ok |
211 | 251 | |
212 | 252 | The number of entries in a table must be |
213 | greater than 0 and less than or equal to 256. | |
253 | greater than 0 and less than or equal to 65536. | |
254 | ||
255 | (In previous versions, a table could have at | |
256 | most 256 entries. They can now have more, however | |
257 | the offset-access syntax can only access the | |
258 | first 256. To access more, a pointer is required.) | |
214 | 259 | |
215 | 260 | | word table[512] many |
216 | 261 | | |
221 | 266 | | { |
222 | 267 | | ld x, 0 |
223 | 268 | | copy 9999, many + x |
269 | | } | |
270 | = ok | |
271 | ||
272 | | byte table[65536] many | |
273 | | | |
274 | | define main routine | |
275 | | inputs many | |
276 | | outputs many | |
277 | | trashes a, x, n, z | |
278 | | { | |
279 | | ld x, 0 | |
280 | | copy 99, many + x | |
281 | | } | |
282 | = ok | |
283 | ||
284 | | byte table[65537] many | |
285 | | | |
286 | | define main routine | |
287 | | inputs many | |
288 | | outputs many | |
289 | | trashes a, x, n, z | |
290 | | { | |
291 | | ld x, 0 | |
292 | | copy 99, many + x | |
224 | 293 | | } |
225 | 294 | ? SyntaxError |
226 | 295 | |
281 | 350 | | |
282 | 351 | | define main routine { |
283 | 352 | | ld a, lives |
353 | | } | |
354 | = ok | |
355 | ||
356 | Named constants can be used as offsets. | |
357 | ||
358 | | const lives 3 | |
359 | | const w1 1000 | |
360 | | | |
361 | | byte table[w1] those | |
362 | | | |
363 | | define main routine { | |
364 | | ld y, 0 | |
365 | | ld a, those + lives + y | |
284 | 366 | | } |
285 | 367 | = ok |
286 | 368 | |
589 | 671 | | } |
590 | 672 | ? Expected '}', but found 'ld' |
591 | 673 | |
592 | Buffers and pointers. | |
593 | ||
594 | | buffer[2048] buf | |
674 | Tables and pointers. | |
675 | ||
676 | | byte table[2048] buf | |
595 | 677 | | pointer ptr |
596 | 678 | | pointer ptrb |
597 | 679 | | byte foo |
598 | 680 | | |
599 | 681 | | define main routine { |
600 | | copy ^buf, ptr | |
601 | | copy 123, [ptr] + y | |
602 | | copy [ptr] + y, foo | |
603 | | copy [ptr] + y, [ptrb] + y | |
682 | | point ptr into buf { | |
683 | | copy 123, [ptr] + y | |
684 | | copy [ptr] + y, foo | |
685 | | copy [ptr] + y, [ptrb] + y | |
686 | | } | |
604 | 687 | | } |
605 | 688 | = ok |
606 | 689 |