; Disk Copy Utility from CPM Users Group ; Modified for CDOS by Trevor Marshall ; Elec Eng Dept ; Uni W.A. ; Jan 1980 ; ; Rewritten for 2.36 CDOS (Double Density) ; Automatic disk label checking, ; Sept 1980, Trevor Marshall ; ; ORG 100H JP COPY ; BIOS FUNCTION CALLING TABLE WBOOT: CALL BIOSGO CONST: CALL BIOSGO CONIN: CALL BIOSGO CONOUT: CALL BIOSGO LIST: CALL BIOSGO PUNCH: CALL BIOSGO READER: CALL BIOSGO HOME: CALL BIOSGO SELDSK: CALL BIOSGO SETTRK: CALL BIOSGO SETSEC: CALL BIOSGO SETDMA: CALL BIOSGO READ: CALL BIOSGO WRITE: CALL BIOSGO BIOSGO: EX (SP),HL ; Get call addr in HL, save HL on stack PUSH DE ; Save DE EX DE,HL ; Move call addr to DE LD HL,(1) ; Get BIOS entry address ADD HL,DE ; Add call addr to entry addr LD DE,[WBOOT+3]; Get start of table ; Subtract DE from HL in 8080 code (for compatibility) LD A,E CPL LD E,A LD A,D CPL LD D,A ;Now have 1s compl of DE INC DE ;2s compl ADD HL,DE ;Done! POP DE ; Restore DE EX (SP),HL ; Restore HL, put jump addr on stack RET ; Jump to BIOS routine ; Original coding by: ; L.E. HUGHES 8080SDC 77/10/29 ; ; Modified by Trevor Marshall ; E.E.Dept ; Uni W.A. ; ; to work with any sized CDOS system ; and to prompt for drives ; ; MISC SYMBOLS LF EQU 0AH ;LINE FEED CR EQU 0DH ;CARRIAGE RETURN ITRK EQU 0 ;INITIAL TRACK TO COPY LTRK EQU 76 ;LAST TRACK TO COPY BDOS: EQU 5 ; COPY: LD HL,0 ADD HL,SP LD (OLDSP),HL LD SP,STACK+64 ; ; ALLOW USER TO MOUNT DISK(S) BEFORE PROCEEDING AGAIN: LD C,96H ;Turn drive motors off CALL BDOS ; LD HL,CRLF CALL WASC LD HL,STR1 ;PRINT 'Source Disk Drive,etc' CALL WASC CALL RACC CP 3 ;CTL-C ABORT JP Z,EXIT1 CALL WACC SUB 'A' JP C,AGAIN ;Invalid entry CP 3 JP NC,AGAIN LD (SOURCE),A LD HL,CRLF CALL WASC ; Now prompt for destination LD HL,STR1A CALL WASC CALL RACC CALL WACC SUB 'A' JP C,AGAIN CP 3 JP NC,AGAIN LD (DEST),A LD HL,SOURCE LD C,(HL) CP A,C JP Z,AGAIN LD HL,CRLF CALL WASC ; Now we must determine whether single or double ; density disks are in use, and check that we ; are not trying to copy between incompatible formats ; ; We will use the CDOS 1BH cal (Get disk allocation vec) ; as the disk labels are read before this call returns. ; This call returns the number of clusters in DE ; ; LD C,0DH ;Reset CDOS CALL BDOS ; LD A,(SOURCE) ;Select Source drive LD E,A LD C,0EH CALL BDOS ; LD C,1BH ;Get CLUSTER.SIZE size map CALL BDOS ;for first disk LD (ACLUSTERS),DE ; LD A,(DEST) ;Select Dest. drive LD E,A LD C,0EH CALL BDOS ; LD C,1BH ;Get cluster size map CALL BDOS ;for second disk LD (BCLUSTERS),DE ;save it LD HL,(ACLUSTERS) ;get it again XOR A SBC HL,DE ;to compare them LD A,L OR A,L ;see if HL is zero JP NZ,SEND.DIFF.ERROR ;no ; Now display the disks in use and branch to copy S/R LD A,E ;LSB of # clusters CP A,0FEH ;D/D S/S? JP Z,SEND.DD.MSG CP A,060H ;D/D D/S JP Z,SEND.DS.MSG CP A,0F3H ;S/D S/S JR Z,SEND.SD.MSG CP A,0F7H ;S/D D/S JP Z,SEND.DS.MSG ;Dont use this often, ;I wont bother to tidy it up JP SEND.NON.STANDARD.MSG ; SEND.SD.MSG: LD DE,SD.MSG LD C,9 CALL BDOS ;Print it LD A,1BH ;Max sectors/trk+1 LD (SECTORS.PER.TRACK),A LD HL,80H ;The sector size for S/D LD (DMA.INCR),HL LD HL,SDMAP-1 ;Point to sector map LD (SECTOR.MAP.ADDRESS),HL JR MAIN ; SEND.DIFF.ERROR: LD DE,DIFF.ERROR.MSG L5: LD C,9 CALL BDOS JP AGAIN SEND.NON.STANDARD.MSG: LD DE,NON.STANDARD.ERROR.MSG JR L5 ; ; BEGIN SINGLE DENSITY LOOP MAIN: LD DE,MSG1 ;Finish disk msg LD C,9 CALL BDOS ; LD HL,STR4 ;PRINT HEADER CALL WASC LD HL,STR5 CALL WASC ; LD A,ITRK ;INITIAL TRACK NUMBER LD (TRKNO),A COPY2: ; Check for CTL-C abort CALL CONST CP 0 JP Z,FT1 CALL CONIN AND 5FH CP 3 ;CTL-C JP Z,COPYX FT1: LD A,(SOURCE) ;Select Source Disk LD C,A CALL SELDSK LD A,(TRKNO) ;SET TRACK NUMBER LD C,A CALL SETTRK LD HL,TBUF ;SET INITIAL DMA ADDRESS LD (DMAPTR),HL LD A,1 ;INITIAL SECTOR NUMBER LD (SECNO),A COPY3: LD A,(SECNO) ;SET SECTOR NUMBER LD HL,(SECTOR.MAP.ADDRESS) LD D,0 LD E,A ADD HL,DE LD C,(HL) CALL SETSEC LD HL,(DMAPTR) ;SET DMA ADDRESS LD B,H LD C,L CALL SETDMA CALL READ ;READ SECTOR LD HL,(DMAPTR) ;ADD INCR TO DMA PTR PUSH DE LD DE,(DMA.INCR) ADD HL,DE POP DE LD (DMAPTR),HL LD HL,SECNO ;ADD 1 TO SECTOR NUMBER INC (HL) LD A,(HL) LD HL,SECTORS.PER.TRACK CP A,(HL) ;LOOP THRU ENTIRE TRACK JP C,COPY3 ; WRITE TBUF TO CURRENT TRACK ON DISK C LD A,(DEST) ;Select dest dsk LD C,A CALL SELDSK LD A,(TRKNO) ;SET TRACK NUMBER LD C,A CALL SETTRK LD HL,TBUF ;SET DMA ADDRESS LD (DMAPTR),HL LD A,1 ;SET INITIAL SECTOR NUMBER LD (SECNO),A COPY4: LD A,(SECNO) ;SET SECTOR NUMBER LD HL,(SECTOR.MAP.ADDRESS) LD D,0 LD E,A ADD HL,DE LD C,(HL) CALL SETSEC LD HL,(DMAPTR) ;SET DMA ADDRESS LD B,H LD C,L CALL SETDMA CALL WRITE ;WRITE SECTOR LD HL,(DMAPTR) ;ADD INCR TO DMAPTR PUSH DE LD DE,(DMA.INCR) ADD HL,DE POP DE LD (DMAPTR),HL LD HL,SECNO ;ADD 1 TO SECTOR NUMBER INC (HL) LD A,(HL) LD HL,SECTORS.PER.TRACK CP A,(HL) ;LOOP THRU ENTIRE TRACK JP C,COPY4 ; ADVANCE TO NEXT TRACK LD A,'*' CALL WACC LD HL,TRKNO INC (HL) LD A,(HL) CP LTRK+1 ;LOOP THRU ENTIRE DISK JP C,COPY2 ; ALL DONE SINGLE DENSITY COPY5: LD HL,STR2 ;PRINT 'COPY COMPLETE' JP COPY6 COPYX: LD HL,STR3 ;PRINT 'COPY ABORTED' COPY6: CALL WASC JP COPY EXIT1: LD HL,(OLDSP) ;EXIT TO CP/M LD SP,HL JP 0 ; WASC - WRITE ASCII STRING TO CONSOLE WASC: LD A,(HL) OR A RET Z CALL WACC INC HL JP WASC ; WACC - WRITE ASCII CHARACTER TO CONSOLE WACC: PUSH HL PUSH DE PUSH BC PUSH AF LD C,A CALL CONOUT POP AF POP BC POP DE POP HL RET ; RACC - READ ASCII CHARACTER FROM CONSOLE RACC: PUSH HL PUSH DE PUSH BC CALL CONIN AND 5FH ;Make LC=UC POP BC POP DE POP HL RET SEND.DS.MSG: LD A,2 LD (SIZE.FLAG),A LD DE,DS.MSG JR V1 SEND.DD.MSG: LD A,1 LD (SIZE.FLAG),A LD DE,DD.MSG V1: LD C,9 CALL BDOS ; ;Calculate the 2's complement of the block capacity of disk LD HL,(ACLUSTERS) ;Get # on disk ADD HL,HL ; x2 (16 blocks per cluster) ADD HL,HL ; x4 ADD HL,HL ; x8 ADD HL,HL ; x16 EX DE,HL ;into DE SCF ;add 1 to block capacity LD HL,0 SBC HL,DE ;have 2's complement LD (BLOCK.CAPACITY),HL ; We cannot use the same approach for D/D disks ; as for S/D, so we will use a LOGICAL BLOCK ; approach. Each block is 128 Bytes, I have ; ~32K free RAM, so will read ~28K at a time ; (160h, 352d blocks). ; DOUBLE: LD DE,MSG1 ;Finish disk msg LD C,9 CALL BDOS ; LD A,(SIZE.FLAG) ;Is it D/D or D/S ? CP 1 ;D/D only? JR NZ,JJ1 ;No, D/S D/D LD HL,STR6 ;PRINT D/D HEADER CALL WASC LD HL,STR7 CALL WASC JR JJ2 ; JJ1: LD HL,STR8 ;PRINT D/S HEADER CALL WASC LD HL,STR9 CALL WASC ; JJ2: LD HL,0 ;INITIAL BLOCK NUMBER LD (BLKNO),HL ; ; Begin the main (fill buffer) loop DCOPY2: LD HL,TBUF-80H LD (DMAPTR),HL ;Initialize DMA ptr LD DE,(BLKNO) ;Block # to DE LD (FIRSTBLK),DE ;Save first blocK # ; ; Check for CTL-C abort CALL CONST CP 0 JP Z,DFT1 CALL CONIN AND 5FH CP 3 ;CTL-C JP Z,COPYX ;and abort ; ; Get enough blocks to fill buffer, one at a time DFT1: LD HL,(DMAPTR) ;ADD INCR TO DMA PTR LD DE,80H ;Block size is 128 bytes ADD HL,DE LD (DMAPTR),HL ; Are we at end of Buffer, LD DE,-8000H ADD HL,DE ;C means .LT. 8000H JR C,DCOPY6 ;If so write the buffer ;Set DMA for DOS LD DE,(DMAPTR) LD C,1AH CALL BDOS ; Is this block beyond the end of disk? LD DE,(BLOCK.CAPACITY) LD HL,(BLKNO) ADD HL,DE ;see if DE & HL are .EQ. LD A,L OR A,H JP Z,COPY5 ;Z = done copy ; Read the block LD DE,(BLKNO) ;Block # to DE LD C,83H LD HL,SOURCE LD B,(HL) ;disk # to B INC B ;For BDOS SET 7,B ;Set interleaved read CALL BDOS ; Handle error status CP A,1 ;I/O error JP Z,COPYX CP A,2 ;Illegal request JP Z,COPYX CP A,3 ;Illegal Block JP EQ,COPYX ; Now incr the block # LD HL,(BLKNO) INC HL LD (BLKNO),HL ; Is this beyond the end of disk? LD DE,(BLOCK.CAPACITY) ADD HL,DE ;see if DE & HL are .EQ. LD A,L OR A,H JR NZ,DFT1 ;Z = done read ; Must decr last BLKNO so write does not overrun DCOPY6: LD HL,(BLKNO) DEC HL LD (BLKNO),HL ; ;Write the buffer if full DCOPY3: ; Begin the main (empty buffer) loop SENDSTAR: LD A,'*' CALL WACC ; LD HL,TBUF-80H LD (DMAPTR),HL ;Initialize DMA ptr ; Save last BLK # and get first # LD HL,(BLKNO) LD (LASTBLK),HL ; LD DE,(FIRSTBLK) ;First Block # to DE LD (BLKNO),DE ;Save blocK # ; ; Put enough blocks to empty buffer, one at a time DFT2: LD HL,(DMAPTR) ;ADD INCR TO DMA PTR LD DE,80H ;Block size is 128 bytes ADD HL,DE LD (DMAPTR),HL ; Is this beyond the end of disk? ; LD DE,(BLOCK.CAPACITY) ; LD HL,(BLKNO) ;get the block # ; ADD HL,DE ;see if DE & HL are .EQ. ; LD A,L ; OR A,H ; JP Z,COPY5 ;Z = done copy ; Have we written too many blocks LD HL,(LASTBLK) LD DE,(BLKNO) XOR A SBC HL,DE JP C,DCOPY2 ;C if BLKNO > LASTBLK ; On exit BLKNO will be LASTBLK + 1 ; Which is OK for next read loop ;Set DMA for DOS LD DE,(DMAPTR) LD C,1AH CALL BDOS ; ; Write the block LD DE,(BLKNO) ;Block # to DE LD C,84H LD HL,DEST LD B,(HL) ;disk # to B INC B ;for BDOS SET 7,B ;Set interleaved read CALL BDOS ; Handle error status CP A,1 ;I/O error JP Z,COPYX CP A,2 ;Illegal request JP Z,COPYX CP A,3 ;Illegal Block,done JP EQ,COPYX ; Now incr the block # LD HL,(BLKNO) INC HL LD (BLKNO),HL ; JR DFT2 ;Loop for more blocks ; ; ; OUTPUT STRINGS STR1: DEFB 'Source disk drive (A to D) ?',CR,LF DB 'Type if you make an error --- ',0 CRLF: DB cr,lf,0 STR1A: db 'Ensure you mount the disks before' db 'typing the : ',cr,lf DB 'Destination drive (A to D) ? --- ',0 STR2: DEFB CR,LF,'Copy completed',CR,LF,0 STR3: DEFB '***** Copy aborted *****',CR,LF,0 STR4: DEFB 0DH,0AH,0AH DB ' 1 2 3 4' DEFB ' 5 6 7',CR,LF,0 STR5: DEFB '01234567890123456789012345678901234567890' DEFB '123456789012345678901234567890123456',CR,LF,0 STR6: DB 0DH,0AH,0AH DB ' 1',CR,LF,0 STR7: DB '01234567890123456',CR,LF,0 STR8: DB 0DH,0AH,0AH DB ' 1 2 3 4',CR,LF,0 STR9: DB '01234567890123456789012345678901234567890',CR,LF,0 ; DD.MSG: DB 0DH,0AH,'Double Density$' SD.MSG: DB 0DH,0AH,'Single Density$' DS.MSG: DB 0DH,0AH,'Double sided Double Density$' MSG1: DB ' disks mounted.$' DIFF.ERR.MSG: DB 0DH,0AH,'**** DISK LABEL ERROR ***$' NON.STANDARD.ERROR.MSG: DB 0DH,0AH,'**** DISKS ARE NON STANDARD ****$' ; ; SECTOR MAP SDMAP: DB 1,7,0DH,13H,19H,5,0BH,11H,17H,3,9,0FH,15H,2 DB 8,0EH,14H,1AH,6,0CH,12H,18H,4,0AH,10H,16H ;DDMAP: DB 1,0CH,7,2,0DH,8,3,0EH,9,4,0FH,0AH,5,10H,0BH,6 ; OLDSP: DEFS 2 STACK: DEFS 64 ACLUSTERS: DS 2 ;Clusters on DSK 1 BCLUSTERS: DS 2 ;Clusters on DSK 2 BLOCK.CAPACITY: DS 2 ;Blocks on the disk SIZE.FLAG: DS 1 ;D/D OR D/S ? SECTORS.PER.TRACK: DS 1 DMA.INCR: DS 2 SECTOR.MAP.ADDRESS: DS 2 SOURCE: DS 1 ;SOURCE DRIVE # DEST: DS 1 ;DEST DRIVE # TRKNO: DEFS 1 ;TRACK NUMBER SECNO: DEFS 1 ;SECTOR NUMBER DMAPTR: DEFS 2 ;DMA POINTER BLKNO: DS 2 FIRSTBLK: DS 2 LASTBLK: DS 2 ; ; THe buffer for D/D is assumed 45 K long TBUF: DEFS 26*128 ;TRACK BUFFER END 100H