;15/2/80: 1345

;************************************************;
;	          INTEL ASSEMBLER
;	       CROSS REFERENCE PROGRAM  
;	           VERSION 2.1
; ORIGINAL BY: JEFF KRAVITZ (REF. 8.27)
; MODIFIED BY: P.P.H. LEE, TO WORK WITH *.PRN FILES
; GENERATED BY THE CP/M ASM ASSEMBLER, ALSO
; HANDLES NON-EXISTING FILES CORRECTLY, AND
; HAVE BIGGER DISK BUFFER.
; PAGE LENGTH CHANGED TO 60 LINES PER PAGE, AND
; NUMBER OF REFERENCES PER LINE IN THE CROSS
; REFERENCE LIST INCREASED FROM 8 TO 12.
; DATA AREAS REARRANGED AND SYMT INITIALIZED
; DURING EXECUTION, GIVING SHORTER COM FILE.
; PRINTS DIRECTLY TO THE LOGICAL LIST DEVICE
; VIA THE BIOS LIST VECTOR.
; LIMIT OF SYMBOL LENGTH INCREASED FROM 5 TO 7
; CHARACTERS.
;
;MODIFIED 1/3/81 BY WARD CHRISTENSEN TO CHANGE
;LABEL "LINK" TO ALLOW ASSEMBLY UNDER "LINKASM"
;WHICH USES "LINK" AS A PSEUDO-OP.
;
;************************************************;

;********************************;
;          MAIN LOOP
;********************************;

	ORG	100H	;ORIGIN ADDRESS
XREF:	LXI	SP,STACK ;SET STACK POINTER
	LHLD	1	;GET WARM BOOT ADDRESS
	LXI	D,3
	DAD	D	;GET CONSOLE STATUS VECTOR
	SHLD	CONST+1	;SAVE IT
	DAD	D	;GET CONSOLE IN VECTOR
	SHLD	CONIN+1	;SAVE IT
	DAD	D
	DAD	D	;GET LIST VECTOR
	SHLD	LIST+1	;SAVE IT
	MVI	A,0FFH	;INITIALIZE SYMT
	STA	SYMT
	CALL	SETUP	;INITIALIZE
XREF1:	CALL	GETBYT	;GET RID OF LEADING
	CPI	0DH	;CR
	JZ	XREF1	;AND
	CPI	0AH	;LF
	JZ	XREF1
	JMP	MAIN1	;SKIP AROUND
MAIN:	CALL	GETBYT	;GET A BYTE FROM SOURCE FILE
MAIN1:	CALL	SAVBYT	;SAVE BYTE IN PRINT BUFFER
MAIN2:	CALL	CHKNUM	;TEST FOR NUMERIC
	JNC	LNUM	;YES, FOUND A NUMBER, PROCESS
	CALL	CHKALP	;TEST FOR ALPHABETIC
	JNC	LALPH	;YES, PROCESS
	LXI	H,CPTBL	;POINT TO CHARACTER TABLE
	CALL	LOOK	;LOOK UP CHAR IN CHAR TABLE
	JC	MAIN	;NOT FOUND, IGNORE
	PCHL		;EXECUTE ROUTINE

;********************************;
;      FINAL SYMBOL TABLE PRINT
;********************************;

DONE:	LDA	LINES	;CHECK IF ALREADY ON
	ORA	A	;NEW PAGE
	JZ	DONE2	;BRIF IS
	MOV	B,A	;SAVE LINE NUMBER
	MVI	E,0AH	;FINISH DOING CURRENT PAGE
DONE1:	CALL	PRBYT	;BY PRINTING
	MOV	A,B	;THE REQUIRED
	INR	A	;NUMBER OF
	MOV	B,A	;LINE FEEDS
	CPI	60	;FINISHED?
	JC	DONE1	;BRIF NOT
	CALL	PAGE	;ELSE ISSUE PAGE EJECT
DONE2:	LXI	H,XRFHD	;AND PRINT OUT
	MVI	B,16	;THE CROSS REFERENCE
DONE3:	MOV	E,M	;HEADING
	CALL	PRBYT	;BEFORE PRINTING
	INX	H	;THEM
	DCR	B
	JNZ	DONE3
	CALL	CRLF
	CALL	CRLF
	LHLD	SYMBT	;GET SYMBOL TABLE BOTTOM
	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	PRSYM	;PRINT SYMBOL
	LHLD	SYM
	LXI	D,SYMSZ	;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,STESZ	;SIZE OF SYM TABLE ENTRY
	DAD	D
	SHLD	SYM
	MOV	A,M	;GET BYTE
	CPI	0FFH	;END OF TABLE?
	JNZ	DLP1	;BRIF NOT
	JMP	BOOT	;ELSE REBOOT CP/M

;********************************;
;     SYMBOL PRINT ROUTINE
;********************************;

PRSYM:	MVI	B,SYMSZ	;SYMBOL SIZE
PRSYM2:	MOV	E,M	;GET BYTE
	CALL	PRBYT	;PRINT BYTE
	INX	H
	DCR	B
	JNZ	PRSYM2
	MVI	E,' '
	CALL	PRBYT	;PRINT 2 SPACES
	CALL	PRBYT
	RET

;********************************;
;     REFERENCE PRINT ROUTINE
;********************************;

PREFS:	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	PREFX	;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
PREF2:	MOV	E,M
	CALL	PRBYT	;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	PREFX	;NO, EXIT
	XCHG		;YES, SET NEXT BLOCK POINTER
	SHLD	REF	;IN REF
	CALL	CRLF	;PRINT CR,LF
	MVI	B,SYMSZ+2
PREF3:	MVI	E,' '
	CALL	PRBYT	;PRINT SPACES
	DCR	B
	JNZ	PREF3	;PRINT 6 SPACES
	JMP	PREFS
PREFX:	CALL	CRLF	;PRINT CR,LF
	RET

;********************************;
;   CHARACTER PARSING ROUTINES
;********************************;

LALPH:	LXI	H,SBUF	;POINT TO SYMBOL BUFFER
	MVI	C,SYMSZ
	MVI	A,' '
LALX:	MOV	M,A
	INX	H
	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	GETSYM	;COLLECT IDENTIFIER
LALC:	CALL	GETBYT	;GET A BYTE FROM SOURCE FILE
	CALL	SAVBYT	;SAVE BYTE IN PRINT BUFFER
	CALL	CHKNUM	;TEST FOR NUMBER
	JNC	LAL3	;YES, CONTINUE
	CALL	CHKALP	;TEST FOR ALPHABETIC
	JNC	LAL3	;YES, CONTINUE
	CALL	CKRES	;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	ADDREF	;YES, ADD REFERENCE
	JMP	LAL0	;DONE
LAL2:	CALL	ENTSYM	;ENTER SYMBOL DEFINITION
	CALL	ADDREF	;ADD REFERENCE
	JMP	LAL0	;CONTINUE
LAL3:	CALL	GETSYM	;COLLECT IDENTIFIER
	JMP	LALC	;CONTINUE

LNUM:	CALL	GETBYT	;GET BYTE
	CALL	SAVBYT	;SAVE BYTE IN PRINTER BUFFER
	CALL	CHKNUM	;TEST FOR NUMERIC
	JNC	LNUM	;YES, CONTINUE
	CALL	CHKALP	;TEST FOR ALPHABETIC
	JNC	LNUM	;YES, CONTINUE
	JMP	MAIN2	;CONTINUE WITH MAIN SCAN

LQUOT:	CALL	GETBYT	;GET A BYTE
	CALL	SAVBYT	;SAVE BYTE IN PRINTER BUFFER
	CPI	''''	;SEE IF STRING QUOTE
	JNZ	LQUOT	;NO, KEEP LOOPING
	CALL	GETBYT	;GET NEXT BYTE
	CALL	SAVBYT	;SAVE BYTE
	CPI	''''	;TEST FOR DOUBLES
	JZ	LQUOT	;YES, START SCAN AGAIN
	JMP	MAIN2	;NO, CONTINUE IN MAIN SCAN

LSEMI:	CALL	GETBYT	;GET A BYTE
	CALL	SAVBYT	;SAVE BYTE
	CPI	0DH	;WAIT FOR CR
	JNZ	LSEMI	;CONTINUE
	JMP	MAIN2	;ENTER MAIN LOOP

LCR:	CALL	PRLINE	;PRINT LINE
	LHLD	LCNT	;GET LINE NUMBER
	INX	H	;BUMP LINE NUMBER
	SHLD	LCNT	;STORE
	CALL	SKIP	;SKIP 1ST 16 CHAR. OF NEXT LINE
	JMP	MAIN	;CONTINUE

LIGN	EQU	MAIN	;RE-ENTER MAIN LOOP
LLF	EQU	MAIN
LSPC	EQU	MAIN
LTAB	EQU	MAIN
LDOL	EQU	MAIN
LDEL	EQU	MAIN

;********************************;
;          SUBROUTINES
;********************************;

;********************************;
;	SKIP FIRST SIXTEEN
;	CHARACTERS IN A LINE
;********************************;

SKIP:	MVI	B,16	;SET COUNTER
SKIP1:	PUSH	B	;SAVE IT
SKIP2:	CALL	GETBYT	;GET BYTE
	CALL	SAVBYT	;SAVE IT IN PRINT BUFFER
	CPI	EOF	;EOF?
	JZ	DONE	;BRIF IS
	CPI	0AH	;LF?
	JZ	SKIP2	;BRIF IS, IE. DON'T COUNT
	CPI	0DH	;CR?
	JZ	SKIP2	;BRIF IS, IE. DON'T COUNT
	POP	B	;RETRIEVE COUNTER
	DCR	B	;DECREMENT
	JNZ	SKIP1
	RET

;********************************;
;         INITIALIZATION
;********************************;

SETUP:	LXI	D,TFCB	;POINT TO FCB
	CALL	FOPEN	;OPEN FCB
	LXI	D,EMSG2	;POINT TO ERROR MESSAGE
	JC	FERR1	;BRIF FILE NOT FOUND
	LXI	H,PBUF
	SHLD	LPNT	;SET PRINT POINTER
	LXI	H,00001
	SHLD	LCNT
	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
	CALL	PAGE	;ISSUE PAGE EJECT
	RET

;********************************;
;    CHECK FOR RESERVED WORD
;********************************;

CKRES:	LXI	H,RWTBL	;POINT TO RESERVED WORD TABLE
	SHLD	TEMP	;SAVE IN TEMP WORD
CKRES1:	LHLD	TEMP	;GET TABLE POINTER
	LXI	D,SBUF	;POINT TO SYMBOL
	MVI	B,RWSIZ	;RESERVED WORD SIZE
CKRES2:	LDAX	D	;GET SYMBOL BYTE
	CMP	M	;COMPARE AGAINST TABLE ENTRY
	RC		;LESS, NOT IN TABLE
	JNZ	CKRES3	;GREATER, GET NEXT TABLE ENTRY
	INX	D	;BUMP POINTERS
	INX	H
	DCR	B	;DECREMENT BYTE COUNT
	JNZ	CKRES2	;KEEP TESTING
	JMP	CKRES4	;FOUND
CKRES3:	LHLD	TEMP	;GET TABLE POINTER
	LXI	D,RWSIZ	;SIZE OF ENTRY
	DAD	D	;BUMP POINTER
	SHLD	TEMP	;STORE NEW POINTER
	MOV	A,M	;GET TABLE BYTE
	CPI	0FFH	;END OF TABLE?
	JNZ	CKRES1	;NO, LOOP
	STC		;SET CARRY (NOT IN TABLE)
	RET
CKRES4:	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,SYMSZ	;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,STESZ	;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	;POINTER TO ERROR MESSAGE
FERR1:	MVI	C,PRBUF
	CALL	BDOS	;PRINT ERROR MESSAGE
	JMP	BOOT	;EXIT

;********************************;
;   ADD REFERENCE TO REF TABLE
;********************************;

ADDREF:	LHLD	SYM	;GET SYMBOL POINTER
	LXI	D,SYMSZ	;OFFSET PAST SYMBOL
	DAD	D
	MOV	E,M
	INX	H
	MOV	D,M	;GET REFERENCE POINTER
	LXI	H,0000
	CALL	CPHL	;TEST FOR ZERO REF PTR
	JZ	BLDREF	;YES, BUILD REFERENCE ENTRY
LNK:	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	LNK	;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 NOS./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	ENTREF	;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
ENTREF:	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
;********************************;

BLDREF:	LHLD	SYM	;GET SYMBOL POINTER
	LXI	D,SYMSZ	;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	ENTREF	;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	FERR	;YES, NO ROOM
	JNC	FERR	;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
;********************************;

ENTSYM:	LHLD	SYM	;GET SYMBOL POINTER
	XCHG		;INTO DE
	LHLD	SYMTP	;GET SYMBOL TABLE TOP
	CALL	CPHL	;CHECK FOR END OF TABLE
	JZ	NEWSYM	;YES, ADD SYMBOL AT END
	LXI	D,STESZ	;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
	JZ	FERR	;FULL, ERROR
	JC	FERR	;YES, ERROR
	LHLD	SYMTP	;GET TABLE TOP
	LXI	D,STESZ-1 ;BUMP TO END OF ENTRY
	DAD	D
	SHLD	TO	;STORE IN TO ADDRESS
	LXI	D,STESZ
	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	MOVUP	;MOVE TABLE UP IN MEMORY
NEWSYM:	LHLD	SYM	;GET CURRENT POINTER
	LXI	D,SBUF	;POINT TO SYMBOL
	MVI	B,SYMSZ	;SIZE OF SYMBOL
	CALL	MOVE	;COPY SYMBOL TO TABLE
	MVI	A,0
	MOV	M,A
	INX	H
	MOV	M,A	;SET POINTERS TO 0000
	LHLD	SYMTP	;GET SYMBOL TABLE TOP
	LXI	D,STESZ	;GET SYMBOL ENTRY SIZE
	DAD	D	;BUMP
	SHLD	SYMTP	;STORE NEW TOP
	RET

;********************************;
;    MOVE SYMBOL TABLE UP
;********************************;

MOVUP:	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
MOVUP2:	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	MOVUP2	;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.
;********************************;

CHKALP:	CPI	'A'	;ASCII 'A'
	RC		;NO, EXIT
	CPI	'Z'+1
	CMC
	RET

;********************************;
;       TEST FOR NUMERIC CHAR
;********************************;

CHKNUM:	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
;********************************;

SAVBYT:	STA	CHAR	;SAVE CHAR IN CHAR
	LHLD	LPNT	;GET LINE POINTER
	MOV	M,A	;SAVE BYTE
	INX	H	;BUMP POINTER
	SHLD	LPNT	;SAVE POINTER
	RET

;********************************;
;  PRINT SOURCE LINE WITH NUMBER
;********************************;

PRLINE:	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	PRBYT	;PRINT BYTE
	INX	H	;BUMP POINTER
	JMP	PL2
PL3:	MVI	E,':'
	CALL	PRBYT	;PRINT ':'
	MVI	E,' '
	CALL	PRBYT	;PRINT ' '
	CALL	PRBYT	;PRINT SPACE
	LXI	H,PBUF	;POINT TO PRINT BUFFER
PL4:	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	PRBYT	;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	PRBYT	;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
;********************************;

GETSYM:	MOV	B,A	;SAVE CHAR
	LDA	SYMCT	;GET SYMBOL COUNT
	CPI	SYMSZ	;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
;********************************;

PRBYT:	PUSH	B
	MOV	C,E	;SHIFT CHARACTER
	CALL	LIST	;PRINT IT
	POP	B
	RET

;*********************************;
;      ISSUE PAGE EJECT
;*********************************;

PAGE:	MVI	C,0CH
	CALL	LIST
	XRA	A
	STA	LINES	;SET LINE COUNT
	RET

;********************************;
;      ISSUE CR, LF & TEST PAGE
;      ALSO TEST CONSOLE STATUS
;********************************;

CRLF:	MVI	C,0DH
	CALL	LIST
	MVI	C,0AH
	CALL	LIST
	CALL	CONST	;GET CONSOLE STATUS
	ORA	A	;TEST IT
	JZ	CRLF1	;BRIF NO ENTRY
	CALL	CONIN	;ELSE GET CHARACTER
	CPI	19	;CONTROL-S?
	JNZ	BOOT	;BRIF NOT
	CALL	CONIN	;ELSE WAIT TO RESUME
CRLF1:	LDA	LINES
	INR	A
	STA	LINES	;INCREMENT LINE COUNT
	CPI	60	;TEST LINE COUNT
	CZ	PAGE	;IF 56 THEN NEW PAGE
	RET


;********************************;
;       CHARACTER PARSING TABLE
;********************************;

CPTBL:	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	LDEL
	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
;********************************;

RWTBL:	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	'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	'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
RWSIZ	EQU	6	;SIZE OF TABLE ENTRY

 
;********************************;
;       OPERATING SYSTEM EQUATES
;********************************;
 
BOOT	EQU	0	;REBOOT CP/M ENTRY POINT
BDOS	EQU	0005H	;BDOS ENTRY POINT

MEMSZ	EQU	0006H	;END OF MEMORY POINTER
TBSZE	EQU	20H	;TRANS. BUFFER SIZE IN SECTORS
TFCB	EQU	005CH	;TRANS. FCB

PRBUF	EQU	9	;PRINT BUFFER
OPEN	EQU	15	;OPEN FUNCTION CODE
STDMA	EQU	26	;SET DMA FUNCTION CODE
READ	EQU	20	;READ FUNCTION CODE



;********************************;
;           F O P E N            ;
;  ROUTINE TO OPEN A DISK FILE   ;
;  INPUT:     DE=A(FCB)          ;
; OUTPUT:     CARRY=ERROR        ;
;********************************;

FOPEN:	MVI	C,OPEN	;OPEN CODE
	CALL	BDOS	;ISSUE OPEN
	CPI	0FFH	;ERROR?
	JZ	FOERR	;YES
	XRA	A	;CLEAR CARRY
	RET
FOERR:	STC
	RET



;********************************;
;          G E T B Y T           ;
;   ROUTINE TO READ A BYTE       ;
;  OUTPUTS:     A=BYTE           ;
;              CARRY=ERROR       ;
;********************************;

GETBYT:	LHLD	TBDMA	;GET END OF BUFFER
	XCHG		;TO DE
	LHLD	INPTR	;CURRENT POINTER IN HL
	CALL	CPHL	;TEST FOR END OF BUFFER
	JZ	GETB2	;YES, READ
GETB1:	MOV	A,M	;GET BYTE
	INX	H	;BUMP POINTER
	SHLD	INPTR	;SAVE POINTER
	ORA	A	;RESET CARRY
	RET
GETB2:	LDA	TBFLG	;GET FLAG
	ORA	A	;TEST IT
	JNZ	IERR	;BRIF NO MORE
	LXI	H,TBUF-128 ;ELSE SET START OF BUFFER
	MVI	A,TBSZE	;AND BUFFER SIZE IN SECTORS
GETB3:	STA	TBCNT	;SAVE IT
	LXI	D,128	;CALCULATE NEW STARTING
	DAD	D	;ADDRESS
	SHLD	TBDMA	;SAVE IT
	XCHG		;PLACE IT IN DE
	MVI	C,STDMA	;SET DMA ADDRESS
	CALL	BDOS
	MVI	C,READ	;READ CODE
	LXI	D,TFCB	;FCB ADDRESS
	CALL	BDOS	;ISSUE READ
	ORA	A	;CHECK FOR ERROR/E-O-F
	JNZ	GETB4	;BRIF IS
	LHLD	TBDMA	;ELSE GET LAST DMA STARTING ADDR
	LDA	TBCNT	;GET SECTOR COUNT
	DCR	A	;SUBTRACT ONE
	JNZ	GETB3	;BRIF SOME MORE
	LXI	D,128	;ELSE SET NEW END OF BUFFER
	DAD	D
	SHLD	TBDMA
	JMP	GETB5
GETB4:	MVI	A,0FFH	;SET FLAG
	STA	TBFLG
GETB5:	LXI	H,TBUF	;RESET BUFFER POINTER
	JMP	GETB1	;CONTINUE

IERR:	STC
	RET

;********************************;
;   MISCELLANEOUS SUBROUTINES    ;
;********************************;

;********************************;
;	BIOS VECTORS		;
;*******************************;

CONST:	JMP	$-$	;CONSOLE STATUS
CONIN:	JMP	$-$	;CONSOLE INPUT
LIST:	JMP	$-$	;LIST OUTPUT

;********************************;
;             C P H L            ;
;  ROUTINE TO COMPARE HL VS DE   ;
;********************************;

CPHL:	MOV	A,H
	CMP	D
	RNZ
	MOV	A,L
	CMP	E
	RET

;********************************;
;             D A T A            ;
;********************************;

XRFHD:	DB	'CROSS REFERENCE:'
EMSG1:	DB	'SYMBOL TABLE ERROR',0DH,0AH,'$'
EMSG2:	DB	'NO SUCH FILE',0DH,0AH,'$'
SYMSZ	EQU	7	;SYMBOL SIZE
STESZ	EQU	SYMSZ+2	;SYMBOL TABLE ENTRY SIZE
REFSZ	EQU	2+(12*2) ;NUMBER OF BYTES IN REF BLOCK
DEC:	DS	5
	DB	0DH
LINES:	DB	0	;PRINT LINE COUNT
INPTR:	DW	TBUF+(TBSZE*128) ;INPUT BUFFER
TBDMA:	DW	TBUF+(TBSZE*128) ;DMA/E-O-B ADDRESS
TBFLG:	DB	0	;TRANS. BUFFER FLAG
			;00 = SOME MORE
			;FF = NO MORE
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
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
PBUF:	DS	132
SYMCT:	DS	1
SYMPT:	DS	2
SBUF:	DS	SYMSZ
TEMP:	DS	2	;TEMP SAVE WORD
TBCNT:	DS	1	;TRANS. BUFFER SECTOR COUNT
TBUF:	DS	TBSZE*128 ;TRANS. BUFFER
	DS	64
STACK	EQU	$	;TOP OF STACK

;***********************************;
;         SYMBOL TABLE AREA
;    THE SYMBOL TABLE MUST BE
;    THE LAST BYTE OF THE PROGRAM
;***********************************;

SYMT:	ORG	$
	END
