; BE SURE TO CHECK ZCPR3 EQUATE BELOW BEFORE ASSEMBLING ; ; ******************************************** ; * * ; * MAKE USER NUMBER CHANGE * ; * * ; * SYNTAX: MAKE D:AFN O (ALL) * ; * MAKE DU:AFN O (ZCPR3) * ; * MAKE DIR:AFN O (ZCPR3) * ; * * ; * where the drive, drive/user, and * ; * named directory forms (where * ; * appropriate) are optional * ; * * ; * and * ; * * ; * where O is one of the options: * ; * * ; * DIR: new DIR for files (ZCPR3) * ; * nn new user # for files * ; * R set files to R/O * ; * W set files to R/W * ; * S set files to SYS * ; * D set files to DIR * ; * A set files to ARCHIVE * ; * N set files to Non-ARCHIVE * ; * E erase files * ; * U unerase files * ; * blank show status of files * ; * * ; * for built-in help screen enter * ; * MAKE // * ; * * ; ******************************************** ; This primary purpose of this program is to allow one to change the user ; number of designated files on a designated drive to a different user number. ; This is carried out without copying the files and then deleting the original ; files; rather, the user number tag in the disk directory is simply changed ; to reflect the new user number. The program also has options to set files ; attributes of read-only, read-write, system, and directory. Files can also ; be erased or unerased. ; The code assumes that the track and sector are set by the BDOS search ; functions and uses a simple BIOS write to update the directory. Before using ; this program, test it on a garbage disk to make sure that it works with your ; system. ; VERSION 1.0 - 07/05/81 R.E.D. ; 1.1 - 07/15/81 R.E.D. ; ; 1.2 - 8/1/82 ; ENHANCED FILE NAME DISPLAY. ; KEN LOVETT ; 1.3 - 8/6/83 ; FIXED BUG WHICH CAUSED LAST CHAR IN FILE SPEC TO ; BE NON-SIGNIFICANT. ; KEN LOVETT ; 2.0 - 8/7/83 ; ADDED OPTIONS TO SET R/O, R/W, SYS, DIR FLAGS. ALSO ; ERASE AND UNERASE FILES. ; KEN LOVETT ; ; 2.1 - 6/20/85 ; Added (actually, just enabled - it was always there) ; full 32 (0-31) area capability, extended display routine ; to handle the higher user numbers, added a four-byte kludge ; of a display fix required by late-model Kaypros and merely ; harmless otherwise, other minor display stuff. ; Bruce Morgen ; ; 2.2 - 07/02/85 ; Major cleanup of the code (which was highly unstructured and ; thus hard to read). Added check on user number of files so ; that the code will act only on files in the logged in area. ; There is still no checking to make sure that there is not ; already a file in the destination user area of the same name ; as the file that is being moved in. ; Jay Sage ; ; 2.3 - 07/14/85 ; In ZCPR3 systems the CCP processes the DU: and named directory ; forms of directory specification. The drive value is returned ; in the usual way in the FCB, and the user number is placed in ; the S1 byte. Version 22Z of MAKE allows the syntax: ; ; MAKE DIR:AFN OPTION ; ; The files to be operated on can be specified using the full ; ZCPR3 format. The option can even take the DIR: form with a ; named directory. The ZCPR3 equate determines whether a ; ZCPR3 version or a standard version will be assembled. ; For some reason, even though there is no change in allocation, ; performing any write to the directory sets the disk to R/O ; status. I have changed the code I originally put it to ; reset the disk system whenever any write has occurred. I had ; not noticed this problem because I use the ZRDOS replacement ; for the BDOS, and it automatically relogs changed disks. ; Jay Sage ; ; 2.4 - 06/15/86 ; Added ability to SET or RESET the ARCHIVE bit, and added ; the WHEEL conditional, which makes MAKE disappear ; (echoes: MAKE?) unless the wheel byte is set. ; Michael Conley ; ; 2.5 - (an obsolete version # observed in the Chicago area dated ; 1984 - skipped to avoid confusion. ; ; 2.6 - 07/22/86 ; Added "All users" switch for any options not involving a ; user number. A number sign after the selected option ; makes it operate on ALL user areas, not just the current ; one. (e.g., MAKE *.BAK E# -- would erase all .BAK files ; in all user areas.) ; Michael Conley ; ;============================================================================= ; P R O G R A M E Q U A T E S ;============================================================================= VERSION EQU 26 FALSE EQU 0 TRUE EQU NOT FALSE ZCPR3 EQU FALSE ;true for ZCPR3 version, false for general CP/M WHEEL EQU TRUE ;true to test wheel byte before running WHLADR EQU 3EH ;wheel byte location BDOS EQU 0005H ;BDOS entry address DMAADDR EQU 0080H ;default CP/M file buffer FCB EQU 005CH ;default CP/M FCB TFCB EQU 006CH ;temporary FCB Z3ENV EQU 00000H ;ZCPR3 environment address filled in by Z3INS CR EQU 0DH ;(z3env not presently needed anyway) LF EQU 0AH TAB EQU 09H BELL EQU 07H ; ; ; ;============================================================================= ; M A I N P R O G R A M C O D E ;----------------------------------------------------------------------------- ORG 0100H MAKE: IF ZCPR3 ;ZCPR3 initialization (not actually needed here, but if one ;runs the install program it will not report a bad utility) JMP START DB 'Z3ENV' DB 1 ;external enviroment DW Z3ENV ;environment address (value is not used here) START: ENDIF ;ZCPR3 ;set up local stack pointer LXI H,0 ;get CP/M stack pointer DAD SP SHLD OLDSTK ;save it for later return LXI SP,NEWSTK ;set up new local stack ;perform setup tasks IF WHEEL LDA WHLADR ;get wheel byte ORA A ;is it set? JZ EREXIT ;no/don't run ENDIF ;wheel CALL SIGNON ;print signon message CALL CHKHLP ;see if help requested and go there if so IF NOT ZCPR3 ;if ZCPR2, we assume correct version of CP/M CALL CHKVER ;abort if not CP/M version 2.0 or later ENDIF ;NOT ZCPR3 CALL INIT ;set up data areas CALL SETDU ;handle current and specified DU areas CALL GETOPT ;get option from command line (abort if bad) CALL CHKRO ;abort if destination drive is R/O and option ;..requests change in files ;begin main work of program CALL SRCHF ;locate first directory entry (abort if none) LOOP: CALL SETPTR ;set DMAPTR to point to disk directory FCB CALL CHKFIL ;check for applicable file CNC CALLOPT ;if applicable file, process option NEXTFIL: ;go on to next file LXI H,DIRCODE ;point to the directory code INR M ;increase it one MOV A,M ;get new value CPI 04 ;check for four FCB entries completed JNZ LOOP ;if not, go back and continue ;process this group of four files and go on to next LXI H,CHGFLAG ;point to change flag MOV A,M ;get it into A MVI M,0 ;reset it ORA A ;set flags from original value CNZ WRTDE ;if changes were made, write the buffer back ;sequence through files to get new buffer-full of FCB's SRNXT: LXI D,AMBFIL ;point to any-match FCB MVI C,12H ;BDOS search-next function CALL BDOS CPI 0FFH ;see if end of entries JZ QUIT ;quit if no more files CPI 0 ;loop until buffer is updated by BDOS JNZ SRNXT ;jump until dircode is zero STA DIRCODE ;save the 0 in dircode JMP LOOP ; and loop again ;============================================================================= ; O P T I O N P R O C E S S I N G R O U T I N E S ;----------------------------------------------------------------------------- ; Set the file attribute to SYS. If the file was not already SYS, then the ; CHGFLAG is set to indicate the need later to write the modified sector out ; to disk. SETSYS: LHLD DMAPTR ;point to disk directory FCB entry LXI D,10 ;offset 10 to DIR/SYS byte DAD D MOV A,M ;get the SYS/DIR byte ORA A ;test current state of SYS bit CP SETCHGFL ;if not already SYS, set the change flag ORI 80H ;make sure it is set MOV M,A ;write modified byte back out CALL REPORT ;report the new file status RET ;----------------------------------------------------------------------------- ; Set the file attribute to DIR. If the file was not already DIR, then the ; CHGFLAG is set to indicate the need later to write the modified sector out ; to disk. SETDIR: LHLD DMAPTR ;point to disk directory FCB entry LXI D,10 ;offset 10 to SYS/DIR byte DAD D MOV A,M ORA A ;sign flag shows state of SYS bit CM SETCHGFL ;if not already DIR, set change flag ANI 7FH ;clear the DIR bit MOV M,A ;write modified byte back to buffer CALL REPORT ;report the new file status RET ;----------------------------------------------------------------------------- ; Set the file attribute to R/O. If the file was not already R/O, then the ; CHGFLAG is set to indicate the need later to write the modified sector out ; to disk. SETRO: LHLD DMAPTR ;point to disk directory FCB entry LXI D,9 ;offset 9 to R/O-R/W byte DAD D MOV A,M ORA A ;test current state of R/O bit CP SETCHGFL ;if not already R/O, set the change flag ORI 80H ;make sure it is set MOV M,A ;write modified byte back out CALL REPORT ;report the new file status RET ;----------------------------------------------------------------------------- ; Set the file attribute to R/W. If the file was not already R/W, then the ; CHGFLAG is set to indicate the need later to write the modified sector out ; to disk. SETRW: LHLD DMAPTR ;point to disk directory FCB entry LXI D,9 ;offset 9 to R/O-R/W byte DAD D MOV A,M ORA A ;sign flag shows state of R/O-R/W bit CM SETCHGFL ;if not already R/W, set change flag ANI 7FH ;clear the R/O bit MOV M,A ;write modified byte back to buffer CALL REPORT ;report the new file status RET ;----------------------------------------------------------------------------- ; Set the file attribute to ARC. If the file was not already ARC, then the ; CHGFLAG is set to indicate the need later to write the modified sector out ; to disk. SETARC: LHLD DMAPTR ;point to disk directory FCB entry LXI D,11 ;offset 10 to ARCHIVE byte DAD D MOV A,M ;get the ARCHIVE byte ORA A ;test current state of ARC bit CP SETCHGFL ;if not already ARC, set the change flag ORI 80H ;make sure it is set MOV M,A ;write modified byte back out CALL REPORT ;report the new file status RET ;----------------------------------------------------------------------------- ; RESET the ARCHIVE file attribute. If the file was already ARC, then the ; CHGFLAG is set to indicate the need later to write the modified sector out ; to disk. SETNRC: LHLD DMAPTR ;point to disk directory FCB entry LXI D,11 ;offset 10 to ARCHIVE byte DAD D MOV A,M ORA A ;sign flag shows state of ARC bit CM SETCHGFL ;if it was ARC, set change flag ANI 7FH ;clear the ARC bit MOV M,A ;write modified byte back to buffer CALL REPORT ;report the new file status RET ;----------------------------------------------------------------------------- ; Erase the file by writing E5 as the user number tag. We know that the file ; is not already erased because of the work of subroutine CHKFIL earlier. ; Therefore, we must set the change flag to show the need to write the sector ; back out to disk. ERASE: LHLD DMAPTR MVI M,0E5H CALL SETCHGFL ;show need to write sector back to disk CALL REPORT ;report the new file status RET ;----------------------------------------------------------------------------- ; Unerase the file by writing the current user number into the tag byte in ; place of the E5. The comments under ERASE apply here, too. UNERA: LDA DEFUSR ;get logged in user number LHLD DMAPTR MOV M,A ;put it into disk directory user # tag CALL SETCHGFL ;show need to write sector back to disk CALL REPORT ;report the new file status RET ;----------------------------------------------------------------------------- ; Change the user number of the file. CHUSER: LDA OPTION ;get user number LHLD DMAPTR ;point to place to put it MOV M,A ;put new user number in directory CALL SETCHGFL ;show need to write sector back out to disk CALL REPORT ;report the new file status RET ;----------------------------------------------------------------------------- ; This code reports the attributes of the files acted on and, if required, ; the erased or unerased status. This code is called directly by the blank ; option and indirectly by all the other option processors after they have ; finished performing their changes on the files. REPORT: CALL PRTFN ;print the file name CALL ILPRT ;print spacer and equal sign DB ' = ',0 CALL PRTOPT ;print the option letter or number MVI C,3 ;put in three more blank spaces CALL PRTBLK CALL PRTATTR ;print file attributes LDA OPTION ;see if files erased CPI 'E' CZ PRTERA ;if so, print erased message LDA OPTION CPI 'U' ;see if files unerased CZ PRTUNE ;if so, print unerased message RET ;============================================================================= ; P R O G R A M F L O W R O U T I N E S ;----------------------------------------------------------------------------- ; CALLOPT ; This routine uses the value of OPTION to look up the processing routine ; to which to branch. CALLOPT: LXI H,JMPTBL ;point to jump table LDA OPTION ;get user number or option letter MOV B,A ;save it in B LOOKUP: MOV A,M ;get option letter from table INX H ;point to jump address ORA A ;end of table? JZ JMPOPT2 ;if so, jump CMP B ;do we match a table entry JZ JMPOPT1 ;if so, go to code to get jump address INX H ;else jump over jump address to INX H ;..next option character JMP LOOKUP ;and try again JMPOPT1: MOV A,M ;get low part of jump address into A INX H ;point to high part of address MOV H,M ;get it into H MOV L,A ;jump address is in HL PCHL ;jump to it JMPOPT2: LXI H,CHUSER ;default to change user routine PCHL ;jump to it JMPTBL: DB ' ' DW REPORT DB 'S' DW SETSYS DB 'D' DW SETDIR DB 'R' DW SETRO DB 'W' DW SETRW DB 'A' DW SETARC DB 'N' DW SETNRC DB 'E' DW ERASE DB 'U' DW UNERA DB 0 ;end of table mark ;----------------------------------------------------------------------------- ; QUIT ; This code sends a CRLF to the console, restores the original CP/M stack, ; and returns to CP/M. If indicated by the reset flag, it resets the disk ; system. The logged DU area when the program was invoked is restored. The ; entry point QUIT2 is used by code (CHKHLP and CHKVER) that have not changed ; the logged in directory. QUIT: CALL CRLF ;log in the original drive/user LDA DEFDRV ;get original default drive CALL LOGDRV ;log it in LDA DEFUSR ;get original default user CALL LOGUSR ;log it in ;see if we need to reset the disk system QUIT1: LDA RSTFLAG ORA A ;if reset flag is clear JZ QUIT2 ;..we can skip disk system reset ;reset the disk system MVI C,0DH CALL BDOS ;restore the original stack QUIT2: LHLD OLDSTK ;get original stack pointer SPHL ;set it up RET ;back to CP/M IF WHEEL EREXIT: ;if using wheel byte and wheel not set CALL ILPRT ;we pretend the program doesn't exist DB 'MAKE?',0 ;by printing phony CCP error. JMP QUIT2 ENDIF ;wheel ;============================================================================= ; F I L E S E L E C T I O N S U B R O U T I N E S ;----------------------------------------------------------------------------- ; CHKFIL ; This subroutine checks to see whether or not the FCB pointed to in the ; DMA buffer is one that should be acted on. If not, the routine returns ; with the zero flag set. CHKFIL: CALL CHKUN ;check user number and erased status of file RC ;return with carry set to skip file CALL CHKFN ;next check the file name for a match RET ;return showing status of name match ;----------------------------------------------------------------------------- ; CHKUN ; This subroutine checks the user number status of a file and sets the carry ; flag if the file should be skipped over. If the option is UNERASE and the ; file is not an erased file, it should be skipped. If the option is not ; unerase and the file is an erased file, then likewise it should be skipped. ; Finally, if the file is in a user number other than the logged in user, it ; should also be skipped. CHKUN: ;test file erased status LHLD DMAPTR ;point to user number tag of file MOV A,M ;get the tag CPI 0E5H ;carry flag set if not erased PUSH PSW ;save flag ;test program option status LDA OPTION ;get the option CPI 'U' ;is it unerase? JNZ CHKUN1 ;if not, skip to CHKUN1 ;case of unerase option POP PSW ;carry flag set if not erased RET ;file not erased will be skipped ;case of option other than unerase CHKUN1: POP PSW CMC ;carry flag set if file erased RC ;erased file will be skipped ;now check for user number in source area LDA ALLUSR ;see if "all users" was requested CPI '#' ; JZ CHKUN2 ;if so, skip over compare... LDA SRCUSR ;get source user number CMP M ;compare to file tag RZ ;if OK, return (carry is clear) STC ;else set carry CHKUN2: RET ;..and return ;----------------------------------------------------------------------------- ; CHKFN ; This subroutine compares the name of the file in the FCB in the DMA buffer ; with the specification from the command line. CHKFN: ;set up pointers and character count LHLD DMAPTR ;get pointer to FCB in DMA buffer INX H ;point to first character in the name LXI D,FCB+1 ;set DE to name in FCB from command line MVI C,0BH ;load count for compare CP1: LDAX D ;get fcb command line character CPI '?' ;see if anything matches JZ MATCH ;if it is '?', consider it a match SUB M ;get difference (see next instruction) ANI 7FH ;clear attribute bit JZ MATCH ;if zero, characters match STC ;else set carry RET ;..and return MATCH: INX D ;point to next characters INX H DCR C ;decrease count of characters JNZ CP1 ;loop until zero RET ;carry is clear showing names match ;============================================================================= ; D I S K O P E R A T I O N S U B R O U T I N E S ;----------------------------------------------------------------------------- ; SRCHF ; This subroutine uses the fully ambiguous file spec at AMBFIL to locate the ; first directory entry on the disk. The directory code (0-3) is saved. If ; no directory entry is found, then the program gives a message and branches ; to QUIT for a prompt return. SRCHF: LXI D,AMBFIL ;point to match any filename.typ MVI C,11H ;bdos search first function CALL BDOS ;do it STA DIRCODE ;save directory code CPI 0FFH ;see if end of entries RNZ ;if something found, return CALL ILPRT ;else give a message DB BELL,CR,LF DB 'No Files On Disk',CR,LF,0 JMP QUIT ;----------------------------------------------------------------------------- ; WRTDE ; This routine writes the directory buffer back to the disk. I also sets the ; reset flag so that the disk system will be reset on program termination. WRTDE: LXI H,RSTFLAG ;point to the flag MVI M,0FFH ;set the flag MVI C,1H ;set BIOS write to directory ; C = 0 write to allocated ; C = 1 write to directory ; C = 2 write to unallocated CALL WRITE ;do the write ORA A ;check for error RZ ;if none, return CALL ILPRT DB BELL DB CR,LF DB 'Bad Sector Write Error',CR,LF,0 JMP QUIT ;----------------------------------------------------------------------------- ; WRITE ; This routine is filled in during the operation of the program and performs ; a direct BIOS sector write operation on the currently selected track and ; sector. WRITE: JMP 0000 ;vector to bios write routine ;============================================================================= ; P R I N T I N G R O U T I N E S ;----------------------------------------------------------------------------- ; PRTFN ; This subroutine displays (at the beginning of the next line of the screen) ; the name of the file pointed to in the DMA buffer. PRTFN: CALL CRLF ;print cr/lf LHLD DMAPTR ;address of file fcb INX H ;skip to file name LXI D,BLNKCNT ;point to blanks counter XCHG ;exchange pointers MVI M,0 ;preset blank count to zero MVI C,8 ;length of file name CALL PRTSTR ;print the name first MVI A,'.' ;print the period CALL CHAROUT MVI C,3 ;now print the file type CALL PRTSTR MOV C,M ;get number of blanks needed for fill CALL PRTBLK ;print the blanks RET ;----------------------------------------------------------------------------- ; PRTOPT ; This subroutine prints out the option as a letter or as a number as ; appropriate. PRTOPT: LDA OPTION ;get the option value CPI 20H ;if it's a user number, carry will be set JNC CHAROUT ;if not number, just print the character MVI B,'0'-1 ;preset for two-digit calculation later CPI 10 ;see if single digit JNC TWODIG ;if not, print two digits ADI '0' ;else convert to ASCII JMP CHAROUT ;and print it TWODIG: INR B ;count tens digit in B SUI 10 ;keep subtracting 10 until carry is set JNC TWODIG ADI 10 ;get remainder (units digit) back MOV C,A ;save it in C MOV A,B ;print tens digit CALL CHAROUT MOV A,C ;print units digit ADI '0' JMP CHAROUT ;----------------------------------------------------------------------------- ; PRTATTR ; This subroutine prints the attribute status (SYS or DIR and R/O or R/W) ; of the file currently being worked on. PRTATTR: LHLD DMAPTR ;point to file FCB LXI D,9 ;offset to R/O-R/W byte DAD D PUSH H ;save pointer for reuse below MOV A,M RAL ;move R/O bit into carry PUSH PSW ;save flags CC PRTRO ;if carry, print read-only POP PSW ;get flags back to test again CNC PRTRW ;if not carry, print read-write POP H ;get pointer back INX H ;point to SYS/DIR byte PUSH H ;save pointer for reuse below MOV A,M RAL ;move SYS/DIR bit into carry PUSH PSW ;save flags CC PRTSYS ;if carry, print SYS POP PSW ;get them back CNC PRTDIR ;if not carry, print DIR POP H INX H MOV A,M RAL ;move ARCHIVE bit into carry PUSH PSW ;save flags CC PRTARC ;if carry, print ARC POP PSW ;get them back CNC PRTNRC ;if not carry, print Non-ARC RET ;----------------------------------------------------------------------------- ; MESSAGE PRINTING ROUTINES PRTRO: CALL ILPRT ;file is read-only DB ' R/O',0 RET PRTRW: CALL ILPRT ;file is read-write DB ' R/W',0 RET PRTSYS: CALL ILPRT ;file has SYS attribute DB ' SYS',0 RET PRTDIR: CALL ILPRT ;file has DIR attribute DB ' DIR',0 RET PRTARC: CALL ILPRT ;file has ARC attribute DB ' ARCHIVE',0 RET PRTNRC: CALL ILPRT ;file has NO ARC attribute DB ' Non-ARC',0 RET PRTERA: CALL ILPRT ;file erased DB ' *** ERASED ***',0 RET PRTUNE: CALL ILPRT ;file unerased DB ' *** UNERASED ***',0 RET ;----------------------------------------------------------------------------- ; HELP ; This code displays the built in help screen and then jumps to QUIT to ; return to CP/M. HELP: CALL ILPRT DB CR,LF DB TAB,'Syntax: ' IF ZCPR3 DB 'MAKE [DIR:]AMBIG.FIL X[#]',CR,LF,LF DB '* where DIR: is an optional directory specifier',CR,LF DB ' using either the DU: or named directory format',CR,LF,LF ENDIF ;ZCPR3 IF NOT ZCPR3 DB 'MAKE [D:]AMBIG.FIL X[#]',CR,LF,LF DB '* where D: is an optional drive specifier',CR,LF,LF ENDIF ;NOT ZCPR3 DB '* and where `X'' may be ONE of the options:',CR,LF,LF IF ZCPR3 DB TAB,'DIR:',TAB,'Moves files to named directory DIR',CR,LF ENDIF ;ZCPR3 DB TAB,'nn',TAB,'Moves files to user # nn (0-31)',CR,LF,LF DB 'The following operate in the current user area.',CR,LF DB 'Add a # to act on ALL user areas at once.',CR,LF,LF DB TAB,'S',TAB,'Sets file attribute(s) to SYSTEM',CR,LF DB TAB,'D',TAB,'Sets file attribute(s) to DIRECTORY',CR,LF DB TAB,'R',TAB,'Sets file attribute(s) to READ/ONLY',CR,LF DB TAB,'W',TAB,'Sets file attribute(s) to READ/WRITE',CR,LF DB TAB,'A',TAB,'Sets file attribute(s) to ARCHIVE',CR,LF DB TAB,'N',TAB,'Sets file attribute(s) to NON-ARCHIVE',CR,LF DB TAB,'E',TAB,'Erases the specified files',CR,LF DB TAB,'U',TAB,'Unerases the specified files',CR,LF,0 JMP QUIT2 ;============================================================================= ; G E N E R A L - P U R P O S E S U B R O U T I N E S ;----------------------------------------------------------------------------- ; CRTOUT ; This subroutine sends the character in register A to the console. Registers ; BC, DE, and HL are preserved. CHAROUT: PUSH H ;save registers PUSH D PUSH B MOV E,A ;get character into E MVI C,06 ;BDOS direct console I/O CALL BDOS POP B ;restore registers POP D POP H RET ;----------------------------------------------------------------------------- ; CRLF ; This routine sends a carriage return and linefeed to the console. CRLF: CALL ILPRT DB CR,LF,0 RET ;----------------------------------------------------------------------------- ; FILL ; This subroutine fills memory starting at HL for a length B with the byte ; in A. FILL: MOV M,A INX H DCR B JNZ FILL RET ;----------------------------------------------------------------------------- ; ILPRT ; This subroutine prints the string that follows its call. The string must ; be terminated with a null (0). ILPRT: POP H ;get address following call into HL ILPRT1: MOV A,M ;get character from message INX H ;point to next character ORA A ;test for null indicating end of message JZ ILPRT2 ;if end, fix up return address MOV E,A ;have BDOS send character it to console MVI C,2 PUSH H ;save pointer to string CALL BDOS POP H ;restore pointer JMP ILPRT1 ;process it ILPRT2: PUSH H ;set up return address to just past message RET ;----------------------------------------------------------------------------- ; PRTSTR ; This subroutine prints a string of characters pointed to by DE. The number ; of characters is in the C register. Blanks are not printed; instead, the ; blanks counter pointed to by HL is incremented. PRTSTR: LDAX D ;get character CPI ' ' ;see if it is a blank CZ UPCOUNT ;if so, up the count CNZ CHAROUT ;if not, output the character INX D DCR C ;check count JNZ PRTSTR RET UPCOUNT: PUSH PSW ;save flags INR M ;increase the blank counter POP PSW ;restore flags RET ;----------------------------------------------------------------------------- ; PRTBLK ; This subroutine prints blank spaces given by the count in C. The routine ; will work even for a count of zero. PRTBLK: INR C ;turn 0 into 1 PRTBL1: DCR C ;check count RZ ;return if count exhausted MVI A,' ' ;set character to print CALL CHAROUT JMP PRTBL1 ;============================================================================= ; S E T U P S U B R O U T I N E S ;----------------------------------------------------------------------------- ; SIGNON ; This subroutine displays the program signon message. SIGNON: CALL ILPRT DB CR,LF,'MAKE - Version ' DB VERSION / 10 + '0' DB '.' DB VERSION MOD 10 + '0' IF ZCPR3 DB ' for ZCPR3' ENDIF ;ZCPR3 DB CR,LF,0 RET ;----------------------------------------------------------------------------- ; CHKHLP ; This subroutine checks to see if the user has invoked the program in a ; way to request the built-in help screen. The help screen is shown if the ; command has no tail or if the tail begins with a slash. CHKHLP: LDA FCB+1 ;get first character of first parameter CPI ' ' ;no name? JZ HELP ;if so, go to HELP CPI '/' ;parameter starts with slash? JZ HELP ;if so, go to HELP RET ;return with flag set appropriately ;----------------------------------------------------------------------------- ; CHKVER ; This subroutine checks for a valid version number (one that supports user ; numbers). If it is not valid, then this routine displays and error message ; and jumps to QUIT2. IF NOT ZCPR3 CHKVER: MVI C,0CH ;bdos version number function CALL BDOS CPI 20H ;make sure 2.0 or more RNC ;return if OK CALL ILPRT ;else print error message DB BELL,CR,LF DB 'Requires CP/M version 2.0 or higher.',CR,LF,0 JMP QUIT2 ;terminate program ENDIF ;NOT ZCPR3 ;----------------------------------------------------------------------------- ; INIT ; This subroutine initializes the data areas in the program so that GO ; command will re-run the program correctly. INIT: XRA A ;zero the accumulator STA CHGFLAG ;preset control flags STA RSTFLAG STA DIRCODE LXI H,AMBFIL2 MVI B,16 CALL FILL ;clear the fcb MVI A,'?' MVI B,16 LXI H,AMBFIL CALL FILL LHLD 0001 ;get warmboot address (base of bios + 3) LXI D,27H ;offset for jump to bios write DAD D ;compute address for write routine SHLD WRITE + 1 ;load our vector with this address RET ;----------------------------------------------------------------------------- ; SETDU ; This subroutine gets and saves the values of the currently logged in drive ; and user area and the drive and user specified (if any) on the command line ; for the files to be operated on. SETDU: ;get currently logged in user number MVI C,20H ;BDOS get/set user number function MVI E,0FFH ;get user flag CALL BDOS STA DEFUSR STA SRCUSR ;save for now as source user also ;get the currently logged in drive MVI C,19H ;bdos get drive number function CALL BDOS ;get drive number STA DEFDRV INR A ;change range 1-16 and STA SRCDRV ;..save for now as source drive also ;now log in the drive and user in file spec LDA FCB ;get drive spec from FCB ORA A ;see if default specified JZ SETDU1 STA SRCDRV ;save source drive SUI 1 ;get in range 0-15 CALL LOGDRV ;log in the drive XRA A ;and change FCB to show default drive STA FCB SETDU1: IF ZCPR3 LDA FCB+13 ;get user number from S1 byte STA SRCUSR ;save as source user area CALL LOGUSR ;log in the user number ENDIF ;ZCPR3 RET ;----------------------------------------------------------------------------- ; These two routines log in the drive or user number given in the A register. ; No registers are preserved. LOGDRV: MOV E,A MVI C,0EH CALL BDOS RET LOGUSR: MOV E,A MVI C,20H CALL BDOS RET ;----------------------------------------------------------------------------- ; GETOPT ; Process option specified on the command line. If there is an error, the ; routine jumps to HELP which in turn jumps to QUIT. If a named directory ; is specified for the destination on the command line, then its user number ; is obtained from address TFCB+13. The drive is checked to make sure that ; the destination is on the same drive. GETOPT: IF ZCPR3 ;check for destination specified using named directory LDA TFCB ;check for drive number ORA A ;if zero, no DIR: or DU: given JZ GETOPT1 ;check for correct drive spec LXI H,SRCDRV ;point to source drive value CMP M JNZ BADDRV ;if not the same, jump to bad drive message ;get the user number LDA TFCB + 13 STA OPTION ;store user number as option JMP CHKNUM ;check for valid user number BADDRV: CALL ILPRT ;destination and source drives not same DB BELL DB CR,LF,LF DB 'Source and Destination Drives',CR,LF DB 'Must be the Same' DB CR,LF DB 0 JMP QUIT ENDIF ;ZCPR3 GETOPT1: LXI H,TFCB ;point to parsed second parameter MOV A,M ;make sure it wasn't of form 'D:' ORA A ;drive byte should be zero JNZ BADOPT INX H ;now look at data entered MOV A,M ;get the first character CALL GETNUM ;try to read it as a number JC LETTER ;if not, must be a letter or bad MOV B,A ;save digit in B STA OPTION ;..and as interim option INX H ;try next character MOV A,M CPI ' ' ;if it is a blank JZ CHKNUM ;..go to test user number value CALL GETNUM ;see if second character is a number JC BADOPT ;if not, we have a bad option spec MOV C,A ;save second digit MOV A,B ;get first digit back ADD A ;double it three times to make 8x ADD A ADD A ADD B ;now add original in twice to make 10x ADD B ADD C ;finally, add in second digit STA OPTION ;..and save the final result ;check for valid user number (in range and not same ;as logged in user number) CHKNUM: LDA OPTION ;make sure we have the user number CPI 32 ;test for valid user number range JNC BADNUM LXI H,SRCUSR ;see if same as source user CMP M JZ SAMENUM ;if so, give message RET LETTER: ;check for valid letter option PUSH PSW ;save option letter INX H ;check whether option followed by '#' MOV A,M CPI '#' JNZ LETTER0 ;not "all users" switch, don't save it STA ALLUSR LETTER0: POP PSW ;get option character back LXI H,OPTLIST ;point to list of valid options MOV C,M ;get number of options in list LETTER1: ;loop through them checking INX H CMP M ;compare to list entry JZ GOODOPT ;if it matches, we have a good option DCR C ;else, count down JNZ LETTER1 ;..and try again JMP HELP ;we get here if option is not valid GOODOPT: ;we have a good option letter STA OPTION RET BADOPT: ;we have a bad option specifier CALL ILPRT DB BELL,CR,LF DB TAB,'**** BAD OPTION SPECIFIER ****',CR,LF,0 JMP HELP BADNUM: ;we have an illegal user number CALL ILPRT DB BELL,CR,LF DB TAB,'**** ILLEGAL USER NUMBER ****',CR,LF,0 JMP HELP SAMENUM: ;give message about already in that user area CALL ILPRT DB BELL,CR,LF DB 'Destination User Number is the',CR,LF DB 'Same as the Source User Number' DB CR,LF,0 JMP QUIT ;subroutine to check for number character ;returns with carry set if not a number GETNUM: CPI '0' ;see if less than '0' RC ;if so, set carry flag as signal CPI '9'+1 ;see if more than '9' CMC ;reverse sense of carry flag RC ;if >9, return with carry set SUI '0' ;convert to number value RET ;carry is clear ;list of valid options OPTLIST: DB ENDLIST-OPTLIST ;number of options in list DB ' SDRWANEU' ;valid options ENDLIST: ;----------------------------------------------------------------------------- ; CHKRO ; This routine checks to see if the destination drive is read-only. If it ; is, an appropriate error message is displayed and the program aborts with ; a jump to QUIT. If the option is display only (option = space char), then ; this test is skipped. CHKRO: LDA OPTION ;see if display option is in effect CPI ' ' RZ ;if so, skip rest of test MVI C,1DH ;get R/O vector from BDOS CALL BDOS ;calculate number of left-shifts needed LDA SRCDRV ;get the target drive number CMA ;complement it (makes 255-SRCDRV) ADI 17 ;makes A = 16 - SRCDRV (value 1-16) ;shift word in HL to put bit of R/O vector for ;specified drive into high bit position CHKRO1: DCR A ;test for done JZ CHKRO2 ;if so, jump DAD H ;shift HL to left JMP CHKRO1 ;and loop CHKRO2: DAD H ;move high bit into carry flag RNC ;if not R/O, return CALL ILPRT ;else print R/O error message DB BELL,CR,LF DB 'Drive is set to R/O',CR,LF,0 JMP QUIT ;abort program ;============================================================================= ; M I S C E L L A N E O U S R O U T I N E S ;----------------------------------------------------------------------------- ; SETPTR ; This subroutine uses the value of the directory code to calculate a pointer ; to the FCB in the DMA buffer. This is done by multiplying the directory code ; by 32 and adding it to the DMA address (DMAADDR). The result is saved in ; DMAPTR. SETPTR: LDA DIRCODE ;get the directory code ADD A ;offset by 32 bytes per entry ADD A ADD A ADD A ADD A MOV E,A ;move value into DE MVI D,0 LXI H,DMAADDR ;get buffer address DAD D ;compute offset into buffer SHLD DMAPTR ;save the address into the buffer RET ;----------------------------------------------------------------------------- ; SETCHGFL ; This subroutine sets the change-flag to show that the directory sector ; has been modified and needs to be written out to disk. SETCHGFL: PUSH PSW MVI A,0FFH ;set the sector change flag STA CHGFLAG POP PSW RET ;============================================================================= ; D A T A A R E A ;----------------------------------------------------------------------------- OLDSTK: ;place to keep old stack pointer DS 2 BLNKCNT: ;count of blank characters in file name DS 1 CHGFLAG: ;flag for change requiring write DS 1 RSTFLAG: ;flag for need to reset disk system DS 1 AMBFIL: ;working fcb DS 16 AMBFIL2: ;space for rest of FCB DS 19 OPTION: ;storage for new user number or option DS 1 ALLUSR: ;storage for "all users" switch DS 1 DIRCODE: ;storage for directory code (0-3) DS 1 DMAPTR: ;address of FCB in DMA buffer DS 2 DEFDRV: ;current default drive DS 1 SRCDRV: ;source drive DS 1 DEFUSR: ;current default user number DS 1 SRCUSR: ;source user area from file spec DS 1 DS 60 ;room for local stack NEWSTK: END