; ; FINDBAD.ASM Ver. 3.7 ; revised 9/22/80 ; ;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 ; ;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 add conditional assembly for other disk systems, ;or otherwise update this program, 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) ; ;08/06/80 ADDED COMMENTS AND CRUNCHED SOME CODE. ; KELLY SMITH. 805-527-9321 (Modem, 300 Baud) ; 805-527-0518 (Verbal) ; ;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/27/80 FIX MISSING CONDITIONAL ASSEMBLY IN FINDB ROUTINE. ; PUT VERSION NUMBER IN SIGN-ON MESSAGE. (KBP) ; ;08/30/80 ADDED CONDITIONAL ASSEMBLY FOR MICROMATION ; DOUBLE DENSITY FORMAT. (CHARLES H. STROM) ; ;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 ERASED BEFORE DATA AREA IS ; CHECKED. (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) ; ;09/01/80 CHANGED EQUATES SO THAT PARAMETERS ARE AUTOMATICALLY ; SET FOR EACH DISK SYSTEM CONDITIONAL ASSEMBLY (KBP) ; ;09/02/80 ADDED IMDOS DOUBLE-DENSITY EQUATES & MODIFIED FOR ; MORE THAN 256 BLOCKS PER DISK. (AL JEWER) ; ;09/08/80 FIXED SEVERAL ERRORS IN AL JEWER'S MODS. CHANGED ; RETURN TO CP/M TO WARM BOOT SO BITMAP IN MEMORY WILL ; WILL BE PROPERLY UPDATED. ADDED CONDITIONAL ASSEMBLY ; FOR TESTING PROGRAM. (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/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) ; ; ; 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 boot's" 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) ;****************************************************************** ; ;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 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 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 ; IF DJ512S 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 ; 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 ; ;DEFINE ASCII CHARACTERS USED ; CR EQU 0DH ;ASCII CARRIAGE RETURN CHARACTER LF EQU 0AH ;ASCII LINE FEED CHARACTER TAB EQU 09H ;ASCII 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 3.7' DB CR,LF,'Bad sector lockout ' DB 'program',CR,LF ; IF STDDRV DB 'For single-density 8" only' ENDIF ; IF MICROP DB 'For Micropolis Mod II only' ENDIF ; IF MMDBL DB 'For Micromation double-density only' ENDIF ; IF DIGDBL DB 'For Digital Microsystems',CR,LF DB 'FDC3 cntlr dbl dens only' ENDIF ; IF IMDOS DB 'For IMSAI IMDOS double-density ONLY' ENDIF ; IF DJ256S DB 'For Discus 2-dens./1-side/256-byte sectors only' ENDIF ; IF DJ512S DB 'For Discus 2-dens./1-side/512-byte sectors only' ENDIF ; IF DJ1024 DB 'For Discus 2-dens./1-side/1024-byte sectors only' ENDIF ; DB 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 2 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 DJ512S 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 ; 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 ; ;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 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 ; 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 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 (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 H&L REGS. EQUAL 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 60 ;ROOM FOR 30 LEVEL STACK NEWSTK EQU $ ;OUR STACK ; END