/*		DMS OS- AND C-INDEPENDENT BIOS INTERFACE	*/
#define newwm 49
#define ver 1
#define rev 0
#define mod 'a'
#define asm 'g'

#include "mpbios.os"
#include "c:switch.os"

#include "fcbios.os"
#include "userbios.os"
#include "cpmstruc.def"
#include "mpstruct.def"
#include "cpmdata.def"

/*	Change History: 

	0.0a 12/05/83	Cpm80 get/put assign adapted from dmsbdos.c
	0.0b 01/09/84	added local hard disk and diskette assign
	0.0c 02/09/84	error checking on network i/o added
	1.0a 03/10/84	Use DMSBIOS$ handle for MSDOS
	1.0ac 03/26/84	Speed up recnet and sendnet
	1.0ag 04/09/84	Patch in retry loop of nackpoll,sendnet, recnet
*/

#ifdef msdos
CHAR	curOS = 1;
CHAR	handlestatus = 0;
int	handle = 1;
CHAR	vec;
#else
CHAR	curOS = 0;
#endif

#define masteruser 0
#define standalone 0xff

int	localhd;
int	floppy;
int	floptype;
int	donak = FALSE;
int	sendfail = FALSE;

#ifdef cpm80 /* all the way through to bios86 */
extern	CHAR	fastuser;
extern	CHAR	netcode;
extern	int	reclen;
extern	CHAR	*recmsg;

/*page*/
#ifndef userbios
#ifndef fcbios
#include "hardio.h"
/* define externs that I think hardio.h should have */

extern	struct DMSCOM {
	char cmd;
	char sec;
	int trk;
	char unused[2];
	int retries;
	char status[8];
	} h_dms_hd;

struct 	{
	CHAR hdstatus[ 8];
	struct	{
		CHAR volinfo[ 32];
		} volume[ 4];
	} hardstat;

struct	{
	CHAR siz;
	CHAR pname[ 8];
	CHAR pword[ 6];
	CHAR ctl;
	} alloctab[ 8];

struct	{
	CHAR size;
	CHAR unit;
	CHAR control;
	CHAR vol;
	} assparm;
#endif /* fcbios */
#endif /* userbios */
/*page*/
#define notyet() printf("\nNOT IMPLEMENTED YET\n");

#define blkshf1k 3
#define LassignSend 15
#define LassignRec 4
#define unassigned 0xff

int	*biosptr = 0x01;
CHAR	*netmsg;
CHAR	*CCPdisk = 0x04;
CHAR	CCPdisksave;
struct	DPH	*dph;
struct	DPB	*dmsdpb;
struct	DPB	*dmsdpbt;
struct	DPB	*dmsdpbs;
struct	DPB	savedpb;
int	curdisk;

#else /* not cpm80 */

struct { int cs; int ss; int dsreg; int esreg; } rrv;
struct { 
	int ax; int bx; int cx; int dx;
	int si; int di; int ds; int es; 
	} sreg, rreg;
struct { CHAR func; CHAR *offset; CHAR *segment; } biosvect, gutvect;

extern CHAR *PWInput();

struct	DMSinfo DMSInfo;
/*===================================================================*/
/*page*/
int	bios86( c, f, off)
int	f,c;
CHAR	*off;
{

	segread( &rrv);	
#ifdef cpm86
	gutvect.func = 40;
	gutvect.offset = &biosvect;
	gutvect.segment = rrv.dsreg;
#endif
	biosvect.func = f;
	if( c > 1)
		biosvect.offset = off;
	if( c > 2)
		biosvect.segment = rrv.dsreg;
#ifdef cpm86
	rreg.ax = bdos( 50, &gutvect);
#endif
#ifdef msdos
	if( handlestatus == 0)
	{	vec = 0x21;
		sreg.ax = 0x3d00 + 2; /* msdos int 21 func 3d opens a handle */
		sreg.dx = "DMSBIOS$"; /* this is the handle name */
		sreg.ds = rrv.dsreg;
		if( sysint( vec, &sreg, &rreg) & 1 ) /* if carry set, old style */
			handlestatus = 5;
		else
		{	handlestatus = 2;
			handle = rreg.ax;
		}
	}

/*	set up registers for msdos interrupt 21 function 44 (IOControl) */

	sreg.ax = 0x4400 + handlestatus;
	sreg.bx = handle;   /* 'select' drive A (old style) or handle (new) */
	sreg.cx = 5;
	sreg.dx = &biosvect;

	sysint( vec, &sreg, &rreg);
#endif
#ifdef debug
	printf("bios call returned %x\n", rreg.ax);
#endif
	return rreg.ax;
}
#endif /* way back to hardio stuff */
/*======================================================================*/
/*		extracts from x the n bits starting at position p	*/
/*		and to its right, and right justifies the result. 	*/
/*		Bits are counted from the right=0			*/
/*======================================================================*/
getbits( x, p, n)
unsigned x, p, n;
{	return( (x >> (p+1-n)) & ~(~0 << n) );}
#ifndef userbios
/*===================================================================*/
MSAssign() {curOS = 1;}
/*===================================================================*/
CPAssign() {curOS = 0;}
/*===================================================================*/
/*page*/
PWAssign( drive, part)
CHAR	drive;
CHAR	*part;
{
int	tries;
int	i;
CHAR	pass[ LENpass];
struct MakInfoStruct	makbuffer;
int	flag;
static	CHAR	unass[ LENpass] = { 0, 0xff, 0, 0, UNASSmedia, 0 };
static	CHAR	memass[ LENpass] = { 1, 0x43, 0, 0, RAMmedia, 0};

	floppy = FALSE;
	if( userno() == standalone )
		localhd = TRUE;
	else
		localhd = FALSE;

#ifndef fcbios	/* filecopy bios does not need this */
	if( *(part+1) == ':' )
		{flag = *part;
		part += 2;
		}
	else
		flag = 'x';
	switch(	flag)
		{
		case 'H': 	/* local hard disk assign */
#ifdef cpm80
			localhd = TRUE;
#else
			return 1; /* failure if on 86's */
#endif
			break;

		case 'D':	/* floppy assign */
#ifdef cpm80
			floppy = TRUE; /* fall thru to default */
#else
			return 1; /* failure if on 86's */
#endif
		default:
			/* convert partition names that look like floppies to
			floppy assigns. N: below handles ambiguous case */

			floptype = 0;
			switch( *part)	{
			case 'M':	floptype++;	/* 2 */
			case 'S':	floptype++;	/* 1 */
			case 'D':			/* 0 */
					floppy = TRUE;
					if( *(part+1) < '0' || *(part+1) > '7'
					    || *(part+2) != ' ' )
						floppy = FALSE;
					break;
			default:	break;
			}
#ifndef cpm80
			if( floppy) return 1; /* error if on 86's */
#endif
		case 'N':	/* network assign */
			break;
		case 'U':	/* unassign */
#ifdef dbg
			printf("unassign");
#endif
			blockmv( part, unass, LENpass);
			break;
		case 'M':	/* memdisk ultra0 */
#ifndef cpm86
			return 1; /* error if on z80 or msdos */
#else
			blockmv( part, memass, LENpass);
#endif
			break;
		}
#endif

/* try assign first without password and then 3 times with */
	tries = 3;
	clear( pass, LENpass, ' ' );
	while( tries--)
		{
		i = MakAssign( drive, part, pass, &makbuffer);
#ifdef	dbg
		printf( "try %d, makassign returns %d\n", 3-tries, i);
#endif
		if( i == 0 ||  i > 2) return i;
		if( PWInput( pass, LENpass) == FALSE )
			return 1;
		}
	return MakAssign( drive, part, pass, &makbuffer);
}
#endif  /* userbios */
/*==================================================================*/
/*page*/
#define CR	13
#define LF	10
#define CTRLX	0x18
#define CTRLC	0x03
#define	DEL	0x7F
#define BS	8	
#define DirectIO(c)  ( bdos (6, c) & 0xff )

/*======================================================*/
/*	PWInput	- get password and don't echo 		*/
/*======================================================*/
CHAR	*PWInput( buf, len )
CHAR	*buf;
int	len;
{CHAR	*p;
CHAR	c;
int	cnt;
CHAR	bucket;
	
	printf ("Enter Password: "); 

start:	clear ( buf, len, ' ');
	p = buf;
	cnt = len;
	while (cnt) {
		while ( (c = DirectIO (0xFF) ) == 0)
			;
#ifdef dbg
		printf("%d,%x", cnt, c);
#endif
		switch (c) {
		case CR :
		case LF :	printf ("\n");
				return TRUE;

		case CTRLX :	goto start;

		case CTRLC :	printf( "^C");
				return FALSE;
		case DEL :
		case BS	:	if (p != buf) {
					*--p = ' ';
					cnt++;
					}
				break;

		default : 	*p++ = c;
				cnt--;
				break;
		}
		}

	for (;;) {
		while ((bucket = DirectIO (0xFF)) == 0)
			;
		if (bucket == CR || bucket == LF) {
			printf ("\n");
			return TRUE;
			}
		else if (bucket == CTRLX)
			goto start;
		}
	}
/*======================================================================*/
#ifdef cpm80
P0Assign( drive, vol)
CHAR	drive;
CHAR	vol;
{
	dmsdpbt = dmsdpb; /* save current active dpb */
	if( getdph( drive) == 0 ) return 1; /* check for invalid driveletter */
	/* check for invalid net hard disk volume -- not present */

	dmsdpbs = dmsdpb;
	blockmv( &savedpb, dmsdpb, LENDPB); 	/* save old dpb */
	blockmv( dmsdpb, &dpb_type[ 0], LENDPB); /* move 256K dpb */
	dmsdpb -> unit = 0;
	dmsdpb -> control = 0;
	dmsdpb -> hd_volume = vol;
	return dmsdpb;
}
/*===================================================================*/
P0Unassign()
{
	blockmv( dmsdpbs, &savedpb, LENDPB); /* restore old dpb */
	dmsdpb = dmsdpbt; /* save previous active dpb */
}
/*===================================================================*/
#endif
/*=====================================================================*/
#ifndef userbios
userno()
{
#ifdef cpm80
	return ( *( (CHAR *) 0x47 ) );
#else
	GetDMSInfo( &DMSInfo);
	return DMSInfo.netuserno;
#endif
}
/*=====================================================================*/
/*page*/
int	MakAssign( d, part, pw, MakInfo)
CHAR	d;
CHAR	part[ LENpart];
CHAR	pw[ LENpass];
struct	MakInfoStruct *MakInfo;
{
register CHAR *fastptr;
int	i;

	blockmv( MakInfo -> MakName, part, LENpart);
	blockmv( MakInfo -> MakPass, pw, LENpass);

#ifdef cpm80
	if( getdph(d) == 0) return 1;
	if( *part == 0)	/* this is how you do an unassign */
		{dmsdpb -> media_type = unassigned;
		dmsdpb -> unit = unassigned;
		return 0;
		}
#ifndef fcbios
   if( floppy)
	{
	blockmv( dmsdpb, &dpb_floppy[ floptype], LENDPB);
	dmsdpb -> unit = *(part+1) - '0';
	blockmv( &(dmsdpb -> drive_name), part , LENpart);
	}
#endif
   else
	{
	MakInfo -> MakCom = 0x17;
	fastptr = &(MakInfo -> MakSize);
/* band aid to retry several times because called routines still
  too slow for z80 side of 816 */ 
	for(i = 25; ; --i)
	{
		nakpoll();
		sendnet( masteruser, LassignSend, MakInfo);
		if( recnet( LassignRec, fastptr ) ) 
		{	ackpoll();
			if(i <= 0)
				return(1);
			continue;
		}
		ackpoll();
		break;
	}
#ifdef dbg
	printf("assigned size= %d, unit= %d, ctl= %x, vol= %d\n",
		MakInfo -> MakSize, MakInfo -> MakUnit,
		MakInfo -> MakCtl, MakInfo -> MakVolume);
	printf("os bits=%x,curos=%x\n",getbits(MakInfo->MakCtl,2,3),curOS );
#endif
	if( getbits( MakInfo -> MakCtl, 2, 3) != curOS) return 1;
	if( MakInfo -> MakUnit == 0xff ) return 1;
	blockmv( dmsdpb, &dpb_type[ MakInfo -> MakSize-1],
		!strncmp( (CHAR *) ( *( (int *) (6) ) + 0xf ), "HIDOS", 5 ) 
		 	? LENshared : LENDPB );
	blockmv( &(dmsdpb -> drive_name), MakInfo -> MakName, LENpart);
	dmsdpb -> unit = MakInfo -> MakUnit;
	dmsdpb -> control = MakInfo -> MakCtl;
	dmsdpb -> hd_volume = MakInfo -> MakVolume;
#ifndef fcbios
	if( localhd)
		dmsdpb -> media_type = ( hd_controller() == XEBEC ) ?
					HD15media :
					hardmedia;
#endif
   } /* end if not floppy */

	return 0; /* all is ok */
#else
	if( *part <= 1)		/* U: or ULTRA0 */
		blockmv( &(MakInfo -> MakSize), part, LENpart);
	if( *part == 1)		/* ULTRA0 */
		{
		blockmv( &(MakInfo -> MakName), "ultra0  ", LENpart);
		MakInfo -> MakType = RAMmedia;
		}
	if( *part > 1)
		MakInfo -> MakType = NETmedia;
	MakInfo -> MakCtl = curOS;
	MakInfo -> MakDrv = d;
#ifdef dbg
	printf("\ntype: %x, unit: %x\n", MakInfo->MakType, MakInfo->MakUnit);
#endif
	return bios86( 3, 0x15, MakInfo);
#endif
}
/*===================================================================*/
/*page*/
writemode( WMparm)
struct WMstruct *WMparm;
{
#ifdef cpm80
#ifdef newwm
	dmsbioshl( newwm, 0, 0, &(WMparm -> wmrec.WMack), 
			&(WMparm -> wmsend.WMcmd), 0, 0 );
	if( WMparm -> wmrec.WMack == 0x4d )
		return 0;
	else
		return 1;
#else
	WMparm -> wmsend.wmcmd = WMCMD;
	nakpoll();
	sendnet( masteruser, sizeof( struct WMsend), &(WMparm -> wmsend.wmcmd) );
	recnet( sizeof( struct WMrec), &(WMparm -> wmrec.WMack) );
	ackpoll();
	return 0;
#endif
#else
	bios86( 3, 42, &(WMparm -> wmsend.WMcmd) );
	return WMparm -> wmrec.WMack;
#endif
}
#endif /* userbios */
/*=============================================================*/
#ifdef cpm80
getdph(d)
int	d;
{
	CCPdisksave = *CCPdisk; /* when seldisk selects a nonexistant drive */
	dph = seldisk( d);
#ifdef debug
	printf("curdisk: %x, dph: %x, driveno: %x\n", curdisk, dph, d);
#endif
	if (dph == 0) /* drive does not exist */
		{
		if( seldisk( CCPdisksave) == 0 )
		       printf("***Error: default drive reselection failure\n");
		*CCPdisk = CCPdisksave; /* restore location 4 */
		return 0; 
		}
	dmsdpb = (dph -> dpb_address) - (LdmsDPBoffset + 1);
	return dph;
}
#endif
/*===================================================================*/
#ifndef userbios
int	GetAssign( driveno, GetInfo)
int	driveno;
struct	GetInfoStruct *GetInfo;
{
#ifdef	cpm80

	if( getdph( driveno) == 0) return 1; /* drive does not exist */
	GetInfo -> GetDrv = driveno;
	blockmv( GetInfo -> GetName, dmsdpb -> drive_name, DRIVENAMESIZE);
	*( GetInfo -> GetName ) &= 0x7f;

/* determine size in number of K */
	GetInfo -> GetSize = (dmsdpb -> disk_size_mask + 1) << 
			( (dmsdpb -> block_shift) - blkshf1k );
#ifdef debug
	printf( "dsm = %d, bls = %d, size = %d\n", dmsdpb -> disk_size_mask,
		dmsdpb -> block_shift, GetInfo -> GetSize);
#endif
	GetInfo -> GetUnit = dmsdpb -> unit;
	GetInfo -> GetCtl = dmsdpb -> control;
	GetInfo -> GetVol = dmsdpb -> hd_volume;
	GetInfo -> GetType = (dmsdpb -> media_type );
	return 0;
#else
	GetInfo -> GetDrv = driveno; /* pass drive number being queried */
	return bios86( 3, 0x16, GetInfo);
#endif
}
/*=============================================================*/
/*page*/
#ifndef cpm80
/*==============================================================*/
/*	GetWho	- Invokes BIOS to get user and spool info	*/
/*==============================================================*/
int GetWho (buff)
CHAR	*buff;
{
/*	return bios86 ( 3, 0x1C, buff);*/
	return bios86 (	3, 27, buff);
}
/*==============================================================*/
/*	GetDateTime	- Invokes BIOS to get date and time	*/
/*==============================================================*/
int GetDateTime (dt)
struct DATETIME	*dt;
{int	tmp;
	/* currently the 86 BIOS has a bug that this function returns
	   DD when it succeeds.  The following lines should be changed
	   to return bios86 (); when this bug is fixed			*/
/*	tmp = bios86 ( 3, 0x23, dt);*/
	tmp = bios86 ( 3, 34, dt);
	if (tmp = 0xDD) return 0;
	else return tmp;
}
/*==============================================================*/
/*	GetDirNet	- Invokes BIOS to get disk partition	*/
/*			  info					*/
/*==============================================================*/
GetDirNet (vol, dp)
int	vol;
CHAR	*dp;
{
	*dp = vol;
/*	return bios86 ( 3, 0x1D, dp);*/
	return bios86 (	3, 28, dp);
}
/*=============================================================*/
GetDMSInfo ( buff)
CHAR	*buff;
{
	return bios86 ( 3, 41, buff);
}
#endif /* not cpm80 */
/*=============================================================*/
/*page*/
#define notimeout 0x80
#define crc_ovr ( 0x40 | 0x20 )
#define underrun 0 /* replace with real when Joel is ready: 0x08 */
#define validpoll 0x01
#define pollmask ( notimeout | crc_ovr | validpoll )
#define pollok ( notimeout | validpoll )
nakpoll()
{
#ifdef cpm80
	fastuser = userno();
	if( fastuser == masteruser || localhd ) return;
	donak = TRUE; 	/* defer nakpoll to first sendnet */
	ackpoll(); 	/* in case out of sync */
#else
	bios86( 1, 36 );
#endif
}
/*===============================================================*/

ackpoll()
{
#ifdef cpm80
	if( fastuser == masteruser || localhd ) return;
	bioshl( 40, 0, 0);
#else
	bios86( 1, 35 );
#endif
}
/*===============================================================*/

sendnet( user, len, msg)
int	user, len;
CHAR	*msg;
{
int	i, j;

#ifdef cpm80
#ifndef fcbios
	if( localhd )
		{
		for( j=1; (j<10) && ( sendfail = !hd_init() ); ++j)
			{
			displays( "** waiting for hard disk$" );
			for( i=1; ( i<55 ) && ( sendfail = !hd_init() ); ++i) 
				displays( ".$" );
			}
		if( sendfail ) 
			return;
		if( hd_controller() == XEBEC)
			send15( msg, len);
		else
			{
			h_dms_hd.cmd = *msg;
			if( !dms_cmd() ) 
				{
				sendfail = TRUE;
				return;
				}
			if( !dms_send( msg+1, len-1) )
				{
				sendfail = TRUE;
				return;
				}
			}
		return;
		};
#endif
	if( fastuser == masteruser )
		{netmsg = *( *biosptr - 3 + 0x5d + 3*5 + 1 );
		blockmv( netmsg+2, msg+1, len-1);
		netmsg[1] = *msg;
		}
	else
		{
		if( donak) /* keep nakpoll near first send for timing */
			{
			donak = FALSE;
			while( !naksend( user, len, msg) ) 
				{
				ackpoll(); /* re-synchronize dma */
				printf("*waiting %x*\n", netcode );
				}
			}
		else
			sendagain( user, len,  msg);
		}
	return;
#else
struct	{
	CHAR SendUser;
	int SendOffset;
	int SendSegment;
	int SendLength;
	}	SendParm;

	SendParm.SendUser = user;
	SendParm.SendOffset = msg;
	segread( &rrv);
	SendParm.SendSegment = rrv.dsreg;
	SendParm.SendLength = len;

	return bios86( 3, 37, &SendParm );
#endif
}
/*==============================================================*/
/*page*/
recnet( len, msg)
int	len;
CHAR	*msg;
{
register CHAR	retcode;
#ifdef cpm80
#ifndef fcbios
	if( localhd)
		{
		if( sendfail)
			{
			sendfail = FALSE;
			return 1;
			}
		if(hd_controller() == XEBEC )
			rec15( msg, len);
		else
			{
			if( !dms_res() ) return 1;
			blockmv( msg, h_dms_hd.status, len);
			}
		return 0;
		}
#endif
	if( fastuser == masteruser )
		{while( netmsg[1]); /* wait here for it to zero out */
		blockmv( msg, netmsg+2, len);
		}
	else
		{reclen = len;
		recmsg = msg;
		if( receive() ) 
			{
#ifdef dont
			printf( "*net error %x*", netcode);
#endif
			return 1;
			}
		}
	return 0;
#else
struct 	{
	CHAR RecUser;
	int RecOffset;
	int RecSegment;
	int RecLength;
	}	RecParm;

	RecParm.RecUser = userno();
	RecParm.RecOffset = msg;
	RecParm.RecSegment = rrv.dsreg; /* assume segread called by sendnet */
	RecParm.RecLength = len;

	return bios86( 3, 38, &RecParm);
#endif
}
#endif /* userbios */

/*===============================================================*/
/*page*/
#ifdef cpm80
#ifndef userbios
#ifndef fcbios
send15( msg, len)
CHAR *msg;
int len;
{
int subunit;
int sect;

	lclhdstat( &hardstat);
	assparm.vol = 0;
	P0Assign( 0, assparm.vol);
	dmsdpb -> media_type = HD15media;
	setdma( alloctab);
	settrk( 0);
	seldisk( 0);
		
	do	/* each volume */
		{
		if( hardstat.volume[ assparm.vol].volinfo[0] == 0 )
			continue;
		dmsdpb -> hd_volume = assparm.vol;
		assparm.unit = 0;
		for( sect = 0x79; sect <= 0x80; sect++)
			{
			setsect( sect);
			biosread();
			for( subunit = 0; subunit <= (128/16)-1; subunit++)
				{
				if( strncmp( alloctab[ subunit].pname, 
					msg+1, len-1) == 0 )
					{
					assparm.control 
						= alloctab[ subunit].ctl;
					assparm.size
						= alloctab[ subunit].siz;
					sendfail = FALSE;
					return;
					}
				assparm.unit++;
				}; 	/* next subunit */
			};	/* next sector */
		} while( assparm.vol++ < 3 ); /* next volume */
	sendfail = TRUE;
	P0Unassign();
	return;
}
/*=================================================================*/
rec15( msg, len)
CHAR *msg;
int len;
{
	P0Unassign();
	blockmv( msg, &assparm, len);
}
#endif /* fcbios */
/*=================================================================*/
#asm
;
; Code time critical routines into assembler:
;
BASE	equ	0
;
sendnet: jmp	0
recnet:	jmp	0
nakpoll: jmp	0

	common	/netcode_/
netcode: db	0
	common	/fastuser/
recuser: db	0
	common	/reclen_/
reclen:	dw	0
	common	/recmsg_/
recmsg:	dw	0

;
; NAKSEND - codes the nakpoll and sendnet together - needed to ensure
;	    that the send will be done in time for the z816 case
;
	cseg
	public	naksend_
naksend_:
	push	b
	lhld	BASE+1
	xchg
	lxi	h,6ch
	dad	d
	lxi	d,sendnet
	lxi	b,9
;	ldir
	db	0edh
	db	0b0h
	call	nakpoll
	sta	netcode
;	bit	0,a
	db	0cbh
	db	047h
	jnz	.cont
;	xra	a	; force fail
	pop	b
	ret
;
; SENDAGAIN - assumes nakpoll in progress, alternate entrypoint is used
;	      above after nakpoll succeeds
;
	public	sendagain
sendagain:
	push	b
.cont:
	lxi	h,4
	dad	sp
	mov	a,m
	inx	h
	inx	h
	mov	c,m
	inx	h
	mov	b,m
	inx	h
	mov	e,m
	inx	h
	mov	d,m
	xchg
	call	sendnet
	ori	1	; force success
	pop	b
	ret
;
; RECEIVE - receives message from network
;
	public	receive_
receive_:
	push	b
	lda	recuser
;	lbcd	reclen
	db	0edh
	db	04bh
	dw	reclen
;	lhld	recmsg  ;assume recmsg set up (caller beware)
	call	recnet
	sta	netcode
	ani	0e1h
	cpi	080h
; zero is success here!
	pop	b
	ret

#endasm
/*=================================================================*/
#endif /* userbios */
/*page*/
/*	Standard CPM bios jump entry points, plus DMS extensions 
	calling sequence: dmsbioshl(fnc, a, bc, de, hl)		*/

wboot() { bios( 1, 0, 0); } /*ok*/
seldisk( d) { return dmsbioshl( 9, 0, d, 0, 0); }
settrk( t) { return bios( 10, t, 0); }
setsect( s) { return bios(11, s, 0); }
setdma( a) { return bios( 12, a, 0); }
biosread() { return bios( 13, 0, 0); }
bioswrite( bc) { return bios( 14, bc, 0); }
sectran( s, t) { return dmsbioshl( 16, 0, s, t, 0); }
netlock() { return dmsbioshl( 32, 0, 0, 0, 0); }
cpmmap() { return dmsbioshl(33, 0, 0, 0, 0); } /*ok*/
netunlock() { return dmsbioshl( 34, 0, 0, 0, 0); }
lclhdstat( bc) { return dmsbioshl( 44, 0, bc, 0, 0); }
hdrest( bc) { return dmsbioshl( 48, 0, bc, 0, 0); }

#endif	/* cpm80 all the way back to send15 */

hdstat( buffer)
{
#ifdef cpm80
	return dmsbioshl( 45, 0, buffer, 0, 0);
#else
	return bios86( 3, 33, buffer);
#endif
}

displays¨ s© û bdos¨ 9¬ s)» ý /ª displaù strinç thaô endó iî ¤ */

/*===========================================================*/

