Files
opencode.energia.arti.ee 45110270a7 feat(stream): row-strip blit with SPTCH=0 vertical stretch
One PIXBLT B,XY per source row paints the whole screen: DYDX names
`stretch` rows and SPTCH = 0 makes each of them read the same packed
source row, so the vertical stretch costs no extra CPU work.

vram_b.asm carries the primitive (PSIZE=1, CONVSP/CONVDP=0x14,
PMASK=0, OFFSET=0x04000000, DPTCH=0x800, raster op 0) plus DINT/EINT
thunks; vidstream.c masks interrupts for the burst (spec 8.9), refuses
geometry the screen cannot hold, and counts an OS repaint through a
sentinel word in the unused right edge of the last screen row
(OS_TOUCH, 0x01810220).

MAME mode draw injects a 128-row frame (stretch 8 = 1024 screen rows,
ruling R3) and dumps the screen; vidstream_check.py compares all 1024
rows against the bytes actually delivered.  RED: 0/1024 rows matched.
GREEN: 1024/1024, RX_OK=1, OS_TOUCH=0.

Fixes a latent harness bug found on the way: `bpclear <address>` takes
a breakpoint INDEX, so the idle-loop breakpoint was never cleared; once
a burst masked interrupts long enough, its stale action fired again and
re-entered main (a second malloc, counters back to 0).
2026-10-30 23:43:02 +00:00

120 lines
4.6 KiB
NASM

; vram_b.asm -- PIXBLT B,XY: a packed 1-bpp RAM row straight to the screen.
;
; Task 5's blit primitive. PIXBLT's B source form reads the source as packed
; 1-bit pixels from a LINEAR bit address and writes an XY destination, which is
; exactly the strip decoder's shape: a 1280-pixel row (160 bytes) in the slot,
; painted down `stretch` screen rows.
;
; The vertical stretch is one instruction, not a loop over screen rows: SPTCH
; = 0 makes every source row read the SAME source address, so a PIXBLT whose
; DYDX names `stretch` rows paints the one row `stretch` times.
;
; DESCRIPTION-register assignments for PIXBLT B,XY (TMS34010 User's Guide,
; instruction reference; vram.asm already uses the same values for L,XY):
; SADDR = B0 source bit address (1 bpp, packed)
; SPTCH = B1 source pitch in bits; 0 = do not advance the source
; DADDR = B2 destination XY: (y << 16) | x
; DPTCH = B3 destination pitch in bits (0x800 = 256-byte screen row)
; OFFSET = B4 VRAM base bit address
; DYDX = B7 dimensions in XY form: (rows << 16) | columns
;
; COLOR0 = B8 pixel value for a clear source bit (0 = black)
; COLOR1 = B9 pixel value for a set source bit (0xFFFF = white)
;
; B8/B9 are NOT I/O registers: COLOR0 and COLOR1 are implied B-register
; operands of every graphics instruction (vram.asm's FILL passes its pattern in
; B9 for the same reason). They are explicit here because a PIXBLT B expands
; a packed 1-bpp source into pixel values, so whatever the resident OS left in
; B9 becomes the colour of every set pixel -- measured: the first draw run
; painted every row it blitted black because nothing had set B9 (the padding
; past column 1280, which the blit does not write, kept the OS's old bytes).
;
; PSIZE (0C0000150h) 1 one bit per destination pixel
; CONVSP (0C0000130h) 14h 0x800-bit pitch code (1 << (~0x14 & 0x1F) = 2048)
; CONVDP (0C0000140h) 14h same, for the XY destination
; PMASK (0C0000160h) 0 every plane writable
; CONTROL (0C00000B0h) raster op 0 (replace), CD/RM/RR preserved
;
; Entry points. The TMS34010 C compiler passes arguments in its own frame-
; pointer scheme, and no hand-written assembly in this repository has ever
; DECODED that frame: the ROM thunks in entry.asm forward the caller's stack
; frame through `calla` without touching it, and the two array primitives in
; vram.asm take their inputs from published words in .bss with a no-argument
; entry (called by lance_cmd.c, measured on the unit). This primitive follows
; the vram.asm form, so the three interface values named in the plan live in
; blit_src / blit_daddr / blit_dydx (blit_sptch only exists for vram.asm and is
; ignored: this primitive always sets SPTCH = 0). The C side wraps them in
; blit_row() so the call reads as blit_row(src, daddr_xy, dydx).
;
; All inputs are reloaded on every call: PIXBLT leaves SADDR/DADDR advanced, so
; a caller that "saves" them saves a half-consumed rectangle.
;
; _vid_dint_ / _vid_eint_ -- interrupts off/on. The C compiler cannot emit
; DINT/EINT, and spec section 8.9 wants the OS masked for the whole burst so a
; resident repaint cannot tear the picture.
.ref _blit_src
.ref _blit_daddr
.ref _blit_dydx
.text
.globl _vram_blit_b_
_vram_blit_b_:
setf 32, 0, 1
move b0, -*sp, 1
move b1, -*sp, 1
move b2, -*sp, 1
move b3, -*sp, 1
move b4, -*sp, 1
move b7, -*sp, 1
move b8, -*sp, 1
move b9, -*sp, 1
move @_blit_src, b0, 1 ; SADDR: packed 1-bpp source row
clr b1 ; SPTCH = 0: every row is the same source row
move @_blit_daddr, b2, 1 ; DADDR (XY)
movi 800h, b3 ; DPTCH = 2048 bits = 256-byte row pitch
movi 04000000h, b4 ; OFFSET = VRAM base
move @_blit_dydx, b7, 1 ; DYDX (rows, columns)
clr b8 ; COLOR0 = 0 (black)
movi 0FFFFh, b9 ; COLOR1 = all planes set (white)
; --- pixel format ------------------------------------------------------
movi 1, a1
move a1, @0C0000150h, 0 ; PSIZE = 1
movi 14h, a1
move a1, @0C0000130h, 0 ; CONVSP = pitch code for 0x800 bits
move a1, @0C0000140h, 0 ; CONVDP
clr a1
move a1, @0C0000160h, 0 ; PMASK = 0 (all planes writable)
move a1, @0C0000170h, 0 ; upper half, for forward compatibility
; --- graphics operation: replace, no transparency, no window ----------
move @0C00000B0h, a1, 0 ; CONTROL
andi 0801Fh, a1 ; keep CD (15) and RM/RR (2-4) only
move a1, @0C00000B0h, 0
move @0C00000B0h, a1, 0 ; read back: I/O writes are pipelined
pixblt b, xy
move *sp+, b9, 1
move *sp+, b8, 1
move *sp+, b7, 1
move *sp+, b4, 1
move *sp+, b3, 1
move *sp+, b2, 1
move *sp+, b1, 1
move *sp+, b0, 1
rets
.globl _vid_dint_
_vid_dint_:
dint
rets
.globl _vid_eint_
_vid_eint_:
eint
rets
.end