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-2023 The DPS8M Development Team
13 *
14 * This software is made available under the terms of the ICU License.
15 * See the LICENSE.md file at the top-level directory of this distribution.
16 *
17 * ---------------------------------------------------------------------------
18 *
19 * This source file may contain code comments that adapt, include, and/or
20 * incorporate Multics program code and/or documentation distributed under
21 * the Multics License. In the event of any discrepancy between code
22 * comments herein and the original Multics materials, the original Multics
23 * materials should be considered authoritative unless otherwise noted.
24 * For more details and historical background, see the LICENSE.md file at
25 * the top-level directory of this distribution.
26 *
27 * ---------------------------------------------------------------------------
28 */
29
30 #include <stdio.h>
31 #include <ctype.h>
32
33 #include "dps8.h"
34 #include "dps8_dia.h"
35 #include "dps8_sys.h"
36 #include "dps8_cpu.h"
37 #include "dps8_faults.h"
38 #include "dps8_scu.h"
39 #include "dps8_iom.h"
40 #include "dps8_cable.h"
41 #include "dps8_utils.h"
42
43 #include "udplib.h"
44
45 #define DBG_CTR 1
46
47 #if defined(THREADZ)
48 # include "threadz.h"
49 #endif /* if defined(THREADZ) */
50
51 #if defined(FREE)
52 # undef FREE
53 #endif /* if defined(FREE) */
54 #define FREE(p) do \
55 { \
56 free((p)); \
57 (p) = NULL; \
58 } while(0)
59
60 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)
*/
61 {
62 #if defined(THREADZ)
63 lock_mem_rd ();
64 #endif /* if defined(THREADZ) */
65 * data = M [addr] & DMASK;
66 #if defined(THREADZ)
67 unlock_mem ();
68 #endif /* if defined(THREADZ) */
69 }
70 #define N_DIA_UNITS 1 // default
71 #define DIA_UNIT_IDX(uptr) ((uptr) - dia_unit)
72
73 static config_list_t dia_config_list [] =
74 {
75 /* 0 */
76 { "mailbox", 0, 07777, NULL },
77 { NULL, 0, 0, NULL }
78 };
79
80 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)
*/
81 {
82 uint dia_unit_idx = (uint) DIA_UNIT_IDX (uptr);
83 //if (dia_unit_idx >= dia_dev.numunits)
84 if (dia_unit_idx >= N_DIA_UNITS_MAX)
85 {
86 sim_debug (DBG_ERR, & dia_dev, "DIA SET CONFIG: Invalid unit number %ld\r\n",
87 (long) dia_unit_idx);
88 sim_printf ("error: DIA SET CONFIG: Invalid unit number %ld\r\n",
89 (long) dia_unit_idx);
90 return SCPE_ARG;
91 }
92
93 struct dia_unit_data * dudp = dia_data.dia_unit_data + dia_unit_idx;
94
95 config_state_t cfg_state = { NULL, NULL };
96
97 for (;;)
98 {
99 int64_t v;
100 int rc = cfg_parse ("DIA SET CONFIG", cptr, dia_config_list, & cfg_state, & v);
101 switch (rc)
102 {
103 case -2: // error
104 cfg_parse_done (& cfg_state);
105 return SCPE_ARG;
106
107 case -1: // done
108 break;
109
110 case 0: // mailbox
111 dudp -> mailbox_address = (uint) v;
112 break;
113
114 default:
115 sim_printf ("error: DIA SET CONFIG: Invalid cfg_parse rc <%ld>\r\n", (long) rc);
116 cfg_parse_done (& cfg_state);
117 return SCPE_ARG;
118 } // switch
119 if (rc < 0)
120 break;
121 } // process statements
122 cfg_parse_done (& cfg_state);
123 return SCPE_OK;
124 }
125
126 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)
*/
127 UNUSED const void * desc)
128 {
129 long unit_idx = DIA_UNIT_IDX (uptr);
130 if (unit_idx >= (long) N_DIA_UNITS_MAX)
131 {
132 sim_debug (DBG_ERR, & dia_dev,
133 "DIA SHOW CONFIG: Invalid unit number %ld\r\n", (long) unit_idx);
134 sim_printf ("error: Invalid unit number %ld\r\n", (long) unit_idx);
135 return SCPE_ARG;
136 }
137
138 sim_printf ("DIA unit number %ld\r\n", (long) unit_idx);
139 struct dia_unit_data * dudp = dia_data.dia_unit_data + unit_idx;
140
141 sim_printf ("DIA Mailbox Address: %04o(8)\r\n", dudp -> mailbox_address);
142
143 return SCPE_OK;
144 }
145
146 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)
*/
147 UNUSED const void * desc)
148 {
149 long dia_unit_idx = DIA_UNIT_IDX (uptr);
150 if (dia_unit_idx >= (long) dia_dev.numunits)
151 {
152 sim_debug (DBG_ERR, & dia_dev,
153 "DIA SHOW STATUS: Invalid unit number %ld\r\n", (long) dia_unit_idx);
154 sim_printf ("error: Invalid unit number %ld\r\n", (long) dia_unit_idx);
155 return SCPE_ARG;
156 }
157
158 sim_printf ("DIA unit number %ld\r\n", (long) dia_unit_idx);
159 struct dia_unit_data * dudp = dia_data.dia_unit_data + dia_unit_idx;
160
161 sim_printf ("mailbox_address: %04o\r\n", dudp->mailbox_address);
162 return SCPE_OK;
163 }
164
165 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)
*/
166 UNUSED int val, UNUSED const void * desc)
167 {
168 sim_printf("Number of DIA units in system is %d\r\n", dia_dev.numunits);
169 return SCPE_OK;
170 }
171
172 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)
*/
173 const char * cptr, UNUSED void * desc)
174 {
175 if (! cptr)
176 return SCPE_ARG;
177 int n = atoi (cptr);
178 if (n < 1 || n > N_DIA_UNITS_MAX)
179 return SCPE_ARG;
180 dia_dev.numunits = (uint32) n;
181 return SCPE_OK;
182 }
183
184 static MTAB dia_mod [] =
185 {
186 {
187 MTAB_XTD | MTAB_VUN | \
188 MTAB_NMO | MTAB_VALR, /* Mask */
189 0, /* Match */
190 "CONFIG", /* Print string */
191 "CONFIG", /* Match string */
192 set_config, /* Validation routine */
193 show_config, /* Display routine */
194 NULL, /* Value descriptor */
195 NULL /* Help string */
196 },
197
198 {
199 MTAB_XTD | MTAB_VUN | \
200 MTAB_NMO | MTAB_VALR, /* Mask */
201 0, /* Match */
202 "STATUS", /* Print string */
203 "STATUS", /* Match string */
204 NULL, /* Validation routine */
205 show_status, /* Display routine */
206 NULL, /* Value descriptor */
207 NULL /* Help string */
208 },
209
210 {
211 MTAB_XTD | MTAB_VDV | \
212 MTAB_NMO | MTAB_VALR, /* Mask */
213 0, /* Match */
214 "NUNITS", /* Print string */
215 "NUNITS", /* Match string */
216 set_nunits, /* Validation routine */
217 show_nunits, /* Display routine */
218 "Number of DIA units in the system", /* Value descriptor */
219 NULL /* Help */
220 },
221 { 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }
222 };
223
224 UNIT dia_unit [N_DIA_UNITS_MAX] = {
225 {UDATA (NULL, UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL}
226 };
227
228 static DEBTAB dia_DT [] =
229 {
230 { "TRACE", DBG_TRACE, NULL },
231 { "NOTIFY", DBG_NOTIFY, NULL },
232 { "INFO", DBG_INFO, NULL },
233 { "ERR", DBG_ERR, NULL },
234 { "WARN", DBG_WARN, NULL },
235 { "DEBUG", DBG_DEBUG, NULL },
236 { "ALL", DBG_ALL, NULL }, // Don't move as it messes up DBG message
237 { NULL, 0, NULL }
238 };
239
240 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)
*/
241 {
242 return SCPE_OK;
243 }
244
245 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)
*/
246 {
247 if (! cptr)
248 return SCPE_ARG;
249 int unitno = (int) (uptr - dia_unit);
250
251 // ATTACH DNn llll:w.x.y.z:rrrr - connect via UDP to a remote
252
253 t_stat ret;
254 char * pfn;
255
256 // If we're already attached, then detach ...
257 if ((uptr->flags & UNIT_ATT) != 0)
258 detach_unit (uptr);
259
260 // Make a copy of the "file name" argument. udp_create() actually modifies
261 // the string buffer we give it, so we make a copy now so we'll have
262 // something to display in the "SHOW DNn ..." command.
263 pfn = (char *) calloc (CBUFSIZE, sizeof (char));
264 if (pfn == NULL)
265 return SCPE_MEM;
266 strncpy (pfn, cptr, CBUFSIZE);
267
268 // Create the UDP connection.
269 ret = udp_create (cptr, & dia_data.dia_unit_data[unitno].link);
270 if (ret != SCPE_OK)
271 {
272 FREE (pfn);
273 return ret;
274 }
275
276 uptr->flags |= UNIT_ATT;
277 uptr->filename = pfn;
278 return SCPE_OK;
279 }
280
281 // Detach (connect) ...
282 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)
*/
283 {
284 int unitno = (int) (uptr - dia_unit);
285 t_stat ret;
286 if ((uptr->flags & UNIT_ATT) == 0)
287 return SCPE_OK;
288 if (dia_data.dia_unit_data[unitno].link == NOLINK)
289 return SCPE_OK;
290
291 ret = udp_release (dia_data.dia_unit_data[unitno].link);
292 if (ret != SCPE_OK)
293 return ret;
294 dia_data.dia_unit_data[unitno].link = NOLINK;
295 uptr->flags &= ~ (unsigned int) UNIT_ATT;
296 FREE (uptr->filename);
297 uptr->filename = NULL;
298 return SCPE_OK;
299 }
300
301 DEVICE dia_dev = {
302 "DIA", /* Name */
303 dia_unit, /* Units */
304 NULL, /* Registers */
305 dia_mod, /* Modifiers */
306 N_DIA_UNITS, /* #Units */
307 10, /* Address radix */
308 31, /* Address width */
309 1, /* Address increment */
310 8, /* Data radix */
311 9, /* Data width */
312 NULL, /* Examine routine */
313 NULL, /* Deposit routine */
314 reset, /* Reset routine */
315 NULL, /* Boot routine */
316 attach, /* Attach routine */
317 detach, /* Detach routine */
318 NULL, /* Context */
319 DEV_DEBUG, /* Flags */
320 0, /* Debug control flags */
321 dia_DT, /* Debug flag names */
322 NULL, /* Memory size change */
323 NULL, /* Logical name */
324 NULL, /* Attach help */
325 NULL, /* Help */
326 NULL, /* Help context */
327 NULL, /* Device description */
328 NULL
329 };
330
331 t_dia_data dia_data;
332
333 struct dn355_submailbox
334 {
335 word36 word1; // dn355_no; is_hsla; la_no; slot_no
336 word36 word2; // cmd_data_len; op_code; io_cmd
337 word36 command_data [3];
338 word36 word6; // data_addr, word_cnt;
339 word36 pad3 [2];
340 };
341
342 struct fnp_submailbox // 28 words
343 {
344 // AN85
345 word36 word1; // dn355_no; is_hsla; la_no; slot_no // 0 word0
346 word36 word2; // cmd_data_len; op_code; io_cmd // 1 word1
347 word36 mystery [26]; // word2...
348 };
349
350 struct mailbox
351 {
352 word36 dia_pcw;
353 word36 mailbox_requests;
354 word36 term_inpt_mpx_wd;
355 word36 last_mbx_req_count;
356 word36 num_in_use;
357 word36 mbx_used_flags;
358 word36 crash_data [2];
359 struct dn355_submailbox dn355_sub_mbxes [8];
360 struct fnp_submailbox fnp_sub_mbxes [4];
361 };
362
363 #define MAILBOX_WORDS (sizeof (struct mailbox) / sizeof (word36))
364 #define TERM_INPT_MPX_WD (offsetof (struct mailbox, term_inpt_mpx_wd) / sizeof (word36))
365
366 //
367 // Once-only initialization
368 //
369
370 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)
*/
371 {
372 // 0 sets set service to service_undefined
373 (void)memset(& dia_data, 0, sizeof(dia_data));
374 for (uint unit_num = 0; unit_num < N_DIA_UNITS_MAX; unit_num ++)
375 {
376 cables -> cables_from_iom_to_dia [unit_num].iomUnitIdx = -1;
377 dia_data.dia_unit_data[unit_num].link = -1;
378 }
379 }
380
381 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)
*/
382 {
383 #if defined(THREADZ)
384 lock_mem_wr ();
385 #endif /* if defined(THREADZ) */
386 M [addr] = data & DMASK;
387 #if defined(THREADZ)
388 unlock_mem ();
389 #endif /* if defined(THREADZ) */
390 }
391
392 //
393 // Convert virtual address to physical
394 //
395
396 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)
*/
397 {
398 uint pageTable = ptPtr * 64u;
399 uint l66AddressPage = l66Address / 1024u;
400
401 word36 ptw;
402 fnp_core_read (pageTable + l66AddressPage, & ptw, "fnpIOMCmd get ptw");
403 uint page = getbits36_14 (ptw, 4);
404 uint addr = page * 1024u + l66Address % 1024u;
405 return addr;
406 }
407
408 //
409 // udp packets
410 //
411 // pkt[0] = cmd
412 // cmd 1 - bootload
413 //
414
415 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)
*/
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 (void)fprintf (stderr, "udp_send failed\r\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\r\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\r\n", cell);
498 sim_printf ("fnp illegal cell number %d\r\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"\r\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??\r\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 // /* better declaration than the one used when MCS is running */
731 // dcl 1 a_dia_pcw aligned based (mbxp),
732 // 2 address fixed bin (18) unsigned unaligned,
733 // 2 error bit (1) unaligned,
734 // 2 pad1 bit (3) unaligned,
735 // 2 parity bit (1) unaligned,
736 // 2 pad2 bit (1) unaligned,
737 // /* if we used address extension this would be important */
738 // 2 pad3 bit (3) unaligned,
739 // 2 interrupt_level fixed bin (3) unsigned unaligned,
740 // 2 command bit (6) unaligned;
741 //
742 // a_dia_pcw.address = address;
743 //
744
745 //word24 L66Addr = (word24) getbits36_18 (dia_pcw, 0);
746 //sim_printf ("L66 xfer\r\n");
747 //sim_printf ("PCW %012"PRIo64"\r\n", dia_pcw);
748 //sim_printf ("L66Addr %08o\r\n", L66Addr);
749 //sim_printf ("M[] %012"PRIo64"\r\n", M[L66Addr]);
750
751 // 'dump_mpx d'
752 //L66 xfer
753 //PCW 022002000075
754 //L66Addr 00022002
755 //M[] 000000401775
756 //L66 xfer
757 //PCW 022002000075
758 //L66Addr 00022002
759 //M[] 003772401775
760 //L66 xfer
761 //PCW 022002000075
762 //L66Addr 00022002
763 //M[] 007764401775
764 //
765 // The contents of M seem much more reasonable, bit still don't match
766 // fnp_util$setup_dump_ctl_word. The left octet should be '1', not '0';
767 // bit 18 should be 0 not 1. But the offsets and tallies match exactly.
768 // Huh... Looking at 'dump_6670_control' control instead, it matches
769 // correctly. Apparently fnp_util thinks the FNP is a 6670, not a 335.
770 // I can't decipher the call path, so I don't know why; but looking at
771 // multiplexer_types.incl.pl1, I would guess that by MR12.x, all FNPs
772 // were 6670s.
773 //
774 // So:
775 //
776 // dcl 1 dump_6670_control aligned based (data_ptr),
777 // /* word used to supply DIA address and tally for fdump */
778 // 2 fnp_address fixed bin (18) unsigned unaligned,
779 // 2 unpaged bit (1) unaligned,
780 // 2 mbz bit (5) unaligned,
781 // 2 tally fixed bin (12) unsigned unaligned;
782
783 // Since the data is marked 'paged', and I don't understand the
784 // the paging mechanism or parameters, I'm going to punt here and
785 // not actually transfer any data.
786 sim_debug (DBG_TRACE, & dia_dev, "FNP data xfer??\r\n");
787 }
788 else
789 {
790 sim_warn ("bogus fnp command %d (%o)\r\n", command, command);
791 ok = false;
792 }
793
794 if (ok)
795 {
796 //if_sim_debug (DBG_TRACE, & fnp_dev) dmpmbx (dudp->mailbox_address);
797 fnp_core_write (dudp -> mailbox_address, 0, "dia_iom_cmd clear dia_pcw");
798 putbits36_1 (& bootloadStatus, 0, 1); // real_status = 1
799 putbits36_3 (& bootloadStatus, 3, 0); // major_status = BOOTLOAD_OK;
800 putbits36_8 (& bootloadStatus, 9, 0); // substatus = BOOTLOAD_OK;
801 putbits36_17 (& bootloadStatus, 17, 0); // channel_no = 0;
802 fnp_core_write (dudp -> mailbox_address + 6, bootloadStatus,
803 "dia_iom_cmd set bootload status");
804 }
805 else
806 {
807 //dmpmbx (dudp->mailbox_address);
808 sim_printf ("%s not ok\r\n", __func__);
809 // 3 error bit (1) unaligned, /* set to "1"b if error on connect */
810 putbits36_1 (& dia_pcw, 18, 1); // set bit 18
811 fnp_core_write (dudp -> mailbox_address, dia_pcw, "dia_iom_cmd set error bit");
812 }
813 }
814
815 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)
*/
816 {
817 iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
818 p -> stati = 0;
819 //sim_printf ("fnp cmd %d\r\n", p -> IDCW_DEV_CMD);
820 switch (p -> IDCW_DEV_CMD)
821 {
822 case 000: // CMD 00 Request status
823 {
824 p -> stati = 04000;
825 sim_debug (DBG_NOTIFY, & dia_dev, "Request status\r\n");
826 }
827 break;
828
829 default:
830 {
831 p -> stati = 04501;
832 p -> chanStatus = chanStatIncorrectDCW;
833 if (p->IDCW_DEV_CMD != 051) // ignore bootload console probe
834 sim_warn ("dia daze %o\r\n", p->IDCW_DEV_CMD);
835 }
836 return IOM_CMD_ERROR;
837 }
838
839 processMBX (iom_unit_idx, chan);
840
841 return IOM_CMD_NO_DCW; // did command, don't want more
842 }
843
844 /*
845 * dia_iom_cmd()
846 *
847 */
848
849 // 1 ignored command
850 // 0 ok
851 // -1 problem
852
853 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)
*/
854 {
855 sim_printf ("dia_iom_cmd %u %u\r\n", iom_unit_idx, chan);
856 iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
857 // Is it an IDCW?
858
859 if (IS_IDCW (p))
860 {
861 return dia_cmd (iom_unit_idx, chan);
862 }
863 // else // DDCW/TDCW
864 sim_printf ("%s expected IDCW\r\n", __func__);
865 return -1;
866 }
867
868 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)
*/
869 {
870 // pg 45:
871
872 // 3.8 BOOTLOAD OF L6 BY L66
873 //...
874 // The receipt of the Input Stored Boot order causes the coupler,
875 // if the Stored Boot but is ONE, to input data into L6 memory as
876 // specified by the L66 Bootload order. On completion of this,
877 // the Stored Boot bit is cleared.
878 //
879 // ... the PROM program issues the Input Stored Boot IOLD order
880 // to the coupler..
881 //
882 // ... the L66 Bootload command specifies the L6 memory locations into
883 // which the load from L66 is to occur and the extent of the lod;
884 // location (0100)16 in L6 would always be the first location to be
885 // executed by L6 after the load from L66 assuming that the L66
886 // bootload is independent of the mechanization used in L66
887
888 sim_printf ("got load_stored_boot\r\n");
889 }
890
891 #define psz 17000
892 static uint8_t pkt[psz];
893
894 // warning: returns ptr to static buffer
895 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)
*/
896 {
897 int sz = dn_udp_receive (dia_data.dia_unit_data[unitno].link, pkt, psz);
898 if (sz < 0)
899 {
900 sim_printf ("dn_udp_receive failed: %d\r\n", sz);
901 sz = 0;
902 }
903 * pktp = pkt;
904 return sz;
905 }
906
907 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)
*/
908 {
909 // XXX remember
910 // XXX //dudp -> fnpIsRunning = true;
911 // XXX when bootload complete!
912
913 uint8_t * pktp;
914 int sz = poll_coupler (unit_num, & pktp);
915 //sim_printf ("poll_coupler return %d\r\n", sz);
916 if (! sz)
917 {
918 return;
919 }
920
921 uint8_t cmd = pktp [0];
922 switch (cmd)
923 {
924 // IO Load Stored Boot
925 case dn_cmd_ISB_IOLD:
926 {
927 load_stored_boot ();
928 break;
929 }
930
931 default:
932 {
933 sim_printf ("%s got unhandled cmd %u\r\n", __func__, cmd);
934 break;
935 }
936 }
937 }
938
939 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)
*/
940 {
941 for (uint unit_num = 0; unit_num < N_DIA_UNITS_MAX; unit_num ++)
942 {
943 if (dia_data.dia_unit_data[unit_num].link >= 0)
944 dia_unit_process_events (unit_num);
945 }
946 }