DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T v

⟦dd7aa259f⟧ TextFile

    Length: 19417 (0x4bd9)
    Types: TextFile
    Names: »verser2.c«

Derivation

└─⟦060c9c824⟧ Bits:30007080 DKUUG TeX 2/12/89
    └─⟦this⟧ »./DVIware/laser-setters/umd-dvi/dev/verser2.c« 

TextFile

/*
 * Copyright (c) 1987 University of Maryland Department of Computer Science.
 * All rights reserved.  Permission to copy for any purpose is hereby granted
 * so long as this copyright notice remains intact.
 */

#ifndef lint
static char rcsid[] = "$Header: verser2.c,v 2.5 87/06/16 17:15:19 chris Exp $";
#endif

/*
 * Verser2 -- Second half of DVI to Versatec driver
 *
 * Reads pre-sorted pages as put out by verser1, and shovels bitmaps
 * out to the Versatec as fast as possible.  Warning:  there is some
 * inline assembly code used in inner loops, where the C compiler
 * produced particuarly poor code.
 *
 * We use a technique known as a `band buffer', where we keep track
 * of what has yet to be written to the Versatec in a buffer that
 * represents a `band' across the current page, analagous to a magnifying
 * bar across the page.  Only the region in the band can be written,
 * and the band moves only downward; this is why verser1 must sort
 * each page, at least by y coordinate.  This also implies that the
 * `tallest' object we can write is the same height as the band.  This
 * is a problem for large characters.  For these there is some (as yet
 * unimplemented) code that will ask for a `part' of each character
 * to be drawn into the band.  The character would then be repeated
 * with a request for the next part when the band has moved down to
 * just below the bottom of the previous part.  Rules are also broken
 * up as appropriate (and that code *is* implemented).
 *
 * Another important point is that the band buffer is treated as a
 * `cylinder' rather than a `strip': we write bits onto the cylinder,
 * then roll it forward over the page, moving the bits off the cylinder
 * and onto the paper, leaving that part of the cylinder clean, ready
 * for more bits.  The variable `CurRow' points at the current row
 * in the buffer/on the cylinder, and `FirstRow' and `LastRow' bound
 * the `dirty' part of the cylinder.  Modular arithmetic suffices to
 * change linear to cylindrical.
 *
 * Whenever CurRow is more than MIN_OUT rows ahead of FirstRow, we
 * write out that much of the cylinder, cleaning it.  This keeps the
 * paper moving, lest the developer soak one spot and produce `streaky'
 * output.
 *
 * Yet another point of note is that because the band always moves
 * `down' on the page, we need only a positive offset from the current
 * row to move to a new row.  This means (among other things) that we
 * can use negative offsets for special purposes.
 */

#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <sys/vcmd.h>
#ifdef ACCOUNT_FILE
#include <pwd.h>
#endif ACCOUNT_FILE
#include "types.h"
#include "conv.h"
#include "fio.h"
#include "font.h"
#include "verser.h"

#define	SPEED_HACK

char	*ProgName;
extern int errno;
extern char *optarg;
extern int optind;

/* Globals */
jmp_buf	failbuf;		/* in case of Versatec write() problems */

struct font *Fonts[NFONTS];	/* the fonts */

char	TeXFontDesc[256];	/* getenv("TEXFONTDESC") from verser1 */

int	RasterOrientation;	/* ROT_NORM or ROT_RIGHT, based on HFlag */

int	DFlag;			/* -d => output discarded */
int	HFlag;			/* -h => horizontal (rotated bitmaps) */
int	SFlag;			/* -s => silent processing */
int	TFlag;			/* -t => output to tape */
int	Debug;			/* -D => debug flag */

char	VBuffer[ROWS][COLUMNS]; /* Versatec band buffer */

int	CurRow;			/* current row in buffer */
int	CurCol;			/* current column in buffer */
int	FirstRow;		/* the first row used */
int	LastRow;		/* the last row used */
int	NLines;			/* counts lines; used for pagefeeds */
int	Pages;			/* counts pages; for accounting */

int	vp;			/* Versatec file descriptor */

int	pltmd[] = {VPLOT, 0, 0};/* print & plot mode, for ioctl */
int	prtmd[] = {VPRINT, 0, 0};

#ifdef ACCOUNT_FILE
struct passwd *getpwuid();
#endif ACCOUNT_FILE

/*
 * RowsBetween tells how many rows (in cylindrical arithmetic) there
 * are between the first position and the second.  If the second value
 * is less than the first value, add ROWS to do the appropriate modular
 * arithmetic.  We cannot use `%' as C `%' is machine-dependent with
 * respect to negative values.
 */
#define RowsBetween(f, n) ((n) >= (f) ? (n) - (f) : (n) - (f) + ROWS)

/*
 * This is it... on your marks ... get set ... main!
 */
main(argc, argv)
	int argc;
	register char **argv;
{
	register int c;
	register char *s;
	int dpi, usermag, num, denom, dvimag;
	int VFlag = 0;
#ifdef ACCOUNT_FILE
	int acct_fd;

	acct_fd = open(ACCOUNT_FILE, 1);
	(void) setuid(getuid());
#endif ACCOUNT_FILE

	ProgName = *argv;

	if (setjmp(failbuf)) {	/* still have to do accounting */
#ifdef ACCOUNT_FILE
		/*
		 * Kind of strange to charge for a printout that failed
		 * because we ran out of paper, but that was the way they
		 * wanted it....
		 */
		if (NLines)
			Pages++;/* count the partial page */
		DoAccount(acct_fd);
#endif ACCOUNT_FILE
		exit(1);
		/* NOTREACHED */
	}
	while ((c = getopt(argc, argv, "dstv:D")) != EOF) {
		switch (c) {

		case 'd':	/* output to /dev/null */
			DFlag++;
			break;

		case 's':	/* silent processing except for errors */
			SFlag++;
			break;

		case 't':	/* output to tape (not implemented) */
			TFlag++;
			error(0, 0, "tape option not yet implemented");
			break;

		case 'v':	/* Versatec already open as fd <n> */
			VFlag++;
			vp = atoi(optarg);
			break;

		case 'D':
			Debug++;
			break;

		case '?':
			fprintf(stderr, "Usage: %s [-d] [-s] [-t] [file]\n",
				ProgName);
			exit(1);
		}
	}
	if (optind < argc)
		if (freopen(argv[optind], "r", stdin) == NULL)
			error(1, 0, "can't open %s", argv[optind]);

	HFlag = getchar();
	if ((HFlag >> 1) != VERSION)
		error(1, 0, "input file is not version %d", VERSION);
	HFlag &= 1;
	RasterOrientation = HFlag ? ROT_RIGHT : ROT_NORM;

	s = TeXFontDesc;
	c = GetLong(stdin);
	while (--c >= 0)
		*s++ = getchar();
	if (feof(stdin))
		(void) GetByte(stdin);	/* let GetByte do error */
	*s = 0;

	dpi = GetLong(stdin);
	usermag = GetLong(stdin);
	num = GetLong(stdin);
	denom = GetLong(stdin);
	dvimag = GetLong(stdin);
	SetConversion(dpi, usermag, num, denom, dvimag);

	fontinit(*TeXFontDesc ? TeXFontDesc : (char *) NULL);
	ReadFonts();

	if (DFlag) {
		(void) fprintf(stderr, "Output will be discarded\n");
		(void) fflush(stderr);
		vp = open("/dev/null", 1);
	} else {
		if (!VFlag) {
			vp = open(VERSATEC_FILE, 1);
			if (vp < 0) {
				if (errno == ENXIO)
					error(1, 0, "\
can't open versatec---already in use");
				if (errno == EIO)
					error(1, 0, "\
can't open versatec---device offline");
				error(1, errno, "can't open %s",
					VERSATEC_FILE);
			}
		}
		ioctl(vp, VSETSTATE, pltmd);
	}

	if (!HFlag)
		CutMarks();	/* initial cut marks */

	ReadInput();

	FormFeed(0);		/* end up in print mode */

	if (!SFlag)
		(void) putc('\n', stderr);

#ifdef ACCOUNT_FILE
	DoAccounting(acct_fd);
#endif ACCOUNT_FILE

	exit(0);
}

#ifdef ACCOUNT_FILE
/*
 * Accounting is done by writing the program name ("tex"), the user name,
 * and the number of pages at the end of the file.  (The program name is
 * for statistics.)
 */
DoAccounting(fd)
	int fd;
{
	register struct passwd *p;
	char buf[128];

	if (fd < 0 || (p = getpwuid(getuid())) == 0)
		return;

	/*
	 * The '+ 2' is because there is an extra page at the end, and
	 * because the Versatec does pagefeeds when it is opened.
	 */
	(void) sprintf(buf, "tex %s %d\n", p->pw_name, Pages + 2);
	(void) lseek(fd, 0L, 2);
	(void) write(fd, buf, strlen(buf));
}
#endif ACCOUNT_FILE

/*
 * Read the font definitions.
 *
 * Anti-streak hack: get the rasters ahead of time, #ifdef SPEED_HACK.
 */
ReadFonts()
{
	register struct font *f, **fp;
	register int c;
	register char *s;
#ifdef SPEED_HACK
	register struct glyph *g;
#endif
	i32 mag, dsize;
	char *fname;
	char nm[512];

	if (!SFlag)
		(void) fprintf(stderr, "[fonts:\n");
	fp = Fonts;
	while (GetByte(stdin) == 1) {
		(void) GetLong(stdin);	/* checksum */
		mag = GetLong(stdin);	/* magfactor */
		dsize = GetLong(stdin);	/* design size */
		c = GetLong(stdin);
		s = nm;
		while (--c >= 0)
			*s++ = getchar();
		if (feof(stdin))
			(void) GetByte(stdin);	/* let GetByte do error */
		*s = 0;
		f = GetFont(nm, mag, dsize, "versatec", &fname);
		if (f == NULL) {
			GripeCannotGetFont(nm, mag, dsize, "versatec", fname);
			exit(1);
			/* NOTREACHED */
		}
		if (Debug) {
			(void) fprintf(stderr, "[%s -> %s]\n",
				Font_TeXName(f), fname);
			(void) fflush(stderr);
		}
		if (!SFlag) {
			register char *t = fname;

			s = fname;
			while (*s)
				if (*s++ == '/' && *s)
					t = s;
			(void) fprintf(stderr, " %s\n", t);
		}
#ifdef SPEED_HACK
		for (c = 0; c < 128; c++) {
			g = GLYPH(f, c);
			if (GVALID(g))
				(void) RASTER(g, f, RasterOrientation);
		}
#endif
		*fp++ = f;
	}
	if (!SFlag)
		(void) fprintf(stderr, "]\n");
}

/*
 * Read the input stream, decode it, and put character rasters or rules at
 * the positions given.
 */
ReadInput()
{
	register int yx, fcp, height;

	/*
	 * Loop forever.  I had a `for (;;)' but everything crept off the
	 * right side of the screen. 
	 */
next:
	fGetLong(stdin, yx);	/* position */
	fGetLong(stdin, fcp);	/* character, most likely */
	if (feof(stdin))
		return;		/* done */

	/*
	 * A `position' of -1 indicates either a rule or an end of page.
	 * Anything else is a character.
	 */
	if (yx != -1) {		/* place character */
		register struct glyph *g;
		register struct font *f;
		register int fnum;

		/*
		 * Any delta-y required is stored in the upper 16 bits of yx.
		 */
		if ((height = yx >> 16) != 0)
			MoveDown(height);
		/*
		 * Extract the x, font, char, and part info into CurCol,
		 * fnum, yx, and fcp.
		 */
		CurCol = yx & 0xffff;
		fnum = fcp >> FONTSHIFT;
		yx = (fcp >> CHARSHIFT) & CHARMASK;
		fcp = fcp & PARTMASK;
		f = Fonts[fnum];	/* trusting */
		g = GLYPH(f, yx);

		/*
		 * In case this character does not fit, write
		 * out the used part of the band.  It had better
		 * fit afterward....
		 */
		height = g->g_height;
		if (height >= ROWS - RowsBetween(FirstRow, CurRow))
			DumpTopOfBand();
		if (fcp)	/* cannot handle these yet */
			error(0, 0, "\
part code not implemented; skipping char %d in %s",
				yx, f->f_path);
		else if (HASRASTER(g)) {
#ifdef SPEED_HACK
			/* XXX, but saves time */
			VWriteChar(g->g_raster, height, g->g_width);
#else
			VWriteChar(RASTER(g, f, RasterOrientation),
				height, g->g_width);
#endif
		}
		/* dump if we can do at least MIN_OUT rows */
		if (RowsBetween(FirstRow, CurRow) > MIN_OUT)
			DumpTopOfBand();
		goto next;	/* done with character */
	}

	/*
	 * If the `character' is negative, we need to move down first,
	 * possibly because this is an end-of-page.  If this is not the
	 * end of the page, it must be a rule.
	 */
	if (fcp < 0) {		/* move down */
		yx = -fcp;
		fGetLong(stdin, fcp);	/* junk */
		fGetLong(stdin, fcp);
		if (fcp == 0) {	/* end page */
			/* dump entire band */
			WriteBuf(&VBuffer[0][0], FirstRow, LastRow, 1);
			CurRow = LastRow = FirstRow;
			if (!HFlag) {
				WriteBlanks(yx - NLines);
				CutMarks();
			} else
				FormFeed(1);
			if (!SFlag)
				(void) fprintf(stderr, ".");
			NLines = 0;
			Pages++;
			goto next;	/* all done */
		}

		MoveDown(yx);	/* must be a rule; move down by yx rows */
	}

	/*
	 * At this point we have a rule to put at the current
	 * position, CurRow.
	 */
	height = (fcp & 0xff00) >> 8;
	/* make sure it fits */
	if (height >= ROWS - RowsBetween(FirstRow, CurRow))
		DumpTopOfBand();
	VWriteRule(fcp);
	goto next;		/* done with rule */
}

/*
 * Write the given raster for the given character.
 *
 * Basically, the task is to move bits from the raster to the Versatec
 * buffer.  However, because the character being plotted can be on an
 * arbitrary bit boundary, things are not as simple as we might like.
 * The solution used here is to shift each raster value right, OR it
 * into the buffer, then (at the next location) OR in the bits that
 * `fell off the right edge'.
 */
VWriteChar(rastp, height, width)
	char *rastp;		/* raster pointer */
	int height, width;	/* height & width of char */
{
	register char *bp;	/* Versatec buffer pointer [r11] */
	register char *rp;	/* raster pointer	   [r10] */
	register int rshift;	/* right shift index	   [r9]  */
	register int lshift;	/* left shift index	   [r8]  */
	register int j;		/* width loop downcounter */
	register int o;		/* offset to next row in buffer */
	int row;		/* current row in buffer */
	int col;		/* column in buffer of left edge */
	int i;			/* height loop downcounter */
	int w;			/* raster width (bytes) */

	if ((rp = rastp) == NULL)
		return;		/* an all-white character (`cannot happen') */

	row = CurRow;
	col = CurCol >> 3;
	i = height;
	w = (width + 7) >> 3;
	o = COLUMNS - w;

#if defined(lint) || !defined(vax)
	rshift = CurCol & 7;
	lshift = 8 - rshift;
#else lint || !vax
	rshift = -(CurCol & 7);	/* Vax does '>>' as negative '<<' */
	lshift = 8 + rshift;
#endif lint || !vax
	bp = &VBuffer[row][col];

#define avoiding_shifts_is_faster	/* but is it??? */
#ifdef avoiding_shifts_is_faster
	/*
	 * One out of eight or so times, the shift values will be
	 * zero.  This makes the code run faster.
	 */
	if (rshift == 0) {
		while (--i >= 0) {
			j = w;
			while (--j >= 0)
				*bp++ |= *rp++;
			if (++row >= ROWS) {
				row = 0;
				bp = &VBuffer[0][col];
			} else
				bp += o;
		}
	} else
#endif
	{
		while (--i >= 0) {
			j = w;
			while (--j >= 0) {
#if defined(lint) || !defined(vax)
				*bp++ |= (*rp & 255) >> rshift;
				*bp |= (*rp++ & 255) << lshift;
#else lint || !vax
				/*
				 * THE FOLLOWING ASSEMBLY CODE IS INSERTED
				 * BECAUSE THE COMPILER CAN'T OPTIMIZE THE
				 * C CODE WORTH A DARN
				 */
				asm("	movzbl	(r10)+,r1 # *rp++ & 255");
				asm("	ashl	r9,r1,r0  # >> rshift");
				asm("	bisb2	r0,(r11)+ # *bp++ |=");
				asm("	ashl	r8,r1,r0  # << lshift");
				asm("	bisb2	r0,(r11)  # *bp |=");
#endif lint || !vax
			}
			if (++row >= ROWS) {
				row = 0;
				bp = &VBuffer[0][col];
			} else
				bp += o;
		}
	}

	j = height + CurRow - 1;/* have now set bits this far */
	if (j >= ROWS)
		j -= ROWS;	/* keep it modular */

	/*
	 * There are two cases.  Either the buffer is not currently wrapped,
	 * in which case the regions past LastRow or before FirstRow extend
	 * it; or it is wrapped, in which case the region between LastRow
	 * and FirstRow extends it:
	 *
	 *	    case 1		 case 2
	 *	   --------		--------
	 *	   |      |	last  ->| XXXX |
	 * first ->| XXXX |		|      |
	 *	   | XXXX |		|      |
	 * last  ->| XXXX |	first ->| XXXX |
	 *	   |      |		| XXXX |
	 *	   --------		--------
	 *
	 * The `X's mark the region that is in use; the blank spaces
	 * mark the region that causes the `last' value to change.
	 */
	if (FirstRow <= LastRow) {
		/* first case: not wrapped */
		if (j < FirstRow || j > LastRow)
			LastRow = j;
	} else {
		/* second case: wrapped */
		if (j > LastRow && j < FirstRow)
			LastRow = j;
	}
}

/*
 * Write a rule at the current row according to the (packed) information in
 * 'info'.  This includes the x position and the height and width of the
 * rule.
 */
VWriteRule(info)
	int info;
{
	register char *bp;	/* buffer pointer */
	register int j;
	register int lbits;	/* bits along left */
	register int rbits;	/* bits along right */
	register int o;		/* offset to next row */
	register int i;
	register int full;	/* number of 8 bit words to set */
	register int height;	/* rule height */
	register int width;	/* rule width */
	register int row;
	register int col;

	i = info;
	CurCol = (i & 0x7fff0000) >> 16;
	height = (i & 0xff00) >> 8;
	width = i & 0xff;
	col = CurCol >> 3;
	row = CurRow;
	j = CurCol & 7;		/* bit # of start position */
	lbits = 0xff >> j;	/* bits to set along left edge */
	/* there are 8-j bits set in lbits */
	o = 8 - j - width;
	if (o > 0) {		/* then lbits has o too many bits set */
		lbits >>= o;
		lbits <<= o;	/* puts zeros into o righthand bits */
		rbits = 0;
		full = 0;
	} else {
		i = (CurCol + width) & 7;	/* bit # of ending position */
		rbits = 0xff00 >> i;	/* bits to set along right edge */
		/* there are i bits set in rbits (well, in the low byte) */
		full = (width - i - (8 - j)) >> 3;
	}
	bp = &VBuffer[row][col];
	i = height;

	/* Often "full" is zero, which makes things faster */
	if (full) {		/* oh well */
		o = COLUMNS - full - 1;
		while (--i >= 0) {
			*bp++ |= lbits;
			for (j = full; --j >= 0;)
				*bp++ |= 0xff;
			*bp |= rbits;
			if (++row >= ROWS) {
				row = 0;
				bp = &VBuffer[0][col];
			} else
				bp += o;
		}
	} else {
		o = COLUMNS - 1;
		while (--i >= 0) {
			*bp++ |= lbits;
			*bp |= rbits;
			if (++row >= ROWS) {
				row = 0;
				bp = &VBuffer[0][col];
			} else
				bp += o;
		}
	}
	i = CurRow + height - 1;
	if (i >= ROWS)
		i -= ROWS;
	/*
	 * This is another way of expressing both cases 1 and 2 in
	 * VWriteChar().  I think the other way is likely to be
	 * faster, and characters occur far more frequently; but this
	 * is the more readable by far.
	 */
	if (RowsBetween(FirstRow, LastRow) < RowsBetween(FirstRow, i))
		LastRow = i;
}

/*
 * Dump out the top portion of the band (rows [Firstrow, CurRow)).
 */
DumpTopOfBand()
{

	/*
	 * To exclude CurRow, subtract one, but modularly, modularly!
	 */
	WriteBuf(&VBuffer[0][0], FirstRow, CurRow ? CurRow - 1 : ROWS - 1, 1);
	FirstRow = CurRow;
}

/*
 * Move the current row in the band buffer down by delta rows, by,
 * if necessary, writing out the currently-used portion of the buffer.
 */
MoveDown(delta)
	register int delta;
{

	if (delta >= ROWS - RowsBetween(FirstRow, CurRow)) {
		/*
		 * Need to roll the cylinder forward.  Write out the used
		 * part, and then write as many blank lines as necessary.
		 */
		WriteBuf(&VBuffer[0][0], FirstRow, LastRow, 1);
		WriteBlanks(delta - RowsBetween(CurRow, LastRow) - 1);
		CurRow = LastRow = FirstRow;	/* band is now empty */
	} else {
		/*
		 * Because RowsBetween returns nonnegative integers, we
		 * know delta <= ROWS, so can do mod more quickly thus:
		 */
		CurRow += delta;	/* result < 2*ROWS */
		if (CurRow >= ROWS)
			CurRow -= ROWS;	/* now result < ROWS */
	}
}

/*
 * Write the lines between the first and last inclusive from the given
 * buffer.  If 'cl', clear after writing.
 */
WriteBuf(buf, first, last, cl)
	register char *buf;
	register int first, last;
{

	if (first > last) {	/* recursively do wrapped part first */
		WriteBuf(buf, first, ROWS - 1, cl);
		first = 0;
	}
	buf = &buf[first * COLUMNS];
	last = COLUMNS * (first = last - first + 1);

	/*
	 * If the write fails, the Versatec is probably out of paper, and in
	 * any case, things are probably in bad shape. 
	 */
	if (write(vp, buf, last) != last) {
		error(0, errno, "Versatec write error");
		longjmp(failbuf, 1);
	}
	if (cl)
		bzero(buf, (unsigned) last);
	NLines += first;
}

/*
 * Write 'n' blank lines.
 */
WriteBlanks(n)
	register int n;
{
	register int k;
	static char nullbuf[MIN_OUT][COLUMNS];

	while (n > 0) {
		k = n > MIN_OUT ? MIN_OUT : n;
		WriteBuf(&nullbuf[0][0], 0, k - 1, 0);
		n -= k;
	}
}

/*
 * Write cut marks.  We borrow row 0 of VBuffer for this.
 */
CutMarks()
{
	register short *bp = (short *) &VBuffer[0][0];

	*bp = 0xffff;
	bp[(COLUMNS / 2) - 1] = 0xffff;
	WriteBuf(&VBuffer[0][0], 0, 0, 1);
}

/*
 * Perform a page feed.  Restore plot mode if `setplot'.
 */
FormFeed(setplot)
	int setplot;
{
	ioctl(vp, VSETSTATE, prtmd);
	(void) write(vp, "\f", 2); /* \0 really IS required occasionally */
	if (setplot)
		ioctl(vp, VSETSTATE, pltmd);
}