	TITLE	'XREF.ASM  Ver 1.41	03/15/82'
	PAGE	0

;********************************************************
;							*
; This program generates a cross reference list for all	*
; Symbols in a CPM assembler program.  Its input is	*
; either an assembler source file (FN.ASM) or an assem-	*
; bler listing file (FN.PRN).  It will number the lines	*
; and generate a cross reference listing at the end of	*
; the program.  The output defaults to the CPM list	*
; device, but it can be directed to the console or to a *
; disk file instead.  If the output is directed to a	*
; disk file, that file will be named FN.XRF.  The	*
; filename will be the filename of the input file.	*
; It is invoked by entering:				*
;		XREF FN.ASM	(OUTPUT TO LIST DEVICE)	*
;		XREF FN.PRN CON	(OUTPUT TO CONSOLE)	*
;		XREF FN.ASM D	(OUTPUT TO DISK-DEFAULT	*
;						DRIVE)	*
;		XREF FN.PRN B:D	(OUTPUT TO DISK-DRIVE	*
;				NUMBER SPECIFIED)	*
;							*
; To invoke help function enter:			*
;		XREF ?					*
;							*
;********************************************************

;03/15/82 This program would lock up if there was a
;comment line that started with an asterisk('*') instead
;of a semicolon.  The program then processed the whole
;line as a valid line of assemble code.  This usually
;resulted in the cross reference program locking up if
;there were a lot lines that started with an asterisk.
;The program now treats an asterisk the same as a
;semicolon.  This was done by changing the table CTAB1
;so that the program branches to LSEMI when it finds an
;asterisk. Also fixed bug that occurred when the last
;record of a file was completely filled with data and
;thus had no control Z's(EOF characters) in it.  This
;was done by always placing two control Z's after the
;last record was read.  Finally fixed a bug that showed
;up only when a program had no symbols or labels in it.
;Added a check for an empty symbol table before attempting
;to print the symbol table.  (JRM)

;03/06/82 Changed the code so that the size of output
;disk buffer can be specified at assembly time.  The
;size is set by OUTSECT which equals the number of disk
;sectors to be written when the buffer is full.
;Implemented the same feature for disk input.  INSECT sets
;the number of disk records to be read into the input
;buffer.  On file type PRN the was a bug.  Whenever the
;first symbol on a line was preceded by a character, the
;line was not listed in the cross reference listing.  This
;was caused by skipping over the first 16 characters in the
;line on a CR(carriage return) instead of on a LF(line feed).
;This bug has been fixed.  Added help function.  This is
;invoked by entering  XFER ?.  (JRM)


;03/03/82 Added a 2k buffer for disk output writes.  This
;was done to speed up the execution of program when disk
;output was specified.  Fixed bug when page size was
;specified.  CPM assembler was not doing the conditional
;IF assembly correctly.  Changed the code to test if page
;size is in effect by testing LPAGE to see if it is zero.
;Changed test to question to erase existing disk cross
;reference file to handle lower case as well as upper
;case.  (JRM)

;02/20/82 Changed code so that the size of the symbol
;that is referenced in the cross reference listing can
;be set at assembly time.  The original program only
;allowed for 5 char per symbol.  This did not allow for
;separate unique symbols where the uniqueness occurred
;after the first 5 characters.  I tested this function
;for a length of 5, 8, and 10 characters labels.  It
; should work for any length of symbol.  I also
;allowed the '$' character to be a valid character in a
;symbol.  The equ SYMSIZ was added and all other values
;and variables that are dependent on the size of the
;symbol are expressed using SYMSIZ.
;		Example: SSIZ EQU SYMSIZ+3
;Added a disk file output option.  The output disk file
;will the filename of the input file and always a file
;type of 'XRF'.  This option is enabled by specifying
;a 'D' as the second parameter when executing the program.
;The user can optionally specify a different drive than
;the default drive for the cross reference disk output.
;		Example: XREF FN.ASM D
;			 XREF FN.PRN B:D
;						(JRM)

;11/01/81 Changed the code so that the hex addresses and
;the hex translation of the instructions are not included
;in the cross reference listing for PRN files from CPM
;assembler.  The listing can now be directed at execution
;time to the console or the list (LST:) device.  If the
;listing is to be displayed on the console enter
;	XREF FN.FT C
;If the listing is to go to the list device enter
;	XREF FN.FT
;In other words, the output goes to the list device by
;default. (JRM)

;Modifications 4/5/79, 4/26/79, 5/3/79, 8/28/79 by SAN:
;Suppress initial page eject, add page eject at completion
;Automatically convert lower case in source file to UPPER
;Abort execution if ^C typed, but ignore other typed chars
;Add ELSE, PAGE to reserved word table

;********************************************************

;Number of lines per output page set by LPAGE EQUate

;Number of symbol refs per table entry set by NREFS EQUate
;	This value should be set to the average number of
;	references that a symbol will have in program.  It
;	is used to build the symbol reference table entries.
;	Each entry will be equal to (NREFS * 2)+2.  If it is
;	made unnecessarily large, then the symbol reference
;	table will be very large with a lot of unused slots.
;	Example: a program with 300 symbols will require a
;	reference table that is at least 12600 bytes if
;	NREFS=20, whereas the same program will require a
;	reference table that is at least 3600 bytes if
;	NREFS=5.  Each symbol that exceeds NREFS references
;	will require ((Total references/NREFS)rounded up)
;	reference table entries.  Recommended values: 3-5.

;Number of symbol references to be printed per output line
;	This is set by LREFS EQUate.  It should be a multiple
;	of NREFS.  If it is not, then the first number that is
;	less than LREFS and is a multiple of NREFS will be
;	printed on the line.  Example: if NREFS is 5 and
;	LREFS is 13, then only 10 entries will be printed per
;	line.  The total number of references that will fit on
;	one line without overflow is equal to the size of the
;	line minus (SYMSIZ+2) divided by 5.  Example: if the
;	line size is 132 characters and SYMSIZ equals 8,
;	then Total num of ref/line can be INT((132-(8+2))/5)
;	or INT(122/5) or 20.

;Symbol size is set by SYMSIZ.  This is the symbol size that the
;	program will use in the cross reference listing.  It
;	should be large enough to make each symbol unique in
;	cross reference listing.  If a symbol is larger than the
;	value for SYMSIZ, then the symbol will be truncated in the
;	output(eg: LARGE$SYMBOL would be LARGE$SYM if SYMSIZ was
;	set to 9.  Recommended value is 8.

;OUTSECT sets the number of disk records that will be written to
;	disk when the output disk buffer is full.  This value is
;	used by the program to calculate the size of the output
;	disk buffer.  Output buffer size = OUTSECT * 128.
;	Recommended value is from 8 to 32.  16 works well.

;INSECT is the number of disk records that will be read when the
;	disk input buffer is empty.  It is used to calculate the
;	size of the input buffer.  Input buffer size = INSECT * 128.
;	Recommend value is from 8 to 32.  16 works well.

;NOTE:	If OUTSECT and INSECT are made too large, there will not be
;	be enough memory left to build the cross reference table
;	for a large program.  Largest tested values so far(3/6/82)
;	is 64 for OUTSECT and 64 for INSECT on a 64K system.  The
;	file successfully cross referenced was MODEM74.PRN. (JRM)

;Graceful exit after error message if file not found

LPAGE	EQU	00		;NUM LINES PER PAGE IF PEJECT IS TRUE
NREFS	EQU	03		;NUMBER OF REFERENCES PER REF TBL ENTRY
LREFS	EQU	21		;REFERENCES PER OUTPUT LINE
SYMSIZ	EQU	08		;NUMBER OF SYMBOL CHAR
OUTSECT	EQU	32		;NUMBER OF DISK SECTORS WRITTEN FROM BUFFER
INSECT	EQU	32		;NUMBER OF DISK SECTORS READ INTO BUFFER

;********************************;
;
;	   INTEL ASSEMBLER
;	CROSS REFERENCE PROGRAM  
;
;	    VERSION 1.41
;
;	   JEFF KRAVITZ 
;
;	MODIFIED BY JOHN MAHR
;
;********************************;

;********************************;
;
;          MAIN LOOP
;
;********************************;
	ORG	100H		;ORIGIN ADDRESS
XREF:	LXI	SP,STACK	;SET STACK POINTER
	CALL	SETUP		;INITIALIZE
MAIN:	CALL	GETBT		;GET A BYTE FROM SOURCE FILE
	CALL	SAVBT		;SAVE BYTE IN PRINT BUFFER
MAIN2:	CALL	CKNUM		;TEST FOR NUMERIC
	JNC	LNUM		;YES, FOUND A NUMBER, PROCESS
	CALL	CKALP		;TEST FOR ALPHABETIC
	JNC	LALPH		;YES, PROCESS
	LXI	H,CTAB1		;POINT TO CHARACTER TABLE
	CALL	LOOK		;LOOK UP CHAR IN CHAR TABLE
	JC	LIGN		;NOT FOUND, IGNORE
	PCHL			;EXECUTE ROUTINE

;*******************************************************
;
;		DONE
;
;      FINAL SYMBOL TABLE PRINT
;
; DONE gets control when a control Z(1AH) is found
; in the input data stream.  This routine:
;
;	1. issues a page eject
;	2. prints the cross reference table
;	3. issues a page eject
;	4. if disk output, writes out the remaining
;		sectors in the disk output buffer and
;		then closes the disk output file.
;	5. close the disk input file.
;	6. displays end of program message.
;
;*******************************************************

DONE:	CALL	EJECT		;ISSUE PAGE EJECT
	LHLD	SYMBT		;GET SYMBOL TABLE BOTTOM
	MVI	A,0FFH		;check to see if there were...
	CMP	M		;...any symbols in the program
	JZ	DLP4		;no, don't print symbol table
	SHLD	SYM		;SET SYMBOL POINTER
	LHLD	SYMTP		;GET SYMBOL TABLE TOP
	MVI	M,0FFH		;END OFF SYMBOL TABLE
DLP1:	LHLD	SYM		;GET SYMBOL TABLE POINTER
	CALL	PSYM		;PRINT SYMBOL
	LHLD	SYM
	LXI	D,SYMSIZ+1	;OFFSET TO REF LINK
	DAD	D
	MOV	E,M
	INX	H
	MOV	D,M		;GET REF BLOCK ADDR
	XCHG			;INTO HL
	SHLD	REF
	CALL	PREFS		;PRINT REFERENCES
	LHLD	SYM		;GET SYMBOL TABLE POINTER
	LXI	D,SSIZ		;SIZE OF SYM TABLE ENTRY
	DAD	D
	SHLD	SYM
	MOV	A,M		;GET BYTE
	CPI	0FFH		;END OF TABLE?
	JNZ	DLP1		;LOOP

DLP2:	CALL	EJECT		;PAGE EJECT
	LDA	DISKOUT		;GET DISK OUTPUT SWITCH
	ORA	A		;DISK OUTPUT BEING USED?
	JZ	DLP3		;NO, ISSUE END MSG & RETURN TO CPM
	LXI	H,DISKBUF	;GET OUTPUT DISK BUFFER
	LXI	D,OFCB		;AND OUT FCB
	CALL	DCLOSE		;AND CLOSE OUTPUT DISK FILE

DLP3:	LXI	D,TFCB		;CLOSE THE...
	MVI	C,CLOSE		;...INPUT...
	CALL	CPM		;...FILE.
	LXI	D,DONEMSG	;TELL OPERATOR...
	MVI	C,PSTRING	;...THAT PGM IS...
	CALL	CPM		;...DONE
	JMP	BOOT		;AND RETURN TO CP/M

DLP4:	LXI	H,NOSYMS	;get no symbol message
	MVI	B,28		;NUMBER OF CHAR IN MSG
DLP5:	MOV	E,M		;print the...
	CALL	PBYT		;...message
	INX	H
	DCR	B
	JNZ	DLP5
	JMP	DLP2		;go close files



;********************************;
;
;     SYMBOL PRINT ROUTINE
;
;********************************;
PSYM:	MVI	B,SYMSIZ	;SYMBOL SIZE
PSYM2:	MOV	E,M		;GET BYTE
	CALL	PBYT		;PRINT BYTE
	INX	H
	DCR	B
	JNZ	PSYM2
	MVI	E,' '
	CALL	PBYT		;PRINT 2 SPACES
	CALL	PBYT
	RET

;********************************;
;
;     REFERENCE PRINT ROUTINE
;
;********************************;
PREFS:	MVI	A,LREFS/NREFS	;NUMBER OF BLOCKS TO PRINT ON ONE LINE
	STA	NLINS		;TO NLINS
PREF0:	LHLD	REF		;GET REF BLOCK ADDR
	INX	H
	INX	H		;BUMP TO FIRST REF NUMBER
	SHLD	TEMP		;SAVE REF NUM ADDR
	MVI	A,(REFSZ-2)/2	;NUMBER OF REF SLOTS
	STA	SYMCT		;SAVE IN SYMCT
PREF:	LHLD	TEMP		;GET REF SLOT ADDR
	MOV	E,M
	INX	H
	MOV	D,M		;GET REF
	LXI	H,0000		;ZERO?
	CALL	CPHL
	JZ	CRLF		;YES, DONE
	XCHG			;GET NUM IN HL
	CALL	DECOT		;CONVERT
	LXI	H,DEC		;POINT TO DEC STRING
	MVI	M,' '		;BLANK LEADING ZERO
	MVI	B,5		;NUMBER OF DIGITS + 1 IN REF NUM
PREF2:	MOV	E,M
	CALL	PBYT		;PRINT BYTE
	INX	H
	DCR	B
	JNZ	PREF2		;PRINT REFERENCE NUMBER
	LHLD	TEMP		;GET REF SLOT ADDR
	INX	H
	INX	H		;BUMP TO NEXT SLOT
	SHLD	TEMP
	LDA	SYMCT		;GET COUNT
	DCR	A		;DECREMENT
	STA	SYMCT
	JNZ	PREF
	LHLD	REF		;GET REF BLOCK ADDRESS
	MOV	E,M
	INX	H
	MOV	D,M		;GET LINK TO NEXT BLOCK
	LXI	H,0000
	CALL	CPHL		;ANY MORE BLOCKS?
	JZ	CRLF		;NO, EXIT
	XCHG			;YES, SET NEXT BLOCK POINTER IN REF
	SHLD	REF
	LDA	NLINS
	DCR	A		;DECREMENT LINES COUNT
	STA	NLINS
	JNZ	PREF0		;AND PRINT MORE ON SAME LINE
	CALL	CRLF		;PRINT CR,LF
	MVI	B,SYMSIZ+2	;INDENT CONTINUATION LINE...
PREF3:	MVI	E,' '		;...WITH SPACES
	CALL	PBYT		;PRINT SPACES
	DCR	B
	JNZ	PREF3		;PRINT 6 SPACES
	JMP	PREFS

;********************************;
;
;   CHARACTER PARSING ROUTINES
;
;********************************;
LALPH:	LXI	H,SBUF		;POINT TO SYMBOL BUFFER
	MVI	C,SYMSIZ
	MVI	A,' '		;FILL SYMBOL...
LALX:	MOV	M,A		;...WITH...
	INX	H		;...BLANKS
	DCR	C
	JNZ	LALX		;CLEAR SYMBOL BUFFER
	LXI	H,SBUF
	SHLD	SYMPT
	MVI	A,00
	STA	SYMCT		;RESET SYMBOL POINTER+COUNT
	LDA	CHAR		;GET CHARACTER AGAIN
	CALL	GTSYM		;COLLECT IDENTIFIER
LALC:	CALL	GETBT		;GET A BYTE FROM SOURCE FILE
	CALL	SAVBT		;SAVE BYTE IN PRINT BUFFER
	CALL	CKNUM		;TEST FOR NUMBER
	JNC	LAL3		;YES, CONTINUE
	CALL	CKALP		;TEST FOR ALPHABETIC
	JNC	LAL3		;YES, CONTINUE
	CALL	CRES		;TEST FOR RESERVED WORD
	JC	LAL1		;NO, CONTINUE
LAL0:	LDA	CHAR		;GET CHARACTER THAT ENDED ID
	JMP	MAIN2		;CONTINUE SCAN
LAL1:	CALL	FIND		;SEE IF DEFINED
	JC	LAL2		;NO, CONTINUE
	CALL	ADDRF		;YES, ADD REFERENCE
	JMP	LAL0		;DONE
LAL2:	CALL	ENSYM		;ENTER SYMBOL DEFINITION
	CALL	ADDRF		;ADD REFERENCE
	JMP	LAL0		;CONTINUE
LAL3:	CALL	GTSYM		;COLLECT IDENTIFIER
	JMP	LALC		;CONTINUE

LNUM:	CALL	GETBT		;GET BYTE
	CALL	SAVBT		;SAVE BYTE IN PRINTER BUFFER
	CALL	CKNUM		;TEST FOR NUMERIC
	JNC	LNUM		;YES, CONTINUE
	CALL	CKALP		;TEST FOR ALPHABETIC
	JNC	LNUM		;YES, CONTINUE
	JMP	MAIN2		;CONTINUE WITH MAIN SCAN

LQUOT:	CALL	GETBT		;GET A BYTE
	CALL	SAVBT		;SAVE BYTE IN PRINTER BUFFER
	CPI	''''		;SEE IF STRING QUOTE
	JNZ	LQUOT		;NO, KEEP LOOPING
	CALL	GETBT		;GET NEXT BYTE
	CALL	SAVBT		;SAVE BYTE
	CPI	''''		;TEST FOR DOUBLES
	JZ	LQUOT		;YES, START SCAN AGAIN
	JMP	MAIN2		;NO, CONTINUE IN MAIN SCAN

LSEMI:	CALL	GETBT		;GET A BYTE
	CALL	SAVBT		;SAVE BYTE
	CPI	0DH		;WAIT FOR CR
	JNZ	LSEMI		;CONTINUE
	JMP	MAIN2		;ENTER MAIN LOOP

LCR:	CALL	PLINE		;PRINT LINE
	LHLD	LCNT		;GET LINE NUMBER
	INX	H		;BUMP LINE NUMBER
	SHLD	LCNT		;STORE
	JMP	MAIN

LLF:	PUSH	PSW		;save char
	LDA	FTPRN		;is it file...
	ORA	A		;...type PRN?
	JZ	LLF2		;yes, skip 1rst 16 char
	POP	PSW		;restore char
	JMP	MAIN

LLF2:	POP	PSW		;RESTORE CHARACTER
	MVI	B,16		;# OF CHAR TO SKIP OVER

LLF3:	CALL	GETBT		;get next byte
	CALL	SAVBT		;put in print buf
	CPI	0DH		;carriage return?
	JZ	LCR		;yes, get next line
	DCR	B		;skipped all char?
	JNZ	LLF3		;no, continue skipping
	JMP	MAIN		;CONTINUE

LIGN:	JMP	MAIN		;RE-ENTER MAIN LOOP

LSPC	EQU	LIGN
LTAB	EQU	LIGN
LDOL	EQU	LIGN
LDEL	EQU	LIGN

;********************************;
;
;          SUBROUTINES
;
;********************************;

;******************************************************************
;
;		SETUP
;
;         INITIALIZATION
;
; 1. displays program logo and version
; 2. checks for input file type PRN.
;	-if file type PRN, sets PRN switch.
; 3. if oper specified FN.FT CON, then sets console
;	output switch on.  Goes to step 5.
; 4. else check for disk output option.
;	-if not disk output, default to printer output.
;		-go to step 5.
;	-else check to see if cross reference file exists.
;		-if it exists, ask operater if it is to
;		 be erased.
;			-if no erase, terminate program
;			-else erase cross reference file.
;	-then create new cross reference disk file.
;	-initialize output character pointer to disk buffer.
; 5. open input disk file.
; 6. initialize printer buffer, address pointers for symbol table
;    and reference table, set line counter to 1.
; 7. return to caller
;
;******************************************************************

SETUP:	LXI	D,LOGO		;get program logo msg
	MVI	C,PSTRING	;BDOS PRINT STRING
	CALL	CPM		;PRINT LOGO
	LDA	TFCB+1		;get first char of file name
	CPI	'?'		;help function?
	JNZ	SETUP1		;no, go check input file type
	LXI	D,HELP		;display help message
	JMP	FERR1

SETUP1:	LDA	TFCB+9		;get first char of file type
	CPI	'P'		;is it type PRN?
	JNZ	SETUP2		;no, don't set switch
	LDA	TFCB+10		;get 2nd char of file type
	CPI	'R'
	JNZ	SETUP2
	LDA	TFCB+11		;get 3rd char of file type
	CPI	'N'
	JNZ	SETUP2
	XRA	A
	STA	FTPRN		;turn on prn file type

SETUP2:	LDA	TFCB+17		;get 1rst char of 2nd fcb
	CPI	'C'		;output to console?
	JNZ	SETUP3		;no, don't turn on switch
	LDA	TFCB+18		;get 2nd char of 2nd fcb
	CPI	'O'		;output to console?
	JNZ	SETUP3		;no, don't turn on switch
	MVI	A,0FFH		;turn on...
	STA	CONSOLE		;...console output
	JMP	SETUP6		;go open input file
SETUP3:	LDA	TFCB+17		;get 1rst char of 2nd fcb
	CPI	'D'		;output to be written to disk?
	JNZ	SETUP6		;no, go open input file.
	STA	DISKOUT		;turn on disk output switch
	LDA	TFCB+16		;get output file drive number
	STA	OFCB		;move to output fcb
	LXI	D,TFCB		;point to fcb
	CALL	FOPEN		;open disk input file
	LXI	D,TFCB+1	;point to input file name
	MVI	B,8		;length of file name
	LXI	H,OFCB+1	;point to output fcb file name
	CALL	MOVE		;move input file name to output fcb
	LXI	D,OFCB		;does output file...
	MVI	C,OPEN		;...already...
	CALL	CPM		;...exist?
	CPI	0FFH		;open succeed?
	JZ	SETUP4		;no, file does not exist
	LXI	D,OFCB		;close...
	MVI	C,CLOSE		;...the open...
	CALL	CPM		;...file.
	LXI	D,MSG1		;ask operator...
	MVI	C,PSTRING	;...if old file...
	CALL	CPM		;...is to be...
	MVI	C,CONIN		;...deleted
	CALL	CPM
	ANI	05FH		;make upper case
	CPI	'Y'		;operator say yes?
	JNZ	BOOT		;no, terminate
	LXI	D,CONCRLF	;write carriage return...
	MVI	C,PSTRING	;...line feed to console
	CALL	CPM
	LXI	D,OFCB		;point to fcb to delete file
	MVI	C,DELETE	;delete the...
	CALL	CPM		;...file
	CPI	0FFH		;delete succeed?
	LXI	D,EMSG2		;get err msg in case of failure
	JZ	FERR1		;failure, print msg & quit

SETUP4:	LXI	D,OFCB		;point to output fcb
	MVI	C,MAKE		;create new...
	CALL	CPM		;...cross reference file.
	CPI	0FFH		;create succeed?
	LXI	D,EMSG3		;get err msg in case of failure
	JZ	FERR1		;create failed, print msg & quit
	LXI	H,DISKBUF	;get addr of disk buffer
	SHLD	DCHAR		;init disk char pointer

SETUP6:	LXI	D,TFCB		;POINT TO FCB
	CALL	FOPEN		;OPEN FCB
	LXI	H,PBUF
	SHLD	LPNT		;SET PRINT POINTER
	LXI	H,00001		;SET LINE COUNTER...
	SHLD	LCNT		;...TO ONE.
	LXI	H,SYMT		;GET ADDRESS OF SYMBOL TABLE
	SHLD	SYM
	SHLD	SYMBT
	SHLD	SYMTP		;SET SYMBOL TABLE POINTERS
	LHLD	MEMSZ		;GET AVAILABLE MEMORY ADDRESS
	DCX	H
	SHLD	REF
	SHLD	REFBT
	SHLD	REFTP		;SET REFERENCE TABLE POINTERS
	RET

;********************************;
;
;    CHECK FOR RESERVED WORD
;
;********************************;
CRES:	LXI	H,RTAB		;POINT TO RESERVED WORD TABLE
	SHLD	TEMP		;SAVE IN TEMP WORD
CRES1:	LHLD	TEMP		;GET TABLE POINTER
	LXI	D,SBUF		;POINT TO SYMBOL
	MVI	B,5		;SYMBOL SIZE
CRES2:	LDAX	D		;GET SYMBOL BYTE
	CMP	M		;COMPARE AGAINST TABLE ENTRY
	RC			;LESS, NOT IN TABLE
	JNZ	CRES3		;GREATER, GET NEXT TABLE ENTRY
	INX	D		;BUMP POINTERS
	INX	H
	DCR	B		;DECREMENT BYTE COUNT
	JNZ	CRES2		;KEEP TESTING
	JMP	CRES4		;FOUND
CRES3:	LHLD	TEMP		;GET TABLE POINTER
	LXI	D,RSIZ		;SIZE OF ENTRY
	DAD	D		;BUMP POINTER
	SHLD	TEMP		;STORE NEW POINTER
	MOV	A,M		;GET TABLE BYTE
	CPI	0FFH		;END OF TABLE?
	JNZ	CRES1		;NO, LOOP
	STC			;SET CARRY (NOT IN TABLE)
	RET
CRES4:	ORA	A		;RESET CARRY
	RET

;********************************;
;
;     FIND SYMBOL IN TABLE
;
;********************************;
FIND:	LHLD	SYMBT		;GET BEGIN OF SYM TABLE
	SHLD	SYM		;SET TEMP POINTER
FIND1:	LHLD	SYM		;GET TEMP POINTER
	LXI	D,SBUF		;POINT TO CURRENT SYMBOL
	MVI	B,SYMSIZ	;SYMBOL SIZE
FIND2:	LDAX	D		;GET BYTE FROM SBUF
	CMP	M		;COMPARE TO SYM TABLE BYTE
	RC			;GREATER, NOT IN TABLE
	JNZ	FIND3		;LESS, GET NEXT TABLE ENTRY
	INX	D		;BUMP POINTER
	INX	H		;BUMP POINTER
	DCR	B		;DECREMENT BYTE COUNT
	JNZ	FIND2		;LOOP
	RET			;TRUE ZERO, FOUND
FIND3:	LHLD	SYM		;GET CURRENT POINTER
	LXI	D,SSIZ		;SYMBOL TABLE ENTRY SIZE
	DAD	D		;BUMP POINTER
	XCHG			;INTO DE
	LHLD	SYMTP		;GET TOP OF SYMBOL TABLE
	CALL	CPHL		;TEST FOR END OF TABLE
	JZ	FIND4		;YES, DONE
	JC	FERR		;TABLE OVERFLOW, ERROR
	XCHG			;CURRENT POINTER INTO HL
	SHLD	SYM		;SET CURRENT POINTER
	JMP	FIND1		;LOOP
FIND4:	STC			;SET CARRY FOR NOT FOUND
	LHLD	SYMTP		;GET CURRENT TOP
	SHLD	SYM		;SET CURRENT POINTER
	RET

FERR:	LXI	D,EMSG1		;SYMBOL TABLE ERR MSG

FERR1:	MVI	C,PSTRING	;WRITE CONSOLE
	CALL	CPM		;ISSUE ERROR MESSAGE
	JMP	BOOT		;EXIT

FERR2:	LXI	D,EMSG6		;no room for symbol table
	JMP	FERR1

;********************************;
;
;   ADD REFERENCE TO REF TABLE
;
;********************************;
ADDRF:	LHLD	SYM		;GET SYMBOL POINTER
	LXI	D,SYMSIZ+1	;OFFSET PAST SYMBOL&FLAGS
	DAD	D
	MOV	E,M
	INX	H
	MOV	D,M		;GET REFERENCE POINTER
	LXI	H,0000
	CALL	CPHL		;TEST FOR ZERO REF PTR
	JZ	BLDRF		;YES, BUILD REFERENCE ENTRY
LINK1:	XCHG			;REF PTR IN HL
	MOV	E,M		;GET REF LINK
	INX	H
	MOV	D,M		;INTO DE
	DCX	H		;REPOSITION HL
	PUSH	H		;SAVE REF PTR
	LXI	H,0000
	CALL	CPHL		;IF LINK IS ZERO
	POP	H
	JNZ	LINK1		;NON ZERO, GET NEXT LINK
	SHLD	REF		;SAVE REF POINTER
	INX	H
	INX	H		;SKIP TO FIRST REF NUMBER
	MVI	B,(REFSZ-2)/2	;NUMBER OF REF NUMBERS/ENTRY
LINK3:	MOV	E,M		;GET REF NUMBER
	INX	H
	MOV	D,M
	DCX	H		;REPOSITION
	PUSH	H		;SAVE REF NUM ADDR
	LXI	H,0000
	CALL	CPHL		;SEE IF REF NUM IS ZERO
	POP	H
	JZ	ENREF		;YES, ENTER REFERENCE
	INX	H
	INX	H		;SKIP TO NEXT REF NUM
	DCR	B		;DECREMENT COUNT
	JNZ	LINK3		;TRY AGAIN AT NEXT SLOT
	CALL	ADBLK		;ADD NEW REF BLOCK
	LHLD	REF		;GET REF POINTER
	INX	H
	INX	H		;SKIP TO FIRST REF SLOT
ENREF:	PUSH	H		;SAVE REF SLOT ADDR
	LHLD	LCNT		;GET LINE NUMBER
	XCHG			;INTO DE
	POP	H		;GET REF SLOT ADDR
	MOV	M,E
	INX	H
	MOV	M,D		;STORE LINE REF
	RET			;DONE
;********************************;
;
;     BUILD REF TABLE BLOCK
;
;********************************;
BLDRF:	LHLD	SYM		;GET SYMBOL POINTER
	LXI	D,SYMSIZ+1	;OFFSET TO REF POINTER
	DAD	D
	SHLD	REF		;SET TEMP REF POINTER TO HERE
	CALL	ADBLK		;ADD BLOCK
	LHLD	REF		;GET REAL REF POINTER
	INX	H
	INX	H		;POSITION TO FIRST REF SLOT
	JMP	ENREF		;ADD REFERENCE
ADBLK:	LHLD	REFBT		;GET REF BOTTOM
	LXI	D,REFSZ		;SUBTRACT REF SIZE
	MOV	A,L
	SUB	E
	MOV	L,A
	MOV	A,H
	SBB	D
	MOV	H,A
	SHLD	TEMP		;SAVE NEW REF BOTTOM
	XCHG			;INTO DE ALSO
	LHLD	SYMTP		;GET SYMBOL TOP
	CALL	CPHL		;CHECK FOR BUMP
	JZ	FERR2		;YES, NO ROOM
	JNC	FERR2		;NO ROOM
	LHLD	TEMP		;GET REF BOTTOM
	XCHG			;INTO DE
	LHLD	REF		;GET REF POINTER
	MOV	M,E		;SET LINK
	INX	H
	MOV	M,D		;TO NEW REF BLOCK
	LHLD	TEMP		;GET NEW REF BLOCK ADDR
	SHLD	REF		;STORE IN REF
	MVI	B,REFSZ		;SIZE OF REF BLOCK
	MVI	A,00
ADB2:	MOV	M,A		;ZERO THE REF BLOCK
	INX	H
	DCR	B
	JNZ	ADB2
	LHLD	TEMP		;GET NEW REF BOTTOM
	SHLD	REFBT		;SET REFBT
	RET

;********************************;
;
;     ENTER SYMBOL IN SYM TABLE
;
;********************************;
ENSYM:	LHLD	SYM		;GET SYMBOL POINTER
	XCHG			;INTO DE
	LHLD	SYMTP		;GET SYMBOL TABLE TOP
	CALL	CPHL		;CHECK FOR END OF TABLE
	JZ	NWSYM		;YES, ADD SYMBOL AT END
	LXI	D,SSIZ		;SYMBOL TABLE ENTRY SIZE
	DAD	D		;CALCULATE NEW END OF TABLE
	XCHG			;INTO DE
	LHLD	REFBT		;REFERENCE TABLE BOTTOM
	CALL	CPHL		;TEST FOR TABLE OVERFLOW
	LXI	D,EMSG7		;ADDR OF ERR MSG IN OF OVERFLOW
	JZ	FERR1		;FULL, ERROR
	JC	FERR1		;YES, ERROR
	LHLD	SYMTP		;GET TABLE TOP
	LXI	D,SSIZ-1	;BUMP TO END OF ENTRY
	DAD	D
	SHLD	TO		;STORE IN TO ADDRESS
	LXI	D,SSIZ
	MOV	A,L
	SUB	E
	MOV	L,A
	MOV	A,H
	SBB	D
	MOV	H,A		;SUBTRACT SIZE OF ONE ENTRY
	SHLD	FROM		;STORE AS FROM ADDRESS
	LHLD	SYM		;GET CURRENT POINTER
	SHLD	LIMIT		;STORE AS LIMIT ADDRESS
	CALL	MVUP		;MOVE TABLE UP IN MEMORY
NWSYM:	LHLD	SYM		;GET CURRENT POINTER
	LXI	D,SBUF		;POINT TO SYMBOL
	MVI	B,SYMSIZ	;SIZE OF SYMBOL
	CALL	MOVE		;COPY SYMBOL TO TABLE
	MVI	A,0
	MOV	M,A
	INX	H
	MOV	M,A
	INX	H
	MOV	M,A		;SET POINTERS TO 0000
	LHLD	SYMTP		;GET SYMBOL TABLE TOP
	LXI	D,SSIZ		;GET SYMBOL ENTRY SIZE
	DAD	D		;BUMP
	SHLD	SYMTP		;STORE EW TOP
	RET

;********************************;
;
;    MOVE SYMBOL TABLE UP
;
;********************************;
MVUP:	LHLD	TO		;GET TO POINTER
	MOV	B,H
	MOV	C,L		;INTO BC
	LHLD	FROM		;GET FROM POINTER
	XCHG			;INTO DE
	LHLD	LIMIT		;GET LIMIT ADDRESS
MVUP2:	LDAX	D		;GET FROM BYTE
	STAX	B		;STORE AT TO ADDRESS
	CALL	CPHL		;COMPARE FROM TO LIMIT
	RZ			;EXIT IF DONE
	DCX	B		;DECREMENT TO
	DCX	D		;DECRMENT FROM
	JMP	MVUP2		;LOOP

;********************************;
;
;  GENERAL PURPOSE MOVE ROUTINE
;
;********************************;
MOVE:	LDAX	D		;GET BYTE
	MOV	M,A		;STORE BYTE
	INX	D
	INX	H		;BUMP POINTERS
	DCR	B		;DECREMENT COUNT
	JNZ	MOVE		;LOOP
	RET

;********************************;
;
;    BINARY TO DECIMAL CONVERSION
;
;********************************;
DECOT:	LXI	D,DEC
	XCHG
	LXI	B,10000
	CALL	DIG
	LXI	B,1000
	CALL	DIG
	LXI	B,100
	CALL	DIG
	LXI	B,10
	CALL	DIG
	LXI	B,1
	CALL	DIG
	RET

DIG:	MVI	M,'0'
DI0:	MOV	A,E
	SUB	C
	MOV	E,A
	MOV	A,D
	SBB	B
	MOV	D,A
	JM	DI2
	INR	M
	JMP	DI0
DI2:	MOV	A,E
	ADD	C
	MOV	E,A
	MOV	A,D
	ADC	B
	MOV	D,A
	INX	H
	RET

;********************************;
;
;    TEST FOR ALPHABETIC CHAR.
;
;********************************;
CKALP:	cpi	'$'		;treat '$' like a char
	rz

	CPI	'A'		;ASCII 'A'
	RC			;NO, EXIT
	CPI	'Z'+1
	CMC
	RET

;********************************;
;
;       TEST FOR NUMERIC CHAR
;
;********************************;
CKNUM:	CPI	'0'
	RC
	CPI	'9'+1
	CMC
	RET

;********************************;
;
;  LOOK UP CHAR IN PARSE TABLE
;
;********************************;
LOOK:	LXI	D,0003		;TABLE ENTRY SIZE
	MOV	B,A		;ARGUMENT BYTE IN B
LOOK2:	MOV	A,M		;GET TABLE BYTE
	CPI	0FFH		;END OF TABLE?
	JZ	LOOKN		;YES, NOT FOUND
	CMP	B		;COMPARE
	JZ	LOOKY		;FOUND
	DAD	D		;BUMP POINTER
	JMP	LOOK2		;LOOP
LOOKN:	STC			;CARRY = NOT FOUND
	RET

LOOKY:	INX	H		;SKIP TO TABLE BYTE
	MOV	E,M
	INX	H
	MOV	D,M		;TABLE ENTRY IN DE
	XCHG			;INTO HL
	RET

;********************************;
;
;    SAVE BYTE IN LINE BUFFER
;
;********************************;
SAVBT:	LHLD	LPNT		;GET LINE POINTER
	MOV	M,A		;SAVE BYTE
	INX	H		;BUMP POINTER
	SHLD	LPNT		;SAVE POINTER
	call	lwrupr		;convert lower to upper case
	sta	char		;save char in char
	RET

;********************************;
;
;  PRINT SOURCE LINE WITH NUMBER
;
;********************************;
PLINE:	LHLD	LCNT		;GET LINE NUMBER
	CALL	DECOT		;CONVERT TO DECIMAL
	LXI	H,DEC		;POINT TO DEC STRING
PL2:	MOV	E,M		;GET STRING BYTE
	MOV	A,E
	CPI	0DH		;DONE?
	JZ	PL3		;YES
	CALL	PBYT		;PRINT BYTE
	INX	H		;BUMP POINTER
	JMP	PL2
PL3:	MVI	E,':'
	CALL	PBYT		;PRINT ':'
	MVI	E,' '
	CALL	PBYT		;PRINT ' '
	CALL	PBYT		;PRINT SPACE
	LXI	H,PBUF		;POINT TO PRINT BUFFER
	MVI	A,00
	STA	COL		;SET COLUMN COUNT
PL41:	MOV	E,M		;GET BYTE
	MOV	A,E
	CPI	0DH		;DONE?
	JZ	PL5
	CPI	0AH		;LF?
	JZ	PL4A		;YES, IGNORE
	CPI	09H		;TAB?
	JNZ	PL42		;NO, CONTINUE
	PUSH	H		;SAVE HL
PL43:	MVI	E,' '
	CALL	PBYT		;PRINT SPACE
	LXI	H,COL
	INR	M
	MOV	A,M
	ANI	07H		;MODULO 8
	JNZ	PL43
	POP	H
	JMP	PL4A
PL42:	LDA	COL
	INR	A
	STA	COL
	CALL	PBYT		;PRINT BYTE
PL4A:	INX	H
	JMP	PL41
PL5:	CALL	CRLF		;PRINT CR,LF
	LXI	H,PBUF
	SHLD	LPNT		;RESET LINE POINTER
	RET

;********************************;
;
;      COLLECT SYMBOL IN SYM BUF
;
;********************************;
GTSYM:	MOV	B,A		;SAVE CHAR
	LDA	SYMCT		;GET SYMBOL COUNT
	CPI	SYMSIZ		;MAX?
	RNC			;YES, DONE 
	INR	A
	STA	SYMCT
	LHLD	SYMPT
	MOV	M,B
	INX	H		;BUMP SYMBOL POINTER
	SHLD	SYMPT
	RET

;********************************;
;
;       PRINTER INTERFACES
;
;********************************;

;********************************;
;
;       PRINT A SINGLE BYTE
;
;********************************;
PBYT:	PUSH	B
	PUSH	D
	PUSH	H
	PUSH	PSW
	LDA	DISKOUT		;disk output...
	ORA	A		;...in effect?
	JNZ	DBYT		;yes, go write to disk
	MVI	C,05
	LDA	CONSOLE		;get console out switch
	ORA	A		;console output?
	JZ	PBYT2		;no, print output
	MVI	C,2		;console output

PBYT2:	CALL	CPM

PBYT3:	MVI	C,11		;CHECK CONSOLE STATUS
	CALL	CPM
	ORA	A		;IF ZERO, OK
	JZ	PBYTX		;EXIT
	MVI	C,1
	CALL	CPM		;READ CONSOLE CHAR
	CPI	3
	JZ	BOOT		;ABORT IF ^C, OTHERWISE IGNORE
PBYTX:	POP	PSW
	POP	H
	POP	D
	POP	B
	RET

;**************************************;
;
;		DBYT
;
;	PUT OUTPUT CHARACTER
;	INTO DISK BUFFER.
;		INPUT=E(OUTPUT CHAR)
;
; 1. move character to buffer.
; 2. if at end of buffer, go
;	write out disk buffer.
; 3. else add 1 to disk buffer
;	pointer.
; 4. subtract 1 from number of
;	positions left in buffer.
; 5. go check if operator pressed
;	control C.
;
;***************************************;

DBYT:	MOV	A,E		;save char to be put in buffer
	LHLD	DSKCNT		;get num of char left in buf
	XCHG			;put num in D,E
	LHLD	DCHAR		;point to next char in buf
	MOV	M,A		;put char in disk buf
	DCR	E		;at end of buf?
	JNZ	DBYT1		;no, don't write buffer
	DCR	D		;at end of buf?
	JZ	PUTREC		;yes, go write buffer

DBYT1:	INX	H		;point to next char in buf
	SHLD	DCHAR		;save next pos
	XCHG			;put num of char left in H,L
	SHLD	DSKCNT		;save num of remaining char
	JMP	PBYT3		;go check console status

;****************************************************************
;		PUTREC
;	WRITE OUT DISK BUFFER TO DISK
;
;	1. initialize start of disk buffer address and number
;		of sectors to write out.
;	2. get current disk sector buffer address
;	3. add 128 to it and save for DMA of sector after
;		current sector.
;	4. do DMA for current sector buffer address
;	5. write current disk sector
;	6. if not last sector repeat steps 2 thru 5.
;	7. set number of characters positions left in buffer
;		to size of buffer.
;	8. set disk output buffer pointer to front of buffer.
;	9. go check to see if operator pressed control C.
;
;***************************************************************
PUTREC:	LXI	H,DISKBUF	;address of disk buffer
	SHLD	BUFADR		;save it for later DMA's
	MVI	B,OUTSECT	;number of disk sectors to write

PUTRC2:	PUSH	B		;save sector count
	LHLD	BUFADR		;get disk buffer address
	XCHG			;put it in D,E for DMA
	LXI	H,80H		;put 128 H,L
	DAD	D		;add 128 to disk buf adr for next DMA
	SHLD	BUFADR		;save it for next DMA
	MVI	C,SETDMA	;point DMA to...
	CALL	CPM		;...correct buffer
	LXI	D,OFCB		;get output disk fcb
	MVI	C,WRITE		;write sector...
	CALL	CPM		;...to disk
	ORA	A		;was write successfull?
	POP	B		;restore num of sectors left
	LXI	D,EMSG4		;get err msg in case write failed
	JNZ	FERR1		;disk write failed, display err msg, quit
	DCR	B		;written 16 sectors yet?
	JNZ	PUTRC2		;no, continue writing disk sectors
	LXI	H,OUTSECT*128	;put disk buffer size into H,L
	SHLD	DSKCNT		;set num of char left in buf to buffer size
	LXI	H,DISKBUF	;point to front...
	SHLD	DCHAR		;...of disk buffer
	JMP	PBYT3		;go check console status for ^C

;*********************************;
;
;      ISSUE PAGE EJECT
;
;*********************************;
EJECT:	MVI	E,0CH		;printer eject command
	CALL	PBYT
	MVI	E,00H
	MVI	B,10
EJECT2:	CALL	PBYT		;PRINT 10 NULLS
	DCR	B
	JNZ	EJECT2
	MVI	A,00
	STA	LINES		;SET LINE COUNT
	RET

;********************************;
;
;      ISSUE CR, LF & TEST PAGE
;
;********************************;
CRLF:	MVI	E,0DH
	CALL	PBYT
	MVI	E,0AH
	CALL	PBYT
	LDA	LINES
	INR	A
	STA	LINES		;INCREMENT LINE COUNT
	MVI	A,LPAGE		;get number of lines
	ORA	A		;is it zero?
	LDA	LINES
	RZ			;YES, RETURN
	CPI	LPAGE		;TEST LINE COUNT
	CZ	EJECT		;IF EQ TO LPAGE THEN NEW PAGE
	RET


;********************************;
;
;       CHARACTER PARSING TABLE
;
;********************************;
CTAB1:	DB	0DH
	DW	LCR
	DB	0AH
	DW	LLF
	DB	''''
	DW	LQUOT
	DB	';'
	DW	LSEMI
	DB	' '
	DW	LSPC
	DB	09H
	DW	LTAB
	DB	'$'
	DW	LDOL
	DB	'('
	DW	LDEL
	DB	')'
	DW	LDEL
	DB	'+'
	DW	LDEL
	DB	'-'
	DW	LDEL
	DB	'*'
	DW	LSEMI
	DB	'/'
	DW	LDEL
	DB	','
	DW	LDEL
	DB	':'
	DW	LDEL
	DB	EOF
	DW	DONE
	DB	0FFH
	DW	0000H
EOF	EQU	1AH		;EOF CODE

;********************************;
;
;     RESERVED WORD TABLE
;
;********************************;
RTAB:	DB	'A    '
	DB	'ACI  '
	DB	'ADC  '
	DB	'ADD  '
	DB	'ADI  '
	DB	'ANA  '
	DB	'AND  '
	DB	'ANI  '
	DB	'B    '
	DB	'C    '
	DB	'CALL '
	DB	'CC   '
	DB	'CM   '
	DB	'CMA  '
	DB	'CMC  '
	DB	'CMP  '
	DB	'CNC  '
	DB	'CNZ  '
	DB	'CP   '
	DB	'CPE  '
	DB	'CPI  '
	DB	'CPO  '
	DB	'CZ   '
	DB	'D    '
	DB	'DAA  '
	DB	'DAD  '
	DB	'DB   '
	DB	'DCR  '
	DB	'DCX  '
	DB	'DI   '
	DB	'DS   '
	DB	'DW   '
	DB	'E    '
	DB	'EI   '
	DB	'ELSE '
	DB	'END  '
	DB	'ENDIF'
	DB	'ENDM '
	DB	'EQU  '
	DB	'H    '
	DB	'HLT  '
	DB	'IF   '
	DB	'IN   '
	DB	'INR  '
	DB	'INX  '
	DB	'JC   '
	DB	'JM   '
	DB	'JMP  '
	DB	'JNC  '
	DB	'JNZ  '
	DB	'JP   '
	DB	'JPE  '
	DB	'JPO  '
	DB	'JZ   '
	DB	'L    '
	DB	'LDA  '
	DB	'LDAX '
	DB	'LHLD '
	DB	'LXI  '
	DB	'M    '
	DB	'MACRO'
	DB	'MOD  '
	DB	'MOV  '
	DB	'MVI  '
	DB	'NOP  '
	DB	'NOT  '
	DB	'OR   '
	DB	'ORA  '
	DB	'ORG  '
	DB	'ORI  '
	DB	'OUT  '
	DB	'PAGE '
	DB	'PCHL '
	DB	'POP  '
	DB	'PSW  '
	DB	'PUSH '
	DB	'RAL  '
	DB	'RAR  '
	DB	'RC   '
	DB	'RET  '
	DB	'RLC  '
	DB	'RM   '
	DB	'RNC  '
	DB	'RNZ  '
	DB	'RP   '
	DB	'RPE  '
	DB	'RPO  '
	DB	'RRC  '
	DB	'RST  '
	DB	'RZ   '
	DB	'SBB  '
	DB	'SBI  '
	DB	'SET  '
	DB	'SHL  '
	DB	'SHLD '
	DB	'SHR  '
	DB	'SP   '
	DB	'SPHL '
	DB	'STA  '
	DB	'STAX '
	DB	'STC  '
	DB	'SUB  '
	DB	'SUI  '
	DB	'TITLE'
	DB	'XCHG '
	DB	'XOR  '
	DB	'XRA  '
	DB	'XRI  '
	DB	'XTHL '
	DB	0FFH		;END OF RESERVED WORD TABLE
RSIZ	EQU	05		;SIZE OF TABLE ENTRY

;********************************;
;
;       MISCELLANEOUS DATA
;
;********************************;

MSG1:	DB	'DISK CROSS REFERENCE FILE EXISTS - ERASE IT(Y/N)?','$'
EMSG0:	DB	'++ERROR++ INPUT FILE DOES NOT EXIST', 0DH, 0AH, '$'
EMSG1:	DB	'SYMBOL TABLE ERROR',0DH,0AH,'$'
EMSG2:	DB	'FAILED TO DELETE EXISTING CROSS REFERENCE FILE',0DH,0AH,'$'
EMSG3:	DB	'FAILED TO CREATE CROSS REFERENCE FILE',0DH,0AH,'$'
EMSG4:	DB	'DISK WRITE FAILED - OUT OF SPACE?',0DH,0AH,'$'
EMSG5:	DB	'CLOSE FAILED ON DISK CROSS REFERENCE FILE',0DH,0AH,'$'
EMSG6:	DB	'++ERROR++ NOT ENOUGH MEMORY FOR SYMBOL TABLE',0DH,0AH,'$'
EMSG7:	DB	'++ERROR++ SYMBOL TABLE OVERFLOW',0DH,0AH,'$'
LOGO:	DB	0Dh,0Ah,'CPM ASSEMBLER CROSS REFERENCE LIST Ver 1.41',0Dh,0Ah,'$'
DONEMSG: DB	'CROSS REFERENCE LIST IS DONE',0DH,0AH,'$'
NOSYMS:	DB	'NO SYMBOLS IN THIS PROGRAM'
CONCRLF	DB	0DH,0AH,'$'
SSIZ	EQU	SYMSIZ+3	;SYMBOL TABLE ENTRY SIZE
SYMBT:	DS	2		;SYMBOL TABLE BOTTOM ADDRESS
SYMTP:	DS	2		;SYMBOL TABLE TOP ADDRESS
REFBT:	DS	2		;REFERENCE TABLE BOTTOM ADDRESS
REFTP:	DS	2		;REFERENCE TABLE TOP ADDRESS
SYM:	DS	2		;CURRENT SYMBOL TABLE ADDRESS
REFSZ	EQU	2+(NREFS*2)	;NUMBER OF BYTES IN REF BLOCK
REF:	DS	2		;CURRENT REFERENCE TABLE ADDRESS
FROM:	DS	2		;MOVE POINTER
TO:	DS	2		;TO POINTER
LIMIT:	DS	2		;LIMIT POINTER
COL:	DS	1
CHAR:	DS	1
LCNT:	DS	2		;LINE COUNTER
LPNT:	DS	2
DEC:	DS	5
	DB	0DH
PBUF:	DS	132
SYMCT:	DS	1
SYMPT:	DS	2
SBUF:	DS	SYMSIZ		;SYMBOL BUFFER
NLINS:	DB	0		;BUFFERS TO PRINT ON LINE
LINES:	DB	0		;PRINT LINE COUNT
 
;********************************;
;
;       OPERATING SYSTEM EQUATES
;
;********************************;
 
BOOT	EQU	0000H	;REBOOT ENTRY POINT
CPM	EQU	0005H	;CPM ENTRY POINT

MEMSZ	EQU	0006H	;END OF MEMORY POINTER
TFCB	EQU	005CH	;TRANS. FCB

CONIN	EQU	01	;CONSOLE INPUT FUNCTION CODE
PSTRING	EQU	09	;PRINT STRING FUNCTION CODE
OPEN	EQU	15	;OPEN FUNCTION CODE
CLOSE	EQU	16	;CLOSE FILE FUNCTION CODE
DELETE	EQU	19	;DELETE FILE FUNCTION CODE
READ	EQU	20	;READ DISK FUNCTION CODE
WRITE	EQU	21	;WRITE DISK FUNCTION CODE
MAKE	EQU	22	;MAKE FILE FUNCTION CODE
SETDMA	EQU	26	;SET DMA FUNCTION CODE



;********************************;
;           F O P E N            ;
;  ROUTINE TO OPEN A DISK FILE   ;
;				 ;
;  INPUT:     DE=A(FCB)		 ;
;********************************;

FOPEN:	MVI	C,OPEN	;OPEN CODE
	CALL	CPM	;ISSUE OPEN
	CPI	0FFH	;ERROR?
	RNZ		;NO ERROR
	LXI	D,EMSG0
	JMP	FERR1

;********************************;
;				 ;
; ROUTINE TO CLOSE OUTPUT DISK	 ;
;	CROSS REFERENCE FILE	 ;
;  INPUT:	DE=ADDR(FCB)	 ;
;		HL=ADDR(BUFFER)	 ;
;********************************;

DCLOSE:	PUSH	D		;save fcb address
	PUSH	H		;save buffer address
	LHLD	DSKCNT		;put number of char left...
	XCHG			;...in buffer into D,E
	LHLD	DCHAR		;point to next char in output buf
	MVI	A,1AH		;put a control Z in accum

DCLSE2:	MOV	M,A		;fill...
	INX	H		;...rest of...
	DCR	E		;...buffer...
	JNZ	DCLSE2		;...with...
	DCR	D		;...control Z's.
	JNZ	DCLSE2
	POP	H		;point to...

DCLSE4:	XCHG			;...output buffer
	LXI	H,128		;put 128 into H,L
	DAD	D		;add 128 to disk buf addr for next DMA
	SHLD	BUFADR		;save it for next DMA after this one
	MVI	C,SETDMA	;...disk buffer for...
	CALL	CPM		;...last disk write
	POP	D		;do last...
	PUSH	D		;....(D,E contains OFCB)...
	MVI	C,WRITE		;...disk...
	CALL	CPM		;...write
;Check to see if last record has been written.  Do this by comparing
;address of next available char to address of buffer for next disk write.
;If address of next available char is equal or greater, close file.
	LHLD	DCHAR		;put high order address
	XCHG			;...of last char+1...
	MOV	A,D		;...into ACCUM
	LHLD	BUFADR		;get addr of next disk buffer
	CMP	H		;compare to last char+1 in buffer
	JC	DCLSE5		;if next addr is greater, close file
	JNZ	DCLSE4		;if not equal, continue
	MOV	A,E		;put low order address of last char+1
	CMP	L		;into accum and test
	JNC	DCLSE4		;if not less, do another write

DCLSE5:	POP	D		;close the...(D,E contains OFCB)
	MVI	C,CLOSE		;...output disk
	CALL	CPM		;...cross ref file
	CPI	0FFH		;close successfull?
	RNZ			;yes, return
	LXI	D,EMSG5		;no, display error msg...
	JMP	FERR1		;... and quit

;********************************;
;            G E T B T           ;
;   ROUTINE TO READ A BYTE       ;
;                                ;
;  OUTPUTS:     A=BYTE           ;
;				 ;
; 1. is current byte pointer at  ;
;	end of buffer?		 ;
;	-yes, Read disk records	 ;
;	 into buffer.		 ;
; 2. move character from buffer	 ;
;	to ACCUM.		 ;
; 3. add 1 to character pointer	 ;
;	and save pointer.	 ;
; 4. return to caller		 ;
;				 ;
;********************************;

GETBT:	LXI	H,TBUF+(INSECT*128);end of input buf addr
	XCHG		;BUFFER END ADDR. IN DE
	LHLD	INPTR	;CURRENT POINTER IN HL
	CALL	CPHL	;TEST FOR END OF BUFFER
	CZ	GETREC	;YES, READ DISK RECORDS
	MOV	A,M	;GET BYTE
	INX	H	;BUMP POINTER
	SHLD	INPTR	;SAVE POINTER
	ORA	A	;CLEAR CARRY
	RET		;RETURN CARRY RESET

;***********************************************************
;			GETREC
;Read the number of input records specified by INSECT.  The
;buffer starts at TBUF.  Before each DMA, the buffer address
;is incremented by 128 and saved in IDADDR.  This address
;will be used for the DMA after the current sector is read.
;The logic is:
;		1. get current buffer address
;		2. add 128 to it and save it
;		3. issue DMA for current buffer address
;		4. read the disk record,
;			-if end of file goto step 7.
;		5. decrement number of sectors to read
;		6. if not number of sectors not zero
;			-repeat above
;		7. reset character pointer to first buffer
;		8. return to caller.
;***********************************************************

GETREC:	MVI	B,INSECT;number of sectors to read
	LXI	H,TBUF	;get address of disk input buffer
	SHLD	IDADDR	;initialize current buffer address

GETRC2:	LHLD	IDADDR	;get addr of input buffer
	XCHG		;put it in D,E
	LXI	H,128	;add 128 to input buf
	DAD	D	;...addr for read after next
	SHLD	IDADDR	;save it read after next
	PUSH	B	;save sector count
	PUSH	D	;save current buf ptr in case EOF occurrs
	MVI	C,SETDMA;POINT TO INPUT BUFFER...
	CALL	CPM
	MVI	C,READ	;READ CODE
	LXI	D,TFCB	;FCB ADDRESS
	CALL	CPM	;ISSUE READ
	POP	D	;restore current buf pointer
	POP	B	;restore sector count
	CPI	00	;ERROR?
	JNZ	GETRC4	;YES
	DCR	B	;read all of the sectors?
	JNZ	GETRC2	;no, continue reading

GETRC3:	LXI	H,TBUF	;RESET BUFFER POINTER
	SHLD	INPTR
	RET		;RETURN TO CALLER

GETRC4:	XCHG		;POINT TO FIRST CHAR PAST BUF
	MVI	M,26H	;PUT TWO...
	INX	H	;...CONTROL Z's
	MVI	M,26H	;...AFTER LAST REC IN FILE
	JMP	GETRC3	;GO RESET BUFFER POINTER

;********************************;
;   CONVERTS LOWER TO UPPER CASE ;
;	INPUT: A=BYTE(upper/lower;
;	OUTPUTS: A=BYTE UPPER	 ;
;********************************;

lwrupr:	cpi	'A'+20h	;is it upper case?
	cmc		;complement carry
	rnc		;yes, return with carry reset
	Cpi	'Z'+21h	;over lower case 'Z'?
	rnc		;no, return no carry
	sui	20h	;convert to upper case
	ret

;********************************;
;   MISCELLANEOUS SUBROUTINES    ;
;********************************;


;********************************;
;             C P H L            ;
;  ROUTINE TO COMPARE HL VS DE   ;
;	OUTPUT: HL=DE, ZERO=ON	 ;
;		HL<>DE, ZERO=OFF ;
;********************************;

CPHL:	MOV	A,H
	CMP	D
	RNZ
	MOV	A,L
	CMP	E
	RET

;********************************;
;             D A T A            ;
;********************************;

OFCB:	DB	0	;disk output fcb, default drive
	DS	8	;file name
	DB	'XRF'	;file type
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

DCHAR:	DW	DISKBUF	;pointer for disk buffer

BUFADR:	DS	2	;pointer to disk output buffer for DMA

IDADDR:	DS	2	;POINTER TO DISK INPUT BUFFER FOR DMA

DSKCNT:	DW	OUTSECT*128	;number of char left in disk buffer

INPTR:	DW	TBUF+(INSECT*128);POINTER TO CHAR IN INPUT BUFFER

DISKBUF	DS	OUTSECT*128	;output disk buffer

TBUF:	DS	INSECT*128	;DISK INPUT BUFFER

EOFCHR:	DB	26H,26H	;control Z's in case last block is
			;filled with data

CONSOLE	DB	0	;console output switch

DISKOUT	DB	0	;disk output switch

FTPRN:	DB	1	;prn file type

TEMP:	DS	2	;TEMP SAVE WORD

	DS	64	;stack contains 32 words
STACK	EQU	$

;***********************************************
;
;		SYMBOL TABLE AREA
;
;	THE SYMBOL TABLE MUST BE THE
;	LAST BYTE BEFORE THE HELP MESSAGE
;
;***********************************************

	ORG	($ AND 0FFF0H) + 10H	;FORCE 16 BYTE BOUNDARY
SYMT:	DB	0FFH

HELP:	DB	0DH,0AH,'This program can be used to' 
	DB	' create a cross reference listing',0DH,0AH
	DB	'of CPM assembler programs.'
	DB	'  The input can be either the assembler',0DH,0AH
	DB	'language source code(FN.ASM) or the assembly listing(FN.PRN).',0DH,0AH
	DB	'The output can be printed on the CPM list device, displayed on',0DH,0AH
	DB	'the console, or written to disk(file type will be XRF).  It is',0DH,0AH
	DB	'invoked by keying:',0DH,0AH,0AH
	DB	09H,'XREF FN.ASM      (OUTPUT ON LIST DEVICE)',0DH,0AH
	DB	09H,'XREF FN.PRN CON  (OUTPUT ON THE CONSOLE)',0DH,0AH
	DB	09H,'XREF FN.ASM D    (OUTPUT ON DISK-DEFAULT DRIVE)',0DH,0AH
	DB	09H,'XREF FN.PRN B:D  (OUTPUT ON DISK-DRIVE SPECIFIED)',0DH,0AH,'$'

	END
