home *** CD-ROM | disk | FTP | other *** search
- ; FINDBAD.ASM ver. 5.4
- ; (revised 05/21/81)
- ;
- ; NON-DESTRUCTIVE DISK TEST PROGRAM
- ;
- ;FINDBAD will find all bad blocks on a disk and build a file
- ;named [UNUSED].BAD to allocate them, thus "locking out" the
- ;bad blocks so CP/M will not use them.
- ;
- ;Originally written by Gene Cotton, published in "Interface
- ;Age", September 1980 issue, page 80.
- ;
- ;See notes below concerning 'TEST' conditional assembly option,
- ;SYSTST and BADUSR directives.
- ;
- ;********************************************************
- ;* *
- ;* NOTE *
- ;* *
- ;* This program has been re-written to allow it to *
- ;* work with (hopefully) all CP/M 2.x systems, and *
- ;* most 1.4 CP/M systems. It has been tested on sev- *
- ;* eral different disk systems, including Northstar, *
- ;* Micropolis, DJ2D, and Keith Petersen's 10 MByte *
- ;* hard disk system. I have tested it personally on *
- ;* my "modified" Northstar, under several different *
- ;* formats (including >16K per extent), and have ob- *
- ;* no difficulties. *
- ;* If you have have difficulties getting this pro- *
- ;* gram to run, AND if you are using CP/M 2.x, AND *
- ;* if you know your CBIOS to be bug-free, leave *
- ;* me a message on the CBBS mentioned below ... I am *
- ;* interested in making this program as "universal" *
- ;* as possible. *
- ;* I can't help with any version of CP/M 1.4, other *
- ;* than "standard" versions (whatever that means), *
- ;* because there are just too many heavily modified *
- ;* versions available. *
- ;* One possible problem you may find is with the *
- ;* system tracks of your diskettes...if they are of *
- ;* a different density than the data tracks, then *
- ;* see the note regarding the "SYSTST" equate. *
- ;* *
- ;* Ron Fowler *
- ;* Westland, Mich *
- ;* 7 April, 1981 *
- ;* *
- ;********************************************************
- ;
- ;SYSTST and BADUSR 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 103H to a 0 if you don't want the
- ;system tracks tested, otherwise leave it 1. This is also
- ;necessary if you have a "blocked" disk system; that is, when
- ;the same physical disk is seperated into logical disks by use
- ;of the SYSTRK word in the disk parameter block.
- ; If you are a CP/M 2.x user, you may assign the user number
- ;where [UNUSED.BAD] will be created by changing the byte at
- ;104H to the desired user number. If you want it in the
- ;default user, then leave it 0FFH. CP/M 1.4 users can ignore
- ;this byte altogether.
- ;
- ;Note that these changes can be done with DDT as follows:
- ;
- ; A>DDT FINDBAD.COM
- ; -S103
- ; 103 01 0 ;DON'T TEST SYSTEM TRACKS
- ; 104 FF F ;PUT [UNUSED.BAD] IN USER 15
- ; 105 31 . ;DONE WITH CHANGES
- ; -^C
- ; A>SAVE XX FINDBAD.COM
- ;
- ;----------------------------------------------------------------
- ;NOTE: If you want to update this program, make sure you have
- ;the latest version first. After adding your changes, please
- ;modem a copy of the new file to "TECHNICAL CBBS" in Dearborn,
- ;Michigan - phone 313-846-6127 (110, 300, 450 or 600 baud).
- ;Use the filename FINDBAD.NEW. (KBP)
- ;
- ;Modifications/updates: (in reverse order to minimize reading time)
- ;
- ;05/21/81 Corrected error in description of how to set SYSTST
- ; byte at 103h. Added CRLF to block error message. (KBP)
- ;
- ;05/19/81 Corrected omission in DOLOG routine so that BADUSR
- ; will work correctly. Thanks to Art Larky. (CHS)
- ;
- ;04/10/81 Changed extent DB from -1 to 0FFH so program can be
- ; assembled by ASM. Added BADUSR info to instructions
- ; for altering with DDT. (KBP)
- ;
- ;04/09/81 Changed sign-on message, added control-c abort test,
- ; added '*' to console once each track (RGF)
- ;
- ;04/07/81 Re-wrote to add the following features:
- ; 1) "Universal" operation
- ; 2) DDT-changeable "SYSTRK" boolean (see above)
- ; 3) Report to console when bad blocks are detected
- ; 4) Changed the method of printing the number of
- ; bad blocks found (at end of run)...the old
- ; method used too much code, and was too cum-
- ; bersome.
- ; 5) Made several cosmetic changes
- ;
- ; Ron Fowler
- ; Westland, Mich
- ;
- ;03/23/81 Set equates to standard drive and not double-sided. (KBP)
- ;
- ;03/01/81 Corrected error for a Horizon with double sided drive.
- ; This uses 32k extents, which code did not take into account.
- ; (Bob Clyne)
- ;
- ;02/05/81 Merged 2/2/81 and 1/24/81 changes, which were done
- ; independently by Clyne and Mack. (KBP)
- ;
- ;02/02/81 Added equates for North Star Horizon - 5.25" drives,
- ; double density, single and double sided. (Bob Clyne)
- ;
- ;01/24/81 Added equates for Jade DD disk controller
- ; (Pete H. Mack)
- ;
- ;01/19/81 Added equates for Icom Microfloppy 5.25" drives.
- ; (Eddie Currie)
- ;
- ;01/05/81 Added equates for Heath H-17 5.25" drives.
- ; (Ben Goldfarb)
- ;
- ;12/08/80 Added equates for National Multiplex D3S/D4S
- ; double-density board in various formats.
- ; (David Fiedler)
- ;
- ;09/22/80 Added equates for Morrow Disk Jockey 2D/SS, 256,
- ; 512 and 1024-byte sector options. Fix 'S2' update
- ; flag for larger max number of extents. Cleaned up
- ; file. (Ben Bronson and KBP)
- ;
- ;09/14/80 Corrected DGROUP equate for MMDBL. Added new routine
- ; to correct for IMDOS group allocation. Corrected
- ; error in instructions for using TEST routine.
- ; (CHS) (AJ) (KBP) - (a group effort)
- ;
- ;09/08/80 Fixed several errors in Al Jewer's mods. Changed
- ; return to CP/M to warm boot so bitmap in memory will
- ; be properly updated. Added conditional assembly for
- ; testing program. (KBP)
- ;
- ;09/02/80 Added IMDOS double-density equates & modified for
- ; more then 256 blocks per disk. (Al Jewer)
- ;
- ;09/01/80 Changed equates so that parameters are automatically
- ; set for each disk system conditional assembly (KBP)
- ;
- ;08/31/80 Add conditional assembly for Digital Microsystems FDC3
- ; controller board in double-density format and fix to
- ; do 256 blocks in one register. (Thomas V. Churbuck)
- ;
- ;08/31/80 Correct MAXB equate - MAXB must include the directory
- ; blocks as well as the data blocks. Fix to make sure
- ; any [UNUSED].BAD file is erased before data area is
- ; checked. (KBP)
- ;
- ;08/30/80 Added conditional assembly for Micromation
- ; double-density format. (Charles H. Strom)
- ;
- ;08/27/80 Fix missing conditional assembly in FINDB routine.
- ; Put version number in sign-on message. (KBP)
- ;
- ;08/26/80 Modified by Keith Petersen, W8SDZ, to:
- ; (1) Add conditional assembly for 1k/2k groups
- ; (2) Add conditional assembly for standard drives
- ; and Micropolis MOD II
- ; (3) Make compatible with CP/M-2.x
- ; (4) Remove unneeded code to check for drive name
- ; (CP/M does it for you and returns it in the FCB)
- ; (5) Changed to open additional extents as needed for
- ; overflow, instead of additional files
- ; (6) Add conditional assembly for system tracks check
- ; (some double-density disks have single-density
- ; system tracks which cannot be read by this program)
- ; (7) Increased stack area (some systems use more than
- ; others).
- ;
- ;08/06/80 Added comments and crunched some code.
- ; KELLY SMITH. 805-527-9321 (Modem, 300 Baud)
- ; 805-527-0518 (Verbal)
- ;
- ;
- ; Using the Program
- ;
- ; Before using this program to "reclaim" a diskette, it is
- ;recommended that the diskette be reformatted. If this is not
- ;possible, at least assure yourself that any existing files
- ;on the diskette do not contain unreadable sectors. If you
- ;have changed disks since the last warm-boot, you must warm-
- ;boot again before running this program.
- ;
- ; To use the program, insert both the disk containing the
- ;program FINDBAD.COM and the diskette to be checked into the
- ;disk drives. It is possible that the diskette containing the
- ;program is the one to be checked. Assume that the program is
- ;on drive "A" and the suspected bad disk is on drive "B". In
- ;response to the CP/M prompt "A>", type in FINDBAD B:. This
- ;will load the file FINDBAD.COM from drive "A" and test the
- ;diskette on drive "B" for unreadable sectors. The only
- ;allowable parameter after the program name is a drive
- ;specification (of the form " N:") for up to four (A to D)
- ;disk drives. 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 (0 and 1),
- ;and any errors here prohibit the disk from being used on
- ;drive "A", since all "warm boots" occur using the system
- ;tracks from the "A" drive.
- ;
- ; The program next checks the first two data blocks (groups
- ;to some of us) containing the directory of the diskette. If
- ;errors occur here, the program terminates and control
- ;returns to CP/M (no other data blocks are checked since
- ;errors in the directory render the disk useless).
- ;
- ; Finally, all the remaining data blocks are checked. Any
- ;sectors which 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 "XX bad blocks found" is
- ;displayed (where XX is replaced by the number of bad blocks,
- ;or "No" if no read errors occur). If bad blocks occur, the
- ;filname [UNUSED].BAD is created, the list of "bad blocks" is
- ;placed in the allocation map of the directory entry for
- ;[UNUSED].BAD, and the file is closed. Note, that when the
- ;number of "bad blocks" exceeds 16, the program will open
- ;additional extents as required to hold the overflow. I
- ;suggest that 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.
- ;
- ; The nifty part of all this is that if any "bad blocks" do
- ;occur, they are allocated to [UNUSED].BAD and no longer will
- ;be available to CP/M for future allocation...bad sectors are
- ;logically locked out on the diskette!
- ;
- ;
- ; Using the TEST conditional assembly
- ;
- ;A conditional assembly has been added to allow testing this
- ;program to make sure it is reading all sectors 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 start, we must know the number of
- ;sectors/block (8 sectors/block for standard IBM single density
- ;format). If this value is not known, it can easily be
- ;determined 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/sector (the standard CP/M
- ;sector size) will give sectors/block. For our IBM single
- ;density example, we have:
- ;
- ; (1024 bytes/block) / (128 bytes/sector) = 8 sectors/block.
- ;
- ;We can now calculate blocks/track (assuming we know the number
- ;sectors/track). In our example:
- ;
- ; (26 sectors/track) / (8 sectors/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
- ;sectors/block results in total sectors as should be reported
- ;when TEST is set TRUE and a good disk is read. For our example,
- ;this value is 1944 sectors.
- ;
- ;Finally, note that if SYSTST is set to 0, the sectors present
- ;on the first two tracks must be added in as well. In the
- ;previous example, this results in 1944 + 52 = 1996 sectors
- ;reported by the TEST conditional.
- ;
- ;Run the program on a KNOWN-GOOD disk. It should report that it
- ;has read the correct number of sectors. The test conditional
- ;assembly should then be set FALSE and the program re-assembled.
- ;The test routines cannot be left in because this program does
- ;not read all the sectors in a block that is found to be bad and
- ;thus will report an inaccurate number of sectors read.
- ;
- ;
- ;Define TRUE and FALSE
- ;
- FALSE EQU 0
- TRUE EQU NOT FALSE
- ;
- ;******************************************************************
- ;
- ;Conditional assembly switch for testing this program
- ;(for initial testing phase only - see remarks above)
- ;
- TEST EQU FALSE ;TRUE FOR TESTING ONLY
- ;
- ;******************************************************************
- ;
- ;System equates
- ;
- BASE EQU 0 ;STANDARD CP/M BASE ADDRESS (4200H FOR ALTCPM)
- BDOS EQU BASE+5 ;CP/M WARM BOOT ENTRY
- FCB EQU BASE+5CH;CP/M DEFAULT FCB LOCATION
- ;
- ;Define ASCII characters used
- ;
- CR EQU 0DH ;CARRIAGE RETURN CHARACTER
- LF EQU 0AH ;LINE FEED CHARACTER
- TAB EQU 09H ;TAB CHARACTER
- ;
- DPBOFF EQU 3AH ;CP/M 1.4 OFFSET TO DPB WITHIN BDOS
- TRNOFF EQU 15 ;CP/M 1.4 OFFSET TO SECTOR XLATE ROUTINE
- ;
- ;
- ORG BASE+100H
- ;
- JMP START ;JMP AROUND OPTION BYTES
- ;
- ;If you want the system tracks tested, then
- ;put a 1 here, otherwise 0.
- ;
- SYSTST: DB 1 ;0 IF NO SYS TRACKS, OTHERWISE 1
- ;
- ;If you are a CP/M 2.x user, change this byte
- ;to the user number you want [UNUSED].BAD to
- ;reside in. If you want it in the default
- ;user, then leave it 0FFH. CP/M 1.4 users
- ;can ignore this byte altogether.
- ;
- BADUSR: DB 0FFH ;USER # WHERE [UNUSED.BAD] GOES
- ;0FFH = DEFAULT USER
- ;
- START: LXI SP,NEWSTK ;MAKE NEW STACK
- CALL START2 ;GO PRINT SIGNON
- DB CR,LF,'FINDBAD - ver 5.4'
- DB CR,LF,'Bad sector lockout '
- DB 'program',CR,LF
- DB 'Universal version',CR,LF
- DB CR,LF,'Type CTL-C to abort',CR,LF,'$'
- ;
- START2: POP D ;GET MSG ADRS
- MVI C,9 ;BDOS PRINT BUFFER FUNCTION
- CALL BDOS ;PRINT SIGN-ON MSG
- 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
- CALL DECOUT ;OOPS..HAD SOME BAD ONES, REPORT
- JMP PMSG2
- ;
- PMSG1: MVI C,9 ;BDOS PRINT BUFFER FUNCTION
- CALL BDOS
- ;
- PMSG2: LXI D,ENDMSG ;REST OF EXIT MESSAGE
- ;
- PMSG: MVI C,9
- CALL BDOS
- ;
- IF TEST
- MVI A,TAB ;GET A TAB
- CALL TYPE ;PRINT IT
- LHLD SECCNT ;GET NUMBER OF SECTORS READ
- CALL DECOUT ;PRINT IT
- LXI D,SECMSG ;POINT TO MESSAGE
- MVI C,9 ;BDOS PRINT BUFFER FUNCTION
- CALL BDOS ;PRINT IT
- ENDIF ;TEST
- ;
- JMP BASE ;EXIT TO CP/M WARM BOOT
- ;
- ;Get actual address of BIOS routines
- ;
- SETUP: LHLD BASE+1 ;GET BASE ADDRESS OF BIOS VECTORS
- ;
- ;WARNING...Program modification takes place here...do not change.
- ;
- 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 "SETSEC"
- DAD D
- SHLD SETSEC+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 SECTRAN
- DAD D
- SHLD SECTRN+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 SECTRAN
- LHLD BDOS+1 ;SET UP JUMP TO 1.4 SECTRAN
- MVI L,0
- DAD D
- SHLD SECTRN+1
- ;
- ;Check for drive specification
- ;
- GDRIV: LDA FCB ;GET DRIVE NAME
- 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
- MOV C,A
- ;
- GD2: LDA VER2FL ;IF CP/M VERSION 2.x
- ORA A
- JNZ GD3 ; SELDSK WILL RETURN SEL ERR
- ;
- ;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 reason for this:
- ;
- ; The BIOS call is necessary in order to
- ; get the necessary pointer back from CP/M
- ; (2.x) to find the sector translate table.
- ; The BDOS call is necessary to keep CP/M
- ; in step with the BIOS...we may later
- ; have to create a [UNUSED].BAD file, and
- ; CP/M must know which drive we are using.
- ; (RGF)
- ;
- SETDSK: CALL $-$ ;DIRECT BIOS VEC FILLED IN AT INIT
- 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 SECTOR TABLE PNTR
- INX H
- MOV D,M
- INX H
- XCHG
- SHLD SECTBL ;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
- CPI 0FFH ;IF IT IS 0FFH, THEN RETURN
- RZ
- MOV E,A ;BDOS CALL NEEDS USER # IN E
- MVI C,32 ;GET/SET USER CODE
- CALL BDOS
- RET
- ;
- ;Look for bad blocks
- ;
- FINDB: LDA SYSTST
- ORA A
- JZ DODIR ;JUMP IF NO SYS TRACKS TO BE TESTED
- CALL CHKSYS ;CHECK FOR BAD BLOCKS ON TRACK 0 AND 1
- ;
- DODIR: CALL CHKDIR ;CHECK FOR BAD BLOCKS IN DIRECTORY
- CALL TELL1
- DB CR,LF,'Testing data area...',CR,LF,'$'
- ;
- TELL1: POP D
- MVI C,9 ;BDOS PRINT STRING FUNCTION
- CALL BDOS
- 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 SECTORS
- 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: CALL CHSY1 ;PRINT MESSAGE
- DB CR,LF,'Testing system tracks...',CR,LF,'$'
- ;
- CHSY1: POP D
- MVI C,9 ;PRINT STRING FUNCTION
- CALL BDOS
- LXI H,0 ;SET TRACK 0, SECTOR 1
- SHLD TRACK
- INX H
- SHLD SECTOR
- ;
- CHKSY1: CALL READS ;READ A SECTOR
- JNZ SYSERR ;NOTIFY, IF BAD BLOCKS HERE
- LHLD SYSTRK ;SET UP (TRACK-SYSTRK)
- XCHG
- LHLD TRACK
- CALL SUBDE ;DO THE SUBTRACT
- JC CHKSY1 ;LOOP WHILE TRACK < SYSTRK
- RET ;RETURN FROM "CHKSYS"
- ;
- SYSERR: LXI D,ERMSG5 ;SAY NO GO, AND BAIL OUT
- MVI C,9 ;BDOS PRINT BUFFER FUNCTION
- CALL BDOS
- RET ;RETURN FROM "SYSERR"
- ;
- ;Check for bad blocks in directory area
- ;
- CHKDIR: CALL CHKD1
- DB CR,LF,'Testing directory area...',CR,LF,'$'
- ;
- CHKD1: POP D
- MVI C,9 ;BDOS PRINT STRING FUNCTION
- CALL BDOS
- LXI B,0 ;START AT BLOCK 0
- ;
- CHKDI1: CALL READB ;READ A BLOCK
- JNZ ERROR6 ;IF BAD, INDICATE 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 sectors in block, and return zero flag set if none bad
- ;
- READB: CALL CNVRTB ;CONVERT TO TRACK/SECTOR IN H&L REGS.
- LDA BLM
- INR A ;NUMBER OF SECTORS/BLOCK
- MOV D,A ; IN D REG
- ;
- READBA: PUSH D
- CALL READS ;READ SKEWED SECTOR
- POP D
- RNZ ;ERROR IF NOT ZERO...
- DCR D ;DEBUMP SECTOR/BLOCK
- JNZ READBA ;DO NEXT, IF NOT FINISHED
- RET ;RETURN FROM "READBA"
- ;
- ;Convert block number to track and skewed sector 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 ; SECTOR NUMBER
- XCHG ;REL SECTOR # INTO DE
- LHLD SPT ;SECTORS PER TRACK FROM DPB
- CALL NEG ;FASTER TO DAD THAN CALL SUBDE
- XCHG
- LXI B,0 ;INITIALIZE QUOTIENT
- ;
- ;Divide by number of sectors
- ; quotient = track
- ; mod = sector
- ;
- DIVLP: INX B ;DIRTY DIVISION
- DAD D
- JC DIVLP
- DCX B ;FIXUP LAST
- XCHG
- LHLD SPT
- DAD D
- INX H
- SHLD SECTOR ;NOW HAVE LOGICAL SECTOR
- LHLD SYSTRK ;BUT BEFORE WE HAVE TRACK #,
- DAD B ; WE HAVE TO ADD SYS TRACK OFFSET
- SHLD TRACK
- POP B ;THIS WAS OUR GROUP NUMBER
- RET
- ;
- ;READS reads a logical sector (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 PHYSEC ;GET PHYSICAL SECTOR
- MOV B,H ;INTO BC
- MOV C,L
- ;
- SETSEC: CALL $-$ ;ADDRS FILLED IN AT INIT
- ;
- ;QUICK NOTE OF EXPLANATION: This code appears
- ;as if we skipped the SETSEC routine for 1.4
- ;CP/M users. That's not true; in CP/M 1.4, the
- ;call within the LTOP routine to SECTRAN ac-
- ;tually does the set sector, so no need to do
- ;it twice. (RGF)
- ;
- NOTCP2: LHLD TRACK ;NOW SET THE TRACK
- MOV B,H ;CP/M WANTS IT IN BC
- MOV C,L
- ;
- SETTRK: CALL $-$ ;ADDRS FILLED IN AT INIT
- ;
- ;Now do the sector read
- ;
- DREAD: CALL $-$ ;ADDRS FILLED IN AT INIT
- ORA A ;SET FLAGS
- PUSH PSW ;SAVE ERROR FLAG
- ;
- IF TEST
- LHLD SECCNT ;GET SECTOR COUNT
- INX H ;ADD ONE
- SHLD SECCNT ;SAVE NEW COUNT
- ENDIF ;TEST
- ;
- LHLD SECTOR ;GET LOGICAL SECTOR #
- INX H ;WE WANT TO INCREMENT TO NEXT
- XCHG ;BUT FIRST...CHECK OVERFLOW
- LHLD SPT ; BY DOING (SECPERTRK-SECTOR)
- CALL SUBDE ;DO THE SUBTRACTION
- XCHG
- JNC NOOVF ;JUMP IF NOT SECTOR>SECPERTRK
- ;
- ;Sector overflow...bump track number, reset sector
- ;
- LHLD TRACK
- INX H
- SHLD TRACK
- MVI A,'*' ;TELL CONSOLE ANOTHER TRACK DONE
- CALL TYPE
- CALL STOP ;SEE IF CONSOLE WANTS TO QUIT
- LXI H,1 ;NEW SECTOR NUMBER ON NEXT TRACK
- ;
- NOOVF: SHLD SECTOR ;PUT SECTOR AWAY
- POP PSW ;GET BACK ERROR FLAGS
- POP B ;RESTORE GROUP NUMBER
- RET
- ;
- ;Convert logical sector # to physical
- ;
- LTOP: LHLD SECTBL ;SET UP PARAMETERS
- XCHG ; FOR CALL TO SECTRAN
- LHLD SECTOR
- MOV B,H
- MOV C,L
- DCX B ;ALWAYS CALL SECTRAN W/ZERO-REL SEC #
- ;
- SECT1: CALL SECTRN ;DO THE SECTOR TRANSLATION
- LDA SPT+1 ;CHECK IF BIG TRACKS
- ORA A ;SET FLAGS (TRACKS > 256 SECTORS)
- JNZ LTOP1 ;NO SO SKIP
- MOV H,A ;ZERO OUT UPPER 8 BITS
- ;
- LTOP1: SHLD PHYSEC ;PUT AWAY PHYSICAL SECTOR
- RET
- ;
- ;Sector translation vector
- ;
- SECTRN: JMP $-$ ;FILLED IN AT INIT
- ;
- ;Put bad block in bad block list
- ;
- SETBD: PUSH B
- CALL SETBD1
- DB CR,LF,'Bad block: $'
- ;
- SETBD1: POP D ;RETRIEVE ARG
- MVI C,9 ;PRINT STRING
- CALL BDOS
- POP B ;GET BACK BLOCK NUMBER
- MOV A,B
- CALL HEXO ;PRINT IN HEX
- MOV A,C
- CALL HEXO
- CALL CRLF
- LHLD DMCNT ;GET NUMBER OF SECTORS
- LDA BLM ;GET BLOCK SHIFT VALUE
- INR A ;MAKES SECTOR/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 SECTORS
- 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 THROUGH HERE
- RET ;RETURN FROM "SETBD"
- ;
- ;Eliminate any previous [UNUSED].BAD entries
- ;
- ERAB: LXI D,BFCB ;POINT TO BAD FCB
- MVI C,19 ;BDOS DELETE FILE FUNCTION
- CALL BDOS
- 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...CAN'T 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 ;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 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'S IN L (CAN'T BE >16)
- STA BLKEXT ;SETDME WILL NEED THIS LATER
- POP H ;GET BACK REC/EXT
- ;
- SET1: XCHG ;NOW HAVE REC/EXTENT IN DE
- LHLD DMCNT ;COUNT OF BAD SECTORS
- ;
- 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 (SPL CASE)
- MOV H,B ;RESTORE RESULT TO HL
- MOV L,C
- PUSH H ;SAVE TOTAL
- PUSH D ;AND SECTORS/EXTENT
- XCHG
- CALL SETDME ;PUT AWAY ONE EXTENT
- XCHG
- SHLD DMPTR ;PUT BACK NEW DM POINTER
- POP D ;GET BACK SECTORS/EXTENT
- POP H ;AND COUNT OF BAD SECTORS
- 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 W/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 sectors/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 REC COUNT
- ;
- ;Divide record count by 128 to get the number
- ;of logical extents to put in the EX field
- ;
- MVI B,0 ;INIT QUOTIENT
- LXI D,-128 ;-DIVISOR
- MOV A,H ;TEST FOR SPL 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 RC 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,'+++ Warning...System tracks'
- DB ' bad +++',CR,LF,CR,LF,'$'
- ;
- ERROR6: LXI D,ERMSG6 ;OOPS...CLOBBERED DIRECTORY
- JMP PMSG
- ;
- ERMSG6: DB CR,LF,'Bad directory area, try reformatting$'
- ;
- ERROR7: LXI D,ERMSG7 ;SAY NO GO, AND BAIL OUT
- JMP PMSG
- ;
- ERMSG7: DB CR,LF,'Can''t create [UNUSED].BAD$'
- ;
- ;
- ;==== SUBROUTINES ====
- ;
- ;Decimal output routine
- ;
- DECOUT: PUSH B
- PUSH D
- PUSH H
- LXI B,-10
- LXI D,-1
- ;
- DECOU2: DAD B
- INX D
- JC DECOU2
- LXI B,10
- DAD B
- XCHG
- MOV A,H
- ORA L
- CNZ DECOUT
- MOV A,E
- ADI '0'
- CALL TYPE
- POP H
- POP D
- POP B
- RET
- ;
- ;Carriage-return/line-feed to 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
- ;
- ;Subroutine to test console for control-c abort
- ;
- STOP: LHLD 1 ;FIND BIOS IN MEMORY
- MVI L,6 ;OFFSET TO CONSOLE STATUS
- CALL GOHL ;THANKS TO BRUCE RATOFF FOR THIS TRICK
- ORA A ;TEST FLAGS ON ZERO
- RZ ;RETURN IF NO CHAR
- LHLD 1 ;NOW FIND CONSOLE INPUT
- MVI L,9 ;OFFSET FOR CONIN
- CALL GOHL
- CPI 'C'-40H ;IS IT CONTROL-C?
- RNZ ;RETURN IF NOT
- LXI D,ABORTM ;EXIT WITH MESSAGE
- MVI C,9 ;PRINT MESSAGE FUNCTION
- CALL BDOS ;SAY GOODBYE
- JMP 0 ;THEN LEAVE
- ;
- ABORTM: DB CR,LF
- DB 'Test aborted by control-C'
- DB CR,LF,'$'
- ;
- ;A thing to allow a call to @HL
- ;
- GOHL: PCHL
- ;
- ;Zero all of memory to hold DM values
- ;
- ZMEM: LHLD BDOS+1 ;GET TOP-OF-MEM 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
- ;
- ;Subtract DE from HL
- ;
- SUBDE: MOV A,L
- SUB E
- MOV L,A
- MOV A,H
- SBB D
- MOV H,A
- RET
- ;
- ;Negate HL
- ;
- NEG: MOV A,L
- CMA
- MOV L,A
- MOV A,H
- CMA
- MOV H,A
- INX H
- RET
- ;
- ;Move from (HL) to (DE)
- ;Count in BC
- ;
- MOVE: MOV A,M
- STAX D
- INX H
- INX D
- DCR B
- JNZ MOVE
- RET
- ;
- ;Print byte in accumulator in hex
- ;
- HEXO: PUSH PSW ;SAVE FOR SECOND HALF
- RRC ;MOVE INTO POSITION
- RRC
- RRC
- RRC
- CALL NYBBLE ;PRINT MS NYBBLE
- POP PSW
- ;
- NYBBLE: ANI 0FH ;LO NYBBLE ONLY
- ADI 90H
- DAA
- ACI 40H
- DAA
- JMP TYPE ;PRINT IN HEX
- ;
- ;Subroutine to determine the number
- ;of groups reserved for the directory
- ;
- GETDIR: MVI C,0 ;INIT BIT COUNT
- LDA AL0 ;READ DIR GRP BITS
- CALL COLECT ;COLLECT COUNT OF DIR GRPS..
- LDA AL1 ;..IN REGISTER C
- CALL COLECT
- MOV L,C
- MVI H,0 ;BC NOW HAS A DEFAULT START GRP #
- SHLD DIRBKS ;SAVE FOR LATER
- 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
- ;
- ;Shift HL right one place
- ;
- ROTRHL: ORA A ;CLEAR CARRY
- MOV A,H ;GET HI BYTE
- RAR ;SHIFT RIGHT
- MOV H,A ;PUT BACK
- MOV A,L ;GET LO
- RAR ;SHIFT WITH CARRY
- MOV L,A ;PUT BACK
- 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 PARM 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
- ;
- ;--------------------------------------------------
- ;The disk parameter block
- ;is moved here from CP/M
- ;
- DPB EQU $ ;DISK PARAMETER BLOCK (COPY)
- ;
- SPT: DS 2 ;SECTORS 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
- ;
- ;End of disk parameter block
- ;
- DPBLEN EQU $-DPB ;LENGTH OF DISK PARM BLOCK
- ;
- ;--------------------------------------------------
- BLKEXT: DB 0 ;BLOCKS PER EXTENT
- DIRBKS: DW 0 ;CALCULATED # OF DIR BLOCKS
- VER2FL: DB 0 ;VERSION 2.X FLAG
- ;
- BFCB: DB 0,'[UNUSED]BAD',0,0,0,0
- FCBDM: DS 17
- ;
- NOMSG: DB 'No$'
- ENDMSG: DB ' bad blocks found',CR,LF,'$'
- ;
- BADBKS: DW 0 ;COUNT OF BAD BLOCKS
- SECTOR: DW 0 ;CURRENT SECTOR NUMBER
- TRACK: DW 0 ;CURRENT TRACK NUMBER
- PHYSEC: DW 0 ;CURRENT PHYSICAL SECTOR NUMBER
- SECTBL: DW 0 ;SECTOR SKEW TABLE POINTER
- ;
- EXTNUM: DB 0FFH ;USED FOR UPDATING EXTENT NUMBER
- DMCNT: DW 0 ;NUMBER OF BAD SECTORS
- DMPTR: DW DM ;POINTER TO NEXT BLOCK ID
- ;
- SECMSG: DB ' total sectors read',CR,LF,'$'
- ;
- SECCNT: DW 0 ;NUMBER OF SECTORS READ
- ;
- DS 64 ;ROOM FOR 32 LEVEL STACK
- NEWSTK EQU $ ;OUR STACK
- DM EQU $ ;BAD BLOCK ALLOCATION MAP
- ;
- END
-