Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

   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
/*	$NetBSD: iwm.s,v 1.7 2015/01/02 15:50:28 christos Exp $	*/

/*
 * Copyright (c) 1996-99 Hauke Fath.  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.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
 */
	
/*
 * iwm.s -- low level routines for Sony floppy disk access.
 * The present implementation supports the 800K GCR format on non-DMA 
 * machines.
 *
 * The IWM and SWIM chips run in polled mode; they are not capable of
 * interrupting the CPU. That's why interrupts need only be blocked 
 * when there is simply no time for interrupt routine processing, 
 * i.e. during data transfers.
 * 
 * o  The local routines do not block any interrupts.
 *
 * o  The iwmXXX() routines that set/get IWM or drive settings are not
 *    time critical and do not block interrupts.
 *
 * o  The iwmXXX() routines that are called to perform data transfers
 *    block all interrupts because otherwise the current sector data
 *    would be lost.
 *    The old status register content is stored on the stack.
 *
 * o  We run at spl4 to give the NMI switch a chance. All currently 
 *    supported machines have no interrupt sources > 4 (SSC) -- the
 *    Q700 interrupt levels can be shifted around in A/UX mode,
 *    but we're not there, yet.
 *
 * o  As a special case iwmReadSectHdr() must run with interrupts disabled
 *    (it transfers data). Depending on the needs of the caller, it
 *    may be necessary to block interrupts after completion of the routine
 *    so interrupt handling is left to the caller.
 *
 * If we wanted to deal with incoming serial data / serial interrupts,
 * we would have to either call zshard(0) {mac68k/dev/zs.c} or 
 * zsc_intr_hard(0) {sys/dev/ic/z8530sc.c}. Or we would have to roll our 
 * own as both of the listed function calls look rather expensive compared 
 * to a 'tst.b REGADDR ; bne NN'.
 */

#include <m68k/asm.h>

#include <mac68k/obio/iwmreg.h>

#define USE_DELAY	0	/* "1" bombs for unknown reasons */

	
/*
 * References to global name space
 */
	.extern	_C_LABEL(TimeDBRA)	| in mac68k/macrom.c
	.extern _C_LABEL(Via1Base)	| in mac68k/machdep.c
	.extern	_C_LABEL(IWMBase)	| in iwm_fd.c
	

	.data
	
diskTo:
	/*
	 * Translation table from 'disk bytes' to 6 bit 'nibbles', 
	 * taken from the .Sony driver.
	 * This could be made a loadable table (via ioctls) to read
	 * e.g. ProDOS disks (there is a hook for such a table in .Sony).
	 */
	.byte	/* 90 */  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01
	.byte	/* 98 */  0xFF, 0xFF, 0x02, 0x03, 0xFF, 0x04, 0x05, 0x06
	.byte	/* A0 */  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x08
	.byte	/* A8 */  0xFF, 0xFF, 0xFF, 0x09, 0x0A, 0x0B, 0x0C, 0x0D
	.byte	/* B0 */  0xFF, 0xFF, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
	.byte	/* B8 */  0xFF, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A
	.byte	/* C0 */  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
	.byte	/* C8 */  0xFF, 0xFF, 0xFF, 0x1B, 0xFF, 0x1C, 0x1D, 0x1E
	.byte	/* D0 */  0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0x20, 0x21
	.byte	/* D8 */  0xFF, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28
	.byte	/* E0 */  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x29, 0x2A, 0x2B
	.byte	/* E8 */  0xFF, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32
	.byte	/* F0 */  0xFF, 0xFF, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38
	.byte	/* F8 */  0xFF, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F

hdrLeadIn:	
	.byte	0xD5, 0xAA, 0x96
	
hdrLeadOut:
	.byte	0xDE, 0xAA, 0xFF

dataLeadIn:
	.byte	0xD5, 0xAA, 0xAD

dataLeadOut:
	.byte	0xDE, 0xAA, 0xFF, 0xFF
	

toDisk:
	/*
	 * Translation table from 6-bit nibbles [0x00..0x3f] to 'disk bytes'
	 */
	.byte	/* 00 */  0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6
	.byte	/* 08 */  0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3
	.byte	/* 10 */  0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC
	.byte	/* 18 */  0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3
	.byte	/* 20 */  0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE
	.byte	/* 28 */  0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC
	.byte	/* 30 */  0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xf5, 0xF6
	.byte	/* 38 */  0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF

syncPattern:
	/*
	 * This sync pattern creates 4 sync chars with 10 bits each that look
	 * like 0011111111b (i.e. 0x0FF). As the IWM ignores leading zero
	 * bits, it locks on 0xFF after the third sync byte.
	 * For convenience, the bytes of the sector data lead-in
	 * (D5 AA AD) follow.
	 */
	.byte	0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF
	.byte	0xD5, 0xAA, 0xAD


	
	.text

/*
 * Register conventions:	
 *	%a0	IWM base address
 *	%a1	VIA1 base address
 *
 *	%d0	return value (0 == no error)
 *
 * Upper bits in data registers that are not cleared give nasty
 * (pseudo-) random errors when building an address. Make sure those
 *  registers are cleaned with a moveq before use!
 */


	
/**
 **	Export wrappers
 **/

/*
 * iwmQueryDrvFlags -- export wrapper for driveStat
 *
 * Parameters:	stack	l	drive selector
 *		stack	l	register selector
 * Returns:	%d0		flag
 */
ENTRY(iwmQueryDrvFlag)
	link	%a6,#0
	moveml	%d1/%a0-%a1,%sp@-
	movel	_C_LABEL(IWMBase),%a0
	movel	_C_LABEL(Via1Base),%a1

	movel	%a6@(8),%d0		| Get drive #
	beq	quDrv00
	cmpl	#1,%d0
	beq	quDrv01

	bra	quDone			| Invalid drive #

quDrv00:			
	tstb	%a0@(intDrive)		| SELECT; choose drive #0
	bra	queryDrv

quDrv01:		
	tstb	%a0@(extDrive)		| SELECT; choose drive #1

queryDrv:	
	movel	%a6@(12),%d0		| Get register #
	bsr	driveStat

quDone:	
	moveml	%sp@+,%d1/%a0-%a1	
	unlk	%a6
	rts


/*
 * iwmReadSectHdr -- read and decode the next available sector header.
 *
 * Parameters:	stack	l	Address of sector header struct (I/O)
 *				b	side (0, 1)
 *				b	track (0..79)
 *				b	sector (0..11)
 * Returns:	%d0		result code
 */
ENTRY(iwmReadSectHdr)
	link	%a6,#0
	moveml	%d1-%d5/%a0-%a4,%sp@-
	movel	%a6@(0x08),%a4		| Get param block address
	bsr	readSectHdr
	moveml	%sp@+,%d1-%d5/%a0-%a4	
	unlk	%a6
	rts
	


/**
 **	Exported functions
 **/
	
/*
 * iwmInit -- Initialize IWM chip.
 *
 * Parameters:	-
 * Returns:	%d0		result code
 */
ENTRY(iwmInit)
	link	%a6,#0
	moveml	%d2/%a0,%sp@-
	movel	_C_LABEL(IWMBase),%a0

	/*
	 * Reset IWM to known state (clear disk I/O latches)
	 */
	tstb	%a0@(ph0L)		| CA0
	tstb	%a0@(ph1L)		| CA1
	tstb	%a0@(ph2L)		| CA2
	tstb	%a0@(ph3L)		| LSTRB

	tstb	%a0@(mtrOff)		| ENABLE; make sure drive is off
	tstb	%a0@(intDrive)		| SELECT; choose drive 1
	moveq	#0x1F,%d0		| XXX was 0x17 -- WHY!?
	
	/*
	 * First do it quick...
	 */
	tstb	%a0@(q6H)
	andb	%a0@(q7L),%d0		| status register
	tstb	%a0@(q6L)
	cmpib	#iwmMode,%d0		| all is well??
	beq	initDone

	/*
	 * If this doesn't succeed (e.g. drive still running),
	 * we do it thoroughly.
	 */
	movel	#0x00080000,%d2		| ca. 500,000 retries = 1.5 sec
initLp:
	moveq	#initIWMErr,%d0		| Initialization error
	subql	#1,%d2
	bmi	initErr
	tstb	%a0@(mtrOff)		| disable drive
	tstb	%a0@(q6H)
	moveq	#0x3F,%d0
	andb	%a0@(q7L),%d0
	bclr	#5,%d0			| Reset bit 5 and set Z flag
					| according to previous state
	bne	initLp			| Loop if drive still on
	cmpib	#iwmMode,%d0
	beq	initDone
	moveb	#iwmMode,%a0@(q7H)	| Init IWM
	tstb	%a0@(q7L)
	bra	initLp
	
initDone:	
	tstb	%a0@(q6L)		| Prepare IWM for data
	moveq	#0,%d0			| noErr	

initErr:
	moveml	%sp@+,%d2/%a0
	unlk	%a6
	rts


/*
 * iwmCheckDrive -- Check if given drive is available and return bit vector
 *	with capabilities (SS/DS, disk inserted, ...)
 *
 * Parameters:	stack	l	Drive number (0,1)
 * Returns:	%d0	Bit	 0 - 0 = Drive is single sided
 *				 1 - 0 = Disk inserted
 *				 2 - 0 = Motor is running
 *				 3 - 0 = Disk is write protected
 *				 4 - 0 = Disk is DD
 *				31 - (-1) No drive / invalid drive #	
 */
ENTRY(iwmCheckDrive)
	link	%a6,#0
	moveml	%d1/%a0-%a1,%sp@-
	movel	_C_LABEL(IWMBase),%a0
	movel	_C_LABEL(Via1Base),%a1

	moveq	#-1,%d1			| no drive

	movel	%a6@(0x08),%d0		| check drive #
	beq	chkDrv00
	cmpl	#1,%d0
	beq	chkDrv01

	bra	chkDone			| invalid drive #

chkDrv00:			
	tstb	%a0@(intDrive)		| SELECT; choose drive #0
	bra	chkDrive

chkDrv01:		
	tstb	%a0@(extDrive)		| SELECT; choose drive #1
		
chkDrive:
	moveq	#-2,%d1			| error code
	moveq	#drvInstalled,%d0	| Drive installed?
	bsr	driveStat
	bmi	chkDone			| no drive

	moveq	#0,%d1			| Drive found
	tstb	%a0@(mtrOn)		| ENABLE; activate drive
	moveq	#singleSided,%d0	| Drive is single-sided?
	bsr	driveStat
	bpl	chkHasDisk
	/*
	 * Drive is double-sided -- this is not really a surprise as the
	 * old ss 400k drive needs disk speed control from the Macintosh 
	 * and we're not doing that here. Anyway - just in case...
	 * I am not sure m680x0 Macintoshes (x>0) support 400K drives at all
	 * due to their radically different sound support.
	 */ 
	bset	#0,%d1			| 1 = no.
chkHasDisk:
	moveq	#diskInserted,%d0	| Disk inserted?
	bsr	driveStat
	bpl	chkMotorOn
	bset	#1,%d1			| 1 = No.
	bra	chkDone
chkMotorOn:		
	moveq	#drvMotorState,%d0	| Motor is running?
	bsr	driveStat
	bpl	chkWrtProt
	bset	#2,%d1			| 1 = No.
chkWrtProt:	
	moveq	#writeProtected,%d0	| Disk is write protected?
	bsr	driveStat
	bpl	chkDD_HD
	bset	#3,%d1			| 1 = No.
chkDD_HD:
	moveq	#diskIsHD,%d0		| Disk is HD? (was "drive installed")
	bsr	driveStat
	bpl	chkDone
	bset	#4,%d1			| 1 = No.
chkDone:
	movel	%d1,%d0
	moveml	%sp@+,%d1/%a0-%a1	
	unlk	%a6
	rts
	

/*
 * iwmDiskEject -- post EJECT command and toggle LSTRB line to give a 
 * strobe signal.
 * IM III says pulse length = 500 ms, but we seem to get away with 
 * less delay; after all, we spin lock the CPU with it.
 *
 * Parameters:	stack	l	drive number (0,1)
 *		%a0		IWMBase
 *		%a1		VIABase
 * Returns:	%d0		result code
 */
ENTRY(iwmDiskEject)
	link	%a6,#0
	movel	_C_LABEL(IWMBase),%a0
	movel	_C_LABEL(Via1Base),%a1

	movel	%a6@(0x08),%d0		| Get drive #
	beq	ejDrv00
	cmpw	#1,%d0
	beq	ejDrv01

	bra	ejDone			| Invalid drive #

ejDrv00:			
	tstb	%a0@(intDrive)		| SELECT; choose drive #0
	bra	ejDisk

ejDrv01:		
	tstb	%a0@(extDrive)		| SELECT; choose drive #1
ejDisk:		
	tstb	%a0@(mtrOn)		| ENABLE; activate drive
	
	moveq	#motorOffCmd,%d0	| Motor off
 	bsr	driveCmd

	moveq	#diskInserted,%d0	| Disk inserted?
	bsr	driveStat
	bmi	ejDone
	
	moveq	#ejectDiskCmd,%d0	| Eject it
	bsr	selDriveReg

	tstb	%a0@(ph3H)		| LSTRB high
#if USE_DELAY
	movel	#1000,%sp@-		| delay 1 ms
	jsr	_C_LABEL(delay)
	addqw	#4,%sp			| clean up stack
#else
	movew	#1,%d0
	bsr	iwmDelay
#endif
	tstb	%a0@(ph3L)		| LSTRB low
	moveq	#0,%d0			| All's well...
ejDone:
	unlk	%a6
	rts


/*
 * iwmSelectDrive -- select internal (0) / external (1) drive.
 *
 * Parameters:	stack	l	drive ID (0/1)
 * Returns:	%d0		drive #
 */
ENTRY(iwmSelectDrive)
	link	%a6,#0
	moveml	%a0-%a1,%sp@-
	movel	_C_LABEL(IWMBase),%a0
	movel	_C_LABEL(Via1Base),%a1

	movel	%a6@(8),%d0		| Get drive #
	bne	extDrv
	tstb	%a0@(intDrive)
	bra	sdDone
extDrv:
	tstb	%a0@(extDrive)
sdDone:
	moveml	%sp@+,%a0-%a1	
	unlk	%a6
	rts


/*
 * iwmMotor -- switch drive motor on/off
 *
 * Parameters:	stack	l	drive ID (0/1)
 *		stack	l	on(1)/off(0)
 * Returns:	%d0		motor cmd
 */
ENTRY(iwmMotor)
	link	%a6,#0
	moveml	%a0-%a1,%sp@-
	movel	_C_LABEL(IWMBase),%a0
	movel	_C_LABEL(Via1Base),%a1

	movel	%a6@(8),%d0		| Get drive #
	bne	mtDrv1
	tstb	%a0@(intDrive)
	bra	mtSwitch
mtDrv1:
	tstb	%a0@(extDrive)
mtSwitch:
	movel	#motorOnCmd,%d0		| Motor ON
	tstl	%a6@(12)
	bne	mtON
	movel	#motorOffCmd,%d0
mtON:	
	bsr	driveCmd	

	moveml	%sp@+,%a0-%a1	
	unlk	%a6
	rts


/*
 * iwmSelectSide -- select side 0 (lower head) / side 1 (upper head).
 *
 * This MUST be called immediately before an actual read/write access.
 *
 * Parameters:	stack	l	side bit (0/1)
 * Returns:	-
 */
ENTRY(iwmSelectSide)
	link	%a6,#0
	moveml	%d1/%a0-%a1,%sp@-
	movel	_C_LABEL(IWMBase),%a0
	movel	_C_LABEL(Via1Base),%a1

	moveq	#0x0B,%d0		| Drive ready for reading?
	bsr	selDriveReg		| (undocumented)
ss01:	
	bsr	dstatus
	bmi	ss01

	moveq	#rdDataFrom0,%d0	| Lower head
	movel	%a6@(0x08),%d1		| Get side #
	beq	ssSide0
	moveq	#rdDataFrom1,%d0	| Upper head
ssSide0:
	bsr	driveStat

	moveml	%sp@+,%d1/%a0-%a1	
	unlk	%a6
	rts


/*
 * iwmTrack00 -- move head to track 00 for drive calibration.
 *
 * XXX Drive makes funny noises during restore. Tune delay/retry count?
 *
 * Parameters:	-
 * Returns:	%d0		result code
 */
ENTRY(iwmTrack00)
	link	%a6,#0
	moveml	%d1-%d4/%a0-%a1,%sp@-
	movel	_C_LABEL(IWMBase),%a0
	movel	_C_LABEL(Via1Base),%a1

	moveq	#motorOnCmd,%d0		| Switch drive motor on
	bsr	driveCmd

	moveq	#stepOutCmd,%d0		| Step out
	bsr	driveCmd

	movew	#100,%d2		| Max. tries
t0Retry:	
	moveq	#atTrack00,%d0		| Already at track 0?
	bsr	driveStat
	bpl	isTrack00		| Track 0 => Bit 7 = 0
	
	moveq	#doStepCmd,%d0		| otherwise step
	bsr	driveCmd
	movew	#80,%d4			| Retries
t0Still:
	moveq	#stillStepping,%d0	| Drive is still stepping?
	bsr	driveStat
	dbmi	%d4,t0Still

	cmpiw	#-1,%d4
	bne	t002
	
	moveq	#cantStepErr,%d0	| Not ready after many retries
	bra	t0Done
t002:

#if USE_DELAY
	movel	#15000,%sp@-
	jsr	_C_LABEL(delay)		| in mac68k/clock.c
	addqw	#4,%sp 
#else
	movew	#15,%d0
	bsr	iwmDelay
#endif
	
	dbra	%d2,t0Retry
	
	moveq	#tk0BadErr,%d0		| Can't find track 00!!
	bra	t0Done
	
isTrack00:
	moveq	#0,%d0
t0Done:
	moveml	%sp@+,%d1-%d4/%a0-%a1
	unlk	%a6
	rts

	
/*
 * iwmSeek -- do specified # of steps (positive - in, negative - out).
 *
 * Parameters:	stack	l	# of steps
 * returns:	%d0		result code
 */
ENTRY(iwmSeek)
	link	%a6,#0
	moveml	%d1-%d4/%a0-%a1,%sp@-
	movel	_C_LABEL(IWMBase),%a0
	movel	_C_LABEL(Via1Base),%a1

	moveq	#motorOnCmd,%d0		| Switch drive motor on
	bsr	driveCmd

	moveq	#stepInCmd,%d0		| Set step IN
	movel	%a6@(8),%d2		| Get # of steps from stack
	beq	stDone			| 0 steps? Nothing to do.
	bpl	stepOut
	
	moveq	#stepOutCmd,%d0		| Set step OUT
	negl	%d2			| Make # of steps positive
stepOut:
	subql	#1,%d2			| Loop exits for -1
	bsr	driveCmd		| Set direction
stLoop:
	moveq	#doStepCmd,%d0
	bsr	driveCmd		| Step one!
	movew	#80,%d4			| Retries
st01:
	moveq	#stillStepping, %d0	| Drive is still stepping?
	bsr	driveStat
	dbmi	%d4,st01

	cmpiw	#-1,%d4
	bne	st02
	
	moveq	#cantStepErr,%d2	| Not ready after many retries
	bra	stDone
st02:

#if USE_DELAY
	movel	#30,%sp@-
	jsr	_C_LABEL(delay)		| in mac68k/clock.c
	addqw	#4,%sp 
#else
	movew	_C_LABEL(TimeDBRA),%d4	| dbra loops per ms
	lsrw	#5,%d4			| DIV 32
st03:	dbra	%d4,st03		| makes ca. 30 us
#endif

	dbra	%d2,stLoop

	moveq	#0,%d2			| All is well
stDone:
	movel	%d2,%d0
	moveml	%sp@+,%d1-%d4/%a0-%a1	
	unlk	%a6
	rts


/*
 * iwmReadSector -- read and decode the next available sector.
 *
 * TODO:	Poll SCC as long as interrupts are disabled (see top comment)
 *		Add a branch for Verify (compare to buffer)
 *		Understand and document the checksum algorithm!
 *
 *		XXX make "sizeof cylCache_t" a symbolic constant
 *
 * Parameters:	%fp+08	l	Address of sector data buffer (512 bytes)
 *		%fp+12	l	Address of sector header struct (I/O)
 *		%fp+16	l	Address of cache buffer ptr array
 * Returns:	%d0		result code
 * Local:	%fp-2	w	CPU status register
 *		%fp-3	b	side,
 *		%fp-4	b	track,
 *		%fp-5	b	sector wanted
 *		%fp-6	b	retry count
 *		%fp-7	b	sector read
 */
ENTRY(iwmReadSector)
	link	%a6,#-8
	moveml	%d1-%d7/%a0-%a5,%sp@-

 	movel	_C_LABEL(Via1Base),%a1
	movel	%a6@(o_hdr),%a4		| Addr of sector header struct

	moveb	%a4@+,%a6@(-3)		| Save side bit,
	moveb	%a4@+,%a6@(-4)		| track#,
	moveb	%a4@,%a6@(-5)		| sector#
	moveb	#2*maxGCRSectors,%a6@(-6) | Max. retry count

	movew	%sr,%a6@(-2)		| Save CPU status register
	oriw	#0x0600,%sr		| Block all interrupts

rsNextSect:	
	movel	%a6@(o_hdr),%a4		| Addr of sector header struct
	bsr	readSectHdr		| Get next available SECTOR header
	bne	rsDone			| Return if error

	/*
	 * Is this the right track & side? If not, return with error
	 */
	movel	%a6@(o_hdr),%a4		| Sector header struct

	moveb	%a4@(o_side),%d1	| Get actual side
	lsrb	#3,%d1			| "Normalize" side bit (to bit 0)
	andb	#1,%d1
	moveb	%a6@(-3),%d2		| Get wanted side
	eorb	%d1,%d2			| Compare side bits
	bne	rsSeekErr		| Should be equal!

	moveb	%a6@(-4),%d1		| Get track# we want
	cmpb	%a4@(o_track),%d1	| Compare to the header we've read
	beq	rsGetSect
	
rsSeekErr:	
	moveq	#seekErr,%d0		| Wrong track or side found
	bra	rsDone	

	/*
	 * Check for sector data lead-in 'D5 AA AD'
	 * Registers:	
	 *	%a0 points to data register of IWM as set up by readSectHdr
	 *	%a2 points to 'diskTo' translation table
	 *	%a4 points to tags buffer
	 */
rsGetSect:
	moveb	%a4@(2),%a6@(-7)	| save sector number
	lea	%a4@(3),%a4		| Beginning of tag buffer
	moveq	#50,%d3			| Max. retries for sector lookup
rsLeadIn:	
	lea	dataLeadIn,%a3		| Sector data lead-in
	moveq	#0x03,%d4		| is 3 bytes long
rsLI1:	
	moveb	%a0@,%d2		| Get next byte
	bpl	rsLI1
	dbra	%d3,rsLI2
	moveq	#noDtaMkErr,%d0		| Can't find a data mark
	bra	rsDone

rsLI2:
	cmpb	%a3@+,%d2
	bne	rsLeadIn		| If ne restart scan
	subqw	#1,%d4
	bne	rsLI1

	/*
	 * We have found the lead-in. Now get the 12 tag bytes.
	 * (We leave %a3 pointing to 'dataLeadOut' for later.)
	 */
rsTagNyb0:
	moveb	%a0@,%d3		| Get a char,
	bpl	rsTagNyb0
	moveb	%a2@(0,%d3),%a4@+	| remap and store it

	moveq	#0,%d5			| Clear checksum registers
	moveq	#0,%d6
	moveq	#0,%d7
	moveq	#10,%d4			| Loop counter
	moveq	#0,%d3			| Data scratch reg

rsTags:	
rsTagNyb1:
	moveb	%a0@,%d3		| Get 2 bit nibbles
	bpl	rsTagNyb1
	moveb	%a2@(0,%d3),%d1		| Remap disk byte
	rolb	#2,%d1
	moveb	%d1,%d2
	andib	#0xC0,%d2		| Get top 2 bits for first byte
rsTagNyb2:	
	moveb	%a0@,%d3		| Get first 6 bit nibble	
	bpl	rsTagNyb2
	orb	%a2@(0,%d3),%d2		| Remap it and complete first byte

	moveb	%d7,%d3			| The X flag bit (a copy of the carry
	addb	%d7,%d3			| flag) is added with the next addx

	rolb	#1,%d7
	eorb	%d7,%d2
	moveb	%d2,%a4@+		| Store tag byte
	addxb	%d2,%d5			| See above
	
	rolb	#2,%d1
	moveb	%d1,%d2
	andib	#0xC0,%d2		| Get top 2 bits for second byte
rsTagNyb3:
	moveb	%a0@,%d3		| Get second 6 bit nibble
	bpl	rsTagNyb3
	orb	%a2@(0,%d3),%d2		| remap it and complete byte
	eorb	%d5,%d2
	moveb	%d2,%a4@+		| Store tag byte
	addxb	%d2,%d6

	rolb	#2,%d1
	andib	#0xC0,%d1		| Get top 2 bits for third byte
rsTagNyb4:
	moveb	%a0@,%d3		| Get third 6 bit nibble
	bpl	rsTagNyb4
	orb	%a2@(0,%d3),%d1		| remap it and complete byte
	eorb	%d6,%d1
	moveb	%d1,%a4@+		| Store tag byte
	addxb	%d1,%d7
	
	subqw	#3,%d4			| Update byte counter (four 6&2 encoded
	bpl	rsTags			| disk bytes make three data bytes).

	/*
	 * Jetzt sind wir hier...
	 * ...und Thomas D. hat noch was zu sagen...
	 *
	 * We begin to read in the actual sector data. 
	 * Compare sector # to what we wanted: If it matches, read directly
	 * to buffer, else read to track cache.
	 */
	movew	#0x01FE,%d4		| Loop counter
	moveq	#0,%d1			| Clear %d1
	moveb	%a6@(-7),%d1		| Get sector# we have read
	cmpb	%a6@(-5),%d1		| Compare to the sector# we want
	bne	rsToCache
	movel	%a6@(o_buf),%a4		| Sector data buffer
	bra	rsData
rsToCache:
	movel	%a6@(o_rslots),%a4	| Base address of slot array
	lslw	#3,%d1			| sizeof cylCacheSlot_t is 8 bytes
	movel	#-1,%a4@(o_valid,%d1)
	movel	%a4@(o_secbuf,%d1),%a4	| and get its buffer ptr
rsData:
rsDatNyb1:
	moveb	%a0@,%d3		| Get 2 bit nibbles
	bpl	rsDatNyb1
	moveb	%a2@(0,%d3),%d1		| Remap disk byte
	rolb	#2,%d1
	moveb	%d1,%d2
	andib	#0xC0,%d2		| Get top 2 bits for first byte
rsDatNyb2:	
	moveb	%a0@,%d3		| Get first 6 bit nibble	
	bpl	rsDatNyb2
	orb	%a2@(0,%d3),%d2		| Remap it and complete first byte

	moveb	%d7,%d3			| The X flag bit (a copy of the carry
	addb	%d7,%d3			| flag) is added with the next addx

	rolb	#1,%d7
	eorb	%d7,%d2
	moveb	%d2,%a4@+		| Store data byte
	addxb	%d2,%d5			| See above
	
	rolb	#2,%d1
	moveb	%d1,%d2
	andib	#0xC0,%d2		| Get top 2 bits for second byte
rsDatNyb3:
	moveb	%a0@,%d3		| Get second 6 bit nibble
	bpl	rsDatNyb3
	orb	%a2@(0,%d3),%d2		| Remap it and complete byte
	eorb	%d5,%d2
	moveb	%d2,%a4@+		| Store data byte
	addxb	%d2,%d6
	tstw	%d4
	beq	rsCkSum			| Data read, continue with checksums

	rolb	#2,%d1
	andib	#0xC0,%d1		| Get top 2 bits for third byte
rsDatNyb4:
	moveb	%a0@,%d3		| Get third 6 bit nibble
	bpl	rsDatNyb4
	orb	%a2@(0,%d3),%d1		| Remap it and complete byte
	eorb	%d6,%d1
	moveb	%d1,%a4@+		| Store data byte
	addxb	%d1,%d7
	subqw	#3,%d4			| Update byte counter
	bra	rsData

	/*
	 * Next read checksum bytes
	 * While reading the sector data, three separate checksums are
	 * maintained in %D5/%D6/%D7 for the 1st/2nd/3rd data byte of
	 * each group.
	 */
rsCkSum:
rsCkS1:
	moveb	%a0@,%d3		| Get 2 bit nibbles
	bpl	rsCkS1
	moveb	%a2@(0,%d3),%d1		| Remap disk byte
	bmi	rsBadCkSum		| Fault! (Bad read)
	rolb	#2,%d1
	moveb	%d1,%d2
	andib	#0xC0,%d2		| Get top 2 bits for first byte
rsCkS2:	
	moveb	%a0@,%d3		| Get first 6 bit nibble
	bpl	rsCkS2
	moveb	%a2@(0,%d3),%d3		| and remap it
	bmi	rsBadCkSum		| Fault! ( > 0x3f is bad read)
	orb	%d3,%d2			| Merge 6&2
	cmpb	%d2,%d5			| Compare first checksum to %D5
	bne	rsBadCkSum		| Fault! (Checksum)
	
	rolb	#2,%d1
	moveb	%d1,%d2
	andib	#0xC0,%d2		| Get top 2 bits for second byte
rsCkS3:	
	moveb	%a0@,%d3		| Get second 6 bit nibble
	bpl	rsCkS3
	moveb	%a2@(0,%d3),%d3		| and remap it
	bmi	rsBadCkSum		| Fault! (Bad read)
	orb	%d3,%d2			| Merge 6&2
	cmpb	%d2,%d6			| Compare second checksum to %D6
	bne	rsBadCkSum		| Fault! (Checksum)

	rolb	#2,%d1
	andib	#0xC0,%d1		| Get top 2 bits for second byte
rsCkS4:	
	moveb	%a0@,%d3		| Get third 6 bit nibble
	bpl	rsCkS4
	moveb	%a2@(0,%d3),%d3		| and remap it
	bmi	rsBadCkSum		| Fault! (Bad read)
	orb	%d3,%d1			| Merge 6&2
	cmpb	%d1,%d7			| Compare third checksum to %D7
	beq	rsLdOut			| Fault! (Checksum)
	
rsBadCkSum:
	moveq	#badDCkSum,%d0		| Bad data mark checksum
	bra	rsDone

rsBadDBtSlp:
	moveq	#badDBtSlp,%d0		| One of the data mark bit slip 
	bra	rsDone			| nibbles was incorrect

	/*
	 * We have gotten the checksums allright, now look for the
	 * sector data lead-out 'DE AA'
	 * (We have %a3 still pointing to 'dataLeadOut'; this part of the
	 * table is used for writing to disk, too.)
	 */
rsLdOut:
	moveq	#1,%d4			| Is two bytes long {1,0}
rsLdOut1:	
	moveb	%a0@,%d3		| Get token
	bpl	rsLdOut1
	cmpb	%a3@+,%d3
	bne	rsBadDBtSlp		| Fault!
	dbra	%d4,rsLdOut1
	moveq	#0,%d0			| OK.

	/*
	 * See if we got the sector we wanted. If not, and no error 
	 * occurred, mark buffer valid. Else ignore the sector. 
	 * Then, read on.
	 */
rsDone:
	movel	%a6@(o_hdr),%a4		| Addr of sector header struct
	moveb	%a4@(o_sector),%d1	| Get # of sector we have just read
	cmpb	%a6@(-5),%d1		| Compare to the sector we want
	beq	rsAllDone

	tstb	%d0			| Any error? Simply ignore data
	beq	rsBufValid
	lslw	#3,%d1			| sizeof cylCacheSlot_t is 8 bytes
	movel	%a6@(o_rslots),%a4
	clrl	%a4@(o_valid,%d1)	| Mark buffer content "invalid"
	
rsBufValid:
	subqb	#1,%a6@(-6)		| max. retries
	bne	rsNextSect
					| Sector not found, but
	tstb	%d0			| don't set error code if we 
	bne	rsAllDone		| already have one.
	moveq	#sectNFErr,%d0
rsAllDone:
	movew	%a6@(-2),%sr		| Restore interrupt mask
	moveml	%sp@+,%d1-%d7/%a0-%a5	
	unlk	%a6
	rts	
	
	
/*
 * iwmWriteSector -- encode and write data to the specified sector.
 *
 * TODO:	Poll SCC as long as interrupts are disabled (see top comment)
 *		Understand and document the checksum algorithm!
 *
 *		XXX Use registers more efficiently
 *
 * Parameters:	%fp+8	l	Address of sector header struct (I/O)
 *		%fp+12	l	Address of cache buffer ptr array
 * Returns:	%d0		result code
 *
 * Local:	%fp-2	w	CPU status register
 *		%fp-3	b	side,
 *		%fp-4	b	track,
 *		%fp-5	b	sector wanted
 *		%fp-6	b	retry count
 *		%fp-10	b	current slot
 */
ENTRY(iwmWriteSector)
	link	%a6,#-10
	moveml	%d1-%d7/%a0-%a5,%sp@-

 	movel	_C_LABEL(Via1Base),%a1
	movel	%a6@(o_hdr),%a4		| Addr of sector header struct

	moveb	%a4@+,%a6@(-3)		| Save side bit,
	moveb	%a4@+,%a6@(-4)		| track#,
	moveb	%a4@,%a6@(-5)		| sector#
	moveb	#maxGCRSectors,%a6@(-6)	| Max. retry count

	movew	%sr,%a6@(-2)		| Save CPU status register
	oriw	#0x0600,%sr		| Block all interrupts

wsNextSect:
	movel	%a6@(o_hdr),%a4
	bsr	readSectHdr		| Get next available sector header
	bne	wsAllDone		| Return if error

	/*
	 * Is this the right track & side? If not, return with error
	 */
	movel	%a6@(o_hdr),%a4		| Sector header struct

	moveb	%a4@(o_side),%d1	| Get side#
	lsrb	#3,%d1			| "Normalize" side bit...
	andb	#1,%d1
	moveb	%a6@(-3),%d2		| Get wanted side
	eorb	%d1,%d2			| Compare side bits
	bne	wsSeekErr

	moveb	%a6@(-4),%d1		| Get wanted track# 
	cmpb	%a4@(o_track),%d1	| Compare to the read header
	beq	wsCompSect

wsSeekErr:	
	moveq	#seekErr,%d0		| Wrong track or side
	bra	wsAllDone		
	
	/*
	 * Look up the current sector number in the cache.
	 * If the buffer is dirty ("valid"), write it to disk. If not, 
	 * loop over all the slots and return if all of them are clean.
	 *
	 * Alternatively, we could decrement a "dirty sectors" counter here.
	 */
wsCompSect:	
	moveq	#0,%d1			| Clear register
	moveb	%a4@(o_sector),%d1	| get the # of header read
	lslw	#3,%d1			| sizeof cylCacheSlot_t is 8 bytes
	movel	%a6@(o_wslots),%a4
	tstl	%a4@(o_valid,%d1)	| Sector dirty?
	bne	wsBufDirty
	
	moveq	#maxGCRSectors-1,%d2	| Any dirty sectors left?
wsChkDty:
	movew	%d2,%d1
	lslw	#3,%d1			| sizeof cylCacheSlot_t is 8 bytes
	tstl	%a4@(o_valid,%d1)
	bne	wsNextSect		| Sector dirty?
	dbra	%d2,wsChkDty	

	bra	wsAllDone		| We are through with this track.

	
	/*
	 * Write sync pattern and sector data lead-in 'D5 AA'. The 
	 * missing 'AD' is made up by piping 0x0B through the nibble 
	 * table (toDisk).
	 *
	 * To set up IWM for writing:
	 *
	 * access q6H & write first byte to q7H. 
	 * Then check bit 7 of q6L (status reg) for 'IWM ready' 
	 * and write subsequent bytes to q6H.	
	 *
	 * Registers:	
	 *	%a0	IWM base address (later: data register)
	 *	%a1	Via1Base
	 *	%a2	IWM handshake register
	 *	%a3	data (tags buffer, data buffer)
	 *	%a4	Sync pattern, 'toDisk' translation table
	 */
wsBufDirty:
	movel	_C_LABEL(IWMBase),%a0
	lea	%a4@(0,%d1),%a3
	movel	%a3,%a6@(-10)		| Save ptr to current slot
	tstb	%a0@(q6H)		| Enable writing to disk
	movel	%a6@(o_hdr),%a4		| Sector header struct
	lea	%a4@(o_Tags),%a3	| Point %a3 to tags buffer
	lea	syncPattern,%a4

	moveb	%a4@+,%a0@(q7H)		| Write first sync byte
	lea	%a0@(q6L),%a2		| Point %a2 to handshake register
	lea	%a0@(q6H),%a0		| Point %a0 to IWM data register
	
	moveq	#6,%d0			| Loop counter for sync bytes
	moveq	#0,%d2
	moveq	#0,%d3
	movel	#0x02010009,%d4		| Loop counters for tag/sector data

	/*
	 * Write 5 sync bytes and first byte of sector data lead-in
	 */
wsLeadIn:	
	moveb	%a4@+,%d1		| Get next sync byte
wsLI1:	
	tstb	%a2@			| IWM ready?
	bpl	wsLI1
	moveb	%d1,%a0@		| Write it to disk
	subqw	#1,%d0
	bne	wsLeadIn
	
	moveb	%a4@+,%d1		| Write 2nd byte of sector lead-in
	lea	toDisk,%a4		| Point %a4 to nibble translation table
wsLI2:	
	tstb	%a2@			| IWM ready?
	bpl	wsLI2
	moveb	%d1,%a0@		| Write it to disk

	moveq	#0,%d5			| Clear checksum registers
	moveq	#0,%d6
	moveq	#0,%d7

	moveq	#0x0B,%d1		| 3rd byte of sector data lead-in
					| (Gets translated to 0xAD)
	moveb	%a3@+,%d2		| Get 1st byte from tags buffer
	bra	wsDataEntry

	/*
	 * The following loop reads the content of the tags buffer (12 bytes) 
	 * and the data buffer (512 bytes). 
	 * Each pass reads out three bytes and 
	 * a) splits them 6&2 into three 6 bit nibbles and a fourth byte 
	 *    consisting of the three 2 bit nibbles
	 * b) encodes the nibbles with a table to disk bytes (bit 7 set, no 
	 *    more than two consecutive zero bits) and writes them to disk as
	 *
	 *    00mmnnoo		fragment 2 bit nibbles
	 *    00mmmmmm		6 bit nibble -- first byte
	 *    00nnnnnn		6 bit nibble -- second byte
	 *    00oooooo		6 bit nibble -- third byte
	 *
	 * c) adds up three 8 bit checksums, one for each of the bytes written.
	 */
wsSD1:
	movel	%a6@(-10),%a3		| Get ptr to current slot
	movel	%a3@(o_secbuf),%a3	| Get start of sector data buffer

wsData:
	addxb	%d2,%d7
	eorb	%d6,%d2
	moveb	%d2,%d3
	lsrw	#6,%d3			| Put 2 bit nibbles into place
wsRDY01:	
	tstb	%a2@			| IWM ready?
	bpl	wsRDY01
	moveb	%a4@(0,%d3),%a0@	| Translate nibble and write
	subqw	#3,%d4			| Update counter
	moveb	%d7,%d3
	addb	%d7,%d3			| Set X flag (Why?)
	rolb	#1,%d7
	andib	#0x3F,%d0
wsRDY02:
	tstb	%a2@			| IWM ready?
	bpl	wsRDY02
	moveb	%a4@(0,%d0),%a0@	| Translate nibble and write

	/*
	 * We enter with the last byte of the sector data lead-in
	 * between our teeth (%D1, that is).
	 */
wsDataEntry:	
	moveb	%a3@+,%d0		| Get first byte
	addxb	%d0,%d5
	eorb	%d7,%d0
	moveb	%d0,%d3			| Keep top two bits
	rolw	#2,%d3			| by shifting them to MSByte
	andib	#0x3F,%d1
wsRDY03:
	tstb	%a2@			| IWM ready?
	bpl	wsRDY03
	moveb	%a4@(0,%d1),%a0@	| Translate nibble and write

	moveb	%a3@+,%d1			| Get second byte
	addxb	%d1,%d6
	eorb	%d5,%d1
	moveb	%d1,%d3			| Keep top two bits
	rolw	#2,%d3			| by shifting them to MSByte
	andib	#0x3F,%d2
wsRDY04:
	tstb	%a2@			| IWM ready?
	bpl	wsRDY04
	moveb	%a4@(0,%d2),%a0@	| Translate nibble and write

	/*
	 * XXX We have a classic off-by-one error here: the last access
	 * reaches beyond the data buffer which bombs with memory
	 * protection. The value read isn't used anyway...
	 * Hopefully there is enough time for an additional check
	 * (exit the last loop cycle before the buffer access).
	 */
	tstl	%d4			| Last loop cycle?
	beq	wsSDDone		| Then get out while we can.
	
	moveb	%a3@+,%d2		| Get third byte
	tstw	%d4			| First write tag buffer,...
	bne	wsData
	
	swap	%d4			| ...then write data buffer
	bne	wsSD1
		
	/*
	 * Write nibbles for last 2 bytes, then
	 * split checksum bytes in 6&2 and write them to disk
	 */
wsSDDone:
	clrb	%d3			| No 513th byte
	lsrw	#6,%d3			| Set up 2 bit nibbles
wsRDY05:
	tstb	%a2@			| IWM ready?
	bpl	wsRDY05
	moveb	%a4@(0,%d3),%a0@	| Write fragments
	moveb	%d5,%d3
	rolw	#2,%d3
	moveb	%d6,%d3
	rolw	#2,%d3
	andib	#0x3F,%d0
wsRDY06:
	tstb	%a2@			| IWM ready?
	bpl	wsRDY06
	moveb	%a4@(0,%d0),%a0@	| Write 511th byte
	andib	#0x3F,%d1
wsRDY07:	
	tstb	%a2@			| IWM ready?
	bpl	wsRDY07
	moveb	%a4@(0,%d1),%a0@	| write 512th byte
	moveb	%d7,%d3
	lsrw	#6,%d3			| Get fragments ready
wsRDY08:	
	tstb	%a2@			| IWM ready?
	bpl	wsRDY08
	moveb	%a4@(0,%d3),%a0@	| Write fragments
	andib	#0x3F,%d5
wsRDY09:
	tstb	%a2@			| IWM ready?
	bpl	wsRDY09
	moveb	%a4@(0,%d5),%a0@	| Write first checksum byte
	andib	#0x3F,%D6
wsRDY10:
	tstb	%a2@			| IWM ready?
	bpl	wsRDY10
	moveb	%a4@(0,%d6),%a0@	| Write second checksum byte
	andib	#0x3F,%d7
wsRDY11:
	tstb	%a2@			| IWM ready?
	bpl	wsRDY11
	moveb	%a4@(0,%d7),%a0@	| Write third checksum byte

	/*
	 * Write sector data lead-out
	 */
	lea	dataLeadOut,%a4		| Sector data lead-out
	moveq	#3,%d2			| Four bytes long {3,2,1,0}
wsLeadOut:
	moveb	%a2@,%d1		| IWM ready?
	bpl	wsLeadOut
	moveb	%a4@+,%a0@		| Write lead-out
	dbf	%d2,wsLeadOut

	moveq	#0,%d0
	btst	#6,%d1			| Check IWM underrun bit
	bne	wsNoErr

	moveq	#wrUnderRun,%d0		| Could not write
					| fast enough to keep up with IWM
wsNoErr:	
	tstb	%a0@(0x0200)		| q7L -- Write OFF

wsDone:
	tstb	%d0			| Any error? Simply retry
	bne	wsBufInvalid
	
	movel	%a6@(-10),%a4		| Else, get ptr to current slot 
	clrl	%a4@(o_valid)		| Mark current buffer "clean"
	bra	wsNextSect
	
wsBufInvalid:
	subqb	#1,%a6@(-6)		| retries
	bne	wsNextSect
					| Sector not found, but
	tstb	%d0			| don't set error code if we 
	bne	wsAllDone		| already have one.
	moveq	#sectNFErr,%d0
	
wsAllDone:
	movew	%a6@(-2),%sr		| Restore interrupt mask
	moveml	%sp@+,%d1-%d7/%a0-%a5	
	unlk	%a6
	rts

	

/**
 **	Local functions
 **/

/*
 * iwmDelay
 *
 * In-kernel calls to delay() in mac68k/clock.c bomb
 *
 * Parameters:	%d0	delay in milliseconds 
 * Trashes:	%d0, %d1
 * Returns:	-		
 */
iwmDelay:
	/* TimeDBRA is ~8K for 040/33 machines, so we need nested loops */
id00:	movew	_C_LABEL(TimeDBRA),%d1	| dbra loops per ms
id01:	dbra	%d1,id01		|
	dbra	%d0,id00
	rts


/*
 * selDriveReg -- Select drive status/control register
 *
 * Parameters:	%d0	register #
 *			(bit 0 - CA2, bit 1 - SEL, bit 2 - CA0, bit 3 - CA1)
 *		%a0	IWM base address
 *		%a1	VIA base address
 * Returns:	%d0	register # (unchanged)
 */
selDriveReg:	
	tstb	%a0@(ph0H)		| default CA0 to 1 (says IM III)
	tstb	%a0@(ph1H)		| default CA1 to 1
	
	btst	#0,%d0			| bit 0 set => CA2 on
	beq	se00
	tstb	%a0@(ph2H)
	bra	se01
se00:
	tstb	%a0@(ph2L)

se01:
	btst	#1,%d0			| bit 1 set => SEL on (VIA 1)
	beq	se02
	bset	#vHeadSel,%a1@(vBufA)
	bra	se03
se02:
	bclr	#vHeadSel,%a1@(vBufA)

se03:
	btst	#2,%d0			| bit 2 set => CA0 on
	bne	se04
	tstb	%a0@(ph0L)
	
se04:
	btst	#3,%d0			| bit 3 set => CA1 on
	bne	se05
	tstb	%a0@(ph1L)
se05:
	rts

	

/*
 * dstatus -- check drive status (bit 7 - N flag) wrt. a previously
 * set status tag.
 *
 * Parameters:	%d0	register selector
 *		%a0	IWM base address
 * Returns:	%d0	status
 */
dstatus:
	tstb	%a0@(q6H)
	moveb	%a0@(q7L),%d0
	tstb	%a0@(q6L)		| leave in "read data reg" 
	tstb	%d0			| state for safety
	rts


/*
 * driveStat -- query drive status.
 *
 * Parameters:	%a0	IWMBase
 *		%a1	VIABase
 *		%d0	register selector
 * Returns:	%d0	status (Bit 7)
 */
driveStat:
	tstb	%a0@(mtrOn)		| ENABLE; turn drive on	
	bsr	selDriveReg
	bsr	dstatus
	rts

	
/*
 * dtrigger -- toggle LSTRB line to give drive a strobe signal
 * IM III says pulse length = 1 us < t < 1 ms
 *
 * Parameters:	%a0	IWMBase
 *		%a1	VIABase
 * Returns:	-
 */
dtrigger:
	tstb	%a0@(ph3H)		| LSTRB high
	moveb	%a1@(vBufA),%a1@(vBufA)	| intelligent nop seen in q700 ROM
	tstb	%a0@(ph3L)		| LSTRB low
	rts

	
/*
 * driveCmd -- send command to drive.
 *
 * Parameters:	%a0	IWMBase
 *		%a1	VIABase
 *		%d0	Command token
 * Returns:	-
 */
driveCmd:
	bsr	selDriveReg
	bsr	dtrigger
	rts

	
/*
 * readSectHdr -- read and decode the next available sector header.
 *
 * TODO:	Poll SCC as long as interrupts are disabled.
 *
 * Parameters:	%a4	sectorHdr_t address
 * Returns:	%d0	result code
 * Uses:	%d0-%d4, %a0, %a2-%a4
 */
readSectHdr:	
	moveq	#3,%d4			| Read 3 chars from IWM for sync
	movew	#1800,%d3		| Retries to sync to disk
	moveq	#0,%d2			| Clear scratch regs
	moveq	#0,%d1
	moveq	#0,%d0
 	movel	_C_LABEL(IWMBase),%a0	| IWM base address

	tstb	%a0@(q7L)
	lea	%a0@(q6L),%a0		| IWM data register
shReadSy:
	moveb	%a0@,%d2		| Read char
	dbra	%d3,shSeekSync
	
	moveq	#noNybErr,%d0		| Disk is blank?
	bra	shDone

shSeekSync:
	bpl	shReadSy		| No char at IWM, repeat read
	subqw	#1,%d4
	bne	shReadSy
	/*
	 * When we get here, the IWM should be in sync with the data
	 * stream from disk.
	 * Next look for sector header lead-in 'D5 AA 96'
	 */
	movew	#1500,%d3		| Retries to seek header
shLeadIn:	
	lea	hdrLeadIn,%a3		| Sector header lead-in bytes
	moveq	#0x03,%d4		| is 3 bytes long
shLI1:	
	moveb	%a0@,%d2		| Get next byte
	bpl	shLI1			| No char at IWM, repeat read
	dbra	%d3,shLI2
	moveq	#noAdrMkErr,%d0		| Can't find an address mark
	bra	shDone

shLI2:
	cmpb	%a3@+,%d2
	bne	shLeadIn		| If ne restart scan
	subqw	#1,%d4
	bne	shLI1
	/*
	 * We have found the lead-in. Now get the header information.
	 * Reg %d4 holds the checksum.
	 */
	lea	diskTo-0x90,%a2		| Translate disk bytes -> 6&2
shHdr1:	
	moveb	%a0@,%d0		| Get 1st char
	bpl	shHdr1
	moveb	%a2@(0,%d0),%d1		| and remap it
	moveb	%d1,%d4
	rorw	#6,%d1			| separate 2:6, drop hi bits
shHdr2:	
	moveb	%a0@,%d0		| Get 2nd char
	bpl	shHdr2
	moveb	%a2@(0,%d0),%d2		| and remap it
	eorb	%d2,%d4
shHdr3:
	moveb	%a0@,%d0		| Get 3rd char
	bpl	shHdr3
	moveb	%a2@(0,%d0),%d1		| and remap it
	eorb	%d1,%d4
	rolw	#6,%d1			|
shHdr4:
	moveb	%a0@,%d0		| Get 4th char
	bpl	shHdr4
	moveb	%a2@(0,%d0),%d3		| and remap it
	eorb	%d3,%d4
shHdr5:
	moveb	%a0@,%d0		| Get checksum byte
	bpl	shHdr5
	moveb	%a2@(0,%d0),%d5		| and remap it
	eorb	%d5,%d4
	bne	shCsErr			| Checksum ok?
	/*
	 * We now have in
	 * %d1/lsb	track number
	 * %d1/msb	bit 3 is side bit
	 * %d2		sector number
	 * %d3		???
	 * %d5		checksum (=0)
	 *
	 * Next check for lead-out.
	 */
	moveq	#1,%d4			| is 2 bytes long
shHdr6:	
	moveb	%a0@,%d0		| Get token
	bpl	shHdr6
	cmpb	%a3@+,%d0		| Check
	bne	shLOErr			| Fault!
	dbra	%d4,shHdr6
	movew	%d1,%d0			| Isolate side bit
	lsrw	#8,%d0
	moveb	%d0,%a4@+		| and store it
	moveb	%d1,%a4@+		| Store track number
	moveb	%d2,%a4@+		| and sector number
	moveq	#0,%d0			| All is well
	bra	shDone

shCsErr:
	moveq	#badCkSmErr,%d0		| Bad sector header checksum
	bra	shDone
shLOErr:
	moveq	#badBtSlpErr,%d0	| Bad address mark (no lead-out)
	
shDone:
	tstl	%d0			| Set flags
	rts