home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / cpmug / cpmug047.ark / COPYFAST.ASM < prev    next >
Encoding:
Assembly Source File  |  1984-04-29  |  21.7 KB  |  916 lines

  1. ;
  2. ;    COPYFAST    October 1980 version
  3. ;
  4. ;        Written by Chuck Weingart, after the
  5. ;        public domain Tarbell disk copy program
  6. ;        (does not require a Tarbell controller)
  7. ;
  8. ;    This program will copy the data area of one CP/M disk to
  9. ;    another (thats tracks 2 - 76), as fast as possible. All data
  10. ;    written is read back to verify that the write was successful,
  11. ;    and multiple tracks are copied in one pass, for speed. The
  12. ;    version as supplied assumes that the controller CRC checking is
  13. ;    sufficient for verification, but an assembly option allows
  14. ;    complete byte-by-byte comparison on the read back.
  15. ;
  16. ;    The program as supplied copies 12 tracks per pass, and must run
  17. ;    in a 42K or greater version of CP/M. The number of track
  18. ;    buffers, and hence the minimum size, can be changed to suit
  19. ;    your system. If re-assembled, it can copy four tracks at a pass
  20. ;    in a minimum (16K) system, and 18 tracks at a pass in a max-
  21. ;    imum system (64K).  Only CP/M standard CBIOS calls are
  22. ;    used to access the disk. No BDOS calls are used, so the BDOS
  23. ;    and CCP can be overlaid by the track buffers. No other CP/M
  24. ;    functions are assumed. N* CP/M or UCSD Pascal users should be
  25. ;    able to modify this program easily to run on their systems. 
  26. ;
  27. ;    COPYFAST will allow a disk to be copied on a one-drive system,
  28. ;    as an assembly option. A version with 18 buffers (64K) will
  29. ;    require only 5 complete swaps.
  30. ;
  31. ;    To invoke, just type: COPYFAST  The program will then request
  32. ;    the source and destination disks, (drives A - F) and give you a
  33. ;    chance to put in the correct disks in the drives before
  34. ;    continuing (or quit by entering CTRL-C). When done, the program
  35. ;    will ask if another pair of disks is to be copied, and the
  36. ;    process repeated.
  37. ;
  38. ;    COPYFAST runs on an 8080 or similar cpu, CP/M 1.3, 1.4 or 2.2,
  39. ;    or Cromemco (any) disk operating systems as supplied, and does
  40. ;    not use any particular type of controller or disk hardware,
  41. ;    other than the "standard" 77 track, 26 sector-per-track disk.
  42. ;    The latter two numbers can be easily changed in the source.
  43. ;
  44. ;    The program currently assumes that the disk controller can read
  45. ;    the disk in one revolution, but requires two revolutions to
  46. ;    write. This means that an entire track can be written and
  47. ;    checked in three revolutions. One alternate interleave table
  48. ;    is included in the source if this is not possible with your
  49. ;    hardware, and any sector interleave can be used by changing the
  50. ;    table. An assembly option is available to allow interleaved
  51. ;    reads, if your disk controller cannot keep up with the program.
  52. ;
  53. ;    This source can be assembled with the CP/M ASM or MAC.
  54. ;
  55. ;    This program has been run unaltered on a Micromation Doubler
  56. ;    disk controller with Shugart drives, a Tarbell single density
  57. ;    controller board, and a Cromemco 4FDC board (the latter two
  58. ;    use a WD 1771 chip) with Persci drives, and the worst copy
  59. ;    time was 122 seconds. Faster hardware means faster copies.
  60. ;
  61. ;    The only bad feedback I have gotten about this program so
  62. ;    far was a case where it didn't work on a CP/M 2.2 system
  63. ;    using 256 byte sectors. This turned out to be faulty logic in
  64. ;    the sample deblocking routine Digital Research is supplying!
  65. ;
  66.     ORG    0100H
  67. ;
  68. ; **************
  69. ;
  70. ;    Equates
  71. ;
  72. FALSE    EQU    0
  73. TRUE    EQU    NOT FALSE
  74. DUMYAD  EQU    0
  75. EXITCP    EQU    0        ; warm start return to CP/M
  76. CR    EQU    13
  77. LF    EQU    10
  78. CTRLC    EQU    3
  79. ;
  80. ; **************
  81. ;
  82. SINGLE    EQU    FALSE        ; TRUE for single-drive copy program
  83. RSKEW    EQU    FALSE        ; TRUE if read interleaving needed
  84. DOCOMP    EQU    FALSE        ; TRUE if byte-by-byte comparison
  85. ;                ; desired on read-after-write check
  86. NUMERR    EQU    10        ; number of error retries done
  87. ;
  88. ; **************
  89. ;
  90. BUFFNU  EQU    12-(DOCOMP AND 1)    ; the number of full track buffers
  91. ; that will fit in your system. With the current specification
  92. ; of 26 sectors per track, the program will require at least
  93. ; 39K for buffers alone, an so should run in a 42K system minimum.
  94. ; (12 * 128 * 26 = 39936. plus 3K for CBIOS and program)
  95. FIRSTRK EQU    2        ; the first track copied
  96. ;                ; Note: UCSD Pascal users should
  97. ;                ; begin copying at track 1
  98. LASTRK    EQU    77        ; the last track copied plus one
  99. SDLAST  EQU    26        ; the number of 128 byte sectors per track
  100. ;
  101. ; **************
  102. ;    A set of dummy branch points to the CBIOS that are
  103. ;    filled in by the VECTOR routine.
  104. ;
  105. START:
  106.     JMP    VECTOR        ; go initialize the branches
  107. WBOOT:
  108.     JMP    DUMYAD        ; not used
  109. CONST:
  110.     JMP    DUMYAD        ; not used
  111. CONIN:
  112.     JMP    DUMYAD
  113. CONOUT:
  114.     JMP    DUMYAD
  115. LIST:
  116.     JMP    DUMYAD        ; not used
  117. PUNCH:
  118.     JMP    DUMYAD        ; not used
  119. READER:
  120.     JMP    DUMYAD        ; not used
  121. HOME:
  122.     JMP    DUMYAD
  123. SELDIS:
  124.     JMP    DUMYAD
  125. SETTRK:
  126.     JMP    DUMYAD
  127. SETSCT:
  128.     JMP    DUMYAD
  129. SETDMA:
  130.     JMP    DUMYAD
  131. READ:
  132.     JMP    DUMYAD
  133. WRITE:
  134.     JMP    DUMYAD
  135. ;
  136. ; **************
  137. ;    initialize and put in copy limits
  138. ;
  139. BUFMSR:
  140.     LXI    SP,STKTOP
  141.     MVI    L,FIRSTRK    ; first track copied
  142.     MVI    H,LASTRK    ; last track + 1 copied
  143.     SHLD    TRKSRT
  144. ;
  145. ; **************
  146. ;    this is the point where the program returns to repeat
  147. ;    the copy. Everything is re-initialized.
  148. ;
  149. REPEAT:
  150.     LXI    SP,STKTOP    ; re-initialize stack
  151.     LXI    D,INIT
  152.     CALL    PRINT        ; start prompt sequence here
  153.     LXI    D,SOURCE
  154.     CALL    PRINT        ; ask for source drive
  155. SRCELU:
  156.     CALL    CONIN        ; read response (upper case)
  157.     CPI    CTRLC
  158.     JZ    EXITCP        ; CTRL-C means abort
  159.     ANI    5FH
  160.     CPI    'A'    ;41H
  161.     JC    SRCELU        ; bad value - less than A
  162.     CPI    'F'    ;46H
  163.     JZ    SETSOU
  164.     JC    SETSOU
  165.     JMP    SRCELU        ; bad value - greater than F
  166. SETSOU:
  167.     STA    SRCEME        ; save the source drive
  168.     IF    SINGLE
  169.     STA    OBJMES
  170.     ENDIF
  171.     SUI    'A'    ;41H
  172.     STA    SRCEDR        ; convert value to CP/M number
  173.     IF    NOT SINGLE
  174.     LXI    D,CRLF
  175.     CALL    PRINT
  176.     LXI    D,OBJECT    ; prompt for destination disk
  177.     CALL    PRINT
  178. OBJLUP:                ; read response
  179.     CALL    CONIN
  180.     CPI    CTRLC        ; CTRL-C means abort
  181.     JZ    EXITCP
  182.     ANI    5FH        ; convert to upper case
  183.     CPI    'A'    ;41H
  184.     JC    OBJLUP        ; bad value - less than A
  185.     CPI    'F'    ;46H
  186.     JZ    SETOBJ
  187.     JC    SETOBJ
  188.     JMP    OBJLUP        ; bad value - greater than F
  189. SETOBJ:
  190.     LXI    H,SRCEME    ; Cannot have a one drive copy
  191.     CMP    M
  192.     JZ    OBJLUP
  193.     STA    OBJMES        ; save the destination drive
  194.     SUI    'A'    ;41H
  195.     STA    OBJDRI        ; convert value to CP/M number
  196.     LXI    D,SIGNON
  197.     CALL    PRINT        ; now give chance to change disks
  198. ;                ; or give up
  199. AGIN:
  200.     CALL    CONIN        ; read response from keyboard
  201.     CPI    CTRLC
  202.     JZ    EXITCP        ; ctrl-C means quit
  203.     CPI    CR
  204.     JNZ    AGIN         ; CR means go. Ignore anything else
  205.     ENDIF
  206. ;
  207. ; **************
  208. ;    now go do it !
  209. ;
  210.     LXI    D,CRLF
  211.     CALL    PRINT        ; now start actual copy
  212.     CALL    COPY
  213.     LXI    D,DONMSG
  214.     CALL    PRINT        ; copy is now done, say so
  215. ;
  216. ;    end of this copy
  217. ;
  218. EXIT:
  219.     LXI    D,REPMES    ; ask if another copy is desired
  220.     CALL    PRINT
  221.     CALL    CONIN        ; read response, upper case
  222.     ANI    5FH
  223.     CPI    'R'        ; R means repeat
  224.     JZ    REPEAT
  225.     CPI    CR        ; carriage return means back to CP/M
  226.     JNZ    EXIT
  227.     MVI    C,0        ; set default disk back to A
  228.     CALL    SELDSK
  229.     JMP    EXITCP        ; and warmstart back to CP/M
  230. ;
  231. ; **************
  232. ;    convert value in A reg. to ASCII hex and print it
  233. ;
  234. PRTHEX:
  235.     PUSH    PSW        ; save for LSN
  236.     RAR
  237.     RAR            ; shift MSN nibble to LSN
  238.     RAR
  239.     RAR
  240.     CALL    PRTNBL        ; now print it
  241.     POP    PSW        ; and then do LSN
  242. PRTNBL:
  243.     ANI    0FH
  244.     ADI    '0'        ;convert to ASCII value
  245.     CPI    '0'+10        ; over 9 ?
  246.     JC    SML 
  247.     ADI    7        ; convert 10 to A, etc.
  248. SML:
  249.     MOV    C,A        ; move to C for BDOS call
  250.     CALL    CONOUT
  251.     RET
  252. ;
  253. ; **************
  254. ;
  255. ;    this is the main copy routine
  256. ;
  257. COPY:
  258.     LDA    SRCEDR        ; first, select source drive
  259.     MOV    C,A
  260.     CALL    SELDSK
  261.     CALL    HOME        ; home the disk first, in case
  262. ;                ; the controller requires it.
  263. ;                ; (this might be the first time
  264. ;                ; the drive has been used)
  265.     LDA    TRKSRT
  266.     STA    TRK        ; start with first track
  267.     MOV    C,A
  268.     CALL    SETTRK        ; move to that track
  269.     IF    NOT SINGLE
  270.     LDA    OBJDRI
  271.     MOV    C,A        ; now, select destination drive
  272.     CALL    SELDSK
  273.     CALL    HOME        ; and home that disk, in case
  274.     ENDIF
  275. ;
  276. ;    return here to continue copy
  277. ;
  278. RDLOOP:
  279.     LDA    TRK        ; note current track
  280.     STA    TRKSAV
  281.     XRA    A        ; reset error counter
  282.     STA    CMPERR
  283. TRYAGA:
  284.     LXI    D,TRKM        ; print the current starting track
  285.     CALL    PRINT        ; being copied
  286.     LDA    TRKSAV
  287.     CALL    PRTHEX
  288.     IF    SINGLE
  289.     LXI    D,SIGNON    ; now give operator chance to change disk
  290.     ENDIF
  291.     LDA    SRCEDR        ; select source drive
  292. ;
  293. ;    read  loop
  294. ;
  295.     CALL    STARTL        ; start the copy loop (reading source)
  296. LOOP1:
  297.     CALL    READT        ; read one track
  298.     JNZ    LOOP1        ; if not all tracks read, loop back
  299.     LDA    ERR1
  300.     ORA    A
  301.     JZ    LOOP1        ; if any read errors, dont bother writing
  302. ;
  303. ;    write loop
  304. ;
  305.     IF    SINGLE
  306.     LXI    D,OBJMSG    ; give chance to put in object disk
  307.     ENDIF
  308.     LDA    OBJDRI        ; now select destination disk
  309.     CALL    STARTL        ; start the write loop
  310. LOOP2:
  311.     CALL    WRITET        ; write one track (and readback check)
  312.     JZ    LOOP3        ; if all tracks written, go check errors
  313.     LDA    ERR1
  314.     ORA    A        ; not all done, but see if error already
  315.     JNZ    LOOP2
  316. ;
  317. ;    now see if any errors in the previous operations
  318. ;
  319. LOOP3:
  320.     LDA    ERR1        ; now check if any errors
  321.     ORA    A
  322.     JNZ    SKIP        ; jump if no errors at all
  323. ;
  324. ;    allow NUMERR errors before giving up
  325. ;
  326.     LDA    CMPERR        ; check the retry counter
  327.     INR    A
  328.     STA    CMPERR
  329.     CPI    NUMERR        ; normally ten retries max
  330.     JNZ    TRYAGA
  331.     LXI    D,MESGC        ; if maximum error count,
  332.     CALL    PRINT        ;   print message
  333.     LDA    TRK        ;   and set next track 
  334.     INR    A        ;   past track in error
  335.     SUI    BUFFNU
  336.     STA    TRKSAV
  337. ;
  338. ;    copied all tracks correctly (or NUMERR errors)
  339. ;
  340. SKIP:
  341.     LDA    TRKSAV        ; bump up track counter
  342.     ADI    BUFFNU
  343.     STA    TRK  
  344.     LXI    H,TRKSRT+1    ; see if copy operation is done
  345.     CMP    M
  346.     RNC
  347.     JNZ    RDLOOP        ; go back and do more
  348.     RET
  349. ;
  350. ; **************
  351. ;    this routine selects the disk, and initializes
  352. ;    the buffer address, buffer counter, and track counter,
  353. ;    and seeks to the right track
  354. ;
  355. STARTL:
  356.     IF    SINGLE
  357.     PUSH    PSW
  358.     CALL    PRINT        ; now give chance to change disks
  359. ;                ; or give up
  360. AGIN:
  361.     CALL    CONIN        ; read response from keyboard
  362.     CPI    CTRLC
  363.     JZ    EXITCP        ; ctrl-C means quit
  364.     CPI    CR
  365.     JNZ    AGIN         ; CR means go. Ignore anything else
  366.     POP    PSW
  367.     ENDIF
  368.     MOV    C,A        ; select the disk first
  369.     CALL    SELDSK
  370.     LXI    H,BUF0        ; load address of first buffer
  371.     SHLD    BUF0SA
  372.     MVI    A,10H        ; reset error flag
  373.     STA    ERR1 
  374.     MVI    A,BUFFNU    ; load number of buffers
  375.     STA    BUFFCO
  376.     LDA    TRKSAV        ; load first track copied
  377.     STA    TRK  
  378.     MOV    C,A
  379.     JMP    SETTRK        ; and set the track
  380. ;
  381. ; **************
  382. ;    set the DMA address (in HL)
  383. ;
  384. DMASET:
  385.     MOV    C,L        ; move HL to BC
  386.     MOV    B,H
  387.     PUSH    B        ; save result and call CBIOS
  388.     CALL    SETDMA
  389.     POP    B
  390.     RET
  391. ;
  392. ; **************
  393. ;    these are the disk error handling routines
  394. ;
  395. FAILR:
  396.     LXI    D,MESGD        ; read error message
  397.     JMP    DIE
  398. FAILW:
  399.     LXI    D,MESGE        ; write error message
  400. DIE:
  401.     CALL    PRINT        ; print the main error message
  402.     LXI    D,ERM 
  403.     CALL    PRINT
  404.     LDA    TRK          ; print the track number
  405.     CALL    PRTHEX
  406.     LXI    D,MESGB        ; print sector message
  407.     CALL    PRINT
  408.     LDA    SECTOR        ; and print sector
  409.     CALL    PRTHEX
  410.     LXI    D,DRIVE        ; print drive message
  411.     CALL    PRINT
  412.     LDA    CURRDI
  413.     ADI    'A'        ; convert drive number to ASCII
  414.     MOV    C,A
  415.     CALL    CONOUT        ; and finally print drive
  416.     XRA    A
  417.     STA    ERR1         ; note the error so this track is retried
  418.     JMP    ENDLUP
  419. ;
  420. ; **************
  421. ;    read the full track now, no interleaving
  422. ;
  423. READT:
  424.     MVI    A,10H        ; reset error flag so all tracks get read
  425.     STA    ERR1        ; before trying to write
  426.     IF    (NOT RSKEW)
  427.     LHLD    BUF0SA        ; first, get beginning of buffer
  428.     SHLD    DMAAD
  429.     ENDIF
  430.     MVI    C,0
  431.     MVI    B,SDLAST    ; initialize sector count
  432. RT3:
  433.     INR    C        ; increment sector counter
  434.     PUSH    B
  435.     IF    RSKEW
  436.     LXI    H,READTAB    ; find the interleaved sector number
  437.     MVI    B,0
  438.     DAD    B        ; using the READTAB
  439.     MOV    C,M
  440.     CALL    SETSEC        ; and set the sector
  441.     MVI    H,0
  442.     DCR    C        ; now compute the buffer location
  443.     MOV    L,C
  444.     DAD    H        ; corresponding to that sector
  445.     DAD    H
  446.     DAD    H        ; by multiplying by 128
  447.     DAD    H
  448.     DAD    H
  449.     DAD    H
  450.     DAD    H
  451.     XCHG
  452.     LHLD    BUF0SA        ; and then adding to the buffer start
  453.     DAD    D
  454.     CALL    DMASET        ; set the DMA and do the read
  455.     ENDIF
  456.     IF    (NOT RSKEW)
  457.     CALL    SETSEC        ; set the sector
  458.     LHLD    DMAAD
  459.     CALL    DMASET        ; set the DMA
  460.     LXI    H,128
  461.     DAD    B        ; bump up the DMA for next time
  462.     SHLD    DMAAD
  463.     ENDIF
  464.     CALL    READ        ; now read one sector
  465.     RAR
  466.     CC    FAILR        ; if returned 01, read error
  467.     POP    B
  468.     DCR    B        ; see if all sectors read
  469.     JNZ    RT3  
  470.     JMP    ENDLUP        ; return with complete track read
  471. ;
  472. ; **************
  473. ;    write the full track, with interleaving, and then
  474. ;    check it by reading it all back in
  475. ;
  476. WRITET:
  477.     LHLD    BUF0SA        ; first, get the beginning of buffer
  478.     SHLD    DMAAD
  479.     MVI    C,0
  480.     MVI    B,SDLAST    ; initialize sector counter
  481. WT3:
  482.     PUSH    B
  483.     LXI    H,WRITAB    ; find the interleaved sector number
  484.     MVI    B,0
  485.     DAD    B        ; using the WRITAB
  486.     MOV    C,M
  487.     CALL    SETSEC        ; and set the sector
  488.     MVI    H,0
  489.     DCR    C        ; now compute the buffer location
  490.     MOV    L,C
  491.     DAD    H        ; corresponding to that sector
  492.     DAD    H
  493.     DAD    H        ; by multiplying by 128
  494.     DAD    H
  495.     DAD    H
  496.     DAD    H
  497.     DAD    H
  498.     XCHG
  499.     LHLD    DMAAD        ; and then adding to the buffer start
  500.     DAD    D
  501.     CALL    DMASET        ; set the DMA and do the write
  502.     CALL    WRITE
  503.     RAR            ; if 01 returned, write error
  504.     CC    FAILW
  505.     POP    B
  506.     INR    C        ; increment sector count
  507.     DCR    B
  508.     JNZ    WT3        ; and loop back if not done
  509.     IF    DOCOMP AND (NOT RSKEW)
  510.     LXI    H,BUF1        ; first, get beginning of buffer
  511.     SHLD    DMAAD
  512.     ENDIF
  513.     MVI    C,0
  514.     MVI    B,SDLAST    ; reinitialize sector counts for read
  515. WT4:
  516.     INR    C        ; bump up sector counter
  517.     PUSH    B
  518.     IF    RSKEW
  519.     LXI    H,READTAB    ; find the interleaved sector number
  520.     MVI    B,0
  521.     DAD    B        ; using the READTAB
  522.     MOV    C,M
  523.     CALL    SETSEC        ; and set the sector
  524.     ENDIF
  525.     IF    RSKEW AND DOCOMP
  526.     MVI    H,0
  527.     DCR    C        ; now compute the buffer location
  528.     MOV    L,C
  529.     DAD    H        ; corresponding to that sector
  530.     DAD    H
  531.     DAD    H        ; by multiplying by 128
  532.     DAD    H
  533.     DAD    H
  534.     DAD    H
  535.     DAD    H
  536.     XCHG
  537.     LXI    H,BUF1        ; and then adding to the buffer start
  538.     DAD    D
  539.     CALL    DMASET        ; now set the read buffer
  540.     ENDIF
  541.     IF    (NOT RSKEW) AND DOCOMP
  542.     CALL    SETSEC        ; set the sector
  543.     LHLD    DMAAD
  544.     CALL    DMASET        ; set the DMA
  545.     LXI    H,128
  546.     DAD    B        ; bump up the DMA for next time
  547.     SHLD    DMAAD
  548.     ENDIF
  549.     IF    RSKEW AND (NOT DOCOMP)
  550.     LXI    H,BUF1        ; load the buffer address
  551.     CALL    DMASET        ; and set the read buffer
  552.     ENDIF
  553.     IF    (NOT RSKEW) AND (NOT DOCOMP)
  554.     CALL    SETSEC        ; now set the sector
  555.     LXI    H,BUF1
  556.     CALL    DMASET        ; and set the read buffer
  557.     ENDIF
  558.     CALL    READ 
  559.     RAR            ; was bit 0 set by disk error?
  560.     CC    FAILR
  561.     POP    B        ; no error, see if all sectors read
  562.     DCR    B
  563.     JNZ    WT4        ; if not all done, go back
  564.     IF    DOCOMP
  565.     LXI    B,128*SDLAST    ; now, compare the track read in
  566.     LHLD    BUF0SA
  567.     LXI    D,BUF1
  568. CMPLP:    LDAX    D        ; get read data
  569.     CMP    M
  570.     JNZ    CERR        ; and if not what was written, error
  571.     INX    H
  572.     INX    D        ; bump counters
  573.     DCX    B
  574.     MOV    A,C        ; and count BC down to zero
  575.     ORA    B
  576.     JNZ    CMPLP        ; if all done, return
  577.     JMP    ENDLUP
  578. ;
  579. ;    print read verify compare error
  580. ;
  581. CERR:    PUSH    H        ; save the goodies
  582.     PUSH    D
  583.     PUSH    B
  584.     LXI    D,MESGA        ; start the error message
  585.     CALL    PRINT
  586.     LDA    TRK        ; print the track number
  587.     CALL    PRTHEX
  588.     LXI    D,MESGB        ; print more
  589.     CALL    PRINT
  590.     POP    H        ; pop the down counter
  591.     DCX    H
  592.     DAD    H        ; multiply by 2 to get sectors left
  593.     MVI    A,SDLAST
  594.     SUB    H        ; subtract from total number of sectors
  595.     CALL    PRTHEX        ; to get sector number, and print it
  596.     LXI    D,MEM
  597.     CALL    PRINT        ; print second line
  598.     POP    H
  599.     MOV    A,M        ; get byte read
  600.     STA    DATA1        ; and save it
  601.     PUSH    H
  602.     MOV    A,H        ; print high order byte of address
  603.     CALL    PRTHEX
  604.     POP    H
  605.     MOV    A,L        ; print low order byte of address
  606.     CALL    PRTHEX
  607.     MVI    C,','
  608.     CALL    CONOUT        ; comma
  609.     POP    H
  610.     MOV    A,M        ; get byte written
  611.     STA    DATA2        ; and save it
  612.     PUSH    H
  613.     MOV    A,H        ; print high order byte of address
  614.     CALL    PRTHEX
  615.     POP    H
  616.     MOV    A,L        ; print low order byte of address
  617.     CALL    PRTHEX
  618.     LXI    D,DATAM        ; print data header
  619.     CALL    PRINT
  620.     LDA    DATA1        ; print byte read
  621.     CALL    PRTHEX
  622.     MVI    C,','        ; comma
  623.     CALL    CONOUT
  624.     LDA    DATA2        ; print byte written
  625.     CALL    PRTHEX
  626.     XRA    A
  627.     STA    ERR1        ; note the error so this track is retried
  628.     ENDIF
  629. ;
  630. ;    this routine is used to check if another track is
  631. ;    to be read/written: it increments buffer address and
  632. ;    track counter, and decrements the buffer counter.
  633. ;    Then, it terminates the loop if all buffers are
  634. ;    full or the last track has been processed (Z flag set)
  635. ;
  636. ENDLUP:
  637.     LDA    ERR1        ; now check if any errors
  638.     ORA    A        ; and return if so
  639.     RZ
  640.     LDA    TRK        ; increment track
  641.     INR    A
  642.     LXI    H,TRKSRT+1    ; check if last track
  643.     CMP    M
  644.     RZ            ; return if last track
  645.     STA    TRK  
  646.     MOV    C,A        ; move to the next track
  647.     CALL    SETTRK
  648.     LXI    H,BUFFCO    ; decrement buffer counter
  649.     DCR    M
  650.     RZ            ; return if all buffers full/empty
  651.     LXI    D,128*SDLAST
  652.     LHLD    BUF0SA        ; increment buffer address
  653.     DAD    D
  654.     SHLD    BUF0SA
  655.     ORI    255        ; non-zero to indicate more
  656.     RET        
  657. ;
  658. ; **************
  659. ;    this routine writes messages to the console.
  660. ;    Message address is in DE, and terminates on a $
  661. ;    The BDOS call is not used here because BDOS may
  662. ;    be destroyed by the track buffers
  663. ;
  664. PRINT:
  665.     LDAX    D        ; get the character
  666.     CPI    '$'    ;24H
  667.     RZ            ; quit if $
  668.     PUSH    D
  669.     MOV    C,A        ; send it to the console
  670.     CALL    CONOUT
  671.     POP    D        ; go check next character
  672.     INX    D
  673.     JMP    PRINT
  674. ;
  675. ; **************
  676. ;    set the next sector to be used, and save that
  677. ;    number for the error routine, in case
  678. ;
  679. SETSEC:
  680.     MOV    A,C        ; save the sector number
  681.     STA    SECTOR
  682.     PUSH    B        ; save regs, in case
  683.     CALL    SETSCT        ; now go set the sector
  684.     POP    B
  685.     RET
  686. ;
  687. ; **************
  688. ;    set the disk to be used, and save that
  689. ;    for the error routine, in case
  690. ;
  691. SELDSK:
  692.     MOV    A,C        ; save the disk number
  693.     STA    CURRDI
  694.     CALL    SELDIS        ; now select the disk
  695.     RET
  696. ;
  697. ; **************
  698. ;    all messages here for convenience in disassembling
  699. ;
  700. DONMSG:
  701.     DB    CR,LF,CR,LF,'******** '
  702.     DB    '   COPY COMPLETE    ********$'
  703. DRIVE:
  704.     DB    '  DRIVE $'
  705. ERM:
  706.     DB    CR,LF,'+ ERROR ON TRACK $'
  707. MESGB:
  708.     DB    '(HEX) SECTOR (HEX)$'
  709. MESGC:
  710.     DB    CR,LF,'**********     PERMANENT '
  711.     DB    '     **********$'
  712. MESGD:
  713.     DB    CR,LF,'+ READ ERROR  $'
  714. MESGE:
  715.     DB    CR,LF,'+ WRITE ERROR  $'
  716. SIGNON:
  717.     DB    CR,LF,'+SOURCE ON '
  718. SRCEME:
  719.     DB    0            ; will be filled in later
  720.     IF    NOT SINGLE
  721.     DB    CR,LF,'+OBJECT ON '
  722. OBJMES:
  723.     DB    0            ; will be filled in later
  724.     ENDIF
  725. SINOFF:
  726.     DB    CR,LF,'+TYPE <RET>$'
  727.     IF    SINGLE
  728. OBJMSG:
  729.     DB    CR,LF,'+OBJECT ON '
  730. OBJMES:
  731.     DB    0            ; will be filled in later
  732.     DB    CR,LF,'+TYPE <RET>$'
  733.     ENDIF
  734. REPMES:
  735.     DB    CR,LF,'TYPE RETURN (OR R TO REPEAT)$'
  736. CRLF:
  737.     DB    CR,LF,'$'
  738. INIT:
  739.     DB    CR,LF,'COPY PROGRAM FOR '
  740.     DB    'CP/M$'
  741. SOURCE:
  742.     DB    CR,LF,'SOURCE DRIVE? A,B,C,D,E,F$'
  743.     IF    NOT SINGLE
  744. OBJECT:
  745.     DB    CR,LF,'OBJECT DRIVE? A,B,C,D,E,F$'
  746.     ENDIF
  747. TRKM:
  748.     DB    CR,LF,'COPYING TRACK $'
  749. ;
  750.     IF    DOCOMP
  751. MESGA:
  752.     DB    CR,LF,'+ MEMORY COMPARE ERROR ON TRACK $'
  753. MEM:
  754.     DB    CR,LF,'MEMORY ADDRESS   $'
  755. DATAM:
  756.     DB    '           DATA     $'
  757.     ENDIF
  758. ;
  759. ; **************
  760. ;
  761. ;    This is the sector interleave table. If you want the
  762. ;    program to work, all sector numbers must be here
  763. ;    somewhere. The sectors are listed in the order they
  764. ;    will be written to disk.
  765. ;    NOTE:  the peculiar ordering is due to the fact that
  766. ;    some disk controllers cannot switch between writing
  767. ;    sector 26 and reading sector 1 in time - so the table
  768. ;    begins with 25, proceed up every other sector, and
  769. ;    ends with number 24. While the head is passing sectors
  770. ;    25 and 26, the program will be switching to read back
  771. ;    the entire track. There is generally no problem
  772. ;    starting with sector 25, because simply moving the
  773. ;    head after the previous read on most drives will take
  774. ;    about one half revolution. This table was determined em-
  775. ;    perically using Shugart drives and doing actual tests
  776. ;    with two different types of controller boards, and it
  777. ;    is the fastest variation found. Change at your own risk.
  778. ;
  779. ;
  780. WRITAB:
  781.     DB    25,1,3,5,7,9,11,13,15,17,19,21,23
  782.     DB    26,2,4,6,8,10,12,14,16,18,20,22,24
  783. ;
  784. ;    the following table is recommended for those whose controllers
  785. ;    cannot write even every other sector:
  786. ;    There is no peculiar starting sector here because the inter-
  787. ;    leave ends on sector 24, and that is the same as the table 
  788. ;    above. This might be a better choice for a "universal" table.
  789. ;
  790. ;    DB    1,4,7,10,13,16,19,22,25
  791. ;    DB    2,5,8,11,14,17,20,23,26
  792. ;    DB    3,6,9,12,15,18,21,24
  793. ;
  794.     IF    RSKEW
  795. ;
  796. ;    this is the read skew table, if needed. The same general
  797. ;    considerations as the write skew table apply here also,
  798. ;    but the table should start with sector 1. Both the read
  799. ;    and the read-after write use this table. As you can see,
  800. ;    the write and read interleaving doesn't have to be the
  801. ;    same. (Are you listening, Digital Research?)
  802. ;
  803. READTAB:
  804.     DB    1,3,5,7,9,11,13,15,17,19,21,23,25
  805.     DB    2,4,6,8,10,12,14,16,18,20,22,24,26
  806.     ENDIF
  807. ;
  808. ; **************
  809. ;    this is one-time code to initialize the branch table
  810. ;    to the CBIOS vectors. Only those vectors used are
  811. ;    initialized. This routine occupies the lowest area
  812. ;    of the stack, and may be clobbered by the stack during
  813. ;    operation, but it is used only once, so who cares.
  814. ;
  815. VECTOR:
  816.     LHLD    1        ; get warm boot address
  817.     SPHL            ; and save it in SP for DAD
  818.     LXI    H,6
  819.     DAD    SP
  820.     SHLD    CONIN+1
  821. ;
  822.     LXI    H,9
  823.     DAD    SP
  824.     SHLD    CONOUT+1
  825. ;
  826.     LXI    H,15H
  827.     DAD    SP
  828.     SHLD    HOME+1
  829. ;
  830.     LXI    H,18H
  831.     DAD    SP
  832.     SHLD    SELDIS+1
  833. ;
  834.     LXI    H,1BH
  835.     DAD    SP
  836.     SHLD    SETTRK+1
  837. ;
  838.     LXI    H,1EH
  839.     DAD    SP
  840.     SHLD    SETSCT+1
  841. ;
  842.     LXI    H,21H
  843.     DAD    SP
  844.     SHLD    SETDMA+1
  845. ;
  846.     LXI    H,24H
  847.     DAD    SP
  848.     SHLD    READ+1
  849. ;
  850.     LXI    H,27H
  851.     DAD    SP
  852.     SHLD    WRITE+1
  853. ;
  854.     LXI    SP,STKTOP
  855.     LXI    H,BUFMSR
  856.     SHLD    START+1
  857.     PCHL
  858. ;
  859. ; **************
  860. ;
  861. STK:                ; stack
  862.     DS    32
  863. STKTOP:
  864.     DB    0
  865. TRKSRT:                ; first and last+1 track numbers
  866.     DB    0,0
  867. BUF0SA:                ; buffer address
  868.     DB    0,0
  869. TRKSAV:                ; track save area during read and write
  870.     DB    0
  871. BUFFCO:                ; buffer counter
  872.     DB    0
  873. CMPERR:                ; number of disk errors
  874.     DB    0
  875. TRK:                ; current track
  876.     DB    0
  877. SRCEDR:                ; source drive
  878.     IF    NOT SINGLE
  879.     DB    0
  880.     ENDIF
  881. OBJDRI:                ; destination drive
  882.     DB    0
  883. CURRDI:                ; drive for current operation
  884.     DB    0
  885. DMAAD:                ; DMA address for current operation
  886.     DB    0,0
  887. ERR1:                ; error flag (0 = error)
  888.     DB    0
  889. SECTOR:                ; sector number for current operation
  890.     DB    0
  891. ;
  892. ; **************
  893. ;    the track buffers. BUFEND must not overlay the BIOS !
  894. ;
  895. ;    BUF0 is where all input tracks are read
  896. ;
  897. BUF0:    DS    128*SDLAST*BUFFNU
  898. ;
  899. ;    BUF1 is where the read-after-write is performed
  900. ;
  901.     IF    DOCOMP
  902. DATA1:
  903.     DS    1        ; used in compare
  904. DATA2:
  905.     DS    1
  906. BUF1:
  907.     DS    128*SDLAST    ; space for a full track read
  908.     ENDIF
  909.     IF    NOT DOCOMP
  910. BUF1:
  911.     DS    128        ; just one sector for CRC only
  912.     ENDIF
  913. BUFEND: DS    1
  914. ;
  915.     END
  916.