; This program is derived from MAKE26 and CFA10. ; See CUT10.HIS (and CFA10.HIS) for upgrade notes and programmer credits. ; See CUT10.DOC for full instructions, usage notes, warnings, etc. ;============================================================================= ; P R O G R A M E Q U A T E S ;============================================================================= 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 CR EQU 0DH LF EQU 0AH TAB EQU 09H ;============================================================================= ; M A I N P R O G R A M C O D E ;----------------------------------------------------------------------------- ORG 0100H START: ; 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 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 ;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 CUT ;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 ;----------------------------------------------------------------------------- ; First prints matching filename; check for Cut or Bind operation; do ; it; and finish by showing new filename. CUT: CALL PRTFN ; show original file name LDA OPTION ; check option again CPI 0 ; see if it was 0 JZ UNCUT ; if so, Bind instead of Cut LHLD DMAPTR ; point to disk directory FCB entry LXI D,9 ; offset first byte in filetype DAD D LDA EXTLTR MOV M,A ; change first letter of filetype LHLD DMAPTR ; point to disk directory FCB entry LXI D,12 ; offset to extent byte DAD D LDA MRKSEG MOV M,A ; change extent number INR A ; bump the extent counter STA MRKSEG ; and store it MOV B,A LDA OPTION CMP B ; see if we've reached the limit JNZ CUTX ; if not, keep it as is SUB A ; otherwise zero it out STA MRKSEG ; store extent counter (again) LDA EXTLTR ; load letter for filename INR A ; increment it STA EXTLTR ; and store it, too CUTX: CALL SETCHGFL ; set change flag CALL PRTFN ; report the new file status JMP CRLF ;----------------------------------------------------------------------------- ; Reverse of CUT operation. Binds splyit files back together. ; Renames all segments with character '-' as first byte of filetype. UNCUT: LHLD DMAPTR ; point to disk directory FCB entry LXI D,12 ; offset to extent DAD D LDA MRKSEG ; get current extent number MOV M,A ; restore original extent INR A ; increment it STA MRKSEG ; and store for next round LHLD DMAPTR ; point to disk directory FCB entry LXI D,9 ; first byte of filetype DAD D MVI A,'-' ; arbitrary code to mark Bind MOV M,A ; write modified byte back to buffer CALL SETCHGFL ; set change flag CALL PRTFN ; report the new file status JMP CRLF ;============================================================================= ; P R O G R A M F L O W R O U T I N E S ;----------------------------------------------------------------------------- ; QUIT ; 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 CHKHL ; which that doesn't change the logged in directory. QUIT: 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 ;============================================================================= ; 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 CMC ;carry flag set if file erased RC ;erased file will be skipped ;now check for user number in source area LDA SRCUSR ;get source user number CMP M ;compare to file tag RZ ;if OK, return (carry is clear) STC ;else set carry 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 'Empty disk?',0 JMP QUIT ;----------------------------------------------------------------------------- ; WRTDE ; This routine writes the directory buffer back to the disk. 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 'Bad Sector Write Error',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: 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 MVI C,2 ;get 2 blanks AFTER file name also CALL PRTBLK ;print the blanks RET ;----------------------------------------------------------------------------- ; HELP ; Displays a little help then jumps to QUIT. HELP: CALL ILPRT DB 'Use: CUT [d:]filename.typ ',CR,LF DB ' may be 0 to combine files.',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 ; Turns up one blank line CRLF: CALL ILPRT DB CR,LF,0 ;----------------------------------------------------------------------------- ; 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 ;----------------------------------------------------------------------------- ; INIT ; This subroutine initializes the data areas in the program so that GO ; command will re-run the program correctly. INIT: MVI A,'A' STA EXTLTR ; set first letter for segment rename XRA A ;zero the accumulator STA CHGFLAG ;preset control flags STA RSTFLAG STA DIRCODE STA MRKSEG 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 RZ 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 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: 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 HELP INX H ;now look at data entered MOV A,M ;get the first character CALL GETNUM ;try to read it as a number JC HELP ;if not, must be a letter or bad STA OPTION ;..and as interim option RET ;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 ;----------------------------------------------------------------------------- ; 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: 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 'Drive is R/O',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 command line option 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 MRKSEG: DS 1 ; extent counter for split segments EXTLTR: DS 1 ; letter code for renamed segments DS 60 ; room for local stack NEWSTK: END