"""Generates the CoCo cartridge viewer (Motorola 6809 machine code). Runs from the Program Pak ROM at $C000 (the CoCo autostarts it). Sets the VDG graphics mode via PIA1 $FF22 and the SAM video registers (PMODE 3 and 4 are both 6144-byte SAM mode 6; only the $FF22 byte differs), copies the 6144-byte image from cart ROM down to the video page at $0E00, then holds it. display: forever (hold), key (poll the keyboard then reset), seconds (count 60 Hz field-syncs then reset). A Program Pak can't cleanly return to BASIC, so key and seconds reset the machine (which re-displays the picture). """ from __future__ import annotations from .mc6809 import Asm CART_BASE = 0xC000 VIDEO = 0x0E00 SCREEN_BYTES = 6144 RATE = 60 # NTSC field-syncs per second def build(data_src: int, vdg: int = 0xF8, display: str = "forever", seconds: int = 0) -> bytes: a = Asm(CART_BASE) a.orcc(0x50) # mask IRQ + FIRQ; we run standalone a.lda_imm(vdg) a.sta_ext(0xFF22) # VDG graphics mode (PMODE 3 = $E0/$E8, PMODE 4 = $F8) # SAM video mode 6 (V2=1, V1=1, V0=0). SAM regs toggle by address; data ignored. a.sta_ext(0xFFC5) # V2 set a.sta_ext(0xFFC3) # V1 set a.sta_ext(0xFFC0) # V0 clear # SAM video offset = 7 ($0E00): F0,F1,F2 set; F3..F6 clear. a.sta_ext(0xFFC7); a.sta_ext(0xFFC9); a.sta_ext(0xFFCB) a.sta_ext(0xFFCC); a.sta_ext(0xFFCE); a.sta_ext(0xFFD0); a.sta_ext(0xFFD2) # copy SCREEN_BYTES from cart ROM (data_src) to the video page a.ldx_imm(data_src) a.ldu_imm(VIDEO) a.label("copy") a.lda_postinc("x") a.sta_postinc("u") a.cmpu_imm(VIDEO + SCREEN_BYTES) a.bne("copy") # ---- hold the picture ---- if display == "key": a.clra() a.sta_ext(0xFF02) # drive all keyboard columns low a.label("kwait") a.lda_ext(0xFF00) # row sense a.ora_imm(0x80) # ignore the joystick-compare bit 7 a.cmpa_imm(0xFF) a.beq("kwait") # all rows high -> no key, keep waiting a.jmp_ind(0xFFFE) # reset elif display == "seconds": a.ldd_imm(max(1, min(0xFFFF, int(seconds) * RATE))) a.label("swait") a.lda_ext(0xFF03) # PIA0 CB1 (field-sync) status a.bpl("swait") # bit 7 clear -> no new field yet a.lda_ext(0xFF02) # read PB data to clear the field-sync flag a.subd_imm(1) a.bne("swait") a.jmp_ind(0xFFFE) # reset else: # forever a.label("hang") a.bra("hang") return a.resolve()