/*   SCCS Id: @(#)vidtxt.c   3.2     94/04/04                       */
/*   Copyright (c) NetHack PC Development Team 1993                 */
/*   NetHack may be freely redistributed.  See license for details. */
/*                                                                  */
/*
 * vidtxt.c - Textmode video hardware support (BIOS and DJGPPFAST)
 *                                                  
 *Edit History:
 *     Initial Creation              M. Allison      93/04/04
 *     Add djgpp support             K. Smolkowski   93/04/26
 *     Add runtime monoadapter check M. Allison      93/05/09
 */

#define VIDEO_TEXT

#include "hack.h"
#include "pcvideo.h"
#include "wintty.h"

#include <dos.h>
#include <ctype.h>

#if defined(_MSC_VER)
# if _MSC_VER >= 700
#pragma warning(disable:4018)	/* signed/unsigned mismatch */
#pragma warning(disable:4127)	/* conditional expression is constant */
#pragma warning(disable:4131)	/* old style declarator */
#pragma warning(disable:4305)	/* prevents complaints with MK_FP */
#pragma warning(disable:4309)	/* initializing */
#pragma warning(disable:4759)	/* prevents complaints with MK_FP */
# endif
#endif

/* void FDECL(txt_xputc,(char, int)); /* write out character (and attribute) */

STATIC_VAR char buf[BUFSZ];
extern int attrib_text_normal;	/* text mode normal attribute */
extern int attrib_gr_normal;	/* graphics mode normal attribute */
extern int attrib_text_intense;	/* text mode intense attribute */
extern int attrib_gr_intense;	/* graphics mode intense attribute */

#ifdef OVLB

void
txt_get_scr_size()
{
	union REGS regs;

	if (!iflags.BIOS) {
		CO = 80;
		LI = 24;
		return;
	}

# ifdef PC9800
	regs.h.ah = SENSEMODE;
	(void) int86(CRT_BIOS, &regs, &regs);

	CO = (regs.h.al & 0x02) ? 40 : 80;
	LI = (regs.h.al & 0x01) ? 20 : 25;
# else 
	regs.x.ax = FONTINFO;
	regs.x.bx = 0;			/* current ROM BIOS font */
	regs.h.dl = 24;			/* default row count */
					/* in case no EGA/MCGA/VGA */
	(void) int86(VIDEO_BIOS, &regs, &regs); /* Get Font Information */

	/* MDA/CGA/PCjr ignore INT 10h, Function 11h, but since we
	 * cleverly loaded up DL with the default, everything's fine.
	 *
	 * Otherwise, DL now contains rows - 1.  Also, CX contains the
	 * points (bytes per character) and ES:BP points to the font
	 * table.  -3.
	 */

	regs.h.ah = GETMODE;
	(void) int86(VIDEO_BIOS, &regs, &regs); /* Get Video Mode */

	/* This goes back all the way to the original PC.  Completely
	 * safe.  AH contains # of columns, AL contains display mode,
	 * and BH contains the active display page.
	 */

	LI = regs.h.dl + 1;
	CO = regs.h.ah;
# endif /* PC9800 */
}
#endif /*OVLB*/

/*
 * --------------------------------------------------------------
 * The rest of this file is only compiled if NO_TERMS is defined.
 * --------------------------------------------------------------
 */

#ifdef NO_TERMS
/* #include "wintty.h" */

# ifdef SCREEN_DJGPPFAST
#include <pc.h>
#include <unistd.h>
# endif

void FDECL(txt_gotoxy, (int,int));

# if defined(SCREEN_BIOS) && !defined(PC9800)
void FDECL(txt_get_cursor, (int *, int *));
# endif

# ifdef SCREEN_DJGPPFAST
#define txt_get_cursor(x,y) ScreenGetCursor(y,x)
# endif

extern int  g_attribute;	/* Current attribute to use */
extern int  monoflag;		/* 0 = not monochrome, else monochrome */

# ifdef OVLB
void
txt_backsp()
{
#  ifdef PC9800
	union REGS regs;

	regs.h.dl = 0x01;		  /* one column */
	regs.h.ah = CURSOR_LEFT;
	regs.h.cl = DIRECT_CON_IO;

	int86(DOS_EXT_FUNC, &regs, &regs);

	txt_xputc(' ',attrib_text_normal);

	regs.h.dl = 0x01;		  /* one column */
	regs.h.ah = CURSOR_LEFT;
	regs.h.cl = DIRECT_CON_IO;

	(void) int86(DOS_EXT_FUNC, &regs, &regs);
#  else
	int col,row;

	txt_get_cursor(&col, &row);
	if (col > 0) col = col-1;
	txt_gotoxy(col,row);
	txt_xputc(' ',attrib_text_normal);
	txt_gotoxy(col,row);
#  endif
}

void
txt_nhbell()
{
        union REGS regs;

        if (flags.silent) return;
        regs.h.dl = 0x07;			/* bell */
        regs.h.ah = 0x02; 			/* Character Output function */
        (void) int86(DOSCALL, &regs, &regs);
}
# endif /* OVLB */

# ifdef OVL0
void
txt_clear_screen()
/* djgpp provides ScreenClear(), but in version 1.09 it is broken
 * so for now we just use the BIOS Routines
 */
{
	union REGS regs;
#  ifdef PC9800
	regs.h.dl = attr98[attrib_text_normal];
	regs.h.ah = SETATT;
	regs.h.cl = DIRECT_CON_IO;

	(void) int86(DOS_EXT_FUNC, &regs, &regs);

	regs.h.dl = 0x02;		/* clear whole screen */
	regs.h.ah = SCREEN_CLEAR;
	regs.h.cl = DIRECT_CON_IO;

	(void) int86(DOS_EXT_FUNC, &regs, &regs);
#  else
	regs.h.dl = (char)(CO - 1);	/* columns */
	regs.h.dh = (char)(LI - 1);	/* rows */
	regs.x.cx = 0;			/* CL,CH = x,y of upper left */
	regs.x.ax = 0;	
	regs.x.bx = 0;
	regs.h.bh = (char)attrib_text_normal;
	regs.h.ah = (char)SCROLL;
					  	/* DL,DH = x,y of lower rt */
	(void) int86(VIDEO_BIOS, &regs, &regs); /* Scroll or init window   */
	txt_gotoxy(0,0);
#  endif
}

void
txt_cl_end(col,row)	/* clear to end of line */
int col,row;
{
	union REGS regs;
#  ifndef PC9800
	int count;
#  endif

#  ifdef PC9800
	regs.h.dl = attr98[attrib_text_normal];
	regs.h.ah = SETATT;
	regs.h.cl = DIRECT_CON_IO;

	(void) int86(DOS_EXT_FUNC, &regs, &regs);

	regs.h.dl = 0x00;		/* clear to end of line */
	regs.h.ah = LINE_CLEAR;
	regs.h.cl = DIRECT_CON_IO;

	(void) int86(DOS_EXT_FUNC, &regs, &regs);
#  else
	count = CO - col;
	txt_gotoxy(col,row);
	regs.h.ah = PUTCHARATT;	     /* write attribute & character */	
	regs.h.al = ' ';	     /* character */
	regs.h.bh = 0;		     /* display page */
				     /* BL = attribute */
	regs.h.bl = (char)attrib_text_normal;
	regs.x.cx = count;
	if (count != 0)
		(void) int86(VIDEO_BIOS, &regs, &regs); /* write attribute 
							   & character */
#  endif
}

void
txt_cl_eos()	/* clear to end of screen */
{
	union REGS regs;
#  ifndef PC9800
	int col,row;
#  endif

#  ifdef PC9800
	regs.h.dl = attr98[attrib_text_normal];
	regs.h.ah = SETATT;
	regs.h.cl = DIRECT_CON_IO;

	(void) int86(DOS_EXT_FUNC, &regs, &regs);

	regs.h.dl = 0x00;		/* clear to end of screen */
	regs.h.ah = SCREEN_CLEAR;
	regs.h.cl = DIRECT_CON_IO;

	(void) int86(DOS_EXT_FUNC, &regs, &regs);
#  else
	txt_get_cursor(&col, &row);
	txt_cl_end(col,row);			/* clear to end of line */
	txt_gotoxy(0,(row < (LI-1) ? row+1 : (LI-1)));		
	regs.h.dl = (char) (CO-1);	/* X  of lower right */
	regs.h.dh = (char) (LI-1);	/* Y  of lower right */
	regs.h.cl = 0;			/* X  of upper left */
  				  	/* Y (row)  of upper left */
	regs.h.ch = (char) (row < (LI-1) ? row+1 :(LI-1));
	regs.x.cx = 0; 
	regs.x.ax = 0;
	regs.x.bx = 0;
	regs.h.bh = (char)attrib_text_normal;
	regs.h.ah = SCROLL;
	(void) int86(VIDEO_BIOS, &regs, &regs); /* Scroll or initialize window */
# endif
}
# endif /* OVL0 */

# ifdef OVLB
void
txt_startup(wid, hgt)
    int *wid, *hgt;
{
	txt_get_scr_size();
	*wid = CO;
	*hgt = LI;

	attrib_gr_normal    = attrib_text_normal;
	attrib_gr_intense   = attrib_text_intense;
	g_attribute         = attrib_text_normal;		/* Give it a starting value */
}
# endif /* OVLB */

/*
 * Screen output routines (these are heavily used).
 *
 * These are the 3 routines used to place information on the screen
 * in the NO_TERMS PC tty port of NetHack.  These are the routines
 * that get called by routines in other NetHack source files (such
 * as those in win/tty).
 *
 * txt_xputs - Writes a c null terminated string at the current location.
 *         Depending on compile options, this could just be a series
 *         of repeated calls to xputc() for each character.
 * txt_xputc - Writes a single character at the current location. Since
 *         various places in the code assume that control characters
 *         can be used to control, we are forced to interpret some of
 *         the more common ones, in order to keep things looking correct.
 *
 * NOTES:
 *         wintty.h uses macros to redefine common output functions
 *         such as puts, putc, putchar, so that they get steered into
 *         either xputs (for strings) or xputc (for single characters).
 *         References to puts, putc, and putchar in other source files
 *         (that include wintty.h) are actually using these routines.
 */

# ifdef OVL0
/*JP*/
void
txt_xputc2(ch, ch2, attr)	/* write out 2byte code character */
     char ch;
     char ch2;
     int attr;
{
#  ifdef PC9800
  unsigned char uc1, uc2;

  uc1 = *((unsigned char *)&ch);
  uc2 = *((unsigned char *)&ch2);

  union REGS regs;

  regs.h.dl = attr98[attr];
  regs.h.ah = SETATT;
  regs.h.cl = DIRECT_CON_IO;

  (void) int86(DOS_EXT_FUNC, &regs, &regs);

  regs.h.dl = uc1;
  regs.h.ah = PUTCHAR;
  regs.h.cl = DIRECT_CON_IO;

  (void) int86(DOS_EXT_FUNC, &regs, &regs);

  regs.h.dl = uc2;
  regs.h.ah = PUTCHAR;
  regs.h.cl = DIRECT_CON_IO;

  (void) int86(DOS_EXT_FUNC, &regs, &regs);
#  else
#   ifdef SCREEN_BIOS
  union REGS regs;
#   endif
  int col,row;
  unsigned char uc1, uc2;

  uc1 = *((unsigned char *)&ch);
  uc2 = *((unsigned char *)&ch2);

  txt_get_cursor(&col,&row);
/*
  ++col;
  txt_gotoxy(col,row);
*/

#   ifdef SCREEN_DJGPPFAST
  ScreenPutChar((int)uc1,attr,col,row);
  ScreenPutChar((int)uc2,attr,col,row);
#   endif
#   ifdef SCREEN_BIOS
  regs.h.ah = PUTCHARATT;  /* write att & character */
  regs.h.al = uc1;	 /* character             */
  regs.h.bh = 0;           /* display page          */
  regs.h.bl = (char)attr;  /* BL = attribute        */
  regs.x.cx = 1;	   /* one character         */
  (void) int86(VIDEO_BIOS, &regs, &regs);
  if (col < (CO - 1)) ++col;
  txt_gotoxy(col,row);

  regs.h.ah = PUTCHARATT;  /* write att & character */
  regs.h.al = uc2;	 /* character             */
  regs.h.bh = 0;           /* display page          */
  regs.h.bl = (char)attr;  /* BL = attribute        */
  regs.x.cx = 1;	   /* one character         */
  (void) int86(VIDEO_BIOS, &regs, &regs);
#   endif
  if (col < (CO - 1 )) ++col;
  txt_gotoxy(col,row);
#  endif /* PC9800 */
}
void
txt_xputc(ch,attr)	/* write out character (and attribute) */
char ch;
int attr;
{
#  ifdef PC9800
	union REGS regs;

	regs.h.dl = attr98[attr];
	regs.h.ah = SETATT;
	regs.h.cl = DIRECT_CON_IO;

	(void) int86(DOS_EXT_FUNC, &regs, &regs);

	if (ch == '\n') {
		regs.h.dl = '\r';
		regs.h.ah = PUTCHAR;
		regs.h.cl = DIRECT_CON_IO;

		(void) int86(DOS_EXT_FUNC, &regs, &regs);
	}
	regs.h.dl = ch;
	regs.h.ah = PUTCHAR;
	regs.h.cl = DIRECT_CON_IO;

	(void) int86(DOS_EXT_FUNC, &regs, &regs);
#  else
#   ifdef SCREEN_BIOS
	union REGS regs;
#   endif
	int col,row;
	txt_get_cursor(&col,&row);
	switch(ch) {
	    case '\n':	
			break;
	    default:
#   ifdef SCREEN_DJGPPFAST
			ScreenPutChar((int)ch,attr,col,row);
#   endif
#   ifdef SCREEN_BIOS
			regs.h.ah = PUTCHARATT;  /* write att & character */
			regs.h.al = ch;		 /* character             */
			regs.h.bh = 0;           /* display page          */
			regs.h.bl = (char)attr;  /* BL = attribute        */
			regs.x.cx = 1;	   /* one character         */
			(void) int86(VIDEO_BIOS, &regs, &regs);
#   endif
			if (col < (CO -1 )) ++col;
			break;
	} /* end switch */
	txt_gotoxy(col,row);
#  endif /* PC9800 */
}

void
txt_xputs(s,col,row)
const char *s;
int col,row;
{
  unsigned int buf[2];

  if(s){
    while(*s){
      txt_gotoxy(col, row);
      jbuffer(*s, buf, NULL, xputc, xputc2);
      ++col;
      ++s;
    }
  }
}
# endif /* OVL0 */

/*
 * This marks the end of the general screen output routines that are
 * called from other places in NetHack.
 * ---------------------------------------------------------------------
 */

/*
 * Cursor location manipulation, and location information fetching
 * routines.
 * These include:
 *
 * txt_get_cursor(x,y)  - Returns the current location of the cursor.  In
 *                    some implementations this is implemented as a
 *                    function (BIOS), and in others it is a macro
 *                    (DJGPPFAST).
 *
 * txt_gotoxy(x,y)      - Moves the cursor on screen to the specified x and
 *                    y location.  This routine moves the location where
 *                    screen writes will occur next, it does not change
 *                    the location of the player on the NetHack level.
 */
 
# ifdef OVL0
#  if defined(SCREEN_BIOS) && !defined(PC9800)
/*
 * This is implemented as a macro under DJGPPFAST.
 */
void
txt_get_cursor(x,y)	/* get cursor position */
int *x, *y;
{
	union REGS regs;

	regs.x.dx = 0;
	regs.h.ah = GETCURPOS;		 /* get cursor position */
	regs.x.cx = 0;
	regs.x.bx = 0;	
	(void) int86(VIDEO_BIOS, &regs, &regs); /* Get Cursor Position */
	*x = regs.h.dl;
	*y = regs.h.dh;
}
#  endif /* SCREEN_BIOS && !PC9800 */

void
txt_gotoxy(x,y)
int x,y;
{
#  ifdef SCREEN_BIOS
	union REGS regs;

#   ifdef PC9800
	regs.h.dh = (char)y;		/* row */
	regs.h.dl = (char)x;		/* column */
	regs.h.ah = SETCURPOS;
	regs.h.cl = DIRECT_CON_IO;
	(void) int86(DOS_EXT_FUNC, &regs, &regs); /* Set Cursor Position */
#   else
	regs.h.ah = SETCURPOS;
	regs.h.bh = 0;			/* display page */
	regs.h.dh = (char)y;		/* row */
	regs.h.dl = (char)x;		/* column */
	(void) int86(VIDEO_BIOS, &regs, &regs); /* Set Cursor Position */
#   endif
#  endif
#  if defined(SCREEN_DJGPPFAST)
	ScreenSetCursor(y,x);
#  endif
	/* The above, too, goes back all the way to the original PC.  If
	 * we ever get so fancy as to swap display pages (i doubt it),
	 * then we'll need to set BH appropriately.  This function
	 * returns nothing.  -3.
	 */
}
# endif /* OVL0 */

/*
 * This marks the end of the cursor manipulation/information routines.
 * -------------------------------------------------------------------
 */ 

# ifdef OVLB
#  ifdef MONO_CHECK
int txt_monoadapt_check()
{
	union REGS regs;

	regs.h.al = 0;
	regs.h.ah = GETMODE;			/* get video mode */
	(void) int86(VIDEO_BIOS, &regs, &regs);
	return (regs.h.al == 7) ? 1 : 0;	/* 7 means monochrome mode */
}
#  endif /* MONO_CHECK */
# endif /* OVLB */
#endif /* NO_TERMS  */

/* vidtxt.c */
