Google
 

Trailing-Edge - PDP-10 Archives - FORTRAN-10_V7wLink_Feb83 - forfmt.mac
There are 11 other files named forfmt.mac in the archive. Click here to see a list.

	SEARCH	FORPRM
	TV	FORFMT	FORMAT PROCESSOR,7(3250)

;COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1981, 1983

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
;ONLY  IN  ACCORDANCE  WITH  THE  TERMS  OF  SUCH LICENSE AND WITH THE
;INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE OR ANY  OTHER
;COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
;OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF THE  SOFTWARE  IS  HEREBY
;TRANSFERRED.

;THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT  NOTICE
;AND  SHOULD  NOT  BE  CONSTRUED  AS A COMMITMENT BY DIGITAL EQUIPMENT
;CORPORATION.

;DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY  OF  ITS
;SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.


COMMENT \

***** Begin Revision History *****

1100	JLC	New

1164	CKS	27-Oct-80
	Prevent TL from going off left end of record

1207	JLC	19-Dec-80
	Prevent %%GETIO from substituting REAL for COMPLEX for
	list-directed I/O

1215	JLC	6-Jan-81
	SFDEL was dropping off the end into dollar format. Inserted a
	POPJ. Edit revhist actually inserted 9-Feb-81, was lost.

1230	JLC	20-Jan-81
	Increased size of repeat count to 9 bits, decreased size of
	exponent field to 3 bits.

1306	DAW	26-Feb-81
	New arg list format from %SAVE

1321	DAW	6-Mar-81
	More changes for extended addressing support.

1324	EDS	9-Mar-81	Q20-01396
	Add an encoding address for DOLLAR ($) and COLON (:) format
	so that ($10A5) is not treated as ($10,A5).  These should
	use the same encoding as SLASH (/) format.

1334	DAW	12-Mar-81
	Use new macros $BLDBP, $LODBP, $STRBP to clean up the code.

1416	JLC	10-Apr-81
	Q format must use IRBUF.

1464	DAW	12-May-81
	Error messages.

1467	CKS	18-May-81
	Change EXTADD to I.EXT, change TITLE to FORFMT to correspond to
	file name

1473	CKS	21-May-81
	Error message fixes.

1521	JLC	26-Jun-81
	Fix formatted EOF processing: in input loop, check if pending
	EOF. If so, toss rest of I/O list.

1532	DAW	14-Jul-81
	OPEN rewrite: Base level 1

1535	JLC	14-Jul-81
	Remove EOF handling from here, now handled in %DOIO.

1552	DMN	21-Jul-81
	Performance enhancements.

1562	DMN	28-Jul-81
	Performance enhancements.

1566	JLC	29-Jul-81
	Add error diagnostics for digits with no formats, any repeat
	count for $,/,Q, etc.

1575	JLC	05-Aug-81
	Edit 1566 was too all-inclusive, made repeat count of left
	paren break. Fix by making left paren not a delimiter,
	then check in left paren routine for previous format.

1600	DAW	11-Aug-81
	Allow "X" to mean "1X" (as it used to!)

1605	JLC	12-Aug-81
	Fix ambiguous (A419X) to be error. Fix T to be a delimiter.

1615	DAW	19-Aug-81
	Get rid of two word BP option.

1622	JLC	21-Aug-81
	Fix Q format encoding, was accumulating digits after it.
	Fix no-comma syntax, compile prev format if leftover
	format char.

1625	DAW	21-Aug-81
	Get rid of "DF".

1705	JLC	11-Sep-81
	Fixed serious T-format bug. Now send %SPOS position desired
	for next character (minimum 1) so that ORVIR in %OBYTE will
	never have zero for a positioning command.

1730	JLC	18-Sep-81
	Another bug fix for T-format. Sped up format encoding by
	eliminating FMTSS and using a local stack instead.

1775	JLC	9-Oct-81
	Fix X format. Was being truncated to 8 bits.

2033	JLC	15-Nov-81
	Make "Data in IO list but not in format" go to %ABORT.
	Fix A free format.

***** Begin Version 6A *****

2045	EGM	18-Mar-82
	Allow error ARC (Ambiguous repeat count in FORMAT) to continue.

***** Begin Version 7 *****

3006	AHM/JLC	29-Oct-81
	When storing the indefinite repeat pointer (%FMRPT) in LPRENC,
	use an XMOVEI to generate a global address.

3012	JLC	5-Nov-81
	Changes for new arg copier - A.FMS is an address of the
	size of the format.

3014	JLC	5-Nov-81
	Added flag (SKPFLG) to stop skipping for delimiters
	with free-format A. Simplified %%GETIO.

3023	JLC	15-Nov-81
	Make fatal error msg "Data in IO list but not in format" go
	to %ABORT.

3031	JLC	11-Dec-81
	Modify format encoder to get format size from word before format
	if there is no size in the arg block (i.e., if the address containing
	the value is zero).

3035	JLC	5-Feb-82
	Rework of arg passing mechanism. Coroutine now isolated to this
	module.

3056	JLC	23-Mar-82
	Implement 2-word encoded formats, along with encoding in a fixed
	area (expandable) and allocating separately. Implemented range-
	checking, and format/type checking (warning if minor conflict,
	hard error if character variable and other than A or G format).

3061	JLC	25-Mar-82
	Catch Hollerith and quoted string input to character formats -
	now illegal. Make sure the encoded list pntr can't overflow
	on left parens.

3064	JLC	26-Mar-82
	Implement new range-checking mechanism, simple one just would
	not do.

3065	JLC	26-Mar-82
	Fix format encoder to drop relative addresses in links and
	indefinite repeat pointer, as the encoded format is moved
	after it is encoded.

3077	JLC	5-Apr-82
	Fix format encoder to do all calculations involving indefinite
	repeat and links as relative addresses - recursive calls across
	format buffer expansions caused bad links.

3122	JLC	28-May-82
	Output warning message for Hollerith and quoted string input.

3131	JLC	11-Jun-82
	Fix Iw.m, was accumulating default value instead of clearing
	it upon getting period.

3136	JLC	26-Jun-82
	Give user value of bad variable for format/variable mismatch.
	Do some entry code optimization. Make ENC.LR available for
	alphabetic I/O optimization.

3175	JLC	8-Sep-82
	Fix SAVFMT, was not initializing FMT.BG.

3202	JLC	26-Oct-82
	Fix SAVFMT/CLRFMT so they don't use the descriptor address
	for hashing, since it can be a Q-temp.

3225	JLC	24-Nov-82
	Change SIXVRT so it converts tabs to spaces.

3231	JLC	14-Dec-82
	Fix type mismatch check so it catches TP%LIT going to
	other than alphabetic

3250	JLC	7-Jan-83
	Pay attention to number of args in SAVFMT.

***** End Revision History *****

\
	ENTRY	%IFSET,%OFSET

	EXTERN	%OBYTE,%IBYTE,%IBYTC,%IRECS,%OREC,%ORECS,%SETAV
	EXTERN	%RIPOS,%ROPOS,%SIPOS,%SOPOS,%CIPOS,%COPOS,%MVBLK
	EXTERN	%ALPHI,%ALPHO,%DIRT,%DOUBT,%ERIN,%EOUT,%FLIRT,%FLOUT
	EXTERN	%LINT,%LOUT,%GRIN,%GROUT,%INTI,%INTO,%RIGHI,%RIGHO
	EXTERN	%GLINT,%GLOUT,%GINTI,%GINTO,%GOCTI,%GOCTO
	EXTERN	%OCTI,%OCTO,%HEXI,%HEXO,%SIZTB,%UNFXD
	EXTERN	%GTBLK,%FREBL,%SAVE4,%IBACK,%OVNUM
	EXTERN	%ABORT,%POPJ,%POPJ1
	EXTERN	FMT.LS,%FAREA,%FTSLB
	EXTERN	A.FMT,T.FMT,A.FMS
	INTERN	%FMTSV,%FMTCL,%IFORM,%OFORM
	INTERN	SCL.SV,USR.AD,USR.SZ,ENC.WD,ENC.W2,ENC.LR
	INTERN	W.PNTR,D.PNTR,X.PNTR
	INTERN	FMT.BG,FMT.BP,FMT.SZ

;ENCODED FORMAT BLOCK PARAMS
%FMTNX==0			;NEXT ENCODED FORMAT ADDR
%FMTAD==1			;ACTUAL ADDR OF FORMAT STATEMENT
%FMTOV==2			;OVERLAY NUMBER
%FMTYP==3			;FORMAT TYPE
%FMTRP==4			;INDEFINITE REPEAT PNTR
%FMTEN==5			;FIRST WORD OF ENCODED FORMAT

DATFLG==1,,0			;DATA READ OR WRITTEN
FDFLG==2,,0			;FORMAT DELIMITER
REGFLG==4,,0			;REGISTER FORMAT CHARACTER
IGNFLG==10,,0			;IGNORE CHARACTER
IOLFLG==20,,0			;I/O LIST ENTRY NEEDED
SKPFLG==40,,0			;CALL SFDEL AFTER FREE-FORMAT INPUT
DRFLG==FDFLG+REGFLG		;DELIMITER+REGISTER
DRIFLG==DRFLG+IOLFLG		;DELIMITER+REGISTER+I/O LIST ENTRY
DTIFLG==DATFLG+REGFLG+IOLFLG	;DATA+REGISTER+I/O LIST ENTRY
DTISFL==DTIFLG+SKPFLG		;DATA+REGISTER+I/O LIST ENTRY+CALL SFDEL

	SEGMENT	DATA

;FOR RECUSIVE I/O (I/O WITHIN I/O), THESE SHOULD BE
;DDB BLOCK VARIABLES - USED DURING EXECUTION
FMT.LK:	BLOCK	1	;PNTR TO ENCODED LEFT PAREN (RH=LINK TO AFTER LOOP)
SCL.SV:	BLOCK	1	;SCALE FACTOR
ENC.AD:	BLOCK	1	;ADDRESS OF CURRENT ENCODED FORMAT
ENC.PT:	BLOCK	1	;FORMAT STACK POINTER
ENC.RP:	BLOCK	1	;LOOP EXECUTION REPEAT COUNT
ENC.LR:	BLOCK	1	;LOCAL REPEAT COUNT

;LOCAL VARIABLES - USED BY THE FORMAT ENCODER AND/OR AS TEMPS
;BY THE FORMAT EXECUTION
FASIZE:	BLOCK	1	;FORMAT AREA SIZE
FMTSTA:	BLOCK	1	;ENCODING STATE
SAV.EF:	BLOCK	1	;ADDR OF LOC TO SAVE ADDR OF ENCODED FORMAT
RPT.PT:	BLOCK	1	;INDEFINITE REPEAT POINTER
FMT.BG:	BLOCK	1	;BYTE POINTER TO BEGINNING OF FORMAT
FMT.BP:	BLOCK	1	;FORMAT BYTE POINTER
FMT.IU:	BLOCK	1	;I/O LIST ENTRY USED FLAG
NUM.AD:	BLOCK	1	;DIGIT ACCUMULATOR PNTR

ENC.WD:	BLOCK	2	;ENCODED FORMAT WORDS
ENC.W2=ENC.WD+1


USR.AD:	BLOCK	2	;ACTUAL FORMAT ADDRESS, OVERLAY NUMBER
USR.OV=USR.AD+1		;OVERLAY NUMBER

USR.SZ:	BLOCK	1	;SIZE OF FORMAT IN CHARACTERS
FMT.CC:	BLOCK	1	;CURRENT FORMAT CHAR
FMT.PC:	BLOCK	1	;PREVIOUS CHAR
FMT.SZ:	BLOCK	1	;SIZE OF FORMAT IN CHARS

FMT.DB:			;ENCODING DATABASE - CLEARED BY FMTINT
GTDGFL:	BLOCK	1	;GOT DIGIT FLAG
FMT.SG:	BLOCK	1	;SIGN
FMT.CH:	BLOCK	1	;FORMAT CHARACTER (SIXBIT)

;THE NEXT FOUR MUST BE IN ORDER, AS THEY ARE REFERENCED BY STATE TABLE
FMTACC:
FMT.RP:	BLOCK	1	;REPEAT COUNT
FMT.FW:	BLOCK	1	;FORMAT WIDTH
FMT.DW:	BLOCK	1	;DECIMAL WIDTH
FMT.EW:	BLOCK	1	;EXPONENT WIDTH

FMT.EN==.-1		;END OF ENCODING DATABASE

	SEGMENT	CODE

;BYTE POINTERS TO FORMAT ATTRIBUTES (WIDTH, DECIMAL WIDTH, ETC.)

W.PNTR:
WIDPNT:	POINT	18,ENC.W2,35	;TOTAL WIDTH OF FORMAT ELEMENT
FWMAX==777777

RPTPNT:	POINT	18,ENC.WD,35	;REPEAT COUNT
RPTMAX==777777

D.PNTR:
DECPNT:	POINT	6,ENC.WD,11	;DECIMAL WIDTH
DWMAX==77

X.PNTR:
EWPNT:	POINT	4,ENC.WD,5	;EXPONENT WIDTH
EWMAX==17			;AVOID BIT 0 SO IT WON'T BE NEGATIVE
				;AS NEGATIVE MEANS PAREN REPEAT

CODPNT:	POINT	6,ENC.WD,17	;FORMAT CODE (SIXBIT CHAR)

;THE FOLLOWING TABLE CONTAINS INFORMATION ON HOW TO TREAT
;EACH CHARACTER FOUND IN A FORMAT STATEMENT. THE
;TABLE IS ARRANGED SO THAT THE CHARACTER (TRANSLATED TO SIXBIT)
;CAN BE USED AS AN INDEX INTO THE TABLE. THE LEFT HALF OF EACH
;WORD CONTAINS THE FLAGS ASSOCIATED WITH THE CHARACTER (FOR INSTANCE,
;THE LEFT HALF OF THE ENTRY FOR 'A' HAS DTIFLG, WHICH IS A COMPOSITE
;OF FLAGS WHICH DIRECT THE FORMAT ENCODER TO REGISTER THE FORMAT,
;THAT IS, IT IS A "MAIN" FORMAT CHARACTER,
;AND THAT THIS FORMAT CHARACTER HAS AN ITEM OF DATA ASSOCIATED WITH IT).
;THE RIGHT HALF OF EACH ENTRY IS THE ADDRESS OF ANOTHER TABLE ENTRY
;WHICH CONTAINS THE ADDRESS OF ANY SPECIAL
;CODE TO EXECUTE FOR THE PROCESSING OF THE FORMAT CHARACTER.
FMT.CT:	IGNFLG			;SPACE
	0			;!
	0			;"
	0			;#
	DRFLG+DOLFMT		;$
	0			;%
	0			;&
	DRFLG+SQFMT		;'
	LPRFMT			;(
	FDFLG+RPRFMT		;)
	0			;*
	IGNFLG			;+
	FDFLG			;COMMA
	MINFMT			;-
	PERFMT			;.
	DRFLG+SLHFMT		;/
	DIGFMT			;0
	DIGFMT			;1
	DIGFMT			;2
	DIGFMT			;3
	DIGFMT			;4
	DIGFMT			;5
	DIGFMT			;6
	DIGFMT			;7
	DIGFMT			;8
	DIGFMT			;9
	DRFLG+COLFMT		;:
	0			;;
	0			;<
	0			;=
	0			;>
	0			;?
	0			;@
	DTIFLG+AFMT		;A
	DRFLG+BFMT		;B
	0			;C
	DTISFL+DFMT		;D
	DTISFL+EFMT		;E
	DTISFL+FFMT		;F
	DTIFLG+GFMT		;G
	REGFLG+HFMT		;H
	DTISFL+IFMT		;I
	0			;J
	0			;K
	DTISFL+LFMT		;L
	0			;M
	0			;N
	DTISFL+OFMT		;O
	REGFLG+PFMT		;P
	DRIFLG+QFMT		;Q
	DTISFL+RFMT		;R
	DRFLG+SFMT		;S
	DRFLG+TFMT		;T
	0			;U
	0			;V
	0			;W
	REGFLG+XFMT		;X
	0			;Y
	DTISFL+ZFMT		;Z
	0			;[
	0			;\
	0			;]
	0			;^
	0			;_

;THIS IS THE STATE TABLE. THERE ARE 4 STATES:
;
RPSTA==0	;COLLECTING A REPEAT COUNT
FWSTA==1	;COLLECTING A FORMAT WIDTH
DWSTA==2	;COLLECTING A DECIMAL WIDTH
EWSTA==3	;COLLECTING AN EXPONENT WIDTH


RPNOK==1
FWNOK==2
DWNOK==4
EWNOK==10
RPZOK==20
FWZOK==40
DWZOK==100
EWZOK==200
RPBOK==400
FWBOK==1000
DWBOK==2000
EWBOK==4000

STAMAX:	RPTMAX
	FWMAX
	DWMAX
	EWMAX

STANEG:	RPNOK
	FWNOK
	DWNOK
	EWNOK

STAZER:	RPZOK
	FWZOK
	DWZOK
	EWZOK

STABLK:	RPBOK
	FWBOK
	DWBOK
	EWBOK

STAERR:	$ECALL	IRC,%ABORT
	$ECALL	IFW,%ABORT
	$ECALL	IFW,%ABORT
	$ECALL	IFW,%ABORT

;THERE ARE TWO STEPS INVOLVED HERE, FORMAT ENCODING AND FORMAT
;EXECUTION. FORMAT ENCODING INVOLVES CODING EACH "MAIN" FORMAT
;CHARACTER (SUCH AS A,E,I, ETC.) INTO A WORD (OR 2) CONTAINING
;THE FORMAT WIDTH, REPEAT COUNT, DECIMAL WIDTH, ETC. THIS ENCODING
;IS DONE BECAUSE STRAIGHT INTERPRETIVE EXECUTION OF FORMAT
;STATEMENTS IS SLOW AND AWKWARD (ESPECIALLY WITH INDEFINITE REPEAT).
;ONCE A FORMAT STATEMENT IS ENCODED, THE ENCODED VERSION IS SAVED
;FOR LATER USE. THERE IS SOME OVERHEAD SEARCHING FOR THE
;ENCODED VERSION, BUT THIS IS CUT DOWN BY HAVING FMTN DIFFERENT LINKED
;LISTS WHERE IT CAN RESIDE. (DIVIDING BY FMTN GIVES
;WHICH LIST TO SEARCH). FORMATS IN ARRAYS ARE ALSO ENCODED, BUT
;UNDER NORMAL CIRCUMSTANCES THE ENCODED VERSIONS ARE NOT SAVED;
;THE USER MAY FORCE FOROTS TO KEEP THEM, HOWEVER, WITH
;A SPECIAL SUBROUTINE CALL (FMTSAV).
;IN ORDER TO FACILITATE THE PROCESSING OF PARENS IN THE FORMAT
;STATEMENT, THE CODE FOR PROCESSING IS FULLY RECURSIVE. A PAREN
;IS ENCODED WITH AN ASSOCIATED COUNT AND A LINK
;TO THE ENCODED ENTRIES AFTER THE ASSOCIATED RIGHT PAREN. A
;RIGHT PAREN IS ENCODED AS A ZERO ENTRY. THIS STRUCTURE FACILITATES
;A SIMPLE RECURSIVE PROCEDURE FOR EXECUTION OF THE FORMAT
;AND ALSO FACILITATES THE PROCESSING OF INDEFINITE REPEAT. ITS
;DISADVANTAGE IS THAT THE STACK LENGTH RESTRICTS THE NUMBER
;OF IMBEDDED PARENS.
;THERE ARE SEVERAL PATHOLOGICAL CASES OF INTEREST.
;IF THE USER SPECIFIES A FORMAT IN AN ARRAY, S/HE MAY LEAVE OFF
;ONE OR BOTH PARENS. IF THE LEFT PAREN IS MISSING, AND THERE ARE
;NO LEFT PARENS IN THE REST OF THE FORMAT STATEMENT, THE ENCODER
;WILL NEVER BE CALLED RECURSIVELY; WHETHER OR NOT THE FORMAT ENDS
;WITH A RIGHT PAREN, AN "END PAREN" ENTRY (A ZERO WORD) WILL
;BE PLACED AT THE END OF THE ENCODED FORMAT, AND WILL THUS
;EXECUTE CORRECTLY. IF THERE ARE LEFT PARENS IMBEDDED IN THE FORMAT,
;THE ENCODER WILL BE CALLED CORRECTLY, RECURSIVELY. THE ENCODER
;WILL CONTINUE BEYOND THE MATCHING RIGHT PARENS UNTIL IT ENCOUNTERS
;EITHER AN UNMATCHED RIGHT PAREN OR THE END OF THE FORMAT; IN THIS CASE
;IT WILL ALSO EXECUTE CORRECTLY, AS IF THERE HAD BEEN A BEGINNING
;LEFT PAREN.
;INDEFINITE REPEAT IS HANDLED BY SAVING A POINTER TO THE LAST ENCODED
;LEFT PAREN IN THE FORMAT STATEMENT MATCHING THE
;NEXT TO LAST RIGHT PAREN (WHICH MAY BE THE INITIAL LEFT
;LEFT PAREN, OR IMPLIED LEFT PAREN IF THE USER FORGOT IT...). THUS,
;IN ACCORDANCE WITH THE ANSI-66 AND ANSI-77 STANDARDS, THE LOOP
;USED IS THE ONE PRECEDING THE FINAL RIGHT PAREN, OR THE ENTIRE
;FORMAT STATEMENT IF THERE IS NO INTERNAL LOOP.

;AN ENCODED FORMAT IS AS FOLLOWS:
;
;WORD			DESCRIPTION
;
;  0			ADDR OF NEXT ENCODED FORMAT IN THIS LIST
;  1			ADDR OR BYTE POINTER OF ACTUAL FORMAT
;  2			OVERLAY NUMBER
;  3			INDEFINITE REPEAT POINTER
;  4			ENCODED WORD 1
;  5			ENCODED WORD 2
;  .				.
;  .				.
;
;THERE ARE MANY SUCH LINKED LISTS OF ENCODED FORMATS, THE NUMBER
;DETERMINED BY THE FORPRM PARAMETER FMTN, WHICH SERVES AS A
;HASHING NUMBER - THE USER'S FORMAT ADDRESS IS DIVIDED BY THIS
;NUMBER, AND THE REMAINDER IS USED AS AN INDEX INTO THE ENCODED
;FORMAT LIST OF LISTS (FMT.LS). EACH ENTRY IN FMT.LS POINTS
;TO THE BEGINNING OF A LINKED LIST OF ENCODED FORMATS.
;
;PROVISION HAS BEEN MADE FOR THE USER TO SPECIFY THAT A FORMAT
;IN AN ARRAY SHOULD BE ENCODED AND STORED (UNDER NORMAL CIRCUMSTANCES
;THE ENCODED FORMAT FROM AN ARRAY IS THROWN AWAY). THIS FEATURE IS
;PROVIDED VIA TWO SPECIAL CALLS - FMTSAV & FMTCLR:
;
;	CALL FMTSAV (array name, number of array elements)
;
;		THIS CALL CALLS THE FORMAT ENCODER AND FORCES IT
;		TO SAVE THE ENCODED FORMAT. IF THIS ROUTINE IS
;		CALLED WITH AN ARRAY WHICH HAS ALREADY BEEN
;		ENCODED, THE OLD ENCODING WILL BE THROWN AWAY
;		AND A NEW ONE CREATED.
;
;	CALL FMTCLR (array name)
;
;		THIS CALL THROWS AWAY THE ENCODED FORMAT BY
;		DEALLOCATING THE CORE AND RELINKING THE
;		OTHER ENCODED FORMATS IN THE LIST.
;
;THE IMPLEMENTATION OF THESE SUBROUTINES MAKES IT NECESSARY TO
;SEARCH THE LISTS OF ENCODED FORMATS WHETHER OR NOT THE FORMAT IS
;IN AN ARRAY. HOWEVER, SINCE THE LISTS HAVE BEEN HASH-CODED, THIS
;SHOULD NOT SLOW PROCESSING SIGNIFICANTLY.
;%IFSET AND %OFSET ARE THE FORMAT CALLS FROM FOROTS. WE FIRST SEARCH FOR
;THE FORMAT IN THE LIST OF ENCODED FORMATS. IF IT EXISTS,
;WE GO ON TO FORMAT EXECUTION. OTHERWISE, WE ENCODE IT AND THEN
;EXECUTE IT.

%IFSET:	SKIPA	T1,[JRST DATIN]		;SETUP FOR INPUT
%OFSET:	MOVE	T1,[JRST DATOUT]	;SETUP FOR OUTPUT
	MOVEM	T1,DATENT
	MOVEM	P,OTHERP		;SAVE THE CURRENT PDP
	MOVE	P,IOPDP			;GET A PRIVATE ONE FOR COROUTINE
	MOVE	T1,A.FMT		;GET ADDRESS OF FORMAT
	PUSHJ	P,%OVNUM		;AND LINK NUMBER
	DMOVEM	T1,USR.AD		;SAVE THEM
	MOVE	T2,T.FMT		;GET ARG TYPE
	CAIE	T2,TP%CHR		;TYPE CHARACTER?
	 JRST	FNCHR			;NO

	SKIPN	T3,A.FMS		;ANY SIZE SPECIFIED?
	 SKIPA	T3,1(T1)		;NO. GET IT FROM DESCRIPTOR
	  MOVE	T3,(T3)			;YES. GET IT
	MOVEM	T3,USR.SZ		;SAVE IT
	MOVE	T3,(T1)			;GET THE ACTUAL BP
	MOVEM	T3,FMT.BG		;SAVE IT
	JRST	GOTSIZ

FNCHR:	SKIPN	T3,A.FMS		;ANY SIZE SPECIFIED?
	 SKIPA	T3,-1(T1)		;NO. GET IT FROM FORMAT ITSELF
	  MOVE	T3,(T3)			;YES. GET IT
	IMULI	T3,5			;GET # CHARS
	MOVEM	T3,USR.SZ		;SAVE IT
	$BLDBP	T1			;Make 7-bit byte ptr.
	MOVEM	T1,FMT.BG		;SAVE IT

GOTSIZ:	PUSHJ	P,FMTSRH		;SEARCH FOR ENCODED FORMAT
	MOVEM	T1,ENC.AD		;SAVE THE ADDR FOUND
	JUMPN	T1,FMTEXC		;GO EXECUTE IF ALREADY ENCODED

	PUSHJ	P,FMTENC		;ENCODE THE FORMAT
	MOVE	T1,T.FMT		;GET FORMAT TYPE
	CAIN	T1,TP%LBL		;LABEL?
	 PUSHJ	P,BLTFMT		;YES. SAVE IT SOMEWHERE
	JRST	FMTEXC			;EXECUTE IT

BLTFMT:	XMOVEI	T1,1(P4)		;GET TOP+1 OF ENCODED FORMAT
	SUB	T1,ENC.AD		;GET # WORDS IN IT
	PUSHJ	P,%GTBLK		;GET A BLOCK THAT SIZE
	MOVEM	T1,@SAV.EF		;SAVE ITS ADDRESS AND LINK IT
	HRRI	T2,(T1)			;PREPARE FOR MOVING IT
	HRL	T2,ENC.AD
	XMOVEI	P4,(P4)			;GET TOP, EXTENDED
	SUB	P4,ENC.AD		;GET # WORDS-1
	ADDI	P4,(T1)			;GET LAST ADDRESS
	BLT	T2,(P4)			;TRANSFER IT
	POPJ	P,

%ADR==0				;USER ARRAY ADDRESS
%SIZ==1				;USER ARRAY SIZE (IN ARRAY ELEMENTS)

;THIS IS THE FAMOUS ARRAY FORMAT ENCODER.  IT ALLOWS THE USER
;TO HAVE FOROTS SAVE AWAY THE ENCODED VERSION OF THE FORMAT IN AN ARRAY.
;IF IT HAS ALREADY BEEN ENCODED, THE OLD ENCODING IS THROWN AWAY
;AND THE NEW ONE IS INSERTED AT THE END OF THE APPROPRIATE LINKED
;ENCODED FORMAT LIST.
%FMTSV:	XMOVEI	T1,@%ADR(L)		;GET ARRAY ADDR
	PUSHJ	P,%OVNUM		;GET LINK NUMBER ALSO
	DMOVEM	T1,USR.AD		;SAVE THEM
	LDB	T2,[POINTR %ADR(L),ARGTYP];GET ARRAY TYPE
	CAIE	T2,TP%CHR		;TYPE CHARACTER?
	 JRST	SVNCHR			;NO

	MOVE	T3,(T1)			;GET ACTUAL BYTE PNTR
	MOVEM	T3,USR.AD		;SAVE IT AS USER ADDRESS
	HLRE	T3,-1(L)		;GET ARG COUNT
	AOJE	T3,NOCSZ		;IF ONLY 1, NO ARRAY SIZE
	SKIPN	T3,@%SIZ(L)		;ANY SIZE SPECIFIED?
NOCSZ:	 SKIPA	T3,1(T1)		;NO. GET IT FROM DESCRIPTOR
	  IMUL	T3,1(T1)		;YES. MULTIPLY BY ENTRY SIZE
	MOVEM	T3,USR.SZ		;SAVE IT
	MOVE	T3,(T1)			;GET THE ACTUAL BP
	MOVEM	T3,FMT.BG		;SAVE IT
	JRST	SVGOT

SVNCHR:	HLRE	T3,-1(L)		;GET ARG COUNT
	AOJE	T3,%POPJ		;IF NONE, CALL IS A NOP
	SKIPG	T3,@%SIZ(L)		;ANY SIZE SPECIFIED?
	 POPJ	P,			;ERROR SOMEDAY, NOP FOR NOW
	IMUL	T3,%SIZTB(T2)		;GET # WORDS
	IMULI	T3,5			;GET # CHARS
	MOVEM	T3,USR.SZ		;SAVE IT
	$BLDBP	T1			;Make 7-bit byte ptr.
	MOVEM	T1,FMT.BG		;SAVE IT

SVGOT:	PUSHJ	P,FMTSRH		;SEARCH FOR THE FORMAT
	JUMPE	T1,GOENC		;IF NOT FOUND, GO ENCODE IT
	PUSHJ	P,FMTDEL		;FOUND. DELETE IT
	PUSHJ	P,FMTSR1		;SEARCH AGAIN
	JUMPN	T1,FMTER3		;BETTER NOT BE THERE AGAIN!
GOENC:	PUSHJ	P,FMTENC		;ENCODE THE FORMAT
	PJRST	BLTFMT			;SAVE IT AWAY

;AND THIS IS THE FAMOUS ENCODED FORMAT DEALLOCATOR, WHICH
;IS HERE MAINLY FOR SYMMETRY (OR FOR THE USER WHO REALLY CARES
;ABOUT THE EXTRA FEW WORDS OF CORE ALLOCATED FOR THE ENCODED
;FORMAT).
%FMTCL:	XMOVEI	T1,@%ADR(L)		;GET ARRAY ADDR
	PUSHJ	P,%OVNUM		;AND OVERLAY NUMBER
	LDB	T3,[POINTR %ADR(L),ARGTYP] ;GET ARGUMENT TYPE
	CAIN	T3,TP%CHR		;CHARACTER?
	 MOVE	T1,(T1)			;YES. GET ENTIRE BYTE POINTER
	DMOVEM	T1,USR.AD		;SAVE FOR SEARCH
	PUSHJ	P,FMTSRH		;SEARCH FOR THE FORMAT
	JUMPE	T1,%POPJ		;NOT FOUND. JUST LEAVE
;	PJRST	FMTDEL			;DELETE IT

;SUBROUTINE TO DELETE AN ENCODED FORMAT FROM A LINKED LIST.
;T1 POINTS TO THE ENCODED FORMAT TO BE DELETED, SAV.EF POINTS
;TO THE PREVIOUS ENCODED FORMAT.
FMTDEL:	MOVE	T2,(T1)			;GET ADDR OF NEXT FORMAT
	MOVEM	T2,@SAV.EF		;RELINK THE LIST
	PJRST	%FREBL			;FREE THE ALLOCATED CORE

;THIS IS THE FORMAT ENCODER SETUP. THE FORMAT IS ENCODED INTO A
;FIXED AREA OF MEMORY (%FAREA); WHEN THE FORMAT HAS BEEN SUCCESSFULLY
;ENCODED, IT IS MOVED TO AN AREA PRECISELY THE RIGHT SIZE.
;IF, DURING ENCODING, THE FIXED AREA DOES NOT HAVE ENOUGH ROOM,
;IT IS COPIED TO AN AREA TWICE AS LARGE AND THE OLD ONE IS
;DEALLOCATED.
FMTENC:	SKIPE	T1,%FAREA		;DO WE HAVE AN ENCODE AREA YET?
	 JRST	GOTFA			;YES
	MOVEI	T1,IFMTSZ		;NO. GET ONE
	MOVEM	T1,FASIZE		;SAVE ITS SIZE
	PUSHJ	P,%GTBLK
	MOVEM	T1,%FAREA		;SAVE IT
GOTFA:	MOVEM	T1,ENC.AD		;SAVE IT
	SETZM	FMT.PC			;CLEAR PREV CHAR
	MOVE	T1,USR.SZ		;GET SIZE OF FORMAT STRING
	MOVEM	T1,FMT.SZ		;SAVE THE SIZE
	MOVN	P4,FASIZE		;GET NEG SIZE OF FORMAT AREA
	HRLI	P4,(P4)			;IN LEFT HALF
	HRR	P4,ENC.AD		;CREATE A FORMAT PNTR
	SUBI	P4,1			;MAKE IT A PUSH PNTR
	PUSH	P4,[0]			;CLEAR "NEXT LINK" ADDR
	PUSH	P4,USR.AD		;SAVE THE ENCODED FORMAT ADDR
	PUSH	P4,USR.OV		;SAVE OVERLAY NUMBER
	PUSH	P4,T.FMT		;SAVE FORMAT TYPE
	PUSH	P4,[0]			;CLEAR INDEF RPT PNTR
	XMOVEI	T1,1(P4)		;GET RELATIVE ADDR OF 1ST ENCODED WORD
	SUB	T1,ENC.AD
	MOVEM	T1,FMT.LK		;SAVE IT
	MOVEM	T1,RPT.PT		;INIT INDEF RPT PNTR
	MOVE	T1,FMT.BG		;GET FORMAT POINTER
	MOVEM	T1,FMT.BP		;Store ptr to beginning of format
	PUSHJ	P,FMTPRC		;PROCESS FORMAT
	MOVE	T1,ENC.AD		;GET BLOCK ADDR
	MOVE	T2,RPT.PT		;GET INDEF RPT PNTR
	MOVEM	T2,%FMTRP(T1)		;SAVE IT IN BLOCK
	POPJ	P,
;FMTINT - INITIALIZE THE FORMAT PARAMETERS. THE DIGIT COLLECTOR
;IS SET UP TO POINT TO THE REPEAT COUNT, THE SIGN IS
;SET TO +, AND THE TEMP FMT DATABASE IS CLEARED.
FMTINT:	SETZM	FMT.DB			;CLEAR 1ST DATABASE WORD
	MOVE	T1,[FMT.DB,,FMT.DB+1]	;CLEAR REST WITH BLT
	BLT	T1,FMT.EN
	MOVEI	T1,1			;SET SIGN TO 1
	MOVEM	T1,FMT.SG
	SETZM	FMTSTA			;SET STATE TO "GETTING REPEAT"
	SETZM	ENC.WD			;CLEAR THE ENCODED WORDS
	SETZM	ENC.W2
	POPJ	P,

;FMTPRC - THIS IS THE FORMAT PROCESSOR OR ENCODER. EACH CHARACTER
;(OTHER THAN IN QUOTED STRINGS AND HOLLERITH CONSTANTS) ARE CONVERTED
;TO SIXBIT. THE RESULTING VALUE IS USED AS AN INDEX INTO A TABLE
;GIVING A SET OF FLAGS (LEFT HALF) AND POSSIBLY AN ADDRESS OF
;A WORD WHICH MAY HAVE THE ADDRESS OF A SPECIAL PROCESSING ROUTINE
;FOR THAT CHARACTER. FOR INSTANCE, THE TABLE ENTRY FOR LEFT PAREN
;HAS FDFLG IN THE LEFT HALF, WHICH INDICATES THAT ANY PREVIOUS DATA
;ENCOUNTERED SHOULD BE ENCODED INTO A FORMAT WORD AND REGISTERED INTO
;THE ENCODED FORMAT. IT HAS THE ADDRESS "RPRFMT" IN THE RIGHT HALF.
;"RPRFMT" IS THE OFFSET OF THE LEFT PAREN ENCODING SUBROUTINE - LPRENC.
;IF "REGFLG" IS ON IN THE LEFT HALF OF THE TABLE ENTRY, IT MEANS THAT
;THE CHARACTER IS A "MAIN" FORMAT CHARACTER, THAT IS, IT WILL EVENTUALLY
;BE ENCODED AS A WORD IN THE ENCODED FORMAT AFTER
;ITS WIDTH, DECIMAL WIDTH, EXPONENT WIDTH, AND REPEAT COUNT ARE COLLECTED.

FMTPRC:	PUSHJ	P,FMTINT		;INIT DATABASE
FMTLP:	SKIPG	FMT.SZ			;ANYTHING LEFT?
	JRST	REGRP			;NO. FAKE A RIGHT PAREN
	PUSHJ	P,GTFCHR		;GET A CHAR
	JUMPE	T1,FMTLP		;SKIP NULLS
	PUSHJ	P,SIXVRT		;CONVERT TO SIXBIT
	MOVEI	P1,(T1)			;COPY IT
	MOVEM	P1,FMT.CC		;SAVE AS CURRENT CHAR
	SKIPN	FMT.CT(P1)		;ANY STUFF?
	 $ECALL	ILF,%ABORT		;ILLEGAL CHAR IN FORMAT
	PUSHJ	P,ETEST			;SPECIAL TEST FOR E-FORMAT
	MOVE	P3,FMT.CT(P1)		;GET FLAGS
	TXNE	P3,FDFLG		;DELIMITER?
	 PUSHJ	P,FMTCMP		;YES. COMPILE PREV FORMAT
	TXNN	P3,REGFLG		;REGISTER THE CHAR?
	 JRST	NOREG			;NO
	SKIPE	FMT.FW			;IS THE FORMAT WIDTH ZERO?
	 $ECALL	ARC			;[2045] NO, AMBIGUOUS REPEAT COUNT
	SKIPE	FMT.CH			;ANY LEFTOVER FORMAT CHAR?
	 PUSHJ	P,FMTCMP		;YES. COMPILE PREV FORMAT
	MOVEM	P1,FMT.CH		;SAVE THE CHAR AWAY
	TXNN	P3,FDFLG		;WAS IT A DELIMITER?
	 PUSHJ	P,CHKWID		;NO. ACCUMULATE REPEAT, CHECK IT
	AOS	FMTSTA			;POINT TO COLLECTING FIELD WIDTH
	SETZM	GTDGFL			;CLEAR "GOT DIGIT" FLAG
NOREG:	HRRZ	P3,FMT.CT(P1)		;GET TABLE ADDR
	JUMPE	P3,FMTLP		;BACK IF NO DISPATCHES
	HRRZ	T1,ENCTAB(P3)		;GET ADDR OF DISPATCH
	JUMPE	T1,FMTLP		;BACK IF NO ADDR
	PUSHJ	P,(T1)			;IF ADDR, DO IT
	 JRST	FMTLP			;BACK FOR MORE
	POPJ	P,			;RECURSIVE RETURN
REGRP:	PUSHJ	P,FMTCMP		;COMPILE ANY INCOMPLETE FORMAT
	PUSHJ	P,RPRENC		;FAKE A RIGHT PAREN
	 JFCL				;ALWAYS SKIP RETURNS
	POPJ	P,

ETEST:	CAIE	P1,'E'			;IS IT AN 'E'?
	 POPJ	P,			;NO. LEAVE
	MOVE	T2,FMT.CH		;GET THE FORMAT CHAR
	CAIE	T2,'D'			;YES. IS IT A "D"
	 CAIN	T2,'E'			;OR AN 'E'?
	  JRST	ASMEXW			;YES. SET FOR EXPONENT WIDTH
	CAIE	T2,'G'			;SAME FOR 'G'
	 POPJ	P,			;NO. LEAVE
ASMEXW:	PUSHJ	P,CHKWID		;CHECK WIDTH
	AOS	T1,FMTSTA		;INCREMENT STATE
	CAIE	T1,EWSTA		;SHOULD NOW BE COLLECTING EXPONENT WIDTH
	 $ECALL	IFW,%ABORT
	SETZM	GTDGFL			;CLEAR "GOT DIGIT" FLAG
EXPLP:	SETZ	P1,			;MAKE THE CHAR A SPACE
	SKIPN	FMT.SZ			;ANY CHARS LEFT?
	 JRST	FMTCMP			;NO. COMPILE THE FORMAT
	PUSHJ	P,GTFCHR		;YES. GET ONE
	PUSHJ	P,SIXVRT		;CONVERT TO SIXBIT
	MOVEI	P1,(T1)			;COPY THE CHAR
	CAIG	P1,'9'			;IS IT A DIGIT?
	 CAIGE	P1,'0'
	  JRST	FMTCMP			;NO. GO USE AS NEXT CHAR
	PUSHJ	P,DIGENC		;YES. ACCUMULATE IT
	JRST	EXPLP			;AND GO BACK FOR MORE
;FMTSRH - SEARCHES FOR AN ENCODED FORMAT IN ONE OF THE
;LINKED LISTS (ADDR IN SAV.EF) MATCHING THE SPECIFIED
;USER'S FORMAT (ADDR IN USR.AD). RETURNS THE ADDR OF THE
;ENCODED FORMAT IN T1 IF FOUND, 0 IF NOT FOUND. ALSO, SAV.EF
;IS LEFT WITH THE ADDR OF THE FORMAT PREVIOUS TO
;ONE FOUND, OR THE ADDR OF THE LAST ENCODED FORMAT
;IN THE LINKED LIST, IF NOT FOUND. [NOTE: THE ORDER OF WHEN THE
;ADDRESS OF THE FORMAT IS SAVED IN SAV.EF (AFTER THE COMPARE
;AND EXIT) IS CRUCIAL FOR THE PROPER OPERATION OF FMTDEL,
;WHICH NEEDS THE ADDRESS PREVIOUS TO THE ONE MATCHED
;FOR RELINKING THE LIST.]

FMTSRH:	MOVM	T1,USR.AD		;GET FORMAT ADDR
	IDIVI	T1,FMTN			;HASH CODE...CHOOSE LIST
	XMOVEI	T1,FMT.LS(T2)		;GET ADDR OF PNTR TO BEG ENTRY
	MOVEM	T1,SAV.EF		;SAVE IT
FMTSR1:	MOVE	T1,SAV.EF		;GET ENC ADDR PNTR
	DMOVE	T2,USR.AD		;AND UNENCODED ADDR
FSLP1:	SKIPN	T1,%FMTNX(T1)		;GET NEXT ADDR, RETURN IF NO MORE
	 POPJ	P,
	CAMN	T2,%FMTAD(T1)		;ADDRESSES EQUAL?
	 JRST	CHKOV			;YES. GO CHECK OVERLAY NUMBER
	MOVEM	T1,SAV.EF		;NO. SAVE NEW ADDR
	JRST	FSLP1			;AND TRY AGAIN

CHKOV:	CAMN	T3,%FMTOV(T1)		;OVERLAY NUMBERS EQUAL?
	 POPJ	P,			;YES. MATCH
	MOVEM	T1,SAV.EF		;NO. SAVE NEW ADDR
	JRST	FSLP1			;AND TRY AGAIN

;FORMAT COMPILATION - THROWS TOGETHER THE VARIOUS PARAMETERS
;ASSEMBLED SO FAR FOR A FORMAT CODE AND ASSEMBLES IT INTO
;AN ENCODED FORMAT WORD. SPECIAL HANDLING IS USED FOR THE
;CURRENT CHARACTER BEING "E" - THE ANSI-77 STANDARD
;ALLOWS THIS CHARACTER TO BE USED FOR BOTH E FORMAT AND
;FOR THE EXPONENT PART OF SCIENTIFIC NOTATION. THEREFORE
;IF THE CURRENT CHARACTER IS "E", WE CHECK IF THE FORMAT ABOUT
;TO BE COMPILED IS D,E,F OR G. IF SO, WE DEFER COMPILATION
;UNTIL COLLECTING THE EXPONENT WIDTH.

XENC:	MOVEI	T1,1			;Incase "X" by itself
	SKIPN	FMT.RP
	 MOVEM	T1,FMT.RP		;"X" = "1X" (DEC extension)
	JRST	FMTNC			;DON'T CHECK WIDTH AGAIN

FMTCMP:	SKIPE	T1,FMT.CH		;ANY FORMAT CHAR YET?
	 JRST	FMTOK			;YES.
	SKIPE	FMT.RP			;NO. IS REPEAT COUNT ZERO?
	 $ECALL	IRC,%ABORT		;NO. ILLEGAL REPEAT COUNT
	POPJ	P,			;YES. JUST IGNORE THE WHOLE THING

FMTOK:	PUSHJ	P,CHKWID		;CHECK ACCUMULATED WIDTH
FMTNC:	SKIPE	T1,FMT.FW		;GET FORMAT WIDTH
	DPB	T1,WIDPNT		;RECORD IT
	SKIPE	T1,FMT.DW		;GET DECIMAL WIDTH
	DPB	T1,DECPNT
	SKIPE	T1,FMT.EW		;AND EXPONENT WIDTH
	DPB	T1,EWPNT
	SKIPE	T1,FMT.RP		;AND REPEAT COUNT
	DPB	T1,RPTPNT
	MOVE	T1,FMT.CH		;AND FORMAT CODE
	DPB	T1,CODPNT
	PUSH	P4,ENC.WD		;STORE WORDS ON STACK
	PUSH	P4,ENC.W2
	PUSHJ	P,STKCHK		;CHECK IF ENOUGH ROOM ON THE STACK
	JRST	FMTINT			;INITIALIZE THE DATABASE

;HERE WE HAVE RUN OUT OF ROOM IN THE CURRENTLY ASSIGNED FORMAT
;ENCODING AREA. SO WE CALL %MVBLK TO ALLOCATE A NEW ONE TWICE
;AS LARGE, MOVE THE DATA OVER, SET THE PUSH PNTR (P4) APPROPRIATELY,
;AND TOSS THE OLD AREA.
STKCHK:	CAMG	P4,[-3,,0]		;LEAVE AT LEAST 3
	 POPJ	P,			;THAT'S ENOUGH FOR NOW
	MOVE	T1,%FAREA		;GET THE OLD ADDR
	MOVE	T2,FASIZE		;GET THE OLD SIZE
	MOVEI	T3,(T2)			;COPY IT
	LSH	T3,1			;DOUBLE IT
	PUSHJ	P,%MVBLK		;GET NEW ONE, BLT, TOSS OLD ONE
	MOVEM	T1,ENC.AD		;SAVE FOR CURRENT ENCODING
	EXCH	T1,%FAREA		;SAVE NEW ADDR, GET OLD ONE
	SUB	T1,%FAREA		;GET OLD-NEW
	SUB	P4,T1			;FIXUP RH OF PUSH PNTR
	EXCH	T3,FASIZE		;SAVE NEW SIZE, GET OLD SIZE
	SUB	T3,FASIZE		;GET OLD-NEW
	HRLZI	T3,(T3)			;IN LEFT HALF
	ADD	P4,T3			;FIXUP LH OF PUSH PNTR
	POPJ	P,

CHKWID:	MOVE	T1,FMT.CH		;GET THE FORMAT CHARACTER
	HRRZ	T1,FMT.CT(T1)		;GET FORMAT INDEX
	MOVE	T1,CHKTAB(T1)		;GET THE FLAG CHECK TABLE
	MOVE	T2,FMTSTA		;GET THE CURRENT STATE
	SKIPL	T3,FMT.SG		;ANY SIGN?
	 JRST	NCKNEG			;NO. DON'T CHECK NEGATIVE LEGAL
	IMULM	T3,FMTACC(T2)		;ACCUMULATE IT
	TDNN	T1,STANEG(T2)		;CHECK IF NEGATIVE OK
	 JRST	STAERR(T2)
NCKNEG:	MOVM	T3,FMTACC(T2)		;GET THE ACCUMULATED WIDTH
	CAMLE	T3,STAMAX(T2)		;WITHIN RANGE?
	 JRST	STAERR(T2)		;NO
	JUMPN	T3,%POPJ		;NON-ZERO NEEDS NO MORE CHECKING
	SKIPN	GTDGFL			;ZERO. DID WE GET ANY DIGITS?
	 JRST	CKBLNK			;NO. GO CHECK IF BLANK IS OK
	TDNN	T1,STAZER(T2)		;CHECK IF ZERO OK
	 JRST	STAERR(T2)		;N.G.
	POPJ	P,

CKBLNK:	TDNN	T1,STABLK(T2)		;IS BLANK OK?
	 JRST	STAERR(T2)		;NO
	POPJ	P,
;SIXVRT - CONVERTS ASCII CHARACTERS FROM THE FORMAT STATEMENT
;TO SIXBIT.
SIXVRT:	CAILE	T1,140			;LOWER CASE?
	SUBI	T1,40			;YES. CONVERT TO UPPER
	SUBI	T1,40			;CONVERT TO SIXBIT
	JUMPG	T1,%POPJ		;OK
	SETZ	T1,			;NEGATIVE - TREAT AS SPACE
	POPJ	P,

;RIGHT PAREN ENCODER - A RIGHT PAREN IS TRANSLATED INTO A ZERO
;WORD DROPPED ONTO THE STACK. SINCE THE FORMAT ENCODER IS RECURSIVE,
;THE RETURN FROM THIS SUBROUTINE IS ALWAYS A SKIP RETURN TO SPECIFY
;A RECURSIVE RETURN
RPRENC:	PUSH	P4,[0]			;DROP A ZERO ON THE STACK
	PJRST	%POPJ1			;AND SKIP RETURN

;THIS ROUTINE IS CALLED BY I,O, AND Z FORMATS. THE ANSI STANDARD
;SPECIFIES THAT A FORMAT SUCH AS "I5.0" MEANS THAT NO DIGITS ARE
;PRINTED IF THE VALUE OF THE VARIABLE IS ZERO. THE DEFAULT FOR
;A FORMAT SUCH AS "I5" IS AT LEAST ONE DIGIT PRINTED. THUS THIS
;ROUTINE MUST BE CALLED TO SET THE DEFAULT NUMBER OF CHARACTERS
;PRINTED TO 1; WHEN A PERIOD IS ENCOUNTERED IT IS RESET TO ZERO
;TO PROPERLY COLLECT THE DIGITS.
MENC:	MOVEI	T1,1			;SET DEFAULT FOR I,O,Z TO 1
	MOVEM	T1,FMT.DW		;FOR DECIMAL WIDTH
	POPJ	P,

;PERIOD ENCODING - UPON ENCOUNTERING A PERIOD, THE DECIMAL WIDTH
;(POSSIBLY SET TO NON-ZERO DEFAULT) IS CLEARED, AND THE DIGIT
;COLLECTER IS SET TO POINT TO THE DECIMAL WIDTH
PERENC:	PUSHJ	P,CHKWID		;CHECK ACCUMULATED WIDTH
	AOS	T1,FMTSTA		;INCREMENT STATE
	CAIE	T1,DWSTA		;SHOULD BE NOW COLLECTING DECIMAL WIDTH
	 $ECALL	IFW,%ABORT		;NOT. BAD FORMAT
	SETZM	GTDGFL			;CLEAR "GOT DIGIT" FLAG
	SETZM	FMT.DW			;CLEAR PREVIOUS (DEFAULT) DEC WID
	POPJ	P,

;DIGENC - THE DIGIT ENCODER. NUM.AD POINTS TO THE CURRENT
;DIGIT COLLECTOR.
DIGENC:	MOVE	T2,FMTSTA		;GET CURRENT STATE
	MOVE	T1,FMTACC(T2)		;GET ACC NUM
	CAML	T1,[1000000000.]	;PREVENT OVERFLOW
	 JRST	STAERR(T1)		;GIVE ILLEGAL WIDTH MESSAGE
	IMULI	T1,12			;MUL BY 10
	ADDI	T1,-20(P1)		;ADD IT IN
	MOVEM	T1,FMTACC(T2)		;SAVE IT AGAIN
	SETOM	GTDGFL			;SET "GOT DIGIT" FLAG
	POPJ	P,

;LPRENC - THE LEFT PAREN ENCODER. A LEFT PAREN IS
;ENCODED AS A WORD CONTAINING THE NEGATIVE OF ITS REPEAT COUNT
;IN THE LEFT HALF, AND THE ADDRESS OF A "LINK" IN THE RIGHT HALF.
;A "LINK" IS THE ADDRESS OF THE 1ST WORD ENCODED AFTER THE RIGHT
;PAREN CORRESPONDING TO THE CURRENT LEFT PAREN. THE LEFT PAREN
;INITIATES A RECURSIVE CALL TO THE FORMAT ENCODER, AND THE LINK
;IS ESTABLISHED AFTER THE RETURN FROM THIS RECURSIVE CALL.
;UPON RETURNING, WE MUST DO SOMETHING WHICH LOOKS RATHER CURIOUS - WE SAVE
;THE CURRENT LINK (THAT IS, LEFT PAREN) POINTER IN THE INDEFINITE REPEAT
;POINTER, UNLESS WE ARE AT THE TOP LEVEL. THIS HAS THE EFFECT OF POINTING
;TO THE LEFT PAREN WHICH MATCHES THE NEXT TO LAST RIGHT PAREN (WE AVOID
;RESETTING IT TO THE OUTERMOST PAREN (THE INITIAL SETTING) BY NOT SAVING
;THE NO-LINK ENTRY.
;IF, AFTER RETURNING, THE SAVED FORMAT STACK ADDRESS IS THE
;BEGINNING OF THE FORMAT STACK, WE HAVE REACHED THE END OF
;THE FORMAT, SO THE LINK IS SET TO ZERO. IN THIS CASE,
;WE ALSO SKIP RETURN, TO INDICATE A RECURSIVE
;RETURN, AND THUS THE END OF ENCODING.


LPRENC:	SKIPE	T1,FMT.CH		;ANY PREVIOUS FORMAT CHARACTER?
	 PUSHJ	P,FMTCMP		;YES. PROCESS IT
	XMOVEI	T1,1(P4)		;GET REL ADDR OF PAREN WORD
	SUB	T1,ENC.AD
	PUSH	P,T1			;SAVE IT
	SKIPN	T1,FMT.RP		;GET LATEST REPEAT COUNT
	 MOVEI	T1,1			;ASSUME COUNT OF 1 IF 0
	MOVN	T1,T1			;NEGATE IT
	PUSH	P4,T1			;SAVE ON STACK
	PUSHJ	P,STKCHK		;CHECK IF ENOUGH ROOM ON THE STACK
	PUSHJ	P,FMTPRC		;RECURSIVE CALL
	POP	P,T2			;GET FORMAT LINK ADDR
	CAMN	T2,FMT.LK		;IF LINK ADDR IS START ADDR
	JRST	%POPJ1			;WE'RE DONE
	MOVEM	T2,RPT.PT		;SAVE LOOP ADDR FOR INDEF RPT
	POPJ	P,
;BENC - THE B FORMAT ENCODER - BN FORMAT IS ENCODED AS A 'B' FOR
;THE FORMAT CHARACTER AND "N" FOR THE WIDTH. BZ IS ENCODED WITH
;A "Z" FOR THE FORMAT WIDTH.
BENC:	PUSHJ	P,GTFCHR		;GET FORMAT CHAR
	CAIE	T1,"N"			;N OR Z?
	CAIN	T1,"Z"
	  JRST	GOTNZ			;YES, OK
	$ECALL	ILF,%ABORT		;ILLEGAL CHAR IN FORMAT

GOTNZ:	MOVEM	T1,FMT.FW		;SAVE AS WIDTH FIELD
	PJRST	FMTNC			;COMPILE IT

;A MINUS IN THE FORMAT MERELY NEGATES THE SIGN.
MINENC:	MOVNS	T1,FMT.SG			;NEGATE THE SIGN
	JUMPL	T1,%POPJ		;OK IF NOW NEGATIVE
	 $ECALL	ILF,%ABORT		;ILLEGAL IF NOW POSITIVE

;SENC - FOR S,SS, &SP FORMATS. AN "S" OR "P" ARE ENCODED
;INTO THE WIDTH POSITION OF THE FORMAT WORD.
SENC:	PUSHJ	P,GTFCHR		;GET A FORMAT CHAR
	CAIE	T1,"S"			;IS IT AN S
	CAIN	T1,"P"			;IS IT A P
	JRST	MATPS			;YES
	MOVEM	T1,FMT.PC		;NO. SAVE AS PREVIOUS CHAR
	MOVEI	T1,"S"			;DEFAULT IS AN S
MATPS:	MOVEM	T1,FMT.FW		;SAVE AS WIDTH
	PJRST	FMTNC			;COMPILE IT
;HOLLERITH ENCODING - WE ARE ALLOWING ANY CHARACTERS WHATSOEVER
;IN HOLLERITH AND QUOTED STRINGS, SO WE USE THE ACTUAL BYTE
;PNTR INTO THE FORMAT STATEMENT TO RETRIEVE THE CHARS RATHER
;THAN THE SUBROUTINE GTFCHR. THE FORMAT WORD IS COMPILED AND THE
;BYTE POINTER IS SAVED AS A SECOND WORD IN THE FORMAT STACK.
HENC:	SKIPG	T1,FMT.RP		;GET THE REPEAT COUNT
;	IOERR	(IHC,,,?,Illegal Hollerith constant,,%ABORT)
	 $ECALL	IHC,%ABORT
	CAMLE	T1,FMT.SZ		;BEYOND THE FORMAT SIZE?
	 MOVE	T1,FMT.SZ		;YES. FOR NOW, TRUNCATE IT
	MOVNI	T2,(T1)			;GET NEGATIVE
	ADDM	T2,FMT.SZ		;DECREMENT THE LEFTOVER SIZE
	ADJBP	T1,FMT.BP		;CREATE NEW BYTE POINTER
	EXCH	T1,FMT.BP		;POINTING AFTER THE CONSTANT
	MOVEM	T1,ENC.W2		;SAVE THE OLD ONE
	PJRST	FMTNC			;COMPILE THE FORMAT

;SIMILAR TO HOLLERITH. IF A SINGLE QUOTE IS NOT
;FOLLOWED BY ANOTHER, IT IS THE END OF THE QUOTED STRING;
;HOWEVER, WE HAVE ALREADY RETRIEVED THE CHARACTER, SO WE HAVE TO
;SAVE IT FOR LATER.
SQENC:	SETZM	FMT.RP			;CLEAR REPEAT COUNT
	MOVE	T1,FMT.BP		;GET THE BYTE PNTR
	MOVEM	T1,ENC.W2		;SAVE FOR ENCODING
SQLP1:	SOSGE	FMT.SZ			;ANY CHARS LEFT?
	 JRST	SQEDON			;NO. CLOSE THE QUOTE
	ILDB	T1,FMT.BP		;NON-CHECKING SCAN
	CAIE	T1,"'"			;SINGLE QUOTE?
	JRST	NOTSQ			;NOPE
	SOSGE	FMT.SZ			;ANY CHARS LEFT?
	 JRST	SQEDON			;NO. CLOSE THE QUOTE
	ILDB	T1,FMT.BP		;GET THE NEXT CHAR
	CAIE	T1,"'"			;ANOTHER QUOTE?
	JRST	SQEDON			;NO. STOP
	AOS	FMT.RP			;YES. COUNT BOTH OF THEM
NOTSQ:	AOS	FMT.RP			;NO. INCR THE COUNT
	JRST	SQLP1			;AND TRY FOR MORE

SQEDON:	MOVEM	T1,FMT.PC		;NO. SAVE AS PREVIOUS CHAR
	PJRST	FMTNC			;COMPILE THE FORMAT

;T FORMAT - THE ANSI STANDARD HAS CREATED 2 MORE CONFUSING FORMATS -
;TR AND TL (TAB RIGHT AND TAB LEFT). SINCE WE ONLY HAVE A SIXBIT
;CHARACTER POSITION FOR THE FORMAT CHAR, WE STORE THE "R" OR "L"
;IN THE DECIMAL WIDTH PORTION OF THE FORMAT WORD.
TENC:	PUSHJ	P,GTFCHR		;GET NEXT CHAR
	CAIE	T1,"L"			;L OR R
	CAIN	T1,"R"
	JRST	GOTLR			;YES
	MOVEM	T1,FMT.PC		;NO. SAVE THE CHAR FOR LATER
	POPJ	P,

GOTLR:	PUSHJ	P,SIXVRT		;CONVERT TO SIXBIT
	MOVEM	T1,FMT.DW		;SAVE AS DECIMAL WIDTH
	POPJ	P,
;THIS IS THE LIST OF EXECUTION/ENCODING ADDRESSES.
;THERE ARE 6 TABLES OF ADDRESSES OR TRANSFER VECTORS.
;THE FIRST ENTRY IN EACH ENTRY OF FMTTAB IS THE OFFSET INTO
;EACH TABLE FOR THAT FORMAT. THE SECOND IS THE INPUT EXECUTION
;ADDRESS, THE THIRD IS THE OUTPUT EXECUTION ADDRESS, AND THE FOURTH
;IS THE ENCODING ADDRESS. THE FIFTH IS A MASK
;WHICH IS USED TO CHECK THE FORMAT AGAINST THE VARIABLE
;TYPE FOR A MISMATCH. THE SIXTH IS A RANGE CHECK FLAG WORD.

;THE WARNING MASK IS ASSEMBLED AS FOLLOWS: BIT 0 REPRESENTS VARIABLE
;TYPE 0 (UNKNOWN), BIT 1 REPRESENTS TYPE 1 (LOGICAL), ETC. THE BIT
;IS SET IF THE VARIABLE TYPE CONFLICTS WITH THE FORMAT IN QUESTION.
;FOR EXAMPLE, F-FORMAT CONFLICTS WITH
;INTEGER (2), DOUBLE INTEGER (11), AND CHARACTER (15).

FWARN==1B<TP%INT>+1B<TP%DPI>+1B<TP%CHR>+1B<TP%LIT>
IWARN==1B<TP%SPR>+1B<TP%DPR>+1B<TP%DPX>+1B<TP%CPX>+1B<TP%CHR>+1B<TP%LIT>
QWARN==IWARN


;THE RANGE CHECKING FLAG WORD CONTAINS ONE OF 12 BITS, 3 EACH
;OF 4 STATES (REPEAT COUNT, FORMAT WIDTH, DECIMAL WIDTH
;EXPONENT WIDTH) TO CHECK FOR NEGATIVE OK, ZERO OK, AND
;NO DIGITS SPECIFIED OK.

REFLAG==RPBOK+FWBOK+DWZOK+DWBOK
AFLAG==RPBOK+FWBOK
PFLAG==RPZOK+RPNOK
XFLAG==RPBOK

	DEFINE	FMTTAB
<
FMTENT	(JNKFMT,NOEXEC,NOEXEC,0)		;WASTED LOCATIONS FOR 0 OFFSET
FMTENT	(COLFMT,%POPJ,COLEXE,FMTNC)
FMTENT	(SLHFMT,%IRECS,%ORECS,FMTNC)
FMTENT	(DOLFMT,NOCR,NOCR,FMTNC)
FMTENT	(MINFMT,NOEXEC,NOEXEC,MINENC)
FMTENT	(DIGFMT,NOEXEC,NOEXEC,DIGENC)
FMTENT	(LPRFMT,NOEXEC,NOEXEC,LPRENC)
FMTENT	(RPRFMT,NOEXEC,NOEXEC,RPRENC)
FMTENT	(PERFMT,NOEXEC,NOEXEC,PERENC)
FMTENT	(SQFMT,SQIN,SQOUT,SQENC)
FMTENT	(AFMT,%ALPHI,%ALPHO,0,0,AFLAG)
FMTENT	(BFMT,BNZ,BNZ,BENC)
FMTENT	(DFMT,%DIRT,%DOUBT,0,FWARN,REFLAG)
FMTENT	(EFMT,%ERIN,%EOUT,0,FWARN,REFLAG)
FMTENT	(FFMT,%FLIRT,%FLOUT,0,FWARN,REFLAG)
FMTENT	(GFMT,GIN,GOUT,0,0,REFLAG)
FMTENT	(HFMT,HIN,HOUT,HENC)
FMTENT	(IFMT,%INTI,%INTO,MENC,IWARN,REFLAG)
FMTENT	(LFMT,%LINT,%LOUT,0,0,REFLAG)
FMTENT	(OFMT,%OCTI,%OCTO,0,0,REFLAG)
FMTENT	(PFMT,PFACT,PFACT,FMTNC,0,PFLAG)
FMTENT	(QFMT,QIN,QOUT,FMTNC,QWARN)
FMTENT	(RFMT,%RIGHI,%RIGHO,0,0,REFLAG)
FMTENT	(SFMT,SSP,SSP,SENC)
FMTENT	(TFMT,TIN,TOUT,TENC)
FMTENT	(XFMT,XIN,XOUT,XENC,0,XFLAG)
FMTENT	(ZFMT,%HEXI,%HEXO,0,0,REFLAG)

GENT	(GNONE,%GINTI,%GINTO,0)	;NO TYPE GIVEN
GENT	(GLOGIC,%GLINT,%GLOUT,0)	;LOGICAL
GENT	(GINTEG,%GINTI,%GINTO,0)	;INTEGER
GENT	(G3,NOEXEC,NOEXEC,0)		;UNDEFINED
GENT	(GREAL,%GRIN,%GROUT,0)		;REAL
GENT	(G5,NOEXEC,NOEXEC,0)		;UNDEFINED
GENT	(GOCTAL,%GOCTI,%GOCTO,0)	;OCTAL
GENT	(GLABEL,NOEXEC,NOEXEC,0)	;LABEL
GENT	(GDREAL,%GRIN,%GROUT,0)		;DOUBLE REAL
GENT	(GDINT,%GINTI,%GINTO,0)		;DOUBLE INTEGER
GENT	(GDOCT,%OCTI,%OCTO,0)		;DOUBLE OCTAL
GENT	(GDGFL,%GRIN,%GROUT,0)		;EXTENDED DOUBLE REAL
GENT	(GCPX,%GRIN,%GROUT,0)		;COMPLEX
GENT	(GALPHA,%ALPHI,%ALPHO,0)	;FORTRAN CHARACTER
GENT	(G16,NOEXEC,NOEXEC,0)		;BASIC STRING
GENT	(G17,NOEXEC,NOEXEC,0)		;ASCIZ
>
	FMTN==0
	DEFINE	FMTENT(A,B,C,D,E,F)
	<A==FMTN
	FMTN==FMTN+1>
	DEFINE	GENT(A,B,C,D,E,F)
	<A==FMTN
	FMTN==FMTN+1>

	FMTTAB

	DEFINE	FMTENT(A,B,C,D,E,F)
	<JRST	B>
	DEFINE	GENT(A,B,C,D,E,F)
	<JRST	B>
INTAB:	FMTTAB

	DEFINE	FMTENT(A,B,C,D,E,F)
	<JRST	C>
	DEFINE	GENT(A,B,C,D,E,F)
	<JRST	C>
OUTAB:	FMTTAB

	DEFINE	FMTENT(A,B,C,D,E,F)
	<D>
	DEFINE	GENT(A,B,C,D,E,F)
	<>
ENCTAB:	FMTTAB

	DEFINE	FMTENT(A,B,C,D,E,F)
<	IFNB <E>,<E>
	IFB <E>,<0>
>
	DEFINE	GENT(A,B,C,D,E,F)
	<>
WRNTAB:	FMTTAB

	DEFINE	FMTENT(A,B,C,D,E,F)
<	IFNB <F>,<F>
	IFB <F>,<0>
>
	DEFINE	GENT(A,B,C,D,E,F)
	<>
CHKTAB:	FMTTAB


;G-FORMAT CONVERSION ROUTINE ADDRESS TABLE.
;LH=INPUT, RH=OUTPUT
GTAB:	SKPFLG+GNONE		;NO TYPE GIVEN
	SKPFLG+GLOGIC		;LOGICAL
	SKPFLG+GINTEG		;INTEGER
	G3			;UNDEFINED
	SKPFLG+GREAL		;REAL
	G5			;UNDEFINED
	SKPFLG+GOCTAL		;OCTAL
	GLABEL			;LABEL
	SKPFLG+GDREAL		;DOUBLE REAL
	SKPFLG+GDINT		;DOUBLE INTEGER
	SKPFLG+GDOCT		;DOUBLE OCTAL
	SKPFLG+GDGFL		;EXTENDED DOUBLE REAL
	SKPFLG+GCPX		;COMPLEX
	GALPHA			;FORTRAN CHARACTER
	G16			;BASIC STRING
	G17			;ASCIZ

NOEXEC:	$ECALL	UDT,%ABORT	;UNDEFINED DATA TYPE


;THIS IS THE CALL TO FORMAT EXECUTION, EXEPRC.

FMTEXC:	SETZM	ENC.RP			;CLEAR LOOP REPEAT COUNT
	SETZM	ENC.LR			;AND LOCAL REPEAT COUNT
	SETZM	FMT.LK			;AND LINK WORD
	SETZM	SCL.SV			;CLEAR SCALE FACTOR
	SETZM	FMT.IU			;CLEAR I/O LIST USED FLAG
	SETZM	IO.ADR			;WE HAVE NO DATA YET!
	MOVE	T2,FLAGS(D)		;T2= DDB flags
	TXZ	T2,D%SP+D%BZ		;Default is BN and SS
	LOAD	T1,BLNK(U)		;UNLESS SET OTHERWISE IN DDB
	CAIN	T1,BL.ZERO		;BLANK=ZERO?
	  TXO	T2,D%BZ			;YES, SET BZ
	MOVEM	T2,FLAGS(D)		;Set new DDB flags
	MOVE	T1,ENC.AD		;GET ENCODED FORMAT ADDR
	ADDI	T1,%FMTEN		;POINT TO 1ST FMT WORD
	MOVEM	T1,ENC.PT		;SAVE IN STACK PNTR
	PUSHJ	P,EXEPRC		;EXECUTE THE FORMAT

EXELP:	SKIPN	IO.ADR			;ANY I/O ADDR READY?
	 PUSHJ	P,%GETIO		;NO. GET ANOTHER I/O ADDR
	SKIPN	FMT.IU			;DATA USED BY LAST SCAN?
	 $ECALL	DLF,%ABORT		;Data in IO list but not in format
	MOVEI	T1,SLHFMT		;EXECUTE EOL FOR EACH REPEAT
	MOVEM	T1,IO.ENT
	PUSHJ	P,DATENT		;READ OR WRITE A RECORD
	MOVE	T1,ENC.AD		;GET ENCODED BLOCK ADDR
	MOVE	T1,%FMTRP(T1)		;GET INDEF RPT PNTR
	MOVEI	T2,(T1)			;COPY IT
	ADD	T2,ENC.AD		;MAKE RELATIVE ABSOLUTE
	MOVEM	T2,ENC.PT		;FOR FORMAT STACK
	CAIE	T1,%FMTEN		;IF WE'RE NOT AT BEG OF FORMAT
	 MOVEM	T2,FMT.LK		;STORE A LINK
	SETZM	ENC.RP			;CLEAR LOOP REPEAT COUNT
	SETZM	ENC.LR			;AND LOCAL REPEAT COUNT
	SETZM	FMT.IU			;CLEAR I/O LIST ENTRY USED FLAG
	PUSHJ	P,EXEPRC		;GO EXECUTE FORMAT
	JRST	EXELP			;GO BACK FOR MORE

;EXEPRC - THE ENCODED FORMAT EXECUTIONER (SIMILAR TO
;LORD HIGH EXECUTIONER). THIS IS A TOTALLY RECURSIVE EXECUTION SEQUENCE.
;STARTING AT THE GIVEN FORMAT STACK POINTER, ENCODED WORDS ARE
;LOADED, AND DEPENDING ON THE FORMAT CODE, A DATA ITEM MAY BE RETRIEVED,
;AND THEN THE PROPER SUBROUTINE IS CALLED. THE FORMAT ENCODER HAS
;ENCODED LEFT PARENS AS WORDS WITH A NEGATIVE LEFT HALF (NEGATIVE REPEAT COUNT),
;AND A LINK TO THE FORMAT CODES AFTER THE LOOP IN THE RIGHT HALF. THE FIRST
;ENCODED LEFT PAREN HAS A ZERO LINK, AS THERE ARE NO FORMAT ITEMS AFTER
;ITS MATCHING RIGHT PAREN. HERE, WHEN A NEGATIVE ENTRY (AN ENCODED LEFT PAREN)
;IS ENCOUNTERED, THE EXECUTION SEQUENCE IS CALLED RECURSIVELY, SAVING
;THE CURRENT VALUE OF THE LOOP REPEAT COUNT AND CURRENT LEFT PAREN
;ADDRESS (REFFERED TO AS THE "LINK" ADDRESS) ON THE PUSHDOWN STACK.
;UPON RETURN, THE FORMAT STACK POINTER IS SET TO THE ADDRESS SPECIFIED BY
;THE ENCODED LEFT PAREN (IN FMT.LK).

EXEPRC:	SKIPL	T1,@ENC.PT		;PAREN?
	JRST	EXENRM			;NO. NORMAL EXECUTION
	PUSH	P,ENC.RP		;YES. SAVE OLD REPEAT COUNT
	PUSH	P,FMT.LK		;AND LINK ADDR
	MOVMM	T1,ENC.RP		;SAVE NEW REPEAT COUNT
	MOVE	T1,ENC.PT		;GET THE ADDR OF THE PAREN WORD
	MOVEM	T1,FMT.LK		;SAVE FOR LINK
	AOS	ENC.PT			;POINT TO NEXT ITEM
	PUSHJ	P,EXEPRC		;RECURSIVE CALL
	POP	P,FMT.LK		;RESTORE PREVIOUS LINK
	POP	P,ENC.RP		;AND REPEAT COUNT
	SKIPN	FMT.LK			;DO WE HAVE ANY MORE?
	 POPJ	P,			;NO. WE'RE DONE
	AOS	ENC.PT			;YES. PROCEED TO NEXT ITEM
	JRST	EXEPRC			;AND START AGAIN
EXENRM:	DMOVE	T1,@ENC.PT		;GET FORMAT WORDS
	JUMPE	T1,EXENLP		;HIT END OF LIST
	DMOVEM	T1,ENC.WD		;SAVE FOR CODE RETRIEVAL
	LDB	T1,CODPNT		;GET FORMAT CHAR
	MOVE	T1,FMT.CT(T1)		;GET TABLE ENTRY
	MOVEM	T1,IO.ENT		;Save flags and table entry for later
	TXNN	T1,IOLFLG		;DO WE NEED I/O LIST ENTRY?
	 JRST	NODATA			;NO. GO DO INPUT OR OUTPUT
	SETOM	FMT.IU			;YES, SET I/O LIST ENTRY USED FLAG
	LDB	T1,RPTPNT		;GET NEW REPEAT COUNT
	MOVEM	T1,ENC.LR		;SAVE IT
DATLP:	SKIPN	IO.ADR			;DO WE HAVE ADDR ALREADY?
	 PUSHJ	P,%GETIO		;NO. GET ONE. (MAY NOT EVER RETURN...)
	HRRZ	T1,IO.ENT		;GET FORMAT INDEX
	SKIPN	T2,WRNTAB(T1)		;ANY FATAL OR WARNING TYPE MISMATCH?
	 JRST	FVOK			;NO. ANYTHING GOES
	MOVE	T3,IO.TYP		;GET DATA TYPE
	LSH	T2,(T3)			;MOVE THE WARNING BITS
	JUMPGE	T2,FVOK			;NG IF BIT 0 = 1
	CAIN	T3,TP%CHR		;BUT TYPE CHAR CONFLICTS ARE FATAL
	 $ECALL	FVF,%ABORT		;FORMAT/VARIABLE MISMATCH FATAL
	MOVE	T3,%SIZTB(T3)		;GET DATA SIZE
	XCT	GETINS(T3)		;GET THE DATA
	DMOVEM	T1,%UNFXD		;STORE AS UNFIXED RESULT
	$ECALL	FVM			;ISSUE WARNING

FVOK:
DATLP1:	PUSHJ	P,DATENT		;GO DO INPUT OR OUTPUT
	SKIPN	T1,IO.INC		;ADD OFFSET TO I/O ADDR
	 JRST	NOINC			;NO INCREMENT
	XCT	IO.INS			;DO THE INCREMENT INSTRUCTION
	MOVEM	T1,IO.ADR		;SAVE NEW PNTR
NOINC:	SOSG	ENC.LR			;DECR LOCAL REPEAT COUNT
	 JRST	NEWFMT			;GO ON TO NEXT FMT, BUT DECR IO.NUM
	SOSLE	IO.NUM			;DECR LOCAL DATA COUNT
	 JRST	DATLP1			;STILL SOME DATA
	SETZM	IO.ADR			;CLEAR I/O ADDR IF NONE LEFT
	JRST	DATLP			;AND GET MORE

NEWFMT:	SOSG	IO.NUM			;DECR LOCAL DATA COUNT
	 SETZM	IO.ADR			;CLEAR I/O ADDR IF NONE LEFT
	MOVEI	T1,2			;ON TO NEXT FORMAT PAIR
	ADDM	T1,ENC.PT
	JRST	EXEPRC			;AND BACK FOR MORE FORMAT

NODATA:	PUSHJ	P,DATENT		;DO INPUT OR OUTPUT
	MOVEI	T1,2			;ON TO NEXT FORMAT
	ADDM	T1,ENC.PT
	JRST	EXEPRC

DATOUT:	HRRZ	T1,IO.ENT		;GET I/O ROUTINE OFFSET
	JRST	OUTAB(T1)		;DO IT

DATIN:	HRRZ	T1,IO.ENT		;GET I/O ROUTINE OFFSET
	PUSHJ	P,INTAB(T1)		;DO IT
	LDB	T1,WIDPNT		;FREE FORMAT?
	JUMPN	T1,%POPJ		;NO.
	MOVE	T1,IO.ENT		;Get flags and table entry
	TXNE	T1,SKPFLG		;SCAN FOR NEXT DELIMITER
	 PUSHJ	P,SFDEL			;YES. SCAN FOR NEXT DELIM
	POPJ	P,

EXENLP:	SOSG	ENC.RP			;DECR REPEAT COUNT
	POPJ	P,			;END OF LIST OR COUNT EXHAUSTED
	MOVE	T1,FMT.LK		;GET ADDR BEFORE START
	ADDI	T1,1			;RESET PNTR
	MOVEM	T1,ENC.PT		;TO LOOP
	JRST	EXEPRC			;START AGAIN

;G-FORMAT I/O. USES THE DATA TYPE TO FIGURE OUT WHAT TO DO
GIN:	MOVE	T1,IO.TYP		;GET VARIABLE TYPE
	MOVE	T1,GTAB(T1)		;GET I/O CONV ADDRESS
	MOVEM	T1,GENTRY		;SETUP FOR I/O
	MOVEI	T1,(T1)			;GET JUST INDEX INTO TABLE
	PUSHJ	P,INTAB(T1)		;DO IT
	LDB	T1,WIDPNT		;FREE FORMAT?
	JUMPN	T1,%POPJ		;NO.
	MOVE	T1,GENTRY		;Get flags and table entry
	TXNE	T1,SKPFLG		;SCAN FOR NEXT DELIMITER
	 PUSHJ	P,SFDEL			;YES. SCAN FOR NEXT DELIM
	POPJ	P,

GOUT:	MOVE	T1,IO.TYP		;GET VARIABLE TYPE
	MOVE	T1,GTAB(T1)		;GET I/O CONV ADDRESS
	MOVEI	T1,(T1)			;GET JUST INDEX INTO TABLE
	JRST	OUTAB(T1)		;DO IT

GETINS:	NOP			;0 - DON'T LOAD ANYTHING
	MOVE	T1,@IO.ADR	;1 - SINGLE
	DMOVE	T1,@IO.ADR	;2 - DOUBLE

;SCAN FOR A DELIMITER - FOR "FREE-FORMAT" INPUT,
;WE HAVE TO SCAN UNTIL WE REACH A "DELIMITER" FOR THIS TYPE
;OF INPUT, TO AVOID GETTING FAKE NULL VALUES FOR SUCH THINGS
;AS <DATA><SPACES><COMMA><SPACES><DATA>. THE DELIMITERS FOR
;FREE-FORMAT ARE ANY NON-BLANK CHARACTERS AND END-OF-LINE.
;IF THE NON-BLANK CHARACTER IS NOT A COMMA, THE BYTE POINTER
;IS BACKED UP VIA A CALL TO %IBACK SO THAT THE NEXT CALL
;TO %IBYTE WILL GET IT.
SFDEL:	PUSHJ	P,%IBYTC		;GET CURRENT CHAR
	JRST	DELGOT			;SKIP NEW CHAR
SFDLP1:	PUSHJ	P,%IBYTE		;GET NEXT CHAR
DELGOT:	SKIPGE	IRCNT(D)		;END OF RECORD?
	 POPJ	P,			;YES
	CAIE	T1," "			;SKIP SPACES AND TABS
	CAIN	T1,"	"
	 JRST	SFDLP1
	CAIE	T1,","			;LEAVE ON COMMA
	 JRST	%IBACK			;ELSE PUSH THE POINTER BACK
	POPJ	P,

;DOLLAR SIGN EXECUTION (SOUNDS LIKE SOCIALISM OR SOMETHING)...
;CALL SPECIAL ROUTINE ON OUTPUT WHICH OUTPUTS
;THE RECORD WITHOUT A CARRIAGE RETURN FOR OUTPUT.
;RIGHT NOW, JUST SET THE FLAG FOR THIS, AND SET
;THE RECORD POSITION IF A VIRTUAL ONE EXISTS (THAT IS,
;IF AN X OR T FORMAT WAS DONE IMMEDIATELY BEFORE THE $),
;SO THAT THE SPACES AT THE END WILL ACTUALLY APPEAR.
NOCR:	MOVX	T0,D%STCR		;Suppress trailing CR
	IORM	T0,FLAGS(D)
	POPJ	P,

;COLEXE - COLON FORMAT EXECUTION
;CALL %GETIO TO GET AN I/O LIST ENTRY (IF THERE ISN'T ONE ALREADY)
;AND LEAVE. NEVER RETURNS IF THERE IS NO MORE.
COLEXE:	SKIPN	IO.ADR			;ANY I/O ADDRESS?
	 PUSHJ	P,%GETIO		;NO. GET ONE (MAY NEVER RETURN)
	POPJ	P,

;BNZ - BN AND BZ FORMAT
;IF THE LETTER AFTER THE B WAS A "Z", TURN ON THE
;BZ FORMAT FLAG, WHICH FORCES BLANKS TO BE INTERPRETED
;AS ZEROES FOR FIXED FORMAT FIELDS. OTHERWISE TURN OFF
;THE BZ FORMAT FLAG
BNZ:	LDB	T1,WIDPNT		;GET ARG
	MOVX	T0,D%BZ			;Assume not Z
	ANDCAM	T0,FLAGS(D)
	CAIN	T1,"Z"
	 IORM	T0,FLAGS(D)		;Was "Z", set flag
	POPJ	P,
;H-FORMAT - THE BYTE POINTER TO THE HOLLERITH STRING HAS
;BEEN STORED IN THE NEXT ENTRY IN THE FORMAT STACK. THE
;NUMBER OF CHARACTERS IS IN THE REPEAT FIELD.
HOUT:	LDB	T2,RPTPNT		;GET # CHARS
	MOVE	T3,ENC.W2		;Get byte ptr
HOUTLP:	ILDB	T1,T3			;GET CHAR
	PUSHJ	P,%OBYTE		;OUTPUT IT
	SOJG	T2,HOUTLP		;BACK FOR MORE
	POPJ	P,

;Q FORMAT - FOR INPUT, RETURNS THE NUMBER OF CHARS LEFT IN THE RECORD
;INTO THE SPECIFIED VARIABLE (INTEGER!). FOR OUTPUT, RETURNS THE NUMBER
;OF CHARACTERS WRITTEN IN THE RECORD.
QIN:	SKIPGE	T1,IRCNT(D)		;GET # CHARS LEFT
	  SETZ	T1,			;PAST END OF RECORD, RETURN 0
	MOVEM	T1,@IO.ADR		;PUT INTO USER'S VARIABLE
	POPJ	P,

QOUT:	MOVE	T1,ORSIZ(D)		;GET OUTPUT BUFFER LENGTH
	SUB	T1,ORCNT(D)		;GET CURRENT POSITION (0=COL 1)
	CAMG	T1,ORLEN(D)		;.GT. LAST RECORDED LENGTH
	 MOVE	T1,ORLEN(D)		;NO. RETURN THE LENGTH
	POPJ	P,
;P FORMAT - SETS THE SCALE FACTOR. THE SCALE FACTOR
;HAS BEEN STORED AS IF IT WERE A REPEAT COUNT, SO WE
;HAVE TO DO SOME CONTORTIONS IN ORDER TO EXTEND THE SIGN
PFACT:	HRRE	T1,ENC.WD		;EXTEND REPEAT COUNT
	MOVEM	T1,SCL.SV		;SAVE IT
	POPJ	P,

;SSP - HANDLES S,SP, AND SS FORMATS.
;THE DEC IMPLEMENTATION OF THIS FORMAT MEANS THAT S FORMAT
;MEANS SS, SINCE WE DO NOT OUTPUT A PLUS SIGN
;UNDER NORMAL CIRCUMSTANCES. SO THERE IS A SINGLE FLAG, WHICH JUST
;SAYS WHETHER OR NOT TO FORCE A PLUS SIGN (AND THEN ONLY IF IT WILL
;FIT!), CORRESPONDING TO SP AND SS.
SSP:	LDB	T1,WIDPNT		;GET THE CODE
	MOVX	T0,D%SP			;Assume not "P"
	ANDCAM	T0,FLAGS(D)
	CAIN	T1,"P"			;IS IT P?
 	 IORM	T0,FLAGS(D)		;Yes, set flag
	POPJ	P,

;T FORMAT - POSITIONS RECORD POINTER.
;ARGUMENT IS IN WIDTH FIELD
TIN:	LDB	T2,DECPNT		;GET L OR R
	JUMPN	T2,ILRPOS		;GO PROCESS THEM IF THERE
	LDB	T1,WIDPNT		;GET VALUE
	PJRST	%SIPOS			;POSITION PNTR/COUNT

ILRPOS:	LDB	T1,WIDPNT		;GET FORMAT ARG
	CAIN	T2,'L'			;IS IT TAB LEFT?
	MOVNI	T1,(T1)			;YES. NEGATE TAB
	PJRST	%CIPOS			;GO CHANGE POSITION

TOUT:	LDB	T2,DECPNT		;GET L OR R
	JUMPN	T2,OLRPOS		;GO PROCESS THEM IF THERE
	LDB	T1,WIDPNT		;GET VALUE
	PJRST	%SOPOS			;GO SET POSITION

OLRPOS:	LDB	T1,WIDPNT		;GET FORMAT ARG
	CAIN	T2,'L'			;IS IT TAB LEFT?
	MOVNI	T1,(T1)			;YES. NEGATE TAB
	PJRST	%COPOS			;GO CHANGE POSITION

;X FORMAT - SIMILAR TO T FORMAT, BUT WITH REPEAT COUNT INSTEAD
;OF WIDTH FOR ITS VALUE
XIN:	LDB	T1,RPTPNT		;GET ARG OF X-FORMAT
	PJRST	%CIPOS			;GO CHANGE POSITION

XOUT:	LDB	T1,RPTPNT		;GET ARG OF X-FORMAT
	PJRST	%COPOS			;GO CHANGE POSITION

;SINGLE QUOTE OUTPUT - LIKE HOLLERITH, THE BYTE POINTER IS STORED
;AS THE NEXT (1 OR 2) WORD ON THE FORMAT STACK, AND THE CHARACTER COUNT IS
;STORED AS THE WIDTH. UNLIKE HOLLERITH, DOUBLE APOSTROPHES MUST BE
;TURNED INTO SINGLE APOSTROPHES.
SQOUT:	LDB	T2,RPTPNT		;GET THE # CHARS
	JUMPE	T2,SQODON		;DONE IF NO CHARS
	MOVE	T3,ENC.W2		;Get the byte ptr.
SQOLP:	ILDB	T1,T3			;GET CHAR
	CAIE	T1,"'"			;IS IT A QUOTE?
	JRST	PUTNSQ			;NO. GO OUTPUT IT
	SOJLE	T2,SQODON		;DECR AND LEAVE IF NO MORE
	IBP	T3			;INCR PAST THE NEXT CHAR
PUTNSQ:	PUSHJ	P,%OBYTE		;AND PRINT CHAR
	SOJG	T2,SQOLP		;BACK FOR MORE
SQODON:	POPJ	P,

;SINGLE QUOTE INPUT - ALLOWS THE USER TO OVERWRITE A QUOTED STRING
;IN THE FORMAT STATEMENT. DISALLOWED BY THE ANSI STANDARD, BUT WE
;DO IT ANYWAY.

;HOLLERITH INPUT - A STUPID WAY TO WRITE PROGRAMS, BUT ELLIOT
;ORGANICK USES IT AS AN EXAMPLE IN HIS INTRO TO FORTRAN! THE NUMBER
;OF CHARACTERS SPECIFIED IN THE FIELD ARE READ FROM THE INPUT RECORD
;AND WRITTEN INTO THE ORIGINAL FORMAT STATEMENT.
;BOTH OF THESE ARE LIKELY TO BE MADE ILLEGAL SOMEDAY, SO
;WE OUTPUT A WARNING. IT IS STRICTLY ILLEGAL FOR CHARACTER
;CONSTANTS SPECIFIED AS FORMATS, AND THEREFORE PRODUCES A
;FATAL ERROR.
SQIN:
HIN:	MOVE	T1,ENC.AD		;GET ENCODED ADDR
	MOVE	T1,%FMTYP(T1)		;GET THE FORMAT TYPE
	CAIN	T1,TP%CHR		;TYPE CHARACTER?
	 $ECALL	RIC,%ABORT		;YES. CAN'T READ INTO IT
	$ECALL	RIF			;NO. JUST ISSUE WARNING
	LDB	T2,RPTPNT		;GET # CHARS TO INPUT
	MOVE	T3,ENC.W2		;Get the byte ptr
HINLP:	PUSHJ	P,%IBYTE		;GET A CHAR
	IDPB	T1,T3			;DEPOSIT INTO FORMAT
	SOJG	T2,HINLP		;BACK FOR MORE
	POPJ	P,

;GTFCHR - GET THE NEXT CHARACTER IN THE FORMAT.
;EXCEPT FOR H-FORMAT AND QUOTED STRINGS, WE DO NOT WANT
;NULL CHARACTERS FROM THE FORMAT STATEMENT. ADDITIONALLY,
;PROVISION HAS BEEN MADE IF WE READ TOO FAR WE SAVE THE
;CHARACTER READ AS THE "PREVIOUS CHAR" AND RETRIEVE IT THE
;NEXT TIME GTFCHR IS CALLED.

GTFCHR:	SKIPE	T1,FMT.PC		;Is there a previous char?
	JRST	PREVCH			;Yes, go clear it
NOPREV:	SOSGE	FMT.SZ			;ANY CHARS LEFT?
	POPJ	P,			;NO. RETURN WITH NULL
	ILDB	T1,FMT.BP		;GET A CHAR
	JUMPE	T1,NOPREV		;SKIP NULLS
	CAILE	T1,140			;LOWER CASE?
	SUBI	T1,40			;YES. CONVERT TO UPPER
	POPJ	P,

PREVCH:	SETZM	FMT.PC			;IF ONE, NOW CLEAR IT
	POPJ	P,			;SO WE WON'T GET IT AGAIN


FMTER3:	$SNH

%EXCH:
%GETIO:	EXCH	P,OTHERP	;COROUTINE JUMP
	POPJ	P,

%IFORM:	SKIPN	T1,IO.ADR	;DO WE HAVE ANY DATA?
	 JRST	%SETAV		;NO. IT'S A FIN CALL
	MOVE	T1,IO.TYP	;GET DATA TYPE
	CAIN	T1,TP%CPX	;COMPLEX?
	 JRST	CPXSUB		;YES
	EXCH	P,OTHERP	;NO. JUST DROP BACK INTO FMTEXC
	POPJ	P,

%OFORM:	SKIPN	T1,IO.ADR	;DO WE HAVE ANY DATA?
	 JRST	FINOUT		;NO. FINISH OUTPUT
	SETZM	%FTSLB		;ALLOW LEADING BLANKS ON OUTPUT
	MOVE	T1,IO.TYP	;GET DATA TYPE
	CAIN	T1,TP%CPX	;COMPLEX?
	 JRST	CPXSUB		;YES
	EXCH	P,OTHERP	;NO. JUST DROP BACK INTO FMTEXC
	POPJ	P,

FINOUT:	PUSHJ	P,%OREC		;OUTPUT THE RECORD
	PJRST	%SETAV		;GO STORE ASSOC VAR

CPXSUB:	MOVEI	T1,TP%SPR	;SUBSTITUTE REAL
	MOVEM	T1,IO.TYP	;FOR THE DATA TYPE
	MOVE	T1,[ADD	T1,IO.ADR] ;SETUP INCR INST
	MOVEM	T1,IO.INS
	MOVE	T1,IO.SIZ	;GET DATA SIZE
	CAME	T1,IO.INC	;SAME AS INCREMENT?
	 JRST	CMPCPX		;NO. COMPLICATED COMPLEX
	IMULM	T1,IO.NUM	;YES. JUST MULTIPLY NUMBER ELEMENTS
	MOVEI	T1,1		;AND SUBSTITUTE 1 FOR SIZE
	MOVEM	T1,IO.SIZ
	MOVEM	T1,IO.INC	;AND FOR INCREMENT
	EXCH	P,OTHERP	;THEN DROP BACK INTO FMTEXC
	POPJ	P,

CMPCPX:	MOVE	T1,IO.NUM	;GET # ELEMENTS
	CAIN	T1,1		;ONLY ONE ELEMENT?
	 JRST	CPXONE		;YES.
	MOVEM	T1,FIONUM	;NO. SAVE LOCALLY
	MOVEI	T1,1		;SUBSTITUTE 1 FOR SIZE
	MOVEM	T1,IO.SIZ
	EXCH	T1,IO.INC	;AND FOR INCREMENT
	MOVEM	T1,FIOINC	;AND SAVE REAL INCREMENT LOCALLY
	MOVE	T1,IO.ADR	;SAVE DATA ADDRESS LOCALLY
	MOVEM	T1,FIOADR	;AS THE DATA LOOP CLEARS IT
CPXLP:	MOVEI	T1,2		;SUBSTITUTE 2 FOR
	MOVEM	T1,IO.NUM	;NUMBER OF ELEMENTS
	PUSHJ	P,%EXCH		;DROP BACK TO FMTEXC
	MOVE	T1,FIOINC	;INCR THE DATA PNTR
	ADDB	T1,FIOADR
	MOVEM	T1,IO.ADR	;SETUP THE DATA ADDRESS AGAIN
	SOSLE	FIONUM		;DECR THE COMPLEX ENTRY COUNT
	 JRST	CPXLP		;BACK FOR MORE
	POPJ	P,		;DONE

CPXONE:	MOVEI	T1,1		;SET INCREMENT TO 1
	MOVEM	T1,IO.INC
	MOVEM	T1,IO.SIZ	;AS WELL AS SIZE
	MOVEI	T1,2		;AND SETUP FOR 2 ELEMENTS
	MOVEM	T1,IO.NUM
	PJRST	%EXCH		;GO DO I/O

IOSIZ==200
IOPDP:	IOWD	IOSIZ,IOPDL	;PRIVATE PDP

	SEGMENT	DATA

OTHERP:	BLOCK	1		;PLACE TO SAVE USER STACK
IOPDL:	BLOCK	IOSIZ		;PRIVATE STACK FOR FORMAT EXECUTION ONLY

IO.ADR:: BLOCK	1
IO.TYP:: BLOCK	1
IO.NUM:: BLOCK	1		;NUMBER OF ELEMENTS
IO.INC:: BLOCK	1
IO.INF:: BLOCK	1
IO.SIZ:: BLOCK	1		;SIZE OF VARIABLE
IO.INS:: BLOCK	1		;INCREMENT INSTRUCTION

FIONUM:	BLOCK	1		;LOCAL DATA COUNT
FIOINC:	BLOCK	1		;LOCAL INCREMENT
FIOADR:	BLOCK	1		;LOCAL ADDRESS
IO.ENT:	BLOCK	1		;Save flags and table address in EXENRM
DATENT:	BLOCK	1		;INST TO EXECUTE AFTER %GETIO CALL
GENTRY:	BLOCK	1		;FLAGS AND INDEX FOR G-FORMAT

ILLEG.:: 0

	SEGMENT	CODE

	PURGE	$SEG$
	END