mirror of https://github.com/wwarthen/RomWBW.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1325 lines
37 KiB
1325 lines
37 KiB
;
|
|
; Copy Slice - COPYSL.COM
|
|
;
|
|
; CP/M Command - That will allow the contents of a disk
|
|
; slice to be copied (completely) to another slice.
|
|
; This depends on running under CP/M 2.2 on a machine with
|
|
; an RomWBW HBIOS, and works with both hd1k (modern)
|
|
; and hd512 (legacy) disk formats
|
|
;
|
|
; Versions History
|
|
; ----------------
|
|
; 0.1 - Initial Version written by Mark Pruden
|
|
; 0.2 - Added support for /v (verify) option.
|
|
; 0.3 - Refresh CP/M disk buffers after completion
|
|
; 0.4 - Correct slice fit within partition calculation
|
|
; ----------------
|
|
;
|
|
.ORG 100H
|
|
jp start
|
|
;
|
|
.include "crc.asm" ; comparison of data blocks, used for verify
|
|
.include "cio.asm" ; bdos IO routines
|
|
.include "bdos.asm" ; bdos general routines
|
|
.include "hbios.asm" ; hbios routines
|
|
;
|
|
; -------------------------
|
|
;
|
|
mainblkbuf .EQU 8000h ; Main Disk IO Buffer
|
|
mainblkbuf2 .EQU 9000h ; Disk Buff for Read Verify
|
|
;
|
|
xf_sectorcount .EQU 8 ; sectors to transfer per R/W / Verify
|
|
xf_blockbytes .EQU 512 * xf_sectorcount ; bytes in main transfer buffer
|
|
;
|
|
; =========================================
|
|
; START OF MAIN PROGRAM
|
|
; =========================================
|
|
;
|
|
start:
|
|
; welcome message
|
|
ld hl,msg_welcome
|
|
call prtstr
|
|
|
|
; parse command line args
|
|
call process_args ; call the cli arg handler
|
|
jp nz, exit ; args invalid exit at this point
|
|
|
|
; Inits Disk IO routines. Bank Ram
|
|
call initdiskio
|
|
|
|
media_disc1:
|
|
; sense the media
|
|
ld a, (args_src_diskunit) ; cmd line parameter
|
|
ld ix, sourcemedia
|
|
call sensemedia
|
|
jp nz, media_err
|
|
|
|
; calc the slice details
|
|
ld a, (args_src_slice) ; cmd line parameter
|
|
call slicecalc ; lba of chosen slice
|
|
jp nz, slice_def_err
|
|
|
|
; print details of the media
|
|
call iprtstr
|
|
.DB 13,10,"Source",0
|
|
call printdetails
|
|
|
|
media_disc2:
|
|
; sense the media
|
|
ld a, (args_target_diskunit) ; cmd line parameter
|
|
ld ix, targetmedia
|
|
call sensemedia
|
|
jp nz, media_err
|
|
|
|
; calc the slice details
|
|
ld a, (args_target_slice) ; cmd line parameter
|
|
call slicecalc ; lba of chosen slice
|
|
jp nz, slice_def_err
|
|
|
|
; print details of the media
|
|
call iprtstr
|
|
.DB "Target",0
|
|
call printdetails
|
|
|
|
check_hard_disc:
|
|
; ponters to the MCB's of source target
|
|
ld ix, sourcemedia
|
|
ld iy, targetmedia
|
|
|
|
; check media type are for HD only, others not supported
|
|
ld a, (ix+off_mediaid)
|
|
call isaharddrive
|
|
jp z, device_type_err
|
|
|
|
; check media type are for HD only, others not supported
|
|
ld a, (iy+off_mediaid)
|
|
call isaharddrive
|
|
jp z, device_type_err
|
|
|
|
check_layout:
|
|
; compare disk types hd1k hd512 are identical for source/target
|
|
ld a, (ix+off_mediaid)
|
|
cp (iy+off_mediaid)
|
|
jp nz, device_diff_err
|
|
|
|
check_same:
|
|
; check the source drive / slice are NOT the same
|
|
ld a, (ix + off_diskunit)
|
|
cp (iy + off_diskunit)
|
|
jr nz, compare_same ; disk units are differemt
|
|
ld a, (ix + off_slice)
|
|
cp (iy + off_slice)
|
|
jr nz, compare_same ; slices are different
|
|
jp args_same_err ; they are the same
|
|
compare_same:
|
|
|
|
; if target Slice=0 and hd512
|
|
ld a, (iy + off_slice) ; check the target slice
|
|
cp 0 ; is Zero
|
|
jr nz, startprocess ; ignore if slice number is Zero
|
|
ld a, (iy + off_mediaid)
|
|
cp MID_HD ; IS hd512
|
|
jr nz, startprocess ; ignore if NOT hd512
|
|
|
|
; warn the user about to overite partition
|
|
ld hl, msg_overite_partition
|
|
call prtstr
|
|
|
|
startprocess:
|
|
; if full copy - SKIP THIS
|
|
ld a, ( args_option_f ) ; full disk copy argument
|
|
or a
|
|
jr nz, fulldiskcopy ; if option then compute full disk
|
|
|
|
startdirscan:
|
|
ld hl, msg_parse_dir ; Processing Directory
|
|
CALL prtstr
|
|
|
|
; init DE HL with sector of first dir entry
|
|
CALL finddir ; used by processdir
|
|
|
|
; read and proess 512 directory entries in 32kb buffer
|
|
CALL processdir
|
|
JP NZ, io_error
|
|
|
|
; for hd1k - do it twice
|
|
ld a, (src_mediaid)
|
|
cp MID_HD
|
|
JR Z, startdirscan1 ; hd512 only need to scan 1x 32k block
|
|
|
|
; read and proess SECOND 512 directory entries in 32kb buffer
|
|
CALL processdir ; process second 512 dir entries
|
|
JP NZ, io_error
|
|
|
|
startdirscan1:
|
|
; check we have a legal directory
|
|
ld a, (illegaldir) ; flag indicating we found illegal dir
|
|
cp 0
|
|
JR Z, startdirscan2 ; legal dir so just continue
|
|
; illegal
|
|
ld hl, msg_cant_find_dir
|
|
CALL prtstr ; illegal directory, so print msg
|
|
JR fulldiskcopy ; and do a full disk copy
|
|
|
|
startdirscan2:
|
|
; compute number of blocks to transfer based on directory
|
|
CALL compute_xfer ; result is in total16xfer
|
|
|
|
; write info about dir entries found, blocks to copy
|
|
CALL compute_print
|
|
|
|
; now ask for confirmation
|
|
JR userconfirm ; skip the full disk copy
|
|
|
|
fulldiskcopy:
|
|
; copy everthing from source drive
|
|
ld ix, sourcemedia
|
|
CALL compute_full ; result is in total16xfer
|
|
|
|
userconfirm:
|
|
; unless an option is provided for unassisted
|
|
ld a, (args_option_u) ; was the arg set
|
|
or a
|
|
JR NZ, startcopy ; then skip confirmation
|
|
|
|
; are you sure
|
|
ld hl, msg_confirm
|
|
CALL prtstr
|
|
CALL getchr
|
|
cp 'Y'
|
|
JR z, startcopy
|
|
cp 'y'
|
|
JR Z, startcopy
|
|
JP exit
|
|
|
|
startcopy:
|
|
CALL prtcrlf
|
|
ld hl,msg_copy_main
|
|
CALL prtstr
|
|
|
|
; init start time of process
|
|
CALL sysgetseconds
|
|
ld (statstarttime), hl ; save the Hl register
|
|
|
|
copydatablocks:
|
|
; loop ALL the blocks
|
|
|
|
; READ
|
|
CALL readblock
|
|
JP NZ, io_error
|
|
|
|
; WRITE
|
|
CALL writeblock
|
|
JP C, crc_error
|
|
JP NZ, io_error
|
|
|
|
; display progress and count blocks
|
|
CALL progress_bar
|
|
|
|
; Loop Again ?
|
|
ld bc, (total4kxfr)
|
|
dec bc
|
|
ld a, b
|
|
or c
|
|
JR Z, finishcopy ; escape out once copied
|
|
ld (total4kxfr), bc ; store the remainder
|
|
JR copydatablocks ; go round again
|
|
|
|
finishcopy:
|
|
CALL iprtstr
|
|
.DB 13, 10, "Copied ", 0
|
|
|
|
; print if verified also
|
|
ld a, (args_option_v)
|
|
or a ; check the verify opton is set
|
|
JR Z, finishcopy1 ; not doing verify
|
|
CALL iprtstr
|
|
.DB "and verified ", 0
|
|
|
|
finishcopy1:
|
|
; print blocks copied
|
|
ld hl, (stat_blocks_written)
|
|
CALL prtdecword
|
|
CALL iprtstr
|
|
.DB " kBytes in ", 0
|
|
|
|
; subtrat end time from start time and display
|
|
CALL sysgetseconds
|
|
ld DE, (statstarttime) ; save the Hl register
|
|
xor A ; clear carry bits
|
|
sbc HL, DE ; subtract start time - IGNORE BORROW
|
|
CALL prtdecword ; print hl as decimal
|
|
|
|
CALL iprtstr
|
|
.DB " seconds.", 13, 10, 0
|
|
|
|
; Force BDOS to reset (logout) all drives
|
|
CALL drvrst
|
|
|
|
end:
|
|
ld hl,msg_complete
|
|
CALL prtstr
|
|
JP 0
|
|
RET
|
|
exit:
|
|
CALL prtcrlf
|
|
JP 0
|
|
RET
|
|
|
|
; =========================================
|
|
;
|
|
; END OF MAIN PROGRAM
|
|
; FLOW HERE
|
|
;
|
|
; =========================================
|
|
;
|
|
msg_welcome:
|
|
.DB "CopySlice v0.4 (RomWBW) December 2025 - M.Pruden", 13, 10, 0
|
|
msg_overite_partition:
|
|
.DB 13,10
|
|
.DB "Warning: Copying to Slice 0 of hd512 media, "
|
|
.DB "will override partition table! ", 13,10,0
|
|
msg_parse_dir:
|
|
.DB 13,10
|
|
.DB "Parsing directory. ", 0
|
|
msg_cant_find_dir:
|
|
.DB " -> Directory Not Found!"
|
|
.DB 13,10
|
|
.DB "Will perform a full copy of the Slice.", 0
|
|
msg_confirm:
|
|
.DB 13,10
|
|
.DB "Continue (Y,N) ? ", 0
|
|
msg_copy_main:
|
|
.DB 13,10
|
|
.DB "Copying data blocks. ", 13,10,0
|
|
msg_complete:
|
|
.DB 13, 10
|
|
.DB "Finished.", 13, 10, 0
|
|
;
|
|
; =======================================
|
|
; ERRORS
|
|
; =======================================
|
|
;
|
|
crc_error
|
|
ld hl, msg_crc_error ; error
|
|
JR err
|
|
io_error:
|
|
ld hl, msg_io_error1
|
|
CALL prtstr
|
|
CALL prthex ; pring hex in A reg
|
|
ld hl, msg_io_error2
|
|
JR err
|
|
args_same_err:
|
|
ld hl, msg_args_same
|
|
JR err
|
|
device_diff_err:
|
|
ld hl, msg_device_diff
|
|
JR err
|
|
device_type_err:
|
|
ld hl, msg_device_type
|
|
JR err
|
|
media_err:
|
|
ld hl, msg_media_err
|
|
JR err
|
|
slice_def_err:
|
|
ld hl, msg_slice_def_err
|
|
JR err
|
|
err:
|
|
CALL prtstr
|
|
JP 0
|
|
RET
|
|
|
|
msg_io_error1:
|
|
.DB 13, 10
|
|
.DB "Disk I/O Error (Code 0x" ,0
|
|
msg_io_error2:
|
|
.DB ") Aborting!!", 13, 10, 0
|
|
msg_crc_error:
|
|
.DB 13, 10
|
|
.DB "Verification Failed. Aborting!!", 13, 10, 0
|
|
msg_args_same:
|
|
.DB 13, 10
|
|
.DB "Source and Target disk slices must be different", 13, 10, 0
|
|
msg_device_diff:
|
|
.DB 13, 10
|
|
.DB "Hard disc(s) must have matching layout (hd1k/hd512).", 13, 10, 0
|
|
msg_media_err:
|
|
.DB 13, 10
|
|
.DB "A specified disk device does not exist.", 13, 10, 0
|
|
msg_device_type:
|
|
.DB 13, 10
|
|
.DB "Only hard disc devices are supported.", 13, 10, 0
|
|
msg_slice_def_err:
|
|
.DB 13, 10
|
|
.DB "Slice numbers must be valid and fit on the disk.", 13, 10, 0
|
|
|
|
; =============================================
|
|
;
|
|
; Routines for READ/WRITE/VERIFY a BLOCK of sectors
|
|
;
|
|
readblock:
|
|
ld bc, mainblkbuf ; init disk buffer to HI-memory
|
|
ld (dma), bc ; set this in loop since write/verify changes it.
|
|
|
|
; Sector Address
|
|
ld hl,(src_lbaoffset) ; set DE:HL
|
|
ld de,(src_lbaoffset+2) ; ... to starting lba
|
|
|
|
;; do the read
|
|
ld b, xf_sectorcount ; how many sectors
|
|
ld a,(src_diskunit) ; get source unit
|
|
ld c,a ; put in C
|
|
CALL diskread ; do it
|
|
RET NZ ; abort on error
|
|
|
|
; test we read the expected sectors
|
|
ld a, xf_sectorcount ; the number expected
|
|
cp e ; the actual numer read
|
|
RET NZ
|
|
|
|
; ; calc src CRC if necessary
|
|
; CALL srcblockcrc
|
|
|
|
; increment lba for next block
|
|
ld hl, (src_lbaoffset) ; read LBA low word
|
|
ld bc, xf_sectorcount ; number of sectors transferred
|
|
add hl, bc ; add to LBA value low word
|
|
ld (src_lbaoffset), hl ; store it back
|
|
JR NC, readblock9 ; check for carry
|
|
ld de, (src_lbaoffset+2) ; LBA high word
|
|
inc de ; bump high word
|
|
ld (src_lbaoffset+2), de ; store it back
|
|
readblock9:
|
|
xor a ; ensure A register is cleared
|
|
RET
|
|
;
|
|
; WRITE
|
|
;
|
|
writeblock:
|
|
ld bc, mainblkbuf ; init disk buffer to HI-memory
|
|
ld (dma), bc ; set this in loop since write/verify changes it.
|
|
|
|
; Sector Address
|
|
ld hl,(dest_lbaoffset) ; set DE:HL
|
|
ld de,(dest_lbaoffset+2) ; ... to starting lba
|
|
|
|
;; do the write
|
|
ld b, xf_sectorcount ; sector count to write
|
|
ld a, (dest_diskunit) ; get destination unit
|
|
ld c, a ; put in C
|
|
CALL diskwrite ; do it
|
|
RET NZ ; abort on error
|
|
|
|
; test we wrote the expected sectors
|
|
ld a, xf_sectorcount ; the number expected
|
|
cp e ; the actual numer written
|
|
RET NZ
|
|
|
|
CALL verifyblock ; read and check the block matches
|
|
JR Z, writeblock1 ; no error so just continue
|
|
; ld hl, msg_crc_error ; error
|
|
; call prtstr
|
|
; or 0ffh
|
|
RET
|
|
|
|
writeblock1:
|
|
; increment lba for next block
|
|
ld hl,(dest_lbaoffset) ; set DE:HL
|
|
ld bc, xf_sectorcount ; sectors per Transfer
|
|
add hl, bc ; add to LBA value low word
|
|
ld (dest_lbaoffset), hl
|
|
JR nc, writeblock9 ; check for carry
|
|
ld de, (dest_lbaoffset+2) ; ... to starting lba
|
|
inc de ; if so, bump high word
|
|
ld (dest_lbaoffset+2), de
|
|
writeblock9:
|
|
xor a
|
|
RET
|
|
;
|
|
; Verify Write
|
|
;
|
|
verifyblock:
|
|
ld a, (args_option_v)
|
|
or a ; check the verify opton is set
|
|
RET Z ; nothing to do.
|
|
|
|
; init the dma disk buffer (different), so KNOW read occured and CRC
|
|
ld bc, mainblkbuf2 ; is not just a repeat of the same data
|
|
ld (dma), bc ; if the verify read didnt work correctly
|
|
|
|
; Verify Read Sector Address
|
|
ld hl,(dest_lbaoffset) ; set DE:HL
|
|
ld de,(dest_lbaoffset+2) ; ... to starting lba
|
|
|
|
; READ DISK - do the Verify read
|
|
ld b, xf_sectorcount ; how many sectors
|
|
ld a, (dest_diskunit) ; get destination unit
|
|
ld c, a ; put in C
|
|
CALL diskread ; do it
|
|
RET NZ ; abort on error
|
|
|
|
; test we read the expected sectors
|
|
ld a, xf_sectorcount ; the number expected
|
|
cp e ; the actual numer written
|
|
RET NZ
|
|
|
|
ld hl, mainblkbuf ; buffer address to cpmpare
|
|
ld de, mainblkbuf2 ; with the second buffer
|
|
CALL _cmp20block ; do the comparison - NZ set on error
|
|
RET ; just return this flag
|
|
;
|
|
; =======================================
|
|
;
|
|
; Capture Statistics
|
|
;
|
|
progress_bar:
|
|
; store a count of the amount of 1 kb data written
|
|
; to allow it to be displayed when finished
|
|
ld hl, (stat_blocks_written)
|
|
ld de, xf_sectorcount / 2 ; e.g 8 sectors is 4 kb
|
|
add hl, de
|
|
ld (stat_blocks_written),hl
|
|
|
|
; progress bar DOT - every 16 kb
|
|
ld a, l ; get the LSB
|
|
and 16 - 1 ; clear all but lower 4 bits - mod 16
|
|
JR NZ, progress_bar1 ; if a remainder then skip
|
|
CALL prtdot ; progress bar dot after each 16kb
|
|
progress_bar1:
|
|
|
|
; progress bar CRLF - every 1024 kb
|
|
ld a, h ; get the MSB
|
|
and 4 - 1 ; clear all but lower 2 bits, mod 1024
|
|
or l ; ensuring all LSB bits are Zero
|
|
JR NZ, progress_bar2 ; any of LOWER 10 bits is non zero
|
|
CALL prtcrlf ; print crlf after 1MB boundary
|
|
progress_bar2:
|
|
RET
|
|
;
|
|
; -------------------------------
|
|
;
|
|
stat_blocks_written: .DW 0 ; amount in KB of data copied
|
|
statstarttime: .DW 0 ; start time of the copy
|
|
;
|
|
;=============================================
|
|
; Block Stoarge Info for Source and Target
|
|
;=============================================
|
|
;
|
|
; MCB - MEDIA CONTROL BLOCK - Is Initialised
|
|
; for Source and Target Drives
|
|
;
|
|
; Offsets for the (MCB) - MEDIA Control Block
|
|
;
|
|
off_diskunit .EQU 0 ; disk unit identifier
|
|
off_mediaid .EQU 1 ; media id which correclty identifies hd512 hd1k
|
|
off_lbaoffset .EQU 2 ; 4 byte lba sector offset of start of partition
|
|
; 0 for hd512, or partiion lba for hd1k
|
|
off_lbasize .EQU 6 ; 4 byte media size in sectors, or partition size
|
|
; capacity of the drive for holding slices.
|
|
off_slice .EQU 10 ; the slice number
|
|
off_sliceoffset .EQU 11 ; 4 byte lba sector offset of start of slice
|
|
off_unused1 .EQU 15 ; 1 byte - unused
|
|
off_sps .EQU 16 ; 2 bytes - sectors per slice
|
|
;
|
|
mcb_table_size .EQU 18 ; SIZE OF TABLE
|
|
;
|
|
;-----------------------------
|
|
;
|
|
; MCB for Source Media
|
|
sourcemedia .EQU $
|
|
.FILL mcb_table_size,0 ; define source mcb
|
|
;
|
|
; MCB for Taget Media
|
|
targetmedia .EQU $
|
|
.FILL mcb_table_size,0 ; define target mcb
|
|
;
|
|
; Defines MCB addresses for source and target
|
|
;
|
|
src_diskunit .EQU sourcemedia
|
|
src_mediaid .EQU sourcemedia + off_mediaid
|
|
src_lbaoffset .EQU sourcemedia + off_sliceoffset
|
|
dest_diskunit .EQU targetmedia
|
|
dest_mediaid .EQU targetmedia + off_mediaid
|
|
dest_lbaoffset .EQU targetmedia + off_sliceoffset
|
|
;
|
|
; ===============================
|
|
;
|
|
; BELOW ORIGNALLY FROM CLARGS.Z80
|
|
;
|
|
; ===============================
|
|
;
|
|
; todo possible to use a DRIVE LETTER for the
|
|
; selection of a Disk Unit using CBIOS lookup table
|
|
;
|
|
; working storage for storing captured args
|
|
;
|
|
args_src_slice .DB 0
|
|
args_src_diskunit .DB 0
|
|
args_target_slice .DB 0
|
|
args_target_diskunit .DB 0
|
|
args_option_f .DB 0
|
|
args_option_u .DB 0
|
|
args_option_v .DB 0
|
|
|
|
; -----------------------
|
|
|
|
process_args:
|
|
|
|
; look for start of parms
|
|
ld hl,081h ; point to start of parm area (past len byte)
|
|
call skipws ; skip to next non-blank char
|
|
jp z,showall ; no parms, show all active assignments
|
|
|
|
; target disk unit
|
|
call diskunit
|
|
jr nz, error_args
|
|
; write to variables
|
|
ld (args_target_slice), de
|
|
|
|
; skip the = delimiter
|
|
call skipws
|
|
cp '=' ; proper delimiter?
|
|
jr nz,error_args
|
|
inc hl ; skip over the = sign
|
|
|
|
call skipws ; skip to next non-blank char
|
|
jr z, error_args
|
|
|
|
; source disk unit
|
|
call diskunit
|
|
jr nz,error_args
|
|
; write to variable
|
|
ld (args_src_slice), de
|
|
|
|
; Options
|
|
call skipws
|
|
cp '/' ; proper options
|
|
jr nz, process_args_fin
|
|
call cl_options
|
|
|
|
process_args_fin:
|
|
xor a ; clear Z flag
|
|
RET
|
|
|
|
error_args:
|
|
; dispaly error
|
|
ld hl,msg_invalid
|
|
call prtstr
|
|
|
|
showall:
|
|
; display the args
|
|
ld hl,msg_help
|
|
call prtstr
|
|
ld a, 99h
|
|
or a
|
|
ret
|
|
|
|
; from romldr.asm runcmd1
|
|
; parameters HL - buffer
|
|
; RETURN DE - Unit Slice
|
|
; A register is consumed
|
|
; NZ flag is set if an error
|
|
|
|
diskunit:
|
|
;
|
|
call skipws ; skip whitespace
|
|
call isnum ; do we have a number?
|
|
jp nz,err_invcmd ; invalid format if empty
|
|
call getnum ; parse a number
|
|
jp c,err_invcmd ; handle overflow error
|
|
ld d,a ; save unit
|
|
|
|
; default slice - in E
|
|
xor a ; zero accum
|
|
ld e,a ; save default slice
|
|
|
|
call skipws ; skip possible whitespace
|
|
ld a,(hl) ; get separator char
|
|
or a ; test for terminator
|
|
jp z,diskunit3 ; if so, boot the disk unit
|
|
cp '.' ; otherwise, is '.'?
|
|
jr z,diskunit2 ; yes, handle slice spec
|
|
cp ':' ; or ':'?
|
|
jr z,diskunit2 ; alt sep for slice spec
|
|
jp diskunit3 ; if not, then we finish her
|
|
|
|
diskunit2:
|
|
inc hl ; bump past separator
|
|
call skipws ; skip possible whitespace
|
|
call isnum ; do we have a number?
|
|
jp nz,err_invcmd ; if not, format error
|
|
call getnum ; get number
|
|
jp c,err_invcmd ; handle overflow error
|
|
ld e,a ; load slice into E
|
|
jp diskunit3 ; return the disk unit/slice
|
|
|
|
diskunit3:
|
|
; exit from above is here
|
|
xor a ; set zero flag
|
|
ret
|
|
|
|
cl_options:
|
|
inc hl ; next option
|
|
CALL skipws
|
|
ret z ; EOL so nothing to do
|
|
call upcase
|
|
cp 'F'
|
|
jr z, cl_fullcopy
|
|
cp 'U'
|
|
jr z, cl_unattended
|
|
cp 'V'
|
|
jr z, cl_verify
|
|
jr cl_options
|
|
|
|
cl_fullcopy:
|
|
ld a, 0ffh
|
|
ld (args_option_f),a
|
|
jr cl_options
|
|
|
|
cl_unattended:
|
|
ld a, 0ffh
|
|
ld (args_option_u),a
|
|
jr cl_options
|
|
|
|
cl_verify:
|
|
ld a, 0ffh
|
|
ld (args_option_v),a
|
|
jr cl_options
|
|
|
|
err_invcmd:
|
|
call prthex
|
|
or 0ffh
|
|
ret ; return the nz flag
|
|
|
|
; ---------------------------
|
|
;
|
|
; Messages For processing Arguments
|
|
;
|
|
msg_help:
|
|
.DB 13, 10
|
|
.DB "Copy a full RomWBW hard disk slice to another slice.",13, 10
|
|
.DB 13, 10
|
|
.DB "Syntax:",13,10
|
|
.DB " copysl <destunit>[.<slice>]=<srcunit>[.<slice>] [/options]",13,10
|
|
.DB "Options:",13,10
|
|
.DB " /f - Full copy of slice, ignoring directory allocations.",13,10
|
|
.DB " /u - Unattented, doesnt ask for user confirmation.",13,10
|
|
.DB " /v - Verify, by doing a read and compare after write.",13,10
|
|
.DB "Notes:",13,10
|
|
.DB " - drive identification is by RomWBW disk unit number.",13,10
|
|
.DB " - if slice is omitted a default of 0 is used.",13,10
|
|
.DB " - for full information please see copysl.doc",13,10
|
|
.DB 13, 10, 0
|
|
msg_invalid
|
|
.DB 13, 10
|
|
.DB "Invalid Arguments", 13,10,0
|
|
;
|
|
; ===============================
|
|
;
|
|
; ORIGNALLY FROM MEDIA.Z80
|
|
;
|
|
; ===============================
|
|
;
|
|
; Extended Media Functions to determine Actual Slice attributes
|
|
; Processes FAT Table and determines attributes about a Disk.
|
|
;
|
|
sps_hd1k .EQU 16384
|
|
sps_hd512 .EQU 16640
|
|
;
|
|
; ----------------------------------------------
|
|
; Init a MCB and sense the underlying media
|
|
;
|
|
; Input A - Disk Unit Number
|
|
; Input IX - Pointer to MCB Output Block
|
|
;
|
|
sensemedia:
|
|
|
|
; store the mcb pointer
|
|
ld (mcb_pointer), ix
|
|
|
|
; init the MCB block with 0 bytes
|
|
push ix
|
|
pop hl ; pointer to MCB
|
|
ld d,h ; transfer to hl -> de
|
|
ld e,l
|
|
inc de ; pointer to MCB + 1
|
|
ld (hl), 0 ; store the 0 in the first byte of MCB
|
|
ld bc, mcb_table_size - 1
|
|
LDIR ; fill the rest of the MCB table with 0's
|
|
|
|
; init the disk unit into mcb
|
|
ld (ix+off_diskunit), a
|
|
|
|
; Sense media
|
|
ld c, a ; put disk unit in C for func call
|
|
ld b, BF_DIOMEDIA ; HBIOS func: media
|
|
ld e, 1 ; enable media check/discovery
|
|
RST 08 ; do it
|
|
JP NZ, err_sense ; handle error
|
|
ld (ix + off_mediaid), e ; save media id typically 4 for HD512
|
|
|
|
; Check for hard disk
|
|
ld a, e
|
|
cp MID_HD ; legacy hard disk
|
|
JP NZ, sense_end ; if not hd
|
|
|
|
; load default sps for hd512 into MCB
|
|
ld bc, sps_hd512
|
|
ld (ix + off_sps + 0), c
|
|
ld (ix + off_sps + 1), b
|
|
|
|
; ONLY HDD have a MBR, and a partition table
|
|
|
|
; Attempt to Read MBR
|
|
ld de, 0 ; MBR is at
|
|
ld hl, 0 ; ... first sector
|
|
ld bc, bl_mbrsec ; read into MBR buffer
|
|
ld (dma), bc ; save this pointer to dma
|
|
ld b, 1 ; one sector
|
|
ld a, (ix + off_diskunit) ; get diskunit
|
|
ld c, a ; put in C
|
|
CALL diskread ; do it
|
|
JP nz,err_disk ; abort on error
|
|
|
|
; Check signature - for MBR part table
|
|
ld hl, (bl_mbrsec + 01FEh) ; get signature
|
|
ld a, l ; first byte
|
|
cp 055h ; should be $55
|
|
JR NZ, sense_hd512 ; if not, no part table
|
|
ld a, h ; second byte
|
|
cp 0AAh ; should be $AA
|
|
JR NZ, sense_hd512 ; if not, no part table
|
|
|
|
find_partition:
|
|
|
|
ld a, 0
|
|
ld (foundalready), a ; reset this variable, prior to use
|
|
|
|
; HL address of Byte 4 (partition type) in first partion entry
|
|
ld hl, bl_mbrsec + 01BEH + 4 ; partiton table start + type offset
|
|
; Try to find our entry in part table - DJNZ loop counter
|
|
ld b, 4 ; four entries in part table
|
|
|
|
find_partition2:
|
|
|
|
; load the partition Type, and point to the starting LBA
|
|
ld a, (hl) ; get part type
|
|
ld de, 4 ; LBA is 4 bytes after part type
|
|
add hl, de ; point to the starting LBA of partition
|
|
|
|
; did we find a RomWBW partition table entry
|
|
cp 02Eh ; WBW cp/m partition?
|
|
JR Z, sense_hd1k ; cool, process it
|
|
|
|
; Found a different (not RomWBW )partition type.
|
|
cp 0
|
|
JR NZ, sense_other
|
|
|
|
find_partition3:
|
|
|
|
; not an active entry, skip to next partion entry
|
|
ld de,12 ; partition entry = 16 bytes, we alread moved +4
|
|
add hl,de ; next entry in table / point to part type
|
|
DJNZ find_partition2 ; loop thru table to next entry
|
|
|
|
; have read all 4 partition entries - did not find RomWBW partition
|
|
JR sense_hd512 ; too bad, no RomWBW partition
|
|
|
|
sense_other:
|
|
|
|
; We found a non RomWBW partition, so potentially if we dont latter
|
|
; we dont discover a Rom WBW partition we want the staring LBA to be
|
|
; used as upper bound for the hd512 media size.
|
|
|
|
; capture the starting LBA offset.
|
|
; de already contains start of lba
|
|
ld a, (foundalready)
|
|
or a
|
|
JR NZ, find_partition3 ; already found partition ignore this one.
|
|
; ignoring latter partitions assumes they are sequential with LBA loc.
|
|
|
|
push hl
|
|
push de
|
|
push bc
|
|
|
|
; copy the Starting LBa of partition into MCB media Size
|
|
; thus the reported size of hd512 = starting position of first partion
|
|
; this prevents us from overriding partition data from Slice writes
|
|
ex de, hl ; preserving HL - which contains LBA Start of Partition
|
|
ld hl, (mcb_pointer)
|
|
ld bc, off_lbasize
|
|
add hl, bc
|
|
ex de, hl ; setup DE AS pointer to MCB + 6
|
|
ld bc, 4 ; copy 4 bytes
|
|
LDIR ; from HL (partition start LBA) to DE (MCB + off_lba size)
|
|
|
|
; set flag to say has been done
|
|
ld a, 1 ; update found already
|
|
ld (foundalready), a
|
|
|
|
pop bc
|
|
pop de
|
|
pop hl
|
|
|
|
JR find_partition3 ; loop for next partition
|
|
|
|
sense_hd1k:
|
|
|
|
; NOTE The code below is directly updating MCB, not using offset's
|
|
|
|
; Update the MCB - set a pointer to MCB
|
|
ld de, (mcb_pointer) ; pointer to MCB table
|
|
|
|
; set the media ID to be MID_HDNEW (hd1k))
|
|
inc de ; skip to media id in MCB
|
|
ld a, MID_HDNEW
|
|
ld (de),a ; update mcb with mediaid=hd1k
|
|
|
|
; update the lbaoffset, and lbasize (in MCB) )from partion table
|
|
inc de ; MCB+2 is lba offset, MCB+6 = size in sectors
|
|
ld bc,8 ; 8 bytes is both lba offset and size
|
|
LDIR ; copy 8 bytes from partition table to
|
|
|
|
; load sps (hd1k) into MCB
|
|
ld bc, sps_hd1k
|
|
ld (ix + off_sps + 0),c
|
|
ld (ix + off_sps + 1),b
|
|
|
|
; have detected hd1k correctly
|
|
jr sense_end
|
|
|
|
sense_hd512:
|
|
|
|
; if we already found a non RomWBW partion
|
|
; then we have already captured the lba_size
|
|
; from the offset of the first partition
|
|
ld a, (foundalready)
|
|
cp 0
|
|
jr nz, sense_end
|
|
|
|
; find the Physical capcity of the media call -> DIOCAP
|
|
ld a, (ix + off_diskunit)
|
|
ld c, a ; put disk unit in C for func call
|
|
ld b, BF_DIOCAP ; HBIOS func: to get disk lba capacity
|
|
RST 08 ; do it
|
|
JP NZ, err_sense ; handle error
|
|
|
|
; update the media size into the MCB
|
|
ld (ix + off_lbasize +0), l
|
|
ld (ix + off_lbasize +1), h
|
|
ld (ix + off_lbasize +2), e
|
|
ld (ix + off_lbasize +3), d
|
|
|
|
sense_end:
|
|
xor a ; return Zero
|
|
RET
|
|
|
|
; =======================================
|
|
; Calculate the Slice Offset in our media
|
|
; Only works for HDD
|
|
;
|
|
; Input IX - MCB Pointer
|
|
; A - Slice Number
|
|
;
|
|
slicecalc:
|
|
|
|
; store slice number in MB
|
|
ld (ix + off_slice), a ; store Slice Number in MCB
|
|
|
|
; sps from MCB
|
|
ld c, (ix + off_sps + 0)
|
|
ld b, (ix + off_sps + 1)
|
|
|
|
; starting sector number
|
|
ld hl, 0
|
|
ld de, 0
|
|
|
|
slicecalc1:
|
|
or a ; Slice Number - set flags to check loop ctr
|
|
JR Z, slicecalc3 ; done if counter exhausted
|
|
add hl, bc ; add one slice (SPS) to low word
|
|
JR NC, slicecalc2 ; check for carry
|
|
inc de ; if so, bump high word
|
|
slicecalc2:
|
|
dec a ; dec loop (Slice) counter
|
|
JR slicecalc1 ; and loop
|
|
|
|
slicecalc3:
|
|
|
|
; save the sector offset (SPS * Slice Number)
|
|
push hl
|
|
push de
|
|
|
|
; add sps once again, to get Required (upper sector) needed
|
|
add hl, bc
|
|
jr nc, slicecalc4
|
|
inc de
|
|
|
|
slicecalc4:
|
|
; de:hl has the required number of sectors (on media) for the slice
|
|
push de ; save dsk_req (msw)
|
|
push hl ; save dsk_req (lsw)
|
|
;
|
|
; check dsk_capacity >= cap_required, CF set on overflow
|
|
; no need to save actual result
|
|
or a ; clear carry for sbc
|
|
ld l, (ix + off_lbasize + 0) ; capacity LSW
|
|
ld h, (ix + off_lbasize + 1) ; capacity LSW
|
|
pop bc ; required lsw
|
|
sbc hl, bc ; capacity - required (lsw)
|
|
ld l, (ix + off_lbasize + 2) ; capacity MSW
|
|
ld h, (ix + off_lbasize + 3) ; capacity MSW
|
|
pop bc ; required msw
|
|
sbc hl, bc ; capacity - required (msw)
|
|
;
|
|
; restore starting offset sector
|
|
pop de
|
|
pop hl
|
|
;
|
|
; require - capacity -> generates borrow if capacity > requirement
|
|
jr nc, slicecalc5 ; if we have enough capacity
|
|
or 0FFh ; otherwise signal not enough capacity
|
|
ret
|
|
|
|
slicecalc5:
|
|
; add lba offset to DEHL to get slice offset, commented code above
|
|
ld c, (ix + off_lbaoffset+0)
|
|
ld b, (ix + off_lbaoffset+1)
|
|
add hl, bc
|
|
ex de, hl
|
|
ld c, (ix + off_lbaoffset+2)
|
|
ld b, (ix + off_lbaoffset+3)
|
|
adc hl,bc
|
|
ex de, hl
|
|
|
|
; store slice offset into mcb
|
|
ld (ix + off_sliceoffset + 0), l ; store it in MCB
|
|
ld (ix + off_sliceoffset + 1), h ; store it in MCB
|
|
ld (ix + off_sliceoffset + 2), e ; store it in MCB
|
|
ld (ix + off_sliceoffset + 3), d ; store it in MCB
|
|
|
|
; return without issue
|
|
xor a
|
|
RET
|
|
|
|
; =====================
|
|
;
|
|
; Find the starting sector for Source Slice directory
|
|
; return it in DE - HL
|
|
; NOTE : This routine only works withe th source drive
|
|
;
|
|
finddir:
|
|
|
|
; Sector Address of start of Media
|
|
ld hl,(src_lbaoffset) ; set DE:HL
|
|
ld de,(src_lbaoffset+2) ; ... to starting lba
|
|
|
|
; need to adjust sector to start of directory
|
|
|
|
; hd512 - skip 128kb (system) - 256 sectors
|
|
ld bc, 256
|
|
; is it hd1k
|
|
ld a, (src_mediaid)
|
|
cp MID_HDNEW
|
|
jr nz, finddir1 ; not hd1K
|
|
; hd1k - skip 16kb (system) - 32 sectors
|
|
ld bc, 32
|
|
finddir1:
|
|
; add sector offset
|
|
add hl,bc ; add to LBA value low word
|
|
RET nc ; if NC then just return
|
|
inc de ; if carry, bump high word
|
|
RET
|
|
|
|
; =====================
|
|
;
|
|
; Called to display details in format
|
|
; Disk Unit 2, Slice 11, Type = hdXXX
|
|
;
|
|
printdetails:
|
|
CALL iprtstr
|
|
.DB " Disk Unit ", 0
|
|
ld a, (ix+off_diskunit)
|
|
CALL prtdec ; print disk unit
|
|
CALL iprtstr
|
|
.DB ", Slice ", 0
|
|
ld a, (ix+off_slice)
|
|
CALL prtdec ; print slice number
|
|
CALL iprtstr
|
|
.DB ", Type = ", 0
|
|
ld a, (ix+off_mediaid)
|
|
cp MID_HDNEW
|
|
JR Z, prtdetailsnew
|
|
prtdetailslegacy:
|
|
CALL iprtstr
|
|
.DB "hd512", 13, 10, 0
|
|
RET
|
|
prtdetailsnew
|
|
CALL iprtstr
|
|
.DB "hd1k", 13, 10, 0
|
|
RET
|
|
;
|
|
; --------------------------
|
|
;
|
|
err_disk:
|
|
ld a,2 ; IO ERROR
|
|
or a
|
|
RET
|
|
|
|
err_hbios
|
|
ld a,3 ; HBIOS Error
|
|
or a
|
|
RET
|
|
|
|
err_sense
|
|
ld a,4
|
|
or a
|
|
RET
|
|
;
|
|
;============================================
|
|
;
|
|
; Working Variables
|
|
;
|
|
mcb_pointer .DW 0 ; pointer passed by caller, to MCB
|
|
foundalready .DB 0 ; found a non WBW partition and captured LBA
|
|
;
|
|
;-----------------------------
|
|
;
|
|
; Disk buffers (uninitialized)
|
|
;
|
|
; Master Boot Record sector is read into area below.
|
|
; Note that this buffer is actually shared with bl_infosec
|
|
; buffer below.
|
|
;
|
|
.DB "MBR_START" ; debug
|
|
bl_mbrsec .EQU $
|
|
.FILL 200h,0 ; define 512 mbr sector buffer
|
|
.DB "MBR_END" ; debug
|
|
;
|
|
; NOTE: the MBR_ messages are used to aid debug in memory
|
|
;
|
|
; =======================================
|
|
;
|
|
; ORIGNALLY FROM CPMFS.Z80
|
|
;
|
|
; =======================================
|
|
;
|
|
; Handle CPM File System
|
|
;
|
|
; PROCESS DIRECTORY
|
|
;
|
|
; Read 16kb and proces directory entries
|
|
; DE and HL are the bock address to Read -
|
|
; DE HL are incremented for next 16kb read
|
|
;
|
|
dir_sectors: .EQU 32 ; sectors to read (16kb) & process in directory
|
|
fs_allocsize: .EQU 4 ; smallest size (in kB) for a directory block
|
|
;
|
|
processdir:
|
|
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
|
|
; init disk buffer to HI-memory
|
|
ld bc, mainblkbuf
|
|
ld (dma), bc
|
|
|
|
; READ - do the disk read of the directory
|
|
ld b, dir_sectors
|
|
ld a, (src_diskunit) ; get disk unit
|
|
ld c, a ; put in C
|
|
call diskread ; do it
|
|
ret nz ; abort on error
|
|
|
|
; test we read sectors
|
|
ld a, dir_sectors ; the number expected
|
|
cp e ; the actual numer read
|
|
ret nz
|
|
;
|
|
; Scan directory items to find max alloc
|
|
;
|
|
scandirectory:
|
|
|
|
; variables USED
|
|
; BC loop counter
|
|
; DE used for 16 bit aritmatic
|
|
; HL pointer to the disk directory buffer
|
|
|
|
ld bc, 16 * dir_sectors ; dir entries to process. 16 per sector
|
|
ld hl, mainblkbuf ; address of disk buffer just read
|
|
|
|
nextdirentry:
|
|
|
|
ld a,(hl) ; the first byte "Status" of dir entry
|
|
cp 10h ; entries we process are 0-15 (user Id)
|
|
JR NC, notadirentry ; > 15 we dont need to process
|
|
|
|
isadirectory:
|
|
|
|
; increment the count of dir entries found
|
|
ld de, (dircounter)
|
|
inc de
|
|
ld (dircounter), de
|
|
|
|
; directory entry - jump to alloc blocks
|
|
ld de, 10h ; first 16 bytes are the status / file name
|
|
add hl, de ; skip to allocations
|
|
|
|
push bc ; save the outer loop
|
|
ld c, 8 ; loop counter - num allocations in 16 bytes
|
|
|
|
nextallocation:
|
|
; read the allocation into DE, inc the hl pointer
|
|
ld e, (hl)
|
|
inc hl
|
|
ld d, (hl)
|
|
inc hl
|
|
|
|
; compare with max and update
|
|
push hl
|
|
ld hl, (maxalloc)
|
|
sbc hl, de
|
|
JR NC, dontupdatemax
|
|
ld (maxalloc), de ; update max allocation
|
|
dontupdatemax:
|
|
pop hl
|
|
|
|
dec c
|
|
JR NZ, nextallocation ; loop if more to process
|
|
|
|
pop bc ; restore the outer loop counter
|
|
JR finddirentry ; and contine the outer loop
|
|
|
|
notadirentry:
|
|
; A reg Status byte - is not user id 0-15 - check for invalid and flag
|
|
cp 0E5h ; an Unused Dir Entry
|
|
JR Z, notadirentry2 ; legal so skip to next
|
|
cp 34 ; 0-33 are considered legal Status in CP/M FS
|
|
JR C, notadirentry2 ; if legal then skip to next
|
|
|
|
illegaldirentry:
|
|
; detected illegal directory ( Status not 0-33, E5h ) set a flag
|
|
ld (illegaldir), a ; set NZ, A is the bad status > 34
|
|
|
|
notadirentry2:
|
|
; update pointer - skip to next dir entry
|
|
ld de, 20h
|
|
add hl, de
|
|
|
|
finddirentry:
|
|
; decide if need to loop and process next dir entry
|
|
dec bc ; count down for each dir entry, - outer loop
|
|
ld a, b
|
|
or c
|
|
JR NZ, nextdirentry ; if more dir entries contine (loop) to next
|
|
|
|
processdirfin:
|
|
|
|
; finished - pop HL DE - which contain the LBA Sector pointers
|
|
pop hl
|
|
pop de
|
|
|
|
; increment DE HL lba for next block, for Disk Read
|
|
ld bc, dir_sectors ; sectors
|
|
add hl, bc ; add to LBA value low word
|
|
JR NC, processdirfin2 ;
|
|
inc de ; Carry, So bump high word
|
|
processdirfin2:
|
|
|
|
; and pop the rest of the registers
|
|
pop bc
|
|
pop af
|
|
|
|
xor a ; signal success
|
|
RET
|
|
;
|
|
; ----------------------------------
|
|
;
|
|
; Compute Result;
|
|
; Work out the number of Sectors
|
|
; Compute full disk copy, if user chose to transfer all.
|
|
;
|
|
compute_full:
|
|
; compute full disk size to copy in 4k allocations
|
|
ld hl, 8192 / fs_allocsize ; hd1k - 8192 kb total allocations
|
|
ld a, (ix + off_mediaid)
|
|
cp MID_HDNEW ; is it hd1k
|
|
JR Z, compute_store ; for hd1k - store it now
|
|
ld hl, 8320 / fs_allocsize ; hd512 - 8320 kb total allocations
|
|
JR compute_store
|
|
|
|
;
|
|
; Compute the number of blocks to transfer based on dir scan.
|
|
;
|
|
compute_xfer:
|
|
|
|
; get the max allocation, measured in 4kb blocks, from the dir scan
|
|
ld hl, (maxalloc)
|
|
|
|
; Add the 16kb (hd1k) / 128kb (hd512) System area's'
|
|
ld bc, 16 / fs_allocsize ; hd1k - 16kb system track
|
|
ld a, (src_mediaid)
|
|
cp MID_HDNEW
|
|
JR Z, compute_xfer1 ; if hd1k - add it now
|
|
ld bc, 128 / fs_allocsize ; hd512 - 128kb system track
|
|
compute_xfer1:
|
|
add hl, bc ; add system track block allocations to total
|
|
|
|
compute_store:
|
|
|
|
; ; Divide allocations by 4, to convert to 16k blocks
|
|
; ld a, l ; the LSByte
|
|
; and 0FCh
|
|
; ld l, a ; clear the 2 least sig bits
|
|
; rr h
|
|
; rr l
|
|
; rr h ; rotate right HL 2 bits
|
|
; rr l ; which is like divide 4
|
|
;
|
|
; ; inc by 1, last partial 16 kb bloc is copied, to be sure.
|
|
; ; poss should look for carry bit being set, and conditonaly inc.
|
|
; inc hl
|
|
; ; TODO THIS IS A BUG FOR FULL DISK COPY
|
|
|
|
; Store It
|
|
ld (total4kxfr), hl ; store it
|
|
|
|
; ; todo we need to ensure we DONT OVERSHHOT
|
|
; ; ie: work out what the max is, easy to define
|
|
|
|
RET
|
|
|
|
; ----------------------------------
|
|
;
|
|
; Prints information about the transfer
|
|
;
|
|
compute_print:
|
|
; write info about dir entries found, blocks to copy
|
|
CALL iprtstr
|
|
.DB 13, 10, "Found ",0
|
|
ld hl, (dircounter)
|
|
CALL prtdecword
|
|
CALL iprtstr
|
|
.DB " directory entries, with ",0
|
|
ld hl, (maxalloc)
|
|
CALL prtdecword
|
|
CALL iprtstr
|
|
.DB " (4k) extents.",0
|
|
RET
|
|
;
|
|
; ----------------------------------
|
|
;
|
|
; variable storage for dir processing
|
|
;
|
|
.DB "Debug123" ; TEMP Not Needed
|
|
dircounter .DW 0 ; counter of the total number of directory entries
|
|
maxalloc .DW 8 ; highest block allocation in the directory. Default =
|
|
; 8 * 4k = 32k = 1024 dir entries, safe minimum
|
|
total4kxfr .DW 0 ; number of 4k data transfors to perform
|
|
illegaldir .DB 0 ; set to non-zero if illgal dir has been discovered
|
|
.DB "Debug456" ; TEMP Not Needed
|
|
;
|
|
.END
|
|
|
|
|