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