Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_SRC_3_19910112 - tvedit/io.sai
There are 3 other files named io.sai in the archive. Click here to see a list.
	!
	Copyright (c) 1977 by Pentti Kanerva
	All rights reserved
	;

COMMENT
	These routines are to replace the SAIL "INPUT" and "OUT" in
	TV-Edit.

    NOTATION:  Number of variables are introduced with DEFINE or are
otherwise special, and hence their use requires extra care.  The 
notation is supposed to draw attention to the special "variables".

	f.xxx	FIELD, an integer variable that is a byte pointer
		  that refers to a field xxx in AC0, i.e., address 
		  part and index field are 0.  
		  Ex.:  LDB (F.RDSEG + LINKS + I)
	p.xxx	Byte POINTER to xxx, an integer variable.
		  Ex.:  ILDB (P.INPUT)
	p.xxx!	Byte POINTER to xxx(AC1), uses AC1 as index, hence
		  to be used only in START!CODE.
		  Ex.:	MOVE 1, I
			LDB  2, P.RDSEG!
	m.xxx	MASK, DEFINEd, (i.e., introduced with DEFINE)
		  EX.:	TLNE 1, M.RDSEG
	xxx.	"Indexible" variable, DEFINEd.
		  Ex.:  DIR.(I) _ -1
		  Ex.:	X _ RDSEG.(I)
	xx#	An AC.
		  Ex.:  MOVE PG#, PAGE
	#xxx	Refers to #xxx.TV file.
		  Ex.:  RELJFN (#CHAN)

;

REQUIRE  "IOSYM"  LOAD!MODULE;	! "MEMORY" variables;

EXTERNAL INTEGER
	textP, safeP, holdP, freeP,
	rdPage, wrPage, segment,
	oFdb11, oFdb12, oFdb13, oFdb14, oFdb24, oFffp;

REQUIRE  "/\{}"  DELIMITERS;


COMMENT
			    FILE LAYOUT

File page	Contents
---------	--------
 0...f-1   Text of "old" file
 f...u-1   Free pages
 u...767   Updated file pages, assigned form top down
   770     TEMPPAGE, temp page used at finish time (by RECTIFY)
771...774  Unassigned (reserved)
   775     LINKPAGE, forward pointer chains of active, safe, held,
	     and free pages.
   776     DIRPAGE, run time page directory
   777     ODIRPAGE, page directory of "old" file

NOTES:	f = first free file page
	u = first used file page thereafter
	Page numbers are in OCTAL
;

DEFINE
	tempPage   = "'770",

	linkPage   = "'775",
	dirPage    = "'776",
	oDirPage   = "'777";

INTEGER
	fffp,		! First free file page;
	fufp;		! First used thereafter;
COMMENT
		      MEMORY (FORK) LAYOUT

Mem. Page	Contents
---------	--------
  0...377  SAIL low segment (variables, string space)
400...     High segment (code)
		-- Our PMAP section: --
500...537  Input buffer (IBUF)
   540	   SBUFMP, temp page for search command
541...567  ..free..
   570     OBUFMP, Output buffer (OBUF)
   571     TBUFMP, temp page (for TV-STRINGS.PMAP and others)
572...574  ..free..
   575     LINKMP, maps file page 775
   576     DIRMP, maps file page 776
   577     ODIRMP, maps file page 777 (of #file)
		-- End PMAP section --
600...677  SAIL buffers, segment
700...717  Emulator (for non-TENEX SAIL)
720...737  ..currently unused..
740...777  IDDT (767-777 only for UDDT)
;

DEFINE
	minIBufMP = "'500",
	maxIBufMP = "'537",
	sBufMP	  = "'540",
	oBufMP    = "'570",
	tBufMP    = "'571",
	linkMP    = "'575",
	dirMP     = "'576",
	oDirMP    = "'577",

	iBuf = "iBufMP LSH 9",

	oBuf = "oBufMP LSH 9",
	oBuf.(i) = "memory [oBuf + i]",

	tBuf = "tBufMP LSH 9",
	tBuf.(i) = "memory [tBuf + i]",

        sBuf = "sBufMP LSH 9",

	links = "linkMP LSH 9",
	links.(i) = "memory [links + i]",

	dir = "dirMP LSH 9",
	dir.(i) = "memory [dir + i]",

	oDir = "oDirMP LSH 9",
	oDir.(i) = "memory [oDir + i]";

INTEGER
	iBufMP,			! Current input buffer page;
	n.input, p.input,	! Input count and pointer;
	n.output, p.output;	! Output count and pointer;

DEFINE	eop = "(n.input LEQ 0)";
COMMENT
			LINK PAGE LAYOUT

0...770:  LINKS [i] = 

	     0,		      if file page does not exist,
  [tag, rdseg, wrseg, link],  if the page has text.


	.-.---------.-----------.------------------------.
	|T|  RDSEG  |   WRSEG   |          LINK          |
	'-'---------'-----------'------------------------'
	 0 1       8 9        17 18                    35


	  T   = TAG, used while checking link consistency.
	RDSEG = Segment # at time the page was read (i.e., became "old").
	WRSEG = Segment # at time the page was written (i.e., created).
	LINK  = Link to the next active page, -1 if end-of-file.

771:  textP, pointer to string of text pages
772:  safeP, pointer to string of safe pages
773:  holdP, pointer to string of held pages
774:  freeP, pointer to string of free pages
775:  rdPage, current read page
776:  wrPage, current write page
777:  segment #
;

DEFINE

	textP0 = "'771",
	safeP0 = "'772",
	holdP0 = "'773",
	freeP0 = "'774";

! Next 7 defined in IOSYM;

! !	textP = "links.(textP0)",
	safeP = "links.(safeP0)",
	holdP = "links.(holdP0)",
	freeP = "links.(freeP0)",

	rdPage = "links.('775)",
	wrPage = "links.('776)",
	segment= "links.('777)";

INTEGER
	nText, nSafe, nHold, nFree,
	f.rdSeg, f.wrSeg,	! Fields, byte pointers to AC0;
	p.rdSeg, p.wrSeg;	! Byte pointers that use AC1 as index;

! Masks for the fields, byte pointers to link array;
DEFINE
	m.rdSeg	= "'377000",
	m.wrSeg = "'000377",
	p.rdSeg.(pg) = "(f.rdSeg + links + pg)",
	p.wrSeg.(pg) = "(f.wrSeg + links + pg)",
	rdSeg.(pg) = "ldb (p.rdSeg.(pg))",
	wrSeg.(pg) = "ldb (p.wrSeg.(pg))";
	
COMMENT
		      DIRECTORY PAGE LAYOUT

0...770:  DIR [i] =

	  -1,		if dir entry for page  i  not yet computed.
			NOTE:  Dir entries are computed on demand.
   [n.ff, n.lf, n.ch],	if page  i  entry computed.


		.------------.------------.------------.
		|    N.FF    |    N.LF    |    N.CH    |
		'------------'------------'------------'
		 0         11 12        23 24        35	


	N.FF = No. of Form Feeds in file page  i,
	N.LF = No. of Line Feeds beyond last form feed of file page  i,
	N.CH = No. of charactere in file page  i.

771:  oFdb11, FDB['11] of finished file, for benefit of RESTORE
772:  oFdb12,
773:  oFdb24,
774:  oFffp, page where old file page 0 is stored
775:  oFdb13,
776:  oFdb14,
777:  DIR [777] = -1, flag for TEC/DATAMEDIA directory
;

! Next 4 defined in IOSYM;

! !	oFdb11 = "dir.('771)",
	oFdb12 = "dir.('772)",
	oFdb24 = "dir.('773)",
	oFffp  = "dir.('774)";

INTEGER
	n.ffIn,		! Counts FFs remaining in current input page;
	n.lfIn,		! Counts LFs remaining, once N.FFIN = 0;
	n.chIn,		! No. of characters on current input page;

	n.ffOut,	! Counts FFs of current output page;
	n.lfOut,	! Counts LFs since last FF;
	n.chOut,	! Counts characters of current output page;

	f.nFF, f.nLF, f.nCh;  ! Fields for the above counts--used also
				as byte pointers that refer to AC0;

DEFINE
	p.nFF.(pg) = "(f.nFF + dir + pg)",
	nFF.(pg) = "ldb (p.nFF.(pg))",
	p.nCh.(pg) = "(f.nCh + dir + pg)",
	nCh.(pg) = "ldb (p.nCh.(pg))",

	FF5 =	  "'060301406030";	! To bracket dir entries with
					  when they are on EOF-page;
INTEGER
	eofPage, eofBC, eofWC, 
	filling,
	saving;		! Semaphor-flag to prevent recursive call 
			  of finish;

DEFINE
	stopOnEop = "FALSE",
	inRange (pg) = "(0 LEQ pg < tempPage)",
	outOfRange = "NOT inRange";


! "Parameters" for the SAIL PMAP function;

DEFINE
	fork = "'400000000000 LOR",
	file = "(rdJfn LSH 18) LOR",
	#file= "(wrJfn LSH 18) LOR",
	rd# =  "'100000000000",
	wr# =  "'040000000000",
	copy#= "'000400000000",

	rdJfn = "inJfn", wrJfn = "outJfn";
		!******   GENERAL UTILITY   ******
		 ********************************;


DEFINE	decBP (ac) =
!	-----;
"
	add	ac, ['070000000000];
	tlne	ac,  '400000;
	 sub	ac, ['430000000001];
",

	decBPM (mem) =
!	------;
"
	exch	 mem;
	DECBP(0);
	exch	 mem;
";


DEFINE	changeUSW (jfn) =
!	---------
	Set Mode, Page, Line in USER WORD of FDB.
	Don't touch TV 252 flag.
	;
" BEGIN
     IF writeMode  THEN
     START!CODE
      	movsi	1, '24;
      	hrr	1, jfn;
	hrroi	2, '777000;
      	move	3, fdb['24];
      	CHFDB;			! Change USW;
     END
  END
";


FORWARD SUBROUTINE	unmapM (INTEGER jfn, p1, pm);
SUBROUTINE	salvage;
!		-------
	Move live strings from IBUF to SAIL string space.
	Unmap IBUF, init IBUFMP to MINIBUFMP - 1.
	;
BEGIN "salvage"  

   EXTERNAL INTEGER 
	stLnk,		! Points to string packet chain;
	gogTab, 	! ? table pase address;
   ! The following are indeces to GOGTAB;
	remChr,		! Negative count of free bytes in string space;
	topByte,	! Pointer to free string space;
	spdl,		! Initial SP;
	rACs;		! Where ACs are stored while garb collecting;

   EXTERNAL SUBROUTINE  strnGC;  ! String garbage collector, accepts
				   in AC1 no. bytes requested, wants
				   RF saved in RACS[RF];


   SUBROUTINE	saveStr (INTEGER pointer);
   !		-------
	POINTER points to first word of string descriptor.
	;
   START!CODE "saveStr"  acDef;  LABEL go, loop, xit;
	DEFINE ch# = "2", len# = "3", gp# = "4", pp# = "5", rem# = "6";

	move	1, pointer;
   ! Check length of string;
	hrrz	1, (1);
	jumpe	1, xit;		! Ignor empty string;
   ! Check for space remaining;
	move	user, gogtab;
	addm	1, remChr(user);
	skipg	 remChr(user);	! String space available?;
	 jrst	 go;		!  Yes;
   ! Must carbage collect;
	movei	2, rACs(user);	! To  MOVEM  RF, RACS+RF(USER);
	movem	rf, rf(2);	! Must save RF;
	pushj	p, strnGC;

   go:	move	1, pointer;
	hrrz	len#, (1);
	move	gp#, 1(1);
	move	pp#, topByte(user);
	movem	pp#, 1(1);	! Set new string pointer;
	seto	;
	hllm	 (1);		! To include in str garb collection;

 loop:	ildb	ch#, gp#;	! Transfer a byte;
	idpb	ch#, pp#;
	sojg	len#, loop;	! More bytes to transfer?;

	movem	pp#, topByte(user);	! Update str space pointer;
  xit:
   END "saveStr" 
   ;
   SUBROUTINE	saveBlock (INTEGER pointer, len);
   !		---------
	POINTER points to the first string in a block of LEN strings.
	;
   WHILE (dec(len)) GEQ 0  DO
   START!CODE "saveBlock"  LABEL bot;  acDef;
	DEFINE ptr# = "2", len# = "3", addr# = "4";

	move	ptr#, pointer;
	hrrz	len#, (ptr#);
	jumpe	len#, bot;	! IF NONEMPTY (str)..;
   ! Is string in IBUF?;
	move	addr#, 1(ptr#);
	ibp	 addr#;
	andi	addr#, -1;
	cail	addr#, minIBufMP LSH 9;	! ..AND it is in IBUF..;
	 caile	addr#, maxIBufMP LSH 9 + '777;
	  jrst	 bot;
	push	p, ptr#;	! ..THEN SAVESTR (POINTER); 
	pushj	p, saveStr;

  bot:	movei	 2;
	addm	 pointer;	! POINTER _ POINTER + 2;
   END "saveBlock" 
   ;

			! +  +  +  + ;

   IF iBufMP GEQ minIBufMP  THEN
   BEGIN  INTEGER ptr;
      ptr _ location (stLnk);

      WHILE ptr _ memory [ptr]
      DO saveBlock ((junk _ memory [ptr - 1]) LAND '777777, 
		     junk LSH -18)
      ;
! Save string stack;
      START!CODE  acDef;
	move	user, gogtab;
	hrrz	 spdl(user);
	addi	 1;
	push	p, ;		! Pointer to first entry in spdl;

	movn	 spdl(user);
	add	 sp;		! AC0 _ sp - bottom of stack;
	andi	 -1;
	lsh	 -1;		! Div by 2;
	push	p, ;		! No. of entries;
	pushj	p, saveBlock;
      END
      ;
      unmapM ('400000, minIBufMP, iBufMP MIN maxIBufMP);
   END
   ;
   iBufMP _ minIBufMP - 1;
END "salvage"
;
SUBROUTINE	setBuf (INTEGER buf0, val);
!		------
	A one-page buffer is set to VAL.
	;
START!CODE "setBuf"  DEFINE buf# = "2", pp# = "3";
	move	buf#, buf0;
	hrlz	pp#, buf#;
	hrr	pp#, buf#;
	addi	pp#, 1;		! PP# _ BUF,,BUF+1;
	move	 val;
	movem	 (buf#);	! BUF[0] _ VAL;
	blt	pp#, '777(buf#);! ARRBLT (BUF[1], BUF[0], '777);
END "setBuf" 
;


BOOLEAN SUBROUTINE	exists (INTEGER page);
!			------
	TRUE  iff  file page PAGE exists.
	;
START!CODE "exists"
	hrlz	1, rdJfn;	
	hrr	1, page;

	RPACS;

	movsi	1, '010000;	! Page exists-bit; 
	and	1, 2;			
END "exists"
;


INTEGER SUBROUTINE	rightOf (INTEGER pg);
!			-------
	RETURN (IF PG < 0  THEN -1
		ELSE IF (R _ RH(PG)) LEQ '770  THEN R  ELSE -1) .,
	;
START!CODE "rightOf"  LABEL eofp;
	skipge	1, pg;
	 jrst	 eofp;
	hrrz	1, links(1);
	caile	1, tempPage;
 eofp:	 seto	1, ;
END "rightOf"
;


SUBROUTINE	unMap (INTEGER ffP);	pMap (-1, ffP, 0);
!		-----;



SUBROUTINE	unMapM (INTEGER jfn, p1, pn);
!		------;
START!CODE "unMapM"  LABEL loop, mid, xit;  DEFINE pn# = "4";
	hrlz	2, jfn;
	hrr	2, p1;
	move	pn#, pn;

 loop:	jumpl	2, mid;		! No FFUFP for forks; 
	move	1, 2;

	FFUFP;		    ! More file pages?;

	 jrst	 xit;		! No;
	move	2, 1;

 mid:	caige	pn#, (2);
	 jrst	 xit;
	seto	1, ;
	setz	3,;

	PMAP;		    ! Unmap a page;
	
	aoja	2, loop;	! Next page;
  xit:
END "unMapM"
;


IFC tops20.sw  THENC

 SUBROUTINE	secure (INTEGER pg);
 !		------
	Write a page to disk.
	;
 START!CODE
	hrlz	1, rdJfn;
	hrr	1, pg;
	movei	2, 1;		! One page only;
	UFPGS;			! Update it on disk;
	 HALTF;
 END
 ;

 SUBROUTINE	rd#Map (INTEGER flP, fkP);
 !		------;
 IF exists (flP)  THEN pMap (file(flP), fork(fkP), rd#)
 ELSE unmap (fork(fkP))
 ;

ELSEC

 DEFINE secure (pg) = "";

 DEFINE	rd#Map (flP, fkP) = "pMap (file(flP), fork(fkP), rd#)";

ENDC
INTEGER SUBROUTINE	newEntry (INTEGER pg, n);
!			--------
	Scan  N  bytes of  PG  and make directory entry.
	PG is assumed to be valid (no checking done).
	;
BEGIN "newEntry"
   IF NOT exists (pg)  THEN dir.(pg) _ 0
   ELSE
   BEGIN
      pMap (file(pg), fork(tBufMP), rd#);

      START!CODE  LABEL loop, loop1, don;
	DEFINE	ch# = "2", bc# = "3", bp# = "4", 
		nch# = "5", nlf# = "6", nff# = "7"; 

	move	bp#, ['440700000000 + tBuf];
	move	bc#, n;
	movei	nch#, (bc#);
	setzb	nlf#, nff#;

loop:	sojl	bc#, don;	! A few control characters count here;
loop1:	ildb	ch#, bp#;
	jumpe	ch#, loop;
	movei	nch#, (bc#);	! Count at last non-0 byte;
	trne	ch#, LNOT(LF LOR FF);
	 sojge	bc#, loop1;	! Most chars count here;
	jumpl	bc#, don;
! LF/FF counting;
	cain	ch#, LF;
	 aoja	nlf#, loop;
	caie	ch#, FF;
	 jrst	 loop;
	setz	nlf#, ;
	aoja	nff#, loop;

don:
! Compile directory entry into AC0;
	move	bc#, n;		! Count at start;
	subi	bc#, (nch#);	! Less count at end;
	dpb	bc#, f.nCh;
	dpb	nlf#, f.nLF;
	dpb	nff#, f.nFF;

	move	1, pg;
	movem	 dir(1);	! Set directory word;
      END
      ;
      unMap (fork(tBufMP));  ! Remove fork page;
   END
   ;
   RETURN (dir.(pg));
END "newEntry"
;
INTEGER SUBROUTINE	dirEntry (INTEGER pg);
!			--------
	If NFF of entry is illegal, make a valid entry first using
	  NCH provided that it is legal.
	For pages out of range return 0.
	;
IF outOfRange (pg)  THEN RETURN (0)
ELSE
START!CODE "dirEntry"  acDef;  DEFINE pg# = "2", nff# = "3", nch# = "4";
	LABEL xit;

	move	pg#, pg;
	move	 dir(pg#);
	ldb	nff#, f.nFF;	! NFF _ NFF.(PG), via AC0;
	move	1, 0;		! AC1 _ DIR.(PG), for RETURN;
	caig	nff#, '5000;	! RETURN ( IF NFF LEQ '5000 ..;
	 jrst	 xit;		! .. THEN DIR.(PG) ..;
	push	p, pg#;		! .. ELSE ..;
	ldb	nch#, f.nCh;
	caile	nch#, '5000;
	 movei	nch#, '5000;
	push	p, nch#;
	pushj	p, newEntry;	! .. NEWENTRY (PG, NCH MIN '5000) );
  xit:	
END "dirEntry"  
;
SUBROUTINE	loadN.xxIn;
!		----------
	Unpacks  DIR (RDPAGE)  into  N.FFIN,  N.LFIN,  N.CHIN.
	Makes the entry first if not already present.
	;
START!CODE "loadN.xxIn"  acDef;
	push	p, rdPage;
	pushj	p, dirEntry;	! Makes entry if none exists;
	move	0, 1;		! Result in AC0, since..;
! ..byte pointers refer to AC0;
	ldb	1, f.nFF;
	movem	1, n.ffIn;
	ldb	1, f.nLF;
	movem	1, n.lfIn;
	ldb	1, f.nCh;
	movem	1, n.chIn;
END "loadN.xxIn"
;
DEFINE	addFFLF! (pg, ln, nff, nlf) =
!	--------
	MACRO to add FF and LF counts to PaGe and LiNe no.:
	  PG _ PG + NFF .,
	  LN _ IF NFF THEN NLF ELSE LN + NLF .,
	;
"	move	 nlf;
	skipn	1, nff;
	 add	 ln;
	movem	 ln;
	addm	1, pg;
",

	addFFLF (pg, ln, nff, nlf) =
!	-------
	See comment above.
	;
" START!CODE  addFFLF! (pg, ln, nff, nlf)  END ";



SUBROUTINE	addDirEntry (REFERENCE INTEGER pg, ln; INTEGER filp);
!		-----------;
START!CODE "addDirEntry"  acDef;  DEFINE nff# = "2", nlf# = "3";
	push	p, filp;
	pushj	p, dirEntry;	! Makes entry if none exists;
	move	0, 1;		! Dir entry to AC0;
	ldb	nff#, f.nFF;
	ldb	nlf#, f.nLF;

	ADDFFLF! (pg, ln, nff#, nlf#);
END "addDirEntry" 
;


SUBROUTINE	movePStr (INTEGER p0, pm, q0);
!		--------
	Move (i.e., insert) Page STRing (P0, PM) between pages Q0 and
	  Q1,  Q1 = RIGHTOF(Q0).
	P0 is the page to the left of first page to be moved.  The
	  pages moved are P1...PM,  P1 = RIGHTOF(P0).
	An attempt to move an empty page string (P0 = PM) is a no-op.
	;
START!CODE "movePStr"  LABEL xit;
	DEFINE	p0# = "2", p1# = "3", pm# = "4", pm1# = "5", 
		q0# = "6", q1# = "7";

	hrre	p0#, p0;
	hrre	pm#, pm;
	hrre	q0#, q0;
! Validity check;
	jumpl	pm#, xit;	! PM past EOF?;
	camn	p0#, pm#;
	 jrst	 xit;		! Empty string;

	hrre	p1#, links (p0#);
	hrre	pm1#, links (pm#);
	hrre	q1#, links (q0#);
	
	hrrm	pm1#, links (p0#);	! Remove from where was;
	hrrm	q1#, links (pm#);	! Tie end of page string;
	hrrm	p1#, links (q0#);	! Tie beginning of page string;
  xit:
END "movePStr"
;


DEFINE  moveP1 (p0, q0) = "movePStr (p0, rightOf(p0), q0)";
!	------
	Move (insert) page P1 to right of page Q0.  No checks.
	;


INTEGER SUBROUTINE	nExtraFree;
!			----------
	Return no. of free pages in excess of ones that might be
	  required for forced finish (by PMAPOUT or whoever).
	;
RETURN ( IF readOnly THEN zillion ELSE
	 (nFree - ((textSize + maxY1)*maxLength + n.input) DIV '5000
	  + 1)
       );
	REQUIRE  "ioIo."  SOURCE!FILE;
	REQUIRE  "ioSeg."  SOURCE!FILE;
	REQUIRE  "ioFin."  SOURCE!FILE;
	REQUIRE  "ioInit."  SOURCE!FILE;

REQUIRE  UNSTACK!DELIMITERS;



			! TO CONSIDER:

-- Remove UNMAPs from ends of procedures.

-- PMAP DIR PAGE back to file at end of read-only edit (to save work).

   QUESTION:  Is it possible to PMAP to page if file opened in read
	only mode?
;
COMMENT  End of file  IO;