commit 2efce296a641f7d8802b3175e26a6badadf8a6e7
parent 8b6f030f0a4b88eff65397aa571bc87da9b13b22
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Sun, 25 Dec 2022 10:14:39 -0500
sys/screen: new subsystem
See doc/screen
Diffstat:
8 files changed, 123 insertions(+), 41 deletions(-)
diff --git a/fs/doc/dirs.txt b/fs/doc/dirs.txt
@@ -21,8 +21,8 @@ a wide root directory structure based on general topics of the included code.
/drv Drivers
/emul Emulators
/fs Filesystems
+/gr Graphics
/lib Miscellanous little routines
-/pic Picture processing
/sys Subsystems
/tests Automated tests
/text Text processing
diff --git a/fs/doc/screen.fs b/fs/doc/screen.fs
@@ -0,0 +1,44 @@
+# Screen subsystem
+
+The screen subsystem represents a matrix of pixels on a screen, which allows us
+to draw graphics. The screen can be activated and deactivated to switch between
+graphical and text modes, if the underlying hardware supports this.
+
+This subsystem rests on a hardware specific driver that takes care of the gory
+details. The screen subsystem doesn't handle configuration of the display, which
+has to be done directly through the driver.
+
+The screen subsystem defines the Screen structure as well as a "screen"
+structbind to it. During initialization, what you'll want to do is create an
+instance of the Screen from the driver and bind it to "screen".
+
+## Driver implementation
+
+To implement the screen, we need an underlying driver to extend it and implement
+its methods, namely:
+
+:activate ( self -- )
+ Enable the graphics mode. We expect this word to set appropriate fields such
+ as "width" and "height".
+
+:deactivate ( self -- )
+ If the hardware supports it, bring back the display to text mode.
+
+:pixel' ( x y self -- a )
+ Yield the address corresponding to the specified x and y coordinates.
+
+:rgbcolor ( r8 g8 b8 self -- n )
+ Transform the RGB color into a number corresponding to the in-memory format.
+
+## API
+
+Fields:
+
+width Screen width
+height Screen height
+bpp Screen bits per plane
+
+Methods:
+
+:pixel! ( color a self -- )
+ Set a pixel at address "a" (from ":pixel'") to "color" (from ":rgbcolor").
diff --git a/fs/drv/pc/vesa.fs b/fs/drv/pc/vesa.fs
@@ -1,5 +1,7 @@
\ VESA driver
\ TODO: for now, we only support 24bpp modes
+require /sys/screen.fs
+?f<< /drv/pc/vga.fs
?f<< /lib/fmt.fs
struct[ VBEInfo
@@ -95,17 +97,28 @@ here VBEModeInfo SZ allot structbind VBEModeInfo _curmode
drop rdrop
begin ( -1 ... ) dup 0>= while _.mode repeat ( -1 ) drop ;
-: vesamode! ( modenum -- )
- dup _modeinfo _curmode :self VBEModeInfo SZ move
- 0 swap $4000 or $4f02 int10h ( di bx ax )
- $4f = _assert 2drop ;
+$112 value vesamode
-\ API
-: scrwidth _curmode width ;
-: scrheight _curmode height ;
: pixelbytes _curmode bpp >> >> >> ;
-: pixel' ( x y -- a )
- _curmode pitch * swap pixelbytes * + _curmode framebuffer + ;
-\ TODO: support more than 24bpp
-: rgbcolor ( r8 g8 b8 -- n ) swap 8 lshift or swap 16 lshift or ;
-: pixel! ( color a -- ) over >r 16b !+ r> 16 rshift swap c! ;
+
+extends Screen struct[ VESAScreen
+ : _activate ( self -- )
+ vesamode _modeinfo _curmode :self VBEModeInfo SZ move
+ 0 vesamode $4000 or $4f02 int10h ( self di bx ax )
+ $4f = _assert 2drop ( self )
+ _curmode width over to width
+ _curmode height over to height
+ _curmode bpp swap to bpp ;
+
+ : _deactivate ( self -- ) drop vgatext! ;
+
+ : _pixel' ( x y self -- a ) drop
+ _curmode pitch * swap pixelbytes * + _curmode framebuffer + ;
+
+ \ TODO: support more than 24bpp
+ : _rgbcolor ( r8 g8 b8 self -- color ) drop
+ swap 8 lshift or swap 16 lshift or ;
+
+ : :new ( -- screen )
+ :newbase ['] _activate , ['] _deactivate , ['] _pixel' , ['] _rgbcolor , ;
+]struct
diff --git a/fs/emul/uxn/gui.fs b/fs/emul/uxn/gui.fs
@@ -1,6 +1,7 @@
\ Varvara GUI devices implementation
-require /sys/draw.fs
+require /sys/screen.fs
?f<< /lib/bit.fs
+?f<< /gr/draw.fs
?f<< /emul/uxn/varvara.fs
\ FG Mask: to properly handle FG/BG logic, we need to know which pixels are
@@ -11,7 +12,7 @@ require /sys/draw.fs
create _fgmask FGMASKSZ allot
: _fg' ( x y -- bit a )
- scrwidth * + 8 /mod ( bit a ) _fgmask + ;
+ screen width * + 8 /mod ( bit a ) _fgmask + ;
: _fg! ( x y id -- )
>r _fg' dup c@ ( bit a n )
rot r> if bit1! else bit0! then ( a n )
@@ -26,20 +27,20 @@ create _fgmask FGMASKSZ allot
0 uxn_dev Device dat 8 + ( 'r )
dup short@ r@ _extract ( 'r r8 )
swap 1+ 1+ dup short@ r@ _extract ( r8 'g g8 )
- swap 1+ 1+ short@ r> _extract ( r8 g8 b8 ) rgbcolor ;
+ swap 1+ 1+ short@ r> _extract ( r8 g8 b8 ) screen :rgbcolor ;
: _drawpixel ( x y pixel -- )
3 and screencolor ( x y color )
- rot> pixel' pixel! ;
+ rot> screen :pixel' screen :pixel! ;
: _drawlayer ( x y pixel fg? -- )
if >r 2dup r@ _fg! r> _drawpixel
else >r 2dup _fg? if 2drop rdrop else r> _drawpixel then then ;
:c void _drawlayer(ushort x, ushort y, uchar pixel, int fg);
: screendei ( dev port -- c ) case
- $2 of = drop scrwidth 8 rshift endof
- $3 of = drop scrwidth $ff and endof
- $4 of = drop scrheight 8 rshift endof
- $5 of = drop scrheight $ff and endof
+ $2 of = drop screen width 8 rshift endof
+ $3 of = drop screen width $ff and endof
+ $4 of = drop screen height 8 rshift endof
+ $5 of = drop screen height $ff and endof
Device dat r@ + c@ endcase ;
: _pixel ( dev -- ) >r \ V1=dev
@@ -100,7 +101,7 @@ create _fgmask FGMASKSZ allot
: screendeo ( dev port -- )
0 to@! rgbchanged if \ we need to fill the screen with the new color
- 0 screencolor scrwidth scrheight 0 0 drawrect then
+ 0 screencolor screen width screen height 0 0 drawrect then
case ( dev ) \ V1=case
$e of = _pixel endof
$f of = _sprite endof
@@ -108,6 +109,7 @@ create _fgmask FGMASKSZ allot
\ Check for key presses and return whether the GUI program should exit (when ESC
\ is pressed).
+\ TODO depend on a more generic version ps2@?
: controllerloop ( -- f )
ps2@? if
$8 uxn_dev >r \ V1=dev
@@ -123,13 +125,12 @@ create _fgmask FGMASKSZ allot
r> Device dat short@ ?dup if uxn_exec then ( f )
else 0 then ;
-\ TODO depend on more generic versions of vesamode! vgatext! and ps2@?
: gui_init
_fgmask FGMASKSZ 0 fill
$2 ['] screendei ['] screendeo uxn_set_dev
- $112 vesamode! ;
+ screen :activate ;
-: gui_deinit vgatext! ;
+: gui_deinit screen :deactivate ;
: uxn_gui ( -- )
uxn_init varvara_init gui_init $100 uxn_exec begin ( )
diff --git a/fs/gr/draw.fs b/fs/gr/draw.fs
@@ -0,0 +1,17 @@
+require /sys/screen.fs
+
+\ TODO: "pixelbytes" is specific to drv/pc/vesa and is the wrong approach
+\ anyways.
+: drawrect ( color w h x y -- )
+ >r >r rot> >r >r \ V1=y V2=x V3=w V4=color
+ ( h ) >r begin ( )
+ V2 V1 screen :pixel' ( a ) V3 >r begin ( a )
+ V4 over screen :pixel! pixelbytes + next ( a ) drop
+ 1 to+ V1 next
+ rfree ;
+
+: recttest
+ 255 0 0 screen :rgbcolor ( color )
+ 42 42 ( color w h )
+ screen width >> 21 -
+ screen height >> 21 - ( color w h x y ) drawrect ;
diff --git a/fs/sys/draw.fs b/fs/sys/draw.fs
@@ -1,15 +0,0 @@
-\ Draw subsystem
-
-: drawrect ( color w h x y -- )
- >r >r rot> >r >r \ V1=y V2=x V3=w V4=color
- ( h ) >r begin ( )
- V2 V1 pixel' ( a ) V3 >r begin ( a )
- V4 over pixel! pixelbytes + next ( a ) drop
- 1 to+ V1 next
- rfree ;
-
-: recttest
- 255 0 0 rgbcolor ( color )
- 42 42 ( color w h )
- scrwidth >> 21 -
- scrheight >> 21 - ( color w h x y ) drawrect ;
diff --git a/fs/sys/screen.fs b/fs/sys/screen.fs
@@ -0,0 +1,21 @@
+\ Screen subsystem. see doc/screen
+
+struct[ Screen
+ sfield width
+ sfield height
+ sfield bpp
+ smethod :activate ( self -- )
+ smethod :deactivate ( self -- )
+ smethod :pixel' ( x y self -- a )
+ smethod :rgbcolor ( r8 g8 b8 self -- n )
+
+ \ Creates the first part of the screen structure, but leaves the method fields
+ \ to the caller.
+ : :newbase ( -- partial-screen ) here 0 , 0 , 0 , ;
+
+ \ TODO: support more than 24bpp
+ : :pixel! ( color a self -- )
+ drop over >r 16b !+ r> 16 rshift swap c! ;
+]struct
+
+0 structbind Screen screen
diff --git a/fs/xcomp/i386/pc/init.fs b/fs/xcomp/i386/pc/init.fs
@@ -59,5 +59,6 @@ f<< /sys/loop.fs
f<< /drv/pc/a20.fs
a20$
+f<< /sys/screen.fs
f<< /drv/pc/vesa.fs
-f<< /sys/draw.fs
+VESAScreen :new ' screen rebind