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 d

⟦d62fd89dd⟧ TextFile

    Length: 19246 (0x4b2e)
    Types: TextFile
    Names: »dmd.c«

Derivation

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

TextFile

#ifndef lint
static char rcsid[] = "$Header: imagen1.c,v 2.4 86/11/18 02:26:18 chris Exp $";
#endif

/*
 * DVI to Imagen driver
 *
 * Reads DVI version 2 files and converts to imPRESS commands for spooling to
 * the Imagen (via ipr).
 *
 * TODO:
 *	think about fonts with characters outside [0..127]
 */

#include <stdio.h>
#include "types.h"
#include "conv.h"
#include "dvi.h"
#include "dviclass.h"
#include "dvicodes.h"
#include "fio.h"
#include "font.h"
#include "postamble.h"
#include "search.h"
#include "dmddev.h"
#include "dmdcodes.h"

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

/* Globals */
char serrbuf[BUFSIZ];		/* buffer for stderr */

/*
 * DVI style arithmetic:  when moving horizontally by a DVI distance >=
 * `space', we are to recompute horizontal position from DVI units;
 * otherwise, we are to use device resolution units to keep track of
 * horizontal position.  A similar scheme must be used for vertical
 * positioning.
 */
struct fontinfo {
	struct font *f;		/* the font */
	i32 pspace;		/* boundary between `small' & `large' spaces
				   (for positive horizontal motion) */
	i32 nspace;		/* -4 * pspace, for negative motion */
	i32 vspace;		/* 5 * pspace, for vertical motion */
	int family;		/* DMD family number (we get one) */
#ifdef notyet
	int UseTime;		/* cache info: flush fonts on LRU basis */
#endif
};

/*
 * We use one of the per-glyph user flags to keep track of whether a
 * glyph has been loaded into the Imagen.
 */
#define	GF_LOADED	GF_USR0

/*
 * The exception that proves the rule is that hh and fromSP(dvi_h) are not
 * allowed to get more than MaxDrift units apart.
 */
int	MaxDrift;		/* the maximum allowable difference between
				   hh and fromSP(dvi_h) */

struct fontinfo *CurrentFont;	/* the current font */

int	ExpectBOP;		/* true => BOP ok */
int	ExpectEOP;		/* true => EOP ok */

int	DPI;			/* -d => device resolution (dots/inch) */
int	PFlag = 1;		/* -p => no page reversal */
int	LFlag;			/* -l => landscape mode (eventually...) */
int	SFlag = 1;		/* -s => silent (no page numbers) */
int	Exflag;			/* -x => exit emulator */
int	Debug;			/* -D => debug flag */

int	XOffset;
int	YOffset;		/* offsets for margins */

int	hh;			/* current horizontal position, in DEVs */
int	vv;			/* current vertical position, in DEVs */

/*
 * Similar to dvi_stack, but includes `hh' and `vv', which are usually
 * but not always the same as fromSP(h) and fromSP(v):
 */
struct localstack {
	int stack_hh;
	int stack_vv;
	struct dvi_stack stack_dvi;
};

struct localstack *dvi_stack;	/* base of stack */
struct localstack *dvi_stackp;	/* current place in stack */

int	HHMargin;		/* horizontal margin (in DEVs) */
int	VVMargin;		/* vertical margin (in DEVs) */

long    CurrentPagePointer;     /* current page we are processing */
long	PrevPagePointer;	/* The previous page pointer from the DVI
				   file.  This allows us to read the file
				   backwards, which obviates the need for
				   page reversal (reversal is unsupported
				   on the 8/300). */

int	Numerator;		/* numerator from DVI file */
int	Denominator;		/* denominator from DVI file */
int	DVIMag;			/* magnification from DVI file */
int	UserMag;		/* user-specified magnification */

int	ImHH;			/* Imagen horizontal position */
int	ImVV;			/* Imagen vertical position */
int	ImFamily;		/* Imagen current-font number */

char	*PrintEngine;		/* e.g., canon, ricoh */
struct search *FontFinder;	/* maps from DVI index to internal fontinfo */
int	FontErrors;		/* true => error(s) occurred during font
				   definitions from DVI postamble */

struct fontinfo NoFont;		/* a fake font to help get things started */

char	*getenv(), *malloc();

/* Absolute value */
#define ABS(n) ((n) >= 0 ? (n) : -(n))

/* Put a two-byte (word) value to the Imagen */
#define putword(w) (putchar((w) >> 8), putchar(w))

/*
 * Correct devpos (the actual device position) to be within MaxDrift pixels
 * of dvipos (the virtual DVI position).
 */
#define FIXDRIFT(devpos, dvipos) \
	if ((devpos) < (dvipos)) \
		if ((dvipos) - (devpos) <= MaxDrift) \
			/* void */; \
		else \
			(devpos) = (dvipos) - MaxDrift; \
	else \
		if ((devpos) - (dvipos) <= MaxDrift) \
			/* void */; \
		else \
			(devpos) = (dvipos) + MaxDrift

SelectFont(n)
	i32 n;
{
	int x = S_LOOKUP;

	if ((CurrentFont = (struct fontinfo *)SSearch(FontFinder, n, &x)) == 0)
		GripeNoSuchFont(n);
}

/*
 * Start a page (process a DVI_BOP).
 */
BeginPage()
{
	register int *i;
	static int count[10];	/* the 10 counters */
	static int beenhere;

	if (!ExpectBOP)
		GripeUnexpectedOp("BOP");
	if (beenhere) {
		if (!SFlag)
			putc(' ', stderr);
	} else
		beenhere++;
	CurrentPagePointer = ftell(stdin) - 1;

	dvi_stackp = dvi_stack;

	ExpectBOP = 0;
	ExpectEOP++;		/* set the new "expect" state */

	for (i = count; i < &count[sizeof count / sizeof *count]; i++)
		fGetLong(stdin, *i);
	fGetLong(stdin, PrevPagePointer);

	if (!SFlag) {
		(void) fprintf(stderr, "[%d", count[0]);
		(void) fflush(stderr);
	}
	putchar(DMD_PAGE);
	ImHH = 0;
	ImVV = 0;

	hh = HHMargin;
	vv = VVMargin;
	dvi_h = toSP(hh);
	dvi_v = toSP(vv);
	dvi_w = 0;
	dvi_x = 0;
	dvi_y = 0;
	dvi_z = 0;
}

/*
 * End a page (process a DVI_EOP)
 */
EndPage()
{
	int newpage;

	if (!ExpectEOP)
		GripeUnexpectedOp("EOP");

	if (!SFlag) {
		putc(']', stderr);
		(void) fflush(stderr);
	}
	ExpectEOP = 0;
	ExpectBOP++;

again:
	putchar(DMD_ENDPAGE);
	newpage = pagecmd();
	switch (newpage) {
	case -1:
		if (PrevPagePointer != -1)
			fseek(stdin, PrevPagePointer, 0);
		else
			goto again;
		break;
	case 0:
		fseek(stdin, CurrentPagePointer, 0);
		break;
	case 1:
	default:
		break;
	}
}

/*
 * Store the relevant information from the DVI postamble, and set up
 * various internal things.
 */
PostAmbleHeader(p)
	register struct PostAmbleInfo *p;
{
	register int n;

	PrevPagePointer = p->pai_PrevPagePointer;
	Numerator = p->pai_Numerator;
	Denominator = p->pai_Denominator;
	DVIMag = p->pai_DVIMag;

	/*
	 * Set the conversion factor.  This must be done before using
	 * any fonts.
	 */
	SetConversion(DPI, UserMag, Numerator, Denominator, DVIMag);

	n = p->pai_DVIStackSize * sizeof *dvi_stack;
	dvi_stack = (struct localstack *) malloc((unsigned) n);
	if ((dvi_stackp = dvi_stack) == NULL)
		GripeOutOfMemory(n, "DVI stack");
}

/* Handle one of the font definitions from the DVI postamble. */
PostAmbleFontDef(p)
	register struct PostAmbleFont *p;
{
	register int i;
	register struct glyph *g;
	register struct fontinfo *fi;
	register struct font *f;
	register char *s;
	char *fname;
	int def = S_CREATE | S_EXCL;
	char loaded[16];
	char *rindex(), *strsave();

	fi = (struct fontinfo *) SSearch(FontFinder, p->paf_DVIFontIndex,
		&def);
	if (fi == NULL) {
		if (def & S_COLL)
			GripeFontAlreadyDefined(p->paf_DVIFontIndex);
		else
			error(1, 0, "can't stash font %ld (out of memory?)",
				p->paf_DVIFontIndex);
		/*NOTREACHED*/
	}
	f = GetFont(p->paf_name, p->paf_DVIMag, p->paf_DVIDesignSize,
		PrintEngine, &fname);
	if ((fi->f = f) == NULL) {
		GripeCannotGetFont(p->paf_name, p->paf_DVIMag,
			p->paf_DVIDesignSize, PrintEngine, fname);
		FontErrors++;
		return;
	}
	if (Debug) {
		(void) fprintf(stderr, "[%s -> %s]\n",
			Font_TeXName(f), fname);
		(void) fflush(stderr);
	}
	/* match checksums, if not zero */
	if (p->paf_DVIChecksum && f->f_checksum &&
	    p->paf_DVIChecksum != f->f_checksum)
		GripeDifferentChecksums(fname, p->paf_DVIChecksum,
			f->f_checksum);

	fi->pspace = p->paf_DVIMag / 6;	/* a three-unit "thin space" */
	fi->nspace = -4 * fi->pspace;
	fi->vspace = 5 * fi->pspace;
	putchar(DMD_MKFONT);
	if (s = rindex(fname, '/'))
		s++;
	else
		s = fname;
	while (*s)
		putchar(*s++);
	putchar(0);
	fi->family = inkbd();
	if (fi->family == 255)
		error(1, 0, "out of space in remote dmd");
	s = loaded;
	for (i = 0; i < sizeof loaded; i++)
		*s++ = inkbd();
#define bitset(a, i)    (a[i>>3] & (1 << ((~i) & 07)))
	for (i = 0; i < 128; i++) {
		if (bitset(loaded, i)) {
			g = GLYPH(f, i);
			g->g_pixwidth = fromSP(g->g_tfmwidth);
			g->g_flags |= GF_LOADED;
		}
	}
}

/* Read the postamble. */
ReadPostAmble()
{

	if ((FontFinder = SCreate(sizeof(struct fontinfo))) == 0)
		error(1, 0, "can't create FontFinder (out of memory?)");
	ScanPostAmble(stdin, PostAmbleHeader, PostAmbleFontDef);
	if (FontErrors)
		GripeMissingFontsPreventOutput(FontErrors);
}

/* Read the preamble and do a few sanity checks */
ReadPreAmble()
{
	register int n;

	rewind(stdin);
	if (GetByte(stdin) != Sign8(DVI_PRE))
		GripeMissingOp("PRE");
	if (GetByte(stdin) != Sign8(DVI_VERSION))
		GripeMismatchedValue("version numbers");
	if (GetLong(stdin) != Numerator)
		GripeMismatchedValue("numerator");
	if (GetLong(stdin) != Denominator)
		GripeMismatchedValue("denominator");
	if (GetLong(stdin) != DVIMag)
		GripeMismatchedValue("\\magfactor");
	n = UnSign8(GetByte(stdin));
	while (--n >= 0)
		(void) GetByte(stdin);
}

main(argc, argv)
	int argc;
	register char **argv;
{
	register int c;
	char *inname;

	setbuf(stderr, serrbuf);

	ProgName = *argv;
	UserMag = 1000;
	MaxDrift = DefaultMaxDrift;
	DPI = DefaultDPI;
	inname = "stdin";
	PrintEngine = "dmd";

	while ((c = getopt(argc, argv, "d:e:lm:pr:sxDX:Y:")) != EOF) {
		switch (c) {

		case 'd':	/* max drift value */
			MaxDrift = atoi(optarg);
			break;

		case 'e':	/* engine */
			PrintEngine = optarg;
			break;

		case 'l':	/* landscape mode */
			LFlag++;
			break;

		case 'm':	/* magnification */
			UserMag = atoi(optarg);
			break;

		case 'p':	/* no page reversal */
			PFlag++;
			break;

		case 'r':	/* resolution */
			DPI = atoi(optarg);
			break;

		case 's':	/* silent */
			SFlag++;
			break;

		case 'x':	/* Exit */
			Exflag++;
			break;

		case 'D':
			Debug++;
			break;

		case 'X':	/* x offset, in 1/10 inch increments */
			XOffset = atoi(optarg);
			break;

		case 'Y':	/* y offset */
			YOffset = atoi(optarg);
			break;

		case '?':
			(void) fprintf(stderr, "\
Usage: %s [-d drift] [-m mag] [-x] [more options, see manual] [file]\n",
				ProgName);
			(void) fflush(stderr);
			exit(1);
		}
	}
	if (optind < argc)
		if (freopen(inname = argv[optind], "r", stdin) == NULL)
			error(1, errno, "can't open %s", inname);

	dmdstart();
	if (isatty(fileno(stdin))) {
		putchar(Exflag ? DMD_EXIT : DMD_TERM);
		exit(0);
	}


/*	fontinit((char *) NULL); */

	ReadPostAmble();

	/* Margins -- needs work! */
	HHMargin = DefaultLeftMargin + XOffset * DPI / 10;
	VVMargin = DefaultTopMargin + YOffset * DPI / 10;

	ReadPreAmble();
	ExpectBOP++;
	if (!PFlag)
		(void) fseek(stdin, PrevPagePointer, 0);

	/* All set! */

	/*
	 * If the first command in the DVI file involves motion, we will need
	 * to compare it to the current font `space' parameter; so start with
	 * a fake current font of all zeros. 
	 */
	CurrentFont = &NoFont;
	ReadDVIFile();
	if (!SFlag) {
		(void) fprintf(stderr, "\n");
		(void) fflush(stderr);
	}
	putchar(DMD_TERM);
	exit(0);
}

/*
 * Skip a font definition (since we are using those from the postamble)
 */
/*ARGSUSED*/
SkipFontDef(font)
	i32 font;
{
	register int i;

	(void) GetLong(stdin);
	(void) GetLong(stdin);
	(void) GetLong(stdin);
	i = UnSign8(GetByte(stdin)) + UnSign8(GetByte(stdin));
	while (--i >= 0)
		(void) GetByte(stdin);
}

/*
 * Draw a rule at the current (hh,vv) position.  There are two 4 byte
 * parameters.  The first is the height of the rule, and the second is the
 * width.  (hh,vv) is the lower left corner of the rule.
 */
SetRule(advance)
	int advance;
{
	register i32 h, w, rw;

	fGetLong(stdin, h);
	fGetLong(stdin, rw);

	h = ConvRule(h);
	w = ConvRule(rw);

	/* put the rule out */
	if (ImHH != hh || ImVV != vv)
		ImSetPosition(hh, vv);
	putchar(DMD_RULE);
	putword(w);
	putword(h);
	if (advance) {
		hh += w;
		dvi_h += rw;
		w = fromSP(dvi_h);
		FIXDRIFT(hh, w);
	}
}

/* if anyone ever uses character codes > 127, this driver will need work */
char	chartoobig[] = "Warning: character code %d too big for Imagen!";

/*
 * This rather large routine reads the DVI file and calls on other routines
 * to do anything moderately difficult (except put characters:  there is
 * some ugly code with `goto's which makes things faster).
 */

ReadDVIFile()
{
	register int c;
	register struct glyph *g;
	register struct font *f;
	register i32 p;
	int advance;

	ImFamily = -1;		/* force DMD_SETFONT command */

	/*
	 * Only way out is via "return" statement. I had a `for (;;)' here,
	 * but everything crawled off the right.
	 */
loop:
	/*
	 * Get the DVI byte, and switch on its parameter length and type.
	 * Note that getchar() returns unsigned values.
	 */
	c = getchar();

	/*
	 * Handling characters (the most common case) early makes the
	 * program run a bit faster.
	 */
	if (DVI_IsChar(c)) {
		advance = 1;
do_char:
		f = CurrentFont->f;
		g = GLYPH(f, c);
		if (!GVALID(g)) {
			error(0, 0, "there is no character %d in %s",
				c, f->f_path);
			goto loop;
		}
		if ((g->g_flags & GF_LOADED) == 0)
			DownLoadGlyph(c, g);
		if (HASRASTER(g)) {	/* workaround for Imagen bug */

			/* BEGIN INLINE EXPANSION OF ImSetPosition */
			register int delta;

			if (ImHH != hh) {
				delta = hh - ImHH;
				if (delta == 1)
					putchar(DMD_FORW);
				else if (delta == -1)
					putchar(DMD_BACK);
				else if (-128 <= delta && delta <= 127) {
					putchar(DMD_HREL);
					putchar(delta);
				} else {
					putchar(DMD_HABS);
					putword(hh);
				}
				ImHH = hh;
			}
			if (ImVV != vv) {
				delta = vv - ImVV;
				if (-128 <= delta && delta <= 127) {
					putchar(DMD_VREL);
					putchar(delta);
				} else {
					putchar(DMD_VABS);
					putword(vv);
				}
				ImVV = vv;
			}
			/* END INLINE EXPANSION OF ImSetPosition */
			if (ImFamily != CurrentFont->family) {
				putchar(DMD_SETFONT);
				putchar(CurrentFont->family);
				ImFamily = CurrentFont->family;
			}
			putchar(c);
			ImHH += g->g_pixwidth;
		}
		if (advance) {
			hh += g->g_pixwidth;
			dvi_h += g->g_tfmwidth;
			p = fromSP(dvi_h);
			FIXDRIFT(hh, p);
		}
		goto loop;
	}

	switch (DVI_OpLen(c)) {

	case DPL_NONE:
		break;

	case DPL_SGN1:
		p = getchar();
		p = Sign8(p);
		break;

	case DPL_SGN2:
		fGetWord(stdin, p);
		p = Sign16(p);
		break;

	case DPL_SGN3:
		fGet3Byte(stdin, p);
		p = Sign24(p);
		break;

	case DPL_SGN4:
		fGetLong(stdin, p);
		break;

	case DPL_UNS1:
		p = UnSign8(getchar());
		break;

	case DPL_UNS2:
		fGetWord(stdin, p);
		p = UnSign16(p);
		break;

	case DPL_UNS3:
		fGet3Byte(stdin, p);
		p = UnSign24(p);
		break;

	default:
		panic("DVI_OpLen(%d) = %d", c, DVI_OpLen(c));
		/* NOTREACHED */
	}

	switch (DVI_DT(c)) {

	case DT_SET:
		advance = 1;
		c = p;
		if (c > 127)
			error(0, 0, chartoobig, c);
		goto do_char;

	case DT_PUT:
		advance = 0;
		c = p;
		if (c > 127)
			error(0, 0, chartoobig, c);
		goto do_char;

	case DT_SETRULE:
		SetRule(1);
		break;

	case DT_PUTRULE:
		SetRule(0);
		break;

	case DT_NOP:
		break;

	case DT_BOP:
		BeginPage();
		break;

	case DT_EOP:
		EndPage();
		break;

	case DT_PUSH:
		dvi_stackp->stack_hh = hh;
		dvi_stackp->stack_vv = vv;
		dvi_stackp->stack_dvi = dvi_current;
		dvi_stackp++;
		break;

	case DT_POP:
		dvi_stackp--;
		hh = dvi_stackp->stack_hh;
		vv = dvi_stackp->stack_vv;
		dvi_current = dvi_stackp->stack_dvi;
		break;

	case DT_W0:	/* there should be a way to make these pretty */
		p = dvi_w;
		goto move_right;

	case DT_W:
		dvi_w = p;
		goto move_right;

	case DT_X0:
		p = dvi_x;
		goto move_right;

	case DT_X:
		dvi_x = p;
		goto move_right;

	case DT_RIGHT:
move_right:
		dvi_h += p;
		/*
		 * DVItype tells us that we must round motions in this way:
		 * `When the horizontal motion is small, like a kern, hh
		 * changes by rounding the kern; but when the motion is
		 * large, hh changes by rounding the true position so that
		 * accumulated rounding errors disappear.' 
		 */
		if (p >= CurrentFont->pspace || p <= CurrentFont->nspace)
			hh = fromSP(dvi_h);
		else {
			hh += fromSP(p);
			p = fromSP(dvi_h);
			FIXDRIFT(hh, p);
		}
		break;

	case DT_Y0:
		p = dvi_y;
		goto move_down;

	case DT_Y:
		dvi_y = p;
		goto move_down;

	case DT_Z0:
		p = dvi_z;
		goto move_down;

	case DT_Z:
		dvi_z = p;
		goto move_down;

	case DT_DOWN:
move_down:
		dvi_v += p;
		/*
		 * `Vertical motion is done similarly, but with the threshold
		 * between ``small'' and ``large'' increased by a factor of
		 * 5.  The idea is to make fractions like $1\over2$ round
		 * consistently, but to absorb accumulated rounding errors in
		 * the baseline-skip moves.' 
		 */
		if (ABS(p) >= CurrentFont->vspace)
			vv = fromSP(dvi_v);
		else {
			vv += fromSP(p);
			p = fromSP(dvi_v);
			FIXDRIFT(vv, p);
		}
		break;

	case DT_FNTNUM:
		SelectFont((i32) (c - DVI_FNTNUM0));
		break;

	case DT_FNT:
		SelectFont(p);
		break;

	case DT_XXX:
		DoSpecial(p);
		break;

	case DT_FNTDEF:
		SkipFontDef(p);
		break;

	case DT_PRE:
		GripeUnexpectedOp("PRE");
		/* NOTREACHED */

	case DT_POST:
		if (PFlag)
			return;
		GripeUnexpectedOp("POST");
		/* NOTREACHED */

	case DT_POSTPOST:
		GripeUnexpectedOp("POSTPOST");
		/* NOTREACHED */

	case DT_UNDEF:
		GripeUndefinedOp(c);
		/* NOTREACHED */

	default:
		panic("DVI_DT(%d) = %d", c, DVI_DT(c));
		/* NOTREACHED */
	}
	goto loop;
}

/*
 * Download the character c/g in the current font.
 */
DownLoadGlyph(c, g)
	int c;
	register struct glyph *g;
{
	register char *p;
	register int i, j, w;

	g->g_pixwidth = fromSP(g->g_tfmwidth);
	g->g_flags |= GF_LOADED;
	if (!HASRASTER(g))	/* never load dull glyphs */
		return;

	if (!LFlag) {
		w = 0;
		p = RASTER(g, CurrentFont->f, ROT_NORM);
	} else {
		w = 1 << 14;
		p = RASTER(g, CurrentFont->f, ROT_RIGHT);
	}

	w |= (CurrentFont->family << 7) | c;

	/* Define the character */
	if (-128 <= g->g_pixwidth && g->g_pixwidth <= 127 &&
	    g->g_width <= 255 && g->g_height <= 255 &&
	    -128 <= g->g_xorigin && g->g_xorigin <= 127 &&
	    -128 <= g->g_yorigin && g->g_yorigin <= 127) {
		putchar(DMD_SGLYPH);	/* a.k.a. SGLY */
		putword(w);		/* rotation, family, member */
		putchar(g->g_pixwidth);	/* advance */
		putchar(g->g_width);	/* width */
		putchar(g->g_xorigin);	/* left offset */
		putchar(g->g_height);	/* height */
		putchar(g->g_yorigin);	/* top-offset */
	} else {
		putchar(DMD_BGLYPH);	/* a.k.a. BGLY */
		putword(w);		/* rotation, family, member */
		putchar(g->g_pixwidth);	/* advance */
		putchar(g->g_width);	/* width */
		putchar(g->g_xorigin);	/* left offset */
		putchar(g->g_height);	/* height */
		putchar(g->g_yorigin);	/* top-offset */
	}

	/*
	 * Now put out the bitmap.
	 */
	w = (g->g_width + 7) >> 3;
	for (i = g->g_height; --i >= 0;)
		for (j = w; --j >= 0;)
			(void) putchar(*p++);

	if (g->g_raster) {	/* XXX */
		free(g->g_raster);
		g->g_raster = NULL;
	}

}

/*
 * Set the Imagen's h & v positions.  It is currently at ImHH, ImVV.
 */
ImSetPosition(h, v)
	register int h, v;
{

	register int delta;
	if (ImHH != h) {
		delta = h - ImHH;
		if (delta == 1)
			putchar(DMD_FORW);
		else if (delta == -1)
			putchar(DMD_BACK);
		else if (-128 <= delta && delta <= 127) {
			putchar(DMD_HREL);
			putchar(delta);
		} else {
			putchar(DMD_HABS);
			putword(h);
		}
		ImHH = h;
	}
	if (ImVV != v) {
		delta = v - ImVV;
		if (-128 <= delta && delta <= 127) {
			putchar(DMD_VREL);
			putchar(delta);
		} else {
			putchar(DMD_VABS);
			putword(v);
		}
		ImVV = v;
	}
}