/* OS- and machine-dependent stuff for IBM-PC running MS-DOS
 * Copyright 1991 Phil Karn, KA9Q
 * Mods by KO4KS
 */
#ifdef MSDOS
#include "global.h"
#include <conio.h>
#include "commands.h"
#include <sys/stat.h>
#include <go32.h>
#include <process.h>
#include <fcntl.h>
#include <stdarg.h>
#include <bios.h>
#include <dpmi.h>
#include <signal.h>
#include "mbuf.h"
#include "proc.h"
#include "iface.h"
#include "session.h"
#include "tty.h"
#include "smtp.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: pc.c,v 1.25 1997/07/31 00:44:20 root Exp $";
#endif

#define FP_OFF( fp )( (unsigned )( fp ))
static unsigned Nfiles = 128;


extern void displayStatLine (int offset, int phase, int onlymarquee);
extern void setscreens (int back, int fore, int clr);
static int _go32_dpmi_unchain_protected_mode_interrupt_vector (uint irq, _go32_dpmi_seginfo * info);
static void eoi (void);
void DOSinit (void);
void DOSterm (void);
static void irq0 (void);
static int16 clockbits (void);
static void ctick (void);
static long bioscnt (void);

extern struct proc *Display;
extern char vgaDesired;
extern void volatile *lastkernelevent;
volatile int Tick;
volatile int32 Clock;

static unsigned char orgMode;

#define MAXSCR 20
typedef struct screenstorage {
	char *scrptr;
} screenstorage;

static screenstorage DOSscreens[MAXSCR];
static size_t scrsize;


static int saved_break;

struct int_tab {
	__dpmi_paddr old;	/* Previous handler at this vector */
	_go32_dpmi_seginfo new;	/* Current handler, with wrapper info */
	void (*func) (int);	/* Function to call on interrupt */
	int arg;		/* Arg to pass to interrupt function */
	int chain;		/* Is interrupt chained to old handler? */
} Int_tab [16];


static const char fkeysyntax1[] =
	"key   num    key    num    key    num    key    num   key    num\n"
	"f1     59    sf1     84    cf1     94    af1    104   pgup    73\n"
	"f2     60    sf2     85    cf2     95    af2    105   pgdn    81\n";
static const char fkeysyntax2[] =
	"f3     61    sf3     86    cf3     96    af3    106   home    71\n"
	"f4     62    sf4     87    cf4     97    af4    107   end     79\n"
	"f5     63    sf5     88    cf5     98    af5    108   arup    72\n";
static const char fkeysyntax3[] =
	"f6     64    sf6     89    cf6     99    af6    109   ardn    80\n"
	"f7     65    sf7     90    cf7    100    af7    110   ar l    75\n"
	"f8     66    sf8     91    cf8    101    af8    111   ar r    77\n";
static const char fkeysyntax4[] =
	"f9     67    sf9     92    cf9    102    af9    112   ins     82\n"
	"f10    68    sf10    93    cf10   103    af10   113   del     83\n"
	"f11   133    sf11   135    cf11   137    af11   139   stab    15\n";
static const char fkeysyntax5[] =
	"f12   134    sf12   136    cf12   138    af12   140   cprtsc 114\n"
	"cpgup 132    cpgdn  118    chome  119    cend   117\n"
	"usage: fkey <key number> [<value> | \"string\"]\n";

static const char fkeyoor[] = "fkey number out of range.\n";
static const char fkeynf[] = "fkey number not found\n";
static const char fkeynoval[] = "fkey %d has no assigned value.\n";
static const char noprompt[] = "\b\b\b\b\b\b\b\b        \b\b\b\b\b\b\b\b";
static const char somemore[] = "--More--";

static FILE *Rawterm;
static char Ttbuf[BUFSIZ];



/* Called at startup time to set up misc I/O related functions */
void
ioinit (int hinit)
{
union REGS inregs;
extern int _fmode;

	_fmode = O_BINARY;
	/* Get some memory on the heap so interrupt calls to malloc
	 * won't fail unnecessarily
	 */
	free (malloc ((uint) hinit));

	Rawterm = fopen ("con", "wb");
	setbuf (Rawterm, Ttbuf);

	/* Increase the size of the file table.
	 * Note: this causes MS-DOS
	 * to allocate a block of memory to hold the larger file table.
	 * By default, this happens right after our program, which means
	 * any further sbrk() calls from morecore (called from malloc)
	 * will fail. Hence there is now code in alloc.c that can call
	 * the MS-DOS allocmem() function to grab additional MS-DOS
	 * memory blocks that are not contiguous with the program and
	 * put them on the heap.
	 */
	inregs.h.ah = 0x67;
	inregs.x.bx = Nfiles;	/* Up to the base of the socket numbers */
	(void) intdos (&inregs, &inregs);

	saved_break = getcbrk ();
	(void) setcbrk (0);
	(void) signal (SIGINT, SIG_IGN);

	/* Chain protected mode keyboard interrupt */
	LOCK_FUNCTION(kbint);
	LOCK_FUNCTION(ksignal);
	LOCK_FUNCTION(istate);
	LOCK_VARIABLE(Ksig);
	LOCK_VARIABLE(lastkernelevent);
	(void) setvect (1, 1, (void (*)(int)) kbint, 0);

	/* Chain timer interrupt */
	LOCK_FUNCTION(ctick);
	LOCK_VARIABLE(Tick);
	LOCK_VARIABLE(Clock);
	(void) setvect (0, 1, (void (*)(int)) ctick, 0);

	LOCK_ARRAY(Int_tab);
	LOCK_FUNCTION(eoi);
	LOCK_FUNCTION(irq0);
}



/* Called just before exiting to restore console state */
void
iostop (void)
{
struct iface *ifp, *iftmp;
void (**fp) (void);

	setbuf (Rawterm, NULLCHAR);
	(void) setcbrk (saved_break);

	for (ifp = Ifaces; ifp != NULL; ifp = iftmp) {
		iftmp = ifp->next;
		(void) if_detach (ifp);
	}
	/* Call list of shutdown functions */
	for (fp = Shutdown; *fp != NULL; fp++)
		(**fp) ();

	/* Restore previous timer and keyboard interrupts */
	(void) freevect (0);
	(void) freevect (1);
}



#ifdef SHELL
/* Spawn subshell */
int
doshell (int argc, char *argv[], void *p)
{
char *command;
int ret;

	if (argc == 1 || !stricmp (argv[1], "/c")) {
		if ((command = getenv ("COMSPEC")) == NULLCHAR)
			command = "COMMAND.COM";
		ret = spawnvp (P_WAIT, command, argv);
	} else
		ret = spawnvp (P_WAIT, argv[1], (argv + 1));

	return ret;
}
#endif



#ifdef ALLCMD
/* Spawn mailer as subshell */
int
dobmail (int argc, char *argv[], void *p)
{
char *command;
int ret;

	if ((command = getenv ("MAILER")) == NULLCHAR)
		command = "BM.EXE";
	ret = spawnvp (P_WAIT, command, argv);

	smtptick (NULL);	/* tickle smtp to send any mail */
	return ret;
}
#endif /*ALLCMD*/



static int
kbchar (void)
{
int c;

	while ((c = kbraw ()) == 0)
		kwait (kbint);
	return (c);
}



/* Flush the raw terminal output */
void
rflush (void)
{
	(void) fflush (Rawterm);
}



#ifdef ALLCMD
struct funcstr {
	unsigned char fkey;
	char alloced;
	char *fvalue;
};

static struct funcstr fkeys[] =
{
	15, 0, NULLCHAR,	/* tab + shift */
	59, 1, NULLCHAR,	/* F1 */
	60, 1, NULLCHAR,	/* F2 */
	61, 1, NULLCHAR,	/* F3 */
	62, 1, NULLCHAR,	/* F4 */
	63, 0, NULLCHAR,	/* F5 */
	64, 0, NULLCHAR,	/* F6 */
	65, 0, NULLCHAR,	/* F7 */
	66, 0, NULLCHAR,	/* F8 */
	67, 0, NULLCHAR,	/* F9 */
	68, 0, NULLCHAR,	/* F10 */
	71, 1, NULLCHAR,	/* home*/
	72, 1, "\033[A",	/* up arrow*/
	73, 1, NULLCHAR,	/* pgup */
	75, 1, "\033[D",	/* left arrow */
	77, 1, "\033[C",	/* right arrow */
	79, 1, NULLCHAR,	/* end */
	80, 1, "\033[B",	/* down arrow */
	81, 1, NULLCHAR,	/* pgdn */
	82, 1, NULLCHAR,	/* ins */
	83, 1, NULLCHAR,	/* del */
	84, 0, NULLCHAR,	/* F1 + shift*/
	85, 0, NULLCHAR,	/* F2 + shift*/
	86, 0, NULLCHAR,	/* F3 + shift*/
	87, 0, NULLCHAR,	/* F4 + shift*/
	88, 0, NULLCHAR,	/* F5 + shift*/
	89, 0, NULLCHAR,	/* F6 + shift*/
	90, 0, NULLCHAR,	/* F7 + shift*/
	91, 0, NULLCHAR,	/* F8 + shift*/
	92, 0, NULLCHAR,	/* F9 + shift*/
	93, 0, NULLCHAR,	/* F10 + shift*/
	94, 0, NULLCHAR,	/* F1 + control*/
	95, 0, NULLCHAR,	/* F2 + control*/
	96, 0, NULLCHAR,	/* F3 + control*/
	97, 0, NULLCHAR,	/* F4 + control*/
	98, 0, NULLCHAR,	/* F5 + control*/
	99, 0, NULLCHAR,	/* F6 + control*/
	100, 0, NULLCHAR,	/* F7 + control*/
	101, 0, NULLCHAR,	/* F8 + control*/
	102, 0, NULLCHAR,	/* F9 + control*/
	103, 0, NULLCHAR,	/* F10 + control*/
	104, 0, NULLCHAR,	/* F1 + alt*/
	105, 0, NULLCHAR,	/* F2 + alt*/
	106, 0, NULLCHAR,	/* F3 + alt*/
	107, 0, NULLCHAR,	/* F4 + alt*/
	108, 0, NULLCHAR,	/* F5 + alt*/
	109, 0, NULLCHAR,	/* F6 + alt*/
	110, 0, NULLCHAR,	/* F7 + alt*/
	111, 0, NULLCHAR,	/* F8 + alt*/
	112, 0, NULLCHAR,	/* F9 + alt*/
	113, 0, NULLCHAR,	/* F10 + alt*/
	114, 0, NULLCHAR,	/* PrtSc + ctl*/
	117, 0, NULLCHAR,	/* end  + ctl */
	118, 0, NULLCHAR,	/* pgup + ctl */
	119, 0, NULLCHAR,	/* home + ctl */
	132, 0, NULLCHAR,	/* pgdn + ctl */
	133, 0, NULLCHAR,	/* F11 */
	134, 0, NULLCHAR,	/* F12 */
	135, 0, NULLCHAR,	/* F11 + shift */
	136, 0, NULLCHAR,	/* F12 + shift */
	137, 0, NULLCHAR,	/* F11 + control */
	138, 0, NULLCHAR,	/* F11 + control */
	139, 0, NULLCHAR,	/* F11 + alt */
	140, 0, NULLCHAR,	/* F11 + alt */
	0, 0, NULLCHAR
};

static char Leftover = 0;
static char *Nextkey = NULLCHAR;

#endif /*ALLCMD*/



/* Read characters from the keyboard, translating them to "real" ASCII.
 * If none are ready, block. The F-10 key is special; translate it to -2.
 */
#ifdef ALLCMD
int
kbread (void)
{
int c, i, j;

	if ((c = Leftover) != 0) {
		Leftover = *Nextkey++;
		return c;
	}

	c = kbchar ();
	if ((c & 0xff) == 0) {
		/* Lead-in to a special char */
		c = (c >> 8) & 0xff;	/*lint !e702 */
		switch (c) {
			case 3:		/* NULL (bizzare!) */
				c = 0;
				break;
			case 72:	/* UP ARROW key (used as previous history command) */
				if (Current == Command) {
					c = UPARROW;
					break;
				}
				goto all;
			case 80:	/* DOWN ARROW key (used as next history command) */
				if (Current == Command) {
					c = DNARROW;
					break;
				}
				goto all;
			case 68:	/* F-10 key (used as command-mode escape) */
				if (fkeys[10].fvalue == NULLCHAR) {
					c = -2;
					break;
				}
				goto all;
			case 82:	/* INSERT key (used as status line toggle) */
				if (fkeys[19].fvalue == NULLCHAR) {
					c = -105;
					break;
				}
				goto all;
			case 83:	/* DELETE key (used as flow mode toggle) */
				if (fkeys[20].fvalue == NULLCHAR) {
					c = -106;
					break;
				}
				goto all;
			case 71:	/* HOME key (used to kick this session) */
				if (fkeys[11].fvalue == NULLCHAR) {
					c = -107;
					break;
				}
				goto all;
			case 79:	/* END key (used to kill this session) */
				if (fkeys[16].fvalue == NULLCHAR) {
					c = -108;
					break;
				}
				goto all;
			case 73:	/* PGUP key (used to toggle to previous session) */
				if (fkeys[13].fvalue == NULLCHAR) {
					c = -109;
					break;
				}
				goto all;
			case 81:	/* PGDN key (used to toggle to next session) */
				if (fkeys[18].fvalue == NULLCHAR) {
					c = -110;
					break;
				}
				goto all;
			default:	/* Dunno what it is */
all:
				if (c > 58 && c < 68) {	/* F1 to F9 */
					if (fkeys[c - 58].fvalue == NULLCHAR) {
						c = (c - 56) * -1;	/* NO fkey defined - WG7J */
						break;
					}
				}
				for (i = 0; (j = fkeys[i].fkey) != 0; i++)
					if (j == c) {
						Nextkey = fkeys[i].fvalue;
						if (Nextkey == NULLCHAR) {
							c = -1;
							return c;
						}
						/* If first char of fvalue is '~'
						 * switch to command session.
						 */
						if ((c = *Nextkey++) == '~') {
							c = -2;
							Leftover = *Nextkey++;
						} else {
							if (c != 0)
								Leftover = *Nextkey++;
							else
								c = -1;
						}
						return c;
					}
				c = -1;
		}
	} else
		c = c & 0xff;
	return c;
}

int
dofkey (int argc, char *argv[], void *p)
{
int c, i, j;
char *q, *r;
char str[100];

	if (argc == 1) {
		tputs (fkeysyntax1);
		tputs (fkeysyntax2);
		tputs (fkeysyntax3);
		tputs (fkeysyntax4);
		tputs (fkeysyntax5);
		return 0;
	}
	c = atoi (argv[1]);
	if (c == 0 || c > 255) {
		tputs (fkeyoor);
		return 1;
	}
	for (j = 0; (i = fkeys[j].fkey) != 0; j++)
		if (i == c)
			break;

	if (i == 0) {
		tputs (fkeynf);
		return 1;
	}
	if (argc == 2) {
		q = fkeys[j].fvalue;
		r = str;
		if (q == NULLCHAR)
			tprintf (fkeynoval, c);
		else {
			while (*q)
				if (*q < ' ') {	/* This is ASCII dependent !! */
					*r++ = '^';
					*r++ = *q++ + 0x40;
				} else
					*r++ = *q++;
			*r = '\0';
			tprintf ("fkey = %s\n", str);
		}
		return 0;
	}
	if (argc == 3) {
		if (fkeys[j].alloced)
			fkeys[j].alloced = 0;
		else if (fkeys[j].fvalue != NULLCHAR)
			free (fkeys[j].fvalue);

		r = str;
		q = argv[2];
		while (*q) {
			if (*q == '^') {	/* ^ gives control char next */
				q++;
				if (*q == '^') {
					*r++ = *q++;	/* No, he wants a ^ */
				} else {
					*r++ = *q++ & 0x1f;
				}
			} else
				*r++ = *q++;
		}
		*r = '\0';
		fkeys[j].fvalue = strdup (str);
	}
	return 0;
}

#else /*ALLCMD*/

int
kbread (void)
{
int c;

	if ((c = kbchar ()) == 0) {
		/* Lead-in to a special char */
		c = kbchar ();
		if (Current == Command) {	/* Check for command recall */
			if (c == 72)	/* UP arrow */
				return UPARROW;
			if (c == 80)	/* DOWN arrow */
				return DNARROW;
		}
		switch (c) {
			case 3:/* NULL (bizzare!) */
				c = 0;
				break;
			case 68:	/* F-10 key (used as command-mode escape) */
				c = -2;
				break;
			case 83:	/* DEL key */
				c = 0x7f;
				break;
			default:	/* Dunno what it is */
				if (c > 58 && c < 68)	/* F1 to F9 */
					c = (c - 56) * -1;
				else
					c = -1;
		}
	}
	return c;
}
#endif /*ALLCMD*/



/* Disable hardware interrupt */
int
maskoff (uint irq)
{
	if (irq < 8)
		setbit (0x21, (char) (1 << irq));
	else if (irq < 16) {
		irq -= 8;
		setbit (0xa1, (char) (1 << irq));
	} else
		return -1;

	return 0;
}



/* Enable hardware interrupt */
int
maskon (uint irq)
{
	if (irq < 8)
		clrbit (0x21, 1 << irq);
	else if (irq < 16) {
		irq -= 8;
		clrbit (0xa1, 1 << irq);
	} else
		return -1;

	return 0;
}



/* Return 1 if specified interrupt is enabled, 0 if not, -1 if invalid */
int
getmask (unsigned irq)
{
	if (irq < 8)
		return (inportb (0x21) & (1 << irq)) ? 0 : 1;
	else if (irq < 16) {
		irq -= 8;
		return (inportb (0xa1) & (1 << irq)) ? 0 : 1;
	} else
		return -1;
}



/* Called from assembler stub linked to BIOS interrupt 1C, called on each
 * hardware clock tick. Signal a clock tick to the timer process.
 */
static void
ctick (void)
{
	Tick++;
	Clock++;		/* Keep system time */
	ksignal ((void *) &Tick, 1);
}

END_OF_FUNCTION(ctick)



/* Called from the timer process on every tick. NOTE! This function
 * can NOT be called at interrupt time because it calls the BIOS
 */
void
pctick (void)
{
long t;
static long oldt = 0;	/* Value of bioscnt() on last call */

	/* Check for day change */
	t = bioscnt ();
	if (t < oldt) 	/* Call the regular DOS time func to handle the midnight flag */
		(void) time (NULL);

}



/* Set bit(s) in I/O port */
void
setbit (uint port, uint8 bits)
{
	outportb ((int16) port, inportb ((int16) port) | bits);
}



/* Clear bit(s) in I/O port */
void
clrbit (uint port, uint8 bits)
{
	outportb ((int16) port, inportb ((int16) port) & ~bits);
}



/* Set or clear selected bits(s) in I/O port */
void
writebit (uint port, uint8 mask, int val)
{
uint8 x;

	x = inportb ((int16) port);
	if (val)
		x |= mask;
	else
		x &= ~mask;
	outportb ((int16) port, x);
}



static void stowit (size_t index, int save, unsigned char attr);
extern unsigned char SCREENwidth, SCREENlength;
extern int STATLINE;
extern struct session *ScreenOwner;	/* Session currently displayed */



void
newscreen (struct session *sp)
{
	if (sp != NULLSESSION) {
		sp->screen = callocw (1, sizeof (struct screen));
		sp->screen->statline = uchar(STATLINE);
	}
}



void
freescreen (struct session *sp)
{
	if (sp == NULLSESSION || sp->screen == NULLSCREEN)
		return;
	if ((sp->screen->save != NULLCHAR) && sp->screen->save != (char *) 1)
		free (sp->screen->save);
	free ((char *) sp->screen);
}



/* Save specified session screen and resume console screen */
void
swapscreen (struct session *old, struct session *new)
{
struct text_info tr;
unsigned char attr;

	if (old == new)
		return;		/* Nothing to do */

	(void) fflush (Rawterm);
	gettextinfo (&tr);
	if (old != NULLSESSION) {
		/* Save old screen */
		if (old->screen->save == NULLCHAR)
			old->screen->save = (char *) 1;
		old->screen->row = wherey ();
		old->screen->col = wherex ();
		old->screen->attr = tr.attribute;

		if (old->screen->save != NULLCHAR) {
			if (old->split) {
				window (1, 1, SCREENwidth, SCREENlength);
				tr.winbottom = SCREENlength;
			}
			stowit ((unsigned long) old->index, 1, 0);
			/* save it */
		}
	}
	if (new != NULLSESSION) {
		/* Load new screen */
		if (new->screen->save != NULLCHAR) {
			if (new->split)
				window (1, 1, SCREENwidth, SCREENlength - 2);
			else
				window (1, 1, SCREENwidth, SCREENlength);

			setscreens ((new->screen->attr >> 4) & 0x07, new->screen->attr & 0x0f, 0);
			stowit ((unsigned long) new->index, 0, new->screen->attr);
			/* retrieve it */
				new->screen->attr &= 0x7f;
			/* remove color change bit */

				/* if (STATLINE && (Current == Command)) */
				window (1, 1 + new->screen->statline, SCREENwidth, SCREENlength - (new->split * 2));
			gotoxy (new->screen->col, new->screen->row);
			textattr (new->screen->attr);
			new->screen->save = NULLCHAR;
		} else {
			window (1, 1, SCREENwidth, SCREENlength);
			clrscr ();	/* Start with a fresh slate */
			if (new->split) {
				new->tsavex = 1;
				new->tsavey = 1;
				new->bsavex = 1;
				new->bsavey = SCREENlength - 1;
				window (1, SCREENlength - 1, SCREENwidth, SCREENlength);
				gettextinfo (&tr);
				attr = ((tr.attribute & 0x0f) << 4) + ((tr.attribute & 0x70) >> 4);
				textattr (attr);
				clrscr ();
				cputs ("_\b");
				window (1, 1 + new->screen->statline, SCREENwidth, SCREENlength - 2 - new->screen->statline);
				textattr (tr.attribute);
			} else
				window (1, 1 + new->screen->statline, SCREENwidth, SCREENlength - new->screen->statline);
		}
	}
	ScreenOwner = new;
	displayStatLine (0, 1, 0);
	alert (Display, 1);	/* Wake him up */
}



void
display (int i, void *v1, void *v2)
{
int c;
struct session *sp;
int statsize;

	server_disconnect_io ();
	/* This is very tricky code. Because the value of "Current" can
	 * change any time we do a kwait, we have to be careful to detect
	 * any change and go back and start again.
	 */
	for ( ; ; ) {
		sp = Current;

		if (sp->morewait) {
			kwait (&sp->row);
			if (sp != Current || sp->row <= 0) {
				/* Current changed value, or the user
				 * hasn't really hit a key
				 */
				continue;
			}
			/* Erase the prompt */
			cprintf (noprompt);
			rflush ();
		}
		sp->morewait = 0;
		if ((c = rrecvchar (sp->output)) == EOF) {
			/* the alert() in swapscreen will cause this to
			 * return EOF when current changes
			 */
			kwait (NULL);	/* Prevent a nasty loop */
			continue;
		}
#ifdef SCREENSAVER
		sskick ();
#endif
		if (sp->split) {
			if (c == 0x0a) {
				cputs (Eol);
				clreol ();
			} else
				putch (c);
		} else
			cprintf ("%c", c);

		/* Fix by Ron Murray, vk6zjm */
		if (sp->record != NULLFILE)	/* Don't save CR if ascii mode */
			if (c != '\r' || sockmode (sp->output, -1) != SOCK_ASCII)
				putc (c, sp->record);

		statsize = 0;
		if (sp->screen->statline)
			statsize = ((sp == Command) ? getStatlines() : 1);

		if (sp->flowmode && c == '\n' && sp->row > 0 && --sp->row == statsize) {
			cprintf (somemore);
			sp->morewait = 1;
			sp->row = 0;
		}
	}
}



/* Return time since startup in milliseconds. Resolution is improved
 * below 55 ms (the clock tick interval) by reading back the instantaneous
 * 8254 counter value and combining it with the global clock tick counter.
 *
 * Reading the 8254 is a bit tricky since a tick could occur asynchronously
 * between the two reads. The tick counter is examined before and after the
 * hardware counter is read. If the tick counter changes, try again.
 * Note: the hardware counter counts down from 65536.
 */
int32
msclock (void)
{
int32 hi;
uint lo;
uint64 x;

	do {
		hi = rdclock ();
		lo = clockbits ();
	} while (hi != rdclock ());

	x = ((uint64) (uint32) hi << 16) - lo;
	return (int32) ((x * 11) / 13125);
}



/* Return clock in seconds */
int32
secclock (void)
{
int32 hi;
uint lo;
uint64 x;

	do {
		hi = rdclock ();
		lo = clockbits ();
	} while (hi != rdclock ());

	x = ((uint64) (uint32) hi << 16) - lo;
	return (int32) ((x * 11) / 13125000);
}



/* Directly read BIOS count of time ticks. This is used instead of
 * calling biostime(0,0L). The latter calls BIOS INT 1A, AH=0,
 * which resets the midnight overflow flag, losing days on the clock.
 */
static long
bioscnt (void)
{
long rval;
int i_state;

	i_state = disable ();
	dosmemget (0x46c, sizeof (rval), &rval);
	restore (i_state);
	return rval;
}



/* Atomic read-and-decrement operation.
 * Read the variable pointed to by p. If it is
 * non-zero, decrement it. Return the original value.
 */
int
arddec (volatile int *p)
{
int tmp;
int i_state;

	i_state = disable ();
	tmp = *p;
	if (tmp != 0)
		(*p)--;
	restore (i_state);
	return tmp;
}



void
restore (int state)
{
	(void) (state ? enable (): disable ());
}



int
istate (void)
{
long flags;

#ifndef _lint
	asm ( "pushfl\n\t"	/* We save the old ccr, which has interrupt mask bit. */
	      "popl %0\n\t":"=r" (flags));
#else
	flags = 0;
#endif
	return (flags >> 9) & 1;	/*lint !e704 */
}

END_OF_FUNCTION(istate)


/* This function is called by exit() in the GCC libc. We define
 * it here to supersede the one defined in libc's stdio
 */
void
_cleanup (void)
{

}



/* clockbits - Read low order bits of timer 0 (the TOD clock)
 * This works only for the 8254 chips used in ATs and 386s.
 *
 * The timer runs in mode 3 (square wave mode), counting down
 * by twos, twice for each cycle. So it is necessary to read back the
 * OUTPUT pin to see which half of the cycle we're in. I.e., the OUTPUT
 * pin forms the most significant bit of the count. Unfortunately,
 * the 8253 in the PC/XT lacks a command to read the OUTPUT pin...
 *
 * The PC's clock design is soooo brain damaged...
 */
static int16
clockbits (void)
{
int i_state;
unsigned int thestat, count;

	do {
		i_state = disable ();
		outportb (0x43, 0xc2);	/* latch timer 0 count and status for reading */
		thestat = inportb (0x40);	/* get status of timer 0 */
		count = inportb (0x40);	/* lsb of count */
		count |= inportb (0x40) << 8;	/* msb of count */
		restore (i_state);	/* no more chip references */
	} while (thestat & 0x40);	/* reread if NULL COUNT bit set */

	thestat = (thestat & 0x80) << 8;	/* Shift OUTPUT to msb of 16-bit word */
	count >>= 1;		/* count /= 2 */
	if (count == 0)
		return (int16) (thestat ^ 0x8000);	/* return complement of OUTPUT bit */
	else
		return (int16) (count | thestat);	/* Combine OUTPUT with counter */
}



void
kbint (void)
{
	ksignal (kbint, 1);
}

END_OF_FUNCTION(kbint)



void
giveup (void)
{
	__dpmi_yield ();
}



void
sysreset (void)
{
	/* nothing! */
}



int16
lcsum (int16 * buf, int16 cnt)
{
uint32 sum = 0;

	while (cnt-- != 0)
		sum += *buf++;
	while (sum > 65535)
		sum = (sum & 0xffff) + (sum >> 16);
	return ((sum >> 8) | (sum << 8)) & 0xffff;
}



/* What a crock. All this inelegance should be replaced with something
 * that figures out what interrupt is being serviced by reading the 8259.
 */
#define IRQFN(n) static void irq##n (void) { eoi (); (*Int_tab[n].func) (Int_tab[n].arg);}

IRQFN(0)
IRQFN(1)
IRQFN(2)
IRQFN(3)
IRQFN(4)
IRQFN(5)
IRQFN(6)
IRQFN(7)
IRQFN(8)
IRQFN(9)
IRQFN(10)
IRQFN(11)
IRQFN(12)
IRQFN(13)
IRQFN(14)
IRQFN(15)

END_OF_FUNCTION(irq0)


static void (*Vectab[16]) (void) = {
	irq0, irq1, irq2, irq3, irq4, irq5, irq6, irq7, irq8, irq9, irq10, irq11,
	irq12, irq13, irq14, irq15
};



int
setvect (uint irq, int chain, void (*func) (int), int arg)
{
struct int_tab *ip;
uint intno;
int i;

	if (irq > 15)
		return -1;	/* IRQ out of legal range */

	ip = &Int_tab[irq];
	if (ip->func != NULL)
		return -1;	/* Already in use */
	/* Convert irq to actual CPU interrupt vector */
	intno = (irq < 8) ? irq + 8 : 0x70 + irq - 8;

	(void) __dpmi_get_protected_mode_interrupt_vector ((int) intno, &ip->old);
	ip->func = func;
	ip->arg = arg;
	ip->new.pm_offset = (unsigned long) Vectab[irq];
	ip->new.pm_selector = (int16) _go32_my_cs ();
	ip->chain = chain;

	if (chain)
		return _go32_dpmi_chain_protected_mode_interrupt_vector ((int) intno, &ip->new);

	if ((i = _go32_dpmi_allocate_iret_wrapper (&ip->new)) != 0)
		return i;
	return _go32_dpmi_set_protected_mode_interrupt_vector ((int) intno, &ip->new);
}



int
freevect (uint irq)
{
struct int_tab *ip;
int i;

	if (irq > 15)
		return -1;	/* IRQ out of legal range */

	ip = &Int_tab[irq];
	ip->func = NULL;
	/* Convert irq to actual CPU interrupt vector */
	irq = (irq < 8) ? irq + 8 : 0x70 + irq - 8;
	if (ip->chain)
		return _go32_dpmi_unchain_protected_mode_interrupt_vector (irq, &ip->new);
	if ((i = __dpmi_set_protected_mode_interrupt_vector ((int) irq, &ip->old)) != 0)
		return i;
	return _go32_dpmi_free_iret_wrapper (&ip->new);
}



/* Re-arm 8259 interrupt controller(s)
 * Should be called just after taking an interrupt, instead of just
 * before returning. This is because the 8259 inputs are edge triggered, and
 * new interrupts arriving during an interrupt service routine might be missed.
 */
static void
eoi (void)
{
	/* read in-service register from secondary 8259 */
	outportb (0xa0, 0x0b);
	if (inportb (0xa0))
		outportb (0xa0, 0x20);	/* Send EOI to secondary 8259 */
	outportb (0x20, 0x20);	/* Send EOI to primary 8259 */
}


END_OF_FUNCTION(eoi)


char Hashtab[256];



int16
hash_ip (uint32 ipaddr)
{
int h;

	h = ((ipaddr >> 16) & 0xFFFF) ^ (ipaddr & 0xFFFF);
	return (int16) uchar(Hashtab[((h >> 8) & 0xFF) ^ (h & 0xFF)]);	/*lint !e702 */
}



/* Convert a pointer to a long integer */
long
ptol (void *p)
{
long x;

	x = (long) FP_OFF (p);
	x |= (long) ((uint32) p << 16);	/*lint !e703 */
	return x;
}



void
strrev (char *str)
{
char *cp, *save, *cp2;

	save = mallocw (strlen (str) + 1);
	cp2 = str;
	cp = &save[strlen (str)];
	*cp-- = 0;
	while (*cp2)
		*cp-- = *cp2++;
	strcpy (str, save);
	free (save);
}



long
dostounix (struct date *dp, struct time *tp)
{
static struct tm tm;
struct tm *tx;
time_t now;

	tm.tm_year = dp->da_year - 1900;
	tm.tm_mon = dp->da_mon - 1;
	tm.tm_mday = dp->da_day;
	tm.tm_hour = tp->ti_hour;
	tm.tm_min = tp->ti_min;
	tm.tm_sec = tp->ti_sec;
	/* This desperately needs to be fixed.  How? */
	(void) time (&now);
	tx = localtime (&now);
	tm.tm_isdst = tx->tm_isdst;
	return (long) mktime (&tm);
}



static void
stowit (size_t theindex, int save, unsigned char attr)
{
char *nerf;
register int k, l, split;
register char *cp;

	nerf = (char *) mallocw(scrsize);
	if (save)	{
		ScreenRetrieve(nerf);
		if (DOSscreens[theindex].scrptr)
			free (DOSscreens[theindex].scrptr);
		DOSscreens[theindex].scrptr = nerf;
	} else	{
		memcpy (nerf, DOSscreens[theindex].scrptr, scrsize);
		if (attr & 0x80)	{
			attr &= 0x7f;
			l = (int) (scrsize / 2);
			split = Sessions[theindex].split;
			if (split)
				l -= 160;	/* last 2 lines reversed */
			cp = (nerf + 1);
			for (k = 0; k < l; k++)	{
				*cp &= 8;	/* keep high video bit */
				*cp |= attr;
				cp += 2;
				}
			attr = ((attr & 0x0f) << 4) + ((attr & 0x70) >> 4);
			if (split)
				for (k = 0; k < 160; k++)	{
					*cp &= 8;	/* keep high video bit */
					*cp |= attr;
					cp += 2;
				}
		}
		ScreenUpdate(nerf);
		free (nerf);
	}
}



void DOSinit ()
{
struct text_info tr;

	gettextinfo(&tr);
	orgMode = tr.currmode;
	if (vgaDesired)		{
		textmode ((int) C4350);
		gettextinfo(&tr);
	}
	SCREENwidth = tr.screenwidth;
	SCREENlength = tr.screenheight;
	window (1,1,SCREENwidth,SCREENlength);
	scrsize = 2 * tr.screenheight * tr.screenwidth;
}

void DOSterm ()
{
int k;

	for (k = 0; k < MAXSCR; k++)
		if (DOSscreens[k].scrptr)
			free (DOSscreens[k].scrptr);
	textmode (orgMode);
}


#undef free

/* Written to extend gopint.c in djgpp library */
static int
_go32_dpmi_unchain_protected_mode_interrupt_vector (uint irq, _go32_dpmi_seginfo * info)
{
__dpmi_paddr v;
char *stack;
char *wrapper;

	(void) __dpmi_get_protected_mode_interrupt_vector ((int) irq, &v);
	/* Sanity check: does the vector point into our program? A bug in gdb
	 * keeps us from hooking the keyboard interrupt when we run under its
	 * control. This test catches it.
	 */
	if (v.selector != _go32_my_cs ())
		return -1;
	wrapper = (char *) v.offset32;
	/* Extract previous vector from the wrapper chainback area */
	v.offset32 = *(unsigned long *) (wrapper + 0x5b);
	v.selector = *(unsigned short *) (wrapper + 0x5f);
	/* Extract stack base from address of _call_count variable in wrapper */
	stack = (char *) (*(long *) (wrapper + 0x0F) - 8);
#define	STACK_WAS_MALLOCED	(1 << 0)

	if (*(long *) stack & STACK_WAS_MALLOCED)
		free (stack);
	free (wrapper);
	(void) __dpmi_set_protected_mode_interrupt_vector ((int) irq, &v);
	return 0;
}


#endif /* MSDOS */
