/*	memory-'tape' routines for first test of ZTDRIVER ...
	w.j.m. jun 1989
*/

#ifndef TRACE
#define TRACE 1
#endif

#include ssdef

#include stdio
#include stddef
#include string
#include descrip
typedef struct dsc$descriptor DESCR;

#define CHECK(x) do {unsigned s=x; if(!(s&1)) LIB$STOP(s);} while(0)
#define FEHLER(m) do {$DESCRIPTOR(d,m); Fehler(&d);} while(0)

/* data & entries used by "main" program */

extern unsigned char *bufp;
extern unsigned long *bufbctp;

extern int /*logical*/ hw_online,hw_medonline,hw_bot,hw_eof,hw_eot,hw_hwl;

/* forward */
void tape_init(void);
unsigned tape_writemark(void),
	tape_nop(void),
	tape_rewind(int/*logical*/ /*unload*/),
	tape_skiprec(int /*+-#blocks*/,int * /*#blocks skipped*/),
	tape_compare(int/*logical*/ /*reverse*/),
	tape_read(int/*logical*/ /*reverse*/,int/*logical*/ /*check*/),
	tape_write(int/*logical*/ /*check*/);

#if TRACE
static void trace_tape(char *,int,int,int);	/* forward */
static int tpos;	/* true position (in blocks) */
#endif

/* the 'memory tape' */
/* structure:
	short l1; char [l1]; short l1; short l2; char [l2]; short l1; ...
		l=0 stands for end of written area,
		l=1 stands for tape mark
*/

#define TAPELEN 2000000			/* bytes */
#define TAPEEOT (TAPELEN-200000)	/* room after EOT */
static unsigned char tm[TAPELEN], *tp;

#define T_eotape 0
#define T_mark 1

#define T_nextlen (*((unsigned short *) tp))
#define T_prevlen (*(((unsigned short *) tp) - 1))
#define T_data (tp + sizeof(unsigned short))

#define T_bot (tp == tm)
#define T_eot (tp >= (tm + TAPEEOT))

#define T_toomuch(l) (tp >= (tm + TAPELEN - 3*sizeof(unsigned short) - l))

#define T_prevpos (tp - (T_prevlen + 2*sizeof(unsigned short)))
#define T_nextpos (tp + (T_nextlen + 2*sizeof(unsigned short)))


/*****/

void tape_init(void)
{

	tp = tm;
	T_nextlen = T_eotape;
#if TRACE
	tpos = 0;
#endif

	hw_online = 1;
	hw_medonline = 1;
	hw_hwl = 0;

	hw_eof = 0;
	hw_bot = T_bot;
	hw_eot = T_eot;
}

unsigned tape_writemark(void)
{
#if TRACE
	trace_tape("WRITEMARK",0,0,0);
#endif
	if(!hw_online) return SS$_DEVOFFLINE;
	if(!hw_medonline) return SS$_MEDOFL;

	if(T_toomuch(T_mark)) return SS$_UNSAFE;

	T_nextlen = T_mark;
	tp = T_nextpos;
	T_prevlen = T_mark;
	T_nextlen = T_eotape;
#if TRACE
	tpos ++;
#endif

	hw_eof = 1; 		/* ??? */
	hw_bot = T_bot;
	hw_eot = T_eot;

	return SS$_NORMAL;
}

unsigned tape_nop(void)
{
#if TRACE
	trace_tape("NOP",0,0,0);
#endif
	/* tape status does not change ... */
	if(!hw_online) return SS$_DEVOFFLINE;
	if(!hw_medonline) return SS$_MEDOFL;
	return SS$_NORMAL;
}

unsigned tape_rewind(int/*logical*/ unload)
{
#if TRACE
	trace_tape("REWIND",1,unload,0);
#endif
	if(!hw_online) return SS$_DEVOFFLINE;
	if(!hw_medonline) return SS$_MEDOFL;

	/* leave (mysteriously) medium online in spite of unload */
	/* i.e. 'unload' is ignored(!) */

	tp = tm;
#if TRACE
	tpos = 0;
#endif

	hw_eof = 0;
	hw_bot = T_bot;
	hw_eot = T_eot;

	return SS$_NORMAL;
}

unsigned tape_skiprec(int sb,int *sap)
/* sb = (signed) # blocks to be skipped
   sap = pointer to return value: absolute # blocks skipped */
/* terminate after EOF mark, or at BOT */
{
	unsigned xstat;


#if TRACE
	trace_tape("SKIPREC",1,sb,0);
#endif
	if(!hw_online) return SS$_DEVOFFLINE;
	if(!hw_medonline) return SS$_MEDOFL;

	*sap = 0;

	xstat = SS$_NORMAL;	/* assume success, 
				this includes EOF & BOT termination */
	if(sb > 0) {
		do {
			if(T_nextlen == T_eotape) {
				xstat = SS$_TAPEPOSLOST;
				break;
			}
			hw_eof = (T_nextlen == T_mark);
			tp = T_nextpos;
#if TRACE
			tpos ++;
#endif
			(*sap) ++;
		} while(!hw_eof && (*sap < sb));
	} else if(sb < 0) {
		do {
			if(T_bot) break;
			hw_eof = (T_prevlen == T_mark);
			tp = T_prevpos;
#if TRACE
			tpos --;
#endif
			(*sap) ++;
		} while(!hw_eof && ((*sap + sb) < 0));
	}

	hw_bot = T_bot;
	hw_eot = T_eot;

	return xstat;
}

unsigned tape_compare(int/*logical*/ reverse)
/* (buffer,bufbct) has memory data.
	compare to tape block,
	return length of tape block in bufbct */
{
	unsigned xstat;


#if TRACE
	trace_tape("COMPARE",1,reverse,0);
#endif
	if(!hw_online) return SS$_DEVOFFLINE;
	if(!hw_medonline) return SS$_MEDOFL;

	if(reverse) return SS$_BADPARAM;	/* reverse not supported */


	xstat = SS$_NORMAL;	/* assume success, includes EOF read */

	if(T_nextlen == T_eotape) {
		xstat = SS$_TAPEPOSLOST;
	} else {
		hw_eof = (T_nextlen == T_mark);
		if(hw_eof) {
			*bufbctp = 0;
		} else {
			if(*bufbctp < T_nextlen ||
			   memcmp(T_data,bufp,T_nextlen)) {
				xstat = SS$_DATACHECK;
			}
			*bufbctp = T_nextlen;
		}
		tp = T_nextpos;
#if TRACE
		tpos ++;
#endif
	}

	hw_bot = T_bot;
	hw_eot = T_eot;

	return xstat;
}


unsigned tape_read(int/*logical*/ reverse, int/*logical*/ check)
/* put data into (buffer,bufbct) */
/* 'check' (ignored) requests data checking */
{
#if TRACE
	trace_tape("READ",2,reverse,check);
#endif
	if(!hw_online) return SS$_DEVOFFLINE;
	if(!hw_medonline) return SS$_MEDOFL;

	if(reverse) return SS$_BADPARAM;	/* reverse not supported */

	if(T_nextlen == T_eotape) return SS$_TAPEPOSLOST;

	hw_eof = (T_nextlen == T_mark);
	if(hw_eof) {
		*bufbctp = 0;
	} else {
		*bufbctp = T_nextlen;
		memcpy(bufp,T_data,T_nextlen);
	}
	tp = T_nextpos;
#if TRACE
	tpos ++;
#endif

	hw_bot = T_bot;
	hw_eot = T_eot;

	return SS$_NORMAL;
}

unsigned tape_write(int/*logical*/ check)
/* process data from (buffer,bufbct) */
/* 'check' (ignored) requests data checking */
{
#if TRACE
	trace_tape("WRITE",1,check,0);
#endif
	if(!hw_online) return SS$_DEVOFFLINE;
	if(!hw_medonline) return SS$_MEDOFL;

	if(T_toomuch(*bufbctp)) return SS$_UNSAFE;

	T_nextlen = *bufbctp;
	memcpy(T_data,bufp,*bufbctp);
	tp = T_nextpos;
	T_prevlen = *bufbctp;
	T_nextlen = T_eotape;
#if TRACE
	tpos ++;
#endif

	hw_eof = 0;
	hw_bot = T_bot;
	hw_eot = T_eot;

	return SS$_NORMAL;
}


/*****/

#if TRACE

static void trace_tape(char *fname,int ac,int a1,int a2)
{
	fprintf(stdout,"\tTAPE_%s(",fname);
	if(ac > 0) {
		fprintf(stdout,"%d",a1);
		if(ac > 1) {
			fprintf(stdout,",%d",a2);
		}
	}
	fprintf(stdout,") tpos=%d\n",tpos);
}

#endif
