/
disk.asm
1320 lines (1224 loc) · 36.9 KB
/
disk.asm
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
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
PAGE 58,132
TITLE DISK ----- 06/10/85 FIXED DISK BIOS
.286C
.LIST
CODE SEGMENT BYTE PUBLIC
PUBLIC DISK_IO
PUBLIC DISK_SETUP
PUBLIC HD_INT
EXTAN CMOS_READ:NEAR
EXTRN CMOS_WRITE:NEAR
EXTRN DDS:NEAR
EXTRN E_MSG:NEAR
EXTRN F1780:NEAR
EXTRN F1781:NEAR
EXTRN F1782:NEAR
EXTRN F1790:NEAR
EXTRN F1791;NEAR
EXTRN FD_TBL:NEAR
;--- INT 13H ----------------------------------------------------------------------------
; :
; FIXED DISK I/O INTERFACE :
; :
; THIS INTERFACE PROVIDES ACCESS TO 5 1/4" FIXED DISKS THROUGH :
; THE IBM FIXED DISK CONTROLLER. :
; :
; THE BIOS ROUTINES ARE MEANT TO BE ACCESSED THROUGH :
; SOFTWARE INTERRUPTS ONLY. ANY ADDRESSES PRESENT IN :
; THESE LISTINGS ARE INCLUDED ONLY FOR COMPLETENESS, :
; NOT FOR REFERENCE. APPLICATIONS WHICH REFERENCE ANY :
; ABSOLUTE ADDRESSES WITHIN THE CODE SEGMENTS OF BIOS :
; VIOLATE THE STRUCTURE AND DESIGN OF BIOS. :
; :
;----------------------------------------------------------------------------------------
; :
; INPUT (AH)= HEX COMMAND VALUE :
; :
; (AH)= 00H RESET DISK (DL = 80H,81H) / DISKETTE :
; (AH)= 01H READ THE STATUS OF THE LAST DISK OPERATION INTO (AL) :
; NOTE: DL < 80H - DISKETTE :
; DL > 80H - DISK :
; (AH)= 02H READ THE DESIRED SECTORS INTO MEMORY :
; (AH)= 03H WRITE THE DESIRED SECTORS FROM MEMORY :
; (AH)= 04H VERIFY THE DESIRED SECTORS :
; (AH)= 05H FORMAT THE DESIRED TRACK :
; (AH)= 06H UNUSED :
; (AH)= 07H UNUSED :
; (AH)= 08H RETURN THE CURRENT DRIVE PARAMETERS :
; (AH)= 09H INITIALIZE DRIVE PAIR CHARACTERISTICS :
; INTERRUPT 41 POINTS TO DATA BLOCK FOR DRIVE 0 :
; INTERRUPT 46 POINTS TO DATA BLOCK FOR DRIVE 1 :
; (AH)= 0AH READ LONG :
; (AH)= 0BH WRITE LONG (READ A WRITE LONG ENCOMPASS 512 + 4 BYTES ECC) :
; (AH)= 0CH SEEK :
; (AH)= 0DH ALTERNATE DISK RESET (SEE DL) :
; (AH)= 0EH UNUSED :
; (AH)= 0FH UNUSED :
; (AH)= 10H TEST DRIVE READY :
; (AH)= 11H RECALIBRATE :
; (AH)= 12H UNUSED :
; (AH)= 13H UNUSED :
; (AH)= 14H CONTROLLER INTERNAL DIAGNOSTIC :
; (AH)= 15H READ DASD TYPE :
; :
;----------------------------------------------------------------------------------------
; :
; REGISTERS USED FOR FIXED DISK OPERATIONS :
; :
; (DL) - DRIVE NUMBER (80H-81H FOR DISK. VALUE CHECKED) :
; (DH) - HEAD NUMBER (0-15 ALLOWED, NOT VALUE CHECKED) :
; (CH) - CYLINDER NUMBER (0-1023, NOT VALUE CHECKED)(SEE CL) :
; (CL) - SECTOR NUMBER (1-17, NOT VALUE CHECKED) :
; :
; NOTE: HIGH 2 BITS OF CYLINDER NUMBER ARE PLACED :
; IN THE HIGH 2 BITS OF THE CL REGISTER :
; 10 BITS TOTAL) :
; :
; (AL) - NUMBER OF SECTORS (MAXIMUM POSSIBLE RANGE 1-80H, :
; FOR READ/WRITE LONG 1-79H) :
; :
; (ES:BX) - ADDRESS OF BUFFER FOR READS AND WRITES, :
; (NOT REQUIRED FOR VERIFY) :
; :
; FORMAT (AH=5) ES:BX POINTS TO A 512 BYTE BUFFER. THE FIRST :
; 2*(SECTORS/TRACK) BYTES CONTAIN F,N FOR EACH SECTOR. :
; F = 00H FOR A GOOD SECTOR :
; 80H FOR A BAD SECTOR :
; N = SECTOR NUMBER :
; FOR AN INTERLEAVE OF 2 AND 17 SECTORS/TRACK :
; THE TABLE SHOULD BE: :
; :
; DB 00H,01H,00H,0AH,00H,02H,00H,0BH,00H,03H,00H,0CH :
; DB 00H,04H,00H,0DH,00H,05H,00H,0EH,00H,06H,00H,0FH :
; DB 00H,07H,00H,10H,00H,08H,00H,11H,00H,09H :
; :
;----------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------
; OUTPUT :
; AH = STATUS OF CURRENT OPERATION :
; STATUS BITS ARE DEFINED IN THE EQUATES BELOW :
; CY = 0 SUCCESSFUL OPERATION (AH=0 ON RETURN) :
; CY = 1 FAILED OPERATION (AH HAS ERROR REASON) :
; :
; NOTE: ERROR 11H INDICATES THAT THE DATA READ HAD A RECOVERABLE :
; ERROR WHICH WAS CORRECTED BY THE ECC ALGORITHM. THE DATA :
; IS PROBABLY GOOD, HOWEVER THE BIOS ROUTINE INDICATES AN :
; ERROR TO ALLOW THE CONTROLLING PROGRAM A CHANCE TO DECIDE :
; FOR ITSELF. THE ERROR MAY NOT RECUR IF THE DATA IS :
; REWRITTEN. :
; :
; IF DRIVE PARAMETERS WERE REQUESTED (DL >= 80H), :
; INPUT: :
; (DL) = DRIVE NUMBER :
; OUTPUT: :
; (DL) = NUMBER OF CONSECUTIVE ACKNOWLEDGING DRIVES ATTACHED (1-2) :
; (CONTROLLER CARD ZERO TALLY ONLY) :
; (DH) = MAXIMUM USEABLE VALUE FOR HEAD NUMBER :
; (CH) = MAXIMUM USEABLE VALUE FOR CYLINDER NUMBER :
; (CL) = MAXIMUM USEABLE VALUE FOR SECTOR NUMBER :
; AND CYLINDER NUMBER HIGH BITS :
; :
; IF READ DASD TYPE WAS REQUESTED, :
; :
; AH = 0 - NOT PRESENT :
; 1 - DISKETTE - NO CHANGE LINE AVAILABLE :
; 2 - DISKETTE - CHANGE LINE AVAILABLE :
; 3 - FIXED DISK :
; :
; CX,DX = NUMBER OF 512 BYTE BLOCKS WHEN AH = 3 :
; :
; REGISTERS WILL BE PRESERVED EXCEPT WHEN THEY ARE USED TO RETURN :
; INFORMATION. :
; :
; NOTE: IF AN ERROR IS REPORTED BY THE DISK CODE, THE APPROPRIATE :
; ACTION IS TO RESET THE DISK, THEN RETRY THE OPERATION. :
; :
;----------------------------------------------------------------------------------------
SENSE_FAIL EQU 0FFH ; NOT IMPLEMENTED
NO_ERR EQU 0E0H ; STATUS ERROR/ERROR REGISTER=0
WRTTE_FAULT EQU 0CCH ; WRITE FAULT ON SELECTED DRIVE
UNDEF_ERR EQU 0BBH ; UNDEFINED ERROR OCCURRED
NOT_RDY EQU 0AAH ; DRIVE NOT READY
TIME_OUT EQU 80H ; ATTACHMENT FAILED TO RESPOND
BAD_SEEK EQU 40H ; SEEK OPERATION FAILED
BAD_CNTLR EQU 20H ; CONTROLLER HAS FAILED
DATA_CORRECTED EQU 11H ; ECC CORRECTED DATA ERROR
BAD_ECC EQU 10H ; BAD ECC ON DISK READ
BAD_TRACK EQU 0BH ; NOT IMPLEMENTED
BAD_SECTOR EQU 0AH ; BAD SECTOR FLAG DETECTED
DMA_BOUNDARY EQU 09H ; DATA EXTENDS TOO FAR
INIT_FAIL EQU 07H ; DRIVE PARAMETER ACTIVITY FAILED
BAD_RESET EQU 05H ; RESET FAILED
RECORD_NOT_FND EQU 04H ; REQUESTED SECTOR NOT FOUND
BAD_ADDR_MARK EQU 02H ; ADDRESS MARK NOT FOUND
BAD_CMD EQU 01H ; BAD COMMAND PASSED TO DISK I/O
;------------------------------------------------------------
; :
; FIXED DISK PARAMETER TABLE :
; - THE TABLE IS COMPOSED OF A BLOCK DEFINED AS: :
; :
; +0 (1 WORD) - MAXIMUM NUMBER OF CYLINDERS :
; +2 (1 BYTE) - MAXIMUM NUMBER OF HEADS :
; +3 (1 WORD) - NOT USED/SEE PC-XT :
; +5 (1 WORD) - STARTING WRITE PRECOMPENSATION CYL :
; +7 (1 BYTE) - MAXIMUM ECC DATA BURST LENGTH :
; +8 (1 BYTE) - CONTROL BYTE :
; BIT 7 DISABLE RETRIES -OR- :
; BIT 6 DISABLE RETRIES :
; BIT 3 MORE THAN 8 HEADS :
; +9 (3 BYTES)- NOT USED/SEE PC-XT :
; +12 (1 WORD) - LANDING ZONE :
; +14 (1 BYTE) - NUMBER OF SECTORS/TRACK :
; +15 (1 BYTE) - RESERVED FOR FUTURE USE :
; :
; - TO DYNAMICALLY DEFINE A SET OF PARAMETERS :
; BUILD A TABLE FOR UP TO 15 TYPES AND PLACE :
; THE CORRESPONDING VECTOR INTO INTERRUPT 41 :
; FOR DRIVE 0 AND INTERRUPT 46 FOR DRIVE 1. :
; :
;------------------------------------------------------------
;------------------------------------------------------------
; :
; HARDWARE SPECIFIC VALUES :
; :
; - CONTROLLER I/O PORT :
; > WHEN READ FROM: :
; HF_PORT+0 - READ DATA (FROM CONTROLLER TO CPU) :
; HF_PORT+1 - GET ERROR REGISTER :
; HF_PORT+2 - GET SECTOR COUNT :
; HF_PORT+3 - GET SECTOR NUMBER :
; HF_PORT+4 - GET CYLINDER LOW :
; HF_PORT+5 - GET CYLINDER HIGH (2 BITS) :
; HF_PORT+6 - GET SIZE/DRIVE/HEAD :
; HF_PORT+7 - GET STATUS REGISTER :
; :
; > WHEN WRITTEN TO: :
; HF_PORT+0 - WRITE DATA (FROM CPU TO CONTROLLER) :
; HF_PORT+1 - SET PRECOMPENSATION CYLINDER :
; HF_PORT+2 - SET SECTOR COUNT :
; HF_PORT+3 - SET SECTOR NUMBER :
; HF_PORT+4 - SET CYLINDER LOW :
; HF_PORT+5 - SET CYLINDER HIGH (2 BITS) :
; HF_PORT+6 - SET SIZE/DRIVE/HEAD :
; HF_PORT+7 - SET COMMAND REGISTER :
; :
;------------------------------------------------------------
HF_PORT EQU 01F0H ; DISK PORT
HF_REG_PORT EQU 03F6H
;----- STATUS REGISTER
ST_ERROR EQU 00000001B ;
ST_INDEX EQU 00000010B ;
ST_CORRCTD EQU 00000100B ; ECC CORRECTION SUCCESSFUL
ST_DRQ EQU 00001000B ;
ST_SEEK_COMPL EQU 00010000B ; SEEK COMPLETE
ST_WRT_FLT EQU 00100000B ; WRITE FAULT
ST_READY EQU 01000000B ;
ST_BUSY EQU 10000000B ;
;----- ERROR REGISTER
ERR_DAM EQU 00000001B ; DATA ADDRESS MARK NOT FOUND
ERR_TRK_0 EQU 00000010B ; TRACK 0 NOT FOUND ON RECAL
ERR_ABORT EQU 00000100B ; ABORTED COMMAND
; EQU 00001000B ; NOT USED
ERR_ID EQU 00010000B ; ID NOT FOUND
; EQU 00100000B ; NOT USED
ERR_DATA_ECC EQU 01000000B
ERR_BAD_BLOCK EQU 10000000B
RECAL_CMD EQU 00010000B ; DRIVE RECAL (10H)
READ_CMD EQU 00100000B ; READ (20H)
WRITE_CMD EQU 00110000B ; WRITE (30H)
VERIFY_CMD EQU 01000000B ; VERIFY (40H)
FMTTRK_CMD EQU 01010000B ; FORMAT TRACK (50H)
INIT_CMD EQU 01100000B ; INITIALIZE (60H)
SEEK_CMD EQU 01110000B ; SEEK (70H)
DIAG_CMD EQU 10010000B ; DIAGNOSTIC (90H)
SET_PARM_CMD EQU 10010001B ; DRIVE PARMS (91H)
NO_RETRIES EQU 00000001B ; CHD MODIFIER (01H)
ECC_MODE EQU 00000010B ; CMD MODIFIER (02H)
BUFFER_MODE EQU 00001000B ; CMD MODIFIER (08H)
MAX_FILE EQU 2
S_MAX_FILE EQU 2
DELAY_1 EQU 25H ; DELAY FOR OPERATION COMPLETE
DELAY_2 EQU 0600H ; DELAY FOR READY
DELAY_3 EQU 0100H ; DELAY FOR DATA REQUEST
HF-FAIL EQU 08H ; CMOS FLAG IN BYTE 0EH
;----- COMMAND BLOCK REFERENCE
@CMD_BLOCK EQU BYTE PTR [BP]-8 ; @CMD_BLOCK REFERENCES BLOCK HEAD IN SS
; (BP) POINTS TO COMMAND BLOCK TAIL
; AS DEFINED BY THE "ENTER" PARMS
;----------------------------------------------------------------
; FIXED DISK I/O SETUP :
; :
; - ESTABLISH TRANSFER VECTORS FOR THE FIXED DISK :
; - PERFORM POWER ON DIAGNOSTICS :
; SHOULD AN ERROR OCCUR A "1701" MESSAGE IS DISPLAYED :
; :
;----------------------------------------------------------------
ASSUME CS:CODE,DS:ABS0 ; WORK OFF DS REGISTER
DISK_SETUP PROC NEAR
CLI
MOV AX,ABS0 ; GET ABSOLUTE SEGMENT
MOV DS,AX ; SET SEGMENT REGISTER
MOV AX,WORD PTR @ORG_VECTOR ; GET DISKETTE VECTOR
MOV WORD PTR @DISK_VECTOR,AX ; INTO INT 40H
MOV AX,WORD PTR @ORG_VECTOR+2
MOV WORD PTR @DISK_VECTOR+2,AX
MOV WORD PTR @ORG_VECTOR,OFFSET DISK_IO ; FIXED DISK HANDLER
MOV WORD PTR @ORG_VECTOR+2,CS
MOV WORD PTR @HDISK_INT,OFFSET HD_INT ; FIXED DISK INTERRUPT
MOV WORD PTR @HDISK_INT_2,CS
MOV WORD PTR @HF_TBL_VEC,OFFSET FD_TBL ; PARM TABLE DRIVE 80
MOV WORD PTR @HF_TBL_VEC+2,CS
MOV WORD PTR @HF1_TBL_VEC,OFFSET FD_TBL ; PARM TABLE DRIVE 81
MOV WORD PTR @HF1_TBL_VEC+2,CS
IN AL,INTB01 ; TURN ON SECOND INTERRUPT CHIP
AND AL,0BFH
JMP $+2
OUT INTB01,AL
IN AL,INTA01 ; LET INTERRUPTS PASS THRU TO
AND AL,0FBH ; SECOND CHIP
JMP $+2
OUT INTA01,AL
STI
ASSUME DS:DATA,ES:ABS0
PUSH DS ; MOVE ABS0 POINTER TO
POP ES ; EXTRA SEGMENT POINTER
CALL DDS ; ESTABLISH DATA SEGMENT
MOV @DISK_STATUS1,0 ; RESET THE STATUS INDICATOR
MOV @HF_NUM,0 ; ZERO NUMBER OF FIXED DISKS
MOV @CONTROL_BYTE,0
MOV AL,CMOS_DIAG+NMI
CALL CMOS_READ ; CHECK CMOS VALIDITY
MOV AH,AL ; SAVE CMOS FLAG
AND AL,BAD_BAT+BAD_CKSUM ; CHECK FOR VALID CMOS
JZ L1
JMP POD_DONE ; CMOS NOT VALID -- NO FIXED DISK
L1:
AND AH,NOT HF_FAIL ; ALLOW FIXED DISK IPL
MOV AL,CMOS_DIAG+NMI ; WRITE IT BACK
CALL CMOS_WRITE
MOV AL,CMOS_DISK+NMI
CALL CMOS_READ
MOV @PORT_OFF,0 ; ZERO CARD OFFSET
MOV BL,AL ; SAVE FIXED DISK BYTE
AND AX,00F0H ; GET FIRST DRIVE TYPE AS OFFSET
JZ POD_DONE ; NO FIXED DISKS
CMP AL,0F0H ; CHECK FOR EXTENDED DRIVE TYPE BYTE USE
JNE L2 ; USE DRIVE TYPE 1 --> 14 IF NOT IN USE
MOV AL,CMOS_DISK_1+NMI ; GET EXTENDED TYPE POP DRIVE C:
CALL CMOS_READ ; FROM CMOS
CMP AL,0 ; IS TYPE SET TO ZERO
JE POD_DONE ; EXIT IF NOT VALID AND NO FIXED DISKS
CMP AL,47 ; IS TYPE WITHIN VALID RANGE
JA POD_DONE ; EXIT WITH NO FIXED DISKS IF NOT VALID
SHL AX,4 ; ADJUST TYPE TO HIGH NIBBLE
L2:
ADD AX,OFFSET FD_TBL-16D ; COMPUTE OFFSET OF FIRST DRIVE TABLE
MOV WORD PTR @HF_TBL_VEC,AX ; SAVE IN VECTOR POINTER
MOV @HF_NUM,1 ; AT LEAST ONE DRIVE
MOV AL,BL
SHL AL,4 ; GET SECOND DRIVE TYPE
JZ SHORT L4 ; ONLY ONE DRIVE
MOV AH,0
CMP AL,0F0H ; CHECK FOR EXTENDED DRIVE TYPE BYTE USE
JNE L3 ; USE DRIVE TYPE 1 --> 14 IF NOT IN USE
MOV AL,CMOS_DISK_2+NMI ; GET EXTENDED TYPE FOR DRIVE C:
CALL CMOS_READ ; FROM CMOS
CMP AL,0 ; IS TYPE SET TO ZERO
JE L4 ; SKIP IF SECOND FIXED DISK NOT VALID
CMP AL,47 ; IS TYPE WITHIN VALID RANGE
JA L4 ; SKIP IF NOT VALID
SHL AX,4 ; ADJUST TYPE TO HIGH NIBBLE
L3:
ADD AX,OFFSET FD_TBL-16D ; COMPUTE OFFSET FOR SECOND FIXED DISK
MOV bX,AX
CMP WORD PTR CS:[BX],0 ; CHECK FOR ZERO CYLINDERS IN TABLE
JE L4 ; SKIP DRIVE IF NOT A VALID TABLE ENTRY
MOV WORD PTR @HF1_TBL_VEC,AX
MOV @HF_NUM,2 ; TWO DRIVES
L4: MOV DL,80H ; CHECK THE CONTROLLER
MOV AH,14H ; USE CONTROLLER DIAGNOSTIC COMMAND
INT 13H ; CALL BIOS WITH DIAGNOSTIC COMMAND
JC CTL_ERRX ; DISPLAY ERROR MESSAGE IF BAD RETURN
MOV AX,@TIMER_LOW ; GET START TIMER COUNTS
MOV BX,AX
ADD AX,6*182 ; 60 SECONDS* 18.2
MOV CX,AX
CALL HD_RESET_1 ; SET UP DRIVE 0
CMP @HF_NUM,1 ; WERE THERE TWO DRIVES?
JBE POD_DONE ; NO-ALL DONE
MOV DL,81H ; SET UP DRIVE 1
CALL HD_RESET_1
POD_DONE:
RET
;----- POD_ERROR
CTL_ERRX:
MOV SI,OFFSET F1782 ; CONTROLLER ERROR
CALL SET_FAIL ; DO NOT IPL FROM DISK
CALL E_MSG ; DISPLAY ERROR AND SET (BP) ERROR FLAG
JMP POD_DONE
HD_RESET_1 PROC NEAR
PUSH BX ; SAVE TIMER LIMITS
PUSH CX
RES_1: MOV AH,09H ; SET DRIVE PARAMETERS
INT 13H
JC RES_2
MOV AH,11H ; RECALIBRATE DRIVE
INT 13H
JNC RES_CK ; DRIVE OK
RES_2: CALL POD_TCHK ; CHECK TIME OUT
JNC RES_1
RES_FL: MOV SI,OFFSET F1781 ; INDICATE DISK 1 FAILURE
TEST DL,1
JNZ RES_E1
MOV SI,OFFSET F1780 ; INDICATE DISK 0 FAILURE
CALL SET_FAIL ; DO NOT TRY TO IPL DISK 0
JMP SHORT RES_E1
RES_RS: MOV AH,00H ; RESET THE DRIVE
INT 13H
RES_CK: MOV AH,08H ; GET MAX CYLINDER,HEAD,SECTOR
MOV BL,DL ; SAVE DRIVE CODE
INT 13H
JC RES_ER
MOV WORD PTR @NEC_STATUS,CX ; SAVE MAX CYLINDER, SECTOR
MOV DL,BL ; RESTORE DRIVE CODE
RES_3: MOV AX,0401H ; VERIFY THE LAST SECTOR
INT 13H
JNC RES_OK ; VERIFY OK
CMP AH,BAD_SECTOR ; OK ALSO IF JUST ID READ
JE RES_OK
CMP AH,DATA_CORRECTED
JE RES_OK
CMP AH,BAD_ECC
JE RES_OK
CALL POD_TCHK ; CHECK FOR TIME OUT
JC RES_ER ; FAILED
MOV CX,WORD PTR @NEC_STATUS ; GET SECTOR ADDRESS, AND CYLINDER
MOV AL,CL ; SEPARATE OUT SECTOR NUMBER
AND AL,3FH
DEC AL ; TRY PREVIOUS ONE
JZ RES_RS ; WE'VE TRIED ALL SECTORS ON TRACK
AND CL,0C0H ; KEEP CYLINDER BITS
OR CL,AL ; MERGE SECTOR WITH CYLINDER BITS
MOV WORD PTR @NEC_STATUS,CX ; SAVE CYLINDER, NEW SECTOR NUMBER
JMP RES_3 ; TRY AGAIN
RES_ER: MOV SI,OFFSET F1791 ; INDICATE DISK 1 ERROR
TEST DL,1
JNZ RES_E1
MOV SI,OFFSET F1790 ; INDICATE DISK 0 ERROR
RES_E1:
CALL E_MSG ; DISPLAY ERROR AND SET (BP) ERROR FLAG
RES_OK: POP CX ; RESTORE TIMER LIMITS
POP AX
RET
HD_RESET_1 ENDP
SET_FAIL PROC NEAR
MOV AX,X*(CMOS_DIAG+NMI) ; GET CMOS ERROR BYTE
CALL CMOS_READ
OR AL,HF_FAIL ; SET DO NOT IPL FROM DISK FLAG
XCHG AH,AL ; SAVE IT
CALL CMOS_WRITE ; PUT IT OUT
RET
SET-FAIL ENDP
POD_TCHK PROC NEAR ; CHECK FOR 30 SECOND TIME OUT
POP AX ; SAVE RETURN
POP CX ; GET TIME OUT LIMITS
POP BX
PUSH BX ; AND SAVE THEM AGAIN
PUSH CX
PUSH AX ; RESTORE RETURN
MOV AX,@TIMER_LOW ; AX = CURRENT TIME
; BX = START TIME
; CX = END TIME
CMP BX,CX
JB TCHK1 ; START < END
CMP BX,AX
JB TCHKG ; END < START < CURRENT
JMP SHDRT TCHK2 ; END, CURRENT < START
TCHK1: CMP AX,BX
JB TCHKNG ; CURRENT < START < END
TCHK2: CMP AX CX
JB TCHKG ; START < CURRENT < END
; OR CURRENT < END < START
TCHKNG: STC ; CARRY SET INDICATES TIME OUT
RET
TCHKG: CLC ; INDICATE STILL TIME
RET
POD_TCHK ENDP
DISK_SETUP ENDP
;------------------------------------------
; FIXED DISK BIOS ENTRY POINT :
;------------------------------------------
OISK_IO PROC FAR
ASSUME DS:DATA,ES:NOTHING
CMP DL,80H ; TEST FOR FIXED DISK DRIVE
JAE A1 ; YES, HANDLE HERE
INT 40H ; DISKETTE HANDLER
RET_2:
RET 2 ; BACK TO CALLER
A1:
STI ; ENABLE INTERRUPTS
OR AH,AH
JNZ A2
INT 40H ; RESET NEC WHEN AH=0
SUB AH,AH
CMP DL,(80H + S_MAX_FILE - 1)
JA RET_2
A2:
CMP AH,08H ; GET PARAMETERS IS A SPECIAL CASE
JNZ A3
JMP GET_PARM_N
A3: CMP AH,15H ; READ DASD TYPE IS ALSO
JNZ A4
JMP READ_DASD_TYPE
A4: ; SAVE REGISTERS DURING OPERATION
ENTER 8,0 ; SAVE (BP) AND MAKE ROOM FOR @CMD_BLOCK
PUSH BX ; IN THE STACK, THE COMMAND BLOCK IS:
PUSH CX ; @CMD_BLOCK == BYTE PTR [BP]-8
PUSH DX
PUSH DS
PUSH ES
PUSH SI
PUSH DI
OR AH,AH ; CHECK FOR RESET
JNZ A5
MOV DL,80H ; FORCE DRIVE 80 FOR RESET
A5: CALL DISK_IO_CONT ; PERFORM THE OPERATION
CALL DDS ; ESTABLISH SEGMENT
MAY AH,@DISK_STATUS1 ; GET STATUS FROM OPERATION
CMP AH,1 ; SET THE CARRY FLAG TO INDICATE
CMC ; SUCCESS OR FAILURE
POP DI ; RESTORE REGISTERS
POP SI
POP ES
POP DS
POP DX
POP CX
POP BX
LEAVE ; ADJUST (SP) AND RESTORE (BP)
RET 2 ; THROW AWAY SAVED FLAGS
DISK_IO ENDP
M1 LABEL WORD ; FUNCTION TRANSFER TABLE
DW DISK_RESET ; 000H
DW RETURN_STATUS ; 001H
DW DISK_READ ; 002H
DW DISK_WRITE ; 003H
DW DISK_VERF ; 004H
DW FMT_TRK ; 005H
DW BAD_COMMAND ; 006H FORMAT BAD SECTORS
DW BAD_COMMAND ; 007H FORMAT DRIVE
DW BAD_COMMAND ; 008H RETURN PARAMETERS
DW INIT_DRV ; 009H
DW RD_LONG ; 00AH
DW WR_LONG ; 00BH
DW DISK_SEEK ; 00CH
DW DISK_RESET ; 00DH
DW BAD_COMMAND ; 00EH READ BUFFER
DW BAD_COMMAND ; 00FH WRITE BUFFER
DW TST_RDY ; 010H
DW HDISK_RECAL ; 011H
DW BAD_COMMAND ; 012H MEMORY DIAGNOSTIC
DW BAD_COMMAND ; 013H DRIVE DIAGNOSTIC
DW CTLR_DIAGNOSTIC ; 014H CONTROLLER DIAGNOSTIC
M1L EQU $-M1
DISK_IO_CONT PROC NEAR
CALL DDS ; ESTABLISH SEGMENT
CMP AH,01H ; RETURN STATUS
JNZ SU0
JMP RETURN_STATUS
SU0:
MOV @DISK_STATUS1,0 ; RESET THE STATUS INDICATOR
PUSH BX ; SAVE DATA ADDRESS
MOV BL,@HF_NUM ; GET NUMBER OF DRIVES
PUSH AX
AND DL,7FH ; GET DRIVE AS 0 OR 1
CMP BL,DL
JBE BAD_COMMAND_POP ; INVALID DRIVE
PUSH ES
CALL GET_VEC ; GET DISK PARAMETERS
MOV AX,WORD PTR ES:[BX][5] ; GET WRITE PRE-COMPENSATION CYLINDER
SHR AX,2
MOV @CMD_BLOCK,AL
MOV AL,BYTE PTR ES:[BX][8] ; GET CONTROL BYTE MODIFIER
PUSH DX
MOV DX,HF_REG_PORT
OUT DX,AL ; SET EXTRA HEAD OPTION
POP DX
POP ES
MOV AH,@CONTROL_BYTE ; SET EXTRA HEAD OPTION IN
AND AH,0C0H ; CONTROL BYTE
OR AH,AL
MOV @CONTROL_BYTE,AH
POP AX
MOV @CMD_BLOCK+1,AL ; SECTOR COUNT
PUSH AX
MOV AL,CL ; GET SECTOR NUMBER
AND AL,3FH
MOV @CMD_BLOCK+2,AL
MOV @CMD_BLOCK+3,CH ; GET CYLINDER NUMBER
MOV AL,CL
MOV @CMD_BLOCK+4,AL ; CYLINDER HIGH ORDER 2 BITS
MOV AL,DL ; DRIVE NUMBER
SHL AL,4
AND DH,0FH ; HEAD NUMBER
OR AL,DH
OR AL,80H OR 20H ; ECC AND 512 BYTE SECTORS
MOV @CMD_BLOCK+5,AL ; ECC/SIZE/DRIVE/HEAD
POP AX
PUSH AX
MOV AL,AH ; GET INTO LOW BYTE
XOR AH,AH ; ZERO HIGH BYTE
SAL AX,1 ; *2 FOR TABLE LOOKUP
MOV SI,AX ; PUT INTO SI FOR BRANCH
CMP AX,M1L ; TEST WITHIN RANGE
JNB BAD_COMMAND_POP
POP AX ; RESTORE AX
POP BX ; AND DATA ADDRESS
PUSH CX
PUSH AX ; ADJUST ES:BX
MOV CX,BX ; GET 3 HIGH ORDER NIBBLES OF BX
SHR CX,4
MOV AX,ES
ADD AX,CX
MOV ES,AX
AND BX,000FH ; ES:BX CHANGED TO ES:000X
POP AX
POP CX
JMP WORD PTR CS:[SI + OFFSET M1]
BAD_COMMAND_POP:
POP AX
POP BX
BAD_COMMAND:
MOV @DISK_STATUS1,BAD_CMD ; COMMAND ERROR
MOV AL,0
RET
DISK_IO_CONT ENDP
;------------------------------------------
; RESET THE DISK SYSTEM (AH=00H) :
;------------------------------------------
DISK_RESET PROC NEAR
CLI
IN AL,INTB01 ; GET THE MASK REGISTER
JMP $+2
AND AL,0BFH ; ENABLE FIXED DISK INTERRUPT
OUT INTB01,AL
STI ; START INTERRUPTS
MOV AL,04H
MOV DX,HF_REG_PORT
OUT DX,AL ; RESET
MOV CX,10 ; DELAY COUNT
DRD: DEC CX
JNZ DRD ; WAIT 4.8 MICRO-SEC
MOV AL,@CONTROL_BYTE
AND AL,0FH ; SET HEAD OPTION
OUT DX,AL ; TURN RESET OFF
CALL NOT_BUSY
JNZ DRERR ; TIME OUT ON RESET
MOV DX,HF_PORT+1
IN AL,DX ; GET RESET STATUS
CMP AL,1
JNZ DRERR ; BAD RESET STATUS
AND @CMD_BLOCK+5,0EFH ; SET TO DRIVE 0
SUB DL,DL
CALL INIT_DRV ; SET MAX HEADS
CALL HDISK_RECAL ; RECAL TO RESET SEEK SPEED
CMP @HF_NUM,1 ; CHECK FOR DRIVE
JBE DRE
OR @CMD_BLOCK+5,010H ; SET TO DRIVE
MOV DL,1
CALL INIT_DRV ; SET MAX HEADS
CALL HDISK_RECAL ; RECAL TO RESET SEEK SPEED
DRE: MOV @DISK_STATUS1,0 ; IGNORE ANY SET UP ERRORS
RET
DRERR: MOV @DISK_STATUS1,BAD_RESET ; CARD FAILED
RET
DISK_RESET ENDP
;------------------------------------------
; DISK STATUS ROUTINE (AH = 01H) :
;------------------------------------------
RETURN_STATUS PROc NEAR
MOV AL,@DISK_STATUS1 ; OBTAIN PREVIOUS STATUS
MOV @DISK_STATUS1,0 ; RESET STATUS
RET
RETURN_STATUS ENDP
;------------------------------------------
; DISK READ ROUTINE (AH = 02H) :
;------------------------------------------
DISK_READ PROC NEAR
MOV @CMD_BLOCK+6, READ_CMD
JMP COMMANDI
DISK_READ ENDP
;------------------------------------------
; DISK WRITE ROUTINE (AH = 03H) :
;------------------------------------------
DISK_WRITE PROC NEAR
MOV @CMD_BLOCK+6,WRITE_CMD
JMP COMMANDO
DISK_WRITE ENDP
:------------------------------------------
; DISK VERIFY (AH = 04H) :
;------------------------------------------
DISK_VERF PROC NEAR
MOV @CMD_BLOCK+6,VERIFY_CMD
CALL COMMAND
JNZ VERF_EXIT ; CONTROLLER STILL BUSY
CALL WAIT
JNZ VERF EXIT ; TIME OUT
CALL CHECK_STATUS
VERF_EXIT:
RET
DISK_VERF ENDP
;------------------------------------------
; FORMATTING (AH = 05H) :
;------------------------------------------
FMT_TRK PROC NEAR ; FORMAT TRACK (AH = 005H)
MOV @CMD_BLOCK+6,FMTTRK_CMD
PUSH ES
PUSH BX
CALL GET_VEC ; GET DISK PARAMETERS ADDRESS
MOV AL,ES:[BX][14] ; GET SECTORS/TRACK
MOV @CMD_BLOCK+1,AL ; SET SECTOR COUNT IN COMMAND
POP BX
POP ES
FMT_TRK JMP CMD_OF ; GO EXECUTE THE COMMAND
ENDP
;------------------------------------------
; READ DASD TYPE (AH = 15H) :
;------------------------------------------
READ_DASD_TYPE LABEL NEAR
READ_D_T PROC FAR ; GET DRIVE PARAMETERS
PUSH DS ; SAVE REGISTERS
PUSH ES
PUSH BX
ASSUME DS:DATA
CALL DDS ; ESTABLISH ADDRESSING
MOV @DISK_STATUS1,0
MOV BL,@HF_NUM ; GET NUMBER OF DRIVES
AND DL,7FH ; GET DRIVE NUMBER
CMP BL,DL
JBE RDT_NOT_PRESENT ; RETURN DRIVE NOT PRESENT
CALL GET_VEC ; GET DISK PARAMETER ADDRESS
MOV AL,ES:[BX][2] ; HEADS
MOV CL,ES:[BX][14]
IMUL CL ; NUMBER OF SECTORS
MOV CX,ES:[BX] ; MAX NUMBER OF CYLINDERS
DEC CX ; LEAVE ONE FOR DIAGNOSTICS
IMUL CX ; NUMBER OF SECTORS
MOV CX,DX ; HIGH ORDER HALF
MOV DX,AX ; LOW ORDER HALF
SUB AX,AX
MOV AH,03H ; INDICATE FIXED DISK
RDT2: POP BX ; RESTORE REGISTERS
POP ES
POP DS
CLC ; CLEAR CARRY
RET 2
RDT_NOT_PRESENT:
SUB AX,AX ; DRIVE NOT PRESENT RETURN
MOV CX,AX ; ZERO BLOCK COUNT
MOV DX,AX
JMP RDT2
READ_D_T ENDP
;------------------------------------------
; GET PARAMETERS (AH = 08H) :
;------------------------------------------
GET_PARM_N LABEL NEAR
GET_PARM PROC FAR ; GET DRIVE PARAMETERS
PUSH DS ; SAVE REGISTERS
PUSH ES
PUSH BX
ASSUME DS:ABS0
MOV AX,ABS0 ; ESTABLISH ADDRESSING
MOV DS,AX
TEST DL,1 ; CHECK FOR DRIVE 1
JZ G0
LES BX,@HF1_TBL_VEC
JMP SHORT G1
G0: LES BX,@HF_TBL_VEC
ASSUME DS:DATA
G1:
CALL DDS ; ESTABLISH SEGMENT
SUB DL,80H
CMP DL,MAX_FILE ; TEST WITHIN RANGE
JAE G4
MOV @DISK_STATUS1,0
MOV AX,ES:[BX] ; MAX NUMBER OF CYLINDERS
SUB AX,2 ; ADJUST FOR 0-N
MOV CH,AL
AND AX,0300H ; HIGH TWO BITS OF CYLINDER
SHR AX,1
SHR AX,1
OR AL,ES:[BX][14] ; SECTORS
MOV CL,AL
MOV DH,ES:[BX][2] ; HEADS
DEC DH ; 0-N RANGE
MOV DL,@HF_NUM ; DRIVE COUNT
SUB AX,AX
G5:
POP BX ; RESTORE REGISTERS
POP ES
POP DS
RET 2
G4:
MOV @DISK_STATUS1,INIT_FAIL ; OPERATION FAILED
MOV AH,INIT_FAIL
SUB AL,AL
SUB DX,DX
SUB CX,CX
STC ; SET ERROR FLAG
JMP G5
GET_PARM ENDP
;------------------------------------------
; INITIALIZE DRIVE (AH = 09H) :
;------------------------------------------
INIT_DRV PROC NEAR
MOV @CMD_BLOCK+6,SET_PARM_CMD
CALL GET_VEC ; ES:BX -> PARAMETER BLOCK
MOV AL,ES:[BX][2] ; GET NUMBER OF HEADS
DEC AL ; CONVERT TO 0-INDEX
MOV AH,@CMD_BLOCK+5 ; GET SDH REGISTER
AND AH,0F0H ; CHANCE HEAD NUMBER
OR AH,AL ; TO MAX HEAD
MOV @CMD_BLOCK+5,AH
MOV AL,ES:[BX][14] ; MAX SECTOR NUMBER
MOV @CMD_BLOCK+1,AL
SUB AX,AX
MOV @CMD_BLOCK+3,AL ; ZERO FLAGS
CALL COMMAND ; TELL CONTROLLER
JNZ INIT_EXIT ; CONTROLLER BUSY ERROR
CALL NOT_BUSY ; WAIT FOR IT TO BE DONE
JNZ INIT_EXIT ; TIME OUT
CALL CHECK_STATUS
INIT_EXIT:
RET
INIT_DRV ENDP
;------------------------------------------
; READ LONG (AH = 0AH) :
;------------------------------------------
RD_LONG PROC NEAR
MOV @CMD_BLOCK+6,READ_CMD OR ECC_MODE
JMP COMMANDI
RD_LONG ENDP
;------------------------------------------
; WRITE LONG (AH = 0BH) :
;------------------------------------------
WR_LONG PROC NEAR
MOV @CMD_BLOCK+6,WRITE_CMD OR ECC_MODE
JMP COMMANDO
WR_LONG ENDP
;------------------------------------------
; SEEK AH = 0CH) :
;------------------------------------------
DISK_SEEK PROC NEAR
MOV @CMD_BLOCK+6,SEEK_CMD
CALL COMMAND
JNZ DS_EXIT ; CONTROLLER BUSY ERROR
CALL WAIT
JNZ DS_EXIT ; TIME OUT ON SEEK
CALL CHECK_STATUS
CMP @DISK_STATUS1,BAD_SEEK
JNE DS_EXIT
MOV @DISK_STATUS1,0
DS_EXIT:
RET
DISK_SEEK ENDP
;------------------------------------------
; TEST DISK READY (AH = 10H) :
;------------------------------------------
TST_RDY PROC NEAR ; WAIT FOR CONTROLLER
CALL NOT_BUSY
JNZ TR_EX
MOV AL,CMD_BLOCK+5 ; SELECT DRIVE
MOV DX,HF_PORT+6
OUT DX,AL
CALL CHECK_ST ; CHECK STATUS ONLY
JNZ TR_EX
MOV @DISK_STATUS1,0 ; WIPE OUT DATA CORRECTED ERROR
TR_EX: RET
TST_RDY ENDP
;------------------------------------------
; RECALIBRATE (AH = 10H) :
;------------------------------------------
HDISK_RECAL PROC NEAR
MOV @CMD_BLOCK+6,RECAL_CMD
CALL COMMAND ; START THE OPERATION
JNZ RECAL_EXIT ; ERROR
CALL WAIT ; WAIT FOR COMPLETION
JZ RECAL_X ; TIME OUT ONE OK ?
CALL WAIT ; WAIT FOR COMPLETION LONGER
JNZ RECAL_EXIT ; TIME OUT TWO TIMES IS ERROR
RECAL_X:
CALL CHECK STATUS
CMP @DISK_STATUS1,BAD_SEEK ; SEEK NOT COMPLETE
JNE RECAL_EXIT ; IS OK
MOV @DISK_STATUS1,0
RECAL_EXIT:
CMP @DISK_STATUS1,0
RET
HDISK_RECAL ENDP
;------------------------------------------
; CONTROLLER DIAGNOSTIC (AH = 14H) :
;------------------------------------------
CTLR_DIAGNOSTIC PROC NEAR
CLI ; DISABLE INTERRUPTS WHILE CHANGING M
IN AL,INTB01 ; TURN ON SECOND INTERRUPT CHIP
AND AL,0BFH
JMP $+2
OUT INTB01,AL
IN AL,INTA01 ; LET INTERRUPTS PASS THRU TO
AND AL,0FBH ; SECOND CHIP
JMP $+2
OUT INTA01,AL
STI
CALL NOT_BUSY ; WAIT FOR CARD
JNZ CD_ERR ; BAD CARD
MOV DX,HF_PORT+7
MOV AL,DIAG_CMD ; START DIAGNOSE
OUT DX,AL
CALL NOT_BUSY ; WAIT FOR IT TO COMPLETE
MOV AH,TIME_OUT
JNZ CD_EXIT ; TIME OUT ON DIAGNOSTIC
MOV DX,HF_PORT+1 ; GET ERROR REGISTER
IN AL,DX
MOV @HF_ERROR,AL ; SAVE IT
MOV AH,0
CMP AL,1 ; CHECK FOR ALL OK
JE SHORT CD_EXIT
CD_ERR: MOV AH,BAD_CNTLR
CD_EXIT:
MOV @DISK_STATUS1,AH
RET
CTLR_DIAGNOSTIC ENDP
;------------------------------------------
; COMMANDI :
; REPEATEDLY INPUTS DATA TILL :
; NSECTOR RETURNS ZERO :
;------------------------------------------
COMMANDI:
CALL CHECK_DMA ; CHECK 64K BOUNDARY ERROR
JC CMD_ABORT
MOV DI,BX
CALL COMMAND ; OUTPUT COMMAND
JNZ CMD_ABORT
CMD_I1:
CALL WAIT ; WAIT FOR DATA REQUEST INTERRUPT
JNZ TM_OUT ; TIME OUT
MOV CX,256D ; SECTOR SIZE IN WORDS
MOV DX,HF_PORT
CLI
CLD
REP INSW ; GET THE SECTOR
STI
TEST @CMD_BLOCK+6,ECC_MODE ; CHECK FOR NORMAL INPUT
JZ CMD_I3
CALL WAIT_DRQ ; WAIT FOR DATA REQUEST
JC TM_OUT
MOV DX,HF_PORT
MOV CX,4 ; GET ECC BYTES
CMD_I2: IN AL,DX
MOV ES:BYTE PTR [DI],AL ; GO SLOW FOR BOARD
INC DI
LOOP CMD_I2
CMD_I3: CALL CHECK_STATUS
JNZ CMD_ABORT ; ERROR RETURNED
DEC @CMD_BLOCK+1 ; CHECK FOR MORE
JNZ SHORT CMD_I1
CMD_ABORT:
TM_OUT: RET