From f7b8efd96402273ac6fd956b051884cb4077700e Mon Sep 17 00:00:00 2001 From: Mark Pruden Date: Thu, 27 Feb 2025 10:21:29 +1100 Subject: [PATCH] Added source Code for copySL, build scripts not updated --- Source/Apps/copysl/cio.asm | 328 ++++++++ Source/Apps/copysl/copysl.asm | 1319 +++++++++++++++++++++++++++++++++ Source/Apps/copysl/copysl.doc | 39 +- Source/Apps/copysl/crc.asm | 110 +++ Source/Apps/copysl/hbios.asm | 178 +++++ 5 files changed, 1954 insertions(+), 20 deletions(-) create mode 100644 Source/Apps/copysl/cio.asm create mode 100644 Source/Apps/copysl/copysl.asm create mode 100644 Source/Apps/copysl/crc.asm create mode 100644 Source/Apps/copysl/hbios.asm diff --git a/Source/Apps/copysl/cio.asm b/Source/Apps/copysl/cio.asm new file mode 100644 index 00000000..cd1a0a69 --- /dev/null +++ b/Source/Apps/copysl/cio.asm @@ -0,0 +1,328 @@ + +BDOS .EQU 5 + +; bdos commands +CONIN .EQU 1 +CONOUT .EQU 2 +DIRCONIO .EQU 6 + +; TODO for more routines see assign.asm + +; =============== +; INPUT + +; Console Input +getchr: + PUSH BC + PUSH DE + PUSH HL + LD C,CONIN + CALL BDOS + POP HL + POP DE + POP BC + RET + +; direct console io +; BDOS 6 - FF FE FD - commands +conread: + RET +constatus: + RET +coninput: + RET + +; ======================================= +; STANDARD OUTPUT + +; +; Print character in A without destroying any registers +; +prtchr: + ; PUSH AF + PUSH HL ; We must preserve HL, as the BDOS call sets it + PUSH BC + PUSH DE + LD C, CONOUT + LD E, A + CALL BDOS + POP DE + POP BC + POP HL + ; POP AF + RET +; +prtdot: + push af + ld a, '.' + call prtchr + pop af + ret +; +; Print a zero terminated string at (HL) without destroying any registers +; +prtstr: + PUSH AF + PUSH BC + push de +prtstr1: + ld a,(hl) + or 0 + jr z,prtstr2 + ld c, CONOUT + ld e,a + push hl + call BDOS + pop hl + inc hl + jr prtstr1 +prtstr2: + pop de + pop bc + pop af + ret + +; +; Print the value in A in hex without destroying any registers +; +prthex: + push af ; save AF + push de ; save DE + call hexascii ; convert value in A to hex chars in DE + ld a,d ; get the high order hex char + call prtchr ; print it + ld a,e ; get the low order hex char + call prtchr ; print it + pop de ; restore DE + pop af ; restore AF + ret ; done + +; +; print the hex word value in bc +; +prthexword: + push af + ld a,b + call prthex + ld a,c + call prthex + pop af + ret +; +; Convert binary value in A to ascii hex characters in DE +; +hexascii: + ld d,a ; save A in D + call hexconv ; convert low nibble of A to hex + ld e,a ; save it in E + ld a,d ; get original value back + rlca ; rotate high order nibble to low bits + rlca + rlca + rlca + call hexconv ; convert nibble + ld d,a ; save it in D + ret ; done +; +; Convert low nibble of A to ascii hex +; +hexconv: + and 0Fh ; low nibble only + add a,90h + daa + adc a,40h + daa + ret +; +; Print the decimal value of A, with leading zero suppression +; +prtdec: + push hl + ld h,0 + ld l,a + call prtdecword ; print it + pop hl + ret +; +; Print the Decimal value (word) in HL +; +prtdecword: + push af + push bc + push de + push hl + call prtdec0 + pop hl + pop de + pop bc + pop af + ret +; +prtdec0: + ld e,'0' + ld bc,-10000 + call prtdec1 + ld bc,-1000 + call prtdec1 + ld bc,-100 + call prtdec1 + ld c,-10 + call prtdec1 + ld e,0 + ld c,-1 +prtdec1: + ld a,'0' - 1 +prtdec2: + inc a + add hl,bc + jr c,prtdec2 + sbc hl,bc + cp e + ret z + ld e,0 + call prtchr + ret +; +; Print a byte buffer in hex pointed to by DE +; Register A has size of buffer +; +prthexbuf: + or a + ret z ; empty buffer +prthexbuf1: + ld a,' ' + call prtchr + ld a,(de) + call prthex + inc de + djnz prthexbuf1 + ret +; +; Start a new Line +; +prtcrlf2: + call prtcrlf +prtcrlf: + push hl + ld hl, prtcrlf_msg + call prtstr + pop hl + ret +prtcrlf_msg: + .DB 13,10,0 + +; ================================= +; following is from dmamon util.asm +; +; IMMEDIATE PRINT +; ================================= +; +; PRINT A CHARACTER REFERENCED BY POINTER AT TOP OF STACK +; USAGE: +; CALL IPRTCHR +; .DB 'X' +; +iprtchr: + EX (SP),HL + PUSH AF + LD A,(HL) + CALL prtchr + POP AF + INC HL + EX (SP),HL + RET + +; Print a string referenced by pointer at top of stack +; Usage +; call iprtstr +; .DB "text", 0 +; +iprtstr: + EX (SP),HL + CALL prtstr + INC HL + EX (SP),HL + RET +; +; =========================================================== +; +; Following is for INPUT, used to process command line args +; +; =========================================================== +; +; Skip whitespace at buffer adr in DE, returns with first +; non-whitespace character in A. +; +skipws: + ld a,(hl) ; get next char + or a ; check for eol + ret z ; done if so + cp ' ' ; blank? + ret nz ; nope, done + inc hl ; bump buffer pointer + jr skipws ; and loop + +; +; Uppercase character in A +; +upcase: + cp 'a' ; below 'a'? + ret c ; if so, nothing to do + cp 'z'+1 ; above 'z'? + ret nc ; if so, nothing to do + and ~020h ; convert character to lower + ret ; done + +; +; Get numeric chars at HL and convert to number returned in A +; Carry flag set on overflow +; C is used as a working register +; +getnum: + ld c,0 ; C is working register + +getnum1: + ld a,(hl) ; get the active char + cp '0' ; compare to ascii '0' + jr c,getnum2 ; abort if below + cp '9' + 1 ; compare to ascii '9' + jr nc,getnum2 ; abort if above +; + ld a,c ; get working value to A + rlca ; multiply by 10 + ret c ; overflow, return with carry set + rlca ; ... + ret c ; overflow, return with carry set + add a,c ; ... + ret c ; overflow, return with carry set + rlca ; ... + ret c ; overflow, return with carry set + ld c,a ; back to C + ld a,(hl) ; get new digit + sub '0' ; make binary + add a,c ; add in working value + ret c ; overflow, return with carry set + ld c,a ; back to C +; + inc hl ; bump to next char + jr getnum1 ; loop +; +getnum2: + ld a,c ; return result in A + or a ; with flags set, CF is cleared + ret +; +; Is character in A numeric? NZ if not +; +isnum: + cp '0' ; compare to ascii '0' + jr c,isnum1 ; abort if below + cp '9' + 1 ; compare to ascii '9' + jr nc,isnum1 ; abort if above + cp a ; set Z + ret +isnum1: + or 0FFh ; set NZ + ret ; and done + + diff --git a/Source/Apps/copysl/copysl.asm b/Source/Apps/copysl/copysl.asm new file mode 100644 index 00000000..cfd20c9f --- /dev/null +++ b/Source/Apps/copysl/copysl.asm @@ -0,0 +1,1319 @@ +; +; 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. +; ---------------- +; + .ORG 100H + jp start +; + .INCLUDE "crc.asm" ; comparison of data blocks, used for verify + .include "cio.asm" ; bdos IO 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 + +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.2 (RomWBW) Sept 2024 - 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 [.]=[.] [/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 total Sector requirement + + ; subtract the total Media / Partition Sixe from the Capcity + ; we are not interested in the result, just the C Flag + ; + or a ; clear cary flag + ; + ld c, (ix + off_lbasize +0) ; capacity LSW + ld b, (ix + off_lbasize +1) ; capacity LSW + sbc hl, bc ; Requirement - Capacity LSW + ; + ex de, hl ; Requirement MSW + ld c, (ix + off_lbasize +2) ; capacity MSW + ld b, (ix + off_lbasize +3) ; capacity MSW + sbc hl, bc ; Requirement - Capacity MSW + + ; pop Sector Offset + pop de + pop hl + + ; Require - Capacity - generates Cary if Capity > Require + JR C, slicecalc5 ; C -> Require - Capacity : Require <= 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 + diff --git a/Source/Apps/copysl/copysl.doc b/Source/Apps/copysl/copysl.doc index 57fc09ac..f5e704be 100644 --- a/Source/Apps/copysl/copysl.doc +++ b/Source/Apps/copysl/copysl.doc @@ -12,37 +12,37 @@ from one disk slice to another slice Background ---------- -This tool is only supported by RomWBW HBIOS, it uses HDIOS for all its -disk IO. UNA UBIOS is not supported by this tool. +This tool is only supported by RomWBW HBIOS, it uses HDIOS for all its +disk IO. UNA UBIOS is not supported by this tool. This tool is running on CP/M 2.2 or 3.0 and has access to full 64kb of RAM, with a minimum of 48kb TPA -This tool only works with hard disk devices, other media types like -floppy, are not supported at this time. This tool works across different +This tool only works with hard disk devices, other media types like +floppy, are not supported at this time. This tool works across different hard disk device types, even of different physical type -Both hd1k and hd512 are fully supported, however copying from one layout +Both hd1k and hd512 are fully supported, however copying from one layout type to the other is not supported. -During operation data is copied in a single read/write pass, data is not +During operation data is copied in a single read/write pass, data is not verified by default. If there is a write error, it will be reported, and operation will stop. General Usage ------------- -This tool operates at the disk level via RomWBW, thus all disk identifiers +This tool operates at the disk level via RomWBW, thus all disk identifiers are in the RomWBW . format The syntax (similar to copy) for the command is: COPYSL [.]=[.] [/options] - + E.g. - + COPYSL 3.3=2.10 /U -Means copy from slice 10 on disk 2, onto disk 3 slice 3. This is in +Means copy from slice 10 on disk 2, onto disk 3 slice 3. This is in unattended mode, so you will not be asked to confirm the copy operation. Options @@ -54,33 +54,33 @@ V - Verify. Does an additional read and verify after write. Description ----------- -When run COPYSL will perform command line argument validation and display +When run COPYSL will perform command line argument validation and display an error if they are illegal. Also any disk IO errors will cause COPYSL to exit. -When specifying slice number(s) a check is made that the slice number is -valid, i.e. not too large that it would extend past the end of the +When specifying slice number(s) a check is made that the slice number is +valid, i.e. not too large that it would extend past the end of the partition (hd1k), or the end of the media (hd512). For hd512 a check is -also performed to ensure that the slice would not extend into the first +also performed to ensure that the slice would not extend into the first defined partition. The copy operation will be faster if the source disk has been formatted -with the CP/M file system, since during copy the CP/M directory is scanned, -and unused blocks are not copied. +with the CP/M file system, since during copy the CP/M directory is scanned, +and unused blocks are not copied. -If a filesystem is not found, (or the /F option is chosen) all data is +If a filesystem is not found, (or the /F option is chosen) all data is copied. Verification (if option chosen) will do an aditional read (after write) and compare the data read matches what was written. This compare is only -on every 32'nd byte. This is done for efficiency. +on every 32'nd byte. This is done for efficiency. During copy dots "." will be displayed to indicate progress of the copy. Each "." represents 16 kBytes of data. Each line of "." 's is 1 mBytes. Testing ------- -This tool has been writen and tested on a SC126 computer. Testing been on both +This tool has been writen and tested on a SC126 computer. Testing been on both SD and CF media types, and with both hd1k and hd512 formats History @@ -95,4 +95,3 @@ Future This would be at the cost of performance * ability to abort once the copy has started - \ No newline at end of file diff --git a/Source/Apps/copysl/crc.asm b/Source/Apps/copysl/crc.asm new file mode 100644 index 00000000..5a64abb9 --- /dev/null +++ b/Source/Apps/copysl/crc.asm @@ -0,0 +1,110 @@ + +; +; Simple Block Compare for Comparison purposes +; Both HL and DL contain Block pointers to compare +; HL MUST start on an even block e.g. 8000h +; RET NZ - Failure, Z if no issue +; +_cmp20block + ; inc de ; uncommnet to test crc fail! + ld bc, 20h ; 10t Size of Pointer Increment +_cmp20block1: + ld a, (de) ; 7t Do The comparison itself + cp (hl) ; 7t + JR NZ, _cmp20block2 ; 7t / 12t = 21t + + add hl, bc ; 11t Add the Increment to both pointers + ex de, hl ; 4t + add hl, bc ; 11t + ex de, hl ; 4t = 30t + + ld a, h ; 4t High order byte on Even Boundary + bit 4, a ; 8t has bit 4 been set then exceeded 1000h (4k boundary) + JR Z, _cmp20block1 ; 12t / 7t = 24t + xor a ; 4t + RET ; 10t Return Success +_cmp20block2: + scf ; signal CARRY FLAG Also + RET ; This is the error + +; clock cycles for above +; add 40h -> 64 (loop) * 73t =>> 4,672 - 1.56% +; add 20h ->128 (loop) * 73t =>> 9,344 - 3.13% <= WENT WITH THIS +; add 10h ->256 (loop) * 73t =>> 18,688 - 6.25% +; accuracy = 88/4096 => 2.1% + +; ===================================================================== +; From : https://tomdalby.com/other/crc.html +; And : https://map.grauw.nl/sources/external/z80bits.html#6.1 +; ===================================================================== +; +; ===================================================================== +; input - hl=start of memory to check, de=length of memory to check +; returns - a=result crc +; 20b +; ===================================================================== + +; THE COMMNETED LINES NEED TO BE UNCOMMENTED + +_crc8b: + xor a ; 4t - initial value so first byte can be XORed in (CCITT) +; ld c, 07h ; 7t - c=polyonimal used in loop (small speed up) +_byteloop8b: + xor (hl) ; 7t - xor in next byte, for first pass a=(hl) + inc hl ; 6t - next mem +; ld b, 8 ; 7t - loop over 8 bits +_rotate8b: +; add a,a ; 4t - shift crc left one +; jr nc, _nextbit8b ; 12/7t - only xor polyonimal if msb set (carry=1) +; xor c ; 4t - CRC8_CCITT = 0x07 +_nextbit8b: +; djnz _rotate8b ; 13/8t + ld b,a ; 4t - preserve a in b + dec de ; 6t - counter-1 + ld a,d ; 4t - check if de=0 + or e ; 4t + ld a,b ; 4t - restore a + jr nz, _byteloop8b ; 12/7t + ret ; 10t + +; Clock Cycle For above with 4k bypes +; Loop = 4096 * 47 cycles + 11 => 192,523 x 2 (src/dest) => 385,046 +; acuracy = 1 / 256 => 0.4 % + +; ===================================================================== +; CRC-CCITT +; +; CCITT polynomial 1021h +; Initial Value FFFFh +; +; input - de=start of memory to check, bc=length of memory to check +; returns - hl=result crc +; ===================================================================== + +_crc16: + ld hl, 0ffffh ; 10t - initial crc = $ffff +_byte16: +; push bc ; 11t - preserve counter + ld a,(de) ; 7t - get byte + inc de ; 6t - next mem +; xor h ; 4t - xor byte into crc high byte +; ld h,a ; 4t - back into high byte +; ld b,8 ; 7t - rotate 8 bits +_rotate16: +; add hl,hl ; 11t - rotate crc left one +; jr nc,_nextbit16 ; 12/7t - only xor polyonimal if msb set +; ld a,h ; 4t +; xor 10h ; 7t - high byte with $10 +; ld h,a ; 4t +; ld a,l ; 4t +; xor 21h ; 7t - low byte with $21 +; ld l,a ; 4t - hl now xor $1021 +_nextbit16: +; djnz _rotate16 ; 13/8t - loop over 8 bits +; pop bc ; 10t - bring back main counter + dec bc ; 6t + ld a,b ; 4t + or c ; 4t + jr nz,_byte16 ; 12/7t + ret ; 10t +; diff --git a/Source/Apps/copysl/hbios.asm b/Source/Apps/copysl/hbios.asm new file mode 100644 index 00000000..7a98276e --- /dev/null +++ b/Source/Apps/copysl/hbios.asm @@ -0,0 +1,178 @@ +; +; HBIOS FUNCTIONS +; +; +BF_DIO .EQU 010H +BF_DIOSTATUS .EQU BF_DIO + 0 ; DISK STATUS +BF_DIORESET .EQU BF_DIO + 1 ; DISK RESET +BF_DIOSEEK .EQU BF_DIO + 2 ; DISK SEEK +BF_DIOREAD .EQU BF_DIO + 3 ; DISK READ SECTORS +BF_DIOWRITE .EQU BF_DIO + 4 ; DISK WRITE SECTORS +BF_DIOVERIFY .EQU BF_DIO + 5 ; DISK VERIFY SECTORS +BF_DIOFORMAT .EQU BF_DIO + 6 ; DISK FORMAT TRACK +BF_DIODEVICE .EQU BF_DIO + 7 ; DISK DEVICE INFO REPORT +BF_DIOMEDIA .EQU BF_DIO + 8 ; DISK MEDIA REPORT +BF_DIODEFMED .EQU BF_DIO + 9 ; DEFINE DISK MEDIA +BF_DIOCAP .EQU BF_DIO + 10 ; DISK CAPACITY REPORT +BF_DIOGEOM .EQU BF_DIO + 11 ; DISK GEOMETRY REPORT +; +BF_SYS .EQU 0F0H +BF_SYSRESET .EQU BF_SYS + 0 ; SOFT RESET HBIOS +BF_SYSVER .EQU BF_SYS + 1 ; GET HBIOS VERSION +BF_SYSSETBNK .EQU BF_SYS + 2 ; SET CURRENT BANK +BF_SYSGETBNK .EQU BF_SYS + 3 ; GET CURRENT BANK +BF_SYSSETCPY .EQU BF_SYS + 4 ; BANK MEMORY COPY SETUP +BF_SYSBNKCPY .EQU BF_SYS + 5 ; BANK MEMORY COPY +BF_SYSALLOC .EQU BF_SYS + 6 ; ALLOC HBIOS HEAP MEMORY +BF_SYSFREE .EQU BF_SYS + 7 ; FREE HBIOS HEAP MEMORY +BF_SYSGET .EQU BF_SYS + 8 ; GET HBIOS INFO +BF_SYSSET .EQU BF_SYS + 9 ; SET HBIOS PARAMETERS +BF_SYSPEEK .EQU BF_SYS + 10 ; GET A BYTE VALUE FROM ALT BANK +BF_SYSPOKE .EQU BF_SYS + 11 ; SET A BYTE VALUE IN ALT BANK +BF_SYSINT .EQU BF_SYS + 12 ; MANAGE INTERRUPT VECTORS +; +BF_SYSGET_CIOCNT .EQU 00h ; GET CHAR UNIT COUNT +BF_SYSGET_CIOFN .EQU 01h ; GET CIO UNIT FN/DATA ADR +BF_SYSGET_DIOCNT .EQU 10h ; GET DISK UNIT COUNT +BF_SYSGET_DIOFN .EQU 11h ; GET DIO UNIT FN/DATA ADR +BF_SYSGET_RTCCNT .EQU 20h ; GET RTC UNIT COUNT +BF_SYSGET_DSKYCNT .EQU 30h ; GET DSKY UNIT COUNT +BF_SYSGET_VDACNT .EQU 40h ; GET VDA UNIT COUNT +BF_SYSGET_VDAFN .EQU 41h ; GET VDA UNIT FN/DATA ADR +BF_SYSGET_SNDCNT .EQU 50h ; GET VDA UNIT COUNT +BF_SYSGET_SNDFN .EQU 51h ; GET SND UNIT FN/DATA ADR +BF_SYSGET_TIMER .EQU 0D0h ; GET CURRENT TIMER VALUE +BF_SYSGET_SECS .EQU 0D1h ; GET CURRENT SECONDS VALUE +BF_SYSGET_BOOTINFO .EQU 0E0h ; GET BOOT INFORMATION +BF_SYSGET_CPUINFO .EQU 0F0h ; GET CPU INFORMATION +BF_SYSGET_MEMINFO .EQU 0F1h ; GET MEMORY CAPACTITY INFO +BF_SYSGET_BNKINFO .EQU 0F2h ; GET BANK ASSIGNMENT INFO +BF_SYSGET_CPUSPD .EQU 0F3h ; GET CLOCK SPEED & WAIT STATES +BF_SYSGET_PANEL .EQU 0F4h ; GET FRONT PANEL SWITCHES VAL +BF_SYSGET_APPBNKS .EQU 0F5h ; GET APP BANK INFORMATION +; +; MEDIA ID VALUES +; +MID_NONE .EQU 0 +MID_MDROM .EQU 1 +MID_MDRAM .EQU 2 +MID_RF .EQU 3 +MID_HD .EQU 4 +MID_FD720 .EQU 5 +MID_FD144 .EQU 6 +MID_FD360 .EQU 7 +MID_FD120 .EQU 8 +MID_FD111 .EQU 9 +MID_HDNEW .EQU 10 + +; ----------------- +; +; Read timer in sconds. +; +sysgetseconds: + ld b,BF_SYSGET + ld c,BF_SYSGET_SECS + rst 08 ; do it + ret + +; ----------------- +; +; Return non zero if A (media ID) +; is a type of hard drive +; If not A=0 and Z flag is set +; +isaharddrive: + cp MID_HD + jr z, ishdd1 + cp MID_HDNEW + jr z, ishdd1 + xor a ; clear A and set Z flag + ret +ishdd1: + or a ; set Z flag and return + ret + +; ------------------------------------- +; +; used to pass the buffer address argument +; +bankid .DB 0 ; bank id used for read writes +dma .DW 8000h ; address argument for read write +; +; +; basic setup for disk io +; call to get the current bank IO +; +initdiskio: + ; Get current RAM bank + ld b,BF_SYSGETBNK ; HBIOS GetBank function + RST 08 ; do it via RST vector, C=bank id + JP NZ, err_hbios + ld a,c ; put bank id in A + ld (bankid),a ; put bank id in Argument + RET +; +; +; Read disk sector(s) +; DE:HL is LBA, B is sector count, C is disk unit +; (dma) is the buffer address +; (bankid) is the memory bank +; Returns E sectors read, and A status +; +diskread: + ; Seek to requested sector in DE:HL + push bc ; save unit & count + set 7,d ; set LBA access flag + ld b,BF_DIOSEEK ; HBIOS func: seek + rst 08 ; do it + pop bc ; recover unit & count + jp nz,err_diskio ; handle error + + ; Read sector(s) into buffer + ld e,b ; transfer count + ld b,BF_DIOREAD ; HBIOS func: disk read + ld hl,(dma) ; read into info sec buffer + ld a,(bankid) ; user bank + ld d,a + rst 08 ; do it + jp nz,err_diskio ; handle error + xor a ; signal success + ret ; and done +; +; Write disk sector(s) +; DE:HL is LBA, B is sector count, C is disk unit +; (dma) is the buffer address +; (bankid) is the memory bank +; Returns E sectors written, and A status +; +diskwrite: + ; Seek to requested sector in DE:HL + push bc ; save unit & count + set 7,d ; set LBA access flag + ld b,BF_DIOSEEK ; HBIOS func: seek + rst 08 ; do it + pop bc ; recover unit & count + jp nz,err_diskio ; handle error + + ; Write sector(s) from buffer + ld e,b ; transfer count + ld b,BF_DIOWRITE ; HBIOS func: disk write + ld hl,(dma) ; write from sec buffer + ld a,(bankid) ; user bank + ld d,a + rst 08 ; do it + jp nz,err_diskio ; handle error + xor a ; signal success + ret ; and done +; +err_diskio: +; push hl +; ld hl,str_err_prefix +; call prtstr +; pop hl +; or 0ffh ; signal error + ret ; done + +;str_err_prefix db 13,10,13,10,"*** ",0 + +