root/src/dps8/dps8_dia.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fnp_core_read
  2. set_config
  3. show_config
  4. show_status
  5. show_nunits
  6. set_nunits
  7. reset
  8. attach
  9. detach
  10. dia_init
  11. fnp_core_write
  12. virtToPhys
  13. cmd_bootload
  14. interruptL66
  15. processMBX
  16. dia_cmd
  17. dia_iom_cmd
  18. load_stored_boot
  19. poll_coupler
  20. dia_unit_process_events
  21. dia_process_events

   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][next][first][last][top][bottom][index][help] */
  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][next][first][last][top][bottom][index][help] */
  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\n",
  87                 (long) dia_unit_idx);
  88         sim_printf ("error: DIA SET CONFIG: Invalid unit number %ld\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>\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][next][first][last][top][bottom][index][help] */
 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\n", (long) unit_idx);
 134         sim_printf ("error: Invalid unit number %ld\n", (long) unit_idx);
 135         return SCPE_ARG;
 136       }
 137 
 138     sim_printf ("DIA unit number %ld\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)\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][next][first][last][top][bottom][index][help] */
 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\n", (long) dia_unit_idx);
 154         sim_printf ("error: Invalid unit number %ld\n", (long) dia_unit_idx);
 155         return SCPE_ARG;
 156       }
 157 
 158     sim_printf ("DIA unit number %ld\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\n", dudp->mailbox_address);
 162     return SCPE_OK;
 163   }
 164 
 165 static t_stat show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
     /* [previous][next][first][last][top][bottom][index][help] */
 166                            UNUSED int val, UNUSED const void * desc)
 167   {
 168     sim_printf("Number of DIA units in system is %d\n", dia_dev.numunits);
 169     return SCPE_OK;
 170   }
 171 
 172 static t_stat set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
     /* [previous][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 241   {
 242     return SCPE_OK;
 243   }
 244 
 245 static t_stat attach (UNIT * uptr, const char * cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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\n");
 435       }
 436   }
 437 
 438 static int interruptL66 (uint iom_unit_idx, uint chan)
     /* [previous][next][first][last][top][bottom][index][help] */
 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][next][first][last][top][bottom][index][help] */
 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         //                      /* 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\n");
 747         //sim_printf ("PCW  %012"PRIo64"\n", dia_pcw);
 748         //sim_printf ("L66Addr %08o\n", L66Addr);
 749         //sim_printf ("M[] %012"PRIo64"\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??\n");
 787       }
 788     else
 789       {
 790         sim_warn ("bogus fnp command %d (%o)\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][next][first][last][top][bottom][index][help] */
 816   {
 817     iom_chan_data_t * p = & iom_chan_data[iom_unit_idx][chan];
 818     p -> stati = 0;
 819 //sim_printf ("fnp cmd %d\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\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\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][next][first][last][top][bottom][index][help] */
 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\n", __func__);
 865     return -1;
 866   }
 867 
 868 static void load_stored_boot (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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\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][next][first][last][top][bottom][index][help] */
 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\n", sz);
 901         sz = 0;
 902       }
 903     * pktp = pkt;
 904     return sz;
 905   }
 906 
 907 void dia_unit_process_events (uint unit_num)
     /* [previous][next][first][last][top][bottom][index][help] */
 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\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\n", __func__, cmd);
 934            break;
 935          }
 936      }
 937   }
 938 
 939 void dia_process_events (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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   }

/* [previous][next][first][last][top][bottom][index][help] */