; NEWBACK - DISK DIRECTORY AND BACKUP PROGRAM - 11/20/83
;
; COPYRIGHT 1980, GARY YOUNG
;
; THE PRIMARY PURPOSE OF THIS PROGRAM IS TO BACKUP HARD DISKS ON TO
; MULTIPLE FLOPPIES. FIRST, IT EXTRACTS THE DIRECTORIES FROM ALL OF
; THE HARD DISKS, SORTS BY TYPE AND FILE NAME, AND PRINTS A CONSOLIDATED
; DIRECTORY. THEN IT DELETES DUPLICATE FILES FROM THE CONSOLIDATED
; DIRECTORY, DELETES UNWANTED TYPES (PRN, BAK, ETC), AND UNWANTED
; FILES TO CREATE A LIST OF FILES TO BACKUP. THESE ARE COPIED TO
; MULTIPLE FLOPPIES AS NEEDED WHILE PRODUCING AN INDEX OF WHAT FILES
; ARE ON EACH DISKETTE. RESTART CAPABILITY IS PROVIDED TO BEGIN THE
; BACKUP AT ANY FILE.
; - Gary Young
;
;=======================================================================
;
; 11/20/83 Reformatted for standard display. Saved 14k source code by
; v2.4 removing superfluous spaces in favor of tabs.
; - Irv Hoff
;
; 08/20/83 1 - Clock for Sierra Data Sciences SBC-100 BIOS
; v2.3 2 - Option to have no Clock this allows more space in memory
; for Directory entries
; 3 - Backup all CP/M 2.2 user areas
; 4 - Fixed bug that caused crashed system when nothing to
; backup - Alex Soya
;
; 07/01/83 Added features to mark files that have been backed up on the
; v2.2 floppy bye eather setting the 'T3' byte of the FCB for Mo-
; ular computers or setting the 'F4' byte for other computers.
; This is done at assembly time by setting the MOLEC equate
; for either. - Ron Stevenson
;
; 06/23/83 Corrected problem in code for backing up to copying to drive
; v2.1 'P'. Original code stripped off too many bits which would
; not allow program to see drive 'P' no matter what. Cleaned
; up some of the code. - Ron Stevenson
;
; 06/13/83 Added code at begining for setting up the drive to backup
; v2.0 from as well as to. You are now prompted as to which drives
; you want backup and then which drive to put the stuff on.
; - Ron Stevenson
;
;=======================================================================
;
ORG 100H
;
;
JMP START
;
DB 'COPYRIGHT 1980, G. YOUNG, INC.'
;
YES: EQU 0FFH
NO: EQU 0
;
MOLEC: EQU YES ;yes for molecular, no for others
CLOCK: EQU NO ;yes if clock is installed.
SIERRA: EQU NO ;yes if sierra data sc. sbc-100 board
COMPUT: EQU NO ;yes if computime clock board
USERS: EQU YES ;yes if option to back up all user areas
;
;
;
RECSIZ: EQU 12 ;record size as follows
; type = 3 bytes
; file = 8 bytes
; diskid = 1 bytes
NOSKIP: EQU 6 ;number of files in skip list
SKPTYP: DB 'PRNHEXSYMBAK$$$TMP' ;files to not back up
IDSIZE: EQU 1 ;id now means a:, b:, c:, etc
LINSPG: EQU 60 ;lines per page
RECLIN: EQU 4 ;entries per line
;....
;
;
;***********************************************************************
;
;
START: LXI SP,STACK+80
LXI H,RAM ;set up table address
SHLD TABADDR
LXI H,0000H
SHLD TABCNT
XRA A ;the table refers to the remaining area
STA ABORT ;..of ram to hold as many directory en-
STA EOF ;.. tries as possible
STA GOTONE ;flag shows no file found yet if zero
;
START1: LXI D,SIGNON ;print signon message
CALL OUTPUT
LXI D,DRIVMSG ;ask for the drive letters to backup
CALL QUESTION
CPI 0 ;ck to see if anything was added
JZ START1 ;if not start over
ADI 1
STA MAX1
;
IF USERS
STA MAX3 ;used to restore max1 later
ENDIF ;USERS
;
MOV B,A ;setup count for move
LXI H,INREC ;data to move
LXI D,DRIVES ;where to put the date
CALL MOVE
XCHG
DCR B ;correct the count befor cking
;
STRT: MOV A,M ;get a drive char
CPI 'Q' ;ck it for good char
JP START1 ;no good if positive
INX H ;point to next
DCR B ;dec count
JNZ STRT ;loop
MVI M,'$' ;store end-of-string char at end
LXI D,BKUPMSG ;ask for the drive letter to store it to
CALL QUESTION
CPI 1
JNZ START1 ;if no input then exit program
LDA INREC ;get drive letter to store to
STA BACKUPDRV
;
START2: LXI D,CORRMSG ;verify all data is correct
CALL OUTPUT
LXI D,DRIVES ;get drives to backup
CALL OUT1 ;output drive letter chain
LXI D,CORRMSG1
CALL OUT1
LDA BACKUPDRV ;get drive to backup to back again
MOV E,A
MVI C,CONOUT
CALL BDOS ;output drive to backup to out
LXI D,CORRY$N ;get correct message
CALL QUESTION ;and get responce
CPI 1
JNZ START2
LDA INREC ;get answer
CPI 'Y'
JNZ START1
;
IF USERS
LXI D,USRMSG ;ask if all users to back up
CALL QUESTION
CPI 1
JNZ START1 ;thats what you get for a wrong answer
LDA INREC ;get answer
CPI 'Y' ;do all user areas
JNZ NOUSRS ;
MVI A,0 ;set user 0 meaning do them all
JMP DONUSR
;
NOUSRS: MVI A,17 ;set user 17 meaning do only what we are
;
DONUSR: STA CURUSER
ENDIF ;USERS
;
IF NOT CLOCK
MSG1: LXI D,DATEMSG ;get current date for reports
CALL QUESTION ;print msg and get reply
CPI 8 ;8 char must have been entered
JNZ MSG1 ;repeat question if not
LXI H,INREC ;move date from inrec
ENDIF ;NOT CLOCK
;
IF CLOCK
CALL GETDATE ;get date from clock board
LXI H,DATETIME+1
ENDIF ;CLOCK
;
MVI B,8 ;move the date to a hold area
LXI D,DATE ;to "date"
CALL MOVE
;
IF USERS
REPEAT: LDA CURUSER ;get user number
CPI 16 ;check if we do them all
JNC GOON ;no users greater than 15
MOV E,A ;valid user so set it.
MVI C,32 ;set user function
CALL BDOS ;tell cp/m to do some work !
LXI D,USRNOMS ;send user id message
CALL OUTPUT
LDA CURUSER
CALL CONVRT
MOV A,B ;send id to console
STA USERID
MOV A,C
STA USERID+1
LXI D,USERID
CALL OUT1
ENDIF ;USERS
;
GOON: CALL EXTRACT ;get directory entries
LDA ABORT ;see if too many entries for memory size
ORA A ;should be zero to be ok
JZ NOMORE
LXI D,TABFULL ;report will not be complete
CALL OUTPUT ;memory full error
;
NOMORE: LDA GOTONE ;no file found fix
ORA A
JNZ ALLOK
LXI D,NOFILS ;no files message
CALL OUTPUT
JMP FINISHED ;as nothing to do we are done
;
ALLOK: CALL SORT ;sort by type, file name and disk id
MVI A,RECSIZ ;setup to remove duplicate entries
STA COMPSIZE ;compare on all char
CALL KILLDUPS ;remove duplicate entries
CALL REPORT ;print all entries
MVI A,11 ;remove files that are on multiple disks
STA COMPSIZE
CALL KILLDUPS
CALL SELECT ;restrict the files to backup
;
MSG3: LXI D,BACKQUS ;ask if you want backup function
CALL QUESTION
CPI 1
JNZ MSG3
LDA INREC
CPI 'N'
JZ FINISHED
CPI 'Y'
JNZ MSG3
CALL BACKUP ;backup those files reported on
;
FINISHED:
MVI C,0
;
IF USERS
LDA CURUSER
INR A
CPI 16 ;;get user and see if another one to do
JNC DONE ;all done so lets go
STA CURUSER ;else do next user
LDA MAX3 ;restore drive pointer
STA MAX1 ;so we do all drives in next user area.
LXI H,RAM ;restore all the required pointers
SHLD TABADDR ;and things.
LXI H,0000H
SHLD TABCNT
XRA A
STA ABORT
STA EOF
STA GOTONE
JMP REPEAT ;until done
ENDIF ;USERS
;
DONE: JMP BDOS ;reset and return to cpm
;
; the extract routine scans all the drives in the drive table when com-
; plete, it asks for the next drive id when the hard disk is up, it will
; not ask for the drive id but will automatically scan the three hard
; disks and finish without ever asking for the drive id.
;
; the dummyfcb sets up a skeleton to read all files on the directory
;
DUMMYFCB:
DB 0,'????????????',0,0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;
EXTRACT:LXI H,DRIVES ;get address of the drive table
SHLD ADDR1 ;to scan
; MVI A,NODRV+1 ;set up counter for the number to scan
; STA MAX1
;
NEXTDRV:LDA MAX1 ;see if there are more drives to scan
DCR A
STA MAX1 ;return when all have been scanned
RZ
LHLD ADDR1 ;get address of drive table
MOV A,M ;get drive character
STA RDISK
ANI 1FH ;convert a=1...p=16
STA DUMMYFCB ;set in dr of dummyfcb
INX H ;set for next read of drive table
SHLD ADDR1
LXI D,DISKBUF ;set a dma address for directory entries
MVI C,1AH
CALL BDOS ;set dma
LXI D,DUMMYFCB ;get first directory entry
MVI C,11H
CALL BDOS ;get directory
INR A
JZ NEXTDRV ;no more entries on this drive
JMP GETFCB ;extract data from fcb
;
NEXTFCB:LXI D,DUMMYFCB ;get next entry after the first
MVI C,12H
CALL BDOS
INR A
JZ NEXTDRV ;no more directories on this disk
;
GETFCB: LXI D,32 ;each entry is 32 bytes long
LXI H,DISKBUF ;directory record is in diskbuf
CPI 1 ;first entry in record???
JZ FORMREC ;yes
DAD D ;add 32 to address in record
CPI 2 ;second entry in record???
JZ FORMREC
DAD D ;add 64 to address of record
CPI 3 ;third entry in record???
JZ FORMREC
DAD D ;add 96 to address of record
;
FORMREC:INX H ;pass drive byte
LXI D,RFILE ;move file name
MVI B,8 ;move 8 characters
CALL MOVE
LXI D,8 ;position past name to type
DAD D
LXI D,RTYPE ;move type
MVI B,3 ;move 3 characters
CALL MOVE
;
IF NOT MOLEC
PUSH H ;save it for a min
LXI H,RFILE+4 ;get file name to ck for 'F4' taged
MOV A,M
POP H ;get it back
ENDIF ;NOT MOLEC
;
ANI 80H ;ck to see if we should backup this file
JNZ NEXTFCB ;by checking 'T3' for molecular computers
;or 'F4' for other computers
INX H ;position to the extent number
INX H
INX H
MVI A,0FFH
STA GOTONE ;set flag that we have at least on entry
MOV A,M ;get the extent
; ORI 30H ;diagnostic display
; STA RDISK+1
; LXI D,RTYPE
; CALL OUTPUT
; LDA RDISK+1
; ANI 0FH
LXI H,RTYPE ;strip off the high bit set by
MVI B,11 ;the version program
;
STRIP: MOV A,M
ANI 07FH
MOV M,A
INX H
DCR B
JNZ STRIP
LXI H,RTYPE ;skip over junk disk entries
MVI B,11 ;containing nonprinting characters
;
NONPRNT:MOV A,M
CPI 20H ;any char less than a blank
JC NEXTFCB ;go to next one if so
INX H
DCR B
JNZ NONPRNT
;
FIRSTEXT:
LHLD TABADDR ;get memory table address
LXI D,RTYPE ;get memory record address
XCHG ;rtype (hl) to tabaddr (de)
MVI B,RECSIZ ;move recsiz bytes
CALL MOVE
LHLD TABADDR ;increment for next entry
LXI D,RECSIZ ;recsiz bytes in record
DAD D
SHLD TABADDR ;save new address
MVI M,1AH ;set an end-of-table indicator
LHLD TABCNT ;get record count
INX H
SHLD TABCNT ;increment record count
LHLD TABADDR ;see if new address is greater
XCHG ;than the top of tpa-128
LHLD BDOS+1 ;hl=top...de=table+recsiz
LXI B,-128
DAD B ;subtract 128 from top
CALL COMPREG ;compare register values
JNC NEXTFCB ;there is room for more
MVI A,1 ;no more room...abort now
STA ABORT
RET
;.....
;
;
; the following routine is a bubble sort. in pseudobasic this would be
; done as follows: sort addr1=bottom(ram)-recsiz (recsiz is count of
; bytes in one record)
;
; MAX1=0
;LOOP1: MAX1=MAX1+1
; ADDR1=ADDR1+RECSIZ (first time this would be the bottom)
; IF MAX1>TABCNT-1 THEN TO TO SORTED
; ADDR2=ADDR1
; MAS2=MAX1
;LLOP2: MAX2=MAX2+1
; ADDR2=ADDR2+RECSIZ
; IF MAX2>TABCNT THEN GO TO LOOP1
; IF TABLE(ADDR1)
tabcnt - 1 ???
CALL COMPREG ;compare de (current count) to hl (tabcnt-1)
JC SORTED ;finished when max1 > tabcnt-1
LHLD ADDR1 ;setup for the inner loop
SHLD ADDR2
LHLD MAX1
SHLD MAX2
;
LOOP2: LHLD ADDR2 ;start with one entry greater then
LXI D,RECSIZ ;the outer loop and increment
DAD D ;by recsiz each time past the next record
SHLD ADDR2 ;pointer to the current record
LHLD MAX2 ;likewise see if the counter for the
INX H
SHLD MAX2
XCHG
LHLD TABCNT
CALL COMPREG
JC LOOP1 ;when finished, increment outer loop
LHLD ADDR2
XCHG ;addr2 now in de
LHLD ADDR1
CALL COMPARE ;compare strings pointed to by de/hl
JNC LOOP2 ;table(hl) < table (de)
;
;
; exchange the two entries via a temporary record
;
LXI D,TEMP
LHLD ADDR1
MVI B,RECSIZ
CALL MOVE ;temp=table(addr1)
XCHG ;addr1=destination
LHLD ADDR2 ;addr2=source
CALL MOVE ;table(addr1)=table(addr2)
XCHG ;addr2 = destination
LXI H,TEMP
CALL MOVE ;table(addr2)=temp
JMP LOOP2
;.....
;
;
SORTED: RET ;finished sorting
;
;
; report prints the sorted consolidated directory entries. linspg is
; the number of lines per page and reclin is the number of 20 byte en-
; tries per line. each type begins on a new page.
;
REPORT: LHLD TABCNT ;set count of entries
INX H ;plus one
SHLD LASTTYPE ;init lasttype to invalid data
SHLD MAX1 ;to decrement first
LXI H,RAM-RECSIZ ;set starting address
SHLD ADDR1
CALL HEADING
;
NEXTPRT:LHLD ADDR1
LXI D,RECSIZ ;set to add the receive length
DAD D
SHLD ADDR1
LHLD MAX1 ;decrement count to see when done
DCX H
SHLD MAX1
LXI D,0000 ;see if reached zero yet
CALL COMPREG
JZ TOPPAGE ;go to top of next page
LHLD ADDR1 ;current type = last type?
MVI B,3 ;compare 3 characters
LXI D,LASTTYPE
CALL COMPARE
JNZ SKIP3 ;new type on new page
;
PRNTNOW:LDA MAX2 ;decrement records per line
DCR A
STA MAX2
JZ NEWLINE ;line full, go to new line
CALL FORMAT
LXI D,PRNTREC
CALL LIST
JMP NEXTPRT
;
SKIP3: LDA LINECNT
DCR A
JZ NEWPAGE
DCR A
JZ NEWPAGE
DCR A
JZ NEWPAGE
STA LINECNT
LXI D,CRLFLFLF
CALL LIST
JMP CHKTYPE
;
NEWPAGE:CALL HEADING
;
CHKTYPE:LXI D,LASTTYPE ;set last type to current type
LHLD ADDR1
MVI B,3
CALL MOVE
;
SETCNT: MVI A,RECLIN+1
STA MAX2
JMP PRNTNOW
;
NEWLINE:LXI D,CRLF
CALL LIST
LDA LINECNT
DCR A
STA LINECNT
JZ NEWPAGE ;page full
JMP SETCNT
;
TOPPAGE:RET
;.....
;
;
; the purpose of select is to create a final list of files to backup.
; since backing up 26 meg can be quite long, this reduces the list to
; only neccessary files. it also allows a restart at any file. it will
; delete duplicate file names (files on a: will be used since that is
; assumed to be the latest), and will delete the file types listed in
; the skip table. then it will list each type and ask if you do want to
; back up all of this type (y), skip backing up of the entire type (n),
; or select certain files (s) to back up. if the select option is
; chosen, each file name will be printed and you be asked to back up the
; file (y), ignore the file during the backup (n), or continue (c) to
; backup all of the remaining files within this type without asking any
; further names (used for restart). after the list has been reduced, it
; will be listed before the backup actually begins.
;
KILLDUPS:
LXI H,RAM
SHLD ADDR1 ;set the start of the table
;
NEXTEQUAL:
LHLD ADDR1 ;check each entry against the next
LXI D,RECSIZ
DAD D
SHLD ADDR2
;
NEXTCHK:
MOV A,M ;check for end of table
CPI 1AH
RZ
XCHG
LHLD ADDR1 ;compare addr1 with addr2
LDA COMPSIZE
MOV B,A
CALL COMPARE
JNZ SAVELAST
PUSH H ;save current position
LHLD ADDR2 ;set up for moving list
SHLD ADDR1 ;down one entry
LXI D,RECSIZ
DAD D ;move third entry to the second
SHLD ADDR2
CALL MOVELIST
LHLD TABCNT
DCX H
SHLD TABCNT
POP H ;restore current position
SHLD ADDR1 ;now compare 1st & 3rd
JMP NEXTEQUAL
;
SAVELAST:
LHLD ADDR2 ;make new one the current one
SHLD ADDR1
JMP NEXTEQUAL ;compare
SELECT: EQU $
;
NOWSELECT:
LXI H,RAM-RECSIZ ;initialize
SHLD ADDR1
SHLD LASTTYPE ;make sure lasttype does not match
;
NEXTENTRY:
LHLD ADDR1 ;get next record in table
LXI D,RECSIZ
DAD D
SHLD ADDR1 ;increment table entry
;
CHKNEXT:
MOV A,M ;check for the end of the table
CPI 1AH ;cntl-z is at end
JZ LISTBKUP ;list the final table
LXI D,LASTTYPE ;see if the current type is the
LHLD ADDR1 ;last type
MVI B,3
CALL COMPARE
JZ CHKSELECT ;types match, check file select
CALL MOVE ;types don'T MATCH, MAKE THE
;last type = current type now
MVI C,NOSKIP+1 ;see if the new type is in the
LXI D,SKPTYP ;list of file types to skip
MVI B,3
;
NEXTSKIP:
DCR C ;decrement no of files to skip
JZ FORMQUS ;file is good
LHLD ADDR1
CALL COMPARE
JZ SKIPALLTYPE ;file was on the skip list
INX D ;increment to next skip file
INX D
INX D
JMP NEXTSKIP
;
FORMQUS:
LXI D,QTYPE ;form the question about whether
LHLD ADDR1 ;to skip this file type or not
MVI B,3 ;move the file type to the
CALL MOVE ;question print line
XRA A
STA SELFLAG ;reset the select flag
;
REASK1: LXI D,QUEST1
CALL QUESTION ;ask the question about the type
CPI 1 ;one character response allowed
JNZ REASK1
LDA INREC ;get the response
CPI 'N'
JZ SKIPALLTYPE
CPI 'Y'
JZ SAVEALLTYPE
CPI 'S' ;s=select certain files from this
JNZ REASK1 ;type
MVI A,1 ;set the select flag
STA SELFLAG ;to ask the next question
;
ASKFILE:LHLD ADDR1 ;format a question to ask if this
LXI D,QTYPE2 ;particular file is to be saved
MVI B,3
CALL MOVE
LXI D,QFILE2 ;move the file name to the question
INX H
INX H ;position past the file type
INX H ;in the table
MVI B,8
CALL MOVE ;move the name
LXI D,8 ;position past name to the
DAD D ;disk id
LXI D,QDISK2 ;move the disk id
MVI B,IDSIZE
CALL MOVE
;
REASK2: LXI D,QUEST2 ;ask the file question
CALL QUESTION
CPI 1 ;only a 1 char response allowed
LDA INREC
JNZ REASK2
CPI 'C' ;continue with the rest of the
JZ SAVEALLTYPE ;files within this type - restart
CPI 'Y' ;save this particular file
JZ NEXTENTRY
CPI 'N'
JNZ REASK2
LHLD ADDR1 ;shorten the list by one entry
LXI D,RECSIZ
DAD D
SHLD ADDR2 ;skip the last one
CALL MOVELIST ;moves the list down from addr1
;to addr2
LHLD ADDR1
JMP CHKNEXT ;check next entry
;
CHKSELECT:
LDA SELFLAG ;check the select flag
ORA A ;if not zero, ask the question
JNZ ASKFILE ;about each file
JMP NEXTENTRY
;
SKIPALLTYPE:
CALL FINDTYPE ;find the next type not matching
CALL MOVELIST ;this one and move the list down
LHLD ADDR1
JMP CHKNEXT ;to skip this type
;
SAVEALLTYPE:
CALL FINDTYPE ;save all of this type
LHLD ADDR2 ;reset the address to the next
SHLD ADDR1 ;type
JMP CHKNEXT
;
MOVELIST:
LHLD ADDR2 ;move the table from addr2 down
XCHG ;to addr1 thereby eliminating
LHLD ADDR1 ;unwanted entries
;
MOVENEXT:
LDAX D ;and making more room for the
MOV M,A
CPI 1AH
RZ
;
INX H
INX D
JMP MOVENEXT
;
FINDTYPE:
LHLD ADDR1 ;find the next entry
SHLD ADDR2 ;with a type different
;
FINDIT: LHLD ADDR2 ;than the current type
LXI D,RECSIZ
DAD D
SHLD ADDR2
MOV A,M ;stop at end of the table
CPI 1AH
RZ
XCHG
LHLD ADDR1
MVI B,3
CALL COMPARE
RNZ
JMP FINDIT ;addr2 will have the address of
;the next type when finished
LISTBKUP:
SHLD ADDR2 ;save the end of the table for later
LXI H,RAM-RECSIZ
SHLD ADDR1
CALL HEADING2
NEXTONE:
LHLD ADDR1 ;print the next table entry
LXI D,RECSIZ
DAD D
SHLD ADDR1 ;increment to the next entry
MOV A,M ;check for the end of the
CPI 1AH ;table
RZ
CALL FORMAT ;format entry
LXI D,PRNTREC
CALL LIST ;print it
LDA MAX2 ;see if more will fit on this line
DCR A
STA MAX2
JZ LINEFULL
JMP NEXTONE
;
LINEFULL:
LXI D,CRLF
CALL LIST
LDA LINECNT
DCR A
STA LINECNT
JZ PAGEFULL
JMP SETLINE
;
PAGEFULL:
CALL HEADING2
MVI A,LINSPG
STA LINECNT
;
SETLINE:
MVI A,RECLIN
STA MAX2
JMP NEXTONE
;
HEADING2:
LXI D,HEAD2
CALL LIST
LXI D,CRLF
CALL LIST
MVI A,LINSPG
STA LINECNT
MVI A,RECLIN
STA MAX2
RET
;.....
;
;
; backup is used to backup the files in the created list. it will not
; back up any file that will not fit on one disk entirely. the files
; that it backs up will come from any of the hard disks, but not necces-
; sarily in disk order, but rather alphabetical file name order. it
; will prompt for the floppy disk id and print a report showing the
; floppy id and the files copied for index. ; if the file will not fit
; on a disk, it will be deleted from the disk, that disk closed and re-
; moved, and prompted for a new disk to receive the file. blank 1024
; byte sectored disks should be used for best performance. when one
; diskette is full, it will prompt for a new one so many diskettes can
; be used to backup the hard disks. if it is necessary to abort during
; this process, use the restart capability provided in 'SELECT'. the
; area for the disk buffer is all of ram from the end of the table to
; the start of 'BDOS'.
;
BACKUP: CALL TOPOFFORM
LHLD ADDR2 ;get the current end of table
INX H ;plus one for the start of the
SHLD ADDR3 ;read/write buffer
LXI H,RAM ;set the address of the first entry
SHLD ADDR1
LXI D,RECSIZ ;set an address for the second entry
DAD D ;next entry = current + recsiz
SHLD ADDR2 ;later, use movelist to move the list
;down from addr2 to addr1 after each file
;has been copied. this is so that the
;ram buffer will expand as the files are
;copied so that copying will be faster.
IF USERS
LDA CURUSER ;we only mount if the user is 0 or
CPI 17 ;if no user option asked for
JZ OKMOUNT
CPI 1 ;if we do not do users
JNC NOMNT
ENDIF ;USERS
;
OKMOUNT:CALL MOUNT
;
NOMNT: JMP PASSMOVE
;
NEXTFILE:
CALL MOVELIST
INX H ;new start of read/write buffer
SHLD ADDR3
XRA A ;reset the flag for files too big
STA TOOBIG ;to fit on one floppy
;
PASSMOVE;
LXI H,RAM ;the table shrinks so the current
;entry is always at "ram" but the
;read/write buffer grows
MOV A,M
CPI 1AH
JZ DISMOUNT ;finished
;
FORMFCB:
LXI D,HDFCB ;clear the fcb
MVI B,36
XRA A
;
ZEROFCB:
STAX D
INX D
DCR B
JNZ ZEROFCB
LXI H,RAM ;get address of table entry
LXI D,HDTYPE ;move in the type
MVI B,3
CALL MOVE
INX H
INX H ;position to name
INX H
LXI D,HDFILE ;move the file name
MVI B,8
CALL MOVE
LXI D,8
DAD D ;position to disk id, a:, b: etc
MOV A,M
ANI 1FH ;convert a=1...p=16
STA HDFCB
LXI D,FPFCB ;copy the hdfcb to the floppy fcb
LXI H,HDFCB
MVI B,36
CALL MOVE
LDA BACKUPDRV ;set the receiving floppy drive number
ANI 1FH ;convert floppy drive to number
STA FPFCB
LXI D,HDFCB ;open the hd file
MVI C,0FH
CALL BDOS ;open the input file
INR A
JZ NOTFOUND
LXI D,FPFCB ;delete the file on floppy if it
MVI C,13H ;exists
CALL BDOS
LXI D,FPFCB ;create the file on floppy
MVI C,16H
CALL BDOS ;make file
INR A
JZ DISKFULL
LXI H,RAM
CALL FORMAT
LXI D,PRNTREC
CALL OUTPUT
;
COPYLOOP:
CALL LOADBUFF ;load memory with file
CALL WRITEBUF ;write memory file
LDA EOF ;display the status
ADI 30H
MOV E,A ;console output
MVI C,2
CALL BDOS
LDA EOF
CPI 1
JZ ENDOFFILE
CPI 2
JZ DISKFULL
JMP COPYLOOP
;
ENDOFFILE:
LXI D,FPFCB ;close floppy file
MVI C,10H
CALL BDOS ;close
;
IF MOLEC
LXI H,HDFCB+11 ;tag 'T3' byte to indicate backuped
ENDIF ;MOLEC
;
IF NOT MOLEC
LXI H,HDFCB+4 ;tag 'F4' byte to indicate backuped
ENDIF ;NOT MOLEC
MOV A,M ;get 'T3' byte for taging on molecular
;or 'F4' byte for other computers
ORI 80H ;set the bit
MOV M,A ;put it back in hdfcb
LXI D,HDFCB ;setup for attribute setting
MVI C,1EH
CALL BDOS ;write it on the disk
LXI H,RAM
CALL PRINTFILE ;write file name on index list
JMP NEXTFILE
;
DISKFULL:
LXI D,FPFCB
MVI C,13H ;delete file on floppy
CALL BDOS
LDA TOOBIG ;is this the second floppy
ORA A ;it has tried to copy to?
JZ FIRSTTRY ;no, first floppy. retry
LXI D,BIGMSG ;yes, 2nd floppy. send message
CALL LIST ;warning that this file cannot
LXI H,RAM
CALL FORMAT ;be backed up with this program
LXI D,PRNTREC
CALL LIST ;print the file name also
CALL MOUNT
JMP NEXTFILE
;
FIRSTTRY:
INR A ;set indicator that it has tried
STA TOOBIG ;once already to copy it
CALL MOUNT ;get new disk
JMP FORMFCB
;
NOTFOUND:
LXI H,RAM
CALL FORMAT
LXI D,NFMSG
CALL OUTPUT
LXI D,PRNTREC
CALL OUTPUT
RET
;.....
;
;
DISMOUNT:
CALL BELL
;
IF USERS
LDA CURUSER ;only dismount if we are all done
CPI 15
JC NODISMS
ENDIF ;USERS
;
LXI D,DMNTMSG ;dismount floppy
CALL QUESTION
CALL TOPOFFORM
;
NODISMS:RET
;.....
;
;
; assorted routines
;
PRINTFILE:
CALL FORMAT
LXI D,PRNTREC ;print the file entry
CALL LIST
LDA MAX2
DCR A
STA MAX2 ;entries per line counter
RNZ
LXI D,CRLF ;go to new line
CALL LIST
MVI A,RECLIN
STA MAX2
RET
;.....
;
;
HEADING3:
LXI D,CRLFLFLF ;index heading not at top of form
CALL LIST
LXI D,IDISKNO ;move the disk id no to heading
LXI H,VOLSER
MVI B,3
CALL MOVE
LXI D,IDATE
LXI H,DATE
MVI B,8
CALL MOVE
LXI D,INDEX
CALL LIST
LXI D,CRLF
CALL LIST
MVI A,RECLIN
STA MAX2
MVI A,LINSPG
STA LINECNT
RET
;.....
;
;
TOPOFFORM:
MVI E,0CH ;position printer to top of form
MVI C,5
JMP BDOS
;.....
;
;
MOUNT: CALL BELL
LXI D,MNTMSG
CALL QUESTION
CPI 3
JNZ MOUNT
LXI H,INREC
LXI D,ENDLIT
MVI B,3
CALL COMPARE
JZ FINISHED
LXI D,VOLSER
LXI H,INREC
MVI B,3
CALL MOVE ;move volser id to volser
MVI C,0DH ;do a system reset to change disks
CALL BDOS
;
ERA: LXI D,ERASE
CALL QUESTION
CPI 1
JNZ ERA
LDA INREC
CPI 'N'
JZ NOERA
CPI 'Y'
JNZ ERA
LDA BACKUPDRV
ANI 1FH
STA DUMMYFCB
MVI C,13H
LXI D,DUMMYFCB
CALL BDOS ;delete all files on floppy
;
NOERA: CALL HEADING3
LXI D,FPFCB ;clear the fcb to create a file name
MVI B,36
XRA A
;
CLRFCB: STAX D
INX D
DCR B
JNZ CLRFCB
LDA BACKUPDRV ;set up the drive as the backup
ANI 1FH ;convert floppy drive to number
STA FPFCB
LXI H,DATE ;create a null file with the date as
LXI D,FPFCB+1 ;the file name and the 3 digit
MVI B,8
CALL MOVE
LXI H,VOLSER ;3 digit volser as the file type
LXI D,FPFCB+9
MVI B,3 ;this file will be used to identify
CALL MOVE ; the disk and mark the date
LXI D,FPFCB
MVI C,16H ;create the file
CALL BDOS
INR A
JZ MOUNT ;disk full?
LXI D,FPFCB
MVI C,10H ;close the file
CALL BDOS
RET
;.....
;
;
LOADBUFF:
LXI H,0000 ;memory with the file as possible
SHLD MAX1
LHLD ADDR3 ;new top of table +2
SHLD TEMP
XRA A
STA EOF ;clear eof flag
;
LOADNEXT:
LHLD TEMP
XCHG ;set dma address
MVI C,1AH
CALL BDOS
LXI D,HDFCB ;read hard disk
MVI C,14H
CALL BDOS
ORA A
JNZ HDEOF ;eof?
LHLD MAX1
INX H ;increment record count
SHLD MAX1
LHLD TEMP ;see if next record would exceed the
LXI D,128 ;tpa area
DAD D
SHLD TEMP
DAD D ;will the next record overwrite bdos?
XCHG
LHLD BDOS+1 ;find the top of memory
CALL COMPREG ;compare registers
RC ;return if memory already full
JMP LOADNEXT ;get another record
;
HDEOF: MVI A,1 ;set file eof
STA EOF
RET
;.....
;
;
BELL: MVI C,9
LXI D,MSG
CALL BDOS
;
BEEP: MVI C,5 ;bell on printer
MVI E,07H
CALL BDOS
MVI C,2 ;07h on crt
MVI E,07H
CALL BDOS
MVI C,0BH
CALL BDOS ;get console status
ORA A
JZ BEEP
MVI C,01
CALL BDOS ;get the dummy char
RET
;.....
;
;
MSG: DB 0DH,0AH,0AH,'PRESS ANY KEY TO CONTINUE'
DB 0DH,0AH,0AH,'$'
;
WRITEBUF:
LHLD ADDR3
SHLD TEMP
LHLD MAX1 ;allow for files that have no
LXI D,0000 ;records such as restart
CALL COMPREG
RZ
;
WRITENEXT:
LHLD TEMP
XCHG ;set dma address
MVI C,1AH
CALL BDOS
LXI D,FPFCB
MVI C,15H ;write sequential
CALL BDOS
ORA A
JNZ FPFULL ;floppy disk full
LHLD MAX1 ;decrease record count
DCX H
SHLD MAX1
LXI D,0000 ;check for no more to write
CALL COMPREG
RZ
LHLD TEMP
LXI D,128 ;increment write address
DAD D
SHLD TEMP
JMP WRITENEXT
;
FPFULL: MVI A,2 ;full diskette
STA EOF
RET
;.....
;
;
FORMAT: LXI D,PRNTYPE ;format the entry from the table
MVI B,3 ;format to the print format
CALL MOVE ;the table address is assummed to be
LXI D,3 ;in hl. first move the type
DAD D ;now position to the file name
LXI D,PRNFILE ;move the file name
MVI B,8
CALL MOVE
LXI D,8 ;position to the disk id
DAD D
LXI D,PRNTREC ;move the disk id to the line
MVI B,IDSIZE
CALL MOVE
RET
;.....
;
;
COMPREG:
MOV A,H ;compare hl to de
CMP D
RNZ
MOV A,L
CMP E
RET
OUTPUT: PUSH D ;put out a crlf
LXI D,CRLF
MVI C,09
CALL BDOS
POP D ;now put out the message
;
OUT1: MVI C,09
JMP BDOS
;.....
;
;
HEADING:
LXI D,HEAD1
CALL LIST
LXI D,DATE
CALL LIST
LXI D,TABFULL
LDA ABORT
ORA A
CNZ LIST
MVI A,LINSPG
STA LINECNT
LXI D,CRLF
CALL LIST
LXI D,CRLF
JMP LIST
;
LIST: PUSH H ;this differs from output
PUSH B ; in that list goes to the
PUSH D ;list device and output goes
;
LIST1: LDAX D ;to the console device
CPI '$'
JZ LIST2
INX D
PUSH D
MOV E,A
MVI C,5
CALL BDOS
POP D
JMP LIST1
LIST2 POP D
POP B
POP H
RET
;.....
;
;
QUESTION:
CALL OUTPUT ;put out the question
LXI D,INBUF
MVI C,0AH ;input the reply
CALL BDOS
LDA INCNT ;see if anything was entered
RET
;.....
;
;
MOVE: PUSH H ;move data pointed to in hl
PUSH D ;to the area pointed to in de
PUSH B ;by the byte count in b
;
MOVE1: MOV A,M
; ani 7fh ;reset the high order bit because it
;may have been turned on for the type
STAX D
INX H
INX D
DCR B
JNZ MOVE1
POP B ;restore the total environment
POP D
POP H
RET
;.....
;
;
COMPARE:
PUSH H ;compare the strings pointed to in hl
PUSH D ;to the string pointed to in de
PUSH B ;for a length of b characters
;
COMP1: LDAX D ; jc if hl > de
CMP M ; jz if hl = de
JNZ COMP2 ;jnc if hl < de
INX H
INX D
DCR B
JNZ COMP1
;
COMP2: POP B
POP D
POP H
RET
;.....
;
;
; this routine reads the computime clock and formats the time and date
;
IF COMPUT AND CLOCK
GETDATE:
MVI A,16 ;raise the hold signal
OUT C2
MVI A,50 ;delay at least 50 milliseconds
;
WAIT50: DCR A
JNZ WAIT50
LXI H,DATETIME ;set message start. 1st char is space
MVI A,42 ;get month tens char
CALL GETTIME
MVI A,41 ;get month units char
CALL GETTIME
INX H ;skip slash
MVI A,40 ;get day tens char
CALL GETTIME
ANI 0F3H ;drop leap year indicator
MOV M,A
MVI A,39 ;get day units char
CALL GETTIME
INX H ;skip slash
MVI A,44 ;get year tens char
CALL GETTIME
MVI A,43 ;get year units char
CALL GETTIME
INX H ;skip blank
MVI A,37 ;get hour tens char
CALL GETTIME
ANI 0F3H ;drop am/pm and 24hr indicators
MOV M,A
MVI A,36 ;get hour units char
CALL GETTIME
INX H ;skip colon
MVI A,35 ;get minutes tens char
CALL GETTIME
MVI A,34 ;get minutes units char
CALL GETTIME
INX H ;skip colon
MVI A,33 ;get seconds tens char
CALL GETTIME
MVI A,32 ;get seconds units char
CALL GETTIME
XRA A
OUT C2 ;lower hold signal
RET
;.....
;
;
C1: EQU 254 ;clock address port
C2: EQU 253 ;clock data output port
;
GETTIME:
INX H ;advance to next print position
OUT C1
MVI A,6 ;delay at least 6 milliseconds
;
WAIT6: DCR A
JNZ WAIT6
IN C1
ORI 30H ;convert to ascii
MOV M,A
RET
ENDIF ;COMPUT AND CLOCK
;.....
;
;
; this routine reads the time and day on the sierra data sciences bios
; clock. the julian date from the sirra bios is converted to real date.
; (why use a julian date sierra ? does not save any memory anyway ????)
;
IF SIERRA AND CLOCK
DINM: DB 31,28,31,30,31,30,31,31,30,31,30,31
MONTH: DB 1
YEAR: DB 0
TEMDAY: DW 0
;
GETDATE:LDA 64 ;get year
STA YEAR
CALL CONVRT ;make it decimal
MOV A,B
STA DATETIME+7
MOV A,C
STA DATETIME+8
LDA YEAR ;we need to know if this a leap year
SUI 80 ;will only work post 1980 !!!!
;
LEAP: SUI 4 ;you need to change this in year 2000
JZ AJUS ;it is a leapyear
JNC LEAP ;do it until we know
JMP STIM
;
AJUS: MVI A,29
STA DINM+1 ;29 days in feb this year
;
STIM: LXI B,DINM ;point to days in month table
LHLD 65 ;copy date local
SHLD TEMDAY
;
SLOOP: LDAX B ;get no of days in this month
MOV D,A
LDA TEMDAY
SUB D
JNC SIER1 ;we still have too many days
STA TEMDAY ;keep low day here till later
LDA TEMDAY+1 ;get high day
CPI 1 ;still set ?
JNZ GOTMON ;no, we got the month
XRA A ;high date done
STA TEMDAY+1
JMP NXMON
;
SIER1: STA TEMDAY
;
NXMON: INX B ;next month
LDA MONTH
INR A
STA MONTH
JMP SLOOP
;
GOTMON: LDA TEMDAY
ADD D ;correct days
CALL CONVRT ;we got the day so put in string
MOV A,B
STA DATETIME+4
MOV A,C
STA DATETIME+5
LDA MONTH
CALL CONVRT
MOV A,B
STA DATETIME+1
MOV A,C
STA DATETIME+2
LDA 67 ;get the hour
CALL CONVRT
MOV A,B
STA DATETIME+10
MOV A,C
STA DATETIME+11
LDA 68 ;minutes
CALL CONVRT
MOV A,B
STA DATETIME+13
MOV A,C
STA DATETIME+14 ;i know this code is not elegant
LDA 69
CALL CONVRT
MOV A,B
STA DATETIME+16
MOV A,C
STA DATETIME+17 ;finaly
RET
ENDIF ;SIERRA AND CLOCK
;.....
;
;
IF SIERRA OR USERS
CONVRT: MVI B,030H ;convert binary to decimal routine
MVI C,030H ;for nos from 0 to 99 b=tens,c=ones
;
CON1: SUI 10 ;a holds value to convert
JC CON2
INR B
JMP CON1
;
CON2: ADI 10 ;correct it
;
CON3: SUI 1
RC ;we are done
INR C ;do ones
JP CON3
ENDIF ;SIERRA OR USERS
;
DATETIME: DB ' 00/00/00 HH:MM:SS$'
DRIVES: DB 'ABCDEFGHIJKLMNOP' ;drive nos for the hard disks
BACKUPDRV:DS 1 ;backup floppy drive letter
SIGNON: DB 1AH,'HARD DISK CATALOG AND BACKUP v2.3 11/20/83$'
DRIVMSG: DB 0AH,'Enter Letter of Drives to Backup from as '
DB '"ABCD..ect." $'
BKUPMSG: DB 0AH,'Enter Letter of Floppy Drive to Backup to $'
CORRMSG: DB 0AH,'Backing up Drives $'
CORRMSG1: DB ' to Floppy Drive $'
CORRY$N: DB 0AH,'is this Correct (Y/N)? $'
DATEMSG: DB 0AH,'Enter 8 Char Date for Reports MM/DD/YY: $'
USRMSG: DB 0AH,'Backup all user areas (Y/N)? $'
NOFILS: DB 0AH,'NO files to backup here !! $'
USRNOMS: DB 0AH,'Starting Backup procedure in user area : $'
USERID: DB '00$$$'
ERASE: DB 0AH,07H,'ERASE Floppy Disk First (Y/N)? $'
TABFULL: DB 0AH,07H,'MEMORY FULL...REPORT NOT COMPLETE$'
BACKQUS: DB 0AH,'Begin Backup Procedure (Y/N)? $'
SORTMSG: DB 0AH,'SORTING...$'
NFMSG: DB 0AH,07H,'FILE NOT FOUND...ABORTING$'
DMNTMSG: DB 'DISMOUNT BACKUP DISK AND ENTER RETURN$'
MNTMSG: DB 'MOUNT DISKETTE FOR BACKUP AND ENTER '
DB '3 CHAR DISK ID OR "END": $'
ENDLIT: DB 'END'
VOLSER: DS 3
QUEST1: DB 'BACKUP ALL FILES OF TYPE '
QTYPE: DS 3
DB ', (Y/N/S)? $'
QUEST2: DB 'BACKUP FILE NAMED '
QDISK2: DS 1
DB ':'
QFILE2: DS 8
DB '.'
QTYPE2: DS 3
DB ', (Y/N/C)? $'
HEAD2: DB 0CH,'THE FOLLOWING FILES WILL BE COPIED TO DISKETTES$'
PRNTREC: DS 1
DB ':'
PRNFILE: DS 8
DB '.'
PRNTYPE: DS 3
DB ' $'
CRLFLFLF: DB 0DH,0AH,0AH,0AH,'$'
INDEX: DB 'THE FOLLOWING FILES ARE ON DISKETTE NO. '
IDISKNO: DS 3
DB ' AS OF '
IDATE: DS 8
DB '$'
CRLF: DB 0DH,0AH,'$'
HEAD1: DB 0CH,'CONSOLIDATED INDEX BY FILE TYPE AS OF $'
CURUSER: DB 16 ;current user number (local)
DATE: DS 8
DB ' $'
BIGMSG: DB 0DH,0AH,'FILE TOO LARGE TO FIT ON ONE DISKETTE '
DB 'SO NOT BACKED UP **** $'
HDFCB: DS 1
HDFILE: DS 8
HDTYPE: DS 3
DS 24
FPFCB: DS 36
TOP: DB 0CH,'$'
INBUF: DB 30
INCNT: DS 1
INREC: DS 30
STACK: DS 80
EOF: DS 1
ABORT: DS 1
GOTONE: DS 1 ;flag to indicate valid file found, entry made
LINECNT: DS 1
LASTTYPE: DS 3
LASTFILE: DS 8
TABADDR: DS 2
TABCNT: DS 2
SELFLAG: DS 1
TOOBIG: DS 1
ADDR1: DS 2
ADDR2: DS 2
ADDR3: DS 2
MAX1: DS 2
MAX2: DS 2
MAX3: DS 2 ;used to restore max1 between users
RTYPE: DS 3
RFILE: DS 8
RDISK: DS 3
DB '$'
TEMP: DS RECSIZ
DISKBUF: DS 128
COMPSIZE: DS 1
RAM: EQU $
BDOS: EQU 05H
CONIN: EQU 01H
CONOUT: EQU 02H
;.....
;
;
END 100H