1 /* hp2100_ms.c: HP 2100 13181B/13183B Digital Magnetic Tape Unit Interface simulator
2
3 Copyright (c) 1993-2016, Robert M. Supnik
4 Copyright (c) 2017-2019, J. David Bryan
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 Except as contained in this notice, the names of the authors shall not be
24 used in advertising or otherwise to promote the sale, use or other dealings
25 in this Software without prior written authorization from the authors.
26
27 MS 13181B Digital Magnetic Tape Unit Interface
28 13183B Digital Magnetic Tape Unit Interface
29
30 18-Mar-19 JDB Reordered SCP includes
31 24-Jan-19 JDB Removed DEV_TAPE from DEVICE flags
32 27-Dec-18 JDB Added fall through comments in msc_svc
33 05-Jun-18 JDB Revised I/O model
34 28-Feb-18 JDB Added the BMTL
35 23-Feb-18 JDB Eliminated "msc_boot" references to A and S registers
36 20-Jul-17 JDB Removed "msc_stopioe" variable and register
37 11-Jul-17 JDB Renamed "ibl_copy" to "cpu_ibl"
38 15-Mar-17 JDB Trace flags are now global
39 Changed DEBUG_PRI calls to tprintfs
40 13-Mar-17 JDB Deprecated LOCKED/WRITEENABLED for ATTACH -R
41 10-Mar-17 JDB Added IOBUS to the debug table
42 27-Feb-17 JDB ibl_copy no longer returns a status code
43 17-Jan-17 JDB Modified to use "odd_parity" array in hp2100_sys.c
44 13-May-16 JDB Modified for revised SCP API function parameter types
45 30-Dec-14 JDB Added S-register parameters to ibl_copy
46 24-Dec-14 JDB Use T_ADDR_FMT with t_addr values for 64-bit compatibility
47 Added casts for explicit downward conversions
48 11-Dec-14 JDB Updated for new erase gap API, added CRCC/LRCC support
49 10-Jan-13 MP Added DEV_TAPE to DEVICE flags
50 09-May-12 JDB Separated assignments from conditional expressions
51 10-Feb-12 JDB Deprecated DEVNO in favor of SC
52 Added CNTLR_TYPE cast to ms_settype
53 28-Mar-11 JDB Tidied up signal handling
54 26-Oct-10 JDB Changed I/O signal handler for revised signal model
55 11-Aug-08 JDB Revised to use AR instead of saved_AR in boot
56 26-Jun-08 JDB Rewrote device I/O to model backplane signals
57 28-Dec-06 JDB Added ioCRS state to I/O decoders
58 18-Sep-06 JDB Fixed 2nd CLR after WC causing another write
59 Improve debug reporting, add debug flags
60 14-Sep-06 JDB Removed local BOT flag, now uses sim_tape_bot
61 30-Aug-06 JDB Added erase gap support, improved tape lib err reporting
62 07-Jul-06 JDB Added CAPACITY as alternate for REEL
63 Fixed EOT test for unlimited reel size
64 16-Feb-06 RMS Revised for new EOT test
65 22-Jul-05 RMS Fixed compiler warning on Solaris (from Doug Glyn)
66 01-Mar-05 JDB Added SET OFFLINE; rewind/offline now does not detach
67 07-Oct-04 JDB Fixed enable/disable from either device
68 14-Aug-04 JDB Fixed many functional and timing problems (from Dave Bryan)
69 - fixed erroneous execution of rejected command
70 - fixed erroneous execution of select-only command
71 - fixed erroneous execution of clear command
72 - fixed odd byte handling for read
73 - fixed spurious odd byte status on 13183A EOF
74 - modified handling of end of medium
75 - added detailed timing, with fast and realistic modes
76 - added reel sizes to simulate end of tape
77 - added debug printouts
78 06-Jul-04 RMS Fixed spurious timing error after CLC (found by Dave Bryan)
79 26-Apr-04 RMS Fixed SFS x,C and SFC x,C
80 Fixed SR setting in IBL
81 Revised IBL loader
82 Implemented DMA SRQ (follows FLG)
83 25-Apr-03 RMS Revised for extended file support
84 28-Mar-03 RMS Added multiformat support
85 28-Feb-03 RMS Revised for magtape library
86 18-Oct-02 RMS Added BOOT command, added 13183A support
87 30-Sep-02 RMS Revamped error handling
88 29-Aug-02 RMS Added end of medium support
89 30-May-02 RMS Widened POS to 32b
90 22-Apr-02 RMS Added maximum record length test
91
92 References:
93 - 13181B Digital Magnetic Tape Unit Interface Kit Operating and Service Manual
94 (13181-90901, November 1982)
95 - 13183B Digital Magnetic Tape Unit Interface Kit Operating and Service Manual
96 (13183-90901, November 1983)
97 - SIMH Magtape Representation and Handling
98 (Bob Supnik, 30-Aug-2006)
99 */
100
101
102
103 #include "sim_defs.h"
104 #include "sim_tape.h"
105
106 #include "hp2100_defs.h"
107 #include "hp2100_io.h"
108
109
110
111 #define UNIT_V_OFFLINE (MTUF_V_UF + 0) /* unit offline */
112 #define UNIT_OFFLINE (1 << UNIT_V_OFFLINE)
113
114 #define MS_NUMDR 4 /* number of drives */
115 #define DB_N_SIZE 16 /* max data buf */
116 #define DBSIZE (1 << DB_N_SIZE) /* max data cmd */
117 #define FNC u3 /* function */
118 #define UST u4 /* unit status */
119 #define REEL u5 /* tape reel size */
120
121 #define BPI_13181 MT_DENS_800 /* 800 bpi for 13181 cntlr */
122 #define BPI_13183 MT_DENS_1600 /* 1600 bpi for 13183 cntlr */
123 #define GAP_13181 48 /* gap is 4.8 inches for 13181 cntlr */
124 #define GAP_13183 30 /* gap is 3.0 inches for 13183 cntlr */
125 #define TCAP (300 * 12 * 800) /* 300 ft capacity at 800 bpi */
126
127 /* Command - msc_fnc */
128
129 #define FNC_CLR 00110 /* clear */
130 #define FNC_GAP 00015 /* write gap */
131 #define FNC_GFM 00215 /* gap+file mark */
132 #define FNC_RC 00023 /* read */
133 #define FNC_WC 00031 /* write */
134 #define FNC_FSR 00003 /* forward space */
135 #define FNC_BSR 00041 /* backward space */
136 #define FNC_FSF 00203 /* forward file */
137 #define FNC_BSF 00241 /* backward file */
138 #define FNC_REW 00101 /* rewind */
139 #define FNC_RWS 00105 /* rewind and offline */
140 #define FNC_WFM 00211 /* write file mark */
141 #define FNC_RFF 00223 /* read file fwd (diag) */
142 #define FNC_RRR 00061 /* read record rev (diag) */
143 #define FNC_CMPL 00400 /* completion state */
144 #define FNC_V_SEL 9 /* select */
145 #define FNC_M_SEL 017
146 #define FNC_GETSEL(x) (((x) >> FNC_V_SEL) & FNC_M_SEL)
147
148 #define FNF_MOT 00001 /* motion */
149 #define FNF_OFL 00004
150 #define FNF_WRT 00010 /* write */
151 #define FNF_REV 00040 /* reverse */
152 #define FNF_RWD 00100 /* rewind */
153 #define FNF_CHS 00400 /* change select */
154
155 #define FNC_SEL ((FNC_M_SEL << FNC_V_SEL) | FNF_CHS)
156
157 /* Status - stored in msc_sta, unit.UST (u), or dynamic (d) */
158
159 #define STA_PE 0100000 /* 1600 bpi (d) */
160 #define STA_V_SEL 13 /* unit sel (d) */
161 #define STA_M_SEL 03
162 #define STA_SEL (STA_M_SEL << STA_V_SEL)
163 #define STA_ODD 0004000 /* odd bytes */
164 #define STA_REW 0002000 /* rewinding (u) */
165 #define STA_TBSY 0001000 /* transport busy (d) */
166 #define STA_BUSY 0000400 /* ctrl busy */
167 #define STA_EOF 0000200 /* end of file */
168 #define STA_BOT 0000100 /* beg of tape (d) */
169 #define STA_EOT 0000040 /* end of tape (d) */
170 #define STA_TIM 0000020 /* timing error */
171 #define STA_REJ 0000010 /* programming error */
172 #define STA_WLK 0000004 /* write locked (d) */
173 #define STA_PAR 0000002 /* parity error */
174 #define STA_LOCAL 0000001 /* local (d) */
175 #define STA_DYN (STA_PE | STA_SEL | STA_TBSY | STA_BOT | \
176 STA_EOT | STA_WLK | STA_LOCAL)
177
178 /* Controller types */
179
180 typedef enum {
181 A13181,
182 A13183
183 } CNTLR_TYPE;
184
185 /* Interface state */
186
187 typedef struct {
188 FLIP_FLOP control; /* control flip-flop */
189 FLIP_FLOP flag; /* flag flip-flop */
190 FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
191 } CARD_STATE;
192
193 static CARD_STATE msd; /* data per-card state */
194 static CARD_STATE msc; /* command per-card state */
195
196 static CNTLR_TYPE ms_ctype = A13181; /* ctrl type */
197 static int32 ms_timing = 1; /* timing type */
198
199 static int32 msc_sta = 0; /* status */
200 static int32 msc_buf = 0; /* buffer */
201 static int32 msc_usl = 0; /* unit select */
202 static int32 msc_1st = 0; /* first service */
203
204 static int32 msd_buf = 0; /* data buffer */
205 static uint8 msxb [DBSIZE] = { 0 }; /* data buffer */
206 static t_mtrlnt ms_ptr = 0; /* buffer ptrs */
207 static t_mtrlnt ms_max = 0; /* buffer ptrs */
208 static t_bool ms_crc = FALSE; /* buffer ready for CRC calc */
209
210
211 /* Hardware timing at 45 IPS 13181 13183
212 (based on 1580 instr/msec) instr msec SCP instr msec SCP
213 -------------------- --------------------
214 - BOT start delay : btime = 161512 102.22 184 252800 160.00 288
215 - motion cmd start delay : ctime = 14044 8.89 16 17556 11.11 20
216 - GAP traversal time : gtime = 175553 111.11 200 105333 66.67 120
217 - IRG traversal time : itime = 24885 15.75 - 27387 17.33 -
218 - rewind initiation time : rtime = 878 0.56 1 878 0.56 1
219 - data xfer time / word : xtime = 88 55.56us - 44 27.78us -
220
221 NOTE: The 13181-60001 Rev. 1629 tape diagnostic fails test 17B subtest 6 with
222 "E116 BYTE TIME SHORT" if the correct data transfer time is used for
223 13181A interface. Set "xtime" to 115 (instructions) to pass that
224 diagnostic. Rev. 2040 of the tape diagnostic fixes this problem and
225 passes with the correct data transfer time.
226 */
227
228 static int32 msc_btime = 0; /* BOT start delay */
229 static int32 msc_ctime = 0; /* motion cmd start delay */
230 static int32 msc_gtime = 0; /* GAP traversal time */
231 static int32 msc_itime = 0; /* IRG traversal time */
232 static int32 msc_rtime = 0; /* rewind initiation time */
233 static int32 msc_xtime = 0; /* data xfer time / word */
234
235 typedef int32 TIMESET[6]; /* set of controller times */
236
237 static int32 * const timers [] = { &msc_btime, &msc_ctime, &msc_gtime,
238 &msc_itime, &msc_rtime, &msc_xtime };
239
240 static const TIMESET msc_times [3] = {
241 { 161512, 14044, 175553, 24885, 878, 88 }, /* 13181B */
242 { 252800, 17556, 105333, 27387, 878, 44 }, /* 13183B */
243 { 1, 1000, 1, 1, 100, 10 } /* FAST */
244 };
245
246 static INTERFACE msd_interface;
247 static INTERFACE msc_interface;
248
249 static t_stat msc_svc (UNIT *uptr);
250 static t_stat ms_reset (DEVICE *dptr);
251 static t_stat msc_attach (UNIT *uptr, char *cptr);
252 static t_stat msc_detach (UNIT *uptr);
253 static t_stat msc_online (UNIT *uptr, int32 value, char *cptr, void *desc);
254 static t_stat msc_boot (int32 unitno, DEVICE *dptr);
255 static t_stat ms_write_gap (UNIT *uptr);
256 static t_stat ms_map_err (UNIT *uptr, t_stat st);
257 static t_stat ms_settype (UNIT *uptr, int32 val, char *cptr, void *desc);
258 static t_stat ms_showtype (FILE *st, UNIT *uptr, int32 val, void *desc);
259 static t_stat ms_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc);
260 static t_stat ms_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc);
261 static t_stat ms_set_reelsize (UNIT *uptr, int32 val, char *cptr, void *desc);
262 static t_stat ms_show_reelsize (FILE *st, UNIT *uptr, int32 val, void *desc);
263 static void ms_config_timing (void);
264 static char *ms_cmd_name (uint32 cmd);
265 static t_stat ms_clear (void);
266 static uint32 calc_crc_lrc (uint8 *buffer, t_mtrlnt length);
267
268
269 /* Device information blocks */
270
271 static DIB ms_dib [] = {
272 { &msd_interface, /* the device's I/O interface function pointer */
273 MSD, /* the device's select code (02-77) */
274 0, /* the card index */
275 "13181B/13183B Digital Magnetic Tape Unit Interface Data Channel", /* the card description */
276 NULL }, /* the ROM description */
277
278 { &msc_interface, /* the device's I/O interface function pointer */
279 MSC, /* the device's select code (02-77) */
280 0, /* the card index */
281 "13181B/13183B Digital Magnetic Tape Unit Interface Command Channel", /* the card description */
282 "12992D 7970 Magnetic Tape Loader" } /* the ROM description */
283 };
284
285 #define msd_dib ms_dib [0]
286 #define msc_dib ms_dib [1]
287
288
289 /* Data card SCP data structures */
290
291
292 /* Unit list */
293
294 static UNIT msd_unit [] = {
295 /* Event Routine Unit Flags Capacity Delay */
296 /* ------------- ---------- -------- ----- */
297 { UDATA (NULL, 0, 0) }
298 };
299
300
301 /* Register list */
302
303 static REG msd_reg [] = {
304 { ORDATA (BUF, msd_buf, 16) },
305 { FLDATA (CTL, msd.control, 0) },
306 { FLDATA (FLG, msd.flag, 0) },
307 { FLDATA (FBF, msd.flag_buffer, 0) },
308 { BRDATA (DBUF, msxb, 8, 8, DBSIZE) },
309 { DRDATA (BPTR, ms_ptr, DB_N_SIZE + 1) },
310 { DRDATA (BMAX, ms_max, DB_N_SIZE + 1) },
311
312 DIB_REGS (msd_dib),
313
314 { NULL }
315 };
316
317
318 /* Modifier list */
319
320 static MTAB msd_mod [] = {
321 /* Entry Flags Value Print String Match String Validation Display Descriptor */
322 /* ------------------- ----- ------------ ------------ ------------ ------------- ---------------- */
323 { MTAB_XDV, 2u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &ms_dib },
324 { MTAB_XDV | MTAB_NMO, ~2u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &ms_dib },
325 { 0 }
326 };
327
328
329 /* Debugging trace list */
330
331 static DEBTAB msd_deb [] = {
332 { "IOBUS", TRACE_IOBUS }, /* interface I/O bus signals and data words */
333 { NULL, 0 }
334 };
335
336
337 /* Device descriptor */
338
339 DEVICE msd_dev = {
340 "MSD", /* device name */
341 msd_unit, /* unit array */
342 msd_reg, /* register array */
343 msd_mod, /* modifier array */
344 1, /* number of units */
345 10, /* address radix */
346 DB_N_SIZE, /* address width */
347 1, /* address increment */
348 8, /* data radix */
349 8, /* data width */
350 NULL, /* examine routine */
351 NULL, /* deposit routine */
352 &ms_reset, /* reset routine */
353 NULL, /* boot routine */
354 NULL, /* attach routine */
355 NULL, /* detach routine */
356 &msd_dib, /* device information block pointer */
357 DEV_DISABLE | DEV_DEBUG, /* device flags */
358 0, /* debug control flags */
359 msd_deb, /* debug flag name array */
360 NULL, /* memory size change routine */
361 NULL /* logical device name */
362 };
363
364
365 /* Command card SCP data structures */
366
367
368 /* Unit list */
369
370 #define UNIT_FLAGS (UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_OFFLINE)
371
372 static UNIT msc_unit [] = {
373 /* Event Routine Unit Flags Capacity Delay */
374 /* ------------- ---------- -------- ----- */
375 { UDATA (&msc_svc, UNIT_FLAGS, 0) },
376 { UDATA (&msc_svc, UNIT_FLAGS, 0) },
377 { UDATA (&msc_svc, UNIT_FLAGS, 0) },
378 { UDATA (&msc_svc, UNIT_FLAGS, 0) }
379 };
380
381
382 /* Register list */
383
384 static REG msc_reg [] = {
385 { ORDATA (STA, msc_sta, 12) },
386 { ORDATA (BUF, msc_buf, 16) },
387 { ORDATA (USEL, msc_usl, 2) },
388 { FLDATA (FSVC, msc_1st, 0) },
389 { FLDATA (CTL, msc.control, 0) },
390 { FLDATA (FLG, msc.flag, 0) },
391 { FLDATA (FBF, msc.flag_buffer, 0) },
392 { URDATA (POS, msc_unit[0].pos, 10, T_ADDR_W, 0, MS_NUMDR, PV_LEFT) },
393 { URDATA (FNC, msc_unit[0].FNC, 8, 8, 0, MS_NUMDR, REG_HRO) },
394 { URDATA (UST, msc_unit[0].UST, 8, 12, 0, MS_NUMDR, REG_HRO) },
395 { URDATA (REEL, msc_unit[0].REEL, 10, 2, 0, MS_NUMDR, REG_HRO) },
396 { DRDATA (BTIME, msc_btime, 24), REG_NZ + PV_LEFT },
397 { DRDATA (CTIME, msc_ctime, 24), REG_NZ + PV_LEFT },
398 { DRDATA (GTIME, msc_gtime, 24), REG_NZ + PV_LEFT },
399 { DRDATA (ITIME, msc_itime, 24), REG_NZ + PV_LEFT },
400 { DRDATA (RTIME, msc_rtime, 24), REG_NZ + PV_LEFT },
401 { DRDATA (XTIME, msc_xtime, 24), REG_NZ + PV_LEFT },
402 { FLDATA (TIMING, ms_timing, 0), REG_HRO },
403 { FLDATA (CTYPE, ms_ctype, 0), REG_HRO },
404
405 DIB_REGS (msc_dib),
406
407 { NULL }
408 };
409
410
411 /* Modifier list.
412
413 The LOCKED and WRITEENABLED modifiers are deprecated. The supported method
414 of write-protecting a tape drive is to attach the tape image with the -R
415 (read-only) switch or by setting the host operating system's read-only
416 attribute on the tape image file. This simulates removing the write ring
417 from the tape reel before mounting it on the drive. There is no hardware
418 method of write-protecting a mounted and positioned tape reel.
419
420
421 Implementation notes:
422
423 1. The UNIT_RO modifier displays "write ring" if the flag is not set. There
424 is no corresponding entry for the opposite condition because "read only"
425 is automatically printed after the attached filename.
426 */
427
428 static MTAB msc_mod [] = {
429 /* Mask Value Match Value Print String Match String Validation Display Descriptor */
430 /* ------------- ------------- ---------------- --------------- ------------ ------- ---------- */
431 { UNIT_RO, 0, "write ring", NULL, NULL, NULL, NULL },
432
433 { UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL, NULL, NULL },
434 { UNIT_OFFLINE, 0, "online", "ONLINE", &msc_online, NULL, NULL },
435
436 { MTUF_WLK, 0, NULL, "WRITEENABLED", NULL, NULL, NULL },
437 { MTUF_WLK, MTUF_WLK, NULL, "LOCKED", NULL, NULL, NULL },
438
439
440 /* Entry Flags Value Print String Match String Validation Display Descriptor */
441 /* ------------------- ----- ------------ ------------ ----------------- ------------------ ---------------- */
442 { MTAB_XUN, 0, "CAPACITY", "CAPACITY", &ms_set_reelsize, &ms_show_reelsize, NULL },
443 { MTAB_XUN | MTAB_NMO, 1, "REEL", "REEL", &ms_set_reelsize, &ms_show_reelsize, NULL },
444 { MTAB_XUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },
445
446 { MTAB_XDV, 0, NULL, "13181A/B", &ms_settype, NULL, NULL },
447 { MTAB_XDV, 1, NULL, "13183A/B", &ms_settype, NULL, NULL },
448 { MTAB_XDV, 0, "TYPE", NULL, NULL, &ms_showtype, NULL },
449
450 { MTAB_XDV, 0, NULL, "REALTIME", &ms_set_timing, NULL, NULL },
451 { MTAB_XDV, 1, NULL, "FASTTIME", &ms_set_timing, NULL, NULL },
452 { MTAB_XDV, 0, "TIMING", NULL, NULL, &ms_show_timing, NULL },
453
454 { MTAB_XDV, 2u, "SC", "SC", &hp_set_dib, &hp_show_dib, (void *) &ms_dib },
455 { MTAB_XDV | MTAB_NMO, ~2u, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &ms_dib },
456
457 { 0 }
458 };
459
460
461 /* Debugging trace list */
462
463 static DEBTAB msc_deb [] = {
464 { "CMDS", DEB_CMDS },
465 { "RWS", DEB_RWS },
466 { "CPU", DEB_CPU },
467 { "IOBUS", TRACE_IOBUS }, /* interface I/O bus signals and data words */
468 { NULL, 0 }
469 };
470
471
472 /* Device descriptor */
473
474 DEVICE msc_dev = {
475 "MSC", /* device name */
476 msc_unit, /* unit array */
477 msc_reg, /* register array */
478 msc_mod, /* modifier array */
479 MS_NUMDR, /* number of units */
480 10, /* address radix */
481 31, /* address width */
482 1, /* address increment */
483 8, /* data radix */
484 8, /* data width */
485 NULL, /* examine routine */
486 NULL, /* deposit routine */
487 &ms_reset, /* reset routine */
488 &msc_boot, /* boot routine */
489 &msc_attach, /* attach routine */
490 &msc_detach, /* detach routine */
491 &msc_dib, /* device information block pointer */
492 DEV_DISABLE | DEV_DEBUG, /* device flags */
493 0, /* debug control flags */
494 msc_deb, /* debug flag name array */
495 NULL, /* memory size change routine */
496 NULL /* logical device name */
497 };
498
499
500
501 /* Data channel interface */
502
msd_interface(const DIB * dibptr,INBOUND_SET inbound_signals,HP_WORD inbound_value)503 static SIGNALS_VALUE msd_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
504 {
505 INBOUND_SIGNAL signal;
506 INBOUND_SET working_set = inbound_signals;
507 SIGNALS_VALUE outbound = { ioNONE, 0 };
508 t_bool irq_enabled = FALSE;
509 uint32 check;
510
511 while (working_set) { /* while signals remain */
512 signal = IONEXTSIG (working_set); /* isolate the next signal */
513
514 switch (signal) { /* dispatch the I/O signal */
515
516 case ioCLF: /* Clear Flag flip-flop */
517 msd.flag_buffer = CLEAR; /* reset the flag buffer */
518 msd.flag = CLEAR; /* and flag flip-flops */
519 break;
520
521
522 case ioSTF: /* Set Flag flip-flop */
523 msd.flag_buffer = SET; /* set the flag buffer flip-flop */
524 break;
525
526
527 case ioENF: /* Enable Flag */
528 if (msd.flag_buffer == SET) /* if the flag buffer flip-flop is set */
529 msd.flag = SET; /* then set the flag flip-flop */
530 break;
531
532
533 case ioSFC: /* Skip if Flag is Clear */
534 if (msd.flag == CLEAR) /* if the flag flip-flop is clear */
535 outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
536 break;
537
538
539 case ioSFS: /* Skip if Flag is Set */
540 if (msd.flag == SET) /* if the flag flip-flop is set */
541 outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
542 break;
543
544
545 case ioIOI: /* I/O data input */
546 if (ms_crc) { /* ready for CRC? */
547 check = calc_crc_lrc (msxb, ms_max); /* calculate CRCC and LRCC */
548
549 msd_buf = TO_WORD (UPPER_WORD (check), /* position CRCC in upper byte */
550 check); /* and LRCC in lower byte */
551 }
552
553 outbound.value = msd_buf; /* return value */
554 break;
555
556
557 case ioIOO: /* I/O data output */
558 msd_buf = inbound_value; /* store data */
559 break;
560
561
562 case ioPOPIO: /* Power-On Preset to I/O */
563 ms_clear (); /* issue CLR to controller */
564 break;
565
566
567 case ioCRS: /* Control Reset */
568 msd.control = CLEAR; /* clear the control flip-flop */
569 msd.flag_buffer = SET; /* and set the flag buffer flip-flop */
570 break;
571
572
573 case ioCLC: /* Clear Control flip-flop */
574 msd.control = CLEAR; /* clear the control flip-flop */
575 break;
576
577
578 case ioSTC: /* Set Control flip-flop */
579 msd.control = SET; /* set the control flip-flop */
580 ms_crc = FALSE; /* reset CRC ready */
581 break;
582
583
584 case ioSIR: /* Set Interrupt Request */
585 if (msd.control & msd.flag) /* if the control and flag flip-flops are set */
586 outbound.signals |= cnVALID; /* then deny PRL */
587 else /* otherwise */
588 outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
589
590 if (msd.control & msd.flag & msd.flag_buffer) /* if the control, flag, and flag buffer flip-flops are set */
591 outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
592
593 if (msd.flag == SET) /* if the flag flip-flop is set */
594 outbound.signals |= ioSRQ; /* then assert SRQ */
595 break;
596
597
598 case ioIAK: /* Interrupt Acknowledge */
599 msd.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
600 break;
601
602
603 case ioEDT: /* End Data Transfer */
604 msd.flag_buffer = CLEAR; /* reset the flag buffer */
605 msd.flag = CLEAR; /* and flag flip-flops */
606 break;
607
608
609 case ioIEN: /* Interrupt Enable */
610 irq_enabled = TRUE; /* permit IRQ to be asserted */
611 break;
612
613
614 case ioPRH: /* Priority High */
615 if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
616 outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
617
618 if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
619 outbound.signals |= ioPRL; /* then assert it unconditionally */
620 break;
621
622
623 case ioPON: /* not used by this interface */
624 break;
625 }
626
627 IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
628 } /* and continue until all signals are processed */
629
630 return outbound; /* return the outbound signals and value */
631 }
632
633
634 /* Command channel interface.
635
636 Implementation notes:
637
638 1. Commands are usually initiated with an STC cc,C instruction. The CLR
639 command completes immediately and sets the flag. This requires that we
640 ignore the CLF part (but still process the SIR).
641
642 2. The command channel card clears its flag and flag buffer on EDT, but as
643 it never asserts SRQ, it will never get EDT. Under simulation, we omit
644 the EDT handler.
645
646 3. In hardware, the command channel card passes PRH to PRL. The data card
647 actually drives PRL with both channels' control and flag states. That
648 is, the priority chain is broken at the data card, even when the command
649 card is interrupting. This works in hardware, but we must break PRL at
650 the command card under simulation to allow the command card to interrupt.
651 */
652
msc_interface(const DIB * dibptr,INBOUND_SET inbound_signals,HP_WORD inbound_value)653 static SIGNALS_VALUE msc_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
654 {
655 static const uint8 map_sel [16] = {
656 0, 0, 1, 1, 2, 2, 2, 2,
657 3, 3, 3, 3, 3, 3, 3, 3
658 };
659 int32 sched_time;
660 UNIT *uptr = msc_dev.units + msc_usl;
661 INBOUND_SIGNAL signal;
662 INBOUND_SET working_set = inbound_signals;
663 SIGNALS_VALUE outbound = { ioNONE, 0 };
664 t_bool irq_enabled = FALSE;
665
666 while (working_set) { /* while signals remain */
667 signal = IONEXTSIG (working_set); /* isolate the next signal */
668
669 switch (signal) { /* dispatch the I/O signal */
670
671 case ioCLF: /* Clear Flag flip-flop */
672 msc.flag_buffer = CLEAR; /* reset the flag buffer */
673 msc.flag = CLEAR; /* and flag flip-flops */
674 break;
675
676
677 case ioSTF: /* Set Flag flip-flop */
678 msc.flag_buffer = SET; /* set the flag buffer flip-flop */
679 break;
680
681
682 case ioENF: /* Enable Flag */
683 if (msc.flag_buffer == SET) /* if the flag buffer flip-flop is set */
684 msc.flag = SET; /* then set the flag flip-flop */
685 break;
686
687
688 case ioSFC: /* Skip if Flag is Clear */
689 if (msc.flag == CLEAR) /* if the flag flip-flop is clear */
690 outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
691 break;
692
693
694 case ioSFS: /* Skip if Flag is Set */
695 if (msc.flag == SET) /* if the flag flip-flop is set */
696 outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
697 break;
698
699
700 case ioIOI: /* I/O data input */
701 outbound.value = (HP_WORD) msc_sta & ~STA_DYN; /* get card status */
702
703 if ((uptr->flags & UNIT_OFFLINE) == 0) { /* online? */
704 outbound.value |= (HP_WORD) uptr->UST; /* add unit status */
705
706 if (sim_tape_bot (uptr)) /* BOT? */
707 outbound.value |= STA_BOT;
708
709 if (sim_is_active (uptr) && /* TBSY unless RWD at BOT */
710 !((uptr->FNC & FNF_RWD) && sim_tape_bot (uptr)))
711 outbound.value |= STA_TBSY;
712
713 if (sim_tape_wrp (uptr)) /* write prot? */
714 outbound.value |= STA_WLK;
715
716 if (sim_tape_eot (uptr)) /* EOT? */
717 outbound.value |= STA_EOT;
718 }
719
720 else
721 outbound.value |= STA_TBSY | STA_LOCAL;
722
723 if (ms_ctype == A13183) /* 13183? */
724 outbound.value |= STA_PE | (HP_WORD) msc_usl << STA_V_SEL;
725
726 tprintf (msc_dev, DEB_CPU, "Status = %06o\n", outbound.value);
727 break;
728
729
730 case ioIOO: /* I/O data output */
731 msc_buf = inbound_value; /* clear supplied status */
732
733 tprintf (msc_dev, DEB_CPU, "Command = %06o\n", msc_buf);
734
735 msc_sta = msc_sta & ~STA_REJ; /* clear reject */
736
737 if ((msc_buf & 0377) == FNC_CLR) /* clear always ok */
738 break;
739
740 if (msc_sta & STA_BUSY) { /* busy? reject */
741 msc_sta = msc_sta | STA_REJ; /* dont chg select */
742 break;
743 }
744
745 if (msc_buf & FNF_CHS) { /* select change */
746 msc_usl = map_sel [FNC_GETSEL (msc_buf)]; /* is immediate */
747 uptr = msc_dev.units + msc_usl;
748 tprintf (msc_dev, DEB_CMDS, "Unit %d selected\n", msc_usl);
749 }
750
751 if (((msc_buf & FNF_MOT) && sim_is_active (uptr)) ||
752 ((msc_buf & FNF_REV) && sim_tape_bot (uptr)) ||
753 ((msc_buf & FNF_WRT) && sim_tape_wrp (uptr)))
754 msc_sta = msc_sta | STA_REJ; /* reject? */
755 break;
756
757
758 case ioCRS: /* Control Reset */
759 msc.control = CLEAR; /* clear the control flip-flop */
760 msc.flag_buffer = SET; /* and set the flag buffer flip-flop */
761 break;
762
763
764 case ioCLC: /* Clear Control flip-flop */
765 msc.control = CLEAR; /* clear the control flip-flop */
766 break;
767
768
769 case ioSTC: /* Set Control flip-flop */
770 if (!(msc_sta & STA_REJ)) { /* last cmd rejected? */
771 if ((msc_buf & 0377) == FNC_CLR) { /* clear? */
772 ms_clear (); /* issue CLR to controller */
773
774 msc.control = SET; /* set CTL for STC */
775 msc.flag_buffer = SET; /* set flag and flag buffer */
776 msc.flag = SET; /* for immediate completion */
777
778 working_set = working_set & ~ioCLF; /* eliminate possible CLF */
779
780 tprintf (msc_dev, DEB_CMDS, "Controller cleared\n");
781
782 break; /* command completes immediately */
783 }
784
785 uptr->FNC = msc_buf & 0377; /* save function */
786
787 if (uptr->FNC & FNF_RWD) { /* rewind? */
788 if (!sim_tape_bot (uptr)) /* not at BOT? */
789 uptr->UST = STA_REW; /* set rewinding */
790
791 sched_time = msc_rtime; /* set response time */
792 }
793
794 else {
795 if (sim_tape_bot (uptr)) /* at BOT? */
796 sched_time = msc_btime; /* use BOT start time */
797
798 else if ((uptr->FNC == FNC_GAP) || (uptr->FNC == FNC_GFM))
799 sched_time = msc_gtime; /* use gap traversal time */
800
801 else sched_time = 0;
802
803 if (uptr->FNC != FNC_GAP)
804 sched_time += msc_ctime; /* add base command time */
805 }
806
807 if (msc_buf & ~FNC_SEL) { /* NOP for unit sel alone */
808 sim_activate (uptr, sched_time); /* else schedule op */
809
810 tprintf (msc_dev, DEB_CMDS, "Unit %d command %03o (%s) scheduled, "
811 "pos = %" T_ADDR_FMT "d, time = %d\n",
812 msc_usl, uptr->FNC, ms_cmd_name (uptr->FNC),
813 uptr->pos, sched_time);
814 }
815
816 else
817 tprintf (msc_dev, DEB_CMDS, "Unit select (NOP)\n");
818
819 msc_sta = STA_BUSY; /* ctrl is busy */
820 msc_1st = 1;
821 msc.control = SET; /* go */
822 }
823 break;
824
825
826 case ioSIR: /* Set Interrupt Request */
827 if (msc.control & msc.flag) /* if the control and flag flip-flops are set */
828 outbound.signals |= cnVALID; /* then deny PRL */
829 else /* otherwise */
830 outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
831
832 if (msc.control & msc.flag & msc.flag_buffer) /* if the control, flag, and flag buffer flip-flops are set */
833 outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
834
835 if (msc.flag == SET) /* if the flag flip-flop is set */
836 outbound.signals |= ioSRQ; /* then assert SRQ */
837 break;
838
839
840 case ioIAK: /* Interrupt Acknowledge */
841 msc.flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
842 break;
843
844
845 case ioIEN: /* Interrupt Enable */
846 irq_enabled = TRUE; /* permit IRQ to be asserted */
847 break;
848
849
850 case ioPRH: /* Priority High */
851 if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
852 outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
853
854 if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
855 outbound.signals |= ioPRL; /* then assert it unconditionally */
856 break;
857
858
859 case ioEDT: /* not used by this interface */
860 case ioPON: /* not used by this interface */
861 case ioPOPIO: /* not used by this interface */
862 break;
863 }
864
865 IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
866 } /* and continue until all signals are processed */
867
868 return outbound; /* return the outbound signals and value */
869 }
870
871
872 /* Unit service
873
874 If rewind done, reposition to start of tape, set status
875 else, do operation, set done, interrupt.
876
877 In addition to decreasing the timing intervals, the FASTTIME option enables
878 two additional optimizations: WFM for GFM substitution, and BOT gap
879 elimination. If FASTTIME is selected, gap and file mark (GFM) commands are
880 processed as WFM (write file mark) commands. That is, the preceding GAP is
881 not performed. Also, the initial gap that normally precedes the first data
882 record or EOF mark at the beginning of the tape is omitted. These omissions
883 result in smaller tape image files. If REALTIME is selected, the gaps are
884 included. Note that the gaps (and realistic timing) are necessary to pass
885 the 7970 diagnostics.
886 */
887
msc_svc(UNIT * uptr)888 static t_stat msc_svc (UNIT *uptr)
889 {
890 int32 unum;
891 t_mtrlnt tbc;
892 t_stat st, r = SCPE_OK;
893
894 unum = uptr - msc_unit; /* get unit number */
895
896 if ((uptr->FNC != FNC_RWS) && (uptr->flags & UNIT_OFFLINE)) { /* offline? */
897 msc_sta = (msc_sta | STA_REJ) & ~STA_BUSY; /* reject */
898
899 msc.flag_buffer = SET; /* set the flag buffer */
900 io_assert (&msc_dev, ioa_ENF); /* and the flag */
901 return SCPE_OK;
902 }
903
904 switch (uptr->FNC) { /* case on function */
905
906 case FNC_RWS: /* rewind offline */
907 sim_tape_rewind (uptr); /* rewind tape */
908 uptr->flags = uptr->flags | UNIT_OFFLINE; /* set offline */
909 uptr->UST = 0; /* clear REW status */
910 break; /* we're done */
911
912 case FNC_REW: /* rewind */
913 if (uptr->UST & STA_REW) { /* rewind in prog? */
914 uptr->FNC |= FNC_CMPL; /* set compl state */
915 sim_activate (uptr, msc_ctime); /* sched completion */
916 }
917 break; /* anyway, ctrl done */
918
919 case FNC_REW | FNC_CMPL: /* complete rewind */
920 sim_tape_rewind (uptr); /* rewind tape */
921 uptr->UST = 0; /* clear REW status */
922 return SCPE_OK; /* drive is free */
923
924 case FNC_GFM: /* gap + file mark */
925 if (ms_timing == 1) /* fast timing? */
926 goto DO_WFM; /* do plain file mark */
927
928 /* fall through into GAP */
929
930 case FNC_GAP: /* erase gap */
931 tprintf (msc_dev, DEB_RWS, "Unit %d wrote gap\n", unum);
932
933 r = ms_write_gap (uptr); /* write tape gap*/
934
935 if (r || (uptr->FNC != FNC_GFM)) /* if error or not GFM */
936 break; /* then bail out now */
937
938 /* fall through into WFM */
939
940 case FNC_WFM: /* write file mark */
941 if ((ms_timing == 0) && sim_tape_bot (uptr)) { /* realistic timing + BOT? */
942 tprintf (msc_dev, DEB_RWS, "Unit %d wrote initial gap\n", unum);
943
944 st = ms_write_gap (uptr); /* write initial gap*/
945 if (st != MTSE_OK) { /* error? */
946 r = ms_map_err (uptr, st); /* map error */
947 break; /* terminate operation */
948 }
949 }
950 DO_WFM:
951 tprintf (msc_dev, DEB_RWS, "Unit %d wrote file mark\n", unum);
952
953 st = sim_tape_wrtmk (uptr); /* write tmk */
954 if (st != MTSE_OK) /* error? */
955 r = ms_map_err (uptr, st); /* map error */
956 msc_sta = STA_EOF; /* set EOF status */
957 break;
958
959 case FNC_FSR: /* space forward */
960 st = sim_tape_sprecf (uptr, &tbc); /* space rec fwd */
961 if (st != MTSE_OK) /* error? */
962 r = ms_map_err (uptr, st); /* map error */
963 if (tbc & 1)
964 msc_sta = msc_sta | STA_ODD;
965 else msc_sta = msc_sta & ~STA_ODD;
966 break;
967
968 case FNC_BSR: /* space reverse */
969 st = sim_tape_sprecr (uptr, &tbc); /* space rec rev*/
970 if (st != MTSE_OK) /* error? */
971 r = ms_map_err (uptr, st); /* map error */
972 if (tbc & 1)
973 msc_sta = msc_sta | STA_ODD;
974 else msc_sta = msc_sta & ~STA_ODD;
975 break;
976
977 case FNC_FSF: /* space fwd file */
978 while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) {
979 if (sim_tape_eot (uptr)) break; /* EOT stops */
980 }
981 r = ms_map_err (uptr, st); /* map error */
982 break;
983
984 case FNC_BSF: /* space rev file */
985 while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ;
986 r = ms_map_err (uptr, st); /* map error */
987 break;
988
989 case FNC_RFF: /* diagnostic read */
990 case FNC_RC: /* read */
991 if (msc_1st) { /* first svc? */
992 msc_1st = ms_ptr = ms_max = 0; /* clr 1st flop */
993 st = sim_tape_rdrecf (uptr, msxb, &ms_max, DBSIZE); /* read rec */
994 tprintf (msc_dev, DEB_RWS, "Unit %d read %d word record\n",
995 unum, ms_max / 2);
996 if (st == MTSE_RECE) msc_sta = msc_sta | STA_PAR; /* rec in err? */
997 else if (st != MTSE_OK) { /* other error? */
998 r = ms_map_err (uptr, st); /* map error */
999 if (r == SCPE_OK) { /* recoverable? */
1000 sim_activate (uptr, msc_itime); /* sched IRG */
1001 uptr->FNC |= FNC_CMPL; /* set completion */
1002 return SCPE_OK;
1003 }
1004 break; /* err, done */
1005 }
1006 if (ms_ctype == A13183)
1007 msc_sta = msc_sta | STA_ODD; /* set ODD for 13183 */
1008 }
1009 if (msd.control && (ms_ptr < ms_max)) { /* DCH on, more data? */
1010 if (msd.flag) msc_sta = msc_sta | STA_TIM | STA_PAR;
1011 msd_buf = ((uint16) msxb[ms_ptr] << 8) |
1012 ((ms_ptr + 1 == ms_max) ? 0 : msxb[ms_ptr + 1]);
1013 ms_ptr = ms_ptr + 2;
1014
1015 msd.flag_buffer = SET; /* set the flag buffer */
1016 io_assert (&msd_dev, ioa_ENF); /* and the flag */
1017
1018 sim_activate (uptr, msc_xtime); /* re-activate */
1019 return SCPE_OK;
1020 }
1021 if (ms_max & 1) /* set ODD by rec len */
1022 msc_sta = msc_sta | STA_ODD;
1023 else msc_sta = msc_sta & ~STA_ODD;
1024 sim_activate (uptr, msc_itime); /* sched IRG */
1025 if (uptr->FNC == FNC_RFF) /* diagnostic? */
1026 msc_1st = 1; /* restart */
1027 else {
1028 uptr->FNC |= FNC_CMPL; /* set completion */
1029 ms_crc = TRUE; /* and CRC ready */
1030 }
1031 return SCPE_OK;
1032
1033 case FNC_RFF | FNC_CMPL: /* diagnostic read completion */
1034 case FNC_RC | FNC_CMPL: /* read completion */
1035 break;
1036
1037 case FNC_WC: /* write */
1038 if (msc_1st) { /* first service? */
1039 msc_1st = ms_ptr = 0; /* no data xfer on first svc */
1040 if ((ms_timing == 0) && sim_tape_bot (uptr)) { /* realistic timing + BOT? */
1041 tprintf (msc_dev, DEB_RWS, "Unit %d wrote initial gap\n", unum);
1042
1043 st = ms_write_gap (uptr); /* write initial gap */
1044 if (st != MTSE_OK) { /* error? */
1045 r = ms_map_err (uptr, st); /* map error */
1046 break; /* terminate operation */
1047 }
1048 }
1049 }
1050 else { /* not 1st, next char */
1051 if (ms_ptr < DBSIZE) { /* room in buffer? */
1052 msxb[ms_ptr] = (uint8) (msd_buf >> 8); /* store 2 char */
1053 msxb[ms_ptr + 1] = msd_buf & 0377;
1054 ms_ptr = ms_ptr + 2;
1055 }
1056 else msc_sta = msc_sta | STA_PAR;
1057 }
1058 if (msd.control) { /* xfer flop set? */
1059 msd.flag_buffer = SET; /* set the flag buffer */
1060 io_assert (&msd_dev, ioa_ENF); /* and the flag */
1061
1062 sim_activate (uptr, msc_xtime); /* re-activate */
1063 return SCPE_OK;
1064 }
1065 if (ms_ptr) { /* any data? write */
1066 tprintf (msc_dev, DEB_RWS, "Unit %d wrote %d word record\n",
1067 unum, ms_ptr / 2);
1068 st = sim_tape_wrrecf (uptr, msxb, ms_ptr); /* write */
1069 if (st != MTSE_OK) {
1070 r = ms_map_err (uptr, st); /* map error */
1071 break;
1072 }
1073 }
1074 sim_activate (uptr, msc_itime); /* sched IRG */
1075 uptr->FNC |= FNC_CMPL; /* set completion */
1076 ms_max = ms_ptr; /* indicate buffer complete */
1077 ms_crc = TRUE; /* and CRC may be generated */
1078 return SCPE_OK;
1079
1080 case FNC_WC | FNC_CMPL: /* write completion */
1081 break;
1082
1083 case FNC_RRR: /* not supported */
1084 default: /* unknown command */
1085 tprintf (msc_dev, DEB_CMDS, "Unit %d command %03o is unknown (NOP)\n",
1086 unum, uptr->FNC);
1087 break;
1088 }
1089
1090 msc.flag_buffer = SET; /* set the flag buffer */
1091 io_assert (&msc_dev, ioa_ENF); /* and the flag */
1092
1093 msc_sta = msc_sta & ~STA_BUSY; /* update status */
1094
1095 tprintf (msc_dev, DEB_CMDS, "Unit %d command %03o (%s) complete\n",
1096 unum, uptr->FNC & 0377, ms_cmd_name (uptr->FNC));
1097 return r;
1098 }
1099
1100
1101 /* Write an erase gap */
1102
ms_write_gap(UNIT * uptr)1103 static t_stat ms_write_gap (UNIT *uptr)
1104 {
1105 t_stat st;
1106 uint32 gap_len = ms_ctype ? GAP_13183 : GAP_13181; /* establish gap length */
1107
1108 st = sim_tape_wrgap (uptr, gap_len); /* write gap */
1109
1110 if (st != MTSE_OK)
1111 return ms_map_err (uptr, st); /* map error if failure */
1112 else
1113 return SCPE_OK;
1114 }
1115
1116
1117 /* Map tape error status */
1118
ms_map_err(UNIT * uptr,t_stat st)1119 static t_stat ms_map_err (UNIT *uptr, t_stat st)
1120 {
1121 int32 unum = uptr - msc_unit; /* get unit number */
1122
1123 tprintf (msc_dev, DEB_RWS, "Unit %d tape library status = %d\n", unum, st);
1124
1125 switch (st) {
1126
1127 case MTSE_FMT: /* illegal fmt */
1128 msc_sta = msc_sta | STA_REJ; /* reject cmd */
1129 return SCPE_FMT; /* format error */
1130
1131 case MTSE_UNATT: /* unattached */
1132 msc_detach (uptr); /* resync status (ignore rtn) */
1133 msc_sta = msc_sta | STA_REJ; /* reject cmd */
1134 return SCPE_UNATT; /* unit unattached */
1135
1136 case MTSE_OK: /* no error */
1137 return SCPE_IERR; /* never get here! */
1138
1139 case MTSE_EOM: /* end of medium */
1140 case MTSE_TMK: /* end of file */
1141 msc_sta = msc_sta | STA_EOF;
1142
1143 if (ms_ctype == A13181)
1144 msc_sta = msc_sta | STA_ODD; /* EOF also sets ODD for 13181B */
1145 break;
1146
1147 case MTSE_INVRL: /* invalid rec lnt */
1148 msc_sta = msc_sta | STA_PAR;
1149 return SCPE_MTRLNT;
1150
1151 case MTSE_IOERR: /* IO error */
1152 msc_sta = msc_sta | STA_PAR; /* error */
1153 return SCPE_IOERR;
1154
1155 case MTSE_RECE: /* record in error */
1156 msc_sta = msc_sta | STA_PAR; /* error */
1157 break;
1158
1159 case MTSE_WRP: /* write protect */
1160 msc_sta = msc_sta | STA_REJ; /* reject */
1161 break;
1162 }
1163
1164 return SCPE_OK;
1165 }
1166
1167
1168 /* Controller clear */
1169
ms_clear(void)1170 static t_stat ms_clear (void)
1171 {
1172 int32 i;
1173 t_stat st;
1174 UNIT *uptr;
1175
1176 for (i = 0; i < MS_NUMDR; i++) { /* look for write in progr */
1177 uptr = &msc_unit [i]; /* get pointer to unit */
1178
1179 if (sim_is_active (uptr) && /* unit active? */
1180 (uptr->FNC == FNC_WC) && /* and last cmd write? */
1181 (ms_ptr > 0)) { /* and partial buffer? */
1182 tprintf (msc_dev, DEB_RWS, "Unit %d wrote %d word partial record\n", i, ms_ptr / 2);
1183
1184 st = sim_tape_wrrecf (uptr, msxb, ms_ptr | MTR_ERF);
1185
1186 if (st != MTSE_OK)
1187 ms_map_err (uptr, st); /* discard any error */
1188
1189 ms_ptr = 0; /* clear partial */
1190 }
1191
1192 if ((uptr->UST & STA_REW) == 0)
1193 sim_cancel (uptr); /* stop if not rew */
1194 }
1195
1196 msc_sta = msc_1st = 0; /* clr ctlr status */
1197
1198 return SCPE_OK;
1199 }
1200
1201
1202 /* Reset routine */
1203
ms_reset(DEVICE * dptr)1204 static t_stat ms_reset (DEVICE *dptr)
1205 {
1206 int32 i;
1207 UNIT *uptr;
1208
1209 hp_enbdis_pair (dptr, /* make pair cons */
1210 (dptr == &msd_dev) ? &msc_dev : &msd_dev);
1211
1212 if (sim_switches & SWMASK ('P')) /* initialization reset? */
1213 ms_config_timing ();
1214
1215 io_assert (dptr, ioa_POPIO); /* PRESET the device */
1216
1217 msc_buf = msd_buf = 0;
1218 msc_sta = msc_usl = 0;
1219 msc_1st = 0;
1220
1221 for (i = 0; i < MS_NUMDR; i++) {
1222 uptr = msc_dev.units + i;
1223 sim_tape_reset (uptr);
1224 sim_cancel (uptr);
1225 uptr->UST = 0;
1226
1227 if (sim_switches & SWMASK ('P')) /* if this is an initialization reset */
1228 sim_tape_set_dens (uptr, /* then tell the tape library the density in use */
1229 ms_ctype ? BPI_13183 : BPI_13181,
1230 NULL, NULL);
1231 }
1232
1233 return SCPE_OK;
1234 }
1235
1236
1237 /* Attach routine */
1238
msc_attach(UNIT * uptr,char * cptr)1239 static t_stat msc_attach (UNIT *uptr, char *cptr)
1240 {
1241 t_stat r;
1242
1243 r = sim_tape_attach (uptr, cptr); /* attach unit */
1244 if (r == SCPE_OK)
1245 uptr->flags = uptr->flags & ~UNIT_OFFLINE; /* set online */
1246 return r;
1247 }
1248
1249 /* Detach routine */
1250
msc_detach(UNIT * uptr)1251 static t_stat msc_detach (UNIT* uptr)
1252 {
1253 uptr->UST = 0; /* clear status */
1254 uptr->flags = uptr->flags | UNIT_OFFLINE; /* set offline */
1255 return sim_tape_detach (uptr); /* detach unit */
1256 }
1257
1258 /* Online routine */
1259
msc_online(UNIT * uptr,int32 value,char * cptr,void * desc)1260 static t_stat msc_online (UNIT *uptr, int32 value, char *cptr, void *desc)
1261 {
1262 if (uptr->flags & UNIT_ATT) return SCPE_OK;
1263 else return SCPE_UNATT;
1264 }
1265
1266 /* Configure timing */
1267
ms_config_timing(void)1268 static void ms_config_timing (void)
1269 {
1270 uint32 i, tset;
1271
1272 tset = (ms_timing << 1) | (ms_timing ? 0 : ms_ctype); /* select timing set */
1273 for (i = 0; i < (sizeof (timers) / sizeof (timers[0])); i++)
1274 *timers[i] = msc_times[tset][i]; /* assign times */
1275 }
1276
1277 /* Set controller timing */
1278
ms_set_timing(UNIT * uptr,int32 val,char * cptr,void * desc)1279 static t_stat ms_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc)
1280 {
1281 if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG;
1282 ms_timing = val;
1283 ms_config_timing ();
1284 return SCPE_OK;
1285 }
1286
1287 /* Show controller timing */
1288
ms_show_timing(FILE * st,UNIT * uptr,int32 val,void * desc)1289 static t_stat ms_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc)
1290 {
1291 if (ms_timing) fputs ("fast timing", st);
1292 else fputs ("realistic timing", st);
1293 return SCPE_OK;
1294 }
1295
1296 /* Set controller type */
1297
ms_settype(UNIT * uptr,int32 val,char * cptr,void * desc)1298 static t_stat ms_settype (UNIT *uptr, int32 val, char *cptr, void *desc)
1299 {
1300 int32 i;
1301
1302 if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG;
1303 for (i = 0; i < MS_NUMDR; i++) {
1304 if (msc_unit[i].flags & UNIT_ATT) return SCPE_ALATT;
1305 }
1306 ms_ctype = (CNTLR_TYPE) val;
1307 ms_config_timing (); /* update for new type */
1308
1309 sim_tape_set_dens (uptr, ms_ctype ? BPI_13183 : BPI_13181, /* tell the tape library the density in use */
1310 NULL, NULL);
1311
1312 return SCPE_OK;
1313 }
1314
1315 /* Show controller type */
1316
ms_showtype(FILE * st,UNIT * uptr,int32 val,void * desc)1317 static t_stat ms_showtype (FILE *st, UNIT *uptr, int32 val, void *desc)
1318 {
1319 if (ms_ctype == A13183)
1320 fprintf (st, "13183B");
1321 else
1322 fprintf (st, "13181B");
1323 return SCPE_OK;
1324 }
1325
1326 /* Set unit reel size
1327
1328 val = 0 -> SET MSCn CAPACITY=n
1329 val = 1 -> SET MSCn REEL=n */
1330
ms_set_reelsize(UNIT * uptr,int32 val,char * cptr,void * desc)1331 static t_stat ms_set_reelsize (UNIT *uptr, int32 val, char *cptr, void *desc)
1332 {
1333 int32 reel;
1334 t_stat status;
1335
1336 if (val == 0) {
1337 status = sim_tape_set_capac (uptr, val, cptr, desc);
1338
1339 if (status == SCPE_OK)
1340 uptr->REEL = 0;
1341
1342 return status;
1343 }
1344
1345 if (cptr == NULL)
1346 return SCPE_ARG;
1347
1348 reel = (int32) get_uint (cptr, 10, 2400, &status);
1349
1350 if (status != SCPE_OK)
1351 return status;
1352 else
1353 switch (reel) {
1354 case 0:
1355 uptr->REEL = 0; /* type 0 = unlimited/custom */
1356 break;
1357
1358 case 600:
1359 uptr->REEL = 1; /* type 1 = 600 foot */
1360 break;
1361
1362 case 1200:
1363 uptr->REEL = 2; /* type 2 = 1200 foot */
1364 break;
1365
1366 case 2400:
1367 uptr->REEL = 3; /* type 3 = 2400 foot */
1368 break;
1369
1370 default:
1371 return SCPE_ARG;
1372 }
1373
1374 uptr->capac = uptr->REEL ? (TCAP << uptr->REEL) << ms_ctype : 0;
1375 return SCPE_OK;
1376 }
1377
1378 /* Show unit reel size
1379
1380 val = 0 -> SHOW MSC or SHOW MSCn or SHOW MSCn CAPACITY
1381 val = 1 -> SHOW MSCn REEL */
1382
ms_show_reelsize(FILE * st,UNIT * uptr,int32 val,void * desc)1383 static t_stat ms_show_reelsize (FILE *st, UNIT *uptr, int32 val, void *desc)
1384 {
1385 t_stat status = SCPE_OK;
1386
1387 if (uptr->REEL == 0)
1388 status = sim_tape_show_capac (st, uptr, val, desc);
1389 else
1390 fprintf (st, "%4d foot reel", 300 << uptr->REEL);
1391
1392 if (val == 1)
1393 fputc ('\n', st); /* MTAB_NMO omits \n */
1394
1395 return status;
1396 }
1397
1398 /* Translate command to mnemonic for debug logging
1399
1400 The command names and descriptions are taken from the 13181 interface
1401 manual.
1402 */
1403
ms_cmd_name(uint32 cmd)1404 static char *ms_cmd_name (uint32 cmd)
1405 {
1406 switch (cmd & 0377) {
1407 case FNC_WC: return "WCC"; /* Write command */
1408 case FNC_WFM: return "WFM"; /* Write file mark */
1409 case FNC_RC: return "RRF"; /* Read record forward */
1410 case FNC_FSR: return "FSR"; /* Forward space record */
1411 case FNC_FSF: return "FSF"; /* Forward space file */
1412 case FNC_GAP: return "GAP"; /* Write gap */
1413 case FNC_BSR: return "BSR"; /* Backspace record */
1414 case FNC_BSF: return "BSF"; /* Backspace file */
1415 case FNC_REW: return "REW"; /* Rewind */
1416 case FNC_RWS: return "RWO"; /* Rewind off-line */
1417 case FNC_CLR: return "CLR"; /* Clear controller */
1418 case FNC_GFM: return "GFM"; /* Gap file mark */
1419 case FNC_RFF: return "RFF"; /* Read forward until file mark (diag) */
1420 case FNC_RRR: return "RRR"; /* Read record in reverse (diag) */
1421
1422 default: return "???"; /* Unknown command */
1423 }
1424 }
1425
1426
1427 /* 7970B/7970E bootstrap loaders (BMTL and 12992D).
1428
1429 The Basic Magnetic Tape Loader (BMTL) reads an absolute binary program from
1430 tape into memory. Before execution, the S register must be set as follows:
1431
1432 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
1433 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1434 | - - - - - - - - - - | file number |
1435 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1436
1437 If S-register bits 5-0 are zero, the file located at the current tape
1438 position is read. If the bits are non-zero, the tape is rewound, and the
1439 file number (1 - n) specified by the bits is read.
1440
1441 The 12992D boot loader ROM reads an absolute program from tape into memory.
1442 If S-register bit 0 is 0, the file located at the current tape position is
1443 read. If bit 0 is 1, the tape is rewound, and the file number (1 - n)
1444 specified by the A-register value is read.
1445
1446 For either loader, the tape format must be absolute binary, and a tape mark
1447 must end the file. Loader execution ends with one of the following
1448 instructions:
1449
1450 * HLT 00 - a tape read (parity) error occurred.
1451 * HLT 11 - a checksum error occurred; A/B = the calculated/tape value.
1452 * HLT 77 - the end of the file was reached with a successful read.
1453 */
1454
1455 static const LOADER_ARRAY ms_loaders = {
1456 { /* HP 21xx Basic Magnetic Tape Loader (BMTL) */
1457 000, /* loader starting index */
1458 IBL_NA, /* DMA index */
1459 IBL_NA, /* FWA index */
1460 { 0102501, /* 77700: MTAPE LIA 1 */
1461 0013775, /* 77701: AND 77775 */
1462 0003007, /* 77702: CMA,INA,SZA,RSS */
1463 0027714, /* 77703: JMP 77714 */
1464 0073777, /* 77704: STA 77777 */
1465 0067771, /* 77705: LDB 77771 */
1466 0017761, /* 77706: JSB 77761 */
1467 0102311, /* 77707: SFS 11 */
1468 0027707, /* 77710: JMP 77707 */
1469 0067773, /* 77711: LDB 77773 */
1470 0037777, /* 77712: ISZ 77777 */
1471 0027706, /* 77713: JMP 77706 */
1472 0067772, /* 77714: LDB 77772 */
1473 0017761, /* 77715: JSB 77761 */
1474 0103710, /* 77716: STC 10,C */
1475 0017740, /* 77717: JSB 77740 */
1476 0005727, /* 77720: BLF,BLF */
1477 0007004, /* 77721: CMB,INB */
1478 0077777, /* 77722: STB 77777 */
1479 0017740, /* 77723: JSB 77740 */
1480 0074000, /* 77724: STB 0 */
1481 0077776, /* 77725: STB 77776 */
1482 0017740, /* 77726: JSB 77740 */
1483 0177776, /* 77727: STB 77776,I */
1484 0040001, /* 77730: ADA 1 */
1485 0037776, /* 77731: ISZ 77776 */
1486 0037777, /* 77732: ISZ 77777 */
1487 0027726, /* 77733: JMP 77726 */
1488 0017740, /* 77734: JSB 77740 */
1489 0054000, /* 77735: CPB 0 */
1490 0017740, /* 77736: JSB 77740 */
1491 0102011, /* 77737: HLT 11 */
1492 0000000, /* 77740: NOP */
1493 0102310, /* 77741: SFS 10 */
1494 0027745, /* 77742: JMP 77745 */
1495 0107510, /* 77743: LIB 10,C */
1496 0127740, /* 77744: JMP 77740,I */
1497 0102311, /* 77745: SFS 11 */
1498 0027741, /* 77746: JMP 77741 */
1499 0102511, /* 77747: LIA 11 */
1500 0013774, /* 77750: AND 77774 */
1501 0067777, /* 77751: LDB 77777 */
1502 0001727, /* 77752: ALF,ALF */
1503 0002020, /* 77753: SSA */
1504 0102077, /* 77754: HLT 77 */
1505 0002003, /* 77755: SZA,RSS */
1506 0006002, /* 77756: SZB */
1507 0102000, /* 77757: HLT 0 */
1508 0027714, /* 77760: JMP 77714 */
1509 0000000, /* 77761: NOP */
1510 0106611, /* 77762: OTB 11 */
1511 0102511, /* 77763: LIA 11 */
1512 0001323, /* 77764: RAR,RAR */
1513 0001310, /* 77765: RAR,SLA */
1514 0027762, /* 77766: JMP 77762 */
1515 0103711, /* 77767: STC 11,C */
1516 0127761, /* 77770: JMP 77761,I */
1517 0001501, /* 77771: OCT 1501 */
1518 0001423, /* 77772: OCT 1423 */
1519 0000203, /* 77773: OCT 203 */
1520 0016263, /* 77774: OCT 16263 */
1521 0000077, /* 77775: OCT 77 */
1522 0000000, /* 77776: NOP */
1523 0000000 } }, /* 77777: NOP */
1524
1525 { /* HP 1000 Loader ROM (12992D) */
1526 IBL_START, /* loader starting index */
1527 IBL_DMA, /* DMA index */
1528 IBL_FWA, /* FWA index */
1529 { 0106501, /* 77700: ST LIB 1 ; read sw */
1530 0006011, /* 77701: SLB,RSS ; bit 0 set? */
1531 0027714, /* 77702: JMP RD ; no read */
1532 0003004, /* 77703: CMA,INA ; A is ctr */
1533 0073775, /* 77704: STA WC ; save */
1534 0067772, /* 77705: LDA SL0RW ; sel 0, rew */
1535 0017762, /* 77706: FF JSB CMD ; do cmd */
1536 0102311, /* 77707: SFS CC ; done? */
1537 0027707, /* 77710: JMP *-1 ; wait */
1538 0067774, /* 77711: LDB FFC ; get file fwd */
1539 0037775, /* 77712: ISZ WC ; done files? */
1540 0027706, /* 77713: JMP FF ; no */
1541 0067773, /* 77714: RD LDB RDCMD ; read cmd */
1542 0017762, /* 77715: JSB CMD ; do cmd */
1543 0103710, /* 77716: STC DC,C ; start dch */
1544 0102211, /* 77717: SFC CC ; read done? */
1545 0027752, /* 77720: JMP STAT ; no, get stat */
1546 0102310, /* 77721: SFS DC ; any data? */
1547 0027717, /* 77722: JMP *-3 ; wait */
1548 0107510, /* 77723: LIB DC,C ; get rec cnt */
1549 0005727, /* 77724: BLF,BLF ; move to lower */
1550 0007000, /* 77725: CMB ; make neg */
1551 0077775, /* 77726: STA WC ; save */
1552 0102211, /* 77727: SFC CC ; read done? */
1553 0027752, /* 77730: JMP STAT ; no, get stat */
1554 0102310, /* 77731: SFS DC ; any data? */
1555 0027727, /* 77732: JMP *-3 ; wait */
1556 0107510, /* 77733: LIB DC,C ; get load addr */
1557 0074000, /* 77734: STB 0 ; start csum */
1558 0077762, /* 77735: STA CMD ; save address */
1559 0027742, /* 77736: JMP *+4 */
1560 0177762, /* 77737: NW STB CMD,I ; store data */
1561 0040001, /* 77740: ADA 1 ; add to csum */
1562 0037762, /* 77741: ISZ CMD ; adv addr ptr */
1563 0102310, /* 77742: SFS DC ; any data? */
1564 0027742, /* 77743: JMP *-1 ; wait */
1565 0107510, /* 77744: LIB DC,C ; get word */
1566 0037775, /* 77745: ISZ WC ; done? */
1567 0027737, /* 77746: JMP NW ; no */
1568 0054000, /* 77747: CPB 0 ; csum ok? */
1569 0027717, /* 77750: JMP RD+3 ; yes, cont */
1570 0102011, /* 77751: HLT 11 ; no, halt */
1571 0102511, /* 77752: ST LIA CC ; get status */
1572 0001727, /* 77753: ALF,ALF ; get eof bit */
1573 0002020, /* 77754: SSA ; set? */
1574 0102077, /* 77755: HLT 77 ; done */
1575 0001727, /* 77756: ALF,ALF ; put status back */
1576 0001310, /* 77757: RAR,SLA ; read ok? */
1577 0102000, /* 77760: HLT 0 ; no */
1578 0027714, /* 77761: JMP RD ; read next */
1579 0000000, /* 77762: CMD NOP */
1580 0106611, /* 77763: OTB CC ; output cmd */
1581 0102511, /* 77764: LIA CC ; check for reject */
1582 0001323, /* 77765: RAR,RAR */
1583 0001310, /* 77766: RAR,SLA */
1584 0027763, /* 77767: JMP CMD+1 ; try again */
1585 0103711, /* 77770: STC CC,C ; start command */
1586 0127762, /* 77771: JMP CMD,I ; exit */
1587 0001501, /* 77772: SL0RW OCT 1501 ; select 0, rewind */
1588 0001423, /* 77773: RDCMD OCT 1423 ; read record */
1589 0000203, /* 77774: FFC OCT 203 ; space forward file */
1590 0000000, /* 77775: WC NOP */
1591 0000000, /* 77776: NOP */
1592 0000000 } } /* 77777: NOP */
1593 };
1594
1595
1596 /* Device boot routine.
1597
1598 This routine is called directly by the BOOT MSC and LOAD MSC commands to copy
1599 the device bootstrap into the upper 64 words of the logical address space.
1600 It is also called indirectly by a BOOT CPU or LOAD CPU command when the
1601 specified HP 1000 loader ROM socket contains a 12992D ROM.
1602
1603 When called in response to a BOOT MSC or LOAD MSC command, the "unitno"
1604 parameter indicates the unit number specified in the BOOT command or is zero
1605 for the LOAD command, and "dptr" points at the MSC device structure. The
1606 bootstrap supports loading only from unit 0, and the command will be rejected
1607 if another unit is specified (e.g., BOOT MSC1). Otherwise, depending on the
1608 current CPU model, the BMTL or 12992D loader ROM will be copied into memory
1609 and configured for the MSD/MSC select code pair. If the CPU is a 1000, the S
1610 register will be set as it would be by the front-panel microcode.
1611
1612 When called for a BOOT/LOAD CPU command, the "unitno" parameter indicates the
1613 select code to be used for configuration, and "dptr" will be NULL. As above,
1614 the BMTL or 12992D loader ROM will be copied into memory and configured for
1615 the specified select code. The S register is assumed to be set correctly on
1616 entry and is not modified.
1617
1618 For the 12992D boot loader ROM for the HP 1000, the S register is set as
1619 follows:
1620
1621 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
1622 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1623 | ROM # | 0 0 | select code | 0 0 0 0 0 | F |
1624 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
1625
1626 Where:
1627
1628 F = Read current/specified file (0/1)
1629
1630 If bit 0 is 0, the file located at the current tape position is read. If bit
1631 0 is 1, the tape is rewound, and the file number (1 - n) specified by the
1632 A-register content is read.
1633 */
1634
msc_boot(int32 unitno,DEVICE * dptr)1635 static t_stat msc_boot (int32 unitno, DEVICE *dptr)
1636 {
1637 static const HP_WORD ms_preserved = 0000000u; /* no S-register bits are preserved */
1638 static const HP_WORD ms_reposition = 0000001u; /* S-register bit 0 set for a repositioning boot */
1639 uint32 start;
1640
1641 if (dptr == NULL) /* if we are being called for a BOOT/LOAD CPU */
1642 start = cpu_copy_loader (ms_loaders, unitno, /* then copy the boot loader to memory */
1643 IBL_S_NOCLEAR, IBL_S_NOSET); /* but do not alter the S register */
1644
1645 else if (unitno != 0) /* otherwise a BOOT MSC for a non-zero unit */
1646 return SCPE_NOFNC; /* is rejected as unsupported */
1647
1648 else /* otherwise this is a BOOT/LOAD MSC */
1649 start = cpu_copy_loader (ms_loaders, msd_dib.select_code, /* so copy the boot loader to memory */
1650 ms_preserved, /* and configure the S register if 1000 CPU */
1651 sim_switches & SWMASK ('S') ? ms_reposition : 0);
1652
1653 if (start == 0) /* if the copy failed */
1654 return SCPE_NOFNC; /* then reject the command */
1655 else /* otherwise */
1656 return SCPE_OK; /* the boot loader was successfully copied */
1657 }
1658
1659
1660 /* Calculate tape record CRC and LRC characters */
1661
calc_crc_lrc(uint8 * buffer,t_mtrlnt length)1662 static uint32 calc_crc_lrc (uint8 *buffer, t_mtrlnt length)
1663 {
1664 uint32 i;
1665 HP_WORD byte, crc, lrc;
1666
1667 lrc = crc = 0;
1668
1669 for (i = 0; i < length; i++) {
1670 byte = odd_parity [buffer [i]] | buffer [i];
1671
1672 crc = crc ^ byte;
1673 lrc = lrc ^ byte;
1674
1675 if (crc & 1)
1676 crc = crc >> 1 ^ 0474;
1677 else
1678 crc = crc >> 1;
1679 }
1680
1681 crc = crc ^ 0727;
1682 lrc = lrc ^ crc;
1683
1684 return (uint32) crc << 16 | lrc;
1685 }
1686