92ddc7ebc1
Not much of a gain in terms of usability (a bit of a loss in fact, things are a bit slow and glitchy), but it's a necessary move if we want to use upcoming grid-enabled userspace apps, such as a visual text editor.
276 lines
5.2 KiB
NASM
276 lines
5.2 KiB
NASM
; grid - abstraction for grid-like video output
|
|
;
|
|
; Collapse OS doesn't support curses-like interfaces: too complicated. However,
|
|
; in cases where output don't have to go through a serial interface before
|
|
; being displayed, we have usually have access to a grid-like interface.
|
|
;
|
|
; Direct access to this kind of interface allow us to build an abstraction layer
|
|
; that is very much alike curses but is much simpler underneath. This unit is
|
|
; this abstraction.
|
|
;
|
|
; The principle is simple: we have a cell grid of X columns by Y rows and we
|
|
; can access those cells by their (X, Y) address. In addition to this, we have
|
|
; the concept of an active cursor, which will be indicated visually if possible.
|
|
;
|
|
; This module provides PutC and GetC routines, suitable for plugging into stdio.
|
|
; PutC, for obvious reasons, GetC, for less obvious reasons: We need to wrap
|
|
; GetC because we need to update the cursor before calling actual GetC, but
|
|
; also, because we need to know when a bulk update ends.
|
|
;
|
|
; *** Defines ***
|
|
;
|
|
; GRID_COLS: Number of columns in the grid
|
|
; GRID_ROWS: Number of rows in the grid
|
|
; GRID_SETCELL: Pointer to routine that sets cell at row D and column E with
|
|
; character in A. If C is nonzero, this cell must be displayed,
|
|
; if possible, as the cursor. This routine is never called with
|
|
; A < 0x20.
|
|
; GRID_GETC: Routine that gridGetC will wrap around.
|
|
;
|
|
; *** Consts ***
|
|
.equ GRID_SIZE GRID_COLS*GRID_ROWS
|
|
|
|
; *** Variables ***
|
|
; Cursor's column
|
|
.equ GRID_CURX GRID_RAMSTART
|
|
; Cursor's row
|
|
.equ GRID_CURY @+1
|
|
; Whether we scrolled recently. We don't refresh the screen immediately when
|
|
; scrolling in case we have many lines being spit at once (refreshing the
|
|
; display is then very slow). Instead, we wait until the next gridGetC call
|
|
.equ GRID_SCROLLED @+1
|
|
; Grid's in-memory buffer of the contents on screen. Because we always push to
|
|
; display right after a change, this is almost always going to be a correct
|
|
; representation of on-screen display.
|
|
; The buffer is organized as a rows of columns. The cell at row Y and column X
|
|
; is at GRID_BUF+(Y*GRID_COLS)+X.
|
|
.equ GRID_BUF @+1
|
|
.equ GRID_RAMEND @+GRID_SIZE
|
|
|
|
; *** Code ***
|
|
|
|
gridInit:
|
|
xor a
|
|
ld b, GRID_RAMEND-GRID_RAMEND
|
|
ld hl, GRID_RAMSTART
|
|
jp fill
|
|
|
|
; Place HL at row D and column E in the buffer
|
|
; Destroys A
|
|
_gridPlaceCell:
|
|
ld hl, GRID_BUF
|
|
ld a, d
|
|
or a
|
|
jr z, .setcol
|
|
push de ; --> lvl 1
|
|
ld de, GRID_COLS
|
|
.loop:
|
|
add hl, de
|
|
dec a
|
|
jr nz, .loop
|
|
pop de ; <-- lvl 1
|
|
.setcol:
|
|
; We're at the proper row, now let's advance to cell
|
|
ld a, e
|
|
jp addHL
|
|
|
|
; Ensure that A >= 0x20
|
|
_gridAdjustA:
|
|
cp 0x20
|
|
ret nc
|
|
ld a, 0x20
|
|
ret
|
|
|
|
; Push row D in the buffer onto the screen.
|
|
gridPushRow:
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
; Cursor off
|
|
ld c, 0
|
|
ld e, c
|
|
call _gridPlaceCell
|
|
ld b, GRID_COLS
|
|
.loop:
|
|
ld a, (hl)
|
|
call _gridAdjustA
|
|
; A, C, D and E have proper values
|
|
call GRID_SETCELL
|
|
inc hl
|
|
inc e
|
|
djnz .loop
|
|
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
|
|
; Clear row D and push contents to screen
|
|
gridClrRow:
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
ld e, 0
|
|
call _gridPlaceCell
|
|
ld a, ' '
|
|
ld b, GRID_COLS
|
|
call fill
|
|
call gridPushRow
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
|
|
gridPushScr:
|
|
push de
|
|
ld d, GRID_ROWS-1
|
|
.loop:
|
|
call gridPushRow
|
|
dec d
|
|
jp p, .loop
|
|
pop de
|
|
ret
|
|
|
|
; Set character under cursor to A. C is passed to GRID_SETCELL as-is.
|
|
gridSetCur:
|
|
push de
|
|
push hl
|
|
push af ; --> lvl 1
|
|
ld a, (GRID_CURY)
|
|
ld d, a
|
|
ld a, (GRID_CURX)
|
|
ld e, a
|
|
call _gridPlaceCell
|
|
pop af \ push af ; <--> lvl 1
|
|
ld (hl), a
|
|
call _gridAdjustA
|
|
call GRID_SETCELL
|
|
pop af ; <-- lvl 1
|
|
pop hl
|
|
pop de
|
|
ret
|
|
|
|
; Call gridSetCur with C = 1.
|
|
gridSetCurH:
|
|
push bc
|
|
ld c, 1
|
|
call gridSetCur
|
|
pop bc
|
|
ret
|
|
|
|
; Call gridSetCur with C = 0.
|
|
gridSetCurL:
|
|
push bc
|
|
ld c, 0
|
|
call gridSetCur
|
|
pop bc
|
|
ret
|
|
|
|
; Clear character under cursor
|
|
gridClrCur:
|
|
push af
|
|
ld a, ' '
|
|
call gridSetCurL
|
|
pop af
|
|
ret
|
|
|
|
gridLF:
|
|
call gridClrCur
|
|
push de
|
|
push af
|
|
ld a, (GRID_CURY)
|
|
; increase A
|
|
inc a
|
|
cp GRID_ROWS
|
|
jr nz, .noscroll
|
|
; bottom reached, stay on last line and scroll screen
|
|
push hl
|
|
push de
|
|
push bc
|
|
ld de, GRID_BUF
|
|
ld hl, GRID_BUF+GRID_COLS
|
|
ld bc, GRID_SIZE-GRID_COLS
|
|
ldir
|
|
ld hl, GRID_SCROLLED
|
|
inc (hl) ; mark as scrolled
|
|
pop bc
|
|
pop de
|
|
pop hl
|
|
dec a
|
|
.noscroll:
|
|
; A has been increased properly
|
|
ld d, a
|
|
call gridClrRow
|
|
ld (GRID_CURY), a
|
|
xor a
|
|
ld (GRID_CURX), a
|
|
pop af
|
|
pop de
|
|
ret
|
|
|
|
gridBS:
|
|
call gridClrCur
|
|
push af
|
|
ld a, (GRID_CURX)
|
|
or a
|
|
jr z, .lineup
|
|
dec a
|
|
ld (GRID_CURX), a
|
|
pop af
|
|
ret
|
|
.lineup:
|
|
; end of line, we need to go up one line. But before we do, are we
|
|
; already at the top?
|
|
ld a, (GRID_CURY)
|
|
or a
|
|
jr z, .end
|
|
dec a
|
|
ld (GRID_CURY), a
|
|
ld a, GRID_COLS-1
|
|
ld (GRID_CURX), a
|
|
.end:
|
|
pop af
|
|
ret
|
|
|
|
gridPutC:
|
|
cp LF
|
|
jr z, gridLF
|
|
cp BS
|
|
jr z, gridBS
|
|
cp ' '
|
|
ret c ; ignore unhandled control characters
|
|
|
|
call gridSetCurL
|
|
push af ; --> lvl 1
|
|
; Move cursor
|
|
ld a, (GRID_CURX)
|
|
cp GRID_COLS-1
|
|
jr z, .incline
|
|
; We just need to increase X
|
|
inc a
|
|
ld (GRID_CURX), a
|
|
pop af ; <-- lvl 1
|
|
ret
|
|
.incline:
|
|
; increase line and start anew
|
|
call gridLF
|
|
pop af ; <-- lvl 1
|
|
ret
|
|
|
|
gridGetC:
|
|
ld a, (GRID_SCROLLED)
|
|
or a
|
|
jr z, .nopush
|
|
; We've scrolled recently, update screen
|
|
xor a
|
|
ld (GRID_SCROLLED), a
|
|
call gridPushScr
|
|
.nopush:
|
|
ld a, ' '
|
|
call gridSetCurH
|
|
jp GRID_GETC
|