71 lines
2.6 KiB
Python
71 lines
2.6 KiB
Python
"""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()
|