git @ Cat's Eye Technologies SixtyPical / b19267d
Checkpoint import of changes for version 0.19. Chris Pressey 2 years ago
14 changed file(s) with 1224 addition(s) and 479 deletion(s). Raw diff Collapse all Expand all
00 History of SixtyPical
11 =====================
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`.
220
321 0.18
422 ----
1111 Which uses some other storage location instead of the stack. A local static
1212 would be a good candidate for such.
1313
14 ### Associate each pointer with the buffer it points into
14 ### Analyze `call` within blocks?
1515
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?
1817
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?
2119
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?
2321
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?
2523
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`.)
2728
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.
3130
32 They are therefore a "view" of a section of a buffer.
31 ### Pointers associated globally with a table
3332
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.
3635
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.
4039
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.
4244
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.
4574
4675 ### Tail-call optimization
4776
1111
1212 # ----------------------------------------------------------------- #
1313
14 from argparse import ArgumentParser
1415 import codecs
15 from argparse import ArgumentParser
16 import json
1617 from pprint import pprint
1718 import sys
1819 import traceback
4243 program = merge_programs(programs)
4344
4445 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")
4653
4754 compilation_roster = None
4855 if options.optimize_fallthru:
4956 from sixtypical.fallthru import FallthruAnalyzer
5057
5158 def dump(data, label=None):
52 import json
5359 if not options.dump_fallthru_info:
5460 return
5561 if label:
114120 help="Only parse and analyze the program; do not compile it."
115121 )
116122 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(
117129 "--optimize-fallthru",
118130 action="store_true",
119131 help="Reorder the routines in the program to maximize the number of tail calls "
122134 argparser.add_argument(
123135 "--dump-fallthru-info",
124136 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."
126138 )
127139 argparser.add_argument(
128140 "--parse-only",
00 SixtyPical
11 ==========
22
3 This document describes the SixtyPical programming language version 0.15,
3 This document describes the SixtyPical programming language version 0.19,
44 both its static semantics (the capabilities and limits of the static
55 analyses it defines) and its runtime semantics (with reference to the
66 semantics of 6502 machine code.)
1111 Refer to the bottom of this document for an EBNF grammar of the syntax of
1212 the language.
1313
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:
1827
1928 * bit (2 possible values)
2029 * byte (256 possible values)
2130 * 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
2242 * 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 ####
3354
3455 A program may define its own types using the `typedef` feature. Typedefs
3556 must occur before everything else in the program. A typedef takes a
3657 type expression and an identifier which has not previously been used in
3758 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 ###
4263
4364 A primary concept in SixtyPical is the *memory location*. At any given point
4465 in time during execution, each memory location is either *uninitialized* or
5071 There are four general kinds of memory location. The first three are
5172 pre-defined and built-in.
5273
53 ### Registers ###
74 #### Registers ####
5475
5576 Each of these hold a byte. They are initially uninitialized.
5677
5879 x
5980 y
6081
61 ### Flags ###
82 #### Flags ####
6283
6384 Each of these hold a bit. They are initially uninitialized.
6485
6788 v (overflow)
6889 n (negative)
6990
70 ### Constants ###
91 #### Constants ####
7192
7293 It may be strange to think of constants as memory locations, but keep in mind
7394 that a memory location in SixtyPical need not map to a memory location in the
96117 Note that if a word constant is between 256 and 65535, the leading `word`
97118 token can be omitted.
98119
99 ### User-defined ###
120 #### User-defined ####
100121
101122 There may be any number of user-defined memory locations. They are defined
102123 by giving the type (which may be any type except `bit` and `routine`) and the
136157 that literal integers in the code are always immediate values. (But this
137158 may change at some point.)
138159
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.
143188
144189 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
146191
147192 LDA ($02), Y
148193 STA ($02), Y
150195 There are extended instruction modes for using these types of memory location.
151196 See `copy` below, but here is some illustrative example code:
152197
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.
161207
162208 Routines
163209 --------
299345 After execution, dest is considered initialized, and `z` and `n`, and
300346 `a` are considered uninitialized.
301347
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:
313349
314350 copy [<src-memory-location>] + y, <dest-memory-location>
315351 copy <src-memory-location>, [<dest-memory-location>] + y
349385 when the dest is `a`.
350386
351387 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.
353389
354390 ### inc ###
355391
580616 Program ::= {ConstDefn | TypeDefn} {Defn} {Routine}.
581617 ConstDefn::= "const" Ident<new> Const.
582618 TypeDefn::= "typedef" Type Ident<new>.
583 Defn ::= Type Ident<new> [Constraints] (":" Const | "@" LitWord).
619 Defn ::= Type Ident<new> (":" Const | "@" LitWord).
584620 Type ::= TypeTerm ["table" TypeSize].
585621 TypeExpr::= "byte"
586622 | "word"
587 | "buffer" TypeSize
588623 | "pointer"
589624 | "vector" TypeTerm
590625 | "routine" Constraints
593628 TypeSize::= "[" LitWord "]".
594629 Constrnt::= ["inputs" LocExprs] ["outputs" LocExprs] ["trashes" LocExprs].
595630 Routine ::= "define" Ident<new> Type (Block | "@" LitWord).
596 | "routine" Ident<new> Constraints (Block | "@" LitWord)
597 .
598631 LocExprs::= LocExpr {"," LocExpr}.
599 LocExpr ::= Register | Flag | Const | Ident.
632 LocExpr ::= Register | Flag | Const | Ident [["+" Const] "+" Register].
600633 Register::= "a" | "x" | "y".
601634 Flag ::= "c" | "z" | "n" | "v".
602635 Const ::= Literal | Ident<const>.
2020 // and the end of their own routines, so the type needs to be compatible.
2121 // (In a good sense, it is a continuation.)
2222 //
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 //
3123
3224 typedef routine
3325 inputs joy2, press_fire_msg, dispatch_game_state,
3426 actor_pos, actor_delta, actor_logic,
3527 player_died,
36 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
28 screen, colormap
3729 outputs dispatch_game_state,
3830 actor_pos, actor_delta, actor_logic,
3931 player_died,
40 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
32 screen, colormap
4133 trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic
4234 game_state_routine
4335
6153
6254 byte vic_border @ 53280
6355 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
7658 byte joy2 @ $dc00
7759
7860 // ----------------------------------------------------------------
186168 }
187169
188170 define clear_screen routine
189 outputs screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
171 outputs screen, colormap
190172 trashes a, y, c, n, z
191173 {
192174 ld y, 0
193175 repeat {
194176 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
199181
200182 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
205187
206188 inc y
207189 cmp y, 250
281263 call check_new_position_in_bounds
282264
283265 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
291274 // if "collision" is with your own self, treat it as if it's blank space!
292275 cmp a, 81
293276 if z {
295278 }
296279 cmp a, 32
297280 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 }
302286
303287 copy new_pos, pos
304288
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 }
309294 } else {
310295 ld a, 1
311296 st a, player_died
320305 call check_new_position_in_bounds
321306
322307 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 }
330315 // if "collision" is with your own self, treat it as if it's blank space!
331316 cmp a, 82
332317 if z {
334319 }
335320 cmp a, 32
336321 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 }
341327
342328 copy new_pos, pos
343329
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 }
348335 }
349336 } else {
350337 copy delta, compare_target
371358 st on, c
372359 sub a, 64 // yuck. oh well
373360
374 st a, screen1 + y
361 st a, screen + y
375362 }
376363
377364 st off, c
443430
444431 define main routine
445432 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
448434 trashes a, y, n, c, z, vic_border, vic_bg
449435 {
450436 ld a, 5
00 // Include `support/${PLATFORM}.60p` before this source
11 // Should print Y
22
3 buffer[2048] buf
3 byte table[2048] buf
44 pointer ptr @ 254
55 byte foo
66
00 # encoding: UTF-8
11
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 )
35 from sixtypical.model import (
46 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,
79 REG_A, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
810 )
911
106108 unwriteable in certain contexts, such as `for` loops.
107109 """
108110 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}
114116 self._terminated = False
115117 self._gotos_encountered = set()
118 self._pointer_assoc = dict()
116119
117120 for ref in inputs:
118121 if ref.is_constant():
136139 LocationRef.format_set(self._touched), LocationRef.format_set(self._range), LocationRef.format_set(self._writeable)
137140 )
138141
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
139151 def clone(self):
140152 c = Context(self.routines, self.routine, [], [], [])
141153 c._touched = set(self._touched)
142154 c._range = dict(self._range)
143155 c._writeable = set(self._writeable)
156 c._pointer_assoc = dict(self._pointer_assoc)
157 c._gotos_encountered = set(self._gotos_encountered)
144158 return c
145159
146160 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.)"""
147167 self.routines = other.routines
148168 self.routine = other.routine
149169 self._touched = set(other._touched)
150170 self._range = dict(other._range)
151171 self._writeable = set(other._writeable)
152172 self._terminated = other._terminated
153 self._gotos_encounters = set(other._gotos_encountered)
173 self._pointer_assoc = dict(other._pointer_assoc)
154174
155175 def each_meaningful(self):
156176 for ref in self._range.keys():
196216 message += ' (%s)' % kwargs['message']
197217 raise exception_class(self.routine, message)
198218
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)
201224
202225 # inside should always be meaningful
203226 inside_range = self._range[inside]
207230 outside_range = self._range[outside]
208231 else:
209232 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]:
214235 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
217238 )
218239 )
219240
310331 def has_terminated(self):
311332 return self._terminated
312333
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):
314335 if (not TableType.is_a_table_type(src.ref.type, type_)) or (not dest.type == type_):
315336 raise TypeMismatchError(instr, '{} and {}'.format(src.ref.name, dest.name))
316337 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):
320341 if not TableType.is_a_table_type(dest.ref.type, type_):
321342 raise TypeMismatchError(instr, '{}'.format(dest.ref.name))
322343 self.assert_meaningful(dest.index)
323 self.assert_in_range(dest.index, dest.ref)
344 self.assert_in_range(dest.index, dest.ref, offset)
324345 self.set_written(dest.ref)
325346
326347 def extract(self, location):
358379 elif location in self._writeable:
359380 self._writeable.remove(location)
360381
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
361388
362389 class Analyzer(object):
363390
365392 self.current_routine = None
366393 self.routines = {}
367394 self.debug = debug
395 self.exit_contexts_map = {}
368396
369397 def assert_type(self, type_, *locations):
370398 for location in locations:
397425 assert isinstance(routine, Routine)
398426 if routine.block is None:
399427 # it's an extern, that's fine
400 return
428 return None
401429
402430 self.current_routine = routine
403431 type_ = routine.location.type
404432 context = Context(self.routines, routine, type_.inputs, type_.outputs, type_.trashes)
405433 self.exit_contexts = []
406434
407 if self.debug:
408 print("at start of routine `{}`:".format(routine.name))
409 print(context)
410
411435 self.analyze_block(routine.block, context)
412436
413437 trashed = set(context.each_touched()) - set(context.each_meaningful())
414438
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 }
426443
427444 if self.exit_contexts:
428445 # check that they are all consistent
437454 raise InconsistentExitError("Exit contexts are not consistent")
438455 if set(ex.each_writeable()) != exit_writeable:
439456 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.
440461 context.update_from(exit_context)
441462
442463 # these all apply whether we encountered goto(s) in this routine, or not...:
479500 raise IllegalJumpError(instr, instr)
480501 elif isinstance(instr, Save):
481502 self.analyze_save(instr, context)
503 elif isinstance(instr, PointInto):
504 self.analyze_point_into(instr, context)
482505 else:
483506 raise NotImplementedError
484507
493516
494517 if opcode == 'ld':
495518 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)
497520 elif isinstance(src, IndirectRef):
498521 # copying this analysis from the matching branch in `copy`, below
499522 if isinstance(src.ref.type, PointerType) and dest.type == TYPE_BYTE:
500523 pass
501524 else:
502525 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
503532 context.assert_meaningful(src.ref, REG_Y)
504533 elif src.type != dest.type:
505534 raise TypeMismatchError(instr, '{} and {}'.format(src.name, dest.name))
511540 if isinstance(dest, IndexedRef):
512541 if src.type != TYPE_BYTE:
513542 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)
515544 elif isinstance(dest, IndirectRef):
516545 # copying this analysis from the matching branch in `copy`, below
517546 if isinstance(dest.ref.type, PointerType) and src.type == TYPE_BYTE:
518547 pass
519548 else:
520549 raise TypeMismatchError(instr, (src, dest))
550
521551 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
523559 elif src.type != dest.type:
524560 raise TypeMismatchError(instr, '{} and {}'.format(src, dest))
525561 else:
529565 elif opcode == 'add':
530566 context.assert_meaningful(src, dest, FLAG_C)
531567 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)
533569 elif src.type == TYPE_BYTE:
534570 self.assert_type(TYPE_BYTE, src, dest)
535571 if dest != REG_A:
550586 elif opcode == 'sub':
551587 context.assert_meaningful(src, dest, FLAG_C)
552588 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)
554590 elif src.type == TYPE_BYTE:
555591 self.assert_type(TYPE_BYTE, src, dest)
556592 if dest != REG_A:
565601 elif opcode == 'cmp':
566602 context.assert_meaningful(src, dest)
567603 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)
569605 elif src.type == TYPE_BYTE:
570606 self.assert_type(TYPE_BYTE, src, dest)
571607 else:
575611 context.set_written(FLAG_Z, FLAG_N, FLAG_C)
576612 elif opcode == 'and':
577613 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)
579615 else:
580616 self.assert_type(TYPE_BYTE, src, dest)
581617 context.assert_meaningful(src, dest)
587623 context.set_top_of_range(dest, context.get_top_of_range(src))
588624 elif opcode in ('or', 'xor'):
589625 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)
591627 else:
592628 self.assert_type(TYPE_BYTE, src, dest)
593629 context.assert_meaningful(src, dest)
596632 elif opcode in ('inc', 'dec'):
597633 context.assert_meaningful(dest)
598634 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)
600636 context.set_written(dest.ref, FLAG_Z, FLAG_N)
601637 #context.invalidate_range(dest)
602638 else:
619655 elif opcode in ('shl', 'shr'):
620656 context.assert_meaningful(dest, FLAG_C)
621657 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)
623659 context.set_written(dest.ref, FLAG_Z, FLAG_N, FLAG_C)
624660 #context.invalidate_range(dest)
625661 else:
646682
647683 # 1. check that their types are compatible
648684
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):
655686 if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType):
656687 pass
657688 else:
678709 pass
679710 else:
680711 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)
682713
683714 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
684715 if TableType.is_a_table_type(src.ref.type, TYPE_WORD) and dest.type == TYPE_WORD:
688719 pass
689720 else:
690721 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)
692723
693724 elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef):
694725 if src.type == dest.type:
706737
707738 if isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef):
708739 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
711747 elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef):
712748 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)
714756 context.set_written(dest)
715757 elif isinstance(src, IndirectRef) and isinstance(dest, IndirectRef):
716758 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
719771 elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
720772 context.assert_meaningful(src, dest.ref, dest.index)
721773 context.set_written(dest.ref)
904956 else:
905957 context.set_touched(REG_A)
906958 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)
9696 class Save(Instr):
9797 value_attrs = ('locations',)
9898 child_attrs = ('block',)
99
100
101 class PointInto(Instr):
102 value_attrs = ('pointer', 'table',)
103 child_attrs = ('block',)
00 # encoding: UTF-8
11
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 )
35 from sixtypical.model import (
4 ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef,
6 ConstantRef, LocationRef, IndexedRef, IndirectRef,
57 TYPE_BIT, TYPE_BYTE, TYPE_WORD,
6 TableType, BufferType, PointerType, RoutineType, VectorType,
8 TableType, PointerType, RoutineType, VectorType,
79 REG_A, REG_X, REG_Y, FLAG_C
810 )
911 from sixtypical.emitter import Byte, Word, Table, Label, Offset, LowAddressByte, HighAddressByte
5456 length = 2
5557 elif isinstance(type_, TableType):
5658 length = type_.size * (1 if type_.of_type == TYPE_BYTE else 2)
57 elif isinstance(type_, BufferType):
58 length = type_.size
5959 if length is None:
6060 raise NotImplementedError("Need size for type {}".format(type_))
6161 return length
171171 return self.compile_with_interrupts_off(instr)
172172 elif isinstance(instr, Save):
173173 return self.compile_save(instr)
174 elif isinstance(instr, PointInto):
175 return self.compile_point_into(instr)
174176 else:
175177 raise NotImplementedError
176178
189191 elif isinstance(src, ConstantRef):
190192 self.emitter.emit(LDA(Immediate(Byte(src.value))))
191193 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))))
193195 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))))
195197 elif isinstance(src, IndirectRef) and isinstance(src.ref.type, PointerType):
196198 self.emitter.emit(LDA(IndirectY(self.get_label(src.ref.name))))
197199 else:
202204 elif isinstance(src, ConstantRef):
203205 self.emitter.emit(LDX(Immediate(Byte(src.value))))
204206 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))))
206208 else:
207209 self.emitter.emit(LDX(self.absolute_or_zero_page(self.get_label(src.name))))
208210 elif dest == REG_Y:
211213 elif isinstance(src, ConstantRef):
212214 self.emitter.emit(LDY(Immediate(Byte(src.value))))
213215 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))))
215217 else:
216218 self.emitter.emit(LDY(self.absolute_or_zero_page(self.get_label(src.name))))
217219 else:
233235 REG_X: AbsoluteX,
234236 REG_Y: AbsoluteY,
235237 }[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))
237239 elif isinstance(dest, IndirectRef) and isinstance(dest.ref.type, PointerType):
238240 operand = IndirectY(self.get_label(dest.ref.name))
239241 else:
249251 if isinstance(src, ConstantRef):
250252 self.emitter.emit(ADC(Immediate(Byte(src.value))))
251253 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))))
253256 else:
254257 self.emitter.emit(ADC(Absolute(self.get_label(src.name))))
255258 elif isinstance(dest, LocationRef) and src.type == TYPE_BYTE and dest.type == TYPE_BYTE:
315318 if isinstance(src, ConstantRef):
316319 self.emitter.emit(SBC(Immediate(Byte(src.value))))
317320 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))))
319323 else:
320324 self.emitter.emit(SBC(Absolute(self.get_label(src.name))))
321325 elif isinstance(dest, LocationRef) and src.type == TYPE_BYTE and dest.type == TYPE_BYTE:
366370 if isinstance(src, ConstantRef):
367371 self.emitter.emit(cls(Immediate(Byte(src.value))))
368372 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))))
370375 else:
371376 self.emitter.emit(cls(self.absolute_or_zero_page(self.get_label(src.name))))
372377 else:
383388 if dest == REG_A:
384389 self.emitter.emit(cls())
385390 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))))
387393 else:
388394 self.emitter.emit(cls(self.absolute_or_zero_page(self.get_label(dest.name))))
389395 elif opcode == 'call':
454460 self.emitter.emit(cls(Immediate(Byte(src.value))))
455461 elif isinstance(src, IndexedRef):
456462 # 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))))
458465 else:
459466 self.emitter.emit(cls(Absolute(self.get_label(src.name))))
460467
465472 elif dest == REG_Y:
466473 self.emitter.emit(INY())
467474 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))))
469477 else:
470478 self.emitter.emit(INC(Absolute(self.get_label(dest.name))))
471479
476484 elif dest == REG_Y:
477485 self.emitter.emit(DEY())
478486 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))))
480489 else:
481490 self.emitter.emit(DEC(Absolute(self.get_label(dest.name))))
482491
504513 dest_label = self.get_label(dest.ref.name)
505514 self.emitter.emit(LDA(IndirectY(src_label)))
506515 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))))
515516 elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef) and src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
516517 ### copy w, wtab + y
517518 src_label = self.get_label(src.name)
518519 dest_label = self.get_label(dest.ref.name)
520 mode = self.addressing_mode_for_index(dest.index)
519521 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))))
521523 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))))
523525 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):
524526 ### copy vec, vtab + y
525527 # FIXME this is the exact same as above - can this be simplified?
526528 src_label = self.get_label(src.name)
527529 dest_label = self.get_label(dest.ref.name)
530 mode = self.addressing_mode_for_index(dest.index)
528531 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))))
530533 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))))
532535 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):
533536 ### copy routine, vtab + y
534537 src_label = self.get_label(src.name)
535538 dest_label = self.get_label(dest.ref.name)
539 mode = self.addressing_mode_for_index(dest.index)
536540 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))))
538542 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))))
540544 elif isinstance(src, ConstantRef) and isinstance(dest, IndexedRef) and src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
541545 ### copy 9999, wtab + y
542546 dest_label = self.get_label(dest.ref.name)
547 mode = self.addressing_mode_for_index(dest.index)
543548 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))))
545550 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))))
547552 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef) and TableType.is_a_table_type(src.ref.type, TYPE_WORD) and dest.type == TYPE_WORD:
548553 ### copy wtab + y, w
549554 src_label = self.get_label(src.ref.name)
550555 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))))
552558 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))))
554560 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
555561 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):
556562 ### copy vtab + y, vec
557563 # FIXME this is the exact same as above - can this be simplified?
558564 src_label = self.get_label(src.ref.name)
559565 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))))
561568 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))))
563570 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
564571 elif src.type == TYPE_BYTE and dest.type == TYPE_BYTE and not isinstance(src, ConstantRef):
565572 ### copy b1, b2
699706 src_label = self.get_label(location.name)
700707 self.emitter.emit(PLA())
701708 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)
88 def __repr__(self):
99 return 'Type(%r)' % self.name
1010
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
1913
2014
2115 TYPE_BIT = Type('bit', max_range=(0, 1))
2317 TYPE_WORD = Type('word', max_range=(0, 65535))
2418
2519
26
2720 class RoutineType(Type):
2821 """This memory location contains the code for a routine."""
2922 def __init__(self, inputs, outputs, trashes):
30 self.name = 'routine'
3123 self.inputs = inputs
3224 self.outputs = outputs
3325 self.trashes = trashes
3426
3527 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
3830 )
3931
4032 def __eq__(self, other):
4133 return isinstance(other, RoutineType) and (
42 other.name == self.name and
4334 other.inputs == self.inputs and
4435 other.outputs == self.outputs and
4536 other.trashes == self.trashes
4637 )
47
48 def __hash__(self):
49 return hash(self.name) ^ hash(self.inputs) ^ hash(self.outputs) ^ hash(self.trashes)
5038
5139 @classmethod
5240 def executable_types_compatible(cls_, src, dest):
6957 class VectorType(Type):
7058 """This memory location contains the address of some other type (currently, only RoutineType)."""
7159 def __init__(self, of_type):
72 self.name = 'vector'
7360 self.of_type = of_type
7461
7562 def __repr__(self):
7865 )
7966
8067 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
8569
8670
8771 class TableType(Type):
8872 def __init__(self, of_type, size):
8973 self.of_type = of_type
9074 self.size = size
91 self.name = '{} table[{}]'.format(self.of_type.name, self.size)
9275
9376 def __repr__(self):
9477 return '%s(%r, %r)' % (
9578 self.__class__.__name__, self.of_type, self.size
9679 )
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)
9787
9888 @classmethod
9989 def is_a_table_type(cls_, x, of_type):
10090 return isinstance(x, TableType) and x.of_type == of_type
10191
10292
103 class BufferType(Type):
104 def __init__(self, size):
105 self.size = size
106 self.name = 'buffer[%s]' % self.size
107
108
10993 class PointerType(Type):
11094 def __init__(self):
11195 self.name = 'pointer'
96
97 def __eq__(self, other):
98 return other.__class__ == self.__class__
11299
113100
114101 class Ref(object):
138125 return equal
139126
140127 def __hash__(self):
141 return hash(self.name + str(self.type))
128 return hash(self.name + repr(self.type))
142129
143130 def __repr__(self):
144131 return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.name)
182169
183170
184171 class IndexedRef(Ref):
185 def __init__(self, ref, index):
172 def __init__(self, ref, offset, index):
186173 self.ref = ref
174 self.offset = offset
187175 self.index = index
188176
189177 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)
197185
198186 @property
199187 def name(self):
200 return '{}+{}'.format(self.ref.name, self.index.name)
188 return '{}+{}+{}'.format(self.ref.name, self.offset, self.index.name)
201189
202190 def is_constant(self):
203191 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()
254192
255193
256194 class ConstantRef(Ref):
295233 value -= 256
296234 return ConstantRef(self.type, value)
297235
236 @property
237 def name(self):
238 return 'constant({})'.format(self.value)
239
298240
299241 REG_A = LocationRef(TYPE_BYTE, 'a')
300242 REG_X = LocationRef(TYPE_BYTE, 'x')
00 # encoding: UTF-8
11
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 )
35 from sixtypical.model import (
46 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,
79 )
810 from sixtypical.scanner import Scanner
911
8082
8183 def backpatch_constraint_labels(type_):
8284 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)
8688 if isinstance(type_, TableType):
8789 backpatch_constraint_labels(type_.of_type)
8890 elif isinstance(type_, VectorType):
121123 self.typedef()
122124 if self.scanner.on('const'):
123125 self.defn_const()
124 typenames = ['byte', 'word', 'table', 'vector', 'buffer', 'pointer'] # 'routine',
126 typenames = ['byte', 'word', 'table', 'vector', 'pointer'] # 'routine',
125127 typenames.extend(self.context.typedefs.keys())
126128 while self.scanner.on(*typenames):
127129 defn = self.defn()
221223
222224 if self.scanner.consume('table'):
223225 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")
226228 type_ = TableType(type_, size)
227229
228230 return type_
247249 elif self.scanner.consume('routine'):
248250 (inputs, outputs, trashes) = self.constraints()
249251 type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
250 elif self.scanner.consume('buffer'):
251 size = self.defn_size()
252 type_ = BufferType(size)
253252 elif self.scanner.consume('pointer'):
254253 type_ = PointerType()
255254 else:
350349 self.scanner.expect('+')
351350 self.scanner.expect('y')
352351 return IndirectRef(loc)
353 elif self.scanner.consume('^'):
354 loc = self.locexpr()
355 return AddressRef(loc)
356352 else:
357353 return self.indexed_locexpr()
358354
360356 loc = self.locexpr()
361357 if not isinstance(loc, str):
362358 index = None
359 offset = ConstantRef(TYPE_BYTE, 0)
363360 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('+')
364364 index = self.locexpr()
365 loc = IndexedRef(loc, index)
365 loc = IndexedRef(loc, offset, index)
366366 return loc
367367
368368 def statics(self):
473473 locations = self.locexprs()
474474 block = self.block()
475475 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)
476482 elif self.scanner.consume("trash"):
477483 dest = self.locexpr()
478484 return SingleOp(self.scanner.line_number, opcode='trash', src=None, dest=dest)
501501 | ld a, many + x
502502 | }
503503 ? 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
504550
505551 There are other operations you can do on tables. (1/3)
506552
613659 | }
614660 = ok
615661
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
616679 #### tables: range checking ####
617680
618681 It is a static analysis error if it cannot be proven that a read or write
619682 to a table falls within the defined size of that table.
620683
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.
623691
624692 A SixtyPical implementation must be able to prove that the index is inside
625693 the range of the table in various ways. The simplest is to show that a
663731 | }
664732 ? RangeExceededError
665733
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
666761 This applies to `copy` as well.
667762
668763 | word one: 77
702797 | {
703798 | ld x, 32
704799 | 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
705828 | }
706829 ? RangeExceededError
707830
725848 | }
726849 = ok
727850
728 Test for "clipping", but not enough.
851 Tests for "clipping", but not enough.
729852
730853 | word one: 77
731854 | word table[32] many
739862 | ld x, a
740863 | copy one, many + x
741864 | 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
742880 | }
743881 ? RangeExceededError
744882
27782916 | }
27792917 ? TypeMismatchError
27802918
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
28012941 | pointer ptr
28022942 |
28032943 | define main routine
2804 | inputs buf
2805 | outputs y, buf
2944 | inputs tab, ptr
2945 | outputs y, tab
28062946 | trashes a, z, n, ptr
28072947 | {
28082948 | ld y, 0
2809 | copy ^buf, ptr
28102949 | copy 123, [ptr] + y
28112950 | }
2812 = ok
2813
2814 It does use `y`.
2815
2816 | buffer[2048] buf
2951 ? ForbiddenWriteError
2952
2953 | byte table[256] tab
28172954 | pointer ptr
28182955 |
28192956 | define main routine
2820 | inputs buf
2821 | outputs buf
2957 | inputs tab
2958 | outputs y, tab
28222959 | trashes a, z, n, ptr
28232960 | {
2824 | copy ^buf, ptr
2961 | ld y, 0
2962 | point ptr into tab {
2963 | copy 123, [ptr] + y
2964 | }
28252965 | copy 123, [ptr] + y
28262966 | }
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 | }
28273000 ? UnmeaningfulReadError
28283001
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
28323005 | pointer ptr
28333006 | byte foo
28343007 |
28353008 | define main routine
2836 | inputs foo, buf
2837 | outputs y, buf
3009 | inputs foo, tab
3010 | outputs y, tab
28383011 | trashes a, z, n, ptr
28393012 | {
28403013 | 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
28493023 | pointer ptr
28503024 | byte foo
28513025 |
28523026 | define main routine
2853 | inputs buf
3027 | inputs tab
28543028 | outputs foo
28553029 | trashes a, y, z, n, ptr
28563030 | {
28573031 | ld y, 0
2858 | copy ^buf, ptr
3032 | point ptr into tab {
28593033 | 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
28663041 | pointer ptra
28673042 | pointer ptrb
28683043 |
28693044 | define main routine
2870 | inputs buf
2871 | outputs buf
3045 | inputs tab
3046 | outputs tab
28723047 | trashes a, y, z, n, ptra, ptrb
28733048 | {
28743049 | 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`,
28823059 not `copy`.
28833060
2884 | buffer[2048] buf
3061 | byte table[256] tab
28853062 | pointer ptr
28863063 | byte foo
28873064 |
28883065 | define main routine
2889 | inputs buf
3066 | inputs tab
28903067 | outputs a
28913068 | trashes y, z, n, ptr
28923069 | {
28933070 | 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`,
29003078 not `copy`.
29013079
2902 | buffer[2048] buf
3080 | byte table[256] tab
29033081 | pointer ptr
29043082 | byte foo
29053083 |
29063084 | define main routine
2907 | inputs buf
2908 | outputs buf
3085 | inputs tab
3086 | outputs tab
29093087 | trashes a, y, z, n, ptr
29103088 | {
29113089 | 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 | }
29153251 | }
29163252 = ok
29173253
384384 = $081B DEC $081F,X
385385 = $081E RTS
386386
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
387449 Compiling 16-bit `cmp`.
388450
389451 | word za @ 60001
874936 = $0817 RTS
875937 = $0818 INX
876938 = $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
877975
878976 Copy word to word table and back, with both `x` and `y` as indexes.
879977
9171015 = $0848 STA $084D
9181016 = $084B RTS
9191017
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
9201060 Indirect call.
9211061
9221062 | vector routine
10161156 = $082F LDA $0848,X
10171157 = $0832 STA $0846
10181158 = $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
10191210 = $0838 STA $0847
10201211 = $083B JSR $0842
10211212 = $083E RTS
12061397 = $081D STA $0822
12071398 = $0820 RTS
12081399
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
12141405 | pointer ptr @ 254
12151406 |
12161407 | define main routine
1217 | inputs buf
1218 | outputs buf, y
1408 | inputs tab
1409 | outputs tab, y
12191410 | trashes a, z, n, ptr
12201411 | {
12211412 | ld y, 0
1222 | copy ^buf, ptr
1413 | point ptr into tab {
1414 | }
12231415 | }
12241416 = $080D LDY #$00
12251417 = $080F LDA #$18
12301422
12311423 Write literal through a pointer.
12321424
1233 | buffer[2048] buf
1425 | byte table[256] tab
12341426 | pointer ptr @ 254
12351427 |
12361428 | define main routine
1237 | inputs buf
1238 | outputs buf, y
1429 | inputs tab
1430 | outputs tab, y
12391431 | trashes a, z, n, ptr
12401432 | {
12411433 | ld y, 0
1242 | copy ^buf, ptr
1243 | copy 123, [ptr] + y
1434 | point ptr into tab {
1435 | copy 123, [ptr] + y
1436 | }
12441437 | }
12451438 = $080D LDY #$00
12461439 = $080F LDA #$1C
12531446
12541447 Write stored value through a pointer.
12551448
1256 | buffer[2048] buf
1449 | byte table[256] tab
12571450 | pointer ptr @ 254
12581451 | byte foo
12591452 |
12601453 | define main routine
1261 | inputs foo, buf
1262 | outputs y, buf
1454 | inputs foo, tab
1455 | outputs y, tab
12631456 | trashes a, z, n, ptr
12641457 | {
12651458 | ld y, 0
1266 | copy ^buf, ptr
1267 | copy foo, [ptr] + y
1459 | point ptr into tab {
1460 | copy foo, [ptr] + y
1461 | }
12681462 | }
12691463 = $080D LDY #$00
12701464 = $080F LDA #$1D
12711465 = $0811 STA $FE
12721466 = $0813 LDA #$08
12731467 = $0815 STA $FF
1274 = $0817 LDA $101D
1468 = $0817 LDA $091D
12751469 = $081A STA ($FE),Y
12761470 = $081C RTS
12771471
12781472 Read through a pointer, into a byte storage location, or the `a` register.
12791473
1280 | buffer[2048] buf
1474 | byte table[256] tab
12811475 | pointer ptr @ 254
12821476 | byte foo
12831477 |
12841478 | define main routine
1285 | inputs buf
1479 | inputs tab
12861480 | outputs y, foo
12871481 | trashes a, z, n, ptr
12881482 | {
12891483 | 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 | }
12931488 | }
12941489 = $080D LDY #$00
12951490 = $080F LDA #$1F
12971492 = $0813 LDA #$08
12981493 = $0815 STA $FF
12991494 = $0817 LDA ($FE),Y
1300 = $0819 STA $101F
1495 = $0819 STA $091F
13011496 = $081C LDA ($FE),Y
13021497 = $081E RTS
13031498
13041499 Read and write through two pointers.
13051500
1306 | buffer[2048] buf
1501 | byte table[256] tab
13071502 | pointer ptra @ 252
13081503 | pointer ptrb @ 254
13091504 |
13101505 | define main routine
1311 | inputs buf
1312 | outputs buf
1506 | inputs tab
1507 | outputs tab
13131508 | trashes a, y, z, n, ptra, ptrb
13141509 | {
13151510 | 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 | }
13191516 | }
13201517 = $080D LDY #$00
13211518 = $080F LDA #$24
13321529
13331530 Write the `a` register through a pointer.
13341531
1335 | buffer[2048] buf
1532 | byte table[256] tab
13361533 | pointer ptr @ 254
13371534 | byte foo
13381535 |
13391536 | define main routine
1340 | inputs buf
1341 | outputs buf
1537 | inputs tab
1538 | outputs tab
13421539 | trashes a, y, z, n, ptr
13431540 | {
13441541 | 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 | }
13481546 | }
13491547 = $080D LDY #$00
13501548 = $080F LDA #$1C
13581556 Add a word memory location, and a literal word, to a pointer, and then read through it.
13591557 Note that this is *not* range-checked. (Yet.)
13601558
1361 | buffer[2048] buf
1559 | byte table[256] tab
13621560 | pointer ptr @ 254
13631561 | byte foo
13641562 | word delta
13651563 |
13661564 | define main routine
1367 | inputs buf
1565 | inputs tab
13681566 | outputs y, foo, delta
13691567 | trashes a, c, v, z, n, ptr
13701568 | {
13711569 | copy 619, delta
13721570 | ld y, 0
13731571 | 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 | }
13781577 | }
13791578 = $080D LDA #$6B
1380 = $080F STA $1043
1579 = $080F STA $0943
13811580 = $0812 LDA #$02
1382 = $0814 STA $1044
1581 = $0814 STA $0944
13831582 = $0817 LDY #$00
13841583 = $0819 CLC
13851584 = $081A LDA #$42
13871586 = $081E LDA #$08
13881587 = $0820 STA $FF
13891588 = $0822 LDA $FE
1390 = $0824 ADC $1043
1589 = $0824 ADC $0943
13911590 = $0827 STA $FE
13921591 = $0829 LDA $FF
1393 = $082B ADC $1044
1592 = $082B ADC $0944
13941593 = $082E STA $FF
13951594 = $0830 LDA $FE
13961595 = $0832 ADC #$01
13991598 = $0838 ADC #$00
14001599 = $083A STA $FF
14011600 = $083C LDA ($FE),Y
1402 = $083E STA $1042
1601 = $083E STA $0942
14031602 = $0841 RTS
14041603
14051604 ### Trash
162162
163163 Other blocks.
164164
165 | byte table[256] tab
166 | pointer ptr
167 |
165168 | define main routine trashes a, x, c, z, v {
166169 | with interrupts off {
167170 | save a, x, c {
171174 | save a, x, c {
172175 | ld a, 0
173176 | }
177 | point ptr into tab {
178 | ld a, [ptr] + y
179 | }
174180 | }
175181 = ok
176182
179185 | byte byt
180186 | word wor
181187 | vector routine trashes a vec
182 | buffer[2048] buf
188 | byte table[2048] buf
183189 | pointer ptr
184190 |
185191 | define main routine {
191197 | byte table[256] many
192198 | word table[256] wmany
193199 | vector (routine trashes a) table[256] vmany
200 | byte bval
201 | word wval
194202 |
195203 | define main routine {
196204 | ld x, 0
206214 | shr many + x
207215 | inc many + x
208216 | 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
209249 | }
210250 = ok
211251
212252 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.)
214259
215260 | word table[512] many
216261 |
221266 | {
222267 | ld x, 0
223268 | 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
224293 | }
225294 ? SyntaxError
226295
281350 |
282351 | define main routine {
283352 | 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
284366 | }
285367 = ok
286368
589671 | }
590672 ? Expected '}', but found 'ld'
591673
592 Buffers and pointers.
593
594 | buffer[2048] buf
674 Tables and pointers.
675
676 | byte table[2048] buf
595677 | pointer ptr
596678 | pointer ptrb
597679 | byte foo
598680 |
599681 | 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 | }
604687 | }
605688 = ok
606689