home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
simtel
/
sigm
/
vols000
/
vol063
/
copyfst3.asm
< prev
next >
Wrap
Assembly Source File
|
1984-04-29
|
26KB
|
1,151 lines
; 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 <RET> 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 <RET> TO CONTINUE, OR CONTROL-C TO EXIT: $'
ENDIF
REPMES:
DB CR,LF,'TYPE <RET> 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