git @ Cat's Eye Technologies SixtyPical / 0.11
Merge pull request #6 from catseye/develop-0.11 Develop 0.11 Chris Pressey authored 2 years ago GitHub committed 2 years ago
20 changed file(s) with 1049 addition(s) and 1391 deletion(s). Raw diff Collapse all Expand all
00 History of SixtyPical
11 =====================
2
3 0.11
4 ----
5
6 * Each table has a specified size now (although, bounds checking is not performed.)
7 * Initialized `byte table` values need not have all 256 bytes initialized.
8 * Syntax for types has changed. `routine` (with constraints) is a type, while
9 `vector` is now a type constructor (taking `routine`s only) and `table` is
10 also a type constructor. This permits a new `vector table` type.
11 * Added `typedef`, allowing the user to define type aliases for readability.
12 * Added `define name routine {...}` syntax; `routine name {...}` is now legacy.
13 * Ability to copy vectors and routines into vector tables, and vectors out of same.
14 * Removed the evaluator. The reference implementation only analyzes and compiles.
15 * Fixed bug where index register wasn't required to be initialized before table access.
16 * Fixed bug where trampolines for indirect calls weren't including a final `RTS`.
217
318 0.10
419 ----
1126 * Subtract word (constant or memory location) from word memory location.
1227 * `trash` instruction explicitly indicates a value is no longer considered meaningful.
1328 * `copy []+y, a` can indirectly read a byte value into the `a` register.
29 * Initialized `byte table` memory locations.
1430 * Fixed bug which was preventing `if` branches to diverge in what they initialized,
1531 if it was already initialized when going into the `if`.
1632 * Fixed a bug which was making it crash when trying to analyze `repeat forever` loops.
00 SixtyPical
11 ==========
2
3 _Version 0.11. Work-in-progress, everything is subject to change._
24
35 SixtyPical is a very low-level programming language, similar to 6502 assembly,
46 with static analysis through abstract interpretation.
1820 * explicit tail calls
1921 * indirect subroutine calls
2022
21 The reference implementation can execute, analyze, and compile SixtyPical
22 programs to 6502 machine code.
23
24 SixtyPical is a work in progress. The current released version of SixtyPical
25 is 0.10.
23 The reference implementation can analyze and compile SixtyPical programs to
24 6502 machine code.
2625
2726 Documentation
2827 -------------
3837
3938 TODO
4039 ----
41
42 ### Demo game
43
44 Finish the little demo "game" where you can move a block around the screen with
45 the joystick (i.e. bring it up to par with the original demo game that was written
46 for SixtyPical)
47
48 ### `vector table` type
4940
5041 ### `low` and `high` address operators
5142
7970 * `copy x, [ptr] + y`
8071 * Maybe even `copy [ptra] + y, [ptrb] + y`, which can be compiled to indirect LDA then indirect STA!
8172
73 ### Union rule for trashes in `if`
74
75 If one branch trashes {`a`} and the other branch trashes {`b`} then the whole
76 `if` statement trashes {`a`, `b`}.
77
8278 ### And at some point...
8379
8480 * Check that the buffer being read or written to through pointer, appears in approporiate inputs or outputs set.
85 * `byte table` and `word table` of sizes other than 256
86 * always analyze before executing or compiling, unless told not to
8781 * `interrupt` routines -- to indicate that "the supervisor" has stored values on the stack, so we can trash them.
8882 * error messages that include the line number of the source code
8983 * add absolute addressing in shl/shr, absolute-indexed for add, sub, etc.
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
2726 if __name__ == '__main__':
2827 optparser = OptionParser(__doc__.strip())
2928
30 optparser.add_option("--analyze",
29 optparser.add_option("--analyze-only",
3130 action="store_true",
32 help="")
31 help="Only parse and analyze the program; do not compile it.")
3332 optparser.add_option("--basic-prelude",
3433 action="store_true",
35 help="")
36 optparser.add_option("--compile",
37 action="store_true",
38 help="")
34 help="Insert a Commodore BASIC 2.0 snippet before the program "
35 "so that it can be LOADed and RUN on Commodore platforms.")
3936 optparser.add_option("--debug",
4037 action="store_true",
41 help="")
38 help="Display debugging information when analyzing and compiling.")
39 optparser.add_option("--parse-only",
40 action="store_true",
41 help="Only parse the program; do not analyze or compile it.")
4242 optparser.add_option("--traceback",
4343 action="store_true",
44 help="")
45 optparser.add_option("--execute",
46 action="store_true",
47 help="")
44 help="When an error occurs, display a full Python traceback.")
4845
4946 (options, args) = optparser.parse_args(sys.argv[1:])
5047
5148 for filename in args:
5249 text = open(filename).read()
53 parser = Parser(text)
54 program = parser.program()
5550
56 if options.analyze:
57 try:
58 analyzer = Analyzer(debug=options.debug)
59 analyzer.analyze_program(program)
60 except Exception as e:
61 if options.traceback:
62 raise
63 else:
64 traceback.print_exception(e.__class__, e, None)
65 sys.exit(1)
51 try:
52 parser = Parser(text)
53 program = parser.program()
54 except Exception as e:
55 if options.traceback:
56 raise
57 else:
58 traceback.print_exception(e.__class__, e, None)
59 sys.exit(1)
6660
67 if options.compile:
68 fh = sys.stdout
69 start_addr = 0xc000
70 prelude = []
71 if options.basic_prelude:
72 start_addr = 0x0801
73 prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
74 0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
61 if options.parse_only:
62 sys.exit(0)
7563
76 # we are outputting a .PRG, so we output the load address first
77 # we don't use the Emitter for this b/c not part of addr space
78 if not options.debug:
79 fh.write(Word(start_addr).serialize(0))
64 try:
65 analyzer = Analyzer(debug=options.debug)
66 analyzer.analyze_program(program)
67 except Exception as e:
68 if options.traceback:
69 raise
70 else:
71 traceback.print_exception(e.__class__, e, None)
72 sys.exit(1)
8073
81 emitter = Emitter(start_addr)
82 for byte in prelude:
83 emitter.emit(Byte(byte))
84 compiler = Compiler(emitter)
85 compiler.compile_program(program)
86 if options.debug:
87 pprint(emitter.accum)
88 else:
89 emitter.serialize(fh)
74 if options.analyze_only:
75 sys.exit(0)
9076
91 if options.execute:
92 context = Evaluator().eval_program(program)
93 print str(context)
77 fh = sys.stdout
78 start_addr = 0xc000
79 prelude = []
80 if options.basic_prelude:
81 start_addr = 0x0801
82 prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
83 0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
84
85 # we are outputting a .PRG, so we output the load address first
86 # we don't use the Emitter for this b/c not part of addr space
87 if not options.debug:
88 fh.write(Word(start_addr).serialize(0))
89
90 emitter = Emitter(start_addr)
91 for byte in prelude:
92 emitter.emit(Byte(byte))
93 compiler = Compiler(emitter)
94 compiler.compile_program(program)
95 if options.debug:
96 pprint(emitter.accum)
97 else:
98 emitter.serialize(fh)
00 SixtyPical
11 ==========
22
3 This document describes the SixtyPical programming language version 0.10,
4 both its execution aspect and its static analysis aspect (even though
5 these are, technically speaking, separate concepts.)
3 This document describes the SixtyPical programming language version 0.11,
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.
1314 Types
1415 -----
1516
16 There are six *primitive types* in SixtyPical:
17 There are five *primitive types* in SixtyPical:
1718
1819 * bit (2 possible values)
1920 * byte (256 possible values)
2021 * word (65536 possible values)
2122 * routine (code stored somewhere in memory, read-only)
22 * vector (address of a routine)
2323 * pointer (address of a byte in a buffer)
2424
25 There are also two *type constructors*:
26
27 * T table (256 entries, each holding a value of type T, where T is either
28 `byte` or `word`)
25 There are also three *type constructors*:
26
27 * T table[N] (N is a power of 2, 1 ≤ N ≤ 256; each entry holds a value
28 of type T, where T is `byte`, `word`, or `vector`)
2929 * buffer[N] (N entries; each entry is a byte; N is a power of 2, ≤ 64K)
30 * vector T (address of a value of type T; T must be a routine type)
31
32 ### User-defined ###
33
34 A program may define its own types using the `typedef` feature. Typedefs
35 must occur before everything else in the program. A typedef takes a
36 type expression and an identifier which has not previously been used in
37 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.
3039
3140 Memory locations
3241 ----------------
109118 of routines which are compatible. (Meaning, the routine's inputs (resp. outputs,
110119 trashes) must be a subset of the vector's inputs (resp. outputs, trashes.))
111120
112 vector actor_logic
113 inputs a, score
114 outputs x
115 trashes y
116 @ $c000
121 vector routine
122 inputs a, score
123 outputs x
124 trashes y
125 actor_logic @ $c000
117126
118127 Note that in the code of a routine, if a memory location is named by a
119128 user-defined symbol, it is an address in memory, and can be read and written.
499508 Grammar
500509 -------
501510
502 Program ::= {Defn} {Routine}.
511 Program ::= {TypeDefn} {Defn} {Routine}.
512 TypeDefn::= "typedef" Type Ident<new>.
503513 Defn ::= Type Ident<new> [Constraints] (":" Literal | "@" LitWord).
504 Type ::= "byte" ["table"] | "vector"
514 Type ::= "(" Type ")" | TypeExpr ["table" TypeSize].
515 TypeExpr::= "byte"
516 | "word"
517 | "buffer" TypeSize
518 | "pointer"
519 | "vector" Type
520 | "routine" Constraints
521 .
522 TypeSize::= "[" LitWord "]".
505523 Constrnt::= ["inputs" LocExprs] ["outputs" LocExprs] ["trashes" LocExprs].
506 Routine ::= "routine" Ident<new> Constraints (Block | "@" LitWord).
524 Routine ::= "define" Ident<new> Type (Block | "@" LitWord).
525 | "routine" Ident<new> Constraints (Block | "@" LitWord)
526 .
507527 LocExprs::= LocExpr {"," LocExpr}.
508528 LocExpr ::= Register | Flag | Literal | Ident.
509529 Register::= "a" | "x" | "y".
22 // ****************************
33
44 // ----------------------------------------------------------------
5 // System Locations
6 // ----------------------------------------------------------------
7
8 byte vic_border @ 53280
9 byte vic_bg @ 53281
10
11 byte table screen1 @ 1024
12 byte table screen2 @ 1274
13 byte table screen3 @ 1524
14 byte table screen4 @ 1774
15
16 byte table colormap1 @ 55296
17 byte table colormap2 @ 55546
18 byte table colormap3 @ 55796
19 byte table colormap4 @ 56046
20
21 buffer[2048] screen @ 1024
22 byte joy2 @ $dc00
23
24 // ----------------------------------------------------------------
25 // Global Variables
26 // ----------------------------------------------------------------
27
28 pointer ptr @ 254
29
30 word table actor_pos
31 word pos
32 word new_pos
33
34 word table actor_delta
35 word delta
36
37 byte button_down : 0 // effectively static-local to check_button
38 byte table press_fire_msg: "PRESS`FIRE`TO`PLAY"
39
40 byte save_x
41 word compare_target
42
43 //
44 // Points to the routine that implements the current game state.
5 // Type Definitions
6 // ----------------------------------------------------------------
7
8 //
9 // Type of routines (and vectors to those routines) which are called on each frame
10 // to implement a certain state of the game (title screen, in play, game over, etc.)
11 //
12 // This type is also used as the type for the interrupt vector, even though
13 // the interrupt routine saves and restores everything before being called and
14 // thus clearly does not actually trash all the registers. It is declared this
15 // way so that the game state routines, which do trash these registers, can be
16 // assigned to it.
17 //
18 // This type is also used as the type for the location the old interrupt vector
19 // is backed up to, because all the game state routines `goto` the old handler
20 // and the end of their own routines, so the type needs to be compatible.
21 // (In a good sense, it is a continuation.)
22 //
23 // Further,
4524 //
4625 // It's very arguable that screen1/2/3/4 and colormap1/2/3/4 are not REALLY inputs.
4726 // They're only there to support the fact that game states sometimes clear the
5029 // and output. There is probably a better way to do this, but it needs thought.
5130 //
5231
53 vector dispatch_game_state
32 typedef routine
5433 inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
55 actor_pos, pos, new_pos, actor_delta, delta,
34 actor_pos, pos, new_pos, actor_delta, delta, actor_logic,
5635 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
5736 outputs button_down, dispatch_game_state,
58 actor_pos, pos, new_pos, actor_delta, delta,
37 actor_pos, pos, new_pos, actor_delta, delta, actor_logic,
5938 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
60 trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
61
62 //
63 // The constraints on these 2 vectors are kind-of sort-of big fibs.
64 // They're only written this way so they can be compatible with our
65 // routine. In fact, CINV is an interrupt routine where it doesn't
66 // really matter what you trash anyway, because all registers were
67 /// saved by the caller (the KERNAL) and will be restored by the end
68 // of the code of the saved origin cinv routine that we goto.
69 //
70 // I wonder if this could be arranged somehow to be less fibby, in
71 // a future version of SixtyPical.
72 //
73
74 vector cinv
75 inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
76 actor_pos, pos, new_pos, actor_delta, delta,
77 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
78 outputs button_down, dispatch_game_state,
79 actor_pos, pos, new_pos, actor_delta, delta,
80 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
81 trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
82 @ 788
83
84 vector save_cinv
85 inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
86 actor_pos, pos, new_pos, actor_delta, delta,
87 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
88 outputs button_down, dispatch_game_state,
89 actor_pos, pos, new_pos, actor_delta, delta,
90 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
91 trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
39 trashes a, x, y, c, z, n, v, ptr, save_x, compare_target, dispatch_logic
40 game_state_routine
41
42 //
43 // Routines that are called to get the new state of each actor (player, enemy, etc.)
44 //
45 // Routines that conform to this type also follow this convention:
46 //
47 // Set carry if the player perished. Carry clear otherwise.
48 //
49
50 typedef routine
51 inputs pos, delta, joy2, screen
52 outputs pos, delta, new_pos, screen, c
53 trashes a, x, y, z, n, v, ptr, compare_target
54 logic_routine
55
56 // ----------------------------------------------------------------
57 // System Locations
58 // ----------------------------------------------------------------
59
60 byte vic_border @ 53280
61 byte vic_bg @ 53281
62
63 byte table[256] screen1 @ 1024
64 byte table[256] screen2 @ 1274
65 byte table[256] screen3 @ 1524
66 byte table[256] screen4 @ 1774
67
68 byte table[256] colormap1 @ 55296
69 byte table[256] colormap2 @ 55546
70 byte table[256] colormap3 @ 55796
71 byte table[256] colormap4 @ 56046
72
73 buffer[2048] screen @ 1024
74 byte joy2 @ $dc00
75
76 // ----------------------------------------------------------------
77 // Global Variables
78 // ----------------------------------------------------------------
79
80 pointer ptr @ 254
81
82 word table[256] actor_pos
83 word pos
84 word new_pos
85
86 word table[256] actor_delta
87 word delta
88
89 vector (logic_routine) table[256] actor_logic
90 vector logic_routine dispatch_logic
91
92 byte button_down : 0 // effectively static-local to check_button
93 byte table[32] press_fire_msg: "PRESS`FIRE`TO`PLAY"
94
95 byte save_x
96 word compare_target
97
98 //
99 // Points to the routine that implements the current game state.
100 //
101
102 vector game_state_routine
103 dispatch_game_state
104
105 //
106 // Interrupt vector. Has same type as game states (see above.)
107 //
108
109 vector game_state_routine
110 cinv @ 788
111
112 //
113 // Location to which the old interrupt vector is saved before replacement.
114 //
115
116 vector game_state_routine
117 save_cinv
92118
93119 // ----------------------------------------------------------------
94120 // Utility Routines
215241 }
216242
217243 routine init_game
218 inputs actor_pos, actor_delta
219 outputs actor_pos, actor_delta, pos
244 inputs actor_pos, actor_delta, actor_logic
245 outputs actor_pos, actor_delta, pos, actor_logic
220246 trashes a, y, z, n, c, v
221247 {
222248 ld y, 0
224250 repeat {
225251 copy pos, actor_pos + y
226252 copy word 40, actor_delta + y
253 copy forward enemy_logic, actor_logic + y
227254
228255 st off, c
229256 add pos, word 7
235262 ld y, 0
236263 copy word 0, actor_pos + y
237264 copy word 0, actor_delta + y
265 copy forward player_logic, actor_logic + y
238266 }
239267
240268 // ----------------------------------------------------------------
241269 // Actor Logics
242270 // ----------------------------------------------------------------
243271
244 //
245 // Sets carry if the player perished. Carry clear otherwise.
246 //
247
248 routine player_logic
249 inputs pos, delta, joy2, screen
250 outputs pos, delta, new_pos, screen, c
251 trashes a, x, y, z, n, v, ptr, compare_target
272 define player_logic logic_routine
252273 {
253274 call read_stick
254275
303324 }
304325 }
305326
306 //
307 // Sets carry if the player perished. Carry clear otherwise.
308 //
309
310 routine enemy_logic
311 inputs pos, delta, screen
312 outputs pos, delta, new_pos, screen, c
313 trashes a, x, y, z, n, v, ptr, compare_target
327 define enemy_logic logic_routine
314328 {
315329 call calculate_new_position
316330 call check_new_position_in_bounds
376390 // Game States
377391 // ----------------------------------------------------------------
378392
379 //
380 // Because these all `goto save_cinv` at the end, they must have the same signature as that routine.
381 //
382
383 routine game_state_title_screen
384 inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
385 actor_pos, pos, new_pos, actor_delta, delta,
386 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
387 outputs button_down, dispatch_game_state,
388 actor_pos, pos, new_pos, actor_delta, delta,
389 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
390 trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
393 define game_state_title_screen game_state_routine
391394 {
392395 ld y, 0
393396 repeat {
425428 goto save_cinv
426429 }
427430
428 routine game_state_play
429 inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
430 actor_pos, pos, new_pos, actor_delta, delta,
431 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
432 outputs button_down, dispatch_game_state,
433 actor_pos, pos, new_pos, actor_delta, delta,
434 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
435 trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
431 define game_state_play game_state_routine
436432 {
437433 ld x, 0
438434 repeat {
441437
442438 st x, save_x
443439
444 // FIXME need VECTOR TABLEs to make this happen:
445 // copy actor_logic, x dispatch_logic
446 // call indirect_jsr_logic
447 // For now, just check the actor ID to see what type it is, and go from there.
448
449 cmp x, 0
450 if z {
451 call player_logic
452 } else {
453 call enemy_logic
454 }
440 copy actor_logic + x, dispatch_logic
441 call dispatch_logic
455442
456443 if c {
457444 // Player died! Want no dead! Break out of the loop (this is a bit awkward.)
458445 call clear_screen
459446 copy forward game_state_game_over, dispatch_game_state
460447 ld x, 15
461 st x, save_x
462 trash n
463 trash z
464 trash x
465 } else {
448 } else {
449 ld x, save_x
466450 trash c
467451 }
468
469 ld x, save_x
470452
471453 copy pos, actor_pos + x
472454 copy delta, actor_delta + x
478460 goto save_cinv
479461 }
480462
481 routine game_state_game_over
482 inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
483 actor_pos, pos, new_pos, actor_delta, delta,
484 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
485 outputs button_down, dispatch_game_state,
486 actor_pos, pos, new_pos, actor_delta, delta,
487 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
488 trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
463 define game_state_game_over game_state_routine
489464 {
490465 st off, c
491466 call check_button
515490 // * Main Game Loop Driver *
516491 // *************************
517492
518 routine our_cinv
519 inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
520 actor_pos, pos, new_pos, actor_delta, delta,
521 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
522 outputs button_down, dispatch_game_state,
523 actor_pos, pos, new_pos, actor_delta, delta,
524 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
525 trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
493 define our_cinv game_state_routine
526494 {
527495 goto dispatch_game_state
528496 }
0 vector routine
1 trashes a, z, n
2 print
3
4 vector (routine
5 trashes a, z, n)
6 table[32] vectors
7
8 routine chrout
9 inputs a
10 trashes a
11 @ 65490
12
13 routine printa
14 trashes a, z, n
15 {
16 ld a, 65
17 call chrout
18 }
19
20 routine printb
21 trashes a, z, n
22 {
23 ld a, 66
24 call chrout
25 }
26
27 routine main
28 inputs vectors
29 outputs vectors
30 trashes print, a, x, z, n, c
31 {
32 ld x, 0
33 copy printa, print
34 copy print, vectors + x
35 inc x
36 copy printa, print
37 copy print, vectors + x
38 inc x
39 copy printb, print
40 copy print, vectors + x
41 inc x
42 copy printa, print
43 copy print, vectors + x
44 inc x
45 copy printb, print
46 copy print, vectors + x
47
48 copy printa, print
49
50 ld x, 0
51 repeat {
52 copy vectors + x, print
53 call print
54 inc x
55 cmp x, 5
56 } until z
57 }
0 vector routine
1 trashes a, z, n
2 print
3
4 vector (routine
5 trashes a, z, n)
6 table[32] vectors
7
8 routine chrout
9 inputs a
10 trashes a
11 @ 65490
12
13 routine printa
14 trashes a, z, n
15 {
16 ld a, 65
17 call chrout
18 }
19
20 routine printb
21 trashes a, z, n
22 {
23 ld a, 66
24 call chrout
25 }
26
27 routine main
28 inputs vectors
29 outputs vectors
30 trashes print, a, x, z, n, c
31 {
32 ld x, 0
33 copy printa, vectors + x
34 inc x
35 copy printa, vectors + x
36 inc x
37 copy printb, vectors + x
38 inc x
39 copy printa, vectors + x
40 inc x
41 copy printb, vectors + x
42
43 ld x, 0
44 repeat {
45 copy vectors + x, print
46 call print
47 inc x
48 cmp x, 5
49 } until z
50 }
0 vector print
0 vector routine
11 trashes a, z, n
2 print
23
34 routine chrout
45 inputs a
2021 }
2122
2223 routine main
23 inputs printa, printb
2424 trashes print, a, z, n
2525 {
2626 copy printa, print
55 exit 1
66 fi
77 OUT=/tmp/a-out.prg
8 bin/sixtypical --traceback --analyze --compile --basic-prelude $SRC > $OUT || exit 1
8 bin/sixtypical --traceback --basic-prelude $SRC > $OUT || exit 1
99 if [ -e vicerc ]; then
1010 x64 -config vicerc $OUT
1111 else
11
22 from sixtypical.ast import Program, Routine, Block, Instr
33 from sixtypical.model import (
4 TYPE_BYTE, TYPE_WORD, TYPE_BYTE_TABLE, TYPE_WORD_TABLE, BufferType, PointerType, VectorType, ExecutableType,
4 TYPE_BYTE, TYPE_WORD,
5 TableType, BufferType, PointerType, VectorType, RoutineType,
56 ConstantRef, LocationRef, IndirectRef, IndexedRef, AddressRef,
67 REG_A, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
78 )
174175 (location.name, self.current_routine.name)
175176 )
176177
177 def assert_affected_within(self, name, affected, limited_to):
178 def assert_affected_within(self, name, affecting_type, limiting_type):
179 assert name in ('inputs', 'outputs', 'trashes')
180 affected = getattr(affecting_type, name)
181 limited_to = getattr(limiting_type, name)
178182 overage = affected - limited_to
179183 if not overage:
180184 return
181 message = 'in %s: %s are %s but affects %s which exceeds it by: %s ' % (
185 message = 'in %s: %s for %s are %s\n\nbut %s affects %s\n\nwhich exceeds it by: %s ' % (
182186 self.current_routine.name, name,
183 LocationRef.format_set(limited_to), LocationRef.format_set(affected), LocationRef.format_set(overage)
187 limiting_type, LocationRef.format_set(limited_to),
188 affecting_type, LocationRef.format_set(affected),
189 LocationRef.format_set(overage)
184190 )
185191 raise IncompatibleConstraintsError(message)
186192
230236
231237 if opcode == 'ld':
232238 if instr.index:
233 if src.type == TYPE_BYTE_TABLE and dest.type == TYPE_BYTE:
239 if TableType.is_a_table_type(src.type, TYPE_BYTE) and dest.type == TYPE_BYTE:
234240 pass
235241 else:
236242 raise TypeMismatchError('%s and %s in %s' %
237243 (src.name, dest.name, self.current_routine.name)
238244 )
245 context.assert_meaningful(instr.index)
239246 elif src.type != dest.type:
240247 raise TypeMismatchError('%s and %s in %s' %
241248 (src.name, dest.name, self.current_routine.name)
244251 context.set_written(dest, FLAG_Z, FLAG_N)
245252 elif opcode == 'st':
246253 if instr.index:
247 if src.type == TYPE_BYTE and dest.type == TYPE_BYTE_TABLE:
248 pass
249 else:
250 raise TypeMismatchError((src, dest))
254 if src.type == TYPE_BYTE and TableType.is_a_table_type(dest.type, TYPE_BYTE):
255 pass
256 else:
257 raise TypeMismatchError((src, dest))
258 context.assert_meaningful(instr.index)
251259 elif src.type != dest.type:
252 raise TypeMismatchError('%s and %s in %s' %
253 (src.name, dest.name, self.current_routine.name)
260 raise TypeMismatchError('%r and %r in %s' %
261 (src, dest, self.current_routine.name)
254262 )
255263 context.assert_meaningful(src)
256264 context.set_written(dest)
299307 context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C)
300308 elif opcode == 'call':
301309 type = instr.location.type
310 if isinstance(type, VectorType):
311 type = type.of_type
302312 for ref in type.inputs:
303313 context.assert_meaningful(ref)
304314 for ref in type.outputs:
360370 raise TypeMismatchError((src, dest))
361371
362372 elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndexedRef):
363 if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
373 if src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
374 pass
375 elif (isinstance(src.type, VectorType) and isinstance(dest.ref.type, TableType) and
376 RoutineType.executable_types_compatible(src.type.of_type, dest.ref.type.of_type)):
377 pass
378 elif (isinstance(src.type, RoutineType) and isinstance(dest.ref.type, TableType) and
379 RoutineType.executable_types_compatible(src.type, dest.ref.type.of_type)):
364380 pass
365381 else:
366382 raise TypeMismatchError((src, dest))
367383
368384 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
369 if src.ref.type == TYPE_WORD_TABLE and dest.type == TYPE_WORD:
385 if TableType.is_a_table_type(src.ref.type, TYPE_WORD) and dest.type == TYPE_WORD:
386 pass
387 elif (isinstance(src.ref.type, TableType) and isinstance(dest.type, VectorType) and
388 RoutineType.executable_types_compatible(src.ref.type.of_type, dest.type.of_type)):
370389 pass
371390 else:
372391 raise TypeMismatchError((src, dest))
374393 elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef):
375394 if src.type == dest.type:
376395 pass
377 elif isinstance(src.type, ExecutableType) and isinstance(dest.type, VectorType):
378 self.assert_affected_within('inputs', src.type.inputs, dest.type.inputs)
379 self.assert_affected_within('outputs', src.type.outputs, dest.type.outputs)
380 self.assert_affected_within('trashes', src.type.trashes, dest.type.trashes)
396 elif isinstance(src.type, RoutineType) and isinstance(dest.type, VectorType):
397 self.assert_affected_within('inputs', src.type, dest.type.of_type)
398 self.assert_affected_within('outputs', src.type, dest.type.of_type)
399 self.assert_affected_within('trashes', src.type, dest.type.of_type)
381400 else:
382401 raise TypeMismatchError((src, dest))
383402 else:
392411 elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef):
393412 context.assert_meaningful(src.ref, REG_Y)
394413 # TODO this will need to be more sophisticated. the thing ref points to is touched, as well.
395 context.set_touched(src.ref) # TODO and REG_Y? if not, why not?
396 context.set_touched(dest)
397414 context.set_written(dest)
398415 elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
399416 context.assert_meaningful(src, dest.ref, dest.index)
400 context.set_touched(src) # TODO and dest.index?
401417 context.set_written(dest.ref)
402418 elif isinstance(src, ConstantRef) and isinstance(dest, IndexedRef):
403419 context.assert_meaningful(src, dest.ref, dest.index)
404420 context.set_written(dest.ref)
405421 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
406 context.assert_meaningful(src.ref, src.index, dest)
407 context.set_touched(dest) # TODO and src.index?
422 context.assert_meaningful(src.ref, src.index)
423 context.set_touched(dest)
408424 context.set_written(dest)
409425 else:
410426 context.assert_meaningful(src)
427443 location = instr.location
428444 type_ = location.type
429445
430 if not isinstance(type_, ExecutableType):
446 if not isinstance(type_, (RoutineType, VectorType)):
431447 raise TypeMismatchError(location)
432448
433449 # assert that the dest routine's inputs are all initialized
450 if isinstance(type_, VectorType):
451 type_ = type_.of_type
434452 for ref in type_.inputs:
435453 context.assert_meaningful(ref)
436454
437455 # and that this routine's trashes and output constraints are a
438456 # superset of the called routine's
439457 current_type = self.current_routine.location.type
440 self.assert_affected_within('outputs', type_.outputs, current_type.outputs)
441 self.assert_affected_within('trashes', type_.trashes, current_type.trashes)
458 self.assert_affected_within('outputs', type_, current_type)
459 self.assert_affected_within('trashes', type_, current_type)
442460
443461 self.has_encountered_goto = True
444462 elif opcode == 'trash':
22 from sixtypical.ast import Program, Routine, Block, Instr
33 from sixtypical.model import (
44 ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef,
5 TYPE_BIT, TYPE_BYTE, TYPE_BYTE_TABLE, TYPE_WORD, TYPE_WORD_TABLE, BufferType, PointerType, RoutineType, VectorType,
5 TYPE_BIT, TYPE_BYTE, TYPE_WORD,
6 TableType, BufferType, PointerType, RoutineType, VectorType,
67 REG_A, REG_X, REG_Y, FLAG_C
78 )
89 from sixtypical.emitter import Byte, Word, Table, Label, Offset, LowAddressByte, HighAddressByte
5354 length = 1
5455 elif type_ == TYPE_WORD or isinstance(type_, (PointerType, VectorType)):
5556 length = 2
56 elif type_ == TYPE_BYTE_TABLE:
57 length = 256
58 elif type_ == TYPE_WORD_TABLE:
59 length = 512
57 elif isinstance(type_, TableType):
58 length = type_.size * (1 if type_.of_type == TYPE_BYTE else 2)
6059 elif isinstance(type_, BufferType):
6160 length = type_.size
6261 if length is None:
7877 for location, label in self.trampolines.iteritems():
7978 self.emitter.resolve_label(label)
8079 self.emitter.emit(JMP(Indirect(self.labels[location.name])))
80 self.emitter.emit(RTS())
8181
8282 # initialized data
8383 for defn in program.defns:
8989 initial_data = Byte(defn.initial)
9090 elif type_ == TYPE_WORD:
9191 initial_data = Word(defn.initial)
92 elif type_ == TYPE_BYTE_TABLE:
93 initial_data = Table(defn.initial)
92 elif TableType.is_a_table_type(type_, TYPE_BYTE):
93 initial_data = Table(defn.initial, type_.size)
9494 else:
9595 raise NotImplementedError(type_)
9696 label.set_length(initial_data.size())
403403 self.emitter.emit(LDA(Immediate(LowAddressByte(src_label))))
404404 self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1))))
405405 elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
406 if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
406 if src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
407407 src_label = self.labels[src.name]
408408 dest_label = self.labels[dest.ref.name]
409409 self.emitter.emit(LDA(Absolute(src_label)))
410410 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label)))
411411 self.emitter.emit(LDA(Absolute(Offset(src_label, 1))))
412412 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256))))
413 elif isinstance(src.type, VectorType) and isinstance(dest.ref.type, TableType) and isinstance(dest.ref.type.of_type, VectorType):
414 # FIXME this is the exact same as above - can this be simplified?
415 src_label = self.labels[src.name]
416 dest_label = self.labels[dest.ref.name]
417 self.emitter.emit(LDA(Absolute(src_label)))
418 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label)))
419 self.emitter.emit(LDA(Absolute(Offset(src_label, 1))))
420 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256))))
421 elif isinstance(src.type, RoutineType) and isinstance(dest.ref.type, TableType) and isinstance(dest.ref.type.of_type, VectorType):
422 src_label = self.labels[src.name]
423 dest_label = self.labels[dest.ref.name]
424 self.emitter.emit(LDA(Immediate(HighAddressByte(src_label))))
425 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label)))
426 self.emitter.emit(LDA(Immediate(LowAddressByte(src_label))))
427 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256))))
413428 else:
414429 raise NotImplementedError
415430 elif isinstance(src, ConstantRef) and isinstance(dest, IndexedRef):
416 if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
431 if src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
417432 dest_label = self.labels[dest.ref.name]
418433 self.emitter.emit(LDA(Immediate(Byte(src.low_byte()))))
419434 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label)))
422437 else:
423438 raise NotImplementedError
424439 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
425 if src.ref.type == TYPE_WORD_TABLE and dest.type == TYPE_WORD:
440 if TableType.is_a_table_type(src.ref.type, TYPE_WORD) and dest.type == TYPE_WORD:
441 src_label = self.labels[src.ref.name]
442 dest_label = self.labels[dest.name]
443 self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(src_label)))
444 self.emitter.emit(STA(Absolute(dest_label)))
445 self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(Offset(src_label, 256))))
446 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
447 elif isinstance(dest.type, VectorType) and isinstance(src.ref.type, TableType) and isinstance(src.ref.type.of_type, VectorType):
448 # FIXME this is the exact same as above - can this be simplified?
426449 src_label = self.labels[src.ref.name]
427450 dest_label = self.labels[dest.name]
428451 self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(src_label)))
4747
4848
4949 class Table(Emittable):
50 def __init__(self, value):
50 def __init__(self, value, size):
5151 # TODO: range-checking
5252 self.value = value
53 self._size = size
5354
5455 def size(self):
55 return 256
56 return self._size
5657
5758 def serialize(self, addr=None):
5859 bytes = []
+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
1616 def __hash__(self):
1717 return hash(self.name)
1818
19 def backpatch_constraint_labels(self, resolver):
20 def resolve(w):
21 if not isinstance(w, basestring):
22 return w
23 return resolver(w)
24 if isinstance(self, TableType):
25 self.of_type.backpatch_constraint_labels(resolver)
26 elif isinstance(self, VectorType):
27 self.of_type.backpatch_constraint_labels(resolver)
28 elif isinstance(self, RoutineType):
29 self.inputs = set([resolve(w) for w in self.inputs])
30 self.outputs = set([resolve(w) for w in self.outputs])
31 self.trashes = set([resolve(w) for w in self.trashes])
32
1933
2034 TYPE_BIT = Type('bit')
2135 TYPE_BYTE = Type('byte')
22 TYPE_BYTE_TABLE = Type('byte table')
2336 TYPE_WORD = Type('word')
24 TYPE_WORD_TABLE = Type('word table')
25
26
27 class ExecutableType(Type):
28 """Used for routines and vectors."""
29 def __init__(self, name, inputs=None, outputs=None, trashes=None):
30 self.name = name
37
38
39
40 class RoutineType(Type):
41 """This memory location contains the code for a routine."""
42 def __init__(self, inputs=None, outputs=None, trashes=None):
43 self.name = 'routine'
3144 self.inputs = inputs or set()
3245 self.outputs = outputs or set()
3346 self.trashes = trashes or set()
3447
3548 def __repr__(self):
36 return 'RoutineType(%r, inputs=%r, outputs=%r, trashes=%r)' % (
37 self.name, self.inputs, self.outputs, self.trashes
49 return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % (
50 self.__class__.__name__, self.name, self.inputs, self.outputs, self.trashes
3851 )
3952
4053 def __eq__(self, other):
4861 def __hash__(self):
4962 return hash(self.name) ^ hash(self.inputs) ^ hash(self.outputs) ^ hash(self.trashes)
5063
51
52 class RoutineType(ExecutableType):
53 """This memory location contains the code for a routine."""
54 def __init__(self, **kwargs):
55 super(RoutineType, self).__init__('routine', **kwargs)
56
57
58 class VectorType(ExecutableType):
59 """This memory location contains the address of a routine."""
60 def __init__(self, **kwargs):
61 super(VectorType, self).__init__('vector', **kwargs)
64 @classmethod
65 def executable_types_compatible(cls_, src, dest):
66 """Returns True iff a value of type `src` can be assigned to a storage location of type `dest`."""
67 if isinstance(src, VectorType):
68 src = src.of_type
69 if isinstance(dest, VectorType):
70 dest = dest.of_type
71 if isinstance(src, RoutineType) and isinstance(dest, RoutineType):
72 # TODO: I'm sure we can replace some of these with subset-containment, but that requires thought
73 return (
74 src.inputs == dest.inputs and
75 src.outputs == dest.outputs and
76 src.trashes == dest.trashes
77 )
78 else:
79 return False
80
81
82 class VectorType(Type):
83 """This memory location contains the address of some other type (currently, only RoutineType)."""
84 def __init__(self, of_type):
85 self.name = 'vector'
86 self.of_type = of_type
87
88 def __repr__(self):
89 return '%s(%r)' % (
90 self.__class__.__name__, self.of_type
91 )
92
93 def __eq__(self, other):
94 return self.name == other.name and self.of_type == other.of_type
95
96 def __hash__(self):
97 return hash(self.name) ^ hash(self.of_type)
98
99
100 class TableType(Type):
101 def __init__(self, of_type, size):
102 self.of_type = of_type
103 self.size = size
104 self.name = '{} table[{}]'.format(self.of_type.name, self.size)
105
106 def __repr__(self):
107 return '%s(%r, %r)' % (
108 self.__class__.__name__, self.of_type, self.size
109 )
110
111 @classmethod
112 def is_a_table_type(cls_, x, of_type):
113 return isinstance(x, TableType) and x.of_type == of_type
62114
63115
64116 class BufferType(Type):
107159 def is_constant(self):
108160 return isinstance(self.type, RoutineType)
109161
110 def backpatch_vector_labels(self, resolver):
111 if isinstance(self.type, ExecutableType):
112 t = self.type
113 t.inputs = set([resolver(w) for w in t.inputs])
114 t.outputs = set([resolver(w) for w in t.outputs])
115 t.trashes = set([resolver(w) for w in t.trashes])
116
117162 @classmethod
118163 def format_set(cls, location_refs):
119164 return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs)])
11
22 from sixtypical.ast import Program, Defn, Routine, Block, Instr
33 from sixtypical.model import (
4 TYPE_BIT, TYPE_BYTE, TYPE_BYTE_TABLE, TYPE_WORD, TYPE_WORD_TABLE,
5 RoutineType, VectorType, ExecutableType, BufferType, PointerType,
4 TYPE_BIT, TYPE_BYTE, TYPE_WORD,
5 RoutineType, VectorType, TableType, BufferType, PointerType,
66 LocationRef, ConstantRef, IndirectRef, IndexedRef, AddressRef,
77 )
88 from sixtypical.scanner import Scanner
1818 def __init__(self, text):
1919 self.scanner = Scanner(text)
2020 self.symbols = {} # token -> SymEntry
21 self.typedefs = {} # token -> Type AST
2122 for token in ('a', 'x', 'y'):
2223 self.symbols[token] = SymEntry(None, LocationRef(TYPE_BYTE, token))
2324 for token in ('c', 'z', 'n', 'v'):
3435 def program(self):
3536 defns = []
3637 routines = []
37 while self.scanner.on('byte', 'word', 'vector', 'buffer', 'pointer'):
38 while self.scanner.on('typedef'):
39 typedef = self.typedef()
40 typenames = ['byte', 'word', 'table', 'vector', 'buffer', 'pointer'] # 'routine',
41 typenames.extend(self.typedefs.keys())
42 while self.scanner.on(*typenames):
3843 defn = self.defn()
3944 name = defn.name
4045 if name in self.symbols:
4146 raise SyntaxError('Symbol "%s" already declared' % name)
4247 self.symbols[name] = SymEntry(defn, defn.location)
4348 defns.append(defn)
44 while self.scanner.on('routine'):
45 routine = self.routine()
46 name = routine.name
49 while self.scanner.on('define', 'routine'):
50 if self.scanner.consume('define'):
51 name = self.scanner.token
52 self.scanner.scan()
53 routine = self.routine(name)
54 else:
55 routine = self.legacy_routine()
56 name = routine.name
4757 if name in self.symbols:
4858 raise SyntaxError('Symbol "%s" already declared' % name)
4959 self.symbols[name] = SymEntry(routine, routine.location)
5161 self.scanner.check_type('EOF')
5262
5363 # now backpatch the executable types.
64 #for type_name, type_ in self.typedefs.iteritems():
65 # type_.backpatch_constraint_labels(lambda w: self.lookup(w))
5466 for defn in defns:
55 defn.location.backpatch_vector_labels(lambda w: self.lookup(w))
67 defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
5668 for routine in routines:
57 routine.location.backpatch_vector_labels(lambda w: self.lookup(w))
69 routine.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
5870 for instr in self.backpatch_instrs:
5971 if instr.opcode in ('call', 'goto'):
6072 name = instr.location
6173 if name not in self.symbols:
6274 raise SyntaxError('Undefined routine "%s"' % name)
63 if not isinstance(self.symbols[name].model.type, ExecutableType):
75 if not isinstance(self.symbols[name].model.type, (RoutineType, VectorType)):
6476 raise SyntaxError('Illegal call of non-executable "%s"' % name)
6577 instr.location = self.symbols[name].model
6678 if instr.opcode in ('copy',) and isinstance(instr.src, basestring):
6779 name = instr.src
6880 if name not in self.symbols:
6981 raise SyntaxError('Undefined routine "%s"' % name)
70 if not isinstance(self.symbols[name].model.type, ExecutableType):
82 if not isinstance(self.symbols[name].model.type, (RoutineType, VectorType)):
7183 raise SyntaxError('Illegal copy of non-executable "%s"' % name)
7284 instr.src = self.symbols[name].model
7385
7486 return Program(defns=defns, routines=routines)
7587
88 def typedef(self):
89 self.scanner.expect('typedef')
90 type_ = self.defn_type()
91 name = self.defn_name()
92 if name in self.typedefs:
93 raise SyntaxError('Type "%s" already declared' % name)
94 self.typedefs[name] = type_
95 return type_
96
7697 def defn(self):
7798 type_ = self.defn_type()
78
79 self.scanner.check_type('identifier')
80 name = self.scanner.token
81 self.scanner.scan()
82
83 (inputs, outputs, trashes) = self.constraints()
84 if type_ == 'vector':
85 type_ = VectorType(inputs=inputs, outputs=outputs, trashes=trashes)
86 elif inputs or outputs or trashes:
87 raise SyntaxError("Cannot apply constraints to non-vector type")
99 name = self.defn_name()
88100
89101 initial = None
90102 if self.scanner.consume(':'):
91 if type_ == TYPE_BYTE_TABLE and self.scanner.on_type('string literal'):
103 if isinstance(type_, TableType) and self.scanner.on_type('string literal'):
92104 initial = self.scanner.token
93105 else:
94106 self.scanner.check_type('integer literal')
108120
109121 return Defn(name=name, addr=addr, initial=initial, location=location)
110122
123 def defn_size(self):
124 self.scanner.expect('[')
125 self.scanner.check_type('integer literal')
126 size = int(self.scanner.token)
127 self.scanner.scan()
128 self.scanner.expect(']')
129 return size
130
111131 def defn_type(self):
132 type_ = None
133
134 if self.scanner.consume('('):
135 type_ = self.defn_type()
136 self.scanner.expect(')')
137 return type_
138
112139 if self.scanner.consume('byte'):
113 if self.scanner.consume('table'):
114 return TYPE_BYTE_TABLE
115 return TYPE_BYTE
140 type_ = TYPE_BYTE
116141 elif self.scanner.consume('word'):
117 if self.scanner.consume('table'):
118 return TYPE_WORD_TABLE
119 return TYPE_WORD
142 type_ = TYPE_WORD
120143 elif self.scanner.consume('vector'):
121 return 'vector' # will be resolved to a Type by caller
144 type_ = self.defn_type()
145 if not isinstance(type_, RoutineType):
146 raise SyntaxError("Vectors can only be of a routine, not %r" % type_)
147 type_ = VectorType(type_)
148 elif self.scanner.consume('routine'):
149 (inputs, outputs, trashes) = self.constraints()
150 type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
122151 elif self.scanner.consume('buffer'):
123 self.scanner.expect('[')
124 self.scanner.check_type('integer literal')
125 size = int(self.scanner.token)
126 self.scanner.scan()
127 self.scanner.expect(']')
128 return BufferType(size)
129 else:
130 self.scanner.expect('pointer')
131 return PointerType()
152 size = self.defn_size()
153 type_ = BufferType(size)
154 elif self.scanner.consume('pointer'):
155 type_ = PointerType()
156 else:
157 type_name = self.scanner.token
158 self.scanner.scan()
159 if type_name not in self.typedefs:
160 raise SyntaxError("Undefined type '%s'" % type_name)
161 type_ = self.typedefs[type_name]
162
163 if self.scanner.consume('table'):
164 size = self.defn_size()
165 type_ = TableType(type_, size)
166
167 return type_
168
169 def defn_name(self):
170 self.scanner.check_type('identifier')
171 name = self.scanner.token
172 self.scanner.scan()
173 return name
132174
133175 def constraints(self):
134176 inputs = set()
142184 trashes = set(self.labels())
143185 return (inputs, outputs, trashes)
144186
145 def routine(self):
187 def legacy_routine(self):
146188 self.scanner.expect('routine')
147189 name = self.scanner.token
148190 self.scanner.scan()
149191 (inputs, outputs, trashes) = self.constraints()
192 type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
150193 if self.scanner.consume('@'):
151194 self.scanner.check_type('integer literal')
152195 block = None
155198 else:
156199 block = self.block()
157200 addr = None
158 location = LocationRef(
159 RoutineType(inputs=inputs, outputs=outputs, trashes=trashes),
160 name
201 location = LocationRef(type_, name)
202 return Routine(
203 name=name, block=block, addr=addr,
204 location=location
161205 )
206
207 def routine(self, name):
208 type_ = self.defn_type()
209 if not isinstance(type_, RoutineType):
210 raise SyntaxError("Can only define a routine, not %r" % type_)
211 if self.scanner.consume('@'):
212 self.scanner.check_type('integer literal')
213 block = None
214 addr = int(self.scanner.token)
215 self.scanner.scan()
216 else:
217 block = self.block()
218 addr = None
219 location = LocationRef(type_, name)
162220 return Routine(
163221 name=name, block=block, addr=addr,
164222 location=location
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
66 [Falderal]: http://catseye.tc/node/Falderal
77
88 -> Functionality "Analyze SixtyPical program" is implemented by
9 -> shell command "bin/sixtypical --analyze --traceback %(test-body-file) && echo ok"
9 -> shell command "bin/sixtypical --analyze-only --traceback %(test-body-file) && echo ok"
1010
1111 -> Tests for functionality "Analyze SixtyPical program"
1212
235235 | ld a, 0
236236 | st a, foo
237237 | }
238 ? TypeMismatchError: a and foo in main
238 ? TypeMismatchError
239239
240240 ### tables ###
241241
242 Storing to a table, you must use an index, and vice-versa.
242 Storing to a table, you must use an index.
243243
244244 | byte one
245 | byte table many
245 | byte table[256] many
246246 |
247247 | routine main
248248 | outputs one
255255 = ok
256256
257257 | byte one
258 | byte table many
258 | byte table[256] many
259259 |
260260 | routine main
261261 | outputs many
268268 ? TypeMismatchError
269269
270270 | byte one
271 | byte table many
271 | byte table[256] many
272272 |
273273 | routine main
274274 | outputs one
281281 ? TypeMismatchError
282282
283283 | byte one
284 | byte table many
284 | byte table[256] many
285285 |
286286 | routine main
287287 | outputs many
293293 | }
294294 = ok
295295
296 Reading from a table, you must use an index, and vice-versa.
296 The index must be initialized.
297
298 | byte one
299 | byte table[256] many
300 |
301 | routine main
302 | outputs many
303 | trashes a, x, n, z
304 | {
305 | ld a, 0
306 | st a, many + x
307 | }
308 ? UnmeaningfulReadError: x
309
310 Reading from a table, you must use an index.
297311
298312 | byte one
299313 |
319333 | }
320334 ? TypeMismatchError
321335
322 | byte table many
336 | byte table[256] many
323337 |
324338 | routine main
325339 | outputs many
332346 | }
333347 ? TypeMismatchError
334348
335 | byte table many
349 | byte table[256] many
336350 |
337351 | routine main
338352 | outputs many
345359 | }
346360 = ok
347361
362 | byte table[256] many
363 |
364 | routine main
365 | inputs many
366 | outputs many
367 | trashes a, x, n, z
368 | {
369 | ld x, 0
370 | ld a, many + x
371 | }
372 = ok
373
374 The index must be initialized.
375
376 | byte table[256] many
377 |
378 | routine main
379 | inputs many
380 | outputs many
381 | trashes a, x, n, z
382 | {
383 | ld a, many + x
384 | }
385 ? UnmeaningfulReadError: x
386
348387 Copying to and from a word table.
349388
350389 | word one
351 | word table many
390 | word table[256] many
352391 |
353392 | routine main
354393 | inputs one, many
362401 = ok
363402
364403 | word one
365 | word table many
404 | word table[256] many
366405 |
367406 | routine main
368407 | inputs one, many
375414 ? TypeMismatchError
376415
377416 | word one
378 | word table many
417 | word table[256] many
379418 |
380419 | routine main
381420 | inputs one, many
389428
390429 You can also copy a literal word to a word table.
391430
392 | word table many
431 | word table[256] many
393432 |
394433 | routine main
395434 | inputs many
15531592 Routines are constants. You need not, and in fact cannot, specify a constant
15541593 as an input to, an output of, or as a trashed value of a routine.
15551594
1556 | vector vec
1557 | inputs x
1558 | outputs x
1559 | trashes z, n
1595 | vector routine
1596 | inputs x
1597 | outputs x
1598 | trashes z, n
1599 | vec
15601600 |
15611601 | routine foo
15621602 | inputs x
15751615 | }
15761616 ? ConstantConstraintError: foo in main
15771617
1578 | vector vec
1579 | inputs x
1580 | outputs x
1581 | trashes z, n
1618 | vector routine
1619 | inputs x
1620 | outputs x
1621 | trashes z, n
1622 | vec
15821623 |
15831624 | routine foo
15841625 | inputs x
15961637 | }
15971638 ? ConstantConstraintError: foo in main
15981639
1599 | vector vec
1600 | inputs x
1601 | outputs x
1602 | trashes z, n
1640 | vector routine
1641 | inputs x
1642 | outputs x
1643 | trashes z, n
1644 | vec
16031645 |
16041646 | routine foo
16051647 | inputs x
16201662 You can copy the address of a routine into a vector, if that vector is
16211663 declared appropriately.
16221664
1623 | vector vec
1624 | inputs x
1625 | outputs x
1626 | trashes z, n
1665 | vector routine
1666 | inputs x
1667 | outputs x
1668 | trashes z, n
1669 | vec
16271670 |
16281671 | routine foo
16291672 | inputs x
16431686
16441687 But not if the vector is declared inappropriately.
16451688
1646 | vector vec
1689 | vector routine
16471690 | inputs y
16481691 | outputs y
16491692 | trashes z, n
1693 | vec
16501694 |
16511695 | routine foo
16521696 | inputs x
16671711 "Appropriately" means, if the routine affects no more than what is named
16681712 in the input/output sets of the vector.
16691713
1670 | vector vec
1714 | vector routine
16711715 | inputs a, x
16721716 | outputs x
16731717 | trashes a, z, n
1718 | vec
16741719 |
16751720 | routine foo
16761721 | inputs x
16901735
16911736 Routines are read-only.
16921737
1693 | vector vec
1694 | inputs x
1695 | outputs x
1696 | trashes z, n
1738 | vector routine
1739 | inputs x
1740 | outputs x
1741 | trashes z, n
1742 | vec
16971743 |
16981744 | routine foo
16991745 | inputs x
17131759
17141760 Indirect call.
17151761
1716 | vector foo outputs x trashes z, n
1762 | vector routine
1763 | outputs x trashes z, n
1764 | foo
17171765 |
17181766 | routine bar outputs x trashes z, n {
17191767 | ld x, 200
17271775
17281776 Calling the vector does indeed trash the things the vector says it does.
17291777
1730 | vector foo trashes x, z, n
1778 | vector routine trashes x, z, n foo
17311779 |
17321780 | routine bar trashes x, z, n {
17331781 | ld x, 200
18271875
18281876 Indirect goto.
18291877
1830 | vector foo outputs x trashes a, z, n
1878 | vector routine outputs x trashes a, z, n foo
18311879 |
18321880 | routine bar outputs x trashes a, z, n {
18331881 | ld x, 200
18421890 Jumping through the vector does indeed trash, or output, the things the
18431891 vector says it does.
18441892
1845 | vector foo
1893 | vector routine
18461894 | trashes a, x, z, n
1895 | foo
18471896 |
18481897 | routine bar
18491898 | trashes a, x, z, n {
18651914 | }
18661915 ? UnmeaningfulReadError: x in main
18671916
1868 | vector foo
1869 | outputs x
1870 | trashes a, z, n
1917 | vector routine
1918 | outputs x
1919 | trashes a, z, n foo
18711920 |
18721921 | routine bar
18731922 | outputs x
18901939 | ld a, x
18911940 | }
18921941 = ok
1942
1943 ### vector tables ###
1944
1945 A vector can be copied into a vector table.
1946
1947 | vector routine
1948 | outputs x
1949 | trashes a, z, n
1950 | one
1951 | vector (routine
1952 | outputs x
1953 | trashes a, z, n)
1954 | table[256] many
1955 |
1956 | routine bar outputs x trashes a, z, n {
1957 | ld x, 200
1958 | }
1959 |
1960 | routine main
1961 | inputs one, many
1962 | outputs one, many
1963 | trashes a, x, n, z
1964 | {
1965 | ld x, 0
1966 | copy bar, one
1967 | copy one, many + x
1968 | }
1969 = ok
1970
1971 A vector can be copied out of a vector table.
1972
1973 | vector routine
1974 | outputs x
1975 | trashes a, z, n
1976 | one
1977 | vector (routine
1978 | outputs x
1979 | trashes a, z, n)
1980 | table[256] many
1981 |
1982 | routine bar outputs x trashes a, z, n {
1983 | ld x, 200
1984 | }
1985 |
1986 | routine main
1987 | inputs one, many
1988 | outputs one, many
1989 | trashes a, x, n, z
1990 | {
1991 | ld x, 0
1992 | copy many + x, one
1993 | call one
1994 | }
1995 = ok
1996
1997 A routine can be copied into a vector table.
1998
1999 | vector (routine
2000 | outputs x
2001 | trashes a, z, n)
2002 | table[256] many
2003 |
2004 | routine bar outputs x trashes a, z, n {
2005 | ld x, 200
2006 | }
2007 |
2008 | routine main
2009 | inputs many
2010 | outputs many
2011 | trashes a, x, n, z
2012 | {
2013 | ld x, 0
2014 | copy bar, many + x
2015 | }
2016 = ok
2017
2018 A vector in a vector table cannot be directly called.
2019
2020 | vector (routine
2021 | outputs x
2022 | trashes a, z, n)
2023 | table[256] many
2024 |
2025 | routine bar outputs x trashes a, z, n {
2026 | ld x, 200
2027 | }
2028 |
2029 | routine main
2030 | inputs many
2031 | outputs many
2032 | trashes a, x, n, z
2033 | {
2034 | ld x, 0
2035 | copy bar, many + x
2036 | call many + x
2037 | }
2038 ? ValueError
2039
2040 ### typedef ###
2041
2042 A typedef is a more-readable alias for a type. "Alias" means
2043 that types have structural equivalence, not name equivalence.
2044
2045 | typedef routine
2046 | inputs x
2047 | outputs x
2048 | trashes z, n
2049 | routine_type
2050 |
2051 | vector routine_type vec
2052 |
2053 | routine foo
2054 | inputs x
2055 | outputs x
2056 | trashes z, n
2057 | {
2058 | inc x
2059 | }
2060 |
2061 | routine main
2062 | outputs vec
2063 | trashes a, z, n
2064 | {
2065 | copy foo, vec
2066 | }
2067 = ok
2068
2069 The new style routine definitions support typedefs.
2070
2071 | typedef routine
2072 | inputs x
2073 | outputs x
2074 | trashes z, n
2075 | routine_type
2076 |
2077 | vector routine_type vec
2078 |
2079 | define foo routine_type
2080 | {
2081 | inc x
2082 | }
2083 |
2084 | routine main
2085 | outputs vec
2086 | trashes a, z, n
2087 | {
2088 | copy foo, vec
2089 | }
2090 = ok
66 [Falderal]: http://catseye.tc/node/Falderal
77
88 -> Functionality "Compile SixtyPical program" is implemented by
9 -> shell command "bin/sixtypical --basic-prelude --compile %(test-body-file) | tests/appliances/bin/dcc6502-adapter"
9 -> shell command "bin/sixtypical --basic-prelude --traceback %(test-body-file) >/tmp/foo && tests/appliances/bin/dcc6502-adapter </tmp/foo"
1010
1111 -> Tests for functionality "Compile SixtyPical program"
1212
136136 = $081A .byte $BB
137137 = $081B .byte $0B
138138
139 Initialized byte table.
140
141 | byte table message : "WHAT?"
139 Initialized byte table. Bytes allocated, but beyond the string, are 0's.
140
141 | byte table[8] message : "WHAT?"
142142 |
143143 | routine main
144144 | inputs message
157157 = $0818 BRK
158158 = $0819 BRK
159159 = $081A BRK
160 = $081B BRK
161 = $081C BRK
162 = $081D BRK
163 = $081E BRK
164 = $081F BRK
165 = $0820 BRK
166 = $0821 BRK
167 = $0822 BRK
168 = $0823 BRK
169 = $0824 BRK
170 = $0825 BRK
171 = $0826 BRK
172 = $0827 BRK
173 = $0828 BRK
174 = $0829 BRK
175 = $082A BRK
176 = $082B BRK
177 = $082C BRK
178 = $082D BRK
179 = $082E BRK
180 = $082F BRK
181 = $0830 BRK
182 = $0831 BRK
183 = $0832 BRK
184 = $0833 BRK
185 = $0834 BRK
186 = $0835 BRK
187 = $0836 BRK
188 = $0837 BRK
189 = $0838 BRK
190 = $0839 BRK
191 = $083A BRK
192 = $083B BRK
193 = $083C BRK
194 = $083D BRK
195 = $083E BRK
196 = $083F BRK
197 = $0840 BRK
198 = $0841 BRK
199 = $0842 BRK
200 = $0843 BRK
201 = $0844 BRK
202 = $0845 BRK
203 = $0846 BRK
204 = $0847 BRK
205 = $0848 BRK
206 = $0849 BRK
207 = $084A BRK
208 = $084B BRK
209 = $084C BRK
210 = $084D BRK
211 = $084E BRK
212 = $084F BRK
213 = $0850 BRK
214 = $0851 BRK
215 = $0852 BRK
216 = $0853 BRK
217 = $0854 BRK
218 = $0855 BRK
219 = $0856 BRK
220 = $0857 BRK
221 = $0858 BRK
222 = $0859 BRK
223 = $085A BRK
224 = $085B BRK
225 = $085C BRK
226 = $085D BRK
227 = $085E BRK
228 = $085F BRK
229 = $0860 BRK
230 = $0861 BRK
231 = $0862 BRK
232 = $0863 BRK
233 = $0864 BRK
234 = $0865 BRK
235 = $0866 BRK
236 = $0867 BRK
237 = $0868 BRK
238 = $0869 BRK
239 = $086A BRK
240 = $086B BRK
241 = $086C BRK
242 = $086D BRK
243 = $086E BRK
244 = $086F BRK
245 = $0870 BRK
246 = $0871 BRK
247 = $0872 BRK
248 = $0873 BRK
249 = $0874 BRK
250 = $0875 BRK
251 = $0876 BRK
252 = $0877 BRK
253 = $0878 BRK
254 = $0879 BRK
255 = $087A BRK
256 = $087B BRK
257 = $087C BRK
258 = $087D BRK
259 = $087E BRK
260 = $087F BRK
261 = $0880 BRK
262 = $0881 BRK
263 = $0882 BRK
264 = $0883 BRK
265 = $0884 BRK
266 = $0885 BRK
267 = $0886 BRK
268 = $0887 BRK
269 = $0888 BRK
270 = $0889 BRK
271 = $088A BRK
272 = $088B BRK
273 = $088C BRK
274 = $088D BRK
275 = $088E BRK
276 = $088F BRK
277 = $0890 BRK
278 = $0891 BRK
279 = $0892 BRK
280 = $0893 BRK
281 = $0894 BRK
282 = $0895 BRK
283 = $0896 BRK
284 = $0897 BRK
285 = $0898 BRK
286 = $0899 BRK
287 = $089A BRK
288 = $089B BRK
289 = $089C BRK
290 = $089D BRK
291 = $089E BRK
292 = $089F BRK
293 = $08A0 BRK
294 = $08A1 BRK
295 = $08A2 BRK
296 = $08A3 BRK
297 = $08A4 BRK
298 = $08A5 BRK
299 = $08A6 BRK
300 = $08A7 BRK
301 = $08A8 BRK
302 = $08A9 BRK
303 = $08AA BRK
304 = $08AB BRK
305 = $08AC BRK
306 = $08AD BRK
307 = $08AE BRK
308 = $08AF BRK
309 = $08B0 BRK
310 = $08B1 BRK
311 = $08B2 BRK
312 = $08B3 BRK
313 = $08B4 BRK
314 = $08B5 BRK
315 = $08B6 BRK
316 = $08B7 BRK
317 = $08B8 BRK
318 = $08B9 BRK
319 = $08BA BRK
320 = $08BB BRK
321 = $08BC BRK
322 = $08BD BRK
323 = $08BE BRK
324 = $08BF BRK
325 = $08C0 BRK
326 = $08C1 BRK
327 = $08C2 BRK
328 = $08C3 BRK
329 = $08C4 BRK
330 = $08C5 BRK
331 = $08C6 BRK
332 = $08C7 BRK
333 = $08C8 BRK
334 = $08C9 BRK
335 = $08CA BRK
336 = $08CB BRK
337 = $08CC BRK
338 = $08CD BRK
339 = $08CE BRK
340 = $08CF BRK
341 = $08D0 BRK
342 = $08D1 BRK
343 = $08D2 BRK
344 = $08D3 BRK
345 = $08D4 BRK
346 = $08D5 BRK
347 = $08D6 BRK
348 = $08D7 BRK
349 = $08D8 BRK
350 = $08D9 BRK
351 = $08DA BRK
352 = $08DB BRK
353 = $08DC BRK
354 = $08DD BRK
355 = $08DE BRK
356 = $08DF BRK
357 = $08E0 BRK
358 = $08E1 BRK
359 = $08E2 BRK
360 = $08E3 BRK
361 = $08E4 BRK
362 = $08E5 BRK
363 = $08E6 BRK
364 = $08E7 BRK
365 = $08E8 BRK
366 = $08E9 BRK
367 = $08EA BRK
368 = $08EB BRK
369 = $08EC BRK
370 = $08ED BRK
371 = $08EE BRK
372 = $08EF BRK
373 = $08F0 BRK
374 = $08F1 BRK
375 = $08F2 BRK
376 = $08F3 BRK
377 = $08F4 BRK
378 = $08F5 BRK
379 = $08F6 BRK
380 = $08F7 BRK
381 = $08F8 BRK
382 = $08F9 BRK
383 = $08FA BRK
384 = $08FB BRK
385 = $08FC BRK
386 = $08FD BRK
387 = $08FE BRK
388 = $08FF BRK
389 = $0900 BRK
390 = $0901 BRK
391 = $0902 BRK
392 = $0903 BRK
393 = $0904 BRK
394 = $0905 BRK
395 = $0906 BRK
396 = $0907 BRK
397 = $0908 BRK
398 = $0909 BRK
399 = $090A BRK
400 = $090B BRK
401 = $090C BRK
402 = $090D BRK
403 = $090E BRK
404 = $090F BRK
405 = $0910 BRK
406 = $0911 BRK
407 = $0912 BRK
408160
409161 Some instructions.
410162
524276 | trashes a, x, y, z, n, c, v
525277 | {
526278 | ld a, 0
279 | ld y, 0
527280 | if z {
528281 | ld y, 1
529282 | }
530283 | }
531284 = $080D LDA #$00
532 = $080F BNE $0813
533 = $0811 LDY #$01
534 = $0813 RTS
285 = $080F LDY #$00
286 = $0811 BNE $0815
287 = $0813 LDY #$01
288 = $0815 RTS
535289
536290 Compiling `repeat`.
537291
599353 Indexed access.
600354
601355 | byte one
602 | byte table many
356 | byte table[256] many
603357 |
604358 | routine main
605359 | outputs many
618372
619373 Byte tables take up 256 bytes in memory.
620374
621 | byte table tab1
622 | byte table tab2
375 | byte table[256] tab1
376 | byte table[256] tab2
623377 |
624378 | routine main
625379 | inputs tab1
705459
706460 You can also copy a literal word to a word table.
707461
708 | word table many
462 | word table[256] many
709463 |
710464 | routine main
711465 | inputs many
724478
725479 Copy vector to vector.
726480
727 | vector bar
728 | vector baz
481 | vector routine bar
482 | vector routine baz
729483 |
730484 | routine main
731485 | inputs baz
742496
743497 Copy routine to vector, inside an `interrupts off` block.
744498
745 | vector bar
499 | vector routine
500 | inputs x
501 | outputs x
502 | trashes z, n
503 | bar
746504 |
747505 | routine foo
748506 | inputs x
753511 | }
754512 |
755513 | routine main
756 | inputs foo
757514 | outputs bar
758515 | trashes a, n, z
759516 | {
774531 Copy word to word table and back, with both `x` and `y` as indexes.
775532
776533 | word one
777 | word table many
534 | word table[256] many
778535 |
779536 | routine main
780537 | inputs one, many
815572
816573 Indirect call.
817574
818 | vector foo outputs x trashes z, n
819 |
820 | routine bar outputs x trashes z, n {
575 | vector routine
576 | outputs x
577 | trashes z, n
578 | foo
579 |
580 | routine bar
581 | outputs x
582 | trashes z, n
583 | {
821584 | ld x, 200
822585 | }
823586 |
824 | routine main inputs bar outputs x, foo trashes a, z, n {
587 | routine main
588 | outputs x, foo
589 | trashes a, z, n
590 | {
825591 | copy bar, foo
826592 | call foo
827593 | }
828594 = $080D LDA #$1B
829 = $080F STA $0821
595 = $080F STA $0822
830596 = $0812 LDA #$08
831 = $0814 STA $0822
597 = $0814 STA $0823
832598 = $0817 JSR $081E
833599 = $081A RTS
834600 = $081B LDX #$C8
835601 = $081D RTS
836 = $081E JMP ($0821)
602 = $081E JMP ($0822)
603 = $0821 RTS
837604
838605 goto.
839606
840 | routine bar outputs x trashes z, n {
607 | routine bar
608 | inputs y
609 | outputs x, y
610 | trashes z, n
611 | {
841612 | ld x, 200
842613 | }
843614 |
844 | routine main outputs x trashes a, z, n {
615 | routine main
616 | outputs x, y
617 | trashes a, z, n
618 | {
845619 | ld y, 200
846620 | goto bar
847621 | }
850624 = $0812 RTS
851625 = $0813 LDX #$C8
852626 = $0815 RTS
627
628 ### Vector tables
629
630 Copying to and from a vector table.
631
632 | vector routine
633 | outputs x
634 | trashes a, z, n
635 | one
636 | vector (routine
637 | outputs x
638 | trashes a, z, n)
639 | table[256] many
640 |
641 | routine bar outputs x trashes a, z, n {
642 | ld x, 200
643 | }
644 |
645 | routine main
646 | inputs one, many
647 | outputs one, many
648 | trashes a, x, n, z
649 | {
650 | ld x, 0
651 | copy bar, one
652 | copy bar, many + x
653 | copy one, many + x
654 | copy many + x, one
655 | call one
656 | }
657 = $080D LDX #$00
658 = $080F LDA #$3F
659 = $0811 STA $0846
660 = $0814 LDA #$08
661 = $0816 STA $0847
662 = $0819 LDA #$3F
663 = $081B STA $0848,X
664 = $081E LDA #$08
665 = $0820 STA $0948,X
666 = $0823 LDA $0846
667 = $0826 STA $0848,X
668 = $0829 LDA $0847
669 = $082C STA $0948,X
670 = $082F LDA $0848,X
671 = $0832 STA $0846
672 = $0835 LDA $0948,X
673 = $0838 STA $0847
674 = $083B JSR $0842
675 = $083E RTS
676 = $083F LDX #$C8
677 = $0841 RTS
678 = $0842 JMP ($0846)
679 = $0845 RTS
853680
854681 ### word operations
855682
1041868 | routine main
1042869 | inputs buf
1043870 | outputs y, foo, delta
1044 | trashes a, z, n, ptr
871 | trashes a, c, v, z, n, ptr
1045872 | {
1046873 | copy 619, delta
1047874 | ld y, 0
875 | st off, c
1048876 | copy ^buf, ptr
1049877 | add ptr, delta
1050878 | add ptr, word 1
1051879 | copy [ptr] + y, foo
1052880 | }
1053881 = $080D LDA #$6B
1054 = $080F STA $1042
882 = $080F STA $1043
1055883 = $0812 LDA #$02
1056 = $0814 STA $1043
884 = $0814 STA $1044
1057885 = $0817 LDY #$00
1058 = $0819 LDA #$41
1059 = $081B STA $FE
1060 = $081D LDA #$08
1061 = $081F STA $FF
1062 = $0821 LDA $FE
1063 = $0823 ADC $1042
1064 = $0826 STA $FE
1065 = $0828 LDA $FF
1066 = $082A ADC $1043
1067 = $082D STA $FF
1068 = $082F LDA $FE
1069 = $0831 ADC #$01
1070 = $0833 STA $FE
1071 = $0835 LDA $FF
1072 = $0837 ADC #$00
1073 = $0839 STA $FF
1074 = $083B LDA ($FE),Y
1075 = $083D STA $1041
1076 = $0840 RTS
886 = $0819 CLC
887 = $081A LDA #$42
888 = $081C STA $FE
889 = $081E LDA #$08
890 = $0820 STA $FF
891 = $0822 LDA $FE
892 = $0824 ADC $1043
893 = $0827 STA $FE
894 = $0829 LDA $FF
895 = $082B ADC $1044
896 = $082E STA $FF
897 = $0830 LDA $FE
898 = $0832 ADC #$01
899 = $0834 STA $FE
900 = $0836 LDA $FF
901 = $0838 ADC #$00
902 = $083A STA $FF
903 = $083C LDA ($FE),Y
904 = $083E STA $1042
905 = $0841 RTS
1077906
1078907 ### Trash
1079908
+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
99 [Falderal]: http://catseye.tc/node/Falderal
1010
1111 -> Functionality "Check syntax of SixtyPical program" is implemented by
12 -> shell command "bin/sixtypical %(test-body-file) && echo ok"
12 -> shell command "bin/sixtypical --parse-only --traceback %(test-body-file) && echo ok"
1313
1414 -> Tests for functionality "Check syntax of SixtyPical program"
1515
134134
135135 | byte byt
136136 | word wor
137 | vector vec
138 | byte table tab
139 | word table wtab
137 | vector routine trashes a vec
140138 | buffer[2048] buf
141139 | pointer ptr
142140 |
144142 | }
145143 = ok
146144
145 Tables of different types.
146
147 | byte table[256] tab
148 | word table[256] wtab
149 | vector (routine trashes a) table[256] vtab
150 |
151 | routine main {
152 | }
153 = ok
154
155 Typedefs of different types.
156
157 | typedef byte octet
158 | typedef octet table[256] twokay
159 | typedef routine trashes a game_routine
160 | vector game_routine start_game
161 |
162 | routine main {
163 | }
164 = ok
165
166 Can't have two typedefs with the same name.
167
168 | typedef byte frank
169 | typedef word frank
170 |
171 | routine main {
172 | }
173 ? SyntaxError
174
147175 Explicit memory address.
148176
149177 | byte screen @ 1024
176204
177205 User-defined locations of other types.
178206
179 | byte table screen @ 1024
207 | byte table[256] screen @ 1024
180208 | word r1
181209 | word r2 @ 60000
182210 | word r3 : 2000
187215
188216 Initialized byte table.
189217
190 | byte table message : "WHAT DO YOU WANT TO DO NEXT?"
218 | byte table[32] message : "WHAT DO YOU WANT TO DO NEXT?"
191219 |
192220 | routine main {
193221 | }
289317
290318 Declaring byte and word table memory location.
291319
292 | byte table tab
320 | byte table[256] tab
293321 |
294322 | routine main {
295323 | ld x, 0
300328 = ok
301329
302330 | word one
303 | word table many
331 | word table[256] many
304332 |
305333 | routine main {
306334 | ld x, 0
312340
313341 Declaring and calling a vector.
314342
315 | vector cinv
343 | vector routine
316344 | inputs a
317345 | outputs x
318346 | trashes a, x, z, n
319 | @ 788
347 | cinv @ 788
320348 |
321349 | routine foo {
322350 | ld a, 0
343371
344372 Constraints set may only contain labels.
345373
346 | vector cinv
374 | vector routine
347375 | inputs a
348376 | outputs 200
349377 | trashes a, x, z, n
350 | @ 788
378 | cinv @ 788
351379 |
352380 | routine foo {
353381 | ld a, 0
362390
363391 A vector can name itself in its inputs, outputs, and trashes.
364392
365 | vector cinv
393 | vector routine
366394 | inputs cinv, a
367395 | outputs cinv, x
368396 | trashes a, x, z, n
369 | @ 788
397 | cinv @ 788
370398 |
371399 | routine foo {
372400 | ld a, 0
382410 A routine can be copied into a vector before the routine appears in the program,
383411 *however*, it must be marked as such with the keyword `forward`.
384412
385 | vector cinv inputs cinv, a outputs cinv, x trashes a, x, z, n @ 788
413 | vector routine
414 | inputs cinv, a
415 | outputs cinv, x
416 | trashes a, x, z, n
417 | cinv @ 788
386418 | routine main {
387419 | with interrupts off {
388420 | copy foo, cinv
394426 | }
395427 ? SyntaxError: Undefined symbol
396428
397 | vector cinv inputs cinv, a outputs cinv, x trashes a, x, z, n @ 788
429 | vector routine
430 | inputs cinv, a
431 | outputs cinv, x
432 | trashes a, x, z, n
433 | cinv @ 788
398434 | routine main {
399435 | with interrupts off {
400436 | copy forward foo, cinv
424460 | }
425461 = ok
426462
427 | vector foo
463 | vector routine foo
428464 |
429465 | routine main {
430466 | goto foo
455491 | copy [ptr] + y, foo
456492 | }
457493 = ok
494
495 Routines can be defined in a new style.
496
497 | typedef routine
498 | inputs x
499 | outputs x
500 | trashes z, n
501 | routine_type
502 |
503 | vector routine_type vec
504 |
505 | define foo routine
506 | inputs x
507 | outputs x
508 | trashes z, n
509 | {
510 | inc x
511 | }
512 |
513 | routine main
514 | outputs vec
515 | trashes a, z, n
516 | {
517 | copy foo, vec
518 | }
519 = ok
520
521 Only routines can be defined in the new style.
522
523 | define foo byte table[256]
524 |
525 | routine main
526 | trashes a, z, n
527 | {
528 | ld a, 0
529 | }
530 ? SyntaxError