home *** CD-ROM | disk | FTP | other *** search
- ; FBAD.ASM ver. 60A
- ; (revised 10/18/87)
- ; NON-DESTRUCTIVE DISK TEST PROGRAM
- ;
- ; FBAD will find all bad blocks on a disk and build a file [UNUSED].BAD
- ; to allocate them, thus "locking out" the bad blocks so CP/M will not
- ; use them. This allows continued use of the disk as though it had no
- ; bad areas.
- ;
- ; If an [UNUSED].BAD file is found on the disk before the test you will
- ; be prompted to keep the existing file (and all currently flagged bad
- ; blocks) or erase it and only flag the bad blocks found on the current
- ; pass.
- ;
- ; Originally written by Gene Cotton, published in "Interface Age" for
- ; September 1980, page 80.
- ;
- ; See notes below concerning 'TEST' conditional assembly option, SYSTST
- ; and BADUSR directives.
- ;
- ;=======================================================================
- ; current update
- ;
- ; 10/18/87 Modified to allow TEST routines to be left in. If TEST is
- ; v60A YES the number of records read will be reported, but only if
- ; no bad blocks were found. Added help message - A>FBAD $?
- ; Added S, A and X command line options. See help message for
- ; proper usage. Revised handling of system tracks. Modified
- ; Modified LTOP routine so that system track sectors do not go
- ; through sector translation. Reading is slow but system sec-
- ; tors may not use same XLATE as data sectors.
- ; - Tom Head
- ;
- ;=======================================================================
- ;
- ; COMMAND LINE OPTIONS:
- ;
- ; Three command line options can now be specified -- S, A and X.
- ;
- ; If the ASTRS equate is set to NO the TRACK-Nr will be displayed
- ; during testing unless the A option is specified in which case
- ; an * will be printed for each track read.
- ;
- ; If the SYSTST equate is set to NO the system track test will be
- ; skipped unless the S option is specified in which case system
- ; track 0 will be read using the sector count value in the SPT0
- ; equate. All other system tracks will be read using the sector
- ; count value in the OSTK equate. If SPT0 or OSTK is set to zero
- ; then the SPT value for the data tracks will be used.
- ;
- ; This should permit testing of system tracks where track 0 has
- ; a different sector count than other system tracks which, in turn,
- ; may have a different sector count than the data tracks. (e.g. JADE DD)
- ;
- ; The X option is identical to the S option but allows using a second
- ; set of default values for SPT0 and/or OSTK (XSPT0 and XOSTK) in case
- ; you routinely use two different disk formats.
- ;
- ; NOTE THAT YOU SHOULD NOT USE THE X AND S OPTIONS TOGETHER.
- ;
- ; Finally, for the odd case, you may specify one or two numbers
- ; following the S option ( Sn0 or Sn0,n1 ). This will override
- ; the default sector count values set either for SPT0 alone or
- ; for both SPT0 and OSTK.
- ;
- ; SYSTST, BADUSR and ASTRS options:
- ;
- ; Many double-density disk systems have single-density system tracks.
- ; If this is true with your system, you can change the program to skip
- ; the system tracks, without re-assembling it. To do this, set the byte
- ; at at 103H to 0 if you don't want the system tracks tested, otherwise
- ; keep it 1. The program tests if you have a "blocked" disk system,
- ; that is, when the same physical disk is separated into logical disks
- ; by use of the SYSTRK word in the disk parameter block. If more than 5
- ; tracks are specified, the program skips the system tracks.
- ;
- ; If you are using CP/M 2.x , you may assign the user number where the
- ; [UNUSED.BAD] file will be created, by changing the byte at 104H to the
- ; desired user number.
- ;
- ; FBAD displays the TRACK-Nr it has checked on the screen-terminal. If
- ; you like to log the results on a printer (or you have a hardcopy term-
- ; inal, you may want to change LOC 105H to a non-zero value, and FBAD
- ; will display a * for each track checked. The number in 105H controls
- ; the number of *'s per line. (Note patch values are HEX: 76=4CH.) Use
- ; ^P to turn the printer on before running FBAD, it will be automatically
- ; turned off by the warm boot at the end.
- ;
- ; NOTE: These changes can be done with DDT as follows:
- ;
- ; A>DDT FBAD.COM
- ; -S103
- ; 103 01 00 ; Don't test SYSTEM tracks
- ; 104 FF 0F ; Put [UNUSED.BAD] in USER 15
- ; 105 00 4C ; Issue CR/LF after 76 *'s
- ; 106 31 . ; Finished with changes
- ; -^C
- ;
- ; A>SAVE 12 FBAD.COM
- ;
- ;=======================================================================
- ;
- ; USING THE PROGRAM
- ;
- ; Before using this program to "reclaim" a diskette, the diskette should
- ; be reformatted. If this is not possible, at least assure yourself
- ; that any existing files on the diskette do not contain unreadable re-
- ; cords. If you have changed diskettes since the last warm-boot, you
- ; must warm boot again before running this program.
- ;
- ; To use the program, insert both the disk containing FBAD.COM and the
- ; diskette to be checked into the disk drives. The diskette containing
- ; FBAD.COM can be the one that is to be checked. Assume that the pro-
- ; gram is on drive "A" and the suspected bad disk is on drive "B". In
- ; response to the CP/M prompt "A>", type in FBAD B: This will load the
- ; file FBAD.COM from drive "A" and test the diskette on drive "B" for
- ; unreadable records. If no drive is specified, the currently logged-in
- ; drive is assumed to contain the diskette to check.
- ;
- ; The program first checks the CP/M System tracks (up to 5), and any
- ; errors here prohibit the diskette from being used on drive "A", since
- ; all "warm boots" occur using the system tracks from the "A" drive.
- ; Floppy diskettes normally use 2 tracks for the system; Winchester hard
- ; disks may use one or more.
- ;
- ; Version 5.5 and later automatically skip the system check if 5 or
- ; more tracks are reserved for the system. This allows the program to
- ; be used on BOTH floppy and Winchester systems without patching.
- ;
- ; The program next checks the first two data blocks containing the
- ; diskette directory. If errors occur here, the program terminates with
- ; the control returning to CP/M. No other data blockes are checked as
- ; errors in the directory render the diskette useless.
- ;
- ; Finally, all the remaining data blocks are checked. Any records that
- ; are unreadable cause the data block which contains them to be stored
- ; temporarily as a "bad block". At the end of this phase, the message
- ; "nn bad blocks found" is displayed (where nn is replaced by the number
- ; of bad blocks, or "No" if no read errors occur). If bad blocks occur,
- ; the filename [UNUSED].BAD is created, the list of "bad blocks" is put
- ; in the allocation map of the directory entry for [UNUSED].BAD, and the
- ; file is closed. When the number of "bad blocks" exceeds 16, the pro-
- ; gram will open additional extents as required to hold the overflow.
- ; If the diskette has more than 32 "bad blocks", perhaps it should be
- ; sent to the "big disk drive in the sky" for the rest it deserves.
- ;
- ; If any "bad blocks" do occur, they are allocated to [UNUSED].BAD and
- ; no longer will be available to CP/M for future allocation. This ef-
- ; fectively locks out bad records on the diskette allowing its continued
- ; use.
- ;
- ; Using the TEST conditional assembly
- ;
- ; A conditional assembly has been added to allow testing this program to
- ; make sure it is reading all records on your disk that are accessible
- ; to CP/M. The program reads the disk on a block-by-block basis, so it
- ; is necessary to first determine the number of blocks present. To com-
- ; mence, we must know the number of records/block (8 records/block for
- ; standard IBM single density format). If this value is not known, it
- ; can be easily found by saving one page in a test file and interrogating
- ; using the STAT command:
- ;
- ; A>SAVE 1 TEST.SIZ
- ; A>STAT TEST.SIZ
- ;
- ; For standard single-density, STAT will report this file as being 1k.
- ; The file size reported (in bytes) is the size of a block. This value
- ; divided by 128 bytes/record (the standard CP/M record size) will give
- ; records/block. For our IBM single density example, we have:
- ;
- ; (1024 bytes/block) / (128 bytes/record) = 8 records/block.
- ;
- ; We can now calculate blocks/track (assuming we know the number of
- ; records per track. In our example:
- ;
- ; (26 records/track) / (8 records/block) = 3.25 blocks/track
- ;
- ; Now armed with the total number of data tracks (75 in our IBM single
- ; density example), we get total blocks accessible:
- ;
- ; 75 (tracks/disk) x (3.25 blocks/track) = 243.75 blocks/disk
- ;
- ; CP/M cannot access a fractional block, so we round down (to 243 blocks
- ; in our example). Now multiplying total blocks by records per block
- ; results in total records as should be reported when TEST is set YES
- ; and a good disk is read. For our example, this value is 1944 records.
- ;
- ; Finally, note that if SYSTST is set YES, the records present on the
- ; system tracks will be displayed as well. In the previous example,
- ; this results in 1944 + 52 = 1996 records (reported separately by the
- ; TEST conditional). Version 5.4 reported these as a single total at
- ; the end.
- ;
- ; Run the program on a KNOWN-GOOD diskette. It should report that it
- ; has read the correct number of records.
- ; We should not display the number of records read if bad blocks are
- ; found since this program does not read all the records in a
- ; block that is found to be bad and thus will report an inaccurate
- ; number of records read.
- ;
- ;=======================================================================
- ; prior updates
- ;
- ; 03/28/85 Cleaned up some code that sends messages.
- ; v60 Modified STOP routine so that you really can abort the
- ; program with ^C. - Dave Mabry
- ;
- ; 03/16/85 Program will ask you if you want to continue checking on the
- ; v59 same drive as originally entered - added a byte at the end of
- ; the code (ORIGDR) to store the value from the FCB.
- ; - Ken Kaplan
- ;
- ; 12/12/84 Added the ability to keep bad blocks that were flagged in a
- ; v58 previous [UNUSED].BAD file. If a block was ever flagged as
- ; bad by this program, it is probably weak. If on a subse-
- ; quent test, it makes it through the BIOS retires and is read
- ; successfully, I want the block to stay in the [UNUSED].BAD
- ; file. Removed the coded in LTOP which cleared the high byte
- ; of HL after a call to RECTRN. My BIOS (Morrow DJDMA) sets
- ; the high bit of HL to indicated side 1 of a double-sided
- ; drive. - Ron Schwabel
- ;
- ; 11/29/84 Integrated Mike Webbs idea to display Track-Nr. Changed DOC
- ; v57 up front accordingly. - William Earnest
- ;
- ; 07/04/84 Added Ted Shapin's fixes from 1981 that were not included in
- ; v56 the 06/07/84 version. Reformatted.
- ; - Irv Hoff
- ;
- ; 06/07/84 Added code at CHKSYS to skip system tracks if more than 5
- ; v55 are present (most systems use 1 or at most 2 tracks for the
- ; system). This makes the program practical for both floppy
- ; and Winchester systems. Cosmetic change for printer logging
- ; to add CR/LF after 76 *'s. Fixed problem in DECOUT to give
- ; correct total for max size Winchester disks.
- ; - Dave Hardy
- ;
- ;=======================================================================
- ;
- NO EQU 0
- YES EQU NOT NO
- ;
- ;=======================================================================
- ;
- ; Conditional assembly switch for testing this program
- ;
- ;
- TEST EQU YES ; Yes to desplay records read if good disk
- ;
- ;=======================================================================
- ;
- ; System equates
- ;
- WBOOT EQU 0 ; CP/M warm boot entry
- BDOS EQU 0005H ; BDOS entry point
- FCB EQU 005CH ; CP/M default FCB location
- ;
- TBUFF EQU 0080H ; Command line copied here
- SPCHR EQU '$' ; Leadin for options
- ;
- ;
- ; Define ASCII characters used
- ;
- BELL EQU 07H
- CR EQU 0DH ; Carriage return character
- LF EQU 0AH ; Line feed character
- TAB EQU 09H ; Tab character
- VERS EQU 60 ; Version number
- ;
- DPBOFF EQU 3AH ; Cp/m 1.4 offset to DPB within BDOS
- TRNOFF EQU 15 ; Cp/m 1.4 offset to record Xlate routine
- ;
- ;
- ORG 0100H
- ;
- ;
- JMP START ; Jmp around option bytes
- ;
- ;
- ; If you want the system tracks tested, then put a 1 here, otherwise 0.
- ;
- SYSTST: DB 0
- ;
- ;
- ; If using CP/M 2.x change this byte to the user number you want
- ; [UNUSED].BAD to reside in.
- ;
- BADUSR: DB 15 ; User # where [UNUSED.BAD] goes
- ;
- ; Set this byte to the number of *'s you want per display line
- ;
- ASTRS: DB 00 ; Number of *'s per line (0 == 'track xx')
- ;
- ;
- ; Enter default SPT value for system track 0 (26 for 8" SS/SD)
- ;
- DFSPT0 EQU 26 ; If 00, use data track value
- ;
- ; Enter default SPT value for other system track(S)
- ;
- DFOSTK EQU 00 ; If 00, use data track value
- ;
- SPT0: DW DFSPT0
- OSTK: DW DFOSTK
- ;
- ;
- ; The following two equates are for use in conjunction with the 'X' com-
- ; mand line option which will initialize SPT0 and OSTK to the values
- ; entered.
- ;
- XSPT0 EQU 26 ; Special value for system track 0 SPT
- XOSTK EQU 50 ; Special value for other system track(s)
- ; ; 50 for Jade double D controller
- ;
- ;=======================================================================
- ;
- ; PROGRAM STARTS HERE
- ;
- ;=======================================================================
- ;
- START: LXI SP,STACK ; Make new stack
- LXI D,SIGNON ; Introduce ourself
- CALL PSTRNG
- CALL GETOPT ; Check command line options
- ;
- RESTRT: CALL SETUP ; Set BIOS entry, and check drive
- CALL ZMEM ; Zero all available memory
- CALL FINDB ; Establish all bad blocks
- JZ NOBAD ; Say no bad blocks, if so
- CALL SETDM ; Fix DM bytes in FCB
- ;
- NOBAD: CALL CRLF
- MVI A,TAB
- CALL TYPE
- LXI D,NOMSG ; Point first to 'no'
- LHLD BADBKS ; Pick up # bad blocks
- MOV A,H ; Check for zero
- ORA L
- JZ PMSG1 ; Jump if none
- LXI D,BELLS
- CALL PSTRNG
- LHLD BADBKS
- PUSH H
- LXI H,BADBKS
- CALL DECOUT ; Oops..had some bad ones, report
- POP H
- SHLD BADBKS
- LXI D,BELLS
- CALL PSTRNG
- JMP PMSG2
- ;
- PMSG1: CALL PSTRNG
- ;
- PMSG2: LXI D,ENDMSG ; Rest of exit message
- ;
- PMSG: CALL PSTRNG
- ;
- IF TEST
- LHLD BADBKS
- MOV A,H
- ORA L
- JNZ SKPT1
- MVI A,TAB ; Get a tab
- CALL TYPE ; Print it
- LXI H,RECCNT ; Dir+data block count
- CALL DECOUT ; Print it
- LXI D,RECMSG ; Point to message
- CALL PSTRNG ;
- ENDIF ; TEST
- ;
- SKPT1: LXI D,CMSG1 ; Do you want to continue?
- CALL PSTRNG
- MVI C,1 ; BDOS read console function
- CALL BDOS
- ANI 5FH ; Convert lower to upper case
- CPI 'C' ; If C or c entered continue
- JNZ WBOOT ; Exit
- MVI C,0DH ; Do a disk reset to avoid BDOS R/O errors
- CALL BDOS
- LDA ORIGDR ; Get drive value
- STA FCB ; Put it back in FCB
- LXI SP,STACK
- JMP RESTRT ; Start over
- ;
- ;
- ; Get actual address of BIOS routines
- ;
- ; WARNING...Program modification takes place here...do not change.
- ;
- SETUP: LHLD 1 ; Get pointer to warm boot
- LXI D,24 ; Offset to 'SETDSK'
- DAD D
- SHLD SETDSK+1 ; Fix our call address
- LXI D,3 ; Offset to 'SETTRK'
- DAD D
- SHLD SETTRK+1 ; Fix our call address
- LXI D,3 ; Offset to 'SETREC'
- DAD D
- SHLD SETREC+1 ; Fix our call address
- LXI D,6 ; Offset to 'DREAD'
- DAD D
- SHLD DREAD+1 ; Fix our call address
- LXI D,9 ; Offset to CP/M 2.x RECTRAN
- DAD D
- SHLD RECTRN+1 ; Fix our call address
- MVI C,12 ; Get version function
- CALL BDOS
- MOV A,H ; Save as flag
- ORA L
- STA VER2FL
- JNZ GDRIV ; Skip 1.4 stuff if is 2.x
- LXI D,TRNOFF ; Cp/m 1.4 offset to RECTRAN
- LHLD BDOS+1 ; Set up jump to 1.4 RECTRAN
- MVI L,0
- DAD D
- SHLD RECTRN+1
- ;
- ;
- ; Check for drive specification
- ;
- GDRIV: LDA FCB ; Get drive name
- STA ORIGDR ; Store for use later
- MOV C,A
- ORA A ; Zero?
- JNZ GD2 ; If not,then go specify drive
- MVI C,25 ; Get logged-in drive
- CALL BDOS
- INR A ; Make 1-relative
- STA ORIGDR
- MOV C,A
- ;
- GD2: LDA VER2FL ; If CP/M version 2.x
- ORA A
- JNZ GD3 ; Seldsk will return select error
- ;
- ;
- ; Is CP/M 1.4, which doesn't return a select error, so we have to do it
- ; here
- ;
- MOV A,C
- CPI 4+1 ; Check for highest drive number
- JNC SELERR ; Select error
- ;
- GD3: DCR C ; Back off for CP/M
- PUSH B ; Save disk selection
- MOV E,C ; Align for BDOS
- MVI C,14 ; Select disk function
- CALL BDOS
- POP B ; Get back disk number
- ;
- ;
- ; EXPLANATION: Why we do the same thing twice
- ;
- ; You might notice that we are doing the disk selection twice, once by a
- ; BDOS call and once by direct BIOS call. The BIOS call is necessary in
- ; order to get the necessary pointer back from CP/M (2.x) to find the
- ; record translate table. The BDOS call is necessary to keep CP/M in
- ; step with the BIOS. Later the file [UNUSED].BAD may need to be
- ; created and CP/M must know which drive is being used.
- ;
- CALL SETDSK ; Direct BIOS call
- LDA VER2FL
- ORA A
- JZ DOLOG ; Jump if CP/M 1.4
- MOV A,H
- ORA L ; Check for 2.x
- JZ SELERR ; Jump if select error
- MOV E,M ; Get record table pointer
- INX H
- MOV D,M
- INX H
- XCHG
- SHLD RECTBL ; Store it away
- LXI H,8 ; Offset to DPB pointer
- DAD D
- MOV A,M ; Pick up DPB pointer
- INX H ; To use
- MOV H,M ; As parameter
- MOV L,A ; To logit
- ;
- DOLOG: CALL LOGIT ; Log in drive, get disk parms
- CALL GETDIR ; Calculate directory information
- ;
- ;
- ; Now set the required user number
- ;
- LDA VER2FL
- ORA A
- RZ ; No users in CP/M 1.4
- LDA BADUSR ; Get the user number
- MOV E,A ; BDOS call needs user # in 'E'
- MVI C,32 ; Get/set user code
- CALL BDOS
- RET
- ;.....
- ;
- ;
- ; Look for bad blocks
- ;
- FINDB: LHLD SPT
- SHLD SPTSAV ; Save data track SPT
- LDA SYSTST
- ORA A
- JZ DODIR ; Jump if no system tracks to be tested
- CALL CHKSYS ; Check for bad blocks on track 0 and 1
- ;
- DODIR: XRA A
- STA RECCNT
- LXI H,0000
- SHLD RECCNT+1 ; Initialize records read to zero
- LHLD SPTSAV
- SHLD SPT ; Restore data track SPT
- CALL CHKDIR ; Check for bad blocks in directory
- LXI D,TDAMSG ; Testing data area message
- CALL PSTRNG
- LDA ASTRS ; Set column count
- STA COLUMN
- CALL ERAB ; Erase any [UNUSED].BAD file
- LHLD DIRBKS ; Start at first data block
- MOV B,H ; Put into 'BC'
- MOV C,L
- ;
- FINDBA: CALL READB ; Read the block
- CNZ SETBD ; If bad, add block to list
- INX B ; Bump to next block
- LHLD DSM
- MOV D,B ; Set up for (MAXGRP - CURGRP)
- MOV E,C
- CALL SUBDE ; Do subtract: (MAXGRP - CURGRP)
- JNC FINDBA ; Until CURGRP > MAXGRP
- CALL CRLF
- LHLD DMCNT ; Get number of bad records
- MOV A,H
- ORA L ; Set zero flag, if no bad blocks
- RET ; Return from 'FINDB'
- ;.....
- ;
- ;
- ; Check system tracks, notify user if bad, but continue
- ;
- CHKSYS: XRA A
- STA RECCNT
- LXI H,0000
- SHLD RECCNT+1 ; Initialize records read to zero
- LHLD SYSTRK ; Get # system tracks
- MOV A,H ; Get high part
- ORA A
- JNZ SKPSYS ; Skip system track check if non-zero
- MOV A,L ; Get low part
- ORA A
- JZ NOSYS ; No system tracks to check
- CPI 5 ; >5 tracks?
- JNC SKPSYS ; Skip check if so
- LDA ASTRS ; Set column counter
- STA COLUMN
- LXI D,TSTMSG ; Testing system tracks message
- CALL PSTRNG
- LXI H,0 ; Set track 0,record 1
- SHLD TRACK
- INX H
- SHLD RECORD
- LHLD SPT0 ; Get SPT0 value
- MOV A,H
- ORA L ; Is it zero?
- JNZ CHK1
- LHLD SPTSAV ; If yes, use data track SPT
- CHK1: SHLD SPT ; Initialize track 0 SPT
- ;
- CHKSY0: CALL READS ; Do track 0
- JNZ SYSERR
- LXI D,1
- LHLD TRACK
- CALL SUBDE
- JC CHKSY0
- ;
- LHLD OSTK ; Get other system track value
- MOV A,H
- ORA L ; Is it zero?
- JNZ CHK2
- LHLD SPTSAV ; If yes, use data track SPT
- ;
- CHK2: SHLD SPT ; Initialize SPT value
- ;
- CHKSY1: CALL READS ; Read a record
- JNZ SYSERR ; Notify, if bad blocks here
- LHLD SYSTRK ; Set up
- XCHG
- LHLD TRACK
- CALL SUBDE ; Do the subtract
- JC CHKSY1 ; Loop while track < SYSTRK
- ;
- IF TEST
- CALL CRLF
- MVI A,TAB ; Get a tab
- CALL TYPE ; Print it
- LXI H,RECCNT ; Get # records so far
- CALL DECOUT ; Print it
- LXI D,SYSMSG ; Point to message
- CALL PSTRNG
- ENDIF ; Test
- ;
- RET ; Return from "CHKSYS"
- ;.....
- ;
- ;
- NOSYS: LXI D,ZROSYS ; No system tracks here
- JMP PSTRNG
- ;
- ZROSYS: DB CR,LF,'No SYSTEM tracks on this disk',CR,LF,'$'
- ;
- SKPSYS: LXI D,SKPMSG ; Say skipping system tracks
- JMP PSTRNG
- ;
- SKPMSG: DB CR,LF,'Skipping system tracks...',CR,LF,'$'
- ;
- SYSERR:
- LXI D,ERMSG5 ; Say no go, and bail out
- ;
- PSTRNG: MVI C,9 ; BDOS print string function
- CALL BDOS
- RET ; Return from "SYSERR" or subroutine
- ;.....
- ;
- ;
- ; Check for bad blocks in directory area
- ;
- CHKDIR: LXI D,TDRMSG ; Testing directory area message
- CALL PSTRNG
- LXI B,0 ; Start at block 0
- ;
- CHKDI1: CALL READB ; Read a block
- JNZ ERROR6 ; If bad, show error in directory area
- INX B ; Bump for next block
- LHLD DIRBKS ; Set up (CURGRP - DIRBKS)
- DCX H ; Make 0-relative
- MOV D,B
- MOV E,C
- CALL SUBDE ; Do the subtract
- JNC CHKDI1 ; Loop until CURGRP > DIRGRP
- RET ; Return from CHKDIR
- ;.....
- ;
- ;
- ; Read all records in block, and return zero flag set if none bad
- ;
- READB: CALL CNVRTB ; Convert to track/record in 'HL' regs.
- LDA BLM
- INR A ; Number of records/block
- MOV D,A ; In 'D' register
- ;
- READBA: PUSH D
- CALL READS ; Read skewed record
- POP D
- RNZ ; Error if not zero
- DCR D ; Debump record/block
- JNZ READBA ; Do next, if not finished
- RET ; Return from 'READBA'
- ;.....
- ;
- ;
- ; Convert block number to track and skewed record number
- ;
- CNVRTB: PUSH B ; Save current group
- MOV H,B ; Need it in 'HL'
- MOV L,C ; For easy shifting
- LDA BSH ; Dpb value that tells how to
- ;
- SHIFT: DAD H ; Shift group number to get
- DCR A ; Disk-data-area relative
- JNZ SHIFT ; Record number
- XCHG ; Rel record # into 'DE'
- LHLD SPT ; Records per track from DPB
- CALL NEG ; Faster to DAD than call SUB D
- XCHG
- LXI B,0 ; Initialize quotient
- ;
- ;
- ; Divide by number of records
- ; quotient = track
- ; mod = record
- ;
- DIVLP: INX B ; Dirty division
- DAD D
- JC DIVLP
- DCX B ; Fixup last
- XCHG
- LHLD SPT
- DAD D
- INX H
- SHLD RECORD ; Now have logical record
- LHLD SYSTRK ; But before we have track #,
- DAD B ; We have to add system track offset
- SHLD TRACK
- POP B ; This was our group number
- RET
- ;.....
- ;
- ;
- ; Reads a logical record (if it can) and returns zero flag set if no
- ; error.
- ;
- READS: PUSH B ; Save the group number
- CALL LTOP ; Convert logical to physical
- LDA VER2FL ; Now check version
- ORA A
- JZ NOTCP2 ; Skip this stuff if CP/M 1.4
- LHLD PHYREC ; Get physical record
- MOV B,H ; Into 'BC'
- MOV C,L
- CALL SETREC ; BIOS set record call
- ;
- ;
- ; QUICK NOTE OF EXPLANATION: This code appears as if we skipped the
- ; SETREC routine for 1.4 CP/M users. That is not true. In CP/M 1.4,
- ; the call within the LTOP routine to RECTRAN actually does the set
- ; record, so no need to do it twice.
- ;
- NOTCP2: LHLD TRACK ; Now set the track
- MOV B,H ; CP/M wants it in 'BC'
- MOV C,L
- CALL SETTRK ; BIOS set track
- ;
- ;
- ; Now do the record read
- ;
- CALL DREAD ; BIOS disk read
- ORA A ; Set flags
- PUSH PSW ; Save error flag
- ;
- IF TEST
- CALL INCREC ; Increment record count
- ENDIF ; Test
- ;
- LHLD RECORD ; Get logical record #
- INX H ; We want to increment to next
- XCHG ; But first,check overflow
- LHLD SPT ; By doing (recpertrk-record)
- CALL SUBDE ; Do the subtraction
- XCHG
- JNC NOOVF ; Jump if not record > recpertrk
- ;
- ;
- ; Record overflow...bump track number, reset record
- ;
- LHLD TRACK
- INX H
- SHLD TRACK
- LDA ASTRS ; Check if column length set
- ORA A
- JNZ HDCOPY ; Non-zero - do *'s
- SHLD DECWRK ; Decout destroys
- LXI D,TRKMSG
- CALL PSTRNG
- LXI H,DECWRK
- CALL DECOUT
- MVI A,' '
- CALL TYPE
- JMP NOCRLF
-
- HDCOPY: MVI A,'*' ; Tell console another track done
- CALL TYPE
- LDA COLUMN ; Check column
- ORA A ; Skip if zero
- JZ NOCRLF
- DCR A
- STA COLUMN
- JNZ NOCRLF ; Jump if less than 80
- LDA ASTRS ; Reset column
- STA COLUMN
- CALL CRLF
- ;
- NOCRLF: CALL STOP ; See if console wants to quit
- LXI H,1 ; New record number on next track
- ;
- NOOVF: SHLD RECORD ; Put record away
- POP PSW ; Get back error flags
- POP B ; Restore group number
- RET
- ;.....
- ;
- ;
- ; Convert logical record # to physical
- ;
- LTOP: LHLD RECTBL ; Set up parameters
- XCHG ; For call to RECTRAN
- LHLD RECORD
- MOV B,H
- MOV C,L
- DCX B ; Always call RECTRN w/zero-rel sec #
- ;
- PUSH H
- PUSH D
- LHLD SYSTRK
- XCHG
- LHLD TRACK
- CALL SUBDE ; No translation..
- POP D
- POP H
- JC LTOP1 ; If doing system tracks
- ;
- RECT1: CALL RECTRN ; Do the record translation
- LDA SPT+1 ; Check if big tracks
- ORA A ; Set flags (tracks > 256 records)
- JNZ LTOP1 ; No so skip
- LDA TRACK ; Check for track 0
- MOV B,A
- LDA TRACK+1 ; High order
- ORA B
- JNZ LTOP1 ; Not track 0
- ;; MOV H,A ; Zero out upper 8 bits
- ;
- LTOP1: SHLD PHYREC ; Put away physical record
- RET
- ;.....
- ;
- ;
- ; Direct BIOS calling is done here...
- ;
- SETDSK: JMP $-$ ; Filled in by SETUP
- SETTRK: JMP $-$ ; Filled in by SETUP
- SETREC: JMP $-$ ; Filled in by SETUP
- DREAD: JMP $-$ ; Filled in by SETUP
- RECTRN: JMP $-$ ; Filled in by SETUP
- ;
- ;
- ; Put bad block in bad block list
- ;
- SETBD: PUSH B
- LXI D,BBMSG ; Bad block message
- CALL PSTRNG
- POP B ; Get back block number
- MOV A,B
- CALL HEXO ; Print in hex
- MOV A,C
- CALL HEXO
- CALL CRLF
- LXI H,DM ; Point to exitsing bad blocks
- ;
- SETBD2: MOV A,M ; Get first 8 bits of bad map entry
- INX H
- CMP C ; Is new entry already there ?
- JZ SETBD4 ; Maybe
- LDA DSM+1 ; Check size of block entries
- ORA A
- JZ SETBD3 ; Small blocks
- INX H ; Skip over high order half
- ;
- SETBD3: PUSH H
- XCHG ; Save 'HL'
- LHLD DMPTR
- XCHG
- CALL SUBDE ; Scan pointer-(DMPTR)
- POP H ; Restore scan pointer
- JC SETBD2 ; Continue searching
- JMP SETBD9 ; Put it away
- ;
- SETBD4: LDA DSM+1
- ORA A
- RZ ; Small blocks = done
- MOV A,M ; Get high order half of existing block
- CMP B ; Compare with block to be added
- RZ ; Really already there
- INX H ; Point to low order of next block
- JMP SETBD3 ; Check if done
- ;
- SETBD9: LHLD DMCNT ; Get number of records
- LDA BLM ; Get block shift value
- INR A ; Makes record/group value
- MOV E,A ; We want 16 bits
- MVI D,0
- DAD D ; Bump by number in this block
- SHLD DMCNT ; Update number of records
- LHLD BADBKS ; Increment number of bad blocks
- INX H
- SHLD BADBKS
- LHLD DMPTR ; Get pointer into DM
- MOV M,C ; And put bad block number
- INX H ; Bump to next available extent
- LDA DSM+1 ; Check if 8 or 16 bit block size
- ORA A
- JZ SMGRP ; Jump if 8 bit blocks
- MOV M,B ; Else store hi byte of block #
- INX H ; And bump pointer
- ;
- SMGRP: SHLD DMPTR ; Save DM pointer, for next time
- RET ; Return from 'SETBD'
- ;.....
- ;
- ;
- ; Eliminate any previous [UNUSED].BAD entries
- ;
- ERAB: LXI D,BFCB ; Bad FCB
- XRA A
- STA BFCB+12 ; Clear extent
- MVI C,15 ; Open file
- CALL BDOS ; Try to open file
- CPI 0FFH ; Not found ?
- RZ ; Yes, no need to delete it
- STA DIROFS ; Directory offset in DMA buffer
- ;
- ERAB0: LXI D,ERABMS
- CALL PSTRNG
- MVI C,1 ; Console input
- CALL BDOS
- CPI 'a'
- JC ERAB1 ; Already upper case
- ANI 05FH ; Force upper case
- ;
- ERAB1: CPI 'Y'
- JZ ERAB4 ; Do it
- CPI 'N'
- JNZ ERAB0 ; Invalid response
- CALL ERAB2 ; Load tables, then erase old file
- ;
- ERAB4: CALL CRLF
- LXI D,BFCB ; Point to bad FCB
- MVI C,19 ; BDOS delete file function
- CALL BDOS
- RET
- ;.....
- ;
- ;
- ERABMS: DB 'Erase Existing [UNUSED].BAD file ? (Y/N) $'
- ; Flag old bad blocks for this run
- ;
- ;
- ;Move bad allocated blocks to DM area
- ;
- ERAB2: LXI H,DM ; Get DM
- SHLD DMPTR ; Save as new pointer
- LDA EXM ; Get the extent shift factor
- MVI C,0 ; Init bit count
- CALL COLECT ; Get shift value
- LXI H,128 ; Starting extent size
- MOV A,C ; First see if any shifts to do
- ORA A
- JZ ERAB2B ; Jump if none
- ;
- ERAB2A: DAD H ; Shift
- DCR A ; Bump
- JNZ ERAB2A ; Loop
- ;
- ERAB2B: PUSH H ; Save this, it is records per extent
- LDA BSH ; Get block shift
- MOV B,A
- ;
- ERAB2C: CALL ROTRHL ; Shift right
- DCR B
- JNZ ERAB2C ; To get blocks per extent
- MOV A,L ; It's in 'L' (can't be >16)
- STA BLKEXT ; SETDME will need this later
- POP H ; Get back recrds/extent
- ;
- ERAB2D: XCHG ; Now have records/extent in 'DE'
- LHLD DMCNT ; Count of bad records
- ;
- ERAB2E: PUSH H
- PUSH D
- LDA BFCB+15 ; Record count
- MOV L,A
- MVI H,0
- MOV B,H ; Save record count in 'BC'
- MOV C,L
- POP D
- CALL SUBDE ; Have to subtract first
- POP H ; This pop makes it compare only
- PUSH PSW
- DAD B ; New count of bad records
- SHLD DMCNT ; Save count of bad records
- POP PSW
- JC ERAB2G ; Jump if less than 1 extent worth
- MOV A,B
- ORA C ; Test if subtract was 0
- RZ ; Extent is empty (special case)
- PUSH H ; Save total
- PUSH D ; And records/extent
- CALL ERAB2G ; Get next extent
- POP D ; Get back records/extent
- POP H ; And count of bad records
- CPI 0FFH ; Check return from search next
- RZ
- JMP ERAB2E ; And loop
- ;
- ;
- ; Load an extent's worth of bad records/block numbers. 'BC' contains
- ; the number of records in this extent
- ;
- ERAB2G: MOV A,B
- ORA C ; Check record count
- MVI A,0FFH ; Assume zero
- RZ ; No more to process
- PUSH B ; Save records in this extent
- LDA BLM ; Block mask
- INR A ; Make records/block
- CMA ; Make negative
- MOV E,A
- MVI D,0FFH
- INX D ; Make 2's complement
- POP H ; Hl = number of records
- LXI B,0
- ;
- ;
- ; Divide records in this extent by recs/block giving blocks
- ;
- ERAB2I: DAD D
- INX B ; Bump quotient
- JC ERAB2I ; Not done yet
- DCX B ; Account for overshoot
- LHLD BADBKS ; Bad block count
- DAD B
- SHLD BADBKS
- ;
- ERAB2K: LXI H,BFCB+16 ; Disk alloc map in FCB
- XCHG
- LHLD DMPTR ; Point to bad allocation map
- ;
- ERAB2L: LDAX D
- MOV M,A
- INX H
- INX D
- ;
- ;
- ; Now see if 16 bit groups...if so, we have to move another byte
- ;
- LDA DSM+1 ; This tells us
- ORA A
- JZ ERAB2M ; If zero, then not
- LDAX D ; Is 16 bits, so do another
- MOV M,A
- INX H
- INX D
- ;
- ERAB2M: DCR C ; Count down
- JNZ ERAB2L
- SHLD DMPTR
- LDA BFCB+12
- INR A
- STA BFCB+12 ; Next extent number
- LXI D,BFCB
- MVI C,15 ; Open next extent
- CALL BDOS
- STA DIROFS
- RET
- ;.....
- ;
- ;
- ; Create [UNUSED].BAD file entry
- ;
- OPENB: LXI D,BFCB ; Point to bad FCB
- MVI C,22 ; BDOS make file function
- CALL BDOS
- CPI 0FFH ; Check for open error
- RNZ ; Return from 'OPENB', if no error
- JMP ERROR7 ; Bail out...cannot create [UNUSED].BAD
- ;
- CLOSEB: XRA A
- LDA BFCB+14 ; Get CP/M 2.x 's2' byte
- ANI 1FH ; Zero update flags
- STA BFCB+14 ; Restore it to our FCB (won't hurt 1.4)
- LXI D,BFCB ; FCB for [UNUSED].BAD
- MVI C,16 ; BDOS close file function
- CALL BDOS
- RET ; Return from 'CLOSEB'
- ;.....
- ;
- ;
- ; Move bad area DM to BFCB
- ;
- SETDM: LXI H,DM ; Get DM
- SHLD DMPTR ; Save as new pointer
- LDA EXM ; Get the extent shift factor
- MVI C,0 ; Initialize bit count
- CALL COLECT ; Get shift value
- LXI H,128 ; Starting extent size
- MOV A,C ; First see if any shifts to do
- ORA A
- JZ NOSHFT ; Jump if none
- ;
- ESHFT: DAD H ; Shift
- DCR A ; Bump
- JNZ ESHFT ; Loop
- ;
- NOSHFT: PUSH H ; Save this, it is records per extent
- LDA BSH ; Get block shift
- MOV B,A
- ;
- BSHFT: CALL ROTRHL ; Shift right
- DCR B
- JNZ BSHFT ; To get blocks per extent
- MOV A,L ; It is in 'L' (cannot be 16)
- STA BLKEXT ; SETDME will need this later
- POP H ; Get back rececord/extent
- ;
- SET1: XCHG ; Now have records/extent in 'DE'
- LHLD DMCNT ; Count of bad records
- ;
- SETDMO: PUSH H ; Set flags on (DMCNT-BADCNT)
- CALL SUBDE ; Have to subtract first
- MOV B,H ; Save result in 'BC'
- MOV C,L
- POP H ; This pop makes it compare only
- JC SETDME ; Jump if less than 1 extent worth
- MOV A,B
- ORA C ; Test if subtract was 0
- JZ EVENEX ; Extent is exactly filled (special case)
- MOV H,B ; Restore result to 'HL'
- MOV L,C
- PUSH H ; Save total
- PUSH D ; And records/extent
- XCHG
- CALL SETDME ; Put away one extent
- XCHG
- SHLD DMPTR ; Put back new DM pointer
- POP D ; Get back records/extent
- POP H ; And count of bad records
- JMP SETDMO ; And loop
- ;
- ;
- ; Handle the special case of a file that ends on an extent boundary.
- ; CP/M requires that such a file have a succeeding empty extent in order
- ; for the BDOS to properly access the file.
- ;
- EVENEX: XCHG ; First set extent with bad blocks
- CALL SETDME
- XCHG
- SHLD DMPTR
- LXI H,0 ; Now set one with no data blocks
- ;
- ;
- ; Fill in an extent's worth of bad records/block numbers. Also fill in
- ; the extent number in the FCB.
- ;
- SETDME: PUSH H ; Save record count
- LDA EXTNUM ; Update extent byte
- INR A
- STA EXTNUM ; Save for later
- STA BFCB+12 ; And put in FCB
- CALL OPENB ; Open this extent
- POP H ; Retrieve record count
- ;
- ;
- ; Divide record count by 128 to get the number of logical extents to put
- ; in the EX field
- ;
- MVI B,0 ; Initialize quotient
- LXI D,-128
- MOV A,H ; Test for special case
- ORA L ; Of no records
- JZ SKIP
- ;
- DIVLOP: DAD D ; Subtract
- INR B ; Bump quotient
- JC DIVLOP
- LXI D,128 ; Fix up overshoot
- DAD D
- DCR B
- MOV A,H ; Test for wraparound
- ORA L
- JNZ SKIP
- MVI L,80H ; Record length
- DCR B
- ;
- SKIP: LDA EXTNUM ; Now fix up extent num
- ADD B
- STA EXTNUM
- STA BFCB+12
- MOV A,L ; Mod is record count
- STA BFCB+15 ; That goes in receive byte
- ;
- MOVDM: LDA BLKEXT ; Get blocks per extent
- MOV B,A ; Into 'B'
- ;
- SETD1: LHLD DMPTR ; Point to bad allocation map
- XCHG
- LXI H,BFCB+16 ; Disk alloc map in FCB
- ;
- SETDML: LDAX D
- MOV M,A
- INX H
- INX D
- ;
- ;
- ; Now see if 16 bit groups...if so, we have to move another byte
- ;
- LDA DSM+1 ; This tells us
- ORA A
- JZ BUMP1 ; If zero, then not
- LDAX D ; Is 16 bits, so do another
- MOV M,A
- INX H
- INX D
- ;
- BUMP1: DCR B ; Count down
- JNZ SETDML
- PUSH D
- CALL CLOSEB ; Close this extent
- POP D
- RET
- ;.....
- ;
- ;
- ; Error messages
- ;
- SELERR: LXI D,SELEMS ; Say no go, and bail out
- JMP PMSG
- SELEMS: DB CR,LF,'Drive specifier out of range$'
- ERMSG5: DB CR,LF,BELL,'+++ Warning...system tracks'
- DB ' bad +++',BELL,CR,LF,'$'
- ERROR6: LXI D,ERMSG6 ; Oops...clobbered directory
- JMP PMSG
- ERMSG6: DB CR,LF,BELL,'Bad directory area, try reformatting'
- DB CR,LF,BELL,'$'
- ERROR7: LXI D,ERMSG7 ; Say no go, and bail out
- JMP PMSG
- ERMSG7: DB CR,LF,BELL,'Can''t create [UNUSED].BAD$'
- BELLS: DB BELL,BELL,BELL,'$'
- ;
- ;
- HELP: LXI D,USAGE
- CALL PSTRNG
- JMP WBOOT
- USAGE: DB CR,LF
- DB 'Usage:',CR,LF,CR,LF
- DB ' FBAD [d:] ['
- DB '$' OR 80H
- DB 'options]',CR,LF,CR,LF
- DB ' d: = drive to check',CR,LF
- DB 'options = S check system tracks',CR,LF
- DB ' Sn0 n0 = sector count for track 0',CR,LF
- DB ' Sn0,n1 n1 = sector count for sys trk > 0',CR,LF
- DB ' (default is '
- DB DFSPT0/10+'0',(DFSPT0 MOD 10)+'0'
- DB ', '
- DB DFOSTK/10+'0',(DFOSTK MOD 10)+'0'
- DB ')',CR,LF
- DB ' X same as S with different default SPT'
- DB CR,LF
- DB ' (default is '
- DB XSPT0/10+'0',(XSPT0 MOD 10)+'0'
- DB ', '
- DB XOSTK/10+'0',(XOSTK MOD 10)+'0'
- DB ')',CR,LF
- DB ' A print "*" instead of track number'
- DB CR,LF
- DB '$'
- ;
- ;
- ; Look for opitons flag (SPCHR)
- ;
- GETOPT: LXI H,TBUFF ; Start at beginning of line
- MOV A,M
- ORA A ; If it is empty
- RZ ; Exit
- INX H ; Point to first character
- LDA FCB
- ORA A ; Was there a drive specified?
- JZ FINDIT
- INX H ; Yes, skip over it
- INX H
- ;
- FINDIT: INX H
- MOV A,M ; Get a character
- CPI 00H ; End of line ?
- RZ ; Continue.
- CPI ' '
- JZ FINDIT ; Skip spaces.
- CPI SPCHR ; Option flag ?
- JNZ HELP ; No, error
- ;
- FINDIT1:INX H ; Check next character
- MOV A,M
- CPI 00H ; End of line ?
- RZ ; Test disk.
- CPI ' '
- JZ FINDIT1 ; Skip spaces
- CPI 'A'
- JZ AOPT ; Toggle asterisk/track number flag
- CPI 'S'
- JZ SOPT ; Test system tracks
- CPI 'X'
- JZ XOPT ; Set special value for system tracks spt
- JMP HELP
- ;
- AOPT: MVI A,76
- STA ASTRS ; Initialize column count
- JMP FINDIT1 ; Look for more options
- ;
- SOPT: MVI A,1
- STA SYSTST ; Set system track test flag.
- ;
- ;
- ; If numeric after S, must be track zero's record count.
- ;
- INX H
- MOV A,M
- CALL VALDEC ; Number from 0 to 9 ?
- JC SOPT1 ; No, check next option
- XCHG ; Get address in de
- LXI H,00 ; Initialize hl to 0
- CALL ATOH ; Convert to hex
- SHLD SPT0 ; Save sector count for first track
- XCHG ; Restore hl
- ;
- ;
- ; If comma after first number, must be record count for other system
- ; tracks.
- ;
- MOV A,M
- CPI ','
- JNZ SOPT1
- INX H
- MOV A,M
- CALL VALDEC
- JC HELP ; Not a valid number, give help
- XCHG
- LXI H,00
- CALL ATOH
- SHLD OSTK
- XCHG
- ;
- SOPT1: DCX H ; Back up to next char.
- JMP FINDIT1 ; See if more options
- ;.....
- ;
- ;
- XOPT: MVI A,1
- STA SYSTST ; Set system track test flag.
- PUSH H
- LXI H,XSPT0 ; Special value for spt0
- SHLD SPT0
- LXI H,XOSTK ; Special value for other system track(s)
- SHLD OSTK
- POP H
- JMP FINDIT1 ; Look for more options
- ;.....
- ;
- ;
- ; ASCII to hex conversion
- ;
- ATOH: MOV B,H ; B = 00
- ;
- ATOH1: LDAX D ; Get character
- CALL VALDEC ; Make 0-9, see if valid
- RC ; Not decimal, stop conversion
- PUSH D
- MOV D,H
- MOV E,L ; De = hl
- DAD H ; 2*n
- DAD H ; 4*n
- DAD D ; 5*n
- DAD H ; 10*n
- POP D
- MOV C,A
- DAD B ; Add in next char
- INX D ; Point to next char
- JMP ATOH1 ; Process it
- ;....
- ;
- ;
- ; Convert ASCII digit to decimal number. Return with carry = 0 if valid
- ; digit from 0-9.
- ;
- VALDEC: SUI '0' ; Subtract ascii offset
- RC ; < 0
- CPI 10 ; > 9
- CMC ; Adjust carry flag
- RET
- ;
- ;
- ;=======================================================================
- ;
- ; SUBROUTINES
- ;
- ;=======================================================================
- ;
- ;
- ADD24: PUSH H
- ORA A ; Clear carry
- MOV A,M ; Do 24-bit add
- ADC C
- MOV M,A
- INX H
- MOV A,M
- ADC B
- MOV M,A
- INX H
- MOV A,M
- ADC B
- MOV M,A
- POP H
- RET
- ;.....
- ;
- ;
- ; CR/LF to the console
- ;
- CRLF: MVI A,CR
- CALL TYPE
- MVI A,LF ; Fall into 'type'
- ;
- TYPE: PUSH B
- PUSH D
- PUSH H
- MOV E,A ; Character to 'e' for cp/m
- MVI C,2 ; Print console function
- CALL BDOS ; Print character
- POP H
- POP D
- POP B
- RET
- ;.....
- ;
- ;
- ; Decimal output routine - If the disk size is at or near the maximum of
- ; 65,536 records, the record addition overflows 16 bits. The counter
- ; has been increased to 24 bits. This routine destroys the counter-
- ; value it is called with.
- ;
- DECOUT: PUSH B
- PUSH D
- LXI B,-10
- LXI D,-1
- ;
- DECOU2: CALL ADD24
- INX D
- JC DECOU2
- LXI B,10
- CALL ADD24
- MOV A,D
- ORA E
- PUSH H
- MOV A,M
- MOV M,E
- MOV E,A
- INX H
- MOV A,M
- MOV M,D
- MOV D,A
- INX H
- MVI M,0
- POP H
- CNZ DECOUT ; Recursive call..
- MOV A,E
- ADI '0'
- CALL TYPE
- POP D
- POP B
- RET
- ;.....
- ;
- ;
- ; Collect the number of '1' bits in 'A' as a count in 'C'
- ;
- COLECT: MVI B,8
- ;
- COLOP: RAL
- JNC COSKIP
- INR C
- ;
- COSKIP: DCR B
- JNZ COLOP
- RET
- ;.....
- ;
- ;
- ; Subroutine to determine the number of groups reserved for the directory
- ;
- GETDIR: MVI C,0 ; Initialize bit count
- LDA AL0 ; Read directory group bits
- CALL COLECT ; Collect count of directory groups..
- LDA AL1 ; In 'c'
- CALL COLECT
- MOV L,C
- MVI H,0 ; 'bc' now has a default start group #
- SHLD DIRBKS ; Save for later
- RET
- ;.....
- ;
- ;
- ; Print byte in accumulator in hex
- ;
- HEXO: PUSH PSW ; Save for second half
- RRC ; Move into position
- RRC
- RRC
- RRC
- CALL NYBBLE ; Print most significant nybble
- POP PSW
- ;
- NYBBLE: ANI 0FH ; Low nybble only
- ADI 90H
- DAA
- ACI 40H
- DAA
- JMP TYPE ; Print in hex
- ;.....
- ;
- ;
- ; Total record count routine (24-bit)
- ;
- INCREC: LXI H,RECCNT ; Point to record count
- MVI A,1 ; Add 1
- ORA A ; Clear carry
- ADC M ; Add current total
- MOV M,A ; And save 1st byte
- RNC ; Return if no carry
- INX H ; Increment pointer
- MVI A,0 ; Reset 'a'
- ADC M ; Add 2nd byte
- MOV M,A ; And save
- RNC ; Return if no carry
- INX H ; Increment pointer
- MVI A,0 ; And again for 3rd byte
- ADC M
- MOV M,A
- RET
- ;.....
- ;
- ;
- ; Routine to fill in disk parameters
- ;
- LOGIT: LDA VER2FL
- ORA A ; If not cp/m 2.x then
- JZ LOG14 ; Do it as 1.4
- LXI D,DPB ; Then move to local
- MVI B,DPBLEN ; Workspace
- CALL MOVE
- RET
- ;.....
- ;
- ;
- LOG14: LHLD BDOS+1 ; First find 1.4 bdos
- MVI L,0
- LXI D,DPBOFF ; Then offset to 1.4's dpb
- DAD D
- MVI D,0 ; So 8 bit parms will be 16
- MOV E,M ; Now move parms
- INX H ; Down from bdos disk parameter block
- XCHG ; To ours
- SHLD SPT
- XCHG
- MOV E,M
- INX H
- XCHG
- SHLD DRM
- XCHG
- MOV A,M
- INX H
- STA BSH
- MOV A,M
- INX H
- STA BLM
- MOV E,M
- INX H
- XCHG
- SHLD DSM
- XCHG
- MOV E,M
- INX H
- XCHG
- SHLD AL0
- XCHG
- MOV E,M
- XCHG
- SHLD SYSTRK
- RET
- ;.....
- ;
- ;
- ; Move from (HL) to (DE), with count in (BC)
- ;
- MOVE: MOV A,M
- STAX D
- INX H
- INX D
- DCR B
- JNZ MOVE
- RET
- ;.....
- ;
- ;
- ; Negate HL
- ;
- NEG: MOV A,L
- CMA
- MOV L,A
- MOV A,H
- CMA
- MOV H,A
- INX H
- RET
- ;.....
- ;
- ;
- ; Shift HL right one place
- ;
- ROTRHL: ORA A ; Clear carry
- MOV A,H ; Get hihg byte
- RAR ; Shift right
- MOV H,A ; Put back
- MOV A,L ; Get low byte
- RAR ; Shift with carry
- MOV L,A ; Put back
- RET
- ;.....
- ;
- ;
- ; Subroutine to test console for CTL-C abort
- ;
- STOP: CALL CSTS ; Get console status
- ORA A ; Test flags on zero
- RZ ; Return if no character
- CALL CI ; Get the character
- ANI 7FH ; Some teminals set parity bit
- CPI 'C'-40H ; Is it ctl-c?
- RNZ ; Return if not
- LXI D,ABORTM ; Exit with message
- CALL PSTRNG
- JMP WBOOT ; Then leave
- ;
- CSTS: LDA VER2FL ; One way for 2.x or higher...
- ORA A ; Check for non-zero
- JZ CSTS14
- MVI C,11
- JMP BDOS
- ;
- CSTS14: LHLD 0001H ; Find bios in memory
- MVI L,6 ; Offset to console status
- PCHL
- ;
- CI: LDA VER2FL
- ORA A
- JZ CI14
- MVI C,1
- JMP BDOS
- ;
- CI14: LHLD 1 ; Now find console input
- MVI L,9 ; Offset for conin
- PCHL
- ;.....
- ;
- ;
- ; Subtract DE from HL
- ;
- SUBDE: MOV A,L
- SUB E
- MOV L,A
- MOV A,H
- SBB D
- MOV H,A
- RET
- ;.....
- ;
- ;
- ; Zero all of memory to hold DM values
- ;
- ZMEM: LHLD BDOS+1 ; Get top-of-memory pointer
- LXI D,DM ; Starting point
- CALL SUBDE ; Get number of bytes
- MOV B,H
- MOV C,L
- XCHG ; Begin in 'hl', count in 'bc'
- ;
- ZLOOP: MVI M,0 ; Zero a byte
- INX H ; Point past
- DCX B ; Count down
- MOV A,B
- ORA C
- JNZ ZLOOP
- RET
- ;.....
- ;
- ;
- SIGNON: DB CR,LF,'FBAD V',(VERS/10 + '0'),(VERS MOD 10)+'0','A'
- DB ' - Bad record lockout program - (^C to abort)',CR,LF,'$'
- ABORTM: DB CR,LF,'Test aborted by CTL-C','$'
- BBMSG: DB CR,LF,'Bad block: $'
- SYSMSG: DB ' system records read',CR,LF,'$'
- RECMSG: DB ' directory+data records read',CR,LF,'$'
- TRKMSG: DB CR,'Track $'
- NOMSG: DB 'No$'
- ENDMSG: DB ' bad blocks found',CR,LF,'$'
- TDAMSG: DB CR,LF,'Testing data area...',CR,LF,'$'
- TSTMSG: DB CR,LF,'Testing system tracks...',CR,LF,'$'
- TDRMSG: DB CR,LF,'Testing directory area...',CR,LF,'$'
- DIROFS: DB 0 ; Offset of directory entry in dma buffer
- BFCB: DB 0,'[UNUSED]BAD',0,0,0,0
- FCBDM: DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
- CMSG1: DB CR,LF,'Press C to continue on the same drive'
- DB CR,LF,'Any other key aborts to CP/M $'
- ;
- ;
- SPTSAV: DW 00 ; Save spt for data tracks
- ;
- ; The disk parameter block is moved here from CP/M
- ;
- DPB EQU $ ; Disk parameter block (copy)
- SPT: DS 2 ; Records per track
- BSH: DS 1 ; Block shift
- BLM: DS 1 ; Block mask
- EXM: DS 1 ; Extent mask
- DSM: DS 2 ; Maximum block number
- DRM: DS 2 ; Maximum directory block number
- AL0: DS 1 ; Directory allocation vector
- AL1: DS 1 ; Directory allocation vector
- CKS: DS 2 ; Checked directory entries
- SYSTRK: DS 2 ; System tracks
- DPBLEN EQU $-DPB ; Length of disk parameter block
- ;
- BLKEXT: DB 0 ; Blocks per extent
- DIRBKS: DW 0 ; Calculated # of directory blocks
- VER2FL: DB 0 ; Version 2.x flag, non-0 ==> 2.x or higher
- BADBKS: DB 0,0,0 ; Count of bad blocks
- RECORD: DW 0 ; Current record number
- TRACK: DB 0,0 ; Current track number
- PHYREC: DW 0 ; Current physical record number
- RECTBL: DW 0 ; Record skew table pointer
- EXTNUM: DB 0FFH ; Used for updating extent number
- DMCNT: DW 0 ; Number of bad records
- DMPTR: DW DM ; Pointer to next block identification
- RECCNT: DB 0,0,0 ; Number of records read
- COLUMN: DB 0 ; Crt column counter
- DECWRK: DB 0,0,0 ; Track-storage for decout
- ORIGDR: DS 1 ; Original drive spec from fcb stored here
- ;
- ;
- DS 200 ; Room for 100 level stack
- STACK EQU $ ; Our stack
- ;
- DM EQU $ ; Bad block allocation map
- ;
- ;
- END
-
-