; ============================================================================= ; osk.asm - on-screen keyboard on the WINDOW layer (CGB). ; ; The terminal owns the BG layer ($9800 map, tiles 0-359). The OSK lives on the ; window ($9C00 map) so toggling it is just LCDC bit 5 (window enable) - the ; terminal underneath is never touched. Key glyphs are built once into spare ; bank-1 VRAM tiles (104+). The highlighted key is a palette swap on its window ; attribute byte (palette 1 = inverted), so moving the cursor is 1-2 byte writes ; with no tile rebuilding. SELECT toggles; d-pad moves; A types into KGetc. ; ============================================================================= INCLUDE "include/gbos.inc" DEF rVBK EQU $FF4F DEF rBCPS EQU $FF68 DEF rBCPD EQU $FF69 DEF rWY EQU $FF4A DEF rWX EQU $FF4B DEF OSK_ROWS EQU 3 DEF OSK_COLS EQU 20 DEF OSK_KEYS EQU OSK_ROWS * OSK_COLS DEF OSK_TILE0 EQU 104 ; first OSK tile index (bank 1; terminal uses 0-103) DEF OSK_MAP EQU $9C00 ; window tilemap SECTION "osk_state", WRAM0 wOskVisible:: DS 1 wOskRow:: DS 1 wOskCol:: DS 1 wOskBuild:: DS 1 ; scratch for the init build loop SECTION "osk_keys", ROM0 ; type values, row-major (OSK_ROWS x OSK_COLS) osk_keys: db "abcdefghijklmnopqrst" db "uvwxyz0123456789 .-/" db 10, 8, ":;,=+*_!?()[]<>@#~" SECTION "osk", ROM0 ; ----------------------------------------------------------------------------- ; osk_disp - A = key type value -> B = left glyph, C = right glyph (2-char label) ; ----------------------------------------------------------------------------- osk_disp: cp 10 jr nz, .n1 ld b, "C" ld c, "R" ret ; enter .n1 cp 8 jr nz, .n2 ld b, "<" ld c, "-" ret ; backspace .n2 cp " " jr nz, .n3 ld b, "S" ld c, "P" ret ; space .n3 ld b, a ; regular: char in left cell ld c, " " ret ; ----------------------------------------------------------------------------- ; osk_init - build key tiles, lay out the window map, set up palette 1. Window ; starts disabled. ; ----------------------------------------------------------------------------- osk_init:: ; CGB BG palette 1 = inverted: color0 black, color1 white ld a, $88 ; BCPS index 8 (palette 1, color 0), auto-inc ld [rBCPS], a xor a ld [rBCPD], a ld [rBCPD], a ; c0 = $0000 black ld a, $FF ld [rBCPD], a ld a, $7F ld [rBCPD], a ; c1 = $7FFF white ; build the key tiles (no cursor underline) xor a ld [wCurMask], a ld [wOskBuild], a .build ld a, [wOskBuild] ld e, a ld d, 0 ld hl, osk_keys add hl, de ld a, [hl] call osk_disp ; B=left, C=right ld a, [wOskBuild] add OSK_TILE0 ld e, a ld d, 1 ; VRAM bank 1 call build_tile ld a, [wOskBuild] inc a ld [wOskBuild], a cp OSK_KEYS jr c, .build xor a ld [rVBK], a ; window map indices (bank 0): [r,c] = OSK_TILE0 + r*20 + c ld c, 0 .i_row ld a, c call .rowbase ; A = OSK_TILE0 + row*20 (clobbers HL/DE) ld e, a call .maprow ; HL = OSK_MAP + row*32 (preserves E) ld b, OSK_COLS .i_col ld a, e ld [hl+], a inc e dec b jr nz, .i_col inc c ld a, c cp OSK_ROWS jr c, .i_row ; window map attributes (bank 1): $08 = bank-1 tile, palette 0 ld a, 1 ld [rVBK], a ld c, 0 .a_row call .maprow ld b, OSK_COLS .a_col ld a, $08 ld [hl+], a dec b jr nz, .a_col inc c ld a, c cp OSK_ROWS jr c, .a_row xor a ld [rVBK], a ; window position: 3 rows at the bottom (screen rows 15-17) ld a, 7 ld [rWX], a ld a, 120 ld [rWY], a ; select window map $9C00 (LCDC bit 6); leave window disabled (bit 5 = 0) ld a, [rLCDC] or %01000000 ld [rLCDC], a xor a ld [wOskVisible], a ld [wOskRow], a ld [wOskCol], a call osk_highlight ret ; HL = OSK_MAP + C*32 .maprow ld a, c ld h, 0 ld l, a add hl, hl add hl, hl add hl, hl add hl, hl add hl, hl ld a, h add HIGH(OSK_MAP) ld h, a ret ; A(row) -> A = OSK_TILE0 + row*20 .rowbase ld h, 0 ld l, a 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 a, l add OSK_TILE0 ret ; ----------------------------------------------------------------------------- ; osk_set_pal - write palette A (0 or 1) into the current (wOskRow,wOskCol) ; window attribute. ; ----------------------------------------------------------------------------- osk_set_pal: or $08 ; bank-1 bit ld e, a ; save attr byte ld a, 1 ld [rVBK], a ld a, [wOskRow] ld h, 0 ld l, a add hl, hl add hl, hl add hl, hl add hl, hl add hl, hl ; row*32 ld a, h add HIGH(OSK_MAP) ld h, a ld a, [wOskCol] add l ld l, a ld a, h adc 0 ld h, a ld a, e ld [hl], a xor a ld [rVBK], a ret osk_highlight: ld a, 1 jr osk_set_pal osk_unhighlight: xor a jr osk_set_pal ; ----------------------------------------------------------------------------- ; osk_toggle - show/hide the keyboard (window enable, LCDC bit 5). ; ----------------------------------------------------------------------------- osk_toggle:: ld a, [wOskVisible] xor 1 ld [wOskVisible], a or a jr z, .hide ; show: shift the view up so the cursor line clears the keyboard ld a, OSK_ROWS call term_set_view ld a, [rLCDC] or %00100000 ld [rLCDC], a ret .hide ld a, [rLCDC] and %11011111 ld [rLCDC], a xor a ; view unshifted -> full 18-row backlog call term_set_view ret ; movement with wrap-around; each moves the highlight osk_up: call osk_unhighlight ld a, [wOskRow] or a jr nz, .d ld a, OSK_ROWS ; 0 -> ROWS, so dec wraps to ROWS-1 .d dec a ld [wOskRow], a jr osk_highlight osk_down: call osk_unhighlight ld a, [wOskRow] inc a cp OSK_ROWS jr c, .s xor a ; past bottom -> wrap to 0 .s ld [wOskRow], a jr osk_highlight osk_left: call osk_unhighlight ld a, [wOskCol] or a jr nz, .d ld a, OSK_COLS .d dec a ld [wOskCol], a jr osk_highlight osk_right: call osk_unhighlight ld a, [wOskCol] inc a cp OSK_COLS jr c, .s xor a .s ld [wOskCol], a jr osk_highlight ; -> A = type value of the selected key osk_selected: ld a, [wOskRow] ld h, 0 ld l, a add hl, hl add hl, hl ld d, h ld e, l add hl, hl add hl, hl add hl, de ; row*20 ld a, [wOskCol] add l ld l, a ld a, h adc 0 ld h, a ld de, osk_keys add hl, de ld a, [hl] ret ; ----------------------------------------------------------------------------- ; osk_handle - process the latest pad edges. Returns CF set + A = typed byte if ; the user pressed A over a key, else CF clear. (SELECT toggles; d-pad moves.) ; ----------------------------------------------------------------------------- osk_handle:: ld a, [wPadNew] bit 2, a ; SELECT jr z, .nosel call osk_toggle .nosel ld a, [wOskVisible] or a jr z, .none ld a, [wPadNew] bit 6, a call nz, osk_up ld a, [wPadNew] bit 7, a call nz, osk_down ld a, [wPadNew] bit 5, a call nz, osk_left ld a, [wPadNew] bit 4, a call nz, osk_right ld a, [wPadNew] bit 3, a ; START -> Enter (CR) jr z, .chka ld a, 10 scf ret .chka ld a, [wPadNew] bit 0, a ; A: type the key as shown jr z, .chkb call osk_selected scf ret .chkb ld a, [wPadNew] bit 1, a ; B: type the key SHIFTED (uppercase a-z) jr z, .none call osk_selected cp "a" jr c, .shifted cp "z"+1 jr nc, .shifted sub 32 ; 'a'..'z' -> 'A'..'Z' .shifted scf ret .none or a ; CF = 0 ret