; DIRFILES: Version 3.0(S) ; ; 09/04/84 Modified to write the data file back to disk only if it ; was edited while we were here. Changed to Version 3.0. C. Horn. ; ; A utility that maintains a data file on any default disk or user area ; containing directory file names for all current files. The data file ; is called in and updated each time DIRFILES is run. The data file ; contains a description of each listed file that is entered by the user ; via the EDIT function within DIRFILES. When the program exits it writes ; the updated data file back to disk. No disk space is needed in this ; process other than the space occupied by the data file when first created. ; When first called, if the data file DIRFILES.DAT does not exist, the ; program provides by menu the option to create it. The maximum number of ; directory entries at the EQUate NUMFILES determines the size of the data ; file. 128 is the correct entry for a standard SSSD CP/M disk, and is ; probably appropriate for SSDD and DSSD disks. This setting creates a ; 10K DIRFILES.DAT file. ; ; Version 2.0(S) contains several assembly options and a few improvements ; over the previous. It is set up to optionally work properly on a RCP/M ; by inclusion of the WHEEL byte (ZCPR) as an on-line switch to exclude ; certain functions. When set to zero, the WHEEL prevents the user from ; editing the data file or write it back to disk. When the WHEEL is set ; for full access, the SYSOP has control of all the features. It also ; offers the assembly option of excluding files with the SYS attribute ; set. In this way the SYSOP can hide any selected files from view. ; This option automatically sets the DIRFILES.DAT file to SYS when it ; is created. The SORT routine is another improvement over previous ; versions. ; ; This program is a product of Horn Engineering Associates. It is placed ; into the public domain for free use by anybody except that it may not ; be sold, either in itself or as any part of a collection of software, ; without the express permission of Horn Engineering Associates. It is ; requested that any user retain our SIGNON logo. ; Copyright (c) 1984 by ; Charles E. Horn,PE ; for Horn Engineering Associates ; TRUE EQU 0FFH FALSE EQU NOT TRUE ; SECURE EQU TRUE ;set to TRUE for RCP/M NOSYS EQU TRUE ;set TRUE to exclude SYS files WHEEL EQU 003EH ;address of the WHEEL byte ; ; CP/M Equates ; BOOT EQU 0 ;org zero CP/M BDOS EQU BOOT+5 ;bdos entry DEFDMA EQU 80H ;normal default DMA address TPA EQU BOOT+100H ;ORG address CONIN EQU 1 ;BDOS console input function CONOUT EQU 2 ;console output function PBUF EQU 9 ;print buffer function RDCON EQU 10 ;read console buffer function CONSTAT EQU 11 ;console status function RESDISK EQU 13 ;reset disk system function SELDISK EQU 14 ;select default disk function OPENF EQU 15 ;open file function CLOSEF EQU 16 ;close file function SRCHF EQU 17 ;search for first dir file function SRCHN EQU 18 ;search for next file function DELF EQU 19 ;delete file function READF EQU 20 ;read file function WRITEF EQU 21 ;write file function MAKEF EQU 22 ;make file function GETDISK EQU 25 ;get default disk function SETDMA EQU 26 ;set DMA address function SETATTR EQU 30 ;set file attributes ; ; Misc Equates ; BELL EQU 07H ;bell character BS EQU 08H ;backspace character LF EQU 0AH ;line feed CR EQU 0DH ;carriage return BLANK EQU 20H ;space character ; ; Program Equates ; NUMFILES EQU 128 ;maximum number of directory entries DATLEN EQU NUMFILES * 80 ;size of DIRFILES.DAT data file ;(80 columns per directory entry) NUMRECORDS EQU DATLEN / 128 ;number of records in data file SCREEN EQU 24 ;number of lines per screen COLUMNS EQU 80 ;80 column screen ; ORG TPA ;program runs here ; ; Main program ; START: JMP START1 ;jump over fixed data DATFNAME: DB 'DIRFILESDAT' ;name of data file (here for easy patch ; ---- >-----------< ---- ;filenames must be exactly this long ;..space fill - type must be last 3 DIRFNAME: DB '???????????' ;wild card directory name START1: LXI H,0 ;find CP/M stack pointer DAD SP LXI SP,STACK ;set up our local stack PUSH H ;store CP/Ms SP on ours ; LXI D,SIGNON ;point to signon message MVI C,PBUF ;and show it CALL BDOS ; ; We will only work on the current default drive which by now we know ; has a mounted disk which might not be logged on. We reset the disk ; system here. If the disk is write protected we find out later. ; MVI C,GETDISK ;get the default drive CALL BDOS PUSH PSW ;save it MVI C,RESDISK ;reset the disk system CALL BDOS POP PSW ;recover the default drive MOV E,A ;move to E MVI C,SELDISK ;set default to original CALL BDOS ; ; Now try to open the directory data file. First we need to move ; in the data file name - case it has been changed or patched. ; LXI B,11 ;11 char to move in LXI H,DATFNAME ;point to data file name LXI D,FCB+1 ;point past the drive number CALL MOVE ;move it in ; LXI D,FCB ;point to file control block MVI C,OPENF ;open the file CALL BDOS INR A ;was 0FFH if error JNZ READIN ;if no error, go read it in ; ; If no data file - create it per menu ; IF SECURE LDA WHEEL ;get wheel byte ORA A ;secure? JZ EXIT0 ;exit with no support message ENDIF ;SECURE ; LXI D,NFDMSG ;file not found and query message MVI C,PBUF CALL BDOS ;print it MVI C,CONIN ;get keyboard response CALL BDOS ANI 5FH ;make response UC CPI 'Y' ;if not yes JNZ EXITA ;abort program ; ; We must create the data file. Fill data buffer with blanks. ; LXI H,FILEBUF ;point to data file buffer LXI B,DATLEN ;length of data file FILLOOP: MVI M,BLANK ;insert a space INX H ;bump pointer DCX B ;bump count down MOV A,C ;get count LS ORA B ;'z' set if done JNZ FILLOOP ;loop til done ; ; Create, write, and close the new data file ; LXI D,FCB ;point to FCB MVI C,MAKEF ;create the file in directory CALL BDOS INR A ;was 0FFH if no directory space JZ EXITB ; CALL WRITEFILE JC EXITC ;cy set if disk full LXI D,FCB ;point to FCB MVI C,CLOSEF ;close the file CALL BDOS ; ; Clean up the FCB here to get ready to read in the data file ; READIN: CALL ZFCB ;zero the FCB ; ; Move in the data file name ; LXI B,11 ;11 char to move LXI H,DATFNAME ;point to the data file name LXI D,FCB+1 ;point past drive in FCB CALL MOVE ;move it in ; ; If not to show SYS files we set the file attribute ; IF NOSYS LXI H,FCB+10 ;point to second type byte MOV A,M ;get it ORI 80H ;set the high byte MOV M,A ;put it back ENDIF ;NOSYS ; ; At least - set the R/W attribute - case it has been set R/O ; LXI D,FCB ;point to FCB MVI C,SETATTR ;set attributes CALL BDOS ; ; Open the data file ; LXI D,FCB MVI C,OPENF CALL BDOS ;no error expected - we know it's there ; ; Read it into memory and close it ; CALL READFILE ;read it LXI D,FCB MVI C,CLOSEF ;if it is ever to work with MP/M CALL BDOS ; ; Here we set up the FCB to read the directory, read it, and one by ; one either match an existing entry in the data file or enter the ; new file if not present. ; CALL ZFCB ;zero the FCB LXI B,11 ;11 char in file name LXI H,DIRFNAME ;the wild card file name LXI D,FCB+1 ;point to FCB file name space CALL MOVE ;move the file name in ; LXI D,DEFDMA ;set the CP/M DMA address MVI C,SETDMA CALL BDOS ; ; Now start the directory search. We set the maximum number of files ; we will enter into the data file buffer to prevent any attempt to ; write past the buffer if there are more files in the directory than ; we have designated as NUMFILES. ; MVI B,NUMFILES-1 ;the maximum number to the buffer PUSH B ;save it LXI D,FCB ;find first match MVI C,SRCHF CALL BDOS LXI H,DEFDMA ;point to DMA buffer CALL FINDFN ;find the file name in the DMA space IF NOSYS PUSH H ;save DMA filename pointer LXI D,10 ;index to SYS attribute byte DAD D ;point to it MOV A,M ;get it ANI 80H ;will be zero if not SYS POP H ;restore pointer JNZ MOREFILES ;skip it if SYS ENDIF ;NOSYS ; INX H ;point past drive number CALL ADDOT ;put name into FNBUF with dot ; ; SEARCH looks for the file name in the data file. Returns with HL ; pointing to data file entry or CY set if not found. If not found, ; HL points to first available blank line. ; CALL SEARCH CC ADDNAME ;add name to file if not found MVI M,0FFH ;flag the entry good POP B ;recover maximum files number DCR B ;bump down - we wrote one PUSH B ;resave it MOREFILES: LXI D,FCB ;look for matches for all files MVI C,SRCHN ;look for next CALL BDOS INR A ;was 0FFH if no more files JZ PURGE ;if done - go purge erased files DCR A ;set directory code back to what is was LXI H,DEFDMA ;point to DMA buffer CALL FINDFN ;find the name in the DMA space IF NOSYS PUSH H ;save pointer LXI D,10 ;index to SYS byte DAD D ;point to it MOV A,M ;get it ANI 80H ;zero if not set POP H ;restore pointer JNZ MOREFILES ;skip it ENDIF ;NOSYS ; INX H ;point past drive number in DMA CALL ADDOT ;file name to FNBUF with dot CALL SEARCH CC ADDNAME ;add name to file if not found there MVI M,0FFH ;flag entry good POP B ;recover maximum files count DCR B ;bump down - we wrote one PUSH B ;save max file count JNZ MOREFILES ;find all directory entries ; ; At this point all directory entries are matched. Found files are ; flagged 0FFH. Erased files are still flagged 0. Blank lines are ; flagged blank. We go through and blank all lines containing erased ; files, then go through again and flag all current files 0 for next time. ; PURGE: POP B ;clear stack LXI H,FILEBUF ;point to data file buffer MVI C,NUMFILES ;maximum entry counter PURGE1: MOV A,M ;get an entry flag ORA A ;flag 0 (erased)? JNZ PURGE2 ;if not PUSH H ;save buffer pointer MVI B,COLUMNS ;number of bytes per entry PURLOOP: MVI M,BLANK ;enter a space INX H ;bump pointer DCR B ;bump column counter down JNZ PURLOOP ;fill line with blanks POP H ;recover buffer pointer PURGE2: LXI D,COLUMNS ;index to next entry DAD D ;set pointer to next DCR C ;bump down max files counter JNZ PURGE1 ;continue for all possible entries ; ; Flag all current files zero ; LXI H,FILEBUF ;point to data buffer MVI C,NUMFILES ;max number of entries FLAGLOOP: MOV A,M ;get entry flag CPI 0FFH ;good entry? JNZ NXTFLAG ;if blank line space XRA A ;else - get a zero MOV M,A ;and flag entry good NXTFLAG: LXI D,COLUMNS ;index to next entry DAD D DCR C ;bump down remaining entries to do JNZ FLAGLOOP ;fix all entries ; ; We now have a clean updated buffer that is suitable for display. ; First we will sort all entries aphabetically. This sort ; moves all entries to the beginning of the buffer, with all ; blanked entries following. ; CALL SORT ;sort it ; ; The data file is now sorted and ready for display and edit. ; We enter here with a clean stack and no passed parameters. We ; return here after editing. The only exit from here is via the ; display menu. ; DISPLAY: CALL CRLF ;move to a clean console line LXI H,FILEBUF ;point to data buffer MVI B,NUMFILES ;max entry counter MVI C,SCREEN-1 ;no of display lines less one for menu ; ; The main display loop ; DISPLAY1: MOV A,M ;get status flag ORA A ;zero if valid entry JNZ DISPLAY3 ;if not valid XRA A ;entry is valid - flag at least 1 found STA NONEFLAG PUSH H ;save registers PUSH B MVI A,CR ;get cursor to left margin CALL PCHAR ;print CR POP B ;restore registers POP H PUSH H ;resave registers PUSH B MVI C,15 ;length of file name entry CALL PSTRING ;print the entry MVI C,COLUMNS-15 ;remainder of string space CALL MINSTRG ;go print minimum text (skip blanks) CALL CRLF ;set up for next POP B ;restore registers POP H DCR C ;screen full? JNZ DISPLAY3 ;continue if not DISPLAY2: PUSH H ;save registers PUSH B LXI D,DISMENU ;point to normal display menu IF SECURE LDA WHEEL ;get wheel byte ORA A ;zero if secure JNZ DISPLAY6 ;if not secure LXI D,SECMENU ;else change pointer to secure menu ENDIF ;SECURE ; DISPLAY6: MVI C,PBUF ;print it CALL BDOS MVI C,CONIN ;get the console reply CALL BDOS POP B ;restore registers POP H ANI 5FH ;make assumed ascii UC CPI 'Q' ;quit? JZ DISPLAY4 ;check for secure? CPI 'E' ;edit? JZ DISPLAY5 ;check for secure? MVI C,SCREEN-1 ;reset screen line counter PUSH H ;save pointer PUSH B ;save counters MVI A,CR ;to left of current line CALL PCHAR MVI A,BLANK ;blank the menu MVI C,COLUMNS ;number of blanks per line CALL PCHARS POP B ;restore counters POP H ;restore pointer JMP DISPLAY3 ;continue display ; DISPLAY4: ;process Quit IF SECURE LDA WHEEL ;get wheel byte ORA A ;zero is secure JZ EXIT ;without rewriting the file ENDIF ;SECURE JMP WRITEBACK ;not secure - write the file back ; DISPLAY5: ;process Edit IF SECURE LDA WHEEL ;wheel byte ORA A ;zero is secure JZ DISPLAY3 ;ignore edit - continue display ENDIF ;SECURE JMP EDIT ;else not secure - go edit ; NONEFLAG: DB 0FFH ;default is files present ; ; Pointer and end of buffer processor ; DISPLAY3: LDA CRLFLAG ;are we at end of buffer? ORA A ;not zero if so JNZ DISPLAY7 ;reset CRLFLAG and go to top of buffer LXI D,COLUMNS ;index to next data entry DAD D ;set the pointer in HL DCR B ;end of buffer? JNZ DISPLAY1 ;display more if not LDA NONEFLAG ;get files present flag ORA A ;zero if present JNZ EXITD ;if none to display LXI H,FILEBUF ;else - reset pointer to top MVI B,NUMFILES ;and reset entry counter PUSH H ;save buffer pointer PUSH B ;save counters CALL CRLF ;put in a blank line MVI A,1 ;set the CRLFLAG STA CRLFLAG POP B ;restore registers POP H JMP DISPLAY2 ;show the menu ; DISPLAY7: XRA A ;reset the CRLFLAG STA CRLFLAG JMP DISPLAY1 ; CRLFLAG: DB 0 ;default not flagged ; DISMENU: ;full access menu DB CR,'*** < Q = Quit > *** < E = Edit > *** ' DB '< ANY OTHER = Continue > *** $' ; SECMENU: ;secure menu DB CR,'*** < Q = Quit > *** < ANY OTHER = Continue > *** $' ; ; The file EDIT code ; ; We first set a flag here to note that we have been here. Flag ; is checked at exit. If there has been no edit, we do not write ; the data file back to disk. ; EDIT: MVI A,1 ;set flag to 1 STA EDITFLG MVI A,CR ;move to left margin CALL PCHAR ;..of current console line MVI A,BLANK ;fill line with spaces MVI C,COLUMNS ;number of spaces to print CALL PCHARS ;print them LXI D,EDTMENU1 ;show the primary editor menu MVI C,PBUF CALL BDOS EDIT1: LXI D,EDTMENU2 ;show the secondary editor menu MVI C,PBUF CALL BDOS ; ; Call for the file name to edit ; CALL SCIN$NE ;read console buffer - no CRLF echo LDA CLEFT ;get number of characters entered ORA A ;zero if none entered JZ DISPLAY ;so go back to main menu ; ; This code gets the entered file name into FNBUF in a directory ; format that can be matched to the data file name entry. This code ; is a kludge but it allows for entry of the file name in lower case ; in any reasonable way. ; CALL MAKEUC ;make the buffer alphas upper case LXI H,CLIN ;point to console buffer text LXI D,FNBUF ;point to the processed file name buf MVI B,8 ;8 char spaces for main filename GETENTRY: MOV A,M ;get char from buf ORA A ;end of entry? JZ FNBLANK ;fill out name field with blanks CPI '.' ;dot? JZ FNBLANK STAX D ;else put char in FNBUF INX H ;bump pointers INX D DCR B ;more? JZ PUTDOT ;if not JMP GETENTRY ;else - loop ; FNBLANK: MVI A,BLANK ;get a space STAX D ;put in FNBUF INX D ;bump pointer DCR B ;downcount JZ PUTDOT ;install the dot JMP FNBLANK ;fill out til 8 ; PUTDOT: MVI A,'.' ;get a dot STAX D ;install it INX H ;bump pointers INX D MVI B,3 ;3 char in file type field PUTDOT1: MOV A,M ;get a type char ORA A ;zero if past entry JZ TYPBLANK ;fill field with blanks CPI '.' ;dot? JZ TYPBLANK STAX D ;good char - install it INX H ;bump pointers INX D DCR B ;downcount JZ FINDMAT ;done - go find a match in data buffer JMP PUTDOT1 ;else - finish it ; TYPBLANK: MVI A,BLANK ;fill unused type field with blanks STAX D INX D DCR B JNZ TYPBLANK ; ; FNBUF now filled. This code finds the file entry in the data buffer ; and returns with HL point to the head of the entry line. If CY ; returned the search failed. ; FINDMAT: CALL SEARCH JC FNF ;if file not found PUSH H ;save line pointer MVI C,COLUMNS CALL PSTRING ;show the line on the console MVI A,BS ;backspace to head of text MVI C,COLUMNS-15 ;number to do CALL PCHARS ;do it POP H ;restore line pointer LXI D,15 ;index to start of text DAD D ;set text pointer in HL PUSH H ;save it ; ; We are now ready for editing the text. Existing text will be ; overwritten. Trailing garbage will not be retained. ; CALL SCIN$NE ;read the console buffer POP D ;the text pointer LDA CLEFT ;get number of char entered ORA A ;zero if none entered JZ DISPLAY ;back to main menu LXI H,CLIN ;point to buffer text MVI B,COLUMNS-15 ;we will accept only this many char BUFLOOP: MOV A,M ;get char from con buffer ORA A ;zero if at end JZ FILLOUT ;go fill out the line with blanks STAX D ;else enter the character INX H ;bump pointers INX D DCR B ;countdown JNZ BUFLOOP ;if not done JMP EDIT1 ;max characters in text line ; FILLOUT: MVI A,BLANK ;get a space STAX D ;put in text line INX D ;move to next DCR B ;countdown JNZ FILLOUT JMP EDIT1 ;back to top of EDIT ; FNF: LXI D,FNFMSG ;file not found prompt MVI C,PBUF CALL BDOS JMP EDIT ;back to top of editor ; FNFMSG: DB CR,LF,LF,'File not found...Try again...',CR,LF,'$' ; EDTMENU1: DB CR,LF,'Just a RETURN while in EDIT will go to the main ' DB 'menu with no changes...',CR,LF,'$' EDTMENU2: DB CR,LF,'Enter File Name to EDIT: $' ; ; This is where we write the data file back to disk before we exit. ; Here we will set the existing data file to R/W just be be safe, ; erase it, recreate it, open it, write it, and close it. This way, ; we do not need more disk space than we already use. ; WRITEBACK: LDA EDITFLG ;did we edit while here? ORA A ;zero if we did not JZ RESDMA ;..so don't write a new data file CALL ZFCB ;clean up the FCB LXI B,11 ;11 characters in file name LXI H,DATFNAME ;point to data file name LXI D,FCB+1 ;place to put it CALL MOVE ;move it in ; ; Erase the old data file ; LXI D,FCB MVI C,DELF ;delete file CALL BDOS ; ; Create a new one ; LXI D,FCB MVI C,MAKEF CALL BDOS ; ; Open it ; LXI D,FCB MVI C,OPENF CALL BDOS ; ; Write it ; CALL WRITEFILE ; ; Close it ; LXI D,FCB MVI C,CLOSEF CALL BDOS ; IF NOSYS ;set it to SYS LXI H,FCB+10 ;point to SYS attribute byte MOV A,M ;get it ORI 80H ;set the high bit MOV M,A ;put it back LXI D,FCB MVI C,SETATTR ;set it to SYS CALL BDOS ENDIF ;NOSYS ; ; Reset CP/M DMA ; RESDMA: LXI D,DEFDMA MVI C,SETDMA CALL BDOS ; ; All done ; JMP EXIT ; ;*********************** ;*** EXIT PROCESSORS *** ;*********************** ; EXIT0: LXI D,NSPMSG ;the no support message MVI C,PBUF ;print it CALL BDOS JMP EXIT ; EXITA: LXI D,ABTMSG ;abort message MVI C,PBUF ;print it CALL BDOS JMP EXIT ;and quit ; EXITB: LXI D,NDSMSG ;no disk space message MVI C,PBUF ;print it CALL BDOS JMP EXITA ;and do abort report ; EXITC: LXI D,FULMSG ;disk full message MVI C,PBUF ;print it CALL BDOS JMP EXITA ;and report abort ; EXITD: LXI D,NONMSG ;no files message MVI C,PBUF ;print it CALL BDOS ;fall through to EXIT ; EXIT: POP H ;recover CP/M SP from stack SPHL ;set it RET ;and exit quietly to CP/M ; ;************************ ;*** CONSOLE MESSAGES *** ;************************ ; SIGNON: DB CR,LF,'DIRFILES, Ver 3.0(S), 1984' DB CR,LF,'from Horn Engineering Associates' DB CR,LF,LF,'(Loading data file...)',CR,LF,LF,'$' ; NFDMSG: DB CR,LF,'DIRFILES.DAT file not found...Shall I create it? $' ; ABTMSG: DB CR,LF,LF,'DIRFILES Aborted...$' ; NDSMSG: DB CR,LF,LF,'No Disk Space...$' ; FULMSG: DB CR,LF,LF,'Disk Full...$' ; NONMSG: DB CR,LF,LF,'++ NO FILES ++$' ; NSPMSG: DB CR,LF,LF,'DIRFILES not supported in this area...$' ; ;************************************** ;*** THE PRIVATE FILE CONTROL BLOCK *** ;************************************** ; FCB: FCB$DISK: DB 0 ;preset default drive FCB$NAME: DB 'DIRFILES' ;preset default data file name FCB$TYP DB 'DAT' ;file type FCB$EXTENT: DB 0 ;preset extent FCB$RESV: DB 0,0 ;reserved by CP/M FCB$RECUSED: DB 0 ;records used FCB$ABUSED: DB 0,0,0,0,0,0,0,0 ;assigned blocks DB 0,0,0,0,0,0,0,0 FCB$SEQREC: DB 0 ;sequential record number FCB$RANREC: DW 0 ;random record number FCB$RANRECO: DB 0 ;record overflow ; ;******************* ;*** SUBROUTINES *** ;******************* ; ; WRITEFILE - Writes the records at FILEBUF to default disk ; WRITEFILE: MVI C,NUMRECORDS ;number of records to write LXI D,FILEBUF ;set DMA to data file buffer WRLOOP: PUSH B ;save number of records PUSH D ;save DMA pointer MVI C,SETDMA ;set the DMA address CALL BDOS LXI D,FCB ;point to the FCB MVI C,WRITEF ;write a record CALL BDOS POP D ;clear stack for possible error POP B ORA A ;zero if no error JNZ RETERR ;if error set cy and return LXI H,128 ;add 128 to DMA pointer DAD D ;original pointer in DE XCHG ;new pointer to DE DCR C ;bump down record count JNZ WRLOOP ;do more if not done XRA A ;clear possible cy flag RET ;with no error RETERR: STC ;set cy for error RET ;with error flag set ; ; READFILE - Read the file specified by the FCB into FILEBUF ; READFILE: LXI D,FILEBUF ;point to file buffer RDLOOP: PUSH D ;save DMA pointer MVI C,SETDMA ;set the DMA CALL BDOS LXI D,FCB ;point to FCB MVI C,READF ;read a record CALL BDOS POP D ;recover DMA pointer ORA A ;non-zero is EOF RNZ ;if done LXI H,128 ;index DMA to next record space DAD D XCHG ;pointer to DE JMP RDLOOP ;loop til done ; ; ZFCB - Zero the 36 bytes in the designated FCB ; ZFCB: MVI C,36 LXI H,FCB ;point to FCB ZLOOP: MVI M,0 ;enter a zero INX H ;bump pointer DCR C ;bump down counter JNZ ZLOOP ;loop for more RET ; ; ADDOT - Moves the file name in the DMA area to FNBUF. Adds the ; dot before file type on the way. ; ADDOT: MVI C,8 ;8 char in filename LXI D,FNBUF ;point to name buffer ADDOT1: MOV A,M ;get a character STAX D ;put into buffer INX H ;bump pointer INX D ;bump pointer DCR C ;bump down counter JNZ ADDOT1 ;move all 8 MVI A,'.' ;get a dot STAX D ;put into buffer INX D ;bump buffer pointer MVI C,3 ;3 char in file type ADDOT2: MOV A,M ;get type char STAX D ;to buffer INX H INX D DCR C JNZ ADDOT2 RET ; FNBUF: DS 12 ;the 12 char name buffer ; ; SEARCH - Searches for a match between directory file name in FNBUF ; and entries in the data file. ; If found: Returns with HL pointing to data file entry line. ; If not found: Returns with HL pointing to the first available ; blank line. CY flag is set. ; SEARCH: LXI H,FILEBUF ;point to data file buffer MVI C,NUMFILES ;max number of tries SRCHLOOP: PUSH H ;save buffer pointer PUSH B ;save try counter INX H ;point past status flag LXI D,FNBUF ;point to file name buffer CALL MATCH ;look for match - CY set if none POP B ;recover count POP H ;recover pointer RNC ;if no CY we found match LXI D,COLUMNS ;index to next data line + 1 DAD D ;set it DCR C ;bump down try counter JNZ SRCHLOOP ;if not done ; ; No match found - find first available blank line - RET to caller ; LXI H,FILEBUF ;buffer pointer BLNKLOOP: MOV A,M ;get flag byte CPI BLANK ;is it a space? JZ NOTFND ;return with CY set LXI D,COLUMNS ;index to next flag DAD D ;set it JMP BLNKLOOP ;we'll find one somewhere NOTFND: STC RET ; ; MATCH - Checks for 12 char match between (HL) and (DE). Returns ; with CY set if no match. ; MATCH: MVI C,12 ;12 char to check MATLOOP: LDAX D ;get char from (DE) CMP M ;compare with (HL) JNZ NOMATCH ;any failure will cause exit with CY INX H INX D DCR C JNZ MATLOOP ;go for all 12 XRA A ;reset CY flag (if set) RET ;got a match NOMATCH: STC ;set no match flag RET ; ; ADDNAME - Entry with HL pointing to head of a blank line. Adds ; the file name in FNBUF to the data buffer, adds a trailing ; colon, and returns with HL pointing to head of line. ; ADDNAME: PUSH H ;save line pointer INX H ;point past status flag XCHG ;put pointer in DE LXI B,12 ;12 char to move LXI H,FNBUF ;point to new file name CALL MOVE ;move it in MVI A,':' ;get a colon STAX D ;add at end of filename POP H ;restore line pointer RET ; ; FINDFN - Locates directory file name in DMA after directory ; search. Returns pointer in HL. Entry with directory code in A. ; FINDFN: ADD A ;*2 ADD A ;*4 ADD A ;*8 ADD A ;*16 ADD A ;*32 MOV E,A ;offset to DE MVI D,0 DAD D ;add to HL RET ; ; MOVE - Moves the number of characters in BC from (HL) to (DE) ; MOVE: MOV A,M ;get char from (HL) STAX D ;put in (DE) INX H INX D DCX B MOV A,C ;get LS count byte ORA B ;both B & C zero? JNZ MOVE ;loop til done RET ; ; PSTRING - Prints string pointed to by HL. ; Number of characters to print in C ; PSTRING: PUSH H ;save string pointer PUSH B ;save count MOV E,M ;get a character MVI C,CONOUT ;print it CALL BDOS POP B ;restore count POP H ;restore pointer INX H ;bump it DCR C ;done? JNZ PSTRING ;if not RET ; ; MINSTRG - Types a string until either 3 blanks in a row or ; the maximum number of characters is reached. ; HL = string pointer ; C = max number of characters in string ; MINSTRG: MVI E,3 ;set blank counter MINSTRG1: MOV A,M ;get a character CPI BLANK ;space? JNZ MINSTRG2 ;no space DCR E ;bump down counter RZ ;exit if 3 spaces in row ; MINSTRG2: PUSH H ;save registers PUSH D PUSH B PUSH PSW MOV E,A ;char to E MVI C,CONOUT ;out to console CALL BDOS POP PSW POP B ;restore registers POP D POP H INX H ;bump pointer DCR C ;downcount characters RZ ;if maximum CPI BLANK ;was it a blank? JZ MINSTRG1 ;yes - don't reset blank counter JMP MINSTRG ;else - loop back to top ; ; ; CRLF - Puts a CR and LF out to console ; CRLF: MVI E,CR MVI C,CONOUT CALL BDOS MVI E,LF MVI C,CONOUT CALL BDOS RET ; ; PCHAR - Prints the character in A ; PCHAR: MOV E,A MVI C,CONOUT CALL BDOS RET ; ; PCHARS - Prints the character in A the number of times in C ; PCHARS: PUSH PSW ;save character PUSH B ;save count MOV E,A ;char to E MVI C,CONOUT ;print one CALL BDOS POP B ;restore count POP PSW ;restore character DCR C ;bump down count JNZ PCHARS ;do some more RET ; ; MAKEUC - Makes the alpha characters in the console buffer upper case ; MAKEUC: LXI H,CLIN ;point to the buffer MAKEUC1: MOV A,M ;get a char ORA A ;zero if past end RZ CPI 'A' ;< 'A'? JC MAKEUC2 ;if not alpha CPI 'z'+1 ;> 'z'? JNC MAKEUC2 ;if not alpha ANI 5FH ;is alpha - make UC MOV M,A ;and put it back INX H ;point to next JMP MAKEUC1 ;and loop ; MAKEUC2: INX H ;not alpha - skip it JMP MAKEUC1 ; ; SCIN$NE - Reads the console buffer without a CRLF echo to console ; SCIN$NE: LXI H,CLIN ;point to buffer string space LDA CBUF ;get buffer length MOV B,A ;put into B XRA A ;get a zero SCIN$NE1: MOV M,A ;move in a zero DCR B ;countdown INX H ;bump pointer JNZ SCIN$NE1 ;fill whole buffer with zeros ; LXI D,CBUF ;point to buffer MVI C,RDCON ;read console function CALL BDOS RET ; ; The console buffer ; CBUF: DB CLEN ;buffer length CLEFT: DB 0 ;number of char in buffer CLIN: DS 128 ;number of buffer spaces CLEN: EQU $-CLIN ;buffer length ; ; SORT - Main sort routine - sorts directory entries alphabetically ; Records handled in pairs. ; String1 = first position pointer ; String2 = second position pointer ; Entry: none ; Exit: entries sorted ; SORT: LXI H,FILEBUF ;point to data buffer first entry LXI B,NUMFILES-1 ;max number of entry pairs SORT1: PUSH B ;save counter PUSH H ;save string1 LXI D,COLUMNS ;offset to string2 DAD D ;string2 to HL XCHG ;string2 to DE POP H ;string1 to HL PUSH H ;save string1 PUSH D ;save string2 MVI C,13 ;length of search string CALL COMP ;CY set if (string2) < (string1) POP D ;restore string2 POP H ;restore string1 PUSH D ;resave string2 CC SWAP ;swap string if CY POP H ;new string1 pointer POP B ;get counter DCX B ;bump it down MOV A,C ORA B ;zero if at bottom JZ BOTTOM JMP SORT1 ;go do next pair ; BOTTOM: LDA SWAPFLG ;is 1 if anything was swapped DCR A STA SWAPFLG ;assume was 1 - reset to zero JZ SORT ;was 1? - sort entire buffer again RET ;was 0 - sort done ; ; SWAP - Swaps record string pair. ; Entry: HL points to string1 ; DE points to string2 ; Process: ; (1) buffer <--- string2 ; (2) string2 <--- string1 ; (3) string1 <--- buffer ; SWAP: PUSH H ;save string1 ptr PUSH D ;save string2 ptr XCHG ;string2 to HL LXI D,SWAPBUF ;point to buffer LXI B,COLUMNS ;size of record CALL MOVE ;move string2 to buffer POP D ;restore string2 POP H ;restore string1 PUSH H ;save string1 LXI B,COLUMNS ;size of record CALL MOVE ;move string1 to string2 position POP D ;string1 to DE LXI H,SWAPBUF ;point HL to buffer LXI B,COLUMNS ;size of record CALL MOVE ;buffer to string1 position MVI A,1 ;flag that a swap was done STA SWAPFLG RET ; SWAPFLG: DB 0 ;default no swap ; ; COMP - Compares two strings. ; Entry: HL points to string1 ; DE points to string2 ; C = maximum length of search ; Exit: Z set if strings match ; CY set if string2 < string1 (flags swap) ; No CY if (1) string1 < string2 ; or (2) string1 = string2 (all blanks - no swap) ; COMP: LDAX D ;get byte from string2 CMP M ;CY if string2 byte < string1 byte RNZ ;if no match INX H ;bump pointers INX D DCR C ;downcount JNZ COMP ;check next RET ;strings match - no CY ; EDITFLG: DB 0 ;default edit never used ; ;************************ ;*** UNITIALIZED AREA *** ;************************ ; SWAPBUF: DS COLUMNS ;string swap buffer DS 64 ;our private stack area STACK: EQU $ ; FILEBUF: DS DATLEN ;the data file buffer ; END