-- Compiler Press/nb
-- last modified by m. stone December 2, 1981  9:31 PM
-- implementing module for press file creation

DIRECTORY
	PressDefs: FROM "PressDefs",
	PointDefs: FROM "PointDefs",
	Real: FROM "Real",
	StyleDefs: FROM "StyleDefs",
	GriffinFontDefs: FROM "GriffinFontDefs",
	AltoDefs: FROM "AltoDefs" USING [maxword],
	StreamDefs: FROM "streamDefs"
		USING [NewByteStream, Write, Append, GetIndex, FileLength, DiskHandle,
		StreamIndex, TruncateDiskStream],
	StringDefs: FROM "StringDefs" USING [AppendString, BcplSTRING, EquivalentString, AppendDecimal],
	TimeDefs: FROM "TimeDefs"
		USING [CurrentDayTime, UnpackDT, AppendFullDayTime, DefaultTime],
	OsStaticDefs: FROM "OsStaticDefs" USING [OsStatics],
	InlineDefs: FROM "InlineDefs" USING [LowHalf, HighHalf, LowByte, BITSHIFT, MesaToBcplLongNumber],
	GriffinMemoryDefs: FROM "GriffinMemoryDefs";


Press:  PROGRAM
	IMPORTS StreamDefs, TimeDefs, StringDefs, GriffinMemoryDefs, InlineDefs, GriffinFontDefs,Real,PointDefs
	EXPORTS PressDefs =

BEGIN OPEN PressDefs, GriffinMemoryDefs;



BYTE: TYPE = [ 0 .. 255]; -- because they are so common in the press format

BytesInSector: CARDINAL = 512;
WordsInSector: CARDINAL = 256;

SectorIndex: TYPE = RECORD [s: CARDINAL [0 .. 3000]];

PageDescriptor: TYPE = RECORD
	[
	next: PageDescriptorHandle,
	start: SectorIndex,
	length: CARDINAL, -- length in sectors: first sector = 0
	padding: BYTE -- unused words at end of last sector of page
	];

PageDescriptorHandle: TYPE = POINTER TO PageDescriptor;

lPageDescriptor: CARDINAL = SIZE [PageDescriptor];

Font: TYPE = RECORD
	[
	next: FontHandle,
	setnum, font, m, n, face, source: BYTE,
	family: STRING,
	size, rotation: CARDINAL
	];


FontHandle: TYPE = POINTER TO Font;

lFont: CARDINAL = SIZE [Font];

EBIndex: TYPE = [0 .. 507];

EntityBody: TYPE = RECORD
	[
	next: EntityBodyHandle,
	body: PACKED ARRAY EBIndex OF BYTE,
	lastbyte: EBIndex -- never "-1" because there's never an empty EntityBody
	];

EntityBodyHandle: TYPE = POINTER TO EntityBody;

lEntityBody: CARDINAL [0 .. WordsInSector] = SIZE [EntityBody]; -- EntityBody must fit in a sector

Entity: TYPE = RECORD
	[
	next: EntityHandle,
	fontset: CARDINAL,
	dlbeginbyte, dlbytelength: LONG CARDINAL,
	entitylength: CARDINAL, -- updated as entitybody chain is extended, and at end
	entitybody: EntityBodyHandle, -- contains actual commands
	finished: BOOLEAN
	];
NilFontSet: CARDINAL = 177777B; -- indicates no fontset chosen yet

EntityHandle: TYPE = POINTER TO Entity;

lEntity: CARDINAL = SIZE [Entity];







pressMaxELength: CARDINAL = AltoDefs.maxword - LAST [EBIndex]/2 - 12; -- must fit in word
pressMaxShortChars: CARDINAL = 32;
pressRot0: CARDINAL = 0;
pressRot90: CARDINAL = 5400;
pressRot180: CARDINAL = 10800;
pressRot270: CARDINAL = 16200;
pressPassWord: CARDINAL = 27183;
pressShowCharsShort: BYTE = 0;
pressSkipCharsShort: BYTE = 40B;
pressSetFont: BYTE = 160B;
pressSetX: BYTE = 356B;
pressSetY: BYTE = 357B;
pressShowChars: BYTE = 360B;
pressSetBrightness: BYTE = 370B;
pressSetHue: BYTE = 371B;
pressSetSaturation: BYTE = 372B;
pressShowObject: BYTE = 373B;
pressShowRect: BYTE = 376B;
pressNop: BYTE = 377B;
pressMoveTo: CARDINAL = 0;
pressDrawTo: CARDINAL = 1;
pressDrawCurve: CARDINAL = 2;



PressError: PUBLIC SIGNAL = CODE;
PressState: TYPE = {notbegun, begun, inpage, inobject};
pressState: PressState ← notbegun;

 -- these variables reflect the current press state
pageList, currentPage: PageDescriptorHandle; -- list of page descriptors
fontList: FontHandle; -- beginning of list of fonts used
nextFontSet, nextFontNum, currentFontNum: CARDINAL;
-- currentFontNum just mirrors internal press state
-- nextFontSet and nextFontNum are next available
entityList, currentEntity: EntityHandle; -- describe entity list for this page
currentEntityBody: EntityBodyHandle; -- as above
-- entityList = NIL until something is placed. 
currentColor: ColorDescriptor; -- current color for this page
currentPressCoords: PressCoords; -- current press coords for this page
objectStart: LONG CARDINAL;

formName: STRING = [51];
pressCopies: CARDINAL;
pressLowerLeft: PressCoords;
diskHandle: StreamDefs.DiskHandle;



PressDebug: PROCEDURE RETURNS [BOOLEAN] = BEGIN
XBEGIN: CARDINAL = 700;
YBEGIN: CARDINAL = 0;
PATCHHEIGHT: CARDINAL = 1800;
PATCHWIDTH: CARDINAL = 985;
WHITESPACE: CARDINAL = 200;
XMAX: CARDINAL = 20500;
YMAX: CARDINAL = 26000;
MAXHUE: CARDINAL = 239;
MAXBRIGHT: CARDINAL = 255;
MAXSAT: CARDINAL = 255;
black: ColorDescriptor = [0, 0, 0];
gacha10: GriffinFontDefs.FontDescriptor = ["Gacha", 0, 0, 10];
pc: PressCoords;
string: STRING = [50];
x, y: CARDINAL;
saturation, hrange: INTEGER;
color: ColorDescriptor;
FOR saturation ← MAXSAT, saturation -MAXSAT/8 UNTIL saturation <= 0 DO
FOR hrange ← 0, hrange+(MAXHUE+1)/2 UNTIL hrange >= MAXHUE DO
	IF saturation <= 10 THEN saturation ← 0;
	string.length ← 0;
	StringDefs.AppendString [string, "Sat"];
	StringDefs.AppendDecimal [string, saturation];
	StringDefs.AppendString [string, IF hrange = 0 THEN "A" ELSE "B"];
	[] ← BeginPress [string, TRUE, 1];
	BeginPage [PressCoords [26,300]];-- arbitrary
	pc [X] ← XBEGIN;
	pc [Y] ← YMAX+600;
	string.length ← 0;
	StringDefs.AppendString [string, "Saturation = "];
	StringDefs.AppendDecimal [string, saturation];
	ShowText [black, string, pc, or0,left, gacha10];
	color.saturation ← saturation;
	FOR x ← XBEGIN, x+PATCHWIDTH+WHITESPACE UNTIL x>XMAX DO
		color.hue ← (x-XBEGIN)/(2*((XMAX - XBEGIN)/MAXHUE)) + hrange;
		string.length ← 0;
		StringDefs.AppendDecimal [string, color.hue];
		pc [X] ← x + 200;
		pc [Y] ← YMAX + 100;
		ShowText [black, string, pc, or0,left, gacha10];
		FOR y ← YBEGIN, y+PATCHHEIGHT+WHITESPACE UNTIL y>=YMAX DO
			color.brightness←(y-YBEGIN)/((YMAX-YBEGIN)/MAXBRIGHT);
			IF x = XBEGIN THEN BEGIN
				pc [X] ← 0;
				pc [Y] ← y + 800;
				string.length ← 0;
				StringDefs.AppendDecimal [string,
						color.brightness];
				ShowText [black, string, pc, or0,left, gacha10];
				END;
			ShowRectangle [color, PressCoords [x, y],
			   PressCoord[PATCHWIDTH],PressCoord [PATCHHEIGHT]];
		ENDLOOP ENDLOOP;
	EndPage;
 	[] ← EndPress [];
	ENDLOOP; ENDLOOP;
RETURN [TRUE];
END;

--only works if no scale, translate between press and object space
PressToScr: PROCEDURE[pc: PressCoords] RETURNS[scr: PointDefs.ScrPt]=
BEGIN OPEN PointDefs;
obj: ObjPt ← [pc[X],pc[Y]];	--convert to REAL;
scr ← ObjToScr[obj];
END;

--only works if no scale, translate between press and object space
ScrToPress: PROCEDURE[scr: PointDefs.ScrPt] RETURNS[pc: PressCoords]=
BEGIN OPEN PointDefs;
obj: ObjPt ← ScrToObj[scr];	--convert to REAL;
pc[X] ← Real.RoundI[obj[X]];
pc[Y] ← Real.RoundI[obj[Y]];
END;


BeginPress: PUBLIC PROCEDURE [filestring: STRING ← , overwrite: BOOLEAN ← FALSE, copies: CARDINAL ← 1] RETURNS [BOOLEAN] = BEGIN
	IF pressState # notbegun THEN SIGNAL PressError;
	fontList ← NIL;
	currentPage ← pageList ← NIL;
	formName.length ← 0;
	StringDefs.AppendString [formName, filestring];
	StringDefs.AppendString [formName, ".Press"];
	pressCopies ← copies;
	diskHandle ← StreamDefs.NewByteStream [formName,
		StreamDefs.Write + StreamDefs.Append];
	diskHandle.reset [diskHandle];
	IF ~overwrite AND
		(StreamDefs.FileLength [diskHandle] # StreamDefs.StreamIndex [0, 0])
		THEN BEGIN 
		diskHandle.destroy [diskHandle];
		diskHandle ← NIL;
		RETURN [FALSE]
		END;
	pressState ← begun;
	nextFontSet ← 1;
	nextFontNum ← 0;
	RETURN [TRUE];
	END;

EndPress: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN
	creationdate: STRING = [50];
	j: CARDINAL; -- used for loop control
	begindd, beginfontdir, beginpartdir: SectorIndex;
	partcount: CARDINAL ← 1; -- 1 for font part, more to come
	username: POINTER TO StringDefs.BcplSTRING;
	tempfont: FontHandle; -- used to scan font list
	temppartdescriptor: PageDescriptorHandle;

	IF pressState # begun THEN SIGNAL PressError;

	-- create dummy font if necessary
	IF fontList = NIL THEN
		BEGIN OPEN fontList;
		fontList ← Allocate [lFont];
		next ← NIL; setnum ← font ← m ← face ← source ← 0; n ← 128;
		family ← AllocateString [20]; family.length ← 0;
		StringDefs.AppendString [family, "GACHA"]; size ← 10; rotation ← 0;
		END;

	-- write out font directory
	beginfontdir ← NextDiskSector [];
	WHILE fontList # NIL DO OPEN fontList;
		PutWord [16];
		PutByte [setnum]; PutByte [font];
		PutByte [m]; PutByte [n];
		PutString [family, 20];FreeString [family]; 
		PutByte [face]; PutByte [0]; -- face, source
		PutWord [size];
		PutWord [rotation];
		tempfont ← next;
		Free [fontList];
		fontList ← tempfont
		ENDLOOP;

	-- write out part directory
	-- font directory entry
	beginpartdir ← NextDiskSector [];
	PutWord [1]; -- font directory part descriptor
	PutWord [beginfontdir];
	PutWord [beginpartdir-beginfontdir];
	PutWord [0]; -- this word ignored for font directory part descriptor
	-- rest of part directory
	WHILE pageList # NIL DO OPEN pageList;
		partcount ← partcount + 1; -- started out at 1 for font directory
		PutWord [0]; -- printed page part descriptor
		PutWord [start];
		PutWord [length];
		PutWord [padding]; 
		temppartdescriptor ← next;
		Free [pageList];
		pageList ← temppartdescriptor
		ENDLOOP;

	-- write out document directory
	begindd ← NextDiskSector []; -- record number where dd begins
	PutWord [pressPassWord];
	PutWord [begindd+1];
	PutWord [partcount];
	PutWord [beginpartdir];
	PutWord [begindd - beginpartdir];
	PutWord [0];
	PutLongWord [TimeDefs.CurrentDayTime []];
	PutWord [1];
	PutWord [pressCopies];
	THROUGH [10 .. 177B] DO PutWord [177777B] ENDLOOP;

	 -- form name saved from BeginPress
	PutString [formName, 52];
	 -- user name extracted from os statics
	username ← OsStaticDefs.OsStatics↑.UserName;
	PutByte [username.length];
	FOR j IN [0 .. username.length) DO
		PutByte [LOOPHOLE [username.char [j], BYTE]]; ENDLOOP;
	THROUGH [username.length .. 30] DO PutByte [0] ENDLOOP;
	-- time synthesised right here
	TimeDefs.AppendFullDayTime[creationdate,TimeDefs.UnpackDT [TimeDefs.DefaultTime]];
	PutString [creationdate, 40];

	-- fill out file
	[] ← NextDiskSector [];

	-- all done
	pressState ← notbegun;
	IF diskHandle = NIL THEN RETURN [FALSE] ELSE
		StreamDefs.TruncateDiskStream [diskHandle];
	RETURN [TRUE];
	END;

ShowText: PUBLIC PROCEDURE [color: ColorDescriptor, textstring: STRING, presscoords: PressCoords, orientation: StyleDefs.Orientation,anchor: StyleDefs.Anchor, font: GriffinFontDefs.FontDescriptor] = 
BEGIN OPEN PointDefs, GriffinFontDefs;
i, twiddle: CARDINAL;
rotation: CARDINAL=font.rotation;
pt,showpt: PointDefs.ObjPt;
presspt: PressCoords;
char: CHARACTER;
chheight,chwidth,height, width, baseline: CARDINAL;
aligned, vertical: BOOLEAN;
textdirection: CARDINAL = SELECT orientation FROM
	or0 => Rot0Degrees,
	or90 => Rot90Degrees,
	or180 => Rot180Degrees,
	or270 => Rot270Degrees,
	ENDCASE => ERROR;
slen: CARDINAL = textstring.length;
IF pressState # inpage THEN SIGNAL PressError;
IF slen = 0 THEN RETURN;
EnsureCurrentEntity;
SetColor [color];
SetFont [font];

height ← StringHeight[textstring, @font, orientation];
width ← StringWidth[textstring, @font, orientation];
baseline ← BaseLine[@font];
pt ← TopLeft[[presscoords[X],presscoords[Y]], height, width, anchor,
		orientation, font.rotation];
vertical ← orientation = or90 OR orientation = or270;
aligned ← IF vertical
	THEN rotation = Rot0Degrees OR rotation = Rot180Degrees
	ELSE rotation = Rot90Degrees OR rotation = Rot270Degrees;

SELECT orientation FROM -- move to end if going backwards
or180 => pt[X] ← pt[X]+width;
or90 => pt[Y] ← pt[Y]-height;
ENDCASE;

IF textdirection = font.rotation THEN -- easy
	BEGIN
	SELECT rotation FROM
	Rot0Degrees=> pt[Y] ← pt[Y]-BaseLine[@font];
	Rot90Degrees=> pt[X] ← pt[X]+baseline;
	Rot180Degrees=> pt[Y] ← pt[Y]-(MaxHeight[@font]-baseline);
	Rot270Degrees=> pt[X] ← pt[X]+(MaxHeight[@font]-baseline);
	ENDCASE;
	presspt ← [Real.RoundI[pt[X]],Real.RoundI[pt[Y]]];
	SetXY [presspt];
	IF slen > pressMaxShortChars THEN
		AppendBytesToEntity [pressShowChars, slen] ELSE
		AppendByteToEntity [pressShowCharsShort+slen-1];
	FOR i IN [0 .. slen) DO
		PutByte [LOOPHOLE [textstring [i], BYTE]] ENDLOOP;
	InvalidXY;
	RETURN
	END;

FOR i IN [0 .. slen) DO
	char ← textstring [i];
	chwidth ← Width[char, @font]; chheight ← Height[char, @font];
	twiddle ← IF vertical THEN (MaxWidth[@font] - chwidth)/2
			ELSE (MaxHeight[@font] - chheight)/2;
	IF aligned THEN IF vertical
		THEN pt[X] ← pt[X] + twiddle
		ELSE pt[Y] ← pt[Y] - twiddle;
	SELECT orientation FROM
		or90 => pt[Y] ← pt[Y] + chheight;
		or180 => pt[X] ← pt[X] - chwidth;
	ENDCASE;
	showpt←pt;
	SELECT rotation FROM
	Rot0Degrees=> showpt[Y] ← showpt[Y]-baseline;
	Rot90Degrees=> 
		BEGIN
		showpt[X] ← showpt[X]+baseline;
		showpt[Y] ← showpt[Y]-chheight;
		END;
	Rot180Degrees=> 
		BEGIN
		showpt[Y] ← showpt[Y]-(MaxHeight[@font]-baseline);
		showpt[X] ← showpt[X]+chwidth;
		END;
	Rot270Degrees=>
		showpt[X] ← showpt[X]+(MaxHeight[@font]-baseline);
	ENDCASE;
	presspt ← [Real.RoundI[showpt[X]],
		Real.RoundI[showpt[Y]]];
	SetXY [presspt];
	AppendByteToEntity [pressShowCharsShort];
	PutByte [LOOPHOLE [char, BYTE]];
	InvalidXY;
	SELECT orientation FROM
		or0 => pt[X] ← pt[X] + chwidth;
		or270 => pt[Y] ← pt[Y] - chheight;
	ENDCASE;
	IF aligned THEN IF vertical
		THEN pt[X] ← pt[X] - twiddle
		ELSE pt[Y] ← pt[Y] + twiddle;
	ENDLOOP;
END;



ShowRectangle: PUBLIC PROCEDURE [color: ColorDescriptor, presscoords: PressCoords, width, height: PressCoord] =
	BEGIN
	IF pressState # inpage THEN SIGNAL PressError;
	EnsureCurrentEntity;
	SetColor [color];
	SetXY [presscoords];
	AppendByteToEntity [pressShowRect];
	AppendWordToEntity [width];
	AppendWordToEntity [height];
	END;




BeginObject: PUBLIC PROCEDURE [color: ColorDescriptor] =
	BEGIN
	IF pressState # inpage THEN SIGNAL PressError;
	EnsureCurrentEntity; -- so we'll know where object begins
	SetColor [color];
	objectStart ← BytesPastBeginPrintedPage [];
	IF objectStart MOD 2 = 1 THEN BEGIN
		objectStart ← objectStart + 1;
		PutByte [0];
		AppendByteToEntity [pressSkipCharsShort+0];
		END;
	pressState ← inobject;
	END;




EndObject: PUBLIC PROCEDURE =
	BEGIN
	objectlength: LONG CARDINAL = (BytesPastBeginPrintedPage [] - objectStart)/2;
	-- error if object longer than 64K words of DL (5000 curves)
	IF InlineDefs.HighHalf [objectlength] # 0 THEN SIGNAL PressError;
	IF pressState # inobject THEN SIGNAL PressError ELSE pressState ← inpage;
	AppendByteToEntity [pressShowObject];
	AppendWordToEntity [InlineDefs.LowHalf [objectlength]];
	END;



MoveTo: PUBLIC PROCEDURE [presscoords: PressCoords] =
	BEGIN
	IF pressState # inobject THEN SIGNAL PressError;
	PutWord [pressMoveTo];
	PutWord [presscoords [X]];
	PutWord [presscoords [Y]];
	END;



DrawTo: PUBLIC PROCEDURE [presscoords: PressCoords] =
	BEGIN
	IF pressState # inobject THEN SIGNAL PressError;
	PutWord [pressDrawTo];
	PutWord [presscoords [X]];
	PutWord [presscoords [Y]];
	END;



DrawCurve: PUBLIC PROCEDURE [spline: PressSpline] =
	BEGIN OPEN spline;
	PutFloat: PROC [num: LONG CARDINAL] = BEGIN
	  PutLongWord [LOOPHOLE[InlineDefs.MesaToBcplLongNumber[num]]];
	  END;
	IF pressState # inobject THEN SIGNAL PressError;
	PutWord [pressDrawCurve];
	PutFloat[Cx];
	PutFloat[Cy];
	PutFloat[Bx];
	PutFloat[By];
	PutFloat[Ax];
	PutFloat[Ay];
	END;

SetXY: PROCEDURE [coords: PressCoords] =
	BEGIN 
	IF pressState # inpage THEN SIGNAL PressError;
	IF coords[X] # currentPressCoords [X] THEN 
		BEGIN
		AppendByteToEntity[pressSetX];
		AppendWordToEntity [coords [X]];
		currentPressCoords [X] ← coords [X];
		END;
	IF coords[Y] # currentPressCoords [Y] THEN
		BEGIN
		AppendByteToEntity[pressSetY];
		AppendWordToEntity[coords [Y]];
		currentPressCoords [Y] ← coords [Y];
		END;
	END;


InvalidXY: PROCEDURE = BEGIN currentPressCoords ← [30000, 30000] END;



SetColor: PROCEDURE [color: ColorDescriptor] =
	BEGIN OPEN color;
	IF pressState # inpage THEN SIGNAL PressError;
	IF hue # currentColor.hue THEN
		BEGIN
		AppendBytesToEntity [pressSetHue, hue];
		currentColor.hue ← hue;
		END;
	IF saturation # currentColor.saturation THEN
		BEGIN
		AppendBytesToEntity [pressSetSaturation, saturation];
		currentColor.saturation ← saturation;
		END;
	IF brightness # currentColor.brightness THEN
		BEGIN
		AppendBytesToEntity [pressSetBrightness, brightness];
		currentColor.brightness ← brightness;
		END;
	END;

SetFont: PROCEDURE [font: GriffinFontDefs.FontDescriptor] =
	BEGIN OPEN PressDefs; 
	rover: FontHandle ← fontList;
	UNTIL rover = NIL DO IF StringDefs.EquivalentString [rover.family, font.name] AND
			 rover.rotation = font.rotation AND rover.face = font.face AND
			 rover.size = font.points AND (rover.setnum = currentEntity.fontset
			OR currentEntity.fontset = NilFontSet) THEN BEGIN
			currentEntity.fontset ← rover.setnum; -- in case it was NilFontSet
			EXIT;
			END;
		rover ← rover.next;
		ENDLOOP;
	IF rover = NIL THEN BEGIN -- must add it
		rover ← Allocate [lFont];
		IF nextFontNum<16 THEN nextFontNum ← nextFontNum+1 ELSE BEGIN
			nextFontSet ← nextFontSet + 1;
			nextFontNum ← 1;
			EndEntity [];
			EnsureCurrentEntity;
			END;
		rover.font ← nextFontNum-1;
		rover.next ← fontList;
		fontList ← rover;
		rover.setnum ← nextFontSet-1;
		rover.m ← 0; rover.n ← 128;
		rover.face ← font.face;
		rover.source ← 0;
		rover.family ← AllocateString [font.name.length];
		rover.family.length ← 0;
		StringDefs.AppendString [rover.family, font.name];
		rover.size ← font.points;
		rover.rotation ← font.rotation;
		END;
	IF currentFontNum # rover.font THEN BEGIN
		AppendByteToEntity [pressSetFont + rover.font];
		currentFontNum ← rover.font
		END;
	END;

BeginPage: PUBLIC PROCEDURE [lowerleft: PressCoords ← PressCoords [0,0]] =
	BEGIN
	prevpage: PageDescriptorHandle ← currentPage;
	currentPage ← Allocate [lPageDescriptor];
	IF pressState # begun THEN SIGNAL PressError;
	IF pageList = NIL THEN pageList ← currentPage ELSE prevpage.next ← currentPage;
	currentPage.next ← NIL;
	currentPage.start ← NextDiskSector []; -- skip to next disk sector
	currentEntity ← entityList ← NIL;
	currentEntityBody ← NIL;
	currentColor ← ColorDescriptor [0, 0, 0];
	currentPressCoords ← PressCoords [0, 0];
	pressLowerLeft ← lowerleft;
	pressState ← inpage;
	END;

EndPage: PUBLIC PROCEDURE = BEGIN
	temp: EntityHandle;
	IF pressState # inpage THEN SIGNAL PressError;
	IF BytesPastBeginPrintedPage [] MOD 2 = 1 THEN PutByte [0];
	EndEntity [];
	PutWord [0];
	WHILE entityList # NIL DO OPEN entityList;
		-- write commands
		WriteEntity [entityList];
		-- write trailer
		IF fontset = NilFontSet THEN fontset ← 0; -- if it was free, set to 0
		PutByte [0]; PutByte [fontset]; 
		PutLongWord [dlbeginbyte];
		PutLongWord [dlbytelength];
		PutWord [pressLowerLeft [X]]; PutWord [pressLowerLeft [Y]];
		PutWord [0];
		PutWord [0];
		PutWord [maxPressDistance - pressLowerLeft [Y]];
		PutWord [maxPressDistance - pressLowerLeft [X]];
		PutWord [entitylength+12];
		temp ← next;
		Free [entityList]; 
		entityList ← temp
		ENDLOOP;
	currentPage.padding ← BytesRemainingInSector []/2;
	currentPage.length ← NextDiskSector [] - currentPage.start;
	pressState ← begun;
	END;

WriteEntity: PROCEDURE [entity: EntityHandle] = BEGIN OPEN entity;
	temp: EntityBodyHandle;
	j: EBIndex;
	WHILE entitybody # NIL DO OPEN entitybody;
		-- write out commands
		FOR j IN [0 .. lastbyte] DO PutByte [body [j]] ENDLOOP;
		temp ← next;
		FreeSegment [entitybody]; 
		entitybody ← temp
		ENDLOOP;
	RETURN
	END;

EnsureCurrentEntity: PROCEDURE = BEGIN
IF currentEntity = NIL OR currentEntity.finished THEN BeginEntity [];
END;

AppendByteToEntity: PROCEDURE [b: BYTE] = BEGIN OPEN currentEntityBody;
	newelength: LONG CARDINAL; -- only used sometimes
	IF currentEntity = NIL OR currentEntity.finished OR
		pressState # inpage THEN SIGNAL PressError;
	IF currentEntityBody = NIL THEN BEGIN
		currentEntityBody ← currentEntity.entitybody ← AllocateSegment [lEntityBody];
		next ← NIL;
		lastbyte ← 0;
		body [0] ← b;
		RETURN
		END;
	IF lastbyte = LAST [EBIndex] THEN BEGIN
		currentEntityBody ← next ← AllocateSegment [lEntityBody];
		newelength ← currentEntity.entitylength + (LAST [EBIndex]+1)/2;
		IF newelength > pressMaxELength THEN SIGNAL PressError;
		currentEntity.entitylength ← InlineDefs.LowHalf [newelength];
		lastbyte ← 0;
		next ← NIL;
		body [0] ← b;
		RETURN
		END;
	lastbyte ← lastbyte + 1;
	body [lastbyte] ← b;
	END;

AppendBytesToEntity: PROCEDURE [b1, b2: BYTE] = BEGIN
	AppendByteToEntity [b1];
	AppendByteToEntity [b2];
	END;

AppendWordToEntity: PROCEDURE [w: CARDINAL] = BEGIN
	AppendByteToEntity [InlineDefs.BITSHIFT [w, -8]];
	AppendByteToEntity [InlineDefs.LowByte [w]];
	END;





EndEntity: PROCEDURE = BEGIN OPEN currentEntity;
	IF pressState # inpage THEN SIGNAL PressError;
	IF currentEntity = NIL OR finished THEN RETURN;
	IF currentEntityBody.lastbyte MOD 2 = 0 THEN AppendByteToEntity [pressNop];
	dlbytelength ← BytesPastBeginPrintedPage [] - dlbeginbyte;
	entitylength ← entitylength + (currentEntityBody.lastbyte+1)/2;
	finished ← TRUE
	END;




BeginEntity: PROCEDURE = BEGIN OPEN currentEntity;
	newentity: EntityHandle = Allocate [lEntity];
	IF pressState # inpage THEN SIGNAL PressError;
	IF currentEntity # NIL THEN currentEntity.next ← newentity ELSE
		entityList ← newentity;
	currentEntity ← newentity;
	fontset ← NilFontSet; -- ie available
	dlbeginbyte ← BytesPastBeginPrintedPage [];
	entitylength ← 0;
	entitybody ← NIL;
	finished ← FALSE;
	next ← NIL;
	currentFontNum ← 0;
	END;



-- private low-level disk access procedures


PutByte: PROCEDURE [byte: CARDINAL [0 .. 255]] =
	BEGIN IF diskHandle # NIL THEN diskHandle.put [diskHandle, byte] END;




PutWord: PROCEDURE [word: CARDINAL] = 
	BEGIN
	IF diskHandle = NIL THEN RETURN;
	diskHandle.put [diskHandle, InlineDefs.BITSHIFT [word, -8]];
	diskHandle.put [diskHandle, InlineDefs.LowByte [word]];
	END;

PutLongWord: PROCEDURE [word: LONG CARDINAL] = 
	BEGIN OPEN InlineDefs;
	highhalf: CARDINAL = HighHalf [word];
	lowhalf: CARDINAL = LowHalf [word];
	IF diskHandle = NIL THEN RETURN;
	diskHandle.put [diskHandle, BITSHIFT [highhalf, -8]];
	diskHandle.put [diskHandle, LowByte [highhalf]];
	diskHandle.put [diskHandle, BITSHIFT [lowhalf, -8]];
	diskHandle.put [diskHandle, LowByte [lowhalf]];
	END;

PutString: PROCEDURE [string: STRING, maxbytes: BYTE] =
	BEGIN OPEN string;
	i: CARDINAL;
	maxchars: CARDINAL = maxbytes-1; -- one byte for length
	charcount: CARDINAL = MIN [maxchars, length];
	IF diskHandle = NIL THEN RETURN;
	diskHandle.put [diskHandle, length];
	FOR i IN [0 .. charcount) DO diskHandle.put [diskHandle, string [i]]; ENDLOOP;
	THROUGH [charcount .. maxchars) DO diskHandle.put [diskHandle, 0] ENDLOOP;
	END;





NextDiskSector: PROCEDURE RETURNS [SectorIndex] =
	BEGIN
	si: StreamDefs.StreamIndex;
	IF diskHandle = NIL THEN RETURN [SectorIndex [0]];
	si ← StreamDefs.GetIndex [diskHandle];
	IF si.byte # 0 THEN
		BEGIN
		THROUGH [si.byte .. 511] DO diskHandle.put [diskHandle, 0] ENDLOOP;
		si.page ← si.page+1
		END;
	RETURN [SectorIndex [si.page]];
	END;

BytesRemainingInSector: PROCEDURE RETURNS [CARDINAL] =
	BEGIN
	temp: CARDINAL;
	IF diskHandle = NIL THEN RETURN [0];
	temp ← BytesInSector - StreamDefs.GetIndex [diskHandle].byte;
	RETURN[IF temp=BytesInSector THEN 0 ELSE temp];
	END;

BytesPastBeginPrintedPage: PROCEDURE RETURNS [byte: LONG CARDINAL] =
	BEGIN
	si: StreamDefs.StreamIndex;
	IF diskHandle = NIL THEN RETURN [LONG [0]];
	si ← StreamDefs.GetIndex [diskHandle];
	RETURN [si.byte + BytesInSector*LONG [si.page - currentPage.start]]
	END;

END .