-
Notifications
You must be signed in to change notification settings - Fork 107
/
Copy pathpdp11_td.c
1582 lines (1371 loc) · 69.4 KB
/
pdp11_td.c
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
/* pdp11_td.c: TU58 simulator
Copyright (c) 2015, Mark Pizzolato
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Mark Pizzolato shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Mark Pizzolato.
td TU58 DECtape
26-Jun-15 MP Initial Unibus/Qbus implemention merged from
vax730_stddev.c (done by Matt Burke) and pdp11_dl.c
Added support for multiple concurrent TU58 devices
This module implements the TU58 functionality for the
VAX730 and VAX750 console devices as well as
Unibus/Qbus connected dual drive TU58s.
PDP-11 TU58 DECtapes are represented in memory by fixed length buffer of 32b words.
16b 256 words per block [256 x 16b]
Extracted from the TU58 DECtape II User's Guide - Programming Chapter:
3.1 GENERAL PRINCIPLES
The TU58 is controlled by a microprocessor that frees the host computer from device-related operations,
such as tape positioning and error retry. Only one high-level command to the microprocessor is necessary to
initiate a complex operation. The host and ru58 communicate via strings of one or more bytes called
packets. One brief packet can contain a message which completely describes a high-level command. The
handshaking sequences between host and TU58 as well as packet format are defined by the radial serial
protocol (RSP), or the modified radial serial protocol (MRSP), and were designed to be suitable for transmission
by asynchronous interfaces.
3.1.1 Block Number, Byte Count, and Drive Number
The TU58 uses a drive number, block number, and byte count to write or read data. 1-4 (Chapter 1)
shows the locations of blocks on the tape. If all of the desired data is contained within a single 512-byte
block, the byte count will be 512 or less. When the host asks for a particular block and a 512-or-Iess byte
count, the TU58 positions the specified drive (unit) at that block and transfers the number of bytes specified.
If the host asks for a block and also a byte count greater than that of the 512-byte boundary, the
TU58 reads as many sequential blocks as are needed to fulfill the byte count. The same process applies to
the write function. This means that the host software or an on-tape file directory need only store the number
of the first block in a file and the file's byte count to read or write all the data without having to know
the additional block numbers.
3.1.2 Special Handler Functions
Some device-related functions are not dealt with directly in the RSP, the MRSP, or in the ru58 firmware.
1. A short routine called Newtape (Appendix B) should be included in a TU58 handler to provide a
complete wind-rewind for new or environmentally stressed tape cartridges. This procedure
brings the tape to proper operating tension levels.
2. A TU58 handler should check the success code (byte 3 of the RSP or MRSP end message) for
the presence of soft errors. This enables action to be taken before hard errors (permanent data
losses) occur.
3.2 RADIAL SERIAL PROTOCOL (RSP) AND MODIFIED RSP (MRSP)
3.2.1 Packets
All communication between the host and the TU58 is accomplished via sequences of bytes called packets.
There are two types of multi-byte packets: Control (Command) and Data. Either RSP or MRSP may be
selected using the command packet switch byte. In addition, there are three single-byte packets used to
manage protocol and control the state of the system: INIT, Continue, and XOFF.
Control (Command) - A Control packet is sent to the TU58 to initiate all operations. The packet
contains a message completely describing the operation to be performed. In the case of a read or
write operation, for example, the message includes the function to be performed, unit (drive) number,
byte count and block number.
A special case of the Control packet, called an End packet, is sent from the TU58 to the host after
completion of an operation or on an error. The End packet includes the status of the completed or
aborted operation.
Data - The Data packet holds messages of between 1 and 128 bytes. This message is actually the
data transferred from or to the TU58 during a read or write operation. For transmissions of larger
than 128 bytes, the transfer is broken up and sent 128 bytes at a time.
INIT - This single-byte packet is sent to the TU58 to cause the power-up sequence. The TU58
returns Continue after completion, to indicate that the power-up sequence has occurred. When the
TU5S makes a protocol error or receives an invalid command, it reinitializes and sends INIT continuously
to the host. When the host recognizes INIT, it sends Break to the TU58 to restore the
protocol.
Bootstrap - A flag byte saying Bootstrap (octal 10), followed by a byte containing a drive number,
causes the TU58 to read block 0 of the selected drive. It returns the 512 bytes without radial serial
packaging. This simplifies bootstrap operations. Bootstrap may be sent by the host instead of a
second INIT as part of the initialization process described below.
Continue - Before the host sends a Data packet to the TU58, it must wait until the TUS8 sends
Continue. This permits the TU58 to control the rate that data packets are sent to it.
XON - An alternate term for Continue.
XOFF - Ordinarily, the TU58 does not have to wait between messages to the host. However, if the
host is unable to receive all of a message from the peripheral at once, it may send XOFF. The
TU58 stops transmitting immediately and waits until the host sends Continue to complete the
transfer when it is ready. (Two characters may be sent by the UART to the host after the TUS8
receives XOFF.)
3.2.1.1 Packet Usage - Position within the packet determines the meaning of each byte. All packets
begin with a flag byte, which announces the type of packet to follow. Flag byte numeric assignments
are as follows.
Packet Type Flag Byte Value
Octal Binary
Data 01 00001
Control (Command) 02 00010
INIT 04 00100
Bootstrap 10 01000
Continue 20 10000
XON 21 10001
XOFF 23 10011
(Bits 5 - 7 of the nag byte are reserved.)
Multiple-byte (Control and Data) packets also contain a byte count byte, message bytes, and two checksum
bytes. The byte count byte is the number of message bytes in the packet. The two checksum bytes
are a 16-bit checksum. The checksum is formed by summing successive byte-pairs taken as 16-bit words
while adding any carry back into the sum (end-around carry), The flag and byte count bytes are included
in the checksum. (See example in Appendix 8.)
3.2.1 Break and Initialization
Break is a unique logic entity that can be interpreted by the TU58 and the host regardless of the state
of the protocol. This is the logical equivalent of a bus init or a master reset. Break is transmitted when
the serial line, which normally switches between two logic states called mark and space, is kept in the
space condition for at least one character time. This causes the TU58's UART to set its framing error
bit. The TU58 interprets the framing error as Break.
If communications breakdown, due to any transient problem, the host may restore order by sending
Break and IN IT as outlined above. The faulty operations are cancelled, and the TU58 reinitializes itself,
returns Continue, and waits for instructions.
With DIGITAL serial interfaces, the initialize sequence may be sent by the following sequence of operations.
Set the Break bit in the transmit control status register, then send two null characters. When the
transmit ready flag is set again, remove the Break bit. This times Break to be one character time long.
The second character is discarded by the TU58controller. Next, send two INIT characters. The first is
discarded by the TU58. The TU58 responds to the second INIT by sending Continue. When Continue
has been received, the initialize sequence is complete and any command packet may follow.
3.2.3 Command Packets
The command packet format is shown in Table 3-1. Bytes 0, 1, 12, and 13 are the message delivery
bytes. Their definitions follow.
Table 3-1 Command Packet Structure
Byte Byte Contents
o Flag = 0000 0010(028)
1 Message Byte Count = 0000 101 O( 128)
2 Op Code
3 Modifier
4 Unit Number
5 Switches
6 Sequence Number - Low
7 Sequence Number - High
8 Byte Count - Low
9 Byte Count - High
10 Block Number - Low
11 Block Number - High
12 Checksum - Low
13 Checksum - High
0 Flag This byte is set to 00000010 to indicate
that the packet is a Command packet.
1 Message Byte Count Number of bytes in the packet, excluding the four message delivery
bytes. This is decimal 10 for all command packets.
12, 13 Checksum The 16-bit checksum of bytes 0 through 11. The checksum is
formed by treating each pair of bytes as a word and summing
words with end-around carry.
The remaining bytes are defined below.
2 Op Code Operation being commanded. (See Table 34 and Paragraph 3.3
for definitions.)
3 Modifier Permits variations of commands.
4 Unit Number Selects drive 0 or I.
5 Switches Selects maintenance mode and specifies RSP or MRSP.
6,7 Sequence Number Always zero for TU58.
8,9 Byte Count Number of bytes to be transferred by a read or write command.
Ignored by other commands.
10,11 Block Numbet The block number to be used by commands requiring tape positioning.
3.1.3.1 Maintenance Mode - Setting bit 4 of the switches byte (byte 5) to I in a read command inhibits
retries on data errors. Instead, the incorrect data is delivered to the host followed by an end packet.
The success code in the end packet indicates a hard dt~.ta error. Since data is transmitted in 128-byte
packets, a multiple packet read progresses normally until a checksum mismatch occurs. Then the bad
data packet is transmitted, followed by the end packet, and the operation terminates.
3.1.3.1 Special Address Mode - Setting the most significant bit of the modifier byte (byte 3) to 1
selects special address mode. In this mode all tape positioning operations are addressed by 128-byte
records (0-2047) instead of 512-byte blocks (0-511). Zero-fill in a write operation only fills out to a 128-
byte boundary in this mode. To translate between normal addressing and special addressing, multiply
the normal address by 4. The result is the address of the first I 28-byte record of the block. Add I, 2, or
3 to get to the next three 128-byte records.
3.1.4 Data Packets
3.1.4.1 Radial Serial Protocol-A data transfer operation uses three or more message packets. The first
packet is the command packet from host to the TU58. Next, the data is transferred in 128-byte packets in
either direction (as required by read or write). After all data is transferred, the TU58 sends an end packet.
If the TUS8 encounters a failure before all data has been transferred, it sends the end packet as soon as the
failure occurs.
The data packet is shown in Table 3-2. The flag byte is set to 0018. The number of data bytes may be
between 1 and 128 bytes. For data transfers larger than 128 bytes, the transaction is broken up and sent
128 bytes at a time. The host is assumed to have enough buffer capacity to accept the entire transaction,
whereas the TU58 only has 128 bytes of buffer space. For write commands, the host must wait between
message packets for the TU58 to send the Continue flag 0208 before sending the next packet. Because the
host has enough buffer space, the TU58 does not wait for a Continue flag between message packets when it
sends back read data.
3.1.4.2 Modified Radial Serial Protocol- When the host does not have sufficient buffer space to accept
entire transactions at the hardware selected data transfer rate, modified radial serial protocol (MRSP) may
be specified using the command packet switch byte. Bit 3 of the switch byte is set to specify the MRSP. Bit
3 remains set until intentionally cleared or cleared during power up. A good practice is to set bit 3 in every
MRSP command packet.
MRSP is identical to RSP except during transmission to the host. When a command packet specifies
MRSP for the first time (that is, bit 3 of the switch byte was previously cleared or cleared during power
up), the ru58 will send one data or end packet byte (whichever occurs first). The subsequent bytes, up to
and including the last byte of the end packet, will not be transmitted until a Continue or an XON is
received from the host. To prevent a protocol error from occurring, it is necessary to transmit Continue or .
XON before transmitting any command packets. If a protocol error is detected, continuous INITs are sent
with the Continue handshake. If a bootstrap is being transmitted, however, no handshake is employed.
3.2.5 End Packets
The end packet is sent to the host by the ru58 after completion or termination of an operation or an error.
End packets are sent using RSP or MRSP as specified by the last command packet. The end packet is
shown in Table 3-3.
Table 3-1 Data Packets
Byte Byte Contents
0 Flag = 0000 0001
1 Byte Count = M
-----------------
2 First Data Byte
3 Data
M Data
M+1 Last Data Byte
-----------------
M+2 Checksum L
M+3 Checksum H
Table 3-3 End Packet
Byte Byte Contents
0 Flag = 0000 0010
1 Byte Count = 0000 1010
-----------------
2 Op Code - 0100 0000
3 Success Code
4 Unit
5 Not Used
6 Sequence No. L
7 Sequence No. H
8 Actual Byte Count L
9 Actual Byte Count H
10 Summary Status L
11 Summary Status H
-----------------
12 Checksum L
13 Checksum H
The definition of bytes 0, 1, 12, and 13 are the same as for the command packet. The remaining bytes
are defined as follows.
Byte 2 Op Code - 0100 0000 for end packet
Byte 3 Success Code
Octal Decimal
0 0 = Normal success
1 1 = Success but with retries
377 -1 = Failed self test
376 -2 = Partial operation (end of medium)
370 -8 = Bad unit number
367 -9 = No cartridge
365 -11 = Write protected
357 -17 = Data check error
340 -32 = Seek error (block not found)
337 -33 = Motor stopped
320 -48 = Bad opcode
311 -55 = Bad block number (> 511)
Byte 4 Unit Number 0 or 1 for drive number.
Byte 5 Always 0
Bytes 6,7 Sequence number - always 0 as in command packet.
Bytes 8,9 Actual byte count - number of bytes handled in transaction. In a <good operation,
this is the same as the data byte count in the command packet.
Bytes 10,11 Summary Status
Byte 10
Bit 0 Reserved
...
Bit 7 Reserved
Byte 11
Bit 0 Reserved
Bit 1 Reserved
Bit 2 Reserved
Bit 3 Reserved
Bit 4 Logic error
Bit 5 Motion error
Bit 6 Transfer error
Bit 7 Special condition (errors)
3.3 INSTRUCTION SET
The operation performed by the TU58 when it receives a Control (command) packet is determined by the
op code byte in the control packet message. Note that while any command can specify modified radial
serial protocol with the switch byte, the response will not be MRSP if a boot operation is being performed.
Instruction set op code byte assignments are listed in Table 3-4.
To allow for future development, certain op codes in the command set have been reserved. These commands
have unpredictable results and should not be used. Op codes not listed in the command set are illegal
and result in the return of an end packet with the "bad op code" success code.
Table 3-4 Instruction Set
OpCode OpCode
Decimal Octal Instuction Set
-----------------
0 0 NOP
1 1 INIT
2 2 Read
3 3 Write
4 4 (Reserved)
5 5 Position
6 6 (Reserved)
7 7 Diagnose
8 10 Gctstatus
9 11 Set status
10 12 (Reserved)
11 13 (Reserved)
The following is a brief description and usage example of each.
OP CODE`O NOP
This instruction causes the TU58 to return an end packet. There are no modifiers to NOP. The NOP
packet is shown below.
BYTE
0 0000 0010 FLAG
1 0000 1010 MESSAGE BYTE CNT
2 0000 0000 OPCODE
3 0000 0000 MODIFIER
4 0000 OOOX UNIT NUMBER (IGNORED)
5 0000 0000 SWITCHES (NOT USED)
6 0000 0000 SEQ NO. (NOT USED)
7 0000 0000 SEQ NO. (NOT USED)
8 0000 0000 BYTE COUNT L NO DATA
9 0000 0000 BYTE COUNT H INVOLVED
10 0000 0000 BLOCKNO L NO TAPE
11 0000 0000 BLOCKNO H POSITION
12 0000 00IX CHECKSUM L
13 0000 1010 CHECKSUM H
The TUS8 returns the following end packet.
0 0000 0010 FLAG
1 0000 1010 MESSAGE BYTE CNT
2 0100 0000 OPCOPE .
3 0000 0000 SUCCESS CODE
4 0000 OOOX UNIT (IGNORED)
S 0000 0000 NOT USED
6 0000 0000 SEQ L
7 0000 0000 SEQ H ....
8 0000 0000 .. ACTUAL BYTE CNT L NO DATA
9 0000 0000 ACTUAL BYTE CNT H INVOLVED
10 0000 0000 SUMMARY STATUS L
11 XXXX XXXX SUMMARY STATUS H
12 ooox XXXX CHECKSUM L
13 XXXX XXXX CHECKSUM H
OP CODE 1 INIT
This instruction causes the TU58 controller to reset itself to a ready state. No tape positioning results
from this operation. The command 'packet is tbe same as for NOP except for the op code and the resultant
change to the low order checksum byte. The TU58 sends the same end packet as for NOP after
reinitializing itself. There are no modifiers to IN IT.
OP CODE 2 Read, and Read with Decreased Sensitivity
This instruction causes the TU58 to position the tape in the drive selected by Unit Number to the block
designated by the block number bytes. It reads data starting at the designated block and continues
reading until the byte count (command bytes 8 and 9) is satisfied. After data has been sent, the TU58
sends an end packet. Byte 3 indicates success, success with retries, or failure of the operation. In the
event of failure, the end packet is sent at the time of failure without filling up the data count. The end
packet is recognized by the host by the flag byte. The host sees a command flag (0000 0010) instead of
a data flag (0000 0001).
There are two modifiers to the read command. Setting the least significant bit of byte 3 to 1 causes the
TU58 to read the tape with decreased sensitivity in the read amplifier. This makes the read amplifier
miss data if any weak spots are present. Thus, if the TU58 can read error-free in this mode, the data is
healthy. The read transaction between TU58 and host is shown for 510 bytes (just under a full block) in
Figure 3-1. Setting the most significant bit of byte 3 to 1 selects special address mode. See Paragraphs
3.2.3.1 and 3.2.3.2.
OP CODE 3 Write, and Write and Read Verify
This op code causes the TU58 to position the tape in the selected driveto the block specified by the
number in bytes 10,11 of the command packet and write data from the first data packet into that block.
It writes data from subsequent data packets into one or more. blocks until the byte count called out in
bytes 8, 9 of the command packet has been satisfied.
The controller automatically zero-fills any remaining bytes ina 512-byte tape block.
There are two modifiers pennitted with the write command. Setting the least significant bit of byte
3 to 1 causes the TU58 to write all of the data and then back up and read the data just written with
decreased sensitivity and test the checksum of each record. If all of the checksums are correct, the
TU58 sends an end packet with the success code set to 0 (or 1 if retries were necessary to read the
data). Failure to read correct data results in a success code of - 17 (3578 ) to indicate a hard read
error. Setting the most significant bit of byte 3 to 1 selects special address mode. See Paragraph
3.2.3.2. . .
The write operation has to cope with the fact that the TU58 only has 128 bytes of buffer space. It is
necessary for the host to send a data packet and wait for the TU58 to write it before sending the next data
packet. This is accomplished using the continue flag. The continue flag is a single byte response of 000 1
0000 from TU58 to host. The RSP write transaction for both write and write/verify operations is shown in
Figure 3.2. The MRSP write transaction for both write and write/verify operations is shown in Figure 3.3.
OP CODE 4 (Resened)
OP CODE 5 Position
This command causes the TU58 to position tape on the selected drive to the block designated by bytes 10,
11. After reaching the selected block, it sends an end packet. See Paragraph 3.2.3.2.
OP CODE 6 (Reserved)
OP CODE 7 Diagnose
This command causes the TU58 to run its internal diagnostic program which tests the processor, ROM,
and RAM. Upon completion, TU58 sends an end packet with appropriate success code (0 = Pass, -1 =
Fail). Note that if the bootstrap hardware option is selected, boot information will be transmitted without
handshaking even if the switch byte specifies MRSP.
OP CODE 8 Get Status
This command is treated as a NOP. The TU58 returns an end packet.
OP CODE 9 Set Status
This command is treated as a NOP because TU58 status cannot be set from the host. The TU58 returns
an end packet.
OP CODE 10 (Resened)
OP CODE 11 (Resened)
*/
#if defined (VM_VAX) /* VAX version */
#include "vax_defs.h"
#else /* PDP-11 version */
#include "pdp11_defs.h"
#endif
#include "pdp11_td.h"
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
/* DL Definitions */
/* registers */
#define DLICSR_RD (CSR_DONE|CSR_IE) /* DL11C */
#define DLICSR_WR (CSR_IE)
#define DLIBUF_ERR 0100000
#define DLIBUF_OVR 0040000
#define DLIBUF_RBRK 0020000
#define DLIBUF_RD (DLIBUF_ERR|DLIBUF_OVR|DLIBUF_RBRK|0377)
#define DLOCSR_XBR 0000001 /* xmit brk, RWNI */
#define DLOCSR_RD (CSR_DONE|CSR_IE|DLOCSR_XBR)
#define DLOCSR_WR (CSR_IE|DLOCSR_XBR)
static BITFIELD rx_csr_bits[] = {
BITNCF(6), /* unused */
BIT(IE), /* Interrupt Enable */
BIT(DONE), /* Xmit Ready */
BITNCF(8), /* unused */
ENDBITS
};
static BITFIELD rx_buf_bits[] = {
BITF(DAT,8), /* data buffer */
BITNCF(5), /* unused */
BIT(RBRK),
BIT(OVR),
BIT(ERR),
ENDBITS
};
static BITFIELD tx_csr_bits[] = {
BIT(XBR), /* Break */
BITNC, /* unused */
BIT(MAINT), /* Maint */
BITNCF(3), /* unused */
BIT(IE), /* Interrupt Enable */
BIT(DONE), /* Xmit Ready */
BITNCF(8), /* unused */
ENDBITS
};
static BITFIELD tx_buf_bits[] = {
BITF(DAT,8), /* data buffer */
BITNCF(8), /* unused */
ENDBITS
};
static BITFIELD *td_reg_bits[] = {
rx_csr_bits,
rx_buf_bits,
tx_csr_bits,
tx_buf_bits,
};
static const char *tdc_regnam[] =
{
"RX_CSR",
"RX_BUF",
"TX_CSR",
"TX_BUF"
};
/* TU58 definitions */
#define TD_NUMCTLR 16 /* #controllers */
#define TD_NUMBLK 512 /* blocks/tape */
#define TD_NUMBY 512 /* bytes/block */
#define TD_SIZE (TD_NUMBLK * TD_NUMBY) /* bytes/tape */
#define TD_OPDAT 001 /* Data */
#define TD_OPCMD 002 /* Command */
#define TD_OPINI 004 /* INIT */
#define TD_OPBOO 010 /* Bootstrap */
#define TD_OPCNT 020 /* Continue */
#define TD_OPXOF 023 /* XOFF */
#define TD_CMDNOP 0000 /* NOP */
#define TD_CMDINI 0001 /* INIT */
#define TD_CMDRD 0002 /* Read */
#define TD_CMDWR 0003 /* Write */
#define TD_CMDPOS 0005 /* Position */
#define TD_CMDDIA 0007 /* Diagnose */
#define TD_CMDGST 0010 /* Get Status */
#define TD_CMDSST 0011 /* Set Status */
#define TD_CMDMRSP 0012 /* MRSP Request */
#define TD_CMDEND 0100 /* END */
#define TD_STSOK 0000 /* Normal success */
#define TD_STSRTY 0001 /* Success with retries */
#define TD_STSFAIL 0377 /* Failed selftest */
#define TD_STSPO 0376 /* Partial operation (end of medium) */
#define TD_STSBUN 0370 /* Bad unit number */
#define TD_STSNC 0367 /* No cartridge */
#define TD_STSWP 0365 /* Write protected */
#define TD_STSDCE 0357 /* Data check error */
#define TD_STSSE 0340 /* Seek error (block not found) */
#define TD_STSMS 0337 /* Motor stopped */
#define TD_STSBOP 0320 /* Bad opcode */
#define TD_STSBBN 0311 /* Bad block number (>511) */
#define TD_GETOPC 0 /* get opcode state */
#define TD_GETLEN 1 /* get length state */
#define TD_GETDATA 2 /* get data state */
#define TD_IDLE 0 /* idle state */
#define TD_READ 1 /* read */
#define TD_READ1 2 /* fill buffer */
#define TD_READ2 3 /* empty buffer */
#define TD_WRITE 4 /* write */
#define TD_WRITE1 5 /* write */
#define TD_WRITE2 6 /* write */
#define TD_END 7 /* empty buffer */
#define TD_END1 8 /* empty buffer */
#define TD_INIT 9 /* empty buffer */
#define TD_BOOTSTRAP 10 /* bootstrap read */
#define TD_POSITION 11 /* position */
static const char *td_states[] = {
"IDLE", "READ", "READ1", "READ2",
"WRITE", "WRITE1", "WRITE2", "END",
"END1", "INIT", "BOOTSTRAP","POSITION"
};
static const char *td_ops[] = {
"NOP", "INI", "RD", "WR", "004", "POS", "006", "DIA",
"GST", "SST", "MRSP", "013", "014", "015", "016", "017",
"020", "021", "022", "023", "024", "025", "026", "027",
"030", "031", "032", "033", "034", "035", "036", "037",
"040", "041", "042", "043", "044", "045", "046", "047",
"050", "051", "052", "053", "054", "055", "056", "057",
"060", "061", "062", "063", "064", "065", "066", "067",
"070", "071", "072", "073", "074", "075", "076", "077",
"END"
};
static const char *td_csostates[] = {
"GETOPC", "GETLEN", "GETDATA"
};
static int32 td_stime = 100; /* seek, per block */
static int32 td_ctime = 150; /* command time */
static int32 td_xtime = 180; /* tr set time */
static int32 td_itime = 180; /* init time */
static int32 td_ctrls = 1; /* number of enabled controllers */
static uint32 tdi_ireq = 0;
static uint32 tdo_ireq = 0;
struct CTLR {
DEVICE *dptr;
UNIT *uptr;
uint16 rx_csr;
uint16 rx_buf;
void (*rx_set_int) (int32 ctlr_num, t_bool val);
uint16 tx_csr;
uint16 tx_buf;
void (*tx_set_int) (int32 ctlr_num, t_bool val);
uint8 ibuf[TD_NUMBY+1]; /* input buffer */
int32 ibptr; /* input buffer pointer */
int32 ilen; /* input length */
uint8 obuf[TD_NUMBY+1]; /* output buffer */
int32 obptr; /* output buffer pointer */
int32 olen; /* output length */
int32 block; /* current block number */
int32 txsize; /* remaining transfer size */
int32 offset; /* offset into current transfer */
int32 p_state; /* protocol state */
int32 o_state; /* output state */
int32 unitno; /* active unit number */
int32 ecode; /* end packet success code */
};
static CTLR td_ctlr[TD_NUMCTLR+1]; /* one for each DL based TU58 plus console */
static t_stat td_rd (int32 *data, int32 PA, int32 access);
static t_stat td_wr (int32 data, int32 PA, int32 access);
static t_stat td_svc (UNIT *uptr);
static t_stat td_reset (DEVICE *dptr);
static t_stat td_set_ctrls (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat td_show_ctlrs (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat td_boot (int32 unitno, DEVICE *dptr);
static t_stat td_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
static void tdi_set_int (int32 ctlr, t_bool val);
static int32 tdi_iack (void);
static void tdo_set_int (int32 ctlr, t_bool val);
static int32 tdo_iack (void);
static const char *td_description (DEVICE *dptr);
static void td_process_packet(CTLR *ctrl);
static t_bool td_test_xfr (UNIT *uptr, int32 state);
/* TU58 data structures
td_dev TD device descriptor
td_unit TD unit list
td_reg TD register list
td_mod TD modifier list
*/
#define IOLN_DL 010
static DIB td_dib = {
IOBA_AUTO, IOLN_DL, &td_rd, &td_wr,
2, IVCL (TDRX), VEC_AUTO, { &tdi_iack, &tdo_iack }, IOLN_DL,
};
static UNIT td_unit[2*TD_NUMCTLR];
static REG td_reg[] = {
{ DRDATAD (CTRLRS, td_ctrls, 4, "number of controllers"), REG_HRO },
{ DRDATAD (CTIME, td_ctime,24, "command time"), PV_LEFT },
{ DRDATAD (STIME, td_stime,24, "seek, per block"), PV_LEFT },
{ DRDATAD (XTIME, td_xtime,24, "tr set time"), PV_LEFT },
{ DRDATAD (ITIME, td_itime,24, "init time"), PV_LEFT },
#define RDATA(nm,loc,wd,desc) STRDATAD(nm,td_ctlr[0].loc,16,wd,0,TD_NUMCTLR+1,sizeof(CTLR),REG_RO,desc)
#define RDATAF(nm,loc,wd,desc,flds) STRDATADF(nm,td_ctlr[0].loc,16,wd,0,TD_NUMCTLR+1,sizeof(CTLR),REG_RO,desc,flds)
{ RDATA (ECODE, ecode, 32, "end packet success code") },
{ RDATA (BLOCK, block, 32, "current block number") },
{ RDATAF (RX_CSR, rx_csr, 16, "input control/status register", rx_csr_bits) },
{ RDATAF (RX_BUF, rx_buf, 16, "input buffer register", rx_buf_bits) },
{ RDATAF (TX_CSR, tx_csr, 16, "output control/status register", tx_csr_bits) },
{ RDATAF (TX_BUF, tx_buf, 16, "output buffer register", tx_buf_bits) },
{ RDATA (P_STATE,p_state,32, "protocol state") },
{ RDATA (O_STATE,o_state,32, "output state") },
{ RDATA (IBPTR, ibptr, 32, "input buffer pointer") },
{ RDATA (OBPTR, obptr, 32, "output buffer pointer") },
{ RDATA (ILEN, ilen, 32, "input length") },
{ RDATA (OLEN, olen, 32, "output length") },
{ RDATA (TXSIZE, txsize, 32, "remaining transfer size") },
{ RDATA (OFFSET, offset, 32, "offset into current transfer") },
{ RDATA (UNITNO, unitno, 32, "active unit number") },
/*
REG entries for each controller's IBUF and OBUF are dynamically established
on first call to td_reset.
*/
{ NULL }
};
static MTAB td_mod[] = {
{ MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED", &set_writelock, &show_writelock, NULL, "Write enable TU58 drive" },
{ MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED", &set_writelock, NULL, NULL, "Write lock TU58 drive" },
{ MTAB_XTD | MTAB_VDV, 0, "CONTROLLERS", "CONTROLLERS", &td_set_ctrls, &td_show_ctlrs, NULL, "Number of Controllers" },
{ MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, &set_addr, &show_addr, NULL, "Bus address" },
{ MTAB_XTD|MTAB_VDV, 1, "VECTOR", NULL, &set_vec, &show_vec, NULL, "Interrupt vector" },
{ 0 }
};
DEVICE tdc_dev = {
"TDC", td_unit, td_reg, td_mod,
2*TD_NUMCTLR, DEV_RDX, 20, 1, DEV_RDX, 8,
NULL, NULL, &td_reset,
&td_boot, NULL, NULL,
&td_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG, 0,
td_deb, NULL, NULL, &td_help, NULL, NULL,
&td_description
};
#define CSI_CLR_INT ctlr->rx_set_int (ctlr-td_ctlr, 0)
#define CSI_SET_INT ctlr->rx_set_int (ctlr-td_ctlr, 1)
#define CSO_CLR_INT ctlr->tx_set_int (ctlr-td_ctlr, 0)
#define CSO_SET_INT ctlr->tx_set_int (ctlr-td_ctlr, 1)
t_stat td_rd_i_csr (CTLR *ctlr, int32 *data)
{
*data = ctlr->rx_csr & DLICSR_RD;
sim_debug_bits_hdr(TDDEB_IRD, ctlr->dptr, "RX_CSR", rx_csr_bits, *data, *data, 1);
return SCPE_OK;
}
t_stat td_wr_i_csr (CTLR *ctlr, int32 data)
{
if ((data & CSR_IE) == 0)
CSI_CLR_INT;
else {
if ((ctlr->rx_csr & (CSR_DONE | CSR_IE)) == CSR_DONE)
CSI_SET_INT;
}
sim_debug_bits_hdr(TDDEB_IWR, ctlr->dptr, "RX_CSR", rx_csr_bits, ctlr->rx_csr, data, 1);
ctlr->rx_csr = (ctlr->rx_csr & ~DLICSR_WR) | (data & DLICSR_WR);
return SCPE_OK;
}
t_stat td_rd_i_buf (CTLR *ctlr, int32 *data)
{
int32 t = ctlr->rx_buf;
ctlr->rx_csr &= ~CSR_DONE; /* clr done */
ctlr->rx_buf &= BMASK; /* clr errors */
sim_debug_bits_hdr(TDDEB_IRD, ctlr->dptr, "RX_BUF", rx_buf_bits, t, ctlr->rx_buf, 1);
CSI_CLR_INT;
*data = t;
return SCPE_OK;
}
t_stat td_wr_i_buf (CTLR *ctlr, int32 data)
{
sim_debug_bits_hdr(TDDEB_IWR, ctlr->dptr, "RX_BUF", rx_buf_bits, ctlr->rx_buf, ctlr->rx_buf, 1);
return SCPE_OK;
}
t_stat td_rd_o_csr (CTLR *ctlr, int32 *data)
{
sim_debug_bits_hdr(TDDEB_ORD, ctlr->dptr, "TX_CSR", tx_csr_bits, ctlr->tx_csr, ctlr->tx_csr, 1);
*data = ctlr->tx_csr & DLOCSR_RD;
return SCPE_OK;
}
t_stat td_wr_o_csr (CTLR *ctlr, int32 data)
{
sim_debug_bits_hdr(TDDEB_OWR, ctlr->dptr, "TX_CSR", tx_csr_bits, data, data, 1);
if ((ctlr->tx_csr & DLOCSR_XBR) && !(data & DLOCSR_XBR)) {
ctlr->ibptr = 0;
ctlr->ibuf[ctlr->ibptr++] = TD_OPINI;
td_process_packet(ctlr); /* check packet */
}
if ((data & CSR_IE) == 0)
CSO_CLR_INT;
else if ((ctlr->tx_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
CSO_SET_INT;
ctlr->tx_csr = (ctlr->tx_csr & ~DLOCSR_WR) | (data & DLOCSR_WR);
return SCPE_OK;
}
t_stat td_rd_o_buf (CTLR *ctlr, int32 *data)
{
*data = 0;
sim_debug_bits_hdr(TDDEB_ORD, ctlr->dptr, "TX_BUF", tx_buf_bits, *data, *data, 1);
return SCPE_OK;
}
t_stat td_wr_o_buf (CTLR *ctlr, int32 data)
{
sim_debug (TDDEB_OWR, ctlr->dptr, "td_wr_o_buf() %s o_state=%s, ibptr=%d, ilen=%d\n", (ctlr->tx_csr & DLOCSR_XBR) ? "XMT-BRK" : "", td_csostates[ctlr->o_state], ctlr->ibptr, ctlr->ilen);
sim_debug_bits_hdr(TDDEB_OWR, ctlr->dptr, "TX_BUF", tx_buf_bits, data, data, 1);
ctlr->tx_buf = data & BMASK; /* save data */
ctlr->tx_csr &= ~CSR_DONE; /* clear flag */
CSO_CLR_INT;
switch (ctlr->o_state) {
case TD_GETOPC:
ctlr->ibptr = 0;
ctlr->ibuf[ctlr->ibptr++] = ctlr->tx_buf & BMASK;
td_process_packet(ctlr); /* check packet */
break;
case TD_GETLEN:
ctlr->ibuf[ctlr->ibptr++] = ctlr->tx_buf & BMASK;
ctlr->ilen = ctlr->tx_buf + 4; /* packet length + header + checksum */
ctlr->o_state = TD_GETDATA;
break;
case TD_GETDATA:
ctlr->ibuf[ctlr->ibptr++] = ctlr->tx_buf & BMASK;
if (ctlr->ibptr >= ctlr->ilen) {
ctlr->o_state = TD_GETOPC;
td_process_packet(ctlr);
}
break;
}
ctlr->tx_csr |= CSR_DONE; /* set input flag */
if (ctlr->tx_csr & CSR_IE)
CSO_SET_INT;
return SCPE_OK;
}
static const char *reg_access[] = {"Read", "ReadC", "Write", "WriteC", "WriteB"};
typedef t_stat (*reg_read_routine) (CTLR *ctlr, int32 *data);
static reg_read_routine td_rd_regs[] = {
td_rd_i_csr,
td_rd_i_buf,
td_rd_o_csr,
td_rd_o_buf
};
t_stat td_rd (int32 *data, int32 PA, int32 access)
{
int32 ctlr = ((PA - td_dib.ba) >> 3);
if (ctlr > td_ctrls) /* validate controller number */
return SCPE_IERR;
if (PA & 1) /* odd address reference? */
return SCPE_OK;
sim_debug (TDDEB_RRD, &tdc_dev, "td_rd(PA=%o(%s), access=%d-%s)\n", PA, tdc_regnam[(PA >> 1) & 03], access, reg_access[access]);
return (td_rd_regs[(PA >> 1) & 03])(&td_ctlr[ctlr], data);
}
typedef t_stat (*reg_write_routine) (CTLR *ctlr, int32 data);
static reg_write_routine td_wr_regs[] = {
td_wr_i_csr,
td_wr_i_buf,
td_wr_o_csr,
td_wr_o_buf
};
static t_stat td_wr (int32 data, int32 PA, int32 access)
{
int32 ctrl = ((PA - td_dib.ba) >> 3);
if (ctrl > td_ctrls) /* validate line number */
return SCPE_IERR;
sim_debug (TDDEB_RWR, &tdc_dev, "td_wr(PA=%o(%s), access=%d-%s, data=%X)\n", PA, tdc_regnam[(PA >> 1) & 03], access, reg_access[access], data);
if (PA & 1) /* odd address reference? */
return SCPE_OK;
sim_debug_bits_hdr (TDDEB_RWR, &tdc_dev, tdc_regnam[(PA >> 1) & 03], td_reg_bits[(PA >> 1) & 03], data, data, 1);
return td_wr_regs[(PA >> 1) & 03](&td_ctlr[ctrl], data);
}
static void td_process_packet(CTLR *ctlr)
{
uint32 unit;
int32 opcode = ctlr->ibuf[0];
const char *opcode_name, *command_name;
switch (opcode) {
case TD_OPDAT:
opcode_name = "OPDAT";
break;
case TD_OPCMD:
opcode_name = "OPCMD";
break;
case TD_OPINI:
opcode_name = "OPINI";
break;
case TD_OPBOO:
opcode_name = "OPBOO";
break;
case TD_OPCNT:
opcode_name = "OPCNT";
break;
case TD_OPXOF:
opcode_name = "OPXOF";
break;
default:
opcode_name = "unknown";
}
sim_debug (TDDEB_TRC, ctlr->dptr, "td_process_packet() Opcode=%s(%d)\n", opcode_name, opcode);
switch (opcode) {
case TD_OPDAT:
if (ctlr->p_state != TD_WRITE1) { /* expecting data? */
sim_debug (TDDEB_ERR, ctlr->dptr, "td_process_packet() Opcode=%s(%d) - TU58 protocol error 1 - Not Expecting Data\n", opcode_name, opcode);
return;
}
if (ctlr->ibptr < 2) { /* whole packet read? */
ctlr->o_state = TD_GETLEN; /* get rest of packet */
return;
}
ctlr->p_state = TD_WRITE2;
sim_activate (ctlr->uptr+ctlr->unitno, td_ctime); /* sched command */
break;
case TD_OPCMD:
if (ctlr->p_state != TD_IDLE) { /* expecting command? */
sim_debug (TDDEB_ERR, ctlr->dptr, "td_process_packet() Opcode=%s(%d) - TU58 protocol error 2 - Not Expecting Command\n", opcode_name, opcode);
return;
}
if (ctlr->ibptr < 2) { /* whole packet read? */
ctlr->o_state = TD_GETLEN; /* get rest of packet */
return;
}
if (ctlr->ibuf[2] > TD_CMDEND)
command_name = "Unknown";
else
command_name = td_ops[ctlr->ibuf[2]];
sim_debug (TDDEB_OPS, ctlr->dptr, "strt: fnc=%d(%s), len=%d, unit=%d, block=%d, size=%d\n", ctlr->ibuf[2], command_name, ctlr->ibuf[1], ctlr->ibuf[4], ((ctlr->ibuf[11] << 8) | ctlr->ibuf[10]), ((ctlr->ibuf[9] << 8) | ctlr->ibuf[8]));
switch (ctlr->ibuf[2]) {
case TD_CMDNOP: /* NOP */
case TD_CMDGST: /* Get status */
case TD_CMDSST: /* Set status */
case TD_CMDINI: /* INIT */
case TD_CMDDIA: /* Diagnose */
ctlr->unitno = ctlr->ibuf[4];
ctlr->p_state = TD_END; /* All treated as NOP */
ctlr->ecode = TD_STSOK;
ctlr->offset = 0;
sim_activate (ctlr->uptr+ctlr->unitno, td_ctime);/* sched command */
break;
case TD_CMDRD:
ctlr->unitno = ctlr->ibuf[4];
ctlr->block = ((ctlr->ibuf[11] << 8) | ctlr->ibuf[10]);
ctlr->txsize = ((ctlr->ibuf[9] << 8) | ctlr->ibuf[8]);
ctlr->p_state = TD_READ;
ctlr->offset = 0;
sim_activate (ctlr->uptr+ctlr->unitno, td_ctime);/* sched command */
break;
case TD_CMDWR:
ctlr->unitno = ctlr->ibuf[4];
ctlr->block = ((ctlr->ibuf[11] << 8) | ctlr->ibuf[10]);
ctlr->txsize = ((ctlr->ibuf[9] << 8) | ctlr->ibuf[8]);
ctlr->p_state = TD_WRITE;
ctlr->offset = 0;
sim_activate (ctlr->uptr+ctlr->unitno, td_ctime);/* sched command */
break;
case TD_CMDPOS:
ctlr->unitno = ctlr->ibuf[4];
ctlr->block = ((ctlr->ibuf[11] << 8) | ctlr->ibuf[10]);
ctlr->txsize = 0;
ctlr->p_state = TD_POSITION;
ctlr->offset = 0;
sim_activate (ctlr->uptr+ctlr->unitno, td_ctime);/* sched command */
break;
case TD_CMDMRSP:
ctlr->rx_buf = TD_OPDAT;
ctlr->rx_csr |= CSR_DONE; /* set input flag */
if (ctlr->rx_csr & CSR_IE)
CSI_SET_INT;
break;
}
break;
case TD_OPINI:
for (unit=0; unit < MIN(ctlr->dptr->numunits, 2); unit++)
sim_cancel (ctlr->uptr+unit);
ctlr->ibptr = 0;
ctlr->obptr = 0;
ctlr->olen = 0;
ctlr->offset = 0;
ctlr->txsize = 0;