Files
vt1200/harness/udpstream/vram_tile.asm
T
arti 9139322892 perf(tiles): program the blit constants once per frame, not once per tile
Spec 8.3's hoist, and the 60 fps work showed it was the per-tile cost that
mattered, not the per-frame one.

Every tile_blit() call used to save 8 B registers, load B1/B3/B4/B7/B8/B9 and
write 11 I/O registers (PSIZE, CONVSP, CONVDP, PMASK, the CONTROL read-modify-
write and its read-back) -- all constants.  The user's guide marks only SADDR
and DADDR changed by PIXBLT, and the TMS34010 agent's reading of it confirms
B1/B3 survive, so those two are the only ones a per-tile call has to reload.

vram_tile.asm now exports tile_init_() (the constants, once per frame, called
from paint_tiles/paint_delta) and a tile_blit_() that is setf, two pushes, two
register loads, pixblt, two pops, rets.

Measured on the real 140-tile deltas at a 60 fps send, same window, terminal's
own status frames:

  before the hoist   162 accepted
  after the hoist    231 accepted   (+43%)

and the one-tile stream (where tile cost is nil) already runs at 60 fps with
DINT: 600 frames sent at 59.8 fps, 579 accepted (96.5%), drops=0.

Pixel verdicts unchanged: modes tiles, delta and replay all RESULT PASS with
tile_check; 155 unit tests pass.

What the remaining gap is, for the next reader: MAME's PIXBLT moves about
3.4 us per destination word (0.22 ms per 32x32 tile, measured from the accepted
rate) against the unit's measured 0.40 us/word for FILL.  A 140-tile delta is
8,960 words, i.e. ~30 ms of MAME CPU against a 16.7 ms frame -- so 60 fps for
this content is bounded by the emulator's pixel engine, not by the design.
Whether the unit can do it turns on the one number nobody has measured there:
PIXBLT B,XY's per-word rate (spec 10, risk table).
2026-10-31 22:40:42 +02:00

131 lines
5.8 KiB
NASM

; vram_tile.asm -- PIXBLT B,XY: one 32x32 1-bpp dictionary tile to the screen.
;
; Task 4's tile primitive. A dictionary entry is 32 rows of FOUR bytes (32 px
; is two 16-bit words) = 128 bytes = 1024 bits, row-major, LSB first within each
; byte (bit 0 is the leftmost pixel -- the scanout order) -- tilecost.py's
; layout. PIXBLT's B source form reads packed 1-bit
; pixels from a linear bit address and writes an XY destination, so one tile is
;
; SADDR = dict_base + index * 1024 (128 bytes a tile: index << 10)
; SPTCH = 32 (4 bytes a source row)
; DADDR = (ty*32 << 16) | (tx*32) (XY destination)
; DYDX = (32 << 16) | 32 (32 rows, 32 pixels)
;
; DESCRIPTION-register assignments for PIXBLT B,XY (TMS34010 User's Guide,
; instruction reference; vram.asm already uses the same values for L,XY and
; vram_b.asm for B,XY):
; SADDR = B0 source bit address (1 bpp, packed)
; SPTCH = B1 source pitch in bits (4 bytes = 32 bits a row)
; 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: they are implied operands of every graphics
; instruction, and a PIXBLT B expands a packed 1-bpp source into pixel values.
; They are set explicitly because the resident OS leaves them arbitrary --
; measured in the strip work: the first draw run painted every row black
; because nothing had set B9. vram_b.asm's comment records the same finding.
;
; 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 point / calling form (ruling R13). The TMS34010 C compiler's argument
; frame is not decoded by any hand-written assembly in this repository; the
; published-word form of vram.asm and vram_b.asm is used instead. This symbol
; takes NO arguments: the two values a caller can change live in .bss words
;
; tile_src the source tile's bit address
; tile_daddr the destination XY word
;
; and the C wrapper tile_blit(src, tx, ty) is the only entry point. B7 (DYDX)
; is the constant 32x32 this primitive exists for.
;
; Two entry points, split by what changes: tile_init_() programs the constants
; (pitch, offset, dimensions, colours, PSIZE/CONVSP/CONVDP/PMASK/CONTROL) once
; per frame, and tile_blit_() then only reloads SADDR and DADDR -- the two
; registers PIXBLT destroys -- before executing it.
;
; Revert note: this file is new in Task 4 and nothing else calls it. To
; revert, delete it, the VRAMTILE entries in build-stream.sh and the
; tile_blit/paint_tiles code in vidstream.c; the strip path (vram_b.asm) does
; not reference any symbol defined here and is unaffected. The disassembly
; this copies is vram_b.asm's, and its value table is quoted above from
; vram_b.asm rather than re-derived.
;
; This file does NOT define _vid_dint_/_vid_eint_: they live in vram_b.asm and
; both objects are linked, so a second definition would be a duplicate symbol.
.ref _tile_src
.ref _tile_daddr
.text
; _tile_init_ -- program everything that does not vary between tiles.
;
; Spec 8.3's hoist, and it is worth real time in MAME: measured per-tile cost
; was ~0.36 ms against the unit's ~40 us, and the call did 11 I/O-register
; accesses plus 8 push/pop pairs for constants that never change. The registers
; this sets -- PSIZE, CONVSP, CONVDP, PMASK, CONTROL and B1 (SPTCH), B3 (DPTCH),
; B4 (OFFSET), B7 (DYDX), B8 (COLOR0), B9 (COLOR1) -- survive PIXBLT (the
; user's guide marks only SADDR/DADDR and B10-B14 as changed), so once per
; frame is enough.
;
; B8/B9 are NOT I/O registers: they are implied operands of every graphics
; instruction, and the resident OS leaves them arbitrary -- measured in the
; strip work: the first draw run painted every row black because nothing had
; set B9.
.globl _tile_init_
_tile_init_:
setf 32, 0, 1
movi 020h, b1 ; SPTCH = 32 bits = four bytes a tile row
movi 800h, b3 ; DPTCH = 2048 bits = 256-byte screen row
movi 04000000h, b4 ; OFFSET = VRAM base
movi 0200020h, b7 ; DYDX = (32 rows << 16) | 32 pixels
clr b8 ; COLOR0 = 0 (black)
movi 0FFFFh, b9 ; COLOR1 = all planes set (white)
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
; raster op 0 (replace), CD/RM/RR preserved
move @0C00000B0h, a1, 0 ; CONTROL
andi 0801Fh, a1
move a1, @0C00000B0h, 0
move @0C00000B0h, a1, 0 ; read back: I/O writes are pipelined
rets
; _tile_blit_ -- one 32x32 tile, everything already programmed.
;
; Only B0 (SADDR) and B2 (DADDR) vary, and PIXBLT destroys both, so those two
; are the only registers this saves and reloads. tile_init_() must have run.
;
; Revert note: this file is new in Task 4 and nothing else calls it. To
; revert, delete it, the VRAMTILE entries in build-stream.sh and the
; tile_blit/paint_tiles code in vidstream.c; the strip path (vram_b.asm) does
; not reference any symbol defined here and is unaffected.
.globl _tile_blit_
_tile_blit_:
setf 32, 0, 1
move b0, -*sp, 1
move b2, -*sp, 1
move @_tile_src, b0, 1 ; SADDR: dictionary entry, index << 10 bits
move @_tile_daddr, b2, 1 ; DADDR (XY): (ty*32 << 16) | tx*32
pixblt b, xy
move *sp+, b2, 1
move *sp+, b0, 1
rets
.end