; XREF27.ASM Cross-reference program for .ASM or .PRN files 11/09/85 ; ; ; This program generates a cross-reference list for all labels in an ; .ASM, .MAC or .PRN file. Thus it works with either an assembly level ; source code file or an assembled print file. No other cross-reference ; program is known to have this sort of versatility and ease of use. It ; will number the lines and generate a cross-reference listing at the ; end of the program. If a numbered listing is not requested, it makes ; a cross-reference listing without the source code - this is often used ; to find what label names have been already used - or which labels were ; actually unnecessary as they were not used at all. The output can be ; shown to the CRT, sent directly to the printer, or to a disk file. If ; a disk file is used, it is given the same name as the original file, ; with an .EXR extent. ; ;----------------------------------------------------------------------- ; current revision ; ; 11/09/85 Fixed an intermittent bug that was causing the program to ; v27 miss seeing the EOF character and keep right on going until ; it bombed by filling the disk. This occured on some longer ; .PRN files when adding line numbers. Took awhile to find, ; as only occured occasionally. Likely been around since the ; program was written. - Irv Hoff ; ;----------------------------------------------------------------------- ; ; It is invoked by entering: ; ; XREF FILEBNME.ASM (output to printer) ; XREF FILENAME.PRN CRT (output to CRT) ; XREF FILENAME.ASM D (output to disk - default drive) ; XREF FILENAME.PRN B:D (output to disk - drive B: in this case) ; ; To get the help guide type: ; ; XREF ? or XREF ; ;----------------------------------------------------------------------- ; Number of lines per output page set by equ LPAGE. Set this equate to ; zero if you don't want any form feeds added in the cross-reference ; listing - it will still put one at the beginning and end. ; ; NREFS sets the number of label references per table entry. This ; value should be set to the average number of references that a label ; will have in the program. It is used to build the label reference ; table entries. Each entry will be equal to (NREFS*2)+2. If it is ; made unnecessarily large, then the label reference table will be very ; large with a lot of unused slots. Example: a program with 300 labels ; 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 label that exceeds NREFS re- ; ferences will require ((Total references/NREFS) rounded up) reference ; table entries. Recommended values: 3-5. ; ; LREFS sets the Number of labels references to be printed per output ; line. 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 refer- ; ences that will fit on one line without overflow is equal to the size ; of the line minus (LABSIZ+2) divided by 6. Example: if the line size ; is 72 characters and LABSIZ equals 10, then total number of references ; per line can be INT((72-(10+2))/6) or INT(60/6) or 10. ; ; LABSIZ sets the Label size. This is the label size that the program ; will use in the cross reference listing. It should be large enough to ; make each label unique in cross-reference listing. If a label is ; larger than the value for LABSIZ, then the label will be truncated in ; the output i.e., LARGE$LABEL would be LARGE$LABE if LABSIZ was set to ; 10. Recommended value is 8. ; ; BUFSIZ 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 and output buffers. Input buffer size = BUFSIZ * 128. Recom- ; mended value is from 8 to 32. 16 works well. This represents a 2k ; buffer. Remember there are separate buffers of this size for input ; and output, doubling the total memory used. ; ; ; NOTE: If INSECT is made too large, there will not be enough memory ; left to build the cross-reference table for a large program. Largest ; tested values so far is 916 on a 64k system. The file successfully ; cross-referenced was MDM740.PRN. - JRM ; ;----------------------------------------------------------------------- ; ; DONE ; ---- ; 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) closes the disk input file ; 6) displays end of program message ; ; ; SETUP OR INITIALIZATION ; ----------------------- ; 1) displays program logo and version ; 2) checks for input file type PRN ; -if file type PRN, sets PRN switch ; 3) if operator 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 label table ; and reference table, set line counter to 1 ; 7) return to caller ; ; DBYT ; ---- ; Put output character into disk buffer ; ; Input = E (output character) ; ; 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 ^C ; ; ; 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 ; ; ; GETBT ; ----- ; 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 ; ; ; GETREC ; ------ ; Read the number of input records specified by BUFSIZ. 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 ; ;----------------------------------------------------------------------- ; prior updates ; 09/09/85 Rewrote the display routine so it has all reference numbers ; v26 lined up uniformly, with a leading asterisk on the number ; representing the line where the label is defined. Other ; changes. - Irv Hoff ; ; 01/13/85 Modified help message and introduced XRF-only option, most ; v25 editors can easily find a line with a number. Added markers ; " >>" for label-definition. Shortened line number display ; to be consistent with CREF output. Moved code to conserve ; space. Moved comments from M80 source which are no longer ; needed as LASM is there and does the job. ; - B. Eiben ; ; 01/30/83 Added operation (and pseudo) codes for the Z80 and M80 as- ; v24 semblers. While M80 CREF80 are superior they are limited by ; the size of the CRF files since instead of TAB space fill is ; used (an 80K MAC file expands to a 240K CRF file). This ; program will handle at least 1200 labels in 48K RAM. Even ; without flagging the defining statement, this program is ; quite useful. - RMG ; ; 03/15/82 This program would lock up if there was a comment line that ; v23 started with an asterisk '*' instead of a semicolon. The ; program then processed the whole line as a valid line of as- ; sembly-level code. This usually resulted in the cross re- ; ference program locking up if there were a lot of lines that ; started with an asterisk. The program now treats an aster- ; isk 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 label table before attempting to print the label ; symbol table. - JRM ; ; 03/06/82 Changed the code so the size of output disk buffer can be ; v22 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 label on a line was preceded by a char- ; acter, the line was not listed in the cross-reference list- ; ing. This was caused by skipping over the first 16 char- ; acters in the line on a CR (carriage return) instead of on a ; LF (line feed). This bug has been fixed. Added help func- ; tion. This is invoked by entering XFER ? ; - JRM ; ; 03/03/82 Added a 2k buffer for disk output writes. This was done to ; v21 speed up the execution of program when disk output was spec- ; ified. Fixed bug when page size was specified. CP/M assem- ; bler was not doing the conditional IF assembly correctly. ; Changed the code to test if page size is in effect by test- ; ing 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 label that is refer- ; v20 enced in the cross-reference listing can be set at assembly ; time. The original program only allowed for 5 characters ; per label. This did not allow for separate unique labels ; where the uniqueness occurred after the first 5 characters. ; I tested this function for a length of 5, 8, and 10 chara- ; cters labels. It should work for any length of label. I ; also allowed the '$' character to be a valid character in a ; label. The equ LABSIZ was added and all other values and ; variables that are dependent on the size of the label are ; expressed using LABSIZ. ; ; Example: LSIZ EQU LABSIZ+3 ; ; Added a disk file output option. The output disk file will ; use the filename of the input file and always have 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 from the de- ; fault drive for the cross-reference disk output. ; ; Example: XREF FILENAME.ASM D ; XREF FILENAME.PRN B:D ; - JRM ; ;----------------------------------------------------------------------- ; ; ; Equates that can be changed ; BUFSIZ EQU 16 ; In and out buffer size, 8 records = 1k LABSIZ EQU 10 ; Number of characters per label LPAGE EQU 0 ; Num lines per page if peject is true LREFS EQU 10 ; References per output line NREFS EQU 5 ; Number of references per ref tbl entry ; ; ; Equates that can't be changed ; LSIZ EQU LABSIZ+3 ; Label table entry size REFSZ EQU 2+(NREFS*2) ; Number of bytes in ref block RSIZ EQU 05 ; Size of rtab table entry ; BS EQU 08H ; Backspace CR EQU 0DH ; Carriage return EOF EQU 1AH ; EOF code LF EQU 0AH ; Line feed ; ; ; Operating system equates ; BOOT EQU 0000H ; Reboot entry point BDOS EQU 0005H ; CP/M 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 ; ; ; Main loop ; ; ORG 100H ; ; START: LXI SP,STACK ; Set stack pointer CALL SETUP ; Initialize ; MAIN: CALL GETBT ; Get a byte from source file ; MAIN1: 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 character in character table JC MAIN ; Not found, ignore PCHL ; Execute routine ;..... ; ; ; Finished ; ; Final label table print ; DONE: MVI E,0CH ; Printer form feed character CALL PBYT MVI A,0FFH ; STA LISTFLG ; CREF-list on always LHLD LABBT ; Get label table bottom MVI A,0FFH ; Check to see if there were... CMP M ; Any labels in the program JZ DLP4 ; No, don't print label table SHLD LAB ; Set label pointer LHLD LABTP ; Get label table top MVI M,0FFH ; End off label table ; DLP1: LHLD LAB ; Get label table pointer CALL PLAB ; Print label LHLD LAB LXI D,LABSIZ+1 ; Offset to reference link DAD D MOV E,M INX H MOV D,M ; Get reference block address XCHG ; Into HL SHLD REF CALL PREF ; Print references LHLD LAB ; Get label table pointer LXI D,LSIZ ; Size of symbol table entry DAD D SHLD LAB MOV A,M ; Get byte CPI 0FFH ; End of table? JNZ DLP1 ; Loop ; DLP2: MVI E,0CH ; Printer form feed character CALL PBYT LDA DISKOUT ; Get disk output switch ORA A ; Disk output being used? JZ DLP3 ; No, issue end msg & return to CP/M 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 input file MVI C,CLOSE CALL BDOS LXI D,DONEMSG ; Tell operator we're done CALL PSOUT JMP BOOT ; And return to CP/M ;..... ; ; DLP4: LXI H,NOLABS ; Get no label message MVI B,28 ; Number of characters in message ; DLP5: MOV E,M ; Print the message CALL PBYT INX H DCR B JNZ DLP5 JMP DLP2 ; Go close files ; ; ; Label print routine ; PLAB: MVI B,LABSIZ ; Label size ; PLAB1: MOV E,M ; Get byte CALL PBYT ; Print byte INX H DCR B JNZ PLAB1 MVI E,' ' CALL PBYT ; Print 2 spaces CALL PBYT RET ;..... ; ; ; Reference print routine ; PREF: MVI A,LREFS/NREFS ; Number of blocks to print on one line STA NLINS ; To nlins ; PREF1: LHLD REF ; Get reference block address INX H INX H ; Bump to first reference number SHLD TEMP ; Save reference number address MVI A,(REFSZ-2)/2 ; Number of ref slots STA LABCT ; Save in symct ; PREF2: LHLD TEMP ; Get reference slot address MOV E,M INX H MOV D,M ; Get reference LXI H,0000 ; Zero? CALL CPHL JZ CRLF ; Yes, done PUSH D MVI E,' ' CALL PBYT POP D MOV A,D ANI 080H ; High bit set ? JZ PREF3 ; Nope ; MOV A,D ; If a label entry, show ANI 07FH MOV D,A ; Clear it PUSH D MVI E,'*' CALL PBYT POP D JMP PREF4 ; Skip next space ; PREF3: PUSH D MVI E,' ' CALL PBYT POP D ; PREF4: XCHG ; Get number in HL CALL DECOT ; Convert CALL DECPNT ; Print it LHLD TEMP ; Get reference slot address INX H INX H ; Bump to next slot SHLD TEMP LDA LABCT ; Get count DCR A ; Decrement STA LABCT JNZ PREF2 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 PREF1 ; And print more on same line CALL CRLF ; Print CR and LF MVI B,LABSIZ+2 ; Indent continuation line... ; PREF5: MVI E,' ' ; With spaces CALL PBYT ; Print spaces DCR B JNZ PREF5 ; Print 6 spaces JMP PREF ;..... ; ; ; Character parsing routines ; LALPH: LXI H,SBUF ; Point to label buffer MVI C,LABSIZ MVI A,' ' ; Fill label... ; LALX: MOV M,A ; With... INX H ; Blanks DCR C JNZ LALX ; Clear label buffer LXI H,SBUF SHLD LABPT MVI A,00 STA LABCT ; Reset label pointer+count LDA CHAR ; Get character again CALL GTLAB ; Collect identifier ; LALC: CALL GETBT ; Get a byte from source file 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: CALL LINUP ; Up LINLAB LDA CHAR ; Get character that ended ID JMP MAIN1 ; Continue scan ;..... ; ; ; LAL1: CALL FIND ; See if defined JC LAL2 ; No, continue CALL ADDRF ; Yes, add reference JMP LAL0 ; Done ;..... ; ; LAL2: CALL ENLAB ; Enter label definition CALL ADDRF ; Add reference JMP LAL0 ; Continue ;..... ; ; LAL3: CALL GTLAB ; Collect identifier JMP LALC ; Continue ;..... ; ; LNUM: CALL GETBT ; Get byte CALL CKNUM ; Test for numeric JNC LNUM ; Yes, continue CALL CKALP ; Test for alphabetic JNC LNUM ; Yes, continue JMP MAIN1 ; Continue with main scan ;..... ; ; LQUOT: CALL GETBT ; Get a byte CPI '''' ; See if string quote JNZ LQUOT ; No, keep looping CALL GETBT ; Get next byte CPI '''' ; Test for doubles JZ LQUOT ; Yes, start scan again JMP MAIN1 ; No, continue in main scan ;..... ; ; LSEMI: CALL GETBT ; Get a byte CPI CR ; Wait for cr JNZ LSEMI ; Continue JMP MAIN1 ; 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 ; LLF1A: PUSH PSW ; MVI A,0 ; Reset LINLAB STA LINLAB POP PSW JMP MAIN ;..... ; ; LLF2: POP PSW ; Restore character MVI B,16 ; # of char to skip over ; LLF3: CALL GETBT ; Get next byte CPI CR ; Carriage return? JZ LCR ; Yes, get next line CPI EOF ; End of file now? JZ DONE ; If yes, finish up DCR B ; Skipped all char? JNZ LLF3 ; No, continue skipping JMP LLF1A ; Continue ;..... ; ; LIGN: JMP MAIN ; Re-enter main loop LSPC EQU LIGN LTAB EQU LIGN LDOL EQU LIGN LDEL EQU LIGN ; ; ;---------------------------------------------------------------------- ; subroutines ; ; 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 label MVI B,5 ; Label size ; CRES2: LDAX D ; Get label 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 label in table ; FIND: LHLD LABBT ; Get begin of sym table SHLD LAB ; Set temp pointer ; FIND1: LHLD LAB ; Get temp pointer LXI D,SBUF ; Point to current label MVI B,LABSIZ ; Label 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 LAB ; Get current pointer LXI D,LSIZ ; Label table entry size DAD D ; Bump pointer XCHG ; Into de LHLD LABTP ; Get top of label table CALL CPHL ; Test for end of table JZ FIND4 ; Yes, done JC FERR ; Table overflow, error XCHG ; Current pointer into HL SHLD LAB ; Set current pointer JMP FIND1 ; Loop ; FIND4: STC ; Set carry for not found LHLD LABTP ; Get current top SHLD LAB ; Set current pointer RET ;..... ; ; FERR: LXI D,EMSG1 ; Label table error message ; FERR1: CALL PSOUT ; JMP BOOT ; Exit ; FERR2: LXI D,EMSG6 ; No room for label table JMP FERR1 ; ; ; Add reference to ref table ; ADDRF: LHLD LAB ; Get label pointer LXI D,LABSIZ+1 ; Offset past label&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 LDA LINLAB ; First label on line ? ORA A JNZ ENREF1 ; Nope CALL LINUP ; Up LINLAB MOV A,D ADI 080H MOV D,A ; Set high bit ; ENREF1: MOV M,E INX H MOV M,D ; Store line ref RET ; Done ;..... ; ; ; Build reference table block ; BLDRF: LHLD LAB ; Get label pointer LXI D,LABSIZ+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 LABTP ; Get label 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 reference check LHLD TEMP ; Get new reference block address SHLD REF ; Store in reference MVI B,REFSZ ; Size of ref block MVI A,0 ; 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 label in label table ; ENLAB: LHLD LAB ; Get label pointer XCHG ; Into DE LHLD LABTP ; Get label table top CALL CPHL ; Check for end of table JZ NWLAB ; Yes, add label at end LXI D,LSIZ ; Label 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 ; Address of error msg in of overflow JZ FERR1 ; Full, error JC FERR1 ; Yes, error LHLD LABTP ; Get table top LXI D,LSIZ-1 ; Bump to end of entry DAD D SHLD TO ; Store in to address LXI D,LSIZ 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 LAB ; Get current pointer SHLD LIMIT ; Store as limit address CALL MVUP ; Move table up in memory ; NWLAB: LHLD LAB ; Get current pointer LXI D,SBUF ; Point to label MVI B,LABSIZ ; Size of label CALL MOVE ; Copy label to table MVI A,0 MOV M,A INX H MOV M,A INX H MOV M,A ; Set pointers to 0000 LHLD LABTP ; Get label table top LXI D,LSIZ ; Get label entry size DAD D ; Bump SHLD LABTP ; Store new top RET ;..... ; ; ; Move label 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 ;..... ; ; ; Print number string in DEC ; DECPNT: LXI H,DEC+1 ; Point to dec string ; DCPNT1: MOV E,M ; Get string byte MOV A,E CPI '$' ; Done? RZ ; Yes ; CALL PBYT ; Print byte INX H ; Bump pointer JMP DCPNT1 ;..... ; ; ; Up LINLAB after label found ; LINUP: PUSH PSW LDA LINLAB INR A STA LINLAB ; Found first separator POP PSW RET ;..... ; ; ; Test for alphabetic character ; CKALP: CPI '$' ; Treat '$' like a char RZ CPI 'A' ; Ascii 'a' RC ; No, exit CPI 'Z'+1 CMC RET ;..... ; ; Test for numeric character ; CKNUM: CPI '0' RC CPI '9'+1 CMC RET ;..... ; ; ; Look up character 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 ;..... ; ; ; Print source line with number ; PLINE: LHLD LCNT ; Get line number CALL DECOT ; Convert to decimal CALL DECPNT ; Print it ; PL1: MVI E,' ' CALL PBYT LDA FTPRN ORA A JZ PL11 MVI E,' ' CALL PBYT ; PL11: LXI H,PBUF ; Point to print buffer XRA A STA COL ; Set column count ; PL41: MOV E,M ; Get byte MOV A,E CPI CR ; Done? JZ PL5 CPI LF ; 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 label in label buffer ; GTLAB: MOV B,A ; Save char LDA LABCT ; Get label count CPI LABSIZ ; Max? RNC ; Yes, done INR A STA LABCT LHLD LABPT MOV M,B INX H ; Bump label pointer SHLD LABPT RET ;..... ; ; ; Printer interfaces ; ; Print a single byte ; PBYT: PUSH B PUSH D PUSH H PUSH PSW ; LDA LISTFLG ; Listing on ? ORA A JZ PBYT3 ; No listing 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 BDOS ; PBYT3: MVI C,11 ; Check console status CALL BDOS ORA A ; If zero, ok JZ PBYTX ; Exit MVI C,1 CALL BDOS ; Read console char CPI 3 JZ BOOT ; Abort if ^C, otherwise ignore ; PBYTX: POP PSW POP H POP D POP B RET ;..... ; ; ; Put output character into disk buffer - input=e (output character) ; 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 ;..... ; ; ; Write out disk buffer to disk ; PUTREC: LXI H,DISKBUF ; Address of disk buffer SHLD BUFADR ; Save it for later dma's MVI B,BUFSIZ ; 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 BDOS ; Correct buffer LXI D,OFCB ; Get output disk fcb MVI C,WRITE ; Write sector... CALL BDOS ; 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,BUFSIZ*128 ; Put disk buffer size into HL 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 CR, LF and test page ; CRLF: MVI E,CR CALL PBYT MVI E,LF 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 RNZ MVI E,0CH ; Printer form feed character CALL PBYT ;..... ; ; ; Character parsing table ; CTAB1: DB CR DW LCR DB LF 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 ;..... ; ; ; Reserved word table ; RTAB: DB '$ ','8080 ' DB 'A ','ACI ','ADC ','ADD ','ADI ' DB 'AF ','ANA ','AND ','ANI ','ASEG ' DB 'B ','BC ','BIT ' DB 'C ','CALL ','CC ','CCF ','CM ' DB 'CMA ','CMC ','CMP ','CNC ','CNZ ' DB 'COMME','COMMO','COND ','CP ','CPD ' DB 'CPDR ','CPE ','CPI ','CPL ','CPO ' DB 'CSEG ','CZ ' DB 'D ','DAA ','DAD ','DB ','DC ' DB 'DCR ','DCX ','DE ','DEC ','DEFB ' DB 'DEFL ','DEFM ','DEFW ','DI ','DJNZ ' DB 'DS ','DSEG ','DW ' DB 'E ','EI ','ELSE ','END ','ENDC ' DB 'ENDIF','ENDM ','ENTRY','EQ ','EQU ' DB 'EX ','EXX ','EXITM','EXT ','EXTRN' DB 'GE ','GLOBA','GT ' DB 'H ','HL ','HIGH ','HALT ' DB 'IF ','IFB ','IFDEF','IFE ','IFF ' DB 'IFNB ','IFNDE','IFT ','IF1 ','IF2 ' DB 'IM ','IN ','INC ','INCLU','IND ' DB 'INDR ','INIR ','INR ','INX ','IRP ' DB 'IRPC ','IX ','IY ' DB 'JC ','JM ','JMP ','JNC ','JNZ ' DB 'JP ','JPE ','JPO ','JR ','JZ ' DB 'L ','LALL ','LD ','LDA ','LDAX ' DB 'LDD ','LDDR ','LDI ','LDIR ','LE ' DB 'LHLD ','LIST ','LOCAL','LOW ','LT ' DB 'LXI ' DB 'M ','MACRO','MOD ','MOV ','MVI ' DB 'NAME ','NC ','NE ','NEG ','NOP ' DB 'NOT ','NUL ','NZ ' DB 'OR ','ORA ','ORG ','ORI ','OTDR ' DB 'OTIR ','OUT ','OUTI ','OUTD ' DB 'P ','PAGE ','PCHL ','PE ','PO ' DB 'POP ','PRINT','PSW ','PUBLI','PUSH ' DB 'RADIX','RAL ','RAR ','RC ','REPT ' DB 'RES ','RET ','RETI ','RL ','RLA ' DB 'RLC ','RLCA ','RLD ','RM ','RNC ' DB 'RNZ ','RP ','RPE ','RPO ','RR ' DB 'RRA ','RRC ','RRCA ','RRD ','RST ' DB 'RZ ' DB 'SALL ','SBB ','SBC ','SBI ','SCF ' DB 'SET ','SHL ','SHLD ','SHR ','SLA ' DB 'SP ','SPHL ','SRA ','SRL ','STA ' DB 'STAX ','STC ','SUB ','SUBTT','SUI ' DB 'TITLE' DB 'XALL ','XCHG ','XLIST','XOR ','XRA ' DB 'XRI ','XTHL ' DB 'Z ','Z80 ' DB 0FFH ; End of reserved word table ;..... ; ; ; Routine to open a disk file ; input: DE=A(FCB) ; FOPEN: MVI C,OPEN ; Open code CALL BDOS ; 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 BDOS ; Last disk write POP D ; Do last... PUSH D ; (d,e contains ofcb)... MVI C,WRITE ; Disk... CALL BDOS ; Write ; ; ; Check to see if last record has been written. Do this by comparing ; the address of the next available character to the address of the buf- ; fer for the next disk write. If the address of the next available ; character is equal to or greater, close the 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 BDOS ; Cross ref file CPI 0FFH ; Close successfull? RNZ ; Yes, return LXI D,EMSG5 ; No, display error msg... JMP FERR1 ; And quit ;..... ; ; ; Rroutine to read a byte ; outputs: A=BYTE ; GETBT: LXI H,TBUF+(BUFSIZ*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 JMP SAVBT ;..... ; ; ; 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 ;..... ; ; GETREC: LDA CONSOLE ; Console on ? ORA A JNZ GTREC1 ; Then he sees progress LHLD LCNT ; Line-count CALL DECOT ; Into ASCII LXI D,DECERA ; Erase area CALL PSOUT LXI D,DEC ; ASCII linecount CALL PSOUT ; To show progress ; GTREC1: MVI B,BUFSIZ ; 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 DE 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 ptr in case EOF occurrs MVI C,SETDMA ; Point to input buffer... CALL BDOS MVI C,READ ; Read code LXI D,TFCB ; FCB address CALL BDOS ; 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 ;..... ; ; ;PSOUT prints string in D ; PSOUT: MVI C,PSTRING CALL BDOS ; Print String RET ;..... ; ; ; 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 ;..... ; ; ; DATA ; EMSG1: DB CR,LF,'Label table error',CR,LF,'$' EMSG2: DB CR,LF,'Failed to delete existing cross reference file' DB CR,LF,'$' EMSG3: DB CR,LF,'Failed to create cross reference file',CR,LF,'$' EMSG4: DB CR,LF,'Disk write failed - Out of space?',CR,LF,'$' EMSG5: DB CR,LF,'Close failed on disk cross reference file' DB CR,LF,'$' EMSG6: DB CR,LF,'++ERROR++ Not enough memory for label table' DB CR,LF,'$' EMSG7: DB CR,LF,'++ERROR++ Label table overflow?',CR,LF,'$' DONEMSG:DB CR,LF,'Cross-reference list is done',CR,LF,'$' NOLABS: DB CR,LF,'No Labels in this Program' CONCRLF:DB CR,LF,'$' 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 BUFSIZ*128 ; Number of char left in disk buffer INPTR: DW TBUF+(BUFSIZ*128) ; Pointer to char in input buffer DISKBUF:DS BUFSIZ*128 ; Output disk buffer TBUF: DS BUFSIZ*128 ; Disk input buffer ; ; CHAR: DS 1 COL: DS 1 FROM: DS 2 ; Move pointer LAB: DS 2 ; Current label table address LABBT: DS 2 ; Label table bottom address LABTP: DS 2 ; Label table top address LCNT: DS 2 ; Line counter LIMIT: DS 2 ; Limit pointer LINLAB: DS 1 ; 0 if first label on line LPNT: DS 2 REF: DS 2 ; Current reference table address REFBT: DS 2 ; Reference table bottom address REFTP: DS 2 ; Reference table top address TO: DS 2 ; To pointer DEC: DS 5 DB '$' ; DECERA: DB BS,BS,BS,BS,BS,'$' ; Erase string for Line-Nr display PBUF: DS 132 LABCT: DS 1 LABPT: DS 2 SBUF: DS LABSIZ ; Label buffer NLINS: DB 0 ; Buffers to print on line LINES: DB 0 ; Print line count ; CONSOLE:DB 0 ; Console output switch DISKOUT:DB 0 ; Disk output switch LISTFLG:DB 0 ; 0=no source listing FTPRN: DB 1 ; Prn file type TEMP: DS 2 ; Temp save word DS 64 ; Stack can have up to 32 addresses STACK EQU $ ;..... ; ; ; Label table area ; ; The label table must be the last byte before ONCE ONLY CODE - XREF ; is NOT RESTARTABLE ; ; ORG $ ; ; LABT: DB 0FFH ; LOGO: DB CR,LF,'CP/M assembler cross-reference list v27' DB CR,LF,'$' ; MSG1: DB ' disk cross-reference file exists, keep it (y/n) ? ' DB '$' ; MSG2: DB ' like to have a line-numbered .XRF listing (y/n) ? ' DB '$' ; EMSG0: DB CR,LF,'++ Can''t find that file ++',CR,LF,'$' ; ; ; HELP: DB CR,LF,'This program can be used to create a cross-' DB 'reference listing',CR,LF,'of CP/M assembler programs.' DB ' The input can be either the assembler',CR,LF DB 'language source code (FILENAME.ASM) or the assembly ' DB 'listing (FILENAME.PRN).',CR,LF DB 'The output can be printed on the CP/M list device, ' DB 'displayed on',CR,LF DB 'the CRT, or written to disk (file type will be ' DB '.XRF). It is',CR,LF,'invoked by keying:',CR,LF,LF DB 09H,'XREF FILENAME.ASM (Output on list device)' DB CR,LF,09H,'XREF FILENAME.PRN CRT (Output on the ' DB 'CRT)',CR,LF,09H,'XREF FILENAME.ASM D (Output on ' DB 'disk-default drive)',CR,LF,09H,'XREF FILENAME.PRN B:D' DB ' (Output on disk-drive specified)' DB CR,LF,CR,LF,CR,LF,CR,LF,CR,LF,'$' ;..... ; ; ; Setup or initialization ; SETUP: LXI D,LOGO ; Get program logo messagew CALL PSOUT ; Display it LDA TFCB+1 ; Get first character of file name CPI ' ' JZ SETUP0 CPI '?' ; Help function? JNZ SETUP1 ; No, go check input file type ; SETUP0: LXI D,HELP ; Display help message JMP FERR1 ;... ; ; SETUP1: MVI A,0 STA LISTFLG ; No listing LDA TFCB+9 ; Get first character of file type CPI 'P' ; Is it type .PRN? JNZ SETUP2 ; No, don't set switch LDA TFCB+10 ; Get 2nd character of file type CPI 'R' JNZ SETUP2 LDA TFCB+11 ; Get 3rd character of file type CPI 'N' JNZ SETUP2 XRA A STA FTPRN ; Turn on .PRN file type ; SETUP2: LDA TFCB+17 ; Get 1st character of 2nd FCB CPI 'C' ; Output to console? JNZ SETUP3 ; No, don't turn on switch LDA TFCB+18 ; Get 2nd character of 2nd FCB CPI 'R' ; Output to CRT? JNZ SETUP3 ; If not, exit MVI A,0FFH ; else turn on CRT STA CONSOLE JMP SETUP5 ; Go open input file ; SETUP3: LDA TFCB+17 ; Get 1st character of 2nd FCB CPI 'D' ; Output to be written to disk? JNZ SETUP5 ; If not, exit MVI A,0FFH 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 already exist? MVI C,OPEN CALL BDOS CPI 0FFH ; Open succeed? JZ SETUP4 ; No, file does not exist LXI D,OFCB ; Close... MVI C,CLOSE ; The open... CALL BDOS ; File. ; LXI D,MSG1 ; Want to keep the old file? CALL PSOUT MVI C,CONIN ; Get the answer CALL BDOS ANI 05FH ; Make upper case CPI 'Y' ; Operator say yes, keep it? JZ BOOT ; If, yes, exit, done LXI D,CONCRLF ; Write CRLF to console CALL PSOUT LXI D,OFCB ; Point to FCB to delete file MVI C,DELETE ; Delete the file CALL BDOS CPI 0FFH ; Delete succeed? LXI D,EMSG2 ; If not, show error message JZ FERR1 ; SETUP4: LXI D,OFCB ; Point to output FCB MVI C,MAKE ; Create new... CALL BDOS ; 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 ; SETUP5: LXI D,TFCB ; Point to fcb CALL FOPEN ; Open fcb LXI D,MSG2 ; "Want Listing.." CALL PSOUT MVI C,CONIN CALL BDOS ; Get answer ANI 05FH ; Make upper case CPI 'Y' JNZ SETUP6 ; No listing MVI A,0FFH STA LISTFLG ; Wants listing ; SETUP6: LXI D,CONCRLF ; Write CRLF to console CALL PSOUT LXI H,PBUF SHLD LPNT ; Set print pointer LXI H,00001 ; Set line counter... SHLD LCNT ; To one. LXI H,LABT ; Get address of label table SHLD LAB SHLD LABBT SHLD LABTP ; Set label table pointers LHLD MEMSZ ; Get available memory address DCX H SHLD REF SHLD REFBT SHLD REFTP ; Set reference table pointers RET ;..... ; ; END START