; ;512 byte deblock copy program ;written by g.w.mulchin 6-3-81 ;tarbell electronics ;copies single or double sided ;last update - 02-11-82 ; ;system equates ; TRUE EQU 0FFFFH FALSE EQU NOT TRUE ; DUAL EQU FALSE ;true if using persci ; DISK EQU 0F8H ;disk controller base addres DMAP EQU 0E0H ;dma controller base address ; ADR0 EQU DMAP+0 ;dma adrress registers WCT0 EQU DMAP+1 ;dma word count registers CMND EQU DMAP+8 ;dma command register ; DCOM EQU DISK+0 ;disk command port DSTAT EQU DCOM ;disk status port TRACK EQU DCOM+1 ;disk track port SECTP EQU DCOM+2 ;disk sector port DDATA EQU DCOM+3 ;disk data port WAIT EQU DCOM+4 ;disk wait port DCONT EQU WAIT ;disk control port DMACHK EQU DCOM+5 ;intrq check port RTCNT EQU 10 ;retry count number HLAB EQU 008H ;head load at beginning of seek STPRAT EQU 001H ;0=3ms,1=6ms,2=10ms,3=15ms VERIFY EQU 004H ;verify bit RESTORE EQU STPRAT ;restore command SEEK EQU 010H ;seek command B128 EQU 128 ;single density sector size B512 EQU 512 ;double density sector size ; WBOOT EQU 0 ;warm boot entry BDOS EQU 5 ;cpm entry point PRINTF EQU 9 ;print buffer function BUFIN EQU 10 ;console buffer input function TERM EQU '$' ;function 9 terminator CR EQU 0DH ;carriage return LF EQU 0AH ;linefeed BEL EQU 07H ;bel code ; ;start of program ; ORG 100H ;runs in tpa area ; START: JMP BEGIN ;jump around subroutines ; ;-------------------------------------------------------------- ; subroutine area ;-------------------------------------------------------------- ; ;messages ; OPENM: DB CR,LF,'Tarbell Deblock Copy Program ver 1.4' DB ' of 02-11-82',CR,LF,TERM MENU: DB CR,LF,'Table of Functions' DB CR,LF,'------------------' DB CR,LF DB CR,LF,' 1 - Copy System' DB CR,LF,' 2 - Copy Data' DB CR,LF,' 3 - Copy All' DB CR,LF,' 4 - Select Drives' DB ' (or ' SRCD: DB 'A: copied to ' DSTD: DB 'B:)' DB CR,LF,' 5 - Exit to System' DB ' (place system disk in first)' DB CR,LF,CR,LF,'Function (1,2,3,4,5)? ',TERM CRLF: DB CR,LF,TERM SIZERR: DB CR,LF,BEL,'TPA space not large enough!' DB CR,LF,BEL,'--------------------------' DB CR,LF,TERM MEMTST: DB CR,LF,'Testing buffer space. ' DB CR,LF,'---------------------',CR,LF,TERM MEMERR: DB CR,LF,BEL,'512copy memory buffer error,' DB ' check for bad memory between ' MEMLOW: DB '0000 hex and ' MEMHGH: DB '0000 hex.',TERM SRCCHG: DB CR,LF,'Source Drive? ',TERM DSTCHG: DB CR,LF,'Destination Drive? ',TERM IDERR: DB CR,LF,BEL,'Disk I.D. do not match each other' DB ' or they are not deblocked disks.' DB CR,LF,'---------------------------------' DB '---------------------------------',CR,LF,TERM TRK0ER: DB CR,LF,BEL,'Track 0 read E R R O R on drive ' TK0DRV: DB ' :',CR,LF,TERM HMERR: DB CR,LF,BEL,'Home error on drive ' HMDRV: DB ' :',CR,LF,TERM CPYER: DB CR,LF,BEL,'Error on track ' CPYTK: DB ' , sector ' CPYSEC: DB ' , of drive ' CPYDRV: DB ' :',CR,LF,TERM COMERR: DB CR,LF,BEL,'Error in data transfered!' DB ' Aborting copy operation.',CR,LF,TERM COMFUN: DB CR,LF,BEL,'Function Complete.',CR,LF,TERM TRKCPY: DB CR,'Copying Track ' TKCPY: DB ' ',TERM CPYTYP: DB CR,LF,'Copying a ',TERM CPYSIN: DB 'Single Sided Disk',CR,LF,TERM CPYDUB: DB 'Double Sided Disk',CR,LF,TERM MEMUSE: DB CR,LF,'Buffer Start: ' MEMUS1: DB '0000 hex.' DB CR,LF,'Buffer End: ' MEMUS2: DB '0000 hex.',CR,LF,TERM SEKERR: DB CR,LF,BEL,'Seek Error on Drive ' SEKDRV: DB ' :',TERM ; ;internal skew table for double density - skew = 4 ; TABLE: DB 1,5,9,13,2,6,10,14,3,7,11,15,4,8,12,16 ; ;seek error report routine ; ERSEEK: MOV A,B ;get drive number ADI 'A' ;ascii adjust it STA SEKDRV ;save for message LXI D,SEKERR ;point to message CALL PRINT ;print it MVI A,1 ;error code RET ; ;initialization subroutine ;entry: none ;exit: constants in program zeroed ;registers used: reg. a,h,l ; INIT: XRA A ;get a zero STA CPYTK ;clear track STA CPYSEC ;clear copy sector MOV L,A ;clear reg l MOV H,A ;clear reg h SHLD TRKTAB ;clear track table SHLD TRKTAB+2 ;clear track table+2 RET ; ;hex to ascii conversion ;entry: h,l = memory address ; d,e -> memory storage ;exit: none ;registers used: all ; HEXOUT: MOV A,H ;get high byte CALL HXOUT ;print high byte address MOV A,L ;get low byte HXOUT: PUSH PSW ;save it RRC ;shift RRC ;right RRC ;4 RRC ;times CALL OUTCHR ;save the ascii byte POP PSW ;restore for low nibble OUTCHR: ANI 0FH ;strip upper nibble ADI 90H ;add offset DAA ;decimal adjust ACI 40H ;add offset DAA ;decimal adjust STAX D ;save the char INX D ;bump for next RET ; ;binary to decimal conversion ;entry: reg. a = value to convert ; reg. h,l -> memory for storage ;exit: none ;registers used: reg. a,b,h,l ; BINDEC: MVI B,10 ;radix value CALL BID1 ;convert the value ADI '0' ;add ascii 0 MOV M,A ;place into memory RET ; BID1: MVI M,'0'-1 BID2: INR M SUB B JNC BID2 ADD B INX H RET ; ;read disk id on track 0 of each disk ;entry: none ;exit: condition of disk id ; e.g. - do they match? ; drive physical code saved ; type disk printed (sing or doub sided) ;registers used: all ; IDREAD: LDA SRCDRV ;get the source disk CALL IDRD ;read the disk id byte ORA A ;any errors RNZ ;return if error found MOV A,B ;get the id byte STA SRCID ;save the byte LDA CLATCH ;get src clatch value STA SLATCH ;save for copy operation LDA DSTDRV ;get the destination disk CALL IDRD ;read its id byte ORA A ;any errors RNZ ;return if yes LDA CLATCH ;get dst clatch value STA DLATCH ;save for copy operation LDA SRCID ;get source id byte CMP B ;do they match? JZ IDMTCH ;jump if so NOMTCH: LXI D,IDERR ;point to id error msg CALL PRINT ;print and return to main line MVI A,1 ;we had an error RET ; IDMTCH: CPI 0DEH ;single sided 512 x 16 ? MVI A,0 ;0 = single sided JZ IDMTC1 ;jump if so MOV A,B ;recover id byte CPI 0DCH ;doub sided 512 x 16? MVI A,1 ;1 = doub sided JZ IDMTC1 ;jump if double sided JMP NOMTCH ;error in id IDMTC1:STA SIDED ;save the code LXI D,CPYTYP ;point to the type of disk CALL PRINT ;print it LDA SIDED ;check which type ORA A ;single sided? LXI D,CPYSIN ;single sided message JZ IDMTYP ;print its single sided LXI D,CPYDUB ;double sided message IDMTYP: CALL PRINT ;print the message XRA A ;no errors found RET ; IDRD: PUSH PSW ;save drive select code MOV C,A ;pass it in reg c CALL SELDSK ;select the disk CALL SIDE0 ;read side 0 CALL SDEN ;set it for single density STA CLATCH ;save this drive phisical code CALL DSKSEL ;physically select the drive CALL HOME ;home the drive ANI 91H ;check for errors JNZ HOMER ;jump if we have an error LXI B,RBUFF ;use buffer address CALL SETDMA ;set the memory address MVI C,1 ;sector 1 CALL SETSEC ;save the sector CALL READSD ;read track 0 sector 1 ORA A ;read error? JNZ RDTKER ;jump if so POP PSW ;clean up stack LDA RBUFF+7EH ;get id byte MOV B,A ;save id in reg b XRA A ;return no error code RET ;return id byte ; HOMER: POP PSW ;recover drive code ADI 'A' ;adjust back to ascii STA HMDRV ;show the drive in msg LXI D,HMERR ;point to home error CALL PRINT ;print the message MVI A,1 ;error found RET ; RDTKER: POP PSW ;recover drive code ADI 'A' ;ascii adjust STA TK0DRV ;show drive in error LXI D,TRK0ER ;point to error msg CALL PRINT ;print and ret to main line MVI A,1 ;error found RET ; ;select source/destination drives ;entry: none ;exit: default drives changed ; same drive not allowed ;registers used: all ; DRIVE: LXI D,SRCCHG ;point to source change CALL PRINT ;print the message CALL INPUT ;get the drive LDA TEMP ;get the char CPI 'E' ;check for out of range JNC SAMDRV ;return if to big STA SRCD ;modify message SUI 'A' ;make physical drive code STA SRCDRV ;save it LXI D,DSTCHG ;point to dst. change msg CALL PRINT ;print the message CALL INPUT ;get a keyboard char. CPI 'E' ;check for out of range JNC SAMDRV ;return if to big LDA TEMP ;get destination drive STA DSTD ;modify drive msg SUI 'A' ;make physical drive STA DSTDRV ;save the code SAMDRV: LDA SRCDRV ;check for using same drive MOV B,A ;save in reg b LDA DSTDRV ; for the copy operation CMP B ;are they the same? JZ DRIVE ;cant have this! RET ; ;select disk routine: ;entry: reg c = drive to select ;exit: drive hardware code (clatch) ;registers used: reg. a ; SELDSK: MOV A,C ;get drive to select ADD A ;shift ADD A ;four ADD A ;places ADD A ;left RET ;return hardware select code ; ;side select subroutines ;entry: hardware select code (reg. a) ;exit: side selected ;registers used: reg. a ; SIDE0: ANI 10111000B ;strip side bit RET ; SIDE1: ORI 01000000B ;set side 1 bit on RET ; ;density change subroutines ;entry: hardware select code ;exit: density change code ;registers used: reg. a ; SDEN: ANI 11110111B ;strip to single density RET ; DDEN: ORI 00001000B ;set to double density RET ; ;keyboard input routine ;entry: none ;exit: temp = key pushed ;registers used: all ; INPUT: MVI A,3 ;buffer length LXI H,0 ;clear h,l SHLD KEYBUF+1 ;clear keyboard buffer STA KEYBUF ;set buffer length LXI D,KEYBUF ;point to buffer MVI C,BUFIN ;function 10 CALL BDOS ;get key from bdos LDA KEYBUF+2 ;get key pushed CPI 'a' ;check the case of char. JC NOCONV ;jump if upper case CPI 'z'+1 JNC NOCONV ;jump if upper ANI 5FH ;strip to upper case if not. NOCONV: STA TEMP ;save the character RET ;return to caller ; ;print buffer routine ;entry: d,e -> message to print ;exit: message printted by bdos ;registers used: all saved and restored ; PRINT: PUSH PSW ;save the world PUSH B PUSH D PUSH H MVI C,PRINTF ;function 9 code CALL BDOS ;use bdos to print POP H POP D POP B POP PSW ;restore the world RET ; ;select drive hardware subroutine and delay ;on a new drive select ;entry: reg. a = physical drive select ;exit: drive physically selected ;registers used: reg. a ; DSKSEL: OUT DCONT ;select it now PUSH PSW ;save accum and flags IN TRACK ;get present track value OUT DDATA ;place into data register MVI A,SEEK+STPRAT ;get seek without headload OUT DCOM ;send it CALL SLOOP ;wait till done POP PSW ;restore accum and flags RET ;return to caller ; ;head loaded check subroutine ;entry: none ;exit: reg. a = load or un-loaded byte ;registers used: reg. a ; HDLD: IN DSTAT ;check disk status ANI 00100000B ;look at head load bit MVI A,00000100B ;if not loaded, then set it RZ ; and return if not loaded MVI A,00000000B ;else, RET ;head already loaded ; ;status loop subroutine ;entry: none ;exit: disk status register ;registers used: reg. a ; SLOOP: IN DMACHK ;check intrq bit (bit 7) RLC ;rotate into carry flag JC SLOOP ;loop till not busy XRA A ;clear dma chip OUT CMND ;stop any possible dma req. IN DSTAT ;get disk status RET ;and return to caller ; ;home subroutine ;entry: none ;exit: drive selected is homed ;registers used: reg. a ; HOME: MVI A,RESTORE ;home using restore command OUT DCOM ;send the command JMP SLOOP ;wait till done and ret. status ; ;set sector subroutine ;entry: reg. c = sector ;exit: none ;registers used: reg. a ; SETSEC: PUSH PSW ;save accum and flags MOV A,C ;get the sector STA SECTOR ;save the sector POP PSW ;restore accum and flags RET ; ;set dma address ;entry: reg b,c = dma address ;exit: none ;registers used: h,l ; SETDMA: MOV H,B ;get high byte MOV L,C ;get low byte SHLD DMAADR ;save the dma address RET ; ;seek track routine ;entry: reg. b = drive # ; reg. c = track to seek ;exit: reg. a = 0 if ok, 1 if error ;registers used: all ; SEEKT: LDA DISKNO ;get last drive IF DUAL ANI 0FEH ;isolate low bit ENDIF MOV E,A ;put into reg.e MVI D,0 ;clear reg d LXI H,TRKTAB ;point to track table PUSH H ;save track table pointer DAD D ;get index IN TRACK ;get present track position MOV M,A ;save it for later MOV A,B ;get new disk number IF DUAL ANI 0FEH ;isolate low bit ENDIF POP H ;recover track table pointer MOV E,A ;get new disk number DAD D ;new table MOV A,M ;get new track number OUT TRACK ;send to floppy chip MOV A,B ;get last disk in use STA DISKNO ;save it MVI A,RTCNT ;get max retrys to do SEKRTY: STA RETRY ;save it IN TRACK ;get the present track CMP C ;do they match MVI A,0 ;condition code = good RZ ;return if good seek MOV A,C ;else, get track to seek OUT DDATA ;send it to disk chip MVI A,SEEK+VERIFY+STPRAT+HLAB OUT DCOM ;send seek command CALL SLOOP ;loop till done ANI 91H ;check for errors MVI A,0 ;condition code = good RZ ;return if none LDA RETRY ;error detected, get retry DCR A ;decrease it JNZ SEKRTY ;try again INR A ;error return code if failed RET ; ;read single density sector subroutine ;entry: none ;exit: reg. a = error condition ;registers used: all ; READSD: MVI A,RTCNT ;get max retry count RDSDRTY:STA RETRY ;set retry counter LXI B,80D0H ;floppy read , force intrp LXI D,4080H ;dma read, count byte CALL DMARW ;use common routine ANI 9DH ;check for errors JZ RDSDOK ;jump if ok LDA RETRY ;get retry counter DCR A ;decrease it JNZ RDSDRTY ;retry if not = 0 MVI A,1 ;return error condition RET ;1 = error in reading of sector ; RDSDOK: SHLD DMAADR ;save pointer for next read MVI A,0 ;return no error code RET ; ;read double density sector routine ;entry: none ;exit: reg. a = error condition ;registers used: all ; READDD: MVI A,RTCNT ;get max retry count RDDDRTY:STA RETRY ;save retry count LXI B,80D0H ;floppy read , force intrp LXI D,4200H ;dma read, count byte (512 byte) CALL DMARW ;use common routine ANI 9DH ;check for errors JZ RDDDOK ;jump if ok LDA RETRY ;get retry counter DCR A ;decrease it JNZ RDDDRTY ;retry if not = 0 MVI A,1 ;return error condition RET ;1 = error in reading of sector ; RDDDOK: SHLD DMAADR ;save pointer for next read MVI A,0 ;return no error code RET ; ;write single density sector subroutine ;entry: none ;exit: reg. a = error condition ;registers used: all ; WRITSD: MVI A,RTCNT ;get max retry count WTSDRTY:STA RETRY ;save retry count LXI B,0A0D0H ;floppy write , force intrp LXI D,8080H ;dma write, count byte CALL DMARW ;use common routine ANI 0FDH ;check for errors JZ WTSDOK ;jump if ok LDA RETRY ;get retry counter DCR A ;decrease it JNZ WTSDRTY ;retry if not = 0 MVI A,1 ;return error condition RET ;1 = error in writing of sector ; WTSDOK: SHLD DMAADR ;save pointer for next write MVI A,0 ;return no error code RET ; ;write double density sector routine ;entry: none ;exit: reg. a = error condition ;registers used: all ; WRITDD: MVI A,RTCNT ;get max retry count WTDDRTY:STA RETRY ;save retry count LXI B,0A0D0H ;floppy write , force intrp LXI D,8200H ;dma write, count byte (512 byte) CALL DMARW ;use common routine ANI 0FDH ;check for errors JZ WTDDOK ;jump if ok LDA RETRY ;get retry counter DCR A ;decrease it JNZ WTDDRTY ;retry if not = 0 MVI A,1 ;return error condition RET ;1 = error in writing of sector ; WTDDOK: SHLD DMAADR ;save pointer for next write MVI A,0 ;return no error code RET ; ;dma read/write subroutine ;entry: reg.b = floppy read/write command ; reg.c = floppy force intrp command ; reg.d = dma read/write cmnd + high byte ; reg.e = dma low byte count ;exit: reg.h,l = dma address + byte count ; reg.a = disk status byte ;registers used: all ; DMARW: STA RETRY ;save retry counter XRA A ;get a zero OUT CMND ;clear dma chip LDA SECTOR ;get sector to do OUT SECTP ;send to floppy chip LHLD DMAADR ;get the memory address to use MOV A,C ;get force intrp command OUT DCOM ;send to floppy DCX D ;byte count - 1 MOV A,E ;get low byte OUT WCT0 ;send to word count low byte MOV A,D ;get dma read/write code OUT WCT0 ;send to word count high byte INX D ;restore d,e MOV A,L ;get low memory address OUT ADR0 ;send to dma addr low byte reg MOV A,H ;get high memory address OUT ADR0 ;send to dma addr high byte reg MVI A,41H ;request channel 0 of dma. OUT CMND ;send to dma command register CALL HDLD ;check for head loaded ORA B ;add in read/write command OUT DCOM ;do the read/write operation MOV A,D ;get dma command byte ANI 3FH ;strip command bits MOV D,A ;restore for add of address DAD D ;h,l = h,l + d,e JMP SLOOP ;check operation and return ; ;check tpa size subroutine ;entry: none ;exit: 0 = tpa large enough, 1 = tpa to small ;registers used: all ; SIZE: LXI D,-(RBUFF) ;point to start of buffers LHLD BDOS+1 ;get top of cpm memory DCX H ;decrease by 1 DAD D ;add clear space needed XCHG ;place into d,e LXI H,-(B512*16*2) ;need 2 tracks worth of memory XRA A ;clear carry flag DAD D ;compute the size MVI A,0 ;condition code RC ;return if tpa large enough INR A ;else, RET ;return error, tpa to small ; ;check buffer memory subroutine ;entry: none ;exit: 0 = ok, 1 = memory error ;registers used: all ; CHECK: LHLD BDOS+1 ;get top of memory DCX H ;decrease by 1 LXI D,-(RBUFF) ;start of buffers DAD D ;get the length of buffers MOV B,H ;get high byte MOV C,L ;length in b,c LXI H,RBUFF ;point to start of buffers LXI D,0FFH ;reg d = 0, reg e = ff CHKLOP: MOV A,E ;put in a ff MOV M,A ;put into memory MOV A,M ;recover it CMP E ;match? MVI A,1 ;error code if not RNZ ;return if not MOV A,D ;get a zero MOV M,A ;put into memory MOV A,M ;recover it CMP D ;match? MVI A,1 ;error code if not RNZ ;return if not INX H ;bump pointer DCX B ;decrease count MOV A,C ;check for end count=0 ORA B ;end yet? JNZ CHKLOP ;loop till done with check XRA A ;return no error condition RET ; ;do requested function ; ;entry: none ;exit: routine executed ;registers used: reg. a,d,e,h,l ; DOIT: LDA TEMP ;get key pushed SUI '1' ;adjust it CPI 5 ;check range RNC ;return if out of range ADD A ;double it MVI D,0 ;index setup MOV E,A ; value LXI H,FUNTAB ;point to function table DAD D ;get the routine MOV E,M ;get low byte INX H ;bump pointer MOV D,M ;get high byte XCHG ;flip PCHL ;go to routine ; ;exit to system ;entry: none ;exit: none ;registers used: none ; EXIT: JMP WBOOT ;go to cpm ; ;function table ; FUNTAB: DW SYSTEM ;doing system DW DATA ;doing data DW ALL ;doing all DW DRIVE ;doing drive change DW EXIT ;done with copy ; ;buffer compare subroutine ;entry: b,c = length to read ;exit: error condition on compare ; or condition code if ok ;registers used: all ; BUFCOM: LXI H,RBUFF ;point to primary buffer LXI D,RBUFF+(512*16);compare buffer BUFLOP: LDAX D ;get a byte CMP M ;match original? JNZ COMER ;jump if not INX H ;bump INX D ;pointers DCX B ;decrease count MOV A,C ;check ORA B ;end JNZ BUFLOP ;loop if not done XRA A ;clear accum RET ;return no errors ; COMER: LXI D,COMERR ;point to compare error msg CALL PRINT ;print the message MVI A,1 ;error code RET ; ;track read routine - single density ;entry: reg. h,l -> sector to start with ; reg. d = # of sectors to do ;exit: buffer filled for 1 track of data ;registers used: all ; RDSDLP: MOV C,M ;get the first sector RDSDL1: PUSH B ;save trk count and # of sects PUSH D ;save sector to do CALL SETSEC ;set the sector CALL READSD ;read a sector POP D ;restore sector number POP B ;restore trk count & # of sects ORA A ;any errors JNZ CPYERR ;jump if so INR C ;bump sector number DCR D ;decrease the number of sectors JNZ RDSDL1 ;loop till all track is read RET ;track has been read ; CPYERR: MOV A,C ;get the sector were at STA SECTOR ;save it for later LDA BEGTRK ;what track are we on now LXI H,CPYTK ;point to track storage CALL BINDEC ;convert to decimal LDA SECTOR ;get last sector LXI H,CPYSEC ;point to storage area CALL BINDEC ;convert to decimal LDA DISKNO ;get current error drive ADI 'A' ;convert to ascii STA CPYDRV ;save for mesage LXI D,CPYER ;point to error msg CALL PRINT ;print and return MVI A,1 ;error code RET ; ;track write routine - single density ;entry: reg. h,l -> sector to use ; reg. d = # of sectors to do ;exit: buffer written to disk ;registers used: all ; WTSDLP: MOV C,M ;get starting sector WTSDL1: PUSH B ;save trk count and # of sects PUSH D ;save sector to do CALL SETSEC ;set the sector CALL WRITSD ;write a sector POP D POP B ORA A ;any errors JNZ CPYERR ;jump if so INR C ;bump sector number DCR D ;decrease the number of sectors JNZ WTSDL1 ;loop till all track is read RET ;track has been written ; ;track read routine - double density ;entry: reg. h,l -> sector to use ; reg. d = # of sectors to do ;exit: buffer filled for 1 track of data ;registers used: all ; RDDDLP: PUSH B ;save trk count and # of sects PUSH D ;save sector to do PUSH H ;save sector pointer MOV C,M ;get the sector CALL SETSEC ;set the sector CALL READDD ;read a sector POP H ;restore sector pointer POP D POP B ORA A ;any errors JNZ CPYERR ;jump if so INX H ;bump sector pointer to next DCR D ;decrease the number of sectors JNZ RDDDLP ;loop till all track is read RET ;track has been read ; ;track write routine - double density ;entry: reg. h,l -> sector to use ; reg. d = # of sectors to do ;exit: buffer written to disk ;registers used: all ; WTDDLP: PUSH B ;save trk count and # of sects PUSH D ;save sector to do PUSH H ;save sector pointer MOV C,M ;get the sector CALL SETSEC ;set the sector CALL WRITDD ;write a sector POP H ;restore sector pointer POP D POP B ORA A ;any errors JNZ CPYERR ;jump if so INX H ;point to next sector DCR D ;decrease the number of sectors JNZ WTDDLP ;loop till all track is read RET ;track has been written ; ;system copy operation ;entry: none ;exit: system copied to dest disk ;registers used: all ; SYSTEM: CALL IDREAD ;read src and dst drives ORA A ;any errors? RNZ ;return if so MVI A,2 ;ending track+1 STA ENDTRK ;save it MVI A,0 ;start track STA BEGTRK ;save it STA OPFLAG ;save doing system JMP CPYTRK ;copy tracks and verify ; ;copy data operation ;entry: none ;exit: data files copied ;registers used: all ; DATA: CALL IDREAD ;check disk id ORA A ;any errors? RNZ ;return if yes LDA SIDED ;doing double sided? ORA A JZ DATA1 ; ;double sided copy of data tracks ; MVI A,76 ;end track STA ENDTRK MVI A,0 ;set for track -1 STA BEGTRK ;save it INR A ;not doing system only STA OPFLAG ;operation flag JMP DREPEAT ;copy tracks and verify ; ;single sided copy of data tracks ; DATA1: MVI A,75 ;end track-1 STA ENDTRK MVI A,1 ;start track STA BEGTRK STA OPFLAG ;operation flag JMP DREPEAT ;copy tracks and verify ; ;copy all operation ;entry: none ;exit: all tracks copied ;registers used: all ; ALL: CALL IDREAD ;check disk id ORA A ;any errors RNZ MVI A,77 ;ending track+1 STA ENDTRK STA OPFLAG ;save operation flag MVI A,0 ;starting track STA BEGTRK CPYTRK: ORA A ;doing track 0? JNZ SSIDED ;no, doing > track 0 ; ;copying track 0 - single density ; LDA SLATCH ;get source drive latch code CALL SIDE0 ;select side 0 CALL SDEN ;switch to single density STA SLATCH ;save it CALL DSKSEL ;select it LDA BEGTRK ;get source begin track PUSH PSW LXI H,TKCPY CALL BINDEC ;convert to decimal LXI D,TRKCPY CALL PRINT POP PSW MOV C,A ;pass to seek routine LDA SRCDRV ;get source drive MOV B,A ;pass to seek routine CALL SEEKT ;seek to the track ORA A JNZ ERSEEK LXI B,RBUFF ;point to read buffer CALL SETDMA ;set the memory address MVI D,26 ;this = the number of sect/trk LXI H,TABLE ;point to sector table CALL RDSDLP ;read 1 tracks worth of data ORA A ;any errors? RNZ ;return if there are. LDA DLATCH ;get destination latch CALL SIDE0 ;switch to side 0 CALL SDEN ;switch to single density STA DLATCH ;save it CALL DSKSEL ;select it LDA BEGTRK ;get dest begin track MOV C,A ;pass to seek routine LDA DSTDRV ;get dst drive MOV B,A ;pass to seek routine CALL SEEKT ;seek to the track ORA A JNZ ERSEEK LXI B,RBUFF ;point to start of buffer CALL SETDMA MVI D,26 ;26 sectors to write LXI H,TABLE ;point to sector table CALL WTSDLP ;write the data out ORA A ;any errors? RNZ ;return if so LXI B,RBUFF+(B512*16) ;next buffer CALL SETDMA MVI D,26 ;sectors to read LXI H,TABLE ;point to sector table CALL RDSDLP ;read the data just written ORA A ;any errors in reading RNZ ;return if so LXI B,B128*26 ;size of track 0 CALL BUFCOM ;compare the buffers ORA A ;any errors RNZ ; ;now check for double sided operation on source disk ; SSIDED: LDA ENDTRK ;get the ending track DCR A ;subtract 1 STA ENDTRK ;save it RZ ;return if last track LDA SIDED ORA A ;doing doub sided? JZ DREPEAT ;jump if not LDA SLATCH ;get source latch code CALL DDEN ;switch to double density CALL SIDE1 ;do side 1 STA SLATCH ;save it CALL DSKSEL ;select the hardware ; ;side 1 ready for copy ; LXI B,RBUFF ;point to read buffer CALL SETDMA ;save memory pointer MVI D,16 ;doing 16 sectors LXI H,TABLE ;point to sector table CALL RDDDLP ;read 1 double density track ORA A ;any errors? RNZ ;return if so LDA DLATCH ;get destination latch code CALL DDEN ;switch to double density CALL SIDE1 ;do side 1 STA DLATCH ;save it CALL DSKSEL ;select the drive ; ;side 1 ready for write ; LXI B,RBUFF ;point to buffer just written CALL SETDMA ;set memory pointer MVI D,16 ;write 16 sectors LXI H,TABLE CALL WTDDLP ;write 1 tracks worth of data ORA A ;any errors? RNZ ; ;now check the data just written doub sided ; LXI B,RBUFF+(B512*16) ;point to top buffer CALL SETDMA ;save the memory pointer MVI D,16 ;read 16 sectors LXI H,TABLE CALL RDDDLP ;read the track in to buffer ; ;now check for errors in buffer for doub sided ; LXI B,B512*16 ;size = 1 tracks worth CALL BUFCOM ;compare the 2 buffers ORA A ;any errors? RNZ LDA SLATCH ;get source latch CALL SIDE0 ;switch back to side 0 STA SLATCH ;save for next track LDA DLATCH ;get destination latch CALL SIDE0 ;switch back to side 0 STA DLATCH ;save for next track LDA OPFLAG ;doing system only ORA A ;does it = 0 RZ ;return if so ; ;copying double density tracks ; DREPEAT:LDA SLATCH ;get source latch code CALL DDEN ;switch to double density CALL SIDE0 STA SLATCH ;save it CALL DSKSEL ;select the hardware now LDA BEGTRK ;get start track value INR A ;bump up by 1 STA BEGTRK ;save next track to do PUSH PSW LXI H,TKCPY CALL BINDEC ;comvert to decimal LXI D,TRKCPY CALL PRINT POP PSW MOV C,A ;pass to seek routine LDA SRCDRV MOV B,A CALL SEEKT ;seek to the track ORA A JNZ ERSEEK ; ;side 0 ready for copy - source disk ; LXI B,RBUFF ;point to read buffer CALL SETDMA ;save memory pointer MVI D,16 ;doing 16 sectors LXI H,TABLE ;point to sector table CALL RDDDLP ;read 1 double density track ORA A ;any errors? RNZ ;return if so LDA DLATCH ;get dest latch code CALL DDEN ;set to double density CALL SIDE0 STA DLATCH ;save it CALL DSKSEL ;select the hardware now LDA BEGTRK ;get present track MOV C,A ;pass to seek routine LDA DSTDRV MOV B,A CALL SEEKT ;seek to it ORA A JNZ ERSEEK ; ;side 0 ready for write - destination disk ; LXI B,RBUFF ;point to buffer just written CALL SETDMA ;set memory pointer MVI D,16 ;write 16 sectors LXI H,TABLE CALL WTDDLP ;write 1 tracks worth of data ORA A ;any errors? RNZ ; ;now check the data just written to destination disk ; LXI B,RBUFF+(B512*16) ;point to top buffer CALL SETDMA ;save the memory pointer MVI D,16 ;read 16 sectors LXI H,TABLE CALL RDDDLP ;read the track in to buffer ; ;now check for errors - in buffer ; LXI B,B512*16 ;size = 1 tracks worth CALL BUFCOM ;compare the 2 buffers ORA A ;any errors? RNZ JMP SSIDED ;loop till all tracks done ; ;------------------------------------------------------------ ; main program ;------------------------------------------------------------ ; BEGIN: LXI SP,STACK ;set local stack pointer CALL SIZE ;check for enough buffer space ORA A ;0 = ok, <> 0 = error JZ BUFT ;jump if ok and test buffer LXI D,SIZERR ;point to error meaasge CALL PRINT ;print that buffer is to small JMP WBOOT ;exit to cpm ; BUFT: LXI D,MEMTST ;show what we are doing CALL PRINT CALL CHECK ;check the buffer memory ORA A ;any errors? JZ BUFOK ;proceed if none LXI D,MEMLOW ;point to low memory error msg LXI H,RBUFF ;rbuff is start of check CALL HEXOUT ;save address in message LXI D,MEMHGH ;point to high memory error msg LHLD BDOS+1 ;get cpm top of memory DCX H ;decrease by 1 CALL HEXOUT ;save error address LXI D,MEMERR ;point to error msg CALL PRINT JMP WBOOT ;exit to cpm ; BUFOK: LXI H,RBUFF ;start of buffer LXI D,MEMUS1 ;storage CALL HEXOUT LHLD BDOS+1 ;top of cpm memory LXI D,MEMUS2 ;storage CALL HEXOUT LXI D,MEMUSE CALL PRINT LXI D,OPENM ;say hi to the folks CALL PRINT AGAIN: CALL INIT ;init constants in program LXI D,MENU ;point to menu of options CALL PRINT ;show it CALL INPUT ;get a key LXI D,CRLF CALL PRINT ;print a cr,lf CALL DOIT ;go to the function requested ORA A ;any errors JNZ AGAIN ;jump and repeat if so LXI D,COMFUN ;else, no errors found CALL PRINT JMP AGAIN ;loop till we quit ; ;storage area ; SRCDRV: DB 0 ;source drive (a) DSTDRV: DB 1 ;dest drive (b) KEYBUF: DS 6 ;keyboard input buffer DS 40 STACK: DS 1 ;local stack area CLATCH: DS 1 ;physical drive code TEMP: DS 1 ;keyboard value SECTOR: DS 1 ;current sector value DMAADR: DS 2 ;current memory address RETRY: DS 1 ;retry counter SIDED: DS 1 ;0 = sing sided, 1 = doub sided SRCID: DS 1 ;source id byte ENDTRK: DS 1 ;ending track BEGTRK: DS 1 ;starting track SLATCH: DS 1 ;source drive select code DLATCH: DS 1 ;dest drive select code TRKTAB: DS 4 ;track table for 4 drives DISKNO: DS 1 ;old disk selected OPFLAG: DS 1 ;operation flag 0= system only ; RBUFF EQU $ ;start of buffer area ; END START