From: Damiano Bolla <[email-masked]>
Date: Mon, 30 Mar 92 12:38:16 +0100

I am sending this article here since my site has problems with the news.
If abc think that this article is worth to be posted it will be nice
if he could poste it in the net.

Thanks to all
==================================

Hello !

This article will give you a simple way to have text and graphics
using a VGA graphic adapter.

Most of the ideas are taken from the mgr graphic interface for minix.

The idea behind this simple patch is described in the following file:

----------------------------------------------------------

VGAREGS(MS-DOS) 	 Programmer's Manual (MS-DOS)          VGAREGS(MS-DOS)

NAME
     vgaregs - examine vga-adapter register values  and  generate  an  include
     file for the MINIX/Linux screen driver.

SYNOPSIS
     vgaregs [ax[_bx] ...] [ > video.h ]

DESCRIPTION
     This program is intended to help you generating a proper include file for
     the minix screen-driver if you are using a VGA display adapter.

     It uses the BIOS video interrupt 0x10 to switch to each one of the  modes
     specified	at the command line.  Register AX is loaded by the 'ax' value,
     and, if 'bx' is also specified, this is loaded into the BX register.   AX
     (and BX) must be given as hexadecimal numbers without a leading '0x'.

     Vgaregs then reads the display  adapter  register	values	and  tries  to
     determine	the  screen  parameters.   These  parameters  are  printed  on
     standard output and should be redirected into a file.

OUTPUT FORMAT
     Defines for the hercules-adapter in text and graphic  modes  are  printed
     first.

     The next define is
	       #define REGBASE 0x3B4
     if you have a monochrome monitor, and
	       #define REGBASE 0x3D4
     if you have a color monitor connected to your VGA adapter.

     For each video mode then definitions are printed in the form

	       #define MODE_XX \
		    `register values'

     and

	       #define PARM_XX \
		    PP( `video mode parameters' )

     where `register values' is a list of the register values  read  from  the
     adapter  (in  the	correct  order	as required by the screen driver), and
     `screen parameters' is a guess of the screen parameters.  The latter list
     is  not always correct, so you should compare this to your documentation.
     'XX' will be replaced by the corresponding  command  line  string.   (The
     macro PP() is a hack for the C-preprocessor).

     The 'screen parameter' entries are in the following order:

	       Physical Start address of video memory (short int)
	       Size of video memory in bytes (long  int)
	       Pixels per line (short int)
	       Pixels per row	   (short int)
	       Number of bitplanes (short int)
		    always assumed to be 4 (16-color modes)
	       Interlace Flag	   (short int)
		    always set to 0
	       Number of interlaced Pages (short int)
		    always set to 1
	       Pagesize of interlaced pages (short int)
		    always set to 0

     The first text mode and the first three graphic video modes given at  the
     command line should be those you actually want to use in the MINIX screen
     driver.  For these modes defines are added  with  names  ?VGA_GRAF_PARMS,
     ?VGA_GRAF_MODE,  VGA_TEXT_PARMS  and VGA_TEXT_MODE, for all other modes a
     comment like /* This mode is not used */.

EXAMPLE
     vgaregs 3 10 12 29 37 > video.h
     will generate an include file named video.h with definitions for textmode
     (mode  3),  640x350 16 colors (mode 0x10), 640x480 16 colors (mode 0x12),
     800x600 16 colors (mode 0x29) and 1024x768 16 colors (mode 0x37) for  the
     'OPTIMA/1024  plus' display adapter (TSENG 3000 chip).  The names MODE_3,
     MODE_10, MODE_12, MODE_29	and  MODE_37  in  the  output  file  hold  the
     register  values,	PARM_3, PARM_10, PARM_12, PARM_29 and PARM_37 hold the
     screen parameters.  The values for PARM_37 are not  determined  correctly
     by  this program (See section BUGS).  The generated include file will use
     BIOS mode 3 as textmode, and modes 10, 12 and 29 as graphic modes.

FURTHER HINTS
     You can use vgaregs to collect  information  about  ALL  videomodes  your
     adapter  can  handle  into one file.  Only the first textmode encountered
     and the first three graphic modes encountered are then used by the  MINIX
     screen driver.
     You can then play with all these modes by simply changing some defines in
     the generated file.

     Vgaregs uses the registers as  the  BIOS  will  set  them.   So  it  will
     generate  the same cursor shape (underline) as MS-DOS uses it by default.
     If you like the MINIX block-type cursor, patch the MODE definition in the
     output  file  for	TEXT mode (entries 11 and 12 should be set to 0x00 and
     0x0F resp.).
     Also worth patching are the color attribute registers (entries 25 - 40 in
     the MODE definition), if you don't like the default colors.

BUGS
     Vgaregs is an MS-DOS program, as it uses the VGA-BIOS.
     Vgaregs does not determine if you actually have a VGA adapter.
     Vgaregs only knows about standard VGA  registers,	so  probably  1024x768
     modes  will  never  work (although modes up to 800x600 resolution usually
     do).
     Vgaregs (and also the screen driver) only works  for  VGA-modes  with  16
     colors or less.
     Vgaregs is ugly code.

-------------------------------------------------------------------------
The above file describes the operations of the MSDOS vgaregs.exe program.
The all purpose of the program is to generate the vga.h file !

After that you can use it to create the new kernel using the following files.
The following one is the actual ioctl switch command...

---------------------------------------------------------------------

/* Ident: fs/vga_ioctl.c
 * Author : Damiano Bolla
 * This implement the vga switching..... what a pain
 * Based of the minix mgr manager
 */

#include <asm/io.h>
#include <asm/system.h>
#include <asm/memory.h>
#include <errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/videodef.h>	/* Definition of video modes		*/ 
#include <linux/screen.h>	/* Structures used by the sys		*/

/* the next one is used to tell to the rest of the system where the
 * Current vido map is and how long it is.
 * NOTE: It is apointer to something ! ( when is set )
 */

struct scr_param *VideoParam = NULL;

/* Some additional structure to keep the informations for switching	*/
struct scr_defs 
   {
   struct scr_param params;
   char regs[60];
   };
   
/*
 * Hack for the preprocessor (the most abused tool in the world?).
 * Video parameters are defined as PP( .... ) in videodef.h
 */
#define PP(x1,x2,x3,x4,x5,x6,x7,x8) \
	x1, x2, x3, x4, x5, x6, x7, x8

static struct scr_defs
  Vga_Text  = { { VGA_TEXT_PARMS }, {  VGA_TEXT_MODE } },
  Vga_Graf  = { { VGA_GRAF_PARMS }, {  VGA_GRAF_MODE } },
/* If you have the right values put them in in here.....		*/  
  Svga_Graf  = { { VGA_GRAF_PARMS }, {  VGA_GRAF_MODE } },
  Evga_Graf  = { { VGA_GRAF_PARMS }, {  VGA_GRAF_MODE } };
  
extern int vga_mode( int mode );

int vga_ioctl(int dev, int cmd, int arg)    
   {
   switch( cmd ) 
      {
      case SCRSETMODE:    
         /* arg defines the requested vga status we want		*/
         vga_mode ( arg );
         return (0);
         break;
      case SCRGETP:
         return (0);
         break;
      }
   return EINVAL;
   }

/* ================= VGA index register ports: =======================	*/
#define CRT_I   0x3D4   /* CRT Controller Index (0x3D4=color, 0x3B4=mono) */
#define ATT_IW  0x3C0   /* Attribute Controller Index & Data Write Reg. */
#define GRA_I   0x3CE   /* Graphics Controller Index 		*/
#define SEQ_I   0x3C4   /* Sequencer Index 			*/

/* VGA data register ports: */
#define CRT_D   0x3D5   /* CRT Contr. Data Reg. (0x3D5=color, 0x3B5=mono) */
#define ATT_R   0x3C1   /* Attribute Controller Data Read Register */
#define GRA_D   0x3CF   /* Graphics Controller Data Register */
#define SEQ_D   0x3C5   /* Sequencer Data Register */
#define MIS_R   0x3CC   /* Misc Output Read Register */
#define MIS_W   0x3C2   /* Misc Output Write Register */
#define IS1_R   0x3DA   /* Input Status Register 1 (Read only)
                           (0x3DA=color, 0x3BA=mono) */
/* VGA indexes max counts: */
#define CRT_C   24      /* 24 -CRT Controller Registers*/
#define ATT_C   21      /* 21 -Attribute Controller Registers */
#define GRA_C   9       /* 9  -Graphics Controller Registers */
#define SEQ_C   5       /* 5  -Sequencer Registers */
#define MIS_C   1       /* 1  -Misc Output Register */
 
/* VGA registers saving indexes */
#define CRT     0               /* CRT Controller Registers start */
#define ATT     CRT+CRT_C       /* Attribute Controller Registers start */
#define GRA     ATT+ATT_C       /* Graphics Controller Registers -"- */
#define SEQ     GRA+GRA_C       /* Sequencer Registers */
#define MIS     SEQ+SEQ_C       /* General Registers */
#define END     MIS+MIS_C       /* last */

int vga_mode( int mode ) 
   {
   static int curr_mode = VGA_TEXT;	/* We know this is the initial	*/
   char *regs;  		/* pointer to new register values 	*/
   int i;
   
#ifndef ROM_CHAR_SET
   static char vga_char_map[8192];       /* character map buffer 	*/
   static map_initialized = 0;		/* The map is not initalized	*/
#endif

   /* Let'see if we need to switch.....				*/     
   if ( curr_mode == mode ) return (0);      
   
   switch(mode) 
      {
      case VGA_TEXT:
         regs = Vga_Text.regs;
         VideoParam = &Vga_Text.params;
         break;
      case VGA_640x480:
         regs = Vga_Graf.regs; 
         VideoParam = &Vga_Graf.params;
         break;
      case VGA_600x800:
         regs = Svga_Graf.regs; 
         VideoParam = &Svga_Graf.params;
         break; 
      case VGA_1024x768:
         regs = Evga_Graf.regs; 
         VideoParam = &Evga_Graf.params;
         break;
      default: 
         return EINVAL;
         break;
      }

   /* Do we want to switch to text mode ??.. if so we need to restore	*/
   if ( (mode == VGA_TEXT) && (map_initialized == 1) ) 
      {
      /* restore character map before setting TEXT mode  		*/
      outb(0x04, GRA_I ); 
      outb(0x02, GRA_D ); 	/* select page 2 where to write map	*/
    
#ifndef ROM_CHAR_SET
      (void) memcpy ((char *)0xA0000L, vga_char_map, sizeof (vga_char_map));
#else
     {   
     int i; 
     for (i=0; i<256; i++)
       (void) memcpy ( (char *)(0xA0000L+(i+32)), (char *)(ROM_CHAR_SET+(i*16)), 16 );
     }
#endif
     }
  
   /* ------------- set VGA registers to new values ---------------------*/
   inb(IS1_R); 			/* clear flip-flop 		*/
   outb(0x00, ATT_IW );	       	/* Disable video 		*/
  
   outb(GRA_I,0x00); 
   outb(GRA_D,0x00);  		/* Set/Reset (graph) 		*/
  
   inb(IS1_R);  	 	/* clear flip-flop 		*/
   outb(SEQ_I,0x00); 
   outb(SEQ_D,0x01); 		/* synchronous reset ON		*/
  
/*
 * IMPORTANT NOTE: (speaking from experience!)
 * BE SURE *** NOT *** TO ACCESS THE VIDEO MEMORY WHILE THE SEQUENCER IS OFF !!!
 * THE CPU WILL WAIT FOREVER ON THE SCREEN ACCESS...
 */
   outb(regs[MIS+0], MIS_W ); 	/* update misc output reg 	*/
  
   outb(0x1, SEQ_I ); 
   outb(regs[SEQ+1], SEQ_D );  	/* update clocking mode 	*/
  
   for (i=2;i<SEQ_C;i++) 		/* Sequencer Registers 	*/
      { 
      outb(i, SEQ_I); 
      outb(regs[SEQ+i], SEQ_D); 
      }

  /* ---------------- deprotect registers 0-7 ----------------------- 	*/
   outb(0x11, CRT_I ); 		/* Vertical retrace end reg	*/
   outb(regs[CRT+0x11]&0x7F, CRT_D); 	/* enable write to reg 0-7 	*/
  
   for (i=0;i<CRT_C;i++) 		/* CRT Controller Registers 	*/
      {  
      outb(i, CRT_I); 
      outb(regs[CRT+i], CRT_D); 
      }

   for (i=0;i<GRA_C;i++) 		/* Graphics Controller Registers */
      {  
      outb(i, GRA_I); 
      outb(regs[GRA+i], GRA_D); 
      }
     
#ifdef NO_ATTRIBUTES_ARE_RESTORED
   outb(0x10, ATT_IW);        		/* Attribute Mode Control Reg	*/
   outb(regs[ATT+0x10] ATT_IW);
   outb(0x13, ATT_IW );        /* Horizontal PEL Panning Reg (Attr) 	*/
   outb(regs[ATT+0x13], ATT_IW);
#else
   for (i=0;i<ATT_C;i++)         	/* Attribute Controller Reg	*/
     {
     inb(IS1_R);         		/* Reset Flipflop 		*/
     outb(i, ATT_IW);
     outb(regs[ATT+i],ATT_IW);
     }
#endif

   outb(0x00, SEQ_I); 
   outb(0x03, SEQ_D);   /* synchronous reset OFF */
  
/* ----------------- now video accesses are possible again --------- 	*/

   inb(IS1_R); 
   outb(0x20, ATT_IW);        /* Enable video 	*/
  
#ifndef ROM_CHAR_SET
   /* If the buffer for the array map is not recorded we record it	*/
   /* The map is one the screen page number 2...			*/
   if ( map_initialized == 0 ) 	
      {
      map_initialized = 1;
      outb(0x04, GRA_I); 
      outb(0x02, GRA_D); 	/* select page 2 to get thw map 	*/
      (void) memcpy ( vga_char_map, (char *)(0xA0000L), sizeof(vga_char_map) );
      outb(0x04, GRA_I);
      outb(regs[GRA+0x04], GRA_D); /* restore map mask 			*/
      }
#endif

   curr_mode = mode; /* keep track of video mode for next call		 */
   return 0;
   }

--------------------------------------------------------------------------

This one is the modified char_dev.c to support a write to the newly
created device. I am using the device 1 0 since this all thing is a test.
NOTE that the all thing is reasonably safe since the write to the device
checks the boundaries.

To create the new device do:
mknod /dev/vga c 1 0

----------------------------------------------------------------------------

/*
 *  linux/fs/char_dev.c
 *
 *  (C) 1991  Linus Torvalds
 */

#include <errno.h>
#include <sys/types.h>

#include <linux/sched.h>
#include <linux/kernel.h>

#include <asm/segment.h>
#include <asm/io.h>

#include <linux/screen.h>	/* To read write to screen	*/

extern int tty_read(unsigned minor,char * buf,int count,unsigned short flags);
extern int tty_write(unsigned minor,char * buf,int count);
extern int lp_write(unsigned minor,char *buf, int count);

typedef (*crw_ptr)(int,unsigned,char *,int,off_t *,unsigned short);

static int rw_ttyx(int rw,unsigned minor,char * buf,int count,off_t * pos, unsigned short flags)
{
	return ((rw==READ)?tty_read(minor,buf,count,flags):
		tty_write(minor,buf,count));
}

static int rw_lp(int rw,unsigned minor,char * buf,int count,off_t * pos)
{
	return ((rw==READ)? -EINVAL:
		lp_write(minor,buf,count));
}

static int rw_tty(int rw,unsigned minor,char * buf,int count, off_t * pos, unsigned short flags)
{
	if (current->tty<0)
		return -EPERM;
	return rw_ttyx(rw,current->tty,buf,count,pos,flags);
}

extern struct scr_param *VideoParam;

static int rw_ram(int rw,char * buf, int count, off_t *pos)
   {
   /* This is supposed to access the VGA video ram.....			*/
   int i = *pos;
   int base, limit;
   
   /* If we have an invalid pointer to the data we can't go on		*/
   if ( VideoParam == NULL ) return (-EIO);
   
   base  = VideoParam->scr_base;
   limit = base + VideoParam->scr_fbsize;
   i+= base;

   while ( (count-- > 0)  && (i <limit )) {
	if (rw==READ)
		put_fs_byte( *(char *)i ,buf++);
	else
		*(char *)i = get_fs_byte ( buf++ );
	i++;
   }
   i -= base;		/* Take avay the base...		*/
   i -= *pos;		/* Count how many read or write		*/
   *pos += i;		/* Update position			*/
   return (i);		/* Return number read			*/
   }

static int rw_mem(int rw,char * buf, int count, off_t * pos)
{
	return -EIO;
}

static int rw_kmem(int rw,char * buf, int count, off_t * pos)
{
	/* kmem by Damiano */
	int i = *pos;	/* Current position where to read	*/

	/* i can go from 0 to LOW_MEM (See include/linux/mm.h	*/
	/* I am not shure about it but it doesn't mem fault :-)	*/
	while ( (count-- > 0)  && (i <LOW_MEM) ) {
		if (rw==READ)
			put_fs_byte( *(char *)i ,buf++);
		else
			*(char *)i = get_fs_byte ( buf++ );
		i++;
	}
	i -= *pos;		/* Count how many read or write		*/
	*pos += i;		/* Update position			*/
	return (i);		/* Return number read			*/
}

static int rw_port(int rw,char * buf, int count, off_t * pos)
{
	int i=*pos;

	while (count-->0 && i<65536) {
		if (rw==READ)
			put_fs_byte(inb(i),buf++);
		else
			outb(get_fs_byte(buf++),i);
		i++;
	}
	i -= *pos;
	*pos += i;
	return i;
}

static int rw_memory(int rw, unsigned minor, char * buf, int count,
	off_t * pos, unsigned short flags)
{
	switch(minor) {
		case 0:
			return rw_ram(rw,buf,count,pos);
		case 1:
			return rw_mem(rw,buf,count,pos);
		case 2:
			return rw_kmem(rw,buf,count,pos);
		case 3:
			return (rw==READ)?0:count;	/* rw_null */
		case 4:
			return rw_port(rw,buf,count,pos);
		default:
			return -EIO;
	}
}

#define NRDEVS ((sizeof (crw_table))/(sizeof (crw_ptr)))

static crw_ptr crw_table[]={
	NULL,		/* nodev */
	rw_memory,	/* /dev/mem etc */
	NULL,		/* /dev/fd */
	NULL,		/* /dev/hd */
	rw_ttyx,	/* /dev/ttyx */
	rw_tty,		/* /dev/tty */
	rw_lp,		/* /dev/lp */
	NULL};		/* unnamed pipes */

int char_read(struct inode * inode, struct file * filp, char * buf, int count)
{
	unsigned int major,minor;
	crw_ptr call_addr;

	major = MAJOR(inode->i_rdev);
	minor = MINOR(inode->i_rdev);
	if (major >= NRDEVS)
		return -ENODEV;
	if (!(call_addr = crw_table[major]))
		return -ENODEV;
	return call_addr(READ,minor,buf,count,&filp->f_pos,filp->f_flags);
}

int char_write(struct inode * inode, struct file * filp, char * buf, int count)
{
	unsigned int major,minor;
	crw_ptr call_addr;

	major = MAJOR(inode->i_rdev);
	minor = MINOR(inode->i_rdev);
	if (major >= NRDEVS)
		return -ENODEV;
	if (!(call_addr=crw_table[major]))
		return -ENODEV;
	return call_addr(WRITE,minor,buf,count,&filp->f_pos,filp->f_flags);
}

-------------------------------------------------------------------------
Of course to have the ioctl to work in the major 1 we need to 
change the ioctl.c too :-)

----------------------------------------------------------------------

/*
 *  linux/fs/ioctl.c
 *
 *  (C) 1991  Linus Torvalds
 */

#include <string.h>
#include <errno.h>
#include <sys/stat.h>

#include <linux/sched.h>

extern int tty_ioctl(int dev, int cmd, int arg);
extern int pipe_ioctl(struct inode *pino, int cmd, int arg);
extern int vga_ioctl(int dev, int cmd, int arg);

typedef int (*ioctl_ptr)(int dev,int cmd,int arg);

#define NRDEVS ((sizeof (ioctl_table))/(sizeof (ioctl_ptr)))

static ioctl_ptr ioctl_table[]={
	NULL,		/* nodev */
	vga_ioctl,	/* /dev/mem (VGA temporarly ) */
	NULL,		/* /dev/fd */
	NULL,		/* /dev/hd */
	tty_ioctl,	/* /dev/ttyx */
	tty_ioctl,	/* /dev/tty */
	NULL,		/* /dev/lp */
	NULL};		/* named pipes */

int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{	
	struct file * filp;
	int dev,mode;

	if (fd >= NR_OPEN || !(filp = current->filp[fd]))
		return -EBADF;
	if (filp->f_inode->i_pipe)
		return (filp->f_mode&1)?pipe_ioctl(filp->f_inode,cmd,arg):-EBADF;
	mode=filp->f_inode->i_mode;
	if (!S_ISCHR(mode) && !S_ISBLK(mode))
		return -EINVAL;
	dev = filp->f_inode->i_rdev;
	if (MAJOR(dev) >= NRDEVS)
		return -ENODEV;
	if (!ioctl_table[MAJOR(dev)])
		return -ENOTTY;
	return ioctl_table[MAJOR(dev)](dev,cmd,arg);
}

-------------------------------------------------------------------------
It is not finish... we need the include files now.
-------------------------------------------------------------------------

/* Data structures for IOCTL on /dev/screen */
/* This goes in /usr/src/linux/include/linux/screen.h */

#ifndef _SCREEN_H
#define _SCREEN_H

/*
 * physical screen attributes; information returned by the
 * SCRGETP ioctl call
 */
struct scr_param {
  unsigned long		scr_base;	/* physical address of screen */
  unsigned int		scr_fbsize;	/* size of framebuffer */
	/* the following fields only have meaning in graphic modes. */
  unsigned short	scr_width;	/* width of screen (pixels) */
  unsigned short	scr_height;	/* height of screen (pixels) */
  unsigned short	scr_depth;	/* no. of bitplanes on screen */
           short	scr_interlace;	/* 1 if interlaced, 0 if not */
	/* the following fields on;y have meaning if interlaced */
  unsigned short	scr_numpages;	/* number of interlaced pages */ 
  unsigned short	scr_pagesize;	/* address distance between the
					 * first pixel in the first line
					 * and the first pixel in the
					 * second line
					 */
};

/* ioctl(2) - commands that you can use with this stuff			*/

#define SCRGETP		1 	/* return scr_parameters 		*/
#define SCRSETMODE	2	/* switch video mode (text/graphic) 	*/

/* Possible vga configurations that you can have			*/

#define VGA_TEXT	3		/* text mode */
#define VGA_640x480	4		/* graphics mode */
#define VGA_600x800	5		/* graphics mode */
#define VGA_1024x768	6		/* graphics mode */

#endif /* _SCREEN_H */

-------------------------------------------------------------------------

/*
 * include/linux/videodef.h
 *
 * The values for EGA/VGA adapters (depending on which adapter you
 * have, and which modes you want to use) are #included below by the file
 * 'vga.h'.
 */

/*
 * struct scr_defs contains:
 * 1. (in struct scr_param) the physical screen parameters, as defined
 *    in <screen.h>.
 *    This information, like number of pixels at the display ... are
 *    made available to user programs by the kernel (via ioctl()-call).
 *
 * 2. (in regs[]) the register values which should be written into the
 *    hardware registers when switching to this video mode.
 *    This information is only needed by the kernel itself.
 */

/*
 * Definitions for EGA/VGA adapters.
 */
#include "vga.h"

/* If you know where the address of your rom character map is
 * you can save 8k in the kernel :-)
 * #define ROM_CHAR_SET 0xC4F64L 
 */

------------------------------------------------------------------------
The next one is the vga.h file generated by my vgaregs.exe using
vgaregs.exe 3 12 >vga.h from DOS.
You should generate you own and use that !
This is included just to show how things are
---------------------------------------------------------------------------
 
#define HERC_TEXT_MODE \
  0x20,0x61,0x50,0x52,0x0f,0x19,0x06,0x19,0x19,0x02,0x0d,0x00,0x0d

#define HERC_TEXT_PARMS \
  0xB0000L, 4000, 0, 0, 1, 0, 1, 0

#define HERC_GRAF_MODE \
  0x02,0x35,0x2d,0x2e,0x07,0x5b,0x02,0x57,0x57,0x02,0x03,0x00,0x00

#define HERC_GRAF_PARMS \
  0xB0000L, 32768, 720, 348, 1, 1, 4, 0x2000

#define REGBASE = 0x3D4

#define MODE_3 \
  0x5F,0x4F,0x50,0x82,0x55,0x81,0xBF,0x1F,0x00,0x4F,0x0D,0x0E, \
  0x00,0x00,0x00,0x00,0x9C,0x8E,0x8F,0x28,0x1F,0x96,0xB9,0xA3, \
  0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07,0x38,0x39,0x3A,0x3B, \
  0x3C,0x3D,0x3E,0x3F,0x0C,0x00,0x0F,0x08,0x00, \
  0x00,0x00,0x00,0x00,0x00,0x10,0x0E,0x00,0xFF, \
  0x03,0x00,0x03,0x00,0x02, \
  0x67

/* PP( ) is a hack for cpp */

#define PARMS_3 \
  PP( 0xB8000L, 4000L,  0, 0, 4, 0, 1, 0 )
#define  VGA_TEXT_MODE	MODE_3
#define  VGA_TEXT_PARMS	PARMS_3

#define MODE_12 \
  0x5F,0x4F,0x50,0x82,0x54,0x80,0x0B,0x3E,0x00,0x40,0x00,0x00, \
  0x00,0x00,0x00,0x00,0xEA,0x8C,0xDF,0x28,0x00,0xE7,0x04,0xE3, \
  0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07,0x38,0x39,0x3A,0x3B, \
  0x3C,0x3D,0x3E,0x3F,0x01,0x00,0x0F,0x00,0x00, \
  0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0F,0xFF, \
  0x03,0x01,0x0F,0x00,0x06, \
  0xE3

/* PP( ) is a hack for cpp */

#define PARMS_12 \
  PP( 0xA0000L, 38400, 640, 480, 4, 0, 1, 0 )
#define  VGA_GRAF_MODE	MODE_12
#define  VGA_GRAF_PARMS	PARMS_12

--------------------------------------------------------------------
Ok. Before putting the actual vgaregs.exe in uuencode format.. few points.
1) I have a MONO monitor. and a unknown chipsed VGA
2) To use the thing my system MUST start in the default 80x25 and COLOR mode
   If I start in a different screen mode then I have trouble!
3) What this gives you is a working example of switching. 
   NO graphics primitives areimplemented :-)
   If you want to play just write something to 
   /dev/vga and see what happens.
4) When you are in graphics mode there is NO WAY to see messages from 
   the kernel or printf...
   Here it is my SIMPLE program to test the thing.

------------------------------------------------------------------------
         
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/screen.h>

main ()
   {
   int fd;
   int i;
   char val='a' ;

   if ( (fd=open("/dev/vga", O_RDWR) ) < 0 )
      {
      printf ("cant open vga \n");
      exit (1);
      }

   ioctl ( fd, SCRSETMODE, VGA_640x480 );

   for (i=0; i<65000; i++ )
      if ( write (fd, &val, 1) < 1 )
         {
         ioctl ( fd, SCRSETMODE, VGA_TEXT );
         printf ("i is %d ",i);
         break;
         }

   sleep (2);
   ioctl ( fd, SCRSETMODE, VGA_TEXT );
   
   lseek (fd, 0, SEEK_SET );
   
   for (i=0; i<4000; i++ )
      write (fd, &val, 1);
   
   sleep (2);
   ioctl ( fd, SCRSETMODE, VGA_640x480 );

   sleep (2);
   ioctl ( fd, SCRSETMODE, VGA_TEXT );
   lseek (fd, 0, SEEK_SET );

   val = 0;
   for (i=0; i<4000; i++ )
      if ( write (fd, &val, 1) < 1 )
         {
         ioctl ( fd, SCRSETMODE, VGA_TEXT );
         printf ("i text is %d ",i);
         break;
         }
   
   close (fd);
   }
      
-----------------------------------------------------------------
The next program is the DOS program to catch the vag registes
------------------------------------------------------------------

Ok, then, It is all. 
Have fun !

Damiano

P.S. It should be easy to use the /dev/vga device to have 
preview in gostscript, after all gostscript needs just B/W and
a framebuffer...... :-)