PAGE 82 .Z80 TITLE XREF241Z.MAC vers 2.41 01/30/83 ; ; ; cross reference program ; for asm and mac files ; i8080 and Z80 mnemonics ; ; version 2.41 ; ; original by Jeff Kravitz ; ; modified by John Mahr ; Ray Malitzke-Goes for Z80 and M80 ; * ; This program generates a cross reference list for all * ; Symbols in a .MAC .ASM assembler program. Its input is ; either an assembler source file (FN.typ) or an assem- * ; bler listing file (FN.PRN). It will number the lines * ; and generate a cross reference listing at the end of * ; the program. The output defaults to the CPM list * ; device, but it can be directed to the console or to a * ; disk file instead. If the output is directed to a * ; disk file, that file will be named FN.XRF. The * ; filename will be the filename of the input file. ; ; It is invoked by entering: ; XREF FN.typ (OUTPUT TO LIST DEVICE) * ; XREF FN.PRN CON (OUTPUT TO CONSOLE) * ; XREF FN.ASM D (OUTPUT TO DISK-DEFAULT * ; DRIVE) * ; XREF FN.PRN B:D (OUTPUT TO DISK-DRIVE * ; NUMBER SPECIFIED) * ; * ; To invoke help function enter: * ; XREF ? * ; ;01/30/83 Added operation (and pseudo) codes for Z80 and ;M80 assembler. While M80 CREF80 are superior they do have ;limitations with the size of the CRF files as instead of ;TAB space fill is used (an 80K MAC file expands to a 240K ;CRF file). This program will at least 1200 labels in 48K. ;Even without flaggin the defining statement this porgram ;is quite useful. (RMG) ; ;03/15/82 This program would lock up if there was a ;comment line that started with an asterisk('*') instead ;of a semicolon. The program then processed the whole ;line as a valid line of assemble code. This usually ;resulted in the cross reference program locking up if ;there were a lot lines that started with an asterisk. ;The program now treats an asterisk the same as a ;semicolon. This was done by changing the table CTAB1 ;so that the program branches to LSEMI when it finds an ;asterisk. Also fixed bug that occurred when the last ;record of a file was completely filled with data and ;thus had no control Z's(EOF characters) in it. This ;was done by always placing two control Z's after the ;last record was read. Finally fixed a bug that showed ;up only when a program had no symbols or labels in it. ;Added a check for an empty symbol table before attempting ;to print the symbol table. (JRM) ; ;03/06/82 Changed the code so that the size of output ;disk buffer can be specified at assembly time. The ;size is set by OUTSECT which equals the number of disk ;sectors to be written when the buffer is full. ;Implemented the same feature for disk input. INSECT sets ;the number of disk records to be read into the input ;buffer. On file type PRN the was a bug. Whenever the ;first symbol on a line was preceded by a character, the ;line was not listed in the cross reference listing. This ;was caused by skipping over the first 16 characters in the ;line on a CR(carriage return) instead of on a LF(line feed). ;This bug has been fixed. Added help function. This is ;invoked by entering XFER ?. (JRM) ; ;03/03/82 Added a 2k buffer for disk output writes. This ;was done to speed up the execution of program when disk ;output was specified. Fixed bug when page size was ;specified. CPM assembler was not doing the conditional ;IF assembly correctly. Changed the code to test if page ;size is in effect by testing LPAGE to see if it is zero. ;Changed test to question to erase existing disk cross ;reference file to handle lower case as well as upper ;case. (JRM) ; ;02/20/82 Changed code so that the size of the symbol ;that is referenced in the cross reference listing can ;be set at assembly time. The original program only ;allowed for 5 char per symbol. This did not allow for ;separate unique symbols where the uniqueness occurred ;after the first 5 characters. I tested this function ;for a length of 5, 8, and 10 characters labels. It ; should work for any length of symbol. I also ;allowed the '$' character to be a valid character in a ;symbol. The equ SYMSIZ was added and all other values ;and variables that are dependent on the size of the ;symbol are expressed using SYMSIZ. ; Example: SSIZ EQU SYMSIZ+3 ;Added a disk file output option. The output disk file ;will the filename of the input file and always a file ;type of 'XRF'. This option is enabled by specifying ;a 'D' as the second parameter when executing the program. ;The user can optionally specify a different drive than ;the default drive for the cross reference disk output. ; Example: XREF FN.ASM D ; XREF FN.PRN B:D ; (JRM) ; ;11/01/81 Changed the code so that the hex addresses and ;the hex translation of the instructions are not included ;in the cross reference listing for PRN files from CPM ;assembler. The listing can now be directed at execution ;time to the console or the list (LST:) device. If the ;listing is to be displayed on the console enter ; XREF FN.FT C ;If the listing is to go to the list device enter ; XREF FN.FT ;In other words, the output goes to the list device by ;default. (JRM) ; ;Modifications 4/5/79, 4/26/79, 5/3/79, 8/28/79 by SAN: ;Suppress initial page eject, add page eject at completion ;Automatically convert lower case in source file to UPPER ;Abort execution if ^C typed, but ignore other typed chars ;Add ELSE, PAGE to reserved word table ; ;Number of lines per output page set by LPAGE EQUate ; ;Number of symbol refs per table entry set by NREFS EQUate ; This value should be set to the average number of ; references that a symbol will have in program. It ; is used to build the symbol reference table entries. ; Each entry will be equal to (NREFS * 2)+2. If it is ; made unnecessarily large, then the symbol reference ; table will be very large with a lot of unused slots. ; Example: a program with 300 symbols will require a ; reference table that is at least 12600 bytes if ; NREFS=20, whereas the same program will require a ; reference table that is at least 3600 bytes if ; NREFS=5. Each symbol that exceeds NREFS references ; will require ((Total references/NREFS)rounded up) ; reference table entries. Recommended values: 3-5. ; ;Number of symbol references to be printed per output line ; This is set by LREFS EQUate. It should be a multiple ; of NREFS. If it is not, then the first number that is ; less than LREFS and is a multiple of NREFS will be ; printed on the line. Example: if NREFS is 5 and ; LREFS is 13, then only 10 entries will be printed per ; line. The total number of references that will fit on ; one line without overflow is equal to the size of the ; line minus (SYMSIZ+2) divided by 5. Example: if the ; line size is 132 characters and SYMSIZ equals 8, ; then Total num of ref/line can be INT((132-(8+2))/5) ; or INT(122/5) or 20. ; ;Symbol size is set by SYMSIZ. This is the symbol size that the ; program will use in the cross reference listing. It ; should be large enough to make each symbol unique in ; cross reference listing. If a symbol is larger than the ; value for SYMSIZ, then the symbol will be truncated in the ; output(eg: LARGE$SYMBOL would be LARGE$SYM if SYMSIZ was ; set to 9. Recommended value is 8. ; ;OUTSECT sets the number of disk records that will be written to ; disk when the output disk buffer is full. This value is ; used by the program to calculate the size of the output ; disk buffer. Output buffer size = OUTSECT * 128. ; Recommended value is from 8 to 32. 16 works well. ; ;INSECT is the number of disk records that will be read when the ; disk input buffer is empty. It is used to calculate the ; size of the input buffer. Input buffer size = INSECT * 128. ; Recommend value is from 8 to 32. 16 works well. ;NOTE: If OUTSECT and INSECT are made too large, there will not be ; be enough memory left to build the cross reference table ; for a large program. Largest tested values so far(3/6/82) ; is 64 for OUTSECT and 64 for INSECT on a 64K system. The ; file successfully cross referenced was MODEM74.PRN. (JRM) ; ; 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. close 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 oper specified FN.FT CON, then sets console ; output switch on. Goes to step 5. ; 4. else check for disk output option. ; -if not disk output, default to printer output. ; -go to step 5. ; -else check to see if cross reference file exists. ; -if it exists, ask operater if it is to ; be erased. ; -if no erase, terminate program ; -else erase cross reference file. ; -then create new cross reference disk file. ; -initialize output character pointer to disk buffer. ; 5. open input disk file. ; 6. initialize printer buffer, address pointers for symbol table ; and reference table, set line counter to 1. ; 7. return to caller ; ; DBYT ; ; PUT OUTPUT CHARACTER ; INTO DISK BUFFER. ; INPUT=E(OUTPUT CHAR) ; 1. move character to buffer. ; 2. if at end of buffer, go ; write out disk buffer. ; 3. else add 1 to disk buffer ; pointer. ; 4. subtract 1 from number of ; positions left in buffer. ; 5. go check if operator pressed ; control C. ; ; 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. ; ; G E T B T ; ROUTINE TO READ A BYTE ; ; OUTPUTS: A=BYTE ; ; 1. is current byte pointer at ; end of buffer? ; -yes, Read disk records ; into buffer. ; 2. move character from buffer ; to ACCUM. ; 3. add 1 to character pointer ; and save pointer. ; 4. return to caller ; ; ; GETREC ;Read the number of input records specified by INSECT. The ;buffer starts at TBUF. Before each DMA, the buffer address ;is incremented by 128 and saved in IDADDR. This address ;will be used for the DMA after the current sector is read. ;The logic is: ; 1. get current buffer address ; 2. add 128 to it and save it ; 3. issue DMA for current buffer address ; 4. read the disk record, ; -if end of file goto step 7. ; 5. decrement number of sectors to read ; 6. if not number of sectors not zero ; -repeat above ; 7. reset character pointer to first buffer ; 8. return to caller. ; ; LPAGE EQU 00 ;num lines per page if peject is true NREFS EQU 03 ;number of references per ref tbl entry LREFS EQU 21 ;references per output line SYMSIZ EQU 08 ;number of symbol char OUTSECT EQU 01 ;number of disk sectors written from buffer INSECT EQU 08 ;number of disk sectors read into buffer ; ; main loop ; ASEG ORG 100H ;origin address XREF: LD SP,STACK ;set stack pointer CALL SETUP ;initialize MAIN: CALL GETBT ;get a byte from source file CALL SAVBT ;save byte in print buffer MAIN2: CALL CKNUM ;test for numeric JP NC,LNUM ;yes, found a number, process CALL CKALP ;test for alphabetic JP NC,LALPH ;yes, process LD HL,CTAB1 ;point to character table CALL LOOK ;look up char in char table JP C,LIGN ;not found, ignore JP (HL) ;execute routine ; ; done ; ; final symbol table print ; DONE: CALL EJECT ;issue page eject LD HL,(SYMBT) ;get symbol table bottom LD A,0FFH ;check to see if there were... CP (HL) ;...any symbols in the program JP Z,DLP4 ;no, don't print symbol table LD (SYM),HL ;set symbol pointer LD HL,(SYMTP) ;get symbol table top LD (HL),0FFH ;end off symbol table DLP1: LD HL,(SYM) ;get symbol table pointer CALL PSYM ;print symbol LD HL,(SYM) LD DE,SYMSIZ+1 ;offset to ref link ADD HL,DE LD E,(HL) INC HL LD D,(HL) ;get ref block addr EX DE,HL ;into hl LD (REF),HL CALL PREFS ;print references LD HL,(SYM) ;get symbol table pointer LD DE,SSIZ ;size of sym table entry ADD HL,DE LD (SYM),HL LD A,(HL) ;get byte CP 0FFH ;end of table? JP NZ,DLP1 ;loop ; DLP2: CALL EJECT ;page eject LD A,(DISKOUT) ;get disk output switch OR A ;disk output being used? JP Z,DLP3 ;no, issue end msg & return to cpm LD HL,DISKBUF ;get output disk buffer LD DE,OFCB ;and out fcb CALL DCLOSE ;and close output disk file ; DLP3: LD DE,TFCB ;close the... LD C,CLOSE ;...input... CALL CPM ;...file. LD DE,DONEMSG ;tell operator... LD C,PSTRING ;...that pgm is... CALL CPM ;...done JP BOOT ;and return to cp/m ; DLP4: LD HL,NOSYMS ;get no symbol message LD B,28 ;number of char in msg DLP5: LD E,(HL) ;print the... CALL PBYT ;...message INC HL DEC B JP NZ,DLP5 JP DLP2 ;go close files ; ; symbol print routine ; PSYM: LD B,SYMSIZ ;symbol size PSYM2: LD E,(HL) ;get byte CALL PBYT ;print byte INC HL DEC B JP NZ,PSYM2 LD E,' ' CALL PBYT ;print 2 spaces CALL PBYT RET ; ; reference print routine ; PREFS: LD A,LREFS/NREFS ;number of blocks to print on one line LD (NLINS),A ;to nlins PREF0: LD HL,(REF) ;get ref block addr INC HL INC HL ;bump to first ref number LD (TEMP),HL ;save ref num addr LD A,REFSZ/2-1 ;number of ref slots LD (SYMCT),A ;save in symct PREF: LD HL,(TEMP) ;get ref slot addr LD E,(HL) INC HL LD D,(HL) ;get ref LD HL,0000 ;zero? CALL CPHL JP Z,CRLF ;yes, done EX DE,HL ;get num in hl CALL DECOT ;convert LD HL,DEC ;point to dec string LD (HL),' ' ;blank leading zero LD B,5 ;number of digits + 1 in ref num PREF2: LD E,(HL) CALL PBYT ;print byte INC HL DEC B JP NZ,PREF2 ;print reference number LD HL,(TEMP) ;get ref slot addr INC HL INC HL ;bump to next slot LD (TEMP),HL LD A,(SYMCT) ;get count DEC A ;decrement LD (SYMCT),A JP NZ,PREF LD HL,(REF) ;get ref block address LD E,(HL) INC HL LD D,(HL) ;get link to next block LD HL,0000 CALL CPHL ;any more blocks? JP Z,CRLF ;no, exit EX DE,HL ;yes, set next block pointer in ref LD (REF),HL LD A,(NLINS) DEC A ;decrement lines count LD (NLINS),A JP NZ,PREF0 ;and print more on same line CALL CRLF ;print cr,lf LD B,SYMSIZ+2 ;indent continuation line... PREF3: LD E,' ' ;...with spaces CALL PBYT ;print spaces DEC B JP NZ,PREF3 ;print 6 spaces JP PREFS ; ; character parsing routines ; LALPH: LD HL,SBUF ;point to symbol buffer LD C,SYMSIZ LD A,' ' ;fill symbol... LALX: LD (HL),A ;...with... INC HL ;...blanks DEC C JP NZ,LALX ;clear symbol buffer LD HL,SBUF LD (SYMPT),HL LD A,00 LD (SYMCT),A ;reset symbol pointer+count LD A,(CHAR) ;get character again CALL GTSYM ;collect identifier LALC: CALL GETBT ;get a byte from source file CALL SAVBT ;save byte in print buffer CALL CKNUM ;test for number JP NC,LAL3 ;yes, continue CALL CKALP ;test for alphabetic JP NC,LAL3 ;yes, continue CALL CRES ;test for reserved word JP C,LAL1 ;no, continue LAL0: LD A,(CHAR) ;get character that ended id JP MAIN2 ;continue scan LAL1: CALL FIND ;see if defined JP C,LAL2 ;no, continue CALL ADDRF ;yes, add reference JP LAL0 ;done LAL2: CALL ENSYM ;enter symbol definition CALL ADDRF ;add reference JP LAL0 ;continue LAL3: CALL GTSYM ;collect identifier JP LALC ;continue ; LNUM: CALL GETBT ;get byte CALL SAVBT ;save byte in printer buffer CALL CKNUM ;test for numeric JP NC,LNUM ;yes, continue CALL CKALP ;test for alphabetic JP NC,LNUM ;yes, continue JP MAIN2 ;continue with main scan ; LQUOT: CALL GETBT ;get a byte CALL SAVBT ;save byte in printer buffer CP '''' ;see if string quote JP NZ,LQUOT ;no, keep looping CALL GETBT ;get next byte CALL SAVBT ;save byte CP '''' ;test for doubles JP Z,LQUOT ;yes, start scan again JP MAIN2 ;no, continue in main scan ; LSEMI: CALL GETBT ;get a byte CALL SAVBT ;save byte CP 0DH ;wait for cr JP NZ,LSEMI ;continue JP MAIN2 ;enter main loop ; LCR: CALL PLINE ;print line LD HL,(LCNT) ;get line number INC HL ;bump line number LD (LCNT),HL ;store JP MAIN ; LLF: PUSH AF ;save char LD A,(FTPRN) ;is it file... OR A ;...type prn? JP Z,LLF2 ;yes, skip 1rst 16 char POP AF ;restore char JP MAIN ; LLF2: POP AF ;restore character LD B,16 ;# of char to skip over ; LLF3: CALL GETBT ;get next byte CALL SAVBT ;put in print buf CP 0DH ;carriage return? JP Z,LCR ;yes, get next line DEC B ;skipped all char? JP NZ,LLF3 ;no, continue skipping JP MAIN ;continue ; LIGN: JP MAIN ;re-enter main loop ; LSPC EQU LIGN LTAB EQU LIGN LDOL EQU LIGN LDEL EQU LIGN ; ; ; subroutines ; ; setup or initialization ; ; SETUP: LD DE,LOGO ;get program logo msg LD C,PSTRING ;bdos print string CALL CPM ;print logo LD A,(TFCB+1) ;get first char of file name CP '?' ;help function? JP NZ,SETUP1 ;no, go check input file type LD DE,HELP ;display help message JP FERR1 ; SETUP1: LD A,(TFCB+9) ;get first char of file type CP 'P' ;is it type prn? JP NZ,SETUP2 ;no, don't set switch LD A,(TFCB+10) ;get 2nd char of file type CP 'R' JP NZ,SETUP2 LD A,(TFCB+11) ;get 3rd char of file type CP 'N' JP NZ,SETUP2 XOR A LD (FTPRN),A ;turn on prn file type ; SETUP2: LD A,(TFCB+17) ;get 1rst char of 2nd fcb CP 'C' ;output to console? JP NZ,SETUP3 ;no, don't turn on switch LD A,(TFCB+18) ;get 2nd char of 2nd fcb CP 'O' ;output to console? JP NZ,SETUP3 ;no, don't turn on switch LD A,0FFH ;turn on... LD (CONSOLE),A ;...console output JP SETUP6 ;go open input file SETUP3: LD A,(TFCB+17) ;get 1rst char of 2nd fcb CP 'D' ;output to be written to disk? JP NZ,SETUP6 ;no, go open input file. LD (DISKOUT),A ;turn on disk output switch LD A,(TFCB+16) ;get output file drive number LD (OFCB),A ;move to output fcb LD DE,TFCB ;point to fcb CALL FOPEN ;open disk input file LD DE,TFCB+1 ;point to input file name LD B,8 ;length of file name LD HL,OFCB+1 ;point to output fcb file name CALL MOVE ;move input file name to output fcb LD DE,OFCB ;does output file... LD C,OPEN ;...already... CALL CPM ;...exist? CP 0FFH ;open succeed? JP Z,SETUP4 ;no, file does not exist ; LD DE,OFCB ;close... LD C,CLOSE ;...the open... CALL CPM ;...file. LD DE,MSG1 ;ask operator... LD C,PSTRING ;...if old file... CALL CPM ;...is to be... LD C,CONIN ;...deleted CALL CPM AND 05FH ;make upper case CP 'Y' ;operator say yes? JP NZ,BOOT ;no, terminate ; LD DE,CONCRLF ;write carriage return... LD C,PSTRING ;...line feed to console CALL CPM LD DE,OFCB ;point to fcb to delete file LD C,DELETE ;delete the... CALL CPM ;...file CP 0FFH ;delete succeed? LD DE,EMSG2 ;get err msg in case of failure JP Z,FERR1 ;failure, print msg & quit ; SETUP4: LD DE,OFCB ;point to output fcb LD C,MAKE ;create new... CALL CPM ;...cross reference file. CP 0FFH ;create succeed? LD DE,EMSG3 ;get err msg in case of failure JP Z,FERR1 ;create failed, print msg & quit LD HL,DISKBUF ;get addr of disk buffer LD (DCHAR),HL ;init disk char pointer ; SETUP6: LD DE,TFCB ;point to fcb CALL FOPEN ;open fcb LD HL,PBUF LD (LPNT),HL ;set print pointer LD HL,00001 ;set line counter... LD (LCNT),HL ;...to one. LD HL,SYMT ;get address of symbol table LD (SYM),HL LD (SYMBT),HL LD (SYMTP),HL ;set symbol table pointers LD HL,(MEMSZ) ;get available memory address DEC HL LD (REF),HL LD (REFBT),HL LD (REFTP),HL ;set reference table pointers RET ; ; check for reserved word ; CRES: LD HL,RTAB ;point to reserved word table LD (TEMP),HL ;save in temp word CRES1: LD HL,(TEMP) ;get table pointer LD DE,SBUF ;point to symbol LD B,5 ;symbol size CRES2: LD A,(DE) ;get symbol byte CP (HL) ;compare against table entry RET C ;less, not in table JP NZ,CRES3 ;greater, get next table entry INC DE ;bump pointers INC HL DEC B ;decrement byte count JP NZ,CRES2 ;keep testing JP CRES4 ;found CRES3: LD HL,(TEMP) ;get table pointer LD DE,RSIZ ;size of entry ADD HL,DE ;bump pointer LD (TEMP),HL ;store new pointer LD A,(HL) ;get table byte CP 0FFH ;end of table? JP NZ,CRES1 ;no, loop SCF ;set carry (not in table) RET CRES4: OR A ;reset carry RET ; ; find symbol in table ; FIND: LD HL,(SYMBT) ;get begin of sym table LD (SYM),HL ;set temp pointer FIND1: LD HL,(SYM) ;get temp pointer LD DE,SBUF ;point to current symbol LD B,SYMSIZ ;symbol size FIND2: LD A,(DE) ;get byte from sbuf CP (HL) ;compare to sym table byte RET C ;greater, not in table JP NZ,FIND3 ;less, get next table entry INC DE ;bump pointer INC HL ;bump pointer DEC B ;decrement byte count JP NZ,FIND2 ;loop RET ;true zero, found FIND3: LD HL,(SYM) ;get current pointer LD DE,SSIZ ;symbol table entry size ADD HL,DE ;bump pointer EX DE,HL ;into de LD HL,(SYMTP) ;get top of symbol table CALL CPHL ;test for end of table JP Z,FIND4 ;yes, done JP C,FERR ;table overflow, error EX DE,HL ;current pointer into hl LD (SYM),HL ;set current pointer JP FIND1 ;loop FIND4: SCF ;set carry for not found LD HL,(SYMTP) ;get current top LD (SYM),HL ;set current pointer RET ; FERR: LD DE,EMSG1 ;symbol table err msg FERR1: LD C,PSTRING ;write console CALL CPM ;issue error message JP BOOT ;exit ; FERR2: LD DE,EMSG6 ;no room for symbol table JP FERR1 ; ; add reference to ref table ; ADDRF: LD HL,(SYM) ;get symbol pointer LD DE,SYMSIZ+1 ;offset past symbol&flags ADD HL,DE LD E,(HL) INC HL LD D,(HL) ;get reference pointer LD HL,0000 CALL CPHL ;test for zero ref ptr JP Z,BLDRF ;yes, build reference entry LINK1: EX DE,HL ;ref ptr in hl LD E,(HL) ;get ref link INC HL LD D,(HL) ;into de DEC HL ;reposition hl PUSH HL ;save ref ptr LD HL,0000 CALL CPHL ;if link is zero POP HL JP NZ,LINK1 ;non zero, get next link LD (REF),HL ;save ref pointer INC HL INC HL ;skip to first ref number LD B,REFSZ/2-1 ;number of ref numbers/entry LINK3: LD E,(HL) ;get ref number INC HL LD D,(HL) DEC HL ;reposition PUSH HL ;save ref num addr LD HL,0000 CALL CPHL ;see if ref num is zero POP HL JP Z,ENREF ;yes, enter reference INC HL INC HL ;skip to next ref num DEC B ;decrement count JP NZ,LINK3 ;try again at next slot CALL ADBLK ;add new ref block LD HL,(REF) ;get ref pointer INC HL INC HL ;skip to first ref slot ENREF: PUSH HL ;save ref slot addr LD HL,(LCNT) ;get line number EX DE,HL ;into de POP HL ;get ref slot addr LD (HL),E INC HL LD (HL),D ;store line ref RET ;done ; ; build ref table block ; BLDRF: LD HL,(SYM) ;get symbol pointer LD DE,SYMSIZ+1 ;offset to ref pointer ADD HL,DE LD (REF),HL ;set temp ref pointer to here CALL ADBLK ;add block LD HL,(REF) ;get real ref pointer INC HL INC HL ;position to first ref slot JP ENREF ;add reference ADBLK: LD HL,(REFBT) ;get ref bottom LD DE,REFSZ ;subtract ref size LD A,L SUB E LD L,A LD A,H SBC A,D LD H,A LD (TEMP),HL ;save new ref bottom EX DE,HL ;into de also LD HL,(SYMTP) ;get symbol top CALL CPHL ;check for bump JP Z,FERR2 ;yes, no room JP NC,FERR2 ;no room LD HL,(TEMP) ;get ref bottom EX DE,HL ;into de LD HL,(REF) ;get ref pointer LD (HL),E ;set link INC HL LD (HL),D ;to new ref block LD HL,(TEMP) ;get new ref block addr LD (REF),HL ;store in ref LD B,REFSZ ;size of ref block LD A,00 ADB2: LD (HL),A ;zero the ref block INC HL DEC B JP NZ,ADB2 LD HL,(TEMP) ;get new ref bottom LD (REFBT),HL ;set refbt RET ; ; enter symbol in sym table ; ENSYM: LD HL,(SYM) ;get symbol pointer EX DE,HL ;into de LD HL,(SYMTP) ;get symbol table top CALL CPHL ;check for end of table JP Z,NWSYM ;yes, add symbol at end LD DE,SSIZ ;symbol table entry size ADD HL,DE ;calculate new end of table EX DE,HL ;into de LD HL,(REFBT) ;reference table bottom CALL CPHL ;test for table overflow LD DE,EMSG7 ;addr of err msg in of overflow JP Z,FERR1 ;full, error JP C,FERR1 ;yes, error LD HL,(SYMTP) ;get table top LD DE,SSIZ-1 ;bump to end of entry ADD HL,DE LD (TO),HL ;store in to address LD DE,SSIZ LD A,L SUB E LD L,A LD A,H SBC A,D LD H,A ;subtract size of one entry LD (FROM),HL ;store as from address LD HL,(SYM) ;get current pointer LD (LIMIT),HL ;store as limit address CALL MVUP ;move table up in memory NWSYM: LD HL,(SYM) ;get current pointer LD DE,SBUF ;point to symbol LD B,SYMSIZ ;size of symbol CALL MOVE ;copy symbol to table LD A,0 LD (HL),A INC HL LD (HL),A INC HL LD (HL),A ;set pointers to 0000 LD HL,(SYMTP) ;get symbol table top LD DE,SSIZ ;get symbol entry size ADD HL,DE ;bump LD (SYMTP),HL ;store ew top RET ; ; move symbol table up ; MVUP: LD HL,(TO) ;get to pointer LD B,H LD C,L ;into bc LD HL,(FROM) ;get from pointer EX DE,HL ;into de LD HL,(LIMIT) ;get limit address MVUP2: LD A,(DE) ;get from byte LD (BC),A ;store at to address CALL CPHL ;compare from to limit RET Z ;exit if done DEC BC ;decrement to DEC DE ;decrment from JP MVUP2 ;loop ; ; general purpose move routine ; MOVE: LD A,(DE) ;get byte LD (HL),A ;store byte INC DE INC HL ;bump pointers DEC B ;decrement count JP NZ,MOVE ;loop RET ; ; binary to decimal conversion ; DECOT: LD DE,DEC EX DE,HL LD BC,10000 CALL DIG LD BC,1000 CALL DIG LD BC,100 CALL DIG LD BC,10 CALL DIG LD BC,1 CALL DIG RET ; DIG: LD (HL),'0' DI0: LD A,E SUB C LD E,A LD A,D SBC A,B LD D,A JP M,DI2 INC (HL) JP DI0 DI2: LD A,E ADD A,C LD E,A LD A,D ADC A,B LD D,A INC HL RET ; ; test for alphabetic char. ; CKALP: CP '$' ;treat '$' like a char RET Z CP 'A' ;ascii 'A' RET C ;no, exit CP 'Z'+1 CCF RET ; ; test for numeric char ; CKNUM: CP '0' RET C CP '9'+1 CCF RET ; ; look up char in parse table ; LOOK: LD DE,0003 ;table entry size LD B,A ;argument byte in b LOOK2: LD A,(HL) ;get table byte CP 0FFH ;end of table? JP Z,LOOKN ;yes, not found CP B ;compare JP Z,LOOKY ;found ADD HL,DE ;bump pointer JP LOOK2 ;loop LOOKN: SCF ;carry = not found RET ; LOOKY: INC HL ;skip to table byte LD E,(HL) INC HL LD D,(HL) ;table entry in de EX DE,HL ;into hl RET ; ; save byte in line buffer ; SAVBT: LD HL,(LPNT) ;get line pointer LD (HL),A ;save byte INC HL ;bump pointer LD (LPNT),HL ;save pointer CALL LWRUPR ;convert lower to upper case LD (CHAR),A ;save char in char RET ; ; print source line with number ; PLINE: LD HL,(LCNT) ;get line number CALL DECOT ;convert to decimal LD HL,DEC ;point to dec string PL2: LD E,(HL) ;get string byte LD A,E CP 0DH ;done? JP Z,PL3 ;yes CALL PBYT ;print byte INC HL ;bump pointer JP PL2 PL3: LD E,':' CALL PBYT ;print ':' LD E,' ' CALL PBYT ;print ' ' CALL PBYT ;print space LD HL,PBUF ;point to print buffer LD A,00 LD (COL),A ;set column count PL41: LD E,(HL) ;get byte LD A,E CP 0DH ;done? JP Z,PL5 CP 0AH ;lf? JP Z,PL4A ;yes, ignore CP 09H ;tab? JP NZ,PL42 ;no, continue PUSH HL ;save hl PL43: LD E,' ' CALL PBYT ;print space LD HL,COL INC (HL) LD A,(HL) AND 07H ;modulo 8 JP NZ,PL43 POP HL JP PL4A PL42: LD A,(COL) INC A LD (COL),A CALL PBYT ;print byte PL4A: INC HL JP PL41 PL5: CALL CRLF ;print cr,lf LD HL,PBUF LD (LPNT),HL ;reset line pointer RET ; ; collect symbol in sym buf ; GTSYM: LD B,A ;save char LD A,(SYMCT) ;get symbol count CP SYMSIZ ;max? RET NC ;yes, done INC A LD (SYMCT),A LD HL,(SYMPT) LD (HL),B INC HL ;bump symbol pointer LD (SYMPT),HL RET ; ; printer interfaces ; ; ; print a single byte ; PBYT: PUSH BC PUSH DE PUSH HL PUSH AF LD A,(DISKOUT) ;disk output... OR A ;...in effect? JP NZ,DBYT ;yes, go write to disk LD C,05 LD A,(CONSOLE) ;get console out switch OR A ;console output? JP Z,PBYT2 ;no, print output LD C,2 ;console output ; PBYT2: CALL CPM ; PBYT3: LD C,11 ;check console status CALL CPM OR A ;if zero, ok JP Z,PBYTX ;exit LD C,1 CALL CPM ;read console char CP 3 JP Z,BOOT ;abort if ^c, otherwise ignore PBYTX: POP AF POP HL POP DE POP BC RET ; ; dbyt ; ; put output character ; into disk buffer. ; input=e(output char) ; ; DBYT: LD A,E ;save char to be put in buffer LD HL,(DSKCNT) ;get num of char left in buf EX DE,HL ;put num in d,e LD HL,(DCHAR) ;point to next char in buf LD (HL),A ;put char in disk buf DEC E ;at end of buf? JP NZ,DBYT1 ;no, don't write buffer DEC D ;at end of buf? JP Z,PUTREC ;yes, go write buffer ; DBYT1: INC HL ;point to next char in buf LD (DCHAR),HL ;save next pos EX DE,HL ;put num of char left in h,l LD (DSKCNT),HL ;save num of remaining char JP PBYT3 ;go check console status ; ; putrec ; write out disk buffer to disk ; PUTREC: LD HL,DISKBUF ;address of disk buffer LD (BUFADR),HL ;save it for later dma's LD B,OUTSECT ;number of disk sectors to write ; PUTRC2: PUSH BC ;save sector count LD HL,(BUFADR) ;get disk buffer address EX DE,HL ;put it in d,e for dma LD HL,80H ;put 128 h,l ADD HL,DE ;add 128 to disk buf adr for next dma LD (BUFADR),HL ;save it for next dma LD C,SETDMA ;point dma to... CALL CPM ;...correct buffer LD DE,OFCB ;get output disk fcb LD C,WRITE ;write sector... CALL CPM ;...to disk OR A ;was write successfull? POP BC ;restore num of sectors left LD DE,EMSG4 ;get err msg in case write failed JP NZ,FERR1 ;disk write failed, display err msg, quit DEC B ;written 16 sectors yet? JP NZ,PUTRC2 ;no, continue writing disk sectors LD HL,OUTSECT*128 ;put disk buffer size into h,l LD (DSKCNT),HL ;set num of char left in buf to buffer size LD HL,DISKBUF ;point to front... LD (DCHAR),HL ;...of disk buffer JP PBYT3 ;go check console status for ^c ; ; issue page eject ; EJECT: LD E,0CH ;printer eject command CALL PBYT LD E,00H LD B,10 EJECT2: CALL PBYT ;print 10 nulls DEC B JP NZ,EJECT2 LD A,00 LD (LINES),A ;set line count RET ; ; issue cr, lf & test page ; CRLF: LD E,0DH CALL PBYT LD E,0AH CALL PBYT LD A,(LINES) INC A LD (LINES),A ;increment line count LD A,LPAGE ;get number of lines OR A ;is it zero? LD A,(LINES) RET Z ;yes, return CP LPAGE ;test line count CALL Z,EJECT ;if eq to lpage then new page RET ; ; character parsing table ; CTAB1: DB 0DH DW LCR DB 0AH DW LLF DB '''' DW LQUOT DB ';' DW LSEMI DB ' ' DW LSPC DB 09H DW LTAB DB '$' DW LDOL DB '(' DW LDEL DB ')' DW LDEL DB '+' DW LDEL DB '-' DW LDEL DB '*' DW LSEMI DB '/' DW LDEL DB ',' DW LDEL DB ':' DW LDEL DB EOF DW DONE DB 0FFH DW 0000H EOF EQU 1AH ;eof code ; ; reserved word table ; RTAB: DB '8080 ' DB 'A ' DB 'ACI ' DB 'ADC ' DB 'ADD ' DB 'ADI ' DB 'AF ' DB 'ANA ' DB 'AND ' DB 'ANI ' DB 'ASEG ' DB 'B ' DB 'BC ' DB 'BIT ' DB 'C ' DB 'CALL ' DB 'CC ' DB 'CCF ' DB 'CM ' DB 'CMA ' DB 'CMC ' DB 'CMP ' DB 'CNC ' DB 'CNZ ' DB 'COMME' DB 'COMMO' DB 'COND ' DB 'CP ' DB 'CPD ' DB 'CPDR ' DB 'CPE ' DB 'CPI ' DB 'CPL ' DB 'CPO ' DB 'CSEG ' DB 'CZ ' DB 'D ' DB 'DAA ' DB 'DAD ' DB 'DB ' DB 'DC ' DB 'DCR ' DB 'DCX ' DB 'DE ' DB 'DEC ' DB 'DEFB ' DB 'DEFL ' DB 'DEFM ' DB 'DEFW ' DB 'DI ' DB 'DJNZ ' DB 'DS ' DB 'DSEG ' DB 'DW ' DB 'E ' DB 'EI ' DB 'ELSE ' DB 'END ' DB 'ENDC ' DB 'ENDIF' DB 'ENDM ' DB 'ENTRY' DB 'EQ ' DB 'EQU ' DB 'EX ' DB 'EXX ' DB 'EXITM' DB 'EXT ' DB 'EXTRN' DB 'GE ' DB 'GLOBA' DB 'GT ' DB 'H ' DB 'HL ' DB 'HIGH ' DB 'HALT ' DB 'IF ' DB 'IFB ' DB 'IFDEF' DB 'IFE ' DB 'IFF ' DB 'IFNB ' DB 'IFNDE' DB 'IFT ' DB 'IF1 ' DB 'IF2 ' DB 'IM ' DB 'IN ' DB 'INC ' DB 'INCLU' DB 'IND ' DB 'INDR ' DB 'INIR ' DB 'INR ' DB 'INX ' DB 'IRP ' DB 'IRPC ' DB 'IX ' DB 'IY ' DB 'JC ' DB 'JM ' DB 'JMP ' DB 'JNC ' DB 'JNZ ' DB 'JP ' DB 'JPE ' DB 'JPO ' DB 'JR ' DB 'JZ ' DB 'L ' DB 'LALL ' DB 'LD ' DB 'LDA ' DB 'LDAX ' DB 'LDD ' DB 'LDDR ' DB 'LDI ' DB 'LDIR ' DB 'LE ' DB 'LHLD ' DB 'LIST ' DB 'LOCAL' DB 'LOW ' DB 'LT ' DB 'LXI ' DB 'M ' DB 'MACRO' DB 'MOD ' DB 'MOV ' DB 'MVI ' DB 'NAME ' DB 'NC ' DB 'NE ' DB 'NEG ' DB 'NOP ' DB 'NOT ' DB 'NUL ' DB 'NZ ' DB 'OR ' DB 'ORA ' DB 'ORG ' DB 'ORI ' DB 'OTDR ' DB 'OTIR ' DB 'OUT ' DB 'OUTI ' DB 'OUTD ' DB 'PAGE ' DB 'PCHL ' DB 'P ' DB 'PE ' DB 'PO ' DB 'POP ' DB 'PRINT' DB 'PSW ' DB 'PUBLI' DB 'PUSH ' DB 'RADIX' DB 'RAL ' DB 'RAR ' DB 'RC ' DB 'REPT ' DB 'RES ' DB 'RET ' DB 'RETI ' DB 'RL ' DB 'RLA ' DB 'RLC ' DB 'RLCA ' DB 'RLD ' DB 'RM ' DB 'RNC ' DB 'RNZ ' DB 'RP ' DB 'RPE ' DB 'RPO ' DB 'RR ' DB 'RRA ' DB 'RRC ' DB 'RRCA ' DB 'RRD ' DB 'RST ' DB 'RZ ' DB 'SALL ' DB 'SBB ' DB 'SBC ' DB 'SBI ' DB 'SCF ' DB 'SET ' DB 'SHL ' DB 'SHLD ' DB 'SHR ' DB 'SLA ' DB 'SP ' DB 'SPHL ' DB 'SRA ' DB 'SRL ' DB 'STA ' DB 'STAX ' DB 'STC ' DB 'SUB ' DB 'SUBTT' DB 'SUI ' DB 'TITLE' DB 'XALL ' DB 'XCHG ' DB 'XLIST' DB 'XOR ' DB 'XRA ' DB 'XRI ' DB 'XTHL ' DB 'Z ' DB 'Z80 ' DB 0FFH ;end of reserved word table RSIZ EQU 05 ;size of table entry ; ; miscellaneous data ; MSG1: DB 'DISK CROSS REFERENCE FILE EXISTS - ERASE IT(Y/N)?','$' EMSG0: DB '++ERROR++ INPUT FILE DOES NOT EXIST',0DH, 0AH, '$' EMSG1: DB 'SYMBOL TABLE ERROR',0DH,0AH,'$' EMSG2: DB 'FAILED TO DELETE EXISTING CROSS REFERENCE FILE',0DH,0AH,'$' EMSG3: DB 'FAILED TO CREATE CROSS REFERENCE FILE',0DH,0AH,'$' EMSG4: DB 'DISK WRITE FAILED - OUT OF SPACE?',0DH,0AH,'$' EMSG5: DB 'CLOSE FAILED ON DISK CROSS REFERENCE FILE',0DH,0AH,'$' EMSG6: DB '++ERROR++ NOT ENOUGH MEMORY FOR SYMBOL TABLE',0DH,0AH,'$' EMSG7: DB '++ERROR++ SYMBOL TABLE OVERFLOW',0DH,0AH,'$' LOGO: DB 0DH,0AH,'CPM ASSEMBLER CROSS REFERENCE LIST Ver 2.41',0DH,0AH,'$' DONEMSG:DB 'CROSS REFERENCE LIST IS DONE',0DH,0AH,'$' NOSYMS: DB 'NO SYMBOLS IN THIS PROGRAM' CONCRLF:DB 0DH,0AH,'$' SSIZ EQU SYMSIZ+3 ;symbol table entry size SYMBT: DS 2 ;symbol table bottom address SYMTP: DS 2 ;symbol table top address REFBT: DS 2 ;reference table bottom address REFTP: DS 2 ;reference table top address SYM: DS 2 ;current symbol table address REFSZ EQU 2+(NREFS*2) ;number of bytes in ref block REF: DS 2 ;current reference table address FROM: DS 2 ;move pointer TO: DS 2 ;to pointer LIMIT: DS 2 ;limit pointer COL: DS 1 CHAR: DS 1 LCNT: DS 2 ;line counter LPNT: DS 2 DEC: DS 5 DB 0DH PBUF: DS 132 SYMCT: DS 1 SYMPT: DS 2 SBUF: DS SYMSIZ ;symbol buffer NLINS: DB 0 ;buffers to print on line LINES: DB 0 ;print line count ; ; operating system equates ; BOOT EQU 0000H ;reboot entry point CPM EQU 0005H ;cpm entry point ; MEMSZ EQU 0006H ;end of memory pointer TFCB EQU 005CH ;trans. fcb ; CONIN EQU 01 ;console input function code PSTRING EQU 09 ;print string function code OPEN EQU 15 ;open function code CLOSE EQU 16 ;close file function code DELETE EQU 19 ;delete file function code READ EQU 20 ;read disk function code WRITE EQU 21 ;write disk function code MAKE EQU 22 ;make file function code SETDMA EQU 26 ;set dma function code ; ; f o p e n ; ; routine to open a disk file ; ; ; ; input: de=a(fcb) ; ; FOPEN: LD C,OPEN ;open code CALL CPM ;issue open CP 0FFH ;error? RET NZ ;no error LD DE,EMSG0 JP FERR1 ; ; ; routine to close output disk ; ; cross reference file ; ; input: de=addr(fcb) ; ; hl=addr(buffer) ; ; DCLOSE: PUSH DE ;save fcb address PUSH HL ;save buffer address LD HL,(DSKCNT) ;put number of char left... EX DE,HL ;...in buffer into d,e LD HL,(DCHAR) ;point to next char in output buf LD A,1AH ;put a control z in accum DCLSE2: LD (HL),A ;fill... INC HL ;...rest of... DEC E ;...buffer... JP NZ,DCLSE2 ;...with... DEC D ;...control z's. JP NZ,DCLSE2 POP HL ;point to... ; DCLSE4: EX DE,HL ;...output buffer LD HL,128 ;put 128 into h,l ADD HL,DE ;add 128 to disk buf addr for next dma LD (BUFADR),HL ;save it for next dma after this one LD C,SETDMA ;...disk buffer for... CALL CPM ;...last disk write POP DE ;do last... PUSH DE ;....(d,e contains ofcb)... LD C,WRITE ;...disk... CALL CPM ;...write ; ;check to see if last record has been written. do this by comparing ;address of next available char to address of buffer for next disk write. ;if address of next available char is equal or greater, close file. LD HL,(DCHAR) ;put high order address EX DE,HL ;...of last char+1... LD A,D ;...into accum LD HL,(BUFADR) ;get addr of next disk buffer CP H ;compare to last char+1 in buffer JP C,DCLSE5 ;if next addr is greater, close file JP NZ,DCLSE4 ;if not equal, continue LD A,E ;put low order address of last char+1 CP L ;into accum and test JP NC,DCLSE4 ;if not less, do another write ; DCLSE5: POP DE ;close the...(d,e contains ofcb) LD C,CLOSE ;...output disk CALL CPM ;...cross ref file CP 0FFH ;close successfull? RET NZ ;yes, return LD DE,EMSG5 ;no, display error msg... JP FERR1 ;... and quit ; ; g e t b t ; routine to read a byte ; ; outputs: a=byte ; GETBT: LD HL,TBUF+(INSECT*128) ;end of input buf addr EX DE,HL ;buffer end addr. in de LD HL,(INPTR) ;current pointer in hl CALL CPHL ;test for end of buffer CALL Z,GETREC ;yes, read disk records LD A,(HL) ;get byte INC HL ;bump pointer LD (INPTR),HL ;save pointer OR A ;clear carry RET ;return carry reset ; ; getrec ; GETREC: LD B,INSECT ;number of sectors to read LD HL,TBUF ;get address of disk input buffer LD (IDADDR),HL ;initialize current buffer address ; GETRC2: LD HL,(IDADDR) ;get addr of input buffer EX DE,HL ;put it in d,e LD HL,128 ;add 128 to input buf ADD HL,DE ;...addr for read after next LD (IDADDR),HL ;save it read after next PUSH BC ;save sector count PUSH DE ;save current buf ptr in case eof occurrs LD C,SETDMA ;point to input buffer... CALL CPM LD C,READ ;read code LD DE,TFCB ;fcb address CALL CPM ;issue read POP DE ;restore current buf pointer POP BC ;restore sector count CP 00 ;error? JP NZ,GETRC4 ;yes DEC B ;read all of the sectors? JP NZ,GETRC2 ;no, continue reading ; GETRC3: LD HL,TBUF ;reset buffer pointer LD (INPTR),HL RET ;return to caller ; GETRC4: EX DE,HL ;point to first char past buf LD (HL),26H ;put two... INC HL ;...control z's LD (HL),26H ;...after last rec in file JP GETRC3 ;go reset buffer pointer ; ; converts lower to upper case ; ; input: a=byte(upper/lower; ; outputs: a=byte upper ; ; LWRUPR: CP 'A'+20H ;is it upper case? CCF ;complement carry RET NC ;yes, return with carry reset CP 'Z'+21H ;over lower case 'Z'? RET NC ;no, return no carry SUB 20H ;convert to upper case RET ; ; miscellaneous subroutines ; ; ; c p h l ; ; routine to compare hl vs de ; ; output: hl=de, zero=on ; ; hl<>de, zero=off ; ; CPHL: LD A,H CP D RET NZ LD A,L CP E RET ; ; d a t a ; OFCB: DB 0 ;disk output fcb, default drive DS 8 ;file name DB 'XRF' ;file type DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DCHAR: DW DISKBUF ;pointer for disk buffer BUFADR: DS 2 ;pointer to disk output buffer for dma IDADDR: DS 2 ;pointer to disk input buffer for dma DSKCNT: DW OUTSECT*128 ;number of char left in disk buffer INPTR: DW TBUF+(INSECT*128) ;pointer to char in input buffer DISKBUF:DS OUTSECT*128 ;output disk buffer TBUF: DS INSECT*128 ;disk input buffer EOFCHR: DB 26H,26H ;control z's in case last block is ;filled with data CONSOLE:DB 0 ;console output switch DISKOUT:DB 0 ;disk output switch FTPRN: DB 1 ;prn file type TEMP: DS 2 ;temp save word DS 64 ;stack contains 32 words STACK EQU $ ; ; symbol table area ; ; the symbol table must be the ; last byte before the help message ; ORG $ ;10h + (0fff0h and $) ;force 16 byte boundary SYMT: DB 0FFH ; HELP: DB 0DH,0AH,'This program can be used to' DB ' create a cross reference listing',0DH,0AH DB 'of CPM assembler programs.' DB ' The input can be either the assembler',0DH,0AH DB 'language source code(FN.ASM) or the assembly listing(FN.PRN).',0DH,0AH DB 'The output can be printed on the CPM list device, displayed on',0DH,0AH DB 'the console, or written to disk(file type will be XRF). It is',0DH,0AH DB 'invoked by keying:',0DH,0AH,0AH DB 09H,'XREF FN.ASM (OUTPUT ON LIST DEVICE)',0DH,0AH DB 09H,'XREF FN.PRN CON (OUTPUT ON THE CONSOLE)',0DH,0AH DB 09H,'XREF FN.ASM D (OUTPUT ON DISK-DEFAULT DRIVE)',0DH,0AH DB 09H,'XREF FN.PRN B:D (OUTPUT ON DISK-DRIVE SPECIFIED)',0DH,0AH,'$' ; END XREF