Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_SRC_3_19910112 - midas/tsrtns.mid
There are 2 other files named tsrtns.mid in the archive. Click here to see a list.
;-*-MIDAS-*-

SUBTTL TS Definitions, parameters

	; For convenience in defining isolated variables/tables,
	; especially when have to know on pass1 where the
	; table is going to be (.VECTOR etc don't know until end of pass)
DEFINE LVAR -LINE
VBLK
LINE
PBLK
TERMIN

DEFINE TMPLOC AT,STUFF
%%%TLC==. ? LOC AT
	STUFF
LOC %%%TLC
TERMIN

	; Nice macro for minimizing coding.  Doesn't hack indirection tho.
	; Could conceivably optimize to do MOVE AC,[FROM,,TO] but that
	; would be overly hairy for something you can do just by writing
	; 2 instructions.
DEFINE BLTMAC AC,LEN,FROM,TO
	MOVSI AC,FROM
	HRRI AC,TO
	BLT AC,TO+LEN-1
TERMIN

	; Also handy for standard zaps (and nice mnemonic)
	; won't work for indirection either.
DEFINE BLTZAC AC,LEN,FROM
	SETZM FROM
IFG LEN-1,[
	MOVEI AC,FROM+1
	HRLI AC,-1(AC)
	BLT AC,FROM+LEN-1
]
TERMIN

	; More convenient when A is clobberable...
DEFINE BLTM LEN,FROM,TO
BLTMAC A,LEN,FROM,TO
TERMIN

DEFINE BLTZ LEN,FROM
BLTZAC A,LEN,FROM
TERMIN

	; Following inserts a SYSCAL for JSYS's.  Be warned that it
	; clobbers T when used!!
IFN TNXSW,.INSRT XJSYS
IFNDEF PMAPSW,PMAPSW==TNXSW	; 1 to assemble PMAP input, 0 for SIN input.
IFNDEF ERRSW,ERRSW==1	; 1 for error file output capability.

IFNDEF TYPDLC,TYPDLC==7	; Maximum total depth of .insrt (including tty)
IFNDEF MX.INS,MX.INS==5	; Maximum depth .insrt files only
IFNDEF MAXIND,MAXIND==6	; Maximum # @: table entries for .insrt

		; Define sizes of various I/O buffers
IFN DECSW,[
IFNDEF DECBFL,DECBFL==203	; Standard DEC buffer size for DSK (200 wds data)
IFN SAILSW,IFNDEF NINBFS,NINBFS==19.	; For SAIL, hack full disk track of input.
IFNDEF NINBFS,NINBFS==2		; # standard-size buffers to use for input.
IFNDEF UTIBFL,UTIBFL==<DECBFL+1>*NINBFS	; Input buffs need 1 wd for EOB hacking
IFNDEF UTOBFL,UTOBFL==DECBFL	; All output chans have just 1 buffer.
IFNDEF CRFBSZ,CRFBSZ==DECBFL
IFNDEF LSTBSZ,LSTBSZ==DECBFL
IFNDEF ERRBSZ,ERRBSZ==DECBFL
] ;DECSW
IFNDEF CMBFL,CMBFL==50		; Length of command buffer.
IFNDEF UTIBFL,UTIBFL==400	;   "  Input buffer.
IFNDEF UTOBFL,UTOBFL==200	;   "  BIN output buffer.
IFNDEF CRFBSZ,CRFBSZ==200	;   "  CREF output buffer.
IFNDEF LSTBSZ,LSTBSZ==200	;   "  LIST output buffer.
IFNDEF ERRBSZ,ERRBSZ==1		;   "  ERR output buffer.  Very small to avoid
				; losing much data if things crash.


ERRC==0		; Err device input channel
TYIC==1		; TTY input channel
TYOC==2		; TTY output channel
CREFC==3	; CREF output
UTYOC==4	; BIN output
LPTC==5		; LIST output (LPT)
ERRFC==6	; ERR Assembly error output file.
UTYIC==7	; 1st input channel, UTYIC+n used for nth .INSRT level in dec version.
SUBTTL File Description Storage (FILBLK's)

VBLK
	; Definitions for indices into a FILBLK.

	; Scratch block FB is formed while defining indices...
FB:	OFFSET -.
		; Lots of crocks depend on the exact order of these 4 items.
$F6DEV:: 0			; SIXBIT Device name
$F6FNM:: $F6FN1:: 0		; SIXBIT Filename (on ITS, FN1)
$F6TYP:: $F6FN2:: $F6EXT:: 0	; SIXBIT Extension (on ITS, FN2)
$F6DIR:: 0			; SIXBIT Directory (may be numerical PPN)
	L$F6BLK==.
$FVERS:: $FGEN:: 0	; File version (or generation).  NUMBER, not string.
IFN TNXSW,[		; Almost all entries here are BP's to ASCIZ strings.
$FDEV::  0		; Device name
$FDIR::  0		; Directory name
$FNAME:: 0		; File name (i.e. main name)
$FTYPE:: $FEXT:: 0	; File type (or extension)
$FTEMP:: 0		; -1 => File is a temporary file.
$FACCT:: 0		; Account string
$FPROT:: 0		; Protection string
$FJFN::  0		; JFN for file (may be <desired JFN>,,<temp JFN>)
]
IFN ITSSW\DECSW,[
$FDEV==:$F6DEV		; These definitions made so some common code can do
$FDIR==:$F6DIR		; the right things.
$FNAME==:$F6FNM
$FTYPE==:$F6TYP
$FEXT==:$F6TYP
]
	L$FBLK==.	; Length of a FILBLK.
	OFFSET 0	; End of index definitions.


	; FILBLK's for various files

ISFB:	BLOCK L$FBLK	; Input file specification as given in command line.
INFB:	BLOCK L$FBLK	; Actual current input file.
OUTFB:	BLOCK L$FBLK	; Output file

IFN CREFSW,	CRFFB:	BLOCK L$FBLK	; CREF output file
IFN LISTSW,	LSTFB:	BLOCK L$FBLK	; Listing output file
IFN ERRSW,	ERRFB:	BLOCK L$FBLK	; Error output file

INFCNT:	0		; AOS'd each time an input file is opened.
INFCUR:	0		; What INFCNT was when current file opened.
INFERR:	0		; What INFCUR held at last err msg.

INDDP:	MAXIND,,TBLOFS		; Pointer into tables below
TBLOFS:	BLOCK MAXIND*L$FBLK	; Actual filenames corresponding to those in TBLSFS, for opening.
TBLSFS:	BLOCK MAXIND*L$FBLK	; Source-specified filenames for @: files


RFNAM1:	0		; .FNAM1, .FNAM2, .FVERS
RFNAM2:	0
RFVERS:	0
IFNM1:	0		; .IFNM1, .IFNM2, .IFVRS
IFNM2:	0
IFVRS:	0
INFFN1==:INFB+$F6FN1	; Some crocks seem to reference this.
OFNM1==:OUTFB+$F6FN1	; Pseudo .OFNM1 needs this.
OFNM2==:OUTFB+$F6FN2	; ditto, .OFNM2
RSYSNM:	0		; Initial system name

IFN TNXSW,[
USRNUM:	0			;User# of person running program
UNAMLN:	0			;# of words in his username.
USRNAM:	BLOCK 40./5		;Max username is 39. characters
FNAMLN:	0
FILNAM:	BLOCK 200./5		;Max filename is around 170. characters.
];TNXSW
PBLK
SUBTTL I/O Buffers

VBLK		; Input buffer and variables

UTIBUF:	BLOCK UTIBFL
UTIHDR:	0		; Input buffer header (dec version)
UREDP:	440700,,UTIBUF	; Input byte pointer
UTICNT:	0		; Input byte count (dec version)
IUREDP:	440700,,UTIBUF	; Initial UREDP, used for re-initializing.
UTIBED:	UTIBUF		; EOF comparison with RH(UREDP), 4.9 => EOF on .IOT

IFN DECSW,UTICHN:	UTYIC

		; BIN Output buffer

UTOBUF:	BLOCK UTOBFL	; Output buffer
UTOHDR:	UTOBFL,,UTOBUF-1
UTYOP:	444400,,	; Output (36. bit) byte pointer
UTYOCT:	0		; # words left in utobuf
IFN ITSSW,OCLOSP: @1(C)	; Turned into bp to unused part of last bffer wd used.

		; CREF output buffer
IFN CREFSW,[
CRFBUF:	BLOCK CRFBSZ
CRFHDR:	CRFBSZ,,CRFBUF-1	; Header, assembled value used only ifn itssw
CRFPTR:	444400,,	; Bp for filling buffer (full words)
CRFCNT:	0		; Num. wds. empty in buffer
]

		; LISTing output buffer
IFN LISTSW,[
LSTBUF:	BLOCK LSTBSZ
LSTHDR:	5*LSTBSZ,,LSTBUF-1
LSTPTR:	440700,,
LSTCNT:	0
]

		; ERRor output buffer
IFN ERRSW,[
ERRBUF:	BLOCK ERRBSZ
ERRHDR:	5*ERRBSZ,,ERRBUF-1
ERRPTR:	440700,,
ERRFCT:	0	; Can't call this ERRCNT since that's used for # errors.
ERRFP:	0	; Non-0 if want error output file.
ERRFOP:	0	; Non-0 if error file open (ie try outputting to it)
]
PBLK
SUBTTL Interrupt Handling

;	Note that only PDL OV is now enabled in general.
; TTY input interrupts are also handled when possible for
; ^H, ^W, and ^V.

.SCALAR INTSVP		; Saves P on interrupt for debugging

IFN ITSSW,[
	TMPLOC 42, JSR TSINT	; Interrupt vector for ITS
VBLK
.JBCNI:
TSINT:	0		; 1st wd interrupts currently considered fatal errors.
.JBTPC:	0		; Error processor re-enables interrupts
	.SUSET [.RJPC,,INTJPC]
	SKIPGE TSINT
	 JRST TTYINT	; Second-word ints.
	JRST TSINT1	; Jump into pure coding and process interrupt
INTJPC:	0		; Saves .JPC at interrupt.
PBLK
	; Jrst here from TSINT for 2nd wd interrupts.
TTYINT:	PUSH P,A
	MOVEI A,TYIC	; The tty chnl is the only one enabled.
	.ITYIC A,
	 JRST TTYINX	; No int. char.
	CAIN A,^W
	 AOS TTYFLG	; ^W silences,
	CAIN A,^V
	 SOS TTYFLG	; ^V unsilences,
	CAIN A,^H
	 SETOM TTYBRF	; ^H says break next time thru ASSEM1 loop.
TTYINX:	REST A
	.DISMIS .JBTPC
] ; IFN ITSSW

IFN DECSW, TMPLOC .JBAPR, TSINT1	; Interrupt vector for DEC

IFN ITSSW\DECSW,[
	; Amazing but can use almost same basic rtn for both!

TSINT1:	MOVEM P,INTSVP		; Save P for possible debugging
IFN ITSSW,.SUSET [.SPICL,,[-1]]	; For ITS, re-enable ints.
	MOVE A,.JBCNI		; Get interrupt request word
	TRNE A,200000		; PDL overflow?
	 JRST CONFLP
	MOVE B,[TYPE "Unknown interrupt - Fatal"]	; anything else.
	MOVEM B,40
	MOVE A,.JBTPC	; So error routine will print out properly
	JSA A,ERROR
]
IFN TNXSW,[
	; TENEX Interrupt handler
; Note that NXP (non-ex page) is enabled, but no provision is
; currently made for handling it.  This causes process termination and
; EXEC will print error message.  If NXP wasn't enabled, a page would
; simply be created without fuss (page is always created, incidentally,
; whether or not interrupt happens)

LVAR MEMDBG:	0	; For nonce, this gets set when PURIFG does.

LEVTAB:	INTPC1		; Where to store PC for level 1 interrupt.
	0 ? 0		; Levels 2 and 3 unused.
CHNTAB:	BLOCK 36.	; Where to go for indicated condition.  Most zero.

.IC.CV==1	; Define user channel 1 for ^V interrupt
.IC.CW==2	;   "  2 for ^W
.IC.CH==3	;   "  3 for ^H
%%LSV==.
LOC CHNTAB+.ICPOV ? 1,,TSINT1	; Put right word in CHNTAB for PDL OV dispatch.
LOC CHNTAB+.IC.CV ? 1,,INT.CV	; Ditto for ^V dispatch
LOC CHNTAB+.IC.CW ? 1,,INT.CW	; "  ^W
LOC CHNTAB+.IC.CH ? 1,,INT.CH	; "  ^H
	; The next 3 are to handle all reasonable interrupts resulting from
	; a failing JSYS.
LOC CHNTAB+.ICILI ? 1,,INT.IL	; Illegal instruction (normally a failing JSYS)
LOC CHNTAB+.ICEOF ? 1,,INT.IL	; EOF encountered
LOC CHNTAB+.ICDAE ? 1,,INT.IL	; Data error encountered
LOC %%LSV

.SCALAR	INTPC1		; Level 1 interrupt PC stored here.

	;  Handle PDL OV interrupt
TSINT1:	MOVEM P,INTSVP		; Save PDL ptr.
	MOVEI A,CONFLP		; OK to clobber A in PDLOV.
	MOVEM A,INTPC1		; Dismiss to CONFLP.
	DEBRK			; Off we go.

	; Handle ^V interrupt
INT.CV:	SOS TTYFLG	; Unsilence typeout
	DEBRK

	; Handle ^W
INT.CW:	AOS TTYFLG	; Silence typeout
	DEBRK

	; Handle ^H
INT.CH:	SETOM TTYBRF	; Set flag to check at main level ASSEM1 loop.
	DEBRK

	; Handle Illegal Instruction (normally a failing JSYS, bletch!)
	; 10X ERJMP-handling interrupt routine.
ERJMPA==:<JUMPA 16,>	; For use instead of ERJMP where JSYS normally skips.
IFNDEF ERJMP,ERJMP==:<JUMP 16,>
IFNDEF ERCAL,ERCAL==:<JUMP 17,>

ERXJMP==:<ERJMP_-27>	; For easier code writing
ERXCAL==:<ERCAL_-27>
ERXJPA==:<ERJMPA_-27>

INT.IL:	PUSH P,A
	PUSH P,B
	MOVE A,INTPC1		; Get PC we got interrupted from
	LDB B,[271500,,(A)]	; Get op-code and AC field of instr
	CAIN B,ERXJPA
	 JRST ERJFAK
	CAIE B,ERXJMP		; Is it a magic cookie?
	 CAIN B,ERXCAL
	  JRST ERJFAK
	AOJ A,
	LDB B,[271500,,(A)]	; Try next instr
	CAIE B,ERXJMP		; Any better luck?
	 CAIN B,ERXCAL
	  JRST ERJFAK
	ETF [ASCIZ "Fatal interrupt encountered"]

ERJFAK:	CAIN B,ERXCAL		; See which action to hack
	 JRST ERJFK2		; Go handle ERCAL, messy.
	MOVEI A,@(A)		; ERJMP, get the jump address desired
	MOVEM A,INTPC1		; Make it the new PC
	POP P,B
	POP P,A
	DEBRK
ERJFK2:	MOVEI B,@(A)		; Get jump address
	MOVEM B,INTPC1		; Make it the new PC
	POP P,B
	AOJ A,			; old PC needs to be bumped for return
	EXCH A,(P)		; Restore old A, and save PC+1 on stack
	DEBRK

; (Actually, since ERCAL is not special except after a JSYS, it would
; still work if the ERCAL-simulation didn't bump the PC; control would
; just drop through to the next instruction on return.  Might confuse
; people looking through the stack frames, though.)
] ;IFN TNXSW
SUBTTL MIDAS BEGINS HERE - Program Startup

VBLK
NVRRUN:	-1	; 0 => MIDAS was run; error to start or purify.
FATAL:	0	; At end of assembly, not 0 iff fatal error occurred.
PBLK

BEG:			; Start address!
IFN DECSW\TNXSW,[
	TDZA A,A
	 SETO A,
	MOVEM A,CCLFLG		; Remember type of start-up
]
	SETZ FF,		; Initialize flags
	MOVE P,[-LPDL,,PDL-1]	; Initialize P

IFN DECSW,[
	RESET
	MOVEI A,600000
	APRENB A,
]
	; For TENEX, must determine right away which system we're on.
IFN TNXSW,[
	RESET
;	TLZ FF,FL20X	; Assume 10X until proven otherwise. (done by SETZ above)

IFN 0,[	; One way of determining OS which doesn't work on some places.
	MOVE A,[112,,11] ; Magic word that will win on 10X,T20 (and maybe T10)
GETTAB=<047000,,41>	; CALLI 41
	GETTAB A,	; Returns 10000 T10, 20000 ITS, 30000 10X, 40000 T20
	 MOVEI A,30000	; Shouldn't ever fail, but if it does, assume 10X.
	LDB A,[140300,,A] ; Flush other fields too
	CAIN A,4	; = Tops-20?
	 TLO FF,FL20X	; Yes, set flag.
]; IFN 0
IFN 0,[	; This is a loser too, since there ARE KL Tenices!
	SETZ A,		; In lieu of above, use hardware hack...
	BLT A,		; test for KL-ness.
	CAIE A,
	 TLO FF,FL20X	; KL will fail to skip, assume that means 20X OS.
];IFN 0
IFN 1,[	; Boy I hope DEC never defines LOADTB! -- MRC
	SYSCAL SYSGT,[['LOADTB]][A ? D]
	SKIPN D		; If LOADTB is not defined
	 TLO FF,FL20X	; it must be a Twenex
]; IFN 1

	SYSCAL SCVEC,[[.FHSLF] ? [-1]]	; and flush compat package,
			; disabling UUO's 40-77; this is good for debugging.

	; Set up stuff for interrupts
	SYSCAL SIR,[[.FHSLF]
			[LEVTAB,,CHNTAB]]	; Specify tables
	SYSCAL EIR,[[.FHSLF]]			; Enable interrupts
	SYSCAL AIC,[[.FHSLF]		; Activate PDL OV and ^V, ^W, ^H
[IRP BIT,,[.ICPOV,.IC.CV,.IC.CW,.IC.CH]
<1_<35.-BIT>>+!TERMIN ]]
	SYSCAL ATI,[[.TICCV,,.IC.CV]]	; Make various mappings from
	SYSCAL ATI,[[.TICCW,,.IC.CW]]	; terminal bits to int. channels.
	SYSCAL ATI,[[.TICCH,,.IC.CH]]	; What a losing interrupt sys 10X has!
	SKIPN MEMDBG			; Hacking memory ref debugging?
	 JRST BEG20
	; Make sure that all low impure pages exist
	; whether or not they consist of all zeros.  Problem is that EXEC SAVE
	; command ignores pages that are all zero, so they won't exist on
	; startup and we have to re-create them or risk getting a NXP int.
	MOVSI A,-2*MINBNK
	MOVE B,(A)		; Reference them all to create them if nec.
	ADDI A,777
	AOBJN A,.-2
	MOVE B,<MXICLR*2000>-1	; Ditto last TNX page of initial MACTAB
	; Now enable interrupts for Non-eXistent Pages.
	SYSCAL AIC,[[.FHSLF] ? [1_<35.-.ICNXP>]]

BEG20:	SYSCAL GJINF,[][USRNUM]			;Get your user# into USRNUM
	SYSCAL DIRST,[[-1,,USRNAM] ? USRNUM]	;And then make username string
	  JFCL
	MOVEI A,1		;# words in username string.
	MOVE B,USRNAM-1(A)
	TRNE B,376		;check last position in this word
	  AOJA A,.-2		;  filled, so check next word.
	MOVEM A,UNAMLN		;Save # words.

] ;TNXSW

IFN ITSSW,[
	MOVE A,[-5,,[		; Set and read various vars in a chunk.
		.SMASK,,[%PIPDL]	; 1st-wd Interrupt only on PDL ovfl.
		.SMSK2,,[1_TYIC]	; 2nd-wd on TTY input channel.
		.SPICL,,[-1]		; and enable interrupt system.
		.RSNAM,,RSYSNM		; Get system name (default dir to use)
		.RXJNAM,,B ]]		; and XJNAME for temp. hacking below.
	.SUSET A
	SYSCAL TTYSET,[1000,,TYIC	; Set TTYST wds - PI echo, no act/int
		[232020,,202020]	; except ctls activate & interrupt
		[232020,,220220]]	; CR, DEL activate but don't int;
					; DEL doesn't echo.
]

	AOSE NVRRUN	; Test for this job's already being run...
	 JRST [	TYPE "Can't restart MIDAS"
		JRST TSRETN]

	; Initialize impure memory for paged systems	
IFN ITSSW\TNXSW,[
	MOVE AA,[MXICLR-MXIMAC,,MXICLR]
	CALL CORGET		; Get MACTAB pages not loaded into.
IFN PURESW,[
	MOVE AA,[MINBNK-MINMAC,,MINBNK]
	CALL CORGET		; Get pages for blank code & symtab.
	SKIPN PURIFG
	 JRST .+3		; If purified, skip cleanup
	JSP F,FLSPGS		; If not purified => flush pages of
	 <MXIMAC-MINPUR>,,MXIMAC ; MACTAB created by loading but not needed.
] ;PURESW
] ;IFN ITSSW\TNXSW

	MOVEI D,SYMDSZ	; Get default symtab size
IFN ITSSW,[			; Remember that B set to XJNAME above.
	CAME B,['MMIDAS]	; Set symtab size larger for MMIDAS
	 CAMN B,[SIXBIT/MM/]	; (random sort of hack now that .SYMTAB exists)
	  MOVEI D,SYMMSZ
]
	SKIPGE ISYMF		; The first time through,
	 MOVEM D,SYMLEN		; Make that the size to use.
	CALL SITINI		; Initialize stuff for .SITE.
	CALL JCLINI		; Now try to fetch JCL; set CMPTR accordingly.
IFN ITSSW,[
	SKIPGE ISYMF		; Skip if syms spread; if not,
	 CALL TSYMGT		; get TS syms from system.
]
	SKIPE CMPTR		; If have JCL,
	 JRST GO2AA		; skip announcing midas's name and version.

IFG PURESW-DECSW,[	; If meaningful,
	SKIPGE PURIFG		; Check for purity
	 TYPE "NOTPUR "		; and type little warning if unpurified.
	]
	TYPE "MIDAS."		; and announce self.
	MOVE B,[MIDVRS]
	PUSHJ P,SIXTYO
	JRST GO2AA
SUBTTL MIDAS Top-level control path

GO2A:	SETZM CMPTR	; Recycles here, so JCL only hacked once.
GO2AA:	SETOM FATAL	; Assume fatal errors, unless cleared at GO8 when done.
	SETZM TTYFLG	; Allow TTY typeout.
	SETZM ERRCNT	; Initialize error counter (total errors)
IFN RUNTSW,[	PUSHJ P,RNTTMA	; Get initial run time.
		MOVEM A,IRUNTM]
	SETZM LSTTTY	; Tell TYOERR not to try output to LST file (none yet!)
	PUSHJ P,CMD	; Get typed in command (or scan cmd line if CMPTR ne 0)

	SKIPGE SMSRTF	; What's this for, I wonder?
	 JRST GO21
	TYPECR "SYMTAB clobbered"
	JRST GO2A

	; Filenames and switches all specified, now see if files can be set up.
GO21:	PUSHJ P,OPNRD	; Open input file
	 JRST GO2A	; Error, msg was typed, go try again with new cmd line.
	PUSHJ P,WINIT	; Open output file, cref file.
IFN DECSW\TNXSW,[
	SKIPGE CCLFLG
	 TYPE "MIDAS:	"
]
IFN A1PSW,[
	SETOM PRGC
	SETOM OUTC
GO3: ]
	MOVE A,WSWCNT
	MOVEM A,TTYFLG	; Turn off typeout if there were (W) switches.
	SETOM LSTTTY	; Allow TYOERR to output to both TTY and LST.
	JSP A,$INIT	; Initialize for assembly
	JSP A,PS1	; Do pass 1
	TRNN FF,FRNPSS	; If 2 pass assembly,
	 JRST GO4
	PUSHJ P,OPNRD	; Then re-open input file
	 JRST GO2A	; Couldn't re-open???? Do something better here.
GO4:	JSP A,PLOD	; Maybe punch out SBLK loader in some format
	JSP A,PS2	; Do pass 2
	JSP A,PSYMS	; Maybe punch out symbol table
IFN A1PSW,[
	TLZ FF,$FLOUT
	AOS PRGC	; Indicate end statement encountered
	SETOM OUTC	; " " "
	TRNN FF,FRNPSS	; If 1 pass assembly,
	 SKIPGE CONTRL
	  CAIA	
	JRST GO3	; Then try to assemble another program
]
IFN FASLP,[
	SKIPGE A,CONTRL
	TRNN A,FASL
	 JRST GO8
	MOVE A,[SIXBIT /*FASL*/]	; "finish" FASL file
	MOVEI B,17
	PUSHJ P,FASO	; Ignore end frob, but output FASL end code
	MOVE A,[ASCIC//]	; pad with ^C's.
	PUSHJ P,FASO1	; Randomness
	PUSHJ P,FASBE	; Write out last block
]
	; Jump directly here for certain main-input EOF conditions.
GO8:	SETZM FATAL	; There was no fatal error: output files get renamed.

	; Jump directly here if hit fatal error (incl .FATAL, illegal UUO, etc)
GO9:	PUSHJ P,.FILE		; Close (and rename if FATAL = 0) output files.
	SETZM LSTTTY
IFN RUNTSW, PUSHJ P,RNTTYO	; Type out run time used since GO2A
	CALL ERRCLS	; File away error file - only thing not closed by .FILE
	JRST TSRETN	; and die according to system's wishes.
SUBTTL MIDAS Death (TSRETN) - system dependent exit routines

IFN ITSSW,[
TSRETN:
IFN PURESW,[
	SKIPGE PURIFG	; If not yet purified, assume being debugged.
	 .VALUE
]
	.LOGOUT		; Come here to commit suicide.
	.BREAK 16,160000
] ;IFN ITSSW

IFN DECSW,[

TSRETN:	SKIPLE A,ERRCNT		; If had any errors,
	 ADDM A,.JBERR		; let loader know about them. (???) Well,
				.SEE ERR1 ; for strange comment.
	SKIPN CCLMOR	; Any more CCL commands?
	 EXIT		; Nope, all done.
	JRST RERUN	; More CCL to hack, start up a new MIDAS.
] ; IFN DECSW


IFN TNXSW,[
TSRETN:	SKIPE CCLMOR	; Need to hack any more CCL?
	 JRST RERUN	; Yeah.
TSRET1:	HALTF
	HRROI 1,[ASCIZ/Can't continue/]
	PSOUT		; Better than dying randomly
	JRST TSRET1
] ; IFN TNXSW
SUBTTL MIDAS Murder - fatal internal error handling (GOHALT)

VBLK
HALTER:	0		; JSR'd here when fatal internal error seen.
	JRST HALTEP	; Jump to pure-code handling
PBLK
HALTEP:
IFN ITSSW,[
	.VALUE [ASCIZ |: ===== Fatal MIDAS internal error! =====
Please send a message to BUG-MIDAS describing circumstances.
Error was at location: 
HALTER/
|]
] ;IFN ITSSW

IFN TNXSW,[
.SCALAR HALTR1,HALTR2,HALTR3
	MOVEM R1,HALTR1	; Save R1 etc. for later examination
	MOVEM R2,HALTR2
	MOVEM R3,HALTR3
	HRROI R1,[ASCIZ | ===== Fatal MIDAS internal error! =====
Please send a message to BUG-MIDAS @ MIT-MC describing circumstances.
Error was at location: |]
	PSOUT
	MOVEI R1,.PRIOU
	HRRZ B,HALTER
	MOVEI C,8.
	NOUT
	 ERJMP .+1
	HRROI R1,[ASCIZ /
/]
	PSOUT
	MOVE R1,HALTR1	; Restore R1 etc. for later examination
	MOVE R2,HALTR2
	MOVE R3,HALTR3
	HALTF
] ;IFN TNXSW

IFN DECSW,[
	OUTSTR [ASCIZ | ===== Fatal MIDAS internal error! =====
Please send a message to BUG-MIDAS @ MIT-MC describing circumstances.
Error location can be found in HALTER/ (please look at it with DDT to
find out where the error came from).
|]
	EXIT
] ;IFN DECSW

	JRST .-1	; Just in case
SUBTTL MIDAS Purification - PURIFY startup, also DECDBM

IFN ITSSW\TNXSW,[
IFN PURESW,[

PURIFY:	SKIPL NVRRUN
IFN ITSSW,[ .VALUE [ASCIZ /: Already run, can't purify 
/]]
IFN TNXSW,[
	 JRST [	HRROI 1,[ASCIZ /? Already run, can't purify
/]
		PSOUT
		HALTF
		JRST .+1]	; If continued, go ahead anyway.
] ;IFN TNXSW
PURIF1:	MOVEI P,17			; Start PDL at 20
	JSP F,FLSPGS			; First flush blank-code pages,
	 <MINBNK-MINMAC>,,MINBNK	;	incl. symbol table area.
	JSP F,FLSPGS			; Flush MACTAB pages created by load
	 <MXICLR-MINPUR>,,MXICLR	;	but not needed.
	JSP F,PURIFD			; Purify pure pages.
	 <MINPUR-MAXPUR>,,MINPUR
	SETZM PURIFG			; Set "purified" flag
IFN TNXSW,SETOM MEMDBG			; For TNX, ask for mem checking.
	MOVE [1,,2]			; Now clear out remains of data of self
	MOVEI 1,0
	BLT 40
IFN ITSSW,[
	.VALUE [ASCIZ /: Purified, type CR to dump 
:PDUMP SYS;TS MIDAS/]
] ;IFN ITSSW

IFN TNXSW,[
	HRROI 1,[ASCIZ / Purified, now SAVE
/]
	PSOUT
	HALTF
] ; IFN TNXSW
	JRST BEG

	; JSP F,FLSPGS
	;  -<# pgs>,,<page to start>
	;	Flush pages specified by page AOBJN

FLSPGS:	MOVE A,(F)	; Get the page AOBJN
IFN ITSSW,[
	SYSCAL CORBLK,[MOVEI 0 ? MOVEI %JSELF ? A]
	 .LOSE 1000
]
IFN TNXSW,[
	ASH A,1		; Multiply # pages, page # by 2.
	HLRE B,A
	HRLI A,.FHSLF
	MOVNS B
	TLO B,(PM%CNT)	; Say hacking repeat count
FLSPG2:	SYSCAL PMAP,[[-1] ? A ? B]	; Flush these pages.
	TLNN FF,FL20X		; If on 20X, that's all.
	 JRST [	HRRI B,-1(B)	; Else, on 10X, must iterate manually.
		TRNE B,400000	; See if became "negative".
		 JRST .+1	; Yep, done with manual iteration.
		AOJA A,FLSPG2]	; Nope, bump page #.
]
	JRST 1(F)

	; JSP F,PURIFD - Just like FLSPGS, but purifies the pages instead.

PURIFD:	MOVE A,(F)	; Get page AOBJN
IFN ITSSW,[
	SYSCAL CORBLK,[MOVEI %CBNDR	; Read access only.
		MOVEI %JSELF ? A]
	 .LOSE 1000
]
IFN TNXSW,[
	ASH A,1		; Double everything to get in terms of TNX pages.
	HLRE B,A
	MOVNS B		; Get # pages in B
	MOVEI C,(A)
	ADDI C,-1(B)	; Find # of last page to purify
	LSH C,9.	; Get addr of 1st wd of last page
	MOVES (C)	; Touch it so that it is guaranteed to exist!
			; This is necessary since last ITS page may only
			; include one TNX page instead of two.
	HRLI A,.FHSLF
PURID1:	SYSCAL SPACS,[A ? [PA%RD+PA%EX]]
	ADDI A,1
	SOJG B,PURID1
]
	JRST 1(F)

IFN TNXSW,[

; PURSAV - A startup routine like PURIFY, for possible use on TNX if
;	the EXEC "SAVE" command does not preserve page access bits.
;	Current T20 EXEC seems to do OK though.  This is only useful
;	when trying to catch illegal writes to "read-only" code.

PURSV0:	PUSHJ P,RDJERR
PURSAV:	MOVEI P,20
	HRROI R1,[ASCIZ /Pure-Save to file: /]
	PSOUT
	MOVSI R1,(GJ%NEW+GJ%FOU+GJ%SHT+GJ%FNS)
	MOVE R2,[.PRIIN,,.PRIOU]
	GTJFN			; Get JFN from TTY
	 JRST PURSV0
	SETZM PURIFG		; Claim purified...
	SETOM MEMDBG		; and keeping watch on memory.
	HRLI R1,.FHSLF
	MOVEI R2,[
		2*<0-MINBNK>,,0+SS%RD+SS%WR+SS%EXE	; Variables/buffers
		2*<MINMAC-MXICLR>,,2*MINMAC+SS%RD+SS%WR+SS%EXE	; MACTAB init
		2*<MINPUR-MAXPUR>,,2*MINPUR+SS%RD+SS%EXE	; Purify pure pages.
		0 ]	; End of SSAVE table
	SETZ R3,
	SSAVE		; Do it!
	HRROI R1,[ASCIZ /Saved./]
	PSOUT
	HALTF
] ; IFN TNXSW

] ; IFN PURESW
] ; IFN ITSSW\TNXSW

IFN DECDBG,[
DECDBM:	0
	HRLZ A,.JBSYM	;GET ADDR OF START OF DDT SYMS,
	HRRI A,DECDBB+200	;LEAVE 200 WD SPACE BEFORE THEM.
	HRRM A,.JBSYM		;MOVE THEM INTO SPACE PROVIDED
	HLRE B,.JBSYM
	MOVMS B
	BLT A,DECDBB+177(B)	;SO THEY WON'T GET IN MACTAB'S WAY.
	JRST @DECDBM
]
SUBTTL System-dependent Symbol Table stuff.

IFN ITSSW,[

; TSYMGT - Gobble syms from system (ITS feature!)
;	TABLE AREA IN SYSTEM:
;	FIRST LOC SYSYMB
;	LAST (AS OPPOSED TO LAST + 1) SYSYME

TSYMGT:	MOVEI A,EISYMT	;EISYMT FIRST LOC FOR ITS SYMS
	MOVE B,[SIXBIT /CALLS/]	;SYSTEM CALLS
	.GETSYS A,	;READ IN SYSTEM CALLS (SHOULD SKIP)
	 .LOSE %LSSYS
	SKIPGE A
	 .LOSE		;.GETSYS DIDN'T UPDATE AOBJN POINTER
	HRRM A,SP1	;MARK END OF SYMS
	ANDI A,-1
	CAIL A,MACTBA+MACL
	 .LOSE		;MACL TOO SMALL!  INITS MIGHT LOSE.
	MOVEI B,EISYMT
	MOVEI AA,SYMC_<-18.+4>	;SQUOZE FLAG FOR SYM
TSYMG2:	DPB AA,[400400,,(B)]
	ADDI B,2
	CAIE B,(A)
	JRST TSYMG2
	POPJ P,
]; IFN ITSSW
SUBTTL .SITE pseudo & initialization (SITINI)

IFN ITSSW,	LVSITE==1	; ITS only uses 1 word of mach name.
IFN DECSW\TNXSW,LVSITE==5	; whereas others need 5 words (25 chars max)

LVAR	V.SITE:	BLOCK LVSITE	; .SITE string stored here.

	; .SITE N, returns nth word of sixbit machine name.

A.SITE:	CALL AGETFD	; Get field as argument.
	JUMPL A,CABPOP	; Ignore negative indices.
	CAIL A,LVSITE	; Make sure index is within bounds of string.
	 JRST CABPOP
	MOVE A,V.SITE(A)	; Win, get indexed word.
	JRST CLBPOP


; SITINI - Initialization routine called only at MIDAS startup, for
;	setting up .SITE and maybe other things.

SITINI:	BLTZ LVSITE,V.SITE	; Clear out string location

IFN ITSSW,[	; For ITS, use up just 1 word and need 1 call to set V.SITE
	SYSCAL SSTATU,[REPEAT 5,[ ? MOVEM A] ? MOVEM V.SITE]
	 .LOSE %LSSYS
	POPJ P,	]

IFN SAILSW,[	; SAIL needs special kludge, since it doesn't have the
	MOVE A,[SIXBIT /SAIL/]	; right GETTAB used.
	MOVEM A,V.SITE
	POPJ P,	]

	; This code sets TNX .OSMIDAS at runtime as appropriate.
IFN TNXSW,[
	MOVE A,[SIXBIT /TENEX/]	; Assume running on 10X
	TLNE FF,FL20X		; unless proved otherwise
	 MOVE A,[SIXBIT /TWENEX/]
	MOVEM A,OSMID		; Store directly as symtab value!
]
	; If TNX and on ARPA network, get Arpanet host name for .SITE
IFN TNXSW,[
	SYSCAL SYSGT,[['LHOSTN]][A ? B]	; Get local host #
	JUMPL A,SITIN3			; Tops-20 release 3 has a LHOSTN table
	JUMPE B,SITIN3			; Jump if none, not on net.
	SYSCAL CVHST,[FNBWP ? A][A]	; Write string into FNBUF.
	 JRST SITIN3		; No string for that host #??
	SETZ B,
	IDPB B,A		; Make sure string is ASCIZ'd.
	MOVE B,FNBWP		; Note that FNBWP isn't altered by the syscal!
	MOVE C,[440600,,V.SITE]
SITIN2:	ILDB A,B
	JUMPE A,APOPJ		; return when string ended.
	TRCE A,140		; Convert char to sixbit.
	 TRCE A,140
	  TRCE A,140
	IDPB A,C
	JRST SITIN2
]

	; For non-network TENEX and DEC in general, very similar.
IFN DECSW\TNXSW,[
IFN TNXSW,[			
SITIN3:	SYSCAL SYSGT,[['SYSVER]][A ? D]	; Best to get table index dynamically,
	JUMPE D,APOPJ		; If can't, lose.
	]
IFN DECSW,MOVEI D,11	; 11 = .GTCNF   But on T10 we can assume this.

	MOVE AA,[440600,,V.SITE]
	MOVSI C,-5	; Process 5 words of .GTCNF (max possible)
SITIN4:	HRLZ B,C	; Get subindex we want,
	HRRI B,(D)	; and produce <subindex>,,<table #>
IFN DECSW, GETTAB B,	; Get 1 word of table, using appropriate call.
IFN TNXSW, SYSCAL GETAB,[B][B]
	 POPJ P,	; If call fails, exit.
SITIN5:	SETZ A,
	LSHC A,7	; Extract an ascii char
	CAIE A,",	; If it's a comma,
	 CAIG A,40	; or ctl or space,
	  POPJ P,	; then let's stop.
	TRCE A,140	; Swap bit 40 with bit 100, thus turning
	 TRCE A,140	; "A to 'A, "a to 'A, "1 to '1, etc, and ^@ to ' .
	  TRCE A,140
	IDPB A,AA	; Store the sixbit into V.SITE
	JUMPN B,SITIN5	; When nothing left of this word of .GTCNF, get next.
	AOBJN C,SITIN4
	POPJ P,
] ;DECSW\TNXSW
SUBTTL RunTime - .MRUNT and end-of-assembly typeout
IFN RUNTSW,[

.SCALAR IRUNTM	; Holds initial run time (set at start of assembly)

	; .MRUNT - Returns runtime since start of assembly.

A.MRUN:	PUSHJ P,RNTTMA	; Get current run time
	SUB A,IRUNTM	; Subtract initial run time
IFN ITSSW,[MULI A,4069.	; ITS - Convert to nanoseconds,
	DIV A,[1.^6]	;   then to milliseconds.
]
	PJRST CLBPOP

	; RNTTMA - internal routine to return in A the current runtime,
	; in whatever units the OS furnishes.
RNTTMA:
IFN ITSSW, .SUSET [.RRUNT,,A]	; Gets runtime in 4.096 usec units.
IFN DECSW, SETZ A, ? RUNTIM A,	; Runtime in msec
IFN TNXSW,[
	IFN A-1, EXCH R1,A
	MOVEI R1,.FHSLF
	RUNTM			; Runtime in msec for self.
	IFN A-1, EXCH R1,A
	]
	POPJ P,

; RNTTYO - Called at end of assembly to type out runtime,
;	# of errors, and # symbols used.

RNTTYO:
IFN DECSW,[	; Nobody wants this on ITS, but other people do...sigh...
	SKIPE A,ERRCNT		; Any assembly errors?
	 JRST [	TYPE "? "	; Yes, error message for batch controllers
		CALL DPNT
		TYPECR " error(s) detected"
		JRST .+1]
	SKIPE CCLFLG			; Called via CCL?
	 RET
]
	TYPE "Run time = "
	CALL A.MRUN	; Get runtime in millisec. in A.
	IDIVI A,10.
	IDIVI A,100.	; Get secs and hundredths.
	HRLM B,(P)	; Save remainder
	PUSHJ P,HMSTYO	; Type out secs
	MOVEI A,".
	CALL TYO
	HLRZ A,(P)
	CALL HMSTY3	; Type out hundredths
	CALL CRR
	CALL A.SYMC
	CALL DPNT
	TYPE " Symbols including initial ones ("
	CALL A.SYMC
	IMULI A,100.
	IDIV A,SYMLEN	; Get % symtab used
	CALL DPNT
	TYPECR "% used)"
	RET

; HMSTYO - Type out H:MM:SS time in A
;	 Doesn't work for times .ge. 60. hours

HMSTYO:	IDIVI A,60.
	JUMPE A,[MOVE A,B ? PJRST DPNT]
	HRLM B,(P)
	PUSHJ P,HMSTYO
	MOVEI A,":
	PUSHJ P,TYO	; Type delimiting char
	HLRZ A,(P)
HMSTY3:	IDIVI A,10.
	PUSHJ P,ADGTYO	; Type out digit in A
	MOVEI A,"0(B)
	PJRST TYO

] ; IFN RUNTSW
SUBTTL COMMON Output Routine WINIT - Open all output files.

; WINIT - Called from top-level control to open all necessary output files.
;

WINIT:
IFN ERRSW,[
	SKIPN ERRFP	; If want error output file,
	 JRST WINIT2
	CALL OINIT	; Open it, first of all.
	 0 ERRFC,ERRFB
	 SIXBIT/ERROUT/
	 ERRHDR,,ERRBUF
	SETOM ERRFOP	; Error file now open.
WINIT2:	]
	PUSHJ P,OINIT		; Open main output file.
	 13^9 UTYOC,OUTFB	; <dec-mode> chnl,name-block.
	 SIXBIT/OUTPUT/
	 UTOHDR,,UTOBUF
IFN ITSSW,[
	TLZ FF,FLPTPF	; Initially assume device not paper tape punch
	.STATUS UTYOC,A	; Get status of output channel
	ANDI A,77	; Mask to device code
	CAIN A,7	; If paper tape punch,
	 TLO FF,FLPTPF	; Then set FLPTPF.
]
IFN LISTSW,[
	SKIPN LISTP
	 JRST WINIT1
	CALL OINIT		; Open listing file if desired.
	 0 LPTC,LSTFB
	 SIXBIT/LSTOUT/
	 LSTHDR,,LSTBUF
WINIT1:
]
IFN CREFSW,[
	SKIPN CREFP	; If cref requested,
	 RET
	PUSHJ P,OINIT		; Open cref file, FN2 = CRFOUT
	 13^9 CREFC,CRFFB
	 SIXBIT/CRFOUT/
	 CRFHDR,,CRFBUF
	MOVE A,[.BYTE 7 ? 177 ? "B ? ^W]
	PUSHJ P,CRFOUT		; Output header to indicate image input.
	PUSHJ P,CRFSSF		; Output set-source-file block.
]
	RET
SUBTTL COMMON Output Routines - Output Chars/Words to BIN, TTY, ERR, CREF, LIST

	; PPB - Punch Binary word.

PPB:	JUMPGE FF,CPOPJ		; Don't punch if not punching pass.
PPBA:			; This entry pt "Always" punches.
TPPB:	SOSGE UTYOCT		; If no more room in buffer,
	 JRST [	CALL TPPBF	; Output & re-init buffer.
		JRST TPPB]
	IDPB A,UTYOP
	RET

TPPBF:	PUSH P,[0 UTYOC,UTOHDR]	; Drop thru to COBUFO.

	; Common OBUFO.  Takes <ch>,<header> on stack, clobbers no ACs.
	; See rtns below for usual calling sequence.
COBUFO:	EXCH C,(P)	; Get arg off stack, save C.
	CALL OBUFO	; Output & re-init buffer.
	REST C
	RET

	; TYO - Output char in A, outputting also to ERR file if possible.
TAB:	MOVEI A,^I
TYO:	SKIPG A.TTYF
	 CALL TYOX	; Actually output to TTY with OS-dependent routine.
			; Then fall through for ERR output.
ERRCHR:
IFE ERRSW,RET
IFN ERRSW,[
	SKIPN ERRFOP	; Output char in A to error file if one is open.
	 RET
	SOSGE ERRFCT
	 JRST [	PUSH P,[ERRCHR]
		PUSH P,[0 ERRFC,ERRHDR]
		PJRST COBUFO]
	IDPB A,ERRPTR
	RET
] ;IFN ERRSW

	; CRFOUT -  Output word in A to CREF file.

IFN CREFSW,[
CRFOUT:	SOSGE CRFCNT
	 JRST [	PUSH P,[CRFOUT]		; Buffer full, go output it.
		PUSH P,[0 CREFC,CRFHDR]
		PJRST COBUFO]
	IDPB A,CRFPTR
	POPJ P,

CRFSSF:	SKIPA A,[1]	; Output set-source-file block.
CRFPSH:	MOVEI A,3	; Output push-source-file block.
REPEAT L$F6BL,[
	CALL CRFOUT
	MOVE A,INFB+$F6DEV+.RPCNT
	]
	JRST CRFOUT
] ; IFN CREFSW


	; PILPT -  Output character in A to listing file.

IFN LISTSW,[
PILPT:	SOSGE LSTCNT
	 JRST [	PUSH P,[PILPT]		; When buffer full, output it.
		PUSH P,[0 LPTC,LSTHDR]
		PJRST COBUFO]
	IDPB A,LSTPTR
	RET

LPTCLS=:CPOPJ	; Hmmm, random noop-ness ref'd by AEND.

] ;END IFN LISTSW,
SUBTTL COMMON Output Routine .FILE - Close all output files.

; .FILE - Counterpart to WINIT.
;	Close input, bin, cref and list files.

.FILE:			; Closing input file is simple enough...
IFN DECSW, RELEAS UTYIC,
IFN ITSSW, .CLOSE UTYIC,
IFN TNXSW,[
IFN PMAPSW, MOVE A,[NIBFPS,,1STBFP] ? CALL DELPGS	; flush buffer pages
	MOVE R1,INFB+$FJFN
	CLOSF
	 JFCL
	SETZM INFB+$FJFN
	SETZM JFNCHS+UTYIC
]
	MOVNI A,1
	SKIPL B,CONTRL	; If relocatable,
	 PUSHJ P,TPPB	; Output a -1 so stink will see EOF
	SETZ A,		; In dec fmt, output a 0 at end.
	TRNE B,DECREL
	 CALL TPPB
	SKIPE OUTFB+$FEXT	; Check general name.
	 JRST .FILE2		; Output fnam2 was explicitly specified

	; Output extension (fn2) wasn't specified, default depends
	; on system and output type.
IFN ITSSW, MOVSI A,'BIN		; Default to SBLK output format; note that
IFE ITSSW, MOVSI A,'SBK		; this will include RIM, RIM10.
	SKIPL B,CONTRL		; Using STINK output format?
IFN ITSSW, MOVSI A,'REL		; Yes, use appropriate thing for site.
IFE ITSSW, MOVSI A,'STK
	TRNE B,DECSAV		; Using DECSAV output format?
	 MOVSI A,'SAV
IFN TNXSW,[
	TRNE B,DECSAV		; If using DECSAV format and
	 TLNN FF,FL20X		; on a 20X, then
	  CAIA
	   MOVSI A,'EXE		; use this extension instead.
]
	TRNE B,DECREL		; Using DECREL output format?
	 MOVSI A,'REL
IFN FASLP,[
	TRNE B,FASL		; Using FASL output format?
IFE DECSW, MOVE A,[SIXBIT /FASL/]	; yes, smash as appropriate.
IFN DECSW, MOVSI A,'FAS
]
IFE TNXSW, MOVEM A,OUTFB+$F6EXT	; For 6bit systems, store final selection.
IFN TNXSW, PUSHJ P,TNXODF	; If Tenex, call output default hackery, since
				; changing stuff is a bit hairier.

.FILE2:	JSP A,OCLOSE
	 0 UTYOC,UTOHDR	; Write out buffer, rename and close output file.
	 OUTFB
IFN LISTSW,[
	SKIPN LISTP	; Listing file open =>
	 JRST .FILE3
	CALL PNTCR	; End with cr and ff.
	MOVEI A,^L
	CALL PILPT
	PUSH P,FATAL	; Rename listing file even if fatal error.
	SETZM FATAL
	JSP A,OCLOSE
	 0 LPTC,LSTHDR	; Output buffer, rename & close it.
	 LSTFB
	POP P,FATAL
.FILE3:
] ;IFN LISTSW
IFN CREFSW,[
	SKIPN CREFP	; If cref file open,
	 POPJ P,
	MOVEI A,0
	PUSHJ P,CRFOUT	; Output eof block,
	JSP A,OCLOSE	; Write buffer, close.
	 0 CREFC,CRFHDR	; 0 chnl,header
	 CRFFB
]
	RET

	; File out error output file.  This isn't done in .FILE so that
	; error file can include a few more goodies and be closed separately
	; later on.
ERRCLS:	SETZM FATAL	; Err file renamed even after fatal error.
IFN ERRSW,[
	SKIPN ERRFOP
	 RET		; There is none.
	MOVEI A,^M
	CALL ERRCHR	; Put crlf at ennd.
	MOVEI A,^J
	CALL ERRCHR
	JSP A,OCLOSE	; Rename and close.
	 0 ERRFC,ERRHDR
	 ERRFB
	SETZM ERRFOP
]
	RET
SUBTTL ITS - Output file Open, Output, Close/Rename.
IFN ITSSW,[

;	PUSHJ P,OINIT	; Open output file
;	 Mode chnl,name-block-addr
;	Sixbit/desired-temporary-fn2/
;	Header,,buffer space	;used only in dec version.
; The mode should be 13^9 for binary, 0 for ascii.

OINIT:	MOVE A,(P)
	HLRZ B,2(A)	; Get addr of header,
	SETOM 2(B)	; Set buffer byte count to -1 => not initted.
	MOVE AA,1(A)	; Get 2nd arg, temp FN2 to use.
	MOVE F,(A)	; Get 1st arg - <mode> <ch>,<filblk>
	SYSCAL TRANS,[5000,,.UAO		; For output mode,
		REPEAT 4,[?       .RPCNT(F) ]		; translate from given names
		REPEAT 4,[? MOVEM .RPCNT+FB+$F6DEV ]]	; into actual names, in scratch blk.
	 JRST OINITL	; (too many translations)

	SYSCAL DELETE,[FB+$F6DEV	; Delete old temp name file.
		TMPFN1 ? AA ? FB+$F6DIR]
	 JFCL				; If none, it's ok.
	LDB A,[270400,,F]	; Get channel num.
	HRLI A,.BAO		; Open mode (default ascii)
	TLNE F,777000		; But maybe want image mode.
	 HRLI A,.BIO		; Yep, use that instead, to get <mode>,,<ch>
	SYSCAL OPEN,[A ? FB+$F6DEV	; Open file,
		TMPFN1 ? AA		; using these temp filenames.
		FB+$F6DIR]
	 JRST OINITL
	BLTM L$F6BL,FB+$F6DEV,$F6DEV(F)	; Copy translated names into
				; name-block for file, for eventual rename.

POPJ3:	AOS (P)		; Skip over 3 args.
POPJ2:	AOS (P)
	JRST POPJ1

TMPFN1:	SIXBIT /_MIDAS/		; FN1 to use for temp filenames.



; OINITL - jumped to from OINIT if some lossage
;	encountered when opening output files.


OINITL:	HLLZ A,@(P)	; Get chnl num,
	TLZ A,777037	; Mask to just ac field (chnl num)
	IOR A,[.STATUS A]
	XCT A		; Read its status,
	PUSHJ P,OPNER	; Type out reason for open failure, and ask
	TYPE "Use what filename instead? "
	PUSHJ P,GTYIP	; Get typein, one line.
	MOVE F,@(P)	; Get <filblk>
	PUSHJ P,RFD	; Get new file description into filblk spec'd by F
	JRST OINIT	; and jump back to try again.

VBLK
ERRDNM:	.UAI,,'ERR ? 3
ERRCOD:	0
IFSTS:	0	; .STATUS word stored by OPNRD1 when .OPEN loses
PBLK

	; Openloss documentation routine

IOPNER:	MOVE A,IFSTS	; Input
OPNER:	MOVEM A,ERRCOD	; Save .status word
	PUSHJ P,TYPFB	; Type out file description
	PUSHJ P,CRRERR	; Now crlf to ensure room for following
	.OPEN ERRC,ERRDNM	; Now get the system to say what's wrong
	 .LOSE %LSSYS		; Can't open err device?
IOPNR2:	.IOT ERRC,A	; Get character from system
	CAIGE A,40	; Ends with ^L or ^C or other cruft.
	 PJRST CRRERR	; Return, typing out CRLF.
	PUSHJ P,TYOERR	; Type out character
	JRST IOPNR2	; Loop back for next


;	JSP A,OCLOSE
;	0 chnl,header
;	Nameblockaddr
; Write out last buffer, rename to names in nameblock and close.

OCLOSE:	MOVE C,(A)		; 1st wd of args is what OBUFO wants.
	LDB B,[360600,,1(C)]	; Just in case this is ascii file,
	DPB B,[300600,,OCLOSP]	; Get bp to unused part of last wd of buffer,
	MOVE B,[ASCIC//]
	DPB B,OCLOSP		; And pad with ^c's.
	SOS 2(C)		; Obufo assumes byte count was sos'd.
	CALL OBUFO		; Write out last partial buffer
	MOVE F,1(A)		; Get <filblk>
	LDB C,[270400,,(A)]	; Get chnl num.
	SKIPE FATAL
	 JRST OCLOS1		; After fatal error, don't rename outputfiles.
	SYSCAL RENMWO,[C	; Rename (F has nameblock addr)
		$F6FN1(F) ? $F6FN2(F)]
	 .LOSE %LSSYS
OCLOS1:	SYSCAL CLOSE,[C]	; Close channel.
	 .LOSE %LSSYS
	JRST 2(A)	; Skip over args on return.


; OBUFO - Write out and reinitialize buffer for file.
;	Assumes byte count (header 3rd wd) was sos'd.
;	C has <0 chnl,header>
;	In ITS version, header 1st wd has <size in bytes>,,<buffer addr>-1

OBUFO:	PUSH P,A
	PUSH P,AA
	AOSGE 2(C)	; Was count sos'd from -1?
	 JRST OBUFO1	; Yes, buffer hadn't been initted, don't write it.
	MOVN A,1(C)
	ADD A,(C)	; RH(A) has -<# wds used in buffer>.
	MOVSI A,(A)
	HRR A,(C)
	AOS A		; A has aobjn -> used part of buffer.
	HLLZ AA,C
	IOR AA,[.IOT A]
	CAIGE A,
	 XCT AA		; Write it in file.
OBUFO1:	MOVE A,1(C)
	HRR A,(C)	; Position the b.p. before start of buffer,
	TLZ A,770000	; After last byte in wd (idpb will use 1st buffer wd)
	MOVEM A,1(C)
	HLRE A,(C)
	MOVEM A,2(C)	; Set up byte count.
	REST AA
	JRST POPAJ

TFEED:	TLNN FF,FLPTPF	; If output device not PTP,
	 POPJ P,	; Then do nothing
	PUSHJ P,TPPBF	; Otherwise output the buffer,
TFEED1:	.FEED UTYOC,	; Feed a line,
	TLZA FF,FLPTPF	; If this is executed, utyoc doesn't have ptp after all
	 SOJG B,TFEED1	; Feed the specified number of lines,
	POPJ P,		; And return

] ; IFN ITSSW
SUBTTL DEC - Output file Open, Output, Close/Rename

IFN DECSW,[

OINIT:	MOVE AA,(P)
	MOVE F,(AA)	; Get <mode> <ch>,<filblk>
	HLLZ TT,F
	TLZ TT,#(0 17,)	; Mask off AC field in TT
	HRRZ D,2(AA)	; Get buffer space addr.
	HLLZ C,2(AA)	; Get header addr.
	HLRZ A,C
	SETZM (A)	; Clear out its-version contents of 1st header wd.
	LDB A,[331100,,F]	; Get mode to open in (will be ascii or image binary)
	IOR TT,[OPEN A]		; Cons up OPEN instruction for chan,
	MOVE B,$F6DEV(F)	; and bring in last arg.
	XCT TT			; Open channel,a
	 JRST OINITL		; Lost?

	PUSH P,.JBFF	; Now to fake out DEC system into consing up buffer
	MOVEM D,.JBFF	; at this location.  T10 uses .JBFF as pointer.
	XOR TT,[<OPEN A>#<OUTBUF 1>]	; Request buffer setup (one of)
	XCT TT
	REST .JBFF
	MOVE A,[SIXBIT /000MD /]
	PJOB B,		; Get job number, to make sixbit /<nnn>md<e, o, or l>/
	IDIVI B,10.
	DPB C,[220400,,A]
	IDIVI B,10.
	DPB C,[300400,,A]	; Put the digits of the job number into the sixbit word.
	DPB B,[360400,,A]
	MOVE AA,(P)
	LDB B,[360600,,1(AA)]	; Get 1st char of 'output, 'lstout, 'crfout, 'errout.
	IOR A,B			; Use it as last char of temp file name.
	MOVSI B,'TMP		; Set up ext (fn2),
	SETZ C,			; zap prot/date/time etc to default,
	MOVE D,$F6DIR(F)	; and PPN.
	XOR TT,[<OUTBUF 1>#<ENTER A>]
	XCT TT			; Do ENTER UTYOC,A
	 JRST OINITL
POPJ3:	AOS (P)
POPJ2:	AOS (P)
	JRST POPJ1

; OINITL - jumped to from OINIT if some lossage
;	encountered when opening output files.  Jumps back to OINIT
;	directly.

OINITL:	PUSHJ P,OPNER	; Type out reason for open failure, and ask:
	TYPE "Use what filename instead? "
	PUSHJ P,GTYIP	; Get typein, one line.
	PUSHJ P,RFD	; Get new file description into filblk spec'd by F
	JRST OINIT	; and jump back to try again.

	; Openloss documentation routine - not much to say.

IOPNER:		; Input
OPNER:	PUSHJ P,TYPFB		; Type out file description
	PUSHJ P,CRRERR		; Now crlf to ensure room for following
	TYPE "OPEN failed"
	PJRST CRRERR		; Return, typing out another CRLF.
;CLOSE AN OUTPUT FILE, SEE NON-DEC VERSION FOR ARGS.

OCLOSE:	PUSH P,A	; Save return addr
	MOVE F,1(A)	; Get <filblk>
	SKIPGE FATAL	; If fatal error happened,
	 JRST OCLOS2	; don't rename, just close.
	MOVE C,$F6DEV(F)	; Delete any file with names
	SETZB B,D	; we want to rename to.
	OPEN ERRC,B	; Use ERRC as temporary channel.
	 JRST OCLOS1
	MOVE A,$F6FN1(F)
	HLLZ B,$F6EXT(F)
	SETZ C,
	MOVE D,$F6DIR(F)
	LOOKUP ERRC,A
	 JRST OCLOS1	; There is none, just rename.
	SETZ A,		; Say to delete this file
	MOVE D,$F6DIR(F)	; From right UFD
	RENAME ERRC,A
	 JFCL
	RELEAS ERRC,
OCLOS1:	MOVE A,$F6FN1(F)	; Desired fn1.
	HLLZ B,$F6EXT(F)	; Desired fn2.
	SETZ C,			; Bottoms-10 DATE75 lossage must never be forgotten!
	MOVE D,$F6DIR(F)	; Sname (that is, ppn)
	HLLZ AA,@(P)		; Get just chnl num.
	IOR AA,[CLOSE]		; Close it & finalize,
	XCT AA
	XOR AA,[CLOSE#<RENAME A>]
	XCT AA			; Then rename to desired names.
	 JFCL			; at this point, ignore any lossage, sigh.
OCLOS2:	HLLZ B,@(P)	; Get chnl in ac field.
	IOR B,[RELEAS]
	XCT B		; Finally, release channel.
	JRST POPJ2	; and skip over args on return.

	; Write out buffer of output file, C has <0 chnl,header>
OBUFO:	AND C,[0 17,]	; Get just chnl num. (sys remembers where header is for ch)
	TLO C,(OUT)	; Output current buffer.
	XCT C
	 RET		; Normal return!
	PUSH P,A		; Error return from out uuo.
	XOR C,[OUT#<GETSTS A>]
	XCT C		; Read file status.
	TRZ A,74^4	; Clear error bits.
	ETR [ASCIZ /Output data error/]
	XOR C,[<GETSTS A>#<SETSTS (A)>]
	XCT C
	JRST POPAJ

	; Paper tape stuff, do nothing.
TFEED:	RET

] ;END IFN DECSW,
SUBTTL TNX - Output file Open, Output, Close/Rename

IFN TNXSW,[

TFEED:	RET	; Again, null out paper-tape hack.

; OINIT - Open Output file.
;	P points to first word of args which follow the call:
;	1: <mode> <ch>,<filblk>		; <mode> is 0 for ascii, 13^9 for bin.
;	2: sixbit /<desired temp fn2>/
;	3: <header>,,<buffer-space>
;	<return to this location>
;	Clobbers A,B,C

; For Tenex, it is necessary to fudge the fileblock consistency slightly;
; $FJFN has in RH the actual JFN used to write to the temporary-name
; file, and in LH the JFN for the final desired filename.  Note that if
; the $FEXT is null for main output file, it will be defaulted by TNXODF
; at close time, (to SAV, EXE, or REL) and the
; "final desired" JFN won't actually be used.
;	Both JFNS are "active" rather than just a file spec.

OINIT:	MOVE C,(P)	; Get addr of arg block
	HLRZ A,2(C)	; Get <header>,
	SETOM 2(A)	; and set buffer byte cnt to -1 to mark for init.
	MOVE F,(C)	; Get <mode> <ch>,<filblk>
	PUSHJ P,GETJFO		; Get output JFN for filblk.
	 JRST OINIT5		; Lost?
OINIT2:	HRLZS $FJFN(F)		; Won, move JFN over into LH.

	; Aha, successfully grabbed a JFN for desired output filename.
	; Now must get another one for the temporary filename...
	MOVSI A,(GJ%FOU+GJ%NEW)
	PUSHJ P,TFMAP		; Must set up block again, may have changed due to RDJFNO.
	MOVE A,1(C)		; Get sixbit/tmpfn2/
	PUSHJ P,CV6STR		; Convert to ASCIZ and return BP to string.
	MOVEM A,GTJBLK+.GJEXT	; Store in long-form call blk.
	SYSCAL GTJFN,[[GTJBLK] ? [0]][A]	; Repeat the GTJFN call.
	 JRST [	MOVEM A,ERRCOD	; Ugh????
		JRST OINIT5]
	HRRM A,$FJFN(F)		; Good, got it...

	; Now have both JFN's packed away, can finally open the
	; temporary filename.
	HRRZ B,A		; Need JFN in RH with LH clear...
	LDB A,[331100,,F]	; Get <mode>
	CAIN A,
	 MOVSI A,070000		; If 0, use ASCII (7-bit bytes)
	TRNE A,-1
	 MOVSI A,440000		; If not 0, use WORD (36-bit bytes)
	TRO A,OF%WR		; Get write access.
	SYSCAL OPENF,[B ? A][A]	; Open up the temp file (JFN in RH)
	 JRST [	MOVEM A,ERRCOD ? JRST OINIT6]	; damn

	; Won, successfully opened output file stuff etc, now wrap up.
	HRRZ A,$FJFN(F)		; Get JFN used,
	LDB C,[270400,,F]	; and channel number argument,
	MOVEM A,JFNCHS(C)	; and store JFN away in channel slot.
	PUSHJ P,CVFSIX		; Now put right things in $F6 entries.
	MOVEI A,3
	ADDM A,(P)
	POPJ P,

.SCALAR ERRCOD

	; Come here when GTJFN fails trying to get a JFN for GTJBLK long
	; form argument block.  Must print out bad filename.
	; OINIT5 should really use names in GTJBLK, and
	; OINIT6 should really hack GJFNS call to get names, but for now...

OINIT5:	SKIPA A,[[ASCIZ /GTJFN failed for /]]
OINIT6:	 MOVEI A,[ASCIZ /OPENF failed for /]
	PUSHJ P,CRRERR
	TYPR (A)
	PUSHJ P,OPNER1		; Type out filename and error message.
	PUSHJ P,RDJFNO		; Read new JFN
	JRST OINIT2		; try to open it.

IOPNER:	PUSH P,[CRRERR]	; Do following and add CRLF.
OPNER1:	PUSHJ P,TYPFB
	TYPE "
Error - "		; Drop thru to TERSTR.


TERSTR:	MOVE A,ERRCOD
	HRLI A,.FHSLF
	SYSCAL ERSTR,[[-1,,ERSTRB] ? A ? [-LERSTR,,]][A ? A ? B]
	 JRST TERST7	; undefined err #?
	 GOHALT		; destination bad?
	TYPR ERSTRB
	POPJ P,
TERST7:	TYPE "Unknown error"
	POPJ P,

	LERSTR==80.
.VECTOR ERSTRB(<LERSTR+4>/5)
; RDJFNO - Hack to get a new JFN by reading from TTY, using recognition.
; RDJFNI - Same but for input.  Uses current FB for defaults.
;	Stashes JFN away in RH of $FJFN(F).

RDJFNO:	SKIPA A,[GJ%FOU+GJ%NEW+GJ%CFM]	; For output
RDJFNI:	 MOVSI A,(GJ%OLD+GJ%CFM)	; for input
	PUSHJ P,TFMAP
	MOVE A,[.PRIIN,,.PRIOU]	; Use primary JFNs (TTY) for I/O
	MOVEM A,GTJBLK+.GJSRC
	PUSH P,R1
	PUSH P,R2
	PUSH P,R3
	SYSCAL DTI,[[.TICCV]]	; Disable ^V as an interrupt character
	CAIA
RDJFN2:	 PUSHJ P,RDJERI		; Come here when get error in GTJFN.
	MOVEI R1,.PRIIN		; Make sure that
	CFIBF			; TTY input is reset.
	HRROI R1,[ASCIZ /
Use what filename instead? /]
	PSOUT
	MOVEI R1,
	MOVEI R1,GTJBLK
	SETZ R2,
	GTJFN
	 JRST RDJFN2	; Error, report it.
	SYSCAL ATI,[[.TICCV,,.IC.CV]]	; Turn back on ^V
	POP P,R3
	POP P,R2
	HRRM R1,$FJFN(F)
	POP P,R1
	PJRST JFNSTB	; Smash FB with names of the JFN we got, and return.
	
; RDJERR - Report last error message directly to TTY (primary output).
;	Useful when doing quick direct user interaction.

RDJERR:	TROA R2,-1	; Here to get last error, whatever it was.
RDJERI:	 MOVE R2,R1	; Here to use err code in R1.
	HRLI R2,.FHSLF
	HRROI R1,ERSTRB
	MOVSI R3,-LERSTR
	ERSTR		; Get error string
	 JRST RDJER6
	 GOHALT
	SKIPA R1,[-1,,ERSTRB]
RDJER6:	 HRROI R1,[ASCIZ /Unknown error/]
	ESOUT		; Output to TTY amid other hackery.
	POPJ P,
; TNXODF - Hack to get yet another "desired" JFN so that when no
;	extension was specified for binary output file, one appropriate to
;	the type can be selected.
;	Basically do a GTJFN again for binary output filenames, furnishing
;	the default extension selected, and use that to replace the one
;	already in LH of $FJFN.

TNXODF:	PUSHJ P,CV6STR	; Convert sixbit word in A to string, get BP in A
	MOVEI F,OUTFB		; Point at right filblk,
	MOVEM A,$FEXT(F)	; Store, and now
	PUSH P,$FJFN(F)		; Save current set of JFNs before
	PUSHJ P,GETJFO		; getting another one
	 JRST POPAJ		; If lossage, stick to old JFN.
	POP P,A
	HRLZS $FJFN(F)		; GETJFO puts JFN into RH, we want it in LH.
	HRRM A,$FJFN(F)		; now restore previous RH.
	HLRZS A			; and get old "desired" JFN in position for
	SYSCAL RLJFN,[A]	; releasing.
	 JFCL
	POPJ P,
; OCLOSE - Close output file, writing out remainder of buffer and renaming
;	from temporary to desired filename.
;	JSP A,OCLOSE
;	1: 0 <ch>,,<header>
;	2: <filblk>
;	Clobbers F,C (and obviously A)

; 10x must do CLOSF, not releasing the JFN, and then a RNAMF from temp
; JFN to desired JFN, after which both can be released.  The desired and
; used JFNs are in LH and RH respectively of $FJFN in <filblk>.  <ch>
; is ignored except to wipe out its JFNCHS entry.

OCLOSE:	PUSH P,A
	MOVE C,(A)	; Get <ch>,,<header>
	SOS 2(C)	; OBUFO assumes count was SOS'd before each call
	PUSHJ P,OBUFO	; Write out anything remaining in buffer.
	LDB C,[270400,,(A)]	; Get channel number
	MOVE F,1(A)	; Get <filblk>
	HRRZ A,$FJFN(F)	; Find JFN being used...
	CAME A,JFNCHS(C)	; Should be same as JFN for channel.
	 GOHALT		; Synch error or something.
	TLO A,(CO%NRJ)	; Say don't release JFN
	SYSCAL CLOSF,[A]	; Close file...
	 GOHALT		; ?!?!
	HRRZS A		; Get back 0,,jfn
	SETZM JFNCHS(C)	; Indicate "channel" closed...
	SKIPE FATAL	; If fatal error happened in assembly,
	 JRST OCLOS5	; don't rename from temp filenames.
	HLRZ C,$FJFN(F)	; Now see what if anything to rename it to.
	JUMPE C,OCLOS5	; If no renaming needed, skip hair.
	SYSCAL RNAMF,[A ? C]	; Rename file from JFN in A to JFN in C.
	 GOHALT		; WTF?
	SYSCAL RLJFN,[C]
	 GOHALT
	JRST OCLOS6	; JFN in A released by RNAMF.

OCLOS5:	SYSCAL RLJFN,[A]
	 GOHALT
OCLOS6:	SETZM $FJFN(F)
	POP P,A
	JRST 2(A)
; OBUFO - Output Buffer and reinitialize.
;	C/ 0 <ch>,<header>
;	Clobbers no ACs.

; 10X is pretty much like ITS, JFN is kept in JFNCHS table indexed by <ch>.

OBUFO:	PUSH P,A
	PUSH P,B
	MOVE A,1(C)	; Get write BP,
	HRR A,(C)	; and reset it...
	TLZ A,770000	; to point at start of buffer,
	MOVEM A,1(C)	; and store it back, which is OK since we have byte cnt
	AOSGE 2(C)	; Was buffer marked for initialization (cnt -1)?
	 JRST OBUFO1	; Yes, don't write anything, just go init rest of it.
	HLRZ A,(C)	; Get buffer size in wds,
	MOVNI A,(A)	; make negative,
	ADD A,2(C)	; and add count of bytes left to get -<# bytes used>.
	LDB B,[270400,,C]	; Get channel # as index to JFN
	PUSH P,T
	SYSCAL SOUT,[JFNCHS(B) ? 1(C) ? A]
	POP P,T
OBUFO1:	HLRZ A,(C)	; Get buffer size again,
	MOVEM A,2(C)	; and reset count with it.
	POP P,B
	POP P,A
	POPJ P,

] ;END IFN TNXSW
SUBTTL COMMON Input Routines - Main File Open, EOF handling

; Open main input file for reading (filespec in ISFB)

OPNRD:
IFN ITSSW, .IOPDL		; Re-initialize IO pdl
IFN DECSW\TNXSW, CALL IOPDLC	; Non-ITS systems must simulate.
	INSIRP SETZM,INFCNT INFCUR INFERR
	MOVE A,[-TYPDLS-1,,TTYPDL]
	MOVEM A,ITTYP		; Initialize "tty pdl"
	PUSHJ P,MACIN1		; Clobber macro expansion status
	MOVE A,[ISFB,,INFB]	; Copy ISFB specs to INFB (which will hold
	BLT A,INFB+L$FBLK-1	; actual names of current input file)
	MOVE A,ISFB+$FDEV	; Get device name
	CAMN A,FSTTY		; TTY?
	 JRST [	MOVE A,[ISFB+$F6FN1,,IFNM1]	; TTY specified, treat special
		BLT A,IFNM2		; Clobber .IFNM1, .IFNM2 to specified
		MOVE A,ISFB+$FVERS
		MOVEM A,IFVRS
		TYPECR "Reading from TTY:"
		MOVEI A,3	; => input from tty, don't quit on cr
		JRST OPNRT2]
	MOVEI F,INFB		; Point things at INFB.
	PUSHJ P,OPNRD1		; Try opening file
	 JRST [	PUSHJ P,IOPNER	; Open lost, type out message
		POPJ P,]	; Read new command (this may screw on pass2?)
	MOVEM A,INFERR		; Err msg in main file shouldn't type names.
	MOVEI A,0		; => input from file
IFN TNXSW,[
	MOVE T,INFB+$FJFN	; Copy actual jfn to avoid re-GTJFN
	MOVEM T,ISFB+$FJFN
]
OPNRT2:	MOVE T,[IFNM1,,RFNAM1]
	BLT T,RFVERS		; Set up .FNAM1, .FNAM2
	SETOM NEDCRL
	AOS (P)			; Won, skip on return.
	JRST RCHSET		; Set up to read from file or tty. (arg in A)


	; Common stuff for OPNRD1 in all (DEC/ITS/TENEX) versions.
OPNRD3:	HRRZM A,UTIBED		; Say buffer empty,
	MOVSI A,EOFCH_13
	MOVEM A,@UTIBED		; Cause immediate reload.
OPNRD4:	BLTM 2,$F6FN1(F),IFNM1	; Set up .IFNM1, .IFNM2 from filblk F points at
	MOVE A,$FVERS(F)
	MOVEM A,IFVRS
	AOS A,INFCNT		; Assign this file a number.
	MOVEM A,INFCUR		; OPNRD expects this left in A.
	JRST POPJ1

		; EOF while trying to read character

RPAEOF:	PUSH P,B	; Save B
RPAEO1:	MOVE B,ITTYP	; Get pdl pointer
	PUSHJ P,BPOPJ	; Call pop routine (maybe NED's out)
	JRST RCHTRB	; Return to get character

		; EOF from main file

NEDCHK:	TRNE FF,FRCMND	; ^C read in command, :KILL self.
	 JRST TSRETN
	SKIPN RCHMOD
	 AOSE NEDCRL
	  JRST NEDCH1

	; Invent one crlf after end of main file.
	MOVE B,[440700,,[.BYTE 7 ? ^M ? ^J ? EOFCH]]
	MOVEM B,UREDP
	HRRZM B,UTIBED
IFN PMAPSW,[
	HRLI B,170700	; Make BP pointing at last (3rd) char
	MOVEM B,UTIBPE	; Set EOF BP properly.
]
	RET
NEDCH1:
IFN A1PSW,[
	PUSHJ P,OUTCHK
	MOVSI A,-LNEDT
	XCT NEDT(A)	; Skips if NED condition to be complained about
	AOBJN A,.-1
	JUMPGE A,GO8
]
	ETF [ASCIZ /No END statement/]

.SCALAR	NEDCRL	; -1 => haven't yet supplied a CRLF at EOF of main file.

IFN A1PSW,[	; Holler "NED" if any of the following:
NEDT:	SKIPL PRGC	; No end statements have been encountered
	SKIPGE OUTC	; Output has occured not matched by an end statement
	SKIPGE OUTN1	; Output has occured other than in 1pass mode
	TRNN FF,FRPSS2	; Currently in pass 2
LNEDT==.-NEDT	; Length of table
]
SUBTTL ITS - Input file Open, buffer input
IFN ITSSW,[

	; Try .OPENing input file pointed to by F.  Skips if successful.
	; Sets filenames to actual names.

OPNRD1:	SYSCAL OPEN,[[.BAI,,UTYIC]
		$F6DEV(F) ? $F6FN1(F) ? $F6EXT(F) ? $F6DIR(F)]
	 JRST [	.STATUS UTYIC,IFSTS	; Lost, save status now before possible
		POPJ P,]		; .IOPOP, and make failure return.
	SYSCAL RFNAME,[%CLIMM,,UTYIC	; Now find true filenames.
		MOVEM A
		MOVEM C			; But need to check FN1, FN2 so
		MOVEM D			; put them in ACs instead.
		MOVEM $F6DIR(F)]
	 .LOSE %LSFIL
	CAMN A,[SIXBIT/DSK/]
	 MOVE A,V.SITE		; Use machine name instead of DSK.
	MOVEM A,$F6DEV(F)
	CAIE C,			; If FN1 meaningless for device, skip to use
	 MOVEM C,$F6FN1(F)	; spec'd FN1 anyway, but else store actual FN1.
	CAIE D,
	 MOVEM D,$F6FN2(F)	; Ditto for FN2.
	MOVE D,[440600,,$F6FN2(F)]
	SETZ A,
OPNRD7:	TLNN D,770000
	 JRST OPNRD6
	ILDB C,D		; Calculate version number as number from fn2.
	CAIL C,'0		; Ignore non-digits.
	 CAILE C,'9
	  JRST OPNRD7
	IMULI A,10.
	ADDI A,-'0(C)
	JRST OPNRD7

OPNRD6:	SKIPN A			; No digits in FN2 => use -1 as version.
	 SETO A,
	MOVEM A,$FVERS(F)
	MOVE A,IUREDP		; Set up reading ptr,
	MOVEM A,UREDP
	JRST OPNRD3	; Set up ^C after buffer, infcur, etc.


	; EOFCH encountered on read, reload and jump back for next char

INCHR3:	HRRZ CH1,UREDP	; Get byte pointer
	CAME CH1,UTIBED	; End of block?
	 RET		; No, ^C in file.
	MOVE A,IUREDP
	MOVEM A,UREDP
	MOVE A,[-UTIBFL,,UTIBUF]
	.IOT UTYIC,A	; Read in block
	ANDI A,-1
	CAIN A,UTIBUF	; If the iot didn't give us anything, we are at EOF.
	 JRST RPAEOF
	HRRZM A,UTIBED	; Store RH (updated pointer) for EOF check at INCHR3
	MOVSI A,EOFCH_<18.-7>
	MOVEM A,@UTIBED	; Store a ^C after the data we read, so at EOB we come to INCHR3.
	JRST RCHTRA	; Now try next char
] ;END IFN ITSSW
SUBTTL DEC - Input file Open, buffer input

IFN DECSW,[

OPNRD1:	MOVEI C,UTIHDR	; Open the input file w/ names in dnam ... snam.
	SETZ A,		; Mode ascii.
	MOVEI D,UTIBUF
	MOVE TT,UTICHN	; Get channel num. to use.
	LSH TT,27	; Put in ac field.
	IOR TT,[OPEN A]
	MOVE B,$F6DEV(F)
	XCT TT		; Open channel,a
	 RET
	CALL BUFINI	; Initialize the input buffers and header.
	MOVE D,$F6DIR(F)
	MOVE A,$F6FNM(F)
	HLLZ B,$F6EXT(F)
	TLC TT,(OPEN#LOOKUP)
	XCT TT		; Lookup channel,a
	 RET		; Failed.
IFE SAILSW,[
	MOVE A,$F6DEV(F)
	DEVNAM A,	; Get real name of device.
	 CAIA
	MOVEM A,$F6DEV(F)
]
	MOVE D,[440600,,$F6FN2(F)]
	SETZ A,
OPNRD7:	TLNN D,770000
	 JRST OPNRD6
	ILDB C,D		; Calculate version number as number from fn2.
	CAIL C,'0		; Ignore non-digits.
	 CAILE C,'9
	  JRST OPNRD7
	IMULI A,10.
	ADDI A,-'0(C)
	JRST OPNRD7

OPNRD6:	SKIPN A			; No digits in FN2 => use -1 as version.
	 SETO A,
	MOVEM A,$FVERS(F)
	MOVE A,UREDP
	JRST OPNRD3


	; Reload buffer, DEC style.
INCHR3:	HRRZ CH1,UREDP	; Is this ^C at end of buffer?
	CAME CH1,UTIBED
	 RET		; No, ^C in file.
	PUSH P,B
	MOVE A,UTICHN
	LSH A,27	; Channel num. in ac fld.
	TLO A,(IN)
	XCT A		; Get next bufferfull.
	 CAIA		; Succeed.
	JRST INCHR4	; Error.
INCHR5:	MOVE A,UTICNT
	ADDI A,9
	IDIVI A,5
	ADD A,UREDP	; -> 1st wd not read into.
	HRRZM A,UTIBED
	HRRZ A,UREDP
	AOS A
	MOVEI B,1	; Scan the file and replace all line numbers with nulls.
INCHR6:	CAMN A,UTIBED
	 JRST INCHR7
	TDNE B,(A)
	 MOVEM B,(A)
	AOJA A,INCHR6

INCHR7:	MOVSI B,EOFCH_13
	MOVEM B,(A)	; Put EOF char after buffer, in extra word.
	JRST RCHTRB	; Retry RCH.

INCHR4:	XOR A,[<GETSTS B>#IN]
	XCT A
	TRZE B,74^4
	 ETR [ASCIZ /Input data error/]
	XOR A,[<GETSTS B>#<SETSTS (B)>]
	XCT A		; Clear error bits in status.
	TRNN B,2^4
	 JRST INCHR5
	JRST RPAEO1	; EOF.

; BUFINI - Create DEC-style buffer ring, with 1 extra word following
;	each buffer...
;	A/ <mode>
;	B/ <device name in 6bit>
;	C/ <header addr>
;	D/ <buffer space addr>
; Note that this extra-word crock is necessary just so it can be filled
; with ^C's to stop read loop and switch to next buffer.

BUFINI:	MOVEI AA,A
IFE SAILSW,DEVSIZ AA,
	 SKIPA AA,[DECBFL+1]	; Default buffer size is that for dsk.
	AOJLE AA,.-1	; Get size including extra wd.
	MOVEI T,1(D)	; Addr of wd 2 of 1st buffer.
	HRLI AA,T	; @AA is addr of 2nd wd of next buffer.
	SUBI D,(AA)	; Facilitate test for end of buffer space.
	HRLI T,400000
	MOVEM T,(C)	; Header -> a buffer, sign set.
	HRRM T,1(C)	; Make rh of bp -> buffer 1st wd.
	MOVSI T,440000	; Set up p-field of b.p.
	IORM T,1(C)
	HRRZ T,1(C)
	AOS 1(C)
	HRLI T,-3(AA)	; Data-area-size +1,,addr-of-2nd-wd
BUFIN1:	CAIGE D,-UTIBFL(T)	; Room for another after this buffer?
	 JRST BUFIN2	; No, wrap up.
	MOVEM T,@AA	; Yes, make next buffer -> this one,
	HRRI T,@AA	; Point to next one.
	JRST BUFIN1

BUFIN2:	ADDI D,1(AA)	; -> 2nd wd of 1st buffer.
	MOVEM T,(D)	; 1st buffer -> last, making ring.
	RET

] ;END IFN DECSW,
SUBTTL TNX - Input file Open, buffer input

IFN TNXSW,[

; OPNRD1 - Open File for Reading.  Old stuff assumed fnm in DNAM
;	using UTYIC channel, but new should furnish arguments:
;	F/ <filblk> to open

; Essentially just GTJFN and OPENF like OINIT does, with same
; sort of error handling, except that when reading from cmd line
; as opposed to .INSRT, just go back to get completely new command.
; (perhaps if typein is just CRLF, go to special TNX style cmd input?)

OPNRD1:	CAIN F,INFB	; Horrible kludge necessary because MIDAS main
			; level doesn't bother to explicitly close main
			; input file when pass 1 is done, and TNX barfs if
			; you try to re-open a JFN... sigh.
	 JRST [	SKIPN $FJFN(F)	; Main file.  Already opened it?
		 JRST .+1	; nope, get JFN & open normally.
		 IFE PMAPSW,[	; Already open. If not mapping, reset read ptr.
			SYSCAL SFPTR,[$FJFN(F) ? [0]][ERRCOD]
			 POPJ P,]
		JRST OPNRD2]	; and avoid attempt to re-open the JFN.
	SKIPN $FJFN(F)
	 JRST [	PUSHJ P,GETJFI		; No JFN, get one for input.
		 POPJ P,		; Could fail.
		JRST .+1]
	PUSH P,T		; Read access, full word input.
	SYSCAL OPENF,[$FJFN(F) ? [440000,,OF%RD]][ERRCOD]
	 JRST [POP P,T ? POPJ P,]	; Failure
	POP P,T
OPNRD2:	HRRZ A,$FJFN(F)
	MOVEM A,JFNCHS+UTYIC	; Indicate "channel" open with this JFN.
	PUSHJ P,JFNSTB		; Get actual names/version #.
	PUSHJ P,CVFSIX		; Put right stuff in $F6 entries.
	MOVE A,IUREDP		; Opened, set up buffer.
	MOVEM A,UREDP		; Initialize BP into buffer.
IFE PMAPSW,	JRST OPNRD3
IFN PMAPSW,	JRST OPNR50	; for PMAP hacking, lots of stuff to do.

	; Get a JFN for current FILBLK (in F) and stick it into $FJFN(F).
	; A should hold flags in LH to use in 1st wd of block.
	; GETJFI - sets usual flags for input
	; GETJFO - sets " " output
	; GETJFN - takes whatever A holds.

GETJFO:	SKIPA A,[GJ%FOU+GJ%NEW]	; If hacking output, ask for new version.
GETJFI:	 MOVSI A,(GJ%OLD)	; If hacking input, file must exist.
GETJFN:	PUSHJ P,TFMAP	; Stick filblk stuff into GTJFN scratch block.
	PUSH P,R1
	PUSH P,R2
	MOVEI R1,GTJBLK
	SETZ R2,
	GTJFN
	 JRST [	MOVEM R1,ERRCOD	; failure, save error code.
		JRST GETJF5]
	HRRM R1,$FJFN(F)	; Win, save JFN.
	AOS -2(P)
GETJF5:	POP P,R2	; Can't return in ACs cuz don't know what R1 etc are,
	POP P,R1	; and might clobber them here.
	POPJ P,

; TFMAP - Map Tenex filenames from filblk pointed to by F into
;	standard scratch block for long-form GTJFN.
;	A/ <flags>,,0	; flags will go into LH of .GJGEN.
;	Clobbers only A.

TFMAP:	HRR A,$FVERS(F)		; Put version # in RH
	SKIPE $FTEMP(F)		; If asking for temp file,
	 TLO A,(GJ%TMP)		; set appropriate flag.
	MOVEM A,GTJBLK+.GJGEN
IRP FROM,,[$FDEV,$FDIR,$FNAME,$FTYPE,$FPROT,$FACCT,$FJFN]TO,,[.GJDEV,.GJDIR,.GJNAM,.GJEXT,.GJPRO,.GJACT,.GJJFN]
	MOVE A,FROM(F)
	MOVEM A,GTJBLK+TO
TERMIN
	MOVE A,[.NULIO,,.NULIO]
	MOVEM A,GTJBLK+.GJSRC	; Don't hack I/O in gtjfn.
	POPJ P,

.VECTOR GTJBLK(10.)	; Need exactly this many wds for non-extended long call

IFE PMAPSW,[
	; EOFCH seen in input, check it here.

INCHR3:	HRRZ CH1,UREDP	; Get byte pointer
	CAME CH1,UTIBED	; End of block?
	 RET		; No, ^C in file.
	MOVE A,IUREDP
	MOVEM A,UREDP
	PUSH P,T
	SYSCAL SIN,[JFNCHS+UTYIC ? [444400,,UTIBUF] ? [-UTIBFL]][A ? A ? A]
	POP P,T
	ADDI A,UTIBUF+UTIBFL	; Get UTIBUF + <# bytes stored>
	CAIG A,UTIBUF	; If the sin didn't give us anything, we are at eof.
	 JRST RPAEOF
	HRRZM A,UTIBED	; Store rh (updated pointer) for eof check at inchr3
	MOVSI A,EOFCH_<18.-7>
	MOVEM A,@UTIBED	; Store a ^c after the data we read
	JRST RCHTRA	; Now try next character

] ; IFE PMAPSW
IFN PMAPSW,[	; New stuff for PMAP'ing input etc.

VBLK
IFNDEF NIBFPS,NIBFPS==10	; # of pages per buffer
PGBFL==NIBFPS*1000		; Length of a buffer in wds.
IFNDEF 1STBFP,1STBFP==500	; # of first page to start buffers at.

INBFPG:	1STBFP	; # of 1st buffer page (in our address space)
INFPAG:	0	; # of page in file corresponding to 1st page in buffer.
INPGCT:	0	; -# times to refill buffer with new pages.
INLPGS:	0	; # pages to slurp on last refill (instead of NIBFPS)
UTIBPE:	0	; BP to last byte of data in buffer (holding ^C)
UTIBPL:	0	; BP to last byte position in buffer area (constant)
UTIBPX:	0	; BP to last byte of data when last pages have been mapped.
INLCHR:	0	; Place to save char that ^C replaces.  If -1, no char.
;SOSSW:	0	; non-Z if hacking SOS line-number type file.
FBBYV:	0	; GTFDB dumps cruft in these two locs.
FBSIZ:	0	; e.g. this gets size of file in bytes.
PBLK

	; Wrap up open of an input file, by initializing all the cruft
	; above.
OPNR50:	SYSCAL GTFDB,[$FJFN(F) ? [2,,.FBBYV] ? MOVEI FBBYV]
	LDB C,[300600,,FBBYV]	; Get byte size of file
	CAIN C,
	 MOVEI C,36.	; If 0 use 36-bit bytes (full wds)
	MOVEI A,36.
	IDIVI A,(C)	; Get bytes per wd, ignore remainder.
	MOVE B,FBSIZ	; Now, with # bytes in file,
	EXCH A,B
	IDIVI A,(B)	; find <# in fil>/<# per wd> = # wds in file
	CAIE B,		; Also hack
	 ADDI A,1	; rounding up (gasp, wheeze, finally done.)
	IDIVI A,PGBFL	; Now get # times buffer will need slurping...
	ADDI A,1	; And another for the final slurp (even if it will be empty)
	MOVNM A,INPGCT	; Store -# slurps.
	MOVEI A,777(B)
	LSH A,-9.	; Find # pages last slurp really needs.
	MOVEM A,INLPGS	; and store away.
	HRLI B,010700
	MOVEM B,UTIBPX	; Store relative BP to last ch (when last pages mapped)
	HRRI B,PGBFL	; And relative BP to last char in whole buffer
	MOVEM B,UTIBPL	; Note UTIBPX and UTIBPL actually point to next wd
			; but this is fixed when abs addr is added in.

	MOVE A,INBFPG	; Find page # buffer starts at in core,
	LSH A,9.	; Get address, and
	SUBI A,1	; Subtract one, to fix UTIBPX, UTIBPL, and IUREDP.
	ADDM A,UTIBPX	; add into the BP's to make them absolute.
	ADDM A,UTIBPL
	HRLI A,010700	; And use for initial read pointer -
			; MUST be "canonical form", so that SEMIC hackery
	MOVEM A,IUREDP	; will work with weird way INCHR3 returns here.
	MOVNI A,NIBFPS	; Use this as initial file page #, so the ADDB in
	MOVEM A,INFPAG	; INCHR3 will do right thing to it.
	MOVE A,[440700,,[EOFCH_35]]
	MOVEM A,UREDP	; set up things so first RCH will instantly cause reload.
	ILDB B,A
	MOVEM A,UTIBPE
	SETOM INLCHR	; Mustn't forget that we don't have a stored char yet.
	JRST OPNRD4	; Finally done with PMAP init stuff.

	; Come here when hit ^C
INCHR3:	MOVE CH1,UREDP		; Get current read ptr
	CAME CH1,UTIBPE		; At end of buffer?
	 POPJ P,		; Nope, ^C in file, actual input.
	AOSLE CH1,INPGCT	; Aha, end of buffer.  Bump times refilled...
	 JRST INCH56		; and if no more refills, go handle EOF.
	MOVE A,IUREDP
	MOVEM A,UREDP
IFN A-1,PUSH P,R1
IFN A-2,PUSH P,R2
IFN A-3,PUSH P,R3
	MOVEI R1,NIBFPS		; Get # of input buffer pages
	ADDB R1,INFPAG		; and find current page in file to get
	HRL R1,$FJFN+INFB	; current input file's JFN
	MOVE R2,INBFPG		; and usual pointer to destination buffer page
	HRLI R2,.FHSLF		; Why the fuck doesn't 10X default this?!?!
	MOVEI R3,NIBFPS		; Set # pages to slurp up
	JUMPN CH1,INCH51	; But if this is last slurp,
	SKIPG R3,INLPGS		; Use pre-calculated # to avoid non-ex pages.
	 JRST INCH55		; No pages in last slurp!  Avoid new PMAP.

INCH51:	TLO R3,(PM%CNT+PM%RD+PM%CPY)	; Read access, copy-on-write.
INCH52:	PMAP		; Gobble gobble
	TLNN FF,FL20X	; If on 20X, that's all.
	 JRST [	HRRI R3,-1(R3)	; Else, on 10X, must iterate manually.
		TRNE R3,400000	; See if became "negative".
		 JRST INCH53	; Yep, done with manual iteration.
		ADDI R2,1	; Nope, bump page #'s.
		AOJA R1,INCH52]
INCH53:
IFN A-3,POP P,R3
IFN A-2,POP P,R2
IFN A-1,POP P,R1
	CAIE CH1,		; Was this the last slurp?
	 SKIPA CH1,UTIBPL	; no, use BP to Last char at end of buffer.
	  MOVE CH1,UTIBPX	; yes, need BP to last char in last page.
IFN 0,[	SKIPE SOSSW		; If hacking line number lossage,
	 JRST [	MOVE A,(CH1)	; must beware of getting wiped, so have to
		TRNE A,1	; check here, and if depositing EOFCH in #,
		 HRLI CH1,350700	; then move the EOFCH to beg of word!
		JRST .+1]
]
	LDB A,CH1		; Replace last char of buffer's data
	MOVEI CH2,EOFCH
	DPB CH2,CH1		; with the EOF char.
	MOVEM CH1,UTIBPE	; Remember ptr to end of data,
	EXCH A,INLCHR		; and save char for then, returning whatever
	JUMPL A,RCHTRA		; was the last char of last bufferfull.
				; (may be -1, in which case RCHTRA tries again)

	; Jump here to return a new char in A, something like
	; RCHTRA without all the fuss.
INCHR7:	POP P,CH1		; Get return addr
	ANDI CH1,-1
	CAIE CH1,RREOF+1
	 JRST -2(CH1)		; Note -2 not -3 as in RCHTRA!
	JRST (CH1)		; Special hack since -2 loses for RREOF.
				; Perhaps someday it will win.

INCH55:		; Here when doing last slurp and no pages to slurp.
IFN A-3,POP P,R3
IFN A-2,POP P,R2
IFN A-1,POP P,R1
INCH56:	SKIPGE A,INLCHR		; No more refills, see if last char left
	 JRST RPAEOF		; No?  All done, true EOF.
	SETOM INLCHR		; Almost, one last char.
	MOVE CH1,UREDP		; Must bump ptr back one char, so next read
	ADD CH1,[070000,,]	; will also stop.
	CAIG CH1,
	 SUB CH1,[430000,,1]
	MOVEM CH1,UREDP
	JRST INCHR7		; Return very last char in A.

] ; IFN PMAPSW

] ;END IFN TNXSW
ifn 0,[ ; turn off but keep around for a while.
SUBTTL old .INSRT Processing

;	.INSRT <filedescription><CR>	; Insert file here
; TTY: => ok, reads line at a time, rubout allowed within line
; Pushes macro expansion, other .INSRT's
; In filedescription, ^R => reset file name counter [?!? - KLH]
; If device is "@:", always ask for translation.

A.INSR:	NOVAL
	MOVE A,[ISFB,,FB]	; Default names are those of spec'd input file
	BLT A,FB+L$FBLK-1	; Zap them into scratch filblk.
	MOVEI F,FB		; And point at it.
	MOVE A,FSDSK
	MOVE B,FSTTY		; Compare "TTY" with
	CAMN B,$FDEV(F)		; device name, and if identical,
	 MOVEM A,$FDEV(F)	; default to DSK.
IFE ITSSW,MOVE A,FSMID		; Always set default extension to "MID" or ">"
IFN ITSSW,MOVE A,FSGRTN
	MOVEM A,$FEXT(F)

	TLO FF,FLUNRD
A.IN1:	PUSHJ P,RFD		; Read file description
	MOVE A,$FDEV(F)		; Get specified device name
	CAME A,FSATSN		; Atsign?
	 PUSHJ P,A.ITRY		; No, try opening file

	; If return, open failed.
	MOVE A,$F6DEV(F)
	AOJE A,A.INT1		; Already trying to set up table entry
	SKIPA F,[MAXIND,,TBLOFS]	; Atsign, or fnf, search table

A.IN2:	 SUBI F,-L$FBLK	; Loop point searching table, increment to next entry, count down LH
	CAMN F,INDDP	; Compare with pointer to top of table
	 JRST A.IN3	; Agree => this file not in table

;	MOVEI A,-TBLOFS(F)	; Get index relative to table base.
; MIDAS complains "illegal use of relocation" when try to use above addr, so must use next 2 instructions instead - barf barf
	MOVEI A,(F)
	SUBI A,TBLOFS

	MOVSI B,-L$FBLK		; And index into FB.
	MOVE T,TBLSFS(A)	; Get specification name this entry
A.IN25:	CAMN T,FB(B)		; Compare with that just specified
	 AOBJN B,[AOJA A,.-2]	; Check all names this entry
IFE TNXSW, JUMPL B,A.IN2
IFN TNXSW,[JUMPL B,[	MOVEI C,(B)
			CAIN C,$FJFN	; One item of entry didn7t match, was it JFN?
			 JRST A.IN25	; Yes, ignore it and continue.
			JRST A.IN2]	; Sigh, was something else, entry doesn't match.
	]

		; File is in table
	MOVSI A,(F)		; Move description from TBLOFS to FB.
	HRRI A,FB
	BLT A,FB+L$FBLK-1
IFN TNXSW, SETZM FB+$FJFN	; Since re-opening, must zap previous JFN.
	PUSHJ P,A.ITRY		; Try opening file

	; If return, open failed.
	MOVSI A,TBLSFS-TBLOFS(F)	; Set up LH(BLT pointer),
	HRRI A,FB
	BLT A,FB+L$FBLK-1	; Unmap to original names(TBLSFS to FB)
	PUSHJ P,TYPFB		; Type out specified names
	TYPE " -> "		; Type out pointer
	MOVSI A,(F)		; Copy translation (TBLOFS entry) back to FB.
	HRRI A,FB
	BLT A,FB+L$FBLK-1
	SETOM $F6DEV(F)		; "half-kill" entry in TBLOFS

A.INT1:	PUSH P,F
	MOVEI F,FB
	PUSHJ P,IOPNER	; Open lost, type out cruft
	POP P,F
	TYPE "Use what filename instead? "
A.INT2:	PUSHJ P,GTYIP	; Prepare to read one line from tty
	JRST A.IN1	; Try again with what he types in

		; File not in table, try to add a translation for it.

A.IN3:	TLNN F,-1	; More room for another entry in table?
	 ETF [ASCIZ /Too many @: files/]
	MOVEI A,TBLSFS-TBLOFS(F)	; Copy FB into TBLSFS (specified name)
	HRLI A,FB
	BLT A,TBLSFS-TBLOFS+L$FBLK-1(F)
	SETOM $F6DEV(F)		; Document fact that entry has only key, not translation
	MOVNI A,-L$FBLK
	ADDM A,INDDP		; Update pointer into table
	MOVE A,FB+$FDEV		; Get specified device name
	CAME A,FSATSN		; Atsign?
	 JRST A.INT1		; No, type out garbage and try again, reading from tty
	MOVE A,ISFB+$FDEV	; Yes, clobber from input device name
	MOVEM A,FB+$FDEV
	JRST A.INT2
;TRY OPENING INPUT FILE FOR .INSRT, RETURN IF UNSUCCESSFUL

A.ITRY:	MOVE A,FB+$FDEV		; Get specified device name
	CAMN A,FSTTY		; TTY?
	 JRST A.ITRT		; Yes, treat special
	TLO FF,FLUNRD
	PUSHJ P,IPUSH		; Save current status
	PUSH P,F		; save what F points at
	MOVEI F,FB
	PUSHJ P,OPNRD1
	 JRST [POP P,F ? JRST IPOPL]	; Lose, pop and return
	POP P,F
	MOVE B,[FB,,INFB]	; Kludge for time being - if win,
	BLT B,INFB+L$FBLK-1	; Copy all stuff into INFB.
IFN ITSSW,CALL SETWH2
	MOVE B,ITTYP
	MOVEI A,-2-TYPDEL(B)	;
	HRLI A,IFNM1
	BLT A,-TYPDEL(B)	; Introduce hysteresis so .INSRT'ing file can reference .IFNM1, .IFNM2
IFN CREFSW,[
	SKIPE CRFONP	; If creffing, output push-file block.
	 PUSHJ P,CRFPSH	; (pop-file block output at ipop)
]
A.ITR2:
	MOVE A,$F6DEV(F)	; Push successful, now check to see if table entry should be finished
	AOJN A,ASSEM1
	MOVEI A,(F)	; Move FB into TBLOFS as translation entry.
	HRLI A,FB
	BLT A,L$FBLK-1(F)
	JRST ASSEM1	; Now assemble from file (ASSEM1 clobbers pdl)

		; .INSRT TTY:

A.ITRT:	PUSHJ P,GTYIPA	; Read from tty, don't quit until .INEOF
	JRST A.ITR2	; Fall back in (doesn't touch .IFNM1, .IFNM2)
] ; end IFN 0
SUBTTL .INSRT Processing

;	.INSRT <filedescription><CR>	; Insert file here
; TTY: => ok, reads line at a time, rubout allowed within line
; Pushes macro expansion, other .INSRT's
; If device is "@:", always ask for translation.

A.INSR:	NOVAL

	; First set up defaults for parsing filename.
	BLTM L$FBLK,ISFB,FB 	; Default names are those of spec'd input file,
	MOVEI F,FB		; stuffed into scratch FB.
	MOVE A,FSDSK
	MOVE B,FSTTY		; Compare "TTY" with
	CAMN B,$FDEV(F)		; device name, and if identical,
	 MOVEM A,$FDEV(F)	; default to DSK.
IFE ITSSW,MOVE A,FSMID		; Always set default extension to "MID" or ">"
IFN ITSSW,MOVE A,FSGRTN
	MOVEM A,$FEXT(F)

	TLO FF,FLUNRD
	PUSHJ P,RFD		; Read file description from current input.
	MOVE A,$FDEV(F)		; Get specified device name
	CAMN A,FSATSN		; Atsign?
	 JRST A.IN50		; If so, check out translation right away.

A.IN2:	CAMN A,FSTTY		; TTY?  Must handle specially.
	 JRST [	PUSHJ P,GTYIPA	; Set up to read until .INEOF or EOF char.
		JRST ASSEM1]	; And don't do anything to .IFNM1/2, etc.
	PUSHJ P,IPUSH		; File, push the world.
	PUSHJ P,OPNRD1		; Try opening file.
	 JRST [	PUSHJ P,IPOPL	; Sigh, failed, pop world back and go
		JRST A.IN50]	; try translation entries or TTY input.
				; Always jumps back to A.IN2.

	; Come here when input file successfully opened.  Clean up etc.
	BLTM L$FBLK,(F),INFB	; Move current filespec to INFB,
IFN ITSSW,CALL SETWH2
	MOVE B,ITTYP
	BLTM 3,IFNM1,-2-TYPDEL(B)	; Copy new .IFNM1, .IFNM2 onto stack,
				; to clobber .IFNM1/2 for previous file, so
			; that .IFNM1/2 etc refers to last file .INSRT'd by
		; current file (or current file if none .INSRT'd yet)
IFN CREFSW,[
	SKIPE CRFONP	; If creffing, output a push-file block.
	 PUSHJ P,CRFPSH	; (pop-file block is output at IPOP)
	]

	JRST ASSEM1		; and jump off to smash things to toplevel.
	; Come here when open attempt fails or @: device specified.
A.IN50:	CAIE F,FB		; Tried translations yet?
	 JRST A.IN60		; Yes, skip table hacking and go get fnm from TTY.
	
	; First open attempt, so OK to search translation table.
	SKIPA D,[MAXIND,,TBLOFS]	; Load up aobjn-style index to transl table
A.IN52:	 SUBI D,-L$FBLK	; Loop point for searching table - increment to next entry, count down LH
	CAMN D,INDDP	; Compare with pointer to top of table
	 JRST A.IN60	; Agree => this file not in table, get from TTY.

	MOVEI A,(D)		; Get scratch index into tables,
	HRLI A,-L$FBLK		; making AOBJN of it,
	MOVEI B,(F)		; and get index into current FB.
A.IN54:	MOVE C,TBLSFS-TBLOFS(A)	; Get a specification name for this entry
IFN TNXSW,CAIE B,$FJFN(F)	; (ignoring the JFN item, for TENEX)
	CAMN C,(B)		; Compare name with that of failed filblk.
	 AOBJN A,[AOJA B,A.IN54]	; Check all names this entry
	JUMPL A,A.IN52		; If not found, try next entry.

	; File is in table, try opening it using TBLOFS description.
	MOVE F,D		; Replace old F by ptr to winning TBLOFS entry.
IFN TNXSW, SETZM $FJFN(F)	; Since re-opening, must zap any previous JFN.
	JRST A.IN2		; Jump off to try opening.

	; Come here when open failed and no matching transl entry.
	; Must set up to gobble down a translation from TTY...
A.IN60:	TYPE "Error in .INSRT; "
	CAIE F,FB		; Were we trying to open a translated entry?
	 JRST [	PUSHJ P,TYPFB	; Yes, so print out appropriate info
		TYPE " -> "	; to show translated stuff.
		JRST A.IN70]

	; First time, no translation entry exists, make one.
	MOVE A,INDDP		; Get current pointer to top of tables
	TLNN A,-1		; Room for more?
	 JRST A.IN70		; Nope, can't remember transl, but get right fnm anyway.
	MOVE F,A		; Yep, use it as pointer to table entry to use.
	SUBI A,-L$FBLK		; and get new table-top pointer with clever
	MOVEM A,INDDP		; SOS of LH and ADDI to RH.
	BLTM L$FBLK,FB,(F)	; Move FB contents to both TBLOFS,
	BLTM L$FBLK,FB,TBLSFS-TBLOFS(F)	; and TBLSFS.

A.IN70:		; Print out filename F points to, & err msg.
IFN TNXSW,[
	PUSHJ P,OPNER1
	PUSHJ P,RDJFNI	; On 10X, get new filename this way.
	]
IFN ITSSW\DECSW,[		; Elsewhere do it painful way.
	PUSHJ P,IOPNER
	TYPE "Use what filename instead? "
	PUSHJ P,GTYIP		; Setup to read 1 line from TTY,
	PUSHJ P,RFD		; and do it, parsing filename.
]
	JRST A.IN2		; now go try opening it.
SUBTTL Misc. .INSRT-related things

		; .INEOF - EOF pseudo

A.IEF2:	PUSHJ P,PMACP	; Loop point, pop entry off macro pdl
A.INEO:	TLNE FF,FLMAC	; Inputting from macro?
	 JRST A.IEF2	; Yes, pop it off
	PUSH P,CMACCR	; Back to inputting from file or tty, cause return to maccr
	MOVE B,ITTYP	; Get pdl pointer
	POPJ B,		; Return to pop routine


	; Call from ERRH; type input file's names if changed since last err msg.
ERRTFL:	MOVE C,INFCUR
	EXCH C,INFERR	; Say last error msg in this file.
	CAMN C,INFERR	; If prev. msg was in other file,
	 POPJ P,
	PUSH P,F
	MOVEI F,INFB	; Point to current input file,
	PUSHJ P,TYPFB	; and type out its filename.
	POP P,F
	PJRST CRRERR
SUBTTL COMMON IO PDL routines for input. (.INSRT support)

;IO PDL ROUTINES FOR INPUT FILE
; Push the input file

IPUSH:	AOSN CMEOF	; Want to pop out of tty? (^C  typed in)
	 CALL POPTT	; Yes, do now before forget.

IFE PMAPSW,[
	MOVE D,UREDP	; Get input byte pointer
IFN ITSSW\TNXSW,[
IFN ITSSW, .IOPUS UTYIC,
IFN TNXSW, MOVEI A,UTYIC ? PUSHJ P,$IOPUSH
	TLNN D,760000		; At end of word?
	 ADD D,[430000,,1]	; Yes, make it point to beginning of next word
	MOVEM D,UREDP
	MOVNI A,-2(D)
	ADD A,UTIBED	; Get # wds we'll need in MACTAB.
	HLR D,UTIBED	; Remember whether EOF on last .IOT.
	HRRZS UTIBED	; Now clear out left half for following
]
IFN DECSW,[
	AOS A,UTICHN	; Do ".IOPUSH" - use next channel.
	LSH A,27
	ADD A,[WAIT-<0 1,>]	; Construct a WAIT uuo for the current input channel.
	MOVE C,RCHMOD		; We mustn't copy the buffers while I/O is going on.
	CAMN A,[WAIT UTYIC,]	; But:  if we are currently in the top-level input file
	 CAIE C,3		; And it is device TTY:, this channel was never opened.
	  XCT A			; Don't move buffers while io going on!
	MOVEI A,UTIBFL+2	; Assume must save all buffer space.
]
	PUSH P,A
	ADD A,FREPTB
	ANDI A,-1
	PUSH P,A
	CAML A,MACTND	; No room in MACTAB => gc it.
	 CALL GCA1
	REST A
	CAML A,MACTND	; Did the GC win?
	 PUSHJ P,GCCORQ	; NO!!  Try to win somehow
	MOVEI A,370
	CALL PUTREL	; Indicate start of saved buffer.
	REST A
	AOS B,FREPTB
	SUBI A,1
	MOVE C,ITTYP	; Get addr of tty pdl wd that'll point to saved buffer.
	ADDI C,1
	HRRZM C,(B)	; Store in rh of 1st wd,
	MOVEI C,(B)	; Remember addr of saved buffer to push on ttypdl.
	HRLM A,(B)	; Put length in lh.
	AOS B
IFN ITSSW\TNXSW,HRL B,UREDP	; LH <- addr of 1st wd to save.
IFN DECSW,HRLI B,UTIBUF
	ADDI A,-2(B)	; Addr of last wd to blt into.
	BLT B,(A)
	HRLI A,041000
	MOVEM A,FREPTB	; Make free bp -> last byte just used.
	SUB A,MACTAD
	ANDI A,-1
	LSH A,2
	ADDI A,4	; Get char addr of next free byte.
	MOVEM A,FREEPT
]
IFN PMAPSW, CALL IOBPUS

	MOVE B,ITTYP	; Get local version of iopdl
IPSHP:
IFE PMAPSW, PUSH B,C		; Push -> saved buffer (GC will relocate)
IFN DECSW,PUSH B,UTIBED ? PUSH B,UTIHDR
REPEAT L$FBLK, PUSH B,INFB+.RPCNT	; Save names of input file.
	PUSH B,INFCUR		; Save number of input file.
IFE PMAPSW, PUSH B,D	; Lh=lh(old uredp), rh=lh(old utibed) (or just UREDP)
IFN PMAPSW, INSIRP PUSH B,[INFPAG INPGCT INLPGS UTIBPE UTIBPL UTIBPX INLCHR UREDP IUREDP ]

		; Following three must be last pushed
	INSIRP PUSH B,[IFNM1 IFNM2 IFVRS]	; Clobbered on pdl if .open successful
INPDEL==.-IPSHP		; Length of each entry on pdl

	MOVE A,FREEPT	; W must use same gc convention as putrel;
	CAML A,MACHI	; Namely, gc after using up the last byte.
	 CALL GCA1
	MOVEI A,0	; => input from file
	MOVEM B,ITTYP	; Store back updated pointer
	JSP B,PUSHTT	; Save stuff, address modify and return


	; Pop into the input file
IPOP:
IFN CREFSW,[ MOVEI A,2	; If creffing, output pop-file block.
	SKIPE CRFONP
	 PUSHJ P,CRFOUT]
IPOPL:	PUSHJ P,POPTT	; Come here if .INSRT's open failed.
	PUSH P,C
	MOVE B,ITTYP	; Get pointer
	INSIRP POP B,[IFVRS IFNM2 IFNM1]	; Pop stuff
IFE PMAPSW, POP B,A		; Pop off UREDP (or halves thereof)
IFN PMAPSW, INSIRP POP B,[ IUREDP UREDP INLCHR UTIBPX UTIBPL UTIBPE INLPGS INPGCT INFPAG]
	POP B,INFCUR
REPEAT L$FBLK,POP B,INFB+L$FBLK-1-.RPCNT
IFN DECSW,[
	POP B,C
	PUSH P,C		; Old UTIHDR
	POP B,UTIBED
]
IFE PMAPSW, POP B,C
	MOVEM B,ITTYP	; Save updated pdl pointer.
IFE PMAPSW,[
	HLRZ B,(C)	; Get length of saved buffer,
IFN ITSSW\TNXSW,[
	PUSH P,A
IFN ITSSW, CALL SETWH2 ? .IOPOP UTYIC,
IFN TNXSW, MOVEI A,UTYIC ? CALL $IOPOP
	REST A
	MOVEI AA,UTIBUF-1(B)	; Get addr of 1st wd won't blt into in utibuf,
	HRLI AA,(A)		; Get saved lh of utibed,
	MOVEM AA,UTIBED
	HRRI A,UTIBUF		; Make A -> 1st wd in buffer,
]
IFN DECSW,[
	MOVE AA,UTICHN
	LSH AA,27
	IOR AA,[RELEAS]
	XCT AA		; This code equivalent to .IOPOP.
	SOS UTICHN
	REST UTIHDR
]
	MOVEM A,UREDP
	MOVSI A,EOFCH_13
	MOVEM A,@UTIBED	; Put EOF char after buffer.
	MOVSI A,1(C)	; Get addr of 1st data wd of saved buffer,
	HRRI A,UTIBUF
	CAIE B,1
	 BLT A,UTIBUF-2(B)
	HLLZS (C)	; Tell GC to reclaim saved buffer.
] ;IFE PMAPSW

IFN PMAPSW, CALL IOBPOP

POPCJ:	REST C
	RET
;SAVE INTERNAL POINTERS CONCERNING INPUT MODE

TYPDEL==2		; Number of words in relevant pdl entry

PUSHTT:	PUSH P,A
	PUSH P,F
	AOSN CMEOF	; If supposed to pop out of tty soon,
	 CALL POPTT	; Do it now before cmeof clobbered.
	MOVE F,ITTYP	; Get relevant pdl pointer
	MOVEI A,0
	EXCH A,CLNN	; Set up new line number
	HRL A,CPGN	; Save current page number
	SETZM CPGN	; Now re-initialize
	SKIPGE CRFILE	; Save cref-all-on-one-line flag.
	TLO A,400000
	PUSH F,A	; Save cpgn,,clnn
	MOVE A,-1(P)	; Retrieve new mode
	PUSHJ P,PSHLMB	; Save limbo1 and set up instructions for new mode
IFN ITSSW,[
	CALL SETWH2
	.SUSET [.SWHO3,,A]
]
	MOVEM F,ITTYP	; Store back updated pointer
	JRST POPFAJ

		; Restore internal pointers concerning input mode

POPTT:	PUSH P,A
	PUSH P,F
	MOVE F,ITTYP	; Get pdl pointer
	PUSHJ P,POPLMB	; Pop into limbo1, set up new mode
	POP F,A		; Get cpgn,,clnn
	SETZM CRFILE	; Restore all-on-one-line flag.
	TLZE A,400000
	 SETOM CRFILE
	HLRZM A,CPGN
	HRRZM A,CLNN
IFN ITSSW,[
	CALL SETWH2
	ADD A,CPGN
	.SUSET [.SWHO3,,A]
]
	MOVEM F,ITTYP	; Store back updated pointer
	JRST POPFAJ

IFN ITSSW,[
SETWH2:	MOVE A,RCHMOD
	CAIL A,2
	 SKIPA A,[SIXBIT /TTY:/]
	  MOVE A,INFB+$F6FN1
	.SUSET [.SWHO2,,A]
	MOVE A,A.PASS
	LSH A,30
	ADD A,[SIXBIT /P0/+1]
	RET
]
SUBTTL Storage for IO PDL stuff

	 ; IO PDL storage stuff

VBLK
TYPDLS==TYPDLC*TYPDEL+INPDEL*MX.INS
		; "tty pdl", stores information about current input mode
		; (similar to macro pdl but not garbage collected)

ITTYP:	-TYPDLS-1,,TTYPDL	; Pdl pointer (typdel=length of each entry)
TTYPDL:	NEDCHK		; Actual pdl: initial entry to overpop routine
	BLOCK TYPDLS	; Pdl proper
PBLK
SUBTTL TNX - IO PDL Routines (IOPDLC, $IOPUSH, $IOPOP)

IFN TNXSW,[

IFN PMAPSW,[
	; Push IO buffer & channel...
IOBPUS:	PUSH P,A
	MOVEI A,UTYIC
	CALL $IOPUSH
	MOVEI A,NIBFPS	; Point at next set of buffer pages.
	ADDM A,INBFPG
	POP P,A
	POPJ P,

	; Pop IO buffer & channel...
IOBPOP:	PUSH P,A
	MOVE A,INBFPG
	HRLI A,NIBFPS
	CALL DELPGS	; flush buffer pages.
	MOVNI A,NIBFPS
	ADDM A,INBFPG	; point down at previous set of buffer pages...
	MOVEI A,UTYIC
	CALL $IOPOP
	POP P,A
	POPJ P,

; DELPGS - Take arg in A as <# pgs>,,<page #> and flush these pages.

DELPGS:	PUSH P,A
	PUSH P,B
	HLRZ B,A
	HRLI A,.FHSLF	; <fork>,,<page #>
	TLO B,(PM%CNT)
	PUSH P,T
DELPG2:	SYSCAL PMAP,[[-1] ? A ? B][A ? A ? B]	; Free up buffer pages.
	TLNN FF,FL20X	; If on 20X, that's all.
	 JRST [	HRRI B,-1(B)	; Else, on 10X, must iterate manually.
		TRNE B,400000	; See if became "negative".
		 JRST .+1	; Yep, done with manual iteration.
		AOJA A,DELPG2]	; Nope, bump page #'s.
	POP P,T
	POP P,B
	POP P,A
	POPJ P,
] ;IFN PMAPSW

; IOPDLC - Clear IOPDL stack, close all channels on it.
;	Clobbers no ACs

; for 10x, need to CLOSF and release each JFN on IOPDL stack.

IOPDLC:	PUSH P,R1
IFE R1-A,.ERR IOPDLC WONT WORK WITH A=1
IFN PMAPSW,[
	MOVEI R1,1STBFP	; Reset to point at 1st page of buffer space.
	MOVEM R1,INBFPG
]
	EXCH A,IOPDLP
	JRST IOPDC3
IOPDC2:	MOVE R1,(A)
	CAME R1,ISFB+$FJFN	; Dont close main input file
	 CLOSF
	 JFCL
	SUB A,[1,,1]
IOPDC3:	CAMLE A,[-LIOPDL,,$IOPDL-1]
	 JRST IOPDC2
	EXCH A,IOPDLP
	POP P,R1
	POPJ P,

; $IOPUSH - Push I/O channel in A onto $IOPDL stack.
;	Clobbers no ACs
; for 10X this means storing JFN on stack and clearing JFNCHS table entry.

$IOPUSH:EXCH B,IOPDLP	; Get stack pointer
	PUSH B,JFNCHS(A)	; save JFN for channel
	EXCH B,IOPDLP
	SETZM JFNCHS(A)	; Zap entry in channel table to make it look gone
	POPJ P,

; $IOPOP - Pops channel off $IOPDL into channel # in A.
;	Clobbers no ACs

; for 10X just pop $IOPDL into JFNCHS, must close and release old JFN tho.

$IOPOP:	PUSH P,T
	SYSCAL CLOSF,[JFNCHS(A)]
	 JFCL
	POP P,T
	EXCH B,IOPDLP	; Get stack ptr
	POP B,JFNCHS(A)
	EXCH B,IOPDLP
	POPJ P,

VBLK
JFNCHS:	BLOCK 20	; Channel table index, JFNCHS(ch) gets JFN for chan.
			; (zero if none)
LIOPDL==8.	; Length of IO PDL
IOPDLP:	-LIOPDL,,$IOPDL-1
$IOPDL:	BLOCK LIOPDL
PBLK
] ; IFN TNXSW
SUBTTL DEC - IO PDL Routines (IOPDLC)

IFN DECSW,[

; IOPDLC - Simulate ITS .IOPDL call.  Flushes all channels from
;	UTICHN downwards to UTYIC.  Actually not a simulation but something
;	that works in the particular situation for which MIDAS uses .IOPDL.

IOPDLC:	MOVEI A,UTYIC
	EXCH A,UTICHN	; Set input chnl num. to lowest.
	LSH A,27
	IOR A,[RELEAS]	; Set up to releas the highest in use first.
IOPDL1:	XCT A		; Releas one input channel,
	CAMN A,[RELEAS UTYIC,]
	 RET		; All done.
	SUB A,[0 1,]
	JRST IOPDL1	; Releas the next one down.
] ;IFN DECSW
SUBTTL COMMON TTY input routines & variables

VBLK
CMBUF:	BLOCK CMBFL	; Typein buffer (also used as JCL buffer)
CMPTR:	0	; Byte pointer to CMBUF.
CMEOF:	0	; -1 => POPTT instead of reloading after this bufferfull.
TTYOPF:	0	; -1 => the TTY is already open.
LINEL:	0	; Width of TTY (may be 1,, meaning assume infinite).
A.TTYFLG:	; Value of .TTYFLG pseudo - another label for TTYFLG.
TTYFLG:	0	; TTY typeout permitted iff >= 0.
WSWCNT:	0	; The number of W-switches in the last cmd string.
TTYBRF:	0	; -1 => ^H break has been requested but not yet done.
PBLK
	; Cause input from tty (main routines)

GTYIPA:	SETZM A.TTYF		; Push to tty, don't stop at cr.

IFN ITSSW,   TYPECR "TTY: .INSRTed, end input with ^C"
IFN DECSW\TNXSW,[
  IFE SAILSW,TYPECR "TTY: .INSRTed, end input with ^Z"
  IFN SAILSW,TYPECR "TTY: .INSRTed, end input with CTL-META-LF"
  ]

GTYIP1:	SKIPA A,[3]
GTYIP:	 MOVEI A,2	; Input from tty, stop after 1 line.
	SETZM CMPTR	; Force reload on 1st read.
	JSP B,PUSHTT	; Set up variables and return
GTYIPR:	SETZM CMPTR	; Return on .ineof or cr
	JRST POPTT

	; Call here from ASSEM1 loop when a ^H interrupt is detected.
TTYBRK:	SETZM A.TTYF
	ETR [ASCIZ/^H - break /]	; Type filename, page and line #.
	SKIPE ASMOUT
	 TYPECR "within a <>, () or []"
	JRST GTYIPA

	; RCHSET routines for reading from TTY
	; RCHMOD=3 => don't quit on CR
	;	 2 => quit on CR.
RCHTRC:
RCHARC:	TLO FF,FLTTY	; Set flag
	JSP A,CPOPJ
RCHAC1:	REPEAT 2,[	; RCH2, RR1
	ILDB A,CMPTR	; Get char
	CAIN A,0	; End of string marked with 0
	 PUSHJ P,TYRLDR	; Reload, jump back for next char
]
	GOHALT		; RRL1
IFN .-RCHAC1-RCHPSN,.ERR RCHAC1 LOSES.
	ILDB A,CMPTR	; SEMIC
	CAIN A,15
	 JRST SEMICR
	JUMPN A,SEMIC
	PUSHJ P,TYRLD
	JRST SEMIC
TYRLD:	MOVEI A,3	; Return after the call, not before.
	ADDM A,(P)

	; TYRLDR - Read in string.
	;	 Reload buffer if ran out in call to RCH.

TYRLDR:	AOSN CMEOF	; EOF detected after last reload =>
	 JRST RPAEOF	; Pop out of tty.
	PUSH P,A
	PUSH P,B
	MOVE B,RCHMOD
	PUSH P,F
	PUSH P,A.TTYF	; If chars rubbed out they should be printed.
	SETZM A.TTYF
IFN TNXSW,SYSCAL DTI,[[.TICCV]]	; Disable ^V as an interrupt character
	MOVE F,[10700,,CMBUF-1]	; Initial byte pointer to buffer
	MOVEM F,CMPTR	; Store as byte pointer for read
TYRLD2:	PUSHJ P,TYI	; Get character
IFN TNXSW,[
	CAMN F,CMPTR	; at beg of line?
	 CAIE A,^J	; and char is LF?
	  CAIA
	   JRST TYRLD2	; If so then ignore it completely.
]
	CAIN A,177	; Rubout?
	 JRST TYRLD3	; Yes
	CAIE A,^C
	 CAIN A,^Z
	  JRST TYRLD7	; ^C, ^Z => EOF.  Ought to be EOFCH for consistency?
	CAIN A,^U
	 JRST TYRLD5	; Rub out all
	CAIE B,2	; For .TTYMAC handling, convert lower case to upper.
	 JRST TYRLD6
	CAIL A,"A+40
	 CAILE A,"Z+40
	  CAIA
	   SUBI A,40
TYRLD6:	CAME F,[010700,,CMBUF+CMBFL-2]
	 IDPB A,F	; Store character in buffer unless buffer nearly full.
	CAIE A,^M	; CR?
	 JRST TYRLD2	; No, go back for next
	CAIN B,2	; .TTYMAC (mode 2) => CR ends input, so fake EOF.
	 SETOM CMEOF
	MOVEI A,^J	; Follow the CR with a LF.
	IDPB A,F
	PUSH P,F		; Output the entire line to the error file
	MOVE F,[10700,,CMBUF-1]
TYRLD8:	CAMN F,(P)
	 JRST TYRLD9
	ILDB A,F
	CAIN A,^M	; If line was ended by a ^C or ^Z, put that in error
	 SKIPL CMEOF	; file, which needs hair since that char is not
	  JRST TYRLD0	; In the string we stored.
	MOVEI A,"^
	CALL ERRCHR
IFN ITSSW,MOVEI A,"C
IFN DECSW\TNXSW,MOVEI A,"Z
	CALL ERRCHR
	LDB A,F
TYRLD0:	CALL ERRCHR
	JRST TYRLD8

TYRLD9:	REST F
	MOVEI A,0
	IDPB A,F	; Mark end of string
	IDPB A,F
IFN TNXSW,SYSCAL ATI,[[.TICCV,,.IC.CV]]	; Turn back on ^V
	REST A.TTYF
	REST F
	REST B
	REST A
	JRST RCHTRA

TYRLD7:	SETOM CMEOF	; ^C, ^Z force EOF,
	CALL TYRLCR	; After turning into ^M.
	MOVEI A,^M
	JRST TYRLD6

TYRLCR:	MOVEI A,^M
	CALL TYOX
	MOVEI A,^J
	JRST TYOX

TYRLD3:	CAMN F,[10700,,CMBUF-1]	; Rubout, beginning of buffer?
	 JRST TYRLD4		; Yes
	LDB A,F			; Get last character in buffer
	CALL TYOX		; Type it out, don't write in error file.
	ADD F,[70000,,]		; Decrement pointer
	JUMPGE F,TYRLD2		; Jump if valid
	SUB F,[430000,,1]	; Was 440700,,something, back it up
	JRST TYRLD2

TYRLD5:	MOVE F,[10700,,CMBUF-1]	; ^U, back to beginning of line
TYRLD4:	PUSHJ P,TYRLCR	; Rubout when at beginning of buffer, type CR
	JRST TYRLD2
SUBTTL ITS - TTY routines (TYOX, TYI, TTYINI) and JCLINI.

IFN ITSSW,[

	; TYOX - Type out char in A
TYOX:	SKIPN TTYOPF
	 CALL TTYINI
	.IOT TYOC,A
	POPJ P,

	; TYI - Get (just typed in) char in A
TYI:	SKIPN TTYOPF
	 CALL TTYINI	; Open the tty if not already done.
	.IOT TYIC,A
	ANDI A,-1	; Non-tty devices can return -1,,3.
	JUMPE A,TYI
	CAIN A,^L	; This must be assuming that ^L clears screen?
	 JRST TYI
	POPJ P,

		; Initialize tty
TTYINI:	PUSH P,A
	.OPEN TYIC,[.UAI,,'TTY]	; Input
	 .LOSE
	.OPEN TYOC,[%TJDIS+.UAO,,'TTY]	; Display mode output
	 .LOSE
	SYSCAL CNSGET,[1000,,TYOC ? 2000,,A ? 2000,,A]
	 MOVSI A,1	; TTY: is translated to something else => assume infinite linel
	MOVEM A,LINEL	; Else linel gets width of tty.
	SETOM TTYOPF	; Say the tty is now open.
	JRST POPAJ

JCLINI:	SETZM CMPTR
	.SUSET [.ROPTIO,,A]
	TLNN A,%OPCMD		; Has our superior said it has a cmd?
	 RET			; No.
	BLTZ CMBFL-1,CMBUF	; Zero all but last word,
	SETOM CMBUF+CMBFL-1	; and ensure last word non-zero.
	.BREAK 12,[5,,CMBUF]	; Try to read command string.
	MOVE A,[440700,,CMBUF]
	SKIPE CMBUF		; If read a cmd-string,
	 MOVEM A,CMPTR		; Tell TYRLD, GO2A it's there.
	POPJ P,

]; END IFN ITSSW
SUBTTL TNX - TTY routines (TYOX, TYI, TTYINI) and JCLINI

IFN TNXSW,[

	; TYOX - Type out char in A
TYOX:	SKIPN TTYOPF
	 CALL TTYINI
IFN A-1,EXCH A,R1
	PBOUT
IFN A-1,EXCH A,R1
	POPJ P,

	; TYI - Get (just typed in) char in A
	;	There is a screw for 20X in that it's not really possible
	;	to know if the system is going to feed you a CR-LF
	;	or just a CR; TYRLD2 checks for that, by flushing LF's, but
	;	this would be the place to check if it were easy to do.
TYI:	SKIPN TTYOPF
	 CALL TTYINI	; Open the tty if not already done.
IFN A-1,EXCH R1,A
	PBIN		; Get char into AC 1
	JUMPE R1,.-1	; Ignore nulls.
	TLNE FF,FL20X	; Cretinous differences between 10X/20X
	 JRST TYI2	; 20X, skip EOL check.
	CAIN R1,^_	; On 10X, CR turned into ^_ (EOL); change it back.
	 MOVEI R1,^M
TYI2:
IFN A-1,EXCH R1,A	; Restore everything to right place if necessary.
	POPJ P,

	; TTYINI - Initialize tty

TTYINI:	PUSH P,A
	PUSH P,T
	SYSCAL RFMOD,[[.PRIIN]][A ? A]
	POP P,T
	HLRZS A
	ANDI A,177	; Terminal width
	CAIGE A,30.	; If too low,
	 ADDI A,128.	; Assume twenex crockishness
	MOVEM A,LINEL	; Linel gets width of tty.
	SETOM TTYOPF	; Say the tty is now open.
	POP P,A
	POPJ P,
	; Read "JCL" - RSCAN buffer or nnnMID.TMP file (from CCL)

JCLINI:	SETZM CMPTR
	SKIPE CCLFLG	; Started at CCL location?
	 JRST JCLIN5	; Yep, go snarf stuff specially.
	TLNN FF,FL20X	; Is this Tenex?
	 JRST [	MOVEI R1,.PRIIN
		BKJFN	; see what previous character was
		 POPJ P,; *Gasp*
		PBIN
		CAIE R1,^_ ; Tenex newline?
		 SETOM CMPTR ; No, set flag saying "TTY but no prompt"
		POPJ P,]; and skip the Twenex hackery below
	SETZ R1,	; If not, check RSCAN.
	RSCAN		; See if have anything in RSCAN buffer.
	 POPJ P,	; Huh?  Shouldn't happen, but ignore it.
	JUMPLE R1,APOPJ	; Also return if char cnt says nothing there.
	MOVNI R3,(R1)	; Aha, set up cnt for SIN
	HRROI R2,CMBUF
	MOVEI R1,.CTTRM	; Now ready for business...
	SIN
	LDB R1,R2	; Now examine wages thereof
	CAIE R1,^M	; Last char CR?
	 JRST [	MOVEI R1,^M
		IDPB R1,R2	; If not, make it so.
		JRST .+1]
	SETZ R1,
	IDPB R1,R2		; Must also ensure ASCIZ.

	MOVE B,[440700,,CMBUF]	; Flush any spaces in front
	ILDB A,B
	CAIN A,40
	 JRST .-2

	; If the rescan line starts with "RUN", skip that.
	MOVE C,B		; Save backup pos
IRPC X,,[RUN]
	CAIE A,"X+40		; Allow lowercase
	 CAIN A,"X
	  CAIA
	   JRST JCLIN2		; Jump as soon as no match
	ILDB A,B		; Matched, get next char.
TERMIN
	
JCLIN2:	CAIE A,40		; Is next char a space?
	 JRST [	MOVE B,C	; When non-space seen, back up to saved pos
		LDB A,B
		JRST JCLIN4]
	ILDB A,B		; Saw space so we won.  Get next char
	MOVE C,B		; Say backup should start here
	JRST JCLIN2		; and flush all spaces.

	ILDB A,B
JCLIN4:	CAILE A,40		; Now skip the filename used to invoke MIDAS.
	 JRST .-2		; Flush until random ctl seen (space, ^M)
	CAIE A,40		; If it wasn't a space,
	 POPJ P,		; then forget about the whole thing.
JCLIN3:	MOVE C,B		; Now flush spaces.  Save last ptr for chars.
	ILDB A,B
	CAIN A,40
	 JRST JCLIN3
	CAIN A,^M		; And is first non-space something besides CR?
	 POPJ P,		; Bah, there wasn't anything in the JCL!!
	MOVEM C,CMPTR		; Else save ptr to start of real goods.
	POPJ P,

	; TNX snarf of CCL file.  No such thing as tmpcor, so just
	; look for real file with appropriate name.
JCLIN5:	SETZM CCLFLG	; Want 0 in case abort out, will reset if win.
	GJINF		; Get job # in R3
	HRROI R1,CMBUF	; Use CMBUF to form filename string.
	MOVEI R2,(R3)
	MOVE R3,[NO%LFL+NO%ZRO+<3_18.>+10.]
	NOUT		; ship out job num in 3 digits, radix 10.
	 GOHALT
	HRROI R2,[ASCIZ /MID.TMP/]
	SETZ R3,
	SOUT		; Flesh out rest of filename string.
	SETZ R2,	; Make sure it's ASCIZ.
	BOUT
	MOVE R1,[GJ%OLD+GJ%SHT]	; Use short-form GTJFN
	HRROI R2,CMBUF		; and gobble name from CMBUF.
	GTJFN
	 POPJ P,		; If failed, forget it.
	MOVE R2,[070000,,OF%RD]	; Read 7-bit bytes
	OPENF
	 POPJ P,		; Bah
	HRROI R2,CMBUF		; Gobble stuff up.
	MOVEI R3,CMBFL*5	; Read until buffer full,
	MOVEI R4,^J		; or LF seen.
	SIN
	JUMPLE R3,APOPJ		; Forget it if too big for buffer!!

	MOVE R2,[440700,,CMBUF]	; Aha, we've got something, so set
	MOVEM R2,CMPTR		; pointer to slurped stuff.
	SETOM CCLFLG
	HRROI R2,UTIBUF		; Slurp rest into larger buffer,
	MOVNI R3,UTIBFL*5	; using count only.
	SIN
	JUMPGE R3,APOPJ		; Refuse to hack grossly large file.
	ADDI R3,UTIBFL*5
	JUMPLE R3,APOPJ		; if nothing read, need write nothing out.
	HRLI R1,(CO%NRJ)	; Don't release JFN,
	CLOSF			; but stop reading from file.
	 POPJ P,
	MOVE R2,[070000,,OF%WR]	; Now try to hack write access.
	OPENF
	 POPJ P,
	MOVE R2,R1		; Source becomes destination...
	HRROI R1,UTIBUF		; and UTIBUF becomes source,
	MOVNS R3		; for just as many bytes as were read.
	SOUT
	MOVEI R1,(R2)		; done, now just close file.
	CLOSF			; (this time, release JFN).
	 POPJ P,
	SETOM CCLMOR		; say that more CCL remains.
	POPJ P,
] ; END IFN TNXSW
SUBTTL DEC - TTY routines (TYOX, TYI, TTYINI)

IFN DECSW,[

	; TYOX - Type out char in A
TYOX:	SKIPN TTYOPF
	 CALL TTYINI
	OUTCHR A
	POPJ P,

	; TYI - Get a typed-in char in A

TYI:	SKIPN TTYOPF	; Open the tty, if not already done.
	 CALL TTYINI
	INCHWL A
IFN SAILSW,[
	CAIN A,612	; On SAIL, EOF is 612,
	 MOVEI A,^Z	; so turn into normal EOF if found.
]
	CAIE A,^M	; Throw away the LF after a CR.
	 RET
	INCHWL A
	MOVEI A,^M	; Note that TYRLDR will put it back in.
	RET

TTYINI:	INSIRP PUSH P,AA A B
IFE SAILSW,[
	PJOB A,
	TRMNO. A,
	 JRST TTYIN1
	MOVEI AA,1012		; .TOWID
	MOVE B,[2,,AA]
	TRMOP. B,		; Read width of tty line into B.
]
TTYIN1:	 MOVEI B,80.		; TRMOP. failed or not tried => assume width is 80.
	MOVEM B,LINEL
	INSIRP POP P,B A AA
	SETOM TTYOPF
	RET

	TMPLOC .JBREN, TTYREN
TTYREN:	SETOM TTYBRF	; "REENTER" command comes here
R: G:	JRST @.JBOPC	; To request a ^H-break.  Note crufty labels pointing here.

];IFN DECSW
SUBTTL DEC Hackery for JCLINI - Read CCL commands.

IFN DECSW\TNXSW,[
VBLK
CCLFLG:	0	; Flag to indicate CCL entry from COMPIL, SNAIL, CCL, or EXEC
CCLMOR: 0	; -1 => There are more lines of CCL commands,
		; so do a RUN SYS:MIDAS when finished.
PBLK
]

IFN DECSW,[	; DEC only hacks CCL as "JCL".

.SCALAR CCLFIL	; Saves FN1 for tmp file hacking.

; Read MID temp core file, if that loses, try nnnMID.TMP file.
;  Clobbers A,B,C,D.

JCLINI:	SETZM CMPTR
	SKIPN CCLFLG		; Was midas called from CCL level?
	 RET			; No, do not snarf tempcore
	SETZM CCLFIL		; No CCL file yet
	SETZM CCLFLG		; If tmpcor loses want this 0 (will re-setom below)
	BLTZ CMBFL,CMBUF	; Zero cmd buffer.
	MOVE A,[2,,['MID,, ? -<CMBFL-1>,,CMBUF-1]]	; read (leave last wd 0)
	TMPCOR A,		; Read compil-generated command
	 JRST [	OPEN [17 ? 'DSK,, ? 0] ; No tempcore, maybe try dump mode.
		 RET		; Argh but let something else die
		PJOB A,		; Get job #
		IDIVI A,100.	; Want decimal job number in sixbit
		ADDI A,'0
		LSH A,6
		IDIVI B,10.
		ADDI A,'0(B)
		LSH A,6
		ADDI A,'0(C)
		LSH A,18.
		HRRI A,'MID	; Form file name as nnnMID.TMP
		MOVEM A,CCLFIL	; Save for writing below
		MOVSI B,'TMP
		SETZB C,D	; No protect or ppn trash
		LOOKUP A	; Try to get file
		 RET		; Give up
		MOVE A,[-<CMBFL-1>,,CMBUF-1]
		SETZ B,
		INPUT A		; Try to read command
		SETZB A,B
		RENAME A	; Try to delete it now
		 JFCL		; Ignore failure
		CLOSE		; Happy sail
		JRST .+1]
	SKIPN CMBUF		; One last check for it to be there
	 RET			; Alas, there is none
	MOVE A,[440700,,CMBUF]	; Load a byte pointer to the command
	SETOM CCLFLG
	MOVEM A,CMPTR		; There is, set command pointer
JCLIN1:	ILDB B,A
	CAIE B,^J		; See if our command file has anything after 1st line.
	 JRST JCLIN1
	ILDB B,A
	JUMPE B,JCLIN3
	SETOM CCLMOR		; It does; set flag so after handling 1st line we'll
	MOVE C,[440700,,UTIBUF+2]
JCLIN2:	IDPB B,C
	ILDB B,A
	JUMPN B,JCLIN2
	SUBI C,UTIBUF+1		; Get # words written in utibuf. operand is relocatable!
	HRLOI C,-1(C)		; These 2 insns turn size into -size,,utibuf+1
	EQVI C,UTIBUF+1
	MOVEM C,UTIBUF+1
	SKIPE A,CCLFIL		; Was this called with a temp file?
	 JRST [	MOVSI B,'TMP
		SETZB C,D
		ENTER A		; Try to re-write file
		 RET		; Sigh
		MOVE A,UTIBUF+1
		SETZ B,
		OUTPUT A
		RELEASE
		RET]
	MOVSI C,'MID
	MOVEM C,UTIBUF
	MOVE C,[3,,UTIBUF]
	TMPCOR C,
	 JFCL		; [KLH - there used to be some random cruft here.]
JCLIN3:	RET

] ;END IFN DECSW
SUBTTL Old Command Line Reader (CMD)
ifn 0,[
	 ; Read command & filenames & hack defaulting.

CMD:	SKIPE CMPTR	; Unless have DDT or RSCAN cmd string,
	 JRST CMD06	; (we don't)
	CALL CRR	; type a CRLF, prompt etc.
CMD05:	SETZM CMPTR
	TYPE "*"
CMD06:	MOVEI A,3	; Read from TTY (or string <- cmptr)
	CALL RCHSET
	MOVEI F,FB	; Point to scratch filblk.
	BLTZ L$FBLK,FB	; and clear the whole thing.
	TRO FF,FRCMND	; Tell RFD it's scanning a command line.
	CALL RFD	; Now see if command null, and whether has _.
IFN DECSW\TNXSW,[
	CAIN A,"!	; If terminator was "!", go run program.
	 JRST RFDRUN
]
	TRNN FF,FRNNUL	; If no filespec was seen,
	 CAIE A,^M	; and terminator is EOL,
	  CAIA
	   JRST CMD05	; then prompt again and get another string.
	TRZ FF,FRARRO	; Got something, clear saw-"_" flag.
CMD07:	CAIN A,"_
	 TRO FF,FRARRO	; FRARRO will be on if there's a "_" in string.
	CAIN A,^M
	 JRST CMD1	; Read thru the whole command.
	CALL RFD
	JRST CMD07

	; Now re-read the string, for real this time.  Previous scan was
	; mainly just to see if "_" existed.  If not, then first filename
	; must be input file, and output filenames are all defaulted.
CMD1:	MOVE T,[440700,,CMBUF]	; Restore original ptr to 
	MOVEM T,CMPTR		; beginning of string.
IFN CREFSW,SETZM CREFP		; Clear all switches before decoding them.
INSIRP SETZM 0,ERRFP TTYINS WSWCNT
IFN LISTSW,[
	SETZM LISTP
	SETOM LISTP1	; Will be AOSed by each (L) switch.
]

	MOVE T,FSDSK
	MOVEM T,$FDEV(F)
IFE TNXSW,[MOVE T,RSYSNM ? MOVEM T,$FDIR(F)]
IFN TNXSW, SETZM $FDIR(F)
	SETZM $FNAME(F)
	SETZM $FEXT(F)

	TRZ FF,FRNNUL
	TRNE FF,FRARRO	; Don't gobble input spec as output!
	 CALL RFD		; Read bin file spec.
	MOVE TT,FF		; Remember whether null
	BLTMAC T,L$FBLK,(F),OUTFB	; Copy from scratch to OUTFB.

	MOVE T,$FDEV(F)
	CAMN T,FSNUL
	 MOVE T,FSDSK
	MOVEM T,$FDEV(F)
IFE ITSSW, MOVE T,FSCRF
IFN ITSSW, MOVE T,FSCREF
	MOVEM T,$FEXT(F)

	TRNN FF,FRARRO	; If "_" doesn't exist in cmd line,
	 MOVEI A,"_	; then only filespec is for input, kludge to get it.
	CAIN A,"_	; If "_" exists in cmd line, did we hit it?
	 JRST CMD2	; Ran out of output specs => just use defaults.
	CALL RFD	; Read cref file spec.
IFN CREFSW,[
	TRNN FF,FRNNUL	; If spec not null or ended by _,
	 CAIN A,"_
	  SETOM CREFP	; We must want to cref.
CMD2:	BLTMAC T,L$FBLK,(F),CRFFB	; Copy specs from FB to CREF FB.
]
IFE CREFSW,CMD2:
	MOVE T,FSERR
	MOVEM T,$FEXT(F)
	CAIN A,"_
	 JRST CMD6	; No more output specs.
	CALL RFD	; Read error file sppec.
IFN ERRSW,[
	TRNN FF,FRNNUL	; Nonnull spec or last spec =>
	 CAIN A,"_
	  SETOM ERRFP	; Must want an error file.
CMD6:	BLTMAC T,L$FBLK,(F),ERRFB	; Copy specs from FB to ERR filblk.
]
IFE ERRSW,CMD6:
IFN LISTSW,[
IFE ITSSW, MOVE T,FSLST
IFN ITSSW, MOVE T,FSLIST
	MOVEM T,$FEXT(F)
	CAIN A,"_	; Any output spec remaining?
	 JRST CMD3
	CALL RFD	; Yes, read one.
	SETOM LISTP	; List spec given implies want listing.
CMD3:	BLTMAC T,L$FBLK,(F),LSTFB	; Copy specs from FB to LST filblk.
]

CMD5:	CAIN A,"_
	 JRST CMD4
	CALL RFD	; Ignore any output specs not needed.
	JRST CMD5

CMD4:	MOVE T,FSDSK		; Default the input names.
	MOVE A,$FDEV(F)
	CAME A,FSPTP		; Don't leave dev name set to common out-only devs.
	 CAMN A,FSNUL
	  MOVEM T,$FDEV(F)
IFE ITSSW, MOVE T,FSMID
IFN ITSSW, MOVE T,FSGRTN	; > on ITS.
	MOVEM T,$FEXT(F)
	MOVE T,FSPROG
	SKIPN $FNAME(F)		; The fn1 alone is sticky across the _.
	 MOVEM T,$FNAME(F)

	TRZ FF,FRARRO		; If only 1 name it should be FNAM1.
	CALL RFD		; Read input spec.
	BLTMAC T,L$FBLK,(F),ISFB	; Copy into specified-input filblk.
	MOVE T,$FNAME(F)	; Default output FN1's to input.
	SKIPN OUTFB+$FNAME
	 MOVEM T,OUTFB+$FNAME
IFN CREFSW,[
	SKIPN CRFFB+$FNAME
	 MOVEM T,CRFFB+$FNAME
]
IFN LISTSW,[
	SKIPN LSTFB+$FNAME
	 MOVEM T,LSTFB+$FNAME
]
IFN ERRSW,[
	SKIPN ERRFB+$FNAME
	 MOVEM T,ERRFB+$FNAME
]
	MOVE A,FSNUL		; The output dev defaults to NUL:
	MOVE T,$FDEV(F)		; If the input is from TTY:
	CAMN T,FSTTY
	 TRNE FF,FRNNUL		; And the bin spec was null.
	  CAIA
	   MOVEM A,OUTFB+$FDEV
	TRZ FF,FRARRO		; Don't louse up .INSRT's reading.
	RET
] ;ifn 0
SUBTTL Command Line Reader (CMD)

	 ; CMD - Read command & filenames & hack defaulting.

.SCALAR CMDPSV	; Saves read ptr into CMBUF, for re-scanning.

CMD:	SKIPLE T,CMPTR	; If we have DDT or RSCAN or CCL string,
	 JRST CMD06	; go hack it without typing anything out.
	CAMN T,[-1]	; If Tenex-type "JCL", normal TTY input 'cept no prompt
	 JRST CMD06X
	CALL CRR	; Nope, must type a CRLF, prompt etc.
CMD05:	TYPE "*"
CMD06X:	SETZB T,CMPTR
CMD06:	MOVEM T,CMDPSV	; Save pointer for later restoration
	MOVEI A,3	; Read from TTY (or string <- cmptr)
	CALL RCHSET

	MOVEI F,ISFB	; Point to input-spec filblk.
	BLTZ L$FBLK,(F)	; Zap it through and through.
	TRO FF,FRCMND	; Tell RFD it's scanning a command line.
	CALL RFD	; Now see if command null, and whether has _.
IFN DECSW\TNXSW,[
	CAIN A,"!	; If terminator was "!", go run program.
	 JRST RFDRUN
]
	TRNN FF,FRNNUL	; If no filespec was seen,
	 CAIE A,^M	; and terminator is EOL,
	  CAIA
	   JRST CMD05	; then prompt again and get another string.
	TRZA FF,FRARRO	; Got something, clear saw-"_" flag.
CMD07:	 CALL RFD
	CAIN A,"_
	 JRST [	TRO FF,FRARRO	; FRARRO will be on if there's a "_" in string.
		CALL RFD	; Gobble next filename, input filespec.
		JRST CMD1]
	CAIE A,^M
	 JRST CMD07	; Read thru the whole command until read input filespec


	; Now re-read the string, for real this time.  Previous scan was
	; mainly to latch onto input filespec and see if "_" existed.

CMD1:	SKIPN T,CMDPSV		; Restore original ptr if there's one,
	 MOVE T,[440700,,CMBUF]		; else point at beg of buffer.
	MOVEM T,CMPTR
	SETZM TTYINS ? SETZM WSWCNT	; Clear all switches.
IFN CREFSW,SETZM CREFP
IFN ERRSW, SETZM ERRFP
IFN LISTSW,SETZM LISTP ? SETOM LISTP1	; Will be AOSed by each (L) switch.
	SETZ A,
	TRNN FF,FRARRO	; If "_" doesn't exist in cmd line,
	 MOVEI A,"_	; then only filespec is for input, kludge to get it.

	MOVEI F,OUTFB
	BLTZAC T,L$FBLK,(F)		; Clear output filblk.
	MOVE T,FSDSK		; Default dev to DSK.
	MOVEM T,$FDEV(F)
	SKIPN T,$FNAME+ISFB	; Now default FN1 from input filespec
	 MOVE T,FSPROG		; (use "PROG" if none)
	MOVEM T,$FNAME(F)
IFE TNXSW,[MOVE T,RSYSNM	; Now default directory if need to
	MOVEM T,$FDIR(F)]

	TRZ FF,FRNNUL
	CAIE A,"_		; If it exists,
	 CALL RFD		; Read bin file spec.
	TRNN FF,FRNNUL		; If spec was null,
	 JRST [	MOVE T,FSTTY	; and input spec was TTY:,
		CAME T,$FDEV+ISFB
		 JRST .+1
		MOVE T,FSNUL	; then set device to NUL:.
		MOVEM T,$FDEV(F)
		JRST .+1]

DEFINE CFMAC SWIT,PTR,INSTR,DEXT
IFN SWIT,[
	MOVE T,DEXT
	MOVE TT,[[INSTR],,PTR]
] .ELSE SETZB T,TT
	PUSHJ P,CMDFGT
TERMIN

	CFMAC CREFSW,CRFFB,[SETOM CREFP][IFN ITSSW,[FSCREF] .ELSE FSCRF]

	CFMAC ERRSW, ERRFB,[SETOM ERRFP] FSERR

	CFMAC LISTSW,LSTFB,[SETOM LISTP][IFN ITSSW,[FSLIST] .ELSE FSLST]

CMD50:	CAIE A,"_
	 JRST [	SETZB T,TT	; Point to scratch FB etc.
		CALL CMDFGT	; Ignore any output specs not needed.
		JRST CMD50]	; Must do this way to retain default stuffs.

	; Finally read input file.
	BLTMAC T,L$FBLK,(F),ISFB	; Copy last stuff to input spec
	MOVEI F,ISFB		; and point at it.
	PUSHJ P,CMDDVX		; Hack device-name default.
IFE ITSSW, MOVE T,FSMID
IFN ITSSW, MOVE T,FSGRTN	; > on ITS.
	MOVEM T,$FEXT(F)
	CALL RFD		; Read input spec.
	RET		; Yep, that's really all!

	; TT has <addr of instr to xct if file spec'd>,,<filblk ptr>
	; T has default $FEXT.
	; Takes defaults from current F, sets F to new filblk.
CMDFGT:	JUMPE TT,[SETZ T,	; If 0, do usual but into oblivion (scratch FB)
		MOVE TT,[[JFCL],,FB]
		JRST .+1]
	BLTMAC B,L$FBLK,(F),(TT)	; Copy from current filblk to new.
	MOVE F,TT			; set new F.
	MOVEM T,$FEXT(F)	; Set default $FEXT
	PUSHJ P,CMDDVX		; Set up device, defaulting to DSK.
	CAIN A,"_	; If last delimiter was start of input spec,
	 POPJ P,	; don't read anything - just use defaults.
	PUSHJ P,RFD
	TRNN FF,FRNNUL	; If spec non-null or
	 CAIN A,"_	; ended by _, then
	  CAIA		; hack specified instr.
	   POPJ P,
	HLRZ T,F
	XCT (T)
	POPJ P,

CMDDVX:	SKIPN T,$FDEV(F)
	 MOVE T,FSDSK
	CAME T,FSPTP
	 CAMN T,FSNUL
	  MOVE T,FSDSK
	MOVEM T,$FDEV(F)
	POPJ P,
SUBTTL ITS/DEC Filename Reader/printer (RFD, TYPFB)

IFN DECSW\ITSSW,[	; Begin moby conditional for sixbit reader.

; RFD -  Reads a single file description from .INSRT or command line,
;	using RCH, into specified FILBLK.
;	F points at FILBLK to store description in.
; 	Implements crufty ^R hack (if see ^R, act as if just starting to
;	read filename, so effect is stuff before ^R has set defaults.)
; If FRCMND set, recognize -, comma, / and ( as special characters,
;	and hack switches.
; Sets FRNNUL if spec was nonnull.
; Clobbers A,B,C only.

RFD:	TRZ FF,FRNNUL
RFD01:	SETZM RFDCNT	; Zero cnt of "normal" filenames.  Jump here if see ^R.

RFD10:	PUSHJ P,GPASST	; Flush out spaces/tabs
	TRNN FF,FRCMND	; If parsing command line,
	 CAIE A,";	; or if char isn't semi-colon,
	  JRST RFD22	; just handle normally.
RFD15:	PUSHJ P,RCH	; Semi-colon and not command line!! Flush rest
	CAIE A,^M	; of line, assuming it's a comment!
	 JRST RFD15
	POPJ P,

RFD2:	PUSHJ P,RCH	; Get character in A
RFD20:	CAIE A,40	; Space  (Come here to scan already-read char.)
	 CAIN A,^I	; or tab?
	  JRST RFD10	; Ach, go into flush-whitespace loop.
RFD22:	CAIN A,^M	; End of line?
	 POPJ P,	; If so, obviously done.
	CAIN A,^R	; Crufty ^R hack?
	 JRST RFD01	; Sigh, pretend just starting to read filename.
	TRNN FF,FRCMND	; Reading command line?
	 JRST RFD40	; Nope, skip over cmnd-line frobs.

	; Reading cmd line, test special chars.
IFN ITSSW\SAILSW, CAIN A,"	; SAIL underscore, ITS backarrow like _.
  .ELSE		CAIN A,"=	; Either gets munged,
	 MOVEI A,"_	; into canonical "_".
	CAIE A,"_	; Backarrow is output_input marker.
	 CAIN A,",	; Comma is also a terminator...
	  POPJ P,
IFN DECSW\TNXSW,[	; I'm not sure if this belongs here, but
	CAIN A,"!	.SEE RFDRUN
	 POPJ P,
]
	PUSHJ P,CMDSW	; Check for switches...
	 JRST RFD20	; Got some, scan next char (returned by CMDSW)
			; Got none, drop thru.


	; No special delimiters,
	; Check for chars which signal what following word is.
RFD40:
IFN DECSW,[
	CAIN A,"[	;] Left bracket signals start of PPN.
	 JRST [	PUSHJ P,RFDPPN		; Slurp it up,
		MOVEM C,$F6DIR(F)	; store it,
		TRO FF,FRNNUL		; saying spec not null.
		JRST RFD20]	; and go process leftover delimiter.

	CAIN A,".	; Period signals start of extension.
	 JRST [	PUSHJ P,RCH		; Get the next character
		PUSHJ P,RFDW		; Read in a word.
		MOVEM C,$F6EXT(F)	; Store it...
		TRO FF,FRNNUL		; and say spec non-null (even if C/ 0)
		JRST RFD20]	; and process delimiting char.
]

	; Here, char doesn't signal the start of anything, so we'll assume
	; it's the start of a name.
	PUSHJ P,RFDW	; Gobble up a word.
	JUMPE C,RFD2	; If nothing was read, must ignore char; get another.

	; Aha, name was read, now examine delimiter to see if it specifies
	; anything we know about.
	TRO FF,FRNNUL	; Set flag saying spec non-null.
	CAIN A,":	; If colon...
	 JRST [	MOVEM C,$F6DEV(F)	; Then store name as device.
		JRST RFD2]		; and flush delimiter.
IFN ITSSW,[
	CAIN A,";	; If semicolon...
	 JRST [	MOVEM C,$F6DIR(F)	; Then store name as directory (sname)
		JRST RFD2]		; and flush delimiter.
]
	; Whatever it is, at this point delimiter doesn't signify anything
	; special in terms of what the name is.  So we just store it, using
	; the usual FN1 FN2 DEV DIR sequence, and hand delimiter off to
	; the prefix scanning stuff.
	MOVE B,RFDCNT		; Get current count for random names.
	XCT RFDTAB(B)		; Either MOVEM C, to right place, or ignore
	 AOS RFDCNT		; by skipping over this instr.
	JRST RFD20		; and go examine delimiter.


.SCALAR RFDCNT		; Count to index RFDTAB by.

RFDTAB:	MOVEM C,$F6FNM(F)	; 1st name.
	MOVEM C,$F6EXT(F)	; 2nd name.
	MOVEM C,$F6DEV(F)	; 3rd name is dev.
	MOVEM C,$F6DIR(F)	; 4th is sname.
	CAIA			; 5th and on ignored, don't incr. cnt.
; RFDW - Reads a "word" - any string of contiguous SIXBIT chars,
;	barring certain delimiters, and leaves SIXBIT result in C.
;	Begins reading with char currently in A.  Returns with delimiter
;	char in A (it's possible this can be the same char!)
;	Clobbers B.

RFDW:	SETZ C,		; First things first, zap result.
	SKIPA B,[440600,,C]
RFDW2:	 PUSHJ P,RCH
	CAIN A,^Q	; Is char the quoter char?
	 JRST [	PUSHJ P,RCH	; Yup, gobble next...
		CAIN A,^M	; and accept anything but CR
		 POPJ P,	; since that terminates the whole line.
		JRST RFDW7]	; OK, go stuff the char into C.
	CAIE A,40	; Space
	 CAIN A,^I	; or tab
	  POPJ P,	; is always a break.
	CAIN A,^M	; As is CR.
	 POPJ P,
	TRNN FF,FRCMND	; And certain chars are bummers when reading cmd.
	 JRST RFDW4
	CAIE A,"/
	 CAIN A,"(
	  POPJ P,
IFN DECSW\TNXSW, CAIE A,"=
	 CAIN A,"_
	  POPJ P,
IFN ITSSW\SAILSW, CAIE A,"
	 CAIN A,",
	  POPJ P,
IFN DECSW\TNXSW,[
	CAIN A,"!
	 POPJ P,
]
	; Not reading cmd line, or no cmd-line type chars seen.
RFDW4:
IFN ITSSW,[
	CAIE A,":	; For ITS filenames, these chars are special.
	 CAIN A,";
	  POPJ P,
]
IFN DECSW,[
	CAIL A,140	; For DEC, allow only alphanumeric.
	 SUBI A,40	; cvt to uppercase, then
	CAIL A,"A	; see if alpha.
	 CAILE A,"Z
	  JRST [CAIL A,"0	; Nope, see if numeric.
		 CAILE A,"9
		  POPJ P,	; Not alphanumeric, assume delimiter.
		JRST .+1]
]
RFDW7:	TLNN B,770000	; Enough room in C for another char?
	 JRST RFDW2	; Nope, ignore it and get next.
	CAIL A,140	; Enuf room, cvt lower to uppercase
	 SUBI A,40
	SUBI A,40	; and cvt to sixbit,
	IDPB A,B	; and deposit.
	JRST RFDW2	; Get another.

] ; END IFN DECSW\ITSSW
IFN DECSW,[	; PPN Reader

RFDPPN:	PUSHJ P,RFDOCT	; Read project num,
IFN CMUSW, JUMPE C,RCMUPP	; At CMU watch for our funny ppns
	HRLM C,(P)
	PUSHJ P,RFDOCT	; Read programmer num.
	HLL C,(P)
	POPJ P,

IFE SAILSW,RFDOCL=="0 ? RFDOCH=="8	; Read octal numbers.
IFN SAILSW,RFDOCL==40 ? RFDOCH==140	; Read sixbit (right-justified).

RFDOCT:	SETZ C,		; Read octal num, return in C.
RFDOC1:	PUSHJ P,RCH
	CAIL A,140
	 SUBI A,40
IFN SAILSW,[ ;[		; Even if reading sixbit names (for SAIL),
	CAIE A,",	; Comma and closebracket are still special.
	 CAIN A,"]
	  POPJ P,
]
	CAIL A,RFDOCL
	 CAIL A,RFDOCH
	  POPJ P,	; Not octal or not 6bit, return.
	IMULI C,RFDOCH-RFDOCL
	ADDI C,-RFDOCL(A)
	JRST RFDOC1

IFN CMUSW,[	; [
RCMUPP:	CAIN A,"]	; Watch out for []
	 POPJ P,
REPEAT 4, SETZM PPNBUF+.RPCNT
	MOVE C,[440700,,PPNBUF]
RCMUPL:	CAIE A,^M		; Don't look too far
	 SKIPE PPNBUF+3
	  JRST RCMUPD
	IDPB A,C
	PUSHJ P,RCH	; [
	CAIE A,"]
	 JRST RCMUPL
RCMUPD:	MOVE A,[C,,PPNBUF]
	CMUDEC A,
	 SETZ C,
	POPJ P,

.VECTOR PPNBUF(4)	; Storage to buffer up a string for CMUDEC uuo to scan.

] ;IFN CMUSW
] ;IFN DECSW
IFN DECSW\ITSSW,[

; TYPFB - Type out current filblk (what F points at) as file specification
;	Clobbers A,B,C

TYPFB:	MOVSI C,-3-ITSSW
	HRR C,F
TYPF1:	MOVE B,$F6DEV(C)	; Get next name
	PUSHJ P,SIXTYO		; Type out name
	HLRZ A,C
	MOVE A,FILSPC+3+ITSSW(A)	; Now get delimiting character
	PUSHJ P,TYOERR	; Type out
	AOBJN C,TYPF1	; Loop for all names
IFN ITSSW, POPJ P,
IFN DECSW,[
	SKIPN B,$F6DEV(C)	; On DEC system PPN is a special case
	 POPJ P,
	MOVEI A,"[ ;]
	CALL TYOERR
IFN CMUSW,[
	MOVE A,[B,,PPNBUF]
	DECCMU A,
	 JRST OCTPPN
	TYPR PPNBUF
	JRST PPNRB
	]
IFE SAILSW,[
OCTPPN:	HLRZ B,$F6DEV(C)	; LH is proj,
	CALL OCTPNT
	]
.ELSE [	HLLZ B,$F6DEV(C)
	CALL SIXTYO
]
	MOVEI A,",
	CALL TYOERR
IFE SAILSW,[
	HRRZ B,$F6DEV(C)
	CALL OCTPNT	; RH is prog.
]
.ELSE [	HRLZ B,$F6DEV(C)
	CALL SIXTYO
	]
PPNRB:			; [
	MOVEI A,"]
	JRST TYOERR
];IFN DECSW

FILSPC:	":
IFN ITSSW, 40 ? 40 ? ";
IFN DECSW, ". ? 0

] ; END IFN DECSW\ITSSW
SUBTTL Command switches

; CMDSW - Hacks either a single switch or switch list; A should
; contain "/ for the former, "( for the latter.
; Returns in A next char after switch hackery done.  This may be ^M.
; Skip returns if neither "/ nor "( was furnished to it.

CMDSW:	CAIN A,"/	; Single switch?
	 JRST [	PUSHJ P,RCH	; Get next char
		CAIN A,^M
		 POPJ P,
		PUSHJ P,CMDSW1
		PJRST RCH]
	CAIE A,"(	; Switch list?
	 JRST POPJ1	; Neither slash nor paren, make skip return.
CMDSWL:	PUSHJ P,RCH
	CAIN A,^M
	 POPJ P,
	CAIN A,")
	 PJRST RCH
	PUSHJ P,CMDSW1
	JRST CMDSWL

	; Command switch processing.  CMDSW1 processes the switch char
	; in A.
CMDSW1:	CAIL A,140	; Lower case to upper.
	 SUBI A,40
	CAIN A,"T
	 SOS TTYINS	; Count # T-switches.

	CAIN A,"W	; W - prevent tty messages, and
IFE ERRSW,AOS WSWCNT	; request error output file if possible.
.ELSE [
	AOSA WSWCNT
	 CAIN A,"E	; E - request error log file.
	  SETOM ERRFP
	]

IFN CREFSW,[
	CAIN A,"C	; C - request CREF output.
	 SETOM CREFP
	]

IFN LISTSW,[
	CAIE A,"L	; L - request listing
	 POPJ P,
	SETOM LISTP	; Say want listing.
	AOS LISTP1	; (starts as -1, will be positive after 2nd (L))
	]

	POPJ P,
SUBTTL TENEX  Filename Reader/printer (RFD, TYPFB)

IFN TNXSW,[	; Moby conditional for Tenex reader.

; TNXRFD - TENEX-style Filename Reader.
;	Takes input from RCH,
;	Deposits name strings into filblk F points to.
;	Clobbers A,B,C,D, (and AA,T,TT due to FNCHK)
; Uses FRFEXT flag to see if already read extension (type) or not.
;	Refuses to accept existing defaults for version, ;T, account,
;	protection, or JFN.  It will also zap an existing directory
;	default if a device is specified, and vice versa.  This is so that
;	logical names will win a little better.
; 	Implements crufty ^R hack (if see ^R, act as if just starting to
;	read filename, so effect is stuff before ^R has set defaults.)

IFNDEF FRFDEV,FRFDEV==2		; Set if read device.
IFNDEF FRFDIR,FRFDIR==1		; Set if read directory.
IFNDEF FRFEXT,FRFEXT==FRFN1	; Borrow this bit.  Set if read extension.

RFD:	TRZ FF,FRNNUL
	SETZM $FJFN(F)	; Zap JFN since the filename we'll read won't match it.
	SETZM $FACCT(F)	; Also zap other things that we don't want defaulted.
	SETZM $FPROT(F)
	SETZM $FTEMP(F)
	SETZM $FVERS(F)
TRFD01:	TRZ FF,FRFEXT+FRFDEV+FRFDIR	; Jump here if ^R seen.
TRFD10:	PUSHJ P,GPASST	; remove tabs, spaces and get first non-tab/space
	TRNN FF,FRCMND	; If parsing command line,
	 CAIE A,";	; or if char isn't semicolon,
	  JRST TRFD21	; just handle normally.
TRFD15:	PUSHJ P,RCH	; Semi-colon and not command line, it's a comment!
	CAIE A,^M	; So flush rest, up to EOL.
	 JRST TRFD15
	POPJ P,

TRFD1:	TLO FF,FLUNRD	; come here to re-read last char
TRFD2:	PUSHJ P,RCH	; Get char
TRFD21:	CAIE A,40	; Space? (come here to scan already-read char)
	 CAIN A,^I	; or tab?
	  JRST [TRNE FF,FRCMND	; Space/tab, if reading command line
		 JRST TRFD2	; then ignore and continue scanning (for switches), but
		JRST TRFD15]	; if not in cmd line, go flush entire rest of line!
	CAIN A,^M	; End of line?
	 POPJ P,	; If so, obviously done.
	CAIN A,^R	; Crufty ^R hack?
	 JRST TRFD01	; Sigh, pretend starting over.
	TRNN FF,FRCMND	; Must we check for cmd line frobs?
	 JRST TRFD22	; Nope, skip them.

	; Must check for chars special only in command line.
	CAIN A,"=
	 MOVEI A,"_
	CAIE A,"_	; backarrow is filename terminator...
	 CAIN A,",	; as is comma.
	  POPJ P,
	CAIN A,"!	; For CCL hacking...
	 POPJ P,	.SEE RFDRUN
	PUSHJ P,CMDSW	; Check for switches...
	 JRST TRFD21	; got some, process next char (returned by CMDSW)
			; Skips if none, drop thru.

	; Now see if char signifies start of anything in particular.
TRFD22:	CAIE A,"<	; Start of directory name?
	 JRST TRFD24	; No
	PUSHJ P,RCH
	PUSHJ P,TRFDW	; Read word, starting with next char
TRFD23:	CAIN A,".	; Allow . as part of directory name
	 JRST [	PUSHJ P,TRFDW5	; Read a continuation to this word
		JRST TRFD23]	; And try again
	MOVEI D,$FDIR	; Set up index.
	CAIN A,">	; Terminator should be end of dir name...
	 PUSHJ P,RCH	; If so, get next to avoid scan of ">".
			; else bleah, but aren't supposed to fail...
	TRNN FF,FRFDEV	; Unless a device has been explicitly given,
	 SETZM $FDEV(F)	; zap any furnished default.  0 means DSK.
	TRO FF,FRFDIR	; Now say dir was explicitly given.
	JRST TRFD6	; Go store it.
TRFD24:	CAIN A,".	; Start of $FTYPE or $FVERS (20x)?
	 JRST [	MOVEI D,$FTYPE	; Assume reading $FTYPE field,
		TLNE FF,FL20X	; always if 10X, but if really on 20X, then
		 TRON FF,FRFEXT	; use $FTYPE only if not already seen.
		  JRST TRFD4	; $FTYPE - jump to get word & store.
		PUSHJ P,TRFDNM	; $FVERS - 20X and $FTYPE already seen. Get #.
		 MOVEM B,$FVERS(F)	; Store it away if successful.
		JRST TRFD1]		; and go re-read delimiting char.

	CAIN A,";	; Start of $FVERS (10x) or attribute?
	 JRST [	PUSHJ P,RCH	; Find what next char is.
		CAIL A,"a	; Must uppercasify.
		 CAILE A,"z
		  CAIA
		   SUBI A,40
		CAIN A,"T	; Temporary file?
		 JRST [	SETOM $FTEMP(C)
			JRST TRFD2]
		CAIN A,"A	; Account?
		 JRST [	MOVEI D,$FACCT	; Set index, and
			JRST TRFD4]	; go gobble following word.
		CAIN A,"P	; Protection?
		 JRST [	MOVEI D,$FPROT	; Set index, and
			JRST TRFD4]	; go gobble following word.
		TLO FF,FLUNRD	; Not alpha, try numeric.  Re-read char,
		PUSHJ P,TRFDNM	; trying to parse as number.
		 MOVEM B,$FVERS(F)	; Win, parsed as number! Store it.
		JRST TRFD1]	; If none of above, ignore ";" entirely.

	PUSHJ P,TRFDW	; Let's try reading it as word,
	JUMPLE C,APOPJ	; If nothing read, assume it's some terminating delimiter.
	CAIN A,":	; Else have something, check trailing delim for special cases
	 JRST [	MOVEI D,$FDEV		; Aha, a device.
		PUSHJ P,RCH		; Flush the terminator & get next char.
		TRNN FF,FRFDIR		; Unless dir was explicitly given,
		 SETZM $FDIR(F)		; zap furnished default. 0 uses connected dir.
		TRO FF,FRFDEV		; Say device was explicitly given, and
		JRST TRFD6]		; store name away.
	MOVEI D,$FNAME	; Else assume it's the filename.
	JRST TRFD6


TRFD4:	PUSHJ P,RCH	; Here when must gobble next char,
TRFD5:	PUSHJ P,TRFDW	; here when first char of wd already read.
TRFD6:	PUSHJ P,FNCHKZ	; Note this can return and store a null string!
	ADDI D,(F)	; Get address (filblk+index), and
	MOVEM A,(D)	; store string pointer in the appropriate place.
	TRO FF,FRNNUL	; Say non-null spec seen,
	JRST TRFD1	; and go re-read the delimiter, to process it.

; TRFDW - Read a word (string), for use by TNXRFD.  Copies sequence of
;	acceptable filename chars into FNBUF, until non-valid char seen.
;	A/ First char of word,
;	Returns A/ delimiting char, C/ count of chars in string,
;	clobbers nothing else.

TRFDW4:	SUBI A,40	; Make lowercase
TRFDW5:	IDPB A,FNBWP	; Deposit into FNBUF,
	PUSHJ P,RCH	; get next char,
	AOSA C		; and bump count, skipping over zap instruction.
TRFDW:	 SETZ C,	; When called, zero cnt of chars in string.
	CAIL A,"A	; See if char is uppercase alpha,
	 CAILE A,"Z
	  CAIA
	   JRST TRFDW5
	CAIL A,"a	; or lowercase alpha,
	 CAILE A,"z
	  CAIA
	   JRST TRFDW4
	CAIL A,"0	; or numeric,
	 CAILE A,"9
	  CAIA
	   JRST TRFDW5
	CAIE A,"$	; or dollarsign
	 CAIN A,"-	; or hyphen
	  JRST TRFDW5
	CAIN A,"_	; Backarrow is special case, because
	 JRST [	TRNN FF,FRCMND	; if reading command,
		 TLNN FF,FL20X	; or running on 10X,
		  POPJ P,	; must treat as delimiter.
		JRST TRFDW5]
	CAIN A,^V	; ^V is quote char...
	 JRST [	PUSHJ P,RCH	; Quote, get next.
		CAIE A,^M	; Quote anything but this.
		 CAIN A,0	; or this.
		  POPJ P,	; time to exit.
		PUSH P,A	; Quote it!  Save char,
		MOVEI A,^V	; so that a quoter can precede it.
		IDPB A,FNBWP	; Fortunately this hair only needs care
		POP P,A		; for quoted chars, which are
		JRST TRFDW5]	; rare.
	TLNE FF,FL20X	; Are we on a 10X?
	 POPJ P,	; If not, anything at this point is delimiter.
	CAIL A,41	; Check general bounds
	 CAIL A,137	; Range from space to _ exclusive.
	  POPJ P,	; If outside that, delimiter.
	CAIL A,72	; This range includes :, ;, <, =, >
	 CAILE A,76
	  CAIA
	   POPJ P,	; delimiter.
	CAIE A,".
	 CAIN A,",
	  POPJ P,
	CAIE A,"*
	 CAIN A,"@
	  POPJ P,
	; Finally, check out chars which are acceptable to 10X but which
	; might be delimiter in cmd line...
	TRNN FF,FRCMND
	 JRST TRFDW5	; Not hacking cmd line, it's an OK char.
	CAIE A,"/
	 CAIN A,"(
	  POPJ P,
	CAIN A,"!
	 POPJ P,
	JRST TRFDW5	; at long last done.


; TRFDNM - Read numerical string, halt when non-digit
;	seen, leaves result (decimal) in B, with delimiting char in A.
;	One peculiarity is skip return if no numerical char is seen at all;
;	else doesn't skip and B has a valid number.

TRFDNM:	PUSHJ P,RCH		; First char needs special check.
	CAIL A,"0
	 CAILE A,"9
	  JRST POPJ1		; Not a number at all?
	TDZA B,B
TRFDN2:	 IMULI B,10.
	ADDI B,-"0(A)		; Convert to number
	PUSHJ P,RCH		; Get following chars.
	CAIL A,"0
	 CAILE A,"9
	  POPJ P,		; Nope, not digit so treat as delimiter.
	JRST TRFDN2	; Yep, a number

] ;IFN TNXSW
IFN TNXSW,[

; TYPFB - Type out FB pointed to by F

TYPFB:	SKIPE B,$FDEV(F)	; First, device name?
	 JRST [	PUSHJ P,TYPZ
		MOVEI A,":
		PUSHJ P,TYOERR
		JRST .+1]
	SKIPE B,$FDIR(F)	; Directory?
	 JRST [	MOVEI A,"<
		PUSHJ P,TYOERR
		PUSHJ P,TYPZ
		MOVEI A,">
		PUSHJ P,TYOERR
		JRST .+1]
	SKIPE B,$FNAME(F)
	 PUSHJ P,TYPZ
	MOVEI A,".
	PUSHJ P,TYOERR
	SKIPE B,$FEXT(F)
	 PUSHJ P,TYPZ
	MOVEI A,".		; 20X uses "." to set off version,
	TLNN FF,FL20X		; but 10X uses ";".
	 MOVEI A,";
	PUSHJ P,TYOERR
	HRRE A,$FVERS(F)
	JUMPL A,[MOVM B,A	; Is possible to have -1, -2, etc.
		MOVEI A,"-
		PUSHJ P,TYOERR
		MOVE A,B
		JRST .+1]
	PUSHJ P,DPNT		; Version # output in decimal.
	SKIPE $FTEMP(F)
	 TYPE ";T"		; May be temporary.
	SKIPE B,$FPROT(F)
	 JRST [	TYPE ";P"
		PUSHJ P,TYPZ
		JRST .+1]
	SKIPE B,$FACCT(F)
	 JRST [	TYPE ";A"
		PUSHJ P,TYPZ
		JRST .+1]
	POPJ P,

	; Takes BP in B, outputs to TYOERR until zero byte seen.
TYPZ:	CAIA
	 PUSHJ P,TYOERR
	ILDB A,B
	JUMPN A,TYPZ+1
	POPJ P,
] ; IFN TNXSW
SUBTTL TENEX misc. Filename Routines, FS string storage

IFN TNXSW,[	.SEE FSDSK	; Part of this page is NOT conditionalized!!

; To handle filenames of ASCIZ strings instead of SIXBIT words, each
; word has instead a byte pointer to an ASCIZ string.  For purposes of
; easy comparison, all of these bp's point into FNBUF, and a routine
; (FNCHK) is provided which checks a just-stored string and returns a bp
; to either this string, if unique, or to a previously stored string if
; it is the same as the one just stored (which is then flushed).  Thus
; strings can be compared for equality simply by a comparison of their
; byte pointers.  While not necessary, strings are stored beginning on
; word boundaries for easier hacking.

	; <# files>*<avg # strings/file>*<avg # words/string>+<# wds for constants>
LFNBUF==<MAXIND+5>*5*3+20	; Enough to hold strings for all output files,
		; all translated files, and all .insrt files encountered.
		; Later a GC'er can be hacked up so that of the latter only
		; enough for the max .insrt level need be allocated.

LVAR	FNBUF:	BLOCK LFNBUF

	; Macro to easily define constant strings for comparison purposes
DEFINE DEFSTR *STR*
440700,,%%FNLC
%%LSAV==.
LOC %%FNLC
ASCIZ STR
%%FNLC==.
LOC %%LSAV
TERMIN
	%%FNLC==FNBUF
] ; IFN TNXSW!!!

	; If not assembling for TENEX, the following strings become
	; simple SIXBIT values.  This makes it possible to write simple
	; code to work for both TENEX and non-TENEX without messy conditionals.

IFE TNXSW,[EQUALS DEFSTR,SIXBIT]

FSDSK:	DEFSTR /DSK/	; This stuff defines various BP's into FNBUF to
FSSYS:	DEFSTR /SYS/	; use for comparison purposes later.
FSTTY:	DEFSTR /TTY/
FSNUL:	DEFSTR /NUL/
FSPTP:	DEFSTR /PTP/
FSATSN:	DEFSTR /@/
FSSBSY:	DEFSTR /SUBSYS/
FSPROG:	DEFSTR /PROG/
FSMID:	DEFSTR /MID/
FSMDAS:	DEFSTR /MIDAS/
FSGRTN:	DEFSTR />/
FSCRF:	DEFSTR /CRF/
FSCREF:	DEFSTR /CREF/
FSERR:	DEFSTR /ERR/
FSLST:	DEFSTR /LST/
FSLIST:	DEFSTR /LIST/
FSSAV:	DEFSTR /SAV/
FSEXE:	DEFSTR /EXE/

IFN TNXSW,[
VBLK
FNBBP:	440700,,FNBUF	; Points to beg of FNBUF (hook for dynamic alloc)
FNBEP:	FNBUF+LFNBUF-1	; Points to last wd in FNBUF (address, not BP)
FNBWP:	440700,,%%FNLC	; Write Pointer into FNBUF.
FNBLWP:	440700,,%%FNLC	; Last Write Pointer, points to beg of string being stored
PBLK
EXPUNG %%FNLC

; NOTE - provided MIDAS never restarts, no initialization is necessary to
; start using FNCHK.  (Unless of course FNBUF is dynamically allocated someday)

; FNCHK - Check out just-stored filename.  Returns BP in A to ASCIZ string,
;	which will be "canonical" for comparison purposes.
;	Clobbers A,B,T,TT,AA
; FNCHKZ - Makes sure just-writ string is ASCIZ'd out before FNCHK'ing.

FNCHKZ:	MOVE B,FNBWP		; Get write ptr,
	LDB A,B			; see if last char was 0,
	JUMPE A,FNCHK0		; if so can skip one clobberage.
	SETZ A,
	IDPB A,B		; zero out bytes,
FNCHK0:	TLNE B,760000		; until at end of word.
	 JRST .-2
	ADD B,[<440700,,1>-<010700,,>]	; bump BP to point canonically at next.
	MOVEM B,FNBWP

FNCHK:	HRRZ B,FNBWP		; See if write ptr
	CAML B,FNBEP		; has hit end of FNBUF, and
	 ETF [ASCIZ /Filename buffer overflow/]	; barf horribly if so.
	MOVE A,FNBBP		; A  - bp to start of existing string
	MOVE AA,FNBLWP		; AA - bp to start of new string to store
FNCHK2:	MOVEI T,(A)		; T  - current addr being checked, existing str
	MOVEI TT,(AA)		; TT - current addr, new str
	CAIL T,(TT)		; If addrs are same, or overran somehow,
	 JRST [	MOVE A,AA	; didn't find any match, accept new string.
		MOVE B,FNBWP
		MOVEM B,FNBLWP	; Set up new last-write-ptr
		POPJ P,]
FNCHK3:	MOVE B,(T)
	CAMN B,(TT)		; Compare strings, full word swoops.
	 JRST [	TRNE B,377			; equal, last char zero?
		 AOJA T,[AOJA TT,FNCHK3]	; no, continue for whole string
		; Found it!  Flush just-stored string, don't want duplicate.
		MOVEM AA,FNBWP		; Clobber write ptr to previous value.
		POPJ P,]
	; Not equal, move to next string to compare
	MOVEI B,377	; Check for ASCIZ,
	TDNE B,(T)	; moving to end of current string
	 AOJA T,.-1
	HRRI A,1(T)	; and updating BP to point at new string.
	JRST FNCHK2	; (T gets pointed there too at FNCHK2).
; JFNSTR - Get filename strings for active JFN.
;	A/ active JFN
;	F/ addr of filename block to clobber.
; JFNSTB - Same, but ignores A and assumes JFN is already stored in block.
;	Clobbers A,C

JFNSTB:	SKIPA A,$FJFN(F)	; JFNSTB gets the JFN from block itself.
JFNSTR:	 MOVEM A,$FJFN(F)	; whereas JFNSTR stores it there...
	MOVSI D,-NJSTRF		; Set up aobjn thru table.
JFNST2:	PUSH P,T
	SYSCAL JFNS,[FNBWP ? $FJFN(F) ? JSTRFX+1(D)][FNBWP]
	POP P,T
	MOVE C,JSTRFX(D)	; Now get index to place it belongs in file block,
	CAIN C,$FVERS		; and check for this, because
	 JRST [	MOVE A,FNBLWP	; it wants to be a number, not a string.
		MOVEM A,FNBWP	; Zap write pointer back to forget string,
		PUSHJ P,CVSDEC	; and quickly convert before anything clobbers it.
		JRST .+2]	; Skip over the FNCHKZ call.
	PUSHJ P,FNCHKZ		; Fix it up, and get BP to it.
	ADDI C,(F)		; make it an addr, and
	MOVEM A,(C)		; store BP. (or value, for $FVERS)
	ADDI D,1
	AOBJN D,JFNST2
	POPJ P,

	; Filblk idx, output format wd for JFNS call
JSTRFX:	$FDEV	? 100000,,
	$FDIR	? 010000,,
	$FNAME	? 001000,,
	$FTYPE	? 000100,,
	$FVERS	? 000010,,
NJSTRF==<.-JSTRFX>/2

; CVSDEC - Converts ASCIZ string to decimal, assumes only digits seen.
;	A/ BP to ASCIZ
;	Returns value in A, clobbers nothing else.

CVSDEC:	PUSH P,B
	PUSH P,C
	MOVE C,A
	SETZ A,
	JRST CVSDC3
CVSDC2:	IMULI A,10.
	ADDI A,-"0(B)
CVSDC3:	ILDB B,C
	JUMPN B,CVSDC2
	POP P,C
	POP P,B
	POPJ P,

; CVSSIX - Converts ASCIZ string to SIXBIT word.
;	A/ BP to ASCIZ string,
;	Returns SIXBIT word in A.  Clobbers nothing else.

CVSSIX:	PUSH P,B
	PUSH P,C
	PUSH P,D
	MOVE D,A
	SETZ A,
	MOVE B,[440600,,A]
	JRST CVSSX3
CVSSX2:	CAIL C,140
	 SUBI C,40	; Uppercase force
	SUBI C,40	; cvt to 6bit
	IDPB C,B	; deposit
	TLNN B,770000	; If BP at end of word,
	 JRST CVSSX5	; leave loop.
CVSSX3:	ILDB C,D
	JUMPN C,CVSSX2
CVSSX5:	POP P,D
	POP P,C
	POP P,B
	POPJ P,

; CV6STR - Takes 6bit word in A, writes into FNBUF and makes a string of
;	it, returning BP in A.
;	Clobbers A,B,T,TT,AA (due to FHCHKZ)

CV6STR:	MOVE B,A
CV6ST2:	SETZ A,
	LSHC A,6	; Get a 6bit char
	ADDI A,40	; Make ASCII
	IDPB A,FNBWP	; deposit
	JUMPN B,CV6ST2	; Continue until nothing left
	PJRST FNCHKZ	; Make output thus far a string.


; CVFSIX - Takes current filblk (pointed to by F) and puts the
;	right stuff in $F6 entries.

CVFSIX:	PUSH P,A
	PUSH P,B
	MOVSI B,-L$F6BL
CVFSX2:	MOVE A,@CVFTAB(B)	; Get BP to string
	PUSHJ P,CVSSIX		; Convert to 6bit
	ADDI B,$F6DEV(F)	; Get index to right place to store.
	MOVEM A,(B)
	SUBI B,$F6DEV(F)	; restore aobjn pointer...
	AOBJN B,CVFSX2
	POP P,B
	POP P,A
	POPJ P,

CVFTAB:	$FDEV(F)
	$FNAME(F)
	$FEXT(F)
	$FDIR(F)
IFN <.-CVFTAB>-L$F6BL, .ERR CVFTAB loses.

] ; IFN TNXSW
SUBTTL DEC/TENEX - RUN hacking. (Process FOO! in CCL)
IFN DECSW,[

; Process "FOO!", which means "run SYS:FOO with an offset of 1".
; Note that the RUN call needs a block of 6 ACs, but at this point
; it doesn't matter what gets clobbered.


	; Entry point for restart, from TSRETN.
RERUN:	MOVE B,FSMDAS		; Get name - using SYS:MIDAS
	SETZB C,D+1		; (no ext or ppn)
	JRST RFDRU1

VBLK

RFDRUN:	MOVE A,$F6DEV(F)	; Load up the 4 filenames to use.
	MOVE B,$F6FNM(F)
	MOVE C,$F6EXT(F)
	MOVE D+1,$F6DIR(F)
	JUMPN A,RFDRU3	; If device specified, use that,
	MOVSI A,'DSK	; else default to DSK
	CAIN D+1,	; if a PPN was given, and
RFDRU1:	 MOVSI A,'SYS	; to SYS: otherwise.
RFDRU3:	SETZB D,D+2	; These acs must always be zero...
	MOVEI D+3,177	; Flush all core above this address.
IFN SAILSW,[
	SETZ D+4,
	CORE2 D+4,	; Flush hiseg by hand on SAIL.
	 GOHALT
	]
.ELSE HRLI D+3,1	; Elsewhere, just set LH to this to flush hiseg.

	MOVE D+4,[RUNCOD,,D+5]	; Move core-less code into position in ACs.
	BLT D+4,<D+5>+LRUNCD-1
	MOVE D+4,[1,,A]	; <start offset>,,<address of arg block>
	JRST D+5	; Go flush core and run program.

RUNCOD:	CORE D+3,	; Flush as much core as possible; RUN uuo can lose
	 GOHALT		; Because of how much we have.
	RUN D+4,
	 GOHALT
LRUNCD==.-RUNCOD
	; Make sure symbols A-D leave enuf room.
IFL 17-<D+5+LRUNCD>, .ERR RFDRUN ACs lose.
PBLK
] ;END IFN DECSW
IFN TNXSW,[
	; On TENEX, we'll do things without compat package (boo hiss)

	; Entry point for starting new MIDAS, come here from TSRETN.
RERUN:	MOVEI F,FB
	BLTZ L$FBLK,FB		; Clear out scratch filblk, point at it.
	MOVE A,FSMDAS		; Get BP to "MIDAS", store in
	MOVEM A,$FNAME(F)	; filblk, and drop thru for defaults.

	; Here to start up specified program, for CCL hacking.
RFDRUN:	TLNN FF,FL20X		; 20X or Tenex?
	 JRST [	MOVE A,FSSBSY	; Tenex, get BP to SUBSYS string
		SKIPN $FDIR(F)	; Unless directory specified,
		 MOVEM A,$FDIR(F)	; default dir to <SUBSYS>.
		MOVE A,FSSAV	; And do similar thing for ext (.SAV)
		JRST RFDRN2]
	MOVE A,FSSYS		; 20X, get BP to SYS string
	SKIPN $FDEV(F)		; Unless device specified,
	 MOVEM A,$FDEV(F)	; default dev to SYS:.
	MOVE A,FSEXE		; And ditto for ext (.EXE)

RFDRN2:	SKIPN $FEXT(F)		; If extension not specified,
	 MOVEM A,$FEXT(F)	; Store appropriate one.
	PUSHJ P,GETJFI		; Get JFN for input...
	 GOHALT			; Ugh, bletch, etc.

	; OK, all ready to smash ACs with loader, etc.
	MOVE R1,$FJFN(F)	; Put JFN into RH
	HRLI R1,.FHSLF		; and fork handle (self) in LH.
	MOVE R2,[RUNCOD,,R3]	; Load into ACs beginning at AC 3
	BLT R2,R3+LRUNCD-1
	JRST R3			; Off we go, never to return...

	; Following code is executed in AC's, position independent.
RUNCOD:	GET		; Load up the file.
	MOVEI R1,.FHSLF
	GEVEC		; Find entry vector word for it, returned in AC 2.
	JRST R1(R2)	; and go execute instruction in reenter slot.
LRUNCD==.-RUNCOD	; Pretty small loader, huh?
] ; IFN TNXSW
SUBTTL Core Allocation routine - GCCORQ gets another K for MACTAB

; Get another K of MACTAB space.

GCCORQ:	MOVE A,MACHI
	LSH A,-2	; Convert to word #
	CAIL A,MXMACL	; Want more than allowed?
	 POPJ P,
	MOVE A,MACTND	; No, get addr of block we want to get.
	PUSH P,A	; Entry, save A in case have to try again
CORRQ1:
IFN ITSSW,[
	LSH A,-10.
	SYSCAL CORBLK,[MOVEI %CBNDR+%CBNDW
		MOVEI %JSELF ? A ? MOVEI %JSNEW]
	 JRST CORRQL	; Lose
]
IFN DECSW,[
	IORI A,1777
	CORE A,
	 JRST CORRQL	; Lose
]
IFN TNXSW,[
	SKIPN MEMDBG	; Only need to hack if want.
	 JRST CORRQ3
	; Super kludge.  No way to ask 10X for a "new page"; must
	; get it via default create-on-reference.  Hence to get page
	; without bombing, must be sure .ICNXP interrupt deactivated!
	PUSH P,T
	SYSCAL DIC,[[.FHSLF] ? [1_<35.-.ICNXP>]]	; Deactivate.
	SETZM (A)			; Reference 1st page
	SETZM 1000(A)			; Reference 2nd page.
	SYSCAL AIC,[[.FHSLF] ? [1_<35.-.ICNXP>]]	; Re-activate.
	POP P,T
CORRQ3:
]

	REST A
	ADDI A,2000
	JRST MACIN2	; Update pointers to end of MACTAB.

IFN ITSSW\DECSW,[
	; Lossage handler for GCCORQ.  Only ITS or DEC can fail.
CORRQL:	PUSH P,C
	PUSH P,D
	TLOE AA,400000
	 JRST CORQL1
	TYPE "
No core for macro table."
CORQL1:	TYPE "
Try again? "
CORQL2:	PUSHJ P,TYI	; Get char
	CAIL A,140	; Cheap uppercase force
	 SUBI A,40
	CAIN A,"Y	; Y,
	 JRST CORRQA	; => try again
	CAIN A,"N	; N,
	 JRST CORRQB	; => back to DDT then try again
	CAIN A,"?	; ?,
	 ERJ CORQL1	; => type out error-type blurb
	TYPE "?  "	; something else
	JRST CORQL2

CORRQB:
IFN ITSSW,.VALUE	; Loop point for don't-proceed
IFN DECSW,EXIT 1,
	TLZ AA,400000
CORRQA:	POP P,D
	POP P,C
	MOVE A,(P)	; Restore A from PDL
	JRST CORRQ1
] ; IFN ITSSW\DECSW
SUBTTL CORGET - allocate fresh pages

; CORGET - Takes arg in AA, an ITS page AOBJN to pages to grab.
;	AA/ -<# pages>,,<1st page #>
;	Clobbers only AA.

CORGET:	JUMPGE AA,APOPJ		; Ignore arg if nothing to do about it.
IFN ITSSW,[
	SYSCAL CORBLK,[	MOVEI %CBNDR+%CBNDW	; Get both read and write.
			MOVEI %JSELF		; Into self
			AA			; AA is AOBJN of pages.
			SETZI %JSNEW]		; Want fresh pages.
	 .LOSE %LSSYS
]
IFN TNXSW,[
	SKIPN MEMDBG	; Ignore anyway if not hacking mem ref debugging.
	 POPJ P,
	PUSH P,R1
	PUSH P,R2
	PUSH P,R3
	MOVE R3,AA
	ASH R3,1	; Get Tenex page AOBJN
	MOVEI R1,(R3)
	LSH R1,9.	; Get word address of first page.
	HRR R3,R1	; Stick back in AOBJN.

	; Super kludge.  No way to ask 10X for a "new page"; must
	; get it via default create-on-reference.  Hence to get page
	; without bombing, must be sure .ICNXP interrupt deactivated!
	MOVEI R1,.FHSLF
	MOVE R2,[1_<35.-.ICNXP>]
	DIC		; Deactivate.
TCORG3:	SETZM (R3)	; Get the page.
	ADDI R3,777	; Bump word address,
	AOBJN R3,TCORG3	; and get next page (note adds 1 more to RH)
	AIC		; Now re-activate...
	POP P,R3
	POP P,R2
	POP P,R1
] ;IFN TNXSW
	POPJ P,