"""A tiny Motorola 6809 machine-code emitter (just what the CoCo viewer needs). No 6809 assembler is installed, so -- as with the TI's TMS9900 emitter -- we emit opcodes directly. Supports labels + 8-bit relative-branch backpatching. Index-register codes for ,R+ postbytes: X=0x80, Y=0xA0, U=0xC0, S=0xE0. """ from __future__ import annotations _IDX = {"x": 0x80, "y": 0xA0, "u": 0xC0, "s": 0xE0} class Asm: def __init__(self, base: int): self.base = base self.code = bytearray() self.labels: dict[str, int] = {} self._fix: list[tuple[int, str]] = [] # (pos of rel byte, label) def pos(self) -> int: return self.base + len(self.code) def label(self, name: str): self.labels[name] = self.pos() def _b(self, *bs): self.code += bytes(bs) def _w(self, v): # 16-bit, big-endian self.code += bytes([(v >> 8) & 0xFF, v & 0xFF]) # ---- inherent ---- def nop(self): self._b(0x12) def clra(self): self._b(0x4F) def clrb(self): self._b(0x5F) def deca(self): self._b(0x4A) def decb(self): self._b(0x5A) def inca(self): self._b(0x4C) def incb(self): self._b(0x5C) def rts(self): self._b(0x39) def sync(self): self._b(0x13) # ---- immediate ---- def lda_imm(self, v): self._b(0x86, v & 0xFF) def ldb_imm(self, v): self._b(0xC6, v & 0xFF) def anda_imm(self, v): self._b(0x84, v & 0xFF) def ora_imm(self, v): self._b(0x8A, v & 0xFF) def cmpa_imm(self, v): self._b(0x81, v & 0xFF) def cmpb_imm(self, v): self._b(0xC1, v & 0xFF) def orcc(self, v): self._b(0x1A, v & 0xFF) def andcc(self, v): self._b(0x1C, v & 0xFF) def ldd_imm(self, v): self._b(0xCC); self._w(v) def subd_imm(self, v): self._b(0x83); self._w(v) def ldx_imm(self, v): self._b(0x8E); self._w(v) def ldu_imm(self, v): self._b(0xCE); self._w(v) def ldy_imm(self, v): self._b(0x10, 0x8E); self._w(v) def lds_imm(self, v): self._b(0x10, 0xCE); self._w(v) def cmpx_imm(self, v): self._b(0x8C); self._w(v) def cmpu_imm(self, v): self._b(0x11, 0x83); self._w(v) # ---- extended (16-bit address) ---- def lda_ext(self, a): self._b(0xB6); self._w(a) def sta_ext(self, a): self._b(0xB7); self._w(a) def ldb_ext(self, a): self._b(0xF6); self._w(a) def stb_ext(self, a): self._b(0xF7); self._w(a) def std_ext(self, a): self._b(0xFD); self._w(a) def jmp_ext(self, a): self._b(0x7E); self._w(a) def jsr_ext(self, a): self._b(0xBD); self._w(a) def jmp_ind(self, a): self._b(0x6E, 0x9F); self._w(a) # JMP [addr] (indirect) # ---- indexed auto-increment: LDA ,R+ / STA ,R+ ---- def lda_postinc(self, r): self._b(0xA6, _IDX[r]) def sta_postinc(self, r): self._b(0xA7, _IDX[r]) def ldb_postinc(self, r): self._b(0xE6, _IDX[r]) def stb_postinc(self, r): self._b(0xE7, _IDX[r]) # ---- 8-bit relative branches ---- def _branch(self, op, label): self._b(op) self._fix.append((len(self.code), label)) self._b(0x00) # placeholder def bra(self, label): self._branch(0x20, label) def bne(self, label): self._branch(0x26, label) def beq(self, label): self._branch(0x27, label) def bpl(self, label): self._branch(0x2A, label) def bmi(self, label): self._branch(0x2B, label) def bcc(self, label): self._branch(0x24, label) def bcs(self, label): self._branch(0x25, label) def resolve(self) -> bytes: for pos, label in self._fix: target = self.labels[label] rel = target - (self.base + pos + 1) if not -128 <= rel <= 127: raise ValueError(f"branch to {label} out of range ({rel})") self.code[pos] = rel & 0xFF return bytes(self.code)