commit 8f77d1ddd2acef25bef8b533a15e48be622e5645
parent 4595025cbc70bf5d61d7a98cedaa7ac860c54636
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Mon, 9 Jan 2023 15:33:23 -0500
drv/pc/vesa: add support for non-linear modes
With this commit, the graphical mode works on my Zenith Data Systems Pentium
75 Mhz laptop!
But his commit does much more than simply adding support for linear mode. The
first problem is that I realized that mode $112 (640x480x24) didn't work on that
laptop (I guess because it lacks video memory), but the $111 (640x480x16) did.
So I switched to that, adding new color encodings to gr/color.
VESA bank switching requires very frequent calls to the $4f05, which forced me
to get my act together w.r.t to PIC+real mode. When returning to real mode
through int10h (not int13h, which isn't supposed to be used after the PIC is
initialized), we temporarily mask all PIC interrupts and restore those mask
before returning to protected mode.
Also, int10h now uses a fixed address for VESA struct buffer.
Diffstat:
15 files changed, 142 insertions(+), 76 deletions(-)
diff --git a/REFS.md b/REFS.md
@@ -30,6 +30,7 @@ helpful:
* [Intel 82077 datasheet (floppy)][82077]
* [Intel 8259 datasheet (PIC)][8259]
* [Intel 82093 datasheet (APIC)][82093]
+* [VGA BIOS OEM Reference Guide][vgabios]
[osdev]: https://wiki.osdev.org/
[freevga]: http://www.osdever.net/FreeVGA/home.htm
@@ -40,3 +41,4 @@ helpful:
[82077]: http://www.buchty.net/casio/files/82077.pdf
[8259]: http://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf
[82093]: https://web.archive.org/web/20161128201003/http://www.intel.com/design/chipsets/datashts/290566.htm
+[vgabios]: https://www.versalogic.com/wp-content/themes/vsl-new/assets/resources/support/pdf/69030BG.pdf
diff --git a/fs/asm/i386.fs b/fs/asm/i386.fs
@@ -298,3 +298,15 @@ $e4 op in, $e6 op out,
mov, ;
\ INT is special
: int, ( n -- ) $cd c, c, ;
+
+\ Useful Dusk-related macros
+: [ebp] bp 0 d) ;
+: [ebp]z? [ebp] -1 i) test, ;
+: [esi] si 0 d) ;
+: ps+, bp CELLSZ i) sub, ;
+: ps-, bp CELLSZ i) add, ;
+: pspush, ( regid -- ) ps+, [ebp] r! mov, ;
+: pspushN, ( n -- ) ps+, [ebp] i) mov, ;
+: pspop, ( regid -- ) r! [ebp] mov, ps-, ;
+\ equivalent to r1 pspop, then r2 pspop,
+: pspop2, ( r1 r2 -- ) r! bp CELLSZ d) mov, r! [ebp] mov, bp CELLSZ << i) add, ;
diff --git a/fs/doc/gr/color.txt b/fs/doc/gr/color.txt
@@ -30,14 +30,21 @@ or it takes care, when reading and writing those colors, to reverse those bytes.
## Color encoding list
-COLOR_RGB24 = $01
+COLOR_RGB565
+ 16-bit RGB encoding with Red=b15:11 Green=b10:5 Blue=b4:0
+COLOR_RGB555
+ 16-bit RGB encoding with Red=b14:10 Green=b9:5 Blue=b4:0
+COLOR_RGB888
24-bit RGB encoding with Red=b23:16 Green=b15:8 Blue=b7:0
## API
-r8g8b8>rgb24 ( r8 g8 b8 -- n )
- Convert 3 8-bit RGB components into a COLOR_RGB24 encoding.
-
colorbpp ( id -- bpp )
Returns the bpp (bits per plane) value, that is, the number of bits that each
pixel of that encoding takes, for the specified encoding ID.
+
+These words below all convert 3 8-bit RGB components into a specific encoding:
+
+>rgb565 ( r8 g8 b8 -- n )
+>rgb555 ( r8 g8 b8 -- n )
+>rgb888 ( r8 g8 b8 -- n )
diff --git a/fs/doc/gr/plane.txt b/fs/doc/gr/plane.txt
@@ -34,14 +34,10 @@ Methods:
Allocate a new buffer of the proper size (considering height and pitch) and
set "buffer" to it.
-:addr ( self -- a )
- Yield memory address corresponding to current position.
-
-:bytes< ( a u self -- )
- Write u bytes from address a at current position.
-
-:bytes> ( a u self -- )
- Write u bytes from current position at address a.
+:xyoff ( x y self -- n )
+ Returns the offset, relative to "buffer", corresponding to position "x,y" and
+ do all preparation necessary (for example bank switching) to make that offset
+ readily accessible.
:pixel! ( self -- )
Set a pixel at tx,ty to the value of field "color".
diff --git a/fs/drv/pc/vesa.fs b/fs/drv/pc/vesa.fs
@@ -1,9 +1,10 @@
\ VESA driver
-\ TODO: for now, we only support 24bpp modes
+\ TODO: for now, we only support 16bpp modes
require /sys/screen.fs
?f<< /lib/struct.fs
?f<< /drv/pc/vga.fs
?f<< /lib/fmt.fs
+?f<< /asm/i386.fs
struct[ VBEInfo
sfield sig
@@ -63,10 +64,13 @@ here VBEModeInfo SZ allot structbind VBEModeInfo _curmode
: seg>32 ( n -- n )
dup 12 rshift $ffff0 and swap $ffff and + ;
+$4e00 const VESASTRUCT
+
: _info ( -- info )
- 0 0 $4f00 int10h ( di bx ax )
+ $32454256 VESASTRUCT ! \ write VBE2 at the start of the struct
+ 0 0 $4f00 int10h ( bx ax )
$4f = _assert drop ( info )
- dup VBEInfo sig $41534556 ( "VESA" ) = _assert ;
+ VESASTRUCT dup VBEInfo sig $41534556 ( "VESA" ) = _assert ;
: .vesa _info >r \ V1=info
r@ VBEInfo version .f" Version: %w\n"
@@ -78,8 +82,8 @@ here VBEModeInfo SZ allot structbind VBEModeInfo _curmode
r> VBEInfo productrev seg>32 .f" Product rev: %z\n" ;
: _modeinfo ( mode -- info )
- ( cx ) 0 $4f01 int10h ( di bx ax )
- $4f = _assert drop ;
+ ( cx ) 0 $4f01 int10h ( bx ax )
+ $4f = _assert drop VESASTRUCT ;
: _.mode ( mode -- )
dup >r _modeinfo >r \ V1=modeid V2=modeinfo
@@ -95,13 +99,15 @@ here VBEModeInfo SZ allot structbind VBEModeInfo _curmode
drop rdrop
begin ( -1 ... ) dup 0>= while _.mode repeat ( -1 ) drop ;
-$112 value vesamode
+$111 value vesamode
-extends Screen struct[ VESAScreen
+extends Screen struct[ VESA2Screen \ for VBE2
+ \ Here, we deal only with linear modes
: _activate ( self -- )
- vesamode _modeinfo _curmode :self VBEModeInfo SZ move
- 0 vesamode $4000 or $4f02 int10h ( self di bx ax )
- $4f = _assert 2drop ( self )
+ vesamode _modeinfo dup VBEModeInfo :linear? _assert ( mode )
+ _curmode :self VBEModeInfo SZ move
+ 0 vesamode $4000 or $4f02 int10h ( self bx ax )
+ $4f = _assert drop ( self )
_curmode width over to width
_curmode height over to height
_curmode pitch over to pitch
@@ -110,7 +116,46 @@ extends Screen struct[ VESAScreen
: _deactivate ( self -- ) 0 to buffer vgatext! ;
: :new ( -- screen )
- 0 0 COLOR_RGB24 Screen :new ( screen )
+ 0 0 COLOR_RGB565 Screen :new ( screen )
['] _activate over ['] :activate sfield!
['] _deactivate over ['] :deactivate sfield! ;
]struct
+
+$a0000 const VESABANK \ for nonlinear modes, the address of the 64K bank
+
+extends Screen struct[ VESA1Screen \ for VBE1.2
+ \ Here, we deal only with nonlinear modes
+ \ We support *only* 64K banks. If the bank is less, this code won't work.
+
+ \ we don't bother with sfields, there can only ever be one VESA1Screen.
+ 0 value bank \ currently activated bank
+ 1 value bankmult
+
+ : _activate ( self -- )
+ vesamode _modeinfo dup VBEModeInfo :linear? not _assert
+ dup VBEModeInfo winfuncptr _assert ( mode )
+ _curmode :self VBEModeInfo SZ move
+ 0 vesamode $4f02 int10h ( self di bx ax )
+ $4f = _assert drop ( self )
+ _curmode width over to width
+ _curmode height over to height
+ _curmode pitch over to pitch
+ $40 _curmode granularity / to bankmult
+ VESABANK swap to buffer ;
+
+ : _deactivate ( self -- ) 0 to buffer vgatext! ;
+
+ : _bank! ( n -- )
+ dup bankmult * 0 $4f05 int10h 2drop ( dup 1 $4f05 int10h 2drop ) to bank ;
+
+ : _?bank! ( off -- ) 16 rshift dup bank = if drop else _bank! then ;
+
+ : _xyoffbank ( x y self -- n ) _xyoff dup _?bank! $ffff and ;
+
+ : :new ( -- screen )
+ 0 0 COLOR_RGB565 Screen :new ( screen )
+ 0 ( bank ) ,
+ ['] _activate over ['] :activate sfield!
+ ['] _deactivate over ['] :deactivate sfield!
+ ['] _xyoffbank over ['] :xyoff sfield! ;
+]struct
diff --git a/fs/drv/pc/vga.fs b/fs/drv/pc/vga.fs
@@ -24,4 +24,4 @@ extends Grid struct[ VgaGrid
]struct
\ Set video mode to text mode, 80x25
-: vgatext! ( -- ) 0 0 3 int10h 2drop drop ;
+: vgatext! ( -- ) 0 0 3 int10h 2drop ;
diff --git a/fs/emul/uxn/gui.fs b/fs/emul/uxn/gui.fs
@@ -25,7 +25,7 @@ create _scrbuf SCRBUFSZ 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 ) r8g8b8>rgb24 ;
+ swap 1+ 1+ short@ r> _extract ( r8 g8 b8 ) >rgb565 ;
: _drawpixel ( x y pixel -- )
3 and screencolor to screen color ( x y )
screen :pos! screen :pixel! ;
diff --git a/fs/gr/color.fs b/fs/gr/color.fs
@@ -1,5 +1,15 @@
-$01 const COLOR_RGB24
+$1000 const COLOR_RGB565
+$1001 const COLOR_RGB555
+$1800 const COLOR_RGB888
-: r8g8b8>rgb24 ( r8 g8 b8 -- n ) swap 8 lshift or swap 16 lshift or ;
+: >rgb888 ( r8 g8 b8 -- n ) swap 8 lshift or swap 16 lshift or ;
+: >rgb565 ( r8 g8 b8 -- n )
+ 3 rshift
+ swap 3 lshift $7e0 and or
+ swap 8 lshift $f800 and or ;
+: >rgb555 ( r8 g8 b8 -- n )
+ 3 rshift
+ swap 2 lshift $3e0 and or
+ swap 7 lshift $7c00 and or ;
-: colorbpp ( id -- bpp ) drop 24 ;
+: colorbpp ( id -- bpp ) 8 rshift ;
diff --git a/fs/gr/cursor.fs b/fs/gr/cursor.fs
@@ -5,9 +5,9 @@ extends Plane struct[ Cursor
sfield parent
8 const CURSZ
- CURSZ CURSZ COLOR_RGB24 Plane :new const CURSOR
+ CURSZ CURSZ COLOR_RGB565 Plane :new const CURSOR
CURSOR :allotbuf
- 0 0 255 r8g8b8>rgb24 to CURSOR color
+ 0 0 255 >rgb565 to CURSOR color
CURSZ CURSZ CURSOR :fill
: parent>cursor ( self -- ) dup dup parent swap :copy< ;
diff --git a/fs/gr/plane.fs b/fs/gr/plane.fs
@@ -5,7 +5,7 @@
?f<< /gr/rect.fs
?f<< /gr/color.fs
-\ TODO: support more than 24bpp
+\ TODO: support more than 16bpp
extends Rect struct[ Plane
sfield encoding
sfield pitch
@@ -13,9 +13,7 @@ extends Rect struct[ Plane
sfield ty
sfield color
sfield buffer
- smethod :addr ( self -- a )
- smethod :bytes< ( a u self -- )
- smethod :bytes> ( a u self -- )
+ smethod :xyoff ( x y self -- n )
: _colorbytes ( id -- nbytes ) colorbpp >> >> >> ;
@@ -23,26 +21,21 @@ extends Rect struct[ Plane
tuck pitch * rot> encoding _colorbytes * + ;
: _addr ( self -- a )
- dup buffer swap dup tx over ty rot _xyoff + ;
-
- : _bytes< ( a u self -- ) _addr swap move ;
- : _bytes> ( a u self -- ) _addr rot> move ;
+ dup buffer swap dup tx over ty rot :xyoff + ;
: :new ( width height encoding -- plane )
>r >r >r 0 0 r> r> Rect :new ( rect )
r> ( encoding ) dup , _colorbytes over Rect width * ( pitch ) ,
- 0 ( tx ) , 0 ( ty ) , 0 ( color ) , 0 ( buffer ) ,
- ['] _addr , ['] _bytes< , ['] _bytes> , ;
+ 0 ( tx ) , 0 ( ty ) , 0 ( color ) , 0 ( buffer ) , ['] _xyoff , ;
: :allotbuf ( self -- ) >r \ V1=self
here r@ pitch r@ height * allot r> to buffer ;
: _rectaddr ( rect self -- )
tuck over x rot y ( self self x y )
- rot _xyoff swap buffer + ;
+ rot :xyoff swap buffer + ;
- : :pixel! ( self -- ) >r \ V1=self
- r@ color r@ _addr 16b !+ r> color 16 rshift swap c! ;
+ : :pixel! ( self -- ) dup color swap _addr w! ;
: _boundsx ( x self -- x ) width mod ;
: _boundsy ( y self -- y ) height mod ;
@@ -63,15 +56,18 @@ extends Rect struct[ Plane
1 V1 :ty+ next ( w x )
2drop rdrop ;
- : :copy< ( rect src self -- ) >r >r >r \ V1=self V2=src V3=rect
- V3 V2 _rectaddr V3 height >r begin ( srcaddr )
- dup V3 width V1 encoding _colorbytes * ( src src u ) V1 :bytes<
- 1 V1 :ty+ V2 pitch + next ( src )
- drop rfree ;
-
- : :copy> ( rect dst self -- ) >r >r >r \ V1=self V2=dst V3=rect
- V3 V2 _rectaddr V3 height >r begin ( dstaddr )
- dup V3 width V1 encoding _colorbytes * ( dst dst u ) V1 :bytes>
- 1 V1 :ty+ V2 pitch + next ( dst )
- drop rfree ;
+ : :copy< ( rect src self -- ) >r >r ( rect ) \ V1=self V2=src
+ dup width V1 encoding _colorbytes * >r ( rect ) \ V3=u
+ dup height >r :topleft begin ( x y )
+ 2dup V2 :xyoff V2 buffer + V1 _addr V3 move
+ 1 V1 :ty+ 1+ next ( x y+1 )
+ 2drop rfree ;
+
+ : :copy> ( rect dst self -- ) >r >r ( rect ) \ V1=self V2=dst
+ dup width V1 encoding _colorbytes * >r ( rect ) \ V3=u
+ dup height >r :topleft begin ( x y )
+ 2dup V2 :xyoff V2 buffer + V1 _addr swap V3 move
+ 1 V1 :ty+ 1+ next ( x y+1 )
+ 2drop rfree ;
+
]struct
diff --git a/fs/tests/manual/screen.fs b/fs/tests/manual/screen.fs
@@ -1,9 +1,14 @@
42 const WIDTH
54 const HEIGHT
-WIDTH HEIGHT COLOR_RGB24 Plane :new structbind Plane myplane
+WIDTH HEIGHT COLOR_RGB565 Plane :new structbind Plane myplane
myplane :allotbuf
-255 0 0 r8g8b8>rgb24 to myplane color
+255 0 0 >rgb565 to myplane color
WIDTH HEIGHT myplane :fill
screen :activate
screen width >> WIDTH >> - screen height >> HEIGHT >> - WIDTH HEIGHT Rect :new
screen :self myplane :copy>
+0 255 0 >rgb565 to screen color
+WIDTH HEIGHT screen :fill
+255 255 255 >rgb565 to screen color
+screen width WIDTH - screen height HEIGHT - screen :pos!
+WIDTH HEIGHT screen :fill
diff --git a/fs/tests/manual/uxn/mandel.tal b/fs/tests/manual/uxn/mandel.tal
@@ -1,6 +1,7 @@
( Source: https://git.sr.ht/~rabbits/uxn
Filename: projects/examples/demos/mandelbrot.tal
License: /license/dll-uxn.txt )
+( TODO: output is borked. when did this happen? not in this commit )
( mandelbrot )
%GTS2 { #8000 ADD2 SWP2 #8000 ADD2 LTH2 }
diff --git a/fs/xcomp/i386/kernel.fs b/fs/xcomp/i386/kernel.fs
@@ -6,16 +6,6 @@
?f<< /xcomp/tools.fs
\ Macros
-: [ebp] bp 0 d) ;
-: [ebp]z? [ebp] -1 i) test, ;
-: [esi] si 0 d) ;
-: ps+, bp CELLSZ i) sub, ;
-: ps-, bp CELLSZ i) add, ;
-: pspush, ( regid -- ) ps+, [ebp] r! mov, ;
-: pspushN, ( n -- ) ps+, [ebp] i) mov, ;
-: pspop, ( regid -- ) r! [ebp] mov, ps-, ;
-\ equivalent to r1 pspop, then r2 pspop,
-: pspop2, ( r1 r2 -- ) r! bp CELLSZ d) mov, r! [ebp] mov, bp CELLSZ << i) add, ;
: wcall, xwordlbl abs>rel call, ;
0 value lblintnoop
: idtgen ( entrycount -- ) >r begin
diff --git a/fs/xcomp/i386/pc/init.fs b/fs/xcomp/i386/pc/init.fs
@@ -51,7 +51,7 @@ a20$
f<< /sys/scratch.fs
f<< /sys/screen.fs
f<< /drv/pc/vesa.fs
-VESAScreen :new ' screen rebind
+VESA2Screen :new ' screen rebind
?f<< /gr/cursor.fs
screen :self Cursor :new structbind Cursor cursor
diff --git a/fs/xcomp/i386/pc/kernel.fs b/fs/xcomp/i386/pc/kernel.fs
@@ -33,18 +33,17 @@ xcode int13h ( drv head cyl sec dst -- )
pc to L1 \ back to protected mode!
\ we still need to push di, bx and ax
dx $10 i) mov, ds dx mov, ss dx mov, es dx mov, gs dx mov, fs dx mov,
- DI pspush, BX pspush, AX pspush,
+ BX pspush, AX pspush,
lblidt m) lidt, sti,
+ \ restore PIC masks
+ ax pop, al $a1 i) out, al ah mov, al $21 i) out,
ret,
pc to L3 1 to realmode \ we're in realmode
dx dx xor, es dx mov, ds dx mov, ss dx mov, L2 m) lidt, sti,
- \ VESA calls need a 512b buffer that can be accessed by real mode. SP is
- \ always in the 64K range, so let's use it along with a little security buf.
- di sp mov, di $300 i) sub,
- \ Write "VBE2" at DI
- di 0 d) $4256 i) mov, ( VB ) di 2 d) $3245 i) mov, ( E2 )
- $10 int,
+ \ VESA calls need a 512b buffer that can be accessed by real mode.
+ di $4e00 i) mov,
+ dx cx mov, $10 int,
\ we've done what we came for, let's go back to 32bit
cli, dx cr0 mov, dx 1 i) or, cr0 dx mov,
$08 L1 jmpfar,
@@ -55,8 +54,11 @@ pc to L4 \ segment with ffff limits
0 L3 jmpfar,
0 to realmode
-xcode int10h ( cx bx ax -- di bx ax )
- AX pspop, BX pspop, CX pspop, di di xor, cli, $18 L4 jmpfar,
+xcode int10h ( cx/dx bx ax -- bx ax )
+ \ save PIC masks and disable PIC for duration of int10h
+ al $21 i) in, ah al mov, al $a1 i) in, ax push,
+ al $ff i) mov, al $21 i) out, al $a1 i) out,
+ AX pspop, BX pspop, CX pspop, cli, $18 L4 jmpfar,
\ To avoid lockups, we map all PIC IRQs on boot to words that acknowledge those
\ IRQs. The rest of PIC initialization is done in /drv/pc/pic.fs