	PAGE ,132 ;
	TITLE MSDISK - BIOS
	%OUT	...MSDISK.ASM

;==============================================================================
;REVISION HISTORY:
;AN000 - New for DOS Version 4.00 - J.K.
;AC000 - Changed for DOS Version 4.00 - J.K.
;AN00x - PTM number for DOS Version 4.00 - J.K.
;==============================================================================
;AN001; d24 Multi Track enable/disable command in CONFIG.SYS	    6/27/87 J.K.
;AN002; d9  Double word MOV instruction 			    7/1/87  J.K.
;AN003: d25 Change DASD ERP to that recommended by Storage systems  7/28/87 J.K.
;AN004; D113 Disable I/O access to unformatted media		    9/03/87 J.K.
;AN005; P985 Allow I/O access to unformatted media		    9/14/87 J.K.
;AN006; P1535 Disallow I/O access to unformatted media.		   10/15/87 J.K.
;	(Give the user to control this - Generic IOCTL subfunction 64h/44h
;AN007; p2828 Do not retry for multi-track format request	   12/08/87 J.K.
;AC008; p3197 Should reset change line after BPB set in BDS table  01/21/88 J.K.
;AN009; p3349 Should retry at hard file timeout error		   02/03/88 J.K.
;AN010; p4696 ECC error handler should cover PC ATs for CMC disks  05/04/88 J.K.
;AN011; p5034 Possible virgin hard file problem with direct INT 13 06/03/88 J.K.
;AN012; P5049 Attempt to write at DISK BASE table in ROM BIOS	   06/06/88 J.K.
;AN013; P5055 Diskcopy fails (Regression of P5049)		   06/09/88 J.K.
;==============================================================================

;for testing, set itest to 1.  So as MSBIO1.ASM.
	itest=0
	EXTRN NUMERR:ABS		;MSDATA

	INCLUDE MSGROUP.INC	;DEFINE CODE SEGMENT
	INCLUDE MSEQU.INC
	INCLUDE PUSHPOP.INC
	INCLUDE MSMACRO.INC
	INCLUDE DEVSYM.INC
	INCLUDE MSDSKPR.INC
	include biostruc.inc

	EXTRN INT2F_DISK:FAR		;MSBIO2
	EXTRN MEDIACHECK:NEAR		;96TPI
	EXTRN HASCHANGE:NEAR		;96TPI
	EXTRN MEDIA_SET_VID:NEAR	;96TPI
	EXTRN HIDENSITY:NEAR		;96TPI
	EXTRN CHECKLATCHIO:NEAR 	;96TPI
	EXTRN CHECKIO:NEAR		;96TPI
	EXTRN SET_CHANGED_DL:NEAR	;96TPI
	EXTRN SET_VOLUME_ID:NEAR	;MSVOLID
	EXTRN SWPDSK:NEAR		;MSBIO2
	EXTRN CMDERR:NEAR		;MSBIO1
	EXTRN STRATEGY:NEAR		;MSBIO1
	EXTRN ERR$CNT:NEAR		;MSBIO1
	EXTRN DSK$IN:NEAR		;MSBIO1
	EXTRN EXIT:NEAR 		;MSBIO1
	EXTRN BUS$EXIT:NEAR		;MSBIO1
	EXTRN ERR$EXIT:NEAR		;MSBIO1
	extrn ResetChanged:near 	;AN000; MS96TPI

;DATA
	EXTRN OLD13:DWORD		;MSBIO2
	EXTRN PTRSAV:DWORD		;MSBIO1
	EXTRN COM1DEV:WORD		;MSAUX
	EXTRN DAYCNT:WORD		;MSCLOCK
	EXTRN TIM_DRV:BYTE		;MSDATA
	EXTRN ACCESSCOUNT:BYTE		;MSDATA
	EXTRN SM92:BYTE 		;MSDATA
	EXTRN DISKSECTOR:BYTE		;MSDATA
	EXTRN MEDIABYTE:BYTE		;MSDATA
	EXTRN SECPERCLUSINSECTOR:BYTE	;MSDATA
	EXTRN BPB_IN_SECTOR:WORD	;MSDATA
	EXTRN DISKSECTOR:BYTE		;MSDATA
	EXTRN STEP_DRV:BYTE		;MSDATA
	EXTRN START_BDS:WORD		;MSDATA
	EXTRN PHYS_DRV:BYTE		;MSDATA
	EXTRN WRTVERIFY:WORD		;MSDATA
	EXTRN FSETOWNER:BYTE		;MSDATA
	EXTRN SINGLE:BYTE		;MSDATA
	EXTRN RFLAG:BYTE		;MSDATA
	EXTRN MEDBYT:BYTE		;MSDATA
	EXTRN SPSAV:WORD		;MSDATA
	EXTRN SECCNT:WORD		;MSDATA
	EXTRN DPT:DWORD 		;MSDATA
	EXTRN CURSEC:BYTE,CURHD:BYTE	;MSDATA
	EXTRN CURTRK:WORD		;MSDATA
	EXTRN EOT:BYTE			;MSDATA
	EXTRN MOTORSTARTUP:BYTE,SETTLECURRENT:BYTE,SETTLESLOW:BYTE ;MSDATA
	EXTRN CURHD:BYTE		;MSDATA
	EXTRN LSTERR:BYTE		;MSDATA
	EXTRN ERRIN:BYTE,ERROUT:BYTE	;MSDATA
	EXTRN PREVOPER:WORD		;MSDATA
	EXTRN ORIG13:DWORD		;MSDATA
	EXTRN FLAGBITS:WORD		;MSDATA
	EXTRN NUMBER_OF_SEC:BYTE	;MSDATA
	EXTRN FHAVE96:BYTE		;MSDATA
	EXTRN NEW_ROM:BYTE		;MSDATA
	EXTRN FORMT_EOT:BYTE,HDNUM:BYTE,TRKNUM:WORD,GAP_PATCH:BYTE ;MSDATA
	EXTRN NEXT2F_13:WORD		;MSDATA
	extrn Save_head_sttl:byte	;msbdata
	extrn Secrete_Code:word 	;msbdata J.K. 11/7/86 Secrete code for DOS 3.3 MSBIO
	extrn Ext_Boot_Sig:byte 	;AN000; msbdata
	extrn Boot_Serial_L:word	;AN000; msbdata
	extrn Boot_Serial_H:word	;AN000; msbdata
	extrn Boot_Volume_Label:byte	;AN000; msbdata
	extrn Boot_System_ID:byte	;AN000; msbdata
	extrn Model_Byte:Byte		;MSBIO2
	extrn Secondary_Model_Byte:Byte ;MSBIO2

;-----------------------------------------------------------------
;
;	DISK INTERFACE ROUTINES
;
; DEVICE ATTRIBUTE BITS:
;	BIT 6 - GET/SET MAP FOR LOGICAL DRIVES AND GENERIC IOCTL.
;

MAXERR	=	5
LSTDRV	=	504H

; SOME FLOPPIES DO NOT HAVE CHANGELINE.  AS A RESULT, MEDIA-CHECK WOULD
; NORMALLY RETURN I-DON'T-KNOW, THE DOS WOULD CONTINUALLY REREAD THE FAT, AND
; DISCARD CACHED DATA.	WE OPTIMIZE THIS BY IMPLEMENTING A LOGICAL DOOR-
; LATCH:  IT IS PHYSICALLY IMPOSSIBLE TO CHANGE A DISK IN UNDER 2 SECONDS.  WE
; RETAIN THE TIME OF THE LAST SUCCESSFUL DISK OPERATION AND COMPARE IT WITH
; THE CURRENT TIME DURING MEDIA-CHECK.	IF < 2 SECONDS AND AT LEAST 1 TIMER
; TICK HAS PASSED, THE WE SAY NO CHANGE.  IF > 2 SECONDS THEN WE SAY I-
; DON'T-KNOW.  FINALLY, SINCE WE CANNOT TRUST THE TIMER TO BE ALWAYS
; AVAILABLE, WE RECORD THE NUMBER OF MEDIA CHECKS THAT HAVE OCCURRED WHEN NO
; APPARENT TIME HAS ELAPSED.  WHILE THIS NUMBER IS < A GIVEN THRESHOLD, WE SAY
; NO CHANGE.  WHEN IT EXCEEDS THAT THRESHOLD, WE SAY I-DON'T-KNOW AND RESET
; THE COUNTER TO 0.  WHEN WE STORE THE TIME OF LAST SUCCESSFUL ACCESS, IF WE
; SEE THAT TIME HAS PASSED TOO, WE RESET THE COUNTER.
;
ACCESSMAX	=	5
;
; DUE TO VARIOUS BOGOSITIES, WE NEED TO CONTINUALLY ADJUST WHAT THE HEAD
; SETTLE TIME IS.  THE FOLLOWING ALGORITHM IS USED:
;
;   GET THE CURRENT HEAD SETTLE VALUE.
;   IF IT IS 0, THEN
;	SET SLOW = 15
;   ELSE
;	SET SLOW = VALUE
;   ...
;*********************************************
;************ OLD ALGORITHM ******************
;*   IF WE ARE SEEKING AND WRITING THEN
;*	 USE SLOW
;*   ELSE
;*	 USE FAST
;*********************************************
;*********** IBM'S REQUESTED LOGIC ***********
;   IF WE ARE SEEKING AND WRITING AND NOT ON AN AT THEN
;	USE SLOW
;   ELSE
;	USE FAST
;   ...
;   RESTORE CURRENT HEAD SETTLE VALUE
;

Set_ID_Flag	db	0	;AN000; If 1, GETBP routine will set the
				;Vol_Serial and FileSys_ID in BDS table
				;from the media Boot record, if it is > DOS 4.00
				;formatted one. Then Set_ID_flag will be set to 2
				;to signal that volume_label is set from the extended
				;boot record and do not set it from the root
				;directory as done in SET_VOLUME_ID routine.
				;For the old version, Vol_Serial
				;will be set to -1, and FileSys_ID will be set
				;to "FAT12   " if it is a floppy.

	public	Fat_12_ID
Fat_12_ID  DB  "FAT12   ",0     ;AN000;  Default System ID for floppy.
	public	Fat_16_ID
Fat_16_ID  DB  "FAT16   ",0     ;AN000;
	public	Vol_No_Name
Vol_No_Name db "NO NAME    ",0  ;AN000;
	public	Temp_H
Temp_H		dw	0		;AN000; Temporary for 32 bit calculation.

	public	Start_Sec_H
Start_Sec_H	dw	0		;AN000; Starting sector number high word.
					;Used as an input to DISKIO subroutine.
Saved_Word	dw	0		;AN000;  Tempory saving place for a word.

;---------------------------------------
;J.K. 6/29/87 For Multi-track
MULTRK_ON	EQU	10000000B	;User spcified Mutitrack=on, or System turns
					; it on after handling CONFIG.SYS file as a
					; default value, if MulTrk_flag = MULTRK_OFF1.
MULTRK_OFF1	EQU	00000000B	;initial value. No "Multitrack=" command entered.
MULTRK_OFF2	EQU	00000001B	;User specified Multitrack=off.
	public	MulTrk_Flag
MulTrk_Flag	dw	0		;AN001;
;J.K. 6/29/87 End of Multi-track definition.
;---------------------------------------------------------------------
	public EC35_Flag
EC35_Flag	db	0		; flags for electrically compatible 3.5 inch disk drives (mrw 4/88)
;---------------------------------------------------------------------
VRetry_Cnt	dw	0		;AN003;
Soft_ECC_Cnt	dw	0		;AN003;
;---------------------------------------------------------------------
MultiTrk_Format_Flag	db	0	;AN007;Testing. If 1, then Multi track format request
;---------------------------------------------------------------------
;
; IF ID IS F9, HAVE A 96TPI DISK ELSE
; IF BIT 2 IS 0 THEN MEDIA IS NOT REMOVABLE AND COULD NOT HAVE CHANGED
;  OTHERWISE IF WITHIN 2 SECS OF LAST DISK OPERATION MEDIA COULD NOT
;    HAVE CHANGED, OTHERWISE DONT KNOW IF MEDIA HAS CHANGED
;
       PUBLIC MEDIA$CHK
MEDIA$CHK PROC	NEAR
	MESSAGE FTESTDISK,<"DISK MEDIA CHECK ">
	MNUM	FTESTDISK,AX
	MESSAGE FTESTDISK,<CR,LF>
	CALL	SETDRIVE

	cmp	cs:Secrete_Code, 'jk'   ;J.K.11/7/86 Secrete code for DOS 3.3 IBMBIO.
	jne	media$done		;J.K.11/7/86

; FOR NON-REMOVABLE DISKS ONLY RETURN CHANGED IF CHANGED BY FORMAT, OTHERWISE
; RETURN 'NOT CHANGED'.
	MOV	SI,1			; ASSUME NO CHANGE
	TEST	WORD PTR [DI].FLAGS,FCHANGED_BY_FORMAT
	JZ	WEARENOTFAKINGIT
	AND	WORD PTR [DI].FLAGS,NOT FCHANGED_BY_FORMAT	; RESET FLAG
; IF MEDIA HAS BEEN CHANGED BY FORMAT, WE MUST ASK THE ROM. CANNOT RELY ON THE
; 2 SECOND TIME CHECK.
	MOV	CS:[TIM_DRV],-1 	; ENSURE THAT WE ASK THE ROM IF MEDIA
					; HAS CHANGED
	TEST	WORD PTR [DI].FLAGS,FNON_REMOVABLE
	JZ	WEHAVEAFLOPPY
	MOV	SI,-1			; INDICATE MEDIA CHANGED
	JMP	SHORT MEDIA$DONE
;
; WE NEED TO RETURN 'NOT CHANGED' IF WE HAVE A HARD FILE.
;
WEARENOTFAKINGIT:
	TEST	WORD PTR [DI].FLAGS,FNON_REMOVABLE
	JNZ	MEDIA$DONE
WEHAVEAFLOPPY:
	XOR	SI,SI			; PRESUME "I DON'T KNOW"
;
; IF WE HAVE A FLOPPY WITH CHANGELINE SUPPORT, WE ASK THE ROM TO DETERMINE IF
; MEDIA HAS CHANGED. WE DO NOT PERFORM THE 2 SECOND CHECK FOR THESE DRIVES.
;----------------------------------------|
; WARNING: DO NOT CHANGE THE FOLLOWING. ;|
;	   IT GETS PATCHED IN MSINIT	;|
	PUBLIC MEDIA_PATCH		;|
MEDIA_PATCH:				;|
	CALL	MEDIACHECK		;|
	JC	ERR$EXITJ		;|
	CALL	HASCHANGE		;|
	JNZ	MEDIA$DONE		;|
;----------------------------------------|
; IF WE COME HERE, WE HAVE A FLOPPY WITH NO CHANGELINE SUPPORT
	MOV	SI,1			; PRESUME NO CHANGE
	MOV	AL,CS:[TIM_DRV] 	; LAST DRIVE ACCESSED
	CMP	AL,BYTE PTR [DI].DRIVENUM   ;IS DRIVE OF LAST ACCESS THE SAME?
	JNZ	MEDIA$UNK		; NO, THEN "I DON'T KNOW"
;
; CHECK TO SEE IF THIS DRIVE HAS BEEN ACCESSED IN THE LAST 2 SECONDS.
	CALL	CHECK_TIME_OF_ACCESS	; SETS SI CORRECTLY
	JMP	SHORT MEDIA$DONE

MEDIA$UNK:
	DEC	SI			; RETURN "I DON'T KNOW"
;
; SI NOW CONTAINS THE CORRECT VALUE FOR MEDIA CHANGE.  CLEAN UP THE LEFT OVERS
;
MEDIA$DONE:
	LES	BX,CS:[PTRSAV]		; GET ORIGINAL PACKET
	MOV	WORD PTR ES:[BX].TRANS,SI
	OR	SI,SI
	JS	INIT_PATCH
	JMP	EXIT
MEDIA$CHK ENDP
;----------------------------------------|
; WARNING: DO NOT CHANGE THE FOLLOWING. ;|
;	   IT GETS PATCHED IN MSINIT	;|
	PUBLIC INIT_PATCH		;|
INIT_PATCH PROC NEAR			;|
	CALL	MEDIA_SET_VID		;|
;----------------------------------------|
	MOV	CS:[TIM_DRV],-1 	; MAKE SURE WE ASK ROM FOR MEDIA CHECK
VOLIDOK:
	JMP	EXIT
INIT_PATCH ENDP

ERR$EXITJ PROC NEAR

	MESSAGE FTESTCOM,<"ERR$EXITJ: ">
	MNUM	FTESTCOM,AX
	MESSAGE FTESTCOM,<" == ">
	CALL	MAPERROR
	MNUM	FTESTCOM,AX
	MESSAGE FTESTCOM,<CR,LF>
	JMP	ERR$EXIT
ERR$EXITJ ENDP

;
; PERFORM A CHECK ON THE TIME PASSED SINCE THE LAST ACCESS FOR THIS PHYSICEL
; DRIVE.
; WE ARE ACCESSING THE SAME DRIVE.  IF THE TIME OF LAST SUCCESSFUL ACCESS WAS
; LESS THAN 2 SECONDS AGO, THEN WE MAY PRESUME THAT THE DISK WAS NOT CHANGED.
; RETURNS IN SI:
;	0 - IF TIME OF LAST ACCESS WAS >= 2 SECONDS
;	1 - IF TIME WAS < 2 SECONDS (I.E NO MEDIA CHANGE ASSUMED)
; REGISTERS AFFECTED AX,CX,DX, FLAGS.
;
CHECK_TIME_OF_ACCESS PROC NEAR
	PUBLIC CHECK_TIME_OF_ACCESS

	MOV	SI,1			; PRESUME NO CHANGE.
;SB33014*************************************************************
	xor	AH,AH			; set command to read time
	int	1Ah			; call rom-bios clock routine
;SB33014*************************************************************
;
; NOW THAT WE ARE READING THE TIME, WE MUST MAKE SURE THAT WE DO NOT LOSE A
; DATE WRAP.  THE ROM WILL RETURN THE VALUE ONLY ONCE, SO WE NEED TO NOTE THIS
; FACT.
;
	SHR	AL,1
	ADC	CS:[DAYCNT],0		; ADD IT IN TO OUR REMEMBERED DAY COUNT
;
; COMPUTE ELAPSED TIME
;
	MOV	AX,WORD PTR DS:[DI].TIM_LO   ; GET STORED TIME
	SUB	DX,AX
	MOV	AX,WORD PTR DS:[DI].TIM_HI
	SBB	CX,AX
;
; CX:DX IS THE ELAPSED TIME
;
	JNZ	TIMECHECK_UNK		; CX <> 0 => > 1 HOUR
	OR	DX,DX			; TIME MUST PASS
	JNZ	TIMEPASSED		; YES, EXAMINE MAX VALUE
;
; NO NOTICEABLE TIME HAS PASSED.  WE CANNOT TRUST THE COUNTER TO BE ALWAYS
; AVAILABLE AS THERE ARE BOGUS PROGRAMS THAT GO AND REPROGRAM THE THING.  WE
; KEEP A COUNT OF THE NUMBER OF MEDIA CHECKS THAT WE'VE SEEN THAT DO NOT HAVE
; ANY TIME PASSING.  IF WE EXCEED A GIVE THRESHOLD, WE GIVE UP ON THE TIMER.
;
	INC	BYTE PTR CS:ACCESSCOUNT
	CMP	BYTE PTR CS:ACCESSCOUNT,ACCESSMAX
	JB	TIMECHECK_RET		; IF COUNT IS LESS THAN THRESHOLD, OK
	DEC	BYTE PTR CS:ACCESSCOUNT ; DON'T LET THE COUNT WRAP
	JMP	SHORT TIMECHECK_UNK	; "I DON'T KNOW" IF MEDIA CHANGED
;
; 18.2 TICS PER SECOND.
;
TIMEPASSED:
	CMP	DX,18 * 2		; MIN ELAPSED TIME?
	JBE	TIMECHECK_RET		; YES, PRESUME NO CHANGE
;
; EVERYTHING INDICATES THAT WE DO NOT KNOW WHAT HAS HAPPENED.
;
TIMECHECK_UNK:
	DEC	SI			 ; PRESUME I DON'T KNOW
TIMECHECK_RET:
	RET
CHECK_TIME_OF_ACCESS ENDP

ERR$EXITJ2: JMP ERR$EXITJ
;
; BUILD A VALID BPB FOR THE DISK IN THE DRIVE.
;
	PUBLIC GET$BPB
GET$BPB PROC	NEAR
	MESSAGE FTESTDISK,<"DISK BUILD BPB ">
	MNUM	FTESTDISK,AX
	MESSAGE FTESTDISK,<CR,LF>
	MOV	AH,BYTE PTR ES:[DI]	;GET FAT ID BYTE READ BY DOS
	CALL	SETDRIVE		; GET THE CORRECT BDS FOR THE DRIVE
	TEST	WORD PTR [DI].FLAGS,FNON_REMOVABLE
	JNZ	ALREADY_GOTBPB		; NO NEED TO BUILD FOR FIXED DISKS
;J.K. Let's set the default value for VOLID,Vol_Serial,FileSys_ID in BDS table
	call	Clear_IDs		;AN000;
	mov	cs:[Set_ID_Flag],1	;AN000; Indicate to set system id in BDS
	CALL	GETBP			;BUILD A BPB IF NECESSARY.
	JC	ERR$EXITJ2		;AN000; If error, Set_ID_flag is set to 0 already.
	cmp	cs:[Set_ID_Flag],2	;AN000; Already, volume_Label set from Boot record
	mov	cs:[Set_ID_Flag],0	;AN000;  to BDS table?
	je	Already_GotBPB		;AN000; Then do not set it again from Root directory.
					;AN000; Otherwise, conventional Boot record.
GET$BPB ENDP
;----------------------------------------|
; WARNING: DO NOT CHANGE THE FOLLOWING. ;|
;	   IT GETS PATCHED IN MSINIT	;|
	PUBLIC SET_PATCH		;|
SET_PATCH PROC	NEAR			;|
	CALL	SET_VOLUME_ID		;|
;----------------------------------------|
	MESSAGE FTESTDISK,<"SET VOLUME ID">
	MNUM	FTESTDISK,DI
	MESSAGE FTESTDISK,<"  ">
	MNUM	FTESTDISK,DS
	MESSAGE FTESTDISK,<CR,LF>

ALREADY_GOTBPB:
	ADD	DI,BYTEPERSEC		; RETURN THE BPB THAT IS IN THE CURRENT BDS

	PUBLIC	SETPTRSAV
SETPTRSAV:				; RETURN POINT FOR DSK$INIT
	LES	BX,CS:[PTRSAV]
	MOV	ES:[BX].MEDIA,AH
	MOV	ES:[BX].COUNT,DI
	MOV	ES:[BX].COUNT+2,DS
	JMP	EXIT
SET_PATCH ENDP

;J.K. Clear IDs in BDS table. Only applied for Floppies.
;Input:  DS:DI -> BDS table
;Output: VOLID set to "NO NAME    "
;	 VOL_SERIAL set to 0.
;	 FileSys_ID set to "FAT12   " or "FAT16   "
;	   depending on the flag FATSIZE in BDS.
;All registers saved.
	public	Clear_IDs
Clear_IDs	proc near		;AN000;
	push	ds			;AN000;
	push	di			;AN000;
	push	es			;AN000;
	push	si			;AN000;
	push	cx			;AN000;

	push	ds			;AN000;
	pop	es			;AN000; es -> bds
	push	cs			;AN000;
	pop	ds			;AN000; ds = cs

	mov	cx, 0			;AN000; no serial number
	mov	word ptr es:[di.VOL_SERIAL],cx	 ;AN000;
	mov	word ptr es:[di.VOL_SERIAL]+2,cx ;AN000;

	mov	cx, BOOT_VOLUME_LABEL_SIZE	 ;AN000; =11
	mov	si, offset VOL_NO_NAME		 ;AN000;
	push	di				 ;AN000; save BDS pointer
	add	di, VOLID			 ;AN000; points to VOLID field
	rep	movsb				 ;AN000;
	pop	di				 ;AN000; restore BDS pointer
	test	es:[di.FATSIZ], FBIG		 ;AN000;
	jnz	CI_BigFat			 ;AN000; small fat
	mov	si, offset FAT_12_ID		 ;AN000;
	jmp	CI_Filesys			 ;AN000;
CI_BigFat:					 ;AN000;
	mov	si, offset FAT_16_ID		 ;AN000; big fat
CI_Filesys:					 ;AN000;
	mov	cx, BOOT_SYSTEM_ID_SIZE 	 ;AN000; =8
	add	di, FILESYS_ID			 ;AN000; points to FILESYS_ID field
	rep	movsb				 ;AN000;

	pop	cx				 ;AN000;
	pop	si				 ;AN000;
	pop	es				 ;AN000;
	pop	di				 ;AN000;
	pop	ds				 ;AN000;
	ret					 ;AN000;
Clear_IDs	endp				 ;AN000;


;   GETBP - RETURN BPB FROM THE DRIVE SPECIFIED BY THE BDS.
;	    IF THE RETURN_FAKE_BPB FLAG IS SET, THEN IT DOES NOTHING.
;	    NOTE THAT WE NEVER COME HERE FOR FIXED DISKS.
;	    FOR ALL OTHER CASES,
;	      - IT READS BOOT SECTOR TO PULL OUT THE BPB
;	      - IF NO VALID BPB IS FOUND, IT THEN READS THE FAT SECTOR,
;		TO GET THE FAT ID BYTE TO BUILD THE BPB FROM THERE.
;
;   INPUTS:	DS:DI POINT TO CORRECT BDS.
;
;   OUTPUTS:	FILLS IN BPB IN CURRENT BDS IF VALID BPB OR FAT ID ON DISK.
;		CARRY SET, AND AL=7 IF INVALID DISK.
;		CARRY SET AND ERROR CODE IN AL IF OTHER ERROR.
;		If failed to recognize the Boot Record, then will set the
;		Set_ID_Flag to 0.
;		J.K. This routine will only work for a floppy diskette.
;		     For a fixed disk, it will just return.

	PUBLIC GETBP
GETBP	PROC	NEAR
; IF RETURNING FAKE BPB THEN RETURN BPB AS IS.
	TEST	WORD PTR [DI].FLAGS,RETURN_FAKE_BPB OR FNON_REMOVABLE
	JZ	GETBP1
	JMP	GETRET_EXIT

GETBP1:
	MESSAGE FTESTDISK,<"BUILDING BPB FROM SCRATCH",CR,LF>
	SAVEREG <CX,DX,ES,BX>
;
; ATTEMPT TO READ IN BOOT SECTOR AND DETERMINE BPB.
; WE ASSUME THAT THE 2.X AND GREATER DOS DISKS ALL HAVE A VALID BOOT SECTOR.
;
RDBOOT:
	CALL	READBOOTSEC
	JC	GETBP_ERR_RET_brdg	; CARRY SET IF THERE WAS ERROR.
	CMP	BX,0			; BX IS 0 IF BOOT SECTOR IS VALID.
	JNZ	DOFATBPB

	CALL	MOVBPB			; MOVE BPB INTO REGISTERS.
	JMP	HAS1

Getbp_err_ret_brdg: jmp Getbp_err_ret
;
; WE HAVE A 1.X DISKETTE.
; IN THIS CASE READ IN THE FAT ID BYTE AND FILL IN BPB FROM THERE.
;
DOFATBPB:
	CALL	READFAT 		; PUTS MEDIA DESCRIPTOR BYTE IN AH
	JC	GETBP_ERR_RET_Brdg
;----------------------------------------|
; WARNING: DO NOT CHANGE THE FOLLOWING. ;|
;	   IT GETS PATCHED IN MSINIT	;|
	PUBLIC GETBP1_PATCH		;|
GETBP1_PATCH:				;|
	CALL	HIDENSITY		;|
;----------------------------------------|
;TEST FOR A VALID 3.5" MEDIUM
	CMP	[DI].FORMFACTOR,FFSMALL
	JNZ	IS_FLOPPY
	CMP	AH,0F9H 		; IS IT A VALID FAT ID BYTE FOR 3.5" ?
	JNZ	GOT_UNKNOWN_MEDIUM
	MOV	BX,OFFSET SM92		; POINTER TO CORRECT BPB
	PUSH	CS
	POP	ES

	ASSUME ES:CODE
;J.K. DS points to segment of BDS. The following should be modified
;J.K. to get spf,csec,spa,spt correctly. It had been wrong if DRIVER.SYS
;J.K. is loaded since the BDS is inside the driver.sys.
	MOV	AL,es:[BX.SPF]
	MOV	CX,es:[BX.CSEC]
	MOV	DX,WORD PTR es:[BX.SPA]
	MOV	BX,WORD PTR es:[BX.SPT]
	JMP	SHORT HAS1
; MUST BE A 5.25" FLOPPY IF WE COME HERE
IS_FLOPPY:
	MOV	CL,AH			;SAVE MEDIA
	AND	CL,0F8H 		;NORMALIZE
	CMP	CL,0F8H 		;COMPARE WITH GOOD MEDIA BYTE
	JNZ	GOT_UNKNOWN_MEDIUM

GOODID:
	MOV	AL,1			;SET NUMBER OF FAT SECTORS
	MOV	BX,64*256+8		;SET DIR ENTRIES AND SECTOR MAX
	MOV	CX,40*8 		;SET SIZE OF DRIVE
	MOV	DX,01*256+1		;SET HEAD LIMIT AND SEC/ALL UNIT
	TEST	AH,00000010B		;TEST FOR 8 OR 9 SECTOR
	JNZ	HAS8			;NZ = HAS 8 SECTORS
	INC	AL			;INC NUMBER OF FAT SECTORS
	INC	BL			;INC SECTOR MAX
	ADD	CX,40			;INCREASE SIZE
HAS8:
	TEST	AH,00000001B		;TEST FOR 1 OR 2 HEADS
	JZ	HAS1			;Z = 1 HEAD
	ADD	CX,CX			;DOUBLE SIZE OF DISK
	MOV	BH,112			;INCREASE NUMBER OF DIRECTORY ENTRIES
	INC	DH			;INC SEC/ALL UNIT
	INC	DL			;INC HEAD LIMIT

HAS1:
	PUBLIC	HAS1

	MOV	BYTE PTR DS:[DI].SECPERCLUS,DH
	MOV	BYTE PTR DS:[DI].CDIR,BH
	MOV	WORD PTR DS:[DI].DRVLIM,CX
	MOV	BYTE PTR DS:[DI].MEDIAD,AH
	MOV	BYTE PTR DS:[DI].CSECFAT,AL
	MOV	BYTE PTR DS:[DI].SECLIM,BL
	MOV	BYTE PTR DS:[DI].HDLIM,DL
;SB34DISK001*******************************************************************
;SB	the HIDSEC_H field and DRVLIM_L field and the 
;SB	DRVLIM_H fields need to be set to 0 since this code here is for floppies
;SB		3 LOCS

	mov	word ptr ds:[di].HIDSEC_H,0
	mov	word ptr ds:[di].HIDSEC_L,0
	mov	word ptr ds:[di].DRVLIM_H,0

;SB34DISK001*******************************************************************
GETRET:
	POP	BX
	RESTOREREG <ES,DX,CX>

	ASSUME ES:NOTHING

GETRET_EXIT:
	RET

GETBP_ERR_RET:
;J.K. Before doing anything else, set Set_ID_Flag to 0.
	mov	cs:Set_ID_Flag, 0	;AN000;
	CALL	MAPERROR
	JMP	SHORT GETRET
;
; WE HAVE A 3.5" DISKETTE FOR WHICH WE CANNOT BUILD A BPB. WE DO NOT ASSUME ANY
; TYPE OF BPB FOR THIS MEDIUM.
;
GOT_UNKNOWN_MEDIUM:
	mov	cs:Set_ID_Flag, 0	;AN000;
	MOV	AL,ERROR_UNKNOWN_MEDIA
	STC
	JMP	SHORT GETRET
GETBP	ENDP

BPBTYPE STRUC
SPF	DB	?
SPT	DB	?
CDIRE	DB	?
CSEC	DW	?
SPA	DB	?
CHEAD	DB	?
BPBTYPE ENDS

;
; READ IN THE BOOT SECTOR. SET CARRY IF ERROR IN READING SECTOR.
; BX IS SET TO 1 IF THE BOOT SECTOR IS INVALID, OTHERWISE IT IS 0.
;
READBOOTSEC PROC NEAR
	 MOV	 DH,0			    ;HEAD 0
	 MOV	 CX,0001		    ;CYLINDER 0, SECTOR 1
	 CALL	 READ_SECTOR
	 JC	 ERR_RET
	 XOR	 BX,BX			    ; ASSUME VALID BOOT SECTOR.

;*******************************************************************************
; PUT A SANITY CHECK FOR THE BOOT SECTOR IN HERE TO DETECT BOOT SECTORS THAT
; DO NOT HAVE VALID BPBS.
; WE EXAMINE THE FIRST TWO BYTES - THEY MUST CONTAIN A LONG JUMP (69H) OR A
; SHORT JUMP (EBH) FOLLOWED BY A NOP (90H), OR A SHORT JUMP (E9H).
; IF THIS TEST IS PASSED, WE FURTHER CHECK BY EXAMINING THE SIGNATURE AT
; THE END OF THE BOOT SECTOR FOR THE WORD AA55H.
; IF THE SIGNATURE IS NOT PRESENT, WE EXAMINE THE MEDIA DESCRIPTOR BYTE TO
; SEE IF IT IS VALID.
;J.K. 10/15/86 DCR00012. For DOS 3.3, this logic is modified a little bit.
; We are not going to check Signature.	Instead we are going to sanity
; check the media byte in BPB regardless of the validity of signature.
; This is to save the already developed commercial products that have
; good jump instruction and signature but with the false BPB informations
; that will crash the diskette drive operation. (For example, Symphony diskette).
;******************************************************************************
	 CMP	BYTE PTR CS:[DISKSECTOR],069H	; IS IT A DIRECT JUMP?
	 JE	Check_bpb_MediaByte		; DON'T NEED TO FIND A NOP
	 CMP	BYTE PTR CS:[DISKSECTOR],0E9H	; DOS 2.0 JUMP?
	 JE	Check_bpb_MediaByte		; NO NEED FOR NOP
	 CMP	BYTE PTR CS:[DISKSECTOR],0EBH	; HOW ABOUT A SHORT JUMP.
	 JNE	INVALIDBOOTSEC
	 CMP	BYTE PTR CS:[DISKSECTOR]+2,090H ; IS NEXT ONE A NOP?
	 JNE	INVALIDBOOTSEC


;J.K. 10/15/86 Don't have to perform the following signature check since
; we need to check the media byte even with the good signatured diskette.
;CHECK_SIGNATURE:
;	  CMP	  WORD PTR CS:[DISKSECTOR+1FEH],0AA55H	 ; SEE IF NON-IBM DISK OR 1.X
;						   ; MEDIA.
;	  JZ	  CHECKSINGLESIDED	 ; GO SEE IF SINGLE SIDED MEDIUM. MAY
;					 ; NEED SOME SPECIAL HANDLING
;
; CHECK FOR NON-IBM DISKS WHICH DO NOT HAVE THE SIGNATURE AA55 AT THE
; END OF THE BOOT SECTOR, BUT STILL HAVE A VALID BOOT SECTOR. THIS IS DONE
; BY EXAMINING THE MEDIA DESCRIPTOR IN THE BOOT SECTOR.
;

Check_bpb_MediaByte:

	 MOV	 AL,BYTE PTR CS:MEDIABYTE
	 AND	 AL,0F0H
	 CMP	 AL,0F0H		; ALLOW FOR STRANGE MEDIA
	 JNZ	 INVALIDBOOTSEC
;
; THERE WERE SOME (APPARENTLY A LOT OF THEM) DISKETTES THAT HAD BEEN FORMATTED
; UNDER DOS 3.1 AND EARLIER VERSIONS WHICH HAVE INVALID BPBS IN THEIR BOOT
; SECTORS. THESE ARE SPECIFICALLY DISKETTES THAT WERE FORMATTED IN DRIVES
; WITH ONE HEAD, OR WHOSE SIDE 0 WAS BAD. THESE CONTAIN BPBS IN THE BOOT
; SECTOR THAT HAVE THE SEC/CLUS FIELD SET TO 2 INSTEAD OF 1, AS IS STANDARD
; IN DOS. IN ORDER TO SUPPORT THEM, WE HAVE TO INTRODUCE A "HACK" THAT WILL
; HELP OUR BUILD BPB ROUTINE TO RECOGNISE THESE SPECIFIC CASES, AND TO
; SET UP OUT COPY OF THE BPB ACCORDINGLY.
; WE DO THIS BY CHECKING TO SEE IF THE BOOT SECTOR IS OFF A DISKETTE THAT
; IS SINGLE-SIDED AND IS A PRE-DOS 3.20 DISKETTE. IF IT IS, WE SET THE
; SEC/CLUS FIELD TO 1. IF NOT, WE CARRY ON AS NORMAL.
CHECKSINGLESIDED:
	MOV	AL,BYTE PTR CS:MEDIABYTE
	TEST	AL,0001H	; IS LOW BIT SET? - INDICATES DOUBLE SIDED
	JNZ	GOODDSK
	CMP	WORD PTR CS:[DISKSECTOR+8],"." SHL 8 + "3"
	JNZ	MUSTBEEARLIER
	CMP	BYTE PTR CS:[DISKSECTOR+10],"2"
	JAE	GOODDSK

; WE MUST HAVE A PRE-3.20 DISKETTE. SET THE SEC/CLUS FIELD TO 1
MUSTBEEARLIER:
	MOV	BYTE PTR CS:[SECPERCLUSINSECTOR],1
	JMP	SHORT GOODDSK
;******************************************************************************

INVALIDBOOTSEC:
	 INC	 BX			    ; INDICATE THAT BOOT SECTOR INVALID
GOODDSK:				    ; CARRY ALREADY RESET
	 CLC
	 RET

ERR_RET:				    ; CARRY IS ALREADY SET ON ENTRY HERE
	 MESSAGE FTESTDISK,<"ERROR IN READBOOT",CR,LF>
	 RET
READBOOTSEC ENDP

; MOVES THE BPB READ FROM THE BOOT SECTOR INTO REGISTERS FOR USE BY
; GETBP ROUTINE AT HAS1
;J.K.-
; If the Set_ID_Flag is 1, and if an extended Boot Record, then set Volume
; Serial Number, Volume Label, File System ID in BDS according to
; the BOOT reocrd.  After that, this routine will set the Set_ID_Flag to 2
; to signal that VOLUME Label is set already from the Extended BOOT record
; (so, don't set it again by calling "SET_VOLUME_ID" routine which uses
; the volume label in the root directory.

MOVBPB	PROC	NEAR
	SAVEREG <DS,DI>
	PUSH	CS
	POP	DS
	MOV	DI,OFFSET BPB_IN_SECTOR
	MOV	DH,BYTE PTR [DI].SECALL     ;SECTORS PER UNIT
	MOV	BH,BYTE PTR [DI].DIRNUM     ;NUMBER OF DIRECTORY ENTRIES
	MOV	CX,WORD PTR [DI].SECNUM     ;SIZE OF DRIVE
	MOV	AH,BYTE PTR [DI].FATID	    ;MEDIA DESCRIPTOR
	MOV	AL,BYTE PTR [DI].FATSIZE    ;NUMBER OF FAT SECTORS
	MOV	BL,BYTE PTR [DI].SLIM	    ;SECTORS PER TRACK
	MOV	DL,BYTE PTR [DI].HLIM	    ;NUMBER OF HEADS
	RESTOREREG <DI,DS>
	cmp	cs:[Set_ID_Flag], 1	     ;AC008 called by GET$BPB?
	jne	MovBPB_Ret		     ;AC008
	call	Mov_Media_IDs		     ;AC008
	jc	MovBPB_Conv		     ;AC008 Conventional boot record?
	mov	cs:[Set_ID_Flag],2	     ;AC008 signals that Volume ID is set.
MovBPB_Conv:				     ;AC008
	cmp	cs:fHave96, 1		     ;AC008
	jne	MovBPB_Ret		     ;AC008
	call	ResetChanged		     ;AC008 Reset Flags in BDS to NOT fCHANGED.
MovBPB_Ret:				     ;AC008
	clc				     ;AC008
	ret				     ;AC008
MOVBPB	ENDP				     ;AC008


;
	public	Mov_Media_IDs
Mov_Media_IDs	Proc	near		    ;AN000;
;copy the boot_serial number, Volume id, and Filesystem id from the
;***Extended Boot record*** in cs:DiskSector to the BDS table pointed
;by DS:DI.
;In.) DS:DI -> BDS
;     CS:DiskSector = Valid extended boot record.
;Out.) Vol_Serial, Volid and System_Id in BDS are set according to
;      the boot record information.
;     Carry flag set if not an extended BPB.
;     All registers saved except the flag.

	cmp	cs:[Ext_Boot_Sig], EXT_BOOT_SIGNATURE ;AN000; = 41
	jne	MMI_Not_Ext		    ;AN000;
	push	cx			    ;AN000;
	mov	cx, cs:[Boot_Serial_L]	    ;AN000;
	mov	word ptr ds:[di.VOL_SERIAL],cx	 ;AN000;
	mov	cx, cs:[Boot_Serial_H]		 ;AN000;
	mov	word ptr ds:[di.VOL_SERIAL+2],cx ;AN000;
	push	ds			    ;AN000; Save regs.
	push	di			    ;AN000;
	push	es			    ;AN000;
	push	si			    ;AN000;

	push	ds			    ;AN000; ds-> cs, es-> bds
	pop	es			    ;AN000;
	push	cs			    ;AN000;
	pop	ds			    ;AN000;

	mov	cx, BOOT_VOLUME_LABEL_SIZE  ;AN000;
	mov	si, offset Boot_Volume_Label;AN000;
	push	di
	add	di, VOLID		    ;AN000;
	rep	movsb			    ;AN000;
	pop	di
	mov	cx, BOOT_SYSTEM_ID_SIZE     ;AN000;  =8
	mov	si, offset Boot_System_ID   ;AN000;
	add	di, FILESYS_ID		    ;AN000;
	rep	movsb			    ;AN000;

	pop	si			    ;AN000;
	pop	es			    ;AN000;
	pop	di			    ;AN000;
	pop	ds			    ;AN000;
	pop	cx			    ;AN000;
	clc				    ;AN000;
MMI_Ret:				    ;AN000;
	ret				    ;AN000;
MMI_Not_Ext:				    ;AN000;
	stc				    ;AN000;
	ret				    ;AN000;
Mov_Media_IDs	endp			    ;AN000;


; READ IN THE FAT SECTOR AND GET THE MEDIA BYTE FROM IT.
; INPUT : AL CONTAINS LOGICAL DRIVE.
; OUTPUT:
;	  CARRY SET IF AN ERROR OCCURS, AX CONTAINS ERROR CODE.
;	  OTHERWISE, AH CONTAINS MEDIA BYTE ON EXIT. AL IS PRESERVED.

READFAT PROC	NEAR
	PUSH	AX			    ; PRESERVE LOGICAL DRIVE IN AL
	MOV	DH,0			    ; HEAD 0
	MOV	CX,0002 		    ; CYLINDER 0, SECTOR 2
	CALL	READ_SECTOR		    ; CS:BX POINTS TO FAT SECTOR
	JC	BAD_FAT_RET
	POP	AX			    ; RESET LOGICAL DRIVE
	MOV	AH,BYTE PTR CS:[BX]	    ; MEDIA BYTE
	RET

BAD_FAT_RET:				     ; CARRY SET ON ENTRY
	MESSAGE FTESTDISK,<"ERROR IN FAT READ",CR,LF>
	POP	CX			     ; CLEAR STACK
	RET
READFAT ENDP

; READ A SINGLE SECTOR INTO THE TEMP BUFFER.
; PERFORM THREE RETRIES IN CASE OF ERROR.
;   INPUTS:	DRIVE HAS PHYSICAL DRIVE TO USE
;		CX HAS SECTOR AND CYLINDER
;		DH HAS HEAD
;   OUTPUTS:	CARRY CLEAR
;		    CS:BX POINT TO SECTOR
;		CARRY SET
;		    AX HAS ROM ERROR CODE
; REGISTERS ES AND BP ARE PRESERVED.

READ_SECTOR PROC NEAR
	PUBLIC READ_SECTOR

	PUSH	BP
	MOV	BP,3		; MAKE 3 ATTEMPTS
	PUSH	ES
;SB33015*****************************************************************
	mov	DL, byte ptr ds:[di].DriveNum			       ;SB;3.30*
	mov	BX, offset DiskSector	; Get ES:BX to point to buffer ;SB;3.30*
	push	CS			;    get the segment right     ;SB;3.30*
	pop	ES			; now ES:BX is correct	       ;SB;3.30*
;SB33015*****************************************************************
RD_RET:
;SB33016*****************************************************************
	mov	AX, 0201h		; number of sectors to 1 (AL=1);SB;3.30*
	int	13h			; call rom-bios disk routines  ;SB;3.30*

;SB33016*****************************************************************
	JNC	OKRET2
Rd_rty:
	CALL	AGAIN		; RESET DISK, DECREMENT BP, PRESERVE AX
	jz	Err_RD_RET
	test	word ptr ds:[di].flags,fNon_Removable
	JNZ	RD_RET
	cmp	cs:[Media_Set_For_Format], 0 ;AN012;
	jne	Rd_Skip1_DPT	;AN012;
	push	ds		;J.K. 11/7/86 For retry, set the head settle time
	push	ax		;to 0Fh. PTM845.
	lds	si,cs:DPT
	mov	al, ds:[si].disk_head_sttl
	mov	cs:[save_head_sttl],al
	mov	byte ptr ds:[si].disk_head_sttl, NormSettle
	pop	ax
	pop	ds
Rd_Skip1_DPT:			;AN012;
;SB33017*****************************************************************
					; SET CMD TO READ (AH=2) AND  ;SB ;3.30
	MOV	AX, 0201h		; NUM OF SECTORS TO 1 (AL=1)  ;SB ;3.30
	INT	13h			; CALL ROM-BIOS DISK ROUTINES ;SB ;3.30
;SB33017*****************************************************************
	pushf			;AN012;
	cmp	cs:[Media_Set_For_Format], 0	;AN012;
	jne	Rd_Skip2_DPT	       ;AN012;
	push	ds
	push	ax
	lds	si,cs:DPT
	mov	al, cs:[save_head_sttl]
	mov	byte ptr ds:[si].disk_head_sttl, al
	pop	ax
	pop	ds
Rd_Skip2_DPT:			;AN012;
	popf			;AN012;
	jnc	OKRET2
	jmp	Rd_rty
ERR_RD_RET:
	MOV	DL,-1		; MAKE SURE WE ASK ROM IF MEDIA HAS CHANGED
	STC			; RETURN ERROR
; UPDATE INFORMATION PERTAINING TO LAST DRIVE ACCESSED, TIME OF ACCESS, LAST
; TRACK ACCESSED IN THAT DRIVE.
OKRET2:
	MOV	CS:[STEP_DRV],DL	; SET UP FOR HEAD SETTLE LOGIC IN DISK.
	MOV	CS:[TIM_DRV],DL 	;SAVE DRIVE LAST ACCESSED
	MOV	BYTE PTR [DI].TRACK,CH	; SAVE LAST TRACK ACCESSED ON THIS DRIVE
	PUSHF				; PRESERVE FLAGS IN CASE ERROR OCCURRED
	CALL	SET_TIM
	POPF				; RESTORE FLAGS
	POP	ES
	POP	BP
	RET
READ_SECTOR ENDP

;-----------------------------------------------------------
;
;		DISK REMOVABLE ROUTINE ARR 2.41
;

DSK$REM PROC	NEAR			;ARR 2.41
	PUBLIC	DSK$REM

	MESSAGE FTESTDISK,<"DISK REMOVABLE ">
	MNUM	FTESTDISK,AX
	MESSAGE FTESTDISK,<CR,LF>
; AL IS UNIT #
	CALL	SETDRIVE		; GET BDS FOR THIS DRIVE
	TEST	WORD PTR [DI].FLAGS,FNON_REMOVABLE
	JNZ	NON_REM
	JMP	EXIT

NON_REM:
	JMP	BUS$EXIT		;ARR 2.41
DSK$REM ENDP

; SETDRIVE SCANS THROUGH THE DATA STRUCTURE OF BDSS, AND RETURNS A POINTER TO
; THE ONE THAT BELONGS TO THE DRIVE SPECIFIED. CARRY IS SET IF NONE EXISTS FOR
; THE DRIVE.
; IF THE BYTE [PHYS_DRV] IS 0 THEN
; ON ENTRY, AL CONTAINS THE LOGICAL DRIVE NUMBER.
; ON EXIT, DS:DI POINTS TO CORRECT BDS FOR THE DRIVE IF CARRY CLEAR.

; ELSE IF THE BYTE [PHYS_DRV] IS 1 THEN (ONLY USED FOR FIXED DISKS WHEN AN ECC
; ERROR OCCURS)
; ON ENTRY, DL CONTAINS THE PYHSICAL DRIVE NUMBER.
; ON EXIT, DS:DI POINTS TO CORRECT BDS FOR THE DRIVE IF CARRY CLEAR.

	PUBLIC SETDRIVE
SETDRIVE PROC	NEAR
	MESSAGE FTESTDISK,<"SETDRIVE",CR,LF>
	PUSH	BX
	PUSH	CS
	POP	DS
	ASSUME	DS:CODE

; ASSUME FIRST BDS IS IN THIS SEGMENT
	MOV	DI,WORD PTR START_BDS
SCAN_LOOP:
	CMP	BYTE PTR CS:[PHYS_DRV],1	; DOES AL HAVE PHYSICAL DRIVE?
	JB	USE_LOGICAL_DRV
	CMP	BYTE PTR [DI].DRIVENUM,AL
	JE	SETDRV
	JMP	SHORT GET_NXT_BDS
USE_LOGICAL_DRV:
	CMP	BYTE PTR [DI].DRIVELET,AL
	JE	SETDRV
GET_NXT_BDS:
	MOV	BX,WORD PTR [DI].LINK+2   ; GO TO NEXT BDS
	MOV	DI,WORD PTR [DI].LINK
	MOV	DS,BX
	ASSUME	DS:NOTHING

	CMP	DI,-1
	JNZ	SCAN_LOOP
	STC
SETDRV:
	POP	BX
	RET
SETDRIVE ENDP

;-----------------------------------------------------------
;
;		DISK I/O ROUTINES
;

DSK$WRITV PROC	NEAR
	PUBLIC	DSK$WRITV

	MESSAGE FTESTDISK,<"DISK WRITE WITH VERIFY ">
	MNUM	FTESTDISK,AX
	MESSAGE FTESTDISK,<" ">
	MNUM	FTESTDISK,DX
	MESSAGE FTESTDISK,<" FOR ">
	MNUM	FTESTDISK,CX
	MESSAGE FTESTDISK,<CR,LF>
	MOV	CS:[WRTVERIFY],103H
	JMP	SHORT DSK$CL

DSK$WRIT:
	PUBLIC	DSK$WRIT
	MESSAGE FTESTDISK,<"DISK WRITE ">
	MNUM	FTESTDISK,AX
	MESSAGE FTESTDISK,<" ">
	MNUM	FTESTDISK,DX
	MESSAGE FTESTDISK,<" FOR ">
	MNUM	FTESTDISK,CX
	MESSAGE FTESTDISK,<CR,LF>
	MOV	CS:[WRTVERIFY],ROMWRITE

DSK$CL:
	CALL	DISKIO
DSK$IO:
	JC	DSKBAD
	JMP	EXIT
DSKBAD:
	JMP	ERR$CNT
DSK$WRITV ENDP

DSK$READ PROC	NEAR
	PUBLIC	DSK$READ
	MESSAGE FTESTDISK,<"DISK READ ">
	MNUM	FTESTDISK,AX
	MESSAGE FTESTDISK,<" ">
	MNUM	FTESTDISK,DX
	MESSAGE FTESTDISK,<" FOR ">
	MNUM	FTESTDISK,CX
	MESSAGE FTESTDISK,<CR,LF>
	CALL	DISKRD
	JMP	DSK$IO
DSK$READ ENDP

; MISCELLANEOUS ODD JUMP ROUTINES.  MOVED OUT OF MAINLINE FOR SPEED.


; IF WE HAVE A SYSTEM WHERE WE HAVE VIRTUAL DRIVES, WE NEED TO PROMPT THE
; USER TO PLACE THE CORRECT DISK IN THE DRIVE.

CHECKSINGLE PROC NEAR
	PUBLIC CHECKSINGLE

	PUSH	AX
	PUSH	BX
	MOV	BX,WORD PTR DS:[DI].FLAGS
 ; IF HARD DRIVE, CANNOT CHANGE DISK.
 ; IF CURRENT OWNER OF PHYSICAL DRIVE, NO NEED TO CHANGE DISKETTE.
	TEST	BL,FNON_REMOVABLE OR FI_OWN_PHYSICAL
	JNZ	SINGLERET
	TEST	BL,FI_AM_MULT		 ; IS THERE A DRIVE SHARING THIS
					 ;   PHYSICAL DRIVE?
	JZ	SINGLERET
; LOOK FOR THE PREVIOUS OWNER OF THIS PHYSICAL DRIVE AND RESET ITS OWNERSHIP
; FLAG.
	MOV	AL,DS:[DI].DRIVENUM	; GET PHYSICAL DRIVE NUMBER
	PUSH	DS			; PRESERVE POINTER TO CURRENT BDS
	PUSH	DI
	PUSH	CS
	POP	DS
	ASSUME	DS:CODE

	MOV	DI,OFFSET START_BDS
SCAN_LIST:
	MOV	BX,WORD PTR [DI].LINK+2     ; GO TO NEXT BDS
	MOV	DI,WORD PTR [DI].LINK
	MOV	DS,BX
	ASSUME	DS:NOTHING

	CMP	DI,-1			; END OF LIST?
	JZ	SINGLE_ERR_RET		; MUST BE ERROR
	CMP	BYTE PTR [DI].DRIVENUM,AL
	JNZ	SCAN_LIST

CHECK_OWN:
	MOV	BX,WORD PTR [DI].FLAGS
	TEST	BL,FI_OWN_PHYSICAL
	JZ	SCAN_LIST
	XOR	BL,FI_OWN_PHYSICAL	    ; RESET OWNERSHIP FLAG
	MOV	WORD PTR DS:[DI].FLAGS,BX
	POP	DI			    ; RESTORE POINTER TO CURRENT BDS
	POP	DS
	XOR	BX,BX
	OR	BL,FI_OWN_PHYSICAL	    ; ESTABLISH CURRENT BDS AS OWNER
	OR	WORD PTR [DI].FLAGS,BX

;
; WE EXAMINE THE FSETOWNER FLAG. IF IT IS SET, THEN WE ARE USING THE CODE IN
; CHECKSINGLE TO JUST SET THE OWNER OF A DRIVE. WE MUST NOT ISSUE THE PROMPT
; IN THIS CASE.
;
	CMP	BYTE PTR CS:[FSETOWNER],1
	JZ	SINGLERET
; TO SUPPORT "BACKWARD" COMPATIBILITY WITH IBM'S "SINGLE DRIVE STATUS BYTE"
; WE NOW CHECK TO SEE IF WE ARE IN A SINGLE DRIVE SYSTEM AND THE APPLICATION
; HAS "CLEVERLY" DIDDLED THE SDSB
	CMP	CS:[SINGLE],2		    ; IF (SINGLE_DRIVE_SYSTEM)
	JNE	SHORT IGNORE_SDSB

	SAVEREG <DS,DI,AX>		    ;	THEN
	MOV	AL,DS:[DI].DRIVELET	    ;	  IF (CURR_DRV == REQ_DRV)
	MOV	AH,AL
	XOR	DI,DI
	MOV	DS,DI
	XCHG	AL,DS:BYTE PTR LSTDRV	    ;	     THEN SWAP(CURR_DRV,REQ_DRV)
	CMP	AH,AL			    ;	     ELSE
	RESTOREREG <AX,DI,DS>		    ;		  SWAP(CURR_DRV,REQ_DRV)
	JE	SINGLERET		    ;		  ISSUE SWAP_DSK_MSG

IGNORE_SDSB:
	CALL	SWPDSK			    ;  ASK USER FOR CORRECT DISK
SINGLERET:
	POP	BX
	POP	AX
	RET

SINGLE_ERR_RET:
	STC
	POP	DI			    ; RESTORE CURRENT BDS
	POP	DS
	JMP	SHORT SINGLERET

BADDRIVE:
	MOV	AL,8			;Sector not found
	jmp	short BadDrive_Ret	;AN004;AN005;AN006;
UnformattedDrive:			;AN004;AN005;AN006;
	mov	al,7			;AN004;Unknown media;AN005;AN006;
BadDrive_Ret:
	STC
IORET:	RET

BOGUSSETTLE:
	MOV	AL,NORMSETTLE		; SOMEONE HAS DIDDLED THE SETTLE
	JMP	GOTSLOWSETTLE
CHECKSINGLE ENDP
;------------------------------------------------------------
;
;	DISK I/O HANDLER
;
;	AL = DRIVE NUMBER (0-6)
;	AH = MEDIA DESCRIPTOR
;	CX = SECTOR COUNT
;	DX = FIRST SECTOR (low)
;	[Start_Sec_H] = FIRST SECTOR (high) ;J.K. 32 bit calculation.
;	DS = CS
;	ES:DI = TRANSFER ADDRESS
;	[RFLAG]=OPERATION (2=READ, 3=WRITE)
;	[VERIFY]=1 FOR VERIFY AFTER WRITE
;
;	IF SUCCESSFUL CARRY FLAG = 0
;	  ELSE CF=1 AND AL CONTAINS ERROR CODE
;
	PUBLIC DISKRD
DISKRD	PROC	NEAR
	MOV	CS:[RFLAG],ROMREAD

DISKIO:
	MOV	BX,DI			; ES:BX = TRANSFER ADDRESS
	CALL	SETDRIVE		; MAP LOGICAL AND PHYSICAL
	MOV	AL,BYTE PTR DS:[DI].MEDIAD
	MOV	CS:MEDBYT,AL		; PRESERVE MEDIA BYTE FOR DRIVE FOR USE
					; IN DETERMINING MEDIA CHANGE.
	JCXZ	IORET
;SB34DISK006******************************************************************
;SB	See if the Media is formatted or not by checking the flags field in
;SB	in the BDS.  If it is unformatted we cannot allow I/O, so we should
;SB	go to the error exit at label UnformattedDrive.  2LOCS

	test	word ptr ds:[di].FLAGS, UNFORMATTED_MEDIA
	jnz	UnformattedDrive
;SB34DISK006******************************************************************
	mov	cs:[SECCNT],CX		;save sector count
	MOV	CS:[SPSAV],SP		; SAVE SP

; ENSURE THAT WE ARE TRYING TO ACCESS VALID SECTORS ON THE DRIVE
;

	mov	ax,dx			;AN000; save DX to AX
	xor	si,si			;AN000;
	add	dx,cx			;AN000;
	adc	si,0			;AN000;
	cmp	[di].DrvLim, 0		;AN000; Is this drive > 32 bit sector ?
	je	Sanity32		;AN000;
	cmp	si,0			;AN000;
	jne	BADDRIVE		;AN000;
	cmp	dx, [di].DrvLim 	;AN000;
	ja	BADDRIVE		;AN000;
	jmp	short SanityOK		;AN000;
Sanity32:				;AN000;
	add	si, cs:[Start_Sec_H]	;AN000;
	cmp	si, [di].DrvLim_H	;AN000;
	jb	SanityOK		;AN000;
	ja	BADDRIVE		;AN000;
	cmp	dx, [di].DrvLim_L	;AN000;
	ja	BADDRIVE		;AN000;
SanityOK:				;AN000;
	mov	dx,cs:[Start_Sec_H]	;AN000;
	add	ax,word ptr [di].HIDSEC_L    ;AN000;
	adc	dx,word ptr [di].Hidsec_H    ;AN000;
;J.K. Now DX;AX have the physical first sector.
;Since the following procedures is going to destroy AX, let's
;save it temporarily to SAVED_WORD.
	mov	cs:[Saved_Word], ax	;AN000; Save the sector number (low)

;	 MOV	 SI,DX
;	 ADD	 SI,CX
;	 ADD	 DX,WORD PTR [DI].HIDSEC ; ADD IN THE HIDDEN SECTORS
;	 CMP	 SI,WORD PTR [DI].DRVLIM ; COMPARE AGAINST DRIVE MAX
;	 JA	 BADDRIVE

; SET UP POINTER TO DISK BASE TABLE IN [DPT]. WE CANNOT ASSUME THAT IOSETUP
; WILL DO IT BECAUSE WE WILL SKIP THE SET UP STUFF WITH HARD DISKS.
	PUSH	DS
	XOR	AX,AX
	MOV	DS,AX
	LDS	SI,DWORD PTR DS:[DSKADR]; CURRENT DISK PARM TABLE
	MOV	WORD PTR CS:DPT,SI
	MOV	WORD PTR CS:DPT+2,DS
	POP	DS
	TEST	WORD PTR [DI].FLAGS,FNON_REMOVABLE
	JNZ	SKIP_SETUP
	CALL	CHECKSINGLE
;
; CHECK TO SEE IF WE HAVE PREVIOUSLY NOTED A CHANGE LINE.  THE ROUTINE
; RETURNS IF EVERYTHING IS OK.	OTHERWISE, IT POPS OFF THE STACK AND RETURNS
; THE PROPER ERROR CODE.
;
;----------------------------------------|
; WARNING: DO NOT CHANGE THE FOLLOWING. ;|
;	   IT GETS PATCHED IN MSINIT	;|
	PUBLIC DISKIO_PATCH		;|
DISKIO_PATCH:				;|
	CALL	CHECKLATCHIO		;|
;----------------------------------------|
;
; SET UP TABLES AND VARIABLES FOR I/O
	CALL	IOSETUP
;
; NOW THE SETTLE VALUES ARE CORRECT FOR THE FOLLOWING CODE
;
SKIP_SETUP:
;J.K. 32 bit sector calculation.
; DX;[Saved_Word] = starting sector number.
	mov	ax,dx			;AN000;
	xor	dx,dx			;AN000;
	DIV	WORD PTR [DI].SECLIM	;DIVIDE BY SEC PER TRACK
	mov	cs:[Temp_H],ax		;AN000;
	mov	ax, cs:[Saved_Word]	;AN000; Restore the lower word
	div	word ptr [di].SecLim	;AN000;
;Now, [Temp_H],AX = track #, DX = sector
	INC	DL			;Sector number is 1 based.
	MOV	CS:[CURSEC],DL		;SAVE CURRENT SECTOR
	MOV	CX,WORD PTR [DI].HDLIM	;GET NUMBER OF HEADS

	push	ax			;AN000;
	XOR	DX,DX			;DIVIDE TRACKS BY HEADS PER CYLINDER
	mov	ax, cs:[Temp_H] 	;AN000;
	DIV	CX
	mov	cs:[Temp_H],ax		;AN000;
	pop	ax			;AN000;
	div	cx			;AN000;
;Now, [Temp_H],AX = cyliner #, DX = head
	cmp	cs:[Temp_H],0		;AN000;
	ja	BADDRIVE_brdg		;AN000;
	cmp	AX, 1024		;AN000; 2**10 currently maxium for track #.
	ja	BADDRIVE_brdg		;AN000;
	MOV	CS:[CURHD],DL		;SAVE CURRENT HEAD
	MOV	CS:[CURTRK],AX		;SAVE CURRENT TRACK

;
; WE ARE NOW SET UP FOR THE I/O.  NORMALLY, WE CONSIDER THE DMA BOUNDARY
; VIOLATIONS HERE.  NOT TRUE.  WE PERFORM THE OPERATION AS IF EVERYTHING IS
; SYMMETRIC; LET THE INT 13 HANDLER WORRY ABOUT THE DMA VIOLATIONS.
;
	MOV	AX, CS:[SECCNT]
	CALL	BLOCK
	CALL	DONE
	RET
DISKRD	ENDP

;
BADDRIVE_Brdg:jmp Baddrive
;

; SET THE DRIVE-LAST-ACCESSED FLAG FOR DISKETTE ONLY.  WE KNOW THAT THE HARD
; DISK WILL NOT BE REMOVED.
; DS:DI -> CURRENT BDS.
; AX,CX,SI ARE DESTROYED.
;
	PUBLIC IOSETUP
IOSETUP PROC	NEAR
	MOV	AL,[DI].DRIVENUM
	MOV	CS:[TIM_DRV],AL 	; SAVE DRIVE LETTER
;
; DETERMINE PROPER HEAD SETTLE VALUES
;
	cmp	cs:[Media_Set_For_Format], 0	;AN012;
	jne	Skip_DPT_Setting		;AN012;
	MOV	CX,DS
	LDS	SI,DWORD PTR CS:[DPT]	; GET POINTER TO DISK BASE TABLE
	MOV	AL,CS:[EOT]
	MOV	[SI].DISK_EOT,AL	; BUMP FOR US
	MOV	AL,[SI].DISK_MOTOR_STRT ; PRESERVE OLD MOTOR START TIME
	MOV	CS:MOTORSTARTUP,AL
;
; FOR 3.5" DRIVES, BOTH EXTERNAL AS WELL AS ON THE K09, WE NEED TO SET THE
; MOTOR START TIME TO 4. THIS CHECKING FOR EVERY I/O IS GOING TO AFFECT
; PERFORMANCE ACROSS THE BOARD, BUT IS NECESSARY!! - RS
;
	PUSH	ES
	MOV	ES,CX			; ES:DI -> TO CURRENT BDS
	CMP	BYTE PTR ES:[DI].FORMFACTOR,FFSMALL
	JNZ	MOTOR_START_OK
	MOV	AL,4
	XCHG	AL,[SI].DISK_MOTOR_STRT
MOTOR_START_OK:
	POP	ES
;
; DS:SI NOW POINTS TO DISK PARAMETER TABLE.  GET CURRENT SETTLE AND SET FAST
; SETTLE
;
	XOR	AL,AL
	INC	AL			; IBM WANTS FAST SETTLE TO BE 1 - RS.
	XCHG	AL,[SI].DISK_HEAD_STTL	; GET SETTLE AND SET UP FOR FAST
	MOV	CS:SETTLECURRENT,AL
	MOV	AL,NORMSETTLE		; SOMEONE HAS DIDDLED THE SETTLE
GOTSLOWSETTLE:
	MOV	DS,CX
	MOV	CS:SETTLESLOW,AL
Skip_DPT_Setting:			;AN012;
	RET
;
; SET TIME OF LAST ACCESS, AND RESET DEFAULT VALUES IN THE DPT.
;
DONE:
	TEST	WORD PTR [DI].FLAGS,FNON_REMOVABLE
	JNZ	RETZ			; DO NOT SET FOR NON-REMOVABLE MEDIA
	CALL	SET_TIM 		; SET TIME OF LAST ACCESS FOR DRIVE
;
; RESTORE HEAD SETTLE AND EOT VALUES
;
DIDDLEBACK:
	pushf					;AN013;Save flag
	cmp	cs:[Media_Set_For_Format], 0	;AN012;
	jne	NoDiddleBack			;AN012;
	PUSH	AX
	MOV	DX,DS
	MOV	AL,CS:SETTLECURRENT
	MOV	AH,CS:MOTORSTARTUP
	LDS	SI,CS:DPT
;	 MOV	 [SI].DISK_EOT,9	 ;J.K. 4/25/86. Should not change the EOT value
					;of diskbase to 9.  This cause a problem
					;with 1.44M diskette in Polaris when the user
					;issue INT 13 with the default system
					;diskbase.
	mov	[si].DISK_EOT,9 	;J.K. 11/5/86. For compatibility reason, return
					;back to set it to 9 ( PTM826 ).
	MOV	[SI].DISK_HEAD_STTL,AL
	MOV	[SI].DISK_SECTOR_SIZ,2
	MOV	[SI].DISK_MOTOR_STRT,AH
	MOV	DS,DX
	POP	AX
NoDiddleBack:				;AN013;
	popf				;AN013;Restore flag
RETZ:
	RET

;READ THE NUMBER OF SECTORS SPECIFIED IN AX, HANDLING TRACK BOUNDARIES
;DS:DI -> BDS FOR THIS DRIVE
BLOCK:
	OR	AX,AX			;SEE IF ANY SECTORS TO READ
	JZ	RETZ
;Fixed disk will not be restricted to the track-by-track basis. -J.K.4/10/86
	test	word ptr [di].Flags, fNon_Removable  ;J.K. Fixed disk?
	jz	BLOCK_FLOPPY				;J.K.
;SB34DISK002*****************************************************************
;SB	Check to see if multi track operation is allowed.  If not
;SB	we have to go to the block_floppy below to break up the operation.
;SB		2 LOCS
	test	word ptr CS:MulTrk_Flag, MulTrk_ON
	jz	BLOCK_FLOPPY
;SB34DISK002*****************************************************************
	call	DISK					;J.K.
	xor	ax,ax
	RET						;J.K.
BLOCK_FLOPPY:						;J.K.4/10/86
;
; READ AT MOST 1 TRACK WORTH.  PERFORM MINIMIZATION AT SECTOR / TRACK
;
	MOV	CL,BYTE PTR [DI].SECLIM
	INC	CL
	SUB	CL,CS:CURSEC		; LEEAC 3.20 ADD SEGMENT OVERRIDE
	XOR	CH,CH
	CMP	AX,CX
	JAE	GOTMIN
	MOV	CX,AX
GOTMIN:
;
; AX IS THE REQUESTED NUMBER OF SECTORS TO READ
; CX IS THE NUMBER THAT WE CAN DO ON THIS TRACK
;
	PUSH	AX
	PUSH	CX
	MOV	AX,CX			; AL IS NUMBER OF SECTORS TO READ
	CALL	DISK
	POP	CX
	POP	AX
;
; CX IS THE NUMBER OF SECTORS JUST TRANSFERRED
;
	SUB	AX,CX			; REDUCE SECTORS-REMAINING BY LAST I/O
	SHL	CL,1
	ADD	BH,CL			; ADJUST TRANSFER ADDRESS
	JMP	BLOCK
IOSETUP ENDP

DskErr_Brdg: jmp DskErr 		;AN003;

;
;PERFORM DISK I/O WITH RETRIES
; AL = NUMBER OF SECTORS (1-8, ALL ON ONE TRACK)
; DI POINT TO DRIVE PARAMETERS
; ES:BX = TRANSFER ADDRESS (MUST NOT CROSS A 64K PHYSICAL BOUNDARY)
; [RFLAG] = 2 IF READ, 3 IF WRITE
; [VERIFY] = 0 FOR NORMAL, 1 FOR VERIFY AFTER WRITE

	PUBLIC DISK
DISK	PROC	NEAR
	MOV	BP,MAXERR		; SET UP RETRY COUNT
	mov	cs:VRetry_Cnt, BP	;AN003;Verify op. retry cnt for Write-Verify.
	mov	cs:Soft_ECC_Cnt, BP	;AN003;Soft ECC error retry count.
	MOV	AH,CS:RFLAG		;GET READ/WRITE INDICATOR

RETRY:
	PUSH	AX

	MOV	DX,CS:[CURTRK]		;LOAD CURRENT CYLINDER

	test	word ptr [di].FLAGS, fNon_Removable ;Fixed disk? - J.K. 4/7/86
	jz	DISK_NOT_MINI		;no, skip this. - J.K. 4/7/86
	cmp	[di].IsMini, 1		;Is this a mini disk? - J.K. 4/7/86
	jnz	DISK_NOT_MINI		;No. continue to next.- J.K. 4/7/86
	add	dx, [di].Hidden_Trks	;else add hidden trks.- J.K. 4/7/86
DISK_NOT_MINI:				;J.K. 4/7/86
	ROR	DH,1
	ROR	DH,1

	OR	DH,CS:[CURSEC]
	MOV	CX,DX
	XCHG	CH,CL			; CL = SECTOR, CH = CYLINDER
	MOV	DH,BYTE PTR CS:[CURHD]	; LOAD CURRENT HEAD NUMBER AND
	MOV	DL,BYTE PTR [DI].DRIVENUM  ; PHYSICAL DRIVE NUMBER
	CMP	BYTE PTR [DI].FORMFACTOR,FFHARDFILE
	JZ	DO_FAST 		; HARD FILES USE FAST SPEED
; IF WE HAVE [STEP_DRV] SET TO -1, WE USE THE SLOW SETTLE TIME.
; THIS HELPS WHEN WE HAVE JUST DONE A RESED DISK OPERATION AND THE HEAD HAS
; BEEN MOVED TO ANOTHER CYLINDER - THE PROBLEM CROPS UP WITH 3.5" DRIVES.
	CMP	CS:[STEP_DRV],-1
	JZ	DO_WRITEJ
	CMP	AH,ROMREAD		; ARR 2.20
	JE	DO_FAST
	CMP	AH, ROMVERIFY
	JE	DO_FAST
DO_WRITEJ:
; READS ALWAYS FAST, UNLESS WE HAVE JUST DONE A DISK RESET OPERATION
	JMP	DO_WRITE		; ARR 2.20 READS ALWAYS FAST
DO_FAST:				; ARR 2.20
	CALL	FASTSPEED		; MZ  2.21 CHANGE SETTLE MODE
TESTERR:				; MZ  2.21
	JC	DSKERR_brdg
; SET DRIVE AND TRACK OF LAST ACCESS
	MOV	CS:[STEP_DRV],DL	; ARR 2.20 SET DRIVE
	MOV	BYTE PTR [DI].TRACK,CH	; ARR 2.20 SAVE TRACK
NO_SET:
	CMP	CS:WRTVERIFY,103H	; CHECK FOR WRITE AND VERIFY
	JZ	DOVERIFY
NOVERIFY:
	POP	AX

;SB34DISK003*****************************************************************
;SB	Check the flags word in the BDS to see if the drive is non removable
;SB	If not we needn't do anything special
;SB	If it is a hard disk then check to see if multi-track operation
;SB	is specified.  If specified we don't have to calculate for the next
;SB	track since we are already done.  So we can go to the exit of this 
;SB	routine.	5 LOCS

	test	word ptr [di].FLAGS, fNON_REMOVABLE
	jz	ITS_REMOVABLE
	test	CS:MulTrk_Flag, MulTrk_ON
	jnz	DISK_RET
ITS_REMOVABLE:
;SB34DISK003*****************************************************************
	AND	CL,03FH 		; ELIMINATE CYLINDER BITS FROM SECTOR
	XOR	AH,AH
	SUB	CS:[SECCNT],AX		; REDUCE COUNT OF SECTORS TO GO
	ADD	CL,AL			; NEXT SECTOR
	MOV	CS:[CURSEC],CL
	CMP	CL,BYTE PTR [DI].SECLIM ; SEE IF SECTOR/TRACK LIMIT REACHED
	JBE	DISK_RET
NEXTTRACK:
	MOV	CS:[CURSEC],1		; START WITH FIRST SECTOR OF NEXT TRACK
	MOV	DH,CS:[CURHD]
	INC	DH
	CMP	DH,BYTE PTR [DI].HDLIM
	JB	NOXOR
	XOR	DH,DH
	INC	CS:[CURTRK]		;NEXT TRACK
NOXOR:
	MOV	CS:[CURHD],DH
DISK_RET:
	CLC				; LEEAC
	RET
DISK	ENDP


; THE REQUEST IS FOR WRITE.  DETERMINE IF WE ARE TALKING ABOUT THE SAME
; TRACK AND DRIVE.  IF SO, USE THE FAST SPEED.

DO_WRITE PROC	NEAR
	CMP	DL,CS:[STEP_DRV]	; ARR 2.20
	JNZ	DO_NORM 		;  WE HAVE CHANGED DRIVES

	CMP	CH,BYTE PTR [DI].TRACK	; ARR 2.20
	JZ	DO_FAST 		; WE ARE STILL ON THE SAME TRACK

DO_NORM:
	CALL	NORMSPEED
	JMP	SHORT TESTERR		; MZ  2.21 TEST FOR ERROR
DO_WRITE ENDP
;
; WE HAVE A VERIFY REQUEST ALSO.  GET STATE INFO AND GO VERIFY
;

DOVERIFY PROC	NEAR
	POP	AX			; RESTORE SECTOR COUNT
	PUSH	AX
	MOV	AH,ROMVERIFY		; REQUEST VERIFY
	CALL	FASTSPEED		; MZ  2.21 CHANGE SETTLE MODE
	JNC	NOVERIFY

;SB34DISK004**************************************************************
;SB	check the error returned in AH to see if it is a SOFT ECC error.
;SB	If it is not we needn't do anything special.  If it is a SOFT
;SB	ECC error then decrement the SOFT_ECC_CNT error retry count. If
;SB	this retry count becomes 0 then we just  ignore the error and go to
;SB	No_verify but if we can still try then we call the routine to reset
;SB	the disk and go to DSKerr1 to retry the operation.	6 LOCS

	cmp	ah,11h			;SOFT ECC error ?
	jnz	Not_SoftECC_Err
	dec	SOFT_ECC_CNT
	jz	NoVerify		;no more retry
	call	ResetDisk		;reset disk
	jmp	DskErr1			;retry

;SB34DISK004**************************************************************

Not_SoftECC_Err:			;AN003;Other error.
	call	ResetDisk		;AN003;
	dec	VRetry_Cnt		;AN003;
	jmp	DskErr0 		;AN003;
DOVERIFY ENDP
;
; NEED TO SPECIAL CASE THE CHANGE-LINE ERROR AH=06H.  IF WE GET THIS, WE
; NEED TO RETURN IT.
;
;----------------------------------------|
; WARNING: DO NOT CHANGE THE FOLLOWING. ;|
;	   IT GETS PATCHED IN MSINIT	;|
	PUBLIC DSKERR			;|
DSKERR	PROC	NEAR			;|
	CALL	CHECKIO 		;|
;---------------------------------------;|

	cmp	cs:MultiTrk_Format_Flag, 1 ;AN007;Multi trk format request?
	jne	DoChkAgain		   ;AN007;
	mov	bp, 1			   ;AN007;No more retry.
	mov	cs:MultiTrk_Format_Flag, 0 ;AN007;Clear the flag.
DoChkAgain:				   ;AN007;
	CALL	AGAIN
DskErr0:				;AN003;
	JZ	HARDERR
	test	word ptr [di].FLAGS, fNon_Removable	;AN009;
	jnz	Skip_TimeOut_Chk			;AN009;
	CMP	AH,80H			;TIMEOUT?
	JZ	HARDERR 		;***
Skip_TimeOut_Chk:			;AN009;
	cmp	ah, 0cch		;AN003;Write Fault error?
	jz	Write_Fault_Err 	;AN003; Then, don't retry.
	mov	cs:Soft_ECC_Cnt, MAXERR ;AN003;Set Soft_ECC_Cnt back to MAXERR
DSKERR1:
	POP	AX			;RESTORE SECTOR COUNT
	JMP	RETRY

Write_Fault_Err:			;AN003;
	mov	bp, 1			;AN003;Just retry only once for Write Fault error.
	jmp	DskErr1 		;AN003;

HARDERR:
	PUBLIC	HARDERR

	CALL	MAPERROR

HARDERR2:			; FOR ROUTINES THAT CALL MAPERROR THEMSELVES
	PUBLIC	HARDERR2

	MOV	CS:[TIM_DRV],-1 	;FORCE A MEDIA CHECK THROUGH ROM
	MOV	CX,CS:SECCNT		;GET COUNT OF SECTORS TO GO
	MOV	SP,CS:[SPSAV]		;RECOVER ENTRY STACK POINTER
;
; SINCE WE ARE PERFORMING A NON-LOCAL GOTO, RESTORE THE DISK PARAMETERS
;
MEDBYT_OK:
	CALL	DIDDLEBACK
	RET				;AND RETURN
DSKERR	ENDP

;
; CHANGE SETTLE VALUE FROM SETTLECURRENT TO WHATEVER IS APPROPRIATE
; NOTE THAT THIS ROUTINE IS NEVER CALLED FOR A FIXED DISK.
;
NORMSPEED PROC	NEAR
	cmp	cs:[Media_Set_For_Format], 0	;AN012;
	jne	FASTSPEED			;AN012;
	PUSH	DS
	PUSH	AX
	MOV	AL,CS:SETTLESLOW
	LDS	SI,CS:DPT		; CURRENT DISK PARM TABLE
	MOV	[SI].DISK_HEAD_STTL,AL
	POP	AX
	POP	DS
	CALL	FASTSPEED
	PUSH	DS
	LDS	SI,CS:DPT
	MOV	[SI].DISK_HEAD_STTL,1	; 1 IS FAST SETTLE VALUE
	POP	DS
	RET
NORMSPEED ENDP

FASTSPEED PROC	NEAR
;
; IF THE DRIVE HAS BEEN MARKED AS TOO BIG (I.E. STARTING SECTOR OF THE
; PARTITION IS > 16 BITS, THEN ALWAYS RETURN DRIVE NOT READY.
;
	TEST	BYTE PTR [DI].FATSIZ,FTOOBIG
 IF iTEST
	JZ	READY
	JMP	NOTREADY
READY:
 ELSE
	JNZ	NOTREADY
 ENDIF

	MESSAGE FTESTINIT,<"<">
	MNUM	FTESTINIT,AX
	MESSAGE FTESTINIT,<",">
	MNUM	FTESTINIT,ES
	MESSAGE FTESTINIT,<":">
	MNUM	FTESTINIT
	MESSAGE FTESTINIT,<",">
	MNUM	FTESTINIT,CX
	MESSAGE FTESTINIT,<",">
	MNUM	FTESTINIT,DX
	MESSAGE FTESTINIT,<">">
	INT	13H
DEATH:
	RET
NOTREADY:
	STC
	MOV	AH,80H
	JMP	DEATH
FASTSPEED ENDP

; MAP ERROR RETURNED BY ROM IN AH INTO CORRESPONDING CODE TO BE RETURNED TO
; DOS IN AL.
;
MAPERROR PROC	NEAR
	PUBLIC	MAPERROR

	PUSH	CX			; SAVE CX
	PUSH	CS
	POP	ES			;MAKE ES THE LOCAL SEGMENT
	MOV	AL,AH			;PUT ERROR CODE IN AL
	MOV	CS:[LSTERR],AL		;TERMINATE LIST WITH ERROR CODE
	MOV	CX,NUMERR		;NUMBER OF POSSIBLE ERROR CONDITIONS
	MOV	DI,OFFSET ERRIN      ;POINT TO ERROR CONDITIONS
	REPNE	SCASB
	MOV	AL,CS:[DI + NUMERR - 1]    ;GET TRANSLATION
	POP	CX			; RESTORE CX
	STC				;FLAG ERROR CONDITION
	RET
MAPERROR ENDP

; SET THE TIME OF LAST ACCESS FOR THIS DRIVE. THIS IS DONE ONLY FOR REMOVABLE
; MEDIA.

	PUBLIC SET_TIM
SET_TIM PROC	NEAR
	PUSH	AX
;SB33018******************************************************************
	xor	AH, AH			; set command to get time      ;SB;3.30*
	int	1Ah			; call rom-bios timer function ;SB;3.30*
;SB33018******************************************************************
	OR	AL,AL
	JZ	NOROLL3
	INC	CS:[DAYCNT]		; CATCH ROLLOVER
NOROLL3:
; WE HAVE THE NEW TIME. IF WE SEE THAT THE TIME HAS PASSED, THEN WE RESET
; THE THRESHOLD COUNTER...
	CMP	DX,WORD PTR [DI].TIM_LO
	JNZ	SETACCESS
	CMP	CX,WORD PTR [DI].TIM_HI
	JZ	DONE_SET
SETACCESS:
	MOV	BYTE PTR CS:[ACCESSCOUNT],0
	MOV	WORD PTR [DI].TIM_LO,DX 	    ;SAVE IT
	MOV	WORD PTR [DI].TIM_HI,CX
DONE_SET:
	CLC
	POP	AX
	RET
SET_TIM ENDP

	ASSUME	CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING

;
; THIS IS THE TRUE INT 13 HANDLER.  WE PARSE THE REQUEST TO SEE IF THERE IS
; A DMA VIOLATION.  IF SO, DEPENDING ON THE FUNCTION, WE:
;   READ/WRITE	BREAK THE REQUEST INTO THREE PIECES AND MOVE THE MIDDLE ONE
;	INTO OUR INTERNAL BUFFER.
;   FORMAT	COPY THE FORMAT TABLE INTO THE BUFFER
;   VERIFY	POINT THE TRANSFER ADDRESS INTO THE BUFFER
;
; THIS IS THE BIGGEST BOGOSITY OF ALL.	THE IBM CONTROLLER DOES NOT HANDLE
; OPERATIONS THAT CROSS PHYSICAL 64K BOUNDARIES.  IN THESE CASES, WE COPY
; THE OFFENDING SECTOR INTO THE BUFFER BELOW AND DO THE I/O FROM THERE.
;
INT13FRAME  STRUC
OLDBP	DW  ?
OLDAX	DW  ?
OLDBX	DW  ?
OLDCX	DW  ?
OLDDX	DW  ?
OLDDD	DD  ?
OLDF	DW  ?
INT13FRAME  ENDS

;J.K. 1/30/87 To handle the ps2_30 machine INT 13h, AH = 8 Problem.
;Save Registers here.
Save_AX   DW  ?
Save_BX   DW  ?
Save_CX   DW  ?
Save_DX   DW  ?
Save_DI   DW  ?
Save_SI   DW  ?
Save_BP   DW  ?
Save_DS   DW  ?
Save_ES   DW  ?
Prev_DX   DW  ?
Save_Flag DW  ?


;   ENTRY CONDITIONS:
;	AH = FUNCTION
;	AL = NUMBER OF SECTORS
;	ES:BX = DMA ADDRESS
;	CX = PACKED TRACK AND SECTOR
;	DX = HEAD AND DRIVE
;   OUTPUT CONDITIONS:
;	NO DMA VIOLATION.
;
	PUBLIC BLOCK13
BLOCK13 PROC	FAR
;
; LET THE OPPERATION PROCEED.  IF THERE IS A DMA VIOLATION, THEN WE DO THINGS.
;
	MOV	CS:PREVOPER,AX		; SAVE REQUEST
	PUSHF
	CMP	AH,ROMFORMAT
	JNZ	NOT_FORMAT
; SET CHANGED BY FORMAT BIT FOR ALL LOGICAL DRIVES USING THIS PHYSICAL DRIVE
;---------------------------------------------------------|
; WARNING: DO NOT CHANGE THE FOLLOWING.
; IT GETS PATCHED IN AT INIT TIME			  |
	PUBLIC CHANGED_PATCH
CHANGED_PATCH:
	MOV	WORD PTR CS:[FLAGBITS],FCHANGED_BY_FORMAT+FCHANGED
	CALL	SET_CHANGED_DL		; INDICATE THAT MEDIA CHANGED BY FORMAT
;							  |
;---------------------------------------------------------|
NOT_FORMAT:
	test	dl, 80h			; floppy or hard disk?
	jnz	not_floppy		; if hard, skip this nonsense
	cmp	cs:EC35_Flag, 0		; any electrically compat. drives?
	jz	not_floppy		; no; proceed unhindered
	SAVEREG	<ax, bx, cx>
	mov	cl, dl			; turn drive number into bit map:
	mov	al, 1			;   assume drive 0
	shl	al, cl			;   shift over correct number of times
	test	al, cs:EC35_Flag	; is THIS drive an electrically compatible 3.5 incher?
	jz	not_EC35		; no; don't change anything
	mov	bl, dl			; which drive was it?
	xor	bh, bh			; need only one byte of index
	push	es			; need a segment register
	mov	ax, 40h			; the machine state byte is in the...
	mov	es, ax			; ...segment at 40h
	mov	byte ptr es:[90h+bx], 93H	; establish drive type as: (360k disk in 360k drive, no double-stepping, 250 kbs transfer rate)
	pop	es			; fix up register again
not_EC35:
	RESTOREREG <cx, bx, ax>
not_floppy:
	cmp	cs:[model_byte], mdl_ps2_30	; is this a ps2/30?
	jne	not_ps2_30		; if not, just do normal call
	cmp	ah, 8			;J.K. 1/30/87 Read Driver Parm ?
	je	ps2_30_Problem		;J.K. 1/30/87
	cmp	ah, 15h
	je	ps2_30_Problem
not_ps2_30:
	CALL	ORIG13			; SIMULATE INT 13
	JC	GOTERR13_br		; ERROR?
	RET	2			; NO, RETURN AND CLEAR FLAGS

GOTERR13_br: jmp Goterr13

;J.K.1/30/87 ps2_30 machine has some problem with AH=8h(Read Drive Parm), Int 13h.
;This function does not reset the common buses after the execution.
;To solve this problem, when we detect AH=8h, then we will save the result and
;will issue AH=1 (Read Status) call to reset the buses.

ps2_30_Problem: 			;J.K. 1/30/87; ps2_30 = PS2 Model 30.
	mov	cs:Prev_DX, DX		;save orignal drive number
	call	Orig13			;Do "Read drive parm"

	mov	cs:Save_AX, AX		;Save registers,flag
	mov	cs:Save_BX, BX
	mov	cs:Save_CX, CX
	mov	cs:Save_DX, DX
	mov	cs:Save_DI, DI
	mov	cs:Save_SI, SI
	mov	cs:Save_BP, BP
	mov	cs:Save_DS, DS
	mov	cs:Save_ES, ES
	pushf
	pop	cs:Save_Flag

	mov	dx, cs:Prev_DX		;restore orignal drive
	pushf
	mov	ah, 1			;Read Status.
	call	Orig13			;Reset the bus as a side effect of this call.

	mov	AX, cs:Save_AX		;restore registers,flag
	mov	BX, cs:Save_BX
	mov	CX, cs:Save_CX
	mov	DX, cs:Save_DX
	mov	DI, cs:Save_DI
	mov	SI, cs:Save_SI
	mov	BP, cs:Save_BP
	mov	DS, cs:Save_DS
	mov	ES, cs:Save_ES
	push	cs:Save_Flag
	popf
	jc	GotErr13		;AH=8 had been an error?
	ret	2

;
; SOME KIND OF ERROR OCCURRED.	SEE IF IT IS DMA VIOLATION
;
GOTERR13:
	PUSHF
	cmp	ah, 09h 		;AN011; DMA error?
	je	Chk_ValidMedia_ERR13	;AN011;
	cmp	ah, 11h 		;AN011; ECC error?
	je	Chk_ValidMedia_ERR13	;AN011;
	jmp	Skip_Ecc_Check		;AN011; Other error.  Just return back.

Chk_ValidMedia_ERR13:			;AN011;If SetDrive fails, then just
	push	ds			;AN011;    return back to INT 13h caller,
	push	di			;AN011;      without performing ECC, DMA
	push	ax			;AN011; 	error handling.
	mov	byte ptr cs:[Phys_Drv], 1 ;AN011;
	mov	al, dl			;AN011;
	call	SetDrive		;AN011;
	mov	byte ptr cs:[Phys_Drv], 0 ;AN011;
	pop	ax			;AN011;
	pop	di			;AN011;
	pop	ds			;AN011;
	jc	Skip_Ecc_Check		;AN011;

; TEST OF BIT PATTERN 08H LET OTHER ERRORS BE PASSED AS DMA ERRORS - PTR 32D0519
;	TEST	AH,08H			; DMA BIT
	CMP	AH, 09H 		; DMA ERROR CODE
	JNZ	CHECK_ECC
	JMP	GOTDMAERR
CHECK_ECC:
;J.K. AN003; Soft ECC bug is only applied to PC1 and PC-XT. So, we will enforce
;this ECC error handler for them.   Also, since CMC hardfiles in PC AT also
;generate a lot of unnecessary ECC errors, we will still cover PC ATs as
;it is done in the earlier version of MSBIO.
;During Format/Verify operation, we are going to consider any Soft Ecc as a
;hard error.

;SB34DISK005*****************************************************************
;SB	See if the machine we are operating on is a PC, XT or AT by checking
;SB	the model byte.  The soft ECC bug is only on these machines and if
;SB	the machine we are operating on is not one of these three then we
;SB	needn't do anything special.  If we are operating one these however
;SB	we check to see if the error occured during format by checking
;SB	media_set_for_format.  If it did occur during format we cannot do
;SB	anything but if not during format then check to see if the error
;SB	returned in AH is the SOFT_ECC error and if so go to OK11 since
;SB	the error can be ignored.  6 LOCS

	cmp	cs:[Media_Set_For_Format], 1 ; formatting?
	je	Skip_Ecc_Check
	cmp	cs:[Model_Byte], 0FEh	; PC or XT?
	jae	Go_Chk_Ecc
	cmp	cs:[Model_Byte], 0FBh	; XT?
	je	Go_Chk_Ecc
	cmp	cs:[Model_Byte], 0FCh
	jne	Skip_Ecc_Check
	cmp	cs:[Secondary_Model_Byte], 2 ; AT?
	ja	Skip_Ecc_Check
Go_Chk_Ecc:				; for PC, XT, AT
	CMP	AH,11H
	JZ	OK11
Skip_Ecc_Check: 			;AN003; Just return back to INT 13h caller.

;SB34DISK005*****************************************************************

	POPF
	RET	2
;
; WE HAVE AN ERROR STATUS 11H.	THIS INDICATES AN ECC-CORRECTED ERROR.	NOTE
; THAT THIS INDICATES THAT THE DATA IS PROBABLY CORRECT BUT NOT CERTAINLY
; CORRECT. THE ROMS ON PC-1S AND PC_XTS HAVE A 'BUG' IN THAT IF AN ECC ERROR
; OCCURS FOR A MULTI-SECTOR READ, ONLY THE SECTORS UP TO THE ONE WHERE THE
; ERROR OCCURRED ARE READ IN. WE HAVE NO WAY OF KNOWING HOW MANY WERE READ IN
; THIS CASE, SO WE REDO THE OPERATION, READING ONE SECTOR AT A TIME. IF WE
; GET AN ECC ERROR ON READING ONE SECTOR, WE IGNORE THE ERROR BECAUSE THE
; SECTOR HAS BEEN READ IN.
;
	PUBLIC OK11
OK11:
;	 POPF
;J.K. 8/29/86 Here, it is better reset the system. So, we are going to
;call Orig13 again

	xor	ah, ah
	call	Orig13			;reset. Don't care about the result

	MOV	AX,CS:[PREVOPER]	; RETRIEVE REQUEST
;
; THIS WILL PROVIDE A TERMINATION POINT.
;
	CMP	AL,1			; IF REQUEST FOR ONE SECTOR, ASSUME OK
	JNZ	ECC_ERR_HANDLE
	XOR	AH,AH			; CLEAR CARRY TOO!
	RET	2

	PUBLIC ECC_ERR_HANDLE
ECC_ERR_HANDLE:
	SAVEREG <BX,CX,DX>
	MOV	CS:[NUMBER_OF_SEC],AL
LOOP_ECC:
	MOV	AX,CS:[PREVOPER]
	MOV	AL,1			; REQUEST FOR ONE SECTOR ONLY
;
; WE DO READS ONE SECTOR AT A TIME. THIS ENSURES THAT WE WILL EVENTUALLY
; FINISH THE REQUEST SINCE ECC ERRORS ON ONE SECTOR DO READ IN THAT SECTOR.
;
; WE NEED TO PUT IN SOME "INTELLIGENCE" INTO THE ECC HANDLER TO HANDLE READS
; THAT ATTEMPT TO READ MORE SECTORS THAN ARE AVAILABLE ON A PARTICULAR
; TRACK.
; WE CALL CHECK_WRAP TO SET UP THE SECTOR #, HEAD # AND CYLINDER # FOR
; THIS REQUEST.
; AT THIS POINT, ALL REGISTERS ARE SET UP FOR THE CALL TO ORIG13, EXCEPT
; THAT THERE MAY BE A STARTING SECTOR NUMBER THAT IS BIGGER THAN THE NUMBER
; OF SECTORS ON A TRACK.
;
	CALL	CHECK_WRAP		; GET CORRECT PARAMETERS FOR INT 13
	PUSHF
	CALL	ORIG13
	JNC	OK11_OP
	CMP	AH,11H			; ONLY ALLOW ECC ERRORS
	JNZ	OK11_EXIT_err		;J.K. 8/26/86 Other error?
	mov	ah, 0			;J.K. ECC error. Reset the system again.
	pushf
	call	Orig13
	xor	ax, ax			; clear the error code so that if this 
					; was the last sector, no error code 
					; will be returned for the corrected 
					; read. (clear carry too.)
OK11_OP:
	DEC	CS:[NUMBER_OF_SEC]
	JZ	OK11_EXIT		; ALL DONE?
	INC	CL			; ADVANCE SECTOR NUMBER
	INC	BH			; ADD 200H TO ADDRESS
	INC	BH
	JMP	SHORT LOOP_ECC
OK11_EXIT_err:
	stc				;J.K. 8/28/86 Set carry bit again.
OK11_EXIT:
	RESTOREREG <DX,CX,BX>
	RET	2
;
; WE TRULY HAVE A DMA VIOLATION.  RESTORE REGISTER AX AND RETRY THE
; OPERATION AS BEST WE CAN.
;
GOTDMAERR:
	POP	AX			; CLEAN UP STACK
	MOV	AX,CS:PREVOPER
	STI
	CMP	AH,ROMREAD		; SAVE USER FLAGS
	JB	INTDONE
	CMP	AH,ROMVERIFY
	JZ	INTVERIFY
	CMP	AH,ROMFORMAT
	JZ	INTFORMAT
	JA	INTDONE
;
; WE ARE DOING A READ/WRITE CALL.  CHECK FOR DMA PROBLEMS
;
	SAVEREG <DX,CX,BX,AX>
	PUSH	BP
	MOV	BP,SP
	MOV	DX,ES			; CHECK FOR 64K BOUNDARY ERROR

	SHL	DX,1
	SHL	DX,1
	SHL	DX,1
	SHL	DX,1			; SEGMENT CONVERTED TO ABSOLUTE ADDRESS

	ADD	DX,BX			; COMBINE WITH OFFSET
	ADD	DX,511			; SIMULATE A TRANSFER
;
; IF CARRY IS SET, THEN WE ARE WITHIN 512 BYTES OF THE END OF THE SEGMENT.
; WE SKIP THE FIRST TRANSFER AND PERFORM THE REMAINING BUFFERING AND TRANSFER
;
	JNC	NO_SKIP_FIRST
	MOV	DH,BYTE PTR [BP.OLDDX+1]	; SET UP HEAD NUMBER
	JMP	BUFFER			;J.K. 4/10/86
;	JMP	SHORT BUFFER
;
; DX IS THE PHYSICAL 16 BITS OF START OF TRANSFER.  COMPUTE REMAINING
; SECTORS IN SEGMENT.
;
NO_SKIP_FIRST:
	SHR	DH,1			; DH = NUMBER OF SECTORS BEFORE ADDRESS
	MOV	AH,128			; AH = MAX NUMBER OF SECTORS IN SEGMENT
	SUB	AH,DH
;
; AH IS NOW THE NUMBER OF SECTORS THAT WE CAN SUCCESSFULLY WRITE IN THIS
; SEGMENT.  IF THIS NUMBER IS ABOVE OR EQUAL TO THE REQUESTED NUMBER, THEN WE
; CONTINUE THE OPERATION AS NORMAL.  OTHERWISE, WE BREAK IT INTO PIECES.
;
	CMP	AH,AL			; CAN WE FIT IT IN?
	JB	DOBLOCK 		; NO, PERFORM BLOCKING.
;
; YES, THE REQUEST FITS.  LET IT HAPPEN
;
	MOV	DH,BYTE PTR [BP.OLDDX+1]	; SET UP HEAD NUMBER
	CALL	DOINT
	JMP	BAD13
;
; VERIFY THE GIVEN SECTORS.  PLACE THE BUFFER POINTER INTO OUR SPACE.
;
INTVERIFY:
	SAVEREG <ES,BX>
	PUSH	CS
	POP	ES
DOSIMPLE:
	MOV	BX,OFFSET DISKSECTOR
	PUSHF
	CALL	ORIG13
	RESTOREREG  <BX,ES>
	RET	2

;
; FORMAT OPERATION.  COPY THE PARAMETER TABLE INTO MEMORY
;
INTFORMAT:
	SAVEREG <ES,BX>
	SAVEREG <SI,DI,DS>
	PUSH	ES
	PUSH	CS
	POP	ES
	POP	DS
	MOV	SI,BX
	MOV	DI,OFFSET DISKSECTOR
	CALL	MOVE
	RESTOREREG  <DS,DI,SI>
	JMP	DOSIMPLE
;
; INLINE CONTINUATION OF OPERATION
;
INTDONE:
	JMP	ORIG13

;
; We can't fit the request into the entire block.  Perform the operation on
; the first block.
;
; DoBlock is modified to correctly handle multi-sector disk I/O. -J.K. 4/10/86
; Old DoBlock had added the number of sectors I/Oed (Ah in Old DoBlock) after
; the DoInt call to CL.  Observing only the lower 6 bits of CL(=max. 64) can
; represent a starting sector, if AH was big, then CL would be clobbered.
; By the way, we still are going to use CL for this purpose since Checkwrap
; routine will use it as an input.  To prevent CL from being clobbered, a
; safe number of sectors should be calculated like "63 - # of sectors/track".
; DoBlock will handle the first block of requested sectors within the
; boundary of this safe value. - J.K. 2/28/86

DoBlock:
;Try to get the # of sectors/track from BDS via Rom drive number.
;For any mini disks installed, here we have to pray that they have the
;same # of sector/track as the main DOS partition disk drive.

	Message ftestDisk,<"!!!DMA DoBlock!!!">

	mov	dx, word ptr [bp.olddx] ;set head #
	push	di
	push	ds
	push	ax			;AH - # of sectors before DMA boundary
					;AL - User requeseted # of sectors for I/O.
	mov	byte ptr CS:[phys_drv],1
	mov	al, dl
	call	SetDrive		;get BDS pointer for this DISK.
	pop	ax
	mov	byte ptr CS:[phys_drv],0
	test	word ptr [DI].Flags, fNon_Removable	;don't have to worry
	jnz	DoBlockHard		;about floppies. They are track by track operation.
	mov	al, ah			;set al = ah for floppies
	jmp	short DoBlockCont
DoBlockHard:
	push	cx
	xor	cx, cx
	mov	cx, [DI].SecLim 	;# of sectors/track
	mov	ch, 63
	sub	ch, cl
	mov	al, ch
	xchg	ah, al			;now ah - safe # of sectors
					;al - # of sectors before DMA boundary
	pop	cx
DoBlockCont:
	pop	ds
	pop	di
DoBlockContinue:
	Message ftestDisk,<"%%DMA DoBlock Loop%%">
	cmp	ah, al			;if safe_# >= #_of_sectors_to_go_before DMA,
	jae	DoBlocklast		;then #_of_sectors_to_go as it is for DoInt.
	push	ax			;save AH, AL
	mov	al, ah			;Otherwise, set al to ah to operate.
	jmp	short DoBlockDoInt	;DoInt will set AH to a proper function in [BP.Oldax]
DoBlocklast:
	mov	ah, al
	push	ax			;save AH
DoBlockDoInt:				;let AH = AL = # of sectors for this shot
	CALL	DoInt
	JC	BAD13			;something happened, bye!
	pop	ax
	SUB	BYTE PTR [BP.oldax], AH ;decrement by the successful operation
	ADD	CL,AH			;advance sector number. Safety gauranteed.
	ADD	BH,AH			;advance DMA address
	ADD	BH,AH			;twice for 512 byte sectors.
	cmp	ah, al			;check the previous value
	je	Buffer			;if #_of_sectors_to_go < safe_#, then we are done already.
	sub	al, ah			;otherwise, #_sector_to_go = #_of_sector_to_go - safe_#
	call	Check_Wrap		;get new CX, DH for the next operation.
	jmp	short DoBlockContinue	;handles next sectors left.
;End of modificaion of DoBlock	- J.K. 2/28/86
;The following is the original one.
;	 PUSH	 AX
;	 MOV	 AL,AH			 ; get max to operate on
;	 MOV	 AH,BYTE PTR [BP.oldax+1]; get function
;	 mov	 dh,byte ptr [BP.olddx+1]	 ; set up head number
;	 CALL	 DoInt
;	 JC	 Bad13			 ; something happened, bye!
;	 POP	 AX
;	 SUB	 BYTE PTR [BP.oldax],AH  ; decrement by the successful operation
;	 ADD	 CL,AH			 ; advance sector number
;	 ADD	 BH,AH			 ; advance DMA address
;	 ADD	 BH,AH			 ; twice for 512 byte sectors.

;
; THE NEXT REQUEST WILL WRAP THE 64K BOUNDARY.	IF WE ARE WRITING, THEN COPY
; THE OFFENDING SECTOR INTO OUR SPACE.
;
;   ES:BX POINTS TO THE SECTOR
;   CX,DX CONTAIN THE CORRECT TRACK/SECTOR/HEAD/DRIVE INFO
;   [BP.OLDAX] HAS CORRECT FUNCTION CODE
;
BUFFER:
	PUSH	BX
	MOV	AH,BYTE PTR [BP.OLDAX+1]
	CMP	AH,ROMWRITE
	JNZ	DOREAD
;
; COPY THE OFFENDING SECTOR INTO LOCAL BUFFER
;
	SAVEREG <DS,ES,SI,DI>
	PUSH	CS			; EXCHANGE SEGMENT REGISTERS
	PUSH	ES
	POP	DS
	POP	ES
	MOV	DI,OFFSET DISKSECTOR	; WHERE TO MOVE
	PUSH	DI			; SAVE IT
	MOV	SI,BX			; SOURCE
	CALL	MOVE
	POP	BX			; NEW TRANSFER ADDRESS
	RESTOREREG  <DI,SI>
	MOV	AL,1
; SEE IF WE ARE WRAPPING AROUND A TRACK OR HEAD
	MOV	DL,BYTE PTR [BP.OLDDX]	; GET DRIVE NUMBER
	CALL	CHECK_WRAP		; SETS UP REGISTERS IF WRAP-AROUND
;
;   AH IS FUNCTION
;   AL IS 1 FOR SINGLE SECTOR TRANSFER
;   ES:BX IS LOCAL TRANSFER ADDRES
;   CX IS TRACK/SECTOR NUMBER
;   DX IS HEAD/DRIVE NUMBER
;   SI,DI UNCHANGED
;
	CALL	DOINT
	RESTOREREG  <ES,DS>
	JC	BAD13			; GO CLEAN UP
	JMP	SHORT DOTAIL
;
; READING A SECTOR.  DO INT FIRST, THEN MOVE THINGS AROUND
;
DOREAD:
	SAVEREG <ES,BX>
	PUSH	CS
	POP	ES
	MOV	BX,OFFSET DISKSECTOR
	MOV	AL,1
; SEE IF OUR REQUEST WILL WRAP A TRACK OR HEAD BOUNDARY
	MOV	DL,BYTE PTR [BP.OLDDX]	; GET DRIVE NUMBER
	CALL	CHECK_WRAP		; SETS UP REGISTERS IF WRAP-AROUND
;
;   AH = FUNCTION
;   AL = 1 FOR SINGLE SECTOR
;   ES:BX POINTS TO LOCAL BUFFER
;   CX, DX ARE TRACK/SECTOR, HEAD/DRIVE
;
	CALL	DOINT
	RESTOREREG  <BX,ES>
	JC	BAD13			; ERROR => CLEAN UP
	SAVEREG <DS,SI,DI>
	PUSH	CS
	POP	DS
	MOV	DI,BX
	MOV	SI,OFFSET DISKSECTOR
	CALL	MOVE
	RESTOREREG  <DI,SI,DS>
;
; NOTE THE FACT THAT WE'VE DONE 1 MORE SECTOR
;
DOTAIL:
	POP	BX			; RETRIEVE NEW DMA AREA
	ADD	BH,2			; ADVANCE OVER SECTOR
	INC	CX
	MOV	AL,BYTE PTR [BP.OLDAX]
	CLC
	DEC	AL
	JZ	BAD13			; NO MORE I/O
; SEE IF WE WRAP AROUND A TRACK OR HEAD BOUNDARY WITH STARTING SECTOR
; WE ALREADY HAVE THE CORRECT HEAD NUMBER TO PASS TO CHECK_WRAP
	MOV	DL,BYTE PTR [BP.OLDDX]	; GET DRIVE NUMBER
	CALL	CHECK_WRAP		; SETS UP REGISTERS IF WRAP-AROUND
	CALL	DOINT
;
; WE ARE DONE.	AX HAS THE FINAL CODE; WE THROW AWAY WHAT WE GOT BEFORE
;
BAD13:
	MOV	SP,BP
	RESTOREREG  <BP,BX,BX,CX,DX>
	RET	2
BLOCK13 ENDP
	PAGE
	INCLUDE MSIOCTL.INC
	PAGE
; CHECK_WRAP IS A ROUTINE THAT ADJUSTS THE STARTING SECTOR, STARTING HEAD
; AND STARTING CYLINDER FOR AN INT 13 REQUEST THAT REQUESTS I/O OF A LOT
; OF SECTORS. IT ONLY DOES THIS FOR FIXED DISKS. IT IS USED IN THE SECTIONS
; OF CODE THAT HANDLE ECC ERRORS AND DMA ERRORS. IT IS NECESSARY, BECAUSE
; ORDINARILY THE ROM WOULD TAKE CARE OF WRAPS AROUND HEADS AND CYLINDERS,
; BUT WE BREAK DOWN A REQUEST WHEN WE GET AN ECC OR DMA ERROR INTO SEVERAL
; I/O OF ONE OR MORE SECTORS. IN THIS CASE, WE MAY ALREADY BE BEYOND THE
; NUMBER OF SECTORS ON A TRACK ON THE MEDIUM, AND THE REQUEST WOULD FAIL.
;
; INPUT CONDITIONS:
;	ALL REGISTERS SET UP FOR AN INT 13 REQUEST.
;
; OUTPUT:
;	DH - CONTAINS STARTING HEAD NUMBER FOR REQUEST
;	CX - CONTAINS STARTING SECTOR AND CYLINDER NUMBERS
;	(THE ABOVE MAY OR MAY NOT HAVE BEEN CHANGED, AND ARE 0-BASED)
;	ALL OTHER REGISTERS PRESERVED.
;
	PUBLIC CHECK_WRAP
CHECK_WRAP:
	Message ftestDisk,<"Entering Check_Wrap...",cr,lf>
	SAVEREG <AX,BX,DS,DI>
	MOV	BYTE PTR CS:[PHYS_DRV],1; USE PHYSICAL DRIVE IN AL TO GET BDS
	MOV	AL,DL			; AL HAS PHYSICAL DRIVE NUMBER
	CALL	SETDRIVE		; GET POINTER TO BDS FOR DRIVE
	MOV	BYTE PTR CS:[PHYS_DRV],0; RESTORE FLAG TO USE LOGICAL DRIVE
	JC	NO_WRAP 		; DO NOTHING IF WRONG PHYSICAL DRIVE
	TEST	WORD PTR [DI].FLAGS,FNON_REMOVABLE
	JZ	NO_WRAP 		; NO WRAPPING FOR REMOVABLE MEDIA
	MOV	BX,[DI].SECLIM
	MOV	AX,CX
	AND	AX,003FH		; EXTRACT SECTOR NUMBER
	CMP	AX,BX			; ARE WE GOING TO WRAP?
	JBE	NO_WRAP
	DIV	BL			; AH=NEW SECTOR #, AL=# OF HEAD WRAPS
; WE NEED TO BE CAREFUL HERE. IF THE NEW SECTOR # IS 0, THEN WE ARE ON THE
; LAST SECTOR ON THAT TRACK.
	OR	AH,AH
	JNZ	NOT_ON_BOUND
	MOV	AH,BL			; SET SECTOR=SECLIM IF ON BOUNDARY
	DEC	AL			; ALSO DECREMENT # OF HEAD WRAPS
NOT_ON_BOUND:
	AND	CL,0C0H 		; ZERO OUT SECTOR #
	OR	CL,AH			; OR IN NEW SECTOR #
	XOR	AH,AH			; AX = # OF HEAD WRAPS
	INC	AX
	ADD	AL,DH			; ADD IN STARTING HEAD #
	ADC	AH,0			; CATCH ANY CARRY
	CMP	AX,[DI].HDLIM		; ARE WE GOING TO WRAP AROUND A HEAD?
	JBE	NO_WRAP_HEAD		; DO NOT LOSE NEW HEAD NUMBER!!
	PUSH	DX			; PRESERVE DRIVE NUMBER AND HEAD NUMBER
	XOR	DX,DX
	MOV	BX,[DI].HDLIM
	DIV	BX			; DX=NEW HEAD #, AX=# OF CYLINDER WRAPS
; CAREFUL HERE! IF NEW HEAD # IS 0, THEN WE ARE ON THE LAST HEAD.
	OR	DX,DX
	JNZ	NO_HEAD_BOUND
	MOV	DX,BX			; ON BOUNDARY. SET TO HDLIM
; IF WE HAD SOME CYLINDER WRAPS, WE NEED TO REDUCE THEM BY ONE!!
	OR	AX,AX
	JZ	NO_HEAD_BOUND
	DEC	AX			; REDUCE NUMBER OF CYLINDER WRAPS
NO_HEAD_BOUND:
	MOV	BH,DL			; BH HAS NEW HEAD NUMBER
	POP	DX			; RESTORE DRIVE NUMBER AND HEAD NUMBER
	DEC	BH			; GET IT 0-BASED
	MOV	DH,BH			; SET UP NEW HEAD NUMBER IN DH
	MOV	BH,CL
	AND	BH,3FH			; PRESERVE SECTOR NUMBER
	MOV	BL,6
	XCHG	CL,BL
	SHR	BL,CL			; GET MS CYLINDER BITS TO LS END
	ADD	CH,AL			; ADD IN CYLINDER WRAP
	ADC	BL,AH			; ADD IN HIGH BYTE
	SHL	BL,CL			; MOVE UP TO MS END
	XCHG	BL,CL			; RESTORE CYLINDER BITS INTO CL
	OR	CL,BH			; OR IN SECTOR NUMBER

NO_WRAP:
	CLC				; RESET CARRY
	RESTOREREG <DI,DS,BX,AX>
	RET

NO_WRAP_HEAD:
	MOV	DH,AL			; DO NOT LOSE NEW HEAD NUMBER
	DEC	DH			; GET IT 0-BASED
	JMP	SHORT NO_WRAP

;
; INT_2F_13:
;		THIS CODE IS CHAINED INTO THE INT_2F INTERRUPT DURING BIOS
;	INITIALIZATION.  IT ALLOWS THE USER TO CHANGE THE ORIG13 INT_13 VECTOR
;	AFTER BOOTING.	THIS ALLOWS TESTING AND IMPLEMENTATION OF CUSTOM INT_13
;	HANDLERS, WITHOUT GIVING UP MS-DOS ERROR RECOVERY
;
;	ENTRY CONDITIONS
;		AH	== RESET_INT_13  (13H)
;		DS:DX	== ADDRESS OF NEW INT_13 HANDLER
;		ES:BX	== ADDRESS OF NEW INT_13 VECTOR USED BY WARM BOOT
;								(INT 19)
;
;	EXIT CONDITIONS
;		ORIG13	== ADDRESS OF NEW INT_13 HANDLER
;		DS:DX	== OLD ORIG13 VALUE
;		ES:BX	== OLD OLD13  VALUE

	ASSUME CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING

	PUBLIC INT_2F_13
INT_2F_13	PROC	FAR

	CMP	AH,13H			; IF (INTERRUPT_VALUE != RESET_INT_13)
	JE	CHG_ORIG13
	JMP	CS:[NEXT2F_13]		;	THEN CONTINUE ON INT_2F CHAIN

CHG_ORIG13:				;	ELSE
	PUSH	WORD PTR CS:[ORIG13]	;	   SAVE OLD VALUE OF OLD13 AND
	PUSH	WORD PTR CS:[ORIG13 + 2];	   ORIG13 SO THAT WE CAN

	PUSH	WORD PTR CS:[OLD13]	;	   RETURN THEM TO CALLER
	PUSH	WORD PTR CS:[OLD13 + 2]

	MOV	WORD PTR CS:[ORIG13],DX ;	   ORIG13 := ADDR. OF NEW INT_13
					;				VECTOR
	MOV	WORD PTR CS:[ORIG13+2],DS

	MOV	WORD PTR CS:[OLD13],BX	;	    OLD13 := ADDR. OF NEW
					;			  BOOT_13 VECTOR
	MOV	WORD PTR CS:[OLD13+2],ES

	POP	ES			;	    ES:BX := OLD OLD13 VECTOR
	POP	BX

	POP	DS			;	    DS:DX := OLD ORIG13 VECTOR
	POP	DX

	IRET				;	END ELSE

INT_2F_13	ENDP

MOVE	PROC	NEAR
	CLD
	PUSH	CX
	MOV	CX,512/2
;J.K. Warning!!! Do not change the position of this label.
;     The following three bytes will be NOPed out by MSINIT if the system
;     does not support the DOUBLE WORD MOV instruction, i.e., (if
;     not 386 base machine.)
;----------------------------------------------------------------------------
	public	DoubleWordMov
DoubleWordMov:			;AN002;
	shr	cx, 1		;AN002;Make it a double word.
	db	66h		;AN002;Machine code for double word mov
;----------------------------------------------------------------------------
	REP	MOVSW
	POP	CX
	RET
MOVE ENDP

DOINT	PROC	NEAR
	MOV	DL,BYTE PTR [BP.OLDDX]		; GET PHYSICAL DRIVE NUMBER
	XOR	AH,AH
	OR	AL,AL
	JZ	DOINTDONE
	MOV	AH,BYTE PTR [BP.OLDAX+1]	; GET REQUEST CODE
	PUSH	[BP.OLDF]
	CALL	ORIG13
	PUSHF
	POP	[BP.OLDF]
DOINTDONE:
	RET
DOINT	ENDP
CODE	ENDS
	END
