125 lines
5.3 KiB
Python
125 lines
5.3 KiB
Python
"""A tiny General Instrument CP1610 machine-code emitter (Intellivision).
|
|
|
|
No CP1610 assembler is installed, so -- as with the TMS9900/Z80/6809 emitters --
|
|
we emit opcodes directly. Encodings validated by disassembling a real cart
|
|
(Astrosmash). Output is a list of 16-bit words (the CP1610 fetches 10-bit
|
|
"decles" from the low bits; the Intellivision ROM is 16-bit big-endian at $5000).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
class Asm:
|
|
def __init__(self, base: int):
|
|
self.base = base
|
|
self.words: list[int] = []
|
|
self.labels: dict[str, int] = {}
|
|
self._fix: list[tuple[int, str, str]] = [] # (word index, label, kind)
|
|
|
|
def pos(self) -> int:
|
|
return self.base + len(self.words)
|
|
|
|
def label(self, name: str):
|
|
self.labels[name] = self.pos()
|
|
|
|
def _w(self, *ws):
|
|
self.words.extend(w & 0xFFFF for w in ws)
|
|
|
|
# ---- implied ----
|
|
def sdbd(self): self._w(0x0001)
|
|
def eis(self): self._w(0x0002)
|
|
def dis(self): self._w(0x0003)
|
|
def tci(self): self._w(0x0005)
|
|
def clrc(self): self._w(0x0006)
|
|
def setc(self): self._w(0x0007)
|
|
def nop(self): self._w(0x0034)
|
|
|
|
# ---- single register ----
|
|
def incr(self, r): self._w(0x0008 | r)
|
|
def decr(self, r): self._w(0x0010 | r)
|
|
def comr(self, r): self._w(0x0018 | r)
|
|
def negr(self, r): self._w(0x0020 | r)
|
|
def adcr(self, r): self._w(0x0028 | r)
|
|
def tstr(self, r): self._w(0x0080 | (r << 3) | r) # MOVR r,r sets flags
|
|
|
|
# ---- register-register ----
|
|
# ---- shifts/rotates (R0-R3 only; n = 1 or 2 bits) ----
|
|
def sll(self, r, n=1): self._w(0x0048 | ((n - 1) << 2) | r) # shift left logical
|
|
def slr(self, r, n=1): self._w(0x0060 | ((n - 1) << 2) | r) # shift right logical
|
|
|
|
def movr(self, s, d): self._w(0x0080 | (s << 3) | d)
|
|
def addr(self, s, d): self._w(0x00C0 | (s << 3) | d)
|
|
def subr(self, s, d): self._w(0x0100 | (s << 3) | d)
|
|
def cmpr(self, s, d): self._w(0x0140 | (s << 3) | d)
|
|
def andr(self, s, d): self._w(0x0180 | (s << 3) | d)
|
|
def xorr(self, s, d): self._w(0x01C0 | (s << 3) | d)
|
|
def clrr(self, r): self._w(0x01C0 | (r << 3) | r) # XORR r,r
|
|
def jr(self, r): self._w(0x0080 | (r << 3) | 7) # MOVR r,R7 = jump
|
|
|
|
# ---- external reference (direct addr / immediate / indirect) ----
|
|
def mvi(self, addr, d): self._w(0x0280 | d, addr) # load (addr)->Rd
|
|
def mvo(self, s, addr): self._w(0x0240 | s, addr) # store Rs->(addr)
|
|
def mvii(self, imm, d): self._w(0x0280 | (7 << 3) | d, imm)
|
|
def addi(self, imm, d): self._w(0x02C0 | (7 << 3) | d, imm)
|
|
def subi(self, imm, d): self._w(0x0300 | (7 << 3) | d, imm)
|
|
def cmpi(self, imm, d): self._w(0x0340 | (7 << 3) | d, imm)
|
|
def andi(self, imm, d): self._w(0x0380 | (7 << 3) | d, imm)
|
|
def xori(self, imm, d): self._w(0x03C0 | (7 << 3) | d, imm)
|
|
|
|
def mvi_at(self, m, d): self._w(0x0280 | (m << 3) | d) # load (Rm)->Rd
|
|
def mvo_at(self, s, m): self._w(0x0240 | (m << 3) | s) # store Rs->(Rm)
|
|
def pshr(self, s): self._w(0x0240 | (6 << 3) | s) # MVO@ Rs,R6
|
|
def pulr(self, d): self._w(0x0280 | (6 << 3) | d) # MVI@ R6,Rd
|
|
|
|
# ---- jumps (absolute, 3 words) ----
|
|
def _jword(self, addr, reg):
|
|
self._w(0x0004, (reg << 8) | (((addr >> 10) & 0x3F) << 2), addr & 0x3FF)
|
|
|
|
def j(self, addr): self._jword(addr, 3) # J (no return)
|
|
def jsr(self, addr): self._jword(addr, 1) # JSR R5,addr
|
|
|
|
def j_label(self, label):
|
|
i = len(self.words)
|
|
self._w(0x0004, 0, 0)
|
|
self._fix.append((i, label, "j"))
|
|
|
|
def jsr_label(self, label):
|
|
i = len(self.words)
|
|
self._w(0x0004, 0, 0)
|
|
self._fix.append((i, label, "jsr"))
|
|
|
|
# ---- branches (opcode + displacement word) ----
|
|
def _branch(self, cond, label):
|
|
i = len(self.words)
|
|
self._w(0x0200 | cond, 0) # direction/disp filled by resolve
|
|
self._fix.append((i, label, "b"))
|
|
|
|
def b(self, label): self._branch(0x0, label)
|
|
def beq(self, label): self._branch(0x4, label)
|
|
def bnze(self, label): self._branch(0xC, label)
|
|
def bc(self, label): self._branch(0x1, label)
|
|
def bnc(self, label): self._branch(0x9, label)
|
|
|
|
def decle(self, *vals):
|
|
self._w(*vals)
|
|
|
|
def resolve(self) -> list[int]:
|
|
for i, label, kind in self._fix:
|
|
target = self.labels[label]
|
|
if kind == "j":
|
|
self.words[i + 1] = (3 << 8) | (((target >> 10) & 0x3F) << 2)
|
|
self.words[i + 2] = target & 0x3FF
|
|
elif kind == "jsr":
|
|
self.words[i + 1] = (1 << 8) | (((target >> 10) & 0x3F) << 2)
|
|
self.words[i + 2] = target & 0x3FF
|
|
else: # branch
|
|
a = self.base + i # address of the branch opcode
|
|
if target <= a: # backward: CPU computes target = a + 1 - disp
|
|
disp = a + 1 - target
|
|
self.words[i] |= (1 << 5)
|
|
else: # forward: target = a + 2 + disp
|
|
disp = target - a - 2
|
|
if not 0 <= disp <= 0xFFFF:
|
|
raise ValueError(f"branch to {label} out of range")
|
|
self.words[i + 1] = disp & 0xFFFF
|
|
return list(self.words)
|