diff options
Diffstat (limited to 'dviware/umddvi/dev/verser2.c')
-rw-r--r-- | dviware/umddvi/dev/verser2.c | 758 |
1 files changed, 758 insertions, 0 deletions
diff --git a/dviware/umddvi/dev/verser2.c b/dviware/umddvi/dev/verser2.c new file mode 100644 index 0000000000..8969e7595d --- /dev/null +++ b/dviware/umddvi/dev/verser2.c @@ -0,0 +1,758 @@ +/* + * 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); +} |