Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
   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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
/*
 * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
 *
 * Copyright (c) 1994-1999 Justin Gibbs.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification, immediately at the beginning of the file.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * Where this Software is combined with software released under the terms of 
 * the GNU General Public License (GPL) and the terms of the GPL would require the 
 * combined work to also be released under the terms of the GPL, the terms
 * and conditions of this License will apply in addition to those of the
 * GPL with the exception of any terms or conditions of this License that
 * conflict with, or are expressly prohibited by, the GPL.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	$Id: aic7xxx.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $
 */

#include "aic7xxx.reg"
#include "scsi_message.h"

/*
 * A few words on the waiting SCB list:
 * After starting the selection hardware, we check for reconnecting targets
 * as well as for our selection to complete just in case the reselection wins
 * bus arbitration.  The problem with this is that we must keep track of the
 * SCB that we've already pulled from the QINFIFO and started the selection
 * on just in case the reselection wins so that we can retry the selection at
 * a later time.  This problem cannot be resolved by holding a single entry
 * in scratch ram since a reconnecting target can request sense and this will
 * create yet another SCB waiting for selection.  The solution used here is to 
 * use byte 27 of the SCB as a pseudo-next pointer and to thread a list
 * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes, 
 * SCB_LIST_NULL is 0xff which is out of range.  An entry is also added to
 * this list everytime a request sense occurs or after completing a non-tagged
 * command for which a second SCB has been queued.  The sequencer will
 * automatically consume the entries.
 */

reset:
	clr	SCSISIGO;		/* De-assert BSY */
	and	SXFRCTL1, ~BITBUCKET;
	/* Always allow reselection */
	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;

	if ((p->features & AHC_CMD_CHAN) != 0) {
		/* Ensure that no DMA operations are in progress */
		clr	CCSGCTL;
		clr	CCSCBCTL;
	}

	call	clear_target_state;
poll_for_work:
	and	SXFRCTL0, ~SPIOEN;
	if ((p->features & AHC_QUEUE_REGS) == 0) {
		mov	A, QINPOS;
	}
poll_for_work_loop:
	if ((p->features & AHC_QUEUE_REGS) == 0) {
		and	SEQCTL, ~PAUSEDIS;
	}
	test	SSTAT0, SELDO|SELDI	jnz selection;
	test	SCSISEQ, ENSELO	jnz poll_for_work;
	if ((p->features & AHC_TWIN) != 0) {
		/*
		 * Twin channel devices cannot handle things like SELTO
		 * interrupts on the "background" channel.  So, if we
		 * are selecting, keep polling the current channel util
		 * either a selection or reselection occurs.
		 */
		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
		test	SSTAT0, SELDO|SELDI	jnz selection;
		test	SCSISEQ, ENSELO	jnz poll_for_work;
		xor	SBLKCTL,SELBUSB;	/* Toggle back */
	}
	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
test_queue:
	/* Has the driver posted any work for us? */
	if ((p->features & AHC_QUEUE_REGS) != 0) {
		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
		mov	NONE, SNSCB_QOFF;
		inc	QINPOS;
	} else {
		or	SEQCTL, PAUSEDIS;
		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
		inc	QINPOS;
		and	SEQCTL, ~PAUSEDIS;
	}

/*
 * We have at least one queued SCB now and we don't have any 
 * SCBs in the list of SCBs awaiting selection.  If we have
 * any SCBs available for use, pull the tag from the QINFIFO
 * and get to work on it.
 */
	if ((p->flags & AHC_PAGESCBS) != 0) {
		mov	ALLZEROS	call	get_free_or_disc_scb;
	}

dequeue_scb:
	add	A, -1, QINPOS;
	mvi	QINFIFO_OFFSET call fetch_byte;

	if ((p->flags & AHC_PAGESCBS) == 0) {
		/* In the non-paging case, the SCBID == hardware SCB index */
		mov	SCBPTR, RETURN_2;
	}
dma_queued_scb:
/*
 * DMA the SCB from host ram into the current SCB location.
 */
	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
	mov	RETURN_2	 call dma_scb;

/*
 * Preset the residual fields in case we never go through a data phase.
 * This isn't done by the host so we can avoid a DMA to clear these
 * fields for the normal case of I/O that completes without underrun
 * or overrun conditions.
 */
	if ((p->features & AHC_CMD_CHAN) != 0) {
		bmov    SCB_RESID_DCNT, SCB_DATACNT, 3;
	} else {
		mov     SCB_RESID_DCNT[0],SCB_DATACNT[0];
		mov     SCB_RESID_DCNT[1],SCB_DATACNT[1];
		mov     SCB_RESID_DCNT[2],SCB_DATACNT[2];
	}
	mov     SCB_RESID_SGCNT, SCB_SGCOUNT;

start_scb:
	/*
	 * Place us on the waiting list in case our selection
	 * doesn't win during bus arbitration.
	 */
	mov	SCB_NEXT,WAITING_SCBH;
	mov	WAITING_SCBH, SCBPTR;
start_waiting:
	/*
	 * Pull the first entry off of the waiting SCB list.
	 */
	mov	SCBPTR, WAITING_SCBH;
	call	start_selection;
	jmp	poll_for_work;

start_selection:
	if ((p->features & AHC_TWIN) != 0) {
		and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
		and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
		or	SINDEX,A;
		mov	SBLKCTL,SINDEX;		/* select channel */
	}
initialize_scsiid:
	if ((p->features & AHC_ULTRA2) != 0) {
		and	A, TID, SCB_TCL;	/* Get target ID */
		and	SCSIID_ULTRA2, OID;	/* Clear old target */
		or	SCSIID_ULTRA2, A;
	} else {
		and	A, TID, SCB_TCL;	/* Get target ID */
		and	SCSIID, OID;		/* Clear old target */
		or	SCSIID, A;
	}
	mov	SCSIDATL, ALLZEROS;		/* clear out the latched */
						/* data register, this */
						/* fixes a bug on some */
						/* controllers where the */
						/* last byte written to */
						/* this register can leak */
						/* onto the data bus at */
						/* bad times, such as during */
						/* selection timeouts */
	mvi	SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;

/*
 * Initialize Ultra mode setting and clear the SCSI channel.
 * SINDEX should contain any additional bit's the client wants
 * set in SXFRCTL0.
 */
initialize_channel:
	or	SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
	if ((p->features & AHC_ULTRA) != 0) {
ultra:
		mvi	SINDEX, ULTRA_ENB+1;
		test	SAVED_TCL, 0x80		jnz ultra_2; /* Target ID > 7 */
		dec	SINDEX;
ultra_2:
		mov     FUNCTION1,SAVED_TCL;
		mov     A,FUNCTION1;
		test	SINDIR, A	jz ndx_dtr;
		or	SXFRCTL0, FAST20;
	} 
/*
 * Initialize SCSIRATE with the appropriate value for this target.
 * The SCSIRATE settings for each target are stored in an array
 * based at TARG_SCSIRATE.
 */
ndx_dtr:
	shr	A,4,SAVED_TCL;
	if ((p->features & AHC_TWIN) != 0) {
		test	SBLKCTL,SELBUSB	jz ndx_dtr_2;
		or	SAVED_TCL, SELBUSB; 
		or	A,0x08;			/* Channel B entries add 8 */
ndx_dtr_2:
	}

	if ((p->features & AHC_ULTRA2) != 0) {
		add	SINDEX, TARG_OFFSET, A;
		mov	SCSIOFFSET, SINDIR;
	}

	add	SINDEX,TARG_SCSIRATE,A;
	mov	SCSIRATE,SINDIR ret;


selection:
	test	SSTAT0,SELDO	jnz select_out;
/*
 * Reselection has been initiated by a target. Make a note that we've been
 * reselected, but haven't seen an IDENTIFY message from the target yet.
 */
initiator_reselect:
	mvi	CLRSINT0, CLRSELDI;
	/* XXX test for and handle ONE BIT condition */
	and	SAVED_TCL, SELID_MASK, SELID;
	mvi	CLRSINT1,CLRBUSFREE;
	or	SIMODE1, ENBUSFREE;		/*
						 * We aren't expecting a
						 * bus free, so interrupt
						 * the kernel driver if it
						 * happens.
						 */
	mvi	SPIOEN call	initialize_channel;
	mvi	MSG_OUT, MSG_NOOP;		/* No message to send */
	jmp	ITloop;

/*
 * After the selection, remove this SCB from the "waiting SCB"
 * list.  This is achieved by simply moving our "next" pointer into
 * WAITING_SCBH.  Our next pointer will be set to null the next time this
 * SCB is used, so don't bother with it now.
 */
select_out:
	/* Turn off the selection hardware */
	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;	/*
						 * ATN on parity errors
						 * for "in" phases
						 */
	mvi	CLRSINT0, CLRSELDO;
	mov	SCBPTR, WAITING_SCBH;
	mov	WAITING_SCBH,SCB_NEXT;
	mov	SAVED_TCL, SCB_TCL;
	mvi	CLRSINT1,CLRBUSFREE;
	or	SIMODE1, ENBUSFREE;		/*
						 * We aren't expecting a
						 * bus free, so interrupt
						 * the kernel driver if it
						 * happens.
						 */
	mvi	SPIOEN call	initialize_channel;
/*
 * As soon as we get a successful selection, the target should go
 * into the message out phase since we have ATN asserted.
 */
	mvi	MSG_OUT, MSG_IDENTIFYFLAG;
	or	SEQ_FLAGS, IDENTIFY_SEEN;

/*
 * Main loop for information transfer phases.  Wait for the target
 * to assert REQ before checking MSG, C/D and I/O for the bus phase.
 */
ITloop:
	call	phase_lock;

	mov	A, LASTPHASE;

	test	A, ~P_DATAIN	jz p_data;
	cmp	A,P_COMMAND	je p_command;
	cmp	A,P_MESGOUT	je p_mesgout;
	cmp	A,P_STATUS	je p_status;
	cmp	A,P_MESGIN	je p_mesgin;

	mvi	INTSTAT,BAD_PHASE;	/* unknown phase - signal driver */
	jmp	ITloop;			/* Try reading the bus again. */

await_busfree:
	and	SIMODE1, ~ENBUSFREE;
	call	clear_target_state;
	mov	NONE, SCSIDATL;		/* Ack the last byte */
	and	SXFRCTL0, ~SPIOEN;
	test	SSTAT1,REQINIT|BUSFREE	jz .;
	test	SSTAT1, BUSFREE jnz poll_for_work;
	mvi	INTSTAT, BAD_PHASE;
	
clear_target_state:
	/*
	 * We assume that the kernel driver may reset us
	 * at any time, even in the middle of a DMA, so
	 * clear DFCNTRL too.
	 */
	clr	DFCNTRL;

	/*
	 * We don't know the target we will connect to,
	 * so default to narrow transfers to avoid
	 * parity problems.
	 */
	if ((p->features & AHC_ULTRA2) != 0) {
		bmov    SCSIRATE, ALLZEROS, 2;
	} else {
		clr     SCSIRATE;
		and     SXFRCTL0, ~(FAST20);
	}
	mvi	LASTPHASE, P_BUSFREE;
	/* clear target specific flags */
	clr	SEQ_FLAGS ret;


data_phase_reinit:
/*
 * If we re-enter the data phase after going through another phase, the
 * STCNT may have been cleared, so restore it from the residual field.
 * On Ultra2, we have to put it into the HCNT field because we have to
 * drop the data down into the shadow layer via the preload ability.
 */
 	if ((p->features & AHC_ULTRA2) != 0) {
		bmov	HADDR, SHADDR, 4;
		bmov    HCNT, SCB_RESID_DCNT, 3;
	}
	if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
		bmov    STCNT, SCB_RESID_DCNT, 3;
	}
	if ((p->features & AHC_CMD_CHAN) == 0) {
		mvi	DINDEX, STCNT;
		mvi	SCB_RESID_DCNT	call bcopy_3;
	}
	jmp	data_phase_loop;
p_data:
 	if ((p->features & AHC_ULTRA2) != 0) {
		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
	} else {
		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
	}
	test	LASTPHASE, IOI jnz . + 2;
	or	DMAPARAMS, DIRECTION;
	call	assert;		/*
				 * Ensure entering a data
				 * phase is okay - seen identify, etc.
				 */
	if ((p->features & AHC_CMD_CHAN) != 0) {
		mvi	CCSGADDR, CCSGADDR_MAX;
	}

	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
	or	SEQ_FLAGS, DPHASE;	/* we've seen a data phase */
	/*
	 * Initialize the DMA address and counter from the SCB.
	 * Also set SG_COUNT and SG_NEXT in memory since we cannot
	 * modify the values in the SCB itself until we see a
	 * save data pointers message.
	 */
	if ((p->features & AHC_CMD_CHAN) != 0) {
		bmov	HADDR, SCB_DATAPTR, 7;
		bmov    SG_COUNT, SCB_SGCOUNT, 5;
		if ((p->features & AHC_ULTRA2) == 0) {
			bmov    STCNT, HCNT, 3;
		}
	} else {
		mvi	DINDEX, HADDR;
		mvi	SCB_DATAPTR	call bcopy_7;
		call	set_stcnt_from_hcnt;
		mvi	DINDEX, SG_COUNT;
		mvi	SCB_SGCOUNT	call bcopy_5;
	}
data_phase_loop:
	/* Guard against overruns */
	test	SG_COUNT, 0xff jnz data_phase_inbounds;
/*
 * Turn on 'Bit Bucket' mode, set the transfer count to
 * 16meg and let the target run until it changes phase.
 * When the transfer completes, notify the host that we
 * had an overrun.
 */
	or	SXFRCTL1,BITBUCKET;
	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
	if ((p->features & AHC_ULTRA2) != 0) {
		bmov	HCNT, ALLONES, 3;
	}
	if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
		bmov	STCNT, ALLONES, 3;
	}
	if ((p->features & AHC_CMD_CHAN) == 0) {
		mvi	STCNT[0], 0xFF;
		mvi	STCNT[1], 0xFF;
		mvi	STCNT[2], 0xFF;
	}

data_phase_inbounds:
/* If we are the last SG block, tell the hardware. */
	if ((p->features & AHC_ULTRA2) != 0) {
		shl	A, 2, SG_COUNT;
		cmp	SG_COUNT,0x01 jne data_phase_wideodd;
		or	A, LAST_SEG;
	} else {
		cmp	SG_COUNT,0x01 jne data_phase_wideodd;
		and	DMAPARAMS, ~WIDEODD;
	}
data_phase_wideodd:
	if ((p->features & AHC_ULTRA2) != 0) {	
		mov	SG_CACHEPTR, A;
		mov	DFCNTRL, DMAPARAMS; /* start the operation */
		test	SXFRCTL1, BITBUCKET jnz data_phase_overrun;
u2_preload_wait:
		test	SSTAT1, PHASEMIS jnz u2_phasemis;
		test	DFSTATUS, PRELOAD_AVAIL jz u2_preload_wait;
	} else {
		mov	DMAPARAMS  call dma;
data_phase_dma_done:
/* Go tell the host about any overruns */
		test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;

/* Exit if we had an underrun.  dma clears SINDEX in this case. */
		test	SINDEX,0xff	jz data_phase_finish;
	}
/*
 * Advance the scatter-gather pointers 
 */
sg_advance:
	if ((p->features & AHC_ULTRA2) != 0) {
		cmp	SG_COUNT, 0x01	je u2_data_phase_finish;
	} else {
		dec	SG_COUNT;
		test	SG_COUNT, 0xff	jz data_phase_finish;
	}

	if ((p->features & AHC_CMD_CHAN) != 0) {

		/*
		 * Do we have any prefetch left???
		 */
		cmp	CCSGADDR, CCSGADDR_MAX jne prefetch_avail;

		/*
		 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
		 */
		add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
		mvi	A, CCSGADDR_MAX;
		jc	. + 2;
		shl	A, 3, SG_COUNT;
		mov	CCHCNT, A;
		bmov	CCHADDR, SG_NEXT, 4;
		mvi	CCSGCTL, CCSGEN|CCSGRESET;
		test	CCSGCTL, CCSGDONE jz .;
		and	CCSGCTL, ~CCSGEN;
		test	CCSGCTL, CCSGEN jnz .;
		mvi	CCSGCTL, CCSGRESET;
prefetch_avail:
		bmov 	HADDR, CCSGRAM, 8;
		if ((p->features & AHC_ULTRA2) == 0) {
			bmov    STCNT, HCNT, 3;
		} else {
			dec	SG_COUNT;
		}
	} else {
		mvi	DINDEX, HADDR;
		mvi	SG_NEXT	call bcopy_4;

		mvi	HCNT[0],SG_SIZEOF;
		clr	HCNT[1];
		clr	HCNT[2];

		or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;

		call	dma_finish;

/*
 * Copy data from FIFO into SCB data pointer and data count.
 * This assumes that the SG segments are of the form:
 * struct ahc_dma_seg {
 *	u_int32_t	addr;	four bytes, little-endian order
 *	u_int32_t	len;	four bytes, little endian order
 * };
 */
 		mvi	DINDEX, HADDR;
		call	dfdat_in_7;
		call	set_stcnt_from_hcnt;
	}
/* Advance the SG pointer */
	clr	A;		/* add sizeof(struct scatter) */
	add	SG_NEXT[0],SG_SIZEOF;
	adc	SG_NEXT[1],A;

	if ((p->features & AHC_ULTRA2) != 0) {
		jmp	data_phase_loop;
	} else {
		test    SSTAT1, REQINIT jz .;
		test	SSTAT1,PHASEMIS	jz data_phase_loop;
	}


/*
 * We've loaded all of our segments into the preload layer.  Now, we simply
 * have to wait for it to finish or for us to get a phasemis.  And, since
 * we'll get a phasemis if we do finish, all we really need to do is wait
 * for a phasemis then check if we did actually complete all the segments.
 */
	if ((p->features & AHC_ULTRA2) != 0) {
u2_data_phase_finish:
		test	SSTAT1, PHASEMIS jnz u2_phasemis;
		test	SG_CACHEPTR, LAST_SEG_DONE jz u2_data_phase_finish;
		clr	SG_COUNT;
		test	SSTAT1, REQINIT	jz .;
		test	SSTAT1, PHASEMIS jz data_phase_loop;
u2_phasemis:
		call	ultra2_dmafinish;
		test	SG_CACHEPTR, LAST_SEG_DONE jnz data_phase_finish;
		test	SSTAT2, SHVALID jnz u2_fixup_residual;
		mvi	INTSTAT, SEQ_SG_FIXUP;
		jmp	data_phase_finish;
u2_fixup_residual:
		shr	ARG_1, 2, SG_CACHEPTR;
u2_phasemis_loop:
		and	A, 0x3f, SG_COUNT;
		cmp	ARG_1, A je data_phase_finish;
/*
 * Subtract SG_SIZEOF from the SG_NEXT pointer and add 1 to the SG_COUNT
 */
 		clr	A;
		add	SG_NEXT[0], -SG_SIZEOF;
		adc	SG_NEXT[1], 0xff;
		inc	SG_COUNT;
		jmp	u2_phasemis_loop;
	}

data_phase_finish:
/*
 * After a DMA finishes, save the SG and STCNT residuals back into the SCB
 * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
 * were transferred on the SCSI (as opposed to the host) bus.
 */
	if ((p->features & AHC_CMD_CHAN) != 0) {
		bmov    SCB_RESID_DCNT, STCNT, 3;
		mov	SCB_RESID_SGCNT, SG_COUNT;
		if ((p->features & AHC_ULTRA2) != 0) {
			or	SXFRCTL0, CLRSTCNT|CLRCHN;
		}
	} else {
		mov	SCB_RESID_DCNT[0],STCNT[0];
		mov	SCB_RESID_DCNT[1],STCNT[1];
		mov	SCB_RESID_DCNT[2],STCNT[2];
		mov	SCB_RESID_SGCNT, SG_COUNT;
	}

	jmp	ITloop;

data_phase_overrun:
/*
 * Turn off BITBUCKET mode and notify the host
 */
	if ((p->features & AHC_ULTRA2) != 0) {
/*
 * Wait for the target to quit transferring data on the SCSI bus
 */
 		test	SSTAT1, PHASEMIS jz .;
		call	ultra2_dmafinish;
	}
	and	SXFRCTL1, ~BITBUCKET;
	mvi	INTSTAT,DATA_OVERRUN;
	jmp	ITloop;




/*
 * Actually turn off the DMA hardware, save our current position into the
 * proper residual variables, wait for the next REQ signal, then jump to
 * the ITloop.  Jumping to the ITloop ensures that if we happen to get
 * brought into the data phase again (or are still in it after our last
 * segment) that we will properly signal an overrun to the kernel.
 */
	if ((p->features & AHC_ULTRA2) != 0) {
ultra2_dmafinish:
		test	DFCNTRL, DIRECTION jnz ultra2_dmahalt;
		and	DFCNTRL, ~SCSIEN;
		test	DFCNTRL, SCSIEN jnz .;
		if ((p->bugs & AHC_BUG_AUTOFLUSH) != 0) {
			or	DFCNTRL, FIFOFLUSH;
		}
ultra2_dmafifoflush:
		if ((p->bugs & AHC_BUG_AUTOFLUSH) != 0) {
			/*
			 * hardware bug alert!  This needless set of jumps
			 * works around a glitch in the silicon.  When the
			 * PCI DMA fifo goes empty, but there is still SCSI
			 * data to be flushed into the PCI DMA fifo (and from
			 * there on into main memory), the FIFOEMP bit will
			 * come on between the time when the PCI DMA buffer
			 * went empty and the next bit of data is copied from
			 * the SCSI fifo into the PCI fifo.  It should only
			 * come on when both FIFOs (meaning the entire FIFO
			 * chain) are emtpy.  Since it can take up to 4 cycles
			 * for new data to be copied from the SCSI fifo into
			 * the PCI fifo, testing for FIFOEMP status for 4
			 * extra times gives the needed time for any
			 * remaining SCSI fifo data to be put in the PCI fifo
			 * before we declare it *truly* empty.
			 */
			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
		}
		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
		test	DFSTATUS, MREQPEND	jnz .;
ultra2_dmahalt:
		and     DFCNTRL, ~(HDMAEN|SCSIEN);
		test	DFCNTRL, (HDMAEN|SCSIEN) jnz .;
		ret;
	}

/*
 * Command phase.  Set up the DMA registers and let 'er rip.
 */
p_command:
	call	assert;

/*
 * Load HADDR and HCNT.
 */
	if ((p->features & AHC_CMD_CHAN) != 0) {
		bmov	HADDR, SCB_CMDPTR, 5;
		bmov	HCNT[1], ALLZEROS, 2;
		if ((p->features & AHC_ULTRA2) == 0) {
			bmov	STCNT, HCNT, 3;
		}
	} else {
		mvi	DINDEX, HADDR;
		mvi	SCB_CMDPTR	call bcopy_5;
		clr	HCNT[1];
		clr	HCNT[2];
		call	set_stcnt_from_hcnt;
	}

	if ((p->features & AHC_ULTRA2) == 0) {
		mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
	} else {
		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
		test	SSTAT0, SDONE jnz .;
p_command_dma_loop:
		test	SSTAT0, SDONE jnz p_command_ultra2_dma_done;
		test	SSTAT1,PHASEMIS	jz p_command_dma_loop;	/* ie. underrun */
p_command_ultra2_dma_done:
		test	SCSISIGI, REQI	jz p_command_ultra2_shutdown;
		test	SSTAT1, (PHASEMIS|REQINIT)	jz p_command_ultra2_dma_done;
p_command_ultra2_shutdown:
		and     DFCNTRL, ~(HDMAEN|SCSIEN);
		test	DFCNTRL, (HDMAEN|SCSIEN) jnz .;
		or	SXFRCTL0, CLRSTCNT|CLRCHN;
	}
	jmp	ITloop;

/*
 * Status phase.  Wait for the data byte to appear, then read it
 * and store it into the SCB.
 */
p_status:
	call	assert;

	mov	SCB_TARGET_STATUS, SCSIDATL;
	jmp	ITloop;

/*
 * Message out phase.  If MSG_OUT is 0x80, build I full indentify message
 * sequence and send it to the target.  In addition, if the MK_MESSAGE bit
 * is set in the SCB_CONTROL byte, interrupt the host and allow it to send
 * it's own message.
 * 
 * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
 * This is done to allow the hsot to send messages outside of an identify
 * sequence while protecting the seqencer from testing the MK_MESSAGE bit
 * on an SCB that might not be for the current nexus. (For example, a
 * BDR message in responce to a bad reselection would leave us pointed to
 * an SCB that doesn't have anything to do with the current target).
 * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
 * bus device reset).
 *
 * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
 * in case the target decides to put us in this phase for some strange
 * reason.
 */
p_mesgout_retry:
	or      SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
p_mesgout:
	mov	SINDEX, MSG_OUT;
	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
p_mesgout_identify:
	if ((p->features & AHC_WIDE) != 0) {
		and	SINDEX,0xf,SCB_TCL;	/* lun */
	} else {
		and	SINDEX,0x7,SCB_TCL;	/* lun */
	}
	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
	or	SINDEX,A;		/* or in disconnect privledge */
	or	SINDEX,MSG_IDENTIFYFLAG;
p_mesgout_mk_message:
	test	SCB_CONTROL,MK_MESSAGE  jz p_mesgout_tag;
	mov	SCSIDATL, SINDEX;	/* Send the last byte */
	jmp	p_mesgout_from_host + 1;/* Skip HOST_MSG test */
/*
 * Send a tag message if TAG_ENB is set in the SCB control block.
 * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
 */
p_mesgout_tag:
	test	SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
	mov	SCSIDATL, SINDEX;	/* Send the identify message */
	call	phase_lock;
	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
	and	SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
	call	phase_lock;
	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
	mov	SCB_TAG	jmp p_mesgout_onebyte;
/*
 * Interrupt the driver, and allow it to send a message
 * if it asks.
 */
p_mesgout_from_host:
	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
	mvi     INTSTAT,AWAITING_MSG;
	nop;
	/*
	 * Did the host detect a phase change?
	 */
	cmp	RETURN_1, MSGOUT_PHASEMIS je p_mesgout_done;

p_mesgout_onebyte:
	mvi	CLRSINT1, CLRATNO;
	mov	SCSIDATL, SINDEX;

/*
 * If the next bus phase after ATN drops is a message out, it means
 * that the target is requesting that the last message(s) be resent.
 */
	call	phase_lock;
	cmp     LASTPHASE, P_MESGOUT    je p_mesgout_retry;

p_mesgout_done:
	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
	mov	LAST_MSG, MSG_OUT;
	cmp	MSG_OUT, MSG_IDENTIFYFLAG jne . + 2;
	and	SCB_CONTROL, ~MK_MESSAGE;
	mvi	MSG_OUT, MSG_NOOP;	/* No message left */
	jmp	ITloop;

/*
 * Message in phase.  Bytes are read using Automatic PIO mode.
 */
p_mesgin:
	mvi	ACCUM		call inb_first;	/* read the 1st message byte */

	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify;
	cmp	A,MSG_DISCONNECT	je mesgin_disconnect;
	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs;
	cmp	ALLZEROS,A		je mesgin_complete;
	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs;
	cmp	A,MSG_EXTENDED		je mesgin_extended;
	cmp	A,MSG_MESSAGE_REJECT	je mesgin_reject;
	cmp	A,MSG_NOOP		je mesgin_done;
	cmp	A,MSG_IGN_WIDE_RESIDUE	je mesgin_wide_residue;

rej_mesgin:
/*
 * We have no idea what this message in is, so we issue a message reject
 * and hope for the best.  In any case, rejection should be a rare
 * occurrence - signal the driver when it happens.
 */
	mvi	INTSTAT,SEND_REJECT;		/* let driver know */

	mvi	MSG_MESSAGE_REJECT	call mk_mesg;

mesgin_done:
	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
	jmp	ITloop;


mesgin_complete:
/*
 * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
 * and trigger a completion interrupt.  Before doing so, check to see if there
 * is a residual or the status byte is something other than STATUS_GOOD (0).
 * In either of these conditions, we upload the SCB back to the host so it can
 * process this information.  In the case of a non zero status byte, we 
 * additionally interrupt the kernel driver synchronously, allowing it to
 * decide if sense should be retrieved.  If the kernel driver wishes to request
 * sense, it will fill the kernel SCB with a request sense command and set
 * RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE we redownload
 * the SCB, and process it as the next command by adding it to the waiting list.
 * If the kernel driver does not wish to request sense, it need only clear
 * RETURN_1, and the command is allowed to complete normally.  We don't bother
 * to post to the QOUTFIFO in the error cases since it would require extra
 * work in the kernel driver to ensure that the entry was removed before the
 * command complete code tried processing it.
 */

/*
 * First check for residuals
 */
	test	SCB_RESID_SGCNT,0xff	jnz upload_scb;
	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Good Status? */
upload_scb:
	mvi	DMAPARAMS, FIFORESET;
	mov	SCB_TAG		call dma_scb;
check_status:
	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
	nop;
	cmp	RETURN_1, SEND_SENSE	jne complete;
	/* This SCB becomes the next to execute as it will retrieve sense */
	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
	mov	SCB_TAG		call dma_scb;
add_to_waiting_list:
	mov	SCB_NEXT,WAITING_SCBH;
	mov	WAITING_SCBH, SCBPTR;
	/*
	 * Prepare our selection hardware before the busfree so we have a
	 * high probability of winning arbitration.
	 */
	call	start_selection;
	jmp	await_busfree;

complete:
	/* If we are untagged, clear our address up in host ram */
	test	SCB_CONTROL, TAG_ENB jnz complete_post;
	mov	A, SAVED_TCL;
	mvi	UNTAGGEDSCB_OFFSET call post_byte_setup;
	mvi	SCB_LIST_NULL call post_byte;

complete_post:
	/* Post the SCB and issue an interrupt */
	if ((p->features & AHC_QUEUE_REGS) != 0) {
		mov	A, SDSCB_QOFF;
	} else {
		mov	A, QOUTPOS;
	}
	mvi	QOUTFIFO_OFFSET call post_byte_setup;
	mov	SCB_TAG call post_byte;
	if ((p->features & AHC_QUEUE_REGS) == 0) {
		inc 	QOUTPOS;
	}
	mvi	INTSTAT,CMDCMPLT;

add_to_free_list:
	call	add_scb_to_free_list;
	jmp	await_busfree;

/*
 * Is it an extended message?  Copy the message to our message buffer and
 * notify the host.  The host will tell us whether to reject this message,
 * respond to it with the message that the host placed in our message buffer,
 * or simply to do nothing.
 */
mesgin_extended:
	mvi	INTSTAT,EXTENDED_MSG;		/* let driver know */
	jmp	ITloop;

/*
 * Is it a disconnect message?  Set a flag in the SCB to remind us
 * and await the bus going free.
 */
mesgin_disconnect:
	or	SCB_CONTROL,DISCONNECTED;
	call	add_scb_to_disc_list;
	jmp	await_busfree;

/*
 * Save data pointers message:
 * Copying RAM values back to SCB, for Save Data Pointers message, but
 * only if we've actually been into a data phase to change them.  This
 * protects against bogus data in scratch ram and the residual counts
 * since they are only initialized when we go into data_in or data_out.
 */
mesgin_sdptrs:
	test	SEQ_FLAGS, DPHASE	jz mesgin_done;
	/*
	 * The SCB SGPTR becomes the next one we'll download,
	 * and the SCB DATAPTR becomes the current SHADDR.
	 * Use the residual number since STCNT is corrupted by
	 * any message transfer.
	 */
	if ((p->features & AHC_CMD_CHAN) != 0) {
		bmov    SCB_SGCOUNT, SG_COUNT, 5;
		bmov    SCB_DATAPTR, SHADDR, 4;
		bmov    SCB_DATACNT, SCB_RESID_DCNT, 3;
	} else {
		mvi	DINDEX, SCB_SGCOUNT;
		mvi	SG_COUNT	call bcopy_5;
		mvi	DINDEX, SCB_DATAPTR;
		mvi	SHADDR		call bcopy_4;
		mvi	SCB_RESID_DCNT	call	bcopy_3;
	}
	jmp	mesgin_done;

/*
 * Restore pointers message?  Data pointers are recopied from the
 * SCB anytime we enter a data phase for the first time, so all
 * we need to do is clear the DPHASE flag and let the data phase
 * code do the rest.
 */
mesgin_rdptrs:
	and	SEQ_FLAGS, ~DPHASE;		/*
						 * We'll reload them
						 * the next time through
						 * the dataphase.
						 */
	jmp	mesgin_done;

/*
 * Identify message?  For a reconnecting target, this tells us the lun
 * that the reconnection is for - find the correct SCB and switch to it,
 * clearing the "disconnected" bit so we don't "find" it by accident later.
 */
mesgin_identify:
	
	if ((p->features & AHC_WIDE) != 0) {
		and	A,0x0f;		/* lun in lower four bits */
	} else {
		and	A,0x07;		/* lun in lower three bits */
	}
	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */

	mvi     ARG_2, SCB_LIST_NULL;   /* SCBID of prev SCB in disc List */
	call	get_untagged_SCBID;
	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
	if ((p->flags & AHC_PAGESCBS) != 0) {
		test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
	}
	/*
	 * If the SCB was found in the disconnected list (as is
	 * always the case in non-paging scenarios), SCBPTR is already
	 * set to the correct SCB.  So, simply setup the SCB and get
	 * on with things.
	 */
	mov	SCBPTR	call rem_scb_from_disc_list;
	jmp	setup_SCB;
/*
 * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
 * If we get one, we use the tag returned to find the proper
 * SCB.  With SCB paging, this requires using search for both tagged
 * and non-tagged transactions since the SCB may exist in any slot.
 * If we're not using SCB paging, we can use the tag as the direct
 * index to the SCB.
 */
snoop_tag:
	mov	NONE,SCSIDATL;		/* ACK Identify MSG */
snoop_tag_loop:
	call	phase_lock;
	cmp	LASTPHASE, P_MESGIN	jne not_found;
	cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
get_tag:
	mvi	ARG_1	call inb_next;	/* tag value */

use_retrieveSCB:
	call	retrieveSCB;
setup_SCB:
	mov	A, SAVED_TCL;
	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
	test	SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
	and	SCB_CONTROL,~DISCONNECTED;
	or	SEQ_FLAGS,IDENTIFY_SEEN;	  /* make note of IDENTIFY */
	/* See if the host wants to send a message upon reconnection */
	test	SCB_CONTROL, MK_MESSAGE jz mesgin_done;
	and	SCB_CONTROL, ~MK_MESSAGE;
	mvi	HOST_MSG	call mk_mesg;
	jmp	mesgin_done;

not_found_cleanup_scb:
	test	SCB_CONTROL, DISCONNECTED jz . + 3;
	call	add_scb_to_disc_list;
	jmp	not_found;
	call	add_scb_to_free_list;
not_found:
	mvi	INTSTAT, NO_MATCH;
	mvi	MSG_BUS_DEV_RESET	call mk_mesg;
	jmp	mesgin_done;

/*
 * Message reject?  Let the kernel driver handle this.  If we have an 
 * outstanding WDTR or SDTR negotiation, assume that it's a response from 
 * the target selecting 8bit or asynchronous transfer, otherwise just ignore 
 * it since we have no clue what it pertains to.
 */
mesgin_reject:
	mvi	INTSTAT, REJECT_MSG;
	jmp	mesgin_done;

/*
 * Wide Residue.  We handle the simple cases, but pass of the one hard case
 * to the kernel (when the residue byte happened to cause us to advance our
 * sg element array, so we know have to back that advance out).
 */
mesgin_wide_residue:
	mvi	ARG_1	call inb_next; /* ACK the wide_residue and get */
				       /* the size byte */
/*
 * In order for this to be reliable, we have to do all sorts of horrible
 * magic in terms of resetting the datafifo and reloading the shadow layer
 * with the correct new values (so that a subsequent save data pointers
 * message will do the right thing).  We let the kernel do that work.
 */
 	mvi	INTSTAT, WIDE_RESIDUE;
	jmp	mesgin_done;
	
/*
 * [ ADD MORE MESSAGE HANDLING HERE ]
 */

/*
 * Locking the driver out, build a one-byte message passed in SINDEX
 * if there is no active message already.  SINDEX is returned intact.
 */
mk_mesg:
	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
	mov	MSG_OUT,SINDEX ret;

/*
 * Functions to read data in Automatic PIO mode.
 *
 * According to Adaptec's documentation, an ACK is not sent on input from
 * the target until SCSIDATL is read from.  So we wait until SCSIDATL is
 * latched (the usual way), then read the data byte directly off the bus
 * using SCSIBUSL.  When we have pulled the ATN line, or we just want to
 * acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
 * spec guarantees that the target will hold the data byte on the bus until
 * we send our ACK.
 *
 * The assumption here is that these are called in a particular sequence,
 * and that REQ is already set when inb_first is called.  inb_{first,next}
 * use the same calling convention as inb.
 */

inb_next:
	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
inb_next_wait:
	/*
	 * If there is a parity error, wait for the kernel to
	 * see the interrupt and prepare our message response
	 * before continuing.
	 */
	test	SSTAT1, REQINIT	jz inb_next_wait;
	test	SSTAT1, SCSIPERR jnz .;
	and	LASTPHASE, PHASE_MASK, SCSISIGI;
	cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
inb_first:
	mov	DINDEX,SINDEX;
	mov	DINDIR,SCSIBUSL	ret;		/*read byte directly from bus*/
inb_last:
	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/

	
mesgin_phasemis:
/*
 * We expected to receive another byte, but the target changed phase
 */
	mvi	INTSTAT, MSGIN_PHASEMIS;
	jmp	ITloop;

/*
 * DMA data transfer.  HADDR and HCNT must be loaded first, and
 * SINDEX should contain the value to load DFCNTRL with - 0x3d for
 * host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
 * during initialization.
 */
if ((p->features & AHC_ULTRA2) == 0) {
dma:
	mov	DFCNTRL,SINDEX;
dma_loop:
	test	SSTAT0,DMADONE	jnz dma_dmadone;
	test	SSTAT1,PHASEMIS	jz dma_loop;	/* ie. underrun */
dma_phasemis:
	test	SSTAT0,SDONE	jnz dma_checkfifo;
	mov	SINDEX,ALLZEROS;		/* Notify caller of phasemiss */

/*
 * We will be "done" DMAing when the transfer count goes to zero, or
 * the target changes the phase (in light of this, it makes sense that
 * the DMA circuitry doesn't ACK when PHASEMIS is active).  If we are
 * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
 * magically on STCNT=0 or a phase change, so just wait for FIFO empty
 * status.
 */
dma_checkfifo:
	test	DFCNTRL,DIRECTION	jnz dma_fifoempty;
dma_fifoflush:
	test	DFSTATUS,FIFOEMP	jz dma_fifoflush;

dma_fifoempty:
	/* Don't clobber an inprogress host data transfer */
	test	DFSTATUS, MREQPEND	jnz dma_fifoempty;
/*
 * Now shut the DMA enables off and make sure that the DMA enables are 
 * actually off first lest we get an ILLSADDR.
 */
dma_dmadone:
	cmp	LASTPHASE, P_COMMAND	je dma_await_nreq;
	test	SCSIRATE, 0x0f	jnz dma_shutdown;
dma_await_nreq:
	test	SCSISIGI, REQI	jz dma_shutdown;
	test	SSTAT1, (PHASEMIS|REQINIT)	jz dma_await_nreq;
dma_shutdown:
	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
dma_halt:
	/*
	 * Some revisions of the aic7880 have a problem where, if the
	 * data fifo is full, but the PCI input latch is not empty, 
	 * HDMAEN cannot be cleared.  The fix used here is to attempt
	 * to drain the data fifo until there is space for the input
	 * latch to drain and HDMAEN de-asserts.
	 */
	if ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0) {
		mov	NONE, DFDAT;
	}
	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
}
return:
	ret;

/*
 * Assert that if we've been reselected, then we've seen an IDENTIFY
 * message.
 */
assert:
	test	SEQ_FLAGS,IDENTIFY_SEEN	jnz return;	/* seen IDENTIFY? */

	mvi	INTSTAT,NO_IDENT 	ret;	/* no - tell the kernel */

/*
 * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
 * or by the SCBID ARG_1.  The search begins at the SCB index passed in
 * via SINDEX which is an SCB that must be on the disconnected list.  If
 * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
 * is set to the proper SCB.
 */
findSCB:
	mov	SCBPTR,SINDEX;			/* Initialize SCBPTR */
	cmp	ARG_1, SCB_LIST_NULL	jne findSCB_by_SCBID;
	mov	A, SAVED_TCL;
	mvi	SCB_TCL	jmp findSCB_loop;	/* &SCB_TCL -> SINDEX */
findSCB_by_SCBID:
	mov	A, ARG_1;			/* Tag passed in ARG_1 */
	mvi	SCB_TAG	jmp findSCB_loop;	/* &SCB_TAG -> SINDEX */
findSCB_next:
	mov     ARG_2, SCBPTR;
	cmp	SCB_NEXT, SCB_LIST_NULL je notFound;
	mov	SCBPTR,SCB_NEXT;
	dec	SINDEX;		/* Last comparison moved us too far */
findSCB_loop:
	cmp	SINDIR, A	jne findSCB_next;
	mov	SINDEX, SCBPTR 	ret;
notFound:
	mvi	SINDEX, SCB_LIST_NULL	ret;

/*
 * Retrieve an SCB by SCBID first searching the disconnected list falling
 * back to DMA'ing the SCB down from the host.  This routine assumes that
 * ARG_1 is the SCBID of interrest and that SINDEX is the position in the
 * disconnected list to start the search from.  If SINDEX is SCB_LIST_NULL,
 * we go directly to the host for the SCB.
 */
retrieveSCB:
	test	SEQ_FLAGS, SCBPTR_VALID	jz retrieve_from_host;
	mov	SCBPTR	call findSCB;	/* Continue the search */
	cmp	SINDEX, SCB_LIST_NULL	je retrieve_from_host;

/*
 * This routine expects SINDEX to contain the index of the SCB to be
 * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
 * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
 * if it is at the head.
 */
rem_scb_from_disc_list:
/* Remove this SCB from the disconnection list */
	cmp     ARG_2, SCB_LIST_NULL    je rHead;
	mov	DINDEX, SCB_NEXT;
	mov	SCBPTR, ARG_2;
	mov	SCB_NEXT, DINDEX;
	mov	SCBPTR, SINDEX ret;
rHead:
	mov	DISCONNECTED_SCBH,SCB_NEXT ret;

retrieve_from_host:
/*
 * We didn't find it.  Pull an SCB and DMA down the one we want.
 * We should never get here in the non-paging case.
 */
	mov	ALLZEROS	call	get_free_or_disc_scb;
	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
	/* Jump instead of call as we want to return anyway */
	mov	ARG_1	jmp dma_scb;

/*
 * Determine whether a target is using tagged or non-tagged transactions
 * by first looking for a matching transaction based on the TCL and if
 * that fails, looking up this device in the host's untagged SCB array.
 * The TCL to search for is assumed to be in SAVED_TCL.  The value is
 * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
 * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
 * in an SCB instead of having to go to the host.
 */
get_untagged_SCBID:
	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
	mvi	ARG_1, SCB_LIST_NULL;
	mov	DISCONNECTED_SCBH call findSCB;
	cmp	SINDEX, SCB_LIST_NULL	je get_SCBID_from_host;
	or	SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
	test	SCB_CONTROL, TAG_ENB	jnz . + 2;
	mov	ARG_1, SCB_TAG	ret;
	mvi	ARG_1, SCB_LIST_NULL ret;

/*
 * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
 * and a base address of SCBID_ADDR.  The byte is returned in RETURN_2.
 */
fetch_byte:
	mov	ARG_2, SINDEX;
	if ((p->features & AHC_CMD_CHAN) != 0) {
		mvi	DINDEX, CCHADDR;
		mvi	SCBID_ADDR call set_1byte_addr;
		mvi	CCHCNT, 1;
		mvi	CCSGCTL, CCSGEN|CCSGRESET;
		test	CCSGCTL, CCSGDONE jz .;
		mvi	CCSGCTL, CCSGRESET;
		bmov	RETURN_2, CCSGRAM, 1 ret;
	} else {
		mvi	DINDEX, HADDR;
		mvi	SCBID_ADDR call set_1byte_addr;
		mvi	HCNT[0], 1;
		clr	HCNT[1];
		clr	HCNT[2];
		mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
		call	dma_finish;
		mov	RETURN_2, DFDAT ret;
	}

/*
 * Prepare the hardware to post a byte to host memory given an
 * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
 */
post_byte_setup:
	mov	ARG_2, SINDEX;
	if ((p->features & AHC_CMD_CHAN) != 0) {
		mvi	DINDEX, CCHADDR;
		mvi	SCBID_ADDR call	set_1byte_addr;
		mvi	CCHCNT, 1;
		mvi	CCSCBCTL, CCSCBRESET ret;
	} else {
		mvi	DINDEX, HADDR;
		mvi	SCBID_ADDR call	set_1byte_addr;
		mvi	HCNT[0], 1;
		clr	HCNT[1];
		clr	HCNT[2];
		mvi	DFCNTRL, FIFORESET ret;
	}

post_byte:
	if ((p->features & AHC_CMD_CHAN) != 0) {
		bmov	CCSCBRAM, SINDEX, 1;
		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
		test	CCSCBCTL, CCSCBDONE jz .;
		clr	CCSCBCTL ret;
	} else {
		mov	DFDAT, SINDEX;
		or	DFCNTRL, HDMAEN|FIFOFLUSH;
		jmp	dma_finish;
	}

get_SCBID_from_host:
	mov	A, SAVED_TCL;
	mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
	mov	RETURN_1,  RETURN_2 ret;

phase_lock:     
	test	SSTAT1, REQINIT jz phase_lock;
	test	SSTAT1, SCSIPERR jnz phase_lock;
	and	SCSISIGO, PHASE_MASK, SCSISIGI;
	and	LASTPHASE, PHASE_MASK, SCSISIGI ret;

if ((p->features & AHC_CMD_CHAN) == 0) {
set_stcnt_from_hcnt:
	mov	STCNT[0], HCNT[0];
	mov	STCNT[1], HCNT[1];
	mov	STCNT[2], HCNT[2] ret;

bcopy_7:
	mov	DINDIR, SINDIR;
	mov	DINDIR, SINDIR;
bcopy_5:
	mov	DINDIR, SINDIR;
bcopy_4:
	mov	DINDIR, SINDIR;
bcopy_3:
	mov	DINDIR, SINDIR;
	mov	DINDIR, SINDIR;
	mov	DINDIR, SINDIR ret;
}

/*
 * Setup addr assuming that A is an index into
 * an array of 32byte objects, SINDEX contains
 * the base address of that array, and DINDEX
 * contains the base address of the location
 * to store the indexed address.
 */
set_32byte_addr:
	shr	ARG_2, 3, A;
	shl	A, 5;
/*
 * Setup addr assuming that A + (ARG_1 * 256) is an
 * index into an array of 1byte objects, SINDEX contains
 * the base address of that array, and DINDEX contains
 * the base address of the location to store the computed
 * address.
 */
set_1byte_addr:
	add	DINDIR, A, SINDIR;
	mov	A, ARG_2;
	adc	DINDIR, A, SINDIR;
	clr	A;
	adc	DINDIR, A, SINDIR;
	adc	DINDIR, A, SINDIR ret;

/*
 * Either post or fetch and SCB from host memory based on the
 * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
 */
dma_scb:
	mov	A, SINDEX;
	if ((p->features & AHC_CMD_CHAN) != 0) {
		mvi	DINDEX, CCHADDR;
		mvi	HSCB_ADDR call set_32byte_addr;
		mov	CCSCBPTR, SCBPTR;
		mvi	CCHCNT, 32;
		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
		jmp	dma_scb_finish;
dma_scb_tohost:
		if ((p->features & AHC_ULTRA2) == 0) {
			mvi	CCSCBCTL, CCSCBRESET;
			bmov	CCSCBRAM, SCB_CONTROL, 32;
			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
			test	CCSCBCTL, CCSCBDONE jz .;
		}
		if ((p->features & AHC_ULTRA2) != 0) {
			if ((p->bugs & AHC_BUG_SCBCHAN_UPLOAD) != 0) {
				mvi     CCSCBCTL, CCARREN|CCSCBRESET;
				cmp     CCSCBCTL, ARRDONE|CCARREN jne .;
                        	mvi     CCHCNT, 32;
				mvi     CCSCBCTL, CCSCBEN|CCSCBRESET;
				cmp     CCSCBCTL, CCSCBDONE|CCSCBEN jne .;
			} else {
				mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
				cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
			}
		}
dma_scb_finish:
		clr	CCSCBCTL;
		test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
		ret;
	}
	if ((p->features & AHC_CMD_CHAN) == 0) {
		mvi	DINDEX, HADDR;
		mvi	HSCB_ADDR call set_32byte_addr;
		mvi	HCNT[0], 32;
		clr	HCNT[1];
		clr	HCNT[2];
		mov	DFCNTRL, DMAPARAMS;
		test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
		/* Fill it with the SCB data */
copy_scb_tofifo:
		mvi	SINDEX, SCB_CONTROL;
		add	A, 32, SINDEX;
copy_scb_tofifo_loop:
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		mov	DFDAT,SINDIR;
		cmp	SINDEX, A jne copy_scb_tofifo_loop;
		or	DFCNTRL, HDMAEN|FIFOFLUSH;
		jmp	dma_finish;
dma_scb_fromhost:
		mvi	DINDEX, SCB_CONTROL;
		if ((p->bugs & AHC_BUG_PCI_2_1_RETRY) != 0) {
			/*
			 * Set the A to -24.  It it hits 0, then we let
			 * our code fall through to dfdat_in_8 to complete
			 * the last of the copy.
			 *
			 * Also, things happen 8 bytes at a time in this
			 * case, so we may need to drain the fifo at most
			 * 3 times to keep things flowing
			 */
			mvi	A, -24;
dma_scb_hang_fifo:
			/* Wait for the first bit of data to hit the fifo */
			test	DFSTATUS, FIFOEMP jnz .;
dma_scb_hang_wait:
			/* OK, now they've started to transfer into the fifo,
			 * so wait for them to stop trying to transfer any
			 * more data.
			 */
			test	DFSTATUS, MREQPEND jnz .;
			/*
			 * OK, they started, then they stopped, now see if they
			 * managed to complete the job before stopping.  Try
			 * it multiple times to give the chip a few cycles to
			 * set the flag if it did complete.
			 */
			test	DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
			test	DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
			test	DFSTATUS, HDONE jnz dma_scb_hang_dma_done;
			/*
			 * Too bad, the chip didn't complete the DMA, but there
			 * aren't any more memory requests pending, so that
			 * means it stopped part way through and hung.  That's
			 * our bug, so now we drain what data there is in the
			 * fifo in order to get things going again.
			 */
dma_scb_hang_empty_fifo:
			call	dfdat_in_8;
			add	A, 8;
			add	SINDEX, A, HCNT;
			/*
			 * If there are another 8 bytes of data waiting in the
			 * fifo, then the carry bit will be set as a result
			 * of the above add command (unless A is non-negative,
			 * in which case the carry bit won't be set).
			 */
			jc	dma_scb_hang_empty_fifo;
			/*
			 * We've emptied the fifo now, but we wouldn't have got
			 * here if the memory transfer hadn't stopped part way
			 * through, so go back up to the beginning of the
			 * loop and start over.  When it succeeds in getting
			 * all the data down, HDONE will be set and we'll
			 * jump to the code just below here.
			 */
			jmp	dma_scb_hang_fifo;
dma_scb_hang_dma_done:
			and	DFCNTRL, ~HDMAEN;
			test	DFCNTRL, HDMAEN jnz .;
			call	dfdat_in_8;
			add	A, 8;
			cmp	A, 8 jne . - 2;
			ret;
		} else {
			call	dma_finish;
			call	dfdat_in_8;
			call	dfdat_in_8;
			call	dfdat_in_8;
		}
dfdat_in_8:
		mov	DINDIR,DFDAT;
dfdat_in_7:
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT;
		mov	DINDIR,DFDAT ret;
	}


/*
 * Wait for DMA from host memory to data FIFO to complete, then disable
 * DMA and wait for it to acknowledge that it's off.
 */
if ((p->features & AHC_CMD_CHAN) == 0) {
dma_finish:
	test	DFSTATUS,HDONE	jz dma_finish;
	/* Turn off DMA */
	and	DFCNTRL, ~HDMAEN;
	test	DFCNTRL, HDMAEN jnz .;
	ret;
}

add_scb_to_free_list:
	if ((p->flags & AHC_PAGESCBS) != 0) {
		mov	SCB_NEXT, FREE_SCBH;
		mov	FREE_SCBH, SCBPTR;
	}
	mvi	SCB_TAG, SCB_LIST_NULL ret;

if ((p->flags & AHC_PAGESCBS) != 0) {
get_free_or_disc_scb:
	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
return_error:
	mvi	SINDEX, SCB_LIST_NULL	ret;
dequeue_disc_scb:
	mov	SCBPTR, DISCONNECTED_SCBH;
dma_up_scb:
	mvi	DMAPARAMS, FIFORESET;
	mov	SCB_TAG		call dma_scb;
unlink_disc_scb:
	mov	DISCONNECTED_SCBH, SCB_NEXT ret;
dequeue_free_scb:
	mov	SCBPTR, FREE_SCBH;
	mov	FREE_SCBH, SCB_NEXT ret;
}

add_scb_to_disc_list:
/*
 * Link this SCB into the DISCONNECTED list.  This list holds the
 * candidates for paging out an SCB if one is needed for a new command.
 * Modifying the disconnected list is a critical(pause dissabled) section.
 */
	mov	SCB_NEXT, DISCONNECTED_SCBH;
	mov	DISCONNECTED_SCBH, SCBPTR ret;