video: added new system to redraw UI
authorLukas Krickl <lukas@krickl.dev>
Tue, 15 Jul 2025 19:17:39 +0000 (21:17 +0200)
committerLukas Krickl <lukas@krickl.dev>
Tue, 15 Jul 2025 19:17:39 +0000 (21:17 +0200)
The new system redraws the UI with a request system.
The entire UI tilemap is mirrored in wram where it can be updated
outside of blanks.

src/defs.s
src/map.s
src/strings.s
src/ui.s
src/update.s
src/video.s
src/wram.s

index ae5bf9e8496e7e603cf7b9f7979bde4aedc2fccd..dbbf2c4da906126dd46d6baf50a31b845d5ebfce 100644 (file)
@@ -32,6 +32,9 @@
 #define WINDOW_Y 112
 #define WINDOW_X 1
 
+#define UI_TILE_WIDTH 20
+#define UI_TILE_HEIGHT 4
+
   ; defines end of scroll location
 #define CURSOR_MIN_X 0
 #define CURSOR_MAX_X 0xF8
 .de act_dialog_opts, 2
 .de act_size, 0
   
-  ; max bge queue size
-#define BGE_MAX 4
-
-  ; max bg updates per frame
-#define BG_UPDATE_MAX 8 
-
-  ; bg update flags
-.se 1
-  ; sets a tile
-.de BGEF_TILE, 1
-  ; sets BGE address
-.de BGEF_ADDR, 2
-
-  ; bg update entry
-.se 0
-  ; if flags is 00 the entry is skipped
-.de bge_flags, 1
-.de bge_new_tile, 1
-.de bge_addr, 2
-.de bge_size, 0
 
 #define MAP_BG_TILE_OFFSET 0
 #define MAP_BG_FLAGS_OFFSET 1
index 4f7c9496f8082c97407a0c590a861a6e72a51255..6ada7886aeb81d5af16514a151031af3806ba61c 100644 (file)
--- a/src/map.s
+++ b/src/map.s
@@ -51,7 +51,7 @@ map_load:
 
   ret
 
-  ; draws map title to UI (SCRN1)
+  ; draws map title to UI
   ; inputs:
   ;   hl: map_ptr
 map_draw_area_title:
@@ -59,7 +59,7 @@ map_draw_area_title:
   add hl, de ; hl = map_name ptr
   
   ld b, MAP_NAME_LEN
-  ld de, SCRN1+1
+  ld de, shadow_ui+1
 @loop:
   ld a, [hl+]
   add a, FONT_OFFSET
@@ -68,6 +68,8 @@ map_draw_area_title:
   dec b
   jr nz, @loop REL
 
+  call ui_request_redraw
+
   ret
 
   ; gets the tile at a y/x position
index 18f8297c9de35d5338a3cfeb0e685e1342e7358e..24b7c1a091953da40c77ab3b7f7cfb58dd6f5cc1 100644 (file)
@@ -67,44 +67,3 @@ puts:
 @done:
   ret
 
-  ; prints a 0-terminated string to the update buffer
-  ; can be called outside of blank
-  ; inputs:
-  ;   hl: the string
-  ;   de: tile location
-bputs:
-  ; the first char is special
-  ld a, [hl+]
-  cp a, 0
-  jr z, @done REL ; if its 0 already skip
-
-  ; if its not 0 write the address to the 
-  ; bg update queue
-  add a, FONT_OFFSET
-  ld b, a ; b = tile data for text
-  ld a, BGEF_TILE | BGEF_ADDR
-  ; de = tile address already 
-  push hl
-  call bg_update_queue_push
-  pop hl
-
-@loop:
-  
-    ; now we just push tiles
-    ; with BGEF_TILE
-    ld a, [hl+]
-    cp a, 0 ; terminator?
-    jr z, @done REL
-
-    push hl
-    add a, FONT_OFFSET
-    ld b, a ; b = tile
-    ld a, BGEF_TILE ; tile only mode
-    ; no need to set location
-    call bg_update_queue_push
-    pop hl
-    
-    jr @loop REL
-@done:
-  ret
-
index 029505aa086fb97aa5216ca0ed675f407b330186..0fca35660578a791334706adb19cb02a6f3bae44 100644 (file)
--- a/src/ui.s
+++ b/src/ui.s
 
   ; inits UI
 ui_init:
+  ; fill shadow UI with default tile
+  ld hl, shadow_ui 
+  ld bc, UI_TILE_WIDTH * UI_TILE_HEIGHT 
+  ld d, UI_WINDOW_BACKGROUND
+  call memset
+
   call ui_redraw_hp
+
+  call ui_request_redraw
   ret
 
   
   ; updates HP UI
 ui_redraw_hp:
   ld hl, STR_TEST
-  ld de, SCRN1 + 34
-  call bputs
+  ld de, shadow_ui + 34
+  call puts
+
+  ret
+
+  ; requests a redraw
+  ; this will set up redraw_bg
+  ; redraw_shadow and redraw_steps
+ui_request_redraw:
+  ld hl, SCRN1
+  ld a, h
+  ld [ui_redraw_bg], a
+  ld a, l
+  ld [ui_redraw_bg+1], a
+
+  ld de, shadow_ui
+  ld a, d
+  ld [ui_redraw_shadow], a
+  ld a, e
+  ld [ui_redraw_shadow+1], a
+  
+  ; steps of 5 * 16
+  ld a, 16
+  ld [ui_redraw_steps], a
+  ret
+
+#macro ui_redraw_vblank_write
+  ld a, [de]
+  inc de
+  ld [hl+], a
+#endmacro
+
+  ; redraws a UI step in vblank
+ui_redraw_vblank:
+  ld a, [ui_redraw_steps]
+  ; no more steps we bail
+  cp a, 0
+  ret z
+
+  ; steps--
+  dec a
+  ld [ui_redraw_steps], a
+  
+  ; load bg ptr
+  ld a, [ui_redraw_bg]
+  ld h, a
+  ld a, [ui_redraw_bg+1]
+  ld l, a
+
+  ; load source address
+  ld a, [ui_redraw_shadow]
+  ld d, a
+  ld a, [ui_redraw_shadow+1]
+  ld e, a
+
+  ; write data
+  ; in an unrolled loop
+  ui_redraw_vblank_write
+  ui_redraw_vblank_write
+  ui_redraw_vblank_write
+  ui_redraw_vblank_write
+  ui_redraw_vblank_write
+
+  ; lastly store current ptr state again
+
+  
+  ld a, d
+  ld [ui_redraw_shadow], a
+  ld a, e
+  ld [ui_redraw_shadow+1], a
+
+  ; skip 12 tiles ahead in BG
+  ; every other step
+  ld a, [ui_redraw_steps]
+  and a, 3
+  jr nz, @no_jump REL
+    ld de, 12
+    add hl, de
+@no_jump:
+  ld a, h
+  ld [ui_redraw_bg], a
+  ld a, l
+  ld [ui_redraw_bg+1], a
 
   ret
+#undefine ui_redraw_vblank_write
index 4a835364f1fab8889f6c99748bfddc374e66d080..8d0b92b35b41566b240ea2edc224abdae43a3986 100644 (file)
@@ -41,9 +41,11 @@ update:
   ; loads default map
 new_game_init:
   call video_fade_out
-  
   call shadow_oam_clear
 
+  call ui_init
+
   call unit_load_default_player
 
   ; init initial map
@@ -66,8 +68,6 @@ game_init:
   ld a, WINDOW_X
   ld [RWX], a
 
-  call ui_init
-
   ; set up palettes for
   ; gameplay
   ld a, BGP 
index 5488045ac3ec7d8e9605db28196425951f26ec69..3228ec5337a03a5c14bf772b49b18da4ef20d356 100644 (file)
@@ -10,12 +10,13 @@ vblank:
   ld a, [RLCD]
   or a, LCDCF_OBJON
   ld [RLCD], a
+  
+  ; check ui update
+  call ui_redraw_vblank
 
   ; dma previous frame's oam
   call OAMDMAFN
 
-  call bg_update_queue_process
-  
   call scroll_write
   
   ; get inputs 
@@ -248,129 +249,3 @@ tiles_load_bank9000:
   jp memcpy
 
   
-
-  ; bg update queue 
-  ; loads update_start tile address
-  ; and updates tiles until the STOP flag is read
-  ; of the loop countr is BGE_MAX
-bg_update_queue_process:
-  ld de, bg_update_queue ; load initial entry 
-  
-  ld b, BGE_MAX ; b == loop counter
-  ; hl = target address
-
-@loop:
-    ld a, [de] ; load flags
-    ; thest big 1 (STOP)
-    cp a, 0 
-    jr z, @skip REL ; skip this entry 
-      
-      ; first test address set mode
-      bit 1, a 
-      jr z, @no_addr_set REL
-
-        push de
-        push af
-
-        ; load addr from entry
-        inc de
-        inc de
-        ; get to addr
-        ld a, [de]
-        ld l, a
-        inc de
-        ld a, [de]
-        ld h, a 
-        ; hl = new target address
-        
-        pop af
-        pop de
-@no_addr_set:
-      ; test tile set bit
-      bit 0, a 
-      jr z, @no_tile_wirte REL
-
-        inc de
-        
-        ; write new tile and inc hl
-        ld a, [de]
-        ld [hl+], a
-
-        dec de
-    
-@no_tile_wirte:
-@skip:
-    ; advance to next entry
-    inc de
-    inc de
-    inc de
-    inc de
-    dec b ; b--
-    jr nz, @loop REL
-  
-
-  ; advance to queue clear
-  
-  ; clears the bg update queue
-  ; sets update index to 0
-bg_update_queue_clear:
-  xor a, a
-  ld [bg_update_index], a
-  
-  ; clear all entries
-  ld hl, bg_update_queue 
-  ld bc, bge_size * BGE_MAX
-  ld d, a ; d = 0
-  jp memset
-
-  ; pushes a new bg update to the queue
-  ; inputs:
-  ;    b: tile data
-  ;    a: entry flags 
-  ;   de: address (unused unless in BGEF_ADDR mode)
-  ; returns:
-  ;   a == 1: if update was queued
-  ;   a == 0: if queue is full
-bg_update_queue_push:
-  push af
-  push bc
-
-  ; first calculate address in queue
-  ld a, [bg_update_index]
-  cp a, BGE_MAX
-  jp z, @full
-
-  add a, a
-  add a, a
-  ; a * 4
-  ld b, 0
-  ld c, a ; de = index offset
-
-  ld hl, bg_update_queue
-  add hl, bc
-
-  pop bc
-  pop af
-
-  ; write flags
-  ld [hl+], a
-  ld a, b 
-  ; write tile data
-  ld [hl+], a
-
-  ; write address
-  ld a, e
-  ld [hl+], a
-  ld a, d
-  ld [hl], a
-  
-  ; index++
-  ld a, [bg_update_index]
-  inc a
-  ld [bg_update_index], a
-
-  ld a, 1
-  ret
-@full:
-  xor a, a ; queue was full
-  ret
index 606445ac3e9d978291a6c21eaa94aa5cdc65c0d8..16e50f23534f317c18aeff02902ad6b7a2064221 100644 (file)
@@ -44,12 +44,19 @@ demo_inputs: .adv 2
   ; menu cursor can be used by any in-game menu
 menu_cursor_index: .adv 1
 
-  ; offset into bg_update_queue 
-bg_update_index: .adv 1 ; current entry 
-bg_update_queue: .adv bge_size * BGE_MAX 
-
 draw_flags: .adv 1
 gameplay_flags: .adv 1
+  
+  ; shadow UI is a buffer for drawing the UI
+  ; this UI is then re-drawn on request in vblank
+shadow_ui: .adv UI_TILE_WIDTH * UI_TILE_HEIGHT
+
+; UI redraw BG address
+ui_redraw_bg: .adv 2 
+; UI redraw shadow address
+ui_redraw_shadow: .adv 2
+; remaining UI redraw steps
+ui_redraw_steps: .adv 1
 
 ; dummy oam 
 ; same memory as empty_unit