mirror of
https://github.com/hsoft/collapseos.git
synced 2024-12-24 14:28:06 +11:00
New kernel module: grid
This commit is contained in:
parent
434c8d5c0d
commit
247b200dcc
@ -16,10 +16,13 @@ Launch the emulator with `./sms /path/to/rom` (you can use the binary from the
|
||||
This will show a window with the screen's content on it. The mappings to the
|
||||
pad are:
|
||||
|
||||
* Arrows
|
||||
* Z --> A
|
||||
* X --> B
|
||||
* C --> C
|
||||
* S --> Start
|
||||
* W --> Up
|
||||
* A --> Left
|
||||
* S --> Down
|
||||
* D --> Right
|
||||
* H --> A
|
||||
* J --> B
|
||||
* K --> C
|
||||
* L --> Start
|
||||
|
||||
Press ESC to quit.
|
||||
|
@ -180,29 +180,29 @@ void event_loop()
|
||||
bool ispressed = e->response_type == XCB_KEY_PRESS;
|
||||
switch (ev->detail) {
|
||||
case 0x09: return; // ESC
|
||||
case 0x27: // S
|
||||
pad_setbtn(&pad, PAD_BTN_START, ispressed);
|
||||
break;
|
||||
case 0x34: // Z
|
||||
pad_setbtn(&pad, PAD_BTN_A, ispressed);
|
||||
break;
|
||||
case 0x35: // X
|
||||
pad_setbtn(&pad, PAD_BTN_B, ispressed);
|
||||
break;
|
||||
case 0x36: // C
|
||||
pad_setbtn(&pad, PAD_BTN_C, ispressed);
|
||||
break;
|
||||
case 0x62:
|
||||
case 0x19: // W
|
||||
pad_setbtn(&pad, PAD_BTN_UP, ispressed);
|
||||
break;
|
||||
case 0x64:
|
||||
case 0x26: // A
|
||||
pad_setbtn(&pad, PAD_BTN_LEFT, ispressed);
|
||||
break;
|
||||
case 0x66:
|
||||
case 0x27: // S
|
||||
pad_setbtn(&pad, PAD_BTN_DOWN, ispressed);
|
||||
break;
|
||||
case 0x28: // D
|
||||
pad_setbtn(&pad, PAD_BTN_RIGHT, ispressed);
|
||||
break;
|
||||
case 0x68:
|
||||
pad_setbtn(&pad, PAD_BTN_DOWN, ispressed);
|
||||
case 0x2b: // H
|
||||
pad_setbtn(&pad, PAD_BTN_A, ispressed);
|
||||
break;
|
||||
case 0x2c: // J
|
||||
pad_setbtn(&pad, PAD_BTN_B, ispressed);
|
||||
break;
|
||||
case 0x2d: // K
|
||||
pad_setbtn(&pad, PAD_BTN_C, ispressed);
|
||||
break;
|
||||
case 0x2e: // L
|
||||
pad_setbtn(&pad, PAD_BTN_START, ispressed);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
228
kernel/grid.asm
Normal file
228
kernel/grid.asm
Normal file
@ -0,0 +1,228 @@
|
||||
; 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.
|
||||
;
|
||||
; Additionally, this module provides a PutC routine, suitable for plugging into
|
||||
; stdio.
|
||||
;
|
||||
; *** 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.
|
||||
;
|
||||
; *** Consts ***
|
||||
.equ GRID_SIZE GRID_COLS*GRID_ROWS
|
||||
|
||||
; *** Variables ***
|
||||
; Cursor's column
|
||||
.equ GRID_CURX GRID_RAMSTART
|
||||
; Cursor's row
|
||||
.equ GRID_CURY @+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
|
||||
ret z
|
||||
push de ; --> lvl 1
|
||||
ld de, GRID_COLS
|
||||
.loop:
|
||||
add hl, de
|
||||
dec a
|
||||
jr nz, .loop
|
||||
pop de ; <-- lvl 1
|
||||
; We're at the proper row, now let's advance to cell
|
||||
ld a, e
|
||||
jp addHL
|
||||
|
||||
; 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)
|
||||
; 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
|
||||
xor 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
|
||||
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 GRID_SETCELL
|
||||
pop af ; <-- lvl 1
|
||||
pop hl
|
||||
pop de
|
||||
ret
|
||||
|
||||
; Clear character under cursor
|
||||
gridClrCur:
|
||||
push af
|
||||
ld a, ' '
|
||||
call gridSetCur
|
||||
pop af
|
||||
ret
|
||||
|
||||
gridLF:
|
||||
call gridClrCur
|
||||
push de
|
||||
push af
|
||||
ld a, (GRID_CURY)
|
||||
call .incA
|
||||
ld d, a
|
||||
call gridClrRow
|
||||
ld (GRID_CURY), a
|
||||
xor a
|
||||
ld (GRID_CURX), a
|
||||
pop af
|
||||
pop de
|
||||
ret
|
||||
.incA:
|
||||
inc a
|
||||
cp GRID_ROWS
|
||||
ret nz ; no rollover
|
||||
; 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
|
||||
pop bc
|
||||
pop de
|
||||
pop hl
|
||||
call gridPushScr
|
||||
dec a
|
||||
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 gridSetCur
|
||||
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
|
@ -181,8 +181,7 @@ padGetC:
|
||||
; no action button pressed, but because our pad status changed, update
|
||||
; VDP before looping.
|
||||
ld a, (PAD_SELCHR)
|
||||
call vdpConv
|
||||
call vdpSpitC
|
||||
call gridSetCur
|
||||
jp padGetC
|
||||
.return:
|
||||
ld a, LF
|
||||
|
@ -17,22 +17,12 @@
|
||||
;
|
||||
.equ VDP_CTLPORT 0xbf
|
||||
.equ VDP_DATAPORT 0xbe
|
||||
|
||||
; *** Variables ***
|
||||
;
|
||||
; Row of cursor
|
||||
.equ VDP_ROW VDP_RAMSTART
|
||||
; Line of cursor
|
||||
.equ VDP_LINE @+1
|
||||
.equ VDP_RAMEND @+1
|
||||
.equ VDP_COLS 32
|
||||
.equ VDP_ROWS 24
|
||||
|
||||
; *** Code ***
|
||||
|
||||
vdpInit:
|
||||
xor a
|
||||
ld (VDP_ROW), a
|
||||
ld (VDP_LINE), a
|
||||
|
||||
ld hl, vdpInitData
|
||||
ld b, vdpInitDataEnd-vdpInitData
|
||||
ld c, VDP_CTLPORT
|
||||
@ -107,19 +97,30 @@ vdpInit:
|
||||
out (VDP_CTLPORT), a
|
||||
ret
|
||||
|
||||
; Spits char set in A at current cursor position. Doesn't move the cursor.
|
||||
; A is a "sega" char
|
||||
vdpSpitC:
|
||||
; Convert ASCII char in A into a tile index corresponding to that character.
|
||||
; When a character is unknown, returns 0x5e (a '~' char).
|
||||
vdpConv:
|
||||
; The font is organized to closely match ASCII, so this is rather easy.
|
||||
; We simply subtract 0x20 from incoming A
|
||||
sub 0x20
|
||||
cp 0x5f
|
||||
ret c ; A < 0x5f, good
|
||||
ld a, 0x5e
|
||||
ret
|
||||
|
||||
; grid routine. Sets cell at row D and column E to character A
|
||||
vdpSetCell:
|
||||
call vdpConv
|
||||
; store A away
|
||||
ex af, af'
|
||||
push bc
|
||||
ld b, 0 ; we push rotated bits from VDP_LINE into B so
|
||||
ld b, 0 ; we push rotated bits from D into B so
|
||||
; that we'll already have our low bits from the
|
||||
; second byte we'll send right after.
|
||||
; Here, we're fitting a 5-bit line, and a 5-bit column on 16-bit, right
|
||||
; aligned. On top of that, our righmost bit is taken because our target
|
||||
; cell is 2-bytes wide and our final number is a VRAM address.
|
||||
ld a, (VDP_LINE)
|
||||
ld a, d
|
||||
sla a ; should always push 0, so no pushing in B
|
||||
sla a ; same
|
||||
sla a ; same
|
||||
@ -127,9 +128,9 @@ vdpSpitC:
|
||||
sla a \ rl b
|
||||
sla a \ rl b
|
||||
ld c, a
|
||||
ld a, (VDP_ROW)
|
||||
ld a, e
|
||||
sla a ; A * 2
|
||||
or c ; bring in two low bits from VDP_LINE into high
|
||||
or c ; bring in two low bits from D into high
|
||||
; two bits
|
||||
out (VDP_CTLPORT), a
|
||||
ld a, b ; 3 low bits set
|
||||
@ -142,147 +143,6 @@ vdpSpitC:
|
||||
out (VDP_DATAPORT), a
|
||||
ret
|
||||
|
||||
vdpPutC:
|
||||
; Then, let's place our cursor. We need to first send our LSB, whose
|
||||
; 6 low bits contain our row*2 (each tile is 2 bytes wide) and high
|
||||
; 2 bits are the two low bits of our line
|
||||
; special case: line feed, carriage return, back space
|
||||
cp LF
|
||||
jr z, vdpLF
|
||||
cp CR
|
||||
jr z, vdpCR
|
||||
cp BS
|
||||
jr z, vdpBS
|
||||
|
||||
push af
|
||||
|
||||
; ... but first, let's convert it.
|
||||
call vdpConv
|
||||
|
||||
; and spit it on screen
|
||||
call vdpSpitC
|
||||
|
||||
; Move cursor. The screen is 32x24
|
||||
ld a, (VDP_ROW)
|
||||
cp 31
|
||||
jr z, .incline
|
||||
; We just need to increase row
|
||||
inc a
|
||||
ld (VDP_ROW), a
|
||||
|
||||
pop af
|
||||
ret
|
||||
.incline:
|
||||
; increase line and start anew
|
||||
call vdpCR
|
||||
call vdpLF
|
||||
pop af
|
||||
ret
|
||||
|
||||
vdpCR:
|
||||
call vdpClrPos
|
||||
push af
|
||||
xor a
|
||||
ld (VDP_ROW), a
|
||||
pop af
|
||||
ret
|
||||
|
||||
vdpLF:
|
||||
; we don't call vdpClrPos on LF because we expect it to be preceded by
|
||||
; a CR, which already cleared the pos. If we cleared it now, we would
|
||||
; clear the first char of the line.
|
||||
push af
|
||||
ld a, (VDP_LINE)
|
||||
call .incA
|
||||
call vdpClrLine
|
||||
; Also clear the line after this one
|
||||
push af ; --> lvl 1
|
||||
call .incA
|
||||
call vdpClrLine
|
||||
pop af ; <-- lvl 1
|
||||
ld (VDP_LINE), a
|
||||
pop af
|
||||
ret
|
||||
.incA:
|
||||
inc a
|
||||
cp 24
|
||||
ret nz ; no rollover
|
||||
; bottom reached, roll over to top of screen
|
||||
xor a
|
||||
ret
|
||||
|
||||
vdpBS:
|
||||
call vdpClrPos
|
||||
push af
|
||||
ld a, (VDP_ROW)
|
||||
or a
|
||||
jr z, .lineup
|
||||
dec a
|
||||
ld (VDP_ROW), a
|
||||
pop af
|
||||
ret
|
||||
.lineup:
|
||||
; end of line
|
||||
ld a, 31
|
||||
ld (VDP_ROW), a
|
||||
; we have to go one line up
|
||||
ld a, (VDP_LINE)
|
||||
or a
|
||||
jr z, .nowrap
|
||||
; We have to wrap to the bottom of the screen
|
||||
ld a, 24
|
||||
.nowrap:
|
||||
dec a
|
||||
ld (VDP_LINE), a
|
||||
pop af
|
||||
ret
|
||||
|
||||
; Clear tile under cursor
|
||||
vdpClrPos:
|
||||
push af
|
||||
xor a ; space
|
||||
call vdpSpitC
|
||||
pop af
|
||||
ret
|
||||
|
||||
; Clear line number A
|
||||
vdpClrLine:
|
||||
; see comments in vdpSpitC for VRAM details.
|
||||
push af
|
||||
; first, get the two LSB at MSB pos.
|
||||
rrca \ rrca
|
||||
push af ; --> lvl 1
|
||||
and 0b11000000
|
||||
; That's our first address byte
|
||||
out (VDP_CTLPORT), a
|
||||
pop af ; <-- lvl 1
|
||||
; Then, get those 3 other bits at LSB pos. Our popped A has already
|
||||
; done 2 RRCA, which means that everything is in place.
|
||||
and 0b00000111
|
||||
or 0x78
|
||||
out (VDP_CTLPORT), a
|
||||
; We're at the right place. Let's just spit 32*2 null bytes
|
||||
xor a
|
||||
push bc ; --> lvl 1
|
||||
ld b, 64
|
||||
.loop:
|
||||
out (VDP_DATAPORT), a
|
||||
djnz .loop
|
||||
pop bc ; <-- lvl 1
|
||||
pop af
|
||||
ret
|
||||
|
||||
; Convert ASCII char in A into a tile index corresponding to that character.
|
||||
; When a character is unknown, returns 0x5e (a '~' char).
|
||||
vdpConv:
|
||||
; The font is organized to closely match ASCII, so this is rather easy.
|
||||
; We simply subtract 0x20 from incoming A
|
||||
sub 0x20
|
||||
cp 0x5f
|
||||
ret c ; A < 0x5f, good
|
||||
ld a, 0x5e
|
||||
ret
|
||||
|
||||
; VDP initialisation data
|
||||
vdpInitData:
|
||||
; 0x8x == set register X
|
||||
|
@ -16,12 +16,16 @@
|
||||
.equ PAD_RAMSTART RAMSTART
|
||||
.inc "sms/pad.asm"
|
||||
|
||||
.equ VDP_RAMSTART PAD_RAMEND
|
||||
.inc "sms/vdp.asm"
|
||||
.equ GRID_RAMSTART PAD_RAMEND
|
||||
.equ GRID_COLS VDP_COLS
|
||||
.equ GRID_ROWS VDP_ROWS
|
||||
.equ GRID_SETCELL vdpSetCell
|
||||
.inc "grid.asm"
|
||||
|
||||
.equ STDIO_RAMSTART VDP_RAMEND
|
||||
.equ STDIO_RAMSTART GRID_RAMEND
|
||||
.equ STDIO_GETC padGetC
|
||||
.equ STDIO_PUTC vdpPutC
|
||||
.equ STDIO_PUTC gridPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
; *** BASIC ***
|
||||
@ -51,6 +55,7 @@ init:
|
||||
|
||||
ld sp, RAMEND
|
||||
|
||||
call gridInit
|
||||
call padInit
|
||||
call vdpInit
|
||||
call basInit
|
||||
|
@ -18,12 +18,16 @@
|
||||
.equ KBD_FETCHKC smskbdFetchKCB
|
||||
.inc "kbd.asm"
|
||||
|
||||
.equ VDP_RAMSTART KBD_RAMEND
|
||||
.inc "sms/vdp.asm"
|
||||
.equ GRID_RAMSTART KBD_RAMEND
|
||||
.equ GRID_COLS VDP_COLS
|
||||
.equ GRID_ROWS VDP_ROWS
|
||||
.equ GRID_SETCELL vdpSetCell
|
||||
.inc "grid.asm"
|
||||
|
||||
.equ STDIO_RAMSTART VDP_RAMEND
|
||||
.equ STDIO_RAMSTART GRID_RAMEND
|
||||
.equ STDIO_GETC kbdGetC
|
||||
.equ STDIO_PUTC vdpPutC
|
||||
.equ STDIO_PUTC gridPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
; *** BASIC ***
|
||||
@ -64,6 +68,7 @@ init:
|
||||
out (0x3f), a
|
||||
|
||||
call kbdInit
|
||||
call gridInit
|
||||
call vdpInit
|
||||
call basInit
|
||||
jp basStart
|
||||
|
@ -47,12 +47,16 @@
|
||||
.equ KBD_FETCHKC smskbdFetchKCB
|
||||
.inc "kbd.asm"
|
||||
|
||||
.equ VDP_RAMSTART KBD_RAMEND
|
||||
.inc "sms/vdp.asm"
|
||||
.equ GRID_RAMSTART KBD_RAMEND
|
||||
.equ GRID_COLS VDP_COLS
|
||||
.equ GRID_ROWS VDP_ROWS
|
||||
.equ GRID_SETCELL vdpSetCell
|
||||
.inc "grid.asm"
|
||||
|
||||
.equ STDIO_RAMSTART VDP_RAMEND
|
||||
.equ STDIO_RAMSTART GRID_RAMEND
|
||||
.equ STDIO_GETC kbdGetC
|
||||
.equ STDIO_PUTC vdpPutC
|
||||
.equ STDIO_PUTC gridPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.equ MMAP_START 0xd700
|
||||
@ -124,6 +128,7 @@ init:
|
||||
call fsOn
|
||||
|
||||
call kbdInit
|
||||
call gridInit
|
||||
call vdpInit
|
||||
|
||||
call basInit
|
||||
@ -165,7 +170,7 @@ f1PutB:
|
||||
ld ix, FS_HANDLES+FS_HANDLE_SIZE
|
||||
jp fsPutB
|
||||
|
||||
; last time I checked, PC at this point was 0x1e92. Let's give us a nice margin
|
||||
; last time I checked, PC at this point was 0x128f. Let's give us a nice margin
|
||||
; for the start of ed.
|
||||
.fill 0x1f00-$
|
||||
.bin "ed.bin"
|
||||
|
Loading…
Reference in New Issue
Block a user