home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
cpm
/
utils
/
hdutl
/
multcopy.asm
< prev
next >
Wrap
Assembly Source File
|
1994-07-13
|
14KB
|
584 lines
; MULTIPLE FILE COPY PROGRAM
; COPYRIGHT 1982, G. YOUNG, INC.
; UPDATED 10/9/82
*
* TO EXECUTE, ENTER THE PROGRAM NAME FOLLOWED BY THE FILE MASK:
* A>MULTCOPY *.COM
*
* THE PURPOSE OF THIS PROGRAM IS TO COPY MULTIPLE FILES TO A FLOPPY DISK
* FROM A HARD DISK. IF YOU USED PIP FOR D:=*.COM AND THER WERE MORE
* COM FILES THAN WOULD FIT ON ONE DISK, PIP WOULD STOP WHEN THE FIRST
* DISK WAS FULL. IF MULTCOPY, WHEN ONE DISK WAS FULL, IT WOULD ASK
* YOU TO MOUNT A NEW DISK AND CONTINUE COPYING. THE FLOPPY DISK DRIVE
* IS HARD CODED INTO THE PROGRAM. THE SOURCE MAY BE ANY HARD DISK.
* EXAMPLE MULTCOPY B:LN*.ASM
* CHANGED 10/9/82 TO ASK DESTINATION FLOPPY DRIVE
* WRITTEN BY GARY YOUNG, BOX 3218, NO. HOLLYWOOD, CA 91609
TITLE '*** MULTCOPY FILE COPY PROGRAM ***'
BDOS EQU 5
RECSIZE EQU 12
BOOT EQU 0
ORG 100H
JMP START
DB 'COPYRIGHT 1982, G. YOUNG, INC.'
START LXI SP,STACK+80
LDA 5DH ;SEE IF A MASK WAS ENTERED
CPI 20H ;BUFFER WILL BE BLANK IF NOT
JZ NOMASK
MVI C,0DH ;DISK RESET
CALL BDOS
LXI H,RAM ;SET UP TABLE ADDRESS
SHLD TABADDR
LXI H,0000H
SHLD TABCNT
XRA A ;THE TABLE REFERS TO THE REMAINING AREA
STA ABORT ;OF RAM TO HOLD AS MANY DIRECTORY ENTRIES
STA EOF ;AS POSSIBLE
LXI D,SIGNON ;PRINT SIGNON MESSAGE
CALL OUTPUT
LXI D,DESTFLPMSG
CALL QUESTION
ORA A
JNZ QUEST1A
LDA DESTFLP
JMP QUEST1B
QUEST1A LDA INREC
QUEST1B STA FLOPPY
CALL EXTRACT ;GET DIRECTORY ENTRIES
NOMORE CALL SORT
CALL KILLDUPS ;REMOVE ENTRIES FROM MULTIPLE EXTENTS
LDA 5CH ;SOURCE DISK
STA SRCE
LDA FLOPPY ;FLOPPY DISK IS ALWAYS THE DESTINATION
ANI 0FH
STA DEST
LXI D,MNTBLANK
CALL QUESTION
MVI C,0DH
CALL BDOS
CALL CPYFILE
JMP BOOT ;RESET AND RETURN TO CPM
NOMASK LXI D,ERRNOMSK
CALL OUTPUT
JMP BOOT
*
* THE EXTRACT ROUTINE SCANS THE DIRECTORY ON THE FLOPPY DISK
* AND CREATES A LIST OF FILES TO BE COPIED. AFTERS SORTING,
* DUPLICATE ENTRIES RESULTING FROM MULTIPLE EXTENT ENTRIES WILL
* BE REMOVED.
*
* THE DUMMYFCB SETS UP A SKELETON TO READ ALL FILES ON THE DIRECTORY
* TO BE MODIFIED BY THE SKELATON SETUP IN THE CALL (EX: FLOPCOPY *.ASC).
*
DUMMYFCB DB 0,'????????????',0,0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
EXTRACT EQU $
LXI D,DUMMYFCB ;COPY THE MASK SET UP BY CCP
LXI H,5CH ;OVER THE DUMMY FCB TO LIMIT WHAT IS COPIED
MVI B,12
CALL MOVE
LDA DUMMYFCB
ORI 40H
STA RDISK
LXI D,DISKBUF ;SET A DMA ADDRESS FOR DIRECTORY ENTRIES
MVI C,1AH
CALL BDOS ;SET DMA
LXI D,DUMMYFCB ;GET FIRST DIRECTORY ENTRY
MVI C,11H
CALL BDOS ;GET DIRECTORY
INR A
RZ
JMP GETFCB ;EXTRACT DATA FROM FCB
NEXTFCB LXI D,DUMMYFCB ;GET NEXT ENTRY AFTER THE FIRST
MVI C,12H
CALL BDOS
INR A
RZ
GETFCB LXI D,32 ;EACH ENTRY IS 32 BYTES LONG
LXI H,DISKBUF ;DIRECTORY RECORD IS IN DISKBUF
CPI 1 ;FIRST ENTRY IN RECORD???
JZ FORMREC ;YES
DAD D ;ADD 32 TO ADDRESS IN RECORD
CPI 2 ;SECOND ENTRY IN RECORD???
JZ FORMREC
DAD D ;ADD 64 TO ADDRESS OF RECORD
CPI 3 ;THIRD ENTRY IN RECORD???
JZ FORMREC
DAD D ;ADD 96 TO ADDRESS OF RECORD
FORMREC INX H ;PASS DRIVE BYTE
LXI D,RFILE ;MOVE FILE NAME
MVI B,8 ;MOVE 8 CHARACTERS
CALL MOVE
LXI D,8 ;POSITION PAST NAME TO TYPE
DAD D
LXI D,RTYPE ;MOVE TYPE
MVI B,3 ;MOVE 3 CHARACTERS
CALL MOVE
INX H ;POSITION TO THE EXTENT NUMBER
INX H
INX H
LXI H,RTYPE ;STRIP OFF THE HIGH BIT SET BY
MVI B,11 ;THE VERSION PROGRAM
STRIP MOV A,M
ANI 07FH
MOV M,A
INX H
DCR B
JNZ STRIP
LXI H,RTYPE ;SKIP OVER JUNK DISK ENTRIES
MVI B,11 ;CONTAINING NONPRINTING CHARACTERS
NONPRNT MOV A,M
CPI 20H ;ANY CHAR LESS THAN A BLANK
JC NEXTFCB ;GO TO NEXT ONE IF SO
INX H
DCR B
JNZ NONPRNT
FIRSTEXT EQU $ ;GOT THE FIRST EXTENT
LHLD TABADDR ;GET MEMORY TABLE ADDRESS
LXI D,RTYPE ;GET MEMORY RECORD ADDRESS
XCHG ;RTYPE (HL) TO TABADDR (DE)
MVI B,RECSIZE ;MOVE RECSIZE BYTES
CALL MOVE
LHLD TABADDR ;INCREMENT FOR NEXT ENTRY
LXI D,RECSIZE ;RECSIZE BYTES IN RECORD
DAD D
SHLD TABADDR ;SAVE NEW ADDRESS
MVI M,1AH ;SET AN END-OF-TABLE INDICATOR
LHLD TABCNT ;GET RECORD COUNT
INX H
SHLD TABCNT ;INCREMENT RECORD COUNT
LHLD TABADDR ;SEE IF NEW ADDRESS IS GREATER
XCHG ;THAN THE TOP OF TPA-128
LHLD BDOS+1 ;HL=TOP...DE=TABLE+RECSIZE
LXI B,-128
DAD B ;SUBTRACT 128 FROM TOP
CALL COMPREG ;COMPARE REGISTER VALUES
JNC NEXTFCB ;THERE IS ROOM FOR MORE
MVI A,1 ;NO MORE ROOM...ABORT NOW
STA ABORT
RET
* COPY THE FILE FROM ONE DISK TO ANOTHER
* THIS CODE WAS TAKEN FROM BACKUP.ASM SO HDFCB REFERS TO THE SOURCE DISK
* AND FPFCB REFERS TO THE DESTINATION DISK REGARDLESS OF FLOPPY OR HARD.
CPYFILE EQU $
LHLD ADDR2 ;GET THE CURRENT END OF TABLE
INX H ;PLUS ONE FOR THE START OF THE
SHLD ADDR3 ;READ/WRITE BUFFER
LXI H,RAM ;SET THE ADDRESS OF THE FIRST ENTRY
SHLD ADDR1
JMP PASSMOVE
NEXTFILE EQU $
LHLD ADDR1
LXI D,RECSIZE
DAD D
SHLD ADDR1
PASSMOVE LHLD ADDR1
MOV A,M
CPI 1AH
RZ
FORMFCB LXI D,HDFCB ;CLEAR THE FCB
MVI B,36
XRA A
ZEROFCB STAX D
INX D
DCR B
JNZ ZEROFCB
LHLD ADDR1 ;GET ADDRESS OF TABLE ENTRY
LXI D,HDTYPE ;MOVE IN THE TYPE
MVI B,3
CALL MOVE
INX H
INX H ;POSITION TO NAME
INX H
LXI D,HDFILE ;MOVE THE FILE NAME
MVI B,8
CALL MOVE
LXI D,8
DAD D ;POSITION TO DISK ID, A:, B: ETC
LDA SRCE
STA HDFCB
LXI D,FPFCB ;COPY THE HDFCB TO THE FLOPPY FCB
LXI H,HDFCB
MVI B,36
CALL MOVE
LDA DEST
STA FPFCB
LHLD ADDR1
CALL FORMAT
LXI D,PRNTREC
CALL OUTPUT
LXI D,HDFCB ;OPEN THE SOURCE FILE DRIVE
MVI C,0FH
CALL BDOS
INR A
JZ NOTFOUND
LXI D,FPFCB ;SEE IF THE FILE IS ON THE DESTINATION DRV
MVI C,0FH
CALL BDOS ;DUMMY OPEN
INR A
JZ MAKEIT ;NOT THERE...MAKE IT
ERAIT EQU $
LXI D,FPFCB ;DELETE THE FILE ON FLOPPY IF IT
MVI C,13H ;EXISTS
CALL BDOS
MAKEIT EQU $
LXI D,FPFCB ;CREATE THE FILE ON FLOPPY
MVI C,16H
CALL BDOS ;MAKE FILE
INR A
JZ DISKFULL
COPYLOOP CALL LOADBUFF ;LOAD MEMORY WITH FILE
CALL WRITEBUF ;WRITE MEMORY FILE
LDA EOF
CPI 1
JZ ENDOFFILE
CPI 2
JZ DISKFULL
JMP COPYLOOP
ENDOFFILE LXI D,FPFCB ;CLOSE FLOPPY FILE
MVI C,10H
CALL BDOS ;CLOSE
JMP NEXTFILE
DISKFULL LXI D,FPFCB
MVI C,13H ;DELETE FILE ON FLOPPY
CALL BDOS
LXI D,MNTBLANK
CALL QUESTION
MVI C,0DH
CALL BDOS
JMP FORMFCB
NOTFOUND LHLD ADDR1
CALL FORMAT
LXI D,NFMSG
CALL OUTPUT
LXI D,PRNTREC
CALL OUTPUT
RET
* THE FOLLOWING ROUTINE IS A BUBBLE SORT. IN PSEUDOBASIC
* IS WOULD BE DONE AS FOLLOWS:
*SORT ADDR1=BOTTOM(RAM)-RECSIZE (RECSIZE IS COUNT OF BYTES IN ONE RECORD)
* MAX1=0
*LOOP1 MAX1=MAX1+1
* ADDR1=ADDR1+RECSIZE (FIRST TIME THIS WOULD BE THE BOTTOM)
* IF MAX1>TABCNT-1 THEN GO TO SORTED
* ADDR2=ADDR1
* MAX2=MAX1
*LOOP2 MAX2=MAX2+1
* ADDR2=ADDR2+RECSIZE
* IF MAX2>TABCNT THEN GO TO LOOP1
* IF TABLE(ADDR1)<TABLE(ADDR2) THEN GO TO LOOP2
* TEMP=TABLE(ADDR1)
* TABLE(ADDR1)=TABLE(ADDR2)
* TABLE(ADDR2)=TEMP
* GO TO LOOP2
*SORTED RETURN
*
*NOW THAT THIS LOGIC IS INTUITIVELY OBVIOUS, HERE'S THE NOT SO
*OBVIOUS CODE:
SORT
LXI H,0000 ;INITIALIZE COUNT
SHLD MAX1 ;SINCE IT WILL DECREMENT FIRST
LXI D,-RECSIZE ;SUBTRACT RECSIZE FROM THE BEGINNING
LXI H,RAM ;OF THE TABLE SINCE IT WILL ADD
DAD D ;RECSIZE TO IT FIRST
SHLD ADDR1
LOOP1 LHLD ADDR1 ;GET THE CURRENT ADDRESS
LXI D,RECSIZE ;INCREMENT IT BY RECSIZE
DAD D ;PAST THE NEXT ENTRY
SHLD ADDR1
LHLD MAX1 ;SEE IF THE COUNT HAS REACHED THE LIMIT
INX H ;INCREMENT THE COUNTER
SHLD MAX1
XCHG ;MOVE TO DE TO COMPARE TO LIMIT
LHLD TABCNT
DCX H ; IS MAX1 > TABCNT - 1 ???
CALL COMPREG ;COMPARE DE (CURRENT COUNT) TO HL (TABCNT-1)
JC SORTED ;FINISHED WHEN MAX1 > TABCNT-1
LHLD ADDR1 ;SETUP FOR THE INNER LOOP
SHLD ADDR2
LHLD MAX1
SHLD MAX2
LOOP2 LHLD ADDR2 ;START WITH ONE ENTRY GREATER THEN
LXI D,RECSIZE ;THE OUTER LOOP AND INCREMENT
DAD D ;BY RECSIZE EACH TIME PAST THE NEXT RECORD
SHLD ADDR2 ;POINTER TO THE CURRENT RECORD
LHLD MAX2 ;LIKEWISE SEE IF THE COUNTER FOR THE
INX H
SHLD MAX2
XCHG
LHLD TABCNT
CALL COMPREG
JC LOOP1 ;WHEN FINISHED, INCREMENT OUTER LOOP
LHLD ADDR2
XCHG ;ADDR2 NOW IN DE
LHLD ADDR1
CALL COMPARE ;COMPARE STRINGS POINTED TO BY DE/HL
JNC LOOP2 ;TABLE(HL) < TABLE (DE)
* EXCHANGE THE TWO ENTRIES VIA A TEMPORARY RECORD
LXI D,TEMP
LHLD ADDR1
MVI B,RECSIZE
CALL MOVE ;TEMP=TABLE(ADDR1)
XCHG ;ADDR1=DESTINATION
LHLD ADDR2 ;ADDR2=SOURCE
CALL MOVE ;TABLE(ADDR1)=TABLE(ADDR2)
XCHG ;ADDR2 = DESTINATION
LXI H,TEMP
CALL MOVE ;TABLE(ADDR2)=TEMP
JMP LOOP2
SORTED RET ;FINISHED SORTING
KILLDUPS EQU $ ;KILL DUPLICATE ENTRIES FROM MULTIPLE EXTENTS
LXI H,RAM
SHLD ADDR1 ;SET THE START OF THE TABLE
NEXTEQUAL LHLD ADDR1 ;CHECK EACH ENTRY AGAINST THE NEXT
LXI D,RECSIZE
DAD D
SHLD ADDR2
NEXTCHK MOV A,M ;CHECK FOR END OF TABLE
CPI 1AH
RZ
XCHG
LHLD ADDR1 ;COMPARE ADDR1 WITH ADDR2
MVI B,RECSIZE
CALL COMPARE
JNZ SAVELAST
PUSH H ;SAVE CURRENT POSITION
LHLD ADDR2 ;SET UP FOR MOVING LIST
SHLD ADDR1 ;DOWN ONE ENTRY
LXI D,RECSIZE
DAD D ;MOVE THIRD ENTRY TO THE SECOND
SHLD ADDR2
CALL MOVELIST
LHLD TABCNT
DCX H
SHLD TABCNT
POP H ;RESTORE CURRENT POSITION
SHLD ADDR1 ;NOW COMPARE 1ST & 3RD
JMP NEXTEQUAL
SAVELAST LHLD ADDR2 ;MAKE NEW ONE THE CURRENT ONE
SHLD ADDR1
JMP NEXTEQUAL ;COMPARE
MOVELIST LHLD ADDR2 ;MOVE THE TABLE FROM ADDR2 DOWN
XCHG ;TO ADDR1 THEREBY ELIMINATING
LHLD ADDR1 ;UNWANTED ENTRIES
MOVENEXT LDAX D ;AND MAKING MORE ROOM FOR THE
MOV M,A
CPI 1AH
RZ
INX H
INX D
JMP MOVENEXT
* ASSORTED ROUTINES
LOADBUFF EQU $ ;THIS ROUTINE LOADS AS MUCH OF
LXI H,0000 ;MEMORY WITH THE FILE AS POSSIBLE
SHLD MAX1
LHLD ADDR3 ;NEW TOP OF TABLE +2
SHLD TEMP
XRA A
STA EOF ;CLEAR EOF FLAG
LOADNEXT LHLD TEMP
XCHG ;SET DMA ADDRESS
MVI C,1AH
CALL BDOS
LXI D,HDFCB ;READ HARD DISK
MVI C,14H
CALL BDOS
ORA A
JNZ HDEOF ;EOF?
LHLD MAX1
INX H ;INCREMENT RECORD COUNT
SHLD MAX1
LHLD TEMP ;SEE IF NEXT RECORD WOULD EXCEED THE
LXI D,128 ;TPA AREA
DAD D
SHLD TEMP
DAD D ;WILL THE NEXT RECORD OVERWRITE BDOS?
XCHG
LHLD BDOS+1 ;FIND THE TOP OF MEMORY
CALL COMPREG ;COMPARE REGISTERS
RC ;RETURN IF MEMORY ALREADY FULL
JMP LOADNEXT ;GET ANOTHER RECORD
HDEOF MVI A,1 ;SET FILE EOF
STA EOF
RET
WRITEBUF EQU $ ;WRITE FROM THE MEMORY BUFFER
LHLD ADDR3
SHLD TEMP
LHLD MAX1 ;ALLOW FOR FILES THAT HAVE NO
LXI D,0000 ;RECORDS SUCH AS RESTART
CALL COMPREG
RZ
WRITENEXT LHLD TEMP
XCHG ;SET DMA ADDRESS
MVI C,1AH
CALL BDOS
LXI D,FPFCB
MVI C,15H ;WRITE SEQUENTIAL
CALL BDOS
ORA A
JNZ FPFULL ;FLOPPY DISK FULL
LHLD MAX1 ;DECREASE RECORD COUNT
DCX H
SHLD MAX1
LXI D,0000 ;CHECK FOR NO MORE TO WRITE
CALL COMPREG
RZ
LHLD TEMP
LXI D,128 ;INCREMENT WRITE ADDRESS
DAD D
SHLD TEMP
JMP WRITENEXT
FPFULL MVI A,2 ;FULL DISKETTE
STA EOF
RET
FORMAT LXI D,PRNTYPE ;FORMAT THE ENTRY FROM THE TABLE
MVI B,3 ;FORMAT TO THE PRINT FORMAT
CALL MOVE ;THE TABLE ADDRESS IS ASSUMMED TO BE
LXI D,3 ;IN HL. FIRST MOVE THE TYPE
DAD D ;NOW POSITION TO THE FILE NAME
LXI D,PRNFILE ;MOVE THE FILE NAME
MVI B,8
CALL MOVE
LXI D,8 ;POSITION TO THE DISK ID
DAD D
LDA SRCE
ORI 40H
STA PRNTREC
MVI A,':'
STA PRNTREC+1
MVI A,'.'
STA PRNTYPE-1
RET
COMPREG MOV A,H ;COMPARE HL TO DE
CMP D
RNZ
MOV A,L
CMP E
RET
OUTPUT PUSH D ;PUT OUT A CRLF
LXI D,CRLF
MVI C,09
CALL BDOS
POP D ;NOW PUT OUT THE MESSAGE
OUT1 MVI C,09
JMP BDOS
QUESTION CALL OUTPUT ;PUT OUT THE QUESTION
LXI D,INBUF
MVI C,0AH ;INPUT THE REPLY
CALL BDOS
LDA INCNT ;SEE IF ANYTHING WAS ENTERED
RET
MOVE PUSH H ;MOVE DATA POINTED TO IN HL
PUSH D ;TO THE AREA POINTED TO IN DE
PUSH B ;BY THE BYTE COUNT IN B
MOVE1 MOV A,M
ANI 7FH ;RESET THE HIGH ORDER BIT BECAUSE IT
;MAY HAVE BEEN TURNED ON FOR THE TYPE
STAX D
INX H
INX D
DCR B
JNZ MOVE1
POP B ;RESTORE THE TOTAL ENVIRONMENT
POP D
POP H
RET
COMPARE PUSH H ;COMPARE THE STRINGS POINTED TO IN HL
PUSH D ;TO THE STRING POINTED TO IN DE
PUSH B ;FOR A LENGTH OF B CHARACTERS
COMP1 LDAX D ; JC IF HL > DE
CMP M ; JZ IF HL = DE
JNZ COMP2 ;JNC IF HL < DE
INX H
INX D
DCR B
JNZ COMP1
COMP2 POP B
POP D
POP H
RET
SIGNON DB 'MULTIPLE FILE COPY PROGRAM '
DB '10/9/82$'
ERRNOMSK DB 'ERROR...NO FILE MASK IN COMMAND'
DB CR,LF,'COMMAND MUST BE IN THE FORMAT "MULTCOPY *.*"'
DB CR,LF,'$'
NFMSG DB 'FILE NOT FOUND...ABORTING$'
DMNTMSG DB 'DISMOUNT DISKETTE AND ENTER RETURN$'
MNTBLANK DB 'MOUNT A FORMATTED BLANK DISKETTE AND ENTER RETURN$'
PRNTREC DS 1
DB ':'
PRNFILE DS 8
DB '.'
PRNTYPE DS 3
DB ' $'
CRLFLFLF DB 0DH,0AH,0AH,0AH,'$'
CRLF DB 0DH,0AH,'$'
BIGMSG DB 0DH,0AH,'FILE TOO LARGE TO FIT ON ONE DISKETTE '
DB 'SO NOT BACKED UP **** $'
DESTFLPMSG DB 'DESTINATION FLOPPY DRIVE (DEFAULT='
DESTFLP DB 'D) $'
HDFCB DS 1
HDFILE DS 8
HDTYPE DS 3
DS 24
FPFCB DS 36
FLOPPY DS 1
SRCE DS 1
DEST DS 1
INBUF DB 30
INCNT DS 1
INREC DS 30
STACK DS 80
EOF DS 1
ABORT DS 1
LINECNT DS 1
LASTTYPE DS 3
LASTFILE DS 8
TABADDR DS 2
TABCNT DS 2
SELFLAG DS 1
TOOBIG DS 1
ADDR1 DS 2
ADDR2 DS 2
ADDR3 DS 2
MAX1 DS 2
MAX2 DS 2
RTYPE DS 3
RFILE DS 8
RDISK DS 3
DB '$'
TEMP DS RECSIZE
DISKBUF DS 128
COMPSIZE DS 1
CR EQU 0DH
LF EQU 0AH
RAM EQU $
END 100H