; COPYFAST.ASM Version 3.5 ; (See the DOC file for change history) ; ORG 0100H ; ; ; Equates ; FALSE EQU 0 ; define false TRUE EQU NOT FALSE; define true ; EXITCP EQU 0 ; warm start return to CP/M FCB EQU 5CH ; default FCB address ; CR EQU 0DH ; ASCII Carriage return LF EQU 0AH ; ASCII line feed CTRLC EQU 3 ; ASCII control-C ; ; User-modifiable switches ; SINGLE EQU FALSE ; TRUE for single drive copy program RSKEW EQU FALSE ; TRUE if read interleaving needed ; ; Note: change READTAB if TRUE DOCOMP EQU TRUE ; TRUE if byte-by-byte comparison ; ; desired on read-after-write check TRSKW EQU TRUE ; TRUE if track skewing is wanted ; ; on non-interleaved reads ; ; (RSKEW MUST be false) WRSWCH EQU FALSE ; TRUE if CP/M 2.2 block/deblock ; ; routines need various values in ; ; reg. C during writes. See WRTAB ; NUMERR EQU 4 ; number of error retries done ; BUFFNU EQU 0 ; the number of full track buffers ; that will fit in your system. This figure includes ; the space used by the read-back buffers, if used ; (minimum 2). If zero, the number of buffers will ; be automatically computed at execution. ; TSKEW EQU 6 ; Amount of track-to-track skew ; ; (if TRSKW is TRUE and RSKEW is FALSE) ; ; Should be less than SDLAST ; ; The next two values specify the copy range, and the program ; can be run in other ways by the parameter (first character ; of the first filename) given when COPYFAST is first invoked: ; ; All 0-(Lastrk-1) Entire disk ; Data Firstrk-(Lastrk-1) CP/M data area ; First Firstrk CP/M directory track ; Last (Lastrk-1) Last track on disk ; One 1 Track one, UCSD directory ; Pascal 1-(Lastrk-1) UCSD Pascal data area ; System 0-(Firstrk-1) CP/M bootstrap ; Zero 0 Track zero, UCSD bootstrap ; (Note: only complete tracks are copied) ; The default range, currently Firstrk to Lastrk-1, is given ; in the two values at TRKSRT. ; FIRSTRK EQU 2 ; the first data track copied. ; ; The bootstrap is assumed to be ; ; on tracks 0 to Firstrk-1 LASTRK EQU 77 ; the last track copied plus one ; DIFFTRK EQU 0 ; difference between first source ; ; track and the first object track. ; ; (applies only when default range ; ; is used) ; SDLAST EQU 26 ; the number of sectors per track ; ; Also determines the lengths of ; ; WRTAB, READTAB, and WRITAB ; SECSIZ EQU 128 ; number of bytes per sector. ; WRCODE EQU 2 ; value passed to sector write rtn ; ; in reg. C if WRSWCH is FALSE IF TRSKW AND RSKEW TRSKW SET FALSE ; option is wrong ENDIF ; ; A set of dummy branch points to the CBIOS that are ; filled in by the VECTOR routine. ; START: JMP VECTOR ; go initialize the branches WBOOT: JMP $-$ ; not used CONST: JMP $-$ CONIN: JMP $-$ CONOUT: JMP $-$ LIST: JMP $-$ ; not used PUNCH: JMP $-$ ; not used READER: JMP $-$ ; not used HOME: JMP $-$ SELDIS: JMP $-$ SETRAK: JMP $-$ SETSCT: JMP $-$ SETDMA: JMP $-$ READ: JMP $-$ WRITE: JMP $-$ ; ; Useful constants placed here for finding easily ; These can be changed using DDT to alter some of ; the characteristics of the program to suit your ; taste. ; TRKSRT: ; default first and last+1 track numbers ; ; Can be changed at run time DB FIRSTRK DB LASTRK BUFFNMB: ; max. number of buffers DB BUFFNU SRCTRAK: ; source track - object track DB DIFFTRK ; ; This is the point where the program returns to repeat the ; copy. Everything is re-initialized. ; REPEAT: LXI SP,STKTOP ; re-initialize stack LXI D,SOURCE CALL PRINT ; ask for source drive SRCELU: CALL CONIN ; read response (upper case) CPI CTRLC JZ EXIT ; CTRL-C means abort ANI 5FH CPI 'A' ;41H JC SRCELU ; bad value - less than A CPI 'F' ;46H JZ SETSOU JC SETSOU JMP SRCELU ; bad value - greater than F SETSOU: STA SRCEME ; save the source drive IF SINGLE STA OBJMES ENDIF SUI 'A' ;41H STA SRCEDR ; convert value to CP/M number LDA SRCEME CALL CONOUT ; echo value to console IF NOT SINGLE LXI D,OBJECT ; prompt for destination disk CALL PRINT OBJLUP: ; read response CALL CONIN CPI CTRLC ; CTRL-C means abort JZ EXIT ANI 5FH ; convert to upper case CPI 'A' ;41H JC OBJLUP ; bad value - less than A CPI 'F' ;46H JZ SETOBJ JC SETOBJ JMP OBJLUP ; bad value - greater than F SETOBJ: LXI H,SRCEME ; Cannot have a one drive copy CMP M JZ OBJLUP STA OBJMES ; save the destination drive SUI 'A' ;41H STA OBJDRI ; convert value to CP/M number LDA OBJMES CALL CONOUT ; echo object drive LXI D,SIGNON CALL PRINT ; now give chance to change disks ; ; or give up AGIN: CALL CONIN ; read response from keyboard CPI CTRLC JZ EXIT ; ctrl-C means quit CPI CR JNZ AGIN ; CR means go. Ignore anything else ENDIF ; ; now go do it ! ; LXI D,CRLF CALL PRINT ; now start actual copy CALL COPY LXI D,DONMSG CALL PRINT ; copy is now done, say so ; ; end of this copy ; EXIT: LXI SP,STKTOP ; re-initialize stack LDA SRCEDR ; first, select source drive MOV C,A CALL SELDSK CALL HOME ; home the disk in case IF NOT SINGLE LDA OBJDRI MOV C,A ; now, select destination drive CALL SELDSK CALL HOME ; and home that disk, in case ENDIF LXI D,REPMES ; ask if another copy is desired CALL PRINT CALL CONIN ; read response, upper case ANI 5FH CPI 'R' ; R means repeat JZ REPEAT CPI CR ; carriage return means back to CP/M JNZ EXIT MVI C,0 ; set default disk back to A CALL SELDSK JMP EXITCP ; and warmstart back to CP/M ; ; convert value in A reg. to ASCII hex and print it ; PRTHEX: PUSH PSW ; save for LSN RAR RAR ; shift MSN nibble to LSN RAR RAR CALL PRTNBL ; now print it POP PSW ; and then do LSN PRTNBL: ANI 0FH ADI '0' ;convert to ASCII value CPI '0'+10 ; over 9 ? JC SML ADI 7 ; convert 10 to A, etc. SML: MOV C,A ; move to C for BDOS call CALL CONOUT RET ; ; ; this is the main copy routine ; COPY: LDA SRCEDR ; first, select source drive MOV C,A CALL SELDSK CALL HOME ; home the disk first, in case ; ; the controller requires it. ; ; (this might be the first time ; ; the drive has been used) LDA TRKSRT CALL SETTRK ; now start with first track IF NOT SINGLE LDA OBJDRI MOV C,A ; now, select destination drive CALL SELDSK CALL HOME ; and home that disk, in case ENDIF ; ; return here to continue copy ; RDLOOP: LDA TRK ; note current track STA TRKSAV XRA A ; reset error counter STA CMPERR LXI D,TRKM ; print the current starting track CALL PRINT ; being copied LDA TRKSAV CALL PRTHEX TRYRDA: IF SINGLE LXI D,SIGNON ; now give operator chance to change disk ENDIF LDA SRCEDR ; select source drive ; ; read loop ; CALL STARTL ; start the copy loop (reading source) LOOP1: CALL READT ; read one track JZ LOOP4 ; if all tracks read, go check errors LDA ERR1 ORA A ; not all done, but see if error already JNZ LOOP1 ; and go try another track ; ; now see if any errors in the previous operations ; LOOP4: LDA ERR1 ; now check if any errors ORA A JNZ RDSKIP ; jump if no errors at all MVI A,10H STA ERR1 ; reset error flag ; ; allow NUMERR errors before giving up ; LDA CMPERR ; check the retry counter INR A STA CMPERR CPI NUMERR ; normally ten retries max JNZ LOOP1 ; WAS TRYRDA LXI D,MESGC ; if maximum error count, CALL PRINT ; print message XRA A STA CMPERR ; full track error, reset error counter CALL ENDLUP JNZ LOOP1 ; now bump up track and see if done ; ; write loop ; RDSKIP: XRA A ; reset error counter STA CMPERR TRYAGA: IF SINGLE LXI D,OBJMSG ; give chance to put in object disk ENDIF LDA OBJDRI ; now select destination disk CALL STARTL ; start the write loop LOOP2: CALL WRITET ; write one track (and readback check) JZ LOOP3 ; if all tracks written, go check errors LDA ERR1 ORA A ; not all done, but see if error already JNZ LOOP2 ; ; now see if any errors in the previous operations ; LOOP3: LDA ERR1 ; now check if any errors ORA A JNZ SKIP ; jump if no errors at all ; ; allow NUMERR errors before giving up ; LDA CMPERR ; check the retry counter INR A STA CMPERR CPI NUMERR ; normally ten retries max JNZ TRYAGA LXI D,MESGC ; if maximum error count, CALL PRINT ; print message LDA BUFFNMB MOV H,A LDA TRK ; and set next track INR A ; past track in error SUB H STA TRKSAV ; ; copied all tracks correctly (or NUMERR errors) ; SKIP: LDA BUFFNMB ; get number of buffers MOV H,A LDA TRKSAV ; bump up track counter ADD H STA TRK LXI H,TRKSRT+1 ; see if copy operation is done CMP M RNC JNZ RDLOOP ; go back and do more RET ; ; This routine selects the disk, and initializes the buffer ; address, buffer counter, and track counter,and seeks to the ; right track. ; STARTL: IF SINGLE CALL HOME ; Home the disk for a deblocking CBIOS ; ; to get a chance to flush the buffer CALL PRINT ; now give chance to change disks ; ; or give up AGIN: CALL CONIN ; read response from keyboard CPI CTRLC JZ EXIT ; CTRL-C means quit CPI CR JNZ AGIN ; CR means go. Ignore anything else ENDIF IF NOT SINGLE MOV C,A ; select the disk first CALL SELDSK ENDIF IF TRSKW XRA A ; zero out track sector skew STA TSECT STA TBUFF ; zero out coresponding buffer addr STA TBUFF+1 ENDIF LXI H,BUF0 ; load address of first buffer SHLD BUF0SA MVI A,10H ; reset error flag STA ERR1 LDA BUFFNMB ; load number of buffers STA BUFFCO LDA TRKSAV ; load first track copied ; ; set the track to be used, and add offset if source ; drive. Save track number for error routine. ; SETTRK: STA TRK ; save current track IF (NOT SINGLE) LDA CURRDI ; check drive MOV C,A LDA SRCEDR ; is it source CMP C LDA TRK ; if object, skip JNZ SETTR0 MOV C,A ; now get difference LDA SRCTRAK ADD C ; and do correction SETTR0: ENDIF MOV C,A ; now go set track JMP SETRAK ; ; set the DMA address (in HL) ; DMASET: MOV C,L ; move HL to BC MOV B,H PUSH B ; save result and call CBIOS CALL SETDMA POP B RET ; ; these are the disk error handling routines ; FAILR: LXI D,MESGD ; read error message JMP DIE FAILW: LXI D,MESGE ; write error message DIE: CALL PRINT ; print the main error message LXI D,ERM CALL PRINT LDA TRK ; print the track number CALL PRTHEX LXI D,MESGB ; print sector message CALL PRINT LDA SECTOR ; and print sector CALL PRTHEX LXI D,DRIVE ; print drive message CALL PRINT LDA CURRDI ADI 'A' ; convert drive number to ASCII MOV C,A CALL CONOUT ; and finally print drive XRA A STA ERR1 ; note the error so this track is retried CALL CONST ORA A ; see if any console input present JZ ENDLUP CALL CONIN ; yes, see if aborting CPI CTRLC JZ EXIT ; die if CTRL-C was hit JMP ENDLUP ; ; read the full track now, no interleaving ; READT: CALL CONST ORA A ; see if any console input present JZ READT0 CALL CONIN ; yes, see if aborting CPI CTRLC JZ EXIT ; die if CTRL-C was hit READT0: IF (NOT RSKEW) AND (NOT TRSKW) LHLD BUF0SA ; first, get beginning of buffer SHLD DMAAD ENDIF IF TRSKW LHLD BUF0SA ; first, get beginning of buffer XCHG LHLD TBUFF ; and correct for skew DAD D SHLD DMAAD LDA TSECT ; initialize first sector MOV C,A ENDIF IF (NOT TRSKW) MVI C,0 ; initialize first sector ENDIF MVI B,SDLAST ; initialize sector count RT3: IF TRSKW MOV A,C ; check for skew too big CPI SDLAST JC RT4 ; jump if sector within range XRA A MOV C,A ; out of range, back to sector 1 LHLD BUF0SA SHLD DMAAD RT4: ENDIF IF RSKEW INR C ; increment sector counter PUSH B LXI H,READTAB-1 ; find the interleaved sector number MVI B,0 DAD B ; using the READTAB MOV C,M CALL SETSEC ; and set the sector MVI H,0 DCR C ; now compute the buffer location MOV L,C ; DAD H ; corresponding to that sector DAD H DAD H ; by multiplying by 128 DAD H DAD H ; The number of DAD H instructions DAD H ; MUST correspond to the buffer size DAD H ; i.e. 7 DADs means 128 byte (2^7) ; XCHG LHLD BUF0SA ; and then adding to the buffer start DAD D CALL DMASET ; set the DMA and do the read ENDIF IF (NOT RSKEW) INR C ; increment sector counter PUSH B CALL SETSEC ; set the sector LHLD DMAAD CALL DMASET ; set the DMA LXI H,SECSIZ DAD B ; bump up the DMA for next time SHLD DMAAD ENDIF CALL READ ; now read one sector RAR CC FAILR ; if returned 01, read error POP B DCR B ; see if all sectors read JNZ RT3 IF TRSKW LHLD TBUFF ; bump up skewed buffer LXI D,SECSIZ*TSKEW DAD D ; add the skew SHLD TBUFF LDA TSECT ; now bump starting sector ADI TSKEW STA TSECT ; and put it back SBI SDLAST JC ENDLUP ; jump if sector within range STA TSECT LHLD TBUFF LXI D,-SDLAST*SECSIZ; correct sector start and DAD D SHLD TBUFF ; buffer skew address ENDIF JMP ENDLUP ; return with complete track read ; ; Write the full track, with interleaving, and then check it ; by reading it all back in. ; WRITET: CALL CONST ORA A ; see if any console input present JZ WRITE0 CALL CONIN ; yes, see if aborting CPI CTRLC JZ EXIT ; die if CTRL-C was hit WRITE0: LHLD BUF0SA ; first, get the beginning of buffer SHLD DMAAD MVI C,0 MVI B,SDLAST ; initialize sector counter WT3: PUSH B LXI H,WRITAB ; find the interleaved sector number MVI B,0 DAD B ; using the WRITAB MOV C,M CALL SETSEC ; and set the sector MVI H,0 DCR C ; now compute the buffer location MOV L,C ; DAD H ; corresponding to that sector DAD H DAD H ; by multiplying by 128 DAD H DAD H ; NOTE: see comments in RT3 for DAD H ; changing this code for other DAD H ; than 128 byte sectors ; XCHG LHLD DMAAD ; and then adding to the buffer start DAD D CALL DMASET ; set the DMA and do the write IF NOT WRSWCH MVI C,WRCODE ; value for CP/M 2.2 routine ENDIF IF WRSWCH POP B ; get sector number PUSH B LXI H,WRTAB-1 ; find the C reg. value for this MVI B,0 DAD B ; sector using the WRTAB MOV C,M ENDIF CALL WRITE RAR ; if 01 returned, write error CC FAILW POP B INR C ; increment sector count DCR B JNZ WT3 ; and loop back if not done IF DOCOMP AND (NOT RSKEW) LXI H,BUF1 ; first, get beginning of buffer SHLD DMAAD ENDIF MVI C,0 MVI B,SDLAST ; reinitialize sector counts for read WT4: INR C ; bump up sector counter PUSH B IF RSKEW LXI H,READTAB-1 ; find the interleaved sector number MVI B,0 DAD B ; using the READTAB MOV C,M CALL SETSEC ; and set the sector ENDIF IF RSKEW AND DOCOMP MVI H,0 DCR C ; now compute the buffer location MOV L,C DAD H ; corresponding to that sector DAD H DAD H ; by multiplying by 128 DAD H DAD H ; (2 ^ 7 = 128) DAD H DAD H XCHG LXI H,BUF1 ; and then adding to the buffer start DAD D CALL DMASET ; now set the read buffer ENDIF IF (NOT RSKEW) AND DOCOMP CALL SETSEC ; set the sector LHLD DMAAD CALL DMASET ; set the DMA LXI H,SECSIZ DAD B ; bump up the DMA for next time SHLD DMAAD ENDIF IF RSKEW AND (NOT DOCOMP) LXI H,BUF1 ; load the buffer address CALL DMASET ; and set the read buffer ENDIF IF (NOT RSKEW) AND (NOT DOCOMP) CALL SETSEC ; now set the sector LXI H,BUF1 CALL DMASET ; and set the read buffer ENDIF CALL READ RAR ; was bit 0 set by disk error? CC FAILR POP B ; no error, see if all sectors read DCR B JNZ WT4 ; if not all done, go back IF DOCOMP LXI B,SECSIZ*SDLAST ; now, compare the track read in LHLD BUF0SA LXI D,BUF1 CMPLP: LDAX D ; get read data CMP M JNZ CERR ; and if not what was written, error INX H INX D ; bump counters DCX B MOV A,C ; and count BC down to zero ORA B JNZ CMPLP ; if all done, return JMP ENDLUP ; ; print read verify compare error ; CERR: PUSH H ; save the goodies PUSH D PUSH B LXI D,MESGA ; start the error message CALL PRINT LDA TRK ; print the track number CALL PRTHEX LXI D,MESGB ; print more CALL PRINT POP H ; pop the down counter DCX H DAD H ; multiply by 2 to get sectors left MVI A,SDLAST SUB H ; subtract from total number of sectors CALL PRTHEX ; to get sector number, and print it LXI D,MEM CALL PRINT ; print second line POP H MOV A,M ; get byte read STA DATA1 ; and save it PUSH H MOV A,H ; print high order byte of address CALL PRTHEX POP H MOV A,L ; print low order byte of address CALL PRTHEX MVI C,',' CALL CONOUT ; comma POP H MOV A,M ; get byte written STA DATA2 ; and save it PUSH H MOV A,H ; print high order byte of address CALL PRTHEX POP H MOV A,L ; print low order byte of address CALL PRTHEX LXI D,DATAM ; print data header CALL PRINT LDA DATA1 ; print byte read CALL PRTHEX MVI C,',' ; comma CALL CONOUT LDA DATA2 ; print byte written CALL PRTHEX XRA A STA ERR1 ; note the error so this track is retried ENDIF ; ; This routine is used to check if another track is to be ; read/written: it increments buffer address and track ; counter, and decrements the buffer counter. Then, it ; terminates the loop if all buffers are full or the last ; track has been processed (Z flag set). ; ENDLUP: LDA ERR1 ; now check if any errors ORA A ; and return if so RZ LDA TRK ; increment track INR A LXI H,TRKSRT+1 ; check if last track CMP M RZ ; return if last track CALL SETTRK LXI H,BUFFCO ; decrement buffer counter DCR M RZ ; return if all buffers full/empty LXI D,SECSIZ*SDLAST LHLD BUF0SA ; increment buffer address DAD D SHLD BUF0SA ORI 255 ; non-zero to indicate more RET ; ; this routine writes messages to the console. Message ; address is in DE, and terminates on a $. The BDOS call is ; not used here because BDOS may be destroyed by the track ; buffers ; PRINT: LDAX D ; get the character CPI '$' ;24H RZ ; quit if $ PUSH D MOV C,A ; send it to the console CALL CONOUT POP D ; go check next character INX D JMP PRINT ; ; set the next sector to be used, and save that ; number for the error routine, in case ; SETSEC: MOV A,C ; save the sector number STA SECTOR PUSH B ; save regs, in case CALL SETSCT ; now go set the sector POP B RET ; ; set the disk to be used, and save that ; for the error routine, in case ; SELDSK: MOV A,C ; save the disk number STA CURRDI JMP SELDIS ; now select the disk ; ; all messages here for convenience in disassembling ; DONMSG: DB CR,LF,'*** COPY COMPLETE ***$' DRIVE: DB ', DRIVE $' ERM: DB CR,LF,'+ ERROR ON TRACK (HEX)$' MESGB: DB ' SECTOR (HEX)$' MESGC: DB CR,LF,'++PERMANENT $' MESGD: DB CR,LF,'+ READ ERROR $' MESGE: DB CR,LF,'+ WRITE ERROR $' SIGNON: DB CR,LF,'SOURCE ON ' SRCEME: DB 0 ; will be filled in later IF NOT SINGLE DB ': OBJECT ON ' OBJMES: DB 0 ; will be filled in later DB ':' ENDIF SINOFF: DB CR,LF,'TYPE TO CONTINUE, OR CONTROL-C TO EXIT: $' IF SINGLE OBJMSG: DB CR,LF,'OBJECT ON ' OBJMES: DB 0 ; will be filled in later DB ':' DB CR,LF,'TYPE TO CONTINUE, OR CONTROL-C TO EXIT: $' ENDIF REPMES: DB CR,LF,'TYPE OR "R", TO REPEAT COPY: $' CRLF: DB CR,LF,'$' SOURCE: DB CR,LF,'SOURCE DRIVE (A THRU F): $' IF NOT SINGLE OBJECT: DB CR,LF,'OBJECT DRIVE (A THRU F): $' ENDIF TRKM: DB CR,LF,'COPYING TRACK $' ; IF DOCOMP MESGA: DB CR,LF,'+ MEMORY COMPARE ERROR ON TRACK (HEX)$' MEM: DB CR,LF,'+ MEMORY ADDRESS $' DATAM: DB ' (OBJ,SRC) DATA $' ENDIF ; ; This is the sector interleave table. If you want the ; program to work, all sector numbers must be here somewhere. ; WRITAB: ; ; Interleave table for very fast controllers ; DB 25,26,1,2,3,4,5,6,7,8,9,10,11,12 DB 13,14,15,16,17,18,19,20,21,22,23,24 ; ; IF WRSWCH ; ; This is the write switch table. The values in this table ; are passed to the sector write routine of CP/M 2.2 in ; reg. C when each write occurs. This table is modified if ; and only if some particular pattern is needed for your ; blocking routine to work as fast or as well as possible. ; Refer to the CP/M 2.2 Alteration Guide for more details. ; WRTAB: DB 2,2,2,2,2,2,2,2,2,2,2,2,2 DB 2,2,2,2,2,2,2,2,2,2,2,2,2 ENDIF ; IF RSKEW ; ; This is the read skew table, if needed. The same general ; considerations as the write skew table apply here also, but ; the table should start with sector 1. Both the read and the ; read-after write use this table. As you can see, the write ; and read interleaving doesn't have to be the same. ; READTAB: DB 1,3,5,7,9,11,13,15,17,19,21,23,25 DB 2,4,6,8,10,12,14,16,18,20,22,24,26 ENDIF ; ; This is the initialization code, and occupies the lowest area ; of the stack, and may be clobbered by the stack during operation, ; but it is used only once. (The stack is about 32 bytes long) ; VECTOR: LHLD 1 ; get bottom of CBIOS MOV B,H LXI D,SECSIZ*SDLAST ; get size of buffers LXI H,BUF0 ; start checking where buffer starts VECT0: DAD D ; add buffer size to buffer addr MOV A,H CMP B ; check hi order byte if high JZ VECT1 ; or equal JNC VECT1 LDA BUFTMP ; buffer fits, add one to count INR A STA BUFTMP ; and store JMP VECT0 ; ; the stack ; DS 8 STKTOP: DB 0 ; ; variables ; BUF0SA: ; buffer address DB 0,0 TRKSAV: ; track save area during read and write DB 0 BUFFCO: ; buffer counter DB 0 CMPERR: ; number of disk errors DB 0 TRK: ; current track DB 0 SRCEDR: ; source drive IF NOT SINGLE DB 0 ENDIF OBJDRI: ; destination drive DB 0 CURRDI: ; drive for current operation DB 0 DMAAD: ; DMA address for current operation DB 0,0 ERR1: ; error flag (0 = error) DB 0 SECTOR: ; sector number for current operation DB 0 ; IF TRSKW TSECT: DB 0 ; skewed sector start for track TBUFF: DB 0,0 ; skewed buffer address ENDIF ; ; the track buffers. BUFEND must not overlay the BIOS ! ; ; BUF1 is where the read-after-write is performed ; IF DOCOMP DATA1: DS 1 ; used in compare DATA2: DS 1 BUF1: DS SECSIZ*SDLAST ; space for a full track read ENDIF ; IF NOT DOCOMP BUF1: DS SECSIZ ; just one sector for CRC only ENDIF ; ; BUF0 is where all input tracks are read ; Tho space for only one track is allocated here, ; the program will use BUFFNU track buffers, or ; up to the CBIOS, whichever is smaller ; BUF0: DS SECSIZ*SDLAST ; ORG BUF1 ; ; This is one-time code to initialize the branch table to ; the CBIOS vectors. Only those vectors used are initialized. ; Placed here so that it wont get clobbered by the stack ; VECT1: LHLD 1 ; get warm boot address SPHL ; and save it in SP for DAD LXI H,3 DAD SP SHLD CONST+1 ; LXI H,6 DAD SP SHLD CONIN+1 ; LXI H,9 DAD SP SHLD CONOUT+1 ; LXI H,15H DAD SP SHLD HOME+1 ; LXI H,18H DAD SP SHLD SELDIS+1 ; LXI H,1BH DAD SP SHLD SETRAK+1 ; LXI H,1EH DAD SP SHLD SETSCT+1 ; LXI H,21H DAD SP SHLD SETDMA+1 ; LXI H,24H DAD SP SHLD READ+1 ; LXI H,27H DAD SP SHLD WRITE+1 ; ; Now check what kind of copy is wanted ; LXI SP,STKTOP ; initial stack LXI D,INIT CALL PRINT ; start program LHLD TRKSRT LDA FCB+1 ; get character of parameter ANI 5FH CPI 0 ; check for default JZ COPYDEF MOV B,A XRA A ; no track shift STA SRCTRAK MOV A,B CPI 'A' ; check for All JZ COPYALL CPI 'D' ; check for Data JZ COPYDAT CPI 'F' ; check for First JZ COPYFIR CPI 'L' ; check for Last JZ COPYLAS CPI 'O' ; check for One JZ COPYONE CPI 'P' ; check for Pascal JZ COPYPAS CPI 'S' ; check for System JZ COPYSYS CPI 'Z' ; check for Zero JZ COPYZER LXI D,CALLERR ; got a bad value CALL PRINT JMP EXITCP COPYALL: MVI H,LASTRK ; All MVI L,0 JMP COPYDEF COPYDAT: MVI H,LASTRK ; Data MVI L,FIRSTRK JMP COPYDEF COPYFIR: MVI H,FIRSTRK+1 ; First MVI L,FIRSTRK JMP COPYDEF COPYLAS: MVI H,LASTRK ; Last MVI L,LASTRK-1 JMP COPYDEF COPYONE: MVI H,2 ; One MVI L,1 JMP COPYDEF COPYPAS: MVI H,LASTRK ; Pascal MVI L,1 JMP COPYDEF COPYSYS: MVI H,FIRSTRK ; System MVI L,0 JMP COPYDEF COPYZER: MVI H,1 ; Zero MVI L,0 ; ; The one time finish - up routine ; COPYDEF: SHLD TRKSRT LXI D,BGMES1 ; Now print message giving copy range CALL PRINT LDA TRKSRT CALL PRTHEX ; print first track LXI D,BGMES2 CALL PRINT LDA TRKSRT+1 ; print last track DCR A CALL PRTHEX LDA BUFFNMB ; load desired buffer number ORA A JZ VECT3 ; if no autosize, put in IF DOCOMP DCR A ; subtract one for compare buffer STA BUFFNMB ENDIF LXI H,BUFTMP CMP M ; compare against number found JZ VECT2 JC VECT2 ; branch if smaller LXI D,BUFERR CALL PRINT ; print out error msg LDA BUFTMP CALL PRTHEX ; print out buffer number VECT3: LDA BUFTMP STA BUFFNMB ; put in smaller buffer number VECT2: LXI H,REPEAT ; go to mainline code now SHLD START+1 PCHL ; BUFTMP: DB 0 ; temporary storage for buffer counter INIT: DB CR,LF,'FAST DISKETTE COPY PROGRAM, VER. 3.5$' BUFERR: DB CR,LF,'CP/M IS TOO SMALL - BUFFER SPACE REDUCED: $' CALLERR: DB CR,LF,'INVALID PARAMETER .. VALID COPYFAST PARAMETERS ARE' DB CR,LF,'ALL, DATA, FIRST, LAST, ONE, PASCAL, SYSTEM, ZERO$' BGMES1: DB CR,LF,'COPYING FROM TRACK $' BGMES2: DB ' TO TRACK $' ; ; END