.TITLE	DBSINS
.IDENT	/230683/
.ENABL	LC
;
;	Written by	Ray Di Marco
;			6-May-82.
;
;
;	Version		060582/06
;
;
;----------------------------------------------------------------------------
;
; This is the main module for the database management program DBSINS, that
;
; The program can be envoked as a CUSP from a menu program, and will accept
; the name of the FORMAT and SORTER file, as well as the entry size from data
; passed in core common. 
;

.SBTTL	DECLARATIONS
.ENABL	LC
;
;
.MCALL	.PUSH,.POP			; Stacking
.MCALL	CNAF				; Binary -> Ascii
.MCALL	.EXIT,.PRINT,.TTYOUT		; Abort Exit
.MCALL	.DATE				; returns date in R0
;
;
.GLOBL	$$FORM				; Index for DBS support routines
.GLOBL	FORM$$				; Start of database format block
.GLOBL	DBSLDR				; Database loader
.GLOBL	LOCFLD				; DBSSUP - locates DBS field block
.GLOBL	FILINT,FIL.DR,FIL.KR,KEY$$B	; DBSSUP - 'FILEIO'
.GLOBL	CON.ST,CON.LI,CON.CO,CON.EX	; "CONIO"
.GLOBL	CON.LO,CON.ES			; "CONIO"
.GLOBL	WND.BL,WND.IN,WND.FL		; WNDIO
.GLOBL	FLD.OU,FLD.CK,FLD.IN,FLD.ZE	; FLDIO
;
;
;
	.PSECT	CODE			; OPEN CODE SECTION
;	------	----
;

.SBTTL	Macro Definitions
;
;
.MACRO	PRINT	STR=R0
	.GLOBL	CON.LO
	MOV	STR,R0
	CALL	CON.LO
.ENDM	PRINT
;
;
.MACRO	GTLIN	BUF=R0,STR
	.IF	NB,STR
		.GLOBL	CON.LO
		MOV	STR,R0
		CALL	CON.LO
	.ENDC
	MOV	BUF,R0
	.GLOBL	CON.LI
	CALL	CON.LI
.ENDM	GTLIN
;
;
.MACRO	DECODE	TABLE
	MOV	#TABLE,R1
	CALL	DECODE
.ENDM	DECODE
;
; ABORT is used to abort execution if CND is true; MES is printed out
;
.MACRO	ABORT	MES,CND,FILERR=NO,?LB,?LC
	.IF NB,CND
		B'CND	LB
		BR	LC
LB':	.ENDC
	.IIF IDN,FILERR,NO,	JSR	R1,ABORT
	.IIF DIF,FILERR,NO,	JSR	R1,ABORTF
	.ASCII	/'MES'/<200>
	.EVEN
	.IIF NB,CND,	LC':
.ENDM	ABORT
;

.SBTTL	INITIALIZATION SECTION
;
;
;	Initialize important parameters such as buffer size and name
;	of files to be used. We use the CONIO module to do I/O, so
;	must initialize this first.
;
START:	CALL	CON.ST			; initialize TTY interface
	PRINT	#7000$			; identify self
	TST	$$FORM			; is template linked in?
	BNE	100$			;  yes -> skip
	GTLIN	#TMPB1,#7010$		; get template name
	MOV	#TMPB1,R0		; R0 -> name
	CALL	DBSLDR			; load template
	MOV	R0,$$FORM		; save load address in pointer
100$:	JMP	ACCESS			; access data files
;
.NLIST	BIN
7000$:	.ASCIZ	/DBSINS RDM270683/
7010$:	.ASCII	/		DB Template: /<200>
.EVEN
.LIST	BIN
;

;
;	Time gain access to files used and to zero out variables needed
;	later, and set up system date in variable D4 for the user.
;
ACCESS:	CALL	FILINT			; open data base
	.DATE				; R0 =	date in RT-11 format
	BIC	#^C37777,R0		;	strip unwanted bits
	CLR	R1			; R1 =	0
	ASHC	#-5,R0			; R1 =	year count
	ADD	R1,R0			; R0 =	date in internal format
	MOV	R0,D.....+<4*2>		;	set up D4 with system date
	CALL	NEWPRG			;	enter a program
;
CMDLP:	CLR	RECNUM			;	start with record 0
	CLR	NUMSEL
	CALL	CON.ES
	PRINT	#7000$	
	MOV	#PRGBUF,INPRWD		;	pointer to input
	CALL	NEXT			;	execute program
	CALL	EDTPRG			;	exit program
	BR	CMDLP			;	done
7000$:	.ASCIZ	<0>
;
ABORT:	.PRINT	#7000$			;	header
	.PRINT	R1			;	reason for abort
	.PRINT	#7100$			;	trailer
	MOV	@#42,SP
	CMP	INPFTH,@#78.		;	valid pointer
	BHIS	1000$			;	 no --> skip
	MOV	INPFTH,$$XPOS		;	 yes -> point to error
1000$:	CALL	EDTPRG
	JMP	CMDLP
;
.nlist	bin
7000$:	.ASCII	<15><12>|DBSINS-error-|<200>
7100$:	.ASCIZ	|!|
.even
.list	bin
;

.SBTTL	Code - "NEXT"		...	Find Next Record to be Processed
;
;	In this section we read in the database records one at a time and
;	call PROCESS for each one. We skip to 1000$ when have no more
;	records to do.
;
NEXT:	INC	RECNUM			; Point to next record
	MOV	$$FORM,R0		; R0 -> FDT
	CMP	RECNUM,14(R0)		; all done?
	BHI	1000$			; yes -> skip
	MOV	RECNUM,R0		; R0 = record number
	CALL	FIL.KR			; read in keys for record
	TST	KEY$$B			; record in use?
	BEQ	NEXT			; no -> ignore it
	MOV	INPRWD,INPFTH		; reset fetch pointer
	CALL	PROCESS			; Process record
	BR	NEXT			; attempt find next record
;
;	Output number of records selected and then exit.
;
1000$:	CNAF	SSSSD,STRING=#7100$,NUMBER=NUMSEL
	PRINT	#7000$			;	show result
	RETURN
;
.NLIST	BIN
7000$:	.ASCII	/ Number selections: /
7100$:	.ASCIZ		/....../
.EVEN
.LIST	BIN
;
;

.SBTTL	Routine - "Process"	...	supervises the processing of a record
;
;  This routine is called to process the record whose number is in 'RECNUM'.
;  The routine loads the record into memory, and then executes the user
;  commands stored in the format file.
;
;
;
PROCES:	MOV	RECNUM,R0		; R0 =	record number
	CALL	FIL.DR			;	load record into memory
	CLR	P.ACPT			;	record not yet accepted
;
;	Use 700$ to decode format file and envoke required routines.
;
400$:	CALL	INPUT			; get next input character
	DECODE	700$			; decode via 700$, error = 600$
	BR	400$			; loop
;
;
;	Actioning routines	500$ envoked to terminate processing
;				570$ envoked if character to be ignored
;				600$ envoked if character is illegal
;
500$:	ADD	#2,SP			; discard 'process' return address
570$:	RETURN				; return to caller
600$:	ABORT	<PROCESS: illegal character in format file!>
;
;
;	Table used to decode input. Call routine when encounter char.
;
700$:	.WORD	'E+200,	500$		; end of format file
	.WORD	11,	570$		; ignore 'TB'
	.WORD	12,	570$		; ignore 'LF'
	.WORD	15,	570$		; ignore 'CR'
	.WORD	40,	570$		; ignore 'spaces'
	.WORD	';,	..COMM		; comment
	.WORD	')+200,	..SEND		; send output to terminal
	.WORD	'*+200,	..MARK		; mark rewind point in file
	.WORD	'?+200,	ARGINP		; input argument value	
	.WORD	'(+200,	FLDINP		; input name of a field	
	.WORD	'T+200,	..TEST		; test time
	.WORD	'L+200,	..LOAD		; load data time
	.WORD	0,	600$		; abort address
;

.SBTTL	Process Routine '..COMM'	...	Comment
;
;  Discard all input untill hit end of comment marker, a 'LF'!
;
..COMM:	CALL	INPUT			; get a character
	CMP	R0,#12			; hit a 'LF'?
	BNE	..COMM			; no -> loop
	RETURN				; loop
;
;

.SBTTL	Process Routine '..MARK'	...	mark rewind point
;
;  Load INPRWD with INPFTH so that will no rewind past this part of the
; program
;
..MARK:	MOV	INPFTH,INPRWD		; update pointer
	RETURN				; loop
;
;

.SBTTL	Process Routine '..SEND'	...	Send output to terminal
;
; All characters upto but excluding terminator are to be sent to terminal.
;
..SEND:	CALL	INPUT			; get character
	CMP	R0,#'$+200		; terminator?
	BEQ	1000$			; yes -> skip
	CALL	CON.CO			; output
	BR	..SEND			; loop
1000$:	RETURN				; exit
;
;

.SBTTL	Process Routine '..TEST'	...	Carry out a test
;
;  This code is called by 'PROCESS' to carry out an 'acceptance' test. If
;  the record passes all the tests we increment the 'P.ACPT' flag.
;
;
..TEST:	CLR	(PC)+			; assume all ok
100$:	.WORD	0			; 0 -> test ok, 1 -> fail
;
400$:	CALL	INPUT			; get next input character
	DECODE	700$			; decode via 700$, error = 600$
	BR	400$			; loop
;
;	Actioning routines	500$ envoked to terminate processing
;				510$ invoked to UNSET/CLEAR/RESET P.ACPT
;				570$ envoked if character to be ignored
;				600$ envoked if character is illegal
;
500$:	ADD	#2,SP			; discard 'process' return address
	TST	100$			; did we fail the test?
	BNE	570$			; yes -> skip
	MOV	#1,P.ACPT		; indicate that passed
	RETURN				; return to caller
510$:	CLR	P.ACPT			; clear P.ACPT flag
570$:	RETURN				; return to caller
600$:	ABORT	<..TEST:illegal primary argument!>
;
;
;	Table used to decode input. Call routine when encounter char.
;
700$:	.WORD	'$+200,	500$		; end of test
	.WORD	'C+200,	510$		; CLEAR P.ACPT FLAG
	.WORD	11,	570$		; ignore 'TB'
	.WORD	12,	570$		; ignore 'LF'
	.WORD	15,	570$		; ignore 'CR'
	.WORD	40,	570$		; ignore 'spaces'
	.WORD	';,	..COMM		; comment
	.IRPC	X,^|'"[<0123456789NL@!D|; .... argument ....
	 .WORD	''X,	1000$		;	-> decode out
	.ENDR				; ..................
	.WORD	0,	600$		; eot
;

;
; Detected start of an argument. Use ARGDCD to load arguments value into
; TMPB1, and then look for 'operator'.
;
;
;	Load contents of specified field into buffer.
;
1000$:	MOV	#TMPB1,R5		; R5 -> storage buffer
	CALL	ARGDCD			;	load buffer with argument
;
;
;	Decode out next character in format file.
;
1400$:	CALL	INPUT			; get next input character
	DECODE	1700$			; decode via 1700$, error = 1600$
	BR	1400$			; loop
;
;	Actioning routines		1570$ envoked if character is ignored
;					1600$ envoked if character is illegal
;
1570$:	RETURN				; ignore character
1600$:	ABORT	<..TEST:expecting operator!>
;
;
;	Table used to decode input. Call routine when encounter char.
;
1700$:	.WORD	11,	1570$		; ignore 'TB'
	.WORD	40,	1570$		; ignore 'spaces'
	.WORD	'.,	2000$		; start of operator
	.WORD	0,	1600$		; eot
;

;
;  Have encountered start of operator character. Use 'OPRDCD' to decode
;  operator; this routine returns an equivalent 'BR .+2' conditional
;  instruction in R0 that we use to patch location 3400$.
;
;
2000$:	ADD	#2,SP			; discard 1000$ address
	CALL	OPRDCD			; decode out operator
	MOV	R0,3400$		; patch 'br' location
;
;
;	Decode out next character.
;
2400$:	CALL	INPUT			;	get char
	DECODE	2700$			;	decode input, error = 2600$
	BR	2400$			;	loop
;
;	Actioning routines	2570$ envoked if character to be ignored
;				2600$ envoked if character is illegal
;
2570$:	RETURN				; ignore character
2600$:	ABORT	<..TEST:illegal secondary argument!>
;
;	Table used to decode input. Call routine when encounter char.
;
2700$:	.WORD	11,	2570$		; ignore 'TB'
	.WORD	40,	2570$		; ignore 'spaces'
	.IRPC	X,^|'"[<0123456789NL@!D|; .... argument ....
	 .WORD	''X,	3000$		;	-> decode out
	.ENDR				; ..................
	.WORD	0,	2600$			; eot
;
;

;
; 	Hit second argument. Load it into TMPB2 ready for comparision.
;
3000$:	ADD	#2,SP			; clear '2000$' phase address
	MOV	#TMPB2,R5		; R5 -> buffer
	CALL	ARGDCD			; get argument
;
;
;	Compare two args and set 100$ flag iff test fails. Note that
;	location 3400$ patched by 2000$ phase so that branch over next
;	instruction iff test passes.
;
	MOV	#TMPB1,R0		; R0 -> arg1
	MOV	#TMPB2,R1		; R1 -> arg2
	MOV	#100$,R5		; R5 -> flag
	CALL	ARGCMP			;	compare arg1,arg2
3400$:	NOP				; **** changed by 2000$ ****
;;	INC	(R5)			;	set fail flag
	BR	3700$			;	get here if fail
	RETURN				;	get here if suceed
;
;
; get here if test fails; set fail flag and abort set of tests
;
3700$:	INC	(R5)			;	set fail flag
3710$:	CALL	INPUT			;	discard
	CMPB	R0,#200+'$		;	 rest of
	BNE	3710$			;	  test instructions
	JMP	500$			;	exit test code
;
;

.SBTTL	Process Routine '..LOAD'	...	Load data into sorter file
;
;
..LOAD:	TST	P.ACPT			; load data?
	BNE	70$			;  yes -> skip
10$:	CALL	INPUT			;; no --> discard
	CMPB	R0,#'$+200		;;         rest of load
	BNE	10$			;;          instructions
	RETURN				;; all done
;
70$:	MOV	RECNUM,TMPB2		; TMPB2 bytes 0:1 = record num
	MOV	#TMPB2+2,(PC)+		; set up store pointer
100$:	.WORD	0			; storage pointer
;
400$:	CALL	INPUT			; get next input character
	DECODE	700$			; decode via 700$, error = 600$
	BR	400$			; loop
;
;	Actioning routines	500$ envoked to terminate processing
;				570$ envoked if character to be ignored
;				600$ envoked if character is illegal
;
500$:	ADD	#2,SP			; discard 'process' return address
	TST	P.ACPT			; load data?
	BEQ	570$			;  no -> skip
	CMP	100$,#TMPB2+2		; any data to load?
	BEQ	570$			; no -> skip
	CLRB	@100$			;	set eos
	.PRINT	#TMPB2+2		;	display data
	INC	NUMSEL			;	one more entry
570$:	RETURN				; return to caller
600$:	ABORT	<..LOAD:illegal argument!>
;

;
;	Table used to decode input. Call routine when encounter char.
;
700$:	.WORD	'$+200, 500$		; finish
	.WORD	11,	570$		; ignore 'TB'
	.WORD	12,	570$		; ignore 'LF'
	.WORD	15,	570$		; ignore 'CR'
	.WORD	40,	570$		; ignore 'spaces'
	.WORD	';,	..COMM		; comment
	.IRPC	X,^|[<0123456789"'NL@!D|; ..... arguments .....
	 .WORD	''X,	1000$		;	load buffer
	.ENDM				; .....................
	.WORD	'^+200,	2000$		; convert upper to lower case
	.WORD	'~+200,	3000$		; NULL if string all spaces
	.WORD	'<+200,	4000$		; trim trailing spaces
	.WORD	'>+200,	4100$		; right justify data
	.WORD	'-+200,	4200$		; remove specified character
	.WORD	':+200,	5000$		; position load pointer
	.WORD	'S+200,	6000$		; switch tag/data
	.WORD	'#+200,	6100$		; convert number to ascii
	.WORD	0,	 600$		; eot
;

;
;	Use 'ARGDCD' to load value of next argument into TMPB1 and then
;	append to data in TMPB2.
;
1000$:	MOV	#TMPB1,R5		; R5 -> buffer
	CALL	ARGDCD			;	decode argument
	MOV	100$,R0			; R0 ->	buffer
	MOV	#TMPB1+4,R1		; R1 -> data
	MOV	TMPB1+2,R2		; R2 =	number bytes
	BEQ	1700$			;	skip no data
1100$:	MOVB	(R1)+,(R0)+		;	copy
	SOB	R2,1100$		;	loop till all done
	MOV	R0,100$			;	save pointer
1700$:	RETURN				;	all done
;
;

;
;	This routine converts all lower case characters in the buffer
;	into uppercase characters.
;
2000$:	MOV	100$,R2			; R2 ->	end data
	SUB	#TMPB2+2,R2		; R2 =	number characters in buffer
	BEQ	2240$			;	none -> skip
	MOV	#TMPB2+2,R1		; R1 ->	data
2100$:	CMPB	(R1)+,#140		;	LC?
	BLOS	2200$			;	 no -> skip
	BICB	#40,-1(R1)		;	 convert to LC
2200$:	SOB	R2,2100$		;	loop
2240$:	RETURN				;	exit
;

;
;	This routine resets the load buffer iff the buffer is full
;	of spaces.
;
3000$:	MOV	100$,R2			; R2 ->	end data
	SUB	#TMPB2+2,R2		; R2 =	number characters in buffer
	BEQ	3200$			;	none -> skip
	MOV	#TMPB2+2,R1		; R1 ->	data
3100$:	CMPB	(R1)+,#40		;	space?
	BNE	3200$			;	 no -> skip
	SOB	R2,3100$		;	loop
	MOV	#TMPB2+2,100$		;	reset pointer
3200$:	RETURN				;	exit
;

;
;	This routine deletes/trims all trailing blanks from
;	the end of the currently loaded sting.
;
4000$:	MOV	100$,R0			; R0 -> eos
4020$:	CMP	R0,#TMPB2+2		;	at start of buffer
	BEQ	4040$			;	 yes -> skip
	CMPB	-(R0),#40		;	is it a trailing space?
	BEQ	4020$			;	yes -> discard
	INC	R0			;	point to next free byte
4040$:	MOV	R0,100$			;	save pointer
	RETURN				;	all done
;
;
;	This routine RIGHT JUSTIFIES the string by removing trailing
;	spaces from the right and inserting them at the front.
;
4100$:	MOV	100$,R1			; R1 ->	eos'
	CALL	4000$			;	trim string
	MOV	R1,100$			;	untrim string
4120$:	CMP	R0,#TMPB2+2		;	at start of buffer?
	BEQ	4160$			;	 yes -> skip
	MOVB	-(R0),-(R1)		;	 justify to right
	BR	4120$			;	loop
4160$:	CMP	R1,#TMPB2+2		;	at start of buffer?
	BEQ	4170$			;	 yes -> all done
	MOVB	#40,-(R1)		;	justify with spaces
	BR	4160$			;	loop
4170$:	RETURN				; 	all done
;
; 	This routine is used to remove a specified characters from the load
;	buffer.
;
4200$:	CALL	INPUT			; R0 =	character to remove
	MOV	#TMPB2+2,R1		; R1 ->	source
	MOV	R1,R2			; R2 ->	destination
4210$:	CMP	R1,100$			;	done?
	BHIS	4270$			;	 yes -> exit
	CMPB	(R1)+,#40		;	space?
	BEQ	4210$			;	 yes ->	ignore
	MOVB	-1(R1),(R2)+		;	 no -->	copy it
	BR	4210$			;		loop
4270$:	MOV	R2,100$			;	save eos pointer
	RETURN				;	done
;

;
; This routine is called to either TRIM or PAD the contents of the
; load buffer to ensure that the next item loaded goes into a specified
; byte; the POSITION operation requires a numeric argument that indicates
; the desired position.
;
5000$:	CALL	INPUT			; R0 =	first character of argument
	MOV	#TMPB1,R5		; R5 -> buffer
	CALL	ARGDCD			;	decode argument
	TST	TMPB1			;	is type = 0 (ie numeric)
	BNE	5070$			;	 no -> error
	MOVB	TMPB1+5,R1		; R1 =	desired position
	BEQ	5070$			;	 zero -> illegal
	BIC	#^C377,R1		;	ensure > 256
	ADD	#TMPB2+1,R1		; R1 =	tab position
	MOV	100$,R0			; R0 ->	old eos
	MOV	R1,100$			;	setup new eos
5030$:	CMP	R0,R1			;	need padding?
	BHIS	5040$			;	 no -> skip
	MOVB	#40,(R0)+		;	pad buffer
	BR	5030$			;	loop
5040$:	RETURN				;	done
5070$:	ABORT	<..LOAD:invalid POSITION argument!>
;

;
;	This routine switchs the connents of the tag field with
;	the first two data bytes
;
6000$:	MOV	TMPB2,R0		; R0 =	tag
	MOV	TMPB2+2,R1		; R1 =	data bytes 1,2
	SWAB	R0			;	make tag bytes
	MOV	R0,TMPB2+2		;	 data bytes
	SWAB	R1			;	make data bytes
	MOV	R1,TMPB2		;	 tag bytes
	RETURN				;	exit	
;
;	This routine removes the last two bytes from the load buffer
;	and replaces them with ascii decimal string that is the
;	equivalent to the 16 bit number they represent.
;
6100$:	MOV	100$,R1			; R1 ->	EOD
	MOVB	-(R1),R2		; R2 =	LOB
	BIC	#^C377,R2		;	discard unwanted bits
	MOVB	-(R1),R0		; R0 =	HOB
	BIC	#^C377,R0		;	discard unwanted bits
	SWAB	R0			;	merge together
	BIS	R0,R2			;	 HOB and LOB
	CMP	R1,#TMPB2+2		;	load buffer held data?
	BLO	6170$			;	 no --> abort
	CNAF	SSSSD			;	 yes ->	convert to ascii
	MOV	R1,100$			;		save pointer
6170$:	RETURN				;	done
;

.SBTTL	Primitive - 'DECODE'	...	used to decode input
;
;	Routine called with R1 pointing to table. Table consists
;	of two word entries. Control passed to routine whose address
;	is in secon word iff R0 matchs first word in entry. Table
;	is terminated with an entry that has first word of 0. If cannot
;	find matching entry in table, pass control to routine whose
;	address is specified in terminating (null word 1) entry.
;
DECODE:	TST	(R1)			; eot?
	BEQ	1200$			; yes -> abort
	CMP	R0,(R1)			; match?
	BEQ	1200$			; yes -> skip
	ADD	#4,R1			; bump pointer
	BR	DECODE			; loop
;
1200$:	JMP	@2(R1)			; pass over control
;

.SBTTL	PRIMITIVE - "ARGDCD"	...	convert argument from ascii to bin
;
;
; This routine performs the function equivalent to fortrans DECODE statement,
; in that it converts an argument from ascii into its equivalent form. The
; routine uses the 'INPUT' routine the obtain all but the first character
; that makes up the argument; the first character is passed in R0. The entry
; conditions are
;
;		R0	holds the first chracter in argument
;		R5	holds address of buffer to hold result.
;
; All registers other than R0 are preserved through the routine. At exit, the
; buffer pointed to by R5 holds the following data
;
;	Buffer	.Word	type	; type of argument
;		.Word	size	; number bytes in arguments value
;		.Blkb	size	; value of argument
;
; The 'type' code indicates the type of data returned. Valid types are 0
; for 2 byte numeric values and 2 for strings of bytes. The 'size' entry
; indicates the number of bytes that make up the value. In all cases, the
; argument values are arranged so that the most significant byte is first.
;
;
; The first character of the argument, (passed in R0) is used to determ how
; the argument is to be interpreted. Valid first characters are
;
;	LEADIN		TYPE OF				TERMINATING
;	CHARACTER	ARGUMENT			CHARACTER
;
;	any digit	numeric constant		non-digit
;	!		date constant			non-digit after year
;	' or "		literal constant		leadin char
;	[ or <		contents of database field	] or >
;	N		numeric variable
;	D		date variable
;	L		literal variable
;	@		contents on field, name in LV
;
;

;
;
; Save registers, and then use '700$' table to determine which routine is to
; be called to process argument whose first character is in R0.
;
;
ARGDCD:	.PUSH	<R1,R2,R3,R4,R5>		; save
	DECODE	700$				; decode via leadin char
	.POP	<R5,R4,R3,R2,R1>		; restore
	RETURN					; exit
;
;
; The 700$ table indicates which routine is to be called to process character
; in R0 (and the rest of the argument). If leadin character not in table we
; abort via 600$, print an error message!
;
600$:	ABORT	<ARGDCD:illegal argument!>
700$:	.IRPC	X,<0123456789>		; .... digits ....
	 .WORD	''X,	1000$		; 	number
	.ENDR				; ................
	.WORD	'!,	1400$		;	date constant
	.IRPC	X,<"'>			; .... quaotes ....
	 .WORD	''X,	2000$		;	literal
	.ENDR				; .................
	.IRPC	X,^|<[|			; .... brackets ....
	 .WORD	''X,	3000$		;	field
	.ENDR				; ..................
	.WORD	'N,	4000$		; numeric variable
	.WORD	'D,	4400$		;	date variable
	.WORD	'L,	5000$		; literal variable
	.WORD	'@,	6000$		; indirrection on LV
	.WORD	'D,	1400$		; DATE
	.WORD	0,	600$		; eot
;

;
; Get here if have to evaluate a numeric constant. The binary value of the
; constant is returned in the buffer @R5. The constant is terminated by any
; non-digit character. Use DCDNUM to return value in R3 and delimiter in R0.
;
1000$:	MOV	R0,INPUT$		; force digit 1 back into stream
	MOV	#INPUT,R4		; R4 =	address get char routine
	CALL	DCDNUM			; R3 =	numeric value (R0=delimiter)
	MOV	R0,INPUT$		; rewind over char
	CLR	(R5)+			; type = 0 (numeric)
	MOV	#2,(R5)+		; size = 2 bytes
	MOVB	R3,1(R5)		; return LOB
	SWAB	R3			; get HOB
	MOVB	R3,(R5)			; return LOB
	RETURN
;
; Get here if have to evaluate a DATE constant. The binary value of the 
; constanti s returned in the buffer @R5. The constant format is DD-MM-YY 
; where the DD, MM and YY fields are numeric constants. Use DCDDAT to return
; value of date in R3 and the delimiter that terminated the YY field in R0.
;
1400$:	MOV	#INPUT,R4		; R4 =	address get char routine
	CALL	DCDDAT			; R3 =	numeric value (R0=delimiter)
	MOV	R0,INPUT$		; force delimiter back into stream
	CLR	(R5)+			; type = 0 (numeric)
	MOV	#2,(R5)+		; size = 2 bytes
	MOVB	R3,1(R5)		; return LOB
	SWAB	R3			; get HOB
	MOVB	R3,(R5)			; return LOB
	RETURN
;

;
;
;	Are here to decode a literal constant. The constant consists of all
;	characters upto but excluding the terminating delimiter, that is
;	the same as the leadin character!
;
2000$:	MOV	R5,R4			; R4 -> buffer
	ADD	#4,R4			; R4 -> start of buffer data area
	MOV	R0,R3			; R3 =	delimiter
	MOV	#100,R2			; R2 =	max allowed sting size
2100$:	CALL	INPUT			; get character
	CMP	R0,R3			; hit delimiter?
	BEQ	2200$			; yes -> skip
	CMP	R0,#15			; hit <CR>?
	BEQ	2600$			; yes -> error
	MOVB	R0,(R4)+		; save char
	SOB	R2,2100$		; loop till got all chars
	BR	2600$			; illegal literal
;
2200$:	SUB	R5,R4			; R4 =	total lenght
	SUB	#4,R4			; R4 =	number chars
	MOV	#2,(R5)+		;	set up TYPE
	MOV	R4,(R5)+		;	set up size
	RETURN				;	all done
;
;
2600$:	ABORT	<ARGDCD:illegal literal constant!>
;

;
;
;	Are here to return the value the data stored in a database record.
;	The argument being decoded is the actual name of the field. We must
;	get the name, translate it into the offset of the field descriptor
;	entry, and then use this to load the actual data. A long process, but
;	worth it in the end.
;
;
;
;	Get name of field.
;
3000$:	MOV	R5,R4			; R4 ->	buffer
	MOV	#100,R2			; R2 =	max allowed name
	MOV	#'],R3			; assume delimiter is '['
	CMP	R0,#'[			; was '[?
	BEQ	3100$			; yes -> ok
	MOV	#'>,R3			; must have been a '<'
3100$:	CALL	INPUT			; get character
	CMP	R0,R3			; hit delimiter?
	BEQ	3200$			; yes -> skip
	MOVB	R0,(R4)+		; save char
	SOB	R2,3100$		; loop till got all name
	BR	3600$			; illegal name
;
;	Locate form entry for field.
;
3200$:	CLRB	(R4)			;	terminate name
3277$:	MOV	R5,R1			; R1 ->	name
	MOV	$$FORM,R0		; R0 ->	form
	CALL	LOCFLD			;	find correct entry
	BCS	3610$			;	failed -> abort
;
;	Call appropriate routine to load field data into buffer.
;
	MOV	(R1),R0			; R0 =	flags
	BIC	#^C7,R0			; R0 =	type
	ASL	R0			; R0 =	type*2
	JMP	@3700$(R0)		;	return data
;
;

;
;	Code to load contents of numeric field.
;
3300$:	CLR	(R5)+			; operator type = 0 (numeric)
	MOV	#2,(R5)+		; lenght = 2 bytes
	MOV	4(R1),R0		; R0 = offset to data
	ADD	@$$FORM,R0		; R0 -> data
	MOVB	(R0),1(R5)		; return lob
	MOVB	1(R0),(R5)		; return hob
	RETURN				; exit
;
;	Code to load contents of an ascii field.
;
3400$:	MOV	#2,(R5)+		; operator type = 2 (string)
	MOV	2(R1),R2		; R2 = number bytes in string
	MOV	R2,(R5)+		; set up in buffer
	MOV	4(R1),R0		; R0 = offset to data
	ADD	@$$FORM,R0		; R0 -> data
3410$:	MOVB	(R0)+,(R5)+		; return lob
	SOB	R2,3410$		; loop till all done
	RETURN				; exit
;
;
;	Abort points.
;
3600$:	ABORT	<ARGDCD:illegal field name!>
3610$:	ABORT	<ARGDCD:cannot locate field!>
3620$:	ABORT	<ARGDCD:illegal type of field!>
;
;
;	Index into table via field type to get address load routine.
;
3700$:	.WORD	3400$			;	ascii type
	.WORD	3400$			;	ascii type
	.WORD	3400$			;	ascii type
	.WORD	3300$			;	numeric	(number)
	.WORD	3300$			;	numeric	(date)
	.WORD	3610$			;	illegal type
	.WORD	3610$			;	illegal type
;

;
;	Are to return the contents of a numeric variable. The next character
;	must be a digit that indicates which variable is requested.
;
4000$:	CALL	INPUT			; get next character
	SUB	#'0,R0			; convert digit to binary
	CMP	R0,#9.			; valid index?
	BHI	4060$			; invalid index -> skip
	ASL	R0			; 2*index -> R0
	ADD	#N.....,R0		; R0 -> value
4050$:	CLR	(R5)+			; type = 0
	MOV	#2,(R5)+		; size = 2 bytes
	MOVB	1(R0),(R5)+		; setup hob
	MOVB	 (R0),(R5)+		; setup lob
	RETURN				; all done
4060$:	ABORT	<ARGDCD:invalid numeric variable!>
;
;
4400$:	CALL	INPUT			; get next character
	SUB	#'0,R0			; convert digit to binary
	CMP	R0,#4.			; valid index?
	BHI	4460$			; invalid index -> skip
	ASL	R0			; 2*index -> R0
	ADD	#D.....,R0		; R0 -> value
	BR	4050$			;	return value
4460$:	ABORT	<ARGDCD:invalid DATE variable!>
;

;
;	This code returns the contents of a literal variable. The next
;	character is a digit that indicates which variable is wanted. The
;	literals are stored in 128 byte buffers starting at "L.....", and
;	the first word of each buffer is the number of bytes in the
;	variable. Note that a null variable is returned as one space.
;
5000$:	CALL	INPUT			; get next character
	SUB	#'0,R0			; convert digit to binary
	CMP	R0,#3.			; valid index?
	BHI	5600$			; invalid index -> skip
	ASL	R0			; 002*index -> R0
	ASL	R0			; 004*index -> R0
	ASL	R0			; 008*index -> R0
	ASL	R0			; 016*index -> R0
	ASL	R0			; 032*index -> R0
	ASL	R0			; 064*index -> R0
	ASL	R0			; 128*index -> R0
	ADD	#L.....,R0		; R0 -> value
	MOV	#2,(R5)+		; type = 2
	MOV	(R0)+,R2		; R2 = number bytes
	BNE	5200$			;	none-zero -> skip
	MOV	#1,R2			;	force lenght = 1
	MOVB	#40,(R2)		;	force to a space
5200$:	MOV	R2,(R5)+		;	return lenght
5300$:	MOVB	(R0)+,(R5)+		; copy data
	SOB	R2,5300$		; loop
	CLRB	(R5)			; terminate
	RETURN				; all done
5600$:	ABORT	<ARGDCD:invalid literal  variable!>
;
;

;
;	Here to return the data stored in the field whose name is store
;	in a literal variable.
;
6000$:	.PUSH	R5			; save pointer
	CALL	INPUT			; get next character
	CMP	R0,#'L			; must be literal
	BNE	6600$			; not -> error
	CALL	INPUT			; get next character
	SUB	#'0,R0			; convert digit to binary
	CMP	R0,#3.			; valid index?
	BHI	6610$			; invalid index -> skip
	ASL	R0			; 002*index -> R0
	ASL	R0			; 004*index -> R0
	ASL	R0			; 008*index -> R0
	ASL	R0			; 016*index -> R0
	ASL	R0			; 032*index -> R0
	ASL	R0			; 064*index -> R0
	ASL	R0			; 128*index -> R0
	ADD	#L.....,R0		; R0 -> value
	MOV	(R0)+,R2		; R2 = number bytes
	BEQ	6620$			;	none -> error
6300$:	MOVB	(R0)+,(R5)+		; copy data
	SOB	R2,6300$		; loop
	CLRB	(R5)			; terminate
	.POP	R5			; restore pointer
	JMP	3277$			; get field data
6600$:	ABORT	<ARGDCD:no literal variable after @ operator!>
6610$:	ABORT	<ARGDCD:invalid literal index number!>
6620$:	.POP	R5			; restore R5
	MOV	#2,(R5)+		; assume type = string
	CLR	(R5)+			; lenght = 0
	RETURN				; exit
;;****	ABORT	<ARGDCD:no data in literal variable!>
;

.SBTTL	Primitive - "ARGINP"	...	Input argument value
;
;
;	This routine when called allows the oprtator to input a value
;	via the terminal for numeric, date or literal variables. The
;	Literal variables have names of the form 'L?', while numeric
;	variable names are 'N?', where ? is a digit 0-9. Date variable
;	names are of the format D0-D3.
;
;	Numeric variable N?/D? are stored as 16 bit numbers in the N.....
;	and D..... vectors at word offset ?.
;
;	Literal variable L? is allocated a 128 byte buffer at byte
;	offset 128*? from L.....; the first word is the number of
;	characters stored in the variable. The characters are stored
;	sequentially in the following bytes.
;
;	On exit R0 points to the variable that has been input.
;
ARGINP:	.PUSH	<R1,R2,R3,R4,R5>	; save
	CALL	INPUT			; get variable name
	DECODE	700$			; decode time
	.POP	<R5,R4,R3,R2,R1>	; restore
	RETURN				; exit
;
;
700$:	.WORD	'N,	1000$		; numeric variable
	.WORD	'D,	1400$		; date variable
	.WORD	'L,	2000$		; literal variable
	.WORD	 0,	 600$		; eot
;
600$:	ABORT	<ARGINP:invalid variable name!>
610$:	ABORT	<ARGINP:invalid variable index!>
;
;

;
;	Are here to input a number from TTY and store its value in N?.
;
1000$:	CALL	INPB1			;	get input
	CALL	DCDNUM			; R3 =	number's value
	CALL	INPUT			; R0 =	variable index
	SUB	#'0,R0			; R0 =	variable number
	CMP	R0,#9.			;	valid variable number?
	BHI	610$			;	 no -> abort
	ASL	R0			; R0 =	variable offset
	ADD	#N.....,R0		; R0 ->	storage area
	MOV	R3,(R0)			;	save value
	RETURN				;	all done
;
;	Are here to input a date from TTY and store its value in D?.
;
1400$:	CALL	INPB1			;	get input
	CALL	DCDDAT			; R3 =	date's value
	CALL	INPUT			; R0 =	variable index
	SUB	#'0,R0			; R0 =	variable number
	CMP	R0,#3			;	valid variable number?
	BHI	610$			;	 no -> abort
	ASL	R0			; R0 =	variable offset
	ADD	#D.....,R0		; R0 ->	storage area
	MOV	R3,(R0)			;	save value
	RETURN				;	all done
;

;
;	We are here to input a literal string from the terminal.
;
2000$:	CALL	INPUT			; get next character
	SUB	#'0,R0			; convert digit to binary
	CMP	R0,#3.			; valid index?
	BHI	610$			; invalid index -> skip
	ASL	R0			; 002*index -> R0
	ASL	R0			; 004*index -> R0
	ASL	R0			; 008*index -> R0
	ASL	R0			; 016*index -> R0
	ASL	R0			; 032*index -> R0
	ASL	R0			; 064*index -> R0
	ASL	R0			; 128*index -> R0
	ADD	#L.....+2,R0		; R0 -> buffer
	MOV	R0,R5			; R5 -> buffer
	MOV	#TMPB1,R0		; R0 -> IO buffer
	CALL	CON.LI			;	get input
	MOV	#TMPB1,R0		; R0 ->	source
	MOV	R5,R1			; R1 =	destination
2400$:	MOVB	(R0)+,(R1)+		;	copy
	BNE	2400$			;	loop
	SUB	R5,R1			; R1 =	size
	DEC	R1			;	ignore <NULL>
	CMP	R1,#126.		;	valid size?
	BHIS	2600$			;	too large -> abort
	MOV	R5,R0			; R0 ->	variable
	MOV	R1,-(R5)		;	setup size
	RETURN				; all done
2600$:	ABORT	<ARGINP:literal string too long!>
;
;

.SBTTL	Primitive - "FLDINP"	...	Input field name into litaral variable
;
; This routine is invoked to allow the operator to input the name of a field
; into a literal variable. It function is similar to ARGINP. The difference
; is that only a literal (L?) variable may be specified and the literal
; is checked to insure it is a valid field name.
;
;
FLDINP:	.PUSH	<R1,R2,R3,R4,R5>	; 	save
	CALL	INPUT			; R0 =	1st letter of variable name
	CMP	R0,#'L			;	must be an L
	ABORT	<FLDINP:invalid variable>,NE
	CALL	INPUT			; R0 =	2nd character of var name
	SUB	#'0,R0			;	convert digit to binary
	CMP	R0,#3.			;	must be a 0-3
	ABORT	<FLDINP:invalid variable>,HI
	ASL	R0			; R0 =	002*index
	ASL	R0			; R0 =	004*index
	ASL	R0			; R0 =	008*index
	ASL	R0			; R0 =	016*index
	ASL	R0			; R0 =	032*index
	ASL	R0			; R0 =	064*index
	ASL	R0			; R0 =	128*index
	ADD	#L.....+2,R0		; R0 -> buffer
	MOV	R0,R5			; R5 -> buffer
	BR	2100$			;	go get field name
;
2000$:	MOV	#7000$,R0		; R0 ->	warning message
	CALL	CON.LO			;	display it
2100$:	MOV	#TMPB1,R0		; R0 -> IO buffer
	CALL	CON.LI			;	get input
	MOV	#TMPB1,R1		; R1 ->	input
	MOV	$$FORM,R0		; R0 ->	template
	CALL	LOCFLD			;	valid field name?
	BCS	2000$			;	 no --> try again
;
	MOV	#TMPB1,R0		; R0 ->	source
	MOV	R5,R1			; R1 =	destination
2400$:	MOVB	(R0)+,(R1)+		;	copy
	BNE	2400$			;	loop
	SUB	R5,R1			; R1 =	size
	DEC	R1			;	ignore <NULL>
	CMP	R1,#126.		;	valid size?
	BHIS	2600$			;	too large -> abort
	MOV	R5,R0			; R0 ->	variable
	MOV	R1,-(R5)		;	setup size
	.POP	<R5,R4,R3,R2,R1>	; restore
	RETURN				; all done
2600$:	ABORT	<FLDINP:literal string too long!>
;
.nlist bin
7000$:	.ASCII	|FLDINP:invalid field name?|<15><12>
	.ASCII	|         Enter FIELD name: |<200>
.even
.list bin

.SBTTL	Primitive - "INPB1"	...	input line of text into TMPB1
;
;	This primitive is called to get a line of input from the console,
;	and to set up R4 with the address of a routine that will return the
;	line input a character at a time in R0.
;
INPB1:	MOV	#TMPB1,R0		; R0 -> IO buffer
	CALL	CON.LI			;	input -> buffer
	MOV	#TMPB1,7700$		;	setup fetch pointer
	MOV	#7400$,R4		; R4 =	address GETCHAR routine
	RETURN				;	all done for now
7400$:	MOVB	@7700$,R0		; R0 =	next character
	BEQ	7410$			;	eos -> skip
	INC	7700$			;	up pointer
7410$:	RETURN				;	exit
7700$:	.WORD	0			;	fetch pointer
;

.SBTTL	Primitive - 'DCDNUM'	...	decode numeric string
;
;
; This primitive is called with R4 holding the address of an 'INPUT' routine
; that returns the next character in a string that has to be decoded in R0.
; The function of the routine is to decode the numeric string returning the
; binary value equivalent to the (decimal) numeric string in R3. The delimiter
; that terminated the decoding is passed back in R0, and all other registers
; are preseved.
;
DCDNUM:	CLR	R3			; R3 =	accumulator
1000$:	CALL	@R4			; R0 =	next character
	SUB	#'0,R0			; R0 =	binary value of digit
	CMP	R0,#9.			;	is legal decimal digit?
	BHI	1700$			;	 no -> skip
	MUL	#10.,R3			; R3 =	total
	ADD	R0,R3			;	add in new digit
	BR	1000$			;	loop
1700$:	ADD	#'0,R0			; R0 =	terminating delimiter
	RETURN				;	exit
;
;

.SBTTL	Primitive - 'DCDDAT'	...	Decode date string
;
; This primitive is called with R4 holding the address of an 'INPUT' routine
; that returns the next character in a string that has to be decoded in R0.
; The function of the routine is to decode a DATE (DD-MM-YY) string and to
; it equivalent binary value in R3. The delimiter that terminated the 
; decoding is passed back in R0, and all other registers are preseved. The
; date format is
;				year	= bits 15-11 to base 1972
;				month	= bits  8-5
;				day	= bits  4-0
;
DCDDAT:	.PUSH	R2			;	save R2, needed as work reg
	CLR	R2			; R2 =	accumulator
	CALL	DCDNUM			; R3 =	day count
	JSR	R4,2400$		;	process day count
	.WORD	0.,31.,1.		;	base, max, shift
	CALL	DCDNUM			; R3 = 	month count
	JSR	R4,2400$		;	process month count
	.WORD	0.,12.,32.		;	base, max, shift
	CALL	DCDNUM$			; R3 =	year count
	JSR	R4,2410$		;	process year count
	.WORD	72.,31.,2048.		;	base, max, shift
	MOV	R2,R3			; R3 =	result
	.POP	R2			;	restore R2
	RETURN				;	exit
;
;	This code is called with a JSR R4 and uses the three words after
;	the call points as arguments needed to merge in the value passed
;	in R3 to the result being bulit in R2.
;
2400$:	CMPB	R0,#'-			;	valid delimieter?
	BNE	2600$			;	 no  -> abort
2410$:	TST	R3			;	count = 0?
	BEQ	2600$			;	 yes -> abort
	SUB	(R4)+,R3		;	convert to required base
	CMP	R3,(R4)+		;	count in range?
	BHI	2600$			;	 no -> abort
	MUL	(R4)+,R3		;	shift bits into niblet
	BIS	R3,R2			;	merge bits
	RTS	R4			;	exit
2600$:	ABORT	<DCDDAT:invalid DATE string!>
;

.SBTTL	Primitive - "ARGCMP"	...	comapre arguments
;
;
; The following routine is called to compare the arguments stored in the
; buffers pointed to by R0 and R1 at entry. These buffers are structured as 
; follows
;		.WORD	Arg type
;		.WORD	Arg size
;		.BLKW	value
;
; The routine sets the PSW at exit as if a CMP ARG0,ARG1 instruction was done.
; Registers R0, R1 and R2 are volitile.
;
;
;
;	Dispatch control to correct code for argument types.
;
ARGCMP:	CMP	(R0),(R1)			; same types?
	BNE	600$				; no -> abort
	MOV	#700$-4,R2			; R2 -> table
100$:	ADD	#4,R2				; R2 ->	next entry
	TST	(R2)				; eot?
	BMI	610$				; yes -> error
	CMP	(R0),(R2)			; match?
	BNE	100$				; no -> loop
	JMP	@2(R2)				; envoke test code
;
600$:	ABORT	<ARGCMP:argument types different!>
610$:	ABORT	<ARGCMP:illegal argument type>
700$:	.WORD	00,	1000$			; numeric
	.WORD	02,	2000$			; string
	.WORD	-1,	 610$			; eot
;
;
;	This code is called to compare two numeric arguments.
;
1000$:	CMPB	4(R0),4(R1)		; MSB same?
	BNE	1100$			;  no -> done
	CMPB	5(R0),5(R1)		; LSB same?
1100$:	RETURN				; all done
;
;	This code is called to compare two ascii strings.
;
2000$:	ADD	#4,R0			; R0 ->	data
	ADD	#4,R1			; R1 ->	data
	MOV	-2(R0),R2		; R2 =	number bytes in arg 1
	CMP	R2,-2(R1)		;	is this shortest?
	BLOS	2100$			;	yes -> loop time
	MOV	-2(R1),R2		;	use shorter lenght
2100$:	TST	R2			;	lenght=0?
	BEQ	2200$			;	 yes -> exit
2110$:	MOVB	(R1)+,-(SP)		; SP ->	character @R1
	CALL	3000$			;	insure upper case
	MOVB	(R0)+,-(SP)		; SP ->	character @R0
	CALL	3000$			;	insure upper case
	CMPB	(SP)+,(SP)+		;	do a CMPB (R0)+,(R1)+
	BNE	2200$			;	different -> stop
	SOB	R2,2110$		;	loop till all done
2200$:	RETURN				;	bye
;
; This routine is called to convert the character pushed onto the stack
; into Upper Case.
;
3000$:	CMPB	2(SP),#140		;	is character LC?	
	BLO	3100$			;	 no --> skip
	BIC	#40,2(SP)		;	 yes ->	make UC
3100$:	RETURN				;	done
;

.SBTTL	Primitive - "OPRDCD"	...	Decode operator
;
; This routine is used to decode the operator contained in the input
; stream. It returns in R0 the equivalent conditional branch instruction
; in R0 that will cause a PDP11 to skip one (1 word) instruction if the
; condition is met.
;
OPRDCD:	.PUSH	<R1,R2,R3,R4,R5>	; save
	CALL	INPUT			; get character
	MOV	R0,R1			; save character
	CALL	INPUT			; get second character
	SWAB	R0			; switch char to HOB
	ADD	R1,R0			; operator -> R0
;
	CLR	R1			; R1 =	index
2100$:	CMP	R0,2710$(R1)		;	found match
	BEQ	2300$			;	yes -> skip
	ADD	#2,R1			;	bump up
	TST	2710$(R1)		;	hit EOT?
	BNE	2100$			;	no -> loop
2200$:	ABORT	<..TEST:invalid operator!>
2300$:	MOV	2720$(R1),-(SP)		;	save instruction
	CALL	INPUT			;	get character
	CMP	R0,#'.			;	delimiter?
	BNE	2200$			;	no -> abort
	.POP	<R0,R5,R4,R3,R2,R1>	;	restore
	RETURN				;	exit
;
;
;  Tables of valid operators and equivalent PDP11 instructions. The entries in
;  the two tables must correspond, so that when an operator in the 2710$ table
;  is decoded, the correct PDP11 code from the 2720$ table is returned.
;
2710$:	.WORD	"EQ			; operator
	.WORD	"GE			; operator
	.WORD	"GT			; operator
	.WORD	"LE			; operator
	.WORD	"LT			; operator
	.WORD	"NE			; operator
	.WORD	"eq			; operator
	.WORD	"ge			; operator
	.WORD	"gt			; operator
	.WORD	"le			; operator
	.WORD	"lt			; operator
	.WORD	"ne			; operator
	.WORD	0			; eot
;
2720$:	BEQ	.+4			; equivalent instruction
	BHIS	.+4			; equivalent instruction
	BHI	.+4			; equivalent instruction
	BLOS	.+4			; equivalent instruction
	BLO	.+4			; equivalent instruction
	BNE	.+4			; equivalent instruction
	BEQ	.+4			; equivalent instruction
	BHIS	.+4			; equivalent instruction
	BHI	.+4			; equivalent instruction
	BLOS	.+4			; equivalent instruction
	BLO	.+4			; equivalent instruction
	BNE	.+4			; equivalent instruction
;

.SBTTL	Primitives -"???PRG"	...	input NEW, EDiT, ZERo or SHoW PRGs
;
; The NEWPRG and EDTPRG routines are called to allow the user to modify the
; program stored in the buffer; the editted program is returned in ASCIZ
; format in PRGBUF.
;
NEWPRG:	CALL	ZERPRG				;	zero program
EDTPRG:	CALL	SHWPRG				;	display program
	MOV	#PRGFDB,R0			; R0 ->	field descriptor block
	MOV	#PRGBUF,R5			; R5 ->	buffer
	CMP	WND.BL+4,#7.			;	XBASE valid?
	BNE	400$				;	 no -> skip
	CMP	WND.BL+6,#0.			;	YBASE valid?
	BEQ	600$				;	 yes-> all ok
400$:	CALL	FLD.OU				;	 no --> reactivate fld
600$:	MOV	$$XPOS,WND.BL			;	reposition cursor	
1000$:;;MOVB	#255.,WND.FL+1			;	allow auto-insert
	CALL	WND.IN				;	input data
	CALL	FLD.CK				;	check data
	BCS	1000$				;	ignore bad data
	CALL	FLD.IN				;	accept data
	CLRB	78.(R5)				;	terminate input
	MOV	WND.BL,$$XPOS			;	save X position
	RETURN					;	all done
;
;
; ZERPRG is called to zero the contents of the program buffer
;
ZERPRG:	MOV	#PRGFDB,R0			; R0 ->	field descriptor block
	MOV	#PRGBUF,R5			; R5 ->	buffer
	CALL	FLD.ZE				;	zero field
	CALL	FLD.OU				;	output contents
	CLR	$$XPOS				;	reset cursor
	RETURN					;	done
;
;
; SHWPRG is called to display the contents of the program buffer
;
SHWPRG:	MOV	#PRGFDB,R0			; R0 ->	field descriptor block
	MOV	#PRGBUF,R5			; R5 ->	buffer
	CALL	FLD.OU				;	output contents
	RETURN					;	done
;
;
$$XPOS:	.WORD	0				; WND.BL XPOS entry
PRGFDB:	.WORD	1				; ascii field
	.WORD	78.				; 70 characters in field
	.WORD	0				; offset from SOR to field
	.WORD	0.,0.				; X/Y cordinates of field
	.WORD	0				; check word
PRGBUF:	.BLKB	80.
;

.SBTTL	Primitive - "INPUT"	...	return next input character
;
; If INPUT$ <> 0 a byte has been pushed back onto the input stream
;
INPUT:	TST	INPUT$			;	pushed char available?
	BEQ	100$			;	 no --> skip
	MOV	INPUT$,R0		;	 yes ->	return it
	CLR	INPUT$			;		clear for next time
	RETURN				;		all done
;
; This code returns the next byte from the program buffer in R0
;
40$:	MOVB	@INPFTH,R0		; R0 =	next input byte
	BIC	#^C377,R0		;	discard spare bits
	ABORT	<End of Program>,EQ	;	trap unexpected EOP
	INC	INPFTH			;	bump pointer
	RETURN				;	done
;
; Have to return next program character. Look out for _, $ or ^ modifiers
;
100$:	CALL	40$			; R0 =	next character
	CMP	R0,#'_			;	Leadin Character?
	BEQ	40$			;	 yes -> return next char
	CMP	R0,#'$			;	Leadin Character?
	BEQ	2000$			;	 yes -> skip
	CMP	R0,#'^			;	Leadin Character?
	BEQ	3000$			;	 yes -> 300$
	RETURN				; 	all done
;
; next character is to be returned as an escape code
;
2000$:	CALL	40$			; R0 =	next chracter
	BIS	#200,R0			;	translate to escape code
	RETURN				;	all done
;
; translate next character to control code; watch out for ^@
;
3000$:	CALL	40$			; R0 =	next chracter
	BIC	#^C37,R0		; R0 =	equivalent control code
	BNE	100$			;	 done if not a ^@
;
; if REPCNT <> 0 in middle of a repeat block
;
	TST	INPRCT			;	repeat count = 0?
	BEQ	3400$			;	 yes -> setup repeat block
	DEC	INPRCT			;	repeat operation done?
	BEQ	100$			;	 yes -> ignore ^@
	MOV	INPRST,INPFTH		;	 no --> point start of block
	BR	100$			;		repeat time
;
; are setting up a repeat block
;
3400$:	CALL	100$			; R0 =	next character
	MOV	R0,INPRCT		;	save repeat count
	MOV	INPUT$+2,INPRST		;	save pointer
	BR	100$			;	get next character

.SBTTL	Variables
;
$$FORM:	.WORD	FORM$$			; address of template
RECNUM:	.WORD	0			; number of record in memory
N.....:	.WORD	0,0,0,0,0,0,0,0,0,0	; Variables N0 to N9
D.....:	.WORD	0,0,0,0,0,0,0,0,0,0	; Variables D0 to D4 (D5-D9 reserved)
L.....:	.WORD	0			; Literal variable L0	size
	.BLKB	126.			;			data
	.WORD	0			; Literal variable L1	size
	.BLKB	126.			;			data
	.WORD	0			; Literal variable L2	size
	.BLKB	126.			;			data
	.WORD	0			; Literal variable L3	size
	.BLKB	126.			;			data
;
P.ACPT:	.WORD	0			; 0 -> accepted, 1 -> not accepted
NUMSEL:	.WORD	0			; size of sorter records
INPUT$:	.WORD	0			; used to push char back
INPFTH:	.WORD	0			; input fetch pointer
INPRWD:	.WORD	0			; restart pointer
INPRCT:	.WORD	0			; repeat count
INPRST:	.WORD	0			; repeat pointer
TMPB1:	.WORD	0			; type
	.WORD	0			; size
	.BLKB	400			; data area
TMPB2:	.WORD	0			; type
	.WORD	0			; size
	.BLKB	2000			; data area
;
;
.END	START
