aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-18 21:56:21 +0200
committeruser <user@clank>2026-07-18 21:56:21 +0200
commitbea52bc8f99ee6dbcc8154a366560fc18e7b24c8 (patch)
tree0772b5d9e98de7f6c495198844c230537c7fb9d6
parentterm: SCY ring-scroll - scroll writes ONE map row; OSK toggle is 212 cycles (diff)
downloadgbos-bea52bc8f99ee6dbcc8154a366560fc18e7b24c8.tar.gz
gbos-bea52bc8f99ee6dbcc8154a366560fc18e7b24c8.tar.xz
gbos-bea52bc8f99ee6dbcc8154a366560fc18e7b24c8.zip
term: latch SCY in VBlank - fix ring-scroll shear on live displays
The ring-scroll wrote SCY mid-frame; SCY is sampled per scanline (on hardware and in sl0pboy's PPU), so a scroll landing mid-frame rendered the top of the frame at the old offset and the bottom at the new one - a one-frame shear, invisible in frame-sampled GIF captures but ugly on a live sixel view. term_view_update now writes hSCY (HRAM) and a transparent VBlank ISR (push af / apply / pop af / reti, same profile as TimerISR) copies it to rSCY, so the view only ever moves at frame boundaries. The scroll's tile+map writes stay immediate: the new map row is invisible at the old SCY by construction (it's the ring row one past the visible 18).
-rw-r--r--include/gbos.inc3
-rw-r--r--src/boot.asm21
-rw-r--r--src/hram.asm4
-rw-r--r--src/term.asm5
4 files changed, 27 insertions, 6 deletions
diff --git a/include/gbos.inc b/include/gbos.inc
index be6009f..cbd2670 100644
--- a/include/gbos.inc
+++ b/include/gbos.inc
@@ -13,7 +13,8 @@ DEF rIE EQU $FFFF ; interrupt enable
DEF rTIMA EQU $FF05 ; timer counter
DEF rTMA EQU $FF06 ; timer modulo (reload)
DEF rTAC EQU $FF07 ; timer control (bit2 = enable, bits1-0 = clock)
-DEF IEF_TIMER EQU %00000100
+DEF IEF_TIMER EQU %00000100
+DEF IEF_VBLANK EQU %00000001
DEF rDIV EQU $FF04 ; divider register: free-running 8-bit @ 16384 Hz
DEF rSVBK EQU $FF70 ; CGB WRAM bank select ($D000-$DFFF): 1..7
DEF wCurMask EQU $D589 ; term/osk: cursor underline bits OR'd into a tile's last row
diff --git a/src/boot.asm b/src/boot.asm
index 4049f9d..88eb240 100644
--- a/src/boot.asm
+++ b/src/boot.asm
@@ -15,7 +15,7 @@ SECTION "rst30_syscall", ROM0[$30]
; Interrupt vectors
; -----------------------------------------------------------------------------
SECTION "vblank", ROM0[$40]
- reti
+ jp VBlankISR ; applies the latched SCY (see hram.asm hSCY)
SECTION "stat", ROM0[$48]
reti
SECTION "timer", ROM0[$50]
@@ -83,7 +83,8 @@ KernelInit:
; system tick: hardware timer at 16384 Hz input, TMA=0 -> TIMA overflow
; IRQ at 64 Hz. TimerISR just counts (wTicks); scheduling stays
- ; cooperative. Only the timer bit is enabled in IE.
+ ; cooperative. VBlank is enabled too, but its ISR is equally
+ ; transparent: it only applies the latched SCY (terminal ring-scroll).
xor a
ld [rTIMA], a
ld [rTMA], a
@@ -91,7 +92,7 @@ KernelInit:
ld [rTAC], a
xor a
ld [rIF], a
- ld a, IEF_TIMER
+ ld a, IEF_TIMER | IEF_VBLANK
ld [rIE], a
ei
@@ -113,3 +114,17 @@ ClearKernelRAM:
or c
jr nz, .loop
ret
+
+; -----------------------------------------------------------------------------
+; VBlankISR - transparent, like TimerISR: applies the latched SCY so terminal
+; ring-scrolls land at frame boundaries. SCY is sampled per scanline, so a
+; mid-frame write shears the picture (top at the old offset, bottom at the
+; new) - exactly the sixel-view glitch this ISR exists to prevent.
+; -----------------------------------------------------------------------------
+SECTION "vblank_isr", ROM0
+VBlankISR:
+ push af
+ ldh a, [hSCY]
+ ldh [$FF42], a ; rSCY
+ pop af
+ reti
diff --git a/src/hram.asm b/src/hram.asm
index 9e65982..facccd0 100644
--- a/src/hram.asm
+++ b/src/hram.asm
@@ -17,6 +17,10 @@ SECTION "hram_vars", HRAM
hCurBankRAM: DS 1 ; shadow of active cart-RAM bank
hCurBankWRAM: DS 1 ; shadow of active SVBK bank
hSwitchTgt: DS 2 ; &incoming PCB (set before the pushes clobber DE)
+hSCY:: DS 1 ; VBlank-latched SCY: term_view_update writes this,
+ ; the VBlank ISR applies it - a mid-frame SCY write
+ ; would shear the picture (SCY is sampled per
+ ; scanline), so ring-scrolls land at frame boundaries
; -----------------------------------------------------------------------------
; Source image (lives in ROM0); CopyHramCode blits it to HRAM at boot.
diff --git a/src/term.asm b/src/term.asm
index 6a94024..46cd23e 100644
--- a/src/term.asm
+++ b/src/term.asm
@@ -60,6 +60,7 @@ term_init::
ld [rLCDC], a ; LCD off
ld [rSCX], a
ld [rSCY], a
+ ldh [hSCY], a ; keep the VBlank latch in sync
; Load all 8 CGB BG palettes from PalData (see tools/gencolor.py). Palettes
; 0 and 2..7 are the 7 Fano-plane text palettes (each = bg + 3 of our 7
@@ -168,8 +169,8 @@ term_view_update::
add a
add a
add a ; *8 px
- ld [rSCY], a
- ret
+ ldh [hSCY], a ; latched: the VBlank ISR applies it, so the
+ ret ; view never moves mid-frame (no shear)
; -----------------------------------------------------------------------------
; map_row_one - write the BG-map row for logical row A (0..17): tile indices