
/* IBM bios screen control package
   Copyright Dave Newman 1991 
   Permission to use these routines for any reason
   is granted as long as this copyright notice is
   included.
   This should compile on any DOS C compiler
   that supports int86() */

/* declarations for these functions */
#include <bios.h>
/* needed for REGS union */
#include <dos.h>


/* these variables are global */
/* these vars hold information about the system.
   They are valid after the call to bios_open().
   If the information in them is not relevant,        
   the call to bios_open() is not necessary. */
int cur_mode;         /* current display mode */
int cur_page;         /* current display page */
int num_cols;         /* number of cols on screen */

/* adapter types */
char    cga,         /* color graphics adapter */
   ega,          /* Enhanced graphics adapter */
   vga,          /* video graphics array */
   mcga,         /* PS/2 mcga display */
   mono,         /* just black and white */
   herc,         /* hercules graphics adapter */
   none;         /* no monitor at all */

char   color,    /* is using a color monitor */
   b_w;          /* or black+white? */

/* these vars hold info about the cursor position
   and the current color/attribute being used.
   (no bios_open call necessary) */

/* display attribute (use to change colors) */
unsigned char cur_attr;
int cur_row;         /* current row of cursor */
int cur_col;         /* current column of cursor */
/* end global values */

/* bios open function.
   Set up the initial values in all of the variables
   above. */
void bios_open()
   {
   union REGS regs;
   void vtest(void);

   /* determine the current display mode */
   regs.x.ax = 0x0f00;
   int86(0x10,&regs,&regs);
   cur_mode = regs.h.al;
   cur_page = regs.h.bh;
   num_cols = regs.h.ah;

   /* determine the type of display in the system
      and set it's type value to 1
      on return the proper video adapter variable
      will be set as will the color or b_w value */
   none=mono=herc=cga=ega=vga=mcga=color=b_w=0;
   vtest();
   }

/* and a bios_close function just for the sake of
   consistency. Please use this function. It may do
   something one day (like restore the screen
   to the previous state) */
void bios_close()
   {}

/* change value of cursor.
   range 0x0000 to 0xFFFF (FFFF == OFF) */
void bios_cursor(int state)
   {
   union REGS regs;
   regs.h.ah = 1;
   /* ch == start line. cl == end line */
   regs.x.cx = state;
   int86(0x10,&regs,&regs);
   }

/* move cursor to row,col */
void bios_move(int row,int col)
   {
   union REGS regs;

   regs.h.dh = row;
   regs.h.dl = col;
   regs.h.ah = 2;
   regs.h.bh = cur_page;
   int86(0x10,&regs,&regs);
   cur_row = row;
   cur_col = col;
   }


/* scroll active page up.
   use to clear boxes on screen */
void bios_scroll_up(
             int count,int sr,int sc,int er,int ec)
   {
   union REGS regs;

   regs.h.al = count;
   regs.h.ch = sr;
   regs.h.cl = sc;
   regs.h.dh = er;
   regs.h.dl = ec;
   regs.h.bh = cur_attr;
   regs.h.ah = 6;
   int86(0x10,&regs,&regs);
   }

/* scroll active page down.
   good for clearing entire screen */
void bios_scroll_dn(
             int count,int sr,int sc,int er,int ec)
   {
   union REGS regs;

   regs.h.al = count;
   regs.h.ch = sr;
   regs.h.cl = sc;
   regs.h.dh = er;
   regs.h.dl = ec;
   regs.h.bh = cur_attr;
   regs.h.ah = 7;
   int86(0x10,&regs,&regs);
   }

/* return current video state */
int bios_mode()
   {
   union REGS regs;

   regs.h.ah = 15;
   int86(0x10,&regs,&regs);
   cur_mode = regs.h.al;
   return(regs.h.al);
   }

/* return current cursor position
   into global values */
void bios_rc()
   {
   union REGS regs;

   regs.h.bh = cur_page;
   regs.h.ah = 3;
   int86(0x10,&regs,&regs);
   cur_row = regs.h.dh;
   cur_col = regs.h.dl;
   }


/* print char (attribute at r/c unchanged)
   at current cursor position */
void bios_putchar(char ch)
   {
   union REGS regs;

   regs.h.bh = cur_page;
   regs.x.cx =1;
   regs.h.al = ch;
   regs.h.ah = 0x0a;
   int86(0x10,&regs,&regs);
   }

/* print char & attribute at cusor position */
void bios_pchatt(char ch)
   {
   union REGS regs;

   regs.h.bh = cur_page;
   regs.h.bl = cur_attr;
   regs.x.cx = 1;
   regs.h.al = ch;
   regs.h.ah = 0x09;
   int86(0x10,&regs,&regs);
   }

/* this function replaces puts()
   (but no linefeed on end) 
   recognises LF,CR,BS and TAB 
   Use sprintf() with this to simulate printf()
   (need large buffer too) */
void bios_puts(char *str)
   {
   int i;
   bios_rc();
   while(*str)
      {
      if(*str == 10) /* do LF */
         {
         cur_col = 0;
         cur_row++;
         bios_move(cur_row,cur_col);
         str++;
         if(!*str)
            break;
         }
      if(*str == 8)   /* do BS */
         {
         if(cur_col == 0)
            cur_row--;
         else
            cur_col--;
         bios_move(cur_row,cur_col);
         str++;
         if(!*str)
            break;
         }
      if(*str == 13)   /* do CR */
         {
         cur_col = 0;
         bios_move(cur_row,cur_col);
         str++;
         if(!*str)
            break;
         }
      if(*str == 9)   /* do TAB */
         {
         i = (1+(cur_col / 8)) *8;
         while(cur_col < i)
            {
            bios_pchatt(' ');
            cur_col++;
            bios_move(cur_row,cur_col);
            }      
         str++;
         if(!*str)
            break;
         }

      bios_pchatt(*str);
      str++;
      cur_col++;
      bios_move(cur_row,cur_col);
      }
   }

/* returns the char under the cursor */
char bios_rdchar()
   {
   union REGS regs;

   regs.h.bh = cur_page;
   regs.h.ah = 8;
   int86(0x10,&regs,&regs);
   return((char)regs.h.al);
   }

/* returns char AND attribute under cursor */
int bios_rdchatt()
   {
   union REGS regs;

   regs.h.bh = cur_page;
   regs.h.ah = 8;
   int86(0x10,&regs,&regs);
   /* char is in AL, attribute is in AH */
   return((int)regs.x.ax);
   }

/* this function was written by Andrew Binstock.
   The text of the function
   and the supporting artical can be found in 
   the magazine: C Gazette, Summer 1989 issue.
   (v4 #1) p27.
   Only minor modifications have been made by me.
*/
/* test to determine type of
   video adapter in the system */
void vtest()
   {
   union REGS regs;
   unsigned char hold_a_byte;
   int i;

   /* identify VGA/MCGA/EGA */
   regs.h.ah = 0x1a;
   regs.h.al = 0;
   int86(0x10,&regs,&regs);

   /* true only for VGA, MCGA */
   if(regs.h.al == 0x1a)
      {
      if(regs.h.bl < 0x0a)
         vga =1;
      else
         mcga =1;
      color = 1;
      return;
      }

   /* next test for EGA */
   regs.h.ah = 0x12;
   regs.h.bl = 0x10;
   int86(0x10,&regs,&regs);
   if(regs.h.bl != 0x10)   /* it's an EGA */
      {
      ega = 1;

      /* EGA can be color or B+W */
      if(regs.h.bh ==0)
         color = 1;
      else
         b_w = 1;
      return;
      }

   /* not any of the above,
      so it must be CGA or MONO (or HERC) */

   /* test for MONO by looking at the current mode */
   bios_mode();

   /* go to MONO test. CGA can't use mode 7 */
   if(cur_mode != 7)
      {
      /* test CGA by looking at the CGA registers */
      outp(0x3d4,0x0f);      /* get value of reg 15 */
      hold_a_byte = inp(0x3d5);   /* save it */
      outp(0x3d5,0x63);  /* write an unlikely value */
      for(i=0;i< 100;i++)      /* wait a bit */
         ;
      if(inp(0x3d5) == 0x63)      /* read it back */
         {
         outp(0x3d5,hold_a_byte); /* restore value */
         cga = 1;
         color = 1;
         return;
         }
      else
         {
         outp(0x3d5,hold_a_byte);
         none = 1;
         color =1;
         return;
         }
      }
   /* not CGA, Test for MDA and HERC.
      Same test as CGA but to mono ports */
   outp(0x3b4,0xf);
   hold_a_byte = inp(0x3b5);
   outp(0x3b5,0x63);
   for(i=0;i<100;i++)
      ;
   if(inp(0x3b5) != 0x63)
      {
      outp(0x3b5,hold_a_byte);
      none = 1;
      b_w = 1;
      return;
      }
   outp(0x3b5,hold_a_byte);
   /* must be a MONO but is it a HERCULES????? */
   hold_a_byte = inp(0x3ba);   /* check status port */
   hold_a_byte &= 0x80;      /* isolate bit 7 */
   for(i=0; i< 1000; i++)
      {
      if(inp(0x3ba) & 0x80 != hold_a_byte)
         {
      /* if value changes, hercules is present */
         herc = 1;
         break;
         }
      }
   if(!herc)
      {
      /* must be just MDA */
      mono = 1;
      b_w = 1;
      return;
      }

   /* determine if the card is herc mono or color */
   hold_a_byte = inp(0x3ba);
   switch(hold_a_byte)
      {
      case   0x50:   color = 1;
            break;
      case   0x00:   b_w = 1;
            break;
   /*   case   0x10:    graphics plus card found */
      }
   }



