git @ Cat's Eye Technologies SixtyPical / 15072ef
The evaluator doesn't add much and keeps falling behind; remove it. Chris Pressey 3 years ago
7 changed file(s) with 7 addition(s) and 714 deletion(s). Raw diff Collapse all Expand all
77 * Initialized `byte table` values need not have all 256 bytes initialized.
88 * Constraints for `vector` type come immediately after the type, not the variable.
99 * `vector table` storage, and ability to copy vectors in and out of same.
10 * Removed the evaluator. The reference implementation only analyzes and compiles.
1011 * Fixed bug where index register wasn't required to be initialized before table access.
1112
1213 0.10
2020 * explicit tail calls
2121 * indirect subroutine calls
2222
23 The reference implementation can execute, analyze, and compile SixtyPical
24 programs to 6502 machine code.
23 The reference implementation can analyze and compile SixtyPical programs to
24 6502 machine code.
2525
2626 Documentation
2727 -------------
4343 Finish the little demo "game" where you can move a block around the screen with
4444 the joystick (i.e. bring it up to par with the original demo game that was written
4545 for SixtyPical)
46
47 ### `vector table` type
4846
4947 ### `low` and `high` address operators
5048
11
22 """Usage: sixtypical [OPTIONS] FILES
33
4 Analyzes and/or executes and/or compiles a Sixtypical program.
4 Analyzes and compiles a Sixtypical program.
55 """
66
77 from os.path import realpath, dirname, join
1818 import traceback
1919
2020 from sixtypical.parser import Parser
21 from sixtypical.evaluator import Evaluator
2221 from sixtypical.analyzer import Analyzer
2322 from sixtypical.emitter import Emitter, Byte, Word
2423 from sixtypical.compiler import Compiler
4039 action="store_true",
4140 help="")
4241 optparser.add_option("--traceback",
43 action="store_true",
44 help="")
45 optparser.add_option("--execute",
4642 action="store_true",
4743 help="")
4844
8783 pprint(emitter.accum)
8884 else:
8985 emitter.serialize(fh)
90
91 if options.execute:
92 context = Evaluator().eval_program(program)
93 print str(context)
11 ==========
22
33 This document describes the SixtyPical programming language version 0.11,
4 both its execution aspect and its static analysis aspect (even though
5 these are, technically speaking, separate concepts.)
4 both its static semantics (the capabilities and limits of the static
5 analyses it defines) and its runtime semantics (with reference to the
6 semantics of 6502 machine code.)
67
78 This document is nominally normative, but the tests in the `tests` directory
89 are even more normative.
+0
-208
src/sixtypical/evaluator.py less more
0 # encoding: UTF-8
1
2 from sixtypical.ast import Program, Routine, Block, Instr
3 from sixtypical.model import (
4 ConstantRef, LocationRef, PartRef, IndirectRef,
5 REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
6 )
7
8
9 class Context(object):
10 def __init__(self):
11 self._store = {}
12
13 def __str__(self):
14 return '\n'.join("%s: %s" % (name, value)
15 for (name, value) in sorted(self._store.iteritems())
16 if not isinstance(value, Routine))
17
18 def get(self, ref):
19 if isinstance(ref, ConstantRef):
20 return ref.value
21 elif isinstance(ref, LocationRef):
22 return self._store[ref.name]
23 elif isinstance(ref, PartRef):
24 value = self.get(ref.ref)
25 if ref.height == 0:
26 return value & 255
27 elif ref.height == 1:
28 return (value >> 8) & 255
29 else:
30 raise NotImplementedError
31 else:
32 raise ValueError(ref)
33
34 def set(self, ref, value):
35 if isinstance(ref, PartRef):
36 old = self.get(ref.ref)
37 if ref.height == 0:
38 value = (old & (255 << 8)) | value
39 elif ref.height == 1:
40 value = (value << 8) | (old & 255)
41 else:
42 raise NotImplementedError
43 ref = ref.ref
44 assert isinstance(ref, LocationRef)
45 self._store[ref.name] = value
46
47
48 class Evaluator(object):
49
50 def eval_program(self, program):
51 assert isinstance(program, Program)
52 context = Context()
53 for ref in (REG_A, REG_X, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C):
54 context.set(ref, 0)
55 main = None
56
57 for defn in program.defns:
58 if defn.initial is not None:
59 context.set(defn.location, defn.initial)
60
61 for routine in program.routines:
62 context.set(routine.location, routine)
63 if routine.name == 'main':
64 main = routine
65
66 self.eval_routine(main, context)
67 return context
68
69 def eval_routine(self, routine, context):
70 assert isinstance(routine, Routine)
71 self.next_routine = routine
72 while self.next_routine:
73 routine = self.next_routine
74 self.next_routine = None
75 self.eval_block(routine.block, context)
76
77 def eval_block(self, block, context):
78 assert isinstance(block, Block)
79 for i in block.instrs:
80 self.eval_instr(i, context)
81 if self.next_routine:
82 break
83
84 def eval_instr(self, instr, context):
85 assert isinstance(instr, Instr)
86 opcode = instr.opcode
87 dest = instr.dest
88 src = instr.src
89
90 if opcode == 'ld':
91 result = context.get(src)
92 context.set(FLAG_Z, 1 if result == 0 else 0)
93 context.set(FLAG_N, 1 if result & 128 else 0)
94 context.set(dest, result)
95 elif opcode == 'st':
96 context.set(dest, context.get(src))
97 elif opcode == 'add':
98 carry = context.get(FLAG_C)
99 val = context.get(src)
100 now = context.get(dest)
101 result = now + val + carry
102 if result > 255:
103 result &= 255
104 context.set(FLAG_C, 1)
105 else:
106 context.set(FLAG_C, 0)
107 context.set(FLAG_Z, 1 if result == 0 else 0)
108 context.set(FLAG_N, 1 if result & 128 else 0)
109 context.set(dest, result)
110 elif opcode == 'sub':
111 carry = context.get(FLAG_C)
112 val = context.get(src)
113 now = context.get(dest)
114 result = now - val - carry
115 if result < 0:
116 result &= 255
117 context.set(FLAG_C, 1)
118 else:
119 context.set(FLAG_C, 0)
120 context.set(FLAG_Z, 1 if result == 0 else 0)
121 context.set(FLAG_N, 1 if result & 128 else 0)
122 context.set(dest, result)
123 elif opcode == 'inc':
124 val = context.get(dest)
125 result = (val + 1) & 255
126 context.set(FLAG_Z, 1 if result == 0 else 0)
127 context.set(FLAG_N, 1 if result & 128 else 0)
128 context.set(dest, result)
129 elif opcode == 'dec':
130 val = context.get(dest)
131 result = (val - 1) & 255
132 context.set(FLAG_Z, 1 if result == 0 else 0)
133 context.set(FLAG_N, 1 if result & 128 else 0)
134 context.set(dest, result)
135 elif opcode == 'cmp':
136 val = context.get(src)
137 now = context.get(dest)
138 result = now - val
139 context.set(FLAG_Z, 1 if result == 0 else 0)
140 context.set(FLAG_N, 1 if result & 128 else 0)
141 if result < 0:
142 result &= 255
143 context.set(FLAG_C, 1)
144 else:
145 context.set(FLAG_C, 0)
146 elif opcode == 'and':
147 result = context.get(dest) & context.get(src)
148 context.set(FLAG_Z, 1 if result == 0 else 0)
149 context.set(FLAG_N, 1 if result & 128 else 0)
150 context.set(dest, result)
151 elif opcode == 'or':
152 result = context.get(dest) | context.get(src)
153 context.set(FLAG_Z, 1 if result == 0 else 0)
154 context.set(FLAG_N, 1 if result & 128 else 0)
155 context.set(dest, result)
156 elif opcode == 'xor':
157 result = context.get(dest) ^ context.get(src)
158 context.set(FLAG_Z, 1 if result == 0 else 0)
159 context.set(FLAG_N, 1 if result & 128 else 0)
160 context.set(dest, result)
161 elif opcode == 'shl':
162 val = context.get(dest)
163 carry = context.get(FLAG_C)
164 context.set(FLAG_C, 1 if val & 128 else 0)
165 result = ((val << 1) + carry) & 255
166 context.set(FLAG_Z, 1 if result == 0 else 0)
167 context.set(FLAG_N, 1 if result & 128 else 0)
168 context.set(dest, result)
169 elif opcode == 'shr':
170 val = context.get(dest)
171 carry = context.get(FLAG_C)
172 context.set(FLAG_C, 1 if val & 1 else 0)
173 result = (val >> 1) + (carry * 128)
174 context.set(FLAG_Z, 1 if result == 0 else 0)
175 context.set(FLAG_N, 1 if result & 128 else 0)
176 context.set(dest, result)
177 elif opcode == 'call':
178 self.eval_routine(context.get(instr.location), context)
179 elif opcode == 'goto':
180 self.next_routine = context.get(instr.location)
181 elif opcode == 'if':
182 val = context.get(src)
183 test = (val != 0) if not instr.inverted else (val == 0)
184 if test:
185 self.eval_block(instr.block1, context)
186 elif instr.block2:
187 self.eval_block(instr.block2, context)
188 elif opcode == 'repeat':
189 self.eval_block(instr.block, context)
190 while context.get(src) == 0:
191 self.eval_block(instr.block, context)
192 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
199 context.set(dest, context.get(src))
200 # these are trashed; so could be anything really
201 context.set(REG_A, 0)
202 context.set(FLAG_Z, 0)
203 context.set(FLAG_N, 0)
204 elif opcode == 'with-sei':
205 self.eval_block(instr.block)
206 else:
207 raise NotImplementedError
11
22 falderal --substring-error \
33 tests/SixtyPical\ Syntax.md \
4 tests/SixtyPical\ Execution.md \
54 tests/SixtyPical\ Analysis.md \
65 tests/SixtyPical\ Compilation.md
+0
-490
tests/SixtyPical Execution.md less more
0 SixtyPical Execution
1 ====================
2
3 This is a test suite, written in [Falderal][] format, for the dynamic
4 execution behaviour of the Sixtypical language, disgregarding static analysis.
5
6 [Falderal]: http://catseye.tc/node/Falderal
7
8 -> Functionality "Execute SixtyPical program" is implemented by
9 -> shell command "bin/sixtypical --execute %(test-body-file)"
10
11 -> Tests for functionality "Execute SixtyPical program"
12
13 Rudimentary program.
14
15 | routine main {
16 | ld a, 0
17 | add a, 1
18 | }
19 = a: 1
20 = c: 0
21 = n: 0
22 = v: 0
23 = x: 0
24 = y: 0
25 = z: 0
26
27 Program accesses a memory location.
28
29 | byte lives
30 |
31 | routine main {
32 | ld a, 0
33 | st a, lives
34 | ld x, lives
35 | add x, 1
36 | st x, lives
37 | }
38 = a: 0
39 = c: 0
40 = lives: 1
41 = n: 0
42 = v: 0
43 = x: 1
44 = y: 0
45 = z: 0
46
47 Program accesses a memory location with initial value.
48
49 | byte lives : 3
50 |
51 | routine main {
52 | ld a, lives
53 | }
54 = a: 3
55 = c: 0
56 = lives: 3
57 = n: 0
58 = v: 0
59 = x: 0
60 = y: 0
61 = z: 0
62
63 Add honours carry.
64
65 | routine main {
66 | ld a, 255
67 | st on, c
68 | add a, 0
69 | }
70 = a: 0
71 = c: 1
72 = n: 0
73 = v: 0
74 = x: 0
75 = y: 0
76 = z: 1
77
78 | routine main {
79 | ld a, $ff
80 | st off, c
81 | add a, 1
82 | }
83 = a: 0
84 = c: 1
85 = n: 0
86 = v: 0
87 = x: 0
88 = y: 0
89 = z: 1
90
91 Subtract honours carry.
92
93 | routine main {
94 | ld a, 0
95 | st on, c
96 | sub a, 0
97 | }
98 = a: 255
99 = c: 1
100 = n: 1
101 = v: 0
102 = x: 0
103 = y: 0
104 = z: 0
105
106 | routine main {
107 | ld a, 0
108 | st off, c
109 | sub a, 1
110 | }
111 = a: 255
112 = c: 1
113 = n: 1
114 = v: 0
115 = x: 0
116 = y: 0
117 = z: 0
118
119 Inc and dec do not honour carry, but do set n and z.
120
121 | routine main {
122 | ld x, 254
123 | st on, c
124 | inc x
125 | }
126 = a: 0
127 = c: 1
128 = n: 1
129 = v: 0
130 = x: 255
131 = y: 0
132 = z: 0
133
134 | routine main {
135 | ld y, 1
136 | st on, c
137 | dec y
138 | }
139 = a: 0
140 = c: 1
141 = n: 0
142 = v: 0
143 = x: 0
144 = y: 0
145 = z: 1
146
147 Compare affects, but does not use, carry.
148
149 | routine main {
150 | ld a, 1
151 | st on, c
152 | cmp a, 1
153 | }
154 = a: 1
155 = c: 0
156 = n: 0
157 = v: 0
158 = x: 0
159 = y: 0
160 = z: 1
161
162 | routine main {
163 | ld a, 1
164 | st off, c
165 | cmp a, 5
166 | }
167 = a: 1
168 = c: 1
169 = n: 1
170 = v: 0
171 = x: 0
172 = y: 0
173 = z: 0
174
175 AND.
176
177 | routine main {
178 | ld a, 15
179 | and a, 18
180 | }
181 = a: 2
182 = c: 0
183 = n: 0
184 = v: 0
185 = x: 0
186 = y: 0
187 = z: 0
188
189 OR.
190
191 | routine main {
192 | ld a, 34
193 | or a, 18
194 | }
195 = a: 50
196 = c: 0
197 = n: 0
198 = v: 0
199 = x: 0
200 = y: 0
201 = z: 0
202
203 XOR.
204
205 | routine main {
206 | ld a, 34
207 | xor a, 18
208 | }
209 = a: 48
210 = c: 0
211 = n: 0
212 = v: 0
213 = x: 0
214 = y: 0
215 = z: 0
216
217 Shift left.
218
219 | routine main {
220 | ld a, 129
221 | st off, c
222 | shl a
223 | }
224 = a: 2
225 = c: 1
226 = n: 0
227 = v: 0
228 = x: 0
229 = y: 0
230 = z: 0
231
232 | routine main {
233 | ld a, 0
234 | st on, c
235 | shl a
236 | }
237 = a: 1
238 = c: 0
239 = n: 0
240 = v: 0
241 = x: 0
242 = y: 0
243 = z: 0
244
245 Shift right.
246
247 | routine main {
248 | ld a, 129
249 | st off, c
250 | shr a
251 | }
252 = a: 64
253 = c: 1
254 = n: 0
255 = v: 0
256 = x: 0
257 = y: 0
258 = z: 0
259
260 | routine main {
261 | ld a, 0
262 | st on, c
263 | shr a
264 | }
265 = a: 128
266 = c: 0
267 = n: 1
268 = v: 0
269 = x: 0
270 = y: 0
271 = z: 0
272
273 Call routine.
274
275 | routine up {
276 | inc x
277 | inc y
278 | }
279 | routine main {
280 | ld x, 0
281 | ld y, 1
282 | call up
283 | call up
284 | }
285 = a: 0
286 = c: 0
287 = n: 0
288 = v: 0
289 = x: 2
290 = y: 3
291 = z: 0
292
293 If.
294
295 | routine main {
296 | ld x, 40
297 | cmp x, 40
298 | if z {
299 | ld a, 1
300 | } else {
301 | ld a, 8
302 | }
303 | ld x, 2
304 | }
305 = a: 1
306 = c: 0
307 = n: 0
308 = v: 0
309 = x: 2
310 = y: 0
311 = z: 0
312
313 | routine main {
314 | ld x, 39
315 | cmp x, 40
316 | if z {
317 | ld a, 1
318 | } else {
319 | ld a, 8
320 | }
321 | ld x, 2
322 | }
323 = a: 8
324 = c: 1
325 = n: 0
326 = v: 0
327 = x: 2
328 = y: 0
329 = z: 0
330
331 If without else.
332
333 | routine main {
334 | ld x, 39
335 | cmp x, 40
336 | if z {
337 | ld a, 1
338 | }
339 | ld x, 2
340 | }
341 = a: 0
342 = c: 1
343 = n: 0
344 = v: 0
345 = x: 2
346 = y: 0
347 = z: 0
348
349 `not` inverts the sense of the test.
350
351 | routine main {
352 | ld x, 40
353 | cmp x, 40
354 | if not z {
355 | ld a, 1
356 | } else {
357 | ld a, 8
358 | }
359 | ld x, 2
360 | }
361 = a: 8
362 = c: 0
363 = n: 0
364 = v: 0
365 = x: 2
366 = y: 0
367 = z: 0
368
369 | routine main {
370 | ld x, 39
371 | cmp x, 40
372 | if not z {
373 | ld a, 1
374 | }
375 | ld x, 2
376 | }
377 = a: 1
378 = c: 1
379 = n: 0
380 = v: 0
381 = x: 2
382 = y: 0
383 = z: 0
384
385 Repeat loop.
386
387 | routine main {
388 | ld x, 0
389 | ld y, 15
390 | repeat {
391 | inc x
392 | inc y
393 | cmp x, 10
394 | } until z
395 | }
396 = a: 0
397 = c: 0
398 = n: 0
399 = v: 0
400 = x: 10
401 = y: 25
402 = z: 1
403
404 Copy instruction. Note that the state of a, z, and n are not defined
405 after copy executes.
406
407 | routine main {
408 | ld x, 5
409 | copy x, y
410 | }
411 = a: 0
412 = c: 0
413 = n: 0
414 = v: 0
415 = x: 5
416 = y: 5
417 = z: 0
418
419 Copy word to word.
420
421 | word foo : 2000
422 | word bar
423 |
424 | routine main {
425 | copy foo, bar
426 | }
427 = a: 0
428 = bar: 2000
429 = c: 0
430 = foo: 2000
431 = n: 0
432 = v: 0
433 = x: 0
434 = y: 0
435 = z: 0
436
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
453 Indirect call.
454
455 | vector foo outputs x trashes z, n
456 |
457 | routine bar outputs x trashes z, n {
458 | ld x, 200
459 | }
460 |
461 | routine main inputs bar outputs x, foo trashes a, z, n {
462 | copy bar, foo
463 | call foo
464 | }
465 = a: 0
466 = c: 0
467 = n: 1
468 = v: 0
469 = x: 200
470 = y: 0
471 = z: 0
472
473 goto.
474
475 | routine bar outputs x trashes z, n {
476 | ld x, 200
477 | }
478 |
479 | routine main outputs x trashes a, z, n {
480 | ld y, 200
481 | goto bar
482 | }
483 = a: 0
484 = c: 0
485 = n: 1
486 = v: 0
487 = x: 200
488 = y: 200
489 = z: 0