# Copyright (c) 2014-2024, Chris Pressey, Cat's Eye Technologies. # This file is distributed under a 2-clause BSD license. See LICENSES/ dir. # SPDX-License-Identifier: LicenseRef-BSD-2-Clause-X-SixtyPical # encoding: UTF-8 from sixtypical.ast import ( Program, Routine, Block, SingleOp, Reset, Call, GoTo, If, Repeat, For, WithInterruptsOff, Save, PointInto ) from sixtypical.model import ( ConstantRef, LocationRef, IndexedRef, IndirectRef, TYPE_BIT, TYPE_BYTE, TYPE_WORD, TableType, PointerType, RoutineType, VectorType, REG_A, REG_X, REG_Y, FLAG_C ) from sixtypical.emitter import Byte, Word, Table, Label, Offset, LowAddressByte, HighAddressByte from sixtypical.gen6502 import ( Immediate, Absolute, AbsoluteX, AbsoluteY, ZeroPage, Indirect, IndirectY, Relative, LDA, LDX, LDY, STA, STX, STY, TAX, TAY, TXA, TYA, PHA, PLA, CLC, SEC, ADC, SBC, ROL, ROR, INC, INX, INY, DEC, DEX, DEY, CMP, CPX, CPY, AND, ORA, EOR, BCC, BCS, BNE, BEQ, BPL, BMI, JMP, JSR, RTS, SEI, CLI, NOP, ) class UnsupportedOpcodeError(KeyError): pass class Compiler(object): def __init__(self, symtab, emitter): self.symtab = symtab self.emitter = emitter self.routines = {} # routine.name -> Routine self.routine_locals = {} # routine.name -> { local.name -> Label } self.labels = {} # global.name -> Label ("global" includes routines) self.trampolines = {} # Location -> Label self.pointer_assoc = {} # pointer name -> table name (I'm not entirely happy about this) self.current_routine = None # - - - - helper methods - - - - def get_type_for_name(self, name): if self.current_routine and self.symtab.has_local(self.current_routine.name, name): return self.symtab.fetch_local_type(self.current_routine.name, name) return self.symtab.fetch_global_type(name) def get_type(self, ref): if isinstance(ref, ConstantRef): return ref.type if not isinstance(ref, LocationRef): raise NotImplementedError return self.get_type_for_name(ref.name) def addressing_mode_for_index(self, index): if index == REG_X: return AbsoluteX elif index == REG_Y: return AbsoluteY else: raise NotImplementedError(index) def compute_length_of_defn(self, defn): length = None type_ = self.get_type_for_name(defn.name) if type_ == TYPE_BYTE: length = 1 elif type_ == TYPE_WORD or isinstance(type_, (PointerType, VectorType)): length = 2 elif isinstance(type_, TableType): length = type_.size * (1 if type_.of_type == TYPE_BYTE else 2) if length is None: raise NotImplementedError("Need size for type {}".format(type_)) return length def get_label(self, name): if self.current_routine: local_label = self.routine_locals.get(self.current_routine.name, {}).get(name) if local_label: return local_label return self.labels[name] def absolute_or_zero_page(self, label): if label.addr is not None and label.addr < 256: return ZeroPage(label) else: return Absolute(label) # - - - - visitor methods - - - - def compile_program(self, program, compilation_roster=None): assert isinstance(program, Program) declarations = [] for defn in program.defns: length = self.compute_length_of_defn(defn) label = Label(defn.name, addr=defn.addr, length=length) self.labels[defn.name] = label declarations.append((defn, self.symtab.fetch_global_type(defn.name), label)) for routine in program.routines: self.routines[routine.name] = routine label = Label(routine.name) if routine.addr is not None: label.set_addr(routine.addr) self.labels[routine.name] = label self.current_routine = routine local_labels = {} for defn in routine.locals: length = self.compute_length_of_defn(defn) label = Label(defn.name, addr=defn.addr, length=length) local_labels[defn.name] = label declarations.append((defn, self.symtab.fetch_local_type(routine.name, defn.name), label)) self.routine_locals[routine.name] = local_labels self.current_routine = None if compilation_roster is None: compilation_roster = [['main']] + [[routine.name] for routine in program.routines if routine.name != 'main'] for roster_row in compilation_roster: for i, routine_name in enumerate(roster_row): if i < len(roster_row) - 1: self.compile_routine(self.routines[routine_name], next_routine=self.routines[roster_row[i + 1]]) else: self.compile_routine(self.routines[routine_name]) for location, label in self.trampolines.items(): self.emitter.resolve_label(label) self.emitter.emit(JMP(Indirect(self.get_label(location.name)))) self.emitter.emit(RTS()) # initialized data for defn, type_, label in declarations: if defn.initial is not None: initial_data = None if type_ == TYPE_BYTE: initial_data = Byte(defn.initial) elif type_ == TYPE_WORD: initial_data = Word(defn.initial) elif TableType.is_a_table_type(type_, TYPE_BYTE): initial_data = Table([Byte(i) for i in defn.initial], type_.size) elif TableType.is_a_table_type(type_, TYPE_WORD): initial_data = Table([Word(i) for i in defn.initial], type_.size) else: raise NotImplementedError(type_) label.set_length(initial_data.size()) self.emitter.resolve_label(label) self.emitter.emit(initial_data) # uninitialized, "BSS" data for defn, type_, label in declarations: if defn.initial is None and defn.addr is None: self.emitter.resolve_bss_label(label) def compile_routine(self, routine, next_routine=None): assert isinstance(routine, Routine) self.current_routine = routine if routine.block: self.emitter.resolve_label(self.get_label(routine.name)) self.compile_block(routine.block) needs_rts = True last_op = self.emitter.get_tail() if isinstance(last_op, JSR): if isinstance(last_op.operand, Absolute): if isinstance(last_op.operand.value, Label): label = last_op.operand.value self.emitter.retract() self.emitter.emit(JMP(Absolute(label))) last_op = self.emitter.get_tail() if isinstance(last_op, JMP): needs_rts = False if isinstance(last_op.operand, Absolute): if isinstance(last_op.operand.value, Label): if next_routine and last_op.operand.value.name == next_routine.name: self.emitter.retract() if needs_rts: self.emitter.emit(RTS()) self.current_routine = None def compile_block(self, block): assert isinstance(block, Block) block.shallow_contains_goto = False for instr in block.instrs: self.compile_instr(instr) if isinstance(instr, GoTo): block.shallow_contains_goto = True def compile_instr(self, instr): if isinstance(instr, SingleOp): return self.compile_single_op(instr) elif isinstance(instr, Call): return self.compile_call(instr) elif isinstance(instr, GoTo): return self.compile_goto(instr) elif isinstance(instr, If): 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) elif isinstance(instr, Save): return self.compile_save(instr) elif isinstance(instr, PointInto): return self.compile_point_into(instr) elif isinstance(instr, Reset): return self.compile_reset(instr) else: raise NotImplementedError def compile_single_op(self, instr): opcode = instr.opcode dest = instr.dest src = instr.src if opcode == 'ld': if dest == REG_A: if src == REG_X: self.emitter.emit(TXA()) elif src == REG_Y: self.emitter.emit(TYA()) elif isinstance(src, ConstantRef): self.emitter.emit(LDA(Immediate(Byte(src.value)))) elif isinstance(src, IndexedRef) and src.index == REG_X: self.emitter.emit(LDA(AbsoluteX(Offset(self.get_label(src.ref.name), src.offset.value)))) elif isinstance(src, IndexedRef) and src.index == REG_Y: self.emitter.emit(LDA(AbsoluteY(Offset(self.get_label(src.ref.name), src.offset.value)))) elif isinstance(src, IndirectRef) and isinstance(self.get_type(src.ref), PointerType): self.emitter.emit(LDA(IndirectY(self.get_label(src.ref.name)))) else: self.emitter.emit(LDA(self.absolute_or_zero_page(self.get_label(src.name)))) elif dest == REG_X: if src == REG_A: self.emitter.emit(TAX()) elif isinstance(src, ConstantRef): self.emitter.emit(LDX(Immediate(Byte(src.value)))) elif isinstance(src, IndexedRef) and src.index == REG_Y: self.emitter.emit(LDX(AbsoluteY(Offset(self.get_label(src.ref.name), src.offset.value)))) else: self.emitter.emit(LDX(self.absolute_or_zero_page(self.get_label(src.name)))) elif dest == REG_Y: if src == REG_A: self.emitter.emit(TAY()) elif isinstance(src, ConstantRef): self.emitter.emit(LDY(Immediate(Byte(src.value)))) elif isinstance(src, IndexedRef) and src.index == REG_X: self.emitter.emit(LDY(AbsoluteX(Offset(self.get_label(src.ref.name), src.offset.value)))) else: self.emitter.emit(LDY(self.absolute_or_zero_page(self.get_label(src.name)))) else: raise UnsupportedOpcodeError(instr) elif opcode == 'st': if dest == FLAG_C and src == ConstantRef(TYPE_BIT, 0): self.emitter.emit(CLC()) elif dest == FLAG_C and src == ConstantRef(TYPE_BIT, 1): self.emitter.emit(SEC()) else: op_cls = { REG_A: STA, REG_X: STX, REG_Y: STY }.get(src, None) if isinstance(dest, IndexedRef): mode_cls = { REG_X: AbsoluteX, REG_Y: AbsoluteY, }[dest.index] operand = mode_cls(Offset(self.get_label(dest.ref.name), dest.offset.value)) elif isinstance(dest, IndirectRef) and isinstance(self.get_type(dest.ref), PointerType): operand = IndirectY(self.get_label(dest.ref.name)) else: operand = self.absolute_or_zero_page(self.get_label(dest.name)) if op_cls is None: raise UnsupportedOpcodeError(instr) self.emitter.emit(op_cls(operand)) elif opcode == 'add': if dest == REG_X or dest == REG_Y: raise UnsupportedOpcodeError(instr) if dest == REG_A: if isinstance(src, ConstantRef): self.emitter.emit(ADC(Immediate(Byte(src.value)))) elif isinstance(src, IndexedRef): mode = self.addressing_mode_for_index(src.index) self.emitter.emit(ADC(mode(Offset(self.get_label(src.ref.name), src.offset.value)))) else: self.emitter.emit(ADC(Absolute(self.get_label(src.name)))) elif isinstance(dest, LocationRef) and self.get_type(src) == TYPE_BYTE and self.get_type(dest) == TYPE_BYTE: if isinstance(src, ConstantRef): dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(dest_label))) self.emitter.emit(ADC(Immediate(Byte(src.low_byte())))) self.emitter.emit(STA(Absolute(dest_label))) elif isinstance(src, LocationRef): src_label = self.get_label(src.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(dest_label))) self.emitter.emit(ADC(Absolute(src_label))) self.emitter.emit(STA(Absolute(dest_label))) else: raise UnsupportedOpcodeError(instr) elif isinstance(dest, LocationRef) and self.get_type(src) == TYPE_WORD and self.get_type(dest) == TYPE_WORD: if isinstance(src, ConstantRef): dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(dest_label))) self.emitter.emit(ADC(Immediate(Byte(src.low_byte())))) self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(Absolute(Offset(dest_label, 1)))) self.emitter.emit(ADC(Immediate(Byte(src.high_byte())))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) elif isinstance(src, LocationRef): src_label = self.get_label(src.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(dest_label))) self.emitter.emit(ADC(Absolute(src_label))) self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(Absolute(Offset(dest_label, 1)))) self.emitter.emit(ADC(Absolute(Offset(src_label, 1)))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) else: raise UnsupportedOpcodeError(instr) elif isinstance(dest, LocationRef) and self.get_type(src) == TYPE_WORD and isinstance(self.get_type(dest), PointerType): if isinstance(src, ConstantRef): dest_label = self.get_label(dest.name) self.emitter.emit(LDA(ZeroPage(dest_label))) self.emitter.emit(ADC(Immediate(Byte(src.low_byte())))) self.emitter.emit(STA(ZeroPage(dest_label))) self.emitter.emit(LDA(ZeroPage(Offset(dest_label, 1)))) self.emitter.emit(ADC(Immediate(Byte(src.high_byte())))) self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1)))) elif isinstance(src, LocationRef): src_label = self.get_label(src.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(ZeroPage(dest_label))) self.emitter.emit(ADC(Absolute(src_label))) self.emitter.emit(STA(ZeroPage(dest_label))) self.emitter.emit(LDA(ZeroPage(Offset(dest_label, 1)))) self.emitter.emit(ADC(Absolute(Offset(src_label, 1)))) self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1)))) else: raise UnsupportedOpcodeError(instr) else: raise UnsupportedOpcodeError(instr) elif opcode == 'sub': if dest == REG_X or dest == REG_Y: raise UnsupportedOpcodeError(instr) if dest == REG_A: if isinstance(src, ConstantRef): self.emitter.emit(SBC(Immediate(Byte(src.value)))) elif isinstance(src, IndexedRef): mode = self.addressing_mode_for_index(src.index) self.emitter.emit(SBC(mode(Offset(self.get_label(src.ref.name), src.offset.value)))) else: self.emitter.emit(SBC(Absolute(self.get_label(src.name)))) elif isinstance(dest, LocationRef) and self.get_type(src) == TYPE_BYTE and self.get_type(dest) == TYPE_BYTE: if isinstance(src, ConstantRef): dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(dest_label))) self.emitter.emit(SBC(Immediate(Byte(src.low_byte())))) self.emitter.emit(STA(Absolute(dest_label))) elif isinstance(src, LocationRef): src_label = self.get_label(src.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(dest_label))) self.emitter.emit(SBC(Absolute(src_label))) self.emitter.emit(STA(Absolute(dest_label))) else: raise UnsupportedOpcodeError(instr) elif isinstance(dest, LocationRef) and self.get_type(src) == TYPE_WORD and self.get_type(dest) == TYPE_WORD: if isinstance(src, ConstantRef): dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(dest_label))) self.emitter.emit(SBC(Immediate(Byte(src.low_byte())))) self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(Absolute(Offset(dest_label, 1)))) self.emitter.emit(SBC(Immediate(Byte(src.high_byte())))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) elif isinstance(src, LocationRef): src_label = self.get_label(src.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(dest_label))) self.emitter.emit(SBC(Absolute(src_label))) self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(Absolute(Offset(dest_label, 1)))) self.emitter.emit(SBC(Absolute(Offset(src_label, 1)))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) else: raise UnsupportedOpcodeError(instr) else: raise UnsupportedOpcodeError(instr) elif opcode == 'cmp': self.compile_cmp(instr, instr.src, instr.dest) elif opcode in ('and', 'or', 'xor',): cls = { 'and': AND, 'or': ORA, 'xor': EOR, }[opcode] if dest == REG_A: if isinstance(src, ConstantRef): self.emitter.emit(cls(Immediate(Byte(src.value)))) elif isinstance(src, IndexedRef): mode = self.addressing_mode_for_index(src.index) self.emitter.emit(cls(mode(Offset(self.get_label(src.ref.name), src.offset.value)))) else: self.emitter.emit(cls(self.absolute_or_zero_page(self.get_label(src.name)))) else: raise UnsupportedOpcodeError(instr) elif opcode == 'inc': self.compile_inc(instr, instr.dest) elif opcode == 'dec': self.compile_dec(instr, instr.dest) elif opcode in ('shl', 'shr'): cls = { 'shl': ROL, 'shr': ROR, }[opcode] if dest == REG_A: self.emitter.emit(cls()) elif isinstance(dest, IndexedRef): mode = self.addressing_mode_for_index(dest.index) self.emitter.emit(cls(mode(Offset(self.get_label(dest.ref.name), dest.offset.value)))) else: self.emitter.emit(cls(self.absolute_or_zero_page(self.get_label(dest.name)))) elif opcode == 'copy': self.compile_copy(instr, instr.src, instr.dest) elif opcode == 'trash': pass elif opcode == 'nop': self.emitter.emit(NOP()) else: raise NotImplementedError(opcode) def compile_call(self, instr): location = instr.location label = self.get_label(instr.location.name) location_type = self.get_type(location) if isinstance(location_type, RoutineType): self.emitter.emit(JSR(Absolute(label))) elif isinstance(location_type, VectorType): trampoline = self.trampolines.setdefault( location, Label(location.name + '_trampoline') ) self.emitter.emit(JSR(Absolute(trampoline))) else: raise NotImplementedError(location_type) def compile_goto(self, instr): location = instr.location label = self.get_label(instr.location.name) location_type = self.get_type(location) if isinstance(location_type, RoutineType): self.emitter.emit(JMP(Absolute(label))) elif isinstance(location_type, VectorType): self.emitter.emit(JMP(Indirect(label))) else: raise NotImplementedError(location_type) def compile_cmp(self, instr, src, dest): """`instr` is only for reporting purposes""" if isinstance(src, LocationRef) and self.get_type(src) == TYPE_WORD: src_label = self.get_label(src.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(dest_label))) self.emitter.emit(CMP(Absolute(src_label))) end_label = Label('end_label') self.emitter.emit(BNE(Relative(end_label))) self.emitter.emit(LDA(Absolute(Offset(dest_label, 1)))) self.emitter.emit(CMP(Absolute(Offset(src_label, 1)))) self.emitter.resolve_label(end_label) return if isinstance(src, ConstantRef) and self.get_type(src) == TYPE_WORD: dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(dest_label))) self.emitter.emit(CMP(Immediate(Byte(src.low_byte())))) end_label = Label('end_label') self.emitter.emit(BNE(Relative(end_label))) self.emitter.emit(LDA(Absolute(Offset(dest_label, 1)))) self.emitter.emit(CMP(Immediate(Byte(src.high_byte())))) self.emitter.resolve_label(end_label) return 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)))) elif isinstance(src, IndexedRef): # FIXME might not work for some dest's (that is, cls's) mode = self.addressing_mode_for_index(src.index) self.emitter.emit(cls(mode(Offset(self.get_label(src.ref.name), src.offset.value)))) else: self.emitter.emit(cls(Absolute(self.get_label(src.name)))) 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()) elif isinstance(dest, IndexedRef): mode = self.addressing_mode_for_index(dest.index) self.emitter.emit(INC(mode(Offset(self.get_label(dest.ref.name), dest.offset.value)))) 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()) elif isinstance(dest, IndexedRef): mode = self.addressing_mode_for_index(dest.index) self.emitter.emit(DEC(mode(Offset(self.get_label(dest.ref.name), dest.offset.value)))) else: self.emitter.emit(DEC(Absolute(self.get_label(dest.name)))) def compile_copy(self, instr, src, dest): if isinstance(src, (IndirectRef, IndexedRef)): src_ref_type = self.get_type(src.ref) else: src_type = self.get_type(src) if isinstance(dest, (IndirectRef, IndexedRef)): dest_ref_type = self.get_type(dest.ref) else: dest_type = self.get_type(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) self.emitter.emit(LDA(Immediate(Byte(src.value)))) self.emitter.emit(STA(IndirectY(dest_label))) elif isinstance(src, LocationRef) and isinstance(dest, IndirectRef) and src_type == TYPE_BYTE and isinstance(dest_ref_type, PointerType): ### copy b, [ptr] + y src_label = self.get_label(src.name) dest_label = self.get_label(dest.ref.name) self.emitter.emit(LDA(Absolute(src_label))) self.emitter.emit(STA(IndirectY(dest_label))) elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef) and dest_type == TYPE_BYTE and isinstance(src_ref_type, PointerType): ### copy [ptr] + y, b src_label = self.get_label(src.ref.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(IndirectY(src_label))) self.emitter.emit(STA(Absolute(dest_label))) elif isinstance(src, IndirectRef) and isinstance(dest, IndirectRef) and isinstance(src_ref_type, PointerType) and isinstance(dest_ref_type, PointerType): ### copy [ptra] + y, [ptrb] + y src_label = self.get_label(src.ref.name) dest_label = self.get_label(dest.ref.name) self.emitter.emit(LDA(IndirectY(src_label))) self.emitter.emit(STA(IndirectY(dest_label))) elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef) and src_type == TYPE_WORD and TableType.is_a_table_type(dest_ref_type, TYPE_WORD): ### copy w, wtab + y src_label = self.get_label(src.name) dest_label = self.get_label(dest.ref.name) mode = self.addressing_mode_for_index(dest.index) self.emitter.emit(LDA(Absolute(src_label))) self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value)))) self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value + 256)))) elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef) and isinstance(src_type, VectorType) and isinstance(dest_ref_type, TableType) and isinstance(dest_ref_type.of_type, VectorType): ### copy vec, vtab + y # FIXME this is the exact same as above - can this be simplified? src_label = self.get_label(src.name) dest_label = self.get_label(dest.ref.name) mode = self.addressing_mode_for_index(dest.index) self.emitter.emit(LDA(Absolute(src_label))) self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value)))) self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value + 256)))) elif isinstance(src, LocationRef) and isinstance(dest, IndexedRef) and isinstance(src_type, RoutineType) and isinstance(dest_ref_type, TableType) and isinstance(dest_ref_type.of_type, VectorType): ### copy routine, vtab + y src_label = self.get_label(src.name) dest_label = self.get_label(dest.ref.name) mode = self.addressing_mode_for_index(dest.index) self.emitter.emit(LDA(Immediate(HighAddressByte(src_label)))) self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value)))) self.emitter.emit(LDA(Immediate(LowAddressByte(src_label)))) self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value + 256)))) elif isinstance(src, ConstantRef) and isinstance(dest, IndexedRef) and src_type == TYPE_WORD and TableType.is_a_table_type(dest_ref_type, TYPE_WORD): ### copy 9999, wtab + y dest_label = self.get_label(dest.ref.name) mode = self.addressing_mode_for_index(dest.index) self.emitter.emit(LDA(Immediate(Byte(src.low_byte())))) self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value)))) self.emitter.emit(LDA(Immediate(Byte(src.high_byte())))) self.emitter.emit(STA(mode(Offset(dest_label, dest.offset.value + 256)))) elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef) and TableType.is_a_table_type(src_ref_type, TYPE_WORD) and dest_type == TYPE_WORD: ### copy wtab + y, w src_label = self.get_label(src.ref.name) dest_label = self.get_label(dest.name) mode = self.addressing_mode_for_index(src.index) self.emitter.emit(LDA(mode(Offset(src_label, src.offset.value)))) self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(mode(Offset(src_label, src.offset.value + 256)))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef) and isinstance(dest_type, VectorType) and isinstance(src_ref_type, TableType) and isinstance(src_ref_type.of_type, VectorType): ### copy vtab + y, vec # FIXME this is the exact same as above - can this be simplified? src_label = self.get_label(src.ref.name) dest_label = self.get_label(dest.name) mode = self.addressing_mode_for_index(src.index) self.emitter.emit(LDA(mode(Offset(src_label, src.offset.value)))) self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(mode(Offset(src_label, src.offset.value + 256)))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) elif src_type == TYPE_BYTE and dest_type == TYPE_BYTE and not isinstance(src, ConstantRef): ### copy b1, b2 src_label = self.get_label(src.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(src_label))) self.emitter.emit(STA(Absolute(dest_label))) elif src_type == TYPE_WORD and dest_type == TYPE_WORD and isinstance(src, ConstantRef): ### copy 9999, w dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Immediate(Byte(src.low_byte())))) self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(Immediate(Byte(src.high_byte())))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) elif src_type == TYPE_WORD and dest_type == TYPE_WORD and not isinstance(src, ConstantRef): ### copy w1, w2 src_label = self.get_label(src.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(src_label))) self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) elif isinstance(src_type, VectorType) and isinstance(dest_type, VectorType): ### copy v1, v2 src_label = self.get_label(src.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Absolute(src_label))) self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) elif isinstance(src_type, RoutineType) and isinstance(dest_type, VectorType): ### copy routine, vec src_label = self.get_label(src.name) dest_label = self.get_label(dest.name) self.emitter.emit(LDA(Immediate(HighAddressByte(src_label)))) self.emitter.emit(STA(Absolute(dest_label))) self.emitter.emit(LDA(Immediate(LowAddressByte(src_label)))) self.emitter.emit(STA(Absolute(Offset(dest_label, 1)))) else: raise NotImplementedError(src_type) def compile_if(self, instr): cls = { False: { 'c': BCC, 'z': BNE, 'n': BPL, }, True: { 'c': BCS, 'z': BEQ, 'n': BMI, }, }[instr.inverted].get(instr.src.name) if cls is None: raise UnsupportedOpcodeError(instr) else_label = Label('else_label') self.emitter.emit(cls(Relative(else_label))) self.compile_block(instr.block1) if instr.block2 is not None: if instr.block1.shallow_contains_goto: self.emitter.resolve_label(else_label) self.compile_block(instr.block2) else: end_label = Label('end_label') self.emitter.emit(JMP(Absolute(end_label))) self.emitter.resolve_label(else_label) self.compile_block(instr.block2) self.emitter.resolve_label(end_label) else: self.emitter.resolve_label(else_label) def compile_repeat(self, instr): top_label = self.emitter.make_label() self.compile_block(instr.block) if instr.src is None: # indicates 'repeat forever' self.emitter.emit(JMP(Absolute(top_label))) else: cls = { False: { 'c': BCC, 'z': BNE, 'n': BPL, }, True: { 'c': BCS, 'z': BEQ, 'n': BMI, }, }[instr.inverted].get(instr.src.name) if cls is None: 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) final = instr.final.succ() elif instr.direction < 0: self.compile_dec(instr, instr.dest) final = instr.final.pred() self.compile_cmp(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) self.emitter.emit(CLI()) def compile_save(self, instr): for location in instr.locations: if location == REG_A: self.emitter.emit(PHA()) elif location == REG_X: self.emitter.emit(TXA()) self.emitter.emit(PHA()) elif location == REG_Y: self.emitter.emit(TYA()) self.emitter.emit(PHA()) else: src_label = self.get_label(location.name) self.emitter.emit(LDA(Absolute(src_label))) self.emitter.emit(PHA()) self.compile_block(instr.block) for location in reversed(instr.locations): if location == REG_A: self.emitter.emit(PLA()) elif location == REG_X: self.emitter.emit(PLA()) self.emitter.emit(TAX()) elif location == REG_Y: self.emitter.emit(PLA()) self.emitter.emit(TAY()) else: src_label = self.get_label(location.name) self.emitter.emit(PLA()) self.emitter.emit(STA(Absolute(src_label))) def compile_point_into(self, instr): self.pointer_assoc[instr.pointer.name] = instr.table.name self.compile_block(instr.block) del self.pointer_assoc[instr.pointer.name] def compile_reset(self, instr): table_name = self.pointer_assoc[instr.pointer.name] src_label = Offset(self.get_label(table_name), instr.offset.value) dest_label = self.get_label(instr.pointer.name) self.emitter.emit(LDA(Immediate(HighAddressByte(src_label)))) self.emitter.emit(STA(ZeroPage(dest_label))) self.emitter.emit(LDA(Immediate(LowAddressByte(src_label)))) self.emitter.emit(STA(ZeroPage(Offset(dest_label, 1))))