Low level interrupt support routines for PCDOS DSZ.EXE, GSZ.EXE and DSZ.COM.
Specific to 808x processors and 8250 and related serial chips.

#define SDEBUGxx
/*
>>:y5.c 4-18-94
 * Copyright 1984, 1994 Omen Technology Inc All Rights Reserved
 */

#include "yamsys.h"		/* Installation specific stuff */
#include "yam.h"
#include "fnkey.h"

#ifdef M_I86SM
#undef flushmo
#undef flushmoc
#endif

#ifdef SDEBUG
extern short Sdebug;
#endif

extern char Mdobuf, Mdoend, *Mocq, *Mocdq;
extern char Txoff;	/* Character to send to do XOFF or 0 */
extern char Txon;	/* Character to send to do XON or 0 */
extern char Mcrgo;	/* Modem Control Register in GO state */
extern char Mcrstop;	/* Modem Control register in STOP state */
extern char mcrstopped;	/* Flag indicated Mcrstop is active */
extern char Xoff;	/* Non 0 if output is stopped by flow control */

int Porttype = 0;	/* 0: 8250/16450 no fifo  1: 16550A  2: 82510
			   3: ST16C550 on chip flow control
			   4: TI 16C550C on chip flow control  */
extern char ocfc;	/* on chip flow control value used by yp.s */
int Fifopsval=0xFFFF;	/* Controls #bytes to load to UART on interrupt */
int Swhs = TRUE;	/* Enable sw flow control handshake (YAM sending) */

int T1pause = (242*8);	/* Sets 0.1 second timeout for readline */
extern char Hsflag;	/* Bits in MSR to sample for HW handshake */
int Fifotrig = 0107;	/* Trigger level = 4 */
unsigned bauddivisor = 5760;

int Dwyport;		/* Com port Doorway is using */
int Dwyredir;		/* Was Doorway active when I started? */

/* Set on chip flow control for ST16C650 */
pt3cfc(t)
{
	register c, d;

	if (Porttype != 3)
		return;
	d = 0;
	if (t) {
		if (Hsflag == 020)
			d |= 0200;
		if ((Mcrstop & 2) == 0)
			d |= 0100;
	}
	spl7();
	c = inb(Dport+3);	/* save old LCR */
	outb(Dport+3, 0xFF);	/* Enable Enhanced Feature Reg */
	outb(Dport+2, ocfc = d);
	outb(Dport+3, c);	/* restore old LCR */
	spl0();
}

/*
 * setbaud(nbaud) If legal rate, set modem registers and Baudrate
 *
 *  For IBM 8250 family interrupt driven modem ports
 */
setbaud(nbaud)
long nbaud;
{
	register c;
	long bcmd;
	extern int Fifops;	/* Preset for txFIFO counter */
#ifndef DSZ
	char bstr[20];
	extern char *Speedstr;
#endif

	Lastcar = TRUE;
	if(nbaud < 44)
		return ERROR;
	outb(Dport+1, 0);	/* Disable port interrupts */
	purgemo();		/* Clear output buffer */
	bcmd = bauddivisor * 10L;
	bcmd /= ((nbaud & ~1)/2L);	/* this must be done unsigned! */
	if (bcmd == 0)
		return ERROR;
	outb(Dport+7, 0151);	/* write test pattern to scratchpad register */
	outb(Dport+3, 0203);    /* enable divisor latch */
	outb(Dport+7, 0223);	/* write to ST16C650 Xoff2 register?? */
	outb(Dport, bcmd);
	outb(Dport+1, (bcmd>>8));
	Mcrgo |= 3;  Mcrstop |= 1;

	/*
	 * Set data bits, 1 stop bit (2 if odd or 110 baud rate)
	 * then hardware parity, disable divisor latch
	 */
#ifndef M_I86SM
	switch (Parity) {
	case EVEN8:
		c = 013; break;
	case ODD8:
		c = 033; break;
	default:
		c = 003; break;
	}
#else
	c = 003;
#endif
	if ((nbaud&1) || nbaud == 110)
		c |= 4;
	outb(Dport+3, c);
	/*
	 *  Check the port by attempting to recover data we just wrote
	 */
	if ((inb(Dport+3) ^ c) & 0377) {
		lprintf("\7Port COM%d (%X) defective\7\n", Comport, Dport);
		++Errcnt;
	}
	inb(Sport); inb(Dport); inb(Dport+2);
	spl7();
	/*
 	 * Check for NSC 16550A (not 16550) or Intel 82510, enable rx FIFO
	 *  Above 4800 bps set 16550A rx interrupt threshold to 4 otherwise 1
	 */
	Fifops = 0;
	if (Porttype < 0)
		goto wd16550;
	c = inb(Dport+4);		/* To detect Intel 82501 bank switch */
	outb(Dport+2, Fifotrig);
	/* Check for 16550A which uniquely sets bits 6 and 7 of IIR high */
	if ((inb(Dport+2) & 0300) == 0300) {
		if (inb(Dport+7) == 0151)	/* Check for test pattern */
			Porttype = 3;
		else {
			/* Check for TI 16C550C auto flow control */
			outb(Dport+4, Mcrgo|040);
			Porttype = 1;
			if (inb(Dport+4) & 040)
				Porttype = 4;
		}
		Fifops = Fifopsval;
	}
	else if (Porttype != 1) {
		/* Not 16550A, Try to enable 82510 RX FIFO */
		outb(Dport+2, 0100);		/* 82510 BANK: Select bank 2 */
		if (c != inb(Dport+4)) {	/* Did 82510 switch banks? */
			Porttype = 2;		/* Yes, OK to turn on FIFO */
			outb(Dport+4, 010);	/* IMD Reg: Enable RX FIFO */
		}
wd16550:
		outb(Dport+2, 0);	/* Bank 0, or kill 16550!A FIFO */
	}
	/*
	 * Turn on dtr, rts and output 2, which enables irq to pc-bus
	 */
	outb(Dport+4, Mcrgo);
	outb(Dport+1, 017);	/* Enable port interrupts */
	Baudrate = nbaud;
	spl0();
	purgeline();
#ifndef DSZ
	sprintf(bstr, "%ld", nbaud);
	setstr(&Speedstr, bstr);
#endif
	pt3cfc(1);
	return OK;
}


/*
 * fetch the baudrate from the modem port
 *  illegal baudrate is set to 1200
 *  interrupts are enabled and gated
 */
readbaud()
{
	register char dp3;
	register unsigned div;

	dp3 = inb(Dport+3);
	outb(Dport+3, 0200|dp3);
	div = inb(Dport) | (inb(Dport+1)<<8);	/* fetch divisor */
	outb(Dport+3, dp3);		/* Restore uart modes */
	Baudrate = bauddivisor * 10L;	/* Be sure this remains unsigned! */
	if (div == 0)
		Baudrate = 0;
	else {
		Baudrate /= div; Baudrate <<= 1;
	}
	/* turn on output 2 for interrupt gating to bus */
	outb(Dport+4, 013);
	/* enable rx data available interrupt */
	Mstat = inb(Dport + 6);
	outb(Dport+1, 017);
	if ((dp3 & 04) && Baudrate != 110)
		++Baudrate;
#if 0
	switch (dp3 & 073) {
	case 033:
		Parity = EVEN8; break;
	case 013:
		Parity = ODD8; break;
	default:
		Parity = SEND8; break;
	}
#else
	Parity = SEND8;
#endif
}

#ifdef DSZ
getoff()
{
	pt3cfc(0);
	outb(Dport+4, 010);
}
#else
/*
 * Bye hangs up the line and then resets for another call
 *  To make sure it hangs up, it sends a long break first
 *  in case DTR isn't connected (also hangs up more smoothly).
 */
bye()
{
	if (!isdtr()) {
		setbaud(Baudrate); dsleep(2);
	}
	getoff();
	dsleep(8);
	setbaud(Baudrate);
	dsleep(2);
	purgeline();
}

getoff()
{
	pt3cfc(0);
	Lastcar = FALSE;
	if (Twxmode) {
		sendline(EOT); flushmo();  dsleep(2);
		outb(Dport+3, 0100);	/* Send long space */
		dsleep(10);
		outb(Dport+3, 003);	/* Back to marking */
	}
	Mcrgo &= ~3;  Mcrstop &= ~3;
	outb(Dport+4, mcrstopped = Mcrstop);  dsleep(3);
	purgeline(); cdo();
	checkclog();
}

/* onhook goes off line for good */
onhook()
{
	getoff();
	llhmargin();
	if (Verbose >= 0)
		lprintf("On Hook\n");
}
#endif	/* DSZ */

/*
 * Send a 200 ms spacing signal
 */
int Breaklen = 2;
sendbrk()
{
	register dp3;

	dp3 = inb(Dport+3);
	outb(Dport+3, dp3|0100);	/* Set line to spacing */
	dsleep(Breaklen);
	outb(Dport+3, dp3);		/* Return to marking */
	dsleep(1);			/* Insure marking follows */
	mcrstopped = 1;
#ifndef M_I86SM
	learnbrk();
#endif
}

/* Test for carrier dropout (transition to no carrier) */
cdo()
{
	static lastcdo;

	if (carrier())
		return lastcdo=FALSE;
	if (lastcdo)
		return FALSE;
	return lastcdo=TRUE;
}

#define CARRIER (Mstat&0200)
carrier()
{
	register n;

	if (CARRIER)
		return (Lastcar=TRUE);
	for (n=15; --n >=0; )
		checko();
		if (CARRIER) {
			return (Lastcar=TRUE);
		}
	if (Lastcar && Waitcdo) {
		for (n=5; --n >=0; ) {
			dsleep(4);
			checko();
			if (CARRIER) {
				dsleep(20);  return (Lastcar=TRUE);
			}
		}
	}
#ifndef DSZ
	Xmodem = FALSE;
#endif
	Lastcar = FALSE; return FALSE;
}


/*
 * readline2 (timeout)
 * Time-out is in deciseconds (1/10th's)
 * For top speed, character ready is checked in many places.
 * returns TIMEOUT if kbd character is ready.
 *  jumped to by assem lang readline() if character not ready
 */
readline2(decisecs)
{
	register t;
	long then;
	extern Smartwait;
	extern unsigned Iobufsize;	/* Use setvbuf if this is non 0 */

	then = petime() + 10 * decisecs;
	do {
#if 1
		if (Rfile && Kcount < 0) {
#ifdef SDEBUG
		if (Lstat )
			Sdebug |= 0x10;
#endif
			fflush(fout);
/*
			commit_file(fileno(fout));  
			disk_reset();
*/
			wacksmtv();
			if (Iobufsize)
				Kcount = Iobufsize -1;
			else
				Kcount = (Smartcntk * 1024) -1;
			csleep(Smartwait);
#ifdef GSZ
			if (Gfdisplay)
				gwstate = grun; gszupdate(wstated);
#endif

#ifdef SDEBUG
			if (Lstat )
				Sdebug |= 0x20;
#endif
		}
#endif
		if (Ctrlbrk)
			return TIMEOUT;
		if ((
#ifndef DSZ
		  Hostmode ||
#endif
		  !Supcdo) && !carrier()) {
			return RCDO;
		}
		if (t = rdlkbcheck())
			return t;
		if(miready())
			return michar();
		dvpause();
	} while (petime() <= then);
	return TIMEOUT;
}


/*
 * Alternate version of readline that times out in 3 character times
 *  used to verify that no more characters are coming.  XON/XOFF ignored.
 */
readl0()
{
	register c, t;

	if ((c = Baudrate >> 8) ==0)
		c = 1;
	for(t = T1pause/c;  --t;  ) {
		if(miready()) {
			if (Lstat)
				return ERROR;
			if ((c = michar() & 0177) == XON || c == XOFF)
				continue;
			return c;
		}
	}
	if (Lstat)
		return ERROR;
	return TIMEOUT;
}

/* Set Handshake according to second letter of argument */
handshake(s)
char *s;
{
	int tiafc;

	tiafc = Porttype == 4 ? 040 : 0;
	Swhs = FALSE;
#ifndef DSZ
	if (Overlapio < 0)
		Overlapio = 1;
	Xoffflg = 0;
#endif
	if (Hsflag & 0200)
		Supcdo = FALSE;
	outb(Dport+4, mcrstopped = Mcrstop = 013);
	Mcrgo &= ~tiafc;
	nestrcpy(Handsname, s, HANDSLEN);
	switch (Handsname[1]) {
	case 'r':	/* grim */
		Overlapio = -1;
	case 'o':	/* both */
		Swhs = TRUE; 
		Mcrstop = 011 | tiafc;
		Mcrgo |= tiafc;
		Hsflag = 020;  break;
	case 'l':	/* slow */
		Overlapio = -1;
	case 'n':	/* on */
		Mcrstop = 011 | tiafc;
		Mcrgo |= tiafc;
		Hsflag = 020;  break;
	case 't':	/* cts */
		Mcrgo |= tiafc;
		Hsflag = 020;  break;
	case 's':	/* dsr */
		Hsflag = 040;  break;
	case 'c':	/* dcd */
		Hsflag = 0200;  Supcdo = TRUE;  break;
	case 'w':	/* sw */
		Swhs = TRUE; 
		Hsflag = 0;  break;
	case 'f':	/* off */
		Hsflag = 0;  break;
#ifndef DSZ
	case 'x':	/* Bit mapped hack */
		outb(Dport+4, Mcrstop = Mcrgo = 037 & (010 | Handsname[0]));
		Hsflag = 0;  break;
#endif
	default:
		usage(5);
	}
	pt3cfc(1); settxon();  clearxoff();
	outb(Dport+4, Mcrgo);
}

clearxoff()
{
	Xoff = 0;
	mcrstopped = 1;
	starto();
}
setxoff()
{
	Xoff = 1;
}

/* Set xon and xoff charaxters to send according to current parity */
settxon()
{
	static char fpn[] = { 021, 021, 0021, 0221, 0221, 0021, 0, 0, 021, 021};
	static char fpo[] = { 023, 023, 0223, 0023, 0223, 0023, 0, 0, 023, 023};

	if (Swhs) {
		Txoff = fpo[Parity];  Txon = fpn[Parity];
	} else {
		Txoff = Txon = 0;
	}
}

#ifndef DSZ
mocts()
{
	return ( !Xoff);
}

isdtr()
{
	return (Mcrgo & 01);
}

/* Return non zero if Data Set Ready is Active */
testdsr()
{
	return (inb(Dport+6) & 040);
}

overrun()
{
	return (Lstat & 03);
}

moready()
{
#ifdef M_I86SM
	return (Mocq == Mocdq);
#else
	register char *s;

	s = Mocq;  ++s;
	if (s == &Mdoend)
		s = &Mdobuf;
	if (s == Mocdq)
		return 0;
	return 1;
#endif
}

moempty()
{
	return (Mocq == Mocdq);
}
#endif

/* Clear out the input buffer */
purgeline()
{
	while(miready())
		michar();
	Lstat = 0;
}

/*
 * change modem port to n n=1...8
 *  N==0 means disable
 *  For standard IBM serial port
 */

struct portdata {
	unsigned ioadr;
	unsigned vecnum;
	unsigned divisor;
};
struct portdata ports[] = {
	0x3F8, 4, 5760,	/* com0 Not Used */
	0x3F8, 4, 5760,	/* COM1 3F8 IRQ4 */
	0x2F8, 3, 5760,	/* COM2 2F8 IRQ3 */
	0x3E8, 4, 5760,	/* COM3 3E8 IRQ4 */
	0x2E8, 3, 5760,	/* COM4 2E8 IRQ3 */
	0x2B8, 3, 5760,	/* COM5 2B8 IRQ3 */
	0x2B8, 2, 5760,	/* COM6 2B8 IRQ2 */
	0x3E8, 5, 5760,	/* COM7 3E8 IRQ5 Alternate for "COM3" */
	0x2E8, 5, 5760,	/* COM8 2E8 IRQ5 Alternate for "COM4" */
	0x3F8, 4, 5760 	/* COM9 3F8 IRQ4 (dummy - for "xport" command) */
#define NPORTS 10
};

int Intctl = 0x20;
int Intmsk = 0x21;
int oldmask = 0;	/* previous 8259 mask setting */
int oldpmask;
int oldier;
int oldout2;
int oldlcr;


chngport(n)
register unsigned n;		/* so -1 etc. fails */
{
	static didcom34 = 0;	/* Flag indicated com3 or com4 was addressed */
	static oldxmcr = 0;	/* Value for Siamese Twin IRQ vector */
	static oldxout2 = 0;	/* Old port register value from twinned port */
	extern long Oldisr;
	char *getenv();
	register mask;
#ifdef DSZ
	extern cmdFgiven;		/* The DSZ F command has been given */
#else
	char porstr[10];
	extern char *Dportstr;
#endif
	

	if (n > NPORTS)
		return ERROR;
#ifndef DSZ
	setstr(&Dportstr, "");
	if (oldier && oldout2 && (0377 & ~oldmask & ~oldpmask)
	  && !getenv("HOTPORT"))
		printf("Warning: Old dport=%x ier=%x out2=%x mask=%x isr=%08lX\n",
		 Dport, oldier, oldout2, oldmask, Oldisr);
#endif
	spl7();
	if (Dport) {	/* Disable old serial port */
		if (oldxmcr) {
			outb(oldxmcr, oldxout2); oldxmcr = 0;
		}
#ifdef DSZ
		if (!cmdFgiven)
#endif
			outb(Dport+2, 0);
		outb(Dport+1, 0);
		outb(Dport+3, oldlcr);
		mask = 1 << (7 & ports[Comport].vecnum);
		outb(Intmsk, ((mask|inb(Intmsk)) & (~mask | oldmask)));
		outb(Dport+4, oldout2|(inb(Dport+4)&0367));
		setvec(0); spl7();	/* Bloody DOS does an spl0! */
		outb(Dport+1, oldier);
		inb(Dport); inb(Sport); inb(Dport); inb(Dport+2); inb(Dport+2);
		oldier = Dport = 0;
	}
	if (n == 0) {
		spl0(); return OK;
	}
	if (ports[n].vecnum > 7) {
		Intctl = 0xA0;
		setvec(0x68 + ports[n].vecnum);
	} else {
		Intctl = 0x20;
		setvec(8 + ports[n].vecnum);
	}
	spl7();
	Intmsk = Intctl + 1;
	Sport = (Dport=ports[n].ioadr)+5;
	Iport = Dport + 2;
	oldier = inb(Dport+1);
	outb(Dport+1, 0);
	bauddivisor = ports[n].divisor;
	oldout2 = inb(Dport+4) & 010;
	oldlcr = inb(Dport+3);
	oldmask = inb(Intmsk);
	outb(Intmsk, oldmask & (oldpmask = ~(1<<(7 & ports[n].vecnum))));
	/* Fetch baudrate and enable interrupts and DTR on selected port */
	Porttype = 0;
	readbaud();
	if (Baudrate < 50)
		setbaud(Effbaud = 300L);
	else
		setbaud(Effbaud = Baudrate);
	if (Comport != n) {
		if (inb(Dport+6) & 020)
			handshake(Baudrate > 38400 ? "on" : "both");
		else
			handshake("sw");
	}
	Comport = n;
	if (n >= 3 && n <= 4)
		didcom34 = TRUE;
	if (didcom34 && n <= 4) {
		oldxout2 = inb(oldxmcr = (Dport^0x10) + 4);
		outb(oldxmcr, (oldxout2 & ~010));
	}
	spl0();
#ifdef DSZ
	if (Errcnt) {
		printf("Wait...\7"); dsleep(100);
	}
#else
	sprintf(porstr, "com%d", n);
	setstr(&Dportstr, porstr);
#endif
	return OK;
}

/*
 * Change port to port N (1 <= N <= 19) or or port 9 with ioadr,vector_number
 */
aport(s)
char *s;
{
	int vecnum;
	unsigned ioadr, divsr;

	divsr = 0;
	switch (sscanf(s, "%x,%d,%u", &ioadr, &vecnum, &divsr)) {
	case 3: case 2:
		if ((vecnum > 15) || (vecnum < 2))
			usage(4);
		ports[9].divisor = divsr? divsr : 5760;
		ports[9].vecnum = vecnum;
		ports[9].ioadr = ioadr;
		return (chngport(9));
	case 1:
		if (ioadr >= 0x10)
			ioadr -= 6;
		if (ioadr <= 0)
			return ERROR;
		return (chngport(ioadr));
	default:
		break;
	}
	return ERROR;
}


userinit()
{
	register c;
#ifndef DSZ
	extern Gottdam;			/* What to do on usage error */
#endif

	userin2();
	c = dwredir(0);
	if (c & 0x8000) {
		if (c & 0x80) {
		} else {
			Dwyport = c & 0xff;
			Dwyredir = TRUE;
#ifndef DSZ
			Gottdam = 20000;
#endif
		}
	}
}
userexit()
{
	chngport(0);
	if (Dwyredir)
		dwredir(1);
}

/*
 *  Drain all output, wait for completion (may be some bytes in the UART)
 */
flushmo()
{
	while (Mocq != Mocdq) {
		flushok();
		dvpause();
	}
}

/*
 * Chuck for wedged output flow control condition, start output
 */
flushok()
{
	long future;
	extern Xtimint;
	extern char Morun;
	extern Mustsayterm;

	starto();
	if ( !Xoff)
		return;
#ifdef GSZ
	if (Gfdisplay) {
		gwstate = gflow; gszupdate(wstated);
	} else
#endif
	{
#ifdef DSZ
		lprintf((Xoff&1) ? "XOFF":"FLOW");
#else
		savpstat();
		if (Xoff & 1)
			pstat("		\b\33[mPause (XOFF)");
		else
			pstat("		\b\33[mPause (HW)");
#endif
	}
	for (future=time()+Xtimint; !Xtimint || time()<future; ) {
		if ( !Xoff) {
anyway:
#ifdef GSZ
			if (Gfdisplay) {
				gwstate = grun; gszupdate(wstated);
			} else
#endif
			{
#ifdef DSZ
				lprintf("\b\b\b\b    \b\b\b\b");
#else
				rstpstat();
#endif
			}
			starto();
			return;
		}
		if (
#ifndef DSZ
		  Kbdlock<2 &&
#endif
		  ciready())
			break;
		if ((
#ifndef DSZ
		  Hostmode ||
#endif
		  !Supcdo) && !carrier()) {
			break;
		}
#ifdef GSZ
		if (Gfdisplay)
			gszupdate(stated);
#endif
		dvpause();
		/* Check for lost Delta CTS etc. interrupt */
		checko();
	}
#ifdef GSZ
	if (Gfdisplay)
		gmessage2("Output FLow Control Restraint RELEASED");
	else
#endif
	{
		llhmargin();
		lprintf("Output FLow Control Restraint RELEASED\n");
	}
	Relsxoff = TRUE;
	Morun = Xoff = 0;
#ifndef DSZ
	Mustsayterm = Txgo = TRUE;
	if (Hostmode && !carrier()) {
		handshake("off");
		Xmodem = FALSE;
	}
	if (Ffile) {
		Txwait = Waitecho = FALSE;
	}
#endif
	goto anyway;
}

/* Start output if possible, do not wait */
flushmoc()
{
	if (Mocq != Mocdq)
		starto();
}

/*
 * moderfd(n) if n==4, set output s/w flow control (XOFF), else start output
 */
moderfd(n)
{
	extern char Rxoff;

	if (n==4 && Swhs)
		Rxoff = 023;
	else 
		Rxoff = -1;
	if (Swhs && !n)
		clearxoff();
}


lperr()
{
#ifdef DSZ
	extern Overruncnt;
#endif

	if ( !Lstat) {
#ifdef GSZ
		gmessage2("");
#endif
		return;
	}
#ifdef DSZ
	if (Lstat & 02)
		++Overruncnt;
#endif
#ifdef GSZ
	gmessage2("Serial Input Error: Line Status Register LSR %02X", Lstat);
#else
	if (Verbose > -4) {
		llhmargin();
		lprintf("Serial Input Error: Line Status Register %02X\n", Lstat);
	}
#endif
	if (Lstat == 0x18)
		setbaud(Baudrate);
	Lstat = 0;
}

	page	64,132
	TITLE YAM 8250 Interrupt Support Functions for IBM PC,XT,AT
	SUBTTL Copyright 1995 Omen Technology Inc
	name YP
;
;  rev 1-20-95
;

	include dos.mac

	if	lprog
X	equ	6		;offset of arguments
	else
X	equ	4		;offset of arguments
	endif


	if	ldata
Y	equ	2		;extra offset due to long pointer
	else
Y	equ	0
	endif

; dport	0	data
;	1	IER  Interrupt Enable reg
;	2	IIR  Interrupt Identification reg
;	3	LCR  Line Control reg
;	4	MCR  Modem Control reg
;	5	LSTAT  Line Status
;	6	MSTAT  Modem Status

DGROUP	group _DATA
_DATA	segment	word public 'DATA'
	assume	ds:DGROUP, ss:DGROUP, es:nothing

	extrn	_Dport:WORD
	extrn	_Iport:WORD
	extrn	_Sport:WORD
	extrn	_Flowcount:WORD
	extrn	_Intctl:WORD
 	extrn	__osmajor:byte
	public	_Lstat, _Mstat
	public	_Mcrgo, _Mcrstop, _mcrstopped, _ocfc
	public	_Xoff, _Rxoff, _Rxon, _Hsflag
	public	_Txoff, _Txon
	public	_Mdbfree
	public	_Mdmbuf, _Mdmend, _mcq, _mcdq, ijtab
	public	_Oldvec, _Oldisr
	public	_Mdobuf, _Mdoend, _Mocq, _Mocdq, _Morun
	public	_Fifops
	public	_Instmsg
	ifdef DSZ
	public	_DSZbanner
	public	_DSZban2
	public	_DSZhelp
	else
	extrn	_Xoffflg:WORD
	endif

MDBHI	EQU	900		;reset value for free count
MDBHYST	EQU	400		;hysterisis
MDBFSIZ	EQU	MDBHI+MDBHYST	;sized to accomodate largest block

ijtab	dw	offset IGROUP:mdmstat
	dw	offset IGROUP:mdmtx
	dw	offset IGROUP:mdmrx
	dw	offset IGROUP:mdmlstat
	ifdef DSZ
_Mdmbuf	db	?
_DSZhelp	db 0Dh, 0ah, 0Ah
 db 'Usage:'
 db 0Dh, 0ah, 0Ah
	ifdef GSZ
	db	'gsz'
	else
	db	'dsz'
	endif
 db ' [CON] [port 1..18] [speed N] [D] [d] [handshake arg] command'
 db 0Dh, 0Ah
 db ' CON          use CON: for messages'
 db 0Dh, 0Ah
 db ' port         DSZPORT (COM1) and current speed are default'
 db 0Dh, 0Ah
 db ' D            Drop DTR if carrier is lost'
 db 0Dh, 0Ah
 db ' d            disable carrier dropout detection'
 db 0Dh, 0Ah
 db ' handshake arg Flow Control Selection'
 db 0Dh, 0Ah
 db ' pxN          (set parameter x to N)'
 db 0Dh, 0Ah
 db ' z pxN        (set ZMODEM parameter x to N)'
 db 0Dh, 0Ah
 db ' sz/sb/sx     Send file(s)'
 db 0Dh, 0Ah
 db ' rz/rb/rx     Receive file(s)'
 db 0Dh, 0Ah
 db ' t        (Talk with ZMODEM AutoDownload(Tm), F1=exit F3=ymodem d/l)'
 db 0Dh, 0Ah, 7, 0Dh, 0Ah
 db '       EXAMPLES:'
 db 0Dh, 0Ah
	ifdef GSZ
	db	'gsz'
	else
	db	'dsz'
	endif
 db ' port 2 rz -m      (ZMODEM-90 MobyTurbo (Tm) receive on COM2)'
 db 0Dh, 0Ah
	ifdef GSZ
	db	'gsz'
	else
	db	'dsz'
	endif
 db ' sz -w zcom*.zoo   (ZMODEM send, 4096 window for 1200b PC Pursuit)'
 db 0Dh, 0Ah
	ifdef GSZ
	db	'gsz'
	else
	db	'dsz'
	endif
 db ' sz -r giant.arc   (ZMODEM crash recovery: resume interrupted transfer)'
 db 0Dh, 0Ah
	ifdef GSZ
	db	'gsz'
	else
	db	'dsz'
	endif
 db ' sz -rr broke.arc  (ZMODEM crash recovery with file compare)'
 db 0Dh, 0Ah
	ifdef GSZ
	db	'gsz'
	else
	db	'dsz'
	endif
 db ' rb -y             (YMODEM receive, erase old file if any)'
 db 0Dh, 0Ah
	ifdef GSZ
	db	'gsz'
	else
	db	'dsz'
	endif
 db ' handshake slow rb -g *(YMODEM-g receive)'
_DSZban2	db	0dh, 0Ah, 0Ah
	db	'        No version of '
	ifdef GSZ
	db	'GSZ'
	else
	db	'DSZ'
	endif
	db	' may be modified or sold without'
	db	0dh, 0Ah
	db	'        the prior written consent of Omen Technology Inc.'
 db 0Dh, 0Ah, 0Ah
 db 'The ZCOMM User Manual (in ZCOMMDOC.ZIP) explains the'
	db 0Dh, 0Ah
 db 'commands, parameters, and options in GSZ, DSZ and ZCOMM.'
 db 0Dh, 0Ah, 0Ah
 db '    * Registration enables this and other advanced features.'
 db 0
	public dmend, dmfix
dmend	db	?
dmfix	equ	dmend - _Mdmbuf
	db	(MDBFSIZ - dmfix)	dup(?)
_Mdmend	db	?
	else
mdmmsg	db	'EXIT', 0Dh, 'EXIT', 0Dh	;message for command.com
_Mdmbuf	db	MDBFSIZ	dup(?)
_Mdmend	db	?
	endif
	even
_mcq	dw	offset DGROUP:_Mdmbuf
_mcdq	dw	offset DGROUP:_Mdmbuf
mdbtimr	dw	0

_Mdobuf	db ?
_Instmsg	db	0dh, 0Ah
	ifndef DSZ
	db	1Bh, 'S'
	db	'This Program Copy has an Incorrect Serial Number.'
	db	1Bh, 'q', 0Dh, 0Ah
	db	'Please insert the correct Serial/Number/Password (SNP) for this'
	db	0dh, 0Ah
	db	'particular program (not DSZ) into this Copy with PUTSNP.'
	db	00
	if lprog
	db	200	dup(?)
	endif
	endif
	ifdef DSZ
_DSZbanner	db	0dh, 0Ah, 0ah
	ifdef GSZ
	db	'GSZ Registration and Documentation: $30 Check/MO'
	else
	db	'DSZ Registration and Documentation: $20 Check/MO'
	endif
	db	0dh, 0Ah
	db	'Omen Technology Inc, P.O. BOX 4681, Portland OR 97208'
	db	0dh, 0Ah, 0ah
	db	'VISA/MC ZERO PAPERWORK Registration!  Data: 503-617-1698'
	db	0dh, 0Ah
	db	'VOICE: 503-614-0430'
	db	0dh, 0Ah, 0Ah
	db	00
	endif

_Mdoend	db	?


ERRORMASK	equ	036q	;8250 framing + parity + overrun + break
OVERRUN		equ	2	;overrun error mask
_Mstat		db	0	;modem line status bits stored here
_Lstat		db	0	;modem error bits are stored in this
_Rxoff		db	023q	;character to compare to for xoff or -1
_Rxon		db	021q	;character to compare to for xon or -1
_Hsflag		db	0	;hardware flow control bit to sample
_Xoff		db	0	;1 bit is s/w XOFF, rest h/w, <>0 means nogo
_Txoff		db	023q	;character to send to stop modem or 0
_Txon		db	021q	;character to send to start modem or 0
_Mcrstop	db	013q	;modem control register bits to stop modem
_Mcrgo		db	013q	;modem control register bits to start modem
_mcrstopped	db	1	;modem flow control has been stopped
_ocfc		db	0	;on chip flow control for ST16C650
_Morun		db	0		; <>0 if output running
wedgeo		db	0		; counter for wedged output
txnowch		db	0		;char to immediately send to modem
_Oldvec		db	0		;old interrupt number
mdxflg		db	0		;==1 means do DOS RET on no DCD

	even
_Mocq		dw	offset DGROUP:_Mdobuf
_Mocdq		dw	offset DGROUP:_Mdobuf
_Fifops		dw	0
fifocnt		dw	0		; counter for space in tx FIFO
_Mdbfree	dw	MDBHI	;number of free characers in int buff
_Oldisr		dw	0 		;old interrupt service routine
		dw	0
_oldb14		dw	0		;old entry for BIOS int 14h handler
		dw	0
_oldb1B		dw	0		;old entry for BIOS int 1Bh handler
		dw	0

_DATA	ends

IGROUP	group	_TEXT
_TEXT	segment word public 'CODE'
	assume	cs:_TEXT

	extrn	datseg:NEAR
	extrn	_readline2:NEAR
	public	_miready, _michar, _readline
	public	_setvec
	public	_dwredir
	ifndef DSZ
	public	_setv14, _resv14
	endif
	public	_mdmint, mdmrx, mdmtx, mdmlstat, xof1 ;for debugging

	public	_disk_reset, _commit_file
	public	_stopi, _starto, _checko, _purgemo

	INTA00	equ	20h
	EOI	equ	20h

	ENAINTS	equ	0Fh		;all 8250 interrupts enabled
	ENRINTS	equ	0Dh		;8250 interrupts enabled ex. transmit

;
;Interrupt service routine for 8250 UART
;
;	(PROCEDURE MDMINT)
;
;
;	public	_Mdobuf, _Mdoend, _Mocq, _Mocdq
;

_mdmint	proc	near
	push	ax
	push	bx
	push	dx
	push	ds
	mov	ds, word ptr cs:datseg
	mov	dx, _Iport
	in	al, dx
	test	al, 1
	jne	mdmstq		;;; called from user level sim intr?
	and	ax, 06			;mask possible garbage
	mov	bx, ax
	jmp	ijtab[bx]		;dispatch to highest priority routine
mdmstq:	mov	bl, _Mstat		; mainly if called from user sim intr
	add	dl, 4			;check for lost Delta CTS interrupt
	in	al, dx
	cmp	al, bl
	jne	mdmst1
mdm999:	jmp	mdm99

mdmtx:	mov	dx, _Iport
	add	dx, 3
	in	al, dx
	test	al, 20h			; holding reg really empty?
	je	mdm999
	mov	dx, _Dport		;bump back to data port
	mov	ax, _Fifops
	mov	fifocnt, ax		;allow multi byte loads with 16550A
	mov	al, txnowch
	test	al, al
	jne	mdmtx1
	test	_Xoff, -1	;check for incoming flow control
	jne	mdm01
	mov	bx, _Mocdq
mdmtxm:	cmp	bx, _Mocq
	je	mdm01		;all sent, let interrupt enable die
	mov	al, [bx]
	out	dx, al
	inc	bx
	cmp	bx, offset DGROUP:_Mdoend
	jne	mdmtx2
	mov	bx, offset DGROUP:_Mdobuf
mdmtx2:
	mov	_Mocdq, bx
	mov	wedgeo, 0
	shr	fifocnt,1
	jne	mdmtxm
	jmp	mdmtx0			;enable transmitter interrupt
mdmtx1:	out	dx, al
	mov	txnowch, 0
mdmtx0:	inc	dx
;	mov	al, ENRINTS		; trans int off 9-16-94
;	out	dx, al
	mov	al, ENAINTS		; trans int on 9-16-94
	mov	_Morun, al
	out	dx, al
	jmp	mdm00

mdm01:	inc	dx
	mov	_Morun, 0		;transmitter finished
	mov	al, ENRINTS		; trans int off 9-16-94
;	out	dx, al
	jmp	mdm00

mdmstat:	add	dl, 4		; bump pointer to modem stat port
	in	al, dx
mdmst1:	mov	_Mstat, al		;store modem status
	mov	ah, _Hsflag		;h/w flow bit to sample
	test	ah, ah			; none - ignore this
	je	mdm00
	not	al			; complement to get Xoff stop flag
	and	ah, al
	mov	al, _Xoff		; get possible s/w part of flag
	and	al, 1
	or	ah, al		; or in to get both h/w and s/w restraint
	mov	_Xoff, ah
	jne	mdm00
	sub	dl, 5		;bump port pointer to IER
	mov	al, ENRINTS		; trans int off 9-16-94
;	out	dx, al
	inc	_Flowcount
	mov	al, ENAINTS		; trans on
	out	dx, al
	jmp	short mdm00

mdmlstat:	add	dl, 3		; bump pointer to line stat port
	in	al, dx
	and	al, ERRORMASK
	or	_Lstat, al		;or in new error bit(s)
	nop
	nop
	nop
	nop
	mov	dx, _Dport		; hack for Hayes ESP board
	in	al, dx
	nop
	nop
	nop
	nop
	nop
	nop
	mov	dx, _Iport
	add	dl, 3		; bump pointer to line stat port
	in	al, dx			; 2nd chance to clear UART error
	and	al, ERRORMASK
	or	_Lstat, al		;or in new error bit(s)
	;;;;;   ****** FALL THRU TO ****** ;;;;;
mdm00:
mdm99:
	mov	al, EOI			; send EOI to our 8259
	mov	dx, _Intctl
	out	dx, al
	or	dl, dl			; slave 8259?
	jns	mdm99a
	out	INTA00, al		; EOI to master 8259 also
mdm99a:	mov	dx, _Iport
	in	al, dx
	test	al, 1
	jne	mdm99b
	and	ax, 06			;mask possible garbage
	mov	bx, ax
	jmp	ijtab[bx]		;dispatch to highest priority routine

mdm99b:	pop	ds
	pop	dx
	pop	bx
	pop	ax
	iret

mdmov:	or	_Lstat, 01	; add in virtual overrun
	jmp	mdm00

mdmrx:	sub	dl, 2			;bump back to data port
mdmrx1:	in	al, dx
	mov	bx, _mcq
	mov	[bx], al
	and	al, 0177q		;7 bit compare
	cmp	al, _Rxoff
	jg	mdm23
	jne	mdm21
xof1:	or	_Xoff, 1		;we saw xoff
	inc	_Flowcount
	jmp	short mdm23
mdm21:	cmp	al, _Rxon
	jne	mdm23
	and	_Xoff, not 1		;turn off s/w restraint bit
mdm23:	inc	bx
	cmp	bx, offset DGROUP:_Mdmend
	jne	mdm1
	mov	bx, offset DGROUP:_Mdmbuf
mdm1:	mov	_mcq, bx
	cmp	bx, _mcdq
	je	mdmov
	dec	_Mdbfree
	je	mdm2
	add	dl, 5		; check for more input bytes
	in	al, dx
	test	al, 1
	je	mdm00i
	sub	dl, 5
	jmp	mdmrx1
mdm2:	inc	dx		;bump port pointer to IER
	mov	al, ENRINTS		; trans int off
	out	dx, al
	mov	al, ENAINTS	;enable all interrupts
	sub	_Mdbfree, MDBHYST
	out	dx, al
	mov	al, _Txoff
	mov	txnowch, al	; queue XOFF character
	add	dl, 3		; bump to MCR
	mov	al, _Mcrstop
	mov	_mcrstopped, al
	out	dx, al		; activate hardware flow ctl
	inc	_Flowcount
;;;  bug in ST16C650 chip - auto on chip auto RTS ignores MCR!!!
	test	_ocfc, 40h	; ST16C650 on chip auto RTS ?
	je	mdm00i
	dec	dx
	in	al, dx
	mov	bx, ax
	mov	al, 0BFh
	out	dx, al		; enable EFR access
	dec	dx
	xor	al, al		;turn off on chip auto fc
	out	dx, al
	inc	dx
	mov	ax, bx
	out	dx, al
mdm00i:	jmp	mdm00

_mdmint	endp

	ifndef DSZ
; get virtual modem status into ah
mbsah	proc	near
	mov	dx, _Sport	;load stat reg ptr for caller
	mov	bl, al
	in	al, dx		;get line status
	and	al, 07Eh	;mask off timeout, data avail
	mov	ah, _Lstat	;get stored errors
	and	ah, 1Eh		;mask off all but error bits
	or	ah, al
	mov	al, bl
	mov	bx, _mcdq	;load dequeue pointer for caller
	cmp	bx, _mcq
	je	mbsa2
	or	ah, 1		;or in data virtual ready
mbsa2:	ret
mbsah	endp
;
;Alternate to BIOS service routine for modem functions
;	ah=0 set params (but not speed), get status to AX
;	ah=1 send char in al, get stat in ah
;	ah=2 receive char to al, get error bits only in ah
;	ah=3 get full status to AX
;	ah=? get full status to AX
;Refer to IBM Tech Ref manual INT 14 listing for details
;
	PUBLIC mdmbios
mdmbios	proc	far
	sti
	push	bx
	push	dx
	push	ds
	mov	ds, word ptr cs:datseg
	push	ax
	call	mbsah		;wait for character to appear
	inc	dx			;get modem stats to al
	in	al, dx
	test	al, 080h	; check for DCD
	jne	mdb24
	test	mdxflg, 1	;==1 means do DOS RET on no DCD
	je	mdb24
	mov	mdxflg, 0
	mov	ax, offset DGROUP:mdmmsg
	mov	_mcdq, ax	; preset exit message
	mov	ax, 4C01h	; no DCD, DOS exit with 001 code
	int	21h
mdb24:	pop	ax
	test	ah, 1
	mov	mdbtimr, 0
	cmp	ah, 1		;Chuck request code
	jne	mdb2
mdb1:	call	mbsah		;ah=1 wait, send char
	test	ah, 20h
	je	mdb1		;wait for trans holding empty
	mov	dx, _Dport
	out	dx, al
	jmp	short mdb9
mdb2:	cmp	ah, 2		;ah=2 wait, receive char
	jne	mdb3
mdb22:	call	mbsah		;wait for character to appear
	test	ah, 1
	jne	mdb23
	dec	mdbtimr
	jne	mdb22
	or	ah, 80h
	jmp	short	mdb9
mdb23:	mov	al, [bx]	;in buffer
	inc	bx
	cmp	bx, offset DGROUP:_Mdmend
	jne	mdmbi1
	mov	bx, offset DGROUP:_Mdmbuf
mdmbi1:	mov	_mcdq, bx
	cli			;;;; very slightly critical region
	mov	ah, _Lstat		;get stored modem error
	mov	_Lstat, 0		;and reset it
	sti
	and	ah, 1Eh			;mask off all but error bits
	jmp	short mdb9
mdb3:	or	ah, ah		;ah=0 set word length, etc., status to AX
	jne	mdb4
	mov	dx, _Dport
	add	dl, 3		;bump to Line Control Register
	and	al, 1Fh		;keep just word len, stop bits, parity
	out	dx, al
mdb4:	call	mbsah		;ah=3, default: status to AX
	inc	dx			;get modem stats to al
	in	al, dx
;	test	al, 080h	; check for DCD
;	jne	mdb5
;	or	ah, 1		; if no DCD, fake rx char avail
mdb5:	or	al, 0B0h	;fake in DCD,DSR,CTS 2-5-87
mdb9:	pop	ds
	pop	dx
	pop	bx
	iret
mdmbios	endp
	endif		; DSZ

;
;setvec(vector)
;Restore old isr pointer from saved vector iff saved isr not zero.
;
;	if new vector == 0, return
;		else save old isr pointer for new vector,
;		and set new isr pointer to cs:_mdmint.
;	******* BUG: int 21h enables interrupts!
;
	if lprog
_setvec	proc	far
	else
_setvec	proc	near
	endif
	push	bp
	mov	bp, sp

	mov	al, _Oldvec
	test	al,al
	je	setve1		;check for old vector stored

	push	ds
	lds	dx, dword ptr _Oldisr
	mov	ah, 25h
	int	21h
	pop	ds

setve1:	mov	ax, [bp+X]
	mov	_Oldvec, al
	test	al, al		;argument of 0 means just restore
	je	setve2

	push	es
	push	ax
	mov	ah, 35h		;get previous isr for this vector
	int	21h
	mov	_Oldisr, bx	;save it for when we leave
	mov	_Oldisr+2, es
	pop	ax
	pop	es

	push	ds
	push	cs
	pop	ds
	mov	dx, offset _mdmint
	mov	ah, 25h
	int	21h
	pop	ds

setve2:	pop	bp
	ret
_setvec	endp

;
;	dwredir(flag)
;	  1 = redirect on
;	  0 = redirect off
;	return value:
;	  8000h bit means Doorway present
;	  80h bit means no change in state,
;	  otherwise low order 8 bits contain com port
;
	if lprog
_dwredir	proc	far
	else
_dwredir	proc	near
	endif
	push	bp		; save frame pointer
	mov	bp,sp		; stack pointer to frame pointer
	mov	al,[bp+X]	; get argument
	mov	ah, 067H	; Doorway's fraternity handshake
	int	16h		; keyboard BIOS call
	pop	bp
	ret
_dwredir	endp

	ifndef DSZ
;
;setv14 saves old int 14h vector and inserts pointer to mdmbios
;
;resv14 restores old interrupt 14h contents
;
	if lprog
_setv14	proc	far
	else
_setv14	proc	near
	endif
	mov	mdxflg, 1	;==1 means do DOS RET on no DCD
	push	es		;save previous int 14h vector
	mov	ax, 3514h
	int	21h
	mov	_oldb14, bx
	mov	_oldb14+2, es
	pop	es

	push	ds		;store our vector in int 14h
	push	cs
	pop	ds
	mov	dx, offset mdmbios
	mov	ax, 2514h
	int	21h
	pop	ds
	ret
_setv14	endp


	if lprog
_resv14	proc	far
	else
_resv14	proc	near
	endif
	push	ds
	mov	al, 14h			;RS232 i/o
	lds	dx, dword ptr _oldb14
	mov	ah, 25h
	int	21h
	pop	ds
	ret
_resv14	endp
	endif

mdmarts	proc near
;;;  bug in ST16C650 chip - auto on chip auto RTS ignores MCR!!!
	test	_ocfc, 40h	; ST16C650 on chip auto RTS ?
	je	mdmartse
	push	dx
	mov	dx, _Dport
	add	dl, 3
	in	al, dx
	mov	bx, ax
	mov	al, 0BFh
	out	dx, al		; enable EFR access
	dec	dx
	mov	al, _ocfc
	out	dx, al
	inc	dx
	mov	ax, bx
	out	dx, al
	pop	dx
mdmartse:	ret
mdmarts	endp

; Return non 0 iff a modem char is ready.
; If none ready, send XON if XOFF has been sent. Reset free fount.

	if lprog
_miready	proc	far
	else
_miready	proc	near
	endif
	mov	ax, _mcdq
	cmp	ax, _mcq
	je	mempty
	ret
mempty:	mov	dx, _Dport
	cli			; Adjust our shorts
	add	dl, 4		; bump to MCR
	cmp	ax, _mcq	; still empty????
	jne	miyes
	test	_mcrstopped, -1
	jz	mi10
	ifndef DSZ
	cmp	byte ptr _Xoffflg, 'X'
	je	mi9
	endif
	mov	al, _Mcrgo	;turn on h/w flow control
	out	dx, al
	call	mdmarts
	mov	_mcrstopped, 0
	test	_Mdbfree, -1	;negative implies XOFF sent or h/w FC
	jns	mi10
	sub	dx, 3		; bump back to IER
	mov	al, ENRINTS		; trans int off
	out	dx, al		; disable 8250 interrupts (to get toggle)
	mov	al, _Txon
	mov	txnowch, al
	mov	al, ENAINTS	;enable 8250 interrupts
	out	dx, al
mi10:	mov	_Mdbfree, MDBHI		;reset free count if empty
mi9:	xor	ax,ax
miyes:	sti
	ret
_miready	endp

; Return next modem character; valid only after <>0 return from miready.
; If michar is called after a 0 return from miready, the last MDBFSIZ bytes
; in the buffer may be fetched.
; If the free character count increases to 0, add in the hysterisis and
; send XON.

	if lprog
_michar	proc	far
	else
_michar	proc	near
	endif
	mov	bx, _mcdq
	mov	al,[bx]
	inc	bx
	cmp	bx, offset DGROUP:_Mdmend
	jne	michar1
	mov	bx, offset DGROUP:_Mdmbuf
michar1:
	cli
	mov	_mcdq, bx
	xor	ah,ah
	inc	_Mdbfree
	je	michar3
	sti
	ret
michar3:	add	_Mdbfree, MDBHYST
	push	ax
	ifndef DSZ
	cmp	byte ptr _Xoffflg, 'X'
	je	mich9
	endif
	mov	dx, _Dport
	inc	dx
	mov	al, ENRINTS		; trans int off
	out	dx, al
	mov	al, _Txon
	mov	txnowch, al
	mov	al, ENAINTS	;enable all interrupts
	out	dx, al
	mov	al, _Mcrgo	;turn on h/w flow control
	add	dl, 3
	out	dx, al
	call	mdmarts
mich9:	sti
	pop	ax
	ret
_michar	endp

;
; readline(timeout)
;
	if lprog
_readline	proc	far
	else
_readline	proc	near
	endif
	mov	bx, _mcdq
	cmp	bx, _mcq
	je	rempty
	mov	al,[bx]
	inc	bx
	cmp	bx, offset DGROUP:_Mdmend
	je	rchar1
rchar2:	mov	_mcdq, bx
	sub	ah, ah
	inc	_Mdbfree
;;;;	je	michar3
	ret
rchar1:	mov	bx, offset DGROUP:_Mdmbuf
	jmp	rchar2
rempty:	jmp	_readline2
_readline	endp

MULT_SMARTDRV                  equ 4A10h      ; (ax)

SD_CACHE_DRIVE                 equ 3          ; (bx)
CACHE_DRIVE_GET                 equ 0      ; (dl)
CACHE_DRIVE_READ_ENABLE         equ 1      ; (dl)
CACHE_DRIVE_READ_DISABLE        equ 2      ; (dl)
CACHE_DRIVE_WRITE_ENABLE        equ 3      ; (dl)
CACHE_DRIVE_WRITE_DISABLE       equ 4      ; (dl)


;	SD_CACHE_DRIVE:
;	Enables and disables read or write caching for a particular
;	drive unit. Returns the cache state of the drive in DL. Get
;	takes no action, but simply returns the cache state for the
;	drive unit in DL.
;	
;	Input:    AX = MULT_SMARTDRV
;		BX = SD_CACHE_DRIVE
;		DL = One of:
;			CACHE_DRIVE_GET
;			CACHE_DRIVE_READ_ENABLE
;			CACHE_DRIVE_READ_DISABLE
;			CACHE_DRIVE_WRITE_ENABLE
;			CACHE_DRIVE_WRITE_DISABLE
;		BP = Drive unit number
;	
;	Output:   DL = Cache state of unit:
;		     -1          -> Not a cachable drive
;		     Bit 8 set   -> No caching enabled for this unit
;		     Bit 8 clear -> Read caching enabled for this unit
;		     Bit 7 set   -> Write caching not enabled for this
;				    unit
;		     Bit 7 clear -> Write caching enabled for this
;				    unit
;	
;	Uses:     All except DS, ES
;	
;		Try to get Smartdrv to operate smarter....
;
	public	_wacksmtv
	if lprog
_wacksmtv	proc	far
	else
_wacksmtv	proc	near
	endif
	push	bp
	push	si
	push	di

 	cmp	__osmajor,6
	jb	wack1
	mov	ah, 19h
	int	21h
	xor	ah, ah
	inc	al
	mov	bp, ax		; default drive
	mov	ax, MULT_SMARTDRV
	mov	bx , SD_CACHE_DRIVE
	mov	dl, CACHE_DRIVE_GET
	push	bp
	int	2Fh
	pop	bp
	test	dl, 80h
	je	wack1
	mov	ax, MULT_SMARTDRV
	mov	bx , SD_CACHE_DRIVE
	mov	dl, CACHE_DRIVE_WRITE_DISABLE
	push	bp
	int	2Fh
	pop	bp
	mov	ax, MULT_SMARTDRV
	mov	bx , SD_CACHE_DRIVE
	mov	dl, CACHE_DRIVE_WRITE_ENABLE
	int	2Fh

wack1:	pop	di
	pop	si
	pop	bp
	ret
_wacksmtv	endp


;
;	(void) disk_reset - Reset hard disk - dump smartdrv cache, etc.
;
	if lprog
_disk_reset	proc	far
	else
_disk_reset	proc	near
	endif
	mov	ah, 0Dh
	int	21h		; MSDOS disk reset fnx to write all
	ret
_disk_reset	endp
;
;
;commit_file(handle)
;
;	Write pending data to disk including smartdrv
;		fall back to disk reset if error
;
	if lprog
_commit_file	proc	far
	else
_commit_file	proc	near
	endif
	push	bp
 	cmp	__osmajor,5
	jb	cf1
	mov	bp, sp
	mov	bx, [bp+X]
	mov	ah, 68h
	int 21h			; commit file to disk
	jnc	cf2		; if error just do disk reset
cf1:	mov	ah, 0Dh
	int	21h		; MSDOS disk reset fnx to write all
cf2:	pop	bp
	ret
_commit_file	endp

;
;	(void) stopi - Stop input by deasserting RTS
;	  Restart with starti or by miready with empty buffer
;
	if lprog
_stopi	proc	far
	else
_stopi	proc	near
	endif
	cli
	test	_mcrstopped, -1
	jne	stop4
	mov	al, _Txoff
	mov	dx, _Dport
	inc	dx
	test	al, al
	je	stop3
	mov	txnowch, al		; queue XOFF
	mov	al, ENRINTS		; trans int off
	out	dx, al
	nop
	nop
	nop
	mov	al, ENAINTS	;enable 8250 interrupts
	out	dx, al
stop3:	add	dl, 3		; bump to MCR
	mov	al, _Mcrstop
	mov	_mcrstopped, al
	out	dx, al		; Turn off RTS
;;;  bug in ST16C650 chip - auto on chip auto RTS ignores MCR!!!
	test	_ocfc, 40h	; ST16C650 on chip auto RTS ?
	je	stop4
	dec	dx
	in	al, dx
	mov	bx, ax
	mov	al, 0BFh
	out	dx, al		; enable EFR access
	dec	dx
	xor	al, al		;turn off on chip auto fc
	out	dx, al
	inc	dx
	mov	ax, bx
	out	dx, al
stop4:	mov	_Mdbfree, -10000		;set free count really low
	sti
	ret
_stopi	endp

	if 0
;
;	(void) starti - Start input by asserting RTS
;
	if lprog
_starti	proc	far
	else
_starti	proc	near
	endif
	test	_Mdbfree, -1		;negative implies XOFF sent
	js	si10
	cli
	mov	dx, _Dport
	add	dl, 4		; bump to MCR
	mov	al, _Mcrgo
	out	dx, al		; Turn on RTS
	call	mdmarts
	sti
si10:	ret
_starti	endp
	endif

;
;	(void) starto - Start output if not already running
;	  AND not flow controlled
;
	if lprog
_starto	proc	far
	else
_starto	proc	near
	endif
	mov	dx, _Dport
	inc	dx
	cli
	test	_Morun, -1	;already running
	jne	sto1
	test	_Xoff, -1	;stopped for flow control
	jne	sto3
	mov	al, ENRINTS
	out	dx, al
	mov	al, ENAINTS
	mov	al, ENAINTS
	mov	al, ENAINTS
	out	dx, al
sto1:	inc	wedgeo
	jnz	sto3
	pushf			; simulate interrupt
	push cs
	call _mdmint
sto3:	sti
	ret
_starto	endp
;
;	(void) checko()   Check for lost interrupts
;
	if lprog
_checko	proc	far
	else
_checko	proc	near
	endif
	pushf			; simulate interrupt
	cli
	push cs
	call _mdmint
	ret
_checko	endp
;
;	(void) purgemo - Purge output buffer
;
	if lprog
_purgemo	proc	far
	else
_purgemo	proc	near
	endif
	cli
	sub	ax,ax
	mov	txnowch, al
	mov	_Morun, al
	mov	wedgeo, al
	mov	ax, _Mocq
	mov	_Mocdq, ax
	sti
	ret
_purgemo	endp

	if	1
;
;sendline(char)
;
;	queue char in the output buffer
;
	public	_xsendline, _sendline
	if lprog
_xsendline	proc	far
	else
_xsendline	proc	near
	endif
_xsendline	endp
	if lprog
	EXTRN	_flushok:FAR
_sendline	proc	far
	else
	EXTRN	_flushok:NEAR
_sendline	proc	near
	endif
	push	bp
	mov	bp, sp

	mov	al, [bp+X]
	mov	bx, _Mocq
	mov	[bx], al
	inc	bx
	cmp	bx, offset DGROUP:_Mdoend
	jne	sl1
	mov	bx, offset DGROUP:_Mdobuf
sl1:	cmp	bx, _Mocdq
	jne	sl2
	push	bx
	call	_flushok
	pop	bx
	jmp	sl1
sl2:	mov	_Mocq, bx
	pop	bp
	ret
_sendline	endp
	endif

	ifndef DSZ
;
;miungetcn(n)
;
;	unget  n  bytes from modem, assuming at least n have been read
;		n > 0
;		this vsn always returns 0 (success) in ax
;
	public	_miungetcn
	if lprog
_miungetcn	proc	far
	else
_miungetcn	proc	near
	endif
	push	bp
	mov	bp, sp

	mov	cx, [bp+X]
	mov	bx, _mcdq
usl0:	cmp	bx, offset DGROUP:_Mdmbuf
	jne	usl1
	mov	bx, offset DGROUP:_Mdmend
usl1:	dec	bx
	loop	usl0
	mov	_mcdq, bx
	sub	ax, ax
	pop	bp
	ret
_miungetcn	endp
	endif


_TEXT	ends
	end
