summaryrefslogtreecommitdiff
path: root/v4.0/src/DOS/DISK2.ASM
blob: bb76e89800affb3c2a0053e3e14357a1bc499d1f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
;	SCCSID = @(#)disk2.asm	1.3 85/06/19
;	SCCSID = @(#)disk2.asm	1.3 85/06/19
TITLE	DISK2 - Disk utility routines
NAME	Disk2
; Low level Read and write routines for local SFT I/O on files and devs
;
;   DskRead
;   DWRITE
;   DSKWRITE
;   HarderrRW
;   SETUP
;   BREAKDOWN
;   READ_LOCK_VIOLATION
;   WRITE_LOCK_VIOLATION
;   DISKREAD
;   SET_ACC_ERR_DS
;   SET_ACC_ERR
;   SETSFT
;   SETCLUS
;   AddRec
;
;   Revision history:
;
;    AN000  version 4.00 Jan. 1988
;

;
; get the appropriate segment definitions
;
.xlist
include dosseg.asm

CODE	SEGMENT BYTE PUBLIC  'CODE'
	ASSUME	SS:DOSGROUP,CS:DOSGROUP

.xcref
INCLUDE DOSSYM.INC
INCLUDE DEVSYM.INC
include version.inc
.cref
.list

Installed = TRUE

	i_need	THISSFT,DWORD
	i_need	DMAADD,DWORD
	i_need	NEXTADD,WORD
	i_need	ThisDrv,BYTE
	i_need	SecClusPos,BYTE
	i_need	ClusNum,WORD
	i_need	ReadOp,BYTE
	i_need	Trans,BYTE
	i_need	BytPos,4
	i_need	SecPos,DWORD		 ; DOS 4.00 >32mb			;AN000;
	i_need	BytSecPos,WORD
	i_need	BytCnt1,WORD
	i_need	BytCnt2,WORD
	i_need	SecCnt,WORD
	i_need	ThisDPB,DWORD
	i_need	LastPos,WORD
	i_need	EXTERRPT,DWORD
	i_need	CALLVIDRW,DWORD
	i_need	ALLOWED,BYTE
	i_need	DEVCALL,BYTE
	i_need	CALLSCNT,WORD
	i_need	DISK_FULL,BYTE		  ; disk full flag for ran blk wrt
	i_need	FSeek_drive,BYTE	  ; DOS 4.00	  ;AN000;
	i_need	FSeek_firclus,WORD	  ; DOS 4.00	  ;AN000;
	i_need	HIGH_SECTOR,WORD	  ; F.C. >32mb	  ;AN000;
	i_need	TEMP_VAR2,WORD		  ; LB. 	  ;AN000;
	i_need	TEMP_VAR,WORD		  ; LB. 	  ;AN000;
	i_need	IFS_DRIVER_ERR,WORD	  ; LB. 	  ;AN000;
	i_need	CurHashEntry,DWORD	     ; DOS 4.00 current Hash entry	;AN000;
	i_need	BUF_HASH_PTR,DWORD	     ; DOS 4.00 Hash table pointer	;AN000;
	i_need	BUF_HASH_COUNT,WORD	     ; DOS 4.00 Hash table entries	;AN000;
	i_need	LastBuffer,DWORD
	i_need	FIRST_BUFF_ADDR,WORD	     ; first buffer address		;AN000;

IF	BUFFERFLAG
	EXTRN	SAVE_MAP:NEAR
	EXTRN	RESTORE_MAP:NEAR
	EXTRN	SAVE_USER_MAP:NEAR
	EXTRN	RESTORE_USER_MAP:NEAR
	i_need	BUF_EMS_SAFE_FLAG,BYTE
	i_need	BUF_EMS_MODE,BYTE
	i_need	CURADD,WORD
ENDIF


Break	<DSKREAD -- PHYSICAL DISK READ>

; Inputs:
;	DS:BX = Transfer addr
;	CX = Number of sectors
;	[HIGH_SECTOR] = Absolute record number (HIGH)
;	DX = Absolute record number	       (LOW)
;	ES:BP = Base of drive parameters
; Function:
;	Call BIOS to perform disk read
; Outputs:
;	DI = CX on entry
;	CX = Number of sectors unsuccessfully transfered
;	AX = Status word as returned by BIOS (error code in AL if error)
;	Zero set if OK (from BIOS) (carry clear)
;	Zero clear if error (carry clear)
; SI Destroyed, others preserved

	procedure   DskRead,NEAR
ASSUME	DS:NOTHING,ES:NOTHING

	Assert	ISDPB,<ES,BP>,"DskRead"
	PUSH	CX
	MOV	AH,ES:[BP.dpb_media]
	MOV	AL,ES:[BP.dpb_UNIT]
	PUSH	BX
	PUSH	ES
	invoke	SETREAD
	JMP	DODSKOP

Break	<DWRITE -- SEE ABOUT WRITING>

; Inputs:
;	DS:BX = Transfer address
;	CX = Number of sectors
;	[HIGH_SECTOR] = Absolute record number (HIGH)
;	DX = Absolute record number	       (LOW)
;	ES:BP = Base of drive parameters
;	[ALLOWED] must be set in case HARDERR called
; Function:
;	Calls BIOS to perform disk write. If BIOS reports
;	errors, will call HARDERRRW for further action.
; Output:
;	Carry set if error (currently, user FAILed to I 24)
; BP preserved. All other registers destroyed.

	entry	DWRITE
ASSUME	DS:NOTHING,ES:NOTHING

	Assert	ISDPB,<ES,BP>,"DWrite"
	CALL	DSKWRITE
	retz			; Carry clear
	MOV	BYTE PTR [READOP],1
	invoke	HARDERRRW
	CMP	AL,1		; Check for retry
	JZ	DWRITE
	CMP	AL,3		; Check for FAIL
	CLC
	JNZ	NO_CAR2 	; Ignore
	STC
NO_CAR2:
	return

Break	<DSKWRITE -- PHYSICAL DISK WRITE>

; Inputs:
;	DS:BX = Transfer addr
;	CX = Number of sectors
;	DX = Absolute record number	       (LOW)
;	[HIGH_SECTOR] = Absolute record number (HIGH)
;	ES:BP = Base of drive parameters
; Function:
;	Call BIOS to perform disk read
; Outputs:
;	DI = CX on entry
;	CX = Number of sectors unsuccessfully transfered
;	AX = Status word as returned by BIOS (error code in AL if error)
;	Zero set if OK (from BIOS) (carry clear)
;	Zero clear if error (carry clear)
; SI Destroyed, others preserved

	entry	DSKWRITE
ASSUME	DS:NOTHING,ES:NOTHING

	Assert	ISDPB,<ES,BP>,"DskWrite"
	PUSH	CX
	MOV	AH,ES:[BP.dpb_media]
	MOV	AL,ES:[BP.dpb_UNIT]
	PUSH	BX
	PUSH	ES
	invoke	SETWRITE
DODSKOP:
	MOV	CX,DS		; Save DS
	POP	DS		; DS:BP points to DPB
	PUSH	DS
	LDS	SI,DS:[BP.dpb_driver_addr]
	invoke	DEVIOCALL2
	MOV	DS,CX		; Restore DS
	POP	ES		; Restore ES
	POP	BX
	MOV	CX,[CALLSCNT]	; Number of sectors transferred
	POP	DI
	SUB	CX,DI
	NEG	CX		; Number of sectors not transferred
	MOV	AX,[DEVCALL.REQSTAT]
	MOV	[IFS_DRIVER_ERR],AX ;IFS. save it for IFS			;AN000;
	TEST	AX,STERR
	return
EndProc DskRead



Break	<HardErrRW - map extended errors and call harderr>

; Inputs:
;	AX is error code from read or write
;	Other registers set as per HARDERR
; Function:
;	Checks the error code for special extended
;	errors and maps them if needed. Then invokes
;	Harderr
; Outputs:
;	Of HARDERR
; AX may be modified prior to call to HARDERR.
; No other registers altered.

	procedure   HARDERRRW,near
ASSUME	DS:NOTHING,ES:NOTHING

	CMP	AL,error_I24_wrong_disk
	JNZ	DO_ERR				; Nothing to do
	PUSH	DS
	PUSH	SI
	LDS	SI,[CALLVIDRW]			; Get pointer from dev
	MOV	WORD PTR [EXTERRPT+2],DS	; Set ext err pointer
	MOV	WORD PTR [EXTERRPT],SI
	POP	SI
	POP	DS
DO_ERR:
	invoke	HARDERR
	return

EndProc HARDERRRW

Break	<SETUP -- SETUP A DISK READ OR WRITE FROM USER>

; Inputs:
;	ES:DI point to SFT (value also in THISSFT)
;	[DMAADD] contains transfer address
;	CX = Byte count
;   WARNING Stack must be clean, two ret addrs on stack, 1st of caller,
;		2nd of caller of caller.
; Outputs:
;	    CX = byte count
;	    [THISDPB] = Base of drive parameters if file
;		      = Pointer to device header if device or NET
;	    ES:DI Points to SFT
;	    [NEXTADD] = Displacement of disk transfer within segment
;	    [TRANS] = 0 (No transfers yet)
;	    [BYTPOS] = Byte position in file
;
;	The following fields are relevant to local files (not devices) only:
;
;	    [SECPOS] = Position of first sector (local files only)
;	    [BYTSECPOS] = Byte position in first sector (local files only)
;	    [CLUSNUM] = First cluster (local files only)
;	    [SECCLUSPOS] = Sector within first cluster (local files only)
;	    [THISDRV] = Physical unit number (local files only)
;
;      RETURNS ONE LEVEL UP WITH:
;	   CX = 0
;	   CARRY = Clear
;	IF AN ERROR IS DETECTED
; All other registers destroyed

	procedure   SETUP,NEAR
	DOSAssume   CS,<DS>,"SetUp"
	ASSUME	ES:NOTHING

	Assert	    ISSFT,<ES,DI>,"SetUp"
	LDS	SI,ES:[DI.sf_devptr]
ASSUME	DS:NOTHING
	MOV	WORD PTR [THISDPB+2],DS
	context DS
	MOV	WORD PTR [THISDPB],SI
	MOV	BX,WORD PTR [DMAADD]
	MOV	[NEXTADD],BX		;Set NEXTADD to start of Xaddr
	MOV	BYTE PTR [TRANS],0	;No transferes
	MOV	AX,WORD PTR ES:[DI.sf_Position]
	MOV	DX,WORD PTR ES:[DI.sf_Position+2]
	MOV	WORD PTR [BYTPOS+2],DX	;Set it
	MOV	WORD PTR [BYTPOS],AX
	TEST	ES:[DI.sf_flags],sf_isnet + devid_device
	JNZ	NOSETSTUFF		;Following not done on devs or NET
	PUSH	ES
	LES	BP,[THISDPB]		;Point at the DPB
	Assert	ISDPB,<ES,BP>,"Setup"
	MOV	BL,ES:[BP.dpb_drive]
	MOV	[THISDRV],BL		;Set THISDRV
	MOV	BX,ES:[BP.dpb_sector_size]
;	CMP	DX,BX		; See if divide will overflow
;	JNC	EOFERR		; for 16 bit sector
;; 32 bit divide
	invoke	DIV32			      ; F.C. >32mb   ;AN000;
	MOV	WORD PTR [SECPOS],AX	      ; F.C. >32mb   ;AN000;
	MOV	BX,[HIGH_SECTOR]	      ; F.C. >32mb   ;AN000;
	MOV	WORD PTR [SECPOS+2],BX	      ; F.C. >32mb   ;AN000;

	MOV	[BYTSECPOS],DX
	MOV	DX,AX
	AND	AL,ES:[BP.dpb_cluster_mask]
	MOV	[SECCLUSPOS],AL
	MOV	AX,CX		; Save byte count
;	MOV	CL,ES:[BP.dpb_cluster_shift]
	PUSH	WORD PTR [SECPOS+2]	     ; F.C. >32mb	   ;AN000;
	POP	[HIGH_SECTOR]		     ; F.C. >32mb	   ;AN000;
	PUSH	AX			     ; F.C. >32mb save ax  ;AN000;
	MOV	AX,DX			     ; F.C. >32mb ax=dx    ;AN000;
	invoke	SHR32			     ; F.C. >32mb shift ax ;AN000;
	MOV	DX,AX			     ; F.C. >32mb dx=ax    ;AN000;
	POP	AX			     ; F.C. >32mb restore dx ;AN000;

;	SHR	DX,CL
	CMP	DX,ES:[BP.dpb_max_cluster]   ;>32mb  if > disk size ;AN000;	;AN000;
	JA	EOFERR			     ;>32mb    then EOF     ;AN000;	;AN000;

	MOV	[CLUSNUM],DX
	POP	ES		; ES:DI point to SFT
	MOV	CX,AX		; Put byte count back in CX
NOSETSTUFF:
	MOV	AX,CX		; Need it in AX too
	ADD	AX,WORD PTR [DMAADD]	 ; See if it will fit in one segment
	JNC	OK		; Must be less than 64K
	MOV	AX,WORD PTR [DMAADD]
	NEG	AX		; Amount of room left in segment (know
				;    less than 64K since max value of CX
				;    is FFFF).
	JNZ	NoDec
	DEC	AX
NoDec:
	MOV	CX,AX		; Can do this much
	JCXZ	NOROOM		; Silly user gave Xaddr of FFFF in segment
OK:
	return

EOFERR:
	POP	ES		; ES:DI point to SFT
	XOR	CX,CX		; No bytes read
;;;;;;;;;;; 7/18/86
;	MOV	BYTE PTR [DISK_FULL],1	    ; set disk full flag
;;;;;;;;;;;
NOROOM:
	POP	BX		; Kill return address
	CLC
	return			; RETURN TO CALLER OF CALLER
EndProc SETUP

Break	<BREAKDOWN -- CUT A USER READ OR WRITE INTO PIECES>

; Inputs:
;	CX = Length of disk transfer in bytes
;	ES:BP = Base of drive parameters
;	[BYTSECPOS] = Byte position witin first sector
; Outputs:
;	[BYTCNT1] = Bytes to transfer in first sector
;	[SECCNT] = No. of whole sectors to transfer
;	[BYTCNT2] = Bytes to transfer in last sector
; AX, BX, DX destroyed. No other registers affected.

	procedure   BREAKDOWN,near
	DOSAssume   CS,<DS>,"BreakDown"
	ASSUME	ES:NOTHING

	Assert	    ISDPB,<ES,BP>,"BreakDown"
	MOV	AX,[BYTSECPOS]
	MOV	BX,CX
	OR	AX,AX
	JZ	SAVFIR		; Partial first sector?
	SUB	AX,ES:[BP.dpb_sector_size]
	NEG	AX		; Max number of bytes left in first sector
	SUB	BX,AX		; Subtract from total length
	JAE	SAVFIR
	ADD	AX,BX		; Don't use all of the rest of the sector
	XOR	BX,BX		; And no bytes are left
SAVFIR:
	MOV	[BYTCNT1],AX
	MOV	AX,BX
	XOR	DX,DX
	DIV	ES:[BP.dpb_sector_size]  ; How many whole sectors?
	MOV	[SECCNT],AX
	MOV	[BYTCNT2],DX	; Bytes remaining for last sector
	OR	DX,[BYTCNT1]
	retnz			; NOT (BYTCNT1 = BYTCNT2 = 0)
	CMP	AX,1
	retnz
	MOV	AX,ES:[BP.dpb_sector_size]	 ; Buffer EXACT one sector I/O
	MOV	[BYTCNT2],AX
	MOV	[SECCNT],DX		; DX = 0
RET45:
	return
EndProc BreakDown

; ES:DI points to SFT. This entry used by NET_READ
; Carry set if to return error (CX=0,AX=error_sharing_violation).
; Else do retrys.
; ES:DI,DS,CX preserved

	procedure READ_LOCK_VIOLATION,NEAR
	DOSAssume   CS,<DS>,"Read_Lock_Violation"
	ASSUME	ES:NOTHING

	Assert	    ISSFT,<ES,DI>,"ReadLockViolation"

	MOV	[READOP],0
ERR_ON_CHECK:
	TEST	ES:[DI.sf_mode],sf_isfcb
	JNZ	HARD_ERR
	PUSH	CX
	MOV	CL,BYTE PTR ES:[DI.sf_mode]
	AND	CL,sharing_mask
	CMP	CL,sharing_compat
	POP	CX
	JNE	NO_HARD_ERR
HARD_ERR:
	invoke	LOCK_VIOLATION
	retnc				; User wants Retrys
NO_HARD_ERR:
	XOR	CX,CX			;No bytes transferred
	MOV	AX,error_lock_violation
	STC
	return

EndProc READ_LOCK_VIOLATION

; Same as READ_LOCK_VIOLATION except for READOP.
; This entry used by NET_WRITE
	procedure WRITE_LOCK_VIOLATION,NEAR
	DOSAssume   CS,<DS>,"Write_Lock_Violation"
	ASSUME	ES:NOTHING
	Assert	    ISSFT,<ES,DI>,"WriteLockViolation"

	MOV	[READOP],1
	JMP	ERR_ON_CHECK

EndProc WRITE_LOCK_VIOLATION


Break	<DISKREAD -- PERFORM USER DISK READ>

; Inputs:
;	Outputs of SETUP
; Function:
;	Perform disk read
; Outputs:
;    Carry clear
;	CX = No. of bytes read
;	ES:DI point to SFT
;	SFT offset and cluster pointers updated
;    Carry set
;	CX = 0
;	ES:DI point to SFT
;	AX has error code

	procedure   DISKREAD,NEAR
	DOSAssume   CS,<DS>,"DiskRead"
	ASSUME	ES:NOTHING

	Assert	ISSFT,<ES,DI>,"DISKREAD"
	PUSH	ES:[DI.sf_firclus]	; set up 1st cluster # for FastSeek
	POP	[FSeek_firclus] 	; 11/5/86

	MOV	AX,WORD PTR ES:[DI.sf_size]
	MOV	BX,WORD PTR ES:[DI.sf_size+2]
	SUB	AX,WORD PTR [BYTPOS]
	SBB	BX,WORD PTR [BYTPOS+2]
	JB	RDERR			;Read starts past EOF
	JNZ	ENUF			;More than 64k to EOF
	OR	AX,AX
	JZ	RDERR			;Read starts at EOF
	CMP	AX,CX
	JAE	ENUF			;I/O fits
	MOV	CX,AX			;Limit read to up til EOF
ENUF:
	invoke	CHECK_READ_LOCK 	;IFS. check read lock			 ;AN000;
	JNC	Read_Ok 		; There are no locks
	return

READ_OK:
	LES	BP,[THISDPB]
	Assert	ISDPB,<ES,BP>,"DISKREAD/ReadOK"
	MOV	AL,ES:[BP.dpb_drive]	; set up drive # for FastSeek
	MOV	[FSeek_drive],AL	; 11/5/86  ;AN000;

	CALL	BREAKDOWN
	MOV	CX,[CLUSNUM]
	invoke	FNDCLUS
;------------------------------------------------------------------------
IF	NOT IBMCOPYRIGHT
	JC	SET_ACC_ERR_DS		; fix to take care of I24 fail
					; migrated from 330a - HKN
ENDIF
;------------------------------------------------------------------------
	OR	CX,CX
	JZ	SKIPERR
RDERR:
	MOV	[DISK_FULL],1		;MS. EOF detection  ;AN000;
	MOV	AH,0EH			;MS. read/data/fail ;AN000;
	transfer WRTERR22
RDLASTJ:JMP	RDLAST
SETSFTJ2: JMP	SETSFT

CANOT_READ:
	POP	CX		; Clean stack
	POP	CX
	POP	BX

	entry	SET_ACC_ERR_DS
ASSUME	DS:NOTHING,ES:NOTHING
	Context DS

	entry	SET_ACC_ERR
	DOSAssume   CS,<DS>,"SET_ACC_ERR"

	XOR	CX,CX
	MOV	AX,error_access_denied
	STC
	return

SKIPERR:
	MOV	[LASTPOS],DX
	MOV	[CLUSNUM],BX
	CMP	[BYTCNT1],0
	JZ	RDMID
	invoke	BUFRD
	JC	SET_ACC_ERR_DS
RDMID:
	CMP	[SECCNT],0
	JZ	RDLASTJ
	invoke	NEXTSEC
	JC	SETSFTJ2
	MOV	BYTE PTR [TRANS],1	; A transfer is taking place
ONSEC:
	MOV	DL,[SECCLUSPOS]
	MOV	CX,[SECCNT]
	MOV	BX,[CLUSNUM]
RDLP:
	invoke	OPTIMIZE
	JC	SET_ACC_ERR_DS
	PUSH	DI
	PUSH	AX
	PUSH	BX
	MOV	[ALLOWED],allowed_RETRY + allowed_FAIL + allowed_IGNORE
	MOV	DS,WORD PTR [DMAADD+2]
ASSUME	DS:NOTHING
	PUSH	DX
	PUSH	CX
	invoke	SET_RQ_SC_PARMS 	 ;LB. do this for SC		       ;AN000;

IF	BUFFERFLAG
	pushf
	cmp	[BUF_EMS_SAFE_FLAG], 1
	je	safe_read
	call	save_map
	call	restore_user_map
safe_read:
	popf
ENDIF

	invoke	DREAD

IF	BUFFERFLAG
	pushf
	cmp	[BUF_EMS_SAFE_FLAG], 1
	je	safe_mapping
	call	save_user_map
	call	restore_map
safe_mapping:
	popf
ENDIF

	POP	BX
	POP	DX
	JNC	SKP_CANOT_READ
	JMP	CANOT_READ
SKP_CANOT_READ:
	MOV	[TEMP_VAR],BX		  ;LB. save sector count		;AN000;
	MOV	[TEMP_VAR2],DX		  ;LB. 1st sector			;AN000;
SCAN_NEXT:
;;;;;;; invoke	GETCURHEAD		  ;LB. get buffer header		;AN000;
	PUSH	DX			  ;LB. save regs			;AN000;
	PUSH	AX			  ;LB.					;AN000;
	PUSH	BX			  ;LB.					;AN000;
	MOV	AX,DX			  ;LB.
;	MOV	DX,[HIGH_SECTOR]	  ;LB. HASH(sector#) and get entry #	;AN000;
	XOR	DX,DX			  ;LB. to avoid divide overflow 	;AN000;
	DIV	[BUF_HASH_COUNT]	  ;LB. get remainder			;AN000;
	ADD	DX,DX			  ;LB. 8 bytes per entry		;AN000;
	ADD	DX,DX			  ;LB.					;AN000;
	ADD	DX,DX			  ;LB. times 8				;AN000;

	LDS	DI,[BUF_HASH_PTR]	  ;LB. get Hash Table addr		;AN000;
	ADD	DI,DX			  ;LB position to entry 		;AN000;
	CMP	[DI.Dirty_Count],0	  ;LB dirty hash entry ?		;AN000;
	JNZ	yesdirty		  ;LB yes and map it			;AN000;
	POP	BX			  ;LB.					;AN000;
	POP	AX			  ;LB.					;AN000;
	POP	DX			  ;LB.					;AN000;

IF	NOT	BUFFERFLAG
	JMP	SHORT end_scan		  ;LB.					;AN000;
ELSE
	JMP	END_SCAN
ENDIF

yesdirty:
	MOV	WORD PTR [CurHashEntry+2],DS ;LB. update current Hash entry ptr ;AN000;
	MOV	WORD PTR [CurHashEntry],DI ;LB. 				;AN000;
	MOV	WORD PTR [LASTBUFFER],-1   ;LB. invalidate last buffer		;AN000;
	MOV	BX,[DI.EMS_PAGE_NUM]	  ;LB. logical page			;AN000;

IF	NOT	BUFFERFLAG
	LDS	DI,[DI.BUFFER_BUCKET]	  ;LB. ds:di is 1st buffer addr 	;AN000;
	MOV	[FIRST_BUFF_ADDR],DI	  ;LB. save first buff addr 1/19/88	;AN000;
	invoke	SET_MAP_PAGE		  ;LB. activate handle if EMS there	;AN000;
ELSE
;	int	3
	push	ds
	push	di		; save hash ptr

	LDS	DI,[DI.BUFFER_BUCKET]	  ;ds:di is 1st buffer addr
	POP	AX		; Recall transfer address
	PUSH	AX
	PUSH	DI		; Save search environment
	PUSH	DX		     ; F.C. no need for high sector, <64K
	push	cx

	MOV	DX,[TEMP_VAR2]	     ;LB. get 1st sector #
	SUB	DX,WORD PTR [DI.buf_sector]   ; How far into transfer?
	NEG	DX
	MOV	DI,AX
	MOV	AX,DX
	MOV	CX,ES:[BP.dpb_sector_size]
	MUL	CX
	ADD	DI,AX		; Put the buffer here
	mov	[CURADD], di
	
	pop	cx
	pop	dx
	pop	di

	invoke	SET_MAP_PAGE		  ;LB. activate handle if EMS there	;AN000;
	pop	di			; restore hash ptr.
	pop	ds
	LDS	DI,[DI.BUFFER_BUCKET]	  ;LB. ds:di is 1st buffer addr 	;AN000;
	MOV	[FIRST_BUFF_ADDR],DI	  ;LB. save first buff addr 1/19/88	;AN000;
ENDIF
										;AN000;
	POP	BX			  ;LB.					;AN000;
	POP	AX			  ;LB.					;AN000;
	POP	DX			  ;LB.					;AN000;


	Assert	ISDPB,<ES,BP>,"DISKREAD/RdLp"
	MOV	AL,ES:[BP.dpb_drive]
NXTBUF: 			; Must see if one of these sectors is buffered
	invoke	BUFF_RANGE_CHECK			   ;F.C. >32mb
	JNC	inrange 		   ;LB. 				;AN000;
	mov	DI,[DI.buf_next]	   ;LB. get next buffer 1/19/88 	;AN000;
	JMP	DONXTBUF		   ;LB. 				;AN000;
inrange:
	TEST	[DI.buf_flags],buf_dirty
	JZ	CLBUFF			; Buffer is clean, so OK
; A sector has been read in when a dirty copy of it is in a buffer
; The buffered sector must now be read into the right place
	POP	AX		; Recall transfer address
	PUSH	AX
	PUSH	DI		; Save search environment
	PUSH	DX		     ; F.C. no need for high sector, <64K

	MOV	DX,[TEMP_VAR2]	     ;LB. get 1st sector #
	SUB	DX,WORD PTR [DI.buf_sector]   ; How far into transfer?
	NEG	DX
	MOV	SI,DI
	MOV	DI,AX
	MOV	AX,DX
	MOV	CX,ES:[BP.dpb_sector_size]
	MUL	CX
	ADD	DI,AX		; Put the buffer here
	LEA	SI,[SI].BUFINSIZ
	SHR	CX,1
	PUSH	ES
	MOV	ES,WORD PTR [DMAADD+2]
	REP	MOVSW
	JNC	EVENMOV
	MOVSB
EVENMOV:
	POP	ES
	POP	DX
	POP	DI
	MOV	AL,ES:[BP.dpb_drive]
	invoke	SCANPLACE		 ;LB. done with this chain		;AN000;
	JMP	SHORT end_scan		 ;LB.					;AN000;
CLBUFF:
	invoke	SCANPLACE
DONXTBUF:
	CMP	DI,[FIRST_BUFF_ADDR]	 ;LB. end of buffers			;AN000;
	JNZ	NXTBUF
end_scan:
	ADD	DX,1			 ;LB. next sector #			;AN000;
	ADC	[HIGH_SECTOR],0 	 ;LB.					;AN000;
	DEC	[TEMP_VAR]		 ;LB. decrement count			;AN000;
	JZ	SCAN_DONE		 ;LB. scan next sector			;AN000;
	JMP	SCAN_NEXT		 ;LB. scan next sector			;AN000;
SCAN_DONE:
	Context DS
	POP	CX
	POP	CX
	POP	BX
	JCXZ	RDLAST
	invoke	IsEOF			; test for eof on fat size
	JAE	SETSFT
	MOV	DL,0
	INC	[LASTPOS]	; We'll be using next cluster
	JMP	RDLP

RDLAST:
	MOV	AX,[BYTCNT2]
	OR	AX,AX
	JZ	SETSFT
	MOV	[BYTCNT1],AX
	invoke	NEXTSEC
	JC	SETSFT
	MOV	[BYTSECPOS],0
	invoke	BUFRD
	JNC	SETSFT
	JMP	SET_ACC_ERR_DS

; Inputs:
;	[NEXTADD],[CLUSNUM],[LASTPOS] set to determine transfer size
;		and set cluster fields
; Function:
;	Update [THISSFT] based on the transfer
; Outputs:
;	sf_position, sf_lstclus, and sf_cluspos updated
;	ES:DI points to [THISSFT]
;	CX No. of bytes transferred
;	Carry clear

	entry	SETSFT
	DOSAssume   CS,<DS>,"SetSFT"
	ASSUME	ES:NOTHING

	LES	DI,[THISSFT]

; Same as SETSFT except ES:DI already points to SFT
	entry	SETCLUS
	DOSAssume   CS,<DS>,"SetClus"
	ASSUME	ES:NOTHING

	Assert	ISSFT,<ES,DI>,"SetClus"
	MOV	CX,[NEXTADD]
	SUB	CX,WORD PTR [DMAADD]	 ; Number of bytes transfered
	TEST	ES:[DI.sf_flags],devid_device
	JNZ	ADDREC			 ; don't set clusters if device
	MOV	AX,[CLUSNUM]
	MOV	ES:[DI.sf_lstclus],AX
	MOV	AX,[LASTPOS]
	MOV	ES:[DI.sf_cluspos],AX

; Inputs:
;	ES:DI points to SFT
;	CX is No. Bytes transferred
; Function:
;	Update the SFT offset based on the transfer
; Outputs:
;	sf_position updated to point to first byte after transfer
;	ES:DI points to SFT
;	CX No. of bytes transferred
;	Carry clear

	entry	AddRec
	DOSAssume   CS,<DS>,"AddRec"
	ASSUME	ES:NOTHING

	Assert	ISSFT,<ES,DI>,"AddRec"
	JCXZ	RET28		; If no records read,  don't change position
	ADD	WORD PTR ES:[DI.sf_position],CX  ; Update current position
	ADC	WORD PTR ES:[DI.sf_position+2],0
RET28:	CLC
	return
EndProc DISKREAD

CODE	ENDS
    END