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