; FINDBAD.ASM ver. 4.2 ; (revised 02/05/81) ; ;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. ; ;This program now supports the following disk drives: ; ; - STANDARD 8" SINGLE DENSITY ; - MICROPOLIS MOD II ; - MICROMATION DOUBLE DENSITY ; - DIGITAL MICROSYSTEMS FDC3 DBL DENS ; - IMSAI DOUBLE DENSITY (IMDOS) ; - DISCUS 2D (SINGLE SIDED) 256/512/1024 BYTE SECTORS ; - NATIONAL MULTIPLEX DD 1/2 SIDED 256/512 BYTE SECTORS ; - HEATH H-17 5.25" SINGLE SIDED SINGLE DENSITY ; - ICOM MICROFLOPPY 5.25" SINGLE SIDED, SINGLE DENSITY ; - JADE DOUBLE DENSITY/SINGLE SIDED ; - NORTH STAR HORIZON DOUBLE DENSITY, SINGLE OR DOUBLE SIDED ; ;As presently set up, this program will perform properly on an ;8" single-density soft-sectored disk recorded in standard IBM ;format (i.e., 77 tracks, 26 sectors/track, 243 blocks/disk, 8 ;sectors/block, 128 bytes/sector). If your disk is not an IBM ;8" standard, then you must set the conditional assembly ;switches to one of the defined disk systems or modify the ;existing disk parameter definitions according to the ;guidelines established in this documentation. See notes ;below concerning 'TEST' conditional assembly option. ; ;NOTE: If you want to add conditional assembly for other disk ;systems, or otherwise 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) ; ;Revisions/modifications: (listed in reverse order ;to minimize reading time) ; ;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 ;of 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 toatal 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 SYSTEM is set TRUE, 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 switches (only one should be true) STDDRV EQU TRUE ;TRUE IF STANDARD 8" SINGLE DENSITY DRIVE MICROP EQU FALSE ;TRUE IF MICROPOLIS MOD II MMDBL EQU FALSE ;TRUE IF MICROMATION DOUBLE DENSITY DIGDBL EQU FALSE ;TRUE IF DIGITAL MICROSYSTEMS FDC3 DBL DENS IMDOS EQU FALSE ;TRUE IF IMSAI DOUBLE DENSITY DJ256S EQU FALSE ;TRUE IF MORROW 2D/SS (256-BYTE SECTOR) DJ512S EQU FALSE ;TRUE IF MORROW 2D/SS (512-BYTE SECTOR) DJ1024 EQU FALSE ;TRUE IF MORROW 2D/SS (1024-BYTE SECTOR) NM256 EQU FALSE ;TRUE IF NATMUX 2D (256-BYTE SECTOR) NM512 EQU FALSE ;TRUE IF NATMUX 2D (512-BYTE SECTOR) H17 EQU FALSE ;TRUE IF HEATH H-17 5.25" SGL. DENS. ICOM EQU FALSE ;TRUE IF ICOM MICROFLOPPY JADEDD EQU FALSE ;TRUE IF JADE DD DISK CONTROLLER HORIZON EQU FALSE ;TRUE IF NORTH STAR HORIZON ;****************************************************************** ; ;Conditional assembly switch for double-sided drives ;(presently supported for National Multiplex and Horizon only) ; SIDES2 EQU FALSE ;TRUE FOR NATMUX D3S/D4S OR HORIZON DOUBLE SIDED ONLY ; ;****************************************************************** ; ;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 disk system parameters ; IF STDDRV SYSTEM EQU TRUE ;TRUE IF CHECK SYSTEM TRACKS WANTED TRACKS EQU 77 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 26 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 243 ;MAX NUMBER OF BLOCKS (including directory) BLOCK EQU 8 ;NUMBER OF SECTORS/BLOCK DGROUP EQU FALSE ;TRUE IF 2k GROUP SIZE ENDIF ;STDDRV ; IF MICROP SYSTEM EQU TRUE ;TRUE IF CHECK SYSTEM TRACKS WANTED TRACKS EQU 77 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 32 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 150 ;MAX NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;MICROP ; IF MMDBL SYSTEM EQU FALSE ;FALSE IF CHECK SYSTEM TRACKS NOT WANTED TRACKS EQU 77 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 52 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK FOR DATA MAXB EQU 243 ;MAXIMUM NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;MMDBL ; IF DIGDBL SYSTEM EQU FALSE ;FALSE IF CHECK SYSTEM TRACKS NOT WANTED TRACKS EQU 77 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 58 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 256 ;MAXIMUM NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;DIGDBL ; IF IMDOS SYSTEM EQU FALSE ;TRUE IF CHECK SYSTEM TRACKS WANTED TRACKS EQU 77 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 58 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 271 ;MAX NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;IMDOS ; IF DJ256S OR NM256 AND NOT SIDES2 SYSTEM EQU FALSE ;FALSE IF CHECK SYSTEM TRACKS NOT WANTED TRACKS EQU 77 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 52 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 243 ;MAXIMUM NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;MORROW DJ256S OR NM256 ; IF DJ512S OR NM512 AND NOT SIDES2 SYSTEM EQU FALSE ;FALSE IF CHECK SYSTEM TRACKS NOT WANTED TRACKS EQU 77 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 60 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 281 ;MAXIMUM NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;MORROW DJ512S OR NM512 ; IF DJ1024 SYSTEM EQU FALSE ;FALSE IF CHECK SYSTEM TRACKS NOT WANTED TRACKS EQU 77 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 64 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 300 ;MAXIMUM NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;MORROW DJ1024 ; IF NM256 AND SIDES2 SYSTEM EQU FALSE ;FALSE IF CHECK SYSTEM TRACKS NOT WANTED TRACKS EQU 77 * 2 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 52 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 487 ;MAXIMUM NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;MORROW DJ256S OR NM256 ; IF NM512 AND SIDES2 SYSTEM EQU FALSE ;FALSE IF CHECK SYSTEM TRACKS NOT WANTED TRACKS EQU 77 * 2 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 60 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 281 * 2 ;MAXIMUM NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;MORROW DJ512S OR NM512 ; IF H17 SYSTEM EQU TRUE ;FALSE IF CHECK SYSTEM TRACKS NOT WANTED TRACKS EQU 40 ;NUMBER OF TRACKS/DISK SECTS EQU 20 ;NUMBER OF SECTS/TRACK DBASE EQU 3 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 1 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 92 ;MAXIMUM NUMBER OF BLOCKS (including directory) BLOCK EQU 8 ;NUMBER OF SECTORS/BLOCK DGROUP EQU FALSE ;TRUE IF 2k GROUP SIZE ENDIF ;H17 ; IF ICOM SYSTEM EQU TRUE ;FALSE IF CHECK SYSTEM TRACKS NOT WANTED TRACKS EQU 35 ;NUMBER OF TRACKS/DISK SECTS EQU 18 ;NUMBER OF SECTS/TRACK DBASE EQU 3 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 72 ;MAXIMUM NUMBER OF BLOCKS (including directory) BLOCK EQU 8 ;NUMBER OF SECTORS/BLOCK DGROUP EQU FALSE ;TRUE IF 2k GROUP SIZE ENDIF ;ICOM ; IF JADEDD SYSTEM EQU FALSE ;FALSE IF CHECK SYSTEM TRACKS NOT WANTED TRACKS EQU 77 ;NUMBER OF TRACKS/DISK SECTS EQU 50 ;NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 233 ;MAXIMUM NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;JADEDD ; IF HORIZON AND NOT SIDES2 SYSTEM EQU TRUE ;TRUE IF CHECK SYSTEM TRACKS WANTED TRACKS EQU 35 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 40 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 165 ;MAX NUMBER OF BLOCKS (including directory) BLOCK EQU 8 ;NUMBER OF SECTORS/BLOCK DGROUP EQU FALSE ;TRUE IF 2k GROUP SIZE ENDIF ;HORIZON ; IF HORIZON AND SIDES2 SYSTEM EQU TRUE ;TRUE IF CHECK SYSTEM TRACKS WANTED TRACKS EQU 70 ;TOTAL NUMBER OF TRACKS/DISK SECTS EQU 40 ;TOTAL NUMBER OF SECTORS/TRACK DBASE EQU 2 ;DIRECTORY & DATA AREA STARTING TRACK NUMBER BBASE EQU 2 ;FIRST BLOCK NUMBER FOR DATA MAXB EQU 170 ;MAX NUMBER OF BLOCKS (including directory) BLOCK EQU 16 ;NUMBER OF SECTORS/BLOCK DGROUP EQU TRUE ;TRUE IF 2k GROUP SIZE ENDIF ;HORIZON ; ;Define ASCII characters used ; CR EQU 0DH ;CARRIAGE RETURN CHARACTER LF EQU 0AH ;LINE FEED CHARACTER TAB EQU 09H ;TAB CHARACTER ; ; ORG BASE+100H ; START: LXI SP,NEWSTK ;MAKE NEW STACK ; IF IMDOS XRA A OUT 0FFH ;CLEAR FRONT PANEL ENDIF ;IMDOS ; LXI D,IDMSG ;IDENT MESSAGE CALL START2 ;GO PRINT IT ; IDMSG: DB CR,LF,'FINDBAD - ver 4.2' DB CR,LF,'Bad sector lockout ' DB 'program',CR,LF ; IF STDDRV DB 'For single-density 8"' ENDIF ; IF MICROP DB 'For Micropolis Mod II' ENDIF ; IF MMDBL DB 'For Micromation double-density' ENDIF ; IF DIGDBL DB 'For Digital Microsystems',CR,LF DB 'FDC3 cntlr dbl dens' ENDIF ; IF IMDOS DB 'For IMSAI IMDOS double-density' ENDIF ; IF DJ256S DB 'For Discus 2-dens./1-side/256-byte sectors' ENDIF ; IF DJ512S DB 'For Discus 2-dens./1-side/512-byte sectors' ENDIF ; IF DJ1024 DB 'For Discus 2-dens./1-side/1024-byte sectors' ENDIF ; IF NM256 OR NM512 DB 'For National Multiplex double density' ENDIF ; IF HORIZON DB 'For North Star Horizon - 5.25" drives, double density,' ENDIF ; IF SIDES2 AND (NM256 OR NM512 OR HORIZON) DB ' double sided' ENDIF ; IF NOT SIDES2 AND (NM256 OR NM512 OR HORIZON) DB ' single sided' ENDIF ; IF NM256 DB ' 256-byte sectors' ENDIF ; IF NM512 DB ' 512-byte sectors' ENDIF ; IF H17 DB 'For Heath H-17 5.25" drives' ENDIF ; IF ICOM DB 'For ICOM Microfloppy' ENDIF ; IF JADEDD DB 'For Jade Double Density/Single Sided' ENDIF ; DB ' only.',CR,LF,'$' ; START2 POP D ;GET MSG ADRS MVI C,9 ;BDOS PRINT BUFFER FUNCTION CALL BDOS ;PRINT SIGN-ON MSG CALL IBIOS ;SET BIOS ENTRY, AND CHECK DRIVE CALL FINDB ;ESTABLISH ALL BAD BLOCKS JZ NOBAD ;SAY NO BAD BLOCKS, IF SO CALL OPENB ;OPEN [UNUSED].BAD ALLOCATION CALL SETDM ;FIX DM BYTES IN FCB CALL CLOSEB ;CLOSE [UNUSED].BAD CALL SETNUM ;PUT NUMBER OF BAD BLOCKS IN MESSAGE ; NOBAD: LXI D,ENDMSG ;SAY HOW MANY BAD BLOCKS ; PMSG: MVI C,9 ;BDOS PRINT BUFFER FUNCTION 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 ; IF IMDOS XRA A OUT 0FFH ;CLEAR FRONT PANEL ENDIF ;IMDOS ; JMP BASE ;EXIT TO CP/M WARM BOOT ; ;Get actual address of BIOS routines ; IBIOS: LHLD BASE+1 ;GET BASE ADDRESS OF BIOS VECTORS ; ;WARNING...Program modification takes place here...do not change. ; LXI D,27 ;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 ; ;Check for drive specification ; LDA FCB ;GET DRIVE NAME ORA A ;ZERO? RZ ;YES, NO DRIVE CHANGE REQUIRED CPI 4+1 ;CHECK FOR HIGHEST DRIVE NUMBER JNC ERROR4 DCR A ;BACK OFF FOR CP/M MOV E,A ;MAKE DISK NUMBER MVI C,14 ;BDOS SELECT DISK FUNCTION CALL BDOS RET ;RETURN FROM "IBIOS" ; ;Look for bad blocks ; FINDB: EQU $ ; IF SYSTEM CALL CHKSYS ;CHECK FOR BAD BLOCKS ON TRACK 0 AND 1 ENDIF ;SYSTEM ; CALL CHKDIR ;CHECK FOR BAD BLOCKS IN DIRECTORY CALL ERAB ;ERASE ANY [UNUSED].BAD FILE LXI B,BBASE ;START AT FIRST DATA BLOCK ; FINDBA: CALL READB ;READ THE BLOCK CNZ SETBD ;IF BAD, ADD BLOCK TO LIST INX B ;BUMP TO NEXT BLOCK MOV A,C ;SEE IF MORE TO CHECK CPI MAXB AND 0FFH JNZ FINDBA MOV A,B ;THEN CHECK HI BYTE CPI MAXB SHR 8 JNZ FINDBA ;LOOP TILL DONE LHLD DMCNT ;GET NUMBER OF BAD SECTORS MOV A,H ORA L ;SET ZERO FLAG, IF NO BAD BLOCKS RET ;RETURN FROM "FINDB" ; IF SYSTEM ; ;Check system tracks, notify user if bad, but continue ; CHKSYS: LXI H,1 ;SET TRACK 0, SECTOR 1 ; CHKSY1: CALL READS ;READ A SECTOR JNZ SYSERR ;NOTIFY, IF BAD BLOCKS HERE MOV A,H ;BOTH SYSTEM TRACKS DONE? CPI DBASE JC CHKSY1 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" ; ENDIF ;SYSTEM ; ;Check for bad blocks in directory area ; CHKDIR: 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 MOV A,C ;GET BLOCK NUMBER CPI BBASE ;ALL DONE CHECKING DIRECTORY AREA? JC CHKDI1 ;PRESS ON, IF NOT 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. MVI D,BLOCK ;NUMBER OF SECTORS/BLOCK ; 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 BLOCK NUMBER MOV L,C ;BLOCK NUMBER TO H&L REGS. MOV H,B DAD H ;*2 DAD H ;*4 DAD H ;*8 ; IF DGROUP DAD H ;*16 FOR 2k GROUP SIZE ENDIF ;DGROUP ; LXI D,DBASE*256 ;MAKE BASE TRACK NUMBER LXI B,-SECTS ;DIVIDE BY SECTORS/TRACK ; CNVRTC: MOV A,H ;OVER SECTORS... ORA A JNZ CNVRTT ;...BYE GROUPS? MOV A,L ;OVER SECTORS... CPI SECTS JC CNVRTS ;...AND DOWN TO TRACKS? ; CNVRTT: DAD B ;TAKE AWAY SECTORS INR D ;+1 TO TRACK NUMBER JMP CNVRTC ;...AND GO BACK FOR MORE ; CNVRTS: MOV E,L ;RESIDUAL = SKEWED SECTOR-1 INR E ;BUMP FOR SECTORS 1 TO 32 XCHG ;TRACK/SECTOR IN H&L REGS. POP B ;RECOVER BLOCK NUMBER RET ;RETURN FROM "CNVRTB" ; ;Reads a logical sector (if it can), and returns zero flag set if no error ; READS: PUSH B ;EXILE BLOCK PUSH H ;...AND TRACK/SECTOR CALL LTOP ;CONVERT LOGICAL TO PHYSICAL SECTOR PUSH H ;SAVE SECTOR NUMBER MOV C,H ;TRACK NUMBER IN H REG... ; SETTRK: CALL $-$ ;BIOS SET TRACK (MODIFIED BY IBIOS) POP B ;PUT SECTOR IN C ; SETSEC: CALL $-$ ;BIOS SET SECTOR (MODIFIED BY IBIOS) ; DREAD: CALL $-$ ;BIOS READ SECTOR (MODIFIED BY IBIOS) ORA A ;SET FLAGS FOR POSSIBLE BAD SECTOR ; IF TEST LHLD SECCNT ;GET NUMBER OF SECTORS READ INX H ;INCREMENT SHLD SECCNT ;SAVE NEW NUMBER ENDIF ;TEST ; POP H POP B ;BACK FROM EXILE... PUSH PSW ;SAVE FLAGS INR L ;BUMP FOR NEXT SECTOR MOV A,L CPI SECTS+1 ;TRACK OVERFLOW? JC READSR MVI L,1 ;YUP, RESET SECTOR NUMBER TO 1... INR H ;...AND BUMP TRACK NUMBER ; READSR: POP PSW ;GET FLAGS, TO CHECK IF ERROR ON RETURN RET ;RETURN FROM "READS" ; ;Convert logical to physical sector ; LTOP: XCHG LXI B,LPMAP-1 ;GET BASE OF LOGICAL TO PHYSICAL MAPPING MOV L,E MVI H,0 ;LOGICAL SECTOR OFFSET DAD B ;+ BIAS MOV E,M ;GET PHYSICAL SECTOR XCHG ;PUT H&L REGS. BACK... RET ;RETURN FROM "LTOP" ; ;Logical to physical mapping vectors (sector skew table) ; IF STDDRV LPMAP: DB 01,07,13,19,25,05,11,17,23,03,09,15,21 DB 02,08,14,20,26,06,12,18,24,04,10,16,22 ENDIF ;STDDRV ; IF MICROP LPMAP: DB 01,02,11,12,21,22,31,32,09,10,19,20,29,30,07,08 DB 17,18,27,28,05,06,15,16,25,26,03,04,13,14,23,24 ENDIF ;MICROP ; IF MMDBL LPMAP: DB 01,14,27,40,10,23,36,49,06,19,32,45,02,15,28,41 DB 11,24,37,50,07,20,33,46,03,16,29,42,12,25,38,51 DB 08,21,34,47,04,17,30,43,13,26,39,52,09,22,35,48 DB 05,18,31,44 ENDIF ;MMDBL ; IF DIGDBL LPMAP: DB 01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16 DB 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32 DB 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48 DB 49,50,51,52,53,54,55,56,57,58 ENDIF ;DIGDBL ; IF IMDOS LPMAP: DB 1,8,15,22,29,36,43,50,57,6,13,20,27,34,41,48,55 DB 4,11,18,25,32,39,46,53,2,9,16,23,30,37,44,51,58 DB 7,14,21,28,35,42,49,56,5,12,19,26,33,40,47,54 DB 3,10,17,24,31,38,45,52 ENDIF ;IMDOS ; IF DJ256S LPMAP: DB 01,02,19,20,37,38,03,04,21,22,39,40,05,06,23,24 DB 41,42,07,08,25,26,43,44,09,10,27,28,45,46,11,12 DB 29,30,47,48,13,14,31,32,49,50,15,16,33,34,51,52 DB 17,18,35,36 ENDIF ;DJ256S ; IF NM256 LPMAP: DB 01,02,23,24,45,46,15,16,37,38,07,08,29,30,51,52 DB 21,22,43,44,13,14,35,36,05,06,27,28,49,50,19,20 DB 41,42,11,12,33,34,03,04,25,26,47,48,17,18,39,40 DB 09,10,31,32 ENDIF ;NM256 ; IF DJ512S OR NM512 LPMAP: DB 01,02,03,04,17,18,19,20,33,34,35,36,49,50,51,52 DB 05,06,07,08,21,22,23,24,37,38,39,40,53,54,55,56 DB 09,10,11,12,25,26,27,28,41,42,43,44,57,58,59,60 DB 13,14,15,16,29,30,31,32,45,46,47,48 ENDIF ;DJ512S or NM512 ; IF DJ1024 LPMAP: DB 01,02,03,04,05,06,07,08,25,26,27,28,29,30,31,32 DB 49,50,51,52,53,54,55,56,09,10,11,12,13,14,15,16 DB 33,34,35,36,37,38,39,40,57,58,59,60,61,62,63,64 DB 17,18,19,20,21,22,23,24,41,42,43,44,45,46,47,48 ENDIF ;DJ1024 ; IF H17 LPMAP: DB 01,02,09,10,17,18,05,06,13,14,03,04,11,12,19,20 DB 07,08,15,16 ENDIF ;H17 ; IF ICOM LPMAP: DB 01,05,09,13,17,04,08,12,16,03,07,11,15,02,06,10 DB 14,18 ENDIF ;ICOM ; IF JADEDD LPMAP: DB 01,11,21,31,41,02,12,22,32,42,03,13,23,33,43 DB 04,14,24,34,44,05,15,25,35,45,06,16,26,36,46 DB 07,17,27,37,47,08,18,28,38,48,09,19,29,39,49 DB 10,20,30,40,50 ENDIF ;JADEDD ; IF HORIZON LPMAP: DB 01,02,03,04,21,22,23,24 DB 05,06,07,08,25,26,27,28 DB 09,10,11,12,29,30,31,32 DB 13,14,15,16,33,34,35,36 DB 17,18,19,20,37,38,39,40 ENDIF ;HORIZON ; ;Put bad block in bad block list ; SETBD: LHLD DMCNT ;GET NUMBER OF SECTORS LXI D,BLOCK DAD D ;BUMP BY NUMBER IN THIS BLOCK SHLD DMCNT ;UPDATE NUMBER OF SECTORS LHLD DMPTR ;GET POINTER INTO DM MOV M,C ;...AND PUT BAD BLOCK NUMBER INX H ;BUMP TO NEXT AVAILABLE EXTENT ; IF IMDOS OR DJ512S OR DJ1024 OR NM256 OR NM512 MOV M,B ;PUT IN 2ND BYTE FOR IMDOS OR DJ512/1024 INX H ;POINT TO NEXT AVAILABLE EXTENT ENDIF ;IMDOS OR DJ512S OR DJ1024 OR NM256 OR NM512 ; 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 PUSH D ;SAVE IT... MVI C,22 ;BDOS MAKE FILE FUNCTION CALL BDOS POP D ;RECOVER BAD FCB POINTER MVI C,15 ;BDOS OPEN 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 ; ;Move bad area DM to BFCB ; SETDM: LXI H,DM ;GET DM SHLD DMPTR ;SAVE AS NEW POINTER LHLD DMCNT ;GET THE COUNT ; SETDM0: MOV A,H ORA A JNZ GOBIG MOV A,L CPI 129 ;ALL BYTES MOVED? JC SETDME ; GOBIG: LXI D,-128 DAD D PUSH H MVI A,128 CALL SETDME XCHG SHLD DMPTR CALL CLOSEB ;CLOSE OLD EXTENT LDA EXTNUM ;GET OLD EXTENT NUMBER INR A ;INCREMENT IT STA EXTNUM ;SAVE NEW EXTENT NUMBER STA BFCB+12 ;PUT NEW EXTENT NUMBER INTO OUR FCB CALL OPENB ;OPEN NEW EXTENT POP H JMP SETDM0 ; SETDME: STA BFCB+15 ;PUT RC IN PLACE IF NOT DGROUP MVI B,16 ;NUMBER OF BYTES TO MOVE ENDIF ;NOT DGROUP ; IF DGROUP MVI B,8 ;NUMBER OF BYTES TO MOVE ENDIF ;DGROUP ; LHLD DMPTR ;GET BAD DMAP POINTER XCHG ;TO DE LXI H,BFCB+16 ;POINT AT OUR FCB ; SETDML: EQU $ ; IF NOT IMDOS LDAX D ;GET BYTE FROM DMAP MOV M,A ;MOVE TO OUR FCB INX D ;INCREMENT DMAP POINTER INX H ;INCREMENT OUR FCB POINTER ENDIF ;NOT IMDOS (1 BYTE GROUP #) ; IF DJ512S OR DJ1024 OR NM256 OR NM512 LDAX D ;GET SECOND BYTE FROM DMAP MOV M,A ;MOVE TO OUR FCB INX D ;INCREMENT DMAP POINTER INX H ;INCREMENT OUR FCB POINTER ENDIF ;DJ512S OR DJ1024 OR NMXXXDS (2 BYTE GROUP #) ; IF IMDOS LDAX D ;GET FIRST (LO ORDER) BYTE FROM DMAP MOV C,A ;SAVE IT IN C INX D ;INCREMENT DMAP POINTER LDAX D ;THEN GET SECOND (HI ORDER) BYTE MOV M,A ;STORE HI BYTE FIRST INX H ;INCREMENT FCB POINTER MOV M,C ;THEN LO BYTE FOR 16-BIT POINTER INX H ;INCREMENT OUR FCB POINTER ENDIF ;IMDOS (2 BYTE GROUP #) ; DCR B ;ONE LESS BYTE TO MOVE JNZ SETDML ;NOT DONE, GO MOVE MORE RET ;ELSE RETURN FROM "SETDM" ; 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" ; ;Convert number of blocks to decimal ASCII, for printing ; SETNUM: LHLD DMCNT ;GET NUMBER OF SECTORS DAD H ;*2 DAD H ;*4 DAD H ;*8 DAD H ;*16 ; IF NOT DGROUP DAD H ;*32 FOR 1k GROUP SIZE ENDIF ; ;H reg now equals number of blocks LXI D,255 DAD D ;ROUND UP MOV L,H MVI H,0 ;NOW HL=NUMBER OF BLOCKS LXI D,NUMBAD CALL DCNV RET ;RETURN FROM "SETNUM" ; DCNV: MVI B,' ' ;SET FOR PLUS MOV A,H ORA A JP H3 MVI B,'-' MOV A,L CMA INR A MOV L,A MOV A,H CMA JNZ H2 INR A ; H2: MOV H,A ; H3: SHLD DCNVHL MVI A,' ' STAX D MOV A,B STA DCNVPM XCHG SHLD DCNVAD XRA A STA DCNVFL LXI B,-10000 CALL DFL8 CALL DSTC LXI B,-1000 CALL DFL8 CALL DSTC LXI B,-100 CALL DFL8 CALL DSTC LXI B,-10 CALL DFL8 CALL DSTC LDA DCNVHL ORI '0' MOV E,A ; DSTC: LHLD DCNVAD LDA DCNVFL ORA A JNZ DSTC3 ; DSTC1: ADD E STA DCNVFL JNZ DSTC2 MVI A,' ' JMP DSTC4 ; DSTC2: LDA DCNVPM MOV M,A ; DSTC3: MVI A,'0' ORA E ; DSTC4: INX H MOV M,A SHLD DCNVAD RET ;RETURN FROM "SETDM" ; DCNVFL: DB 0 DCNVHL: DW 0 DCNVAD: DW 0 DCNVPM: DB 0 ; DFL8: LHLD DCNVHL MVI E,0 ; DF1: DAD B MOV A,H ORA A RM INR E SHLD DCNVHL JMP DF1 ; BFCB: DB 0,'[UNUSED]BAD',0,0,0,0 DS 17 ; ENDMSG: DB CR,LF,' ' ; NUMBAD: DB ' No' DB ' bad blocks found',CR,LF,'$' ; EXTNUM: DB 0 ;USED IF MORE THAN 16 BAD BLOCKS DMCNT: DW 0 ;NUMBER OF BAD SECTORS DMPTR: DW DM ;POINTER TO NEXT BLOCK ID ; ;Allocation map for bad blocks ; DM: DB 0,0,0,0,0,0,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,0 DB 0,0,0,0,0,0,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,0 DB 0,0,0,0,0,0,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,0 DB 0,0,0,0,0,0,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,0 DB 0,0,0,0,0,0,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,0 DB 0,0,0,0,0,0,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,0 DB 0,0,0,0,0,0,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,0 DB 0,0,0,0,0,0,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,0 ; ;Error messages ; ERROR4: LXI D,ERMSG4 ;SAY NO GO, AND BAIL OUT JMP PMSG ; ERMSG4: DB CR,LF,'Only drives A to D allowed$' ; IF SYSTEM ERMSG5: DB CR,LF,'Warning...System tracks bad$' ENDIF ;SYSTEM ; 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$' ; IF TEST ; ;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 ; 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 ; SECMSG: DB ' total sectors read',CR,LF,'$' ; SECCNT: DW 0 ;NUMBER OF SECTORS READ ; ENDIF ;TEST ; DS 64 ;ROOM FOR 32 LEVEL STACK NEWSTK EQU $ ;OUR STACK ; END