;
;                   FINDBAD.ASM Ver. 3.8
;			revised 12/08/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
;	- NATIONAL MULTIPLEX DD 1/2 SIDED 256/512 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)
;
;12/08/80 ADDED EQUATES FOR NATIONAL MULTIPLEX D3S/D4S
;         DOUBLE DENSITY BOARD IN VARIOUS FORMATS.
;         (DAVID FIEDLER)
;
;			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)
NM256	EQU	FALSE	;TRUE IF NATMUX 2D     (256-BYTE SECTOR)
NM512	EQU	FALSE	;TRUE IF NATMUX 2D     (512-BYTE SECTOR)
;******************************************************************
;
;CONDITIONAL ASSEMBLY SWITCH FOR DOUBLE SIDED DRIVES
;presently supported for National Multiplex only
;
SIDES2	EQU	FALSE	;TRUE for NatMux D3S/D4S 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
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
;
;
;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.8'
	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
;
	IF	NM256 OR NM512
	DB	'For National Multiplex double density'
	ENDIF
;
	IF	SIDES2 AND (NM256 OR NM512)
	DB	' double'
	ENDIF
;
	IF	NOT SIDES2 AND (NM256 OR NM512)
	DB	' single'
	ENDIF
;	
	IF	NM256
	DB	' sided 256-byte sectors only'
	ENDIF
;
	IF	NM512
	DB	' sided 512-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	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
;
;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 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
