1 /*
2  * Copyright (c) 2007-2013 Michael Mondy
3  * Copyright (c) 2015-2018 Charles Anthony
4  * Copyright (c) 2021 The DPS8M Development Team
5  *
6  * All rights reserved.
7  *
8  * This software is made available under the terms of the ICU
9  * License, version 1.8.1 or later.  For more details, see the
10  * LICENSE.md file at the top-level directory of this distribution.
11  */
12 
13 #include <stdio.h>
14 #include <ctype.h>
15 #include <unistd.h>
16 #include <stdint.h>
17 
18 #include "dps8.h"
19 #include "dps8_iom.h"
20 #include "dps8_absi.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 #include "udplib.h"
29 
30 #define DBG_CTR 1
31 
32 static struct absi_state
33   {
34     char device_name [MAX_DEV_NAME_LEN];
35     int link;
36   } absi_state [N_ABSI_UNITS_MAX];
37 
38 #define N_ABSI_UNITS 1 // default
39 
40 #define UNIT_FLAGS ( UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | \
41                      UNIT_IDLE )
42 UNIT absi_unit[N_ABSI_UNITS_MAX] =
43   {
44     {UDATA (NULL, UNIT_FLAGS, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL}
45   };
46 
47 #define ABSI_UNIT_IDX(uptr) ((uptr) - absi_unit)
48 
49 static DEBTAB absi_dt[] =
50   {
51     { "NOTIFY", DBG_NOTIFY, NULL },
52     { "INFO", DBG_INFO, NULL },
53     { "ERR", DBG_ERR, NULL },
54     { "WARN", DBG_WARN, NULL },
55     { "DEBUG", DBG_DEBUG, NULL },
56     { "ALL", DBG_ALL, NULL }, // don't move as it messes up DBG message
57     { NULL, 0, NULL }
58   };
59 
absi_show_nunits(UNUSED FILE * st,UNUSED UNIT * uptr,UNUSED int val,UNUSED const void * desc)60 static t_stat absi_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
61                                 UNUSED int val, UNUSED const void * desc)
62   {
63     sim_printf ("Number of ABSIunits in system is %d\n", absi_dev.numunits);
64     return SCPE_OK;
65   }
66 
absi_set_nunits(UNUSED UNIT * uptr,UNUSED int32 value,const char * cptr,UNUSED void * desc)67 static t_stat absi_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
68                                const char * cptr, UNUSED void * desc)
69   {
70     if (! cptr)
71       return SCPE_ARG;
72     int n = atoi (cptr);
73     if (n < 1 || n > N_ABSI_UNITS_MAX)
74       return SCPE_ARG;
75     absi_dev.numunits = (uint32) n;
76     return SCPE_OK;
77   }
78 
absi_show_device_name(UNUSED FILE * st,UNIT * uptr,UNUSED int val,UNUSED const void * desc)79 static t_stat absi_show_device_name (UNUSED FILE * st, UNIT * uptr,
80                                     UNUSED int val, UNUSED const void * desc)
81   {
82     int n = (int) ABSI_UNIT_IDX (uptr);
83     if (n < 0 || n >= N_ABSI_UNITS_MAX)
84       return SCPE_ARG;
85     sim_printf("Controller device name is %s\n", absi_state [n].device_name);
86     return SCPE_OK;
87   }
88 
absi_set_device_name(UNIT * uptr,UNUSED int32 value,const char * cptr,UNUSED void * desc)89 static t_stat absi_set_device_name (UNIT * uptr, UNUSED int32 value,
90                                    const char * cptr, UNUSED void * desc)
91   {
92     int n = (int) ABSI_UNIT_IDX (uptr);
93     if (n < 0 || n >= N_ABSI_UNITS_MAX)
94       return SCPE_ARG;
95     if (cptr)
96       {
97         strncpy (absi_state[n].device_name, cptr, MAX_DEV_NAME_LEN-1);
98         absi_state[n].device_name[MAX_DEV_NAME_LEN-1] = 0;
99       }
100     else
101       absi_state[n].device_name[0] = 0;
102     return SCPE_OK;
103   }
104 
105 
106 #define UNIT_WATCH UNIT_V_UF
107 
108 static MTAB absi_mod[] =
109   {
110     { UNIT_WATCH, 1, "WATCH", "WATCH", 0, 0, NULL, NULL },
111     { UNIT_WATCH, 0, "NOWATCH", "NOWATCH", 0, 0, NULL, NULL },
112     {
113       MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_VALR, /* mask */
114       0,                /* match */
115       "NUNITS",         /* print string */
116       "NUNITS",         /* match string */
117       absi_set_nunits,  /* validation routine */
118       absi_show_nunits, /* display routine */
119       "Number of ABSIunits in the system", /* value descriptor */
120       NULL /* Help */
121     },
122     {
123       MTAB_XTD | MTAB_VUN | MTAB_VALR | MTAB_NC, /* mask */
124       0,                     /* match */
125       "NAME",                /* print string */
126       "NAME",                /* match string */
127       absi_set_device_name,  /* validation routine */
128       absi_show_device_name, /* display routine */
129       "Set the device name", /* value descriptor */
130       NULL /* Help */
131     },
132     MTAB_eol
133   };
134 
absi_reset(UNUSED DEVICE * dptr)135 static t_stat absi_reset (UNUSED DEVICE * dptr)
136   {
137     //absiResetRX (0);
138     //absiResetTX (0);
139     return SCPE_OK;
140   }
141 
absiAttach(UNIT * uptr,const char * cptr)142 static t_stat absiAttach (UNIT * uptr, const char * cptr)
143   {
144     if (! cptr)
145       return SCPE_ARG;
146     int unitno = (int) (uptr - absi_unit);
147 
148     //    ATTACH HIn llll:w.x.y.z:rrrr - connect via UDP to a remote simh host
149 
150     t_stat ret;
151     char * pfn;
152     //uint16 imp = 0; // we only support a single attachment to a single IMP
153 
154     // If we're already attached, then detach ...
155     if ((uptr->flags & UNIT_ATT) != 0)
156       detach_unit (uptr);
157 
158     // Make a copy of the "file name" argument.  udp_create() actually modifies
159     // the string buffer we give it, so we make a copy now so we'll have
160     // something to display in the "SHOW HIn ..." command.
161     pfn = (char *) calloc ((CBUFSIZE + 1), sizeof (char));
162     if (pfn == NULL)
163       return SCPE_MEM;
164     strncpy (pfn, cptr, CBUFSIZE);
165 
166     // Create the UDP connection.
167     ret = udp_create (cptr, & absi_state[unitno].link);
168     if (ret != SCPE_OK)
169       {
170         free (pfn);
171         return ret;
172       }
173 
174     uptr->flags |= UNIT_ATT;
175     uptr->filename = pfn;
176     return SCPE_OK;
177   }
178 
179 // Detach (connect) ...
absiDetach(UNIT * uptr)180 static t_stat absiDetach (UNIT * uptr)
181   {
182     int unitno = (int) (uptr - absi_unit);
183     t_stat ret;
184     if ((uptr->flags & UNIT_ATT) == 0)
185       return SCPE_OK;
186     if (absi_state[unitno].link == NOLINK)
187       return SCPE_OK;
188 
189     ret = udp_release (absi_state[unitno].link);
190     if (ret != SCPE_OK)
191       return ret;
192     absi_state[unitno].link = NOLINK;
193     uptr->flags &= ~ (unsigned int) UNIT_ATT;
194     free (uptr->filename);
195     uptr->filename = NULL;
196     return SCPE_OK;
197   }
198 
199 
200 DEVICE absi_dev = {
201     "ABSI",       /* name */
202     absi_unit,    /* units */
203     NULL,         /* registers */
204     absi_mod,     /* modifiers */
205     N_ABSI_UNITS, /* #units */
206     10,           /* address radix */
207     24,           /* address width */
208     1,            /* address increment */
209     8,            /* data radix */
210     36,           /* data width */
211     NULL,         /* examine */
212     NULL,         /* deposit */
213     absi_reset,   /* reset */
214     NULL,         /* boot */
215     absiAttach,   /* attach */
216     absiDetach,   /* detach */
217     NULL,         /* context */
218     DEV_DEBUG,    /* flags */
219     0,            /* debug control flags */
220     absi_dt,      /* debug flag names */
221     NULL,         /* memory size change */
222     NULL,         /* logical name */
223     NULL,         // help
224     NULL,         // attach help
225     NULL,         // attach context
226     NULL,         // description
227     NULL
228 };
229 
230 /*
231  * absi_init()
232  */
233 
234 // Once-only initialization
235 
absi_init(void)236 void absi_init (void)
237   {
238     memset (absi_state, 0, sizeof (absi_state));
239     for (int i = 0; i < N_ABSI_UNITS_MAX; i ++)
240       absi_state[i].link = NOLINK;
241   }
242 
absi_cmd(uint iomUnitIdx,uint chan)243 static iom_cmd_rc_t absi_cmd (uint iomUnitIdx, uint chan)
244   {
245     iom_chan_data_t * p = &iom_chan_data[iomUnitIdx][chan];
246 // sim_printf ("absi_cmd CHAN_CMD %o DEV_CODE %o DEV_CMD %o COUNT %o\n",
247 //p->IDCW_CHAN_CMD, p->IDCW_DEV_CODE, p->IDCW_DEV_CMD, p->IDCW_COUNT);
248     sim_debug (DBG_TRACE, & absi_dev,
249                "absi_cmd CHAN_CMD %o DEV_CODE %o DEV_CMD %o COUNT %o\n",
250                p->IDCW_CHAN_CMD, p->IDCW_DEV_CODE, p->IDCW_DEV_CMD,
251                p->IDCW_COUNT);
252 
253 
254     // Not IDCW?
255     if (IS_NOT_IDCW (p))
256       {
257         sim_warn ("%s: Unexpected IOTx\n", __func__);
258         return IOM_CMD_ERROR;
259       }
260 
261     switch (p->IDCW_DEV_CMD)
262       {
263         case 000: // CMD 00 Request status
264           {
265             p->stati = 04000;
266 sim_printf ("absi request status\n");
267           }
268           break;
269 
270         case 001: // CMD 01 Read
271           {
272             p->stati = 04000;
273 sim_printf ("absi read\n");
274           }
275           break;
276 
277         case 011: // CMD 11 Write
278           {
279             p->stati = 04000;
280 sim_printf ("absi write\n");
281           }
282           break;
283 
284         case 020: // CMD 20 Host switch down
285           {
286             p->stati = 04000;
287 sim_printf ("absi host switch down\n");
288           }
289           break;
290 
291         case 040: // CMD 40 Reset status
292           {
293             p->stati = 04000;
294 sim_printf ("absi reset status\n");
295           }
296           break;
297 
298         case 060: // CMD 60 Host switch up
299           {
300             p->stati = 04000;
301 sim_printf ("absi host switch up\n");
302           }
303           break;
304 
305         default:
306           {
307             if (p->IDCW_DEV_CMD != 051) // ignore bootload console probe
308               sim_warn ("%s: ABSI unrecognized device command  %02o\n", __func__, p->IDCW_DEV_CMD);
309             p->stati = 04501; // cmd reject, invalid opcode
310             p->chanStatus = chanStatIncorrectDCW;
311           }
312           return IOM_CMD_ERROR;
313       }
314 
315     if (p->IDCW_CHAN_CMD == 0)
316       return IOM_CMD_DISCONNECT; // don't do DCW list
317     return IOM_CMD_PROCEED;
318   }
319 
320 // 1 ignored command
321 // 0 ok
322 // -1 problem
absi_iom_cmd(uint iomUnitIdx,uint chan)323 iom_cmd_rc_t absi_iom_cmd (uint iomUnitIdx, uint chan)
324   {
325     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
326 // Is it an IDCW?
327 
328     if (IS_IDCW (p))
329       {
330         return absi_cmd (iomUnitIdx, chan);
331       }
332     sim_printf ("%s expected IDCW\n", __func__);
333     return IOM_CMD_ERROR;
334   }
335 
absi_process_event(void)336 void absi_process_event (void)
337   {
338 #define psz 17000
339     uint16_t pkt[psz];
340     for (uint32 unit = 0; unit < absi_dev.numunits; unit ++)
341       {
342         if (absi_state[unit].link == NOLINK)
343           continue;
344         //int sz = udp_receive ((int) unit, pkt, psz);
345         int sz = udp_receive (absi_state[unit].link, pkt, psz);
346         if (sz < 0)
347           {
348             fprintf (stderr, "udp_receive failed\n");
349           }
350         else if (sz == 0)
351           {
352             //fprintf (stderr, "udp_receive 0\n");
353           }
354         else
355           {
356             for (int i = 0; i < sz; i ++)
357               {
358                 fprintf (stderr, "  %06o  %04x  ", pkt[i], pkt[i]);
359                 for (int b = 0; b < 16; b ++)
360                   fprintf (stderr, "%c", pkt[i] & (1 << (16 - b)) ? '1' : '0');
361                 fprintf (stderr, "\n");
362               }
363             // Send a NOP reply
364             //int16_t reply[2] = 0x0040
365             int rc = udp_send (absi_state[unit].link, pkt, (uint16_t) sz,
366                                PFLG_FINAL);
367             if (rc < 0)
368               {
369                 fprintf (stderr, "udp_send failed\n");
370               }
371           }
372       }
373   }
374