	.TITLE	ILINK - IMP PROGRAM LINKER
;
; R.D. EAGER
; UNIVERSITY OF KENT AT CANTERBURY
; APRIL 1976
;
	.NLIST	BEX
;
	.IDENT	/V01.03/
;
	.SBTTL	DECLARATIONS
;
; REGISTER DEFINITIONS
;
	R0	= %0
	R1	= %1
	R2	= %2
	R3	= %3
	R4	= %4
	R5	= %5
	SP	= %6
	PC	= %7
;
; SYSTEM MACROS
;
	.MCALL	.INIT, .OPEN, .READ, .WRITE, .WAIT
	.MCALL	.CLOSE, .RLSE, .CSI1, .CSI2, .CORE
	.MCALL	.DATE, .TIME, .RADUP, .CVTDT, .BIN2O
	.MCALL	.RSTRT, .DELET, .LOOK
;
; OFFSETS OF ITEMS IN BUFFER HEADERS
;
	STATUS	= 3		;STATUS BYTE
	BC	= 4		;ACTUAL BYTE COUNT
;
; FILE BLOCK "HOW OPEN" FLAGS
;
	OUT	= 2
	IN	= 4
;
; TRANSFER MODES
;
	FAN	= 0		;FORMATTED ASCII NORMAL
	UAN	= 2		;UNFORMATTED ASCII NORMAL
	UBN	= 3		;UNFORMATTED BINARY NORMAL
	DMP	= 4		;DUMP (ADDITIVE MODE)
;
; CHARACTER CODES
;
	LF	= 12		;LINEFEED
	VT	= 13		;VERTICAL TABULATE
	CR	= 15		;CARRIAGE RETURN
	SPACE	= 40
;
; OFFSETS OF ITEMS IN A COMD
;
	CLP	= 4		;COMD LOAD POINT
	LP	= 10		;PROGRAM LOAD POINT
	SIZE	= 12		;PROGRAM SIZE (BYTES)
	PXAD	= 14		;PROGRAM TRANSFER ADDRESS
	OXAD	= 16		;ODT TRANSFER ADDRESS
	ID1	= 26		;PROGRAM IDENTIFICATION (WORD 0)
	ID2	= 30		;PROGRAM IDENTIFICATION (WORD 1)
;
; NAMES OF BITS IN A WORD
;
	BIT0	= 1
	BIT1	= 2
	BIT7	= 200
	BIT8	= 400
	BIT13	= 20000
	BIT14	= 40000
	BIT15	= 100000
;
; MISCELLANEOUS CONSTANTS
;
	ONEONE	= BIT0!BIT8	;[1,1] USER IDENTIFICATION CODE
	FNOSET	= I1.FB-I1.LB	;OFFSET OF FILENAME FROM LINK BLOCK
	KBBSIZ	= ^D83		;SIZE OF A DOS KEYBOARD BUFFER
	BUFSIZ	= ^D128		;SIZE OF A DOS FILE BUFFER
	BLKSIZ	= 400		;MAXIMUM LOAD BLOCK SIZE
	SWTSIZ	= 10		;SIZE OF SWITCH AREA IN LINK BLOCK (WORDS)
	.SBTTL	MACROS
;
; MACRO TO DECLARE LINK BLOCKS AND FILE BLOCKS
;
	.MACRO	IOBLKS	CH, NAM, DEV, IN.OUT, IOERR, OPNERR
	.WORD	IOERR		;ERROR RETURN ADDRESS
CH'.LB:	.BLKW	1		;USED BY DOS
	.RAD50	/NAM/		;LOGICAL NAME
	.BYTE	SWTSIZ+1	;WORDS TO FOLLOW
	.BLKB	1		;UNIT #
CH'.PD:	.RAD50	/DEV/		;PHYSICAL DEVICE NAME
CH'.SW:	.BLKW	SWTSIZ		;SWITCH AREA
	.WORD	OPNERR		;ERROR RETURN ADDRESS
	.BYTE	IN.OUT		;HOW OPEN
	.BLKB	1		;ERROR CODE
CH'.FB:	.BLKW	4		;FILNAM.EXT[UIC]
	.BYTE	0,0		;PROTECT CODE, SPARE
	.ENDM	IOBLKS
;
; MACRO TO DECLARE BUFFER HEADERS AND BUFFERS
;
	.MACRO	BUFHDR	CH, MODE, BSIZE
CH'.BH:	.WORD	BSIZE		;MAXIMUM BYTE COUNT
	.BYTE	MODE		;TRANSFER MODE
CH'.ST:	.BLKB	1		;STATUS BYTE
CH'.BC:	.BLKW	1		;ACTUAL BYTE COUNT
CH'.BUF:.BLKB	BSIZE		;THE BUFFER ITSELF
	.EVEN
	.ENDM	BUFHDR
;
; MACRO TO DECLARE CSI CONTROL BLOCK
;
	.MACRO	CSIBLK	CH
CH'.CB:	.WORD	CMDBUF
	.WORD	CH'.LB
	.WORD	CH'.FB
	.ENDM	CSIBLK
;
	.MACRO	SELECT	CH
	MOV	#CH'.LB,R3
	MOV	#CH'.BH,R4
	MOV	#CH'.PT,R5
	.ENDM	SELECT
;
; MACRO TO PRINT AN ERROR MESSAGE
;
	.MACRO	ERROR	TEXT, TYPE, NL
	.IF IDN <TYPE>,<FATAL>
	JSR	R1,FTLMSG	;NON-RECOVERABLE ERROR
	.IFF
	JSR	R1,WRNMSG	;WARNING MESSAGE ONLY
	.ENDC
	.IF NB NL
	.ASCII	"TEXT"
	.BYTE	CR, LF, 0
	.IFF
	.ASCIZ	"TEXT"
	.ENDC
	.EVEN
	.ENDM	ERROR
;
; MACRO TO PRINT A MESSAGE
;
	.MACRO	MESS	TEXT, NL
	JSR	R1,MESOUT	;MESSAGE OUTPUT ROUTINE
	.IF NB NL
	.ASCII	"TEXT"
	.BYTE	CR, LF, 0
	.IFF
	.ASCIZ	"TEXT"
	.ENDC
	.EVEN
	.ENDM	MESS
	.SBTTL	I/O CONTROL BLOCKS
;
; KEYBOARD LINK BLOCKS, FILE BLOCKS AND BUFFER HEADERS.
; THE AREA 'CMDBUF' IS A WORK AREA FOR THE CSI AND MUST
; IMMEDIATELY PRECEDE THE COMMAND BUFFER HEADER
;
	IOBLKS	COM, CMI, KB, IN, 0, 0
CMDBUF:	.BLKW	7		;CSI WORK AREA
	BUFHDR	COM, FAN, KBBSIZ
;
	IOBLKS	MES, CMO, KB, OUT, 0, 0
	BUFHDR	MES, FAN, KBBSIZ
;
; INPUT FILE 1 (TRAP PACKAGE, DOS .LDA FILE)
;
	IOBLKS	I1, I1, SY, IN, I1.ERR, I1.OF
	BUFHDR	I1, UBN, BUFSIZ
	CSIBLK	I1
;
; INPUT FILE 2 (IMP PROGRAM, .LDI FILE)
;
	IOBLKS	I2, I2, SY, IN, I2.ERR, I2.OF
	BUFHDR	I2, UBN, BUFSIZ
	CSIBLK	I2
;
; OUTPUT FILE 1 (DOS .LDA FILE, LINKED PROGRAM)
;
	IOBLKS	O, O, SY, OUT, OUTERR, O.OF
	BUFHDR	O, UBN, BUFSIZ
	CSIBLK	O
;
; OUTPUT FILE 2 (LOAD MAP)
;
	IOBLKS	MAP, MAP, SY, OUT, MAPERR, MAP.OF
;
; MAP FILE HAS SPECIAL BUFFER HEADER, SINCE OUTPUT
; IS DONE IN DUMP MODE
;
MAP.BH:	.WORD	LMEND-LDMAP	;MAXIMUM BYTE COUNT
	.BYTE	UAN!DMP		;UNFORMATTED ASCII, DUMP MODE
MAP.ST:	.BLKB	1		;STATUS BYTE
MAP.BC:	.WORD	LMEND-LDMAP	;ACTUAL BYTE COUNT
	.WORD	LDMAP		;ADDRESS OF BUFFER
;
	CSIBLK	MAP
;
; FILE BLOCK USED IN ATTEMPT TO DELETE OUTPUT
; FILE AT START OF RUN (IN CASE IT EXISTS ALREADY).
;
	.WORD	DO.ERR		;ERROR RETURN ADDRESS
	.BYTE	0,0		;IRRELEVANT FOR DELETE
DO.FB:	.BLKW	4		;FILNAM.EXT[UIC]
	.BYTE	0,0		;PROTECT CODE, SPARE
;
; FILE BLOCK USED IN ATTEMPT TO DELETE MAP FILE
; BEFORE OUTPUT OF LOAD MAP (IN CASE IT EXISTS ALREADY)
;
	.WORD	DM.ERR		;ERROR RETURN ADDRESS
	.BYTE	0,0		;IRRELEVANT FOR DELETE
DM.FB:	.BLKW	4		;FILNAM.EXT[UIC]
	.BYTE	0,0		;PROTECT CODE, SPARE
;
; LINK BLOCK AND FILE BLOCK USED TO SEARCH FOR
; THE FILE SY:ITRAP.LDA (DEFAULT TRAP PACKAGE)
;
	.WORD	0		;ERROR RETURN ADDRESS
I1D.LB:	.BLKW	1		;USED BY DOS
	.WORD	0		;LOGICAL NAME - NONE
	.BYTE	1,0		;WORDS TO FOLLOW, UNIT #
SYSDEV:	.RAD50	/SY /		;PHYSICAL DEVICE NAME
	.WORD	0		;ERROR RETURN ADDRESS
	.BYTE	IN		;HOW OPEN
	.BLKB	1		;ERROR CODE
I1D.FB:	.RAD50	/ITR/		;FILNAM.EXT[UIC]
	.RAD50	/AP /
	.RAD50	/LDA/
	.WORD	0
	.BYTE	0,0		;PROTECT CODE, SPARE
	.SBTTL	VARIABLES AND DATA AREAS
;
; DEFAULT FILENAME EXTENSIONS
;
EXTLDA:	.RAD50	/LDA/
EXTLDI:	.RAD50	/LDI/
EXTMAP:	.RAD50	/MAP/
;
; DUMMY COMD, USED IN PRODUCTION OF LINKED PROGRAM FILE
;
COMD:	.BYTE	1,0		;HEADER BYTES
CSIZE:	.WORD	44		;BYTE COUNT (EXCLUDING CHECKSUM)
CLDPT:	.BLKW	1		;COMD LOAD POINT (SAME AS PROGRAM LOAD POINT)
	.BYTE	1		;SECTION TYPE (MEANS A COMD)
	.BYTE	14		;WORDS TO FOLLOW (HEADER PART)
LOADPT:	.BLKW	1		;PROGRAM LOAD POINT
PRGSIZ:	.BLKW	1		;PROGRAM SIZE (IN BYTES)
XFADD:	.BLKW	1		;PROGRAM TRANSFER ADDRESS (SAME AS LOAD POINT)
	.BYTE	1,0		;ODT TRANSFER ADDRESS (NONE)
	.WORD	0		;FIRST RELATIVE BLOCK OF CORE IMAGE
	.RAD50	/.MAIN./	;PROGRAM TITLE (2 WORDS)
CIDENT:	.BLKW	2		;PROGRAM IDENTIFICATION (FROM TRAP PACKAGE)
CTIME:	.BLKW	2		;TIME OF CREATION (2 WORDS)
CDATE:	.BLKW	1		;DATE OF CREATION (1 WORD)
	.BYTE	2		;EMT CALLS RESIDENT SECTION
	.BYTE	0		;WORDS TO FOLLOW (EMT SECTION)
	.WORD	0		;END OF COMD MARKER
CCHECK:	.BLKB	1		;COMD CHECKSUM
;
; BYTE VARIABLES
;
MAPFLG:	.BLKB	1		;= 1 IFF LOAD MAP REQUIRED
BLKBUF:	.BLKB	BLKSIZ+1	;LOAD BLOCK INPUT BUFFER
WKSP:	.BLKB	6		;WORKSPACE FOR FILENAME OUTPUT
	.BYTE	'.		;DITTO
	.BLKB	3		;DITTO
NULFLG:	.BLKB	1		;= 1 IFF NO OUTPUT FILE
BTFLG:	.BLKB	1		;<0 FOR /B, >0 FOR /T, =0 FOR NEITHER
	.EVEN
;
; WORD VARIABLES
;
MAXCOR:	.BLKW	1		;DEFAULT TOP ADDRESS
BTVAL:	.BLKW	1		;/B OR /T VALUE
INCHEK:	.BLKW	1		;INPUT CHECKSUM
OUCHEK:	.BLKW	1		;OUTPUT CHECKSUM
TBASE:	.BLKW	1		;ORIGINAL LOAD POINT OF TRAP PACKAGE
TRPSIZ:	.BLKW	1		;SIZE OF TRAP PACKAGE (BYTES)
IMPSIZ:	.BLKW	1		;TRUE SIZE OF IMP PROGRAM (BYTES)
BIAS:	.BLKW	1		;BIAS APPLIED TO LOAD ADDRESSES
SPSAVE:	.BLKW	1		;SP VALUE ON ENTRY TO CONSOLIDATOR
I1.PT:	.BLKW	1		;INPUT FILE 1 BUFFER POINTER
I1.PTI:	.WORD	I1.BUF		;INITIAL VALUE OF ABOVE - DO NOT MOVE
I2.PT:	.BLKW	1		;INPUT FILE 2 BUFFER POINTER
I2.PTI:	.WORD	I2.BUF		;INITIAL VALUE OF ABOVE - DO NOT MOVE
O.PT:	.BLKW	1		;OUTPUT BUFFER POINTER
MES.PT:	.BLKW	1		;KEYBOARD OUTPUT BUFFER POINTER
	.SBTTL	MAIN PROGRAM
;
BEGIN:	MOV	SP,SPSAVE	;SAVE ENTRY VALUE FOR ERROR RECOVERY
	MOV	#MES.BUF,MES.PT	;INITIALISE KEYBOARD OUTPUT BUFFER POINTER
	CLR	MES.BC		;AND BYTE COUNT
	JSR	PC,GETCOR	;DETERMINE CORE SIZE
	CLR	COM.LB		;MUST BE ZERO BEFORE INIT
	CLR	MES.LB
	CLR	I1.LB
	CLR	I2.LB
	CLR	O.LB
	CLR	MAP.LB
	CLR	I1D.LB
	.INIT	#COM.LB		;OPEN KEYBOARD FOR INPUT AND OUTPUT
	.INIT	#MES.LB
	.OPEN	#COM.LB,#COM.FB
	.OPEN	#MES.LB,#MES.FB
	MESS	<ILINK V01-03>
	.RSTRT	#SHUTUP		;SET RESTART ADDRESS
ASK:	MOV	SPSAVE,SP	;RESTORE ENTRY VALUE OF SP
	JSR	PC,RDSPEC	;READ FILE SPECIFICATIONS
	JSR	PC,OPEN		;OPEN FILES
	JSR	PC,PHASE1	;EXECUTE PHASE 1
	JSR	PC,PHASE2	;EXECUTE PHASE 2
	TSTB	MAPFLG		;MAP REQUIRED?
	BEQ	ASK		;NO - GO DO SOME MORE LINKING
	JSR	PC,MAPOUT	;OTHERWISE GO DO IT
	BR	ASK		;GO DO SOME MORE LINKING
;
; ERROR RECOVERY CODE
; CLOSES ALL FILES THAT ARE OPEN, THEN GOES
; TO GET NEW COMMAND STRING
;
SHUTUP:	TST	I1.LB		;TRAP PACKAGE FILE
	BEQ	1$
	.CLOSE	#I1.LB
	.RLSE	#I1.LB
	CLR	I1.LB
1$:	TST	I2.LB		;IMP PROGRAM FILE
	BEQ	2$
	.CLOSE	#I2.LB
	.RLSE	#I2.LB
	CLR	I2.LB
2$:	TST	O.LB		;DOS PROGRAM OUTPUT FILE
	BEQ	3$
	.CLOSE	#O.LB
	.RLSE	#O.LB
	CLR	O.LB
3$:	TST	MAP.LB		;LOAD MAP FILE
	BEQ	4$
	.CLOSE	#MAP.LB
	.RLSE	#MAP.LB
	CLR	MAP.LB
4$:	TST	I1D.LB		;TRAP PACKAGE SEARCH
	BEQ	5$
	.RLSE	#I1D.LB
	CLR	I1D.LB
5$:	MOV	#MES.BUF,MES.PT	;RESET KEYBOARD OUTPUT BUFFER POINTER
	CLR	MES.BC		;AND BYTE COUNT
	BR	ASK		;IN CASE THIS WAS A RESTART
	.SBTTL	READ FILE SPECIFICATIONS AND SWITCHES
;
RDSPEC:	JSR	R1,MESOUT	;PUT OUT A HASH SIGN
	.BYTE	CR, LF, '#, VT, 0
	.EVEN
	.READ	#COM.LB,#COM.BH	;READ THE COMMAND STRING
	.WAIT	#COM.LB
	.CSI1	#CMDBUF		;CONDENSE AND CHECK
	TST	(SP)+		;OK?
	BEQ	20$		;YES
	JMP	SYNERR		;NO
20$:	CMPB	#LF,COM.BUF+1	;NULL LINE?
	BEQ	RDSPEC		;YES - ASK AGAIN
	CLRB	MAPFLG		;SET NO MAP INITIALLY
	CLRB	BTFLG		;SET NO /B OR /T INITIALLY
;
; PROGRAM OUTPUT FILE
;
	MOV	#2,CMDBUF	;GET AN OUTPUT FILE SPEC
	.CSI2	#O.CB
	TST	O.FB+4		;ANY EXTENSION?
	BNE	1$		;YES - LEAVE
	MOV	EXTLDA,O.FB+4	;NO - USE .LDA
1$:	TST	O.SW		;ANY SWITCHES?
	BEQ	7$		;NO - OK
	JMP	O.SE		;YES - ERROR
7$:	BIT	#BIT0,(SP)+	;ANY MORE OUTPUT SPECS?
	BNE	6$		;NO - MAP NOT REQUIRED
	INCB	MAPFLG		;YES - SET FLAG FOR LATER
;
; MAP FILE
;
	MOV	#2,CMDBUF	;GET AN OUTPUT FILE SPEC
	.CSI2	#MAP.CB
	TST	MAP.FB+4	;ANY EXTENSION?
	BNE	2$		;YES - LEAVE
	MOV	EXTMAP,MAP.FB+4	;NO - USE .MAP
2$:	TST	MAP.SW		;ANY SWITCHES?
	BEQ	8$		;NO - OK
	JMP	MAP.SE		;YES - ERROR
8$:	BIT	#BIT0,(SP)+	;ANY MORE OUTPUT SPECS?
	BNE	6$		;NO - OK
	JMP	OMANY		;YES - ERROR
6$:	CLRB	NULFLG
	TST	O.PD		;WAS THERE AN OUTPUT FILE SPEC?
	BNE	5$		;YES
	INCB	NULFLG		;NO - REMEMBER THAT
;
; TRAP PACKAGE FILE
;
5$:	CLR	CMDBUF		;GET AN INPUT FILE SPEC
	.CSI2	#I1.CB
	TST	I1.FB+4		;ANY EXTENSION?
	BNE	3$		;YES - LEAVE
	MOV	EXTLDA,I1.FB+4	;NO - USE .LDA
3$:	BIC	#^C3,@SP	;ONLY WANT BITS 0-1
	MOV	#I1.SW,R1	;POINT TO SWITCH AREA
	BIT	#BIT1,@SP	;WAS THERE SWITCH OVERFLOW?
	BEQ	9$		;NO
	JMP	I1.SO		;YES - ERROR
9$:	TST	(R1)		;ANY MORE SWITCHES?
	BEQ	10$		;NO
	MOV	(R1),R2		;YES - NUMBER OF WORDS TO FOLLOW
	ASL	R2		;CONVERT TO NO OF BYTES
	ADD	R1,R2		;R2 -> SWITCH ITSELF
	CMP	#'B,(R2)	;/B SWITCH?
	BNE	11$		;NO
	JSR	PC,SETB		;YES - GET VALUE
	BR	12$		;GO TO DO NEXT SWITCH
11$:	CMP	#'T,(R2)	;/T SWITCH?
	BEQ	13$		;YES
	JMP	I1.SE		;NO - ILLEGAL SWITCH
13$:	JSR	PC,SETT		;GET /T VALUE
12$:	MOV	R2,R1
	TST	(R1)+		;POINT R1 TO NEXT SWITCH AREA
	BR	9$		;GO PROCESS ANY MORE SWITCHES
10$:	TST	(SP)+		;ANY MORE SPECS?
	BEQ	23$		;YES
	JMP	MINERR		;NO -ERROR
23$:	TST	I1.PD		;IF THE INPUT DEVICE IS NULL...,
	BNE	24$		;...TRY FOR DEFAULT FILENAME...
	.INIT	#I1D.LB		;...OF SY:ITRAP.LDA ON CURRENT UIC...
	.LOOK	#I1D.LB,#I1D.FB	;...THEN ON [1,1]
	.RLSE	#I1D.LB
	CLR	I1D.LB
	CLR	I1.FB+6		;SET TO DEFAULT UIC INITIALLY
	TST	(SP)+		;REMOVE UNWANTED '# OF BLOCKS' WORD
	BIT	#BIT7,(SP)+	;DOES FILE EXIST ON CURRENT UIC?
	BNE	25$		;YES
	MOV	#ONEONE,I1.FB+6	;NO - USE [1,1]
25$:	MOV	#I1D.FB,R0
	MOV	#I1.FB,R1
	MOV	(R0)+,(R1)+	;COPY DEFAULT FILENAME INTO...
	MOV	(R0)+,(R1)+	;...FILE BLOCK FOR INPUT FILE 1
	MOV	(R0),(R1)	;...AND SET UP DEVICE SY:...
	MOV	SYSDEV,I1.PD	;...AS PHYSICAL DEVICE NAME
;
; IMP PROGRAM FILE
;
24$:	CLR	CMDBUF		;GET AN INPUT FILE SPEC
	.CSI2	#I2.CB
	TST	I2.FB+4		;ANY EXTENSION?
	BNE	4$		;YES - LEAVE
	MOV	EXTLDI,I2.FB+4	;NO - USE .LDI
4$:	BIC	#^C3,@SP	;ONLY WANT BITS 0-1
	MOV	#I2.SW,R1	;POINT TO SWITCH AREA
	BIT	#BIT1,@SP	;WAS THERE SWITCH OVERFLOW?
	BEQ	14$		;NO
	JMP	I2.SO		;YES - ERROR
14$:	TST	(R1)		;ANY MORE SWITCHES?
	BEQ	19$		;NO
	MOV	(R1),R2		;NUMBER OF WORDS TO FOLLOW
	ASL	R2		;CONVERT TO NO OF BYTES
	ADD	R1,R2		;R2 -> SWITCH ITSELF
	CMP	#'B,(R2)	;/B SWITCH?
	BNE	15$		;NO
	JSR	PC,SETB		;YES - GET VALUE
	BR	16$		;GO TO DO NEXT SWITCH
15$:	CMP	#'T,(R2)	;/T SWITCH?
	BEQ	17$		;YES
	JMP	I2.SE		;NO - ILLEGAL SWITCH
17$:	JSR	PC,SETT		;GET /T VALUE
16$:	MOV	R2,R1
	TST	(R1)+		;POINT R1 TO NEXT SWITCH AREA
	BR	14$		;GO PROCESS ANY MORE SWITCHES
19$:	DEC	(SP)+		;ANY MORE INPUT SPECS?
	BEQ	21$		;NO
	JMP	IMANY		;YES - ERROR
21$:	TST	I2.PD		;IF THE INPUT DEVICE IS NULL...
	BNE	22$		;...IT IS AN ERROR AGAIN
	JMP	MINERR
22$:	RTS	PC		;OK - RETURN
;
; PROCESS /B AND /T SWITCH VALUES
; ON ENTRY:-
;    R1 -> 'WORDS TO FOLLOW' WORD
;    R2 -> SWITCH NAME WORD
; ON EXIT, BTFLG AND BTVAL ARE SET CORRECTLY
;
	.ENABL	LSB
SETT:	MOVB	#1,BTFLG	;SET 'TOP' VALUE
	BR	1$
SETB:	MOVB	#BIT7,BTFLG	;SET 'BOTTOM' VALUE
1$:	TST	(R1)+		;POINT R1 AT LAST VALUE POINTER
	CMP	R1,R2		;R1 = R2 MEANS NO VALUE SPECIFIED
	BEQ	NBTVAL		;THEY ARE THE SAME - ERROR
	MOV	(R1),R3		;POINT R3 TO FIRST BYTE OF VALUE STRING
	CLR	R4		;R4 WILL ACCUMULATE THE VALUE
4$:	MOVB	(R3)+,R0	;GET NEXT BYTE OF VALUE STRING
	BIC	#^C377,R0	;REMOVE HIGH ORDER RUBBISH
	SUB	#'0,R0		;CONVERT TO BINARY VALUE
	BMI	3$		;NOT A DIGIT - FINISH
	CMPB	#7,R0
	BLT	3$		;NOT A DIGIT - FINISH
	BIT	#BIT15!BIT14!BIT13,R4
	BNE	IBTVAL		;VALUE TOO BIG - ERROR
	ASL	R4
	ASL	R4
	ASL	R4
	BIS	R0,R4		;ADD IN THE NEW DIGIT
	BR	4$		;GO PROCESS ANY MORE DIGITS
3$:	DEC	R3		;WAS THE TERMINATOR CHARACTER...
	CMP	R3,(R1)		;...THE FIRST ONE?
	BEQ	IBTVAL		;YES - ERROR
	BIT	#1,R4		;NO - WAS VALUE ODD?
	BNE	OBTVAL		;YES - ERROR
	MOV	R4,BTVAL	;NO - STORE THIS CORRECT VALUE
	RTS	PC		;AND RETURN
	.DSABL	LSB
;
; SWITCH ERRORS
;
OBTVAL:	ERROR	<ODD>, FATAL
	BR	EBTVAL
IBTVAL:	ERROR	<ILLEGAL>, FATAL
	BR	EBTVAL
NBTVAL:	ERROR	<MISSING>, FATAL
EBTVAL:	MESS	< /B OR /T SWITCH VALUE>, NL
	CMP	(SP)+,(SP)+	;REMOVE UNWANTED LINK AND CSI INDICATOR
	JMP	RDSPEC		;ASK AGAIN
I1.SO:	MOV	#I1.FB,R3
	BR	SOALL
I2.SO:	MOV	#I2.FB,R3
SOALL:	ERROR	<TOO MANY SWITCHES >, FATAL
	BR	SFNOUT		;GO TO PRINT FILENAME, THEN GIVE UP
I1.SE:	MOV	#I1.FB,R3
	BR	SEALL
I2.SE:	MOV	#I2.FB,R3
	BR	SEALL
O.SE:	MOV	#O.FB,R3
	BR	SEALL
MAP.SE:	MOV	#MAP.FB,R3
SEALL:	ERROR	<ILLEGAL SWITCH >, FATAL
SFNOUT:	MOV	R3,R0		;POINTER TO FILENAME
	JSR	PC,FNOUT	;PRINT THE FILENAME
	JSR	R1,MESOUT	;FINISH OFF
	.BYTE	CR, LF, 0
	.EVEN
	BR	REASK
;
; FILE SPECIFICATION ERRORS
;
SYNERR:	ERROR	<COMMAND SYNTAX ERROR>, FATAL, NL
	BR	REASK		;ASK AGAIN
;
OMANY:	ERROR	<TOO MANY OUTPUT FILES>, FATAL, NL
	BR	REASK
;
IMANY:	ERROR	<TOO MANY INPUT FILES>, FATAL, NL
	BR	REASK
;
MINERR:	ERROR	<MISSING INPUT FILE>, FATAL, NL
REASK:	JMP	RDSPEC
	.SBTTL	OPEN FILES
;
; OPEN INPUT FILES
;
OPEN:	.INIT	#I1.LB		;TRAP PACKAGE FILE
	.OPEN	#I1.LB,#I1.FB
	.INIT	#I2.LB		;IMP PROGRAM FILE
	.OPEN	#I2.LB,#I2.FB
;
; OPEN OUTPUT FILE
;
	TSTB	NULFLG		;OUTPUT SUPPRESSED?
	BNE	OPNRET		;YES - OMIT THE OUTPUT FILE STUFF
;
; ATTEMPT TO DELETE OUTPUT FILE, IN CASE IT 
; ALREADY EXISTS
;
	.INIT	#O.LB		;INITIALISE THE DEVICE DRIVER
	MOV	#O.FB,R0	;COPY FILNAM.EXT[UIC] TO
	MOV	#DO.FB,R1	;THE DELETION FILE BLOCK
	MOV	(R0)+,(R1)+
	MOV	(R0)+,(R1)+
	MOV	(R0)+,(R1)+
	MOV	(R0),(R1)
	.DELET	#O.LB,#DO.FB	;ATTEMPT THE DELETE
;
; CONTROL ALWAYS RETURNS HERE, REGARDLESS OF WHETHER OR
; NOT THE DELETE WAS SUCCESSFUL.
; THE OUTPUT FILE IS NOW OPENED, AND IF THE DELETION FAILED DUE
; TO AN ACCESS VIOLATION RATHER THAN AN EXISTENCE ERROR, THE
; OPEN FAILURE WILL PRODUCE A SUITABLE MESSAGE.
;
DO.ERR:	.OPEN	#O.LB,#O.FB
;
; THE MAP FILE IS OPENED (IF REQUIRED) WITHIN
; THE MAP OUTPUT ROUTINE.
;
OPNRET:	RTS	PC
	.SBTTL	PHASE 1
;
; PHASE 1 READS THE INPUT FILES AND DETERMINES THE EVENTUAL SIZE OF
; THE PROGRAM (IN BYTES). AT THE END OF THE PHASE, THIS VALUE IS IN
; THE VARIABLE 'PRGSIZ'.
; THE SIZE OF THE TRAP PACKAGE IS ASCERTAINED BY READING ITS DOS
; COMMUNICATIONS DIRECTORY (COMD) WHICH CONTAINS THE NECESSARY
; INFORMATION. THE FILE CONTAINING THE TRAP PACKAGE IS LEFT OPEN
; IN READINESS FOR PHASE 2.
; SINCE AN IMP PROGRAM FILE IS NOT A STANDARD DOS FILE, IT HAS NO
; COMD. HOWEVER, IT HAS AN 8-WORD HEADER WHICH IS USED BY THE
; MUSS11 SYSTEM, AND THE FIRST WORD OF THIS HEADER GIVES THE
; PROGRAM SIZE.
; PHASE 1 READS ALL 8 HEADER WORDS, AND AFTER EXTRACTING THE ABOVE
; INFORMATION, DISCARDS THEM. THE FILE CONTAINING THE IMP PROGRAM IS
; LEFT OPEN FOR PHASE 2.
;
;
; INPUT FILE INITIALISATION
;
PHASE1:	CLR	I1.BC		;INITIALISE INPUT BYTE COUNTS
	CLR	I2.BC
;
; READ TRAP PACKAGE COMD
;
	SELECT	I1		;MAKE IT THE CURRENT INPUT FILE
	JSR	PC,RDBLK	;READ THE COMD LOAD BLOCK
	MOV	LP(R1),R0	;TRAP PACKAGE LOAD POINT...
	CMP	R0,CLP(R1)	;...SHOULD EQUAL COMD LOAD POINT
	BEQ	3$		;PROBABLY OK - BUT NOT A PERFECT TEST
	JMP	FERR		;DEFINITELY WRONG - ERROR
3$:	MOV	R0,TBASE	;ORIGINAL TRAP PACKAGE BASE
	MOV	SIZE(R1),TRPSIZ	;TRAP PACKAGE SIZE (BYTES)
	DEC	PXAD(R1)	;PROGRAM TRANSFER ADDRESS
	BEQ	1$		;SHOULD BE A 1
	JSR	PC,XADERR	;IF NOT, WARN THE USER
1$:	DEC	OXAD(R1)	;SAME FOR THE ODT TRANSFER ADDRESS
	BEQ	2$
	JSR	PC,ODTERR
2$:	MOV	ID1(R1),CIDENT	;PROGRAM IDENTIFICATION (WORD 0)
	MOV	ID2(R1),CIDENT+2;PROGRAM IDENTIFICATION (WORD 1)
;
; THE TRAP PACKAGE FILE IS LEFT OPEN FOR PHASE 2.
; THE IMP PROGRAM IS NOW READ, AND ITS SIZE IS ASCERTAINED
; FROM ITS MUSS11 HEADER
;
RDIMP:	SELECT	I2		;MAKE IT THE CURRENT INPUT FILE
	JSR	PC,RDBLK	;READ THE MUSS11 HEADER BLOCK
	CMP	#26,2(R1)	;BLOCK SHOULD BE 26 BYTES
	BEQ	1$		;YES - OK
	JMP	FERR		;NO - FORMAT ERROR
1$:	MOV	6(R1),R0	;IMP PROGRAM SIZE
	MOV	R0,IMPSIZ	;REMEMBER IT
	ADD	TRPSIZ,R0	;GET TOTAL PROGRAM SIZE...
	MOV	R0,PRGSIZ	;...AND STOW THE ANSWER AWAY
	JSR	PC,GWORD	;EXHAUST THE LINE NUMBER BYTES
	RTS	PC		;RETURN FOR PHASE 2
	.SBTTL	PHASE 2
;
; PHASE 2 CONSTRUCTS A DOS COMMUNICATIONS DIRECTORY (COMD) FOR THE
; LINKED PROGRAM, AND WRITES THIS TO THE OUTPUT FILE.
; IT THEN CALCULATES THE BIAS TO BE APPLIED TO ALL LOAD ADDRESSES
; IN THE TRAP PACKAGE, AND COPIES ALL OF THE TRAP PACKAGE (EXCLUDING
; ITS COMD AND ITS TERMINATOR BLOCK) TO THE OUTPUT FILE, ADJUSTING
; ALL LOAD ADDRESSES BY THIS BIAS, AND RE-COMPUTING A CORRECT
; CHECKSUM FOR EACH BLOCK COPIED.
; A BIAS IS THEN CALCULATED FOR THE IMP PROGRAM, WHICH IS SUBSEQUENTLY
; COPIED TO THE OUTPUT FILE, WITH LOAD ADDRESS MODIFICATION BEING
; APPLIED IN A SIMILAR WAY TO THE METHOD USED FOR THE TRAP PACKAGE.
; FINALLY, A TERMINATOR BLOCK IS WRITTEN, CONTAINING A TRANSFER ADDRESS
; WHICH IS THE LOWEST ADDRESS OCCUPIED BY THE FINAL CONSOLIDATED
; PROGRAM. THIS SHOULD BE THE FIRST WORD OF THE TRAP PACKAGE.
; BOTH INPUT FILES, AND THE OUTPUT FILE, ARE THEN CLOSED, AND A RETURN
; MADE TO THE MAIN PROGRAM.
;
PHASE2:	JSR	PC,CLRBUF	;INITIALISE OUTPUT BUFFER
;
; CONSTRUCT COMD AND PUT IT OUT
;
	MOV	BTVAL,R0	;/B OR /T SWITCH VALUE (IF ANY)
	TSTB	BTFLG		;ANY /B OR /T VALUE?
	BEQ	5$		;NO - PUT PROGRAM AT TOP OF ADDRESS SPACE
	BPL	6$		;/T VALUE
	MOV	R0,R1		;/B VALUE SPECIFIED, CHECK THAT...
	ADD	PRGSIZ,R1	;...PROGRAM WILL NOT EXTEND OUTSIDE...
	CMP	R1,#160000	;...POSSIBLE ADDRESS SPACE
	BLOS	7$		;BRANCH IF OK
	JSR	PC,RNGERR	;/B VALUE TOO HIGH, WARN THE USER...
	BR	7$		;...BUT CONTINUE
5$:	MOV	MAXCOR,R0	;DEFAULT 'TOP' VALUE
6$:	DEC	R0		;'TOP' IS REALLY FIRST FREE BYTE
	MOV	PRGSIZ,R1	;/T VALUE SPECIFIED, CHECK THAT...
	DEC	R1		;...PROGRAM WILL NOT LOAD BELOW...
	CMP	R1,R0		;...ADDRESS 0
	BLOS	8$		;BRANCH IF OK
	JSR	PC,RNGERR	;/T VALUE TOO LOW, WARN THE USER...
8$:	SUB	R1,R0		;...BUT CONTINUE
7$:	MOV	R0,LOADPT
	MOV	R0,CLDPT	;SET UP ALL 3 LOAD POINT ENTRIES...
	MOV	R0,XFADD	;...IN THE DUMMY COMD
	.TIME			;GET THE CREATION TIME FROM DOS...
	MOV	(SP)+,CTIME	;...AND PUT IT INTO THE COMD
	MOV	(SP)+,CTIME+2
	.DATE			;GET THE CREATION DATE FROM DOS...
	MOV	(SP)+,CDATE	;...AND PUT THAT INTO THE COMD
	MOV	#COMD,R1	;NOW THE CHECKSUM BYTE FOR THE NEW
	MOV	CSIZE,R2	;COMD MUST BE CALCULATED
	CLR	R3		;THE SUM OF ALL BYTES IN THE COMD
1$:	MOVB	(R1)+,R0	;IS ACCUMULATED IN R3...
	ADD	R0,R3
	DEC	R2
	BNE	1$
	NEG	R3		;...NEGATED...
	MOVB	R3,CCHECK	;...AND USED AS THE CHECKSUM
	MOV	#COMD,R1	;NOW WRITE OUT THE COMD
	MOV	CSIZE,R2	;NUMBER OF BYTES IN THE COMD...
	INC	R2		;...PLUS ONE FOR THE CHECKSUM
2$:	MOVB	(R1)+,R0
	JSR	PC,PBYTE	;PUT OUT A BYTE
	DEC	R2
	BNE	2$
	CLR	R0		;PAD WITH A NULL BYTE
	JSR	PC,PBYTE	;TO MAKE FILE TIDY
;
; CALCULATE LOAD ADDRESS BIAS FOR TRAP PACKAGE
;
	MOV	TBASE,BIAS	;ORIGINAL TRAP PACKAGE LOAD POINT...
	SUB	LOADPT,BIAS	;...LESS THE FINAL LOAD POINT
;
; COPY TRAP PACKAGE ACROSS, ADJUSTING LOAD ADDRESSES AND CHECKSUMS
;
	SELECT	I1		;MAKE THE TRAP PACKAGE THE CURRENT INPUT FILE
4$:	JSR	PC,RDBLK	;READ THE NEXT LOAD BLOCK
	MOV	2(R1),R2	;BYTE COUNT
	CMP	#6,R2		;LAST BLOCK?
	BEQ	ENDTRP		;YES - GO DO IMP PROGRAM
	SUB	BIAS,4(R1)	;BIAS LOAD ADDRESS
	CLR	OUCHEK		;INITIALISE OUTPUT CHECKSUM
3$:	MOVB	(R1)+,R0	;COPY BLOCK TO OUTPUT FILE
	ADD	R0,OUCHEK	;UPDATE OUTPUT CHECKSUM
	JSR	PC,PBYTE
	DEC	R2
	BNE	3$
	MOV	OUCHEK,R0	;NOW THE CHECKSUM BYTE
	NEG	R0
	JSR	PC,PBYTE
	BIT	#1,O.BC		;ODD NUMBER OF BYTES IN BLOCK?
	BEQ	4$		;NO - GO DO NEXT BLOCK
	CLR	R0		;YES - PAD WITH NULL BYTE TO
	JSR	PC,PBYTE	;MAKE FILE TIDY
	BR	4$		;GO TO DO NEXT LOAD BLOCK
	.CLOSE	#I1.LB		;CLOSE TRAP PACKAGE FILE
	.RLSE	#I1.LB
	CLR	I1.LB
;
; NOW COPY THE IMP PROGRAM TO THE OUTPUT FILE, ADJUSTING
; AS FOR THE TRAP PACKAGE
;
ENDTRP:	SELECT	I2		;MAKE IMP PROGRAM FILE THE CURRENT ONE
	MOV	LOADPT,R0
	ADD	TRPSIZ,R0
	SUB	#20,R0		;CALCULATE LOAD BIAS
	MOV	R0,BIAS
1$:	JSR	PC,RDBLK	;READ THE NEXT LOAD BLOCK
	MOV	2(R1),R2	;BYTE COUNT
	CMP	#6,R2		;LAST BLOCK?
	BEQ	DOTERM		;YES - GO WRITE TERMINATOR BLOCK
	ADD	BIAS,4(R1)	;BIAS LOAD ADDRESS
	CLR	OUCHEK		;INITIALISE OUTPUT CHECKSUM
2$:	MOVB	(R1)+,R0	;COPY THE BLOCK TO THE OUTPUT FILE
	ADD	R0,OUCHEK	;UPDATE OUTPUT CHECKSUM
	JSR	PC,PBYTE
	DEC	R2
	BNE	2$
	MOV	OUCHEK,R0	;NOW THE CHECKSUM BYTE
	NEG	R0
	JSR	PC,PBYTE
	BIT	#1,O.BC		;ODD NUMBER OF BYTES IN BLOCK?
	BEQ	3$		;NO
	CLR	R0		;YES - PAD WITH A NULL BYTE
	JSR	PC,PBYTE	;TO MAKE FILE TIDY
3$:	JSR	PC,GWORD	;EXHAUST LINE NUMBER BYTES
	BR	1$		;GO TO DO NEXT LOAD BLOCK
;
; END OF INPUT FILE - WRITE TERMINATOR BLOCK
;
DOTERM:	MOV	#1,R0
	JSR	PC,PWORD	;OUTPUT HEADER BYTES...
	MOV	#6,R0
	JSR	PC,PWORD	;...BYTE COUNT...
	MOV	LOADPT,R0
	JSR	PC,PWORD	;...AND TRANSFER ADDRESS
	MOV	#7,R0		;(1 + 6) FOR CHECKSUM
	MOV	LOADPT,R1	;ADD IN BOTH BYTES...
	ADD	R1,R0		;...OF THE TRANSFER ADDRESS...
	SWAB	R1
	ADD	R1,R0
	NEG	R0		;...THEN NEGATE TO OBTAIN BLOCK CHECKSUM
	JSR	PC,PBYTE	;OUTPUT IT
	TSTB	NULFLG		;OUTPUT SUPPRESSED?
	BNE	2$		;YES - OMIT CLOSE
	TST	O.BC		;ANYTHING LINGERING IN OUTPUT BUFFER?
	BEQ	1$		;NO
	JSR	PC,WRBLK	;YES - SHOVE IT OUT
1$:	.CLOSE	#O.LB		;CLOSE OUTPUT FILE
	.RLSE	#O.LB
	CLR	O.LB
2$:	.CLOSE	#I2.LB		;AND IMP PROGRAM INPUT FILE
	.RLSE	#I2.LB
	CLR	I2.LB
	RTS	PC		;END OF PHASE 2
	.SBTTL	MAP OUTPUT ROUTINE
;
; ATTEMPT TO DELETE MAP FILE, IN CASE IT
; ALREADY EXISTS
;
MAPOUT:	.INIT	#MAP.LB		;INITIALISE THE DEVICE DRIVER
	MOV	#MAP.FB,R0	;COPY FILNAM.EXT[UIC] TO
	MOV	#DM.FB,R1	;THE DELETION FILE BLOCK
	MOV	(R0)+,(R1)+
	MOV	(R0)+,(R1)+
	MOV	(R0)+,(R1)+
	MOV	(R0),(R1)
	.DELET	#MAP.LB,#DM.FB	;ATTEMPT THE DELETE
;
; CONTROL ALWAYS RETURNS HERE, REGARDLESS OF WHETHER OR
; NOT THE DELETE WAS SUCCESSFUL
;
DM.ERR:	.OPEN	#MAP.LB,#MAP.FB	;OPEN THE MAP FILE FOR OUTPUT
;
; FILL IN THE DETAILS OF THE MAP
;
	MOV	#FILNAM,R0	;CLEAR OUT ANY OLD FILENAME
	MOV	#12,R1
9$:	MOVB	#SPACE,(R0)+
	DEC	R1
	BNE	9$
	MOVB	#'.,FILNAM+6	;RESTORE THE PERIOD
	TSTB	NULFLG		;OUTPUT SUPPRESSED?
	BNE	10$		;YES - DON'T COPY IN FILENAME
	.RADUP	#FILNAM,O.FB	;OUTPUT FILE NAME
	.RADUP	#FILNAM+3,O.FB+2
	.RADUP	#EXT,O.FB+4
10$:	.CVTDT	#2,#LDATE,CDATE	;UNPACK DATE STRING INTO MAP
	.CVTDT	#3,#LTIME,CTIME+2,CTIME ;AND THE TIME STRING
	TSTB	BTFLG		;/B OR /T VALUE SUPPLIED?
	BNE	2$		;YES - GO PUT STRING AND VALUE INTO MAP
	MOV	#BTTYPE,R0	;NO - FILL AREA WITH NULLS
	MOV	#43,R1
3$:	CLRB	(R0)+
	DEC	R1
	BNE	3$
	BR	4$		;GO TO FILL IN REST OF MAP
2$:	MOV	#BTSTR,R0	;COPY 'ADDRESS SPECIFIED' STRING...
	MOV	#BTLINE,R1	;...INTO THE MAP
	MOV	#24,R2
5$:	MOVB	(R0)+,(R1)+
	DEC	R2
	BNE	5$
	TSTB	BTFLG		;LOW OR HIGH ADDRESS?
	BMI	6$		;BRANCH IF LOW
	MOV	#HSTR,R0	;HIGH
	BR	7$		;GO TO COPY INTO MAP
6$:	MOV	#LSTR,R0
7$:	MOV	#BTTYPE,R1
	MOV	#4,R2
8$:	MOVB	(R0)+,(R1)+	;COPY ' LOW' OR 'HIGH' INTO MAP
	DEC	R2
	BNE	8$
	.BIN2O	#LBTVAL,BTVAL	;PUT /B OR /T VALUE ITSELF INTO MAP
	MOVB	#CR,LBTNL	;FINISH OFF THE LINE
	MOVB	#LF,LBTNL+1
	MOVB	#LF,LBTNL+2	;ADD A BLANK LINE
4$:	.BIN2O	#LLOW,LOADPT	;PROGRAM LOW ADDRESS
	MOV	LOADPT,R0
	ADD	PRGSIZ,R0
	DEC	R0
	MOV	R0,-(SP)	;NEEDED AGAIN LATER
	.BIN2O	#LHIGH,R0	;PROGRAM HIGH ADDRESS
	.BIN2O	#LSIZE,PRGSIZ	;PROGRAM SIZE
	.RADUP	#LIDENT,CIDENT	;PROGRAM IDENTIFICATION
	.RADUP	#LIDENT+3,CIDENT+2
	MOV	#LIDENT,R0	;TRAP PACKAGE IDENTIFICATION - COPY
	MOV	#TIDENT,R1
	MOV	#6,R2
1$:	MOVB	(R0)+,(R1)+
	DEC	R2
	BNE	1$
	.BIN2O	#LXFADD,XFADD	;PROGRAM TRANSFER ADDRESS
	.RADUP	#LTNAME,I1.FB	;TRAP PACKAGE FILE NAME
	.RADUP	#LTNAME+3,I1.FB+2
	.RADUP	#LTEXT,I1.FB+4
	.BIN2O	#LTLOW,LOADPT	;TRAP PACKAGE LOW ADDRESS
	MOV	LOADPT,R0
	ADD	TRPSIZ,R0
	MOV	R0,-(SP)	;IMP PROGRAM LOW ADDRESS - NEEDED LATER
	DEC	R0
	.BIN2O	#LTHIGH,R0	;TRAP PACKAGE HIGH ADDRESS
	.BIN2O	#LTSIZE,TRPSIZ	;TRAP PACKAGE SIZE
	.RADUP	#LINAME,I2.FB	;IMP PROGRAM FILE NAME
	.RADUP	#LINAME+3,I2.FB+2
	.RADUP	#LIEXT,I2.FB+4
	MOV	(SP)+,R0
	.BIN2O	#LILOW,R0	;IMP PROGRAM LOW ADDRESS
	MOV	(SP)+,R0
	.BIN2O	#LIHIGH,R0	;IMP PROGRAM HIGH ADDRESS
	.BIN2O	#LISIZE,IMPSIZ	;IMP PROGRAM SIZE
;
; WRITE THE MAP TO THE MAP FILE
;
	.WRITE	#MAP.LB,#MAP.BH
	.WAIT	#MAP.LB
	TSTB	MAP.ST		;STATUS OK?
	BEQ	MAPOK		;YES - GO CLOSE FILE
MAPERR:	ERROR	<I/O ERROR ON MAP FILE>, DIAG, NL
MAPOK:	.CLOSE	#MAP.LB		;CLOSE THE MAP FILE
	.RLSE	#MAP.LB
	RTS	PC		;END OF MAP OUTPUT
;
; SKELETON LOAD MAP
;
LDMAP:	.ASCII	/FILE /
FILNAM:	.BLKB	6		;OUTPUT FILE NAME
	.BYTE	'.
EXT:	.BLKB	3		;OUTPUT FILE EXTENSION
	.ASCII	/ MEMORY ALLOCATION MAP/
	.BYTE	CR, LF
	.ASCII	/THIS ALLOCATION WAS DONE ON /
LDATE:	.BLKB	11		;DATE STRING
	.BYTE	CR, LF
	.ASCII	/AT /
LTIME:	.BLKB	10		;TIME STRING
	.ASCII	/ ILINK VERSION V01-03/
	.BYTE	CR, LF, LF, LF, LF
BTTYPE:	.BLKB	4		;'HIGH' OR 'LOW'
BTLINE:	.BLKB	24
LBTVAL:	.BLKB	10		;/B OR /T VALUE STRING
LBTNL:	.BLKB	3
	.ASCII	"R/W MEM  LIMITS: "
LLOW:	.BLKB	6		;LOW ADDRESS STRING
	.BYTE	SPACE
LHIGH:	.BLKB	6		;HIGH ADDRESS STRING
	.BYTE	SPACE
LSIZE:	.BLKB	6		;LENGTH STRING
	.BYTE	CR, LF
	.ASCII	/IDENTIFICATION : /
LIDENT:	.BLKB	6		;TRAP PACKAGE .IDENT STRING
	.BYTE	CR, LF
	.ASCII	/PRG XFR ADDRESS: /
LXFADD:	.BLKB	6		;TRANSFER ADDRESS STRING
	.BYTE	CR, LF, LF, LF
	.ASCII	/FILE CONTENTS:/
	.BYTE	CR, LF, LF, LF, LF
	.ASCII	/*** TRAP PACKAGE/
	.ASCII	/   IDENT: /
TIDENT:	.BLKB	6		;TRAP PACKAGE .IDENT STRING
	.ASCII	/  FILE: /
LTNAME:	.BLKB	6		;TRAP PACKAGE FILE NAME
	.BYTE	'.
LTEXT:	.BLKB	3		;TRAP PACKAGE FILE EXTENSION
	.BYTE	CR, LF, LF
	.ASCII	/          /
LTLOW:	.BLKB	6		;TRAP PACKAGE LOW ADDRESS STRING
	.BYTE	SPACE
LTHIGH:	.BLKB	6		;TRAP PACKAGE HIGH ADDRESS STRING
	.BYTE	SPACE
LTSIZE:	.BLKB	6		;TRAP PACKAGE LENGTH STRING
	.BYTE	CR, LF, LF, LF
	.ASCII	/*** IMP PROGRAM/
	.ASCII	/                   FILE: /
LINAME:	.BLKB	6		;IMP PROGRAM FILE NAME
	.BYTE	'.
LIEXT:	.BLKB	3		;IMP PROGRAM FILE EXTENSION
	.BYTE	CR, LF, LF
	.ASCII	/          /
LILOW:	.BLKB	6		;IMP PROGRAM LOW ADDRESS STRING
	.BYTE	SPACE
LIHIGH:	.BLKB	6		;IMP PROGRAM HIGH ADDRESS STRING
	.BYTE	SPACE
LISIZE:	.BLKB	6		;IMP PROGRAM LENGTH STRING
	.BYTE	CR, LF, LF
LMEND:				;END OF LOAD MAP
;
; OPTIONAL HIGH/LOW ADDRESS LINE
;
LSTR:	.ASCII	/ LOW/
HSTR:	.ASCII	/HIGH/
BTSTR:	.ASCII	/ ADDRESS SPECIFIED: /
	.EVEN
	.SBTTL	COMMON INPUT ROUTINES
;
; READ A LOAD BLOCK FROM THE CURRENT INPUT FILE, INTO THE COMMON
; BUFFER 'BLKBUF'. IF THE BLOCK CHECKSUM IS INCORRECT, OR THE BLOCK
; EXCEEDS THE SIZE OF THE BUFFER, A FATAL ERROR IS FORCED
; ON EXIT, R1 -> FIRST BYTE OF BLOCK (THE '1' BYTE)
;
RDBLK:	JSR	PC,GBYTE	;GET A BYTE FROM FILE
	TSTB	R0
	BEQ	RDBLK		;IGNORE NULLS
	MOV	R0,-(SP)
	JSR	PC,GBYTE	;GET NEXT BYTE
	SWAB	R0
	CLRB	R0
	BISB	(SP)+,R0	;CONSTRUCT A WORD
	CMP	#1,R0
	BEQ	4$		;SHOULD BE A 1
	JMP	FERR		;FILE HAS ILLEGAL FORMAT
4$:	MOV	R0,INCHEK	;INITIALISE CHECKSUM
	MOV	#BLKBUF,R1	;BUFFER POINTER
	MOV	R0,(R1)+	;PUT HEADER BYTES IN BUFFER
	JSR	PC,GWORD	;GET BYTE COUNT
	CMP	#BLKSIZ,R0	;BLOCK TOO LARGE?
	BGE	1$		;NO - OK
	JMP	TOOBIG		;YES - ERROR
1$:	MOV	R0,(R1)+	;PUT BYTE COUNT IN BUFFER
	MOV	R0,R2		;USED AS LOOP COUNTER
	SUB	#4,R2		;ADJUST FOR BYTES ALREADY READ
2$:	JSR	PC,GBYTE	;READ IN THE REST OF THE BLOCK
	MOVB	R0,(R1)+
	DEC	R2
	BGE	2$		;INCLUDING CHECKSUM BYTE
	TSTB	INCHEK		;CHECKSUM OK?
	BEQ	3$		;YES
	JMP	CKERR		;NO - ERROR
3$:	MOV	#BLKBUF,R1
	RTS	PC
;
; GET A WORD TO R0 FROM CURRENT INPUT FILE
;
GWORD:	JSR	PC,GBYTE	;GET LOW BYTE
	MOV	R0,-(SP)	;SAVE IT
	JSR	PC,GBYTE	;GET HIGH BYTE
	SWAB	R0
	CLRB	R0
	BISB	(SP)+,R0	;CONSTRUCT WORD
	RTS	PC
;
; GET A BYTE TO R0 FROM CURRENT INPUT FILE
;
GBYTE:	TST	BC(R4)		;ANYTHING IN BUFFER?
	BNE	1$		;YES - GO GET A BYTE
	JSR	PC,DREAD	;NO - GET FRESH BUFFER LOAD
1$:	MOVB	@(R5),R0	;GET A BYTE FROM BUFFER
	INC	@R5
	BIC	#177400,R0	;CLEAN IT UP
	ADD	R0,INCHEK	;UPDATE CHECKSUM
	DEC	BC(R4)		;DECREMENT COUNT OF BYTES REMAINING
	RTS	PC
;
; GET NEXT BUFFER LOAD FROM CURRENT INPUT FILE
; IF AN END-OF-FILE CONDITION IS DETECTED, AND THERE IS
; NO MORE DATA IN THE BUFFER, A FATAL ERROR IS FORCED.
;
DREAD:	.READ	R3,R4		;INITIATE THE READ
	.WAIT	R3		;AWAIT COMPLETION
	MOVB	STATUS(R4),R0	;GET FILE STATUS
	BEQ	1$		;ZERO IS OK
	BITB	#101,R0		;TEST FOR EOF BITS
	BNE	2$		;THEY ARE THERE
	JMP	INERR		;ANYTHING ELSE IS ERROR
2$:	BIT	#1,R0		;ANY ACTUAL DATA?
	BNE	1$		;YES
	JMP	EOFERR		;NO - ERROR
1$:	MOV	2(R5),(R5)	;RESET BUFFER POINTER
	RTS	PC
	.SBTTL	OUTPUT ROUTINES
;
; OUTPUT THE WORD IN R0 TO THE OUTPUT FILE
;
PWORD:	MOV	R0,-(SP)
	JSR	PC,PBYTE	;OUTPUT LOW BYTE
	MOV	(SP)+,R0
	SWAB	R0
	JSR	PC,PBYTE	;OUTPUT HIGH BYTE
	RTS	PC
;
; OUTPUT THE BYTE IN R0 TO THE OUTPUT FILE
;
PBYTE:	TSTB	NULFLG		;OUTPUT SUPPRESSED?
	BNE	PRET		;YES - JUST RETURN
	MOVB	R0,@O.PT	;PUT BYTE IN BUFFER
	INC	O.PT		;BUMP BUFFER POINTER
	INC	O.BC		;BUMP BYTE COUNT
	CMP	#BUFSIZ,O.BC	;FULL BUFFER?
	BNE	PRET		;BRANCH IF NOT FULL
; SPECIAL ENTRY TO WRITE LAST BUFFER
WRBLK:	MOV	O.BC,O.BH	;COPY ACTUAL BYTE COUNT TO MAX B/C
	.WRITE	#O.LB,#O.BH	;INITIATE THE WRITE
	.WAIT	#O.LB		;AWAIT COMPLETION
	MOVB	O.ST,R0		;GET FILE STATUS
	BEQ	1$		;ZERO MEANS OK
	JMP	OUTERR		;OUTPUT FAILURE
1$:	JSR	PC,CLRBUF	;RE-INITIALISE BUFFER
PRET:	RTS	PC
	.SBTTL	INITIALISE OUTPUT BUFFER
;
CLRBUF:	MOV	R1,-(SP)	;DO NOT CORRUPT R1!
	MOV	#O.BUF,R0
	MOV	R0,O.PT		;INITIALISE BUFFER POINTER
	MOV	#BUFSIZ,R1
1$:	CLRB	(R0)+
	DEC	R1
	BNE	1$
	CLR	O.BC		;INITIALISE BYTE COUNT
	MOV	(SP)+,R1	;RESTORE R1 TO ENTRY VALUE
	RTS	PC
	.SBTTL	ERROR ROUTINES
;
	.ENABL	LSB
FERR:	ERROR	<FILE >, FATAL
	MOV	R3,R0		;POINTER TO LINK BLOCK
	ADD	#FNOSET,R0	;POINT TO FILENAME IN FILE BLOCK
	JSR	PC,FNOUT	;PRINT THE FILENAME
	MESS	< HAS ILLEGAL FORMAT>, NL
	JMP	SHUTUP		;GIVE UP
;
XADERR:	ERROR	<FILE >, DIAG
	MOV	#I1.FB,R0
	JSR	PC,FNOUT	;PRINT THE FILENAME
	MESS	< HAS PROGRAM TRANSFER ADDRESS>, NL
	RTS	PC		;CARRY ON
;
ODTERR:	ERROR	<FILE >, DIAG
	MOV	#I1.FB,R0
	JSR	PC,FNOUT	;PRINT THE FILENAME
	MESS	< HAS ODT TRANSFER ADDRESS>, NL
	RTS	PC		;CARRY ON
;
CKERR:	ERROR	<FILE >, FATAL
	MOV	R3,R0		;POINTER TO LINK BLOCK
	ADD	#FNOSET,R0	;POINT TO FILENAME IN FILE BLOCK
	JSR	PC,FNOUT	;PRINT THE FILENAME
	MESS	< HAS CHECKSUM ERROR>, NL
	JMP	SHUTUP		;GIVE UP
;
TOOBIG:	ERROR	<LOAD BLOCK TOO LARGE IN FILE >, FATAL
1$:	MOV	R3,R0		;POINTER TO LINK BLOCK
	ADD	#FNOSET,R0	;POINT TO FILENAME IN FILE BLOCK
2$:	JSR	PC,FNOUT	;PRINT THE FILENAME
	JSR	R1,MESOUT	;FINISH OFF
	.BYTE	CR, LF, 0
	.EVEN
	JMP	SHUTUP		;GIVE UP
;
RNGERR:	MOV	R0,-(SP)	;DO NOT CORRUPT R0!
	ERROR	<LOAD ADDRESS OUT OF RANGE>, DIAG, NL
	MOV	(SP)+,R0
	RTS	PC		;CARRY ON
;
I1.ERR:	MOV	#I1.LB,R3
	BR	INERR
I2.ERR:	MOV	#I2.LB,R3	;DROP THROUGH TO COMMON CODE
;
INERR:	ERROR	<I/O FAILURE ON INPUT FILE >, FATAL
	BR	1$		;GO TO PRINT FILENAME
;
EOFERR:	ERROR	<PREMATURE EOF ON INPUT FILE >, FATAL
	BR	1$		;GO TO PRINT FILENAME
;
OUTERR:	ERROR	<I/O ERROR ON OUTPUT FILE>, FATAL, NL
	JMP	SHUTUP		;GIVE UP
I1.OF:	MOV	#I1.FB,R3
	BR	OFAIL
I2.OF:	MOV	#I2.FB,R3
	BR	OFAIL
O.OF:	MOV	#O.FB,R3
	BR	OFAIL
MAP.OF:	MOV	#MAP.FB,R3
;
OFAIL:	ERROR	<OPEN FAILURE ON FILE >, FATAL
	MOV	R3,R0		;POINTER TO FILENAME
	BR	2$		;GO TO PRINT FILENAME
	.DSABL	LSB
	.SBTTL	MISCELLANEOUS OUTPUT ROUTINES
;
; OUTPUT FILNAM.EXT POINTED AT BY R0, TO THE KEYBOARD
;
FNOUT:	.RADUP	#WKSP,(R0)+	;CALL DOS TO CONVERT RADIX-50
	.RADUP	#WKSP+3,(R0)+	;FILNAM.EXT TO ASCII
	.RADUP	#WKSP+7,(R0)	;MISS OUT THE BYTE CONTAINING A PERIOD
	MOV	R1,-(SP)	;DO NOT CORRUPT R1!
	MOV	#WKSP,R1
	MOV	#12,R2		;COUNT OF BYTES TO OUTPUT
1$:	MOVB	(R1)+,R0
	JSR	PC,KBCHAR
	DEC	R2
	BNE	1$
	MOV	(SP)+,R1
	RTS	PC
	.SBTTL	MESSAGE OUTPUT ROUTINES
;
; OUTPUT A FATAL ERROR MESSAGE
; CALLING SEQUENCE :-
;    JSR    R1,FTLMSG
;   .ASCIZ  "TEXT"
;   .EVEN
; THIS IS USUALLY GENERATED BY THE ERROR MACRO
;
FTLMSG:	JSR	R1,MESOUT	;PUT THE PREAMBLE OUT
	.BYTE	CR, LF
	.ASCIZ	/*FATAL*-/
	.EVEN
	BR	MESOUT		;AND GO TO COMMON CODE
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; OUTPUT A WARNING MESSAGE
; CALLING SEQUENCE :-
;    JSR    R1,WRNMSG
;    .ASCIZ "TEXT"
;    .EVEN
; THIS IS USUALLY GENERATED BY THE ERROR MACRO
;
WRNMSG:	JSR	R1,MESOUT	;PUT THE PREAMBLE OUT
	.BYTE	CR, LF
	.ASCIZ	/*DIAG*-/
	.EVEN			;AND DROP THROUGH INTO COMMON CODE
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; OUTPUT AN ASCIZ STRING TO THE KEYBOARD.
; THE STRING SHOULD IMMEDIATELY FOLLOW THE CALL.
; STOPS ON THE ZERO BYTE, NO CR-LF IS PRINTED
; CALLING SEQUENCE :-
;    JSR    R1,MESOUT
;    .ASCIZ "TEXT"
;	.EVEN
; THIS IS USUALLY GENERATED BY THE MESS MACRO
;
MESOUT:	MOVB	(R1)+,R0	;GET NEXT BYTE, IS IT NULL?
	BEQ	1$		;YES - FINISHED
	JSR	PC,KBCHAR	;NO - PUT IT OUT
	BR	MESOUT		;GO TO DO NEXT BYTE
1$:	INC	R1		;ALIGN R1 UP TO NEXT
	BIC	#1,R1		;WORD BOUNDARY
	RTS	R1		;AND RETURN
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; OUTPUT THE CHARACTER IN R0 TO THE KEYBOARD BUFFER
; A VERTICAL TAB OR A LINEFEED CAUSES THE BUFFER TO BE OUTPUT
; BUFFER OVERFLOW IS NOT DETECTED
;
KBCHAR:	MOVB	R0,@MES.PT	;PUT CHARACTER IN BUFFER
	INC	MES.PT		;BUMP POINTER
	INC	MES.BC		;INCREMENT ACTUAL BYTE COUNT
	CMPB	#VT,R0		;CHECK FOR TERMINATORS
	BEQ	1$
	CMPB	#LF,R0
	BNE	2$		;NOT TERMINATOR - JUST RETURN
1$:	.WRITE	#MES.LB,#MES.BH	;WRITE THE BUFFER
	.WAIT	#MES.LB		;WAIT FOR COMPLETION
	MOV	#MES.BUF,MES.PT	;RESET BUFFER POINTER...
	CLR	MES.BC		;...AND ACTUAL BYTE COUNT
2$:	RTS	PC
	.SBTTL	CORE DETERMINATION
;
; SET 'MAXCOR' TO THE HIGHEST WORD ADDRESS WHICH THE LINKED PROGRAM
; MAY EVENTUALLY OCCUPY, PLUS 2. ALLOWANCE IS MADE FOR THE BOOTSTRAP
; AND ABSOLUTE LOADER AREAS, WHICH ARE LEFT INTACT.
;
GETCOR:	.CORE			;ASK DOS FOR THE CORE SIZE
	SUB	#316,(SP)	;ADJUST TO LEAVE ROOM FOR LOADER
	MOV	(SP)+,MAXCOR
	RTS	PC
;
	.END	BEGIN

