Title Floppy Disk Backup for the MD-HD (20_Jul_84) ; ; Copyright (c), 1983, 1984, ; Morrow Designs, Inc. ; 600 McCormick ; San Leandro, Ca. 94577 .z80 aseg org 100h ;---------------------------------------------------------------------- ; Program Outline ;---------------- ; ;discipline: copy contents of floppy to file on h.d. then to new floppy ; ;ask for source disk ;logon disk ;if disk is not foreign ; if disk is single sided, track 40 else track=80 endif ; if not exist file "a:backup.dsk" ; makefile "a:backup.dsk" ; for x=0 to tracks-1 ; readtrack x ; writefile tracklen ; next x ; closefile ; "finished reading source disk, please remove from drive" ; endif ;*rewind ; "insert formatted destination disk in drive" ; "press any key when ready, or control-c to abort" ; if (getkey)=control-c exit endif ; if disk is foreign, complain and exit ; if disk is single sided track = 40 else track = 80 endif ; compute filesize "a:backup.dsk", convert to track ; if filesize <> track complain and goto rewind ; openfile "a:backup.dsk" ; for x=0 to track-1 ; readfile trackize ; writetrack x ; next x ; closefile ; "care to make another?" ; if (getkey) = "y" goto insert ; else erasefile "a:backup.dsk" ; endif ;else "unable to backup foreign format disks" ;endif ;exit page 60 ;---------------------------------------------------------------------- ; Equates ;-------- ; ; Revision Number ;---------------- ; Frev equ 20h ; Bdos Functions ;--------------- ; Bdos equ 5 getkey equ 1 Print_Line equ 9 getvers equ 12 reset equ 13 seldsk equ 14 open equ 15 close equ 16 delete equ 19 read equ 20 write equ 21 make equ 22 setdma equ 26 getusr equ 32 compute equ 35 flush equ 48 direct$bios equ 50 ;define direct bios functions ;---------------------------- ; Bios_Warm_Start equ 0 ;Bios Warm Start Entry Point bios$func equ 0 read$mtabs equ 3 bios$seldsk equ 9 bios$settrk equ 10 bios$setsec equ 11 bios$setdma equ 12 bios$read equ 13 bios$write equ 14 bios$sectran equ 16 bios$setbnk equ 28 ; define bios masks ;------------------ ; virtual equ 80h foreign equ 40h sides equ 4 harddsk equ 2 page ; Define constants ;----------------- ; seclen equ 1024 maxsec equ 5 reclen equ 128 maxrec equ 5*8 mtabln equ 16 ;Length of an Mtab ; Message Strings ;---------------- ; Str_Title equ 20 ;Title Str_Source equ 21 ;SOURCE Prompt Str_Reading equ 22 ;Reading SOURCE Str_Read_Done equ 23 ;Completed SOURCE Str_Destination equ 24 ;DESTINATION Prompt Str_Writing equ 25 ;Writing DESTINATION Str_Write_Done equ 26 ;Completed DESTINATION Str_Repeat equ 27 ;Repeat Response Prompt Str_ReBoot equ 28 ;Insert System Diskette No_Message equ 30 ;Null Message ; Error Strings ;-------------- ; Error_Message equ 0 ;Error Message Prompt Err_Version equ 1 ;Requires CP/M Plus Err_Banked equ 2 ;Not Banked CP/M Plus Err_Foreign equ 3 ;No foreign Drives Err_Directory equ 4 ;out of directory space Err_Close equ 5 ;Close Error Err_Single equ 7 ;Single Sided Destination Err_Double equ 8 ;Double Sided Destination Err_Read_Track equ 9 ;track READ error Err_Write_Track equ 10 ;track WRITE error Err_Write equ 11 ;write error Err_Read equ 12 ;Read Error Err_Mtab equ 13 ;Mtab Error ; Non Printing Ascii Definitions ;------------------------------- ; ctlc equ 3 Bell equ 7 Tab equ 9 Lf equ 10 Cr equ 13 page ;---------------------------------------------------------------------- ; Main Line of Backup Program (5_Jul_84) ;--------------------------------------- ; Begin_Backup: call Setup_Param ;Loop Setup Parameters/test system or a ; If (there was an Error) jr nz,B_Err ; Quit ld a,Str_Source ; Print the Source Prompt call Continue? ; If (user doesn't want to continue) jr nz,B_Done ; Quit call Get_Source ; Read the Source or a ; If (there was an Error) jr nz,B_Err ; Quit BBlp1: ld a,Str_Destination ; Repeat Print Destination Prompt call Continue? ; If (user doesn't want to continue) jr nz,B_Done ; Quit call Put_Destination ; Write Destination Disk or a ; If (there was an error) jr nz,B_Err ; Quit call Repeat_Response ; If (Repeat Repsonse eq Stop) jr nz,B_Done ; Quit jr c,BBlp1 ; Until (Response ne R (ReCopy) jr Begin_Backup ; (A = Another (new) copy) B_Err: call Report_Error ;Report Errors B_Done: call Restore_Param ;Restore Parameters jp Bios_Warm_Start ;Exit page ;---------------------------------------------------------------------- ; Setup Parameters for Backup (20_Jul_84) ;---------------------------------------- ; Setup_Param: ld c,getvers call bdos ld a,l and 0F0h ;Remove the Minor Revision Number cp 30h ld a,Err_Version ;If (this is the Wrong Version of Cp/m) ret nz ; Return (Error) ld a,Str_Title call Print_Message ;Print 'Title Line' ld hl,(1) ld l,1 ld a,(hl) inc hl ld h,(hl) ld l,a ld a,(hl) cp 22h ;shld instruction is first in func: ld a,Err_Banked ;If (This isn't a Cp/m 3 Banked System) ret nz ; Return (Error) ld c,getusr ld e,-1 call bdos ld a,e ld (user),a ld c,getusr ld e,0 ;set user 0 call bdos ld c,reset call bdos ld hl,IBuf1 ;HL:= Pointer to Fcb + Name_Field ld b,24 ;B:= Counter SuLp1: ld (hl),0 ;Repeat Zero Fill Block inc hl ; Inc Storage Pointer djnz SuLp1 ;Until (whole block filled) ld a,harddsk call getmtab ;get [e]=logical drive of harddisk ld a,e inc a ;in fcb format ld hl,bkfcb ;save in fcb ld (hl),a ex de,hl ;DE:= Backup.Dsk Fcb ld c,delete call bdos ;Delete any existing copyof Backup.Dsk ld a,0 ;Return Status:= Ok ret page ;---------------------------------------------------------------------- ; Read the Source Diskette (3_Jul_84) ;------------------------------------ ; Get_Source: ld a,virtual ;find first virtual drive call getmtab ;find the floppy or a ;If (Mtab not found) ret nz ; Return ld a,(hl) ;test foreign and foreign ld a,Err_Foreign ;If (Drive is Foreign) ret nz ; Return (Error) ld a,e ;logical drive ld (drive),a ld c,seldsk push hl call bdos ;login drive pop hl ld c,80 ;80 track ld a,(hl) and sides jp nz,SUsk5 ld c,40 ;single sided, 40 track SUsk5: ld a,c ld (tracks),a ;set number of tracks ld de,bkfcb ld c,make call bdos ;Make Backup.Dsk inc a ld a,Err_Directory ;If (there's no directory space) ret z ; Return (Error) ld a,Str_Reading call Print_Message ;Print 'Reading Source File' ld a,0 ld (ctrk),a ld (currec),a ld hl,bios$read ld (dcmd),hl ld a,write ld (bcmd),a GSlp1: ld a,(drive) ;Repeat call getxlt ld (xlate),hl call rwtrk ; read track into buffer or a ; If (There was an Error) ret nz ; Return ld a,(bkfcb) dec a call getxlt ; logon hd again (maintain phase w/bdos) call rwfile ; write track to file or a ; If (There was an error) ret nz ; Return ld hl,ctrk ; HL:= current track ld a,(tracks) inc (hl) cp (hl) jp nz,GSlp1 ;until (maxtrack) ld c,close ld de,bkfcb call bdos ;close file inc a ;check for error ld a,Err_Close ;If (The File Couldn't be Closed) ret z ; Return (Error) ld a,Str_Read_Done call Print_Message ;Print 'Completed Reading Source' ld a,0 ;Returned Status:= Ok ret ;Return (Done with Source Read) page ;---------------------------------------------------------------------- ; Write to the Destination Diskette (3_Jul_84) ;--------------------------------------------- ; Put_Destination: ld c,reset call bdos ld hl,bkfcb+12 ld bc,36-12 call clear ld a,virtual call getmtab or a ;If (the drive couldn't be found) ret nz ; Return ld a,(hl) and foreign ld a,Err_Foreign ;If (Drive is Foreign) ret nz ; Return (Error) ld a,e ;drive ld (drive),a ld c,seldsk call bdos ;now make sure it is the same format as the original ld de,bkfcb ld c,compute call bdos ld hl,currec ld (hl),0 inc hl ;r0 byte ld e,(hl) ld (hl),0 ;clear inc hl ld d,(hl) ld (hl),0 ;clear inc hl ld (hl),0 ex de,hl ld de,-1600 ;1600 records for single sided or a ;clear carry add hl,de ;test if equal ld a,h or l ld a,40 jp z,GDsk1 ld a,80 ;80 tracks GDsk1: push af ;save ;now check mtab of destination drive for match ld a,virtual call getmtab or a ;If (Mtab was not found) ret nz ; Return ld a,(hl) ;get mtab byte and sides ld c,40 jp z,GDsk2 ld c,80 GDsk2: pop af cp c ;compare file size and disk size jp z,GDsk4 ld a,40 cp c ;If (2-sided Source and 1-sided Dest) ld a,Err_Single ; A:= Single Sided Dest. Error ret z ; Return ld a,Err_Double ;Else A:= Double Sided Dest. Error ret ; Return GDsk4: ld (tracks),a ;save number of tracks ld de,bkfcb ld c,open call bdos ;we are sure file is there ld a,Str_Writing call Print_Message ;Print 'Writing Destination' ld a,0 ld (ctrk),a ;start at track 0 ld (currec),a ;and record zero ld hl,bios$write ;set for track writes ld (dcmd),hl ld a,read ;set for file reads ld (bcmd),a GDlp1: ld a,(bkfcb) ;Repeat dec a call getxlt ; logon source drive call rwfile ; do file read or a ; If (there was an error) ret nz ; Return (Error) ld a,(drive) call getxlt ld (xlate),hl call rwtrk ; do track write or a ; If (there was an Error) ret nz ; Return (Error) ld hl,ctrk ld a,(tracks) inc (hl) ; track++ cp (hl) jp nz,GDlp1 ;Until (all tracks have been moved) ld c,flush ld e,-1 call bdos ;flush and purge buffers ld c,close ld de,bkfcb call bdos ;close the file ld a,Str_Write_Done call Print_Message ;Print 'Write Done' ld a,0 ;Returned Status:= Ok ret ;Return page ;---------------------------------------------------------------------- ; Get the User's Response (5_Jul_84) ;----------------------------------- ; 1) This routine issues a prompt and then gets a key. If the key ; read was a control_C or a S then the Z_Flag is Cleared. If the ; response was a C then the Z_Flag is Set. Any other response causes ; the prompt to be re:issued. ; Continue?: call Print_Message ;Print the Prompt Clp1: ld c,getkey ;Loop C:= Get Key Function call bdos ; Get the key and 5Fh ; Force it to upper case cp ctlc ; If ( (Key eq Control C) or jr z,Csk1 cp 'S' ; (Key eq S) ) jr nz,Csk2 Csk1: or a ; Clear the Z_Flag ret ; Return Csk2: cp 'C' ; If (Key eq Continue) jr nz,Clp1 ret ; Return (Z_Flag Set) ;---------------------------------------------------------------------- ; Find out if the user wants to repeat the process (3_Jul_84) ;------------------------------------------------------------ ; 1) This routine prints the Repeat Prompt and then gets the user's ; reply. If the reply was Y or y then the Z_Flag is returned Set. ; Repeat_Response: ld a,Str_Repeat call Print_Message ;Print the Repeat Prompt RRlp1: ld c,getkey ;Loop C:= Get Key Function call bdos ; Get the Users Response and 5Fh ; Force it to upper case cp 'A' ; If (Repsonse eq Another Copy) ret z ; Return (Z_Flag=Set, Carry=Clear) cp 'R' ; Else If (Response is ReCopy) scf ret z ; Return (Z_Flag=Set, Carry=Set) cp 'S' ; Else If (Response eq Stop) jr nz,RRlp1 or a ret ; Return (Z_Flag=Clear, Carry=Clear) page ;---------------------------------------------------------------------- ; Restore the System Parameters (20_Jul_84) ;------------------------------------------ ; Restore_Param: call Erase_File ;Erase the Current Backup File ld a,(bkfcb) dec a ;see if hd is boot disk or a ret z ;exit quietly ld a,Str_ReBoot call Print_Message ;Print 'Insert System Diskette' ld c,getkey call bdos xor a ;drive 0 call getxlt ;Re:Select the boot drive ret ;exit ;---------------------------------------------------------------------- ; Erase the Backup File (20_Jul_84) ;---------------------------------- ; Erase_File: ld a,(bkfcb) ;get boot drive dec a call getxlt ;select it ld de,bkfcb ;HL:= Pointer to File's Fcb ld c,delete ;C:= Delete Function call bdos ;Delete the Backup File ret ;Return page ;---------------------------------------------------------------------- ; Read/Write a Track ;------------------- ; read a track into buffer; disk is selected, xlate is setup ; RwTrk: ld hl,bufadr ld (buffer),hl ld hl,0 ;sector 0 ld (TSect),hl call bios db bios$settrk db 0 CTrk: dw 0,0,0 RwTlp1: call bios db bios$sectran db 0 TSect: dw 0 xlate: dw 0,0 ld (sector),hl ld hl,(TSect) ld a,maxsec cp l ld a,0 ;(Return Status eq Ok) ret z ;done if max sectors inc hl ld (TSect),hl call bios db bios$setsec db 0 sector: dw 0,0,0 call bios db bios$setbnk db 1 ;tpa dw 0,0,0 call bios db bios$setdma db 0 buffer: dw 0,0,0 ld hl,(buffer) ld de,seclen add hl,de ld (buffer),hl ;advance dma for next sector call bios dcmd: db 0 ;do read or write db 0 dw 0,0,0 or a ;test for error jp z,RwTlp1 ;loop til done ld a,(dcmd) ;get command cp bios$read ld a,Err_Read_Track ;If (Read Error) ret z ; Return Error ld a,Err_Write_Track ;Else Write Error ret ; Return page ;---------------------------------------------------------------------- ; Read/Write a File ;------------------ ; enter with data in buffer, drive selected ; RwFile: ld hl,bufadr ld (buffer),hl ld hl,0 ld (sector),hl ;record count WrFlp1: ld hl,sector ld a,maxrec cp (hl) ld a,0 ;(Return Status eq Ok) ret z ;exit when done inc (hl) ;record count++ ld hl,(buffer) ld e,l ld d,h ld bc,reclen add hl,bc ld (buffer),hl ld c,setdma call bdos ld de,bkfcb ld a,(bcmd) ld c,a call bdos or a jp z,WrFlp1 ;loop until all written ld a,(bcmd) cp write ld a,Err_Write ;If (it was a Write Error) ret z ; Return ld a,Err_Read ;Else Read Error ret ; Return page ;---------------------------------------------------------------------- ; Get a Drive Translation Table/Select a Drive ;--------------------------------------------- ; enter with a=drive to select, exit hl=xlate ; also used as select disk ; getxlt: ld (sdrv),a call bios db bios$seldsk db 0 sdrv: dw 0 ;drive to select dw 1 ;secondary select dw 0 ld a,(hl) ;read xlate inc hl ld h,(hl) ld l,a ret page ;---------------------------------------------------------------------- ; Find the first mtab with single attribute in [a] ;------------------------------------------------- ; on entry: a=pattern to search for ; on exit, e=locical number of drive, ; hl=mtab of this drive ; getmtab: ld (mtabmsk),a call bios db bios$func ;extended function db read$mtabs dw 0 dw mtab$buffer dw 0 ld hl,mtab$buffer ld bc,mtabln ld de,500h ;5 drives mtablp: ld a,(hl) ;read flags and 0 ;see if conditions met mtabmsk equ $-1 ;self modifying ld a,0 ;(Return Status eq Ok) ret nz ;return when met inc e ;drive++ add hl,bc ;ptr++ dec d ;count-- jp nz,mtablp ld a,Err_Mtab ;A:= Error Code ret ;Return page ;---------------------------------------------------------------------- ; Clear Memory ;------------- ; enter with hl-> first byte to clear, bc=length ; clear: ld (hl),0 inc hl dec bc ld a,b or c jp nz,clear ret ;---------------------------------------------------------------------- ; Execute a Direct Bios Call ;--------------------------- ; bios: pop de ;[de] -> biospb ld hl,8 ;length of biospb add hl,de ;[hl] = new return address push hl ld c,direct$bios jp bdos page ;---------------------------------------------------------------------- ; Report Errors (2_Jul_84) ;------------------------- ; Report_Error: push af ld a,Error_Message call Print_Message ;Print the Error Message pop af call Print_Message ;Print the Error Message ret ;Return ;---------------------------------------------------------------------- ; Print a Message on the Console (29_Jun_84) ;------------------------------------------- ; 1) This routine Prints a Message on the Console. ; 2) Register Usage: ; A -> Has Message Code on entry ; B -> Counter ; C -> Holds copy of Error Code ; DE -> Used to hold pointers/Bdos print string functions ; HL -> Used to hold pointers ; Print_Message: push af ld de,CrLf ;DE:= Pointer to Cr/Lf String ld c,Print_Line ;C:= Print String Function call Bdos ;Print Carriage Return Line Feed pop af ld c,a ;C:= Error Code to Find ld b,Max_Message ld hl,Message_Table ;HL:= Start of the Message table PmLp1: ld a,(hl) ;Repeat A:= Table's Message Code inc hl ld e,(hl) inc hl ld d,(hl) ; DE:= Pointer to String inc hl ; (HL equal to start next entry) cp c ; If (Code eq table) jr z,PmPrn ; Goto Print djnz PmLp1 ;Until (the whole tables been checked) ld de,NoCode ;(set pointer to 'Unrecognized Error') PmPrn: ld c,Print_Line ; call Bdos ;Print the Message ret ;Return page ;---------------------------------------------------------------------- ; Message Strings (2_Jul_84) ;--------------------------- ; ; Message Strings ;---------------- ; Message_Table: db Str_Title ;Title dw StrM1 db Str_Source ;Source Prompt dw StrM2 db Str_Reading ;Reading the SOURCE diskette dw StrM3 db Str_Read_Done ;Completed SOURCE dw StrM4 db Str_Destination ;Destination Prompt dw StrM5 db Str_Writing ;Writing the DESTINATION diskette dw StrM6 db Str_Write_Done ;Completed Destination dw StrM7 db Str_Repeat ;Repeat Response Prompt dw StrM8 db Str_ReBoot ;Insert System Diskette dw StrM9 db No_Message ;No Error Message dw NMmsg ; Error Strings ;-------------- ; db Error_Message ;Error Message Prompt dw ERmsg db Err_Version ;Requires CP/M Plus $' dw ErrM1 db Err_Banked ;Not Banked CP/M Plus $' dw ErrM2 db Err_Foreign ;no foreign Drives dw ErrM3 db Err_Directory ;Out of directory space dw ErrM4 db Err_Close ;File Close Error dw ErrM5 db Err_Single ;double sided Destination dw ErrM7 db Err_Double ;single sided Destination dw ErrM8 db Err_Read_Track ;track READ error dw ErrM9 db Err_Write_Track ;track WRITE error.' dw ErrM10 db Err_Write ;Fatal write error dw ErrM11 db Err_Read ;Fatal Read Error dw ErrM12 db Err_Mtab ;Mtab Search Error dw ErrM13 End_Table: Max_Message equ ( (End_Table - Message_Table)/3 ) + 1 page ;---------------------------------------------------------------------- ; Text Section (3_Jul_84) ;------------------------ ; ; Message Strings ;---------------- ; StrM1: db 1Ah,'MD-HD Floppy Disk Backup Program Rev ' db ((frev and 0f0h) shr 4) + '0', '.', (frev and 0fh) + '0' db 9,9,9,' Copyright 1984' db cr,lf,'Morrow Designs Inc.' db 9,9,9,9,9,9,'San Leandro, Ca' db cr,lf,'$' StrM2: db lf,'Insert the SOURCE diskette into the drive:' db cr,lf,Tab,'Enter C to Continue' db cr,lf,Tab,'Enter S to Stop and Return to CP/M $' StrM3: db lf,'Reading from the SOURCE diskette $' StrM4: db 'Completed Reading from the SOURCE diskette. $' StrM5: db lf,'Insert a Formatted DESTINATION diskette into the drive.' db cr,lf,Tab,'Enter C to Continue' db cr,lf,Tab,'Enter S to Stop and Return to CP/M $' StrM6: db lf,'Writing to the DESTINATION diskette $' StrM7: db 'Completed Writing to the DESTINATION diskette. $' StrM8: db lf, 'Enter R to ReCopy the same disk' db cr,lf,'Enter A to make a copy of Another disk' db cr,lf,'Enter S to Stop copying and return to CP/M $' StrM9: db lf,'Insert System Diskette, and Press any key to reboot $' NMmsg: db '$' ;*=> Null Message CrLf: db Cr,Lf,'$' ; Error Strings ;-------------- ; ERmsg: db Cr,Bell,Lf,Lf,'*=> Error: $' ErrM1: db Tab,'Wrong Version of CP/M (must be CP/M Plus) $' ErrM2: db Tab,'System is not Morrow Micro-Decision with Banked CP/M Plus $' ErrM3: db Tab,'Foreign Drives cannot be backed up with this program $' ErrM4: db Tab,'No Directory Space left on the Hard Disk $' ErrM5: db Tab,'Unable to close "BACKUP.DSK" $' ErrM7: db Tab,'Your BACKUP.DSK file is for a double sided disk,' db cr,lf,Tab,'while the destination diskette is single sided.' db cr,lf,Tab,'Change diskettes and run BACKUP again $' ErrM8: db Tab,'Your BACKUP.DSK file is for a single sided disk,' db cr,lf,Tab,'while the destination diskette is double sided.' db cr,lf,Tab,'Change diskettes and run BACKUP again $' ErrM9: db Tab,'Track READ error $' ErrM10: db Tab,'Track WRITE error $' ErrM11: db Tab,'Write Error $' ErrM12: db Tab,'Read Error $' ErrM13: db Tab,'Unable to find drive address$' NoCode: db Cr,Lf,'Unrecognized Message String $' page ;---------------------------------------------------------------------- ; Storage ;-------- ; user: ds 1 drive: ds 1 tracks: ds 2 bcmd: ds 1 bkfcb: db 1 db 'BACKUP DSK' Ibuf1: db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 currec: db 0,0,0,0 mtab$buffer equ $ bufadr equ $ end