; ; Quicky disk copy for the Micro-Decision. ; ; Copyright (c) 1982, Morrow Designs ; ;*06 20 84 Howard Revision 1.9 released ; 06 20 84 Howard Reversed logic for * on input line ; 06 20 84 Howard Suppressed 1st time select if * on input line ;*05 03 84 Howard Revision 1.8 released ; 05 03 84 Howard Added first time select before warm boot exit ;*12 15 82 Marc Revision 1.7 released ; 12 15 82 Marc Added message + return wait to single copy exit ;*11 24 82 Marc Revision 1.6 released ; 11 24 82 Marc Reinstated the first time select flag ;*11 9 82 Marc Revision 1.5 released ; 11 9 82 Marc Fixed drive name pointer offset calculations ; 11 9 82 Marc Fixed the source disk message ;*10 26 82 Marc Revision 1.4 released ; 11 1 82 Marc Added code to return an error status to INIT ; 10 26 82 Marc Deleted all highlighting for INIT mode ; 10 26 82 Marc Deleted all prompting for INIT mode ;*10 25 82 Marc Revision 1.3 released ; 10 24 82 Marc Added model number to the rev number ; 10 24 82 Marc Made INIT mode ; 10 19 82 Marc Made pretty messages (half/full vidio) ; 10 19 82 Marc The stack is kept clean, no jumps to exit, all ret c ; 10 19 82 Marc Added the * flag for no system return waits ; 10 8 82 Marc Added a character gobbler to stop type ahead ; 10 8 82 Marc Saved/restored the drive swap messages ; 10 8 82 Marc Used different messages for virtual configurations ;* 9 17 82 Marc Revision 1.2 released ; 9 17 82 Marc Restart goes to the very beginning of the program now ; 9 16 82 Marc Converted to M80 + Zilog mnemonics ; 9 16 82 Marc Removed the first time select checker ; 9 15 82 Marc Select error handler removed ; 9 15 82 Marc Disk error handler removed, errors go to EXIT ; 9 15 82 Marc Added the re-copy option ; 9 15 82 Marc Re-did the messages for virtual drive system ; 9 15 82 Marc Fixed track counters for virtual drive systems ; 9 15 82 Marc Revision 1.1 released ; 9 15 82 Marc Added track counters ; 9 15 82 Marc Cleaned up the messages (made them like FORMATS) ; 9 10 82 Marc Revision 0.0 released ; 9 9 82 Marc Initial coding of revision 0.0 ; ; ; Define system constants ; .z80 ;Compile for Zilog mnemonics false equ 0 true equ not false revnum equ 19 ;Revision 1.9 ddt equ false ;Set for debugging (smaller buffers) init equ false ;Set for INIT mode bios equ 0 ;Jmp to the BIOS jump table bdos equ 5 ;System entry ndrive equ 8 ;Number of physical drives fcb equ 5ch ;File control block tpa equ 0100h ;Start of the TPA secsiz equ 128 ;128 byte/sector ramdaty equ 42h ;Offset of RAMDATY pointer from BIOS vmsgpo equ 13 ;Virtual drive message pointer offset vdrvpo equ 15 ;Virtual drive pointer offset vdsko equ 19 ;Virtual disk offset ; ; Define character constants ; cntlc equ 'C'-64 ;Control C bs equ 'H'-64 ;Backspace lf equ 'J'-64 ;Line feed cr equ 'M'-64 ;Carraige return clear equ 'Z'-64 ;Clear screen esc equ '['-64 ;Escape character ; ; Le Programme ; entry backup ;Program entry point backup equ $ if not init ld hl,tpa ;Set up the stack (80h to 0ffh) ld sp,hl endif call start ;Set up the environment if init ld bc,0 ;No error... endif jp c,exit ;Quit on an error call copy ;Copy disks jp exit ;Quit ; ; Set up environment ; start: ld bc,18*3 ;Make a copy of the BIOS jump table ld de,cold ld hl,(bios+1) ld l,0 push hl ;Save for later RAMDATY calculations ldir pop hl ld de,ramdaty ;Offset to RAMDATY pointer add hl,de ld e,(hl) ;Load the RAMDATY pointer inc hl ld d,(HL) ;DE now points to RAMDATY ld hl,vdsko ;Virtual disk offset add hl,de ld (vdsk),hl ld hl,vmsgpo ;Virtual drive message pointer offset add hl,de ld c,(hl) ;Get the old virtual drive message inc hl ld b,(hl) ld hl,vdrvpo ;Virtual drive pointer offset add hl,de ld a,(hl) ;Get the old virtual drive pointer inc hl ld h,(hl) ld l,a or a ;Figure out the drive name address sbc hl,bc ld de,omess add hl,de ld (odrvp),hl ld hl,omess ;Place to save the message ld de,-1 ;Message length inicpy: ld a,(bc) ;Move a byte of the message ld (hl),a inc de ;Bump the length count inc bc ;Bump the message pointers inc hl or a ;Test for the end of the message jr nz,inicpy ld (omesln),de ;Save the message length if not init ;************************************** ld hl,signo0 ;Print signon call puts ld hl,signo1 ;Message for both diskettes ld a,(ndrive) ;Test for single drive mode or a jr nz,start0 ;Skip if a two drive system ld hl,(vdsk) ;Tell the BIOS that we are on drive 0 ld (hl),0 ld hl,signo2 ;Single drive message start0: ld a,(fcb+1) ;Test for a 'backup *' flag cp '?' ;CP/M exapands * into ? jr z,start1 call puts ;Print the signon message ld hl,signo3 ;Ask for [RETURN] or quit call puts call cin ;Wait for response call echo cp cr scf ;Set the quit flag ret nz ;Quit if not a CR endif ;************************************** start1: call crlf ;Echo an extra crlf ld a,0ffh ;Set the current drive ld (cdrive),a xor a ;Set the source and destination drives ld (from),a inc a ld (to),a ld a,(from) ;Test out the drives ld hl,frdpb call drvsel ld a,(to) ld hl,todpb call drvsel ld c,15 ;Make sure the drives are identical ld de,frdpb ld hl,todpb gatcp: ld a,(de) ;Compare the DPB's cp (hl) jr nz,gatcpe ;Skip if DPB's are different inc de ;Bump DPB pointers and counters inc hl dec c jr nz,gatcp ret ;Return no error gatcpe: ld hl,badcp ;Complain about dissimilar drives call puts scf ;Set the error flag ret ; ; Copy destination to source disk ; copy: call calc ;Figure out buffering, disk sizes ld bc,0 ;Source track ld d,b ;Destination track ld e,c ld hl,(dsksiz) ;Number of sectors call docopy ;Copy the disk ret c ;Return if an error occured ld hl,alldon ;Say that we are done call puts or a ;Clear the error flag if init ;************************************** ld bc,0 endif ;************************************** ret ;Done ; ; Copy some tracks ; docopy: ld (sparam),bc ;Source track ld (dparam),de ;Destination track ld (vparam),de ;Verify track ld (maxsec),hl ;Number of sectors to copy ld hl,0 ;Clear the sector numbers ld (sparam+2),hl ld (dparam+2),hl ld (vparam+2),hl docpy0: ld de,(maxsec) ;Get the number of sectors to copy ld a,d ;Test for no more sectors or e ret z ;Return 'done', carry reset ld hl,(numbuf) ;Get the buffer size (in sectors) call hlcde ;Compare jr c,docpy1 ;Skip if NUMBUF > NUMSEC ld h,d ;Do partial buffer load ld l,e docpy1: ld (numsec),hl ;Save number of sectors to move call readbf ;Read from disk to the buffer ret c ;Return if an error occures ld hl,(numsec) call writbf ;Write to disk from the buffer ret c ;Return if an error occures ld hl,(numsec) call veribf ;Verify the disk with the buffer ret c ;Return if an error occures ld de,(numsec) ;Knock off the sectors just copied from ld hl,(maxsec) ; the count call hlmde ;HL = HL-DE ld (maxsec),hl jr docpy0 ;Do another buffer load ; ; Fill the data buffer ; readbf: push hl ;Save the number of sectors to read ld hl,redmes ;Print 'Reading ...' ld (mesptr),hl ld a,(from) ;Select the source drive ld e,1 ;Second time select call select ld hl,datbuf ;Initial DMA address ld (dmaddr),hl ld a,(ndrive) ;Test for single drive copy or a jr nz,read0 ;Skip if two drive copy ld bc,smesln ;Load the source drive message ld de,smess+smesln ld hl,smess call cvmsg call gobble read0: pop hl read1: push hl ;Save the sector count ld hl,sparam ;Source parameters call setup ;Set up DMA, track, and sector values call read ;Read a sector or a ;Test for a read error pop hl ;Get the sector count jp nz,serror ;Quit if an error occures call prtrak ;Conditional 'Reading ...' message dec hl ;Bump the sector count ld a,h or l jr nz,read1 ret ;Return, no errors ; ; Flush the data buffer ; writbf: push hl ;Save the number of sectors to write ld hl,wrtmes ;Print 'writing ...' ld (mesptr),hl ld a,(to) ;Select the destination drive ld e,1 ;Second time select call select ld hl,datbuf ;Initial DMA address ld (dmaddr),hl xor a ;Set allocated write mode ld (allocw),a ld a,(ndrive) ;Test for single drive copy or a jr nz,writ0 ;Skip if two drive copy ld bc,dmesln ;Load the destination drive message ld de,dmess+dmesln ld hl,dmess call cvmsg call gobble writ0: pop hl writ1: push hl ;Save the sector count ld hl,dparam ;Destination position pointer call setup ;Set up DMA, track, and sector values ld a,(allocw) ;Get allocation mode ld c,a ld a,2 ;Future writes are unallocated ld (allocw),a call write ;Write a sector or a ;Test for a write error pop hl ;Get the sector count jp nz,derror ;Quit if an error occures call prtrak ;Conditional 'Writing ...' message dec hl ;Bump the sector count ld a,h or l jr nz,writ1 ret ;Return, no error ; ; Verify the data buffer ; veribf: push hl ld hl,vermes ;Print 'verifying...' ld (mesptr),hl pop hl ld de,verbuf ;Set compare address to data buffer ld (dmaddr),de veri0: push hl ;Save the sector count ld hl,vparam ;Verification parameters call setup ;Set up DMA, track, and sector values ld bc,verbuf ;Reset the DMA address to our buffer call setdma call read ;Read a sector or a ;Test for a read error pop hl ;Get the sector count jp nz,derror ;Quit if an error occures call prtrak ;Print 'Verifying track...' push hl ;Save the sector count ld c,secsiz ;Sector size ld de,verbuf ;Verify buffer pointer ld hl,(dmaddr) ;Data buffer pointer vercp: ld a,(de) ;Compare the buffers cp (hl) jr nz,vererr ;Skip if the buffers are different inc de ;Bump data pointers and counters inc hl dec c jr nz,vercp vererr: pop hl ;Restore the sector count jr nz,cerror ;Handle compare errors dec hl ;Bump the sector count ld a,h or l jr nz,veri0 ret ;Return no errors ; ; Data compare error reporter ; cerror equ $ if not init ;************************************** ld a,(cdrive) ;Get the current drive add a,'A' ;Convert to ASCII ld (errm1),a ;Save in the error message ld hl,errm0 ;'Data compare error ...' call puts endif ;************************************** jr derror ; ; Source disk error handler ; serror equ $ if init ;************************************** ld bc,2 ;Source disk error endif ;************************************** scf ret ; ; Destination disk error handler ; derror equ $ if init ;************************************** ld bc,1 ;Destination disk error endif ;************************************** scf ret ;Return an error ; ; Setup for a buffer operation, bump the DMA, track, and sector pointers ; setup: ld (param),hl ;Save the parameter table pointer ld c,(hl) ;Load the current track inc hl ld b,(hl) ld (track),bc inc hl ;Load the current sector ld e,(hl) inc hl ld d,(hl) ld (sector),de push de ;Save the sector call settrk ;Set the track pop bc ld de,(stranp) ;Sectran table pointer call sectran ;Translate logical to physical sectors ld b,h ld c,l call setsec ;Set the sector ld bc,(dmaddr) ;DMA address ld hl,secsiz ;Sector size add hl,bc ;Bump to the next sectors address ld (dmaddr),hl ;Save for the next read/write call setdma ;Set the DMA address ld bc,(track) ;Bump to the next disk position ld de,(spt) ld hl,(sector) inc hl ;Bump to the next sector call hlcde ;HL - DE jr c,setu0 ;Skip if SPT > SECTOR ld h,b ;Set up for printing ld l,c inc bc ;Bump to the next track ld hl,0 ; and to the start of that track setu0: ex de,hl ld hl,(param) ;Parameter table pointer ld (hl),c ;Save the track inc hl ld (hl),b inc hl ld (hl),e ;Save the sector inc hl ld (hl),d ret ; ; Allocate the buffers, calculate the number of sectors ; ; NUMBUF = (BIOS-DATBUF)/128 Number of data buffers in memory ; ; DSKSIZ = (OFF*SPT) + ((BLM+1)*(DSM+1)) Number of sectors on the disk ; calc: ld de,datbuf ;Start of data buffer area if init ld hl,0 ;Allocate the buffer up to the stack add hl,sp dec h ;Drop a page for the stack else if ddt ld hl,(bdos+1) ;Don't wipe out DDT else ld hl,(bios+1) ;Pointer to the top of the buffer area endif endif call hlmde ;HL = HL-DE ld a,h ;HL = HL/128 rla ld e,a ld a,0 rla ld h,a ld a,l rlca and 1 or e ld l,a ld (numbuf),hl ;Save the number of data buffers ld de,(off) ;Number of system tracks ld hl,(spt) ;Sectors per track call hltde ;HL = HL*DE push hl ;Save the system size ld de,(blm) ;Number of sectors per block - 1 ld d,0 ;BLM is a byte value inc de ld hl,(dsm) ;Number of blocks - 1 inc hl call hltde ;HL = HL*DE pop de ;Get the system size add hl,de ;Add to the data size ld (dsksiz),hl ;Save the number of disk sectors ret ; ; Select a drive, copy DPB ; drvsel: push bc push hl if init ld e,1 else ld e,0 ;First time select endif push af ld a,(fcb+1) ;Test for a 'backup *' flag cp '?' ;CP/M exapands * into ? jr nz,drvjmp ld e,1 drvjmp: pop af call select ;Select the drive pop de pop bc ld c,(hl) ;Save the sectran table pointer inc hl ld b,(hl) ld (stranp),bc ld bc,9 ;Offset to the DPB pointer add hl,bc ld a,(hl) ;Load the DPB pointer inc hl ld h,(hl) ld l,a ld bc,15 ;Copy the DPB ldir ret ; ; Select a drive ; select: ld (cdrive),a ;Save the currently selected drive ld c,a call seldsk ;Select the drive ret ; ; Termination routine ; exit: if init ;************************************** push bc ;Save the error status else ;* * * * * * * * * * * * * * * * * * * ld a,(fcb+1) ;Test for a 'backup *' flag cp '?' ;CP/M exapands * into ? jr z,exit1 ;Different closing message if * exit0: ld hl,goodby ;Print closing message call puts call cin call echo cp 'R' ;Test for re-copy jp z,backup endif ;************************************** exit1: ld a,(ndrive) ;Test for a single drive system or a jr nz,exit2 ;Skip if not a single drive system ld bc,(omesln) ;Restore the virtual drive message ld de,(odrvp) ld hl,omess call cvmsg ld hl,(vdsk) ;Tell the BIOS that we are on drive 0 ld (hl),0 exit2 equ $ if not init ;************************************** ld hl,bright ;Back to full brightness call puts ld a,0 ;drive A ld e,0 ;first time select call select ;select drive A jp warm ;Return to CP/M else ;* * * * * * * * * * * * * * * * * * * pop bc ;Retrieve the error status ret ;Return to the INIT program endif ;************************************** ; ; Print a track number ; prtrak: ld bc,(sector) ;Test for sector 1 dec bc ld a,b or c ret nz ;Not on sector 1, return push hl ;Save junk ld hl,(mesptr) ;Print the track message call puts ld hl,(track) ;Print the track # call puthl pop hl ret ; ; Print HL in decimal ; puthl: ld b,0 ;Leading null ld de,-10000 ;Knock off 10000's digit call puthl0 ld de,-1000 ;Knock off 1000's digit call puthl0 ld de,-100 ;Knock off 100's digit call puthl0 ld de,-10 ;Knock off 10's digit call puthl0 ld b,'0' ;Leading 0 ld de,-1 ;Knock off 1's digit puthl0: xor a ;'divide' count puthl1: inc a ;Bump count ld (hlsav),hl ;Save number add hl,de ;Knock off a digits worth jr c,puthl1 ld c,b ;Set up the leading character dec a ;We went one too far jr z,puthl2 ;Skip if (leading mode) add a,'0' ;Convert to ASCII ld c,a ld b,'0' ;Set up new leading character puthl2: ld a,c ;Test for a NULL or a jr z,puthl3 ;Skip print if null push bc ;Save the leading character call conout pop bc ;Restore leading character puthl3: ld hl,(hlsav) ;Restore number ret ; ; Input a character from the console ; cin: call gobble ;Gobble up extra characters call conin ;Get a character cp '@' ;Test for letters jr c,cinlet ;Skip if not a letter and 5fh ;Convert our letter to upper case cinlet: ret ; ; Gobble up the input buffer, kill type ahead ; gobble: call const ;Test for a character or a ret z call conin ;Get a character jr gobble ; ; Echo (A) + CRLF, don't blow up the flags ; echo: push af ;Save character and flags ld c,a ;Print the character call conout call crlf ;Follow up with a crlf pop af ret ; ; Print a CRLF ; crlf: ld hl,crlfs ;CRLF string call puts ret ; ; Print [hl] to the console ; puts: ld a,(hl) ;Get a character or a ;Test for the end of the string ret z ld c,a push hl call conout ;Print the character pop hl inc hl ;Bump the string pointer jr puts ; ; Hlmde subtracts DE from HL and returns. ; hlmde: ld a,l sub e ld l,a ld a,h sbc a,d ld h,a ret ; ; Hlcde compares HL with DE. On return the Z flag is set if they ; are equal, the Carry flag is set if DE is greater than HL. ; hlcde: ld a,h cp d ret nz ld a,l cp e ret ; ; Multiply the contents of HL by the contents of DE. ; hltde: ld c,l ld b,h ld hl,0 mult: ld a,b or c ret z ld a,b rra ld b,a ld a,c rra ld c,a call c,dadde ex de,hl add hl,hl ex de,hl jr mult dadde: add hl,de ret ; ; BIOS jump table ; cold: jp $ ;Cold boot warm: jp $ ;Warm Boot const: jp $ ;Console status conin: jp $ ;Console input conout: jp $ ;Console output list: jp $ ;Printer output punch: jp $ ;Punch output reader: jp $ ;Reader output home: jp $ ;Home drive seldsk: jp $ ;Select drive settrk: jp $ ;Set track setsec: jp $ ;Set sector setdma: jp $ ;Set DMA address read: jp $ ;Read a sector write: jp $ ;Write a sector listst: jp $ ;List status sectran:jp $ ;Sector translation cvmsg: jp $ ;Change virtual drive message ; ; Messages ; if not init signo0: db esc, '(', clear, esc, ')', 'Micro-Decision ' db esc, '(', 'BACKUP', esc, ')', ' program Rev. ' db revnum/10+'0', '.', revnum mod 10+'0' if ddt db 'D' else db ' ' endif db ' Copyright 1982', cr, lf db 'Morrow Designs, Inc. ' db ' San Leandro, CA.', cr, lf, lf, 0 signo1: db 'Inserô thå ', esc, '(', 'SOURCE', esc, ')' db ' diskette in Drive ', esc, '(', 'A:', esc, ')', cr, lf db 'Insert the ', esc, '(', 'DESTINATION', esc, ')' db ' diskette in Drive ', esc, '(', 'B:', esc, ')', cr, lf, lf, 0 signo2: db 'Insert the ', esc, '(', 'SOURCE', esc, ')' db ' diskette in the disk drive.', cr, lf, lf, 0 signo3: db 'Presó ', esc, '(', '[RETURN]', esc, ')' db ' wheî readù to copy.', cr, lf db 'Typå anù otheò characteò tï quitº ', 0 endif badcp: db cr, lf, esc, '(', 'Error, drive characteristics must be' db ' identical, can''t copy', esc, ')', 0 redmes: db cr, 'Reading track: ', bs, bs, bs, bs, 0 wrtmes: db cr, 'Writing track: ', bs, bs, bs, bs, 0 vermes: db cr, 'Verifying track: ', bs, bs, 0 alldon: db cr, lf, lf, 'Copy complete', 0 errm0: db cr, lf, esc, '(', 'Data compare error on drive ' errm1: db 'X:.', esc, ')', cr, lf db 'Retry the copy with a fresh ', esc, '(' db 'DESTINATION', esc, ')', ' diskette.', 0 if not init goodbi: db cr, lf, lf, 'Insert the system disk in drive A:, press ' db esc, '(', '[RETURN]', esc, ')', ' ', 0 goodby: db cr, lf, lf, 'Press ', esc, '(', 'R', esc, ')' db ' to copy a new disk or, to quit, insert' db cr, lf, 'the system disk in drive A: and press ', esc, '(' db '[RETURN]', esc, ')', ' ', 0 endif smess: db cr, lf, 'Insert the ' if not init db esc, '(', 'SOURCE', esc, ')', ' diskette' else db 'CP/M DISTRIBUTION DISKETTE' endif db ', press ' if not init db esc, '(' endif db '[RETURN]' if not init db esc, ')' endif db ' ', 0, 0 smesln equ $-smess dmess: db cr, lf, 'Insert the ' if not init db esc, '(' endif db 'DESTINATION' if not init db esc, ')' endif db ' diskette, press ' if not init db esc, '(' endif db '[RETURN]' if not init db esc, ')' endif db ' ', 0, 0 dmesln equ $-dmess if not init bright: db esc, '(', 0 ;Turn bright vidio on endif crlfs: db cr, lf, 0 omess: ds 90 ;Space for the old message ; ; Data ; omesln: dw 0 ;Old vitual drive message length odrvp: dw 0 ;Old virtual drive pointer vmsgp: dw 0 ;Virtual message pointer in RAMDATY vdsk: dw 0 ;Virtual drive in RAMDATY cdrive: db 0 ;Currently selected drive numbuf: dw 0 ;Number of data buffers dsksiz: dw 0 ;Number of DISK sectors mesptr: dw 0 ;Pointer to the current message ;Source drive parameters sparam: dw 0 ;Sector position dw 0 ;Track position ;Destination drive parameters dparam: dw 0 ;Sector dw 0 ;Track ;Verification drive parameters vparam: dw 0 ;Sector dw 0 ;Track param: dw 0 ;Current parameter table pointer allocw: db 0 ;Alocated write mode flag maxsec: dw 0 ;Number of sectors to copy numsec: dw 0 ;Number of sectors left to copy dmaddr: dw 0 ;Current DMA address track: dw 0 ;Current track address sector: dw 0 ;Current sector address stranp: dw 0 ;Sector translation table pointer hlsav: dw 0 ;Saved number used during printer from: db 0 ;Source drive name frdpb equ $ ;Drives DPB spt: dw 0 ;Sectors per track bsh: db 0 ;Block shift factor blm: db 0 ;Block limit exm: db 0 ;Extent mask dsm: dw 0 ;Disk size maximum drm: dw 0 ;Directory size maximum al0: db 0 ;Directory allocation 0 al1: db 0 ;Directory allocation 1 cks: dw 0 ;Number of checked directory entries off: dw 0 ;Directory offset to: db 0 ;Destination drive name todpb: ds 15 ;Drives DPB ; ; Disk data buffers ; verbuf equ $ ;Verify buffer datbuf equ verbuf+secsiz ;Data buffers end