2019-02-20 13:48:29 +01:00
|
|
|
# VGA Text Display
|
|
|
|
|
2019-02-20 19:11:52 +01:00
|
|
|
This implements a text display driver for a standard VGA display
|
|
|
|
on x86 hardware. The exposed words will be in a `vga:` prefix.
|
|
|
|
|
|
|
|
First, define the memory region and screen dimensions. Adjust
|
|
|
|
these if your system is non-standard.
|
|
|
|
|
2019-02-20 13:48:29 +01:00
|
|
|
~~~
|
|
|
|
0xB8000 'VGA-BASE const
|
|
|
|
#80 'COLUMNS const
|
|
|
|
#25 'ROWS const
|
2019-02-20 19:11:52 +01:00
|
|
|
~~~
|
|
|
|
|
2019-02-20 19:31:12 +01:00
|
|
|
The VGA display uses a couple of control registers. I name
|
|
|
|
the ones I care about here.
|
|
|
|
|
2019-02-20 19:11:52 +01:00
|
|
|
~~~
|
|
|
|
0x3D4 'VGA-CURSOR const
|
|
|
|
0x3D5 'VGA-DATA const
|
|
|
|
~~~
|
2019-02-20 13:48:29 +01:00
|
|
|
|
2019-02-20 19:11:52 +01:00
|
|
|
Next, a couple of variables to track the cursor position.
|
2019-02-20 18:30:21 +01:00
|
|
|
|
2019-02-20 19:11:52 +01:00
|
|
|
~~~
|
2019-02-20 13:48:29 +01:00
|
|
|
'vga:Row var
|
|
|
|
'vga:Column var
|
2019-02-20 19:11:52 +01:00
|
|
|
~~~
|
2019-02-20 13:48:29 +01:00
|
|
|
|
2019-02-20 19:11:52 +01:00
|
|
|
~~~
|
2019-02-20 15:53:50 +01:00
|
|
|
:vga:update-cursor
|
2019-02-20 15:14:40 +01:00
|
|
|
@vga:Row COLUMNS * @vga:Column + dup
|
2019-02-20 19:11:52 +01:00
|
|
|
0x0F VGA-CURSOR pio:out-byte
|
|
|
|
0xFF and VGA-DATA pio:out-byte
|
|
|
|
0x0E VGA-CURSOR pio:out-byte
|
|
|
|
#8 shift 0xFF and VGA-DATA pio:out-byte ;
|
2019-02-20 15:14:40 +01:00
|
|
|
|
2019-02-20 15:53:50 +01:00
|
|
|
:vga:move-cursor (rc-)
|
|
|
|
!vga:Column !vga:Row vga:update-cursor ;
|
|
|
|
|
2019-02-20 19:11:52 +01:00
|
|
|
{{
|
|
|
|
'vga:Display d:create
|
|
|
|
COLUMNS ROWS * n:inc allot
|
2019-02-20 18:30:21 +01:00
|
|
|
|
2019-02-20 19:31:12 +01:00
|
|
|
:starting-address VGA-BASE COLUMNS #2 * + ;
|
|
|
|
:characters ROWS n:dec COLUMNS * ;
|
|
|
|
:save-byte dup ram:fetch-byte buffer:add #2 + ;
|
|
|
|
:save [ save-byte ] times drop ;
|
2019-02-20 19:11:52 +01:00
|
|
|
:all-but-top
|
|
|
|
[ &vga:Display buffer:set
|
2019-02-20 19:31:12 +01:00
|
|
|
starting-address characters save ] buffer:preserve ;
|
2019-02-20 18:30:21 +01:00
|
|
|
|
2019-02-20 19:11:52 +01:00
|
|
|
:move-up
|
2019-02-20 19:31:12 +01:00
|
|
|
VGA-BASE &vga:Display
|
|
|
|
[ over ram:store-byte #2 + ] s:for-each drop ;
|
|
|
|
|
|
|
|
:last-line VGA-BASE ROWS n:dec COLUMNS * #2 * + ;
|
|
|
|
:erase ASCII:SPACE over ram:store-byte #2 + ;
|
|
|
|
:erase-last-line last-line COLUMNS [ erase ] times drop ;
|
|
|
|
:scroll all-but-top move-up erase-last-line ;
|
|
|
|
:position ROWS n:dec #0 vga:move-cursor ;
|
|
|
|
:scroll? @vga:Row ROWS eq? ;
|
2019-02-20 19:11:52 +01:00
|
|
|
---reveal---
|
2019-02-20 19:31:12 +01:00
|
|
|
:vga:wrap scroll? [ scroll position ] if vga:update-cursor ;
|
2019-02-20 19:11:52 +01:00
|
|
|
}}
|
2019-02-20 14:34:18 +01:00
|
|
|
|
2019-02-20 19:11:52 +01:00
|
|
|
{{
|
2019-02-20 19:31:12 +01:00
|
|
|
:position (-a)
|
|
|
|
@vga:Row COLUMNS * #2 * @vga:Column #2 * + VGA-BASE + ;
|
|
|
|
:next
|
2019-02-20 19:11:52 +01:00
|
|
|
&vga:Column v:inc
|
|
|
|
@vga:Column COLUMNS gt? [ &vga:Row v:inc #0 !vga:Column ] if
|
|
|
|
vga:wrap ;
|
|
|
|
---reveal---
|
|
|
|
:vga:c:put (c-)
|
|
|
|
#10 [ #0 !vga:Column &vga:Row v:inc vga:wrap ] case
|
|
|
|
#13 [ #0 !vga:Column &vga:Row v:inc vga:wrap ] case
|
|
|
|
#8 [ &vga:Column v:dec #32 vga:c:put &vga:Column v:dec vga:update-cursor ] case
|
2019-02-20 19:31:12 +01:00
|
|
|
position ram:store-byte next ;
|
2019-02-20 19:11:52 +01:00
|
|
|
}}
|
2019-02-20 15:14:40 +01:00
|
|
|
|
|
|
|
:clear (-)
|
2019-02-20 19:11:52 +01:00
|
|
|
#0 #0 vga:move-cursor
|
2019-02-20 15:14:40 +01:00
|
|
|
VGA-BASE COLUMNS ROWS * [ ASCII:SPACE over ram:store-byte #2 + ] times drop
|
2019-02-20 19:11:52 +01:00
|
|
|
#0 #0 vga:move-cursor ;
|
2019-02-20 14:34:18 +01:00
|
|
|
|
2019-02-20 19:11:52 +01:00
|
|
|
:vga:initialize #1793 &c:put #2 + store &vga:c:put &c:put #3 + store ;
|
2019-02-20 13:48:29 +01:00
|
|
|
~~~
|
2019-02-20 19:11:52 +01:00
|
|
|
|