Almost compile `for` loops correctly.
Chris Pressey
7 years ago
653 | 653 | context.assert_meaningful(instr.dest) |
654 | 654 | |
655 | 655 | bottom, top = context.get_range(instr.dest) |
656 | final = instr.final.value | |
656 | 657 | |
657 | 658 | if instr.direction > 0: |
658 | if top >= instr.final: | |
659 | raise RangeExceededError(self.routine, "Top of range of {} is {} but must be lower than {}".format( | |
660 | instr.dest, top, instr.final | |
659 | if top >= final: | |
660 | raise RangeExceededError(instr, "Top of range of {} is {} but must be lower than {}".format( | |
661 | instr.dest, top, final | |
661 | 662 | )) |
662 | top = instr.final | |
663 | top = final | |
663 | 664 | |
664 | 665 | if instr.direction < 0: |
665 | if bottom <= instr.final: | |
666 | raise RangeExceededError(self.routine, "Bottom of range of {} is {} but must be higher than {}".format( | |
667 | instr.dest, bottom, instr.final | |
666 | if bottom <= final: | |
667 | raise RangeExceededError(instr, "Bottom of range of {} is {} but must be higher than {}".format( | |
668 | instr.dest, bottom, final | |
668 | 669 | )) |
669 | bottom = instr.final | |
670 | bottom = final | |
670 | 671 | |
671 | 672 | subcontext = context.clone() |
672 | 673 | subcontext.set_range(instr.dest, bottom, top) |
73 | 73 | |
74 | 74 | |
75 | 75 | class If(Instr): |
76 | value_attrs = ('src', 'inverted') | |
76 | value_attrs = ('src', 'inverted',) | |
77 | 77 | child_attrs = ('block1', 'block2',) |
78 | 78 | |
79 | 79 | |
80 | 80 | class Repeat(Instr): |
81 | value_attrs = ('src', 'inverted') | |
81 | value_attrs = ('src', 'inverted',) | |
82 | 82 | child_attrs = ('block',) |
83 | 83 | |
84 | 84 | |
85 | 85 | class For(Instr): |
86 | value_attrs = ('dest', 'direction', 'final') | |
86 | value_attrs = ('dest', 'direction', 'final',) | |
87 | 87 | child_attrs = ('block',) |
88 | 88 | |
89 | 89 |
0 | 0 | # encoding: UTF-8 |
1 | 1 | |
2 | from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, WithInterruptsOff | |
2 | from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff | |
3 | 3 | from sixtypical.model import ( |
4 | 4 | ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef, |
5 | 5 | TYPE_BIT, TYPE_BYTE, TYPE_WORD, |
147 | 147 | return self.compile_if(instr) |
148 | 148 | elif isinstance(instr, Repeat): |
149 | 149 | return self.compile_repeat(instr) |
150 | elif isinstance(instr, For): | |
151 | return self.compile_for(instr) | |
150 | 152 | elif isinstance(instr, WithInterruptsOff): |
151 | 153 | return self.compile_with_interrupts_off(instr) |
152 | 154 | else: |
299 | 301 | else: |
300 | 302 | raise UnsupportedOpcodeError(instr) |
301 | 303 | elif opcode == 'inc': |
302 | if dest == REG_X: | |
303 | self.emitter.emit(INX()) | |
304 | elif dest == REG_Y: | |
305 | self.emitter.emit(INY()) | |
306 | else: | |
307 | self.emitter.emit(INC(Absolute(self.get_label(dest.name)))) | |
304 | self.compile_inc(instr, instr.dest) | |
308 | 305 | elif opcode == 'dec': |
309 | if dest == REG_X: | |
310 | self.emitter.emit(DEX()) | |
311 | elif dest == REG_Y: | |
312 | self.emitter.emit(DEY()) | |
313 | else: | |
314 | self.emitter.emit(DEC(Absolute(self.get_label(dest.name)))) | |
306 | self.compile_dec(instr, instr.dest) | |
315 | 307 | elif opcode == 'cmp': |
316 | cls = { | |
317 | 'a': CMP, | |
318 | 'x': CPX, | |
319 | 'y': CPY, | |
320 | }.get(dest.name) | |
321 | if cls is None: | |
322 | raise UnsupportedOpcodeError(instr) | |
323 | if isinstance(src, ConstantRef): | |
324 | self.emitter.emit(cls(Immediate(Byte(src.value)))) | |
325 | else: | |
326 | self.emitter.emit(cls(Absolute(self.get_label(src.name)))) | |
308 | self.compile_cmp(instr, instr.src, instr.dest) | |
327 | 309 | elif opcode in ('and', 'or', 'xor',): |
328 | 310 | cls = { |
329 | 311 | 'and': AND, |
368 | 350 | else: |
369 | 351 | raise NotImplementedError |
370 | 352 | elif opcode == 'copy': |
371 | self.compile_copy_op(instr) | |
353 | self.compile_copy(instr, instr.src, instr.dest) | |
372 | 354 | elif opcode == 'trash': |
373 | 355 | pass |
374 | 356 | else: |
375 | 357 | raise NotImplementedError(opcode) |
376 | 358 | |
377 | def compile_copy_op(self, instr): | |
378 | ||
379 | opcode = instr.opcode | |
380 | dest = instr.dest | |
381 | src = instr.src | |
382 | ||
359 | def compile_cmp(self, instr, src, dest): | |
360 | """`instr` is only for reporting purposes""" | |
361 | cls = { | |
362 | 'a': CMP, | |
363 | 'x': CPX, | |
364 | 'y': CPY, | |
365 | }.get(dest.name) | |
366 | if cls is None: | |
367 | raise UnsupportedOpcodeError(instr) | |
368 | if isinstance(src, ConstantRef): | |
369 | self.emitter.emit(cls(Immediate(Byte(src.value)))) | |
370 | else: | |
371 | self.emitter.emit(cls(Absolute(self.get_label(src.name)))) | |
372 | ||
373 | def compile_inc(self, instr, dest): | |
374 | """`instr` is only for reporting purposes""" | |
375 | if dest == REG_X: | |
376 | self.emitter.emit(INX()) | |
377 | elif dest == REG_Y: | |
378 | self.emitter.emit(INY()) | |
379 | else: | |
380 | self.emitter.emit(INC(Absolute(self.get_label(dest.name)))) | |
381 | ||
382 | def compile_dec(self, instr, dest): | |
383 | """`instr` is only for reporting purposes""" | |
384 | if dest == REG_X: | |
385 | self.emitter.emit(DEX()) | |
386 | elif dest == REG_Y: | |
387 | self.emitter.emit(DEY()) | |
388 | else: | |
389 | self.emitter.emit(DEC(Absolute(self.get_label(dest.name)))) | |
390 | ||
391 | def compile_copy(self, instr, src, dest): | |
383 | 392 | if isinstance(src, ConstantRef) and isinstance(dest, IndirectRef) and src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType): |
384 | 393 | ### copy 123, [ptr] + y |
385 | 394 | dest_label = self.get_label(dest.ref.name) |
539 | 548 | raise UnsupportedOpcodeError(instr) |
540 | 549 | self.emitter.emit(cls(Relative(top_label))) |
541 | 550 | |
551 | def compile_for(self, instr): | |
552 | top_label = self.emitter.make_label() | |
553 | ||
554 | self.compile_block(instr.block) | |
555 | ||
556 | if instr.direction > 0: | |
557 | self.compile_inc(instr, instr.dest) | |
558 | elif instr.direction < 0: | |
559 | self.compile_dec(instr, instr.dest) | |
560 | self.compile_cmp(instr, instr.final, instr.dest) | |
561 | self.emitter.emit(BNE(Relative(top_label))) | |
562 | ||
542 | 563 | def compile_with_interrupts_off(self, instr): |
543 | 564 | self.emitter.emit(SEI()) |
544 | 565 | self.compile_block(instr.block) |
136 | 136 | return Defn(self.scanner.line_number, name=name, addr=addr, initial=initial, location=location) |
137 | 137 | |
138 | 138 | def literal_int(self): |
139 | self.scanner.check_type('integer literal') | |
140 | c = int(self.scanner.token) | |
141 | self.scanner.scan() | |
142 | return c | |
139 | self.scanner.check_type('integer literal') | |
140 | c = int(self.scanner.token) | |
141 | self.scanner.scan() | |
142 | return c | |
143 | ||
144 | def literal_int_const(self): | |
145 | value = self.literal_int() | |
146 | type_ = TYPE_WORD if value > 255 else TYPE_BYTE | |
147 | loc = ConstantRef(type_, value) | |
148 | return loc | |
143 | 149 | |
144 | 150 | def defn_size(self): |
145 | 151 | self.scanner.expect('[') |
292 | 298 | self.scanner.scan() |
293 | 299 | return loc |
294 | 300 | elif self.scanner.on_type('integer literal'): |
295 | value = int(self.scanner.token) | |
296 | type_ = TYPE_WORD if value > 255 else TYPE_BYTE | |
297 | loc = ConstantRef(type_, value) | |
298 | self.scanner.scan() | |
299 | return loc | |
301 | return self.literal_int_const() | |
300 | 302 | elif self.scanner.consume('word'): |
301 | 303 | loc = ConstantRef(TYPE_WORD, int(self.scanner.token)) |
302 | 304 | self.scanner.scan() |
384 | 386 | else: |
385 | 387 | self.syntax_error('expected "up" or "down", found "%s"' % self.scanner.token) |
386 | 388 | self.scanner.expect('to') |
387 | final = self.literal_int() | |
389 | final = self.literal_int_const() | |
388 | 390 | block = self.block() |
389 | 391 | return For(self.scanner.line_number, dest=dest, direction=direction, final=final, block=block) |
390 | 392 | elif self.scanner.token in ("ld",): |
350 | 350 | = $080D JMP $080D |
351 | 351 | = $0810 RTS |
352 | 352 | |
353 | Compiling `for ... up to`. | |
354 | ||
355 | | byte table[256] tab | |
356 | | | |
357 | | define main routine | |
358 | | inputs tab | |
359 | | trashes a, x, c, z, v, n | |
360 | | { | |
361 | | ld x, 0 | |
362 | | for x up to 15 { | |
363 | | ld a, tab + x | |
364 | | } | |
365 | | } | |
366 | = $080D LDX #$00 | |
367 | = $080F LDA $0818,X | |
368 | = $0812 INX | |
369 | = $0813 CPX #$10 | |
370 | = $0815 BNE $080F | |
371 | = $0817 RTS | |
372 | ||
373 | Compiling `for ... down to`. | |
374 | ||
375 | | byte table[256] tab | |
376 | | | |
377 | | define main routine | |
378 | | inputs tab | |
379 | | trashes a, x, c, z, v, n | |
380 | | { | |
381 | | ld x, 15 | |
382 | | for x down to 0 { | |
383 | | ld a, tab + x | |
384 | | } | |
385 | | } | |
386 | = $080D LDX #$0F | |
387 | = $080F LDA $0818,X | |
388 | = $0812 DEX | |
389 | = $0813 CPX #$FF | |
390 | = $0815 BNE $080F | |
391 | = $0817 RTS | |
392 | ||
353 | 393 | Indexed access. |
354 | 394 | |
355 | 395 | | byte one |