git @ Cat's Eye Technologies SixtyPical / 0.10
Merge pull request #4 from catseye/develop-0.10 Develop 0.10 Chris Pressey authored 5 years ago GitHub committed 5 years ago
12 changed file(s) with 1165 addition(s) and 154 deletion(s). Raw diff Collapse all Expand all
00 History of SixtyPical
11 =====================
2
3 0.10
4 ----
5
6 * Can `call` and `goto` routines that are defined further down in the source code.
7 * The `forward` modifier can also be used to indicate that the symbol being copied
8 in a `copy` to a vector is a routine that is defined further down in the source.
9 * Initialized `word` memory locations.
10 * Can `copy` a literal word to a word table.
11 * Subtract word (constant or memory location) from word memory location.
12 * `trash` instruction explicitly indicates a value is no longer considered meaningful.
13 * `copy []+y, a` can indirectly read a byte value into the `a` register.
14 * Fixed bug which was preventing `if` branches to diverge in what they initialized,
15 if it was already initialized when going into the `if`.
16 * Fixed a bug which was making it crash when trying to analyze `repeat forever` loops.
217
318 0.9
419 ---
2222 programs to 6502 machine code.
2323
2424 SixtyPical is a work in progress. The current released version of SixtyPical
25 is 0.9.
25 is 0.10.
2626
2727 Documentation
2828 -------------
4545 the joystick (i.e. bring it up to par with the original demo game that was written
4646 for SixtyPical)
4747
48 ### `call` routines that are defined further down in the source code
49
50 We might have a graph of states that refer to each other and that want to `goto`
51 each other. Thus we need this. We have it for vectors, but we need it for `call`.
52
53 ### Allow branches to diverge in what they touch
54
55 For example, if the routine inputs and outputs `foo`, and one branch of an `if`
56 sets `foo` and the other does not touch it, that should be OK.
57
5848 ### `vector table` type
5949
6050 ### `low` and `high` address operators
7464 This should be tracked in the abstract interpretation.
7565 (If only because abstract interpretation is the major point of this project!)
7666
67 ### Routine-local static memory locations
68
69 These would not need to appear in the inputs/outputs/trashes sets of the routines
70 that call this routine.
71
72 These might be forced to specify an initial value so that they can always be
73 assumed to be meaningful.
74
75 ### More modes for `copy`
76
77 * don't allow `copy foo, a` probably. insist on `ld a, foo` for this.
78 * have `copy` instruction able to copy a byte to a user-def mem loc, etc.
79 * `copy x, [ptr] + y`
80 * Maybe even `copy [ptra] + y, [ptrb] + y`, which can be compiled to indirect LDA then indirect STA!
81
7782 ### And at some point...
7883
79 * Compare word (constant or memory location) with memory location or pointer. (Maybe?)
80 * `copy x, [ptr] + y`
81 * Maybe even `copy [ptra] + y, [ptrb] + y`, which can be compiled to indirect LDA then indirect STA!
8284 * Check that the buffer being read or written to through pointer, appears in approporiate inputs or outputs set.
8385 * `byte table` and `word table` of sizes other than 256
84 * initialized `byte table` memory locations
8586 * always analyze before executing or compiling, unless told not to
86 * `trash` instruction.
8787 * `interrupt` routines -- to indicate that "the supervisor" has stored values on the stack, so we can trash them.
88 * pre-initialized `word` variables
8988 * error messages that include the line number of the source code
90 * have `copy` instruction able to copy a byte to a user-def mem loc, etc.
9189 * add absolute addressing in shl/shr, absolute-indexed for add, sub, etc.
9290 * check and disallow recursion.
9391 * automatic tail-call optimization (could be tricky, w/constraints?)
00 SixtyPical
11 ==========
22
3 This document describes the SixtyPical programming language version 0.9,
3 This document describes the SixtyPical programming language version 0.10,
44 both its execution aspect and its static analysis aspect (even though
55 these are, technically speaking, separate concepts.)
66
2626 // ----------------------------------------------------------------
2727
2828 pointer ptr @ 254
29
30 word table actor_pos
2931 word pos
32 word new_pos
33
34 word table actor_delta
3035 word delta
36
3137 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
3242
3343 //
3444 // Points to the routine that implements the current game state.
3545 //
46 // It's very arguable that screen1/2/3/4 and colormap1/2/3/4 are not REALLY inputs.
47 // They're only there to support the fact that game states sometimes clear the
48 // screen, and sometimes don't. When they don't, they preserve the screen, and
49 // currently the way to say "we preserve the screen" is to have it as both input
50 // and output. There is probably a better way to do this, but it needs thought.
51 //
3652
3753 vector dispatch_game_state
38 inputs joy2, pos, button_down, dispatch_game_state
39 outputs delta, pos, screen, screen1, button_down, dispatch_game_state
40 trashes a, x, y, c, z, n, v, ptr
54 inputs joy2, button_down, press_fire_msg, dispatch_game_state, save_x,
55 actor_pos, pos, new_pos, actor_delta, delta,
56 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
57 outputs button_down, dispatch_game_state,
58 actor_pos, pos, new_pos, actor_delta, delta,
59 screen, screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
60 trashes a, x, y, c, z, n, v, ptr, save_x, compare_target
4161
4262 //
4363 // The constraints on these 2 vectors are kind-of sort-of big fibs.
5272 //
5373
5474 vector cinv
55 inputs joy2, pos, button_down, dispatch_game_state
56 outputs delta, pos, screen, screen1, button_down, dispatch_game_state
57 trashes a, x, y, c, z, n, v, ptr
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
5882 @ 788
5983
6084 vector save_cinv
61 inputs joy2, pos, button_down, dispatch_game_state
62 outputs delta, pos, screen, screen1, button_down, dispatch_game_state
63 trashes a, x, y, c, z, n, v, ptr
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
6492
6593 // ----------------------------------------------------------------
6694 // Utility Routines
153181 } until z
154182 }
155183
184 routine calculate_new_position
185 inputs pos, delta
186 outputs new_pos
187 trashes a, c, n, z, v
188 {
189 copy pos, new_pos
190 st off, c
191 add new_pos, delta
192 }
193
194 routine check_new_position_in_bounds
195 inputs new_pos
196 outputs c
197 trashes compare_target, a, z, n, v
198 {
199 copy 1000, compare_target
200 st on, c
201 sub compare_target, new_pos
202
203 if not c {
204 copy word 0, compare_target
205 st on, c
206 sub compare_target, new_pos
207 if not c {
208 st off, c
209 } else {
210 st on, c
211 }
212 } else {
213 st on, c
214 }
215 }
216
217 routine init_game
218 inputs actor_pos, actor_delta
219 outputs actor_pos, actor_delta, pos
220 trashes a, y, z, n, c, v
221 {
222 ld y, 0
223 copy word 0, pos
224 repeat {
225 copy pos, actor_pos + y
226 copy word 40, actor_delta + y
227
228 st off, c
229 add pos, word 7
230
231 inc y
232 cmp y, 16
233 } until z
234
235 ld y, 0
236 copy word 0, actor_pos + y
237 copy word 0, actor_delta + y
238 }
239
240 // ----------------------------------------------------------------
241 // Actor Logics
242 // ----------------------------------------------------------------
243
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
252 {
253 call read_stick
254
255 call calculate_new_position
256 call check_new_position_in_bounds
257
258 if c {
259 copy ^screen, ptr
260 st off, c
261 add ptr, new_pos
262 ld y, 0
263
264 // check collision.
265 copy [ptr] + y, a
266 // if "collision" is with your own self, treat it as if it's blank space!
267 cmp a, 81
268 if z {
269 ld a, 32
270 }
271 cmp a, 32
272 if z {
273 copy ^screen, ptr
274 st off, c
275 add ptr, pos
276 copy 32, [ptr] + y
277
278 copy new_pos, pos
279
280 copy ^screen, ptr
281 st off, c
282 add ptr, pos
283 copy 81, [ptr] + y
284
285 st off, c
286 } else {
287 st on, c
288 trash n
289 trash a
290 trash z
291 }
292
293 // FIXME these trashes, strictly speaking, probably shouldn't be needed,
294 // but currently the compiler cares too much about values that are
295 // initialized in one branch of an `if`, but not the other, but trashed
296 // at the end of the routine anyway.
297 trash ptr
298 trash y
299 trash a
300 trash v
301 } else {
302 st off, c
303 }
304 }
305
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
314 {
315 call calculate_new_position
316 call check_new_position_in_bounds
317
318 if c {
319 copy ^screen, ptr
320 st off, c
321 add ptr, new_pos
322 ld y, 0
323
324 // check collision.
325 copy [ptr] + y, a
326 // if "collision" is with your own self, treat it as if it's blank space!
327 cmp a, 82
328 if z {
329 ld a, 32
330 }
331 cmp a, 32
332 if z {
333 copy ^screen, ptr
334 st off, c
335 add ptr, pos
336 copy 32, [ptr] + y
337
338 copy new_pos, pos
339
340 copy ^screen, ptr
341 st off, c
342 add ptr, pos
343 copy 82, [ptr] + y
344
345 st off, c
346 } else {
347 st on, c
348 trash n
349 trash a
350 trash z
351 }
352
353 // FIXME these trashes, strictly speaking, probably shouldn't be needed,
354 // but currently the compiler cares too much about values that are
355 // initialized in one branch of an `if`, but not the other, but trashed
356 // at the end of the routine anyway.
357 trash ptr
358 trash y
359 trash a
360 } else {
361 copy delta, compare_target
362 st on, c
363 sub compare_target, word 40
364 if not z {
365 copy word 40, delta
366 } else {
367 copy $ffd8, delta
368 }
369 trash compare_target
370 }
371
372 st off, c
373 }
374
156375 // ----------------------------------------------------------------
157376 // Game States
158377 // ----------------------------------------------------------------
161380 // Because these all `goto save_cinv` at the end, they must have the same signature as that routine.
162381 //
163382
164 routine game_state_play
165 inputs joy2, pos, button_down, dispatch_game_state
166 outputs delta, pos, screen, screen1, button_down, dispatch_game_state
167 trashes a, x, y, c, z, n, v, ptr
168 {
169 call read_stick
170
171 st off, c
172 add pos, delta
173
174 copy ^screen, ptr
175 st off, c
176 add ptr, pos
177
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
391 {
178392 ld y, 0
179 copy 81, [ptr] + y
180
181 goto save_cinv
182 }
183
184 routine game_state_title_screen
185 inputs joy2, pos, button_down, dispatch_game_state
186 outputs delta, pos, screen, screen1, button_down, dispatch_game_state
187 trashes a, x, y, c, z, n, v, ptr
188 {
189 ld y, 10
190393 repeat {
191 ld a, 90
394 ld a, press_fire_msg + y
395
396 st on, c
397 sub a, 64 // yuck. oh well
398
192399 st a, screen1 + y
193400 inc y
194 cmp y, 20
401 cmp y, 18
195402 } until z
196403
197404 st off, c
198405 call check_button
199406
200407 if c {
201 // call clear_screen
202 // call init_game
203 copy game_state_play, dispatch_game_state
204 } else {
205 // This is sort of a hack. FIXME: let `if` branches diverge this much.
206 copy dispatch_game_state, dispatch_game_state
408 call clear_screen
409 call init_game
410 copy forward game_state_play, dispatch_game_state
411
412 // FIXME these trashes, strictly speaking, probably shouldn't be needed,
413 // but currently the compiler cares too much about values that are
414 // initialized in one branch of an `if`, but not the other, but trashed
415 // at the end of the routine anyway.
416 trash a
417 trash n
418 trash z
419 } else {
420 trash y
421 trash c
422 trash v
423 }
424
425 goto save_cinv
426 }
427
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
436 {
437 ld x, 0
438 repeat {
439 copy actor_pos + x, pos
440 copy actor_delta + x, delta
441
442 st x, save_x
443
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 }
455
456 if c {
457 // Player died! Want no dead! Break out of the loop (this is a bit awkward.)
458 call clear_screen
459 copy forward game_state_game_over, dispatch_game_state
460 ld x, 15
461 st x, save_x
462 trash n
463 trash z
464 trash x
465 } else {
466 trash c
467 }
468
469 ld x, save_x
470
471 copy pos, actor_pos + x
472 copy delta, actor_delta + x
473
474 inc x
475 cmp x, 16
476 } until z
477
478 goto save_cinv
479 }
480
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
489 {
490 st off, c
491 call check_button
492
493 if c {
494 call clear_screen
495 call init_game
496 copy game_state_title_screen, dispatch_game_state
497
498 // FIXME these trashes, strictly speaking, probably shouldn't be needed,
499 // but currently the compiler cares too much about values that are
500 // initialized in one branch of an `if`, but not the other, but trashed
501 // at the end of the routine anyway.
502 trash a
503 trash n
504 trash z
505 } else {
506 trash y
507 trash c
508 trash v
207509 }
208510
209511 goto save_cinv
214516 // *************************
215517
216518 routine our_cinv
217 inputs joy2, pos, button_down, dispatch_game_state
218 outputs delta, pos, screen, screen1, button_down, dispatch_game_state
219 trashes a, x, y, c, z, n, v, ptr
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
220526 {
221527 goto dispatch_game_state
222528 }
242548 copy cinv, save_cinv
243549 copy our_cinv, cinv
244550 }
245 // FIXME: find out why `repeat { } forever` does not analyze OK
246 repeat {
247 ld a, 0
248 } until not z
249 }
551
552 repeat { } forever
553 }
8888 self._writeable.add(ref)
8989
9090 def __str__(self):
91 def locstr(loc):
92 if isinstance(loc, LocationRef):
93 return "{}:{}".format(loc.name, loc.type)
94 else:
95 return str(loc)
96
97 def locsetstr(s):
98 return '{' + ', '.join([locstr(loc) for loc in list(s)]) + '}'
99
10091 return "Context(\n _touched={},\n _meaningful={},\n _writeable={}\n)".format(
101 locsetstr(self._touched), locsetstr(self._meaningful), locsetstr(self._writeable)
92 LocationRef.format_set(self._touched), LocationRef.format_set(self._meaningful), LocationRef.format_set(self._writeable)
10293 )
10394
10495 def clone(self):
184175 )
185176
186177 def assert_affected_within(self, name, affected, limited_to):
187 # We reduce the set of LocationRefs to a set of strings (their labels).
188 # This is necessary because currently, two LocationRefs that refer to the
189 # same location are not considered euqal. (But two LocationRefs with the
190 # same label should always be the same type.)
191
192 affected = set([loc.name for loc in affected])
193 limited_to = set([loc.name for loc in limited_to])
194
195 def loc_list(label_set):
196 return ', '.join(sorted(label_set))
197
198178 overage = affected - limited_to
199179 if not overage:
200180 return
201 message = 'in %s: %s are {%s} but affects {%s} which exceeds by: {%s} ' % (
202 self.current_routine.name, name, loc_list(limited_to), loc_list(affected), loc_list(overage)
181 message = 'in %s: %s are %s but affects %s which exceeds it by: %s ' % (
182 self.current_routine.name, name,
183 LocationRef.format_set(limited_to), LocationRef.format_set(affected), LocationRef.format_set(overage)
203184 )
204185 raise IncompatibleConstraintsError(message)
205186
291272 else:
292273 self.assert_type(TYPE_WORD, dest)
293274 elif opcode == 'sub':
294 self.assert_type(TYPE_BYTE, src, dest)
295275 context.assert_meaningful(src, dest, FLAG_C)
296 context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
276 if src.type == TYPE_BYTE:
277 self.assert_type(TYPE_BYTE, src, dest)
278 context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
279 else:
280 self.assert_type(TYPE_WORD, src, dest)
281 context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
282 context.set_touched(REG_A)
283 context.set_unmeaningful(REG_A)
297284 elif opcode in ('inc', 'dec'):
298285 self.assert_type(TYPE_BYTE, dest)
299286 context.assert_meaningful(dest)
331318 # doesn't really matter if you modified it or not, coming out.
332319 for ref in context1.each_meaningful():
333320 context2.assert_meaningful(
334 ref, exception_class=InconsistentInitializationError, message='initialized in block 1 but not in block 2'
321 ref, exception_class=InconsistentInitializationError,
322 message='initialized in block 1 but not in block 2 of `if {}`'.format(src)
335323 )
336324 for ref in context2.each_meaningful():
337325 context1.assert_meaningful(
338 ref, exception_class=InconsistentInitializationError, message='initialized in block 2 but not in block 1'
326 ref, exception_class=InconsistentInitializationError,
327 message='initialized in block 2 but not in block 1 of `if {}`'.format(src)
339328 )
340329 context.set_from(context1)
341330 elif opcode == 'repeat':
342331 # it will always be executed at least once, so analyze it having
343332 # been executed the first time.
344333 self.analyze_block(instr.block, context)
345 context.assert_meaningful(src)
334 if src is not None: # None indicates 'repeat forever'
335 context.assert_meaningful(src)
346336
347337 # now analyze it having been executed a second time, with the context
348338 # of it having already been executed.
349339 self.analyze_block(instr.block, context)
350 context.assert_meaningful(src)
340 if src is not None:
341 context.assert_meaningful(src)
351342
352343 elif opcode == 'copy':
353344 # 1. check that their types are compatible
368359 else:
369360 raise TypeMismatchError((src, dest))
370361
371 elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
362 elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndexedRef):
372363 if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
373364 pass
374365 else:
402393 context.assert_meaningful(src.ref, REG_Y)
403394 # TODO this will need to be more sophisticated. the thing ref points to is touched, as well.
404395 context.set_touched(src.ref) # TODO and REG_Y? if not, why not?
396 context.set_touched(dest)
405397 context.set_written(dest)
406398 elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
407399 context.assert_meaningful(src, dest.ref, dest.index)
408400 context.set_touched(src) # TODO and dest.index?
409401 context.set_written(dest.ref)
402 elif isinstance(src, ConstantRef) and isinstance(dest, IndexedRef):
403 context.assert_meaningful(src, dest.ref, dest.index)
404 context.set_written(dest.ref)
410405 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
411406 context.assert_meaningful(src.ref, src.index, dest)
412407 context.set_touched(dest) # TODO and src.index?
416411 context.set_written(dest)
417412
418413 context.set_touched(REG_A, FLAG_Z, FLAG_N)
419 context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N)
414 context.set_unmeaningful(FLAG_Z, FLAG_N)
415
416 # FIXME: this is just to support "copy [foo] + y, a". consider disallowing `a` as something
417 # that can be used in `copy`. should be using `st` or `ld` instead, probably.
418 if dest == REG_A:
419 context.set_touched(REG_A)
420 context.set_written(REG_A)
421 else:
422 context.set_unmeaningful(REG_A)
420423
421424 elif opcode == 'with-sei':
422425 self.analyze_block(instr.block, context)
438441 self.assert_affected_within('trashes', type_.trashes, current_type.trashes)
439442
440443 self.has_encountered_goto = True
444 elif opcode == 'trash':
445 context.set_unmeaningful(instr.dest)
441446 else:
442447 raise NotImplementedError(opcode)
55 TYPE_BIT, TYPE_BYTE, TYPE_BYTE_TABLE, TYPE_WORD, TYPE_WORD_TABLE, BufferType, PointerType, RoutineType, VectorType,
66 REG_A, REG_X, REG_Y, FLAG_C
77 )
8 from sixtypical.emitter import Byte, Label, Offset, LowAddressByte, HighAddressByte
8 from sixtypical.emitter import Byte, Word, Table, Label, Offset, LowAddressByte, HighAddressByte
99 from sixtypical.gen6502 import (
1010 Immediate, Absolute, AbsoluteX, AbsoluteY, ZeroPage, Indirect, IndirectY, Relative,
1111 LDA, LDX, LDY, STA, STX, STY,
2929 self.routines = {}
3030 self.labels = {}
3131 self.trampolines = {} # Location -> Label
32
33 # helper methods
34
35 def addressing_mode_for_index(self, index):
36 if index == REG_X:
37 return AbsoluteX
38 elif index == REG_Y:
39 return AbsoluteY
40 else:
41 raise NotImplementedError(index)
42
43 # visitor methods
3244
3345 def compile_program(self, program):
3446 assert isinstance(program, Program)
7183 for defn in program.defns:
7284 if defn.initial is not None:
7385 label = self.labels[defn.name]
74 initial_data = Byte(defn.initial) # TODO: support other types than Byte
86 initial_data = None
87 type_ = defn.location.type
88 if type_ == TYPE_BYTE:
89 initial_data = Byte(defn.initial)
90 elif type_ == TYPE_WORD:
91 initial_data = Word(defn.initial)
92 elif type_ == TYPE_BYTE_TABLE:
93 initial_data = Table(defn.initial)
94 else:
95 raise NotImplementedError(type_)
7596 label.set_length(initial_data.size())
7697 self.emitter.resolve_label(label)
7798 self.emitter.emit(initial_data)
208229 self.emitter.emit(SBC(Immediate(Byte(src.value))))
209230 else:
210231 self.emitter.emit(SBC(Absolute(self.labels[src.name])))
232 elif isinstance(dest, LocationRef) and src.type == TYPE_WORD and dest.type == TYPE_WORD:
233 if isinstance(src, ConstantRef):
234 dest_label = self.labels[dest.name]
235 self.emitter.emit(LDA(Absolute(dest_label)))
236 self.emitter.emit(SBC(Immediate(Byte(src.low_byte()))))
237 self.emitter.emit(STA(Absolute(dest_label)))
238 self.emitter.emit(LDA(Absolute(Offset(dest_label, 1))))
239 self.emitter.emit(SBC(Immediate(Byte(src.high_byte()))))
240 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
241 elif isinstance(src, LocationRef):
242 src_label = self.labels[src.name]
243 dest_label = self.labels[dest.name]
244 self.emitter.emit(LDA(Absolute(dest_label)))
245 self.emitter.emit(SBC(Absolute(src_label)))
246 self.emitter.emit(STA(Absolute(dest_label)))
247 self.emitter.emit(LDA(Absolute(Offset(dest_label, 1))))
248 self.emitter.emit(SBC(Absolute(Offset(src_label, 1))))
249 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
250 else:
251 raise UnsupportedOpcodeError(instr)
211252 else:
212253 raise UnsupportedOpcodeError(instr)
213254 elif opcode == 'inc':
306347 elif opcode == 'repeat':
307348 top_label = self.emitter.make_label()
308349 self.compile_block(instr.block)
309 if src is None:
350 if src is None: # indicates 'repeat forever'
310351 self.emitter.emit(JMP(Absolute(top_label)))
311352 else:
312353 cls = {
343384 else:
344385 raise NotImplementedError((src, dest))
345386 elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef):
346 if dest.type == TYPE_BYTE and isinstance(src.ref.type, PointerType):
387 if dest == REG_A and isinstance(src.ref.type, PointerType):
388 src_label = self.labels[src.ref.name]
389 self.emitter.emit(LDA(IndirectY(src_label)))
390 elif dest.type == TYPE_BYTE and isinstance(src.ref.type, PointerType):
347391 src_label = self.labels[src.ref.name]
348392 dest_label = self.labels[dest.name]
349393 self.emitter.emit(LDA(IndirectY(src_label)))
362406 if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
363407 src_label = self.labels[src.name]
364408 dest_label = self.labels[dest.ref.name]
365 addressing_mode = None
366 if dest.index == REG_X:
367 addressing_mode = AbsoluteX
368 elif dest.index == REG_Y:
369 addressing_mode = AbsoluteY
370 else:
371 raise NotImplementedError(dest)
372409 self.emitter.emit(LDA(Absolute(src_label)))
373 self.emitter.emit(STA(addressing_mode(dest_label)))
410 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label)))
374411 self.emitter.emit(LDA(Absolute(Offset(src_label, 1))))
375 self.emitter.emit(STA(addressing_mode(Offset(dest_label, 256))))
412 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256))))
413 else:
414 raise NotImplementedError
415 elif isinstance(src, ConstantRef) and isinstance(dest, IndexedRef):
416 if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
417 dest_label = self.labels[dest.ref.name]
418 self.emitter.emit(LDA(Immediate(Byte(src.low_byte()))))
419 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(dest_label)))
420 self.emitter.emit(LDA(Immediate(Byte(src.high_byte()))))
421 self.emitter.emit(STA(self.addressing_mode_for_index(dest.index)(Offset(dest_label, 256))))
376422 else:
377423 raise NotImplementedError
378424 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
379425 if src.ref.type == TYPE_WORD_TABLE and dest.type == TYPE_WORD:
380426 src_label = self.labels[src.ref.name]
381427 dest_label = self.labels[dest.name]
382 addressing_mode = None
383 if src.index == REG_X:
384 addressing_mode = AbsoluteX
385 elif src.index == REG_Y:
386 addressing_mode = AbsoluteY
387 else:
388 raise NotImplementedError(src)
389 self.emitter.emit(LDA(addressing_mode(src_label)))
390 self.emitter.emit(STA(Absolute(dest_label)))
391 self.emitter.emit(LDA(addressing_mode(Offset(src_label, 256))))
428 self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(src_label)))
429 self.emitter.emit(STA(Absolute(dest_label)))
430 self.emitter.emit(LDA(self.addressing_mode_for_index(src.index)(Offset(src_label, 256))))
392431 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
393432 else:
394433 raise NotImplementedError
433472 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
434473 else:
435474 raise NotImplementedError(src.type)
475 elif opcode == 'trash':
476 pass
436477 else:
437478 raise NotImplementedError(opcode)
4747
4848
4949 class Table(Emittable):
50 def __init__(self, value):
51 # TODO: range-checking
52 self.value = value
53
5054 def size(self):
5155 return 256
5256
5357 def serialize(self, addr=None):
54 return chr(0) * self.size()
58 bytes = []
59 for b in self.value:
60 bytes.append(chr(ord(b)))
61 while len(bytes) < self.size():
62 bytes.append(chr(0))
63 return ''.join(bytes)
5564
5665 def __repr__(self):
5766 return "%s()" % (self.__class__.__name__)
66
77 def __repr__(self):
88 return 'Type(%r)' % self.name
9
10 def __str__(self):
11 return self.name
912
1013 def __eq__(self, other):
1114 return isinstance(other, Type) and other.name == self.name
8588 def __eq__(self, other):
8689 # Ordinarily there will only be one ref with a given name,
8790 # but because we store the type in here and we want to treat
88 # these objects as immutable, we compare the types, too.
89 # Not sure if very wise.
90 return isinstance(other, self.__class__) and (
91 other.name == self.name and other.type == self.type
92 )
91 # these objects as immutable, we compare the types, too,
92 # just to be sure.
93 equal = isinstance(other, self.__class__) and other.name == self.name
94 if equal:
95 assert other.type == self.type
96 return equal
9397
9498 def __hash__(self):
9599 return hash(self.name + str(self.type))
97101 def __repr__(self):
98102 return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.name)
99103
104 def __str__(self):
105 return "{}:{}".format(self.name, self.type)
106
100107 def is_constant(self):
101108 return isinstance(self.type, RoutineType)
109
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
117 @classmethod
118 def format_set(cls, location_refs):
119 return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs)])
102120
103121
104122 class IndirectRef(Ref):
2222 self.symbols[token] = SymEntry(None, LocationRef(TYPE_BYTE, token))
2323 for token in ('c', 'z', 'n', 'v'):
2424 self.symbols[token] = SymEntry(None, LocationRef(TYPE_BIT, token))
25 self.backpatch_instrs = []
2526
2627 def lookup(self, name):
2728 if name not in self.symbols:
4849 self.symbols[name] = SymEntry(routine, routine.location)
4950 routines.append(routine)
5051 self.scanner.check_type('EOF')
52
5153 # now backpatch the executable types.
5254 for defn in defns:
53 if isinstance(defn.location.type, VectorType):
54 t = defn.location.type
55 t.inputs = set([self.lookup(w) for w in t.inputs])
56 t.outputs = set([self.lookup(w) for w in t.outputs])
57 t.trashes = set([self.lookup(w) for w in t.trashes])
55 defn.location.backpatch_vector_labels(lambda w: self.lookup(w))
5856 for routine in routines:
59 if isinstance(routine.location.type, ExecutableType):
60 t = routine.location.type
61 t.inputs = set([self.lookup(w) for w in t.inputs])
62 t.outputs = set([self.lookup(w) for w in t.outputs])
63 t.trashes = set([self.lookup(w) for w in t.trashes])
57 routine.location.backpatch_vector_labels(lambda w: self.lookup(w))
58 for instr in self.backpatch_instrs:
59 if instr.opcode in ('call', 'goto'):
60 name = instr.location
61 if name not in self.symbols:
62 raise SyntaxError('Undefined routine "%s"' % name)
63 if not isinstance(self.symbols[name].model.type, ExecutableType):
64 raise SyntaxError('Illegal call of non-executable "%s"' % name)
65 instr.location = self.symbols[name].model
66 if instr.opcode in ('copy',) and isinstance(instr.src, basestring):
67 name = instr.src
68 if name not in self.symbols:
69 raise SyntaxError('Undefined routine "%s"' % name)
70 if not isinstance(self.symbols[name].model.type, ExecutableType):
71 raise SyntaxError('Illegal copy of non-executable "%s"' % name)
72 instr.src = self.symbols[name].model
73
6474 return Program(defns=defns, routines=routines)
6575
6676 def defn(self):
7888
7989 initial = None
8090 if self.scanner.consume(':'):
81 self.scanner.check_type('integer literal')
82 initial = int(self.scanner.token)
91 if type_ == TYPE_BYTE_TABLE and self.scanner.on_type('string literal'):
92 initial = self.scanner.token
93 else:
94 self.scanner.check_type('integer literal')
95 initial = int(self.scanner.token)
8396 self.scanner.scan()
8497
8598 addr = None
193206 return loc
194207
195208 def indlocexpr(self):
196 if self.scanner.consume('['):
209 if self.scanner.consume('forward'):
210 return self.label()
211 elif self.scanner.consume('['):
197212 loc = self.locexpr()
198213 self.scanner.expect(']')
199214 self.scanner.expect('+')
272287 self.scanner.scan()
273288 name = self.scanner.token
274289 self.scanner.scan()
275 if name not in self.symbols:
276 raise SyntaxError('Undefined routine "%s"' % name)
277 if not isinstance(self.symbols[name].model.type, ExecutableType):
278 raise SyntaxError('Illegal call of non-executable "%s"' % name)
279 return Instr(opcode=opcode, location=self.symbols[name].model, dest=None, src=None)
290 instr = Instr(opcode=opcode, location=name, dest=None, src=None)
291 self.backpatch_instrs.append(instr)
292 return instr
280293 elif self.scanner.token in ("copy",):
281294 opcode = self.scanner.token
282295 self.scanner.scan()
283296 src = self.indlocexpr()
284297 self.scanner.expect(',')
285298 dest = self.indlocexpr()
286 return Instr(opcode=opcode, dest=dest, src=src)
299 instr = Instr(opcode=opcode, dest=dest, src=src)
300 self.backpatch_instrs.append(instr)
301 return instr
287302 elif self.scanner.consume("with"):
288303 self.scanner.expect("interrupts")
289304 self.scanner.expect("off")
290305 block = self.block()
291306 return Instr(opcode='with-sei', dest=None, src=None, block=block)
307 elif self.scanner.consume("trash"):
308 dest = self.locexpr()
309 return Instr(opcode='trash', src=None, dest=dest)
292310 else:
293311 raise ValueError('bad opcode "%s"' % self.scanner.token)
8585 | trashes x, z, n
8686 | {
8787 | ld x, 0
88 | }
89 = ok
90
91 If a routine reads or writes a user-define memory location, it needs to declare that too.
92
93 | byte b1 @ 60000
94 | byte b2 : 3
95 | word w1 @ 60001
96 | word w2 : 2000
97 |
98 | routine main
99 | inputs b1, w1
100 | outputs b2, w2
101 | trashes a, z, n
102 | {
103 | ld a, b1
104 | st a, b2
105 | copy w1, w2
88106 | }
89107 = ok
90108
369387 | }
370388 ? TypeMismatchError
371389
390 You can also copy a literal word to a word table.
391
392 | word table many
393 |
394 | routine main
395 | inputs many
396 | outputs many
397 | trashes a, x, n, z
398 | {
399 | ld x, 0
400 | copy 9999, many + x
401 | }
402 = ok
403
372404 ### add ###
373405
374406 Can't `add` from or to a memory location that isn't initialized.
560592 | }
561593 ? ForbiddenWriteError: a in main
562594
595 You can `sub` a word constant from a word memory location.
596
597 | word score
598 | routine main
599 | inputs a, score
600 | outputs score
601 | trashes a, c, z, v, n
602 | {
603 | st on, c
604 | sub score, 1999
605 | }
606 = ok
607
608 `sub`ing a word constant from a word memory location trashes `a`.
609
610 | word score
611 | routine main
612 | inputs a, score
613 | outputs score, a
614 | trashes c, z, v, n
615 | {
616 | st on, c
617 | sub score, 1999
618 | }
619 ? UnmeaningfulOutputError: a in main
620
621 You can `sub` a word memory location from another word memory location.
622
623 | word score
624 | word delta
625 | routine main
626 | inputs score, delta
627 | outputs score
628 | trashes a, c, z, v, n
629 | {
630 | st off, c
631 | sub score, delta
632 | }
633 = ok
634
635 `sub`ing a word memory location from a word memory location trashes `a`.
636
637 | word score
638 | word delta
639 | routine main
640 | inputs score, delta
641 | outputs score
642 | trashes c, z, v, n
643 | {
644 | st off, c
645 | sub score, delta
646 | }
647 ? ForbiddenWriteError: a in main
648
563649 ### inc ###
564650
565651 Location must be initialized and writeable.
10011087 | ld x, a
10021088 | }
10031089 ? UnmeaningfulReadError: a in main
1090
1091 ### trash ###
1092
1093 Trash does nothing except indicate that we do not care about the value anymore.
1094
1095 | routine foo
1096 | inputs a
1097 | outputs x
1098 | trashes a, z, n
1099 | {
1100 | st a, x
1101 | ld a, 0
1102 | trash a
1103 | }
1104 = ok
1105
1106 | routine foo
1107 | inputs a
1108 | outputs a, x
1109 | trashes z, n
1110 | {
1111 | st a, x
1112 | ld a, 0
1113 | trash a
1114 | }
1115 ? UnmeaningfulOutputError: a in foo
1116
1117 | routine foo
1118 | inputs a
1119 | outputs x
1120 | trashes a, z, n
1121 | {
1122 | st a, x
1123 | trash a
1124 | st a, x
1125 | }
1126 ? UnmeaningfulReadError: a in foo
10041127
10051128 ### if ###
10061129
10631186 | }
10641187 | }
10651188 ? InconsistentInitializationError: x
1189
1190 However, this only pertains to initialization. If a value is already
1191 initialized, either because it was set previous to the `if`, or is an
1192 input to the routine, and it is initialized in one branch, it need not
1193 be initialized in the other.
1194
1195 | routine foo
1196 | inputs x
1197 | outputs x
1198 | trashes a, z, n, c
1199 | {
1200 | ld a, 0
1201 | cmp a, 42
1202 | if z {
1203 | ld x, 7
1204 | } else {
1205 | ld a, 23
1206 | }
1207 | }
1208 = ok
10661209
10671210 An `if` with a single block is analyzed as if it had an empty `else` block.
10681211
11741317 | }
11751318 ? UnmeaningfulReadError: z in main
11761319
1320 The body of `repeat forever` can be empty.
1321
1322 | routine main
1323 | {
1324 | repeat {
1325 | } forever
1326 | }
1327 = ok
1328
11771329 ### copy ###
11781330
11791331 Can't `copy` from a memory location that isn't initialized.
12331385 | {
12341386 | copy 0, lives
12351387 | }
1236 ? ForbiddenWriteError: a in main
1388 ? ForbiddenWriteError: n in main
12371389
12381390 a, z, and n are trashed, and must not be declared as outputs.
12391391
12431395 | {
12441396 | copy 0, lives
12451397 | }
1246 ? UnmeaningfulOutputError: a in main
1398 ? UnmeaningfulOutputError: n in main
12471399
12481400 Unless of course you subsequently initialize them.
12491401
115115 = $080D LDA $0811
116116 = $0810 RTS
117117 = $0811 .byte $03
118
119 Word memory locations with explicit address, initial value.
120
121 | word w1 @ 60001
122 | word w2 : 3003
123 |
124 | routine main
125 | inputs w1
126 | outputs w2
127 | trashes a, z, n
128 | {
129 | copy w1, w2
130 | }
131 = $080D LDA $EA61
132 = $0810 STA $081A
133 = $0813 LDA $EA62
134 = $0816 STA $081B
135 = $0819 RTS
136 = $081A .byte $BB
137 = $081B .byte $0B
138
139 Initialized byte table.
140
141 | byte table message : "WHAT?"
142 |
143 | routine main
144 | inputs message
145 | outputs x, a, z, n
146 | {
147 | ld x, 0
148 | ld a, message + x
149 | }
150 = $080D LDX #$00
151 = $080F LDA $0813,X
152 = $0812 RTS
153 = $0813 .byte $57
154 = $0814 PHA
155 = $0815 EOR ($54,X)
156 = $0817 .byte $3F
157 = $0818 BRK
158 = $0819 BRK
159 = $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
118408
119409 Some instructions.
120410
296586 = $0810 JMP $080F
297587 = $0813 RTS
298588
589 The body of `repeat forever` can be empty.
590
591 | routine main
592 | {
593 | repeat {
594 | } forever
595 | }
596 = $080D JMP $080D
597 = $0810 RTS
598
299599 Indexed access.
300600
301601 | byte one
402702 = $0812 LDA #$07
403703 = $0814 STA $0819
404704 = $0817 RTS
705
706 You can also copy a literal word to a word table.
707
708 | word table many
709 |
710 | routine main
711 | inputs many
712 | outputs many
713 | trashes a, x, n, z
714 | {
715 | ld x, 0
716 | copy 9999, many + x
717 | }
718 = $080D LDX #$00
719 = $080F LDA #$0F
720 = $0811 STA $081A,X
721 = $0814 LDA #$27
722 = $0816 STA $091A,X
723 = $0819 RTS
405724
406725 Copy vector to vector.
407726
575894 = $081D STA $0822
576895 = $0820 RTS
577896
897 Subtracting a constant word from a word memory location.
898
899 | word score
900 | routine main
901 | inputs score
902 | outputs score
903 | trashes a, c, z, v, n
904 | {
905 | st on, c
906 | sub score, 1999
907 | }
908 = $080D SEC
909 = $080E LDA $081F
910 = $0811 SBC #$CF
911 = $0813 STA $081F
912 = $0816 LDA $0820
913 = $0819 SBC #$07
914 = $081B STA $0820
915 = $081E RTS
916
917 Subtracting a word memory location from another word memory location.
918
919 | word score
920 | word delta
921 | routine main
922 | inputs score, delta
923 | outputs score
924 | trashes a, c, z, v, n
925 | {
926 | st on, c
927 | sub score, delta
928 | }
929 = $080D SEC
930 = $080E LDA $0821
931 = $0811 SBC $0823
932 = $0814 STA $0821
933 = $0817 LDA $0822
934 = $081A SBC $0824
935 = $081D STA $0822
936 = $0820 RTS
937
578938 ### Buffers and Pointers
579939
580940 Load address into pointer.
6441004 = $081A STA ($FE),Y
6451005 = $081C RTS
6461006
647 Read through a pointer.
1007 Read through a pointer, into a byte storage location, or the `a` register.
6481008
6491009 | buffer[2048] buf
6501010 | pointer ptr @ 254
6581018 | ld y, 0
6591019 | copy ^buf, ptr
6601020 | copy [ptr] + y, foo
1021 | copy [ptr] + y, a
6611022 | }
6621023 = $080D LDY #$00
663 = $080F LDA #$1D
1024 = $080F LDA #$1F
6641025 = $0811 STA $FE
6651026 = $0813 LDA #$08
6661027 = $0815 STA $FF
6671028 = $0817 LDA ($FE),Y
668 = $0819 STA $101D
669 = $081C RTS
1029 = $0819 STA $101F
1030 = $081C LDA ($FE),Y
1031 = $081E RTS
6701032
6711033 Add a word memory location, and a literal word, to a pointer, and then read through it.
6721034 Note that this is *not* range-checked. (Yet.)
7121074 = $083B LDA ($FE),Y
7131075 = $083D STA $1041
7141076 = $0840 RTS
1077
1078 ### Trash
1079
1080 Trash does nothing except indicate that we do not care about the value anymore.
1081
1082 | routine main
1083 | inputs a
1084 | outputs x
1085 | trashes a, z, n
1086 | {
1087 | ld x, a
1088 | ld a, 0
1089 | trash a
1090 | }
1091 = $080D TAX
1092 = $080E LDA #$00
1093 = $0810 RTS
6767 | outputs a
6868 | trashes x
6969 | @ 65487
70 = ok
71
72 Trash.
73
74 | routine main {
75 | trash a
76 | trash n
77 | }
7078 = ok
7179
7280 If with not
177185 | }
178186 = ok
179187
188 Initialized byte table.
189
190 | byte table message : "WHAT DO YOU WANT TO DO NEXT?"
191 |
192 | routine main {
193 | }
194 = ok
195
196 Can't initialize anything but a byte table with a string.
197
198 | word message : "WHAT DO YOU WANT TO DO NEXT?"
199 |
200 | routine main {
201 | }
202 ? SyntaxError
203
180204 Can't access an undeclared memory location.
181205
182206 | routine main {
237261 | call x
238262 | }
239263 ? SyntaxError
264
265 But you can call a routine that is yet to be defined, further on.
266
267 | routine main {
268 | ld x, 0
269 | ld y, 1
270 | call up
271 | call up
272 | }
273 | routine up {
274 | ld a, 0
275 | }
276 = ok
240277
241278 Can't define two routines with the same name.
242279
342379 | }
343380 = ok
344381
382 A routine can be copied into a vector before the routine appears in the program,
383 *however*, it must be marked as such with the keyword `forward`.
384
385 | vector cinv inputs cinv, a outputs cinv, x trashes a, x, z, n @ 788
386 | routine main {
387 | with interrupts off {
388 | copy foo, cinv
389 | }
390 | call cinv
391 | }
392 | routine foo {
393 | ld a, 0
394 | }
395 ? SyntaxError: Undefined symbol
396
397 | vector cinv inputs cinv, a outputs cinv, x trashes a, x, z, n @ 788
398 | routine main {
399 | with interrupts off {
400 | copy forward foo, cinv
401 | }
402 | call cinv
403 | }
404 | routine foo {
405 | ld a, 0
406 | }
407 = ok
408
345409 goto.
346410
347411 | routine foo {
349413 | }
350414 | routine main {
351415 | goto foo
416 | }
417 = ok
418
419 | routine main {
420 | goto foo
421 | }
422 | routine foo {
423 | ld a, 0
352424 | }
353425 = ok
354426