From adb53f7a04f9158cb7238f7fef77da5ddf5c3484 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Wed, 14 Mar 2018 15:33:11 +0000 Subject: [PATCH] Almost compile `for` loops correctly. --- src/sixtypical/analyzer.py | 17 +++---- src/sixtypical/ast.py | 6 +-- src/sixtypical/compiler.py | 79 +++++++++++++++++++++------------ src/sixtypical/parser.py | 22 ++++----- tests/SixtyPical Compilation.md | 40 +++++++++++++++++ 5 files changed, 114 insertions(+), 50 deletions(-) diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index bebc897..4b4e55a 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -654,20 +654,21 @@ class Analyzer(object): context.assert_meaningful(instr.dest) bottom, top = context.get_range(instr.dest) + final = instr.final.value if instr.direction > 0: - if top >= instr.final: - raise RangeExceededError(self.routine, "Top of range of {} is {} but must be lower than {}".format( - instr.dest, top, instr.final + if top >= final: + raise RangeExceededError(instr, "Top of range of {} is {} but must be lower than {}".format( + instr.dest, top, final )) - top = instr.final + top = final if instr.direction < 0: - if bottom <= instr.final: - raise RangeExceededError(self.routine, "Bottom of range of {} is {} but must be higher than {}".format( - instr.dest, bottom, instr.final + if bottom <= final: + raise RangeExceededError(instr, "Bottom of range of {} is {} but must be higher than {}".format( + instr.dest, bottom, final )) - bottom = instr.final + bottom = final subcontext = context.clone() subcontext.set_range(instr.dest, bottom, top) diff --git a/src/sixtypical/ast.py b/src/sixtypical/ast.py index 2029173..afd2944 100644 --- a/src/sixtypical/ast.py +++ b/src/sixtypical/ast.py @@ -74,17 +74,17 @@ class SingleOp(Instr): class If(Instr): - value_attrs = ('src', 'inverted') + value_attrs = ('src', 'inverted',) child_attrs = ('block1', 'block2',) class Repeat(Instr): - value_attrs = ('src', 'inverted') + value_attrs = ('src', 'inverted',) child_attrs = ('block',) class For(Instr): - value_attrs = ('dest', 'direction', 'final') + value_attrs = ('dest', 'direction', 'final',) child_attrs = ('block',) diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index 7e160c3..2ffabcd 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -1,6 +1,6 @@ # encoding: UTF-8 -from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, WithInterruptsOff +from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, If, Repeat, For, WithInterruptsOff from sixtypical.model import ( ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef, TYPE_BIT, TYPE_BYTE, TYPE_WORD, @@ -148,6 +148,8 @@ class Compiler(object): return self.compile_if(instr) elif isinstance(instr, Repeat): return self.compile_repeat(instr) + elif isinstance(instr, For): + return self.compile_for(instr) elif isinstance(instr, WithInterruptsOff): return self.compile_with_interrupts_off(instr) else: @@ -300,31 +302,11 @@ class Compiler(object): else: raise UnsupportedOpcodeError(instr) elif opcode == 'inc': - if dest == REG_X: - self.emitter.emit(INX()) - elif dest == REG_Y: - self.emitter.emit(INY()) - else: - self.emitter.emit(INC(Absolute(self.get_label(dest.name)))) + self.compile_inc(instr, instr.dest) elif opcode == 'dec': - if dest == REG_X: - self.emitter.emit(DEX()) - elif dest == REG_Y: - self.emitter.emit(DEY()) - else: - self.emitter.emit(DEC(Absolute(self.get_label(dest.name)))) + self.compile_dec(instr, instr.dest) elif opcode == 'cmp': - cls = { - 'a': CMP, - 'x': CPX, - 'y': CPY, - }.get(dest.name) - if cls is None: - raise UnsupportedOpcodeError(instr) - if isinstance(src, ConstantRef): - self.emitter.emit(cls(Immediate(Byte(src.value)))) - else: - self.emitter.emit(cls(Absolute(self.get_label(src.name)))) + self.compile_cmp(instr, instr.src, instr.dest) elif opcode in ('and', 'or', 'xor',): cls = { 'and': AND, @@ -369,18 +351,45 @@ class Compiler(object): else: raise NotImplementedError elif opcode == 'copy': - self.compile_copy_op(instr) + self.compile_copy(instr, instr.src, instr.dest) elif opcode == 'trash': pass else: raise NotImplementedError(opcode) - def compile_copy_op(self, instr): + def compile_cmp(self, instr, src, dest): + """`instr` is only for reporting purposes""" + cls = { + 'a': CMP, + 'x': CPX, + 'y': CPY, + }.get(dest.name) + if cls is None: + raise UnsupportedOpcodeError(instr) + if isinstance(src, ConstantRef): + self.emitter.emit(cls(Immediate(Byte(src.value)))) + else: + self.emitter.emit(cls(Absolute(self.get_label(src.name)))) - opcode = instr.opcode - dest = instr.dest - src = instr.src + def compile_inc(self, instr, dest): + """`instr` is only for reporting purposes""" + if dest == REG_X: + self.emitter.emit(INX()) + elif dest == REG_Y: + self.emitter.emit(INY()) + else: + self.emitter.emit(INC(Absolute(self.get_label(dest.name)))) + def compile_dec(self, instr, dest): + """`instr` is only for reporting purposes""" + if dest == REG_X: + self.emitter.emit(DEX()) + elif dest == REG_Y: + self.emitter.emit(DEY()) + else: + self.emitter.emit(DEC(Absolute(self.get_label(dest.name)))) + + def compile_copy(self, instr, src, dest): if isinstance(src, ConstantRef) and isinstance(dest, IndirectRef) and src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType): ### copy 123, [ptr] + y dest_label = self.get_label(dest.ref.name) @@ -540,6 +549,18 @@ class Compiler(object): raise UnsupportedOpcodeError(instr) self.emitter.emit(cls(Relative(top_label))) + def compile_for(self, instr): + top_label = self.emitter.make_label() + + self.compile_block(instr.block) + + if instr.direction > 0: + self.compile_inc(instr, instr.dest) + elif instr.direction < 0: + self.compile_dec(instr, instr.dest) + self.compile_cmp(instr, instr.final, instr.dest) + self.emitter.emit(BNE(Relative(top_label))) + def compile_with_interrupts_off(self, instr): self.emitter.emit(SEI()) self.compile_block(instr.block) diff --git a/src/sixtypical/parser.py b/src/sixtypical/parser.py index ede7849..2391d05 100644 --- a/src/sixtypical/parser.py +++ b/src/sixtypical/parser.py @@ -137,10 +137,16 @@ class Parser(object): return Defn(self.scanner.line_number, name=name, addr=addr, initial=initial, location=location) def literal_int(self): - self.scanner.check_type('integer literal') - c = int(self.scanner.token) - self.scanner.scan() - return c + self.scanner.check_type('integer literal') + c = int(self.scanner.token) + self.scanner.scan() + return c + + def literal_int_const(self): + value = self.literal_int() + type_ = TYPE_WORD if value > 255 else TYPE_BYTE + loc = ConstantRef(type_, value) + return loc def defn_size(self): self.scanner.expect('[') @@ -293,11 +299,7 @@ class Parser(object): self.scanner.scan() return loc elif self.scanner.on_type('integer literal'): - value = int(self.scanner.token) - type_ = TYPE_WORD if value > 255 else TYPE_BYTE - loc = ConstantRef(type_, value) - self.scanner.scan() - return loc + return self.literal_int_const() elif self.scanner.consume('word'): loc = ConstantRef(TYPE_WORD, int(self.scanner.token)) self.scanner.scan() @@ -385,7 +387,7 @@ class Parser(object): else: self.syntax_error('expected "up" or "down", found "%s"' % self.scanner.token) self.scanner.expect('to') - final = self.literal_int() + final = self.literal_int_const() block = self.block() return For(self.scanner.line_number, dest=dest, direction=direction, final=final, block=block) elif self.scanner.token in ("ld",): diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index 2ead442..0021f0c 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -351,6 +351,46 @@ The body of `repeat forever` can be empty. = $080D JMP $080D = $0810 RTS +Compiling `for ... up to`. + + | byte table[256] tab + | + | define main routine + | inputs tab + | trashes a, x, c, z, v, n + | { + | ld x, 0 + | for x up to 15 { + | ld a, tab + x + | } + | } + = $080D LDX #$00 + = $080F LDA $0818,X + = $0812 INX + = $0813 CPX #$10 + = $0815 BNE $080F + = $0817 RTS + +Compiling `for ... down to`. + + | byte table[256] tab + | + | define main routine + | inputs tab + | trashes a, x, c, z, v, n + | { + | ld x, 15 + | for x down to 0 { + | ld a, tab + x + | } + | } + = $080D LDX #$0F + = $080F LDA $0818,X + = $0812 DEX + = $0813 CPX #$FF + = $0815 BNE $080F + = $0817 RTS + Indexed access. | byte one