diff options
| author | user <user@clank> | 2026-07-18 21:42:27 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-18 21:42:27 +0200 |
| commit | 554fd415d34235fda6c172fc09b19278764f00c3 (patch) | |
| tree | c97cfaa03a281d61d39820fbddbb3eef0705bcce | |
| parent | term: cache tile attrs in COL_BUF's spare stride bytes (3.5x scroll) (diff) | |
| download | gbos-554fd415d34235fda6c172fc09b19278764f00c3.tar.gz gbos-554fd415d34235fda6c172fc09b19278764f00c3.tar.xz gbos-554fd415d34235fda6c172fc09b19278764f00c3.zip | |
term: 3 more renderer wins - 10.9x scroll, 2.7x putc vs pre-cache
1. color_setup memoization: a call with the same (colL,colR) pair as
last time returns immediately (masks/attr still valid in WRAM).
Runs of same-colored cells - i.e. almost all text - hit this.
2. build_tile blank fast path: two spaces = bg-only planes, 8 constant
rows; term_scroll's cleared line and every blank region skip the
glyph pipeline entirely.
3. build_tile two-phase rewrite: combine both glyphs into wRowBuf with
pointers in registers (the old per-row 16-bit WRAM pointer walk was
most of the blit), then compose planes unrolled with plane-0 masks
in B/C.
4. term_write_tilemap attr pass: split each row at the pos-256 VRAM
bank boundary into two tight cache->tilemap copy runs - no per-tile
addressing or bank test.
Measured (emulator cycle counter, ANSI test screen):
term_scroll 857k -> 248k (attr cache) -> 78.5k T-cycles
term_write_tilemap 723k -> 114k -> 45.4k
term_putc 18.2k -> 6.7k
A scroll is now ~1.1 frames of guest CPU (was 12); a full 40-char line
prints in ~63ms of guest time (was ~173ms).
Diffstat (limited to '')
| -rw-r--r-- | include/gbos.inc | 3 | ||||
| -rw-r--r-- | src/term.asm | 242 |
2 files changed, 144 insertions, 101 deletions
diff --git a/include/gbos.inc b/include/gbos.inc index e63d53f..be6009f 100644 --- a/include/gbos.inc +++ b/include/gbos.inc @@ -39,6 +39,9 @@ DEF wCSset EQU $D5AD ; color_setup scratch: 7-bit set of colors present DEF wCSpb EQU $D5AE ; color_setup scratch: palette*8 (SlotOf base) DEF wCStmp0 EQU $D5AF ; color_setup scratch: assembled plane-0 mask DEF wCStmp1 EQU $D5B0 ; color_setup scratch: assembled plane-1 mask +DEF wCSPL EQU $D5B1 ; color_setup memo: last (colL,colR) pair - a call +DEF wCSPR EQU $D5B2 ; with the same pair returns early (masks valid) +DEF wRowBuf EQU $D5B3 ; build_tile: 8 combined glyph rows (L=hi/R=lo nibble) DEF OSK_DOCK EQU 3 ; on-screen keyboard height in rows (dock at the bottom) DEF rKEY1 EQU $FF4D ; CGB speed switch DEF rSB EQU $FF01 ; serial data diff --git a/src/term.asm b/src/term.asm index aebe7e3..fbeaea3 100644 --- a/src/term.asm +++ b/src/term.asm @@ -34,8 +34,6 @@ DEF ACACHE EQU 40 ; attr cache in COL_BUF's spare stride bytes: the tile ; per tile instead of color_setup per tile per scroll). ; terminal state (WRAMX gap $D580-$D5FF) -DEF wGlyphL EQU $D580 ; 2 -DEF wGlyphR EQU $D582 ; 2 DEF wVram EQU $D584 ; 2 DEF wCurRow EQU $D586 ; cursor screen row 0..17 DEF wCurCol EQU $D587 ; cursor screen col 0..39 @@ -92,6 +90,9 @@ term_init:: ld [wViewTop], a ; view starts unshifted (all 18 rows) ld [wOskVisible], a ; OSK hidden until toggled ld [wAnsiState], a ; ANSI parser idle + ld a, $FF + ld [wCSPL], a ; invalidate the color_setup memo (no packed + ; color is $FF: bg/fg nibbles are 0..7) ld a, 1 ld [wCursorOn], a ld a, 7 @@ -151,15 +152,32 @@ term_write_tilemap:: ld a, 1 ld [rVBK], a - ld c, 0 + xor a + ld [wRT_sr], a ; screen row (render_tile scratch, free here) .a_row - call .slot20 ; DE = pos, HL = tilemap addr, wTMSlot = slot - ld b, TCOLS/2 -.a_col - ; attribute = cached palette bits (render_tile keeps the cache fresh) - ; | bank bit(pos hi). No color math here - just a table read. - push hl ; save tilemap write ptr - push de ; save pos + ld a, [wRT_sr] + ld c, a + call .slot20 ; DE = pos start, HL = tilemap addr, wTMSlot set + ; Split the row at the pos-256 VRAM-bank boundary: the first k tiles are + ; bank 0 (attr = cache byte), the rest bank 1 (attr | $08). Each run is + ; then a tight cache->tilemap copy: no per-tile addressing or bank test. + ld a, d + or a + jr nz, .k0 ; pos >= 256: whole row is bank 1 + xor a + sub e ; 256 - LOW(pos) (mod 256; 0 means 256) + jr z, .k20 + cp TCOLS/2 + jr c, .khave ; boundary lands inside this row +.k20 + ld a, TCOLS/2 + jr .khave +.k0 + xor a +.khave + ld [wRT_tcol], a ; k = bank-0 tiles in this row + ; DE = &cache[slot][0] (pos isn't needed beyond the split above) + push hl ld a, [wTMSlot] ld l, a ld h, 0 @@ -171,26 +189,39 @@ term_write_tilemap:: add hl, hl ; slot*64 ld de, COL_BUF + ACACHE add hl, de - ld a, TCOLS/2 - sub b ; tcol - add l - ld l, a - ld a, h - adc 0 - ld h, a - ld a, [hl] ; A = cached attr (palette bits) - pop de ; restore pos - pop hl ; restore tilemap write ptr - bit 0, d ; HIGH(pos): 0 or 1 -> VRAM bank bit - jr z, .aw - or $08 ; VRAM bank 1 tile -.aw + ld d, h + ld e, l + pop hl + ; run 1: k tiles in bank 0 + ld a, [wRT_tcol] + ld b, a + or a + jr z, .run2 +.r1 + ld a, [de] + inc de ld [hl+], a + dec b + jr nz, .r1 +.run2 + ; run 2: the remaining tiles in bank 1 + ld a, [wRT_tcol] + ld b, a + ld a, TCOLS/2 + sub b + jr z, .arnext + ld b, a +.r2 + ld a, [de] inc de + or $08 + ld [hl+], a dec b - jr nz, .a_col - inc c - ld a, c + jr nz, .r2 +.arnext + ld a, [wRT_sr] + inc a + ld [wRT_sr], a cp TROWS jr c, .a_row xor a @@ -287,9 +318,21 @@ glyph_ptr: ; preserves B, C (so it runs inside the tilemap column loop). ; ----------------------------------------------------------------------------- color_setup: + ; memo: same (colL,colR) pair as the previous call? wPalAttr and all four + ; plane masks are still valid - skip the whole Fano walk. Runs of same- + ; colored cells (= almost all text) hit this. + ld a, [wCSPL] + cp d + jr nz, .miss + ld a, [wCSPR] + cp e + ret z +.miss ld a, d + ld [wCSPL], a ld [wCSL], a ld a, e + ld [wCSPR], a ld [wCSR], a ; set = union of the (non-black) colors of both cells xor a @@ -428,91 +471,88 @@ build_tile:: ld [wVram], a ld a, h ld [wVram+1], a + ; ---- fast path: two spaces = bg-only planes, 8 constant rows ---- + ; (the case term_scroll's cleared line and every blank region hits) ld a, b - call glyph_ptr - ld a, l - ld [wGlyphL], a - ld a, h - ld [wGlyphL+1], a + cp $20 + jr nz, .glyphs ld a, c - call glyph_ptr - ld a, l - ld [wGlyphR], a - ld a, h - ld [wGlyphR+1], a - ld b, 8 ; row counter - ld a, [wVram] - ld l, a - ld a, [wVram+1] - ld h, a ; HL = VRAM dest -.brow + cp $20 + jr nz, .glyphs + ld a, [wBGP0] + ld d, a ; plane 0 = bg mask (g = 0) + ld a, [wBGP1] + ld e, a ; plane 1 = bg mask + ld b, 7 +.blank + ld a, d + ld [hl+], a + ld a, e + ld [hl+], a + dec b + jr nz, .blank + ld a, [wCurMask] ; last row: cursor underline + or d + ld [hl+], a + ld [hl], e + ret + +.glyphs + ; ---- phase 1: combine both glyphs into wRowBuf (8 nibble-pair rows). + ; Sequential pointers live in HL/DE registers; the old per-row 16-bit + ; pointer walk through WRAM was most of the blit cost. + ld a, c + call glyph_ptr ; (clobbers DE, so right glyph first...) push hl - ld a, [wGlyphL] - ld e, a - ld a, [wGlyphL+1] - ld d, a - ld a, [de] + ld a, b + call glyph_ptr ; HL = left glyph + pop de ; DE = right glyph +FOR I, 8 + ld a, [hl+] swap a and $F0 - ld c, a ; left glyph -> high nibble - ld a, [wGlyphR] - ld e, a - ld a, [wGlyphR+1] - ld d, a + ld c, a ; left -> high nibble ld a, [de] + inc de and $0F - or c ; A = glyph row g (left=hi nibble, right=lo) - ld c, a ; C = g + or c ; g = left | right + ld [wRowBuf + I], a +ENDR + ; ---- phase 2: compose the bitplanes, unrolled, plane-0 masks in B/C ---- + ld a, [wFGP0] + ld b, a + ld a, [wBGP0] + ld c, a + ld a, [wVram] + ld l, a + ld a, [wVram+1] + ld h, a ; HL = VRAM dest +FOR I, 8 + ld a, [wRowBuf + I] + ld d, a ; D = g + and b ; g & FGP0 + ld e, a + ld a, d cpl - ld d, a ; D = ~g (the empty pixels = background) - ; plane1 = (g & wFGP1) | (~g & wBGP1) -> hold in E for the write below - ld a, [wFGP1] - and c + and c ; ~g & BGP0 + or e ; plane 0 + IF I == 7 ld e, a - ld a, [wBGP1] - and d + ld a, [wCurMask] ; cursor underline on the last row or e - ld e, a ; E = plane 1 - ; plane0 = (g & wFGP0) | (~g & wBGP0) (+cursor underline on the last row) - ld a, [wFGP0] - and c - ld c, a ; C = g & wFGP0 - ld a, [wBGP0] + ENDC + ld [hl+], a + ld a, d + cpl + ld e, a ; ~g + ld a, [wBGP1] + and e + ld e, a + ld a, [wFGP1] and d - or c ; A = plane 0 base - ld c, a - ld a, b - dec a - jr nz, .nocur ; last row (b==1)? add cursor underline - ld a, [wCurMask] - or c - jr .wr -.nocur - ld a, c -.wr - pop hl - ld [hl+], a ; plane 0 - ld a, e - ld [hl+], a ; plane 1 - ; advance glyph pointers - MUST NOT touch HL (it holds the VRAM dest!) - ld a, [wGlyphL] - inc a - ld [wGlyphL], a - jr nz, .nl - ld a, [wGlyphL+1] - inc a - ld [wGlyphL+1], a -.nl - ld a, [wGlyphR] - inc a - ld [wGlyphR], a - jr nz, .nr - ld a, [wGlyphR+1] - inc a - ld [wGlyphR+1], a -.nr - dec b - jr nz, .brow + or e ; plane 1 + ld [hl+], a +ENDR ret ; ----------------------------------------------------------------------------- |
