git @ Cat's Eye Technologies SixtyPical / 0.9
Merge pull request #3 from catseye/develop-0.9 Develop 0.9 Chris Pressey authored 3 years ago GitHub committed 3 years ago
18 changed file(s) with 1213 addition(s) and 107 deletion(s). Raw diff Collapse all Expand all
00 *.pyc
1 vicerc
00 History of SixtyPical
11 =====================
2
3 0.9
4 ---
5
6 * Add word (constant or memory location) to word memory location.
7 * Add word to pointer (unchecked for now).
8 * Added `word table` type.
9 * Can `copy` from word storage location to word table and back.
10 * A `vector` can name itself in its `inputs` and `outputs` or `trashes` sets.
11 * Implementation: `--debug` shows some extra info during analysis.
12 * Fixed bug where `copy`ing literal word into word storage used wrong endianness.
13 * Fixed bug where every memory location was allocated 2 bytes of storage, regardless of type.
14 * Tests: use https://github.com/tcarmelveilleux/dcc6502 to disassemble code for comparison.
215
316 0.8
417 ---
2121 The reference implementation can execute, analyze, and compile SixtyPical
2222 programs to 6502 machine code.
2323
24 It is a **work in progress**, currently at the **proof-of-concept** stage.
25
26 The current development version of SixtyPical is 0.8.
24 SixtyPical is a work in progress. The current released version of SixtyPical
25 is 0.9.
2726
2827 Documentation
2928 -------------
4039 TODO
4140 ----
4241
43 ### Add 16 bit values.
42 ### Demo game
4443
45 I guess this means making `add` a bit more like `copy`.
44 Finish the little demo "game" where you can move a block around the screen with
45 the joystick (i.e. bring it up to par with the original demo game that was written
46 for SixtyPical)
4647
47 And then: add to pointer. (Not necessarily range-checked yet though.)
48 ### `call` routines that are defined further down in the source code
4849
49 And then write a little demo "game" where you can move a block around the screen with
50 the joystick.
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`.
5152
52 ### `word table` and `vector table` types
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
58 ### `vector table` type
5359
5460 ### `low` and `high` address operators
5561
5662 To turn `word` type into `byte`.
5763
58 ### save registers on stack
64 ### Save registers on stack
5965
60 This preserves them, so semantically, they can be used even though they
66 This preserves them, so that, semantically, they can be used later even though they
6167 are trashed inside the block.
68
69 ### Range checking in the abstract interpretation
70
71 If you copy the address of a buffer (say it is size N) to a pointer, it is valid.
72 If you add a value from 0 to N-1 to the pointer, it is still valid.
73 But if you add a value ≥ N to it, it becomes invalid.
74 This should be tracked in the abstract interpretation.
75 (If only because abstract interpretation is the major point of this project!)
6276
6377 ### And at some point...
6478
79 * Compare word (constant or memory location) with memory location or pointer. (Maybe?)
6580 * `copy x, [ptr] + y`
6681 * Maybe even `copy [ptra] + y, [ptrb] + y`, which can be compiled to indirect LDA then indirect STA!
6782 * Check that the buffer being read or written to through pointer, appears in approporiate inputs or outputs set.
83 * `byte table` and `word table` of sizes other than 256
6884 * initialized `byte table` memory locations
6985 * always analyze before executing or compiling, unless told not to
7086 * `trash` instruction.
71 * `interrupt` routines.
72 * 6502-mnemonic aliases (`sec`, `clc`)
73 * other handy aliases (`eq` for `z`, etc.)
74 * have `copy` instruction able to copy a constant to a user-def mem loc, etc.
87 * `interrupt` routines -- to indicate that "the supervisor" has stored values on the stack, so we can trash them.
88 * pre-initialized `word` variables
89 * 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.
7591 * add absolute addressing in shl/shr, absolute-indexed for add, sub, etc.
7692 * check and disallow recursion.
7793 * automatic tail-call optimization (could be tricky, w/constraints?)
5555
5656 if options.analyze:
5757 try:
58 analyzer = Analyzer()
58 analyzer = Analyzer(debug=options.debug)
5959 analyzer.analyze_program(program)
6060 except Exception as e:
6161 if options.traceback:
00 SixtyPical
11 ==========
22
3 This document describes the SixtyPical programming language version 0.8,
3 This document describes the SixtyPical programming language version 0.9,
44 both its execution aspect and its static analysis aspect (even though
55 these are, technically speaking, separate concepts.)
66
2424
2525 There are also two *type constructors*:
2626
27 * X table (256 entries, each holding a value of type X, where X is `byte`)
27 * T table (256 entries, each holding a value of type T, where T is either
28 `byte` or `word`)
2829 * buffer[N] (N entries; each entry is a byte; N is a power of 2, ≤ 64K)
2930
3031 Memory locations
300301 and initializing them afterwards.
301302
302303 dest and src continue to be initialized afterwards.
304
305 In addition, if dest is of `word` type, then src must also be of `word`
306 type, and in this case this instruction trashes the `a` register.
307
308 NOTE: If dest is a pointer, the addition does not check if the result of
309 the pointer arithmetic continues to be valid (within a buffer) or not.
303310
304311 ### inc ###
305312
0 word score
1 routine main
2 inputs score
3 outputs score
4 trashes a, c, z, v, n
5 {
6 st off, c
7 add score, 1999
8 }
0 // ****************************
1 // * Demo Game for SixtyPical *
2 // ****************************
3
4 // ----------------------------------------------------------------
5 // System Locations
6 // ----------------------------------------------------------------
7
8 byte vic_border @ 53280
9 byte vic_bg @ 53281
10
11 byte table screen1 @ 1024
12 byte table screen2 @ 1274
13 byte table screen3 @ 1524
14 byte table screen4 @ 1774
15
16 byte table colormap1 @ 55296
17 byte table colormap2 @ 55546
18 byte table colormap3 @ 55796
19 byte table colormap4 @ 56046
20
21 buffer[2048] screen @ 1024
22 byte joy2 @ $dc00
23
24 // ----------------------------------------------------------------
25 // Global Variables
26 // ----------------------------------------------------------------
27
28 pointer ptr @ 254
29 word pos
30 word delta
31 byte button_down : 0 // effectively static-local to check_button
32
33 //
34 // Points to the routine that implements the current game state.
35 //
36
37 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
41
42 //
43 // The constraints on these 2 vectors are kind-of sort-of big fibs.
44 // They're only written this way so they can be compatible with our
45 // routine. In fact, CINV is an interrupt routine where it doesn't
46 // really matter what you trash anyway, because all registers were
47 /// saved by the caller (the KERNAL) and will be restored by the end
48 // of the code of the saved origin cinv routine that we goto.
49 //
50 // I wonder if this could be arranged somehow to be less fibby, in
51 // a future version of SixtyPical.
52 //
53
54 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
58 @ 788
59
60 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
64
65 // ----------------------------------------------------------------
66 // Utility Routines
67 // ----------------------------------------------------------------
68
69 routine read_stick
70 inputs joy2
71 outputs delta
72 trashes a, x, z, n
73 {
74 ld x, joy2
75 ld a, x
76 and a, 1 // up
77 if z {
78 copy $ffd8, delta // -40
79 } else {
80 ld a, x
81 and a, 2 // down
82 if z {
83 copy word 40, delta
84 } else {
85 ld a, x
86 and a, 4 // left
87 if z {
88 copy $ffff, delta // -1
89 } else {
90 ld a, x
91 and a, 8 // right
92 if z {
93 copy word 1, delta
94 } else {
95 copy word 0, delta
96 }
97 }
98 }
99 }
100 }
101
102 // You can repeatedly (i.e. as part of actor logic or an IRQ handler)
103 // call this routine.
104 // Upon return, if carry is set, the button was pressed then released.
105
106 routine check_button
107 inputs joy2, button_down
108 outputs c, button_down
109 trashes a, z, n
110 {
111 ld a, button_down
112 if z {
113 ld a, joy2
114 and a, $10
115 if z {
116 ld a, 1
117 st a, button_down
118 }
119 st off, c
120 } else {
121 ld a, joy2
122 and a, $10
123 if not z {
124 ld a, 0
125 st a, button_down
126 st on, c
127 } else {
128 st off, c
129 }
130 }
131 }
132
133 routine clear_screen
134 outputs screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
135 trashes a, y, c, n, z
136 {
137 ld y, 0
138 repeat {
139 ld a, 1
140 st a, colormap1 + y
141 st a, colormap2 + y
142 st a, colormap3 + y
143 st a, colormap4 + y
144
145 ld a, 32
146 st a, screen1 + y
147 st a, screen2 + y
148 st a, screen3 + y
149 st a, screen4 + y
150
151 inc y
152 cmp y, 250
153 } until z
154 }
155
156 // ----------------------------------------------------------------
157 // Game States
158 // ----------------------------------------------------------------
159
160 //
161 // Because these all `goto save_cinv` at the end, they must have the same signature as that routine.
162 //
163
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
178 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
190 repeat {
191 ld a, 90
192 st a, screen1 + y
193 inc y
194 cmp y, 20
195 } until z
196
197 st off, c
198 call check_button
199
200 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
207 }
208
209 goto save_cinv
210 }
211
212 // *************************
213 // * Main Game Loop Driver *
214 // *************************
215
216 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
220 {
221 goto dispatch_game_state
222 }
223
224 routine main
225 inputs cinv
226 outputs cinv, save_cinv, pos, dispatch_game_state,
227 screen1, screen2, screen3, screen4, colormap1, colormap2, colormap3, colormap4
228 trashes a, y, n, c, z, vic_border, vic_bg
229 {
230 ld a, 5
231 st a, vic_border
232 ld a, 0
233 st a, vic_bg
234 ld y, 0
235
236 call clear_screen
237
238 copy game_state_title_screen, dispatch_game_state
239
240 copy word 0, pos
241 with interrupts off {
242 copy cinv, save_cinv
243 copy our_cinv, cinv
244 }
245 // FIXME: find out why `repeat { } forever` does not analyze OK
246 repeat {
247 ld a, 0
248 } until not z
249 }
0 word one
1 word table many
2
3 routine main
4 inputs one, many
5 outputs one, many
6 trashes a, x, y, n, z
7 {
8 ld x, 0
9 ld y, 0
10 copy 777, one
11 copy one, many + x
12 copy one, many + y
13 copy many + x, one
14 copy many + y, one
15 }
00 #!/bin/sh
11
22 SRC=$1
3 if [ "X$1" = "X" ]; then
4 echo "Usage: ./loadngo.sh <source.60p>"
5 exit 1
6 fi
37 OUT=/tmp/a-out.prg
4 bin/sixtypical --analyze --compile --basic-prelude $SRC > $OUT || exit 1
5 x64 $OUT
8 bin/sixtypical --traceback --analyze --compile --basic-prelude $SRC > $OUT || exit 1
9 if [ -e vicerc ]; then
10 x64 -config vicerc $OUT
11 else
12 x64 $OUT
13 fi
614 rm -f $OUT
11
22 from sixtypical.ast import Program, Routine, Block, Instr
33 from sixtypical.model import (
4 TYPE_BYTE, TYPE_BYTE_TABLE, BufferType, PointerType, VectorType, ExecutableType,
5 ConstantRef, LocationRef, IndirectRef, AddressRef,
4 TYPE_BYTE, TYPE_WORD, TYPE_BYTE_TABLE, TYPE_WORD_TABLE, BufferType, PointerType, VectorType, ExecutableType,
5 ConstantRef, LocationRef, IndirectRef, IndexedRef, AddressRef,
66 REG_A, REG_Y, FLAG_Z, FLAG_N, FLAG_V, FLAG_C
77 )
88
8787 raise InconsistentConstraintsError('%s in %s' % (ref.name, routine.name))
8888 self._writeable.add(ref)
8989
90 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
100 return "Context(\n _touched={},\n _meaningful={},\n _writeable={}\n)".format(
101 locsetstr(self._touched), locsetstr(self._meaningful), locsetstr(self._writeable)
102 )
103
90104 def clone(self):
91105 c = Context(self.routines, self.routine, [], [], [])
92106 c._touched = set(self._touched)
156170
157171 class Analyzer(object):
158172
159 def __init__(self):
173 def __init__(self, debug=False):
160174 self.current_routine = None
161175 self.has_encountered_goto = False
162176 self.routines = {}
177 self.debug = debug
163178
164179 def assert_type(self, type, *locations):
165180 for location in locations:
167182 raise TypeMismatchError('%s in %s' %
168183 (location.name, self.current_routine.name)
169184 )
185
186 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
198 overage = affected - limited_to
199 if not overage:
200 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)
203 )
204 raise IncompatibleConstraintsError(message)
170205
171206 def analyze_program(self, program):
172207 assert isinstance(program, Program)
183218 return
184219 type = routine.location.type
185220 context = Context(self.routines, routine, type.inputs, type.outputs, type.trashes)
221 if self.debug:
222 print "at start of routine `{}`:".format(routine.name)
223 print context
186224 self.analyze_block(routine.block, context)
225 if self.debug:
226 print "at end of routine `{}`:".format(routine.name)
227 print context
187228 if not self.has_encountered_goto:
188229 for ref in type.outputs:
189230 context.assert_meaningful(ref, exception_class=UnmeaningfulOutputError)
232273 )
233274 context.assert_meaningful(src)
234275 context.set_written(dest)
235 elif opcode in ('add', 'sub'):
276 elif opcode == 'add':
277 context.assert_meaningful(src, dest, FLAG_C)
278 if src.type == TYPE_BYTE:
279 self.assert_type(TYPE_BYTE, src, dest)
280 context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
281 else:
282 self.assert_type(TYPE_WORD, src)
283 if dest.type == TYPE_WORD:
284 context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
285 context.set_touched(REG_A)
286 context.set_unmeaningful(REG_A)
287 elif isinstance(dest.type, PointerType):
288 context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
289 context.set_touched(REG_A)
290 context.set_unmeaningful(REG_A)
291 else:
292 self.assert_type(TYPE_WORD, dest)
293 elif opcode == 'sub':
236294 self.assert_type(TYPE_BYTE, src, dest)
237295 context.assert_meaningful(src, dest, FLAG_C)
238296 context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V)
309367 pass
310368 else:
311369 raise TypeMismatchError((src, dest))
370
371 elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
372 if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
373 pass
374 else:
375 raise TypeMismatchError((src, dest))
376
377 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
378 if src.ref.type == TYPE_WORD_TABLE and dest.type == TYPE_WORD:
379 pass
380 else:
381 raise TypeMismatchError((src, dest))
382
312383 elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef):
313384 if src.type == dest.type:
314385 pass
315 elif isinstance(src.type, ExecutableType) and \
316 isinstance(dest.type, VectorType):
317 # if dealing with routines and vectors,
318 # check that they're not incompatible
319 if not (src.type.inputs <= dest.type.inputs):
320 raise IncompatibleConstraintsError(src.type.inputs - dest.type.inputs)
321 if not (src.type.outputs <= dest.type.outputs):
322 raise IncompatibleConstraintsError(src.type.outputs - dest.type.outputs)
323 if not (src.type.trashes <= dest.type.trashes):
324 raise IncompatibleConstraintsError(src.type.trashes - dest.type.trashes)
386 elif isinstance(src.type, ExecutableType) and isinstance(dest.type, VectorType):
387 self.assert_affected_within('inputs', src.type.inputs, dest.type.inputs)
388 self.assert_affected_within('outputs', src.type.outputs, dest.type.outputs)
389 self.assert_affected_within('trashes', src.type.trashes, dest.type.trashes)
325390 else:
326391 raise TypeMismatchError((src, dest))
327392 else:
336401 elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef):
337402 context.assert_meaningful(src.ref, REG_Y)
338403 # TODO this will need to be more sophisticated. the thing ref points to is touched, as well.
339 context.set_touched(src.ref)
404 context.set_touched(src.ref) # TODO and REG_Y? if not, why not?
405 context.set_written(dest)
406 elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
407 context.assert_meaningful(src, dest.ref, dest.index)
408 context.set_touched(src) # TODO and dest.index?
409 context.set_written(dest.ref)
410 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
411 context.assert_meaningful(src.ref, src.index, dest)
412 context.set_touched(dest) # TODO and src.index?
340413 context.set_written(dest)
341414 else:
342415 context.assert_meaningful(src)
349422 self.analyze_block(instr.block, context)
350423 elif opcode == 'goto':
351424 location = instr.location
352 type = location.type
353
354 if not isinstance(type, ExecutableType):
425 type_ = location.type
426
427 if not isinstance(type_, ExecutableType):
355428 raise TypeMismatchError(location)
356429
357430 # assert that the dest routine's inputs are all initialized
358 for ref in type.inputs:
431 for ref in type_.inputs:
359432 context.assert_meaningful(ref)
360433
361434 # and that this routine's trashes and output constraints are a
362435 # superset of the called routine's
363436 current_type = self.current_routine.location.type
364 if not (type.outputs <= current_type.outputs):
365 raise IncompatibleConstraintsError(type.outputs - current_type.outputs)
366 if not (type.trashes <= current_type.trashes):
367 raise IncompatibleConstraintsError(type.trashes - current_type.trashes)
437 self.assert_affected_within('outputs', type_.outputs, current_type.outputs)
438 self.assert_affected_within('trashes', type_.trashes, current_type.trashes)
439
368440 self.has_encountered_goto = True
369441 else:
370442 raise NotImplementedError(opcode)
11
22 from sixtypical.ast import Program, Routine, Block, Instr
33 from sixtypical.model import (
4 ConstantRef, LocationRef, IndirectRef, AddressRef,
5 TYPE_BIT, TYPE_BYTE, TYPE_WORD, BufferType, PointerType, RoutineType, VectorType,
4 ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef,
5 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 )
88 from sixtypical.emitter import Byte, Label, Offset, LowAddressByte, HighAddressByte
3434 assert isinstance(program, Program)
3535
3636 for defn in program.defns:
37 label = Label(defn.name)
38 if defn.addr is not None:
39 label.set_addr(defn.addr)
40 self.labels[defn.name] = label
37 # compute length of memory pointed to. this is awful.
38 length = None
39 type_ = defn.location.type
40 if type_ == TYPE_BYTE:
41 length = 1
42 elif type_ == TYPE_WORD or isinstance(type_, (PointerType, VectorType)):
43 length = 2
44 elif type_ == TYPE_BYTE_TABLE:
45 length = 256
46 elif type_ == TYPE_WORD_TABLE:
47 length = 512
48 elif isinstance(type_, BufferType):
49 length = type_.size
50 if length is None:
51 raise NotImplementedError("Need size for type {}".format(type_))
52 self.labels[defn.name] = Label(defn.name, addr=defn.addr, length=length)
4153
4254 for routine in program.routines:
4355 self.routines[routine.name] = routine
5971 for defn in program.defns:
6072 if defn.initial is not None:
6173 label = self.labels[defn.name]
74 initial_data = Byte(defn.initial) # TODO: support other types than Byte
75 label.set_length(initial_data.size())
6276 self.emitter.resolve_label(label)
63 self.emitter.emit(Byte(defn.initial))
77 self.emitter.emit(initial_data)
6478
6579 # uninitialized, "BSS" data
6680 for defn in program.defns:
146160 self.emitter.emit(ADC(Immediate(Byte(src.value))))
147161 else:
148162 self.emitter.emit(ADC(Absolute(self.labels[src.name])))
163 elif isinstance(dest, LocationRef) and src.type == TYPE_WORD and dest.type == TYPE_WORD:
164 if isinstance(src, ConstantRef):
165 dest_label = self.labels[dest.name]
166 self.emitter.emit(LDA(Absolute(dest_label)))
167 self.emitter.emit(ADC(Immediate(Byte(src.low_byte()))))
168 self.emitter.emit(STA(Absolute(dest_label)))
169 self.emitter.emit(LDA(Absolute(Offset(dest_label, 1))))
170 self.emitter.emit(ADC(Immediate(Byte(src.high_byte()))))
171 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
172 elif isinstance(src, LocationRef):
173 src_label = self.labels[src.name]
174 dest_label = self.labels[dest.name]
175 self.emitter.emit(LDA(Absolute(dest_label)))
176 self.emitter.emit(ADC(Absolute(src_label)))
177 self.emitter.emit(STA(Absolute(dest_label)))
178 self.emitter.emit(LDA(Absolute(Offset(dest_label, 1))))
179 self.emitter.emit(ADC(Absolute(Offset(src_label, 1))))
180 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
181 else:
182 raise UnsupportedOpcodeError(instr)
183 elif isinstance(dest, LocationRef) and src.type == TYPE_WORD and isinstance(dest.type, PointerType):
184 if isinstance(src, ConstantRef):
185 dest_label = self.labels[dest.name]
186 self.emitter.emit(LDA(ZeroPage(dest_label)))
187 self.emitter.emit(ADC(Immediate(Byte(src.low_byte()))))
188 self.emitter.emit(STA(ZeroPage(dest_label)))
189 self.emitter.emit(LDA(ZeroPage(Offset(dest_label, 1))))
190 self.emitter.emit(ADC(Immediate(Byte(src.high_byte()))))
191 self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1))))
192 elif isinstance(src, LocationRef):
193 src_label = self.labels[src.name]
194 dest_label = self.labels[dest.name]
195 self.emitter.emit(LDA(ZeroPage(dest_label)))
196 self.emitter.emit(ADC(Absolute(src_label)))
197 self.emitter.emit(STA(ZeroPage(dest_label)))
198 self.emitter.emit(LDA(ZeroPage(Offset(dest_label, 1))))
199 self.emitter.emit(ADC(Absolute(Offset(src_label, 1))))
200 self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1))))
201 else:
202 raise UnsupportedOpcodeError(instr)
149203 else:
150204 raise UnsupportedOpcodeError(instr)
151205 elif opcode == 'sub':
304358 self.emitter.emit(STA(ZeroPage(dest_label)))
305359 self.emitter.emit(LDA(Immediate(LowAddressByte(src_label))))
306360 self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1))))
361 elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef):
362 if src.type == TYPE_WORD and dest.ref.type == TYPE_WORD_TABLE:
363 src_label = self.labels[src.name]
364 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)
372 self.emitter.emit(LDA(Absolute(src_label)))
373 self.emitter.emit(STA(addressing_mode(dest_label)))
374 self.emitter.emit(LDA(Absolute(Offset(src_label, 1))))
375 self.emitter.emit(STA(addressing_mode(Offset(dest_label, 256))))
376 else:
377 raise NotImplementedError
378 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
379 if src.ref.type == TYPE_WORD_TABLE and dest.type == TYPE_WORD:
380 src_label = self.labels[src.ref.name]
381 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))))
392 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
393 else:
394 raise NotImplementedError
395
307396 elif not isinstance(src, (ConstantRef, LocationRef)) or not isinstance(dest, LocationRef):
308397 raise NotImplementedError((src, dest))
309398 elif src.type == TYPE_BYTE and dest.type == TYPE_BYTE:
317406 elif src.type == TYPE_WORD and dest.type == TYPE_WORD:
318407 if isinstance(src, ConstantRef):
319408 dest_label = self.labels[dest.name]
320 hi = (src.value >> 8) & 255
321 lo = src.value & 255
322 self.emitter.emit(LDA(Immediate(Byte(hi))))
323 self.emitter.emit(STA(Absolute(dest_label)))
324 self.emitter.emit(LDA(Immediate(Byte(lo))))
409 self.emitter.emit(LDA(Immediate(Byte(src.low_byte()))))
410 self.emitter.emit(STA(Absolute(dest_label)))
411 self.emitter.emit(LDA(Immediate(Byte(src.high_byte()))))
325412 self.emitter.emit(STA(Absolute(Offset(dest_label, 1))))
326413 else:
327414 src_label = self.labels[src.name]
0 """Binary machine code emitter. Used in SixtyPical to emit 6502 machine code,
1 but not specific to SixtyPical, or 6502. Not even necessarily machine code -
2 though some parts are written around the assumptions of 8-bit architectures."""
3
4
05 class Emittable(object):
16 def size(self):
27 raise NotImplementedError
4146 return "%s(%r)" % (self.__class__.__name__, self.value)
4247
4348
49 class Table(Emittable):
50 def size(self):
51 return 256
52
53 def serialize(self, addr=None):
54 return chr(0) * self.size()
55
56 def __repr__(self):
57 return "%s()" % (self.__class__.__name__)
58
59
4460 class Label(Emittable):
45 def __init__(self, name, addr=None):
61 def __init__(self, name, addr=None, length=None):
4662 self.name = name
4763 self.addr = addr
64 self.length = length
4865
4966 def set_addr(self, addr):
5067 self.addr = addr
68
69 def set_length(self, length):
70 self.length = length
5171
5272 def size(self):
5373 return 2
6585 return Byte(self.addr + offset).serialize()
6686
6787 def __repr__(self):
68 addrs = ', addr=%r' % self.addr if self.addr is not None else ''
69 return "%s(%r%s)" % (self.__class__.__name__, self.name, addrs)
88 addr_s = ', addr=%r' % self.addr if self.addr is not None else ''
89 length_s = ', length=%r' % self.length if self.length is not None else ''
90 return "%s(%r%s%s)" % (self.__class__.__name__, self.name, addr_s, length_s)
7091
7192
7293 class Offset(Emittable):
153174 """Set the given label to be at the current address and
154175 advance the address for the next label, but don't emit anything."""
155176 self.resolve_label(label)
156 self.addr += label.size()
177 self.addr += label.length
00 """Data/storage model for SixtyPical."""
1
12
23 class Type(object):
34 def __init__(self, name):
105106 self.ref = ref
106107
107108 def __eq__(self, other):
108 return self.ref == other.ref
109 return isinstance(other, self.__class__) and self.ref == other.ref
109110
110111 def __hash__(self):
111112 return hash(self.__class__.name) ^ hash(self.ref)
116117 @property
117118 def name(self):
118119 return '[{}]+y'.format(self.ref.name)
120
121 def is_constant(self):
122 return False
123
124
125 class IndexedRef(Ref):
126 def __init__(self, ref, index):
127 self.ref = ref
128 self.index = index
129
130 def __eq__(self, other):
131 return isinstance(other, self.__class__) and self.ref == other.ref and self.index == other.index
132
133 def __hash__(self):
134 return hash(self.__class__.name) ^ hash(self.ref) ^ hash(self.index)
135
136 def __repr__(self):
137 return '%s(%r, %r)' % (self.__class__.__name__, self.ref, self.index)
138
139 @property
140 def name(self):
141 return '{}+{}'.format(self.ref.name, self.index.name)
119142
120143 def is_constant(self):
121144 return False
190213 def is_constant(self):
191214 return True
192215
216 def high_byte(self):
217 return (self.value >> 8) & 255
218
219 def low_byte(self):
220 return self.value & 255
221
193222
194223 REG_A = LocationRef(TYPE_BYTE, 'a')
195224 REG_X = LocationRef(TYPE_BYTE, 'x')
33 from sixtypical.model import (
44 TYPE_BIT, TYPE_BYTE, TYPE_BYTE_TABLE, TYPE_WORD, TYPE_WORD_TABLE,
55 RoutineType, VectorType, ExecutableType, BufferType, PointerType,
6 LocationRef, ConstantRef, IndirectRef, AddressRef,
6 LocationRef, ConstantRef, IndirectRef, IndexedRef, AddressRef,
77 )
88 from sixtypical.scanner import Scanner
99
2727 if name not in self.symbols:
2828 raise SyntaxError('Undefined symbol "%s"' % name)
2929 return self.symbols[name].model
30
31 # --- grammar productions
3032
3133 def program(self):
3234 defns = []
4648 self.symbols[name] = SymEntry(routine, routine.location)
4749 routines.append(routine)
4850 self.scanner.check_type('EOF')
51 # now backpatch the executable types.
52 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])
58 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])
4964 return Program(defns=defns, routines=routines)
5065
5166 def defn(self):
107122 outputs = set()
108123 trashes = set()
109124 if self.scanner.consume('inputs'):
110 inputs = set(self.locexprs())
125 inputs = set(self.labels())
111126 if self.scanner.consume('outputs'):
112 outputs = set(self.locexprs())
127 outputs = set(self.labels())
113128 if self.scanner.consume('trashes'):
114 trashes = set(self.locexprs())
129 trashes = set(self.labels())
115130 return (inputs, outputs, trashes)
116131
117132 def routine(self):
136151 location=location
137152 )
138153
154 def labels(self):
155 accum = []
156 accum.append(self.label())
157 while self.scanner.consume(','):
158 accum.append(self.label())
159 return accum
160
161 def label(self):
162 """Like a locexpr, but does not allow literal values, and the labels do not
163 need to be defined yet. They will be resolved at the end of parsing."""
164 loc = self.scanner.token
165 self.scanner.scan()
166 return loc
167
139168 def locexprs(self):
140169 accum = []
141170 accum.append(self.locexpr())
174203 loc = self.locexpr()
175204 return AddressRef(loc)
176205 else:
177 return self.locexpr()
206 loc = self.locexpr()
207 index = None
208 if self.scanner.consume('+'):
209 index = self.locexpr()
210 loc = IndexedRef(loc, index)
211 return loc
178212
179213 def block(self):
180214 instrs = []
205205 | st 0, lives
206206 | }
207207 ? ForbiddenWriteError: lives in main
208
209 Can't `st` a `word` type.
210
211 | word foo
212 |
213 | routine main
214 | outputs foo
215 | trashes a, n, z
216 | {
217 | ld a, 0
218 | st a, foo
219 | }
220 ? TypeMismatchError: a and foo in main
221
222 ### tables ###
208223
209224 Storing to a table, you must use an index, and vice-versa.
210225
312327 | }
313328 = ok
314329
315 Can't `st` a `word` type.
316
317 | word foo
318 |
319 | routine main
320 | outputs foo
321 | trashes a, n, z
322 | {
323 | ld a, 0
324 | st a, foo
325 | }
326 ? TypeMismatchError: a and foo in main
330 Copying to and from a word table.
331
332 | word one
333 | word table many
334 |
335 | routine main
336 | inputs one, many
337 | outputs one, many
338 | trashes a, x, n, z
339 | {
340 | ld x, 0
341 | copy one, many + x
342 | copy many + x, one
343 | }
344 = ok
345
346 | word one
347 | word table many
348 |
349 | routine main
350 | inputs one, many
351 | outputs one, many
352 | trashes a, x, n, z
353 | {
354 | ld x, 0
355 | copy one, many
356 | }
357 ? TypeMismatchError
358
359 | word one
360 | word table many
361 |
362 | routine main
363 | inputs one, many
364 | outputs one, many
365 | trashes a, x, n, z
366 | {
367 | ld x, 0
368 | copy one + x, many
369 | }
370 ? TypeMismatchError
327371
328372 ### add ###
329373
369413 | {
370414 | st off, c
371415 | add a, 0
416 | }
417 ? ForbiddenWriteError: a in main
418
419 You can `add` a word constant to a word memory location.
420
421 | word score
422 | routine main
423 | inputs a, score
424 | outputs score
425 | trashes a, c, z, v, n
426 | {
427 | st off, c
428 | add score, 1999
429 | }
430 = ok
431
432 `add`ing a word constant to a word memory location trashes `a`.
433
434 | word score
435 | routine main
436 | inputs a, score
437 | outputs score, a
438 | trashes c, z, v, n
439 | {
440 | st off, c
441 | add score, 1999
442 | }
443 ? UnmeaningfulOutputError: a in main
444
445 To be sure, `add`ing a word constant to a word memory location trashes `a`.
446
447 | word score
448 | routine main
449 | inputs score
450 | outputs score
451 | trashes c, z, v, n
452 | {
453 | st off, c
454 | add score, 1999
455 | }
456 ? ForbiddenWriteError: a in main
457
458 You can `add` a word memory location to another word memory location.
459
460 | word score
461 | word delta
462 | routine main
463 | inputs score, delta
464 | outputs score
465 | trashes a, c, z, v, n
466 | {
467 | st off, c
468 | add score, delta
469 | }
470 = ok
471
472 `add`ing a word memory location to a word memory location trashes `a`.
473
474 | word score
475 | word delta
476 | routine main
477 | inputs score, delta
478 | outputs score
479 | trashes c, z, v, n
480 | {
481 | st off, c
482 | add score, delta
483 | }
484 ? ForbiddenWriteError: a in main
485
486 You can `add` a word memory location, or a constant, to a pointer.
487
488 | pointer ptr
489 | word delta
490 | routine main
491 | inputs ptr, delta
492 | outputs ptr
493 | trashes a, c, z, v, n
494 | {
495 | st off, c
496 | add ptr, delta
497 | add ptr, word 1
498 | }
499 = ok
500
501 `add`ing a word memory location, or a constant, to a pointer, trashes `a`.
502
503 | pointer ptr
504 | word delta
505 | routine main
506 | inputs ptr, delta
507 | outputs ptr
508 | trashes c, z, v, n
509 | {
510 | st off, c
511 | add ptr, delta
512 | add ptr, word 1
372513 | }
373514 ? ForbiddenWriteError: a in main
374515
13711512 | }
13721513 ? IncompatibleConstraintsError
13731514
1515 "Appropriately" means, if the routine affects no more than what is named
1516 in the input/output sets of the vector.
1517
1518 | vector vec
1519 | inputs a, x
1520 | outputs x
1521 | trashes a, z, n
1522 |
1523 | routine foo
1524 | inputs x
1525 | outputs x
1526 | trashes z, n
1527 | {
1528 | inc x
1529 | }
1530 |
1531 | routine main
1532 | outputs vec
1533 | trashes a, z, n
1534 | {
1535 | copy foo, vec
1536 | }
1537 = ok
1538
13741539 Routines are read-only.
13751540
13761541 | vector vec
66 [Falderal]: http://catseye.tc/node/Falderal
77
88 -> Functionality "Compile SixtyPical program" is implemented by
9 -> shell command "bin/sixtypical --compile %(test-body-file) | fa-bin-to-hex"
9 -> shell command "bin/sixtypical --basic-prelude --compile %(test-body-file) | tests/appliances/bin/dcc6502-adapter"
1010
1111 -> Tests for functionality "Compile SixtyPical program"
1212
1515 | routine main
1616 | {
1717 | }
18 = 00c060
18 = $080D RTS
1919
2020 Rudimentary program.
2121
2727 | st off, c
2828 | add a, 4
2929 | }
30 = 00c018690460
30 = $080D CLC
31 = $080E ADC #$04
32 = $0810 RTS
3133
3234 Call extern.
3335
4345 | ld a, 65
4446 | call chrout
4547 | }
46 = 00c0a94120d2ff60
48 = $080D LDA #$41
49 = $080F JSR $FFD2
50 = $0812 RTS
4751
4852 Call defined routine.
4953
6165 | {
6266 | call foo
6367 | }
64 = 00c02004c060a900a200a00060
68 = $080D JSR $0811
69 = $0810 RTS
70 = $0811 LDA #$00
71 = $0813 LDX #$00
72 = $0815 LDY #$00
73 = $0817 RTS
6574
6675 Access a defined memory location.
6776
7483 | st y, foo
7584 | ld a, foo
7685 | }
77 = 00c0a0008c09c0ad09c060
86 = $080D LDY #$00
87 = $080F STY $0816
88 = $0812 LDA $0816
89 = $0815 RTS
7890
7991 Memory location with explicit address.
8092
8698 | ld a, 100
8799 | st a, screen
88100 | }
89 = 00c0a9648d000460
101 = $080D LDA #$64
102 = $080F STA $0400
103 = $0812 RTS
90104
91105 Memory location with initial value.
92106
98112 | {
99113 | ld a, lives
100114 | }
101 = 00c0ad04c06003
115 = $080D LDA $0811
116 = $0810 RTS
117 = $0811 .byte $03
102118
103119 Some instructions.
104120
140156 | shl a
141157 | shr a
142158 | }
143 = 00c0a900a200a0008d46c08e46c08c46c0381869016d46c0e901ed46c0ee46c0e8c8ce46c0ca8829ff2d46c009ff0d46c049ff4d46c0c901cd46c0e001ec46c0c001cc46c02a6a60
159 = $080D LDA #$00
160 = $080F LDX #$00
161 = $0811 LDY #$00
162 = $0813 STA $0853
163 = $0816 STX $0853
164 = $0819 STY $0853
165 = $081C SEC
166 = $081D CLC
167 = $081E ADC #$01
168 = $0820 ADC $0853
169 = $0823 SBC #$01
170 = $0825 SBC $0853
171 = $0828 INC $0853
172 = $082B INX
173 = $082C INY
174 = $082D DEC $0853
175 = $0830 DEX
176 = $0831 DEY
177 = $0832 AND #$FF
178 = $0834 AND $0853
179 = $0837 ORA #$FF
180 = $0839 ORA $0853
181 = $083C EOR #$FF
182 = $083E EOR $0853
183 = $0841 CMP #$01
184 = $0843 CMP $0853
185 = $0846 CPX #$01
186 = $0848 CPX $0853
187 = $084B CPY #$01
188 = $084D CPY $0853
189 = $0850 ROL A
190 = $0851 ROR A
191 = $0852 RTS
144192
145193 Compiling `if`.
146194
154202 | ld y, 2
155203 | }
156204 | }
157 = 00c0a900d005a0014c0bc0a00260
205 = $080D LDA #$00
206 = $080F BNE $0816
207 = $0811 LDY #$01
208 = $0813 JMP $0818
209 = $0816 LDY #$02
210 = $0818 RTS
158211
159212 Compiling `if not`.
160213
168221 | ld y, 2
169222 | }
170223 | }
171 = 00c0a900f005a0014c0bc0a00260
224 = $080D LDA #$00
225 = $080F BEQ $0816
226 = $0811 LDY #$01
227 = $0813 JMP $0818
228 = $0816 LDY #$02
229 = $0818 RTS
172230
173231 Compiling `if` without `else`.
174232
180238 | ld y, 1
181239 | }
182240 | }
183 = 00c0a900d002a00160
241 = $080D LDA #$00
242 = $080F BNE $0813
243 = $0811 LDY #$01
244 = $0813 RTS
184245
185246 Compiling `repeat`.
186247
194255 | cmp y, 91
195256 | } until z
196257 | }
197 = 00c0a04198c8c05bd0fa60
258 = $080D LDY #$41
259 = $080F TYA
260 = $0810 INY
261 = $0811 CPY #$5B
262 = $0813 BNE $080F
263 = $0815 RTS
198264
199265 Compiling `repeat until not`.
200266
208274 | cmp y, 91
209275 | } until not z
210276 | }
211 = 00c0a04198c8c05bf0fa60
277 = $080D LDY #$41
278 = $080F TYA
279 = $0810 INY
280 = $0811 CPY #$5B
281 = $0813 BEQ $080F
282 = $0815 RTS
212283
213284 Compiling `repeat forever`.
214285
220291 | inc y
221292 | } forever
222293 | }
223 = 00c0a041c84c02c060
294 = $080D LDY #$41
295 = $080F INY
296 = $0810 JMP $080F
297 = $0813 RTS
224298
225299 Indexed access.
226300
236310 | st a, many + x
237311 | ld a, many + x
238312 | }
239 = 00c0a200a9009d0dc0bd0dc060
313 = $080D LDX #$00
314 = $080F LDA #$00
315 = $0811 STA $0819,X
316 = $0814 LDA $0819,X
317 = $0817 RTS
318
319 Byte tables take up 256 bytes in memory.
320
321 | byte table tab1
322 | byte table tab2
323 |
324 | routine main
325 | inputs tab1
326 | outputs tab2
327 | trashes a, x, n, z
328 | {
329 | ld x, 0
330 | ld a, tab1 + x
331 | st a, tab2 + x
332 | }
333 = $080D LDX #$00
334 = $080F LDA $0816,X
335 = $0812 STA $0916,X
336 = $0815 RTS
337
338 Byte storage locations take up only 1 byte in memory.
339
340 | byte one
341 | byte two
342 |
343 | routine main
344 | outputs one, two
345 | trashes a, x, n, z
346 | {
347 | ld a, 0
348 | st a, one
349 | st a, two
350 | }
351 = $080D LDA #$00
352 = $080F STA $0816
353 = $0812 STA $0817
354 = $0815 RTS
240355
241356 Copy byte to byte.
242357
250365 | {
251366 | copy baz, bar
252367 | }
253 = 00c0ad09c08d07c060
368 = $080D LDA $0815
369 = $0810 STA $0814
370 = $0813 RTS
254371
255372 Copy word to word.
256373
264381 | {
265382 | copy baz, bar
266383 | }
267 = 00c0ad0fc08d0dc0ad10c08d0ec060
384 = $080D LDA $081C
385 = $0810 STA $081A
386 = $0813 LDA $081D
387 = $0816 STA $081B
388 = $0819 RTS
268389
269390 Copy literal word to word.
270391
274395 | outputs bar
275396 | trashes a, n, z
276397 | {
277 | copy 65535, bar
278 | }
279 = 00c0a9ff8d0bc0a9ff8d0cc060
398 | copy 2000, bar
399 | }
400 = $080D LDA #$D0
401 = $080F STA $0818
402 = $0812 LDA #$07
403 = $0814 STA $0819
404 = $0817 RTS
280405
281406 Copy vector to vector.
282407
290415 | {
291416 | copy baz, bar
292417 | }
293 = 00c0ad0fc08d0dc0ad10c08d0ec060
418 = $080D LDA $081C
419 = $0810 STA $081A
420 = $0813 LDA $081D
421 = $0816 STA $081B
422 = $0819 RTS
294423
295424 Copy routine to vector, inside an `interrupts off` block.
296425
313442 | copy foo, bar
314443 | }
315444 | }
316 = 00c078a90d8d0fc0a9c08d10c05860e860
445 = $080D SEI
446 = $080E LDA #$1A
447 = $0810 STA $081C
448 = $0813 LDA #$08
449 = $0815 STA $081D
450 = $0818 CLI
451 = $0819 RTS
452 = $081A INX
453 = $081B RTS
454
455 Copy word to word table and back, with both `x` and `y` as indexes.
456
457 | word one
458 | word table many
459 |
460 | routine main
461 | inputs one, many
462 | outputs one, many
463 | trashes a, x, y, n, z
464 | {
465 | ld x, 0
466 | ld y, 0
467 | copy 777, one
468 | copy one, many + x
469 | copy one, many + y
470 | copy many + x, one
471 | copy many + y, one
472 | }
473 = $080D LDX #$00
474 = $080F LDY #$00
475 = $0811 LDA #$09
476 = $0813 STA $084C
477 = $0816 LDA #$03
478 = $0818 STA $084D
479 = $081B LDA $084C
480 = $081E STA $084E,X
481 = $0821 LDA $084D
482 = $0824 STA $094E,X
483 = $0827 LDA $084C
484 = $082A STA $084E,Y
485 = $082D LDA $084D
486 = $0830 STA $094E,Y
487 = $0833 LDA $084E,X
488 = $0836 STA $084C
489 = $0839 LDA $094E,X
490 = $083C STA $084D
491 = $083F LDA $084E,Y
492 = $0842 STA $084C
493 = $0845 LDA $094E,Y
494 = $0848 STA $084D
495 = $084B RTS
317496
318497 Indirect call.
319498
327506 | copy bar, foo
328507 | call foo
329508 | }
330 = 00c0a90e8d14c0a9c08d15c02011c060a2c8606c14c0
509 = $080D LDA #$1B
510 = $080F STA $0821
511 = $0812 LDA #$08
512 = $0814 STA $0822
513 = $0817 JSR $081E
514 = $081A RTS
515 = $081B LDX #$C8
516 = $081D RTS
517 = $081E JMP ($0821)
331518
332519 goto.
333520
339526 | ld y, 200
340527 | goto bar
341528 | }
342 = 00c0a0c84c06c060a2c860
529 = $080D LDY #$C8
530 = $080F JMP $0813
531 = $0812 RTS
532 = $0813 LDX #$C8
533 = $0815 RTS
534
535 ### word operations
536
537 Adding a constant word to a word memory location.
538
539 | word score
540 | routine main
541 | inputs score
542 | outputs score
543 | trashes a, c, z, v, n
544 | {
545 | st off, c
546 | add score, 1999
547 | }
548 = $080D CLC
549 = $080E LDA $081F
550 = $0811 ADC #$CF
551 = $0813 STA $081F
552 = $0816 LDA $0820
553 = $0819 ADC #$07
554 = $081B STA $0820
555 = $081E RTS
556
557 Adding a word memory location to another word memory location.
558
559 | word score
560 | word delta
561 | routine main
562 | inputs score, delta
563 | outputs score
564 | trashes a, c, z, v, n
565 | {
566 | st off, c
567 | add score, delta
568 | }
569 = $080D CLC
570 = $080E LDA $0821
571 = $0811 ADC $0823
572 = $0814 STA $0821
573 = $0817 LDA $0822
574 = $081A ADC $0824
575 = $081D STA $0822
576 = $0820 RTS
343577
344578 ### Buffers and Pointers
345579
356590 | ld y, 0
357591 | copy ^buf, ptr
358592 | }
359 = 00c0a000a90b85fea9c085ff60
593 = $080D LDY #$00
594 = $080F LDA #$18
595 = $0811 STA $FE
596 = $0813 LDA #$08
597 = $0815 STA $FF
598 = $0817 RTS
360599
361600 Write literal through a pointer.
362601
372611 | copy ^buf, ptr
373612 | copy 123, [ptr] + y
374613 | }
375 = 00c0a000a90f85fea9c085ffa97b91fe60
614 = $080D LDY #$00
615 = $080F LDA #$1C
616 = $0811 STA $FE
617 = $0813 LDA #$08
618 = $0815 STA $FF
619 = $0817 LDA #$7B
620 = $0819 STA ($FE),Y
621 = $081B RTS
376622
377623 Write stored value through a pointer.
378624
389635 | copy ^buf, ptr
390636 | copy foo, [ptr] + y
391637 | }
392 = 00c0a000a91085fea9c085ffad12c091fe60
638 = $080D LDY #$00
639 = $080F LDA #$1D
640 = $0811 STA $FE
641 = $0813 LDA #$08
642 = $0815 STA $FF
643 = $0817 LDA $101D
644 = $081A STA ($FE),Y
645 = $081C RTS
393646
394647 Read through a pointer.
395648
406659 | copy ^buf, ptr
407660 | copy [ptr] + y, foo
408661 | }
409 = 00c0a000a91085fea9c085ffb1fe8d12c060
662 = $080D LDY #$00
663 = $080F LDA #$1D
664 = $0811 STA $FE
665 = $0813 LDA #$08
666 = $0815 STA $FF
667 = $0817 LDA ($FE),Y
668 = $0819 STA $101D
669 = $081C RTS
670
671 Add a word memory location, and a literal word, to a pointer, and then read through it.
672 Note that this is *not* range-checked. (Yet.)
673
674 | buffer[2048] buf
675 | pointer ptr @ 254
676 | byte foo
677 | word delta
678 |
679 | routine main
680 | inputs buf
681 | outputs y, foo, delta
682 | trashes a, z, n, ptr
683 | {
684 | copy 619, delta
685 | ld y, 0
686 | copy ^buf, ptr
687 | add ptr, delta
688 | add ptr, word 1
689 | copy [ptr] + y, foo
690 | }
691 = $080D LDA #$6B
692 = $080F STA $1042
693 = $0812 LDA #$02
694 = $0814 STA $1043
695 = $0817 LDY #$00
696 = $0819 LDA #$41
697 = $081B STA $FE
698 = $081D LDA #$08
699 = $081F STA $FF
700 = $0821 LDA $FE
701 = $0823 ADC $1042
702 = $0826 STA $FE
703 = $0828 LDA $FF
704 = $082A ADC $1043
705 = $082D STA $FF
706 = $082F LDA $FE
707 = $0831 ADC #$01
708 = $0833 STA $FE
709 = $0835 LDA $FF
710 = $0837 ADC #$00
711 = $0839 STA $FF
712 = $083B LDA ($FE),Y
713 = $083D STA $1041
714 = $0840 RTS
128128 | word wor
129129 | vector vec
130130 | byte table tab
131 | word table wtab
131132 | buffer[2048] buf
132133 | pointer ptr
133134 |
249250 | }
250251 ? SyntaxError
251252
252 Declaring a byte table memory location.
253 Declaring byte and word table memory location.
253254
254255 | byte table tab
255256 |
258259 | ld y, 0
259260 | ld a, tab + x
260261 | st a, tab + y
262 | }
263 = ok
264
265 | word one
266 | word table many
267 |
268 | routine main {
269 | ld x, 0
270 | copy one, many + x
271 | copy word 0, many + x
272 | copy many + x, one
261273 | }
262274 = ok
263275
292304 | }
293305 ? SyntaxError
294306
307 Constraints set may only contain labels.
308
309 | vector cinv
310 | inputs a
311 | outputs 200
312 | trashes a, x, z, n
313 | @ 788
314 |
315 | routine foo {
316 | ld a, 0
317 | }
318 | routine main {
319 | with interrupts off {
320 | copy foo, cinv
321 | }
322 | call cinv
323 | }
324 ? SyntaxError
325
326 A vector can name itself in its inputs, outputs, and trashes.
327
328 | vector cinv
329 | inputs cinv, a
330 | outputs cinv, x
331 | trashes a, x, z, n
332 | @ 788
333 |
334 | routine foo {
335 | ld a, 0
336 | }
337 | routine main {
338 | with interrupts off {
339 | copy foo, cinv
340 | }
341 | call cinv
342 | }
343 = ok
344
295345 goto.
296346
297347 | routine foo {
0 #!/usr/bin/env python
1
2 # script that allows the binary output of sixtypical --basic-prelude --compile to be
3 # disassembled by https://github.com/tcarmelveilleux/dcc6502
4
5 import sys
6 import re
7 from subprocess import check_output
8 from tempfile import NamedTemporaryFile
9
10 bytes = sys.stdin.read()
11
12 bytes = bytes[14:]
13
14 f = NamedTemporaryFile(delete=False)
15 filename = f.name
16 f.write(bytes)
17 f.close()
18
19 lines = [line for line in check_output("dcc6502 -o 2061 {}".format(filename), shell=True).split('\n') if line and not line.startswith(';')]
20 lines = [re.sub(r'\s*\;.*$', '', line) for line in lines]
21 lines.pop()
22 sys.stdout.write('\n'.join(lines))