git @ Cat's Eye Technologies SixtyPical / 0.17
Merge pull request #14 from catseye/develop-0.17 Develop 0.17 Chris Pressey authored 2 years ago GitHub committed 2 years ago
49 changed file(s) with 1236 addition(s) and 967 deletion(s). Raw diff Collapse all Expand all
00 History of SixtyPical
11 =====================
2
3 0.17
4 ----
5
6 * `save X, Y, Z { }` now allowed as a shortcut for nested `save`s.
7 * If the name in a location expression isn't found in the symbol
8 table, a forward reference will _always_ be generated; and forward
9 references in _all_ operations will be resolved after parsing.
10 * As a consequence, trying to call or goto a non-routine-typed symbol
11 is now an analysis error, not a syntax error.
12 * Deprecated `routine foo ...` syntax has been removed.
13 * Split TODO off into own file.
14 * `sixtypical` no longer writes the compiled binary to standard
15 output. The `--output` command-line argument should be given
16 to get a compiled binary; otherwise only analysis is run.
17 * Internal cleanups, including a hierarchy of `Outputters`.
18 * All tests pass when `sixtypical` is run under Python 3.5.2.
219
320 0.16
421 ----
00 SixtyPical
11 ==========
22
3 _Version 0.16. Work-in-progress, everything is subject to change._
3 _Version 0.17. Work-in-progress, everything is subject to change._
44
5 **SixtyPical** is a 6502-like programming language with advanced
6 static analysis.
5 **SixtyPical** is a low-level programming language with advanced
6 static analysis. Many of its primitive instructions resemble
7 those of the 6502 CPU — in fact it is intended to be compiled to
8 6502 machine code — but along with these instructions are
9 constructs which ease structuring and analyzing the code. The
10 language aims to fill this niche:
711
8 "6502-like" means that it has similar restrictions as programming
9 in 6502 assembly (e.g. the programmer must choose the registers that
10 values will be stored in) and is concomitantly easy for a compiler to
11 translate it to 6502 machine language code.
12 * You'd use assembly, but you don't want to spend hours
13 debugging (say) a memory overrun that happened because of a
14 ridiculous silly error.
15 * You'd use C or some other "high-level" language, but you don't
16 want the extra overhead added by the compiler to manage the
17 stack and registers.
1218
13 "Advanced static analysis" includes _abstract interpretation_, where we
14 go through the program step by step, tracking not just the changes that
15 happen during a _specific_ execution of the program, but _sets_ of changes
16 that could _possibly_ happen in any run of the program. This lets us
17 determine that certain things can never happen, which we can then formulate
18 as safety checks.
19
20 In practice, this means it catches things like
19 SixtyPical gives the programmer a coding regimen on par with assembly
20 language in terms of size and hands-on-ness, but also able to catch
21 many ridiculous silly errors at compile time, such as
2122
2223 * you forgot to clear carry before adding something to the accumulator
23 * a subroutine that you call trashes a register you thought was preserved
24 * a subroutine that you called trashes a register you thought it preserved
2425 * you tried to read or write a byte beyond the end of a byte array
2526 * you tried to write the address of something that was not a routine, to
2627 a jump vector
2728
28 and suchlike. It also provides some convenient operations based on
29 Many of these checks are done with _abstract interpretation_, where we
30 go through the program step by step, tracking not just the changes that
31 happen during a _specific_ execution of the program, but _sets_ of changes
32 that could _possibly_ happen in any run of the program.
33
34 SixtyPical also provides some convenient operations based on
2935 machine-language programming idioms, such as
3036
3137 * copying values from one register to another (via a third register when
3440 * explicit tail calls
3541 * indirect subroutine calls
3642
37 The reference implementation can analyze and compile SixtyPical programs to
38 6502 machine code.
43 SixtyPical is defined by a specification document, a set of test cases,
44 and a reference implementation written in Python 2. The reference
45 implementation can analyze and compile SixtyPical programs to 6502 machine
46 code, which can be run on several 6502-based 8-bit architectures:
47
48 * Commodore 64
49 * Commodore VIC-20
50 * Atari 2600 VCS
51 * Apple II
3952
4053 Quick Start
4154 -----------
6780 * [Literate test suite for SixtyPical fallthru optimization](tests/SixtyPical%20Fallthru.md)
6881 * [6502 Opcodes used/not used in SixtyPical](doc/6502%20Opcodes.md)
6982 * [Output formats supported by `sixtypical`](doc/Output%20Formats.md)
70
71 TODO
72 ----
73
74 ### `low` and `high` address operators
75
76 To turn `word` type into `byte`.
77
78 Trying to remember if we have a compelling case for this or now. The best I can think
79 of is for implementing 16-bit `cmp` in an efficient way. Maybe we should see if we
80 can get by with 16-bit `cmp` instead though.
81
82 The problem is that once a byte is extracted, putting it back into a word is awkward.
83 The address operators have to modify a destination in a special way. That is, when
84 you say `st a, >word`, you are updating `word` to be `word & $ff | a << 8`, somelike.
85 Is that consistent with `st`? Well, probably it is, but we have to explain it.
86 It might make more sense, then, for it to be "part of the operation" instead of "part of
87 the reference"; something like `st.hi x, word`; `st.lo y, word`. Dunno.
88
89 ### Save multiple values in single block
90
91 As a shortcut for the idiom
92
93 save a { save var {
94 ...
95 } }
96
97 allow
98
99 save a, var {
100 ...
101 }
102
103 ### Save values to other-than-the-stack
104
105 Allow
106
107 save a to temp_a {
108 ...
109 }
110
111 Which uses some other storage location instead of the stack. A local static
112 would be a good candidate for such.
113
114 ### Make all symbols forward-referencable
115
116 Basically, don't do symbol-table lookups when parsing, but do have a more formal
117 "symbol resolution" (linking) phase right after parsing.
118
119 ### Associate each pointer with the buffer it points into
120
121 Check that the buffer being read or written to through pointer, appears in appropriate
122 inputs or outputs set.
123
124 In the analysis, when we obtain a pointer, we need to record, in contect, what buffer
125 that pointer came from.
126
127 When we write through that pointer, we need to set that buffer as written.
128
129 When we read through the pointer, we need to check that the buffer is readable.
130
131 ### Table overlays
132
133 They are uninitialized, but the twist is, the address is a buffer that is
134 an input to and/or output of the routine. So, they are defined (insofar
135 as the buffer is defined.)
136
137 They are therefore a "view" of a section of a buffer.
138
139 This is slightly dangerous since it does permit aliases: the buffer and the
140 table refer to the same memory.
141
142 Although, if they are `static`, you could say, in the routine in which they
143 are `static`, as soon as you've established one, you can no longer use the
144 buffer; and the ones you establish must be disjoint.
145
146 (That seems to be the most compelling case for restricting them to `static`.)
147
148 An alternative would be `static` pointers, which are currently not possible because
149 pointers must be zero-page, thus `@`, thus uninitialized.
150
151 ### Question "consistent initialization"
152
153 Question the value of the "consistent initialization" principle for `if` statement analysis.
154
155 Part of this is the trashes at the end; I think what it should be is that the trashes
156 after the `if` is the union of the trashes in each of the branches; this would obviate the
157 need to `trash` values explicitly, but if you tried to access them afterwards, it would still
158 error.
159
160 ### Tail-call optimization
161
162 More generally, define a block as having zero or one `goto`s at the end. (and `goto`s cannot
163 appear elsewhere.)
164
165 If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can.
166 The constraints should iron out the same both ways.
167
168 And - once we have this - why do we need `goto` to be in tail position, strictly?
169 As long as the routine has consistent type context every place it exits, that should be fine.
170
171 ### "Include" directives
172
173 Search a searchlist of include paths. And use them to make libraries of routines.
174
175 One such library routine might be an `interrupt routine` type for various architectures.
176 Since "the supervisor" has stored values on the stack, we should be able to trash them
177 with impunity, in such a routine.
83 * [TODO](TODO.md)
0 TODO for SixtyPical
1 ===================
2
3 ### 16-bit `cmp`
4
5 This is because we don't actually want `low` and `high` address operators
6 that turn `word` type into `byte`.
7
8 This is because this immediately makes things harder (that is, effectively
9 impossible) to analyze.
10
11 16-bit `cmp` also benefits from some special differences between `cmp`
12 and `sub` on 6502, so it would be nice to capture them.
13
14 ### Save values to other-than-the-stack
15
16 Allow
17
18 save a to temp_a {
19 ...
20 }
21
22 Which uses some other storage location instead of the stack. A local static
23 would be a good candidate for such.
24
25 ### Associate each pointer with the buffer it points into
26
27 Check that the buffer being read or written to through pointer, appears in appropriate
28 inputs or outputs set.
29
30 In the analysis, when we obtain a pointer, we need to record, in context, what buffer
31 that pointer came from.
32
33 When we write through that pointer, we need to set that buffer as written.
34
35 When we read through the pointer, we need to check that the buffer is readable.
36
37 ### Table overlays
38
39 They are uninitialized, but the twist is, the address is a buffer that is
40 an input to and/or output of the routine. So, they are defined (insofar
41 as the buffer is defined.)
42
43 They are therefore a "view" of a section of a buffer.
44
45 This is slightly dangerous since it does permit aliases: the buffer and the
46 table refer to the same memory.
47
48 Although, if they are `static`, you could say, in the routine in which they
49 are `static`, as soon as you've established one, you can no longer use the
50 buffer; and the ones you establish must be disjoint.
51
52 (That seems to be the most compelling case for restricting them to `static`.)
53
54 An alternative would be `static` pointers, which are currently not possible because
55 pointers must be zero-page, thus `@`, thus uninitialized.
56
57 ### Question "consistent initialization"
58
59 Question the value of the "consistent initialization" principle for `if` statement analysis.
60
61 Part of this is the trashes at the end; I think what it should be is that the trashes
62 after the `if` is the union of the trashes in each of the branches; this would obviate the
63 need to `trash` values explicitly, but if you tried to access them afterwards, it would still
64 error.
65
66 ### Tail-call optimization
67
68 More generally, define a block as having zero or one `goto`s at the end. (and `goto`s cannot
69 appear elsewhere.)
70
71 If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can,
72 if the block is in tail position. The constraints should iron out the same both ways.
73
74 And - once we have this - why do we need `goto` to be in tail position, strictly?
75 As long as the routine has consistent type context every place it exits, that should be fine.
76
77 ### "Include" directives
78
79 Search a searchlist of include paths. And use them to make libraries of routines.
80
81 One such library routine might be an `interrupt routine` type for various architectures.
82 Since "the supervisor" has stored values on the stack, we should be able to trash them
83 with impunity, in such a routine.
84
85 ### Line numbers in analysis error messages
86
87 For analysis errors, there is a line number, but it's the line of the routine
88 after the routine in which the analysis error occurred. Fix this.
1717 import sys
1818 import traceback
1919
20 from sixtypical.parser import Parser, ParsingContext
20 from sixtypical.parser import Parser, ParsingContext, merge_programs
2121 from sixtypical.analyzer import Analyzer
22 from sixtypical.emitter import Emitter, Byte, Word
22 from sixtypical.outputter import outputter_class_for
2323 from sixtypical.compiler import Compiler
24
25
26 def merge_programs(programs):
27 """Assumes that the programs do not have any conflicts."""
28
29 from sixtypical.ast import Program
30
31 full = Program(1, defns=[], routines=[])
32 for p in programs:
33 full.defns.extend(p.defns)
34 full.routines.extend(p.routines)
35
36 return full
3724
3825
3926 def process_input_files(filenames, options):
6754 return
6855 if label:
6956 sys.stdout.write("*** {}:\n".format(label))
70 sys.stdout.write(json.dumps(data, indent=4, sort_keys=True))
57 sys.stdout.write(json.dumps(data, indent=4, sort_keys=True, separators=(',', ':')))
7158 sys.stdout.write("\n")
7259
7360 fa = FallthruAnalyzer(debug=options.debug)
7562 compilation_roster = fa.serialize()
7663 dump(compilation_roster)
7764
78 if options.analyze_only:
65 if options.analyze_only or options.output is None:
7966 return
8067
81 fh = sys.stdout
82
83 if options.output_format == 'raw':
84 start_addr = 0x0000
85 prelude = []
86 elif options.output_format == 'prg':
87 start_addr = 0xc000
88 prelude = []
89 elif options.output_format == 'c64-basic-prg':
90 start_addr = 0x0801
91 prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
92 0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
93 elif options.output_format == 'vic20-basic-prg':
94 start_addr = 0x1001
95 prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34,
96 0x31, 0x30, 0x39, 0x00, 0x00, 0x00]
97 elif options.output_format == 'atari2600-cart':
98 start_addr = 0xf000
99 prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9,
100 0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb]
101 else:
102 raise NotImplementedError("Unknown output format: {}".format(options.output_format))
103
68 start_addr = None
10469 if options.origin is not None:
10570 if options.origin.startswith('0x'):
10671 start_addr = int(options.origin, 16)
10772 else:
10873 start_addr = int(options.origin, 10)
10974
110 # If we are outputting a .PRG, we output the load address first.
111 # We don't use the Emitter for this b/c not part of addr space.
112 if options.output_format in ('prg', 'c64-basic-prg', 'vic20-basic-prg'):
113 fh.write(Word(start_addr).serialize(0))
114
115 emitter = Emitter(start_addr)
116 for byte in prelude:
117 emitter.emit(Byte(byte))
118 compiler = Compiler(emitter)
119 compiler.compile_program(program, compilation_roster=compilation_roster)
120
121 # If we are outputting a cartridge with boot and BRK address
122 # at the end, pad to ROM size minus 4 bytes, and emit addresses.
123 if options.output_format == 'atari2600-cart':
124 emitter.pad_to_size(4096 - 4)
125 emitter.emit(Word(start_addr))
126 emitter.emit(Word(start_addr))
127
128 if options.debug:
129 pprint(emitter.accum)
130 else:
131 emitter.serialize(fh)
75 with open(options.output, 'wb') as fh:
76 outputter = outputter_class_for(options.output_format)(fh, start_addr=start_addr)
77 outputter.write_prelude()
78 compiler = Compiler(outputter.emitter)
79 compiler.compile_program(program, compilation_roster=compilation_roster)
80 outputter.write_postlude()
81 if options.debug:
82 pprint(outputter.emitter)
83 else:
84 outputter.emitter.serialize_to(fh)
13285
13386
13487 if __name__ == '__main__':
13992 help="The SixtyPical source files to compile."
14093 )
14194
95 argparser.add_argument(
96 "--output", "-o", type=str, metavar='FILENAME',
97 help="File to which generated 6502 code will be written."
98 )
14299 argparser.add_argument(
143100 "--origin", type=str, default=None,
144101 help="Location in memory where the `main` routine will be "
77 The file contains only the emitted bytes of the compiled SixtyPical
88 program.
99
10 The default origin is $0000; it is not unlikely you will want to
11 override this.
10 The default origin is $0000; you will likely want to override this.
1211
1312 Note that the origin is not stored in the output file in this format;
1413 that information must be recorded separately.
1918 little-endian format. The remainder of the file is the emitted bytes
2019 of the compiled SixtyPical program, starting at that origin.
2120
22 The default origin is $C000; it is likely you will want to
23 override this.
21 The default origin is $C000; you will likely want override this.
2422
2523 This format coincides with Commodore's PRG format for disk files,
2624 thus its name.
0 byte ds_graphics @ $C050
1 byte ds_text @ $C051
2 byte ds_full @ $C052
3 byte ds_split @ $C053
4 byte ds_page1 @ $C054
5 byte ds_page2 @ $C055
6 byte ds_lores @ $C056
7 byte ds_hires @ $C057
8
9 define main routine
10 inputs a
11 outputs ds_lores, ds_page1, ds_split, ds_graphics
12 trashes a, z, n
13 {
14 ld a, 0
15 st a, ds_lores
16 st a, ds_page1
17 st a, ds_split
18 st a, ds_graphics
19 }
0 // Write ">AB>" to "standard output"
1
2 define cout routine
3 inputs a
4 trashes a
5 @ $FDED
6
7 define main routine
8 trashes a, z, n
9 {
10 ld a, 62
11 call cout
12 ld a, 65
13 call cout
14 ld a, 66
15 call cout
16 ld a, 62
17 call cout
18 }
3232 typedef routine
3333 inputs joy2, press_fire_msg, dispatch_game_state,
3434 actor_pos, actor_delta, actor_logic,
35 player_died,
3536 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
3637 outputs dispatch_game_state,
3738 actor_pos, actor_delta, actor_logic,
39 player_died,
3840 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
3941 trashes a, x, y, c, z, n, v, pos, new_pos, delta, ptr, dispatch_logic
4042 game_state_routine
4446 //
4547 // Routines that conform to this type also follow this convention:
4648 //
47 // Set carry if the player perished. Carry clear otherwise.
49 // Set player_died to 1 if the player perished. Unchanged otherwise.
4850 //
4951
5052 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
53 inputs pos, delta, joy2, screen, player_died
54 outputs pos, delta, new_pos, screen, player_died
55 trashes a, x, y, z, n, v, c, ptr
5456 logic_routine
5557
5658 // ----------------------------------------------------------------
8688 word table[256] actor_delta
8789 word delta
8890
91 byte player_died
92
8993 vector logic_routine table[256] actor_logic
9094 vector logic_routine dispatch_logic
9195
116120 // Utility Routines
117121 // ----------------------------------------------------------------
118122
119 routine read_stick
123 define read_stick routine
120124 inputs joy2
121125 outputs delta
122126 trashes a, x, z, n
181185 }
182186 }
183187
184 routine clear_screen
188 define clear_screen routine
185189 outputs screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
186190 trashes a, y, c, n, z
187191 {
204208 } until z
205209 }
206210
207 routine calculate_new_position
211 define calculate_new_position routine
208212 inputs pos, delta
209213 outputs new_pos
210214 trashes a, c, n, z, v
238242 }
239243 }
240244
241 routine init_game
245 define init_game routine
242246 inputs actor_pos, actor_delta, actor_logic
243 outputs actor_pos, actor_delta, actor_logic
247 outputs actor_pos, actor_delta, actor_logic, player_died
244248 trashes pos, a, y, z, n, c, v
245249 {
246250 ld y, 0
258262 } until z
259263
260264 ld y, 0
261 copy word 0, actor_pos + y
265 copy word 40, actor_pos + y
262266 copy word 0, actor_delta + y
263267 copy player_logic, actor_logic + y
268
269 st y, player_died
264270 }
265271
266272 // ----------------------------------------------------------------
300306 st off, c
301307 add ptr, pos
302308 copy 81, [ptr] + y
303
304 st off, c
305309 } else {
306 st on, c
310 ld a, 1
311 st a, player_died
307312 }
308313
309314 // FIXME these trashes, strictly speaking, probably shouldn't be needed,
313318 trash ptr
314319 trash y
315320 trash v
316 } else {
317 st off, c
318321 }
319322 }
320323
350353 st off, c
351354 add ptr, pos
352355 copy 82, [ptr] + y
353
354 st off, c
355 } else {
356 st on, c
357356 }
358357
359358 // FIXME these trashes, strictly speaking, probably shouldn't be needed,
372371 copy $ffd8, delta
373372 }
374373 }
375
376 st off, c
377374 }
378375
379376 // ----------------------------------------------------------------
407404 define game_state_play game_state_routine
408405 {
409406 ld x, 0
407 st x, player_died
410408 for x up to 15 {
411409 copy actor_pos + x, pos
412410 copy actor_delta + x, delta
419417 save x {
420418 copy actor_logic + x, dispatch_logic
421419 call dispatch_logic
422
423 if c {
424 // Player died! Want no dead!
425 call clear_screen
426 copy game_state_game_over, dispatch_game_state
427 }
428420 }
429421
430422 copy pos, actor_pos + x
431423 copy delta, actor_delta + x
424 }
425
426 ld a, player_died
427 if not z {
428 // Player died! Want no dead!
429 call clear_screen
430 copy game_state_game_over, dispatch_game_state
432431 }
433432
434433 goto save_cinv
457456 goto dispatch_game_state
458457 }
459458
460 routine main
459 define main routine
461460 inputs cinv
462461 outputs cinv, save_cinv, pos, dispatch_game_state,
463462 screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
22 // Define where the screen starts in memory:
33 byte table[256] screen @ 1024
44
5 routine main
5 define main routine
66 // These are the values that will be written to by this routine:
77 trashes a, x, z, n, screen
88 {
2323 trashes z, n
2424 save_cinv
2525
26 routine our_cinv
26 define our_cinv routine
2727 inputs vic_border
2828 outputs vic_border
2929 trashes z, n
3232 goto save_cinv
3333 }
3434
35 routine main
35 define main routine
3636 inputs cinv
3737 outputs cinv, save_cinv
3838 trashes a, n, z
22
33 word delta
44
5 routine read_stick
5 define read_stick routine
66 inputs joy2
77 outputs delta
88 trashes a, x, z, n
3535 }
3636 }
3737
38 routine main
38 define main routine
3939 inputs joy2
4040 outputs delta
4141 trashes a, x, z, n, screen
4848 // generating them as part of a SixtyPical program would not
4949 // be practical. So we just jump to this location instead.
5050
51 routine pla_tay_pla_tax_pla_rti
51 define pla_tay_pla_tax_pla_rti routine
5252 inputs a
5353 trashes a
5454 @ $EA81
00 byte screen @ 1024
11
2 routine main
2 define main routine
33 trashes a, z, n, screen
44 {
55 ld a, 83
0 routine add_four
0 define add_four routine
11 inputs a
22 outputs a
33 {
0 routine main
0 define main routine
11 inputs a
22 outputs a
33 trashes c, z, n, v
00 word score
1 routine main
1 define main routine
22 inputs score
33 outputs score
44 trashes a, c, z, v, n
11 pointer ptr @ 254
22 byte foo
33
4 routine main
4 define main routine
55 inputs buf
66 outputs buf, y, foo
77 trashes a, z, n, ptr
0 routine chrout
0 define chrout routine
11 inputs a
22 trashes a
33 @ 65490
44
5 routine print
5 define print routine
66 trashes a, z, n
77 {
88 ld a, 65
99 call chrout
1010 }
1111
12 routine main
12 define main routine
1313 trashes a, z, n
1414 {
1515 call print
0 routine chrout
0 define chrout routine
11 inputs a
22 trashes a
33 @ 65490
44
5 routine main
5 define main routine
66 trashes a, x, y, z, n, c, v
77 {
88 ld a, 0
0 routine chrout
0 define chrout routine
11 inputs a
22 trashes a
33 @ 65490
44
5 routine main
5 define main routine
66 trashes a, x, y, z, n, c, v
77 {
88 ld a, 0
00 byte bar
11 byte baz
22
3 routine main
3 define main routine
44 inputs baz
55 outputs bar
66 trashes a, n, z
00 byte lives
11
2 routine main
2 define main routine
33 inputs lives
44 outputs lives
5 trashes a, x
5 trashes a, x, z, n, c, v
66 {
77 ld a, 0
88 st a, lives
0 routine main
0 define main routine
11 trashes a, y, z, n, c
22 {
33 ld y, 65
0 routine chrout
0 define chrout routine
11 inputs a
22 trashes a
33 @ 65490
44
5 routine bar trashes a, z, n {
5 define bar routine trashes a, z, n {
66 ld a, 66
77 call chrout
88 }
99
10 routine main trashes a, z, n {
10 define main routine trashes a, z, n {
1111 ld a, 65
1212 call chrout
1313 goto bar
0 routine foo
0 define main routine
11 inputs a
22 outputs a
3 trashes z, n, c
34 {
45 cmp a, 42
56 if z {
0 routine chrout
0 define chrout routine
11 inputs a
22 trashes a
33 @ 65490
44
5 routine main
5 define main routine
66 trashes a, y, z, n, c
77 {
88 ld y, 65
00 byte foo
11
2 routine chrout
2 define chrout routine
33 inputs a
44 trashes a
55 @ 65490
66
7 routine print
7 define print routine
88 inputs foo
99 trashes a, z, n
1010 {
1212 call chrout
1313 }
1414
15 routine main
15 define main routine
1616 trashes a, y, z, n, foo
1717 {
1818 ld y, 65
0 routine chrout
0 define chrout routine
11 inputs a
22 trashes a
33 @ 65490
44
5 routine main
5 define main routine
66 inputs a
77 trashes a, z, n
88 {
00 byte table[8] message : "WHAT?"
11
2 routine main
2 define main routine
33 inputs message
44 outputs x, a, z, n
55 {
00 // This will not compile on its own, because there is no `main`.
11 // But this and `vector-main.60p` together will compile.
22
3 routine chrout
3 define chrout routine
44 inputs a
55 trashes a
66 @ 65490
77
8 routine printa
8 define printa routine
99 trashes a, z, n
1010 {
1111 ld a, 65
1212 call chrout
1313 }
1414
15 routine printb
15 define printb routine
1616 trashes a, z, n
1717 {
1818 ld a, 66
1111 // call chrout
1212 // }
1313
14 routine main
14 define main routine
1515 trashes print, a, z, n
1616 {
1717 copy printa, print
1010 trashes a, z, n)
1111 table[32] vectors
1212
13 routine chrout
13 define chrout routine
1414 inputs a
1515 trashes a
1616 @ 65490
1717
18 routine printa
18 define printa routine
1919 trashes a, z, n
2020 {
2121 ld a, 65
2222 call chrout
2323 }
2424
25 routine printb
25 define printb routine
2626 trashes a, z, n
2727 {
2828 ld a, 66
2929 call chrout
3030 }
3131
32 routine main
32 define main routine
3333 inputs vectors
3434 outputs vectors
3535 trashes print, a, x, z, n, c
11 trashes a, z, n
22 print
33
4 routine chrout
4 define chrout routine
55 inputs a
66 trashes a
77 @ 65490
88
9 routine printa
9 define printa routine
1010 trashes a, z, n
1111 {
1212 ld a, 65
1313 call chrout
1414 }
1515
16 routine printb
16 define printb routine
1717 trashes a, z, n
1818 {
1919 ld a, 66
2020 call chrout
2121 }
2222
23 routine main
23 define main routine
2424 trashes print, a, z, n
2525 {
2626 copy printa, print
00 word one
11 word table[256] many
22
3 routine main
3 define main routine
44 inputs one, many
55 outputs one, many
66 trashes a, x, y, n, z
22 // Define where the screen starts in memory:
33 byte table[256] screen @ 7680
44
5 routine main
5 define main routine
66 // These are the values that will be written to by this routine:
77 trashes a, x, z, n, screen
88 {
00 #!/bin/sh
11
2 usage="Usage: loadngo.sh (c64|vic20|atari2600) [--dry-run] <source.60p>"
2 usage="Usage: loadngo.sh (c64|vic20|atari2600|apple2) [--dry-run] <source.60p>"
33
44 arch="$1"
55 shift 1
2020 elif [ "X$arch" = "Xatari2600" ]; then
2121 output_format='atari2600-cart'
2222 emu='stella'
23 elif [ "X$arch" = "Xapple2" ]; then
24 src="$1"
25 out=/tmp/a-out.bin
26 bin/sixtypical --traceback --origin=0x2000 --output-format=raw $src --output $out || exit 1
27 ls -la $out
28 cp ~/scratchpad/linapple/res/Master.dsk sixtypical.dsk
29 # TODO: replace HELLO with something that does like
30 # BLOAD "PROG"
31 # CALL 8192
32 # (not BRUN because it does not always return to BASIC afterwards not sure why)
33 a2rm sixtypical.dsk PROG
34 a2in B sixtypical.dsk PROG $out
35 linapple -d1 sixtypical.dsk -autoboot
36 rm -f $out sixtypical.dsk
37 exit 0
2338 else
2439 echo $usage && exit 1
2540 fi
3752 ### do it ###
3853
3954 out=/tmp/a-out.prg
40 bin/sixtypical --traceback --output-format=$output_format $src > $out || exit 1
55 bin/sixtypical --traceback --output-format=$output_format $src --output $out || exit 1
4156 ls -la $out
4257 $emu $out
4358 rm -f $out
00 # encoding: UTF-8
11
2 from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save
2 from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save
33 from sixtypical.model import (
44 TYPE_BYTE, TYPE_WORD,
55 TableType, BufferType, PointerType, VectorType, RoutineType,
371371 context = Context(self.routines, routine, type_.inputs, type_.outputs, type_.trashes)
372372
373373 if self.debug:
374 print "at start of routine `{}`:".format(routine.name)
375 print context
374 print("at start of routine `{}`:".format(routine.name))
375 print(context)
376376
377377 self.analyze_block(routine.block, context)
378378 trashed = set(context.each_touched()) - set(context.each_meaningful())
379379
380380 if self.debug:
381 print "at end of routine `{}`:".format(routine.name)
382 print context
383 print "trashed: ", LocationRef.format_set(trashed)
384 print "outputs: ", LocationRef.format_set(type_.outputs)
381 print("at end of routine `{}`:".format(routine.name))
382 print(context)
383 print("trashed: ", LocationRef.format_set(trashed))
384 print("outputs: ", LocationRef.format_set(type_.outputs))
385385 trashed_outputs = type_.outputs & trashed
386386 if trashed_outputs:
387 print "TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs)
388 print ''
389 print '-' * 79
390 print ''
387 print("TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs))
388 print('')
389 print('-' * 79)
390 print('')
391391
392392 # even if we goto another routine, we can't trash an output.
393393 for ref in trashed:
549549 context.invalidate_range(dest)
550550 elif opcode == 'call':
551551 type = instr.location.type
552 if not isinstance(type, (RoutineType, VectorType)):
553 raise TypeMismatchError(instr, instr.location)
552554 if isinstance(type, VectorType):
553555 type = type.of_type
554556 for ref in type.inputs:
770772 context.set_writeable(instr.dest)
771773
772774 def analyze_save(self, instr, context):
773 if len(instr.locations) != 1:
774 raise NotImplementedError("Only 1 location in save is supported right now")
775 location = instr.locations[0]
776 self.assert_type(TYPE_BYTE, location)
777
778 baton = context.extract(location)
775 batons = []
776 for location in instr.locations:
777 self.assert_type(TYPE_BYTE, location)
778 baton = context.extract(location)
779 batons.append(baton)
780
779781 self.analyze_block(instr.block, context)
780782 if context.encountered_gotos():
781783 raise IllegalJumpError(instr, instr)
782 context.re_introduce(baton)
783
784
785 for location in reversed(instr.locations):
786 baton = batons.pop()
787 context.re_introduce(baton)
788
789 # We do this check outside the loop, because A is only preserved
790 # if it is the outermost thing being `save`d.
784791 if location == REG_A:
785792 pass
786793 else:
3636 def all_children(self):
3737 for attr in self.children_attrs:
3838 for child in self.attrs[attr]:
39 if child is not None:
40 yield child
41 for subchild in child.all_children():
42 yield subchild
43 for attr in self.child_attrs:
44 child = self.attrs[attr]
45 if child is not None:
3946 yield child
4047 for subchild in child.all_children():
4148 yield subchild
42 for attr in self.child_attrs:
43 child = self.attrs[attr]
44 yield child
45 for subchild in child.all_children():
46 yield subchild
4749
4850
4951 class Program(AST):
00 # encoding: UTF-8
11
2 from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff, Save
2 from sixtypical.ast import Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save
33 from sixtypical.model import (
44 ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef,
55 TYPE_BIT, TYPE_BYTE, TYPE_WORD,
111111 routine_name = roster_row[-1]
112112 self.compile_routine(self.routines[routine_name])
113113
114 for location, label in self.trampolines.iteritems():
114 for location, label in self.trampolines.items():
115115 self.emitter.resolve_label(label)
116116 self.emitter.emit(JMP(Indirect(self.get_label(location.name))))
117117 self.emitter.emit(RTS())
617617 self.emitter.emit(CLI())
618618
619619 def compile_save(self, instr):
620 location = instr.locations[0]
621 if location == REG_A:
622 self.emitter.emit(PHA())
623 self.compile_block(instr.block)
624 self.emitter.emit(PLA())
625 elif location == REG_X:
626 self.emitter.emit(TXA())
627 self.emitter.emit(PHA())
628 self.compile_block(instr.block)
629 self.emitter.emit(PLA())
630 self.emitter.emit(TAX())
631 elif location == REG_Y:
632 self.emitter.emit(TYA())
633 self.emitter.emit(PHA())
634 self.compile_block(instr.block)
635 self.emitter.emit(PLA())
636 self.emitter.emit(TAY())
637 else:
638 src_label = self.get_label(location.name)
639 self.emitter.emit(LDA(Absolute(src_label)))
640 self.emitter.emit(PHA())
641 self.compile_block(instr.block)
642 self.emitter.emit(PLA())
643 self.emitter.emit(STA(Absolute(src_label)))
620 for location in instr.locations:
621 if location == REG_A:
622 self.emitter.emit(PHA())
623 elif location == REG_X:
624 self.emitter.emit(TXA())
625 self.emitter.emit(PHA())
626 elif location == REG_Y:
627 self.emitter.emit(TYA())
628 self.emitter.emit(PHA())
629 else:
630 src_label = self.get_label(location.name)
631 self.emitter.emit(LDA(Absolute(src_label)))
632 self.emitter.emit(PHA())
633
634 self.compile_block(instr.block)
635
636 for location in reversed(instr.locations):
637 if location == REG_A:
638 self.emitter.emit(PLA())
639 elif location == REG_X:
640 self.emitter.emit(PLA())
641 self.emitter.emit(TAX())
642 elif location == REG_Y:
643 self.emitter.emit(PLA())
644 self.emitter.emit(TAY())
645 else:
646 src_label = self.get_label(location.name)
647 self.emitter.emit(PLA())
648 self.emitter.emit(STA(Absolute(src_label)))
77 raise NotImplementedError
88
99 def serialize(self, addr):
10 """Should return an array of unsigned bytes (integers from 0 to 255.)
11 `addr` is the address the value is being serialized at; for most objects
12 it makes no difference, but some objects (like relative branches) do care."""
1013 raise NotImplementedError
1114
1215
1316 class Byte(Emittable):
1417 def __init__(self, value):
15 if isinstance(value, basestring):
18 if isinstance(value, str):
1619 value = ord(value)
1720 if value < -127 or value > 255:
1821 raise IndexError(value)
2326 def size(self):
2427 return 1
2528
26 def serialize(self, addr=None):
27 return chr(self.value)
29 def serialize(self, addr):
30 return [self.value]
2831
2932 def __repr__(self):
3033 return "%s(%r)" % (self.__class__.__name__, self.value)
3841 def size(self):
3942 return 2
4043
41 def serialize(self, addr=None):
44 def serialize(self, addr):
4245 word = self.value
4346 low = word & 255
4447 high = (word >> 8) & 255
45 return chr(low) + chr(high)
48 return [low, high]
4649
4750 def __repr__(self):
4851 return "%s(%r)" % (self.__class__.__name__, self.value)
5861 def size(self):
5962 return self._size
6063
61 def serialize(self, addr=None):
62 buf = ''.join([emittable.serialize() for emittable in self.value])
64 def serialize(self, addr):
65 buf = []
66 for emittable in self.value:
67 buf.extend(emittable.serialize(addr)) # FIXME: addr + offset
6368 while len(buf) < self.size():
64 buf += chr(0)
69 buf.append(0)
6570 return buf
6671
6772 def __repr__(self):
8388 def size(self):
8489 return 2
8590
86 def serialize(self, addr=None, offset=0):
91 def serialize(self, addr, offset=0):
8792 assert self.addr is not None, "unresolved label: %s" % self.name
88 return Word(self.addr + offset).serialize()
93 return Word(self.addr + offset).serialize(addr)
8994
9095 def serialize_relative_to(self, addr):
9196 assert self.addr is not None, "unresolved label: %s" % self.name
92 return Byte(self.addr - (addr + 2)).serialize()
93
94 def serialize_as_zero_page(self, offset=0):
97 return Byte(self.addr - (addr + 2)).serialize(addr)
98
99 def serialize_as_zero_page(self, addr, offset=0):
95100 assert self.addr is not None, "unresolved label: %s" % self.name
96 return Byte(self.addr + offset).serialize()
101 return Byte(self.addr + offset).serialize(addr)
97102
98103 def __repr__(self):
99104 addr_s = ', addr=%r' % self.addr if self.addr is not None else ''
110115 def size(self):
111116 self.label.size()
112117
113 def serialize(self, addr=None):
114 return self.label.serialize(offset=self.offset)
115
116 def serialize_as_zero_page(self, offset=0):
117 return self.label.serialize_as_zero_page(offset=self.offset)
118 def serialize(self, addr):
119 return self.label.serialize(addr, offset=self.offset)
120
121 def serialize_as_zero_page(self, addr, offset=0):
122 return self.label.serialize_as_zero_page(addr, offset=self.offset)
118123
119124 def __repr__(self):
120125 return "%s(%r, %r)" % (self.__class__.__name__, self.label, self.offset)
128133 def size(self):
129134 return 1
130135
131 def serialize(self, addr=None):
132 return self.label.serialize()[0]
136 def serialize(self, addr):
137 return [self.label.serialize(addr)[0]]
133138
134139 def __repr__(self):
135140 return "%s(%r)" % (self.__class__.__name__, self.label)
143148 def size(self):
144149 return 1
145150
146 def serialize(self, addr=None):
147 return self.label.serialize()[1]
151 def serialize(self, addr):
152 return [self.label.serialize(addr)[1]]
148153
149154 def __repr__(self):
150155 return "%s(%r)" % (self.__class__.__name__, self.label)
165170 self.accum.append(thing)
166171 self.addr += thing.size()
167172
168 def serialize(self, stream):
173 def serialize_to(self, stream):
174 """`stream` should be a file opened in binary mode."""
169175 addr = self.start_addr
170176 for emittable in self.accum:
171177 chunk = emittable.serialize(addr)
172 stream.write(chunk)
178 stream.write(bytearray(chunk))
173179 addr += len(chunk)
174180
175181 def make_label(self, name=None):
4242
4343 while pending_routines:
4444 chains = [self.find_chain(k, pending_routines) for k in pending_routines.keys()]
45 chains.sort(key=len, reverse=True)
45 chains.sort(key=lambda x: (len(x), str(x)), reverse=True)
4646 c = chains[0]
4747 roster.append(c)
4848 for k in c:
77 """Size of the operand for the mode (not including the opcode)"""
88 raise NotImplementedError
99
10 def serialize(self, addr=None):
10 def serialize(self, addr):
1111 raise NotImplementedError
1212
1313 def __repr__(self):
1818 def size(self):
1919 return 0
2020
21 def serialize(self, addr=None):
22 return ''
21 def serialize(self, addr):
22 return []
2323
2424 def __repr__(self):
2525 return "%s()" % (self.__class__.__name__)
3333 def size(self):
3434 return 1
3535
36 def serialize(self, addr=None):
37 return self.value.serialize()
36 def serialize(self, addr):
37 return self.value.serialize(addr)
3838
3939
4040 class Absolute(AddressingMode):
4545 def size(self):
4646 return 2
4747
48 def serialize(self, addr=None):
49 return self.value.serialize()
48 def serialize(self, addr):
49 return self.value.serialize(addr)
5050
5151
5252 class AbsoluteX(Absolute):
6565 def size(self):
6666 return 1
6767
68 def serialize(self, addr=None):
69 return self.value.serialize_as_zero_page()
68 def serialize(self, addr):
69 return self.value.serialize_as_zero_page(addr)
7070
7171
7272 class Indirect(AddressingMode):
7777 def size(self):
7878 return 2
7979
80 def serialize(self, addr=None):
81 return self.value.serialize()
80 def serialize(self, addr):
81 return self.value.serialize(addr)
8282
8383
8484 class IndirectY(ZeroPage):
9393 def size(self):
9494 return 1
9595
96 def serialize(self, addr=None):
96 def serialize(self, addr):
9797 return self.value.serialize_relative_to(addr)
9898
9999
107107 def size(self):
108108 return 1 + self.operand.size() if self.operand else 0
109109
110 def serialize(self, addr=None):
111 return (
112 chr(self.opcodes[self.operand.__class__]) +
113 self.operand.serialize(addr)
114 )
110 def serialize(self, addr):
111 return [self.opcodes[self.operand.__class__]] + self.operand.serialize(addr)
115112
116113 def __repr__(self):
117114 return "%s(%r)" % (self.__class__.__name__, self.operand)
1717 def __hash__(self):
1818 return hash(self.name)
1919
20 def backpatch_constraint_labels(self, resolver):
21 def resolve(w):
22 if not isinstance(w, basestring):
23 return w
24 return resolver(w)
25 if isinstance(self, TableType):
26 self.of_type.backpatch_constraint_labels(resolver)
27 elif isinstance(self, VectorType):
28 self.of_type.backpatch_constraint_labels(resolver)
29 elif isinstance(self, RoutineType):
30 self.inputs = set([resolve(w) for w in self.inputs])
31 self.outputs = set([resolve(w) for w in self.outputs])
32 self.trashes = set([resolve(w) for w in self.trashes])
33
3420
3521 TYPE_BIT = Type('bit', max_range=(0, 1))
3622 TYPE_BYTE = Type('byte', max_range=(0, 255))
4026
4127 class RoutineType(Type):
4228 """This memory location contains the code for a routine."""
43 def __init__(self, inputs=None, outputs=None, trashes=None):
29 def __init__(self, inputs, outputs, trashes):
4430 self.name = 'routine'
45 self.inputs = inputs or set()
46 self.outputs = outputs or set()
47 self.trashes = trashes or set()
31 self.inputs = inputs
32 self.outputs = outputs
33 self.trashes = trashes
4834
4935 def __repr__(self):
5036 return '%s(%r, inputs=%r, outputs=%r, trashes=%r)' % (
171157
172158 @classmethod
173159 def format_set(cls, location_refs):
174 return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs)])
160 return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs, key=lambda x: x.name)])
175161
176162
177163 class IndirectRef(Ref):
0 """Executable file writer."""
1
2 from sixtypical.emitter import Emitter, Byte, Word
3
4
5 class Outputter(object):
6 def __init__(self, fh, start_addr=None):
7 self.start_addr = self.__class__.start_addr
8 if start_addr is not None:
9 self.start_addr = start_addr
10 self.prelude = self.__class__.prelude
11 self.fh = fh
12 self.emitter = Emitter(self.start_addr)
13
14 def write_header(self):
15 pass
16
17 def write_prelude(self):
18 self.write_header()
19 for byte in self.prelude:
20 self.emitter.emit(Byte(byte))
21
22 def write_postlude(self):
23 pass
24
25
26 class RawOutputter(Outputter):
27 start_addr = 0x0000
28 prelude = []
29
30
31 class PrgOutputter(Outputter):
32 start_addr = 0xc000
33 prelude = []
34
35 def write_header(self):
36 # If we are outputting a .PRG, we output the load address first.
37 # We don't use the Emitter for this b/c not part of addr space.
38 self.fh.write(bytearray(Word(self.start_addr).serialize(0)))
39
40
41 class C64BasicPrgOutputter(PrgOutputter):
42 start_addr = 0x0801
43 prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
44 0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
45
46
47 class Vic20BasicPrgOutputter(PrgOutputter):
48 start_addr = 0x1001
49 prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34,
50 0x31, 0x30, 0x39, 0x00, 0x00, 0x00]
51
52
53 class Atari2600CartOutputter(Outputter):
54 start_addr = 0xf000
55 prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9,
56 0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb]
57
58 def write_postlude(self):
59 # If we are outputting a cartridge with boot and BRK address
60 # at the end, pad to ROM size minus 4 bytes, and emit addresses.
61 self.emitter.pad_to_size(4096 - 4)
62 self.emitter.emit(Word(self.start_addr))
63 self.emitter.emit(Word(self.start_addr))
64
65
66 def outputter_class_for(output_format):
67 return {
68 'raw': RawOutputter,
69 'prg': PrgOutputter,
70 'c64-basic-prg': C64BasicPrgOutputter,
71 'vic20-basic-prg': Vic20BasicPrgOutputter,
72 'atari2600-cart': Atari2600CartOutputter,
73 }[output_format]
1717 return "%s(%r, %r)" % (self.__class__.__name__, self.ast_node, self.model)
1818
1919
20 class ForwardReference(object):
21 def __init__(self, name):
22 self.name = name
23
24 def __repr__(self):
25 return "%s(%r)" % (self.__class__.__name__, self.name)
26
27
2028 class ParsingContext(object):
2129 def __init__(self):
2230 self.symbols = {} # token -> SymEntry
3240 def __str__(self):
3341 return "Symbols: {}\nStatics: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.statics, self.typedefs, self.consts)
3442
35 def lookup(self, name):
43 def fetch(self, name):
3644 if name in self.statics:
3745 return self.statics[name].model
3846 if name in self.symbols:
4452 def __init__(self, context, text, filename):
4553 self.context = context
4654 self.scanner = Scanner(text, filename)
47 self.backpatch_instrs = []
4855
4956 def syntax_error(self, msg):
5057 self.scanner.syntax_error(msg)
5158
5259 def lookup(self, name):
53 model = self.context.lookup(name)
60 model = self.context.fetch(name)
5461 if model is None:
5562 self.syntax_error('Undefined symbol "{}"'.format(name))
5663 return model
64
65 def declare(self, name, symentry, static=False):
66 if self.context.fetch(name):
67 self.syntax_error('Symbol "%s" already declared' % name)
68 if static:
69 self.context.statics[name] = symentry
70 else:
71 self.context.symbols[name] = symentry
72
73 def clear_statics(self):
74 self.context.statics = {}
75
76 # ---- symbol resolution
77
78 def resolve_symbols(self, program):
79 # This could stand to be better unified.
80
81 def backpatch_constraint_labels(type_):
82 def resolve(w):
83 if not isinstance(w, ForwardReference):
84 return w
85 return self.lookup(w.name)
86 if isinstance(type_, TableType):
87 backpatch_constraint_labels(type_.of_type)
88 elif isinstance(type_, VectorType):
89 backpatch_constraint_labels(type_.of_type)
90 elif isinstance(type_, RoutineType):
91 type_.inputs = set([resolve(w) for w in type_.inputs])
92 type_.outputs = set([resolve(w) for w in type_.outputs])
93 type_.trashes = set([resolve(w) for w in type_.trashes])
94
95 for defn in program.defns:
96 backpatch_constraint_labels(defn.location.type)
97 for routine in program.routines:
98 backpatch_constraint_labels(routine.location.type)
99
100 def resolve_fwd_reference(obj, field):
101 field_value = getattr(obj, field, None)
102 if isinstance(field_value, ForwardReference):
103 setattr(obj, field, self.lookup(field_value.name))
104 elif isinstance(field_value, IndexedRef):
105 if isinstance(field_value.ref, ForwardReference):
106 field_value.ref = self.lookup(field_value.ref.name)
107
108 for node in program.all_children():
109 if isinstance(node, SingleOp):
110 resolve_fwd_reference(node, 'location')
111 resolve_fwd_reference(node, 'src')
112 resolve_fwd_reference(node, 'dest')
57113
58114 # --- grammar productions
59115
69125 typenames.extend(self.context.typedefs.keys())
70126 while self.scanner.on(*typenames):
71127 defn = self.defn()
72 name = defn.name
73 if self.context.lookup(name):
74 self.syntax_error('Symbol "%s" already declared' % name)
75 self.context.symbols[name] = SymEntry(defn, defn.location)
128 self.declare(defn.name, SymEntry(defn, defn.location))
76129 defns.append(defn)
77 while self.scanner.on('define', 'routine'):
78 if self.scanner.consume('define'):
79 name = self.scanner.token
80 self.scanner.scan()
81 routine = self.routine(name)
82 else:
83 routine = self.legacy_routine()
84 name = routine.name
85 if self.context.lookup(name):
86 self.syntax_error('Symbol "%s" already declared' % name)
87 self.context.symbols[name] = SymEntry(routine, routine.location)
130 while self.scanner.consume('define'):
131 name = self.scanner.token
132 self.scanner.scan()
133 routine = self.routine(name)
134 self.declare(name, SymEntry(routine, routine.location))
88135 routines.append(routine)
89136 self.scanner.check_type('EOF')
90137
91 # now backpatch the executable types.
92 #for type_name, type_ in self.context.typedefs.iteritems():
93 # type_.backpatch_constraint_labels(lambda w: self.lookup(w))
94 for defn in defns:
95 defn.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
96 for routine in routines:
97 routine.location.type.backpatch_constraint_labels(lambda w: self.lookup(w))
98 for instr in self.backpatch_instrs:
99 if instr.opcode in ('call', 'goto'):
100 name = instr.location
101 model = self.lookup(name)
102 if not isinstance(model.type, (RoutineType, VectorType)):
103 self.syntax_error('Illegal call of non-executable "%s"' % name)
104 instr.location = model
105 if instr.opcode in ('copy',) and isinstance(instr.src, basestring):
106 name = instr.src
107 model = self.lookup(name)
108 if not isinstance(model.type, (RoutineType, VectorType)):
109 self.syntax_error('Illegal copy of non-executable "%s"' % name)
110 instr.src = model
111
112 return Program(self.scanner.line_number, defns=defns, routines=routines)
138 program = Program(self.scanner.line_number, defns=defns, routines=routines)
139 self.resolve_symbols(program)
140 return program
113141
114142 def typedef(self):
115143 self.scanner.expect('typedef')
249277 outputs = set(self.labels())
250278 if self.scanner.consume('trashes'):
251279 trashes = set(self.labels())
252 return (inputs, outputs, trashes)
253
254 def legacy_routine(self):
255 self.scanner.expect('routine')
256 name = self.scanner.token
257 self.scanner.scan()
258 (inputs, outputs, trashes) = self.constraints()
259 type_ = RoutineType(inputs=inputs, outputs=outputs, trashes=trashes)
260 if self.scanner.consume('@'):
261 self.scanner.check_type('integer literal')
262 block = None
263 addr = int(self.scanner.token)
264 self.scanner.scan()
265 else:
266 block = self.block()
267 addr = None
268 location = LocationRef(type_, name)
269 return Routine(
270 self.scanner.line_number,
271 name=name, block=block, addr=addr,
272 location=location
280 return (
281 set([ForwardReference(n) for n in inputs]),
282 set([ForwardReference(n) for n in outputs]),
283 set([ForwardReference(n) for n in trashes])
273284 )
274285
275286 def routine(self, name):
285296 else:
286297 statics = self.statics()
287298
288 self.context.statics = self.compose_statics_dict(statics)
299 self.clear_statics()
300 for defn in statics:
301 self.declare(defn.name, SymEntry(defn, defn.location), static=True)
289302 block = self.block()
290 self.context.statics = {}
303 self.clear_statics()
291304
292305 addr = None
293306 location = LocationRef(type_, name)
297310 location=location, statics=statics
298311 )
299312
300 def compose_statics_dict(self, statics):
301 c = {}
302 for defn in statics:
303 name = defn.name
304 if self.context.lookup(name):
305 self.syntax_error('Symbol "%s" already declared' % name)
306 c[name] = SymEntry(defn, defn.location)
307 return c
308
309313 def labels(self):
310314 accum = []
311315 accum.append(self.label())
327331 accum.append(self.locexpr())
328332 return accum
329333
330 def locexpr(self, forward=False):
334 def locexpr(self):
331335 if self.scanner.token in ('on', 'off', 'word') or self.scanner.token in self.context.consts or self.scanner.on_type('integer literal'):
332336 return self.const()
333 elif forward:
337 else:
334338 name = self.scanner.token
335339 self.scanner.scan()
336 loc = self.context.lookup(name)
337 if loc is not None:
340 loc = self.context.fetch(name)
341 if loc:
338342 return loc
339343 else:
340 return name
341 else:
342 loc = self.lookup(self.scanner.token)
343 self.scanner.scan()
344 return loc
345
346 def indlocexpr(self, forward=False):
344 return ForwardReference(name)
345
346 def indlocexpr(self):
347347 if self.scanner.consume('['):
348348 loc = self.locexpr()
349349 self.scanner.expect(']')
354354 loc = self.locexpr()
355355 return AddressRef(loc)
356356 else:
357 return self.indexed_locexpr(forward=forward)
358
359 def indexed_locexpr(self, forward=False):
360 loc = self.locexpr(forward=forward)
361 if not isinstance(loc, basestring):
357 return self.indexed_locexpr()
358
359 def indexed_locexpr(self):
360 loc = self.locexpr()
361 if not isinstance(loc, str):
362362 index = None
363363 if self.scanner.consume('+'):
364364 index = self.locexpr()
452452 self.scanner.scan()
453453 name = self.scanner.token
454454 self.scanner.scan()
455 instr = SingleOp(self.scanner.line_number, opcode=opcode, location=name, dest=None, src=None)
456 self.backpatch_instrs.append(instr)
455 instr = SingleOp(self.scanner.line_number, opcode=opcode, location=ForwardReference(name), dest=None, src=None)
457456 return instr
458457 elif self.scanner.token in ("copy",):
459458 opcode = self.scanner.token
460459 self.scanner.scan()
461 src = self.indlocexpr(forward=True)
460 src = self.indlocexpr()
462461 self.scanner.expect(',')
463462 dest = self.indlocexpr()
464463 instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src)
465 self.backpatch_instrs.append(instr)
466464 return instr
467465 elif self.scanner.consume("with"):
468466 self.scanner.expect("interrupts")
478476 return SingleOp(self.scanner.line_number, opcode='trash', src=None, dest=dest)
479477 else:
480478 self.syntax_error('bad opcode "%s"' % self.scanner.token)
479
480
481 # - - - -
482
483
484 def merge_programs(programs):
485 """Assumes that the programs do not have any conflicts."""
486
487 full = Program(1, defns=[], routines=[])
488 for p in programs:
489 full.defns.extend(p.defns)
490 full.routines.extend(p.routines)
491
492 return full
1414
1515 Routines must declare their inputs, outputs, and memory locations they trash.
1616
17 | routine up
17 | define up routine
1818 | inputs a
1919 | outputs a
2020 | trashes c, z, v, n
2626
2727 Routines may not declare a memory location to be both an output and trashed.
2828
29 | routine main
29 | define main routine
3030 | outputs a
3131 | trashes a
3232 | {
3636
3737 If a routine declares it outputs a location, that location should be initialized.
3838
39 | routine main
39 | define main routine
4040 | outputs a, x, z, n
4141 | {
4242 | ld x, 0
4343 | }
4444 ? UnmeaningfulOutputError: a
4545
46 | routine main
46 | define main routine
4747 | inputs a
4848 | outputs a
4949 | {
5353 If a routine declares it outputs a location, that location may or may not have
5454 been initialized. Trashing is mainly a signal to the caller.
5555
56 | routine main
56 | define main routine
5757 | trashes x, z, n
5858 | {
5959 | ld x, 0
6060 | }
6161 = ok
6262
63 | routine main
63 | define main routine
6464 | trashes x, z, n
6565 | {
6666 | }
6868
6969 If a routine modifies a location, it needs to either output it or trash it.
7070
71 | routine main
71 | define main routine
7272 | {
7373 | ld x, 0
7474 | }
7575 ? ForbiddenWriteError: x
7676
77 | routine main
77 | define main routine
7878 | outputs x, z, n
7979 | {
8080 | ld x, 0
8181 | }
8282 = ok
8383
84 | routine main
84 | define main routine
8585 | trashes x, z, n
8686 | {
8787 | ld x, 0
9090
9191 This is true regardless of whether it's an input or not.
9292
93 | routine main
93 | define main routine
9494 | inputs x
9595 | {
9696 | ld x, 0
9797 | }
9898 ? ForbiddenWriteError: x
9999
100 | routine main
100 | define main routine
101101 | inputs x
102102 | outputs x, z, n
103103 | {
105105 | }
106106 = ok
107107
108 | routine main
108 | define main routine
109109 | inputs x
110110 | trashes x, z, n
111111 | {
115115
116116 If a routine trashes a location, this must be declared.
117117
118 | routine foo
118 | define foo routine
119119 | trashes x
120120 | {
121121 | trash x
122122 | }
123123 = ok
124124
125 | routine foo
125 | define foo routine
126126 | {
127127 | trash x
128128 | }
129129 ? ForbiddenWriteError: x
130130
131 | routine foo
131 | define foo routine
132132 | outputs x
133133 | {
134134 | trash x
137137
138138 If a routine causes a location to be trashed, this must be declared in the caller.
139139
140 | routine trash_x
140 | define trash_x routine
141141 | trashes x, z, n
142142 | {
143143 | ld x, 0
144144 | }
145145 |
146 | routine foo
146 | define foo routine
147147 | trashes x, z, n
148148 | {
149149 | call trash_x
150150 | }
151151 = ok
152152
153 | routine trash_x
153 | define trash_x routine
154154 | trashes x, z, n
155155 | {
156156 | ld x, 0
157157 | }
158158 |
159 | routine foo
159 | define foo routine
160160 | trashes z, n
161161 | {
162162 | call trash_x
163163 | }
164164 ? ForbiddenWriteError: x
165165
166 | routine trash_x
166 | define trash_x routine
167167 | trashes x, z, n
168168 | {
169169 | ld x, 0
170170 | }
171171 |
172 | routine foo
172 | define foo routine
173173 | outputs x
174174 | trashes z, n
175175 | {
184184 | word w1 @ 60001
185185 | word w2 : 2000
186186 |
187 | routine main
187 | define main routine
188188 | inputs b1, w1
189189 | outputs b2, w2
190190 | trashes a, z, n
195195 | }
196196 = ok
197197
198 ### call ###
199
200 You can't call a non-routine.
201
202 | byte up
203 |
204 | define main routine outputs x, y trashes z, n {
205 | ld x, 0
206 | ld y, 1
207 | call up
208 | }
209 ? TypeMismatchError: up
210
211 | define main routine outputs x, y trashes z, n {
212 | ld x, 0
213 | ld y, 1
214 | call x
215 | }
216 ? TypeMismatchError: x
217
218 Nor can you goto a non-routine.
219
220 | byte foo
221 |
222 | define main routine {
223 | goto foo
224 | }
225 ? TypeMismatchError: foo
226
198227 ### ld ###
199228
200229 Can't `ld` from a memory location that isn't initialized.
201230
202 | routine main
231 | define main routine
203232 | inputs a, x
204233 | trashes a, z, n
205234 | {
207236 | }
208237 = ok
209238
210 | routine main
239 | define main routine
211240 | inputs a
212241 | trashes a
213242 | {
217246
218247 Can't `ld` to a memory location that doesn't appear in (outputs ∪ trashes).
219248
220 | routine main
249 | define main routine
221250 | trashes a, z, n
222251 | {
223252 | ld a, 0
224253 | }
225254 = ok
226255
227 | routine main
256 | define main routine
228257 | outputs a
229258 | trashes z, n
230259 | {
232261 | }
233262 = ok
234263
235 | routine main
264 | define main routine
236265 | outputs z, n
237266 | trashes a
238267 | {
240269 | }
241270 = ok
242271
243 | routine main
272 | define main routine
244273 | trashes z, n
245274 | {
246275 | ld a, 0
247276 | }
248277 ? ForbiddenWriteError: a
249278
250 | routine main
279 | define main routine
251280 | trashes a, n
252281 | {
253282 | ld a, 0
258287
259288 | word foo
260289 |
261 | routine main
290 | define main routine
262291 | inputs foo
263292 | trashes a, n, z
264293 | {
271300 Can't `st` from a memory location that isn't initialized.
272301
273302 | byte lives
274 | routine main
303 | define main routine
275304 | inputs x
276305 | trashes lives
277306 | {
280309 = ok
281310
282311 | byte lives
283 | routine main
312 | define main routine
284313 | trashes x, lives
285314 | {
286315 | st x, lives
290319 Can't `st` to a memory location that doesn't appear in (outputs ∪ trashes).
291320
292321 | byte lives
293 | routine main
322 | define main routine
294323 | trashes lives
295324 | {
296325 | st 0, lives
298327 = ok
299328
300329 | byte lives
301 | routine main
330 | define main routine
302331 | outputs lives
303332 | {
304333 | st 0, lives
306335 = ok
307336
308337 | byte lives
309 | routine main
338 | define main routine
310339 | inputs lives
311340 | {
312341 | st 0, lives
317346
318347 | word foo
319348 |
320 | routine main
349 | define main routine
321350 | outputs foo
322351 | trashes a, n, z
323352 | {
333362 | byte one
334363 | byte table[256] many
335364 |
336 | routine main
365 | define main routine
337366 | outputs one
338367 | trashes a, x, n, z
339368 | {
346375 | byte one
347376 | byte table[256] many
348377 |
349 | routine main
378 | define main routine
350379 | outputs many
351380 | trashes a, x, n, z
352381 | {
359388 | byte one
360389 | byte table[256] many
361390 |
362 | routine main
391 | define main routine
363392 | outputs one
364393 | trashes a, x, n, z
365394 | {
372401 | byte one
373402 | byte table[256] many
374403 |
375 | routine main
404 | define main routine
376405 | outputs many
377406 | trashes a, x, n, z
378407 | {
387416 | byte one
388417 | byte table[256] many
389418 |
390 | routine main
419 | define main routine
391420 | outputs many
392421 | trashes a, x, n, z
393422 | {
400429
401430 | byte one
402431 |
403 | routine main
432 | define main routine
404433 | outputs one
405434 | trashes a, x, n, z
406435 | {
412441
413442 | byte one
414443 |
415 | routine main
444 | define main routine
416445 | outputs one
417446 | trashes a, x, n, z
418447 | {
424453
425454 | byte table[256] many
426455 |
427 | routine main
456 | define main routine
428457 | outputs many
429458 | trashes a, x, n, z
430459 | {
437466
438467 | byte table[256] many
439468 |
440 | routine main
469 | define main routine
441470 | outputs many
442471 | trashes a, x, n, z
443472 | {
450479
451480 | byte table[256] many
452481 |
453 | routine main
482 | define main routine
454483 | inputs many
455484 | outputs many
456485 | trashes a, x, n, z
464493
465494 | byte table[256] many
466495 |
467 | routine main
496 | define main routine
468497 | inputs many
469498 | outputs many
470499 | trashes a, x, n, z
477506
478507 | byte table[256] many
479508 |
480 | routine main
509 | define main routine
481510 | inputs many
482511 | outputs many
483512 | trashes a, x, c, n, z, v
495524
496525 | byte table[256] many
497526 |
498 | routine main
527 | define main routine
499528 | inputs many
500529 | outputs many
501530 | trashes a, x, c, n, z
512541
513542 | byte table[256] many
514543 |
515 | routine main
544 | define main routine
516545 | inputs many
517546 | outputs many
518547 | trashes a, x, c, n, z
532561 | word one
533562 | word table[256] many
534563 |
535 | routine main
564 | define main routine
536565 | inputs one, many
537566 | outputs one, many
538567 | trashes a, x, n, z
546575 | word one
547576 | word table[256] many
548577 |
549 | routine main
578 | define main routine
550579 | inputs one, many
551580 | outputs one, many
552581 | trashes a, x, n, z
559588 | word one
560589 | word table[256] many
561590 |
562 | routine main
591 | define main routine
563592 | inputs one, many
564593 | outputs one, many
565594 | trashes a, x, n, z
574603
575604 | word table[32] many
576605 |
577 | routine main
606 | define main routine
578607 | inputs many
579608 | outputs many
580609 | trashes a, x, n, z
598627
599628 | byte table[32] many
600629 |
601 | routine main
630 | define main routine
602631 | inputs many
603632 | outputs many
604633 | trashes a, x, n, z
611640
612641 | byte table[32] many
613642 |
614 | routine main
643 | define main routine
615644 | inputs many
616645 | outputs many
617646 | trashes a, x, n, z
623652
624653 | byte table[32] many
625654 |
626 | routine main
655 | define main routine
627656 | inputs many
628657 | outputs many
629658 | trashes a, x, n, z
639668 | word one: 77
640669 | word table[32] many
641670 |
642 | routine main
671 | define main routine
643672 | inputs many, one
644673 | outputs many, one
645674 | trashes a, x, n, z
653682 | word one: 77
654683 | word table[32] many
655684 |
656 | routine main
685 | define main routine
657686 | inputs many, one
658687 | outputs many, one
659688 | trashes a, x, n, z
666695 | word one: 77
667696 | word table[32] many
668697 |
669 | routine main
698 | define main routine
670699 | inputs many, one
671700 | outputs many, one
672701 | trashes a, x, n, z
684713 | word one: 77
685714 | word table[32] many
686715 |
687 | routine main
716 | define main routine
688717 | inputs a, many, one
689718 | outputs many, one
690719 | trashes a, x, n, z
701730 | word one: 77
702731 | word table[32] many
703732 |
704 | routine main
733 | define main routine
705734 | inputs a, many, one
706735 | outputs many, one
707736 | trashes a, x, n, z
719748 | word one: 77
720749 | word table[32] many
721750 |
722 | routine main
751 | define main routine
723752 | inputs a, many, one
724753 | outputs many, one
725754 | trashes a, x, n, z
736765
737766 Can't `add` from or to a memory location that isn't initialized.
738767
739 | routine main
768 | define main routine
740769 | inputs a
741770 | outputs a
742771 | trashes c, z, v, n
747776 = ok
748777
749778 | byte lives
750 | routine main
779 | define main routine
751780 | inputs a
752781 | outputs a
753782 | trashes c, z, v, n
758787 ? UnmeaningfulReadError: lives
759788
760789 | byte lives
761 | routine main
790 | define main routine
762791 | inputs lives
763792 | outputs a
764793 | trashes c, z, v, n
770799
771800 Can't `add` to a memory location that isn't writeable.
772801
773 | routine main
802 | define main routine
774803 | inputs a
775804 | trashes c
776805 | {
782811 You can `add` a word constant to a word memory location.
783812
784813 | word score
785 | routine main
814 | define main routine
786815 | inputs a, score
787816 | outputs score
788817 | trashes a, c, z, v, n
795824 `add`ing a word constant to a word memory location trashes `a`.
796825
797826 | word score
798 | routine main
827 | define main routine
799828 | inputs a, score
800829 | outputs score, a
801830 | trashes c, z, v, n
808837 To be sure, `add`ing a word constant to a word memory location trashes `a`.
809838
810839 | word score
811 | routine main
840 | define main routine
812841 | inputs score
813842 | outputs score
814843 | trashes c, z, v, n
822851
823852 | word score
824853 | word delta
825 | routine main
854 | define main routine
826855 | inputs score, delta
827856 | outputs score
828857 | trashes a, c, z, v, n
836865
837866 | word score
838867 | word delta
839 | routine main
868 | define main routine
840869 | inputs score, delta
841870 | outputs score
842871 | trashes c, z, v, n
850879
851880 | pointer ptr
852881 | word delta
853 | routine main
882 | define main routine
854883 | inputs ptr, delta
855884 | outputs ptr
856885 | trashes a, c, z, v, n
865894
866895 | pointer ptr
867896 | word delta
868 | routine main
897 | define main routine
869898 | inputs ptr, delta
870899 | outputs ptr
871900 | trashes c, z, v, n
880909
881910 Can't `sub` from or to a memory location that isn't initialized.
882911
883 | routine main
912 | define main routine
884913 | inputs a
885914 | outputs a
886915 | trashes c, z, v, n
891920 = ok
892921
893922 | byte lives
894 | routine main
923 | define main routine
895924 | inputs a
896925 | outputs a
897926 | trashes c, z, v, n
902931 ? UnmeaningfulReadError: lives
903932
904933 | byte lives
905 | routine main
934 | define main routine
906935 | inputs lives
907936 | outputs a
908937 | trashes c, z, v, n
914943
915944 Can't `sub` to a memory location that isn't writeable.
916945
917 | routine main
946 | define main routine
918947 | inputs a
919948 | trashes c
920949 | {
926955 You can `sub` a word constant from a word memory location.
927956
928957 | word score
929 | routine main
958 | define main routine
930959 | inputs a, score
931960 | outputs score
932961 | trashes a, c, z, v, n
939968 `sub`ing a word constant from a word memory location trashes `a`.
940969
941970 | word score
942 | routine main
971 | define main routine
943972 | inputs a, score
944973 | outputs score, a
945974 | trashes c, z, v, n
953982
954983 | word score
955984 | word delta
956 | routine main
985 | define main routine
957986 | inputs score, delta
958987 | outputs score
959988 | trashes a, c, z, v, n
967996
968997 | word score
969998 | word delta
970 | routine main
999 | define main routine
9711000 | inputs score, delta
9721001 | outputs score
9731002 | trashes c, z, v, n
9811010
9821011 Location must be initialized and writeable.
9831012
984 | routine main
1013 | define main routine
9851014 | outputs x
9861015 | trashes z, n
9871016 | {
9891018 | }
9901019 ? UnmeaningfulReadError: x
9911020
992 | routine main
1021 | define main routine
9931022 | inputs x
9941023 | trashes z, n
9951024 | {
9971026 | }
9981027 ? ForbiddenWriteError: x
9991028
1000 | routine main
1029 | define main routine
10011030 | inputs x
10021031 | outputs x
10031032 | trashes z, n
10101039
10111040 | word foo
10121041 |
1013 | routine main
1042 | define main routine
10141043 | inputs foo
10151044 | outputs foo
10161045 | trashes z, n
10231052
10241053 Location must be initialized and writeable.
10251054
1026 | routine main
1055 | define main routine
10271056 | outputs x
10281057 | trashes z, n
10291058 | {
10311060 | }
10321061 ? UnmeaningfulReadError: x
10331062
1034 | routine main
1063 | define main routine
10351064 | inputs x
10361065 | trashes z, n
10371066 | {
10391068 | }
10401069 ? ForbiddenWriteError: x
10411070
1042 | routine main
1071 | define main routine
10431072 | inputs x
10441073 | outputs x
10451074 | trashes z, n
10521081
10531082 | word foo
10541083 |
1055 | routine main
1084 | define main routine
10561085 | inputs foo
10571086 | outputs foo
10581087 | trashes z, n
10651094
10661095 Some rudimentary tests for `cmp`.
10671096
1068 | routine main
1097 | define main routine
10691098 | inputs a
10701099 | trashes z, c, n
10711100 | {
10731102 | }
10741103 = ok
10751104
1076 | routine main
1105 | define main routine
10771106 | inputs a
10781107 | trashes z, n
10791108 | {
10811110 | }
10821111 ? ForbiddenWriteError: c
10831112
1084 | routine main
1113 | define main routine
10851114 | trashes z, c, n
10861115 | {
10871116 | cmp a, 4
10921121
10931122 Some rudimentary tests for `and`.
10941123
1095 | routine main
1124 | define main routine
10961125 | inputs a
10971126 | outputs a, z, n
10981127 | {
11001129 | }
11011130 = ok
11021131
1103 | routine main
1132 | define main routine
11041133 | inputs a
11051134 | trashes z, n
11061135 | {
11081137 | }
11091138 ? ForbiddenWriteError: a
11101139
1111 | routine main
1140 | define main routine
11121141 | trashes z, n
11131142 | {
11141143 | and a, 4
11191148
11201149 Some rudimentary tests for `or`.
11211150
1122 | routine main
1151 | define main routine
11231152 | inputs a
11241153 | outputs a, z, n
11251154 | {
11271156 | }
11281157 = ok
11291158
1130 | routine main
1159 | define main routine
11311160 | inputs a
11321161 | trashes z, n
11331162 | {
11351164 | }
11361165 ? ForbiddenWriteError: a
11371166
1138 | routine main
1167 | define main routine
11391168 | trashes z, n
11401169 | {
11411170 | or a, 4
11461175
11471176 Some rudimentary tests for `xor`.
11481177
1149 | routine main
1178 | define main routine
11501179 | inputs a
11511180 | outputs a, z, n
11521181 | {
11541183 | }
11551184 = ok
11561185
1157 | routine main
1186 | define main routine
11581187 | inputs a
11591188 | trashes z, n
11601189 | {
11621191 | }
11631192 ? ForbiddenWriteError: a
11641193
1165 | routine main
1194 | define main routine
11661195 | trashes z, n
11671196 | {
11681197 | xor a, 4
11741203 Some rudimentary tests for `shl`.
11751204
11761205 | byte foo
1177 | routine main
1206 | define main routine
11781207 | inputs foo, a, c
11791208 | outputs foo, a, c, z, n
11801209 | {
11831212 | }
11841213 = ok
11851214
1186 | routine main
1215 | define main routine
11871216 | inputs a, c
11881217 | outputs c, z, n
11891218 | {
11911220 | }
11921221 ? ForbiddenWriteError: a
11931222
1194 | routine main
1223 | define main routine
11951224 | inputs a
11961225 | outputs a, c, z, n
11971226 | {
12041233 Some rudimentary tests for `shr`.
12051234
12061235 | byte foo
1207 | routine main
1236 | define main routine
12081237 | inputs foo, a, c
12091238 | outputs foo, a, c, z, n
12101239 | {
12131242 | }
12141243 = ok
12151244
1216 | routine main
1245 | define main routine
12171246 | inputs a, c
12181247 | outputs c, z, n
12191248 | {
12211250 | }
12221251 ? ForbiddenWriteError: a
12231252
1224 | routine main
1253 | define main routine
12251254 | inputs a
12261255 | outputs a, c, z, n
12271256 | {
12331262
12341263 Some rudimentary tests for `nop`.
12351264
1236 | routine main
1265 | define main routine
12371266 | {
12381267 | nop
12391268 | }
12461275
12471276 | byte lives
12481277 |
1249 | routine foo
1278 | define foo routine
12501279 | inputs x
12511280 | trashes lives
12521281 | {
12531282 | st x, lives
12541283 | }
12551284 |
1256 | routine main
1285 | define main routine
12571286 | {
12581287 | call foo
12591288 | }
12631292
12641293 | byte lives
12651294 |
1266 | routine foo
1295 | define foo routine
12671296 | inputs x
12681297 | trashes lives
12691298 | {
12701299 | st x, lives
12711300 | }
12721301 |
1273 | routine main
1302 | define main routine
12741303 | outputs x, z, n
12751304 | {
12761305 | ld x, 0
12801309
12811310 | byte lives
12821311 |
1283 | routine foo
1312 | define foo routine
12841313 | inputs x
12851314 | trashes lives
12861315 | {
12871316 | st x, lives
12881317 | }
12891318 |
1290 | routine main
1319 | define main routine
12911320 | outputs x, z, n
12921321 | trashes lives
12931322 | {
13001329
13011330 | byte lives
13021331 |
1303 | routine foo
1332 | define foo routine
13041333 | inputs x
13051334 | trashes lives
13061335 | {
13071336 | st x, lives
13081337 | }
13091338 |
1310 | routine main
1339 | define main routine
13111340 | outputs x, z, n, lives
13121341 | {
13131342 | ld x, 0
13191348
13201349 | byte lives
13211350 |
1322 | routine foo
1351 | define foo routine
13231352 | inputs x
13241353 | trashes lives
13251354 | {
13261355 | st x, lives
13271356 | }
13281357 |
1329 | routine main
1358 | define main routine
13301359 | outputs x, z, n, lives
13311360 | {
13321361 | ld x, 0
13381367 If a routine declares outputs, they are initialized in the caller after
13391368 calling it.
13401369
1341 | routine foo
1370 | define foo routine
13421371 | outputs x, z, n
13431372 | {
13441373 | ld x, 0
13451374 | }
13461375 |
1347 | routine main
1376 | define main routine
13481377 | outputs a
13491378 | trashes x, z, n
13501379 | {
13531382 | }
13541383 = ok
13551384
1356 | routine foo
1357 | {
1358 | }
1359 |
1360 | routine main
1385 | define foo routine
1386 | {
1387 | }
1388 |
1389 | define main routine
13611390 | outputs a
13621391 | trashes x
13631392 | {
13691398 If a routine trashes locations, they are uninitialized in the caller after
13701399 calling it.
13711400
1372 | routine foo
1401 | define foo routine
13731402 | trashes x, z, n
13741403 | {
13751404 | ld x, 0
13761405 | }
13771406 = ok
13781407
1379 | routine foo
1408 | define foo routine
13801409 | trashes x, z, n
13811410 | {
13821411 | ld x, 0
13831412 | }
13841413 |
1385 | routine main
1414 | define main routine
13861415 | outputs a
13871416 | trashes x, z, n
13881417 | {
13941423 Calling an extern is just the same as calling a defined routine with the
13951424 same constraints.
13961425
1397 | routine chrout
1426 | define chrout routine
13981427 | inputs a
13991428 | trashes a
14001429 | @ 65490
14011430 |
1402 | routine main
1431 | define main routine
14031432 | trashes a, z, n
14041433 | {
14051434 | ld a, 65
14071436 | }
14081437 = ok
14091438
1410 | routine chrout
1439 | define chrout routine
14111440 | inputs a
14121441 | trashes a
14131442 | @ 65490
14141443 |
1415 | routine main
1444 | define main routine
14161445 | trashes a, z, n
14171446 | {
14181447 | call chrout
14191448 | }
14201449 ? UnmeaningfulReadError: a
14211450
1422 | routine chrout
1451 | define chrout routine
14231452 | inputs a
14241453 | trashes a
14251454 | @ 65490
14261455 |
1427 | routine main
1456 | define main routine
14281457 | trashes a, x, z, n
14291458 | {
14301459 | ld a, 65
14371466
14381467 Trash does nothing except indicate that we do not care about the value anymore.
14391468
1440 | routine foo
1469 | define foo routine
14411470 | inputs a
14421471 | outputs x
14431472 | trashes a, z, n
14481477 | }
14491478 = ok
14501479
1451 | routine foo
1480 | define foo routine
14521481 | inputs a
14531482 | outputs a, x
14541483 | trashes z, n
14591488 | }
14601489 ? UnmeaningfulOutputError: a
14611490
1462 | routine foo
1491 | define foo routine
14631492 | inputs a
14641493 | outputs x
14651494 | trashes a, z, n
14741503
14751504 Both blocks of an `if` are analyzed.
14761505
1477 | routine foo
1506 | define foo routine
14781507 | inputs a
14791508 | outputs x
14801509 | trashes a, z, n, c
14901519
14911520 If a location is initialized in one block, is must be initialized in the other as well.
14921521
1493 | routine foo
1522 | define foo routine
14941523 | inputs a
14951524 | outputs x
14961525 | trashes a, z, n, c
15041533 | }
15051534 ? InconsistentInitializationError: x
15061535
1507 | routine foo
1536 | define foo routine
15081537 | inputs a
15091538 | outputs x
15101539 | trashes a, z, n, c
15181547 | }
15191548 ? InconsistentInitializationError: x
15201549
1521 | routine foo
1550 | define foo routine
15221551 | inputs a
15231552 | outputs x
15241553 | trashes a, z, n, c
15371566 input to the routine, and it is initialized in one branch, it need not
15381567 be initialized in the other.
15391568
1540 | routine foo
1569 | define foo routine
15411570 | outputs x
15421571 | trashes a, z, n, c
15431572 | {
15521581 | }
15531582 = ok
15541583
1555 | routine foo
1584 | define foo routine
15561585 | inputs x
15571586 | outputs x
15581587 | trashes a, z, n, c
15691598
15701599 An `if` with a single block is analyzed as if it had an empty `else` block.
15711600
1572 | routine foo
1601 | define foo routine
15731602 | inputs a
15741603 | outputs x
15751604 | trashes a, z, n, c
15811610 | }
15821611 ? InconsistentInitializationError: x
15831612
1584 | routine foo
1613 | define foo routine
15851614 | inputs a
15861615 | outputs x
15871616 | trashes a, z, n, c
15941623 | }
15951624 = ok
15961625
1597 | routine foo
1626 | define foo routine
15981627 | inputs a
15991628 | outputs x
16001629 | trashes a, z, n, c
16111640 trashes {`a`} and the other branch trashes {`b`} then the whole `if` statement
16121641 trashes {`a`, `b`}.
16131642
1614 | routine foo
1643 | define foo routine
16151644 | inputs a, x, z
16161645 | trashes a, x
16171646 | {
16231652 | }
16241653 = ok
16251654
1626 | routine foo
1655 | define foo routine
16271656 | inputs a, x, z
16281657 | trashes a
16291658 | {
16351664 | }
16361665 ? ForbiddenWriteError: x (in foo, line 10)
16371666
1638 | routine foo
1667 | define foo routine
16391668 | inputs a, x, z
16401669 | trashes x
16411670 | {
16511680
16521681 Repeat loop.
16531682
1654 | routine main
1683 | define main routine
16551684 | outputs x, y, n, z, c
16561685 | {
16571686 | ld x, 0
16661695
16671696 You can initialize something inside the loop that was uninitialized outside.
16681697
1669 | routine main
1698 | define main routine
16701699 | outputs x, y, n, z, c
16711700 | {
16721701 | ld x, 0
16811710 But you can't UNinitialize something at the end of the loop that you need
16821711 initialized at the start.
16831712
1684 | routine foo
1713 | define foo routine
16851714 | trashes y
16861715 | {
16871716 | }
16881717 |
1689 | routine main
1718 | define main routine
16901719 | outputs x, y, n, z, c
16911720 | {
16921721 | ld x, 0
17061735 | word one : 0
17071736 | word two : 0
17081737 |
1709 | routine main
1738 | define main routine
17101739 | inputs one, two
17111740 | outputs two
17121741 | trashes a, z, n
17191748
17201749 The body of `repeat forever` can be empty.
17211750
1722 | routine main
1751 | define main routine
17231752 | {
17241753 | repeat {
17251754 | } forever
17281757
17291758 While `repeat` is most often used with `z`, it can also be used with `n`.
17301759
1731 | routine main
1760 | define main routine
17321761 | outputs y, n, z
17331762 | {
17341763 | ld y, 15
17421771
17431772 Basic "open-faced for" loop. We'll start with the "upto" variant.
17441773
1745 In a "for" loop, we know the exact range the loop variable takes on.
1774 #### upward-counting variant
1775
1776 Even though we do not give the starting value in the "for" construct,
1777 we know the exact range the loop variable takes on.
17461778
17471779 | byte table[16] tab
17481780 |
17741806 | }
17751807 | }
17761808 ? UnmeaningfulReadError
1809
1810 Because routines currently do not include range constraints,
1811 the loop variable may not be useful as an input (the location
1812 is assumed to have the maximum range.)
1813
1814 | byte table[16] tab
1815 |
1816 | define foo routine
1817 | inputs tab, x
1818 | trashes a, x, c, z, v, n {
1819 | for x up to 15 {
1820 | ld a, 0
1821 | }
1822 | }
1823 ? RangeExceededError
17771824
17781825 You cannot modify the loop variable in a "for" loop.
17791826
18451892 | }
18461893 ? RangeExceededError
18471894
1895 You can initialize something inside the loop that was uninitialized outside.
1896
1897 | define main routine
1898 | outputs x, y, n, z
1899 | trashes c
1900 | {
1901 | ld x, 0
1902 | for x up to 15 {
1903 | ld y, 15
1904 | }
1905 | }
1906 = ok
1907
1908 But you can't UNinitialize something at the end of the loop that you need
1909 initialized at the start of that loop.
1910
1911 | define foo routine
1912 | trashes y
1913 | {
1914 | }
1915 |
1916 | define main routine
1917 | outputs x, y, n, z
1918 | trashes c
1919 | {
1920 | ld x, 0
1921 | ld y, 15
1922 | for x up to 15 {
1923 | inc y
1924 | call foo
1925 | }
1926 | }
1927 ? UnmeaningfulReadError: y
1928
1929 The "for" loop does not preserve the `z` or `n` registers.
1930
1931 | define foo routine trashes x {
1932 | ld x, 0
1933 | for x up to 15 {
1934 | }
1935 | }
1936 ? ForbiddenWriteError
1937
1938 But it does preserve the other registers, such as `c`.
1939
1940 | define foo routine trashes x, z, n {
1941 | ld x, 0
1942 | for x up to 15 {
1943 | }
1944 | }
1945 = ok
1946
1947 In fact it does not strictly trash `z` and `n`, as they are
1948 always set to known values after the loop. TODO: document
1949 what these known values are!
1950
1951 | define foo routine outputs z, n trashes x {
1952 | ld x, 0
1953 | for x up to 15 {
1954 | }
1955 | }
1956 = ok
1957
1958 #### downward-counting variant
1959
18481960 In a "for" loop (downward-counting variant), we know the exact range the loop variable takes on.
18491961
18501962 | byte table[16] tab
19022014 | }
19032015 ? RangeExceededError
19042016
1905 You can initialize something inside the loop that was uninitialized outside.
1906
1907 | routine main
1908 | outputs x, y, n, z
1909 | trashes c
1910 | {
1911 | ld x, 0
1912 | for x up to 15 {
1913 | ld y, 15
1914 | }
1915 | }
1916 = ok
1917
1918 But you can't UNinitialize something at the end of the loop that you need
1919 initialized at the start of that loop.
1920
1921 | routine foo
1922 | trashes y
1923 | {
1924 | }
1925 |
1926 | routine main
1927 | outputs x, y, n, z
1928 | trashes c
1929 | {
1930 | ld x, 0
1931 | ld y, 15
1932 | for x up to 15 {
1933 | inc y
1934 | call foo
1935 | }
1936 | }
1937 ? UnmeaningfulReadError: y
2017 The "for" loop does not preserve the `z` or `n` registers.
2018
2019 | define foo routine trashes x {
2020 | ld x, 15
2021 | for x down to 0 {
2022 | }
2023 | }
2024 ? ForbiddenWriteError
2025
2026 But it does preserve the other registers, such as `c`.
2027
2028 | define foo routine trashes x, z, n {
2029 | ld x, 15
2030 | for x down to 0 {
2031 | }
2032 | }
2033 = ok
2034
2035 In fact it does not strictly trash `z` and `n`, as they are
2036 always set to known values after the loop. TODO: document
2037 what these known values are!
2038
2039 | define foo routine outputs z, n trashes x {
2040 | ld x, 15
2041 | for x down to 0 {
2042 | }
2043 | }
2044 = ok
19382045
19392046 ### save ###
19402047
19412048 Basic neutral test, where the `save` makes no difference.
19422049
1943 | routine main
2050 | define main routine
19442051 | inputs a, x
19452052 | outputs a, x
19462053 | trashes z, n
19552062
19562063 Saving any location (other than `a`) will trash `a`.
19572064
1958 | routine main
2065 | define main routine
19592066 | inputs a, x
19602067 | outputs a, x
19612068 | trashes z, n
19692076
19702077 Saving `a` does not trash anything.
19712078
1972 | routine main
2079 | define main routine
19732080 | inputs a, x
19742081 | outputs a, x
19752082 | trashes z, n
19852092 A defined value that has been saved can be trashed inside the block.
19862093 It will continue to be defined outside the block.
19872094
1988 | routine main
2095 | define main routine
19892096 | outputs x, y
19902097 | trashes a, z, n
19912098 | {
20002107 A trashed value that has been saved can be used inside the block.
20012108 It will continue to be trashed outside the block.
20022109
2003 | routine main
2110 (Note, both x and a are unmeaningful in this test.)
2111
2112 | define main routine
20042113 | inputs a
20052114 | outputs a, x
20062115 | trashes z, n
20122121 | ld x, 1
20132122 | }
20142123 | }
2015 ? UnmeaningfulOutputError: x
2124 ? UnmeaningfulOutputError
20162125
20172126 The known range of a value will be preserved outside the block as well.
20182127
20192128 | word one: 77
20202129 | word table[32] many
20212130 |
2022 | routine main
2131 | define main routine
20232132 | inputs a, many, one
20242133 | outputs many, one
20252134 | trashes a, x, n, z
20372146 | word one: 77
20382147 | word table[32] many
20392148 |
2040 | routine main
2149 | define main routine
20412150 | inputs a, many, one
20422151 | outputs many, one
20432152