;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