/* *******************************************************************
 *								     *
 *		     Log the User Out Fast			     *
 *		   last updated 02/21/84 dsb			     *
 *								     *
 ******************************************************************* */

/* #define DBUG YES */

#define VERSION     1
#define REVISION    0
#define PATCH	    'b'

#define Z80 YES
/* #define DMS86 YES */ 			       

#include "stdio.h"

/* *********************** REVISION HISTORY ****************************

1.0a	02/01/84    First try.	Has some hooks for 86's but these are
		      false fronts only.  Works on Z80's.
   b	02/21/84    Uses register variables for speed in tests.  Will
		      retry error from nackpoll (2 tries before death)
		    Timeout in recnet causes message suggesting Master
		      may be running an old system that doesn't ack a
		      logout request.

*********************************************************************** */

/*page*/
/* ********************* MISCELLANEOUS EQUATES *********************** */

#define ever (;;)
#define TRUE  1
#define FALSE 0
#define LOUTNET  0x1F
#define LOUTACK  'A'
#define LOUTDENY 'D'

#define TIMEOUT  0x80	/* timeout error mask, dead if low  */
#define CRC_OVR  0x60	/* error mask for crc or overrun (high)  */
#define POLL_ONLY 0x01	/* poll-only rcv flag */
#define TimeOut    80h	/* same as above but for assembler  */
#define CrcOvr	   60h	/* error mask for crc or overrun (high)  */
#define PollOnly   01h	/* poll-only rcv flag */

/* ************************* GLOBAL STORAGE **************************** */

char netstat;

#ifdef Z80
char *usernum = 0x47;
char *time = 0x40;
#endif

#ifdef DMS86 
char time[8];
#endif

char loutcom[2];
char loutresp;

/*page*/
main()
{
    register int  retry;
    register int  netok = -1;  
    register char user;

    printf("\nLogout vers. %d.%d%c",VERSION,REVISION,PATCH);

#ifdef Z80
    user = *usernum;
#endif

#ifdef DMS86			 /* 86's must ask master for time */
    get_real_time(time);	 /* so ask BEFORE logging out! */
    user = user_num();		 /* and they get the user number differently */
#endif

    if(!user)
	fatal("You can't log out the Master.");
    else if(user == 0xFF)
	fatal("You can't log out from HiNet because you aren't logged in.");


    loutcom[0] = LOUTNET;	 /* set up net request */
    loutcom[1] = user;


    for(retry= 0;  retry < 2; ++retry)	 /* wait for a poll, give it 2 tries */
	if(netok = nackpoll())		/* break out quick if poll rcvd */
	    break;

    if(!netok)			 /* netok is a register variable so this */
	neterror("nackpoll");	 /* test shouldn't slow us down */

    sendnet();			 /* if nackpoll returned ok, send request */

    if(!(netok = recnet()))
	if((!(netstat & TIMEOUT)) || (netstat & POLL_ONLY))  /* total miss */
	{
	    printf("\nMaster doesn't acknowledge logout request.");
	    fatal("(Master needs BIOS version 2.247e or later.)");
	}
	else			    /* general errors are handled here */
	    neterror("recnet");


/* main is continued on the next page */
/*page*/

    switch(loutresp)
    {
	default:	printf("\nbad net response 0x%x",loutresp);
			fatal(NULL);
			break;
	case LOUTACK:	*usernum = 0x80; /* make sure we never ack another */
			break;
	case LOUTDENY:	fatal("logout denied!");
			break;
    }

    printf("\n\n\n\n\n\n\n\n\n\n\n\n\n");
    printf("\nuser %02x logged out at %02d:%02d:%02d",loutcom[1],
		time[3],time[2],time[1]);
    printf("\n\n\n\n\n\n\n\n\n\n\n\n\n");

    sabotage();
} /* main */

/* page */

sabotage()
{
    unsigned *bdosbase = 0x06;	  /* erase reserved memory from bdos to FFFF */
    unsigned death_length;	  /* and loop endlessly in the TPA */

    death_length = 0 - *bdosbase;
    clear(*bdosbase, death_length, '\0');

    for ever 
	;
} /* sabotage (wipe out the system) */





fatal(msg)	    /* A fatal error was found.  Print message, try to boot. */
char *msg;	    /* msg may be NULL but it had better be passed anyway! */
{
    if(msg)
	printf("\n%s",msg);
    exit(-1);
} /* fatal (fatal error found) */




#ifdef Z80

neterror(str)		/* interpret a net xmission error */
char *str;
{
    if(!(netstat & TIMEOUT))
	printf("\nNetwork not operational -- timeout in %s",str);
    else if(netstat & CRC_OVR)
	printf("\nNet error (crc or overrun) in %s",str);
    else
	printf("\nUnknown net error (status %02x) in %s",netstat,str);
    fatal(NULL);
} /* neterror */

#endif

/*page*/
/* ***************** ASSEMBLY ROUTINES FOR BIOS INTERFACE ************** */

#ifdef Z80

#asm		       /* wait for poll, don't ack it; return rcv status in  */
	PUBLIC	nackpoll      /* netstat. Return TRUE iff status is ok. */
nackpoll:
	push	B
	lxi	H,nackret	/* addr BIOS sould return to  */
	push	H
	lhld	1	/* warmboot address */
	lxi	D,72h
	dad	D
	pchl
nackret:			/* interpret results */
	sta	netstat_	/* save acual return status */
	ani	TimeOut OR CrcOvr OR PollOnly	/* relevant bits */
	cpi	TimeOut OR PollOnly	       /* these high, rest low if ok */
	jnz	nackbad 	/* return w/zero */
	mvi	A,1		/* return good */
	jmp	nackout 
nackbad: xra	 A
nackout: mov	L,A
	mvi	H,0
	ora	A
	pop	B
	ret


	PUBLIC	sendnet_     /* send a logout request from loutcom */
sendnet_:		     /* 6Ch */
	push	B	     /* social responsibility */
	lxi	H,snret      /* addr for BIOS to return to */
	push	H
	lhld	1	     /* here's wboot */
	lxi	D,6Ch
	dad	D	     /* sendnet address */
	push	H	     /* addr to go to in BIOS  */
	lxi	H,loutcom_   /* set up BIOS call: block addr in HL */
	lxi	B,2	     /* byte count in BC */
	xra	A	     /* destination (master) in A */
	ret		     /* "ret" to BIOS sendnet */
snret:			     /* which comes back to here */
	pop	B	     /* restore caller's BC */
	ret

/*page*/
	PUBLIC	recnet_       /* receive response from master  */
recnet_:		      /* put result in loutresp,  */
			      /* rcv status in netstat.  */
			      /* return TRUE iff status is ok. */
	push	B	     /* social responsibility */
	lxi	H,rnret      /* addr for BIOS to return to */
	push	H
	lhld	1	     /* here's wboot */
	lxi	D,6Fh
	dad	D	     /* BIOS recnet address */
	push	H	     /* addr to go to in BIOS  */
	lxi	H,loutresp   /* set up BIOS call: block addr in HL */
	lxi	B,1	     /* byte count in BC */
	lda	47h	     /* our own user number in A */
	ret		     /* "ret" to BIOS recnet */
rnret:			     /* which comes back to here */
	sta	netstat_
	ani	TimeOut OR CrcOvr OR PollOnly
	cpi	TimeOut
	jnz	rnbad
	mvi	A,1
	jmp	rnout
rnbad:	xra	A
rnout:	mov	L,A
	mvi	H,0
	ora	A
	pop	B	     /* restore caller's BC */
	ret

#endasm

#endif

	jmp	rnout
rnbad