1*3ff48bf5SDavid du ColombierThe following is a sort of theory of operation for aux/vga and the 2*3ff48bf5SDavid du Colombierkernel vga drivers. 3*3ff48bf5SDavid du Colombier 4*3ff48bf5SDavid du Colombier--- aux/vga and basic kernel drivers 5*3ff48bf5SDavid du Colombier 6*3ff48bf5SDavid du ColombierAux/vga consists of a number of modules each of which conforms to an 7*3ff48bf5SDavid du Colombierinterface called a Ctlr. The Ctlr provides functions snarf, options, 8*3ff48bf5SDavid du Colombierinit, load, and dump, which are explained in more detail below. Video 9*3ff48bf5SDavid du Colombiercards are internally represented as just a collection of Ctlrs. When 10*3ff48bf5SDavid du Colombierwe want to run one of the functions (snarf, etc.) on the whole card, 11*3ff48bf5SDavid du Colombierwe run it on each Ctlr piece in turn. 12*3ff48bf5SDavid du Colombier 13*3ff48bf5SDavid du ColombierIn the beginning of aux/vga, it was common for video cards to mix and 14*3ff48bf5SDavid du Colombiermatch different VGA controller chips, RAMDACs, clock generators, and 15*3ff48bf5SDavid du Colombiersometimes even hardware cursors. The original use for vgadb was to 16*3ff48bf5SDavid du Colombierprovide a recipe for how to deal with each card. The ordering in the 17*3ff48bf5SDavid du Colombierctlr sections was followed during initialization, so that if you said 18*3ff48bf5SDavid du Colombier ctlr 19*3ff48bf5SDavid du Colombier 0xC0076="Tseng Laboratories, Inc. 03/04/94 V8.00N" 20*3ff48bf5SDavid du Colombier link=vga 21*3ff48bf5SDavid du Colombier clock=ics2494a-324 22*3ff48bf5SDavid du Colombier ctlr=et4000-w32p 23*3ff48bf5SDavid du Colombier ramdac=stg1602-135 24*3ff48bf5SDavid du Colombierwhen aux/vga wanted to run, say, snarf on this card it would call the 25*3ff48bf5SDavid du Colombiersnarf routines for the vga, ics2494a, et4000, and stg1602 Ctlrs, in 26*3ff48bf5SDavid du Colombierthat order. The special Ctlrs vga and ibm8514 take care of the 27*3ff48bf5SDavid du Colombiergeneric VGA register set and the extensions to that register set 28*3ff48bf5SDavid du Colombierintroduced by the IBM 8514 chip. Pretty much all graphics cards these 29*3ff48bf5SDavid du Colombierdays still use the VGA register set with some extensions. The only 30*3ff48bf5SDavid du Colombierexceptions currently in vgadb are the Ticket to Ride IV and the 31*3ff48bf5SDavid du ColombierNeomagic (both LCD cards). The S3 line of chips tends to have the IBM 32*3ff48bf5SDavid du Colombier8514 extensions. 33*3ff48bf5SDavid du Colombier 34*3ff48bf5SDavid du ColombierThis "mix and match" diversity has settled down a bit, with one chip 35*3ff48bf5SDavid du Colombiernow usually handling everything. As a result, vgadb entries have 36*3ff48bf5SDavid du Colombierbecome a bit more formulaic, usually listing only the vga link, a 37*3ff48bf5SDavid du Colombiercontroller, and a hardware cursor. For example: 38*3ff48bf5SDavid du Colombier ctlr 39*3ff48bf5SDavid du Colombier 0xC0039="CL-GD540" 40*3ff48bf5SDavid du Colombier link=vga 41*3ff48bf5SDavid du Colombier ctlr=clgd542x 42*3ff48bf5SDavid du Colombier hwgc=clgd542xhwgc 43*3ff48bf5SDavid du Colombier 44*3ff48bf5SDavid du ColombierOn to the controller functions themselves. The functions mentioned 45*3ff48bf5SDavid du Colombierearlier are supposed to do the following. 46*3ff48bf5SDavid du Colombier 47*3ff48bf5SDavid du Colombiervoid snarf(Vga *vga, Ctlr *ctlr) 48*3ff48bf5SDavid du Colombier Read the ctlr's registers into memory, storing them 49*3ff48bf5SDavid du Colombier either in the vga structure (if there is an appropriate 50*3ff48bf5SDavid du Colombier place) or into a privately allocated structure, a pointer 51*3ff48bf5SDavid du Colombier to which can be stored in vga->private [sic]. 52*3ff48bf5SDavid du Colombier [The use of vga->private rather than ctlr->private betrays 53*3ff48bf5SDavid du Colombier the fact that private data has only been added after we got 54*3ff48bf5SDavid du Colombier down to having cards with basically a single controller.] 55*3ff48bf5SDavid du Colombier 56*3ff48bf5SDavid du Colombiervoid options(Vga *vga, Ctlr *ctlr) 57*3ff48bf5SDavid du Colombier This step prepares to edit the in-memory copy of the 58*3ff48bf5SDavid du Colombier registers to implement the mode given in vga->mode. 59*3ff48bf5SDavid du Colombier It's really the first half of init, and is often empty. 60*3ff48bf5SDavid du Colombier Basically, something goes here if you need to influence 61*3ff48bf5SDavid du Colombier one of the other init routines and can't depend on being 62*3ff48bf5SDavid du Colombier called before it. For example, the virge Ctlr rounds 63*3ff48bf5SDavid du Colombier the pixel line width up to a multiple of 16 in its options routine. 64*3ff48bf5SDavid du Colombier This is necessary because the vga Ctlr uses the pixel line 65*3ff48bf5SDavid du Colombier width. If we set it in virge.init, vga.init would already 66*3ff48bf5SDavid du Colombier have used the wrong value. 67*3ff48bf5SDavid du Colombier 68*3ff48bf5SDavid du Colombiervoid init(Vga *vga, Ctlr *ctlr) 69*3ff48bf5SDavid du Colombier Edit the in-memory copy of the registers to implement 70*3ff48bf5SDavid du Colombier the mode given in vga->mode. 71*3ff48bf5SDavid du Colombier 72*3ff48bf5SDavid du Colombiervoid load(Vga *vga, Ctlr *ctlr) 73*3ff48bf5SDavid du Colombier Write all the ctlr's registers, using the in-memory values. 74*3ff48bf5SDavid du Colombier This is the function actually used to switch modes. 75*3ff48bf5SDavid du Colombier 76*3ff48bf5SDavid du Colombiervoid dump(Vga *vga, Ctlr *ctlr) 77*3ff48bf5SDavid du Colombier Print (to the Biobuf stdout) a description of all the 78*3ff48bf5SDavid du Colombier in-memory controller state. This includes the in-memory 79*3ff48bf5SDavid du Colombier copy of the registers but often includes other calculated 80*3ff48bf5SDavid du Colombier state like the intended clock frequencies, etc. 81*3ff48bf5SDavid du Colombier 82*3ff48bf5SDavid du ColombierNow we have enough framework to explain what aux/vga does. It's 83*3ff48bf5SDavid du Colombiereasiest to present it as a commented recipe. 84*3ff48bf5SDavid du Colombier 85*3ff48bf5SDavid du Colombier1. We sniff around in the BIOS memory looking for a match to 86*3ff48bf5SDavid du Colombierany of the strings given in vgadb. (In the future, we intend also to 87*3ff48bf5SDavid du Colombieruse the PCI configuration registers to identify cards.) 88*3ff48bf5SDavid du Colombier 89*3ff48bf5SDavid du Colombier2. Having identified the card and thus made the list of controller 90*3ff48bf5SDavid du Colombierstructures, we snarf the registers and, if the -p flag was 91*3ff48bf5SDavid du Colombiergiven, dump them. 92*3ff48bf5SDavid du Colombier 93*3ff48bf5SDavid du Colombier3. If the -i or -l flag is given, aux/vga then locates the desired 94*3ff48bf5SDavid du Colombiermode in the vgadb and copies it into the vga structure. It then does 95*3ff48bf5SDavid du Colombierany automatic frequency calculations if they need doing. (See the 96*3ff48bf5SDavid du Colombierdiscussion of defaultclock in vgadb(6).) 97*3ff48bf5SDavid du Colombier 98*3ff48bf5SDavid du ColombierFor a good introduction to video modes, read Eric Raymond's XFree86 99*3ff48bf5SDavid du ColombierVideo Timings HOWTO, which, although marked as obsolete for XFree86, 100*3ff48bf5SDavid du Colombieris still a good introduction to what's going on between the video card 101*3ff48bf5SDavid du Colombierand the monitor. 102*3ff48bf5SDavid du Colombierhttp://www.linuxdoc.org/HOWTO/XFree86-Video-Timings-HOWTO/ 103*3ff48bf5SDavid du Colombier 104*3ff48bf5SDavid du Colombier4. Having copied the vgadb mode parameters into the vga structure, 105*3ff48bf5SDavid du Colombieraux/vga calls the options and then the init routines to twiddle the 106*3ff48bf5SDavid du Colombierin-memory registers appropriately. 107*3ff48bf5SDavid du Colombier 108*3ff48bf5SDavid du Colombier5. Now we are almost ready to switch video modes. We dump the 109*3ff48bf5SDavid du Colombierregisters to stdout if we're being verbose. 110*3ff48bf5SDavid du Colombier 111*3ff48bf5SDavid du Colombier6. We tell the kernel (via the "type" vga ctl message) what sort of 112*3ff48bf5SDavid du Colombiervideo card to look for. Specifically, the kernel locates the named 113*3ff48bf5SDavid du Colombierkernel vga driver and runs its enable function. 114*3ff48bf5SDavid du Colombier 115*3ff48bf5SDavid du Colombier7. If we're using a frame buffer in direct-mapped linear mode (see 116*3ff48bf5SDavid du Colombierthe section below), we express this intent with a "linear" vga ctl 117*3ff48bf5SDavid du Colombiermessage. In response, the kernel calls the vga driver's linear 118*3ff48bf5SDavid du Colombierfunction. This should map the video memory into the kernel's address 119*3ff48bf5SDavid du Colombierspace. Conventionally, it also creates a named memory segment for use 120*3ff48bf5SDavid du Colombierwith segattach so that uesr-level programs can get at the video 121*3ff48bf5SDavid du Colombiermemory. If there is a separate memory-mapped i/o space, it too is 122*3ff48bf5SDavid du Colombiermapped and named. These segments are only used for debugging, 123*3ff48bf5SDavid du Colombierspecifically for debugging the hardware acceleration routines from 124*3ff48bf5SDavid du Colombieruser space before putting them into the kernel. 125*3ff48bf5SDavid du Colombier 126*3ff48bf5SDavid du Colombier8. We tell the kernel the layout of video memory in a "size" ctl 127*3ff48bf5SDavid du Colombiermessage. The arguments are the screen image resolution and the pixel 128*3ff48bf5SDavid du Colombierchannel format string. 129*3ff48bf5SDavid du Colombier 130*3ff48bf5SDavid du Colombier9. Everything is set; we disable the video card, call the loads to 131*3ff48bf5SDavid du Colombieractally set the real registers, and reenable the card. 132*3ff48bf5SDavid du Colombier 133*3ff48bf5SDavid du ColombierAt this point there should be a reasonable picture on the screen. It 134*3ff48bf5SDavid du Colombierwill be of random memory contents and thus could be mostly garbage, 135*3ff48bf5SDavid du Colombierbut there should be a distinct image on the screen rather than, say, 136*3ff48bf5SDavid du Colombierfunny changing patterns due to having used an incorrect sync 137*3ff48bf5SDavid du Colombierfrequency. 138*3ff48bf5SDavid du Colombier 139*3ff48bf5SDavid du Colombier10. We write "drawinit" into #v/vgactl, which will initialize the 140*3ff48bf5SDavid du Colombierscreen and make console output from now on appear on the graphics 141*3ff48bf5SDavid du Colombierscreen instead of being written to the CGA text video memory (as has 142*3ff48bf5SDavid du Colombierbeen happening). This calls the kernel driver's drawinit function, 143*3ff48bf5SDavid du Colombierwhose only job is to initialize hardware accelerated fills and scrolls 144*3ff48bf5SDavid du Colombierand hardware blanking if desired. 145*3ff48bf5SDavid du Colombier 146*3ff48bf5SDavid du Colombier11. We write "hwgc <hwgcname>" into #v/vgactl, which calls the enable 147*3ff48bf5SDavid du Colombierfunction on the named kernel hwgc driver. (Plan 9 does not yet support 148*3ff48bf5SDavid du Colombiersoftware graphics cursors.) 149*3ff48bf5SDavid du Colombier 150*3ff48bf5SDavid du Colombier12. We set the actual screen size with an "actualsize" ctl message. 151*3ff48bf5SDavid du ColombierThe virtual screen size (which was used in the "size" message in step 152*3ff48bf5SDavid du Colombier8) controls how the video memory is laid out; the actual screen size 153*3ff48bf5SDavid du Colombieris how much fits on your monitor at a time. Virtual screen size is 154*3ff48bf5SDavid du Colombiersometimes larger than actual screen size, either to implement panning 155*3ff48bf5SDavid du Colombier(which is really confusing and not recommended) or to round pixel 156*3ff48bf5SDavid du Colombierlines up to some boundary, as is done on the ViRGE and Matrox cards. 157*3ff48bf5SDavid du ColombierThe only reason the kernel needs to know the actual screen size is to 158*3ff48bf5SDavid du Colombiermake sure the mouse cursor stays on the actual screen. 159*3ff48bf5SDavid du Colombier 160*3ff48bf5SDavid du Colombier13. If we're being verbose, we dump the vga state again. 161*3ff48bf5SDavid du Colombier 162*3ff48bf5SDavid du Colombier--- hardware acceleration and blanking 163*3ff48bf5SDavid du Colombier 164*3ff48bf5SDavid du ColombierHardware drawing acceleration is accomplished by calling the 165*3ff48bf5SDavid du Colombierkernel-driver-provided fill and scroll routines rather than 166*3ff48bf5SDavid du Colombierdoing the memory operations ourselves. For >8-bit pixel depths, 167*3ff48bf5SDavid du Colombierhardware acceleration is noticeably needed. For typical Plan 9 168*3ff48bf5SDavid du Colombierapplications, accelerating fill and scroll has been fast enough that we haven't 169*3ff48bf5SDavid du Colombierworried about doing anything else. 170*3ff48bf5SDavid du Colombier 171*3ff48bf5SDavid du ColombierThe kernel driver's drawinit function should sniff the card 172*3ff48bf5SDavid du Colombierand decide whether it can use accelerated fill and scroll functions. 173*3ff48bf5SDavid du ColombierIf so, it fills in the scr->fill and scr->scroll function pointers 174*3ff48bf5SDavid du Colombierwith functions that implement the following: 175*3ff48bf5SDavid du Colombier 176*3ff48bf5SDavid du Colombierint fill(VGAscr *scr, Rectangle r, ulong val); 177*3ff48bf5SDavid du Colombier Set every pixel in the given rectangle to val. 178*3ff48bf5SDavid du Colombier Val is a bit pattern already formatted for the screen's 179*3ff48bf5SDavid du Colombier pixel format (rather than being an RGBA quadruple). 180*3ff48bf5SDavid du Colombier Do not return until the operation has completed 181*3ff48bf5SDavid du Colombier (meaning video memory has been updated). 182*3ff48bf5SDavid du Colombier Usually this means a busy wait looping for a bit 183*3ff48bf5SDavid du Colombier in some status register. Although slighty inefficient, 184*3ff48bf5SDavid du Colombier the net effect is still much faster than doing the work 185*3ff48bf5SDavid du Colombier ourselves. It's a good idea to break out of the busy 186*3ff48bf5SDavid du Colombier loop after a large number of iterations, so that 187*3ff48bf5SDavid du Colombier if the driver or the card gets confused we don't 188*3ff48bf5SDavid du Colombier lock up the system waiting for the bit. Look at 189*3ff48bf5SDavid du Colombier any of the accelerated drivers for the conventional 190*3ff48bf5SDavid du Colombier method. 191*3ff48bf5SDavid du Colombier 192*3ff48bf5SDavid du Colombierint scroll(VGAscr *scr, Rectangle r, Rectangle sr); 193*3ff48bf5SDavid du Colombier Set the pixels in rectangle r with the pixels in sr. 194*3ff48bf5SDavid du Colombier r and sr are allowed to overlap, and the correct 195*3ff48bf5SDavid du Colombier thing must be done, just like memmove. 196*3ff48bf5SDavid du Colombier Like fill, scroll must not return until the operation 197*3ff48bf5SDavid du Colombier has completed. 198*3ff48bf5SDavid du Colombier 199*3ff48bf5SDavid du ColombierRuss Cox <rsc@plan9.bell-labs.com> has a user-level scaffold 200*3ff48bf5SDavid du Colombierfor testing fill and scroll routines before putting them into 201*3ff48bf5SDavid du Colombierthe kernel. You can mail him for them. 202*3ff48bf5SDavid du Colombier 203*3ff48bf5SDavid du ColombierFinally, drawinit can set scr->blank to a hardware blanking 204*3ff48bf5SDavid du Colombierfunction. On 8-bit displays we can set the colormap to all 205*3ff48bf5SDavid du Colombierblack to get a sort of blanking, but for true-color displays 206*3ff48bf5SDavid du Colombierwe need help from the hardware. 207*3ff48bf5SDavid du Colombier 208*3ff48bf5SDavid du Colombierint blank(VGAscr *vga, int isblank); 209*3ff48bf5SDavid du Colombier If isblank is set, blank the screen. Otherwise, restore it. 210*3ff48bf5SDavid du Colombier Implementing this function on CRT-based cards is known to 211*3ff48bf5SDavid du Colombier mess up the registers coming out of the blank. 212*3ff48bf5SDavid du Colombier We've had better luck with LCD-based cards although 213*3ff48bf5SDavid du Colombier still not great luck. But there it is. 214*3ff48bf5SDavid du Colombier 215*3ff48bf5SDavid du Colombier--- linear mode and soft screens 216*3ff48bf5SDavid du Colombier 217*3ff48bf5SDavid du ColombierIn the bad old days, the entire address space was only 1MB, but video 218*3ff48bf5SDavid du Colombiermemory (640x480x1) was only 37.5kB, so everything worked out. It got 219*3ff48bf5SDavid du Colombierits own 64kB segment and everyone was happy. When screens got deeper 220*3ff48bf5SDavid du Colombierand then bigger, the initial solution was to use the 64kB segment as a 221*3ff48bf5SDavid du Colombierwindow onto a particular part of video memory. The offset of the 222*3ff48bf5SDavid du Colombierwindow was controlled by setting a register on the card. This works 223*3ff48bf5SDavid du Colombierokay but is a royal pain, especially if you're trying to copy from one 224*3ff48bf5SDavid du Colombierarea of the screen to another and they don't fit in the same window. 225*3ff48bf5SDavid du ColombierWhen we are forced to cope with cards that require accessing memory 226*3ff48bf5SDavid du Colombierthrough the 64kB window, we allocate our own copy of the screen (a 227*3ff48bf5SDavid du Colombierso-called soft screen) in normal RAM, make changes there, and then 228*3ff48bf5SDavid du Colombierflush the changed portions of memory to video RAM through the window. 229*3ff48bf5SDavid du ColombierTo do this, we call the kernel driver-provided page routine: 230*3ff48bf5SDavid du Colombier 231*3ff48bf5SDavid du Colombierint pageset(VGAscr *scr, int page); 232*3ff48bf5SDavid du Colombier Set the base offset of the video window to point 233*3ff48bf5SDavid du Colombier page*64kB into video memory. 234*3ff48bf5SDavid du Colombier 235*3ff48bf5SDavid du ColombierWith the advent of 32-bit address spaces, we can map all of video 236*3ff48bf5SDavid du Colombiermemory and avoid the soft screen. We call this running the card 237*3ff48bf5SDavid du Colombierin linear mode, because the whole video memory is mapped linearly 238*3ff48bf5SDavid du Colombierinto our address space. Aux/vga is in charge of deciding 239*3ff48bf5SDavid du Colombierwhether to do this. (In turn, aux/vga more or less respects 240*3ff48bf5SDavid du Colombiervgadb, which controls it by having or not having "linear=1" in 241*3ff48bf5SDavid du Colombierthe controller entry.) If not, aux/vga doesn't do anything special, 242*3ff48bf5SDavid du Colombierand we use a soft screen. If so, aux/vga writes "linear" and 243*3ff48bf5SDavid du Colombieran address space size into vgactl in step #7 above. In response 244*3ff48bf5SDavid du Colombierthe kernel calls the kernel driver's linear function, whose 245*3ff48bf5SDavid du Colombierjob was described in step #7. 246*3ff48bf5SDavid du Colombier 247*3ff48bf5SDavid du ColombierMost drivers only implement one or the other interface: if you've 248*3ff48bf5SDavid du Colombiergot linear mode, you might as well use it and ignore the paging 249*3ff48bf5SDavid du Colombiercapabilties of the card. Paging is typically implemented only 250*3ff48bf5SDavid du Colombierwhen necessary. 251*3ff48bf5SDavid du Colombier 252*3ff48bf5SDavid du Colombier--- from here 253*3ff48bf5SDavid du Colombier 254*3ff48bf5SDavid du ColombierIf you want to write a VGA driver, it's fairly essential that you get 255*3ff48bf5SDavid du Colombierdocumentation for the video chipset. In a pinch, you might be able to 256*3ff48bf5SDavid du Colombierget by with the XFree86 driver for the chipset instead. (The NVidia 257*3ff48bf5SDavid du Colombierdriver was written this way.) Another alternative is to use 258*3ff48bf5SDavid du Colombierdocumentation for a similar but earlier chipset and then tweak 259*3ff48bf5SDavid du Colombierregisters until you figure out what is different. (The SuperSavage 260*3ff48bf5SDavid du Colombierparts of the virge driver got written this way, starting with the 261*3ff48bf5SDavid du ColombierSavage4 parts, which in turn were written by referring to the Savage4 262*3ff48bf5SDavid du Colombierdocumentation and the Virge parts.) 263*3ff48bf5SDavid du Colombier 264*3ff48bf5SDavid du ColombierEven if you do get documentation, the XFree86 driver is good to 265*3ff48bf5SDavid du Colombierhave to double check. Sometimes the documentation is incomplete, 266*3ff48bf5SDavid du Colombiermisleading, or just plain wrong, whereas the XFree86 drivers, 267*3ff48bf5SDavid du Colombiercomplicated beasts though they are, are known to work most of the time. 268*3ff48bf5SDavid du Colombier 269*3ff48bf5SDavid du ColombierAnother useful method for making sure you understand what is going on 270*3ff48bf5SDavid du Colombieris dumping the card's registers under another system like XFree86 or 271*3ff48bf5SDavid du ColombierMicrosoft Windows. The Plan 9 updates page contains an ANSI/POSIX 272*3ff48bf5SDavid du Colombierport of aux/vga that is useful only for dumping registers on various 273*3ff48bf5SDavid du Colombiersystems. It has been used under Linux, FreeBSD, and Windows 95/98. 274*3ff48bf5SDavid du ColombierIt's not clear what to do on systems like Windows NT or Windows 2000 275*3ff48bf5SDavid du Colombierthat both have reasonable memory protection and are hardware 276*3ff48bf5SDavid du Colombierprogrammer-unfriendly. 277*3ff48bf5SDavid du Colombier 278*3ff48bf5SDavid du ColombierIf you're going to write a driver, it's much easier with a real 279*3ff48bf5SDavid du ColombierPlan 9 network or at least with a do-everything cpu/auth/file server 280*3ff48bf5SDavid du Colombierterminal, so that you can have an editor and compiler going on a 281*3ff48bf5SDavid du Colombierusable machine while you continually frotz and reboot the machine 282*3ff48bf5SDavid du Colombierwith the newfangled video card. Booting this latter machine from 283*3ff48bf5SDavid du Colombierthe network rather than its own disk makes life easier for you 284*3ff48bf5SDavid du Colombier(you don't have to explicitly copy aux/vga from the compiling machine to 285*3ff48bf5SDavid du Colombierthe testing machine) and doesn't wreak havoc on the testing machine's 286*3ff48bf5SDavid du Colombierlocal kfs. 287*3ff48bf5SDavid du Colombier 288*3ff48bf5SDavid du ColombierIt's nice sometimes to have a command-line utility to poke 289*3ff48bf5SDavid du Colombierat the vga registers you care about. We have one that perhaps 290*3ff48bf5SDavid du Colombierwe can clean up and make available. Otherwise, it's not hard 291*3ff48bf5SDavid du Colombierto roll your own. 292*3ff48bf5SDavid du Colombier 293*3ff48bf5SDavid du ColombierThe first step in writing an aux/vga driver is to write the 294*3ff48bf5SDavid du Colombiersnarf and dump routines for the controller. Then you can 295*3ff48bf5SDavid du Colombierrun aux/vga -p and see whether the values you are getting 296*3ff48bf5SDavid du Colombiermatch what you expect from the documentation you have. 297*3ff48bf5SDavid du Colombier 298*3ff48bf5SDavid du ColombierA good first resolution to try to get working is 640x480x8, 299*3ff48bf5SDavid du Colombieras it can use one of the standard clock modes rather than 300*3ff48bf5SDavid du Colombierrequire excessive clock fiddling. 301*3ff48bf5SDavid du Colombier 302*3ff48bf5SDavid du Colombier/sys/src/cmd/aux/vga/template.c is a template for a new 303*3ff48bf5SDavid du Colombiervga controller driver. There is no kernel template 304*3ff48bf5SDavid du Colombierbut any of the current drivers is a decent template. 305*3ff48bf5SDavid du Colombier/sys/src/9/pc/vga3dfx.c is the smallest one that supports 306*3ff48bf5SDavid du Colombierlinear addressing mode. 307*3ff48bf5SDavid du Colombier 308