1 /*
2 * vim: filetype=c:tabstop=4:ai:expandtab
3 * SPDX-License-Identifier: ICU
4 * SPDX-License-Identifier: Multics
5 * scspell-id: 134751ac-f62e-11ec-b67e-80ee73e9b8e7
6 *
7 * ---------------------------------------------------------------------------
8 *
9 * Copyright (c) 2007-2013 Michael Mondy
10 * Copyright (c) 2012-2016 Harry Reed
11 * Copyright (c) 2013-2022 Charles Anthony
12 * Copyright (c) 2021-2022 The DPS8M Development Team
13 *
14 * All rights reserved.
15 *
16 * This software is made available under the terms of the ICU
17 * License, version 1.8.1 or later. For more details, see the
18 * LICENSE.md file at the top-level directory of this distribution.
19 *
20 * ---------------------------------------------------------------------------
21 *
22 * This source file may contain code comments that adapt, include, and/or
23 * incorporate Multics program code and/or documentation distributed under
24 * the Multics License. In the event of any discrepancy between code
25 * comments herein and the original Multics materials, the original Multics
26 * materials should be considered authoritative unless otherwise noted.
27 * For more details and historical background, see the LICENSE.md file at
28 * the top-level directory of this distribution.
29 *
30 * ---------------------------------------------------------------------------
31 */
32
33 #include <stdio.h>
34 #include <ctype.h>
35
36 #include "dps8.h"
37 #include "dps8_dia.h"
38 #include "dps8_sys.h"
39 #include "dps8_faults.h"
40 #include "dps8_scu.h"
41 #include "dps8_cpu.h"
42 #include "dps8_iom.h"
43 #include "dps8_cable.h"
44 #include "dps8_utils.h"
45
46 #include "udplib.h"
47
48 #define DBG_CTR 1
49
50 #ifdef THREADZ
51 # include "threadz.h"
52 #endif
53
54 #ifdef TESTING
55 # undef FREE
56 # define FREE(p) free(p)
57 #endif /* ifdef TESTING */
58
59 static inline void fnp_core_read (word24 addr, word36 *data, UNUSED const char * ctx)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
60 {
61 #ifdef THREADZ
62 lock_mem_rd ();
63 #endif
64 * data = M [addr] & DMASK;
65 #ifdef THREADZ
66 unlock_mem ();
67 #endif
68 }
69 #define N_DIA_UNITS 1 // default
70 #define DIA_UNIT_IDX(uptr) ((uptr) - dia_unit)
71
72 static config_list_t dia_config_list [] =
73 {
74 /* 0 */
75 { "mailbox", 0, 07777, NULL },
76 { NULL, 0, 0, NULL }
77 };
78
79 static t_stat set_config (UNIT * uptr, UNUSED int value, const char * cptr, UNUSED void * desc)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
80 {
81 uint dia_unit_idx = (uint) DIA_UNIT_IDX (uptr);
82 //if (dia_unit_idx >= dia_dev.numunits)
83 if (dia_unit_idx >= N_DIA_UNITS_MAX)
84 {
85 sim_debug (DBG_ERR, & dia_dev, "DIA SET CONFIG: Invalid unit number %ld\n",
86 (long) dia_unit_idx);
87 sim_printf ("error: DIA SET CONFIG: Invalid unit number %ld\n",
88 (long) dia_unit_idx);
89 return SCPE_ARG;
90 }
91
92 struct dia_unit_data * dudp = dia_data.dia_unit_data + dia_unit_idx;
93
94 config_state_t cfg_state = { NULL, NULL };
95
96 for (;;)
97 {
98 int64_t v;
99 int rc = cfg_parse ("DIA SET CONFIG", cptr, dia_config_list, & cfg_state, & v);
100 switch (rc)
101 {
102 case -2: // error
103 cfg_parse_done (& cfg_state);
104 return SCPE_ARG;
105
106 case -1: // done
107 break;
108
109 case 0: // mailbox
110 dudp -> mailbox_address = (uint) v;
111 break;
112
113 default:
114 sim_printf ("error: DIA SET CONFIG: Invalid cfg_parse rc <%ld>\n", (long) rc);
115 cfg_parse_done (& cfg_state);
116 return SCPE_ARG;
117 } // switch
118 if (rc < 0)
119 break;
120 } // process statements
121 cfg_parse_done (& cfg_state);
122 return SCPE_OK;
123 }
124
125 static t_stat show_config (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
126 UNUSED const void * desc)
127 {
128 long unit_idx = DIA_UNIT_IDX (uptr);
129 if (unit_idx >= (long) N_DIA_UNITS_MAX)
130 {
131 sim_debug (DBG_ERR, & dia_dev,
132 "DIA SHOW CONFIG: Invalid unit number %ld\n", (long) unit_idx);
133 sim_printf ("error: Invalid unit number %ld\n", (long) unit_idx);
134 return SCPE_ARG;
135 }
136
137 sim_printf ("DIA unit number %ld\n", (long) unit_idx);
138 struct dia_unit_data * dudp = dia_data.dia_unit_data + unit_idx;
139
140 sim_printf ("DIA Mailbox Address: %04o(8)\n", dudp -> mailbox_address);
141
142 return SCPE_OK;
143 }
144
145 static t_stat show_status (UNUSED FILE * st, UNIT * uptr, UNUSED int val,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
146 UNUSED const void * desc)
147 {
148 long dia_unit_idx = DIA_UNIT_IDX (uptr);
149 if (dia_unit_idx >= (long) dia_dev.numunits)
150 {
151 sim_debug (DBG_ERR, & dia_dev,
152 "DIA SHOW STATUS: Invalid unit number %ld\n", (long) dia_unit_idx);
153 sim_printf ("error: Invalid unit number %ld\n", (long) dia_unit_idx);
154 return SCPE_ARG;
155 }
156
157 sim_printf ("DIA unit number %ld\n", (long) dia_unit_idx);
158 struct dia_unit_data * dudp = dia_data.dia_unit_data + dia_unit_idx;
159
160 sim_printf ("mailbox_address: %04o\n", dudp->mailbox_address);
161 return SCPE_OK;
162 }
163
164 static t_stat show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
165 UNUSED int val, UNUSED const void * desc)
166 {
167 sim_printf("Number of DIA units in system is %d\n", dia_dev.numunits);
168 return SCPE_OK;
169 }
170
171 static t_stat set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
172 const char * cptr, UNUSED void * desc)
173 {
174 if (! cptr)
175 return SCPE_ARG;
176 int n = atoi (cptr);
177 if (n < 1 || n > N_DIA_UNITS_MAX)
178 return SCPE_ARG;
179 dia_dev.numunits = (uint32) n;
180 return SCPE_OK;
181 }
182
183 static MTAB dia_mod [] =
184 {
185 {
186 MTAB_XTD | MTAB_VUN | \
187 MTAB_NMO | MTAB_VALR, /* Mask */
188 0, /* Match */
189 "CONFIG", /* Print string */
190 "CONFIG", /* Match string */
191 set_config, /* Validation routine */
192 show_config, /* Display routine */
193 NULL, /* Value descriptor */
194 NULL /* Help string */
195 },
196
197 {
198 MTAB_XTD | MTAB_VUN | \
199 MTAB_NMO | MTAB_VALR, /* Mask */
200 0, /* Match */
201 "STATUS", /* Print string */
202 "STATUS", /* Match string */
203 NULL, /* Validation routine */
204 show_status, /* Display routine */
205 NULL, /* Value descriptor */
206 NULL /* Help string */
207 },
208
209 {
210 MTAB_XTD | MTAB_VDV | \
211 MTAB_NMO | MTAB_VALR, /* Mask */
212 0, /* Match */
213 "NUNITS", /* Print string */
214 "NUNITS", /* Match string */
215 set_nunits, /* Validation routine */
216 show_nunits, /* Display routine */
217 "Number of DIA units in the system", /* Value descriptor */
218 NULL /* Help */
219 },
220 { 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }
221 };
222
223 UNIT dia_unit [N_DIA_UNITS_MAX] = {
224 {UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL}
225 };
226
227 static DEBTAB dia_DT [] =
228 {
229 { "TRACE", DBG_TRACE, NULL },
230 { "NOTIFY", DBG_NOTIFY, NULL },
231 { "INFO", DBG_INFO, NULL },
232 { "ERR", DBG_ERR, NULL },
233 { "WARN", DBG_WARN, NULL },
234 { "DEBUG", DBG_DEBUG, NULL },
235 { "ALL", DBG_ALL, NULL }, // don't move as it messes up DBG message
236 { NULL, 0, NULL }
237 };
238
239 static t_stat reset (UNUSED DEVICE * dptr)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
240 {
241 return SCPE_OK;
242 }
243
244 static t_stat attach (UNIT * uptr, const char * cptr)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
245 {
246 if (! cptr)
247 return SCPE_ARG;
248 int unitno = (int) (uptr - dia_unit);
249
250 // ATTACH DNn llll:w.x.y.z:rrrr - connect via UDP to a remote
251
252 t_stat ret;
253 char * pfn;
254
255 // If we're already attached, then detach ...
256 if ((uptr->flags & UNIT_ATT) != 0)
257 detach_unit (uptr);
258
259 // Make a copy of the "file name" argument. udp_create() actually modifies
260 // the string buffer we give it, so we make a copy now so we'll have
261 // something to display in the "SHOW DNn ..." command.
262 pfn = (char *) calloc (CBUFSIZE, sizeof (char));
263 if (pfn == NULL)
264 return SCPE_MEM;
265 strncpy (pfn, cptr, CBUFSIZE);
266
267 // Create the UDP connection.
268 ret = udp_create (cptr, & dia_data.dia_unit_data[unitno].link);
269 if (ret != SCPE_OK)
270 {
271 FREE (pfn);
272 return ret;
273 }
274
275 uptr->flags |= UNIT_ATT;
276 uptr->filename = pfn;
277 return SCPE_OK;
278 }
279
280 // Detach (connect) ...
281 static t_stat detach (UNIT * uptr)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
282 {
283 int unitno = (int) (uptr - dia_unit);
284 t_stat ret;
285 if ((uptr->flags & UNIT_ATT) == 0)
286 return SCPE_OK;
287 if (dia_data.dia_unit_data[unitno].link == NOLINK)
288 return SCPE_OK;
289
290 ret = udp_release (dia_data.dia_unit_data[unitno].link);
291 if (ret != SCPE_OK)
292 return ret;
293 dia_data.dia_unit_data[unitno].link = NOLINK;
294 uptr->flags &= ~ (unsigned int) UNIT_ATT;
295 FREE (uptr->filename);
296 uptr->filename = NULL;
297 return SCPE_OK;
298 }
299
300 DEVICE dia_dev = {
301 "DIA", /* Name */
302 dia_unit, /* Units */
303 NULL, /* Registers */
304 dia_mod, /* Modifiers */
305 N_DIA_UNITS, /* #Units */
306 10, /* Address radix */
307 31, /* Address width */
308 1, /* Address increment */
309 8, /* Data radix */
310 9, /* Data width */
311 NULL, /* Examine routine */
312 NULL, /* Deposit routine */
313 reset, /* Reset routine */
314 NULL, /* Boot routine */
315 attach, /* Attach routine */
316 detach, /* Detach routine */
317 NULL, /* Context */
318 DEV_DEBUG, /* Flags */
319 0, /* Debug control flags */
320 dia_DT, /* Debug flag names */
321 NULL, /* Memory size change */
322 NULL, /* Logical name */
323 NULL, /* Attach help */
324 NULL, /* Help */
325 NULL, /* Help context */
326 NULL, /* Device description */
327 NULL
328 };
329
330 t_dia_data dia_data;
331
332 struct dn355_submailbox
333 {
334 word36 word1; // dn355_no; is_hsla; la_no; slot_no
335 word36 word2; // cmd_data_len; op_code; io_cmd
336 word36 command_data [3];
337 word36 word6; // data_addr, word_cnt;
338 word36 pad3 [2];
339 };
340
341 struct fnp_submailbox // 28 words
342 {
343 // AN85
344 word36 word1; // dn355_no; is_hsla; la_no; slot_no // 0 word0
345 word36 word2; // cmd_data_len; op_code; io_cmd // 1 word1
346 word36 mystery [26]; // word2...
347 };
348
349 struct mailbox
350 {
351 word36 dia_pcw;
352 word36 mailbox_requests;
353 word36 term_inpt_mpx_wd;
354 word36 last_mbx_req_count;
355 word36 num_in_use;
356 word36 mbx_used_flags;
357 word36 crash_data [2];
358 struct dn355_submailbox dn355_sub_mbxes [8];
359 struct fnp_submailbox fnp_sub_mbxes [4];
360 };
361
362 #define MAILBOX_WORDS (sizeof (struct mailbox) / sizeof (word36))
363 #define TERM_INPT_MPX_WD (offsetof (struct mailbox, term_inpt_mpx_wd) / sizeof (word36))
364
365 //
366 // Once-only initialization
367 //
368
369 void dia_init (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
370 {
371 // 0 sets set service to service_undefined
372 memset(& dia_data, 0, sizeof(dia_data));
373 for (uint unit_num = 0; unit_num < N_DIA_UNITS_MAX; unit_num ++)
374 {
375 cables -> cables_from_iom_to_dia [unit_num].iomUnitIdx = -1;
376 dia_data.dia_unit_data[unit_num].link = -1;
377 }
378 }
379
380 static inline void fnp_core_write (word24 addr, word36 data, UNUSED const char * ctx)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
381 {
382 #ifdef THREADZ
383 lock_mem_wr ();
384 #endif
385 M [addr] = data & DMASK;
386 #ifdef THREADZ
387 unlock_mem ();
388 #endif
389 }
390
391 //
392 // Convert virtual address to physical
393 //
394
395 static uint virtToPhys (uint ptPtr, uint l66Address)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
396 {
397 uint pageTable = ptPtr * 64u;
398 uint l66AddressPage = l66Address / 1024u;
399
400 word36 ptw;
401 fnp_core_read (pageTable + l66AddressPage, & ptw, "fnpIOMCmd get ptw");
402 uint page = getbits36_14 (ptw, 4);
403 uint addr = page * 1024u + l66Address % 1024u;
404 return addr;
405 }
406
407 //
408 // udp packets
409 //
410 // pkt[0] = cmd
411 // cmd 1 - bootload
412 //
413
414 static void cmd_bootload (uint iom_unit_idx, uint dev_unit_idx, uint chan, word24 l66_addr)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
415 {
416
417 uint fnpno = dev_unit_idx; // XXX
418 //iom_chan_data_t * p = & iom_chan_data [iom_unit_idx] [chan];
419 struct dia_unit_data * dudp = & dia_data.dia_unit_data[fnpno];
420 struct mailbox vol * mbxp = (struct mailbox vol *) & M[dudp->mailbox_address];
421
422 dia_data.dia_unit_data[dev_unit_idx].l66_addr = l66_addr;
423
424 dn_bootload pkt;
425 pkt.cmd = dn_cmd_bootload;
426 //pkt.dia_pcw = mbxp->dia_pcw;
427
428 sim_printf ("XXXXXXXXXXXXXXXXXXXXXXXXXXX cmd_bootload\r\n");
429 int rc = dn_udp_send (dia_data.dia_unit_data[dev_unit_idx].link,
430 (uint8_t *) & pkt,
431 (uint16_t) sizeof (pkt), PFLG_FINAL);
432 if (rc < 0)
433 {
434 fprintf (stderr, "udp_send failed\n");
435 }
436 }
437
438 static int interruptL66 (uint iom_unit_idx, uint chan)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
439 {
440 iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
441 struct device * d = & cables->cablesFromIomToDev[iom_unit_idx].
442 devices[chan][p->IDCW_DEV_CODE];
443 uint dev_unit_idx = d->devUnitIdx;
444 struct dia_unit_data * dudp = &dia_data.dia_unit_data[dev_unit_idx];
445 struct mailbox vol * mbxp = (struct mailbox vol *) & M[dudp->mailbox_address];
446 word36 dia_pcw = mbxp -> dia_pcw;
447
448 // AN85, pg 13-5
449 // When the CS has control information or output data to send
450 // to the FNP, it fills in a submailbox as described in Section 4
451 // and sends an interrupt over the DIA. This interrupt is handled
452 // by dail as described above; when the submailbox is read, the
453 // transaction control word is set to "submailbox read" so that when
454 // the I/O completes and dtrans runs, the mailbox decoder (decmbx)
455 // is called. the I/O command in the submail box is either WCD (for
456 // control information) or WTX (for output data). If it is WCD,
457 // decmbx dispatches according to a table of operation codes and
458 // setting a flag in the IB and calling itest, the "test-state"
459 // entry of the interpreter. n a few cases, the operation requires
460 // further DIA I/O, but usually all that remains to be does is to
461 // "free" the submailbox by turning on the corresponding bit in the
462 // mailbox terminate interrupt multiplex word (see Section 4) and
463 // set the transaction control word accordingly. When the I/O to
464 // update TIMW terminates, the transaction is complete.
465 //
466 // If the I/O command is WTX, the submailbox contains the
467 // address and length of a 'pseudo-DCW" list containing the
468 // addresses and tallies of data buffers in tty_buf. In this case,
469 // dia_man connects to a DCW list to read them into a reserved area
470 // in dia_man. ...
471
472 // interrupt level (in "cell"):
473 //
474 // mbxs 0-7 are CS -> FNP
475 // mbxs 8--11 are FNP -> CS
476 //
477 // 0-7 Multics has placed a message for the FNP in mbx 0-7.
478 // 8-11 Multics has updated mbx 8-11
479 // 12-15 Multics is done with mbx 8-11 (n - 4).
480
481 word6 cell = getbits36_6 (dia_pcw, 24);
482 sim_debug (DBG_TRACE, & dia_dev, "CS interrupt %u\n", cell);
483 if (cell < 8)
484 {
485 //interruptL66_CS_to_FNP ();
486 }
487 else if (cell >= 8 && cell <= 11)
488 {
489 //interruptL66_FNP_to_CS ();
490 }
491 else if (cell >= 12 && cell <= 15)
492 {
493 //interruptL66_CS_done ();
494 }
495 else
496 {
497 sim_debug (DBG_ERR, & dia_dev, "fnp illegal cell number %d\n", cell);
498 sim_printf ("fnp illegal cell number %d\n", cell);
499 // doFNPfault (...) // XXX
500 return -1;
501 }
502 return 0;
503 }
504
505 static void processMBX (uint iom_unit_idx, uint chan)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
506 {
507 iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
508 struct device * d = & cables->cablesFromIomToDev[iom_unit_idx].
509 devices[chan][p->IDCW_DEV_CODE];
510 uint dev_unit_idx = d->devUnitIdx;
511 struct dia_unit_data * dudp = &dia_data.dia_unit_data[dev_unit_idx];
512
513 // 60132445 FEP Coupler EPS
514 // 2.2.1 Control Intercommunication
515 //
516 // "In Level 66 memory, at a location known to the coupler and
517 // to Level 6 software is a mailbox area consisting to an Overhead
518 // mailbox and 7 Channel mailboxes."
519
520 bool ok = true;
521 struct mailbox vol * mbxp = (struct mailbox vol *) & M [dudp -> mailbox_address];
522
523 word36 dia_pcw;
524 dia_pcw = mbxp -> dia_pcw;
525 //sim_printf ("mbx %08o:%012"PRIo64"\n", dudp -> mailbox_address, dia_pcw);
526
527 // Mailbox word 0:
528 //
529 // 0-17 A
530 // 18 I
531 // 19-20 MBZ
532 // 21-22 RFU
533 // 23 0
534 // 24-26 B
535 // 27-29 D Channel #
536 // 30-35 C Command
537 //
538 // A6-A23 A0-A2 A3-A5
539 // Operation C A B D
540 // Interrupt L6 071 --- Int. Level
541 // Bootload L6 072 L66 Addr L66 Addr L66 Addr
542 // A6-A23 A0-A2 A3-A5
543 // Interrupt L66 073 --- --- Intr Cell
544 // Data Xfer to L66 075 L66 Addr L66 Addr L66 Addr
545 // A6-A23 A0-A2 A3-A5
546 // Data Xfer to L6 076 L66 Addr L66 Addr L66 Addr
547 // A6-A23 A0-A2 A3-A5
548
549 //
550 // fnp_util.pl1:
551 // 075 tandd read
552 // 076 tandd write
553
554 // mbx word 1: mailbox_requests fixed bin
555 // 2: term_inpt_mpx_wd bit (36) aligned
556 // 3: last_mbx_req_count fixed bin
557 // 4: num_in_use fixed bin
558 // 5: mbx_used_flags
559 // used (0:7) bit (1) unaligned
560 // pad2 bit (28) unaligned
561 // 6,7: crash_data
562 // fault_code fixed bin (18) unal unsigned
563 // ic fixed bin (18) unal unsigned
564 // iom_fault_status fixed bin (18) unal unsigned
565 // fault_word fixed bin (18) unal unsigned
566 //
567 // crash_data according to dn355_boot_interrupt.pl1:
568 //
569 // dcl 1 fnp_boot_status aligned based (stat_ptr), /* structure of bootload status */
570 // 2 real_status bit (1) unaligned, /* must be "1"b in valid status */
571 // 2 pad1 bit (2) unaligned,
572 // 2 major_status bit (3) unaligned,
573 // 2 pad2 bit (3) unaligned,
574 // 2 substatus fixed bin (8) unal, /* code set by 355, only interesting if major_status is 4 */
575 // 2 channel_no fixed bin (17) unaligned; /* channel no. of LSLA in case of config error */
576 // only 34 bits???
577 // major_status:
578 // dcl BOOTLOAD_OK fixed bin int static options (constant) init (0);
579 // dcl CHECKSUM_ERROR fixed bin int static options (constant) init (1);
580 // dcl READ_ERROR fixed bin int static options (constant) init (2);
581 // dcl GICB_ERROR fixed bin int static options (constant) init (3);
582 // dcl INIT_ERROR fixed bin int static options (constant) init (4);
583 // dcl UNWIRE_STATUS fixed bin int static options (constant) init (5);
584 // dcl MAX_STATUS fixed bin int static options (constant) init (5);
585
586 // 3.5.1 Commands Issued by Central System
587 //
588 // In the issuing of an order by the Central System to the Coupler, the
589 // sequence occurs:
590 //
591 // 1. The L66 program creates a LPW and Pcw for the Central System Connect
592 // channel. It also generates and stores a control word containing a command
593 // in the L66 mailbox. A Connect is then issued to the L66 IOM.
594 //
595 // 2. The Connect Channel accesses the PCW to get the channel number of
596 // the Direct Channel that the coupler is attached to. the direct Channel
597 // sends a signal to the Coupler that a Connect has been issued.
598 //
599 // 3. The Coupler now reads the content of the L66 mailbox, obtaining the
600 // control word. If the control word is legal, the Coupler will write a
601 // word of all zeros into the mailbox.
602 //
603
604 // 4.1.1.2 Transfer Control Word.
605 // The transfer control word, which is pointed to by the
606 // mailbox word in l66 memory on Op Codes 72, 7, 76 contains
607 // a starting address which applies to L6 memory an a Tally
608 // of the number of 36 bit words to be transferred. The l66
609 // memory locations to/from which the transfers occur are
610 // those immediately following the location where this word
611 // was obtained.
612 //
613 // 00-02 001
614 // 03-17 L6 Address
615 // 18 P
616 // 19-23 MBZ
617 // 24-25 Tally
618 //
619 // if P = 0 the l6 address:
620 // 00-07 00000000
621 // 08-22 L6 address (bits 3-17)
622 // 23 0
623 // if P = 1
624 // 00-14 L6 address (bits 3-17)
625 // 15-23 0
626 //
627
628 //uint chanNum = getbits36_6 (dia_pcw, 24);
629 uint command = getbits36_6 (dia_pcw, 30);
630 word36 bootloadStatus = 0;
631
632 if (command == 000) // reset
633 {
634 sim_debug (DBG_TRACE, & dia_dev, "FNP reset??\n");
635 }
636 else if (command == 072) // bootload
637 {
638 // 60132445 pg 49
639 // Extract L66 address from dia_pcw
640
641 // According to 60132445:
642 //word24 A = (word24) getbits36_18 (dia_pcw, 0);
643 //word24 B = (word24) getbits36_3 (dia_pcw, 24);
644 //word24 D = (word24) getbits36_3 (dia_pcw, 29);
645 //word24 l66_addr = (B << (24 - 3)) | (D << (24 - 3 - 3)) | A;
646 // According to fnp_util.pl1:
647 // dcl 1 a_dia_pcw aligned based (mbxp), /* better declaration than the one used when MCS is running */
648 // 2 address fixed bin (18) unsigned unaligned,
649 word24 l66_addr = (word24) getbits36_18 (dia_pcw, 0);
650 sim_printf ("l66_addr %08o\r\n", l66_addr);
651
652 uint phys_addr = virtToPhys (p->PCW_PAGE_TABLE_PTR, l66_addr);
653 sim_printf ("phys_addr %08o\r\n", phys_addr);
654
655 word36 tcw;
656 fnp_core_read (phys_addr, & tcw, "tcw fetch");
657
658 // Got 100000000517 as expected
659 //sim_printf ("tcw %012llo\r\n", tcw);
660
661 //word36 tcw1;
662 //fnp_core_read (phys_addr + 1, & tcw1, "tcw fetch");
663
664 // Got 100002060002, as expected (first word of gicb)
665 //sim_printf ("tcw1 %012llo\r\n", tcw1);
666
667 // pg 50 4.1.1.2 Transfer control word
668 // "The transfer control word, which is pointed to by the
669 // mailbox word in L66 memory on Op Codes 72, 75, 76 contains
670 // a starting address which applies to L6 memory and a Tally
671 // of the number of 36 bit words to be transferred. The L66
672 // memory locations to/from which the transfers occur are
673 // those immediately following the location where this word
674 // was obtained.
675 //
676 // 0-2: 001
677 // 3-17: L6 Address
678 // 18: P
679 // 19-23: MBZ
680 // 24-36: Tally
681 //
682 // The L6 Address field is interpreted as an effective L6 byte
683 // address as follows:
684 //
685 // If P = 0
686 //
687 // 0-7: 00000000
688 // 8-22: L6 Address field (bits 3-17)
689 // 23: 0
690 //
691 // If P = 1
692 //
693 // 0-14: L6 Address field (bits 3-17)
694 // 15-22: 0000000
695 // 23: 0
696
697 cmd_bootload (iom_unit_idx, dev_unit_idx, chan, l66_addr);
698
699 // My understanding is that the FNP shouldn't clear the MBX PCW until
700 // the bootload is complete; but the timeout in fnp_util$connect_to_dia_paged
701 // is short:
702 // do i = 1 to 100000 while (unspec (a_dia_pcw) = old_pcw);
703 // I am going ahead and acking, but it is unclear to me what the mechanism,
704 // if any, is for FNP signaling completed successful bootload.
705
706 // Don't acknowledge the boot yet.
707 //dudp -> fnpIsRunning = true;
708 //return;
709 }
710 else if (command == 071) // interrupt L6
711 {
712 ok = interruptL66 (iom_unit_idx, chan) == 0;
713 }
714 else if (command == 075) // data xfer from L6 to L66
715 {
716 // Build the L66 address from the PCW
717 // 0-17 A
718 // 24-26 B
719 // 27-29 D Channel #
720 // Operation C A B D
721 // Data Xfer to L66 075 L66 Addr L66 Addr L66 Addr
722 // A6-A23 A0-A2 A3-A5
723 // These don't seem to be right; M[L66Add] is always 0.
724 //word24 A = (word24) getbits36_18 (dia_pcw, 0);
725 //word24 B = (word24) getbits36_3 (dia_pcw, 24);
726 //word24 D = (word24) getbits36_3 (dia_pcw, 29);
727 //word24 L66Addr = (B << (24 - 3)) | (D << (24 - 3 - 3)) | A;
728
729 // According to fnp_util:
730 // dcl 1 a_dia_pcw aligned based (mbxp), /* better declaration than the one used when MCS is running */
731 // 2 address fixed bin (18) unsigned unaligned,
732 // 2 error bit (1) unaligned,
733 // 2 pad1 bit (3) unaligned,
734 // 2 parity bit (1) unaligned,
735 // 2 pad2 bit (1) unaligned,
736 // 2 pad3 bit (3) unaligned, /* if we used address extension this would be important */
737 // 2 interrupt_level fixed bin (3) unsigned unaligned,
738 // 2 command bit (6) unaligned;
739 //
740 // a_dia_pcw.address = address;
741 //
742
743 //word24 L66Addr = (word24) getbits36_18 (dia_pcw, 0);
744 //sim_printf ("L66 xfer\n");
745 //sim_printf ("PCW %012"PRIo64"\n", dia_pcw);
746 //sim_printf ("L66Addr %08o\n", L66Addr);
747 //sim_printf ("M[] %012"PRIo64"\n", M[L66Addr]);
748
749 // 'dump_mpx d'
750 //L66 xfer
751 //PCW 022002000075
752 //L66Addr 00022002
753 //M[] 000000401775
754 //L66 xfer
755 //PCW 022002000075
756 //L66Addr 00022002
757 //M[] 003772401775
758 //L66 xfer
759 //PCW 022002000075
760 //L66Addr 00022002
761 //M[] 007764401775
762 //
763 // The contents of M seem much more reasonable, bit still don't match
764 // fnp_util$setup_dump_ctl_word. The left octet should be '1', not '0';
765 // bit 18 should be 0 not 1. But the offsets and tallies match exactly.
766 // Huh... Looking at 'dump_6670_control' control instead, it matches
767 // correctly. Apparently fnp_util thinks the FNP is a 6670, not a 335.
768 // I can't decipher the call path, so I don't know why; but looking at
769 // multiplexer_types.incl.pl1, I would guess that by MR12.x, all FNPs
770 // were 6670s.
771 //
772 // So:
773 //
774 // dcl 1 dump_6670_control aligned based (data_ptr),
775 // /* word used to supply DIA address and tally for fdump */
776 // 2 fnp_address fixed bin (18) unsigned unaligned,
777 // 2 unpaged bit (1) unaligned,
778 // 2 mbz bit (5) unaligned,
779 // 2 tally fixed bin (12) unsigned unaligned;
780
781 // Since the data is marked 'paged', and I don't understand the
782 // the paging mechanism or parameters, I'm going to punt here and
783 // not actually transfer any data.
784 sim_debug (DBG_TRACE, & dia_dev, "FNP data xfer??\n");
785 }
786 else
787 {
788 sim_warn ("bogus fnp command %d (%o)\n", command, command);
789 ok = false;
790 }
791
792 if (ok)
793 {
794 //if_sim_debug (DBG_TRACE, & fnp_dev) dmpmbx (dudp->mailbox_address);
795 fnp_core_write (dudp -> mailbox_address, 0, "dia_iom_cmd clear dia_pcw");
796 putbits36_1 (& bootloadStatus, 0, 1); // real_status = 1
797 putbits36_3 (& bootloadStatus, 3, 0); // major_status = BOOTLOAD_OK;
798 putbits36_8 (& bootloadStatus, 9, 0); // substatus = BOOTLOAD_OK;
799 putbits36_17 (& bootloadStatus, 17, 0); // channel_no = 0;
800 fnp_core_write (dudp -> mailbox_address + 6, bootloadStatus,
801 "dia_iom_cmd set bootload status");
802 }
803 else
804 {
805 //dmpmbx (dudp->mailbox_address);
806 sim_printf ("%s not ok\r\n", __func__);
807 // 3 error bit (1) unaligned, /* set to "1"b if error on connect */
808 putbits36_1 (& dia_pcw, 18, 1); // set bit 18
809 fnp_core_write (dudp -> mailbox_address, dia_pcw, "dia_iom_cmd set error bit");
810 }
811 }
812
813 static int dia_cmd (uint iom_unit_idx, uint chan)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
814 {
815 iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
816 p -> stati = 0;
817 //sim_printf ("fnp cmd %d\n", p -> IDCW_DEV_CMD);
818 switch (p -> IDCW_DEV_CMD)
819 {
820 case 000: // CMD 00 Request status
821 {
822 p -> stati = 04000;
823 sim_debug (DBG_NOTIFY, & dia_dev, "Request status\n");
824 }
825 break;
826
827 default:
828 {
829 p -> stati = 04501;
830 p -> chanStatus = chanStatIncorrectDCW;
831 if (p->IDCW_DEV_CMD != 051) // ignore bootload console probe
832 sim_warn ("dia daze %o\n", p->IDCW_DEV_CMD);
833 }
834 return IOM_CMD_ERROR;
835 }
836
837 processMBX (iom_unit_idx, chan);
838
839 return IOM_CMD_NO_DCW; // did command, don't want more
840 }
841
842 /*
843 * dia_iom_cmd()
844 *
845 */
846
847 // 1 ignored command
848 // 0 ok
849 // -1 problem
850
851 int dia_iom_cmd (uint iom_unit_idx, uint chan)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
852 {
853 sim_printf ("dia_iom_cmd %u %u\r\n", iom_unit_idx, chan);
854 iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
855 // Is it an IDCW?
856
857 if (IS_IDCW (p))
858 {
859 return dia_cmd (iom_unit_idx, chan);
860 }
861 // else // DDCW/TDCW
862 sim_printf ("%s expected IDCW\n", __func__);
863 return -1;
864 }
865
866 static void load_stored_boot (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
867 {
868 // pg 45:
869
870 // 3.8 BOOTLOAD OF L6 BY L66
871 //...
872 // The receipt of the Input Stored Boot order causes the coupler,
873 // if the Stored Boot but is ONE, to input data into L6 memory as
874 // specified by the L66 Bootload order. On completion of this,
875 // the Stored Boot bit is cleared.
876 //
877 // ... the PROM program issues the Input Stored Boot IOLD order
878 // to the coupler..
879 //
880 // ... the L66 Bootload command specifies the L6 memory locations into
881 // which the load from L66 is to occur and the extent of the lod;
882 // location (0100)16 in L6 would always be the first location to be
883 // executed by L6 after the load from L66 assuming that the L66
884 // bootload is independent of the mechanization used in L66
885
886 sim_printf ("got load_stored_boot\n");
887 }
888
889 #define psz 17000
890 static uint8_t pkt[psz];
891
892 // warning: returns ptr to static buffer
893 static int poll_coupler (uint unitno, uint8_t * * pktp)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
894 {
895 int sz = dn_udp_receive (dia_data.dia_unit_data[unitno].link, pkt, psz);
896 if (sz < 0)
897 {
898 sim_printf ("dn_udp_receive failed: %d\n", sz);
899 sz = 0;
900 }
901 * pktp = pkt;
902 return sz;
903 }
904
905 void dia_unit_process_events (uint unit_num)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
906 {
907 // XXX remember
908 // XXX //dudp -> fnpIsRunning = true;
909 // XXX when bootload complete!
910
911 uint8_t * pktp;
912 int sz = poll_coupler (unit_num, & pktp);
913 //sim_printf ("poll_coupler return %d\n", sz);
914 if (! sz)
915 {
916 return;
917 }
918
919 uint8_t cmd = pktp [0];
920 switch (cmd)
921 {
922 // IO Load Stored Boot
923 case dn_cmd_ISB_IOLD:
924 {
925 load_stored_boot ();
926 break;
927 }
928
929 default:
930 {
931 sim_printf ("%s got unhandled cmd %u\n", __func__, cmd);
932 break;
933 }
934 }
935 }
936
937 void dia_process_events (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
938 {
939 for (uint unit_num = 0; unit_num < N_DIA_UNITS_MAX; unit_num ++)
940 {
941 if (dia_data.dia_unit_data[unit_num].link >= 0)
942 dia_unit_process_events (unit_num);
943 }
944 }