1 /*
2  * Copyright (c) 2007-2013 Michael Mondy
3  * Copyright (c) 2012-2016 Harry Reed
4  * Copyright (c) 2013-2016 Charles Anthony
5  * Copyright (c) 2021 The DPS8M Development Team
6  *
7  * All rights reserved.
8  *
9  * This software is made available under the terms of the ICU
10  * License, version 1.8.1 or later.  For more details, see the
11  * LICENSE.md file at the top-level directory of this distribution.
12  */
13 
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <unistd.h>
17 
18 #include "dps8.h"
19 #include "dps8_iom.h"
20 #include "dps8_urp.h"
21 #include "dps8_sys.h"
22 #include "dps8_faults.h"
23 #include "dps8_scu.h"
24 #include "dps8_cable.h"
25 #include "dps8_cpu.h"
26 #include "dps8_utils.h"
27 
28 #define DBG_CTR 1
29 
30 //-- // XXX We use this where we assume there is only one unit
31 //-- #define ASSUME0 0
32 //--
33 
34  /*
35   * Copyright (c) 2007-2013 Michael Mondy
36   *
37   * This software is made available under the terms of the ICU
38   * License, version 1.8.1 or later.  For more details, see the
39   * LICENSE.md file at the top-level directory of this distribution.
40   */
41 
42 #define N_PRU_UNITS 1 // default
43 
44 static struct urpState
45   {
46     enum urpMode
47       {
48         urpNoMode, urpSetDiag, urpInitRdData
49       } ioMode;
50     char deviceName [MAX_DEV_NAME_LEN];
51   } urpState [N_URP_UNITS_MAX];
52 
53 #define UNIT_FLAGS ( UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | \
54                      UNIT_IDLE )
55 UNIT urp_unit [N_URP_UNITS_MAX] = {
56 #ifdef NO_C_ELLIPSIS
57   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
58   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
59   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
60   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
61   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
62   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
63   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
64   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
65   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
66   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
67   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
68   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
69   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
70   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
71   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
72   { UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
73 #else
74   [0 ... N_URP_UNITS_MAX-1] = {
75     UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
76   }
77 #endif
78 };
79 
80 #define URPUNIT_NUM(uptr) ((uptr) - urp_unit)
81 
82 static DEBTAB urp_dt [] =
83   {
84     { "NOTIFY", DBG_NOTIFY, NULL },
85     { "INFO", DBG_INFO, NULL },
86     { "ERR", DBG_ERR, NULL },
87     { "WARN", DBG_WARN, NULL },
88     { "DEBUG", DBG_DEBUG, NULL },
89     { "TRACE", DBG_TRACE, NULL },
90     { "ALL", DBG_ALL, NULL }, // don't move as it messes up DBG message
91     { NULL, 0, NULL }
92   };
93 
urpShowUnits(UNUSED FILE * st,UNUSED UNIT * uptr,UNUSED int val,UNUSED const void * desc)94 static t_stat urpShowUnits (UNUSED FILE * st, UNUSED UNIT * uptr, UNUSED int val, UNUSED const void * desc)
95   {
96     sim_printf("Number of URPunits in system is %d\n", urp_dev.numunits);
97     return SCPE_OK;
98   }
99 
urpSetUnits(UNUSED UNIT * uptr,UNUSED int32 value,const char * cptr,UNUSED void * desc)100 static t_stat urpSetUnits (UNUSED UNIT * uptr, UNUSED int32 value, const char * cptr, UNUSED void * desc)
101   {
102     if (! cptr)
103       return SCPE_ARG;
104     int n = atoi (cptr);
105     if (n < 1 || n > N_URP_UNITS_MAX)
106       return SCPE_ARG;
107     urp_dev.numunits = (uint32) n;
108     return SCPE_OK;
109   }
110 
urpShowDeviceName(UNUSED FILE * st,UNIT * uptr,UNUSED int val,UNUSED const void * desc)111 static t_stat urpShowDeviceName (UNUSED FILE * st, UNIT * uptr, UNUSED int val, UNUSED const void * desc)
112   {
113     int n = (int) URPUNIT_NUM (uptr);
114     if (n < 0 || n >= N_URP_UNITS_MAX)
115       return SCPE_ARG;
116     sim_printf ("Unit record processor device name is %s\n", urpState[n].deviceName);
117     return SCPE_OK;
118   }
119 
urpSetDeviceName(UNUSED UNIT * uptr,UNUSED int32 value,UNUSED const char * cptr,UNUSED void * desc)120 static t_stat urpSetDeviceName (UNUSED UNIT * uptr, UNUSED int32 value, UNUSED const char * cptr, UNUSED void * desc)
121   {
122     int n = (int) URPUNIT_NUM (uptr);
123     if (n < 0 || n >= N_URP_UNITS_MAX)
124       return SCPE_ARG;
125     if (cptr)
126       {
127         strncpy (urpState[n].deviceName, cptr, MAX_DEV_NAME_LEN - 1);
128         urpState[n].deviceName[MAX_DEV_NAME_LEN - 1] = 0;
129       }
130     else
131       urpState[n].deviceName [0] = 0;
132     return SCPE_OK;
133   }
134 
135 #define UNIT_WATCH UNIT_V_UF
136 
137 static MTAB urp_mod [] =
138   {
139     { UNIT_WATCH, 1, "WATCH", "WATCH", 0, 0, NULL, NULL },
140     { UNIT_WATCH, 0, "NOWATCH", "NOWATCH", 0, 0, NULL, NULL },
141     {
142       MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_VALR, /* mask */
143       0,            /* match */
144       "NUNITS",     /* print string */
145       "NUNITS",         /* match string */
146       urpSetUnits, /* validation routine */
147       urpShowUnits, /* display routine */
148       "Number of URPunits in the system", /* value descriptor */
149       NULL // Help
150     },
151     {
152       MTAB_XTD | MTAB_VUN | MTAB_VALR | MTAB_NC, /* mask */
153       0,            /* match */
154       "NAME",     /* print string */
155       "NAME",         /* match string */
156       urpSetDeviceName, /* validation routine */
157       urpShowDeviceName, /* display routine */
158       "Set the device name", /* value descriptor */
159       NULL          // help
160     },
161 
162     { 0, 0, NULL, NULL, 0, 0, NULL, NULL }
163   };
164 
urpReset(UNUSED DEVICE * dptr)165 static t_stat urpReset (UNUSED DEVICE * dptr)
166   {
167     return SCPE_OK;
168   }
169 
170 
171 DEVICE urp_dev = {
172     "URP",       /*  name */
173     urp_unit,    /* units */
174     NULL,         /* registers */
175     urp_mod,     /* modifiers */
176     N_PRU_UNITS, /* #units */
177     10,           /* address radix */
178     24,           /* address width */
179     1,            /* address increment */
180     8,            /* data radix */
181     36,           /* data width */
182     NULL,         /* examine */
183     NULL,         /* deposit */
184     urpReset,   /* reset */
185     NULL,         /* boot */
186     NULL,         /* attach */
187     NULL,         /* detach */
188     NULL,         /* context */
189     DEV_DEBUG,    /* flags */
190     0,            /* debug control flags */
191     urp_dt,      /* debug flag names */
192     NULL,         /* memory size change */
193     NULL,         /* logical name */
194     NULL,         // help
195     NULL,         // attach help
196     NULL,         // attach context
197     NULL,         // description
198     NULL
199 };
200 
201 /*
202  * urp_init()
203  *
204  */
205 
206 // Once-only initialization
207 
urp_init(void)208 void urp_init (void)
209   {
210     memset (urpState, 0, sizeof (urpState));
211   }
212 
213 
urpCmd(uint iomUnitIdx,uint chan)214 static iom_cmd_rc_t urpCmd (uint iomUnitIdx, uint chan) {
215   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
216 #ifdef TESTING
217   if_sim_debug (DBG_TRACE, & urp_dev) dumpDCW (p->DCW, 0);
218 #endif
219  uint ctlrUnitIdx = get_ctlr_idx (iomUnitIdx, chan);
220  uint devUnitIdx = cables->urp_to_urd[ctlrUnitIdx][p->IDCW_DEV_CODE].unit_idx;
221  //UNIT * unitp = & urp_unit [devUnitIdx];
222  //int urp_unit_num = (int) URPUNIT_NUM (unitp);
223  struct urpState * statep = & urpState[devUnitIdx];
224 
225  // IDCW?
226  if (IS_IDCW (p)) {
227     // IDCW
228     statep->ioMode = urpNoMode;
229 
230     switch (p->IDCW_DEV_CMD) {
231       case 000: // CMD 00 Request status
232         if_sim_debug (DBG_TRACE, & urp_dev) { sim_printf ("// URP Request Status\r\n"); }
233         sim_debug (DBG_DEBUG, & urp_dev, "%s: Request Status\n", __func__);
234         p->stati = 04000;
235         break;
236 
237       case 006: // CMD 005 Initiate read data xfer (load_mpc.pl1)
238         if_sim_debug (DBG_TRACE, & urp_dev) { sim_printf ("// URP Initiate Read Data Xfer\r\n"); }
239         sim_debug (DBG_DEBUG, & urp_dev, "%s: Initiate Read Data Xfer\n", __func__);
240         statep->ioMode = urpInitRdData;
241         p->stati = 04000;
242         break;
243 
244 // 011 punch binary
245 // 031 set diagnostic mode
246 
247       case 031: // CMD 031 Set Diagnostic Mode (load_mpc.pl1)
248         if_sim_debug (DBG_TRACE, & urp_dev) { sim_printf ("// URP Set Diagnostic Mode\r\n"); }
249         sim_debug (DBG_DEBUG, & urp_dev, "%s: Set Diagnostic Mode\n", __func__);
250         statep->ioMode = urpSetDiag;
251         p->stati = 04000;
252         break;
253 
254       case 040: // CMD 40 Reset status
255         if_sim_debug (DBG_TRACE, & urp_dev) { sim_printf ("// URP Reset Status\r\n"); }
256         sim_debug (DBG_DEBUG, & urp_dev, "%s: Reset Status\n", __func__);
257         p->stati = 04000;
258         p->isRead = false;
259         break;
260 
261       default:
262         if_sim_debug (DBG_TRACE, & urp_dev) { sim_printf ("// URP unknown command %o\r\n", p->IDCW_DEV_CMD); }
263         p->stati = 04501; // cmd reject, invalid opcode
264         p->chanStatus = chanStatIncorrectDCW;
265         if (p->IDCW_DEV_CMD != 051) // ignore bootload console probe
266           sim_warn ("%s: URP unrecognized device command  %02o\n", __func__, p->IDCW_DEV_CMD);
267         return IOM_CMD_ERROR;
268     } // switch IDCW_DEV_CMD
269 
270     sim_debug (DBG_DEBUG, & urp_dev, "%s: stati %04o\n", __func__, p->stati);
271     return IOM_CMD_PROCEED;
272   } // if IDCW
273 
274   // Not IDCW; TDCW are captured in IOM, so must be IOTD, IOTP or IOTNP
275   switch (statep->ioMode) {
276     case urpNoMode:
277       if_sim_debug (DBG_TRACE, & urp_dev) { sim_printf ("// URP IOT no mode\r\n"); }
278       //sim_printf ("%s: Unexpected IOTx\n", __func__);
279       //sim_warn ("%s: Unexpected IOTx\n", __func__);
280       //return IOM_CMD_ERROR;
281       break;
282 
283     case urpSetDiag:
284       if_sim_debug (DBG_TRACE, & urp_dev) { sim_printf ("// URP IOT Set Diag\r\n"); }
285       // We don't use the DDCW, so just pretend we do. BUG
286       p->stati = 04000;
287       break;
288 
289     case urpInitRdData:
290       if_sim_debug (DBG_TRACE, & urp_dev) { sim_printf ("// URP IOT Init Rd Data\r\n"); }
291       // We don't use the DDCW, so just pretend we do. BUG
292       p->stati = 04000;
293       break;
294 
295      default:
296       if_sim_debug (DBG_TRACE, & urp_dev) { sim_printf ("// URP IOT unkown %d\r\n", statep->ioMode); }
297       sim_warn ("%s: Unrecognized ioMode %d\n", __func__, statep->ioMode);
298       return IOM_CMD_ERROR;
299   }
300   return IOM_CMD_PROCEED;
301 }
302 
urp_iom_cmd(uint iomUnitIdx,uint chan)303 iom_cmd_rc_t urp_iom_cmd (uint iomUnitIdx, uint chan) {
304   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
305   uint devCode = p->IDCW_DEV_CODE;
306   if (devCode == 0)
307     return urpCmd (iomUnitIdx, chan);
308   uint urpUnitIdx = cables->iom_to_ctlr[iomUnitIdx][chan].ctlr_unit_idx;
309   iom_cmd_t * cmd = cables->urp_to_urd[urpUnitIdx][devCode].iom_cmd;
310   if (! cmd) {
311     //sim_warn ("URP can't find device handler\n");
312     //return IOM_CMD_ERROR;
313     p->stati = 04502; // invalid device code
314     return IOM_CMD_DISCONNECT;
315   }
316   return cmd (iomUnitIdx, chan);
317 }
318