summaryrefslogtreecommitdiff
path: root/v4.0/src/DOS/ROM.ASM
blob: ca344099ba85f456c6c6a88d05ac8f76a4edd58b (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
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
;       SCCSID = @(#)rom.asm    1.1 85/04/10
TITLE   ROM - Miscellaneous routines
NAME    ROM
; Misc Low level routines for doing simple FCB computations, Cache
;       reads and writes, I/O optimization, and FAT allocation/deallocation
;
;   SKPCLP
;   FNDCLUS
;   BUFSEC
;   BUFRD
;   BUFWRT
;   NEXTSEC
;   OPTIMIZE
;   FIGREC
;   ALLOCATE
;   RESTFATBYT
;   RELEASE
;   RELBLKS
;   GETEOF
;
;   Modification history:
;
;       Created: ARR 30 March 1983
;

;
; get the appropriate segment definitions
;
.xlist
include dosseg.asm
include fastseek.inc			       ; DOS 4.00
include fastxxxx.inc			       ; DOS 4.00
include version.inc

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

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

        i_need  CLUSNUM,WORD
        i_need  NEXTADD,WORD
        i_need  LASTPOS,WORD
        i_need  SECCLUSPOS,BYTE
        i_need  FATBYT,WORD
        i_need  THISSFT,DWORD
        i_need  TRANS,BYTE
        i_need  BYTCNT1,WORD
        i_need  CURBUF,DWORD
        i_need  BYTSECPOS,WORD
        i_need  DMAADD,WORD
        i_need  SECPOS,DWORD                         ;F.C. >32mb
        i_need  VALSEC,DWORD                         ;F.C. >32mb
        i_need  ALLOWED,BYTE
        i_need  FSeek_drive,BYTE                     ; DOS 3.4
        i_need  FSeek_firclus,WORD                   ; DOS 3.4
        i_need  FSeek_logclus,WORD                   ; DOS 3.4
        i_need  FSeek_logsave,WORD                   ; DOS 3.4
        i_need  FastSeekFlg,BYTE                     ; DOS 3.4
        i_need  XA_condition,BYTE                    ; DOS 3.4
        i_need  HIGH_SECTOR,WORD                     ; DOS 3.4
        i_need  DISK_FULL,BYTE                       ; DOS 3.4
        i_need  Temp_VAR2,WORD                       ; DOS 3.4



Break   <FNDCLUS -- Skip over allocation units>

; Inputs:
;       CX = No. of clusters to skip
;       ES:BP = Base of drive parameters
;       [THISSFT] point to SFT
; Outputs:
;       BX = Last cluster skipped to
;       CX = No. of clusters remaining (0 unless EOF)
;       DX = Position of last cluster
;       Carry set if error (currently user FAILed to I 24)
; DI destroyed. No other registers affected.

        procedure   FNDCLUS,NEAR
        DOSAssume   CS,<DS>,"FndClus"
        ASSUME  ES:NOTHING

        Assert      ISDPB,<ES,BP>,"FndCLus"
;; 10/31/86 FastSeek
        PUSH    ES
        LES     DI,[THISSFT]
        Assert      ISSFT,<ES,DI>,"FndClus"
        MOV     [FSeek_logclus],CX                ; presume CX is the position  ;AN000;
        MOV     BX,ES:[DI.sf_lstclus]
        MOV     DX,ES:[DI.sf_cluspos]
;; 10/31/86 FastSeek
        OR      BX,BX
        JNZ     YCLUS
        JMP     NOCLUS
YCLUS:
        SUB     CX,DX
        JNB     FINDIT
        ADD     CX,DX
        XOR     DX,DX
        MOV     BX,ES:[DI.sf_firclus]
FINDIT:
;; 10/31/86 FastSeek


        POP     ES
        OR      CX,CX
        JNZ     skpclp
        JMP     RET10

entry   SKPCLP
        TEST    [FastSeekflg],Fast_yes            ; fastseek installed?         ;AN000;
        JZ      do_norm                           ; no                          ;AN000;
        TEST    [FastSeekflg],FS_begin            ; do fastseek                 ;AN000;
        JZ      do_norm                           ; no                          ;AN000;
        TEST    [FastSeekflg],FS_insert           ; is in insert mode ?         ;AN000;
        JNZ     do_norm                           ; yes                         ;AN000;
        MOV     [Temp_Var2],BX                    ; save physical cluster       ;AN000;
                                                  ; PTR P005079
SKPCLP2:
        invoke  FastSeek_Lookup                   ; ask for next cluster #      ;AN000;
        JNC     clusfound                         ; yes, we got it              ;AN000;
        CMP     DI,1                              ; valid drive ,e.g. C,D...    ;AN000;
        JNZ     par_found                         ; yes,                        ;AN000;
        AND     [FastSeekflg],Fast_yes            ; no more, fastseek           ;AN000;
        JMP     SHORT do_norm                                                   ;AN000;
                                                                                ;AN000;
par_found:
        CALL    FS_Trunc_EOF                      ; check EOF and truncate      ;AN000;
        JNC     SKPCLP2                           ; redo lookup                 ;AN000;
noteof:
        OR      [FastSeekflg],FS_insert           ; no, start to insert         ;AN000;
        CMP     DX,[FSeek_logsave]                ; is current better than new? ;AN000;
        JBE     OnCache                           ; no, let's use new           ;AN000;
        MOV     [FSeek_logclus],DX                ; use current                 ;AN000;
        MOV     BX,[Temp_Var2]                    ; retore pysical cluster      ;AN000;
        MOV     DI,BX                             ; insert cureent cluster      ;AN000;
        invoke  FastSeek_Insert                   ; insert cluster # to         ;AN000;
        INC     [FSeek_logclus]                   ; get next inserted position  ;AN000;
        JMP     SHORT do_norm
OnCache:
        MOV     CX,[FSeek_logclus]                ; get the number of clusters  ;AN000;
        SUB     CX,[FSeek_logsave]                ; we need to skip             ;AN000;
        MOV     DX,[FSeek_logsave]                ; cluster position            ;AN000;
dodo:
        INC     [FSeek_logsave]                   ; get next inserted position  ;AN000;
        PUSH    [FSeek_logsave]                   ; logclus=logsave             ;AN000;
        POP     [FSeek_logclus]                                                 ;AN000;

do_norm:

        invoke  UNPACK
        retc

        invoke  FastSeek_Insert                   ; insert cluster # to         ;AN000;
cluss:                                                                          ;AN000;
        PUSH    BX                                ; FastSeek                    ;AN000;
        MOV     BX,DI
        Invoke  IsEOF
        POP     BX
        JAE     RET10
        XCHG    BX,DI
        INC     DX
        INC     [FSeek_logclus]                  ; increment for next inserted  ;AN000;
        LOOP    SKPCLPX
        JMP     short  RET10
SKPCLPX:
        JMP     SKPCLP
RET10:                                                                          ;AN000;
        AND     [FastSeekflg],FS_no_insert       ; clear insert mode
        CLC
        return
NOCLUS:
        POP     ES
        INC     CX
        DEC     DX
        CLC
        return
clusfound:
        MOV     DX,[FSeek_logclus]                ; get cluster position        ;AN000;
        MOV     BX,[FSeek_logsave]                ; bx=previous cluster # PTM   ;AN000;
        DEC     DX                                                              ;AN000;
        MOV     CX,1                              ; we found it                 ;AN000;
        JMP     cluss                                                           ;AN000;

EndProc FNDCLUS

Break  <BUFSEC -- BUFFER A SECTOR AND SET UP A TRANSFER>

; Inputs:
;       AH = priority of buffer
;       AL = 0 if buffer must be read, 1 if no pre-read needed
;       ES:BP = Base of drive parameters
;       [CLUSNUM] = Physical cluster number
;       [SECCLUSPOS] = Sector position of transfer within cluster
;       [BYTCNT1] = Size of transfer
; Function:
;       Insure specified sector is in buffer, flushing buffer before
;       read if necessary.
; Outputs:
;       ES:DI = Pointer to buffer
;       SI = Pointer to transfer address
;       CX = Number of bytes
;       [NEXTADD] updated
;       [TRANS] set to indicate a transfer will occur
;       Carry set if error (user FAILed to I 24)

        procedure   BUFSEC,NEAR
        DOSAssume   CS,<DS>,"BufSec"
        ASSUME  ES:NOTHING

        Assert      ISDPB,<ES,BP>,"BufSec"
        MOV     DX,[CLUSNUM]
        MOV     BL,[SECCLUSPOS]
        MOV     [ALLOWED],allowed_FAIL + allowed_RETRY + allowed_IGNORE
        CALL    FIGREC
        invoke  GETBUFFR
        retc
        MOV     BYTE PTR [TRANS],1      ; A transfer is taking place
        MOV     SI,[NEXTADD]
        MOV     DI,SI
        MOV     CX,[BYTCNT1]
        ADD     DI,CX
        MOV     [NEXTADD],DI
        LES     DI,[CURBUF]
        Assert  ISBUF,<ES,DI>,"BufSec"
        OR      ES:[DI.buf_flags],buf_isDATA
        LEA     DI,[DI].BUFINSIZ        ; Point to buffer
        ADD     DI,[BYTSECPOS]
        CLC
        return
EndProc BUFSEC

Break   <BUFRD, BUFWRT -- PERFORM BUFFERED READ AND WRITE>

; Do a partial sector read via one of the system buffers
; ES:BP Points to DPB
; Carry set if error (currently user FAILed to I 24)

        procedure   BUFRD,NEAR
        DOSAssume   CS,<DS>,"BufRd"
        ASSUME  ES:NOTHING

        Assert      ISDPB,<ES,BP>,"BufRd"
        PUSH    ES
        MOV     AX,0
        CALL    BUFSEC
        JNC     BUF_OK
BUF_IO_FAIL:
        POP     ES
        JMP     SHORT RBUFPLACED

BUF_OK:
        MOV     BX,ES
        MOV     ES,[DMAADD+2]
        MOV     DS,BX
ASSUME  DS:NOTHING
        XCHG    DI,SI
        SHR     CX,1
        JNC     EVENRD
        MOVSB
EVENRD:
        REP     MOVSW
        POP     ES
        LDS     DI,[CURBUF]
        Assert  ISBUF,<DS,DI>,"BufRD/EvenRD"
        LEA     BX,[DI.BufInSiz]
        SUB     SI,BX                   ; Position in buffer
        invoke  PLACEBUF
        Assert  ISDPB,<ES,BP>,"BufRD/EvenRD"
        CMP     SI,ES:[BP.dpb_sector_size] ; Read Last byte?
        JB      RBUFPLACEDC             ; No, leave buf where it is
        invoke  PLACEHEAD               ; Make it prime candidate for chucking
                                        ;  even though it is MRU.
RBUFPLACEDC:
        CLC
RBUFPLACED:
        PUSH    SS
        POP     DS
        return
EndProc BUFRD

; Do a partial sector write via one of the system buffers
; ES:BP Points to DPB
; Carry set if error (currently user FAILed to I 24)

        procedure   BUFWRT,NEAR
        DOSAssume   CS,<DS>,"BufWrt"
        ASSUME  ES:NOTHING

        Assert      ISDPB,<ES,BP>,"BufWrt"
        MOV     AX,WORD PTR [SECPOS]
        ADD     AX,1            ; Set for next sector
        MOV     WORD PTR [SECPOS],AX      ;F.C. >32mb                           ;AN000;
        ADC     WORD PTR [SECPOS+2],0     ;F.C. >32mb                           ;AN000;
        MOV     AX,WORD PTR [SECPOS+2]    ;F.C. >32mb                           ;AN000;
        CMP     AX,WORD PTR [VALSEC+2]    ;F.C. >32mb                           ;AN000;
        MOV     AL,1                      ;F.C. >32mb                           ;AN000;
        JA      NOREAD                    ;F.C. >32mb                           ;AN000;
        JB      doread                    ;F.C. >32mb                           ;AN000;
        MOV     AX,WORD PTR [SECPOS]      ;F.C. >32mb                           ;AN000;
        CMP     AX,WORD PTR [VALSEC]     ; Has sector been written before?
        MOV     AL,1
        JA      NOREAD          ; Skip preread if SECPOS>VALSEC
doread:
        XOR     AL,AL
NOREAD:
        PUSH    ES
        CALL    BUFSEC
        JC      BUF_IO_FAIL
        MOV     DS,[DMAADD+2]
ASSUME  DS:NOTHING
        SHR     CX,1
        JNC     EVENWRT
        MOVSB
EVENWRT:
        REP     MOVSW
        POP     ES
        LDS     BX,[CURBUF]
        Assert  ISBUF,<DS,BX>,"BufWrt/EvenWrt"

        TEST    [BX.buf_flags],buf_dirty  ;LB. if already dirty                 ;AN000;
        JNZ     yesdirty                  ;LB.    don't increment dirty count   ;AN000;
        invoke  INC_DIRTY_COUNT           ;LB.                                  ;AN000;
        OR      [BX.buf_flags],buf_dirty
yesdirty:
        LEA     SI,[BX.BufInSiz]
        SUB     DI,SI                   ; Position in buffer
        MOV     SI,DI
        MOV     DI,BX
        invoke  PLACEBUF
        Assert  ISDPB,<ES,BP>,"BufWrt/EvenWrt"
        CMP     SI,ES:[BP.dpb_sector_size]  ; Written last byte?
        JB      WBUFPLACED              ; No, leave buf where it is
        invoke  PLACEHEAD               ; Make it prime candidate for chucking
                                        ;  even though it is MRU.
WBUFPLACED:
        CLC
        PUSH    SS
        POP     DS
        return
EndProc BUFWRT

Break   <NEXTSEC -- Compute next sector to read or write>

; Compute the next sector to read or write
; ES:BP Points to DPB

        procedure   NEXTSEC,NEAR
        DOSAssume   CS,<DS>,"NextSec"
        ASSUME  ES:NOTHING

        Assert      ISDPB,<ES,BP>,"NextSec"
        TEST    BYTE PTR [TRANS],-1
        JZ      CLRET
        MOV     AL,[SECCLUSPOS]
        INC     AL
        CMP     AL,ES:[BP.dpb_cluster_mask]
        JBE     SAVPOS
        MOV     BX,[CLUSNUM]
        Invoke  IsEOF
        JAE     NONEXT
;; 11/5/86 FastSeek
        TEST    [FastSeekflg],Fast_yes            ; fastseek installed?         ;AN000;
        JZ      do_norm2                          ; no                          ;AN000;
        PUSH    [LASTPOS]               ; save logical cluster #                ;AN000;
        POP     [FSeek_logclus]                                                 ;AN000;
        INC     [FSeek_logclus]         ; get next cluster                      ;AN000;
                                                                                ;AN000;
        TEST    [FastSeekflg],FS_begin  ; from R/W                              ;AN000;
        JZ      do_norm2                ; no                                    ;AN000;
look2:                                                                          ;AN000;
        invoke  FastSeek_Lookup         ; call lookup                           ;AN000;
        JNC     clusgot                 ; found one                             ;AN000;

        CMP     DI,1                              ; valid drive ,e.g. C,D...    ;AN000;
        JNZ     parfound2                         ; yes,                        ;AN000;
        AND     [FastSeekflg],Fast_yes            ; no more, fastseek           ;AN000;
        JMP     SHORT do_norm2                                                  ;AN000;
parfound2:
        CALL    FS_TRUNC_EOF            ; check EOF                             ;AN000;
        MOV     BX,[CLUSNUM]            ; don't need partially found cluster
        OR      [FastSeekflg],FS_insert ; prepared for cluster insertion        ;AN000;
                                        ; use the old bx                        ;AN000;
                                                                                ;AN000;
do_norm2:
        invoke  UNPACK
        JC      NONEXT
        invoke  FastSeek_Insert         ; call insert                           ;AN000;
        AND     [FastSeekflg],FS_no_insert  ; clear insert flag                 ;AN000;
                                                                                ;AN000;
clusgot:
;; 11/5/86 FastSeek
        MOV     [CLUSNUM],DI
        INC     [LASTPOS]
        MOV     AL,0
SAVPOS:
        MOV     [SECCLUSPOS],AL
CLRET:
        CLC
        return
NONEXT:
        STC
        return
EndProc NEXTSEC

Break   <OPTIMIZE -- DO A USER DISK REQUEST WELL>

; Inputs:
;       BX = Physical cluster
;       CX = No. of records
;       DL = sector within cluster
;       ES:BP = Base of drives parameters
;       [NEXTADD] = transfer address
; Outputs:
;       AX = No. of records remaining
;       BX = Transfer address
;       CX = No. or records to be transferred
;       DX = Physical sector address            (LOW)
;       [HIGH_SECTOR] = Physical sector address (HIGH)
;       DI = Next cluster
;       [CLUSNUM] = Last cluster accessed
;       [NEXTADD] updated
;       Carry set if error (currently user FAILed to I 24)
; ES:BP unchanged. Note that segment of transfer not set.

        procedure   OPTIMIZE,NEAR
        DOSAssume   CS,<DS>,"Optimize"
        ASSUME  ES:NOTHING

        Assert      ISDPB,<ES,BP>,"Optimize"
        PUSH    DX
        PUSH    BX
        MOV     AL,ES:[BP.dpb_cluster_mask]
        INC     AL              ; Number of sectors per cluster
        MOV     AH,AL
        SUB     AL,DL           ; AL = Number of sectors left in first cluster
        MOV     DX,CX
        MOV     CX,0
;;; 11/5/86 FastSeek
        PUSH    [LASTPOS]               ; save logical cluster #                ;AN000;
        POP     [FSeek_logclus]                                                 ;AN000;
        INC     [FSeek_logclus]         ; get next cluster                      ;AN000;
                                                                                ;AN000;
OPTCLUS:
; AL has number of sectors available in current cluster
; AH has number of sectors available in next cluster
; BX has current physical cluster
; CX has number of sequential sectors found so far
; DX has number of sectors left to transfer
; ES:BP Points to DPB
; ES:SI has FAT pointer

        TEST    [FastSeekflg],Fast_yes            ; fastseek installed?         ;AN000;
        JZ      do_norm3                          ; no                          ;AN000;
        TEST    [FastSeekflg],FS_begin            ; from R/W                    ;AN000;
        JZ      do_norm3                          ; no                          ;AN000;
        TEST    [FastSeekflg],FS_insert           ; is in insert mode ?         ;AN000;
        JNZ     do_norm3                          ; yes                         ;AN000;
        invoke  FastSeek_Lookup                   ; call lookup                 ;AN000;
        JNC     clusgot2                          ; found one                   ;AN000;

        CMP     DI,1                              ; valid drive ,e.g. C,D...    ;AN000;
        JNZ     par_found3                        ; yes,                        ;AN000;
        AND     [FastSeekflg],Fast_yes            ; no more, fastseek           ;AN000;
        JMP     SHORT do_norm3                                                  ;AN000;
par_found3:
        PUSH    BX
        CALL    FS_TRUNC_EOF                      ; file entry not existing     ;AN000;
        POP     BX                                                              ;AN000;
        OR      [FastSeekflg],FS_insert           ; prepare for insertion       ;AN000;
                                                  ; use old bx                  ;AN000;
do_norm3:
        invoke  UNPACK
        JC      OP_ERR
clusgot2:
        invoke  FastSeek_Insert         ; call insert                           ;AN000;
        INC     [FSeek_logclus]         ; insert to next position               ;AN000;
;;; 11/5/86 FastSeek                                                            ;AN000;
        ADD     CL,AL
        ADC     CH,0
        CMP     CX,DX
        JAE     BLKDON
        MOV     AL,AH
        INC     BX
        CMP     DI,BX
        JZ      OPTCLUS
        DEC     BX
FINCLUS:
        MOV     [CLUSNUM],BX    ; Last cluster accessed
        SUB     DX,CX           ; Number of sectors still needed
        PUSH    DX
        MOV     AX,CX
        MUL     ES:[BP.dpb_sector_size]  ; Number of sectors times sector size
        MOV     SI,[NEXTADD]
        ADD     AX,SI           ; Adjust by size of transfer
        MOV     [NEXTADD],AX
        POP     AX              ; Number of sectors still needed
        POP     DX              ; Starting cluster
        SUB     BX,DX           ; Number of new clusters accessed
        ADD     [LASTPOS],BX
        POP     BX              ; BL = sector postion within cluster
        invoke  FIGREC
        MOV     BX,SI
        AND     [FastSeekflg],FS_no_insert  ; clear insert flag
        CLC
        return

OP_ERR:
        ADD     SP,4
        AND     [FastSeekflg],FS_no_insert  ; clear insert flag
        STC
        return

BLKDON:
        SUB     CX,DX           ; Number of sectors in cluster we don't want
        SUB     AH,CL           ; Number of sectors in cluster we accepted
        DEC     AH              ; Adjust to mean position within cluster
        MOV     [SECCLUSPOS],AH
        MOV     CX,DX           ; Anyway, make the total equal to the request
        JMP     SHORT FINCLUS
EndProc OPTIMIZE

Break   <FIGREC -- Figure sector in allocation unit>

; Inputs:
;       DX = Physical cluster number
;       BL = Sector postion within cluster
;       ES:BP = Base of drive parameters
; Outputs:
;       DX = physical sector number           (LOW)
;       [HIGH_SECTOR] Physical sector address (HIGH)
; No other registers affected.

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

        Assert      ISDPB,<ES,BP>,"FigRec"
        PUSH    CX
        MOV     CL,ES:[BP.dpb_cluster_shift]
        DEC     DX
        DEC     DX
        MOV     [HIGH_SECTOR],0              ;F.C. >32mb
        OR      CL,CL                        ;F.C. >32mb
        JZ      noshift                      ;F.C. >32mb
        XOR     CH,CH                        ;F.C. >32mb
rotleft:                                     ;F.C. >32mb
        CLC                                  ;F.C. >32mb
        RCL     DX,1                         ;F.C. >32mb
        RCL     [HIGH_SECTOR],1              ;F.C. >32mb
        LOOP    rotleft                      ;F.C. >32mb
noshift:

;       SHL     DX,CL
        OR      DL,BL
        ADD     DX,ES:[BP.dpb_first_sector]
        ADC     [HIGH_SECTOR],0              ;F.C. >32mb
        POP     CX
        return
EndProc FIGREC

Break   <ALLOCATE -- Assign disk space>

;***    ALLOCATE - Allocate Disk Space
;
;   ALLOCATE is called to allocate disk clusters.  The new clusters are
;   FAT-chained onto the end of the existing file.
;
;   The DPB contains the cluster # of the last free cluster allocated
;   (dpb_next_free).  We start at this cluster and scan towards higher
;   numbered clusters, looking for the necessary free blocks.
;
;   Once again, fancy terminology gets in the way of corrct coding.  When
;   using next_free, start scanning AT THAT POINT and not the one following it.
;   This fixes the boundary condition bug when only free = next_free = 2.
;
;       If we get to the end of the disk without satisfaction:
;
;           if (dpb_next_free == 2) then we've scanned the whole disk.
;               return (insufficient_disk_space)
;           ELSE
;               dpb_next_free = 2; start scan over from the beginning.
;
;   Note that there is no multitasking interlock.  There is no race when
;   examining the entrys in an in-core FAT block since there will be no
;   context switch.  When UNPACK context switches while waiting for a FAT read
;   we are done with any in-core FAT blocks, so again there is no race.  The
;   only special concern is that V2 and V3 MSDOS left the last allocated
;   cluster as "00"; marking it EOF only when the entire alloc request was
;   satisfied.  We can't allow another activation to think this cluster is
;   free, so we give it a special temporary mark to show that it is, indeed,
;   allocated.
;
;   Note that when we run out of space this algorithem will scan from
;   dpb_next_free to the end, then scan from cluster 2 through the end,
;   redundantly scanning the later part of the disk.  This only happens when
;   we run out of space, so sue me.
;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;            C  A  V  E  A  T     P  A  T  T  E  R  S  O  N                ;
;                                                                          ;
;   The use of FATBYT and RESTFATBYT is somewhat mysterious.  Here is the
;   explanation:
;
;   In the NUL file case (sf_firclus currently 0) ALLOCATE is called with
;   entry BX = 0.  What needs to be done in this case is to stuff the cluster
;   number of the first cluster allocated in sf_firclus when the ALLOCATE is
;   complete.  THIS VALUE IS SAVED TEMPORARILY IN CLUSTER 0, HENCE THE CURRENT
;   VALUE IN CLUSTER 0 MUST BE SAVED AND RESTORED.  This is a side effect of
;   the fact that PACK and UNPACK don't treat requests for clusters 0 and 1 as
;   errors.  This "stuff" is done by the call to PACK which is right before
;   the
;           LOOP    findfre         ; alloc more if needed
;   instruction when the first cluster is allocated to the nul file.  The
;   value is recalled from cluster 0 and stored at sf_firclus at ads4:
;
;   This method is obviously useless (because it is non-reentrant) for
;   multitasking, and will have to be changed.  Storing the required value on
;   the stack is recommended.  Setting sf_firclus at the PACK of cluster 0
;   (instead of actually doing the PACK) is BAD because it doesn't handle
;   problems with INT 24 well.
;
;            C  A  V  E  A  T     P  A  T  T  E  R  S  O  N                ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;
;                                                                          ;
;       ENTRY   BX = Last cluster of file (0 if null file)
;               CX = No. of clusters to allocate
;               ES:BP = Base of drive parameters
;               [THISSFT] = Points to SFT
;
;       EXIT    'C' set if insufficient space
;                 [FAILERR] can be tested to see the reason for failure
;                 CX = max. no. of clusters that could be added to file
;               'C' clear if space allocated
;                 BX = First cluster allocated
;                 FAT is fully updated
;                 sf_FIRCLUS field of SFT set if file was null
;
;       USES    ALL but SI, BP

        PROCEDURE   ALLOCATE,NEAR

        DOSAssume   CS,<DS>,"Allocate"
        ASSUME  ES:NOTHING

        Assert      ISDPB,<ES,BP>,"Allocate"
        PUSH    BX                      ; save (bx)
        XOR     BX,BX
        invoke  UNPACK
        MOV     [FATBYT],DI             ; save correct cluster 0 value
        POP     BX
        retc                            ; abort if error   [INTERR?]

        PUSH    CX
        PUSH    BX

        MOV     DX,BX
        Assert      ISDPB,<ES,BP>,"Allocate/Unpack"
        mov     bx,es:[bp.dpb_next_free]
        cmp     bx,2
        ja      findfre

;   couldn't find enough free space beyond dpb_next_free, or dpb_next_free is
;   <2 or >dpb_max_clus.  Reset it and restart the scan

ads1:
        Assert      ISDPB,<ES,BP>,"Alloc/ads1"
        mov     es:[bp.dpb_next_free],2
        mov     bx,1                    ; Counter next instruction so first
                                        ;       cluster examined is 2

;   Scanning both forwards and backwards for a free cluster
;
;       (BX) = forwards scan pointer
;       (CX) = clusters remaining to be allocated
;       (DX) = current last cluster in file
;       (TOS) = last cluster of file

FINDFRE:
        INC     BX
        Assert      ISDPB,<ES,BP>,"Alloc/findfre"
        CMP     BX,ES:[BP.dpb_max_cluster]
        JBE     aupk
        jmp     ads7            ; at end of disk
aupk:
        invoke  UNPACK          ; check out this cluster
        jc      ads4            ; FAT error             [INTERR?]
        jnz     findfre         ; not free, keep on truckin

;   Have found a free cluster.  Chain it to the file
;
;       (BX) = found free cluster #
;       (DX) = current last cluster in file

        mov     es:[bp.dpb_next_free],bx        ; next time start search here
        xchg    ax,dx           ; save (dx) in ax
        mov     dx,1            ; mark this free guy as "1"
        invoke  PACK            ; set special "temporary" mark
        jc      ads4            ; FAT error             [INTERR?]
        CMP     ES:[BP.dpb_free_cnt],-1 ; Free count valid?
        JZ      NO_ALLOC                ; No
        DEC     ES:[BP.dpb_free_cnt]    ; Reduce free count by 1
NO_ALLOC:
        xchg    ax,dx           ; (dx) = current last cluster in file
        XCHG    BX,DX
        MOV     AX,DX
        invoke  PACK            ; link free cluster onto file
                                ;  CAVEAT.. On Nul file, first pass stuffs
                                ;    cluster 0 with FIRCLUS value.
        jc      ads4            ; FAT error             [INTERR?]
        xchg    BX,AX           ; (BX) = last one we looked at
        mov     dx,bx           ; (dx) = current end of file
        LOOP    findfre         ; alloc more if needed

;   We've successfully extended the file.  Clean up and exit
;
;       (BX) = last cluster in file

        MOV     DX,0FFFFH
        invoke  PACK            ; mark last cluster EOF

;   Note that FAT errors jump here to clean the stack and exit.  this saves us
;   2 whole bytes.  Hope its worth it...
;
;       'C' set iff error
;       calling (BX) and (CX) pushed on stack

ads4:   POP     BX
        POP     CX              ; Don't need this stuff since we're successful
        retc
        invoke  UNPACK          ; Get first cluster allocated for return
                                ; CAVEAT... In nul file case, UNPACKs cluster 0.
        retc
        invoke  RESTFATBYT      ; Restore correct cluster 0 value
        retc
        XCHG    BX,DI           ; (DI) = last cluster in file upon our entry
        OR      DI,DI           ; clear 'C'
        retnz                   ; we were extending an existing file

;   We were doing the first allocation for a new file.  Update the SFT cluster
;   info
;; Extended Attributes
;       TEST    [XA_condition],XA_No_SFT      ;FT. don't update SFT when from   ;AN000;
;       JZ      dofastk                       ;FT. GetSet_XA                    ;AN000;
;       AND     [XA_condition],not XA_No_SFT  ;FT. clear the bit                ;AN000;
;       CLC                                   ;FT.                              ;AN000;
;       ret                                   ;FT.                              ;AN000;
;; 11/6/86 FastSeek
dofastk:
        PUSH    DX
        MOV     DL,ES:[BP.dpb_drive]              ; get drive #

        PUSH    ES
        LES     DI,[THISSFT]
        Assert  ISSFT,<ES,DI>,"Allocate/ads4"
        MOV     ES:[DI.sf_firclus],BX
        MOV     ES:[DI.sf_lstclus],BX

        TEST    [FastSeekflg],Fast_yes           ; fastseek installed
        JZ      do_norm5                         ; no
        TEST    [FastSeekflg],FS_begin           ; do fastseek
        JZ      do_norm5                          ; no
        PUSH    CX
        MOV     CX,BX                             ; set up firclus #
        MOV     [FSeek_firclus],BX                ; update firclus varible
        invoke  FastSeek_Open                     ; create this file entry
        POP     CX
do_norm5:
        POP     ES
;; 11/6/86 FastSeek
        POP     DX
        return


;** we're at the end of the disk, and not satisfied.  See if we've scanned ALL
;   of the disk...

ads7:   cmp     es:[bp.dpb_next_free],2
if not debug
        jz      tmplab2         ;
        jmp     ads1            ; start scan from front of disk
tmplab2:
else
        jz      tmplab
        jmp     ads1
tmplab:
endif

;   Sorry, we've gone over the whole disk, with insufficient luck.  Lets give
;   the space back to the free list and tell the caller how much he could have
;   had.  We have to make sure we remove the "special mark" we put on the last
;   cluster we were able to allocate, so it doesn't become orphaned.
;
;       (CX) = clusters remaining to be allocated
;       (TOS) = last cluster of file (before call to ALLOCATE)
;       (TOS+1) = # of clusters wanted to allocate


        POP     BX              ; (BX) = last cluster of file
        MOV     DX,0FFFFH
        invoke  RELBLKS         ; give back any clusters just alloced
        POP     AX              ; No. of clusters requested
                                ; Don't "retc". We are setting Carry anyway,
                                ;   Alloc failed, so proceed with return CX
                                ;   setup.
        SUB     AX,CX           ; AX=No. of clusters allocated
        invoke  RESTFATBYT      ; Don't "retc". We are setting Carry anyway,
                                ;   Alloc failed.
;       fmt     <>,<>,<"$p: disk full in allocate\n">
        MOV     [DISK_FULL],1   ;MS. indicating disk full
        STC
        return

EndProc ALLOCATE

; SEE ALLOCATE CAVEAT
;       Carry set if error (currently user FAILed to I 24)

        procedure   RESTFATBYT,NEAR
        DOSAssume   CS,<DS>,"RestFATByt"
        ASSUME  ES:NOTHING

        PUSH    BX
        PUSH    DX
        PUSH    DI
        XOR     BX,BX
        MOV     DX,[FATBYT]
        invoke  PACK
        POP     DI
        POP     DX
        POP     BX
        return
EndProc RESTFATBYT

Break   <RELEASE -- DEASSIGN DISK SPACE>

; Inputs:
;       BX = Cluster in file
;       ES:BP = Base of drive parameters
; Function:
;       Frees cluster chain starting with [BX]
;       Carry set if error (currently user FAILed to I 24)
; AX,BX,DX,DI all destroyed. Other registers unchanged.

        procedure   RELEASE,NEAR
        DOSAssume   CS,<DS>,"Release"
        ASSUME  ES:NOTHING

        XOR     DX,DX
entry   RELBLKS
        DOSAssume   CS,<DS>,"RelBlks"
        Assert      ISDPB,<ES,BP>,"RelBlks"

;   Enter here with DX=0FFFFH to put an end-of-file mark in the first cluster
;   and free the rest in the chain.

        invoke  UNPACK
        retc
        retz
        MOV     AX,DI
        PUSH    DX
        invoke  PACK
        POP     DX
        retc
        OR      DX,DX
        JNZ     NO_DEALLOC              ; Was putting EOF mark
        CMP     ES:[BP.dpb_free_cnt],-1 ; Free count valid?
        JZ      NO_DEALLOC              ; No
        INC     ES:[BP.dpb_free_cnt]    ; Increase free count by 1
NO_DEALLOC:
        MOV     BX,AX
        dec     ax              ; check for "1"
        retz                    ; is last cluster of incomplete chain
        Invoke  IsEOF
        JB      RELEASE         ; Carry clear if JMP not taken
ret12:  return
EndProc RELEASE

Break   <GETEOF -- Find the end of a file>

; Inputs:
;       ES:BP Points to DPB
;       BX = Cluster in a file
;       DS = CS
; Outputs:
;       BX = Last cluster in the file
;       Carry set if error (currently user FAILed to I 24)
; DI destroyed. No other registers affected.

        procedure   GETEOF,NEAR
        DOSAssume   CS,<DS>,"GetEOF"
        ASSUME  ES:NOTHING

        Assert      ISDPB,<ES,BP>,"GetEof"
        invoke  UNPACK
        retc
        PUSH    BX
        MOV     BX,DI
        Invoke  IsEOF
        POP     BX
        JAE     RET12           ; Carry clear if jmp
        MOV     BX,DI
        JMP     GETEOF
EndProc GETEOF

Break   <FS_TRUNC_EOF - truncate EOF for Fastseek>

; Inputs:
;       ES:BP Points to DPB
;       BX = Cluster in a file
; Functions: if BX=EOF then truncate it from Fastseek Cache
; Outputs:
;       carry set: not EOF
;       carry clear: EOF and do truncate

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

        MOV     BX,DI                             ; get beginning physical#     ;AN000;
        invoke  IsEOF                             ; is EOF                      ;AN000;
        JB      noteof2                           ; no                          ;AN000;
        PUSH    [FSeek_logclus]                   ;                             ;AN000;
        PUSH    [FSeek_logsave]                   ; logclus=logsave             ;AN000;
        POP     [FSeek_logclus]                   ; delete EOF                  ;AN000;
        invoke  FastSeek_Truncate                 ;                             ;AN000;
        POP     [FSeek_logclus]                   ; redo the look up            ;AN000;
        CLC                                                                     ;AN000;
noteof2:                                                                        ;AN000;
        return                                                                  ;AN000;
EndProc FS_TRUNC_EOF                                                            ;AN000;

CODE    ENDS
    END