diff options
| author | user <user@clank> | 2026-07-18 21:50:22 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-18 21:50:22 +0200 |
| commit | 9b813f2a0eb8626f84489de0f55dc6da2ed22420 (patch) | |
| tree | 0887bb78ecc91a1c63fc5ed76be10adc9b8c2743 | |
| parent | term: 3 more renderer wins - 10.9x scroll, 2.7x putc vs pre-cache (diff) | |
| download | gbos-9b813f2a0eb8626f84489de0f55dc6da2ed22420.tar.gz gbos-9b813f2a0eb8626f84489de0f55dc6da2ed22420.tar.xz gbos-9b813f2a0eb8626f84489de0f55dc6da2ed22420.zip | |
term: SCY ring-scroll - scroll writes ONE map row; OSK toggle is 212 cycles
The BG map's 32 rows become a ring: logical row L always lives at map
row (wMapTop + L) & 31, and SCY = (wMapTop + wViewTop)*8 places the
view. Scrolling bumps wMapTop, rebuilds the one new bottom line and
writes its single map row (map_row_one); the other 17 rows move for
free. The whole OSK view dance - shift the cursor above the keys,
restore the backlog on hide - is now term_view_update: one SCY write,
no map rewrite at all.
OSK safety: the keyboard lives on the WINDOW layer, which SCY never
moves. Stale ring rows can only appear under it: wViewTop = max(0,
wCurRow-14) <= 3 is nonzero only while the OSK is visible, and the
window covers exactly those bottom 3 rows (WX=7, WY=120). Verified:
scroll + view shift + scroll-while-OSK-up + backlog restore all
pixel-correct via VRAM screenshots; full README demo passes.
term_scroll: 78.5k -> 35.6k T-cycles (857k pre-optimization: 24x).
OSK toggle: 45k tilemap rewrite -> 212 cycles.
Regenerated demo/gbos-demo.gif on the new renderer.
Diffstat (limited to '')
| -rw-r--r-- | demo/gbos-demo.gif | bin | 511818 -> 350160 bytes | |||
| -rw-r--r-- | src/osk.asm | 4 | ||||
| -rw-r--r-- | src/term.asm | 185 |
3 files changed, 112 insertions, 77 deletions
diff --git a/demo/gbos-demo.gif b/demo/gbos-demo.gif Binary files differindex 0e000a2..754477d 100644 --- a/demo/gbos-demo.gif +++ b/demo/gbos-demo.gif diff --git a/src/osk.asm b/src/osk.asm index d5edad7..0f188eb 100644 --- a/src/osk.asm +++ b/src/osk.asm @@ -247,12 +247,12 @@ osk_toggle:: ld a, [rLCDC] or %00100000 ld [rLCDC], a - jp term_write_tilemap + jp term_view_update ; SCY-only now: the map ring is untouched .hide ld a, [rLCDC] and %11011111 ld [rLCDC], a - jp term_write_tilemap ; offset recomputes to 0 -> full backlog + jp term_view_update ; offset recomputes to 0 -> full backlog ; movement with wrap-around; each moves the highlight osk_up: diff --git a/src/term.asm b/src/term.asm index fbeaea3..6a94024 100644 --- a/src/term.asm +++ b/src/term.asm @@ -35,6 +35,7 @@ DEF ACACHE EQU 40 ; attr cache in COL_BUF's spare stride bytes: the tile ; terminal state (WRAMX gap $D580-$D5FF) DEF wVram EQU $D584 ; 2 +DEF wMapTop EQU $D5BB ; BG-map ring: tilemap row where logical row 0 lives DEF wCurRow EQU $D586 ; cursor screen row 0..17 DEF wCurCol EQU $D587 ; cursor screen col 0..39 DEF wCursorOn EQU $D588 ; draw the cursor? @@ -88,6 +89,7 @@ term_init:: ld [wCurRow], a ld [wCurCol], a ld [wViewTop], a ; view starts unshifted (all 18 rows) + ld [wMapTop], a ; ring starts at map row 0 ld [wOskVisible], a ; OSK hidden until toggled ld [wAnsiState], a ; ANSI parser idle ld a, $FF @@ -128,39 +130,105 @@ term_init:: ret ; ----------------------------------------------------------------------------- -; term_write_tilemap - point each screen row's tilemap entries at assign[]'s -; line-slot tiles. Pass 1 = indices (bank 0), pass 2 = attributes (bank 1). +; The BG map is a 32-row RING. Logical row L always lives at tilemap row +; (wMapTop + L) & 31; SCY = (wMapTop + wViewTop)*8 places the view. So: +; scroll = bump wMapTop, write ONE new map row, set SCY +; OSK toggle / view shift = set SCY only (the OSK itself is on the WINDOW +; layer, which SCY never moves; stale ring rows can only show under the +; window: wViewTop = max(0, wCurRow-14) <= 3 is nonzero only while the +; OSK is visible, and the OSK covers exactly those bottom 3 rows) +; ----------------------------------------------------------------------------- + +; ----------------------------------------------------------------------------- +; term_write_tilemap - (re)write every logical row's map row + place the view. +; Rare: init / full refresh only; scroll and OSK toggling take the fast paths. ; ----------------------------------------------------------------------------- term_write_tilemap:: + xor a +.rows + push af + call map_row_one + pop af + inc a + cp TROWS + jr c, .rows + ; fall through into term_view_update + +; ----------------------------------------------------------------------------- +; term_view_update - place the view: SCY = (wMapTop + wViewTop)*8. The whole +; OSK view dance (cursor above the keys, backlog restore on hide) is one +; register write now - no map rewrite. +; ----------------------------------------------------------------------------- +term_view_update:: call compute_offset + ld a, [wMapTop] + ld hl, wViewTop + add [hl] + and 31 + add a + add a + add a ; *8 px + ld [rSCY], a + ret + +; ----------------------------------------------------------------------------- +; map_row_one - write the BG-map row for logical row A (0..17): tile indices +; (VBK 0), then palette attrs from the cache (VBK 1), split at the pos-256 +; VRAM-bank boundary into two tight runs. +; ----------------------------------------------------------------------------- +map_row_one: + ld c, a ; C = logical row + ; slot = assign[L] -> wTMSlot + add LOW(wAssign) + ld l, a + ld a, HIGH(wAssign) + adc 0 + ld h, a + ld a, [hl] + ld [wTMSlot], a + ; DE = pos = slot*20 + ld l, a + ld h, 0 + add hl, hl + add hl, hl ; *4 + ld d, h + ld e, l + add hl, hl + add hl, hl ; *16 + add hl, de ; *20 + ld d, h + ld e, l ; DE = pos start + ; HL = $9800 + ((wMapTop + L) & 31) * 32 (the ring row) + ld a, [wMapTop] + add c + and 31 + ld l, a + ld h, 0 + add hl, hl + add hl, hl + add hl, hl + add hl, hl + add hl, hl ; *32 + ld a, h + add $98 + ld h, a + ; ---- indices (VBK 0): 20 bytes of LOW(pos); E wraps naturally ---- + push hl ; row addr again for the attr pass + push de ; pos again for the bank split xor a ld [rVBK], a - ld c, 0 ; screen row -.i_row - call .slot20 ; DE = assign[c]*20 (pos start), HL = tilemap row ld b, TCOLS/2 -.i_col +.idx ld a, e - ld [hl+], a ; index = LOW(pos) + ld [hl+], a inc de dec b - jr nz, .i_col - inc c - ld a, c - cp TROWS - jr c, .i_row - + jr nz, .idx + pop de + pop hl + ; ---- attrs (VBK 1): k = tiles still in bank 0, then two cache runs ---- ld a, 1 ld [rVBK], a - xor a - ld [wRT_sr], a ; screen row (render_tile scratch, free here) -.a_row - 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 @@ -176,7 +244,7 @@ term_write_tilemap:: 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) + ; DE = &cache[slot][0] push hl ld a, [wTMSlot] ld l, a @@ -209,7 +277,7 @@ term_write_tilemap:: ld b, a ld a, TCOLS/2 sub b - jr z, .arnext + jr z, .done ld b, a .r2 ld a, [de] @@ -218,53 +286,10 @@ term_write_tilemap:: ld [hl+], a dec b jr nz, .r2 -.arnext - ld a, [wRT_sr] - inc a - ld [wRT_sr], a - cp TROWS - jr c, .a_row +.done xor a ld [rVBK], a ret -; helper: C = screen row -> DE = assign[C]*20, HL = $9800 + C*32 -.slot20 - ld a, [wViewTop] - add c ; logical row = screen row + view offset - cp TROWS - jr c, .lok - ld a, TROWS-1 ; rows the OSK covers clamp to the cursor line -.lok - add LOW(wAssign) - ld l, a - ld a, HIGH(wAssign) - adc 0 - ld h, a - ld a, [hl] ; L = assign[logical row] - ld [wTMSlot], a ; stash slot for the color-aware attr pass - ld l, a - ld h, 0 - add hl, hl - add hl, hl ; L*4 - ld d, h - ld e, l - add hl, hl - add hl, hl ; L*16 - add hl, de ; L*20 - ld d, h - ld e, l ; DE = pos start - ld a, c - ld l, a - ld h, 0 - add hl, hl - add hl, hl - add hl, hl - add hl, hl - add hl, hl ; row*32 - ld a, h - add $98 - ld h, a ; HL = $9800 + row*32 - ret ; ----------------------------------------------------------------------------- ; slot_addr - A = screen row -> HL = TERM_BUF + assign[row]*64 @@ -735,11 +760,13 @@ update_cursor_attr: or $08 .noattr_bank ld c, a ; C = attribute byte - ; physical row = wCurRow - wViewTop + ; ring row = (wMapTop + wCurRow) & 31 - the cursor's map row is fixed by + ; its logical row; the view offset only moves SCY, never the map. ld a, [wCurRow] - ld hl, wViewTop - sub [hl] - ; tilemap attr addr = $9800 + physical*32 + (wCurCol/2), VRAM bank 1 + ld hl, wMapTop + add [hl] + and 31 + ; tilemap attr addr = $9800 + ringrow*32 + (wCurCol/2), VRAM bank 1 ld l, a ld h, 0 add hl, hl @@ -853,8 +880,16 @@ term_scroll:: ld a, c cp TCOLS/2 jr c, .build - call term_write_tilemap - ret + ; ring-scroll: every logical row moved up one, so bump the map top, write + ; the ONE new bottom map row, and let SCY do the other 17 (the OSK window + ; is untouched - SCY doesn't move the window layer). + ld a, [wMapTop] + inc a + and 31 + ld [wMapTop], a + ld a, TROWS-1 + call map_row_one + jp term_view_update ; cursor_down - advance the cursor row, scrolling when it runs past the bottom. cursor_down: @@ -872,7 +907,7 @@ cursor_down: ld a, [wOskVisible] or a ret z - jp term_write_tilemap + jp term_view_update ; SCY-only: no map rewrite ; compute_offset - set wViewTop so the cursor line is always on screen: 0 when ; the OSK is hidden; otherwise max(0, wCurRow - 14) so the cursor sits at the |
