1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-06-09 04:29:31 +00:00

Almost compile for loops correctly.

This commit is contained in:
Chris Pressey 2018-03-14 15:33:11 +00:00
parent b79059faa4
commit adb53f7a04
5 changed files with 114 additions and 50 deletions

View File

@ -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)

View File

@ -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',)

View File

@ -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)

View File

@ -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",):

View File

@ -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