git @ Cat's Eye Technologies SixtyPical / 0.8
Merge pull request #2 from catseye/develop-0.8 Develop 0.8 Chris Pressey authored 2 years ago GitHub committed 2 years ago
18 changed file(s) with 833 addition(s) and 172 deletion(s). Raw diff Collapse all Expand all
00 History of SixtyPical
11 =====================
2
3 0.8
4 ---
5
6 * Explicit word literals prefixed with `word` token.
7 * Can `copy` literals into user-defined destinations.
8 * Fixed bug where loop variable wasn't being checked at end of `repeat` loop.
9 * `buffer` and `pointer` types.
10 * `copy ^` syntax to load the addr of a buffer into a pointer.
11 * `copy []+y` syntax to read and write values to and from memory through a pointer.
212
313 0.7
414 ---
2323
2424 It is a **work in progress**, currently at the **proof-of-concept** stage.
2525
26 The current released version of SixtyPical is 0.7.
26 The current development version of SixtyPical is 0.8.
2727
2828 Documentation
2929 -------------
3030
31 * Design Goals — coming soon.
31 * [Design Goals](doc/Design%20Goals.md)
3232 * [SixtyPical specification](doc/SixtyPical.md)
33 * [SixtyPical history](HISTORY.md)
33 * [SixtyPical revision history](HISTORY.md)
3434 * [Literate test suite for SixtyPical syntax](tests/SixtyPical%20Syntax.md)
3535 * [Literate test suite for SixtyPical execution](tests/SixtyPical%20Execution.md)
3636 * [Literate test suite for SixtyPical analysis](tests/SixtyPical%20Analysis.md)
4040 TODO
4141 ----
4242
43 * `word table` type.
44 * `vector table` type.
45 * zero-page memory locations.
46 * indirect addressing.
47 * `low` and `high` address operators (turn `word` type into `byte`.) Possibly.
48 * save registers on stack or in memory (this preserves them = not trashed)
43 ### Add 16 bit values.
4944
50 At some point...
45 I guess this means making `add` a bit more like `copy`.
5146
47 And then: add to pointer. (Not necessarily range-checked yet though.)
48
49 And then write a little demo "game" where you can move a block around the screen with
50 the joystick.
51
52 ### `word table` and `vector table` types
53
54 ### `low` and `high` address operators
55
56 To turn `word` type into `byte`.
57
58 ### save registers on stack
59
60 This preserves them, so semantically, they can be used even though they
61 are trashed inside the block.
62
63 ### And at some point...
64
65 * `copy x, [ptr] + y`
66 * Maybe even `copy [ptra] + y, [ptrb] + y`, which can be compiled to indirect LDA then indirect STA!
67 * Check that the buffer being read or written to through pointer, appears in approporiate inputs or outputs set.
5268 * initialized `byte table` memory locations
5369 * always analyze before executing or compiling, unless told not to
5470 * `trash` instruction.
0 Design Goals for SixtyPical
1 ===========================
2
3 (draft)
4
5 The intent of SixtyPical is to have a very low-level language that
6 benefits from abstract interpretation.
7
8 "Very low-level" means, on a comparable level of abstraction as
9 assembly language.
10
11 In the original vision for SixtyPical, SixtyPical instructions mapped
12 nearly 1:1 to 6502 instructions. However, many times when programming
13 in 6502 you're using idioms (e.g. adding a 16-bit constant to a 16-bit
14 value stored in 2 bytes) and it's just massively easier to analyze such
15 actions when they are represented by a single instruction.
16
17 So SixtyPical instructions are similar to, inspired by, and have
18 analogous restrictions as 6502 instructions, but in many ways, they
19 are more abstract. For example, `copy`.
20
21 The intent is that programming in SixtyPical is a lot like programming
22 in 6052 assembler, but it's harder to make a stupid error that you have
23 to spend a lot of time debugging.
24
25 The intent is not to make it absolutely impossible to make such errors,
26 just harder.
27
28 ### Some Background ###
29
30 The ideas in SixtyPical came from a couple of places.
31
32 One major impetus was when I was working on [Shelta][], trying to cram
33 all that code for that compiler into 512 bytes. This involved looking
34 at the x86 registers and thinking hard about which ones were preserved
35 when (and which ones weren't) and making the best use of that. And
36 while doing that, one thing that came to mind was: I Bet The Assembler
37 Could Track This.
38
39 Another influence was around 2007 when "Typed Assembly Language" (and
40 "Proof Carrying Code") were all the rage. I haven't heard about them
41 in a while, so I guess they turned out to be research fads? But for a
42 while there, it was all Necula, Necula, Necula. Anyway, I remember at
43 the time looking into TAL and expecting to find something that matched
44 the impression I had pre-formulated about what a "Typed Assembly"
45 might be like. And finding that it didn't match my vision very well.
46
47 I don't actually remember what TAL seemed like to me at the time, but
48 what I had in mind was more like SixtyPical.
49
50 (I'll also write something about abstract interpretation here at some
51 point, hopefully.)
00 SixtyPical
11 ==========
22
3 This document describes the SixtyPical programming language version 0.7,
3 This document describes the SixtyPical programming language version 0.8,
44 both its execution aspect and its static analysis aspect (even though
55 these are, technically speaking, separate concepts.)
66
1313 Types
1414 -----
1515
16 There are five TYPES in SixtyPical:
16 There are six *primitive types* in SixtyPical:
1717
1818 * bit (2 possible values)
1919 * byte (256 possible values)
20 * byte table (256 entries, each holding a byte)
20 * word (65536 possible values)
2121 * routine (code stored somewhere in memory, read-only)
2222 * vector (address of a routine)
23 * pointer (address of a byte in a buffer)
24
25 There are also two *type constructors*:
26
27 * X table (256 entries, each holding a value of type X, where X is `byte`)
28 * buffer[N] (N entries; each entry is a byte; N is a power of 2, ≤ 64K)
2329
2430 Memory locations
2531 ----------------
2632
27 A primary concept in SixtyPical is the MEMORY LOCATION. At any given point
28 in time during execution, each memory location is either UNINITIALIZED or
29 INITIALIZED. At any given point in the program text, too, each memory
33 A primary concept in SixtyPical is the *memory location*. At any given point
34 in time during execution, each memory location is either *uninitialized* or
35 *initialized*. At any given point in the program text, too, each memory
3036 location is either uninitialized or initialized. Where-ever it is one or
3137 the other during execution, it is the same in the corresponding place in
3238 the program text; thus, it is a static property.
6369 off
6470 on
6571
66 and two-hundred and fifty-six byte constants,
72 two hundred and fifty-six byte constants,
6773
6874 0
6975 1
7076 ...
7177 255
7278
79 and sixty-five thousand five hundred and thirty-six word constants,
80
81 word 0
82 word 1
83 ...
84 word 65535
85
86 Note that if a word constant is between 256 and 65535, the leading `word`
87 token can be omitted.
88
7389 ### User-defined ###
7490
7591 There may be any number of user-defined memory locations. They are defined
76 by giving the type, which must be `byte`, `byte table`, or `vector`, and the
92 by giving the type (which may be any type except `bit` and `routine`) and the
7793 name.
7894
7995 byte pos
87103
88104 byte pos : 0
89105
90 A user-defined vector memory location is decorated with READS and WRITES lists
91 like a routine (see below), and it may only hold addresses of routines which
92 are compatible. (Meaning, the routine's inputs (resp. outputs, trashes)
93 must be a subset of the vector's inputs (resp. outputs, trashes.))
106 A user-defined vector memory location is decorated with `inputs`, `outputs`
107 and `trashes` lists like a routine (see below), and it may only hold addresses
108 of routines which are compatible. (Meaning, the routine's inputs (resp. outputs,
109 trashes) must be a subset of the vector's inputs (resp. outputs, trashes.))
94110
95111 vector actor_logic
96112 inputs a, score
98114 trashes y
99115 @ $c000
100116
117 Note that in the code of a routine, if a memory location is named by a
118 user-defined symbol, it is an address in memory, and can be read and written.
119 But if it is named by a literal integer, either decimal or hexadecimal, it
120 is a constant and can only be read (and when read always yields that constant
121 value. So, for instance, to read the value at `screen` above, in the code,
122 you would need to reference the symbol `screen`; attempting to read 1024
123 would not work.
124
125 This is actually useful, at least at this point, as you can rely on the fact
126 that literal integers in the code are always immediate values. (But this
127 may change at some point.)
128
129 ### Buffers and Pointers ###
130
131 Roughly speaking, a `buffer` is a table that can be longer than 256 bytes,
132 and a `pointer` is an address within a buffer.
133
134 A `pointer` is implemented as a zero-page memory location, and accessing the
135 buffer pointed to is implemented with "indirect indexed" addressing, as in
136
137 LDA ($02), Y
138 STA ($02), Y
139
140 There are extended modes of `copy` for using these types of memory location.
141 See `copy` below, but here is some illustrative example code:
142
143 copy ^buf, ptr // this is the only way to initialize a pointer
144 add ptr, 4 // ok, but only if it does not exceed buffer's size
145 ld y, 0 // you must set this to something yourself
146 copy [ptr] + y, byt // read memory through pointer, into byte
147 copy 100, [ptr] + y // write memory through pointer (still trashes a)
148
149 where `ptr` is a user-defined storage location of `pointer` type, and the
150 `+ y` part is mandatory.
151
101152 Routines
102153 --------
103154
104 Every routine must list all the memory locations it READS from, i.e. its
105 INPUTS, and all the memory locations it WRITES to, whether they are OUTPUTS
106 or merely TRASHED. Every memory location that is not written to by the
107 routine (or any routines that the routine calls) is PRESERVED by the routine.
155 Every routine must list all the memory locations it *reads from*, which we
156 call its `inputs`, and all the memory locations it *writes to*. The latter
157 we divide into two groups: its `outputs` which it intentionally initializes,
158 and its `trashes`, which it does not care about, and leaves uninitialized.
159 For example, if it uses a register to temporarily store an intermediate
160 value used in a multiplication, that register has no meaning outside of
161 the multiplication, and is one of the routine's `trashes`.
162
163 It is common to say that the `trashes` are the memory locations that are
164 *not preserved* by the routine.
108165
109166 routine foo
110167 inputs a, score
113170 ...
114171 }
115172
173 The union of the `outputs` and `trashes` is sometimes collectively called
174 "the WRITES" of the routine, for historical reasons and as shorthand.
175
116176 Routines may call only routines previously defined in the program source.
117177 Thus, directly recursive routines are not allowed. (However, routines may
118178 also call routines via vectors, which are dynamically assigned. In this
121181 For a SixtyPical program to be run, there must be one routine called `main`.
122182 This routine is executed when the program is run.
123183
124 The memory locations given given as inputs are considered to be initialized
184 The memory locations given as inputs to a routine are considered to be initialized
125185 at the beginning of the routine. Various instructions cause memory locations
126186 to be initialized after they are executed. Calling a routine which trashes
127187 some memory locations causes those memory locations to be uninitialized after
128188 that routine is called. At the end of a routine, all memory locations listed
129 as outputs must be initialised.
130
131 A routine can also be declared as "external", in which case its body need
132 not be defined but an absolute address must be given for where the routine
133 is located in memory.
189 as outputs must be initialized.
190
191 A literal word can given instead of the body of the routine. This word is the
192 absolute address of an "external" routine located in memory but not defined by
193 the SixtyPical program.
134194
135195 routine chrout
136196 inputs a
140200 Instructions
141201 ------------
142202
203 Instructions are inspired by, and in many cases closely resemble, the 6502
204 instruction set. However, in many cases they do not map 1:1 to 6502 instructions.
205 If a SixtyPical instruction cannot be translated validly to one more more 6502
206 instructions while retaining all the stated constraints, that's a static error
207 in a SixtyPical program, and technically any implementation of SixtyPical, even
208 an interpreter, should flag it up.
209
143210 ### ld ###
144211
145212 ld <dest-memory-location>, <src-memory-location> [+ <index-memory-location>]
147214 Reads from src and writes to dest.
148215
149216 * It is illegal if dest is not a register.
150 * It is illegal if dest does not occur in the WRITES lists of the current
151 routine.
217 * It is illegal if dest does not occur in the WRITES of the current routine.
152218 * It is illegal if src is not of same type as dest (i.e., is not a byte.)
153219 * It is illegal if src is uninitialized.
154220
155221 After execution, dest is considered initialized. The flags `z` and `n` may be
156 changed by this instruction; they must be named in the WRITES lists, and they
222 changed by this instruction; they must be named in the WRITES, and they
157223 are considered initialized after it has executed.
158224
159225 If and only if src is a byte table, the index-memory-location must be given.
168234 Reads from src and writes to dest.
169235
170236 * It is illegal if dest is a register or if dest is read-only.
171 * It is illegal if dest does not occur in the WRITES lists of the current
172 routine.
237 * It is illegal if dest does not occur in the WRITES of the current routine.
173238 * It is illegal if src is not of same type as dest.
174239 * It is illegal if src is uninitialized.
175240
178243
179244 If and only if dest is a byte table, the index-memory-location must be given.
180245
246 ### copy ###
247
248 copy <src-memory-location>, <dest-memory-location>
249
250 Reads from src and writes to dest. Differs from `st` in that is able to
251 copy more general types of data (for example, vectors,) and it trashes the
252 `z` and `n` flags and the `a` register.
253
254 * It is illegal if dest is read-only.
255 * It is illegal if dest does not occur in the WRITES of the current routine.
256 * It is illegal if src is not of same type as dest.
257 * It is illegal if src is uninitialized.
258
259 After execution, dest is considered initialized, and `z` and `n`, and
260 `a` are considered uninitialized.
261
262 There are two extra modes that this instruction can be used in. The first is
263 to load an address into a pointer:
264
265 copy ^<src-memory-location>, <dest-memory-location>
266
267 This copies the address of src into dest. In this case, src must be
268 of type buffer, and dest must be of type pointer. src will not be
269 considered a memory location that is read, since it is only its address
270 that is being retrieved.
271
272 The second is to read or write indirectly through a pointer.
273
274 copy [<src-memory-location>] + y, <dest-memory-location>
275 copy <src-memory-location>, [<dest-memory-location>] + y
276
277 In both of these, the memory location in the `[]+y` syntax must be
278 a pointer.
279
280 The first copies the contents of memory at the pointer (offset by the `y`
281 register) into a byte memory location.
282
283 The second copies a literal byte, or a byte memory location, into
284 the contents of memory at the pointer (offset by the `y` register).
285
286 In addition to the constraints above, `y` must be initialized before
287 this mode is used.
288
181289 ### add dest, src ###
182290
183291 add <dest-memory-location>, <src-memory-location>
186294
187295 * It is illegal if src OR dest OR c is uninitialized.
188296 * It is illegal if dest is read-only.
189 * It is illegal if dest does not occur in the WRITES lists
190 of the current routine.
191
192 Affects n, z, c, and v flags, requiring that they be in the WRITES lists,
297 * It is illegal if dest does not occur in the WRITES of the current routine.
298
299 Affects n, z, c, and v flags, requiring that they be in the WRITES,
193300 and initializing them afterwards.
194301
195302 dest and src continue to be initialized afterwards.
202309
203310 * It is illegal if dest is uninitialized.
204311 * It is illegal if dest is read-only.
205 * It is illegal if dest does not occur in the WRITES lists
206 of the current routine.
207
208 Affects n and z flags, requiring that they be in the WRITES lists,
312 * It is illegal if dest does not occur in the WRITES of the current routine.
313
314 Affects n and z flags, requiring that they be in the WRITES,
209315 and initializing them afterwards.
210316
211317 ### sub ###
216322
217323 * It is illegal if src OR dest OR c is uninitialized.
218324 * It is illegal if dest is read-only.
219 * It is illegal if dest does not occur in the WRITES lists
220 of the current routine.
221
222 Affects n, z, c, and v flags, requiring that they be in the WRITES lists,
325 * It is illegal if dest does not occur in the WRITES of the current routine.
326
327 Affects n, z, c, and v flags, requiring that they be in the WRITES,
223328 and initializing them afterwards.
224329
225330 dest and src continue to be initialized afterwards.
232337
233338 * It is illegal if dest is uninitialized.
234339 * It is illegal if dest is read-only.
235 * It is illegal if dest does not occur in the WRITES lists
236 of the current routine.
237
238 Affects n and z flags, requiring that they be in the WRITES lists,
340 * It is illegal if dest does not occur in the WRITES of the current routine.
341
342 Affects n and z flags, requiring that they be in the WRITES,
239343 and initializing them afterwards.
240344
241345 ### cmp ###
247351
248352 * It is illegal if src OR dest is uninitialized.
249353
250 Affects n, z, and c flags, requiring that they be in the WRITES lists,
354 Affects n, z, and c flags, requiring that they be in the WRITES,
251355 and initializing them afterwards.
252356
253357 ### and, or, xor ###
261365
262366 * It is illegal if src OR dest OR is uninitialized.
263367 * It is illegal if dest is read-only.
264 * It is illegal if dest does not occur in the WRITES lists
265 of the current routine.
266
267 Affects n and z flags, requiring that they be in the WRITES lists of the
368 * It is illegal if dest does not occur in the WRITES of the current routine.
369
370 Affects n and z flags, requiring that they be in the WRITES of the
268371 current routine, and sets them as initialized afterwards.
269372
270373 dest and src continue to be initialized afterwards.
283386 * It is illegal if dest is a register besides `a`.
284387 * It is illegal if dest is read-only.
285388 * It is illegal if dest OR c is uninitialized.
286 * It is illegal if dest does not occur in the WRITES lists
287 of the current routine.
288
289 Affects the c flag, requiring that it be in the WRITES lists of the
389 * It is illegal if dest does not occur in the WRITES of the current routine.
390
391 Affects the c flag, requiring that it be in the WRITES of the
290392 current routine, and it continues to be initialized afterwards.
291393
292394 ### call ###
298400 which will be called indirectly. Execution will be transferred back to the
299401 current routine, when execution of the executable is finished.
300402
301 Just before the call,
302
303 * It is illegal if any of the memory locations in the target executable's
304 READS list is uninitialized.
403 * It is illegal if any of the memory locations listed in the called routine's
404 `inputs` are uninitialized immediately before the call.
305405
306406 Just after the call,
307407
308 * All memory locations listed as TRASHED in the called routine's WRITES
309 list are considered uninitialized.
310 * All memory locations listed as TRASHED in the called routine's OUTPUTS
311 list are considered initialized.
408 * All memory locations listed in the called routine's `trashes` are considered
409 to now be uninitialized.
410 * All memory locations listed in the called routine's `outputs` are considered
411 to now be initialized.
312412
313413 ### goto ###
314414
324424
325425 Just before the goto,
326426
327 * It is illegal if any of the memory locations in the target executable's
328 READS list is uninitialized.
427 * It is illegal if any of the memory locations in the target routine's
428 `inputs` list is uninitialized.
329429
330430 In addition,
331431
332 * The target executable's WRITES lists must not include any locations
333 that are not already included in the current routine's WRITES lists.
432 * The target executable's WRITES must not include any locations
433 that are not already included in the current routine's WRITES.
334434
335435 ### if ###
336436
349449 * It is illegal if any location initialized at the end of the true-branch
350450 is not initialized at the end of the false-branch, and vice versa.
351451
452 The sense of the test can be inverted with `not`.
453
352454 ### repeat ###
353455
354456 repeat {
371473 }
372474 } until z
373475
374 "until" is optional, but if omitted, must be replaced with "forever".
375
376 ### copy ###
377
378 copy <src-memory-location>, <dest-memory-location>
379
380 Reads from src and writes to dest. Differs from `st` in that is able to
381 copy more general types of data (for example, vectors,) and it trashes the
382 `z` and `n` flags and the `a` register.
383
384 * It is illegal if dest is read-only.
385 * It is illegal if dest does not occur in the WRITES lists of the current
386 routine.
387 * It is illegal if src is not of same type as dest.
388 * It is illegal if src is uninitialized.
389
390 After execution, dest is considered initialized, and `z` and `n`, and
391 `a` are considered uninitialized.
476 "until" is optional, but if omitted, must be replaced with "forever":
477
478 repeat {
479 cmp y, 25
480 if z {
481 }
482 } forever
483
484 The sense of the test can be inverted with `not`.
485
486 repeat {
487 cmp y, 25
488 if z {
489 }
490 } until not z
392491
393492 Grammar
394493 -------
0 buffer[2048] buf
1 pointer ptr @ 254
2 byte foo
3
4 routine main
5 inputs buf
6 outputs buf, y, foo
7 trashes a, z, n, ptr
8 {
9 ld y, 0
10 copy ^buf, ptr
11 copy 123, [ptr] + y
12 copy [ptr] + y, foo
13 copy foo, [ptr] + y
14 }
0 word screen @ 1024
1 byte joy2 @ $dc00
2
3 word delta
4
5 routine read_stick
6 inputs joy2
7 outputs delta
8 trashes a, x, z, n
9 {
10 ld x, joy2
11 ld a, x
12 and a, 1 // up
13 if z {
14 copy $ffd8, delta // -40
15 } else {
16 ld a, x
17 and a, 2 // down
18 if z {
19 copy word 40, delta
20 } else {
21 ld a, x
22 and a, 4 // left
23 if z {
24 copy $ffff, delta // -1
25 } else {
26 ld a, x
27 and a, 8 // right
28 if z {
29 copy word 1, delta
30 } else {
31 copy word 0, delta
32 }
33 }
34 }
35 }
36 }
37
38 routine main
39 inputs joy2
40 outputs delta
41 trashes a, x, z, n, screen
42 {
43 repeat {
44 call read_stick
45 copy delta, screen
46 ld a, 1
47 } until z
48 }
11
22 from sixtypical.ast import Program, Routine, Block, Instr
33 from sixtypical.model import (
4 TYPE_BYTE, TYPE_BYTE_TABLE,
5 VectorType, ExecutableType,
6 ConstantRef, LocationRef,
7 REG_A, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
4 TYPE_BYTE, TYPE_BYTE_TABLE, BufferType, PointerType, VectorType, ExecutableType,
5 ConstantRef, LocationRef, IndirectRef, AddressRef,
6 REG_A, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
87 )
98
109
113112 def assert_meaningful(self, *refs, **kwargs):
114113 exception_class = kwargs.get('exception_class', UnmeaningfulReadError)
115114 for ref in refs:
116 if isinstance(ref, ConstantRef) or ref in self.routines:
115 if ref.is_constant() or ref in self.routines:
117116 pass
118117 elif isinstance(ref, LocationRef):
119118 if ref not in self._meaningful:
120119 message = '%s in %s' % (ref.name, self.routine.name)
120 if kwargs.get('message'):
121 message += ' (%s)' % kwargs['message']
121122 raise exception_class(message)
122123 else:
123124 raise NotImplementedError(ref)
127128 for ref in refs:
128129 if ref not in self._writeable:
129130 message = '%s in %s' % (ref.name, self.routine.name)
131 if kwargs.get('message'):
132 message += ' (%s)' % kwargs['message']
130133 raise exception_class(message)
131134
132135 def set_touched(self, *refs):
269272 # probably not; if it wasn't meaningful in the first place, it
270273 # doesn't really matter if you modified it or not, coming out.
271274 for ref in context1.each_meaningful():
272 context2.assert_meaningful(ref, exception_class=InconsistentInitializationError)
275 context2.assert_meaningful(
276 ref, exception_class=InconsistentInitializationError, message='initialized in block 1 but not in block 2'
277 )
273278 for ref in context2.each_meaningful():
274 context1.assert_meaningful(ref, exception_class=InconsistentInitializationError)
279 context1.assert_meaningful(
280 ref, exception_class=InconsistentInitializationError, message='initialized in block 2 but not in block 1'
281 )
275282 context.set_from(context1)
276283 elif opcode == 'repeat':
277284 # it will always be executed at least once, so analyze it having
278285 # been executed the first time.
279286 self.analyze_block(instr.block, context)
280
287 context.assert_meaningful(src)
288
281289 # now analyze it having been executed a second time, with the context
282290 # of it having already been executed.
283291 self.analyze_block(instr.block, context)
284
285 # NB I *think* that's enough... but it might not be?
292 context.assert_meaningful(src)
293
286294 elif opcode == 'copy':
287 # check that their types are basically compatible
288 if src.type == dest.type:
289 pass
290 elif isinstance(src.type, ExecutableType) and \
291 isinstance(dest.type, VectorType):
292 pass
295 # 1. check that their types are compatible
296
297 if isinstance(src, AddressRef) and isinstance(dest, LocationRef):
298 if isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType):
299 pass
300 else:
301 raise TypeMismatchError((src, dest))
302 elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef):
303 if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType):
304 pass
305 else:
306 raise TypeMismatchError((src, dest))
307 elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef):
308 if isinstance(src.ref.type, PointerType) and dest.type == TYPE_BYTE:
309 pass
310 else:
311 raise TypeMismatchError((src, dest))
312 elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef):
313 if src.type == dest.type:
314 pass
315 elif isinstance(src.type, ExecutableType) and \
316 isinstance(dest.type, VectorType):
317 # if dealing with routines and vectors,
318 # check that they're not incompatible
319 if not (src.type.inputs <= dest.type.inputs):
320 raise IncompatibleConstraintsError(src.type.inputs - dest.type.inputs)
321 if not (src.type.outputs <= dest.type.outputs):
322 raise IncompatibleConstraintsError(src.type.outputs - dest.type.outputs)
323 if not (src.type.trashes <= dest.type.trashes):
324 raise IncompatibleConstraintsError(src.type.trashes - dest.type.trashes)
325 else:
326 raise TypeMismatchError((src, dest))
293327 else:
294328 raise TypeMismatchError((src, dest))
295
296 # if dealing with routines and vectors,
297 # check that they're not incompatible
298 if isinstance(src.type, ExecutableType) and \
299 isinstance(dest.type, VectorType):
300 if not (src.type.inputs <= dest.type.inputs):
301 raise IncompatibleConstraintsError(src.type.inputs - dest.type.inputs)
302 if not (src.type.outputs <= dest.type.outputs):
303 raise IncompatibleConstraintsError(src.type.outputs - dest.type.outputs)
304 if not (src.type.trashes <= dest.type.trashes):
305 raise IncompatibleConstraintsError(src.type.trashes - dest.type.trashes)
306
307 context.assert_meaningful(src)
308 context.set_written(dest)
329
330 # 2. check that the context is meaningful
331
332 if isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef):
333 context.assert_meaningful(src, REG_Y)
334 # TODO this will need to be more sophisticated. it's the thing ref points to that is written, not ref itself.
335 context.set_written(dest.ref)
336 elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef):
337 context.assert_meaningful(src.ref, REG_Y)
338 # TODO this will need to be more sophisticated. the thing ref points to is touched, as well.
339 context.set_touched(src.ref)
340 context.set_written(dest)
341 else:
342 context.assert_meaningful(src)
343 context.set_written(dest)
344
309345 context.set_touched(REG_A, FLAG_Z, FLAG_N)
310346 context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N)
347
311348 elif opcode == 'with-sei':
312349 self.analyze_block(instr.block, context)
313350 elif opcode == 'goto':
11
22 from sixtypical.ast import Program, Routine, Block, Instr
33 from sixtypical.model import (
4 ConstantRef,
5 TYPE_BIT, TYPE_BYTE, TYPE_WORD,
6 RoutineType, VectorType,
4 ConstantRef, LocationRef, IndirectRef, AddressRef,
5 TYPE_BIT, TYPE_BYTE, TYPE_WORD, BufferType, PointerType, RoutineType, VectorType,
76 REG_A, REG_X, REG_Y, FLAG_C
87 )
98 from sixtypical.emitter import Byte, Label, Offset, LowAddressByte, HighAddressByte
109 from sixtypical.gen6502 import (
11 Immediate, Absolute, AbsoluteX, AbsoluteY, Indirect, Relative,
10 Immediate, Absolute, AbsoluteX, AbsoluteY, ZeroPage, Indirect, IndirectY, Relative,
1211 LDA, LDX, LDY, STA, STX, STY,
1312 TAX, TAY, TXA, TYA,
1413 CLC, SEC, ADC, SBC, ROL, ROR,
274273 self.compile_block(instr.block)
275274 self.emitter.emit(CLI())
276275 elif opcode == 'copy':
277 if src.type == TYPE_BYTE and dest.type == TYPE_BYTE:
278 src_label = self.labels[src.name]
276 if isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef):
277 if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType):
278 if isinstance(src, ConstantRef):
279 dest_label = self.labels[dest.ref.name]
280 self.emitter.emit(LDA(Immediate(Byte(src.value))))
281 self.emitter.emit(STA(IndirectY(dest_label)))
282 elif isinstance(src, LocationRef):
283 src_label = self.labels[src.name]
284 dest_label = self.labels[dest.ref.name]
285 self.emitter.emit(LDA(Absolute(src_label)))
286 self.emitter.emit(STA(IndirectY(dest_label)))
287 else:
288 raise NotImplementedError((src, dest))
289 else:
290 raise NotImplementedError((src, dest))
291 elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef):
292 if dest.type == TYPE_BYTE and isinstance(src.ref.type, PointerType):
293 src_label = self.labels[src.ref.name]
294 dest_label = self.labels[dest.name]
295 self.emitter.emit(LDA(IndirectY(src_label)))
296 self.emitter.emit(STA(Absolute(dest_label)))
297 else:
298 raise NotImplementedError((src, dest))
299 elif isinstance(src, AddressRef) and isinstance(dest, LocationRef) and \
300 isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType):
301 src_label = self.labels[src.ref.name]
279302 dest_label = self.labels[dest.name]
280 self.emitter.emit(LDA(Absolute(src_label)))
281 self.emitter.emit(STA(Absolute(dest_label)))
303 self.emitter.emit(LDA(Immediate(HighAddressByte(src_label))))
304 self.emitter.emit(STA(ZeroPage(dest_label)))
305 self.emitter.emit(LDA(Immediate(LowAddressByte(src_label))))
306 self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1))))
307 elif not isinstance(src, (ConstantRef, LocationRef)) or not isinstance(dest, LocationRef):
308 raise NotImplementedError((src, dest))
309 elif src.type == TYPE_BYTE and dest.type == TYPE_BYTE:
310 if isinstance(src, ConstantRef):
311 raise NotImplementedError
312 else:
313 src_label = self.labels[src.name]
314 dest_label = self.labels[dest.name]
315 self.emitter.emit(LDA(Absolute(src_label)))
316 self.emitter.emit(STA(Absolute(dest_label)))
282317 elif src.type == TYPE_WORD and dest.type == TYPE_WORD:
283 src_label = self.labels[src.name]
284 dest_label = self.labels[dest.name]
285 self.emitter.emit(LDA(Absolute(src_label)))
286 self.emitter.emit(STA(Absolute(dest_label)))
287 self.emitter.emit(LDA(Absolute(Offset(src_label, 1))))
288 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
318 if isinstance(src, ConstantRef):
319 dest_label = self.labels[dest.name]
320 hi = (src.value >> 8) & 255
321 lo = src.value & 255
322 self.emitter.emit(LDA(Immediate(Byte(hi))))
323 self.emitter.emit(STA(Absolute(dest_label)))
324 self.emitter.emit(LDA(Immediate(Byte(lo))))
325 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
326 else:
327 src_label = self.labels[src.name]
328 dest_label = self.labels[dest.name]
329 self.emitter.emit(LDA(Absolute(src_label)))
330 self.emitter.emit(STA(Absolute(dest_label)))
331 self.emitter.emit(LDA(Absolute(Offset(src_label, 1))))
332 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
289333 elif isinstance(src.type, VectorType) and isinstance(dest.type, VectorType):
290334 src_label = self.labels[src.name]
291335 dest_label = self.labels[dest.name]
6060 assert self.addr is not None, "unresolved label: %s" % self.name
6161 return Byte(self.addr - (addr + 2)).serialize()
6262
63 def serialize_as_zero_page(self, offset=0):
64 assert self.addr is not None, "unresolved label: %s" % self.name
65 return Byte(self.addr + offset).serialize()
66
6367 def __repr__(self):
6468 addrs = ', addr=%r' % self.addr if self.addr is not None else ''
6569 return "%s(%r%s)" % (self.__class__.__name__, self.name, addrs)
7680
7781 def serialize(self, addr=None):
7882 return self.label.serialize(offset=self.offset)
83
84 def serialize_as_zero_page(self, offset=0):
85 return self.label.serialize_as_zero_page(offset=self.offset)
7986
8087 def __repr__(self):
8188 return "%s(%r, %r)" % (self.__class__.__name__, self.label, self.offset)
11
22 from sixtypical.ast import Program, Routine, Block, Instr
33 from sixtypical.model import (
4 ConstantRef, LocationRef, PartRef,
4 ConstantRef, LocationRef, PartRef, IndirectRef,
55 REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
66 )
77
190190 while context.get(src) == 0:
191191 self.eval_block(instr.block, context)
192192 elif opcode == 'copy':
193 if isinstance(src, IndirectRef):
194 raise NotImplementedError("this doesn't actually work")
195 src = src.ref
196 if isinstance(dest, IndirectRef):
197 raise NotImplementedError("this doesn't actually work")
198 dest = dest.ref
193199 context.set(dest, context.get(src))
194200 # these are trashed; so could be anything really
195201 context.set(REG_A, 0)
5757 pass
5858
5959
60 class ZeroPage(AddressingMode):
61 def __init__(self, value):
62 assert isinstance(value, (Label, Offset))
63 self.value = value
64
65 def size(self):
66 return 1
67
68 def serialize(self, addr=None):
69 return self.value.serialize_as_zero_page()
70
71
6072 class Indirect(AddressingMode):
6173 def __init__(self, value):
6274 assert isinstance(value, Label)
6779
6880 def serialize(self, addr=None):
6981 return self.value.serialize()
82
83
84 class IndirectY(ZeroPage):
85 pass
7086
7187
7288 class Relative(AddressingMode):
243259 Absolute: 0xad,
244260 AbsoluteX: 0xbd,
245261 AbsoluteY: 0xb9,
262 IndirectY: 0xb1,
263 ZeroPage: 0xa5,
246264 }
247265
248266
319337 Absolute: 0x8d,
320338 AbsoluteX: 0x9d,
321339 AbsoluteY: 0x99,
340 IndirectY: 0x91,
341 ZeroPage: 0x85,
322342 }
323343
324344
5757 super(VectorType, self).__init__('vector', **kwargs)
5858
5959
60 class BufferType(Type):
61 def __init__(self, size):
62 self.size = size
63 self.name = 'buffer[%s]' % self.size
64
65
66 class PointerType(Type):
67 def __init__(self):
68 self.name = 'pointer'
69
70
6071 class Ref(object):
6172 def is_constant(self):
6273 """read-only means that the program cannot change the value
7586 # but because we store the type in here and we want to treat
7687 # these objects as immutable, we compare the types, too.
7788 # Not sure if very wise.
78 return isinstance(other, LocationRef) and (
89 return isinstance(other, self.__class__) and (
7990 other.name == self.name and other.type == self.type
8091 )
8192
8798
8899 def is_constant(self):
89100 return isinstance(self.type, RoutineType)
101
102
103 class IndirectRef(Ref):
104 def __init__(self, ref):
105 self.ref = ref
106
107 def __eq__(self, other):
108 return self.ref == other.ref
109
110 def __hash__(self):
111 return hash(self.__class__.name) ^ hash(self.ref)
112
113 def __repr__(self):
114 return '%s(%r)' % (self.__class__.__name__, self.ref)
115
116 @property
117 def name(self):
118 return '[{}]+y'.format(self.ref.name)
119
120 def is_constant(self):
121 return False
122
123
124 class AddressRef(Ref):
125 def __init__(self, ref):
126 self.ref = ref
127
128 def __eq__(self, other):
129 return self.ref == other.ref
130
131 def __hash__(self):
132 return hash(self.__class__.name) ^ hash(self.ref)
133
134 def __repr__(self):
135 return '%s(%r)' % (self.__class__.__name__, self.ref)
136
137 @property
138 def name(self):
139 return '^{}'.format(self.ref.name)
140
141 def is_constant(self):
142 return True
90143
91144
92145 class PartRef(Ref):
22 from sixtypical.ast import Program, Defn, Routine, Block, Instr
33 from sixtypical.model import (
44 TYPE_BIT, TYPE_BYTE, TYPE_BYTE_TABLE, TYPE_WORD, TYPE_WORD_TABLE,
5 RoutineType, VectorType, ExecutableType,
6 LocationRef, ConstantRef
5 RoutineType, VectorType, ExecutableType, BufferType, PointerType,
6 LocationRef, ConstantRef, IndirectRef, AddressRef,
77 )
88 from sixtypical.scanner import Scanner
99
3131 def program(self):
3232 defns = []
3333 routines = []
34 while self.scanner.on('byte', 'word', 'vector'):
34 while self.scanner.on('byte', 'word', 'vector', 'buffer', 'pointer'):
3535 defn = self.defn()
3636 name = defn.name
3737 if name in self.symbols:
4949 return Program(defns=defns, routines=routines)
5050
5151 def defn(self):
52 type = None
53 if self.scanner.consume('byte'):
54 type = TYPE_BYTE
55 if self.scanner.consume('table'):
56 type = TYPE_BYTE_TABLE
57 elif self.scanner.consume('word'):
58 type = TYPE_WORD
59 if self.scanner.consume('table'):
60 type = TYPE_WORD_TABLE
61 else:
62 self.scanner.expect('vector')
63 type = 'vector' # will be resolved to a Type below
52 type_ = self.defn_type()
53
6454 self.scanner.check_type('identifier')
6555 name = self.scanner.token
6656 self.scanner.scan()
6757
6858 (inputs, outputs, trashes) = self.constraints()
69 if type == 'vector':
70 type = VectorType(inputs=inputs, outputs=outputs, trashes=trashes)
59 if type_ == 'vector':
60 type_ = VectorType(inputs=inputs, outputs=outputs, trashes=trashes)
7161 elif inputs or outputs or trashes:
7262 raise SyntaxError("Cannot apply constraints to non-vector type")
7363
8676 if initial is not None and addr is not None:
8777 raise SyntaxError("Definition cannot have both initial value and explicit address")
8878
89 location = LocationRef(type, name)
79 location = LocationRef(type_, name)
9080
9181 return Defn(name=name, addr=addr, initial=initial, location=location)
82
83 def defn_type(self):
84 if self.scanner.consume('byte'):
85 if self.scanner.consume('table'):
86 return TYPE_BYTE_TABLE
87 return TYPE_BYTE
88 elif self.scanner.consume('word'):
89 if self.scanner.consume('table'):
90 return TYPE_WORD_TABLE
91 return TYPE_WORD
92 elif self.scanner.consume('vector'):
93 return 'vector' # will be resolved to a Type by caller
94 elif self.scanner.consume('buffer'):
95 self.scanner.expect('[')
96 self.scanner.check_type('integer literal')
97 size = int(self.scanner.token)
98 self.scanner.scan()
99 self.scanner.expect(']')
100 return BufferType(size)
101 else:
102 self.scanner.expect('pointer')
103 return PointerType()
92104
93105 def constraints(self):
94106 inputs = set()
137149 self.scanner.scan()
138150 return loc
139151 elif self.scanner.on_type('integer literal'):
140 loc = ConstantRef(TYPE_BYTE, int(self.scanner.token))
152 value = int(self.scanner.token)
153 type_ = TYPE_WORD if value > 255 else TYPE_BYTE
154 loc = ConstantRef(type_, value)
155 self.scanner.scan()
156 return loc
157 elif self.scanner.consume('word'):
158 loc = ConstantRef(TYPE_WORD, int(self.scanner.token))
141159 self.scanner.scan()
142160 return loc
143161 else:
144162 loc = self.lookup(self.scanner.token)
145163 self.scanner.scan()
146164 return loc
165
166 def indlocexpr(self):
167 if self.scanner.consume('['):
168 loc = self.locexpr()
169 self.scanner.expect(']')
170 self.scanner.expect('+')
171 self.scanner.expect('y')
172 return IndirectRef(loc)
173 elif self.scanner.consume('^'):
174 loc = self.locexpr()
175 return AddressRef(loc)
176 else:
177 return self.locexpr()
147178
148179 def block(self):
149180 instrs = []
215246 elif self.scanner.token in ("copy",):
216247 opcode = self.scanner.token
217248 self.scanner.scan()
218 src = self.locexpr()
249 src = self.indlocexpr()
219250 self.scanner.expect(',')
220 dest = self.locexpr()
251 dest = self.indlocexpr()
221252 return Instr(opcode=opcode, dest=dest, src=src)
222253 elif self.scanner.consume("with"):
223254 self.scanner.expect("interrupts")
2828 self.token = None
2929 self.type = 'EOF'
3030 return
31 if self.scan_pattern(r'\,|\@|\+|\:|\<|\>|\{|\}', 'operator'):
31 if self.scan_pattern(r'\,|\@|\+|\:|\<|\>|\{|\}|\[|\]|\^', 'operator'):
3232 return
3333 if self.scan_pattern(r'\d+', 'integer literal'):
3434 return
10161016 | }
10171017 ? UnmeaningfulReadError: y in main
10181018
1019 And if you trash the test expression (i.e. `z` in the below) inside the loop,
1020 this is an error too.
1021
1022 | word one : 0
1023 | word two : 0
1024 |
1025 | routine main
1026 | inputs one, two
1027 | outputs two
1028 | trashes a, z, n
1029 | {
1030 | repeat {
1031 | copy one, two
1032 | } until z
1033 | }
1034 ? UnmeaningfulReadError: z in main
1035
10191036 ### copy ###
10201037
10211038 Can't `copy` from a memory location that isn't initialized.
11531170 | copy source, dest
11541171 | }
11551172 ? TypeMismatchError
1173
1174 ### copy[] ###
1175
1176 Buffers and pointers.
1177
1178 Note that `^buf` is a constant value, so it by itself does not require `buf` to be
1179 listed in any input/output sets.
1180
1181 However, if the code reads from it through a pointer, it *should* be in `inputs`.
1182
1183 Likewise, if the code writes to it through a pointer, it *should* be in `outputs`.
1184
1185 Of course, unless you write to *all* the bytes in a buffer, some of those bytes
1186 might not be meaningful. So how meaningful is this check?
1187
1188 This is an open problem.
1189
1190 For now, convention says: if it is being read, list it in `inputs`, and if it is
1191 being modified, list it in both `inputs` and `outputs`.
1192
1193 Write literal through a pointer.
1194
1195 | buffer[2048] buf
1196 | pointer ptr
1197 |
1198 | routine main
1199 | inputs buf
1200 | outputs y, buf
1201 | trashes a, z, n, ptr
1202 | {
1203 | ld y, 0
1204 | copy ^buf, ptr
1205 | copy 123, [ptr] + y
1206 | }
1207 = ok
1208
1209 It does use `y`.
1210
1211 | buffer[2048] buf
1212 | pointer ptr
1213 |
1214 | routine main
1215 | inputs buf
1216 | outputs buf
1217 | trashes a, z, n, ptr
1218 | {
1219 | copy ^buf, ptr
1220 | copy 123, [ptr] + y
1221 | }
1222 ? UnmeaningfulReadError
1223
1224 Write stored value through a pointer.
1225
1226 | buffer[2048] buf
1227 | pointer ptr
1228 | byte foo
1229 |
1230 | routine main
1231 | inputs foo, buf
1232 | outputs y, buf
1233 | trashes a, z, n, ptr
1234 | {
1235 | ld y, 0
1236 | copy ^buf, ptr
1237 | copy foo, [ptr] + y
1238 | }
1239 = ok
1240
1241 Read through a pointer.
1242
1243 | buffer[2048] buf
1244 | pointer ptr
1245 | byte foo
1246 |
1247 | routine main
1248 | inputs buf
1249 | outputs foo
1250 | trashes a, y, z, n, ptr
1251 | {
1252 | ld y, 0
1253 | copy ^buf, ptr
1254 | copy [ptr] + y, foo
1255 | }
1256 = ok
11561257
11571258 ### routines ###
11581259
0 Sixtypical Compilation
0 SixtyPical Compilation
11 ======================
22
33 This is a test suite, written in [Falderal][] format, for compiling
4 Sixtypical to 6502 machine code.
4 SixtyPical to 6502 machine code.
55
66 [Falderal]: http://catseye.tc/node/Falderal
77
8 -> Functionality "Compile Sixtypical program" is implemented by
8 -> Functionality "Compile SixtyPical program" is implemented by
99 -> shell command "bin/sixtypical --compile %(test-body-file) | fa-bin-to-hex"
1010
11 -> Tests for functionality "Compile Sixtypical program"
11 -> Tests for functionality "Compile SixtyPical program"
1212
1313 Null program.
1414
266266 | }
267267 = 00c0ad0fc08d0dc0ad10c08d0ec060
268268
269 Copy literal word to word.
270
271 | word bar
272 |
273 | routine main
274 | outputs bar
275 | trashes a, n, z
276 | {
277 | copy 65535, bar
278 | }
279 = 00c0a9ff8d0bc0a9ff8d0cc060
280
269281 Copy vector to vector.
270282
271283 | vector bar
280292 | }
281293 = 00c0ad0fc08d0dc0ad10c08d0ec060
282294
283 Copy instruction inside an `interrupts off` block.
295 Copy routine to vector, inside an `interrupts off` block.
284296
285297 | vector bar
286298 |
328340 | goto bar
329341 | }
330342 = 00c0a0c84c06c060a2c860
343
344 ### Buffers and Pointers
345
346 Load address into pointer.
347
348 | buffer[2048] buf
349 | pointer ptr @ 254
350 |
351 | routine main
352 | inputs buf
353 | outputs buf, y
354 | trashes a, z, n, ptr
355 | {
356 | ld y, 0
357 | copy ^buf, ptr
358 | }
359 = 00c0a000a90b85fea9c085ff60
360
361 Write literal through a pointer.
362
363 | buffer[2048] buf
364 | pointer ptr @ 254
365 |
366 | routine main
367 | inputs buf
368 | outputs buf, y
369 | trashes a, z, n, ptr
370 | {
371 | ld y, 0
372 | copy ^buf, ptr
373 | copy 123, [ptr] + y
374 | }
375 = 00c0a000a90f85fea9c085ffa97b91fe60
376
377 Write stored value through a pointer.
378
379 | buffer[2048] buf
380 | pointer ptr @ 254
381 | byte foo
382 |
383 | routine main
384 | inputs foo, buf
385 | outputs y, buf
386 | trashes a, z, n, ptr
387 | {
388 | ld y, 0
389 | copy ^buf, ptr
390 | copy foo, [ptr] + y
391 | }
392 = 00c0a000a91085fea9c085ffad12c091fe60
393
394 Read through a pointer.
395
396 | buffer[2048] buf
397 | pointer ptr @ 254
398 | byte foo
399 |
400 | routine main
401 | inputs buf
402 | outputs y, foo
403 | trashes a, z, n, ptr
404 | {
405 | ld y, 0
406 | copy ^buf, ptr
407 | copy [ptr] + y, foo
408 | }
409 = 00c0a000a91085fea9c085ffb1fe8d12c060
0 Sixtypical Execution
0 SixtyPical Execution
11 ====================
22
33 This is a test suite, written in [Falderal][] format, for the dynamic
55
66 [Falderal]: http://catseye.tc/node/Falderal
77
8 -> Functionality "Execute Sixtypical program" is implemented by
8 -> Functionality "Execute SixtyPical program" is implemented by
99 -> shell command "bin/sixtypical --execute %(test-body-file)"
1010
11 -> Tests for functionality "Execute Sixtypical program"
11 -> Tests for functionality "Execute SixtyPical program"
1212
1313 Rudimentary program.
1414
434434 = y: 0
435435 = z: 0
436436
437 Copy literal word to word.
438
439 | word bar
440 |
441 | routine main {
442 | copy word 2000, bar
443 | }
444 = a: 0
445 = bar: 2000
446 = c: 0
447 = n: 0
448 = v: 0
449 = x: 0
450 = y: 0
451 = z: 0
452
437453 Indirect call.
438454
439455 | vector foo outputs x trashes z, n
0 Sixtypical Execution
1 ====================
0 SixtyPical Syntax
1 =================
22
33 This is a test suite, written in [Falderal][] format, for the syntax of
44 the Sixtypical language, disgregarding execution, static analysis, etc.
88
99 [Falderal]: http://catseye.tc/node/Falderal
1010
11 -> Functionality "Check syntax of Sixtypical program" is implemented by
11 -> Functionality "Check syntax of SixtyPical program" is implemented by
1212 -> shell command "bin/sixtypical %(test-body-file) && echo ok"
1313
14 -> Tests for functionality "Check syntax of Sixtypical program"
14 -> Tests for functionality "Check syntax of SixtyPical program"
1515
1616 Rudimentary program.
1717
122122 | }
123123 = ok
124124
125 User-defined memory addresses of different types.
126
127 | byte byt
128 | word wor
129 | vector vec
130 | byte table tab
131 | buffer[2048] buf
132 | pointer ptr
133 |
134 | routine main {
135 | }
136 = ok
137
125138 Explicit memory address.
126139
127140 | byte screen @ 1024
307320 | goto foo
308321 | }
309322 ? SyntaxError
323
324 Buffers and pointers.
325
326 | buffer[2048] buf
327 | pointer ptr
328 | byte foo
329 |
330 | routine main {
331 | copy ^buf, ptr
332 | copy 123, [ptr] + y
333 | copy [ptr] + y, foo
334 | }
335 = ok