/* ------------------------------------------------------------------------ */
/*                                 sstvid.c                                 */
/*                                                                          */
/*                         low-level video functions                        */
/*                                                                          */
/*      CopyRight (C) 1991,1992  Steven Lutrov.   All rights reserved.      */
/* ------------------------------------------------------------------------ */
#include <dos.h>
#include <bios.h>
#include <stdio.h>
#include <stdarg.h>
#include <alloc.h>
#include <string.h>

#include "sstvid.h"

/* ------------------------------------------------------------------------ */
/*                        video BIOS (0x10) functions                       */
/* ------------------------------------------------------------------------ */
#define SETCURSORTYPE        0x01
#define SETCURSOR            0x02
#define READCURSOR           0x03
#define READATTRCHAR         0x08
#define WRITEATTRCHAR        0x09
#define HIDECURSOR           0x20

#define ZEROFLAG             0x40    /* zero flag mask  */
#define MAXCSAVES              50    /* maximun cursor saves  */
#define MAXSSAVES              12    /* maximun screen saves  */

#define VIDSEGREG (unsigned)((7 == VIDMODE) ? 0xb000 : 0xb800 )
#define SCRSIZE   (SCREENWIDTH * SCREENHEIGHT * 2)

/* ------------------------------------------------------------------------ */
/*                          local prototypes                                */
/* ------------------------------------------------------------------------ */
static void near getcursor (void);
static void scan350        (void);
static void scan400        (void);
static void getvmode       (void);
static unsigned char far *currentpos (void);
static unsigned char far *scrptr (int x, int y);

/* ------------------------------------------------------------------------ */
/*                           local variables                                */
/* ------------------------------------------------------------------------ */
static int near cursorpos[MAXCSAVES];    /* cursor position buffer */
static int near cursorshape[MAXCSAVES];  /* cursor shape buffer */
static int cs = 0;                       /* cursor save counter */
static char far *scrarray[MAXSSAVES];
static int ss = -1;                      /* screen save counter */

unsigned int vmode;                      /* video mode */
unsigned int vpage;                      /* video page number */

/* ------------------------------------------------------------------------ */
/*              clear the screen in a given attribute by scrolling          */
/* ------------------------------------------------------------------------ */
void vcls(unsigned char attr)

{
    _AH = 0x06;                       /* scroll window up */
    _AL = 0;                          /* clear entire window */
    _BH = attr;
    _CX = 0;                          /* upper left = (0,0) */
    _DH = SCREENHEIGHT-1;
    _DL = SCREENWIDTH-1;
    geninterrupt(VIDEO);
}

/* ------------------------------------------------------------------------ */
/*                            clear a given line                            */
/* ------------------------------------------------------------------------ */
void vclearline(unsigned char row, unsigned char attr)

{
    _AH = 0x06;                       /* scroll window up */
    _AL = 1;                          /* clear 1 line */
    _BH = attr;
    _CH = row;                        /* upper left = (row,0) */
    _CL = 0;
    _DH = row;                        /* lower right = (row,79) */
    _DL = SCREENWIDTH-1;
    geninterrupt(VIDEO);
}

/* ------------------------------------------------------------------------ */
/*                           position the cursor                            */
/* ------------------------------------------------------------------------ */
void vsetcur(int x, int y)
{
    getvmode();
    _DX = ((y << 8) & 0xFF00) + x;
    _AH = SETCURSOR;
    _BX = vpage;
    geninterrupt(VIDEO);
}

/* ------------------------------------------------------------------------ */
/*                   return the current cursor position                     */
/* ------------------------------------------------------------------------ */
void vgetcur(int *x, int *y)
{
    getcursor();
    *x = _DL;
    *y = _DH;
}

/* ------------------------------------------------------------------------ */
/*                         use BIOS set cursor type                         */
/* ------------------------------------------------------------------------ */
void vsetcurtype(unsigned t)
{
    getvmode();
    _AH = SETCURSORTYPE;
    _BX = vpage;
    _CX = t;
    geninterrupt(VIDEO);
}


/* ------------------------------------------------------------------------ */
/*                            test for scroll lock                          */
/* ------------------------------------------------------------------------ */
int Kscrolllock()
{
    _AX = 0x0200;
    geninterrupt(VIDEO);
    return _AL & 0x10;
}


void (*helpfunc)(void);  /* function prototype */
int helpkey = 0;
int helping = 0;

/* ------------------------------------------------------------------------ */
/*        get a keyboard character  and hooks help if help is required      */
/* ------------------------------------------------------------------------ */
unsigned int kgetch(void)
{
    unsigned int c;

	while (1)	{
        _AH = 0x01;
        geninterrupt(KEYBRD);
		if (_FLAGS & 0x40)	{
		     geninterrupt(KBUSYLOOP);
			continue;
		}
        _AH = 0x00;
        geninterrupt(KEYBRD);
	c = (_AL > 0 && _AL < 127) ? _AL : _AX;
	if (c == helpkey && helpfunc)   {
			if (!helping)	{
				helping = 1;
				(*helpfunc)();
				helping = 0;
				continue;
			}
		}
		break;
	}
	return c;
}

/* ------------------------------------------------------------------------ */
/*   get a keyboard char (extended kbd) and hooks help if help is required  */
/* ------------------------------------------------------------------------ */
unsigned int kgetche(void)
{
    unsigned int c;

	while (1)	{
        _AH = 0x11;
	geninterrupt(KEYBRD);
        if (_FLAGS & ZEROFLAG)  {
		     geninterrupt(KBUSYLOOP);
			continue;
		}
        _AH = 0x10;
        geninterrupt(KEYBRD);
	c = (_AL > 0 && _AL < 127) ? _AL : _AX;
	if (c == helpkey && helpfunc)   {
			if (!helping)	{
				helping = 1;
				(*helpfunc)();
				helping = 0;
				continue;
			}
		}
		break;
	}
	return c;
}

/* ------------------------------------------------------------------------ */
/*                            Test for keystroke                            */
/* ------------------------------------------------------------------------ */
int kkeyhit(void)
{
    _AH = 0x11;
    geninterrupt(KEYBRD);
    return (_FLAGS & ZEROFLAG) == 0;
}

/* ------------------------------------------------------------------------ */
/*                  save the current cursor configuration                   */
/* ------------------------------------------------------------------------ */
void vpushcur(void)
{
    if (cs < MAXCSAVES)    {
	getcursor();
	cursorshape[cs] = _CX;
	cursorpos[cs] = _DX;
	cs++;
    }
}

/* ------------------------------------------------------------------------ */
/*                 restore the saved cursor configuration                   */
/* ------------------------------------------------------------------------ */
void vpopcur(void)
{
    if (cs)    {
      --cs;
      getvmode();
      _DX = cursorpos[cs];
      _AH = SETCURSOR;
      _BX = vpage;
      geninterrupt(VIDEO);
      vsetcurtype(cursorshape[cs]);
    }
}

/* ------------------------------------------------------------------------ */
/*                         make a normal cursor                             */
/* ------------------------------------------------------------------------ */
void vnormalcur(void)
{
    vsetcurtype(0x0607);
}

/* ------------------------------------------------------------------------ */
/*                           hide the cursor                                */
/* ------------------------------------------------------------------------ */
void vhidecur(void)
{
    getcursor();
    _CH |= HIDECURSOR;
    _AH = SETCURSORTYPE;
    geninterrupt(VIDEO);
}

/* ------------------------------------------------------------------------ */
/*                             unhide the cursor                            */
/* ------------------------------------------------------------------------ */
void vshowcur(void)
{
    getcursor();
    _CH &= ~HIDECURSOR;
    _AH = SETCURSORTYPE;
    geninterrupt(VIDEO);
}


/* ------------------------------------------------------------------------ */
/*                               test for EGA                               */
/* ------------------------------------------------------------------------ */
int visega(void)
{
    if (visvga())
	return 0;
    _AH = 0x12;
    _BL = 0x10;
    geninterrupt(VIDEO);
    return (_BL != 0x10);
}

/* ------------------------------------------------------------------------ */
/*                               test for VGA                               */
/* ------------------------------------------------------------------------ */
int visvga(void)
{
    _AX = 0x1a00;
    geninterrupt(VIDEO);
    return _AL == 0x1A && _BL > 6;
}

/* ------------------------------------------------------------------------ */
/*                            set 25 line mode                              */
/* ------------------------------------------------------------------------ */
void vset25(void)
{
    if (visvga())
	scan400();
    _AX = (visvga()  ? 0x1114  : 0x1111);
    _BX = 0;
    geninterrupt(VIDEO);
}

/* ------------------------------------------------------------------------ */
/*                           set 43 line mode                               */
/* ------------------------------------------------------------------------ */
void vset43(void)
{
    if (visvga())
	scan350();
    _AX = 0x1112;
    _BX = 0;
    geninterrupt(VIDEO);
}

/* ------------------------------------------------------------------------ */
/*                            set 50 line mode                              */
/* ------------------------------------------------------------------------ */
void vset50(void)
{
    if (visvga())
	scan400();
    _AX = 0x1112;
    _BX = 0;
    geninterrupt(VIDEO);
}



/* ------------------------------------------------------------------------ */
/*                      Toggle intensity/blinking bit                       */
/* ------------------------------------------------------------------------ */
void vblinkbit(int f)       /* NOTE - only valid for EGA/VGA systems */
{
   _BL = f;
   _BH = 0;
   _AX = 0x1003;
   geninterrupt(VIDEO);
}


int far *kbd_status = (int far *) 0x00000417L;

/* ------------------------------------------------------------------------ */
/*          return the state of all the keys by peeking into bios           */
/* ------------------------------------------------------------------------ */
int far *kstate(void)

{
   return (kbd_status);
}


/* ------------------------------------------------------------------------ */
/*             put a char and attribute at the given coordinates            */
/* ------------------------------------------------------------------------ */
void vputch(int x, int y, unsigned char a, register unsigned char c)
{
    register unsigned char far *p;

    p = scrptr(x,y);
    *p++ = c;
    *p++ = a;
}

/* ------------------------------------------------------------------------ */
/*                  put a string at the given coordinates                   */
/* ------------------------------------------------------------------------ */
void vputs(int x, int y, unsigned char a, register char *s )
{
	register unsigned char far *p;

	p = scrptr(x,y);
	while (*s) {
		 *p++ = *s++;
		 *p++ = a;
	}
}

/* ------------------------------------------------------------------------ */
/*             put a formatted string at the given coordinates              */
/* ------------------------------------------------------------------------ */
void vputf( int x, int y, unsigned char a, char *fmt, ... )
{
	register unsigned char far *p;
	char s[120];
	register char *cp = s;
	va_list argptr;

	va_start( argptr, fmt );
	vsprintf( s, fmt, argptr );
	va_end( argptr );

	p = scrptr(x,y);
	while (*cp) {
		 *p++ = *cp++;
		 *p++ = a;
	}
}

/* ------------------------------------------------------------------------ */
/*                    put a string centered on a given line                 */
/* ------------------------------------------------------------------------ */
void vputsc( int y, unsigned char a, char *s )
{
	vputs ( ( 80 - strlen(s) ) / 2, y, a, s );
}

/* ------------------------------------------------------------------------ */
/*      put a formatted and centered string at the given coordinates        */
/* ------------------------------------------------------------------------ */
void vputfc(int y, unsigned char a, char *fmt, ... )
{
	char s[120];
	va_list ap;

	va_start( ap, fmt );
	vsprintf( s, fmt, ap );
	va_end( ap );

	vputs( ( 80 - strlen(s) ) / 2, y, a, s );
}

/* ------------------------------------------------------------------------ */
/*               return the attribute at the given coordinates              */
/* ------------------------------------------------------------------------ */
int vgeta(int x, int y)
{
     unsigned char far *p;

     p = scrptr(x,y);
     return(*(++p));
}

/* ------------------------------------------------------------------------ */
/*               return the character at the given coordinates              */
/* ------------------------------------------------------------------------ */
int vgetch(int x, int y)
{
     unsigned char far *p;

     p = scrptr(x,y);
     return(*p++);
}

/* ------------------------------------------------------------------------ */
/*        return the character and attribute at the given coordinates       */
/* ------------------------------------------------------------------------ */
int vget(int x, int y)
{
     int far *p;

     p = (int far*) scrptr(x,y);
     return(*p++);
}

/* ------------------------------------------------------------------------ */
/*          return the attribute at the current cursor coordinates          */
/* ------------------------------------------------------------------------ */
int vgetac(void)
{
     unsigned char far *scptr = currentpos();
     return( *(++scptr));
}

/* ------------------------------------------------------------------------ */
/*              returns the char at the current cursor coordinates          */
/* ------------------------------------------------------------------------ */
int vgetchc(void)
{
     unsigned char far *scptr = currentpos();
     return (*scptr);
}

/* ------------------------------------------------------------------------ */
/*        fills an area of the screen with a given char and attribute       */
/* ------------------------------------------------------------------------ */
void vfill(int x, int y, int yy, int xx, int c, int a)

{
   char s[80];

   memset(s,c,80);
   s[xx] = '\00';
   while (yy)
      vputs(x,y-1+(yy--),a,s);
}


/* ------------------------------------------------------------------------ */
/*              pushes the screen contents to the far stack                 */
/* ------------------------------------------------------------------------ */
int vpushscreen(void)
{
     if ( ++ss > 15 ) {
	     ss--;
	     return(-1);
     }
     if ( (scrarray[ss] = (char far *)farmalloc(SCRSIZE) ) == NULL )
		return(-2);
     movedata(VIDSEGREG, 0, FP_SEG(scrarray[ss]),
	      FP_OFF(scrarray[ss]), 4000);
     return(0);
}

/* ------------------------------------------------------------------------ */
/*                   pops the screen contents of the far stack              */
/* ------------------------------------------------------------------------ */
void vpopscreen(void)
{
	movedata(FP_SEG(scrarray[ss]), FP_OFF(scrarray[ss]),
		 VIDSEGREG, 0, SCRSIZE);
	farfree(scrarray[ss]);
	ss--;
}
/* ------------------------------------------------------------------------ */
/*                        get cursor shape and position                     */
/* ------------------------------------------------------------------------ */
static void near getcursor(void)

{
    getvmode();
    _AH = READCURSOR;
    _BX = vpage;
    geninterrupt(VIDEO);
}

/* ------------------------------------------------------------------------ */
/*                    interrupt to produce 350 scan lines                   */
/* ------------------------------------------------------------------------ */
static void scan350(void)

{
    _AX = 0x1201;
    _BL = 0x30;
    geninterrupt(VIDEO);
}

/* ------------------------------------------------------------------------ */
/*                    interrupt to produce 400 scan lines                   */
/* ------------------------------------------------------------------------ */
static void scan400(void)
{
    _AX = 0x1202;
    _BL = 0x30;
    geninterrupt(VIDEO);
}

/* ------------------------------------------------------------------------ */
/*                  get the video mode and page from BIOS                   */
/* ------------------------------------------------------------------------ */
static void getvmode(void)

{
    _AH = 15;
    geninterrupt(VIDEO);
    vmode = _AL;
    vpage = _BX;
    vpage &= 0xFF00;
    vmode &= 0x7F;
}

/* ------------------------------------------------------------------------ */
/*                     return the current curosr position                   */
/* ------------------------------------------------------------------------ */
static unsigned char far *currentpos (void)
{
  _AH = 0x03;
  _BX = 0x00;
  geninterrupt(VIDEO);
  return (scrptr( _DL+1, _DH+1 ) );
}

/* ------------------------------------------------------------------------ */
/*           returns the video address for the  given coordinates           */
/* ------------------------------------------------------------------------ */
static unsigned char far *scrptr (int x, int y)

{
   return (VIDSEG + (y * (SCREENWIDTH*2)) + (x << 1) );
}

/* ------------------------------------------------------------------------ */
/*                   puts the current date out to the screen                */
/*      1 = dd-mm-yy   	2 = dd mmm yyyy   3 = mm-dd-yy    4 = yy-mm-dd      */
/* ------------------------------------------------------------------------ */
void vputdate(int x, int y, int attr, int fmt )
{
  char *MonthArray[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
			   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  struct date dt;

  getdate(&dt);
  switch(fmt) {
     case 1:
	     vputf(x, y, attr,"%2d-%02d-%02d", dt.da_day, dt.da_mon,
		   dt.da_year % 100 );
	     break;
     case 2:
	     vputf(x, y, attr,"%2d %3s %04d", dt.da_day,
		   MonthArray[dt.da_mon-1], dt.da_year );
	     break;
     case 3:
	     vputf(x, y, attr,"%02d-%2d-%02d", dt.da_mon, dt.da_day,
		   dt.da_year % 100 );
	     break;
     case 4:
	     vputf(x, y, attr,"%02d-%02d-%2d", dt.da_year % 100 , dt.da_mon,
		   dt.da_day);
	     break;
	}
}

/* ------------------------------------------------------------------------ */
/*                     puts the current time onto the screen                */
/*                 1 = hh:mm:ss (military)   2 = hh:mm	(military)          */
/*		   3 = hh:mm:ss AM/PM        4 = hh:mm	AM/PM               */
/* ------------------------------------------------------------------------ */
void vputtime(int x, int y, int attr, int fmt)
{
   struct time tm;
   char c;

   gettime(&tm);
   switch(fmt) {
	case 1:
		vputf(x, y, attr,"%2d:%02d:%02d",
			tm.ti_hour, tm.ti_min, tm.ti_sec );
		break;
	case 2:
		vputf(x, y, attr,"%2d:%02d",tm.ti_hour, tm.ti_min );
		break;
	case 3:
		c = 'A';
		if ( tm.ti_hour > 11 ) {
			c = 'P';
			tm.ti_hour -= 12;
		}
		if ( tm.ti_hour == 0 )  tm.ti_hour = 12;
		vputf(x, y, attr,"%2d:%02d:%02d %cM",
			tm.ti_hour, tm.ti_min, tm.ti_sec, c );
		break;
	case 4:
		c = 'A';
		if ( tm.ti_hour > 11 ) {
			c = 'P';
			tm.ti_hour -= 12;
		}
		if ( tm.ti_hour == 0 )  tm.ti_hour = 12;
		vputf(x, y, attr,"%2d:%02d %cM",
			tm.ti_hour, tm.ti_min, c );
		break;
   }
}

/* ------------------------------------------------------------------------ */
/*                           makes a beep noise                             */
/* ------------------------------------------------------------------------ */
void vbeep(int m)
{
   if (m)    {
     sound(2000);
     delay(100);
   }
   else {
    sound(500);
    delay(250);
  }
 nosound();
}

