First Commit of my working state
[simh.git] / SDS / sds_io.c
CommitLineData
196ba1fc
PH
1/* sds_io.c: SDS 940 I/O simulator\r
2\r
3 Copyright (c) 2001-2005, Robert M. Supnik\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of Robert M Supnik shall not be\r
23 used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from Robert M Supnik.\r
25*/\r
26\r
27#include "sds_defs.h"\r
28\r
29/* Data chain word */\r
30\r
31#define CHD_INT 040 /* int on chain */\r
32#define CHD_PAGE 037 /* new page # */\r
33\r
34/* Interlace POT */\r
35\r
36#define CHI_V_WC 14 /* word count */\r
37#define CHI_M_WC 01777\r
38#define CHI_GETWC(x) (((x) >> CHI_V_WC) & CHI_M_WC)\r
39#define CHI_V_MA 0 /* mem address */\r
40#define CHI_M_MA 037777\r
41#define CHI_GETMA(x) (((x) >> CHI_V_MA) & CHI_M_MA)\r
42\r
43/* System interrupt POT */\r
44\r
45#define SYI_V_GRP 18 /* group */\r
46#define SYI_M_GRP 077\r
47#define SYI_GETGRP(x) (((x) >> SYI_V_GRP) & SYI_M_GRP)\r
48#define SYI_DIS (1 << 17) /* disarm if 0 */\r
49#define SYI_ARM (1 << 16) /* arm if 1 */\r
50#define SYI_M_INT 0177777 /* interrupt */\r
51\r
52/* Pseudo-device number for EOM/SKS mode 3 */\r
53\r
54#define I_GETDEV3(x) ((((x) & 020046000) != 020046000)? ((x) & DEV_MASK): DEV_MASK)\r
55\r
56#define TST_XFR(d,c) (xfr_req && dev_map[d][c])\r
57#define SET_XFR(d,c) xfr_req = xfr_req | dev_map[d][c]\r
58#define CLR_XFR(d,c) xfr_req = xfr_req & ~dev_map[d][c]\r
59#define INV_DEV(d,c) (dev_dsp[d][c] == NULL)\r
60#define VLD_DEV(d,c) (dev_dsp[d][c] != NULL)\r
61#define TST_EOR(c) (chan_flag[c] & CHF_EOR)\r
62#define QAILCE(a) (((a) >= POT_ILCY) && ((a) < (POT_ILCY + NUM_CHAN)))\r
63\r
64uint8 chan_uar[NUM_CHAN]; /* unit addr */\r
65uint16 chan_wcr[NUM_CHAN]; /* word count */\r
66uint16 chan_mar[NUM_CHAN]; /* mem addr */\r
67uint8 chan_dcr[NUM_CHAN]; /* data chain */\r
68uint32 chan_war[NUM_CHAN]; /* word assembly */\r
69uint8 chan_cpw[NUM_CHAN]; /* char per word */\r
70uint8 chan_cnt[NUM_CHAN]; /* char count */\r
71uint16 chan_mode[NUM_CHAN]; /* mode */\r
72uint16 chan_flag[NUM_CHAN]; /* flags */\r
73static const char *chname[NUM_CHAN] = {\r
74 "W", "Y", "C", "D", "E", "F", "G", "H"\r
75 };\r
76\r
77extern uint32 M[MAXMEMSIZE]; /* memory */\r
78extern uint32 int_req; /* int req */\r
79extern uint32 xfr_req; /* xfer req */\r
80extern uint32 alert; /* pin/pot alert */\r
81extern uint32 X, EM2, EM3, OV, ion, bpt;\r
82extern uint32 nml_mode, usr_mode, rtc_pie;\r
83extern int32 stop_invins, stop_invdev, stop_inviop;\r
84extern int32 mon_usr_trap;\r
85extern UNIT cpu_unit;\r
86extern FILE *sim_log;\r
87extern DEVICE *sim_devices[];\r
88\r
89t_stat chan_reset (DEVICE *dptr);\r
90t_stat chan_read (int32 ch);\r
91t_stat chan_write (int32 ch);\r
92void chan_write_mem (int32 ch);\r
93void chan_flush_war (int32 ch);\r
94uint32 chan_mar_inc (int32 ch);\r
95t_stat chan_eor (int32 ch);\r
96t_stat pot_ilc (uint32 num, uint32 *dat);\r
97t_stat pot_dcr (uint32 num, uint32 *dat);\r
98t_stat pin_adr (uint32 num, uint32 *dat);\r
99t_stat pot_fork (uint32 num, uint32 *dat);\r
100t_stat dev_disc (uint32 ch, uint32 dev);\r
101t_stat dev_wreor (uint32 ch, uint32 dev);\r
102extern t_stat pot_RL1 (uint32 num, uint32 *dat);\r
103extern t_stat pot_RL2 (uint32 num, uint32 *dat);\r
104extern t_stat pot_RL4 (uint32 num, uint32 *dat);\r
105extern t_stat pin_rads (uint32 num, uint32 *dat);\r
106extern t_stat pot_rada (uint32 num, uint32 *dat);\r
107extern t_stat pin_dsk (uint32 num, uint32 *dat);\r
108extern t_stat pot_dsk (uint32 num, uint32 *dat);\r
109t_stat pin_mux (uint32 num, uint32 *dat);\r
110t_stat pot_mux (uint32 num, uint32 *dat);\r
111extern void set_dyn_map (void);\r
112\r
113/* SDS I/O model\r
114\r
115 A device is modeled by its interactions with a channel. Devices can only be\r
116 accessed via channels. Each channel has its own device address space. This\r
117 means devices can only be accessed from a specific channel.\r
118\r
119 I/O operations start with a channel connect. The EOM instruction is passed\r
120 to the device via the conn routine. This routine is also used for non-channel\r
121 EOM's to the device. For channel connects, the device must remember the\r
122 channel number.\r
123\r
124 The device responds (after a delay) by setting its XFR_RDY flag. This causes\r
125 the channel to invoke either the read or write routine (for input or output)\r
126 to get or put the next character. If the device is an asynchronous output\r
127 device, it calls routine chan_set_ordy to see if there is output available.\r
128 If there is, XFR_RDY is set; if not, the channel is marked to wake the\r
129 attached device when output is available. This prevents invalid rate errors.\r
130\r
131 Output may be terminated by a write end of record, a disconnect, or both.\r
132 Write end of record occurs when the word count reaches zero on an IORD or IORP\r
133 operation. It also occurs if a TOP instruction is issued. The device is\r
134 expected to respond by setting the end of record indicator in the channel,\r
135 which will in turn trigger an end of record interrupt.\r
136\r
137 When the channel operation completes, the channel disconnects and calls the\r
138 disconnect processor to perform any device specific cleanup. The differences\r
139 between write end of record and disconnect are subtle. On magtape output,\r
140 for example, both signal end of record; but write end of record allows the\r
141 magtape to continue moving, while disconnect halts its motion.\r
142\r
143 Valid devices supply a routine to handle potentially all I/O operations\r
144 (connect, disconnect, read, write, write end of record, sks). There are\r
145 separate routines for PIN and POT.\r
146\r
147 Channels could, optionally, handle 12b or 24b characters. The simulator can\r
148 support all widths.\r
149*/\r
150 \r
151t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc);\r
152\r
153struct aldisp {\r
154 t_stat (*pin) (uint32 num, uint32 *dat); /* altnum, *dat */\r
155 t_stat (*pot) (uint32 num, uint32 *dat); /* altnum, *dat */\r
156 };\r
157\r
158/* Channel data structures\r
159\r
160 chan_dev channel device descriptor\r
161 chan_unit channel unit descriptor\r
162 chan_reg channel register list\r
163*/\r
164\r
165UNIT chan_unit = { UDATA (NULL, 0, 0) };\r
166\r
167REG chan_reg[] = {\r
168 { BRDATA (UAR, chan_uar, 8, 6, NUM_CHAN) },\r
169 { BRDATA (WCR, chan_wcr, 8, 15, NUM_CHAN) },\r
170 { BRDATA (MAR, chan_mar, 8, 16, NUM_CHAN) },\r
171 { BRDATA (DCR, chan_dcr, 8, 6, NUM_CHAN) },\r
172 { BRDATA (WAR, chan_war, 8, 24, NUM_CHAN) },\r
173 { BRDATA (CPW, chan_cpw, 8, 2, NUM_CHAN) },\r
174 { BRDATA (CNT, chan_cnt, 8, 3, NUM_CHAN) },\r
175 { BRDATA (MODE, chan_mode, 8, 12, NUM_CHAN) },\r
176 { BRDATA (FLAG, chan_flag, 8, CHF_N_FLG, NUM_CHAN) },\r
177 { NULL }\r
178 };\r
179\r
180MTAB chan_mod[] = {\r
181 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_W, "W", NULL,\r
182 NULL, &chan_show_reg, NULL },\r
183 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_Y, "Y", NULL,\r
184 NULL, &chan_show_reg, NULL },\r
185 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_C, "C", NULL,\r
186 NULL, &chan_show_reg, NULL },\r
187 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_D, "D", NULL,\r
188 NULL, &chan_show_reg, NULL },\r
189 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_E, "E", NULL,\r
190 NULL, &chan_show_reg, NULL },\r
191 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_F, "F", NULL,\r
192 NULL, &chan_show_reg, NULL },\r
193 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_G, "G", NULL,\r
194 NULL, &chan_show_reg, NULL },\r
195 { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_H, "H", NULL,\r
196 NULL, &chan_show_reg, NULL }\r
197 };\r
198\r
199DEVICE chan_dev = {\r
200 "CHAN", &chan_unit, chan_reg, chan_mod,\r
201 1, 8, 8, 1, 8, 8,\r
202 NULL, NULL, &chan_reset,\r
203 NULL, NULL, NULL\r
204 };\r
205\r
206/* Tables */\r
207\r
208static const uint32 int_zc[8] = {\r
209 INT_WZWC, INT_YZWC, INT_CZWC, INT_DZWC,\r
210 INT_EZWC, INT_FZWC, INT_GZWC, INT_HZWC\r
211 };\r
212\r
213static const uint32 int_er[8] = {\r
214 INT_WEOR, INT_YEOR, INT_CEOR, INT_DEOR,\r
215 INT_EEOR, INT_FEOR, INT_GEOR, INT_HEOR\r
216 };\r
217\r
218/* dev_map maps device and channel numbers to a transfer flag masks */\r
219\r
220uint32 dev_map[64][NUM_CHAN];\r
221\r
222/* dev_dsp maps device and channel numbers to dispatch routines */\r
223\r
224t_stat (*dev_dsp[64][NUM_CHAN])() = { NULL };\r
225\r
226/* dev3_dsp maps system device numbers to dispatch routines */\r
227\r
228t_stat (*dev3_dsp[64])() = { NULL };\r
229\r
230/* dev_alt maps alert numbers to dispatch routines */\r
231\r
232struct aldisp dev_alt[] = {\r
233 { NULL, NULL },\r
234 { NULL, &pot_ilc }, { NULL, &pot_ilc },\r
235 { NULL, &pot_ilc }, { NULL, &pot_ilc }, \r
236 { NULL, &pot_ilc }, { NULL, &pot_ilc },\r
237 { NULL, &pot_ilc }, { NULL, &pot_ilc }, \r
238 { NULL, &pot_dcr }, { NULL, &pot_dcr },\r
239 { NULL, &pot_dcr }, { NULL, &pot_dcr }, \r
240 { NULL, &pot_dcr }, { NULL, &pot_dcr },\r
241 { NULL, &pot_dcr }, { NULL, &pot_dcr },\r
242 { &pin_adr, NULL }, { &pin_adr, NULL },\r
243 { &pin_adr, NULL }, { &pin_adr, NULL },\r
244 { &pin_adr, NULL }, { &pin_adr, NULL },\r
245 { &pin_adr, NULL }, { &pin_adr, NULL },\r
246 { NULL, &pot_RL1 }, { NULL, &pot_RL2 },\r
247 { NULL, &pot_RL4 },\r
248 { &pin_rads, NULL }, { NULL, &pot_rada },\r
249 { &pin_dsk, &pot_dsk }, { NULL, &pot_fork },\r
250 { &pin_mux, &pot_mux }\r
251 };\r
252\r
253/* Single word I/O instructions */\r
254\r
255t_stat op_wyim (uint32 inst, uint32 *dat)\r
256{\r
257int32 ch, dev;\r
258\r
259ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */\r
260dev = chan_uar[ch] & DEV_MASK; /* get dev # */\r
261if (chan_cnt[ch] <= chan_cpw[ch]) { /* buffer empty? */\r
262 if (dev == 0) return STOP_INVIOP; /* no device? dead */\r
263 return STOP_IONRDY; /* hang until full */\r
264 }\r
265*dat = chan_war[ch]; /* get data */\r
266chan_war[ch] = 0; /* reset war */\r
267chan_cnt[ch] = 0; /* reset cnt */\r
268return SCPE_OK;\r
269}\r
270\r
271t_stat op_miwy (uint32 inst, uint32 dat)\r
272{\r
273int32 ch, dev;\r
274\r
275ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */\r
276dev = chan_uar[ch] & DEV_MASK; /* get dev # */\r
277if (chan_cnt[ch] != 0) { /* buffer full? */\r
278 if (dev == 0) return STOP_INVIOP; /* no device? dead */\r
279 return STOP_IONRDY; /* hang until full */\r
280 }\r
281chan_war[ch] = dat; /* get data */\r
282chan_cnt[ch] = chan_cpw[ch] + 1; /* buffer full */\r
283if (chan_flag[ch] & CHF_OWAK) { /* output wake? */\r
284 if (VLD_DEV (dev, ch)) SET_XFR (dev, ch); /* wake channel */\r
285 chan_flag[ch] = chan_flag[ch] & ~CHF_OWAK; /* clear wake */\r
286 }\r
287return SCPE_OK;\r
288}\r
289\r
290t_stat op_pin (uint32 *dat)\r
291{\r
292uint32 al = alert; /* local copy */\r
293\r
294alert = 0; /* clear alert */\r
295if ((al == 0) || (dev_alt[al].pin == NULL)) CRETIOP; /* inv alert? */\r
296return dev_alt[al].pin (al, dat); /* PIN from dev */\r
297}\r
298\r
299t_stat op_pot (uint32 dat)\r
300{\r
301uint32 al = alert; /* local copy */\r
302\r
303alert = 0; /* clear alert */\r
304if ((al == 0) || (dev_alt[al].pot == NULL)) CRETIOP; /* inv alert? */\r
305return dev_alt[al].pot (al, &dat); /* POT to dev */\r
306}\r
307\r
308/* EOM/EOD */\r
309\r
310t_stat op_eomd (uint32 inst)\r
311{\r
312uint32 mod = I_GETIOMD (inst); /* get mode */\r
313uint32 ch = I_GETEOCH (inst); /* get chan # */\r
314uint32 dev = inst & DEV_MASK; /* get dev # */\r
315uint32 ch_dev = chan_uar[ch] & DEV_MASK; /* chan curr dev # */\r
316t_stat r;\r
317\r
318switch (mod) {\r
319\r
320 case 0: /* IO control */\r
321 if (dev) { /* new dev? */\r
322 if (ch_dev) CRETIOP; /* chan act? err */\r
323 if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */\r
324 chan_war[ch] = chan_cnt[ch] = 0; /* init chan */\r
325 chan_flag[ch] = chan_dcr[ch] = 0;\r
326 chan_mode[ch] = chan_uar[ch] = 0;\r
327 if (ch >= CHAN_E) chan_mode[ch] = CHM_CE;\r
328 if (r = dev_dsp[dev][ch] (IO_CONN, inst, NULL)) /* connect */\r
329 return r;\r
330 if ((inst & I_IND) || (ch >= CHAN_C)) { /* C-H? alert ilc */\r
331 alert = POT_ILCY + ch;\r
332 chan_mar[ch] = chan_wcr[ch] = 0;\r
333 }\r
334 if (chan_flag[ch] & CHF_24B) chan_cpw[ch] = 0; /* 24B? 1 ch/wd */\r
335 else if (chan_flag[ch] & CHF_12B) /* 12B? 2 ch/wd */\r
336 chan_cpw[ch] = CHC_GETCPW (inst) & 1;\r
337 else chan_cpw[ch] = CHC_GETCPW (inst); /* 6b, 1-4 ch/wd */\r
338 chan_uar[ch] = dev; /* connected */\r
339 if ((dev & DEV_OUT) && ion && !QAILCE (alert)) /* out, prog IO? */\r
340 int_req = int_req | int_zc[ch]; /* initial intr */\r
341 }\r
342 else return dev_disc (ch, ch_dev); /* disconnect */\r
343 break;\r
344\r
345 case 1: /* buf control */\r
346 if (QAILCE (alert)) { /* ilce alerted? */\r
347 ch = alert - POT_ILCY; /* derive chan */\r
348 if (ch >= CHAN_E) inst = inst | CHM_CE; /* DACC? ext */\r
349 chan_mode[ch] = inst; /* save mode */\r
350 chan_mar[ch] = (CHM_GETHMA (inst) << 14) | /* get hi mar */\r
351 (chan_mar[ch] & CHI_M_MA);\r
352 chan_wcr[ch] = (CHM_GETHWC (inst) << 10) | /* get hi wc */\r
353 (chan_wcr[ch] & CHI_M_WC);\r
354 }\r
355 else if (dev) { /* dev EOM */\r
356 if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */\r
357 return dev_dsp[dev][ch] (IO_EOM1, inst, NULL);\r
358 }\r
359 else { /* chan EOM */\r
360 inst = inst & 047677;\r
361 if (inst == 040000) { /* alert ilce */\r
362 alert = POT_ILCY + ch;\r
363 chan_mar[ch] = chan_wcr[ch] = 0;\r
364 }\r
365 else if (inst == 002000) alert = POT_ADRY + ch; /* alert addr */\r
366 else if (inst == 001000) alert = POT_DCRY + ch; /* alert DCR */\r
367 else if (inst == 004000) { /* term output */\r
368 if (ch_dev & DEV_OUT) { /* to output dev? */\r
369 if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* busy, DMA? */\r
370 chan_flag[ch] = chan_flag[ch] | CHF_TOP; /* TOP pending */\r
371 else return dev_wreor (ch, ch_dev); /* idle, write EOR */\r
372 } /* end else TOP */\r
373 else if (ch_dev & DEV_MT) { /* change to scan? */\r
374 chan_uar[ch] = chan_uar[ch] | DEV_MTS; /* change dev addr */\r
375 chan_flag[ch] = chan_flag[ch] | CHF_SCAN; /* set scan flag */\r
376 } /* end else change scan */\r
377 } /* end else term output */\r
378 } /* end else chan EOM */\r
379 break; \r
380\r
381 case 2: /* internal */\r
382 if (ch >= CHAN_E) { /* EOD? */\r
383 if (inst & 00300) { /* set EM? */\r
384 if (inst & 00100) EM2 = inst & 07;\r
385 if (inst & 00200) EM3 = (inst >> 3) & 07;\r
386 set_dyn_map ();\r
387 }\r
388 break;\r
389 } /* end if EOD */\r
390 if (inst & 00001) OV = 0; /* clr OV */\r
391 if (inst & 00002) ion = 1; /* ion */\r
392 else if (inst & 00004) ion = 0; /* iof */\r
393 if ((inst & 00010) && (((X >> 1) ^ X) & EXPS)) OV = 1;\r
394 if (inst & 00020) alert = POT_SYSI; /* alert sys int */\r
395 if (inst & 00100) rtc_pie = 1; /* arm clk pls */\r
396 else if (inst & 00200) rtc_pie = 0; /* disarm pls */\r
397 if ((inst & 01400) == 01400) alert = POT_RL4; /* alert RL4 */\r
398 else if (inst & 00400) alert = POT_RL1; /* alert RL1 */\r
399 else if (inst & 01000) alert = POT_RL2; /* alert RL2 */\r
400 if (inst & 02000) { /* nml to mon */\r
401 nml_mode = usr_mode = 0;\r
402 if (inst & 00400) mon_usr_trap = 1;\r
403 }\r
404 break;\r
405\r
406 case 3: /* special */\r
407 dev = I_GETDEV3 (inst); /* special device */\r
408 if (dev3_dsp[dev]) /* defined? */\r
409 return dev3_dsp[dev] (IO_CONN, inst, NULL);\r
410 CRETINS;\r
411 } /* end case */\r
412\r
413return SCPE_OK;\r
414}\r
415\r
416/* Skip if not signal */\r
417\r
418t_stat op_sks (uint32 inst, uint32 *dat)\r
419{\r
420uint32 mod = I_GETIOMD (inst); /* get mode */\r
421uint32 ch = I_GETSKCH (inst); /* get chan # */\r
422uint32 dev = inst & DEV_MASK; /* get dev # */\r
423\r
424*dat = 0;\r
425if ((ch == 4) && !(inst & 037774)) { /* EM test */\r
426 if (((inst & 0001) && (EM2 != 2)) ||\r
427 ((inst & 0002) && (EM3 != 3))) *dat = 1;\r
428 return SCPE_OK;\r
429 }\r
430switch (mod) {\r
431\r
432 case 1: /* ch, dev */\r
433 if (dev) { /* device */\r
434 if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */\r
435 dev_dsp[dev][ch] (IO_SKS, inst, dat); /* do test */\r
436 }\r
437 else { /* channel */\r
438 if (((inst & 04000) && (chan_uar[ch] == 0)) ||\r
439 ((inst & 02000) && (chan_wcr[ch] == 0)) ||\r
440 ((inst & 01000) && ((chan_flag[ch] & CHF_ERR) == 0)) ||\r
441 ((inst & 00400) && (chan_flag[ch] & CHF_IREC))) *dat = 1;\r
442 }\r
443 break;\r
444\r
445 case 2: /* internal test */\r
446 if (inst & 0001) { /* test OV */\r
447 *dat = OV ^ 1; /* skip if off */\r
448 OV = 0; /* and reset */\r
449 break;\r
450 }\r
451 if (((inst & 00002) && !ion) || /* ion, bpt test */\r
452 ((inst & 00004) && ion) ||\r
453 ((inst & 00010) && ((chan_flag[CHAN_W] & CHF_ERR) == 0)) ||\r
454 ((inst & 00020) && ((chan_flag[CHAN_Y] & CHF_ERR) == 0)) ||\r
455 ((inst & 00040) && ((bpt & 001) == 0)) ||\r
456 ((inst & 00100) && ((bpt & 002) == 0)) ||\r
457 ((inst & 00200) && ((bpt & 004) == 0)) ||\r
458 ((inst & 00400) && ((bpt & 010) == 0)) ||\r
459 ((inst & 01000) && (chan_uar[CHAN_W] == 0)) ||\r
460 ((inst & 02000) && (chan_uar[CHAN_Y] == 0))) *dat = 1;\r
461 break;\r
462\r
463 case 3: /* special */\r
464 dev = I_GETDEV3 (inst); /* special device */\r
465 if (dev3_dsp[dev]) dev3_dsp[dev] (IO_SKS, inst, dat); \r
466 else CRETINS;\r
467 } /* end case */\r
468\r
469return SCPE_OK;\r
470}\r
471\r
472/* PIN/POT routines */\r
473\r
474t_stat pot_ilc (uint32 num, uint32 *dat)\r
475{\r
476uint32 ch = num - POT_ILCY;\r
477\r
478chan_mar[ch] = (chan_mar[ch] & ~CHI_M_MA) | CHI_GETMA (*dat);\r
479chan_wcr[ch] = (chan_wcr[ch] & ~CHI_M_WC) | CHI_GETWC (*dat);\r
480chan_flag[ch] = chan_flag[ch] | CHF_ILCE;\r
481return SCPE_OK;\r
482}\r
483\r
484t_stat pot_dcr (uint32 num, uint32 *dat)\r
485{\r
486uint32 ch = num - POT_DCRY;\r
487\r
488chan_dcr[ch] = (*dat) & (CHD_INT | CHD_PAGE);\r
489chan_flag[ch] = chan_flag[ch] | CHF_DCHN;\r
490return SCPE_OK;\r
491}\r
492\r
493t_stat pin_adr (uint32 num, uint32 *dat)\r
494{\r
495uint32 ch = num - POT_ADRY;\r
496\r
497*dat = chan_mar[ch] & PAMASK;\r
498return SCPE_OK;\r
499}\r
500\r
501/* System interrupt POT.\r
502\r
503 The SDS 940 timesharing system uses a permanently asserted\r
504 system interrupt as a way of forking the teletype input\r
505 interrupt handler to a lower priority. The interrupt is\r
506 armed to set up the fork, and disarmed in the fork routine */\r
507\r
508t_stat pot_fork (uint32 num, uint32 *dat)\r
509{\r
510uint32 igrp = SYI_GETGRP (*dat); /* get group */\r
511uint32 fbit = (1 << (VEC_FORK & 017)); /* bit in group */\r
512\r
513if (igrp == (VEC_FORK / 020)) { /* right group? */\r
514 if ((*dat & SYI_ARM) && (*dat & fbit)) /* arm, bit set? */\r
515 int_req = int_req | INT_FORK;\r
516 if ((*dat & SYI_DIS) && !(*dat & fbit)) /* disarm, bit clr? */\r
517 int_req = int_req & ~INT_FORK;\r
518 }\r
519return SCPE_OK;\r
520}\r
521\r
522/* Channel read invokes the I/O device to get the next character and,\r
523 if not end of record, assembles it into the word assembly register.\r
524 If the interlace is on, the full word is stored in memory.\r
525 The key difference points for the various terminal functions are\r
526\r
527 end of record comp: EOT interrupt\r
528 IORD, IOSD: EOR interrupt, disconnect\r
529 IORP, IOSP: EOR interrupt, interrecord\r
530 interlace off: comp: EOW interrupt\r
531 IORD, IORP: ignore\r
532 IOSD, IOSP: overrun error\r
533 --wcr == 0: comp: clear interlace\r
534 IORD, IORP, IOSP: ZWC interrupt\r
535 IOSD: ZWC interrupt, EOR interrupt, disconnect\r
536\r
537 Note that the channel can be disconnected if CHN_EOR is set, but must\r
538 not be if XFR_REQ is set */\r
539 \r
540t_stat chan_read (int32 ch)\r
541{\r
542uint32 dat = 0;\r
543uint32 dev = chan_uar[ch] & DEV_MASK;\r
544uint32 tfnc = CHM_GETFNC (chan_mode[ch]);\r
545t_stat r = SCPE_OK;\r
546\r
547if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */\r
548 if (INV_DEV (dev, ch)) CRETIOP; /* can't read? */\r
549 r = dev_dsp[dev][ch] (IO_READ, dev, &dat); /* read data */\r
550 if (r) chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* error? */\r
551 if (chan_flag[ch] & CHF_24B) chan_war[ch] = dat; /* 24B? */\r
552 else if (chan_flag[ch] & CHF_12B) /* 12B? */\r
553 chan_war[ch] = ((chan_war[ch] << 12) | (dat & 07777)) & DMASK;\r
554 else chan_war[ch] = ((chan_war[ch] << 6) | (dat & 077)) & DMASK;\r
555 if (chan_flag[ch] & CHF_SCAN) /* scanning? */\r
556 chan_cnt[ch] = chan_cpw[ch]; /* never full */\r
557 else chan_cnt[ch] = chan_cnt[ch] + 1; /* insert char */\r
558 if (chan_cnt[ch] > chan_cpw[ch]) { /* full? */\r
559 if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */\r
560 chan_write_mem (ch); /* write to mem */\r
561 if (chan_wcr[ch] == 0) { /* wc zero? */\r
562 chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* clr interlace */\r
563 if ((tfnc != CHM_COMP) && (chan_mode[ch] & CHM_ZC))\r
564 int_req = int_req | int_zc[ch]; /* zwc interrupt */\r
565 if (tfnc == CHM_IOSD) { /* IOSD? also EOR */\r
566 if (chan_mode[ch] & CHM_ER) int_req = int_req | int_er[ch];\r
567 dev_disc (ch, dev); /* disconnect */\r
568 } /* end if IOSD */\r
569 } /* end if wcr == 0 */\r
570 } /* end if ilce on */\r
571 else { /* interlace off */\r
572 if (TST_EOR (ch)) return chan_eor (ch); /* eor? */\r
573 if (tfnc == CHM_COMP) { /* C: EOW, intr */\r
574 if (ion) int_req = int_req | int_zc[ch];\r
575 }\r
576 else if (tfnc & CHM_SGNL) /* Sx: error */\r
577 chan_flag[ch] = chan_flag[ch] | CHF_ERR;\r
578 else chan_cnt[ch] = chan_cpw[ch]; /* Rx: ignore */\r
579 } /* end else ilce */\r
580 } /* end if full */\r
581 } /* end if xfr */\r
582if (TST_EOR (ch)) { /* end record? */\r
583 if (tfnc == CHM_COMP) chan_flush_war (ch); /* C: fill war */\r
584 else if (chan_cnt[ch]) { /* RX, CX: fill? */\r
585 chan_flush_war (ch); /* fill war */\r
586 if (chan_flag[ch] & CHF_ILCE) /* ilce on? store */\r
587 chan_write_mem (ch);\r
588 } /* end else if cnt */\r
589 return chan_eor (ch); /* eot/eor int */\r
590 }\r
591return r; \r
592}\r
593\r
594void chan_write_mem (int32 ch)\r
595{\r
596WriteP (chan_mar[ch], chan_war[ch]); /* write to mem */\r
597chan_mar[ch] = chan_mar_inc (ch); /* incr mar */\r
598chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr wcr */\r
599chan_war[ch] = 0; /* reset war */\r
600chan_cnt[ch] = 0; /* reset cnt */\r
601return;\r
602}\r
603\r
604void chan_flush_war (int32 ch)\r
605{\r
606int32 i = (chan_cpw[ch] - chan_cnt[ch]) + 1;\r
607\r
608if (i) {\r
609 if (chan_flag[ch] & CHF_24B) chan_war[ch] = 0;\r
610 else if (chan_flag[ch] & CHF_12B)\r
611 chan_war[ch] = (chan_war[ch] << 12) & DMASK;\r
612 else chan_war[ch] = (chan_war[ch] << (i * 6)) & DMASK;\r
613 chan_cnt[ch] = chan_cpw[ch] + 1;\r
614 }\r
615return;\r
616}\r
617\r
618/* Channel write gets the next character and sends it to the I/O device.\r
619 If this is the last character in an interlace operation, the end of\r
620 record operation is invoked.\r
621 The key difference points for the various terminal functions are\r
622\r
623 end of record: comp: EOT interrupt\r
624 IORD, IOSD: EOR interrupt, disconnect\r
625 IORP, IOSP: EOR interrupt, interrecord\r
626 interlace off: if not end of record, EOW interrupt\r
627 --wcr == 0: comp: EOT interrupt, disconnect\r
628 IORD, IORP: ignore\r
629 IOSD: ZWC interrupt, disconnect\r
630 IOSP: ZWC interrupt, interrecord\r
631*/\r
632t_stat chan_write (int32 ch)\r
633{\r
634uint32 dat = 0;\r
635uint32 dev = chan_uar[ch] & DEV_MASK;\r
636uint32 tfnc = CHM_GETFNC (chan_mode[ch]);\r
637t_stat r = SCPE_OK;\r
638\r
639if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */\r
640 if (INV_DEV (dev, ch)) CRETIOP; /* invalid dev? */\r
641 if (chan_cnt[ch] == 0) { /* buffer empty? */\r
642 if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */\r
643 chan_war[ch] = ReadP (chan_mar[ch]);\r
644 chan_mar[ch] = chan_mar_inc (ch); /* incr mar */\r
645 chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr mar */\r
646 chan_cnt[ch] = chan_cpw[ch] + 1; /* set cnt */\r
647 }\r
648 else { /* ilce off */\r
649 CLR_XFR (dev, ch); /* cant xfr */\r
650 if (TST_EOR (dev)) return chan_eor (ch); /* EOR? */\r
651 chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* rate err */\r
652 return SCPE_OK;\r
653 } /* end else ilce */\r
654 } /* end if cnt */\r
655 chan_cnt[ch] = chan_cnt[ch] - 1; /* decr cnt */\r
656 if (chan_flag[ch] & CHF_24B) dat = chan_war[ch]; /* 24B? */\r
657 else if (chan_flag[ch] & CHF_12B) { /* 12B? */\r
658 dat = (chan_war[ch] >> 12) & 07777; /* get halfword */\r
659 chan_war[ch] = (chan_war[ch] << 12) & DMASK; /* remove from war */\r
660 }\r
661 else { /* 6B */\r
662 dat = (chan_war[ch] >> 18) & 077; /* get char */\r
663 chan_war[ch] = (chan_war[ch] << 6) & DMASK; /* remove from war */\r
664 }\r
665 r = dev_dsp[dev][ch] (IO_WRITE, dev, &dat); /* write */\r
666 if (r) chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* error? */\r
667 if (chan_cnt[ch] == 0) { /* buf empty? */\r
668 if (chan_flag[ch] & CHF_ILCE) { /* ilce on? */\r
669 if (chan_wcr[ch] == 0) { /* wc now 0? */\r
670 chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* ilc off */\r
671 if (tfnc == CHM_COMP) { /* compatible? */\r
672 if (ion) int_req = int_req | int_zc[ch];\r
673 dev_disc (ch, dev); /* disconnnect */\r
674 } /* end if comp */\r
675 else { /* extended */\r
676 if (chan_mode[ch] & CHM_ZC) /* ZWC int */\r
677 int_req = int_req | int_zc[ch];\r
678 if (tfnc == CHM_IOSD) { /* SD */\r
679 if (chan_mode[ch] & CHM_ER) /* EOR int */\r
680 int_req = int_req | int_er[ch];\r
681 dev_disc (ch, dev); /* disconnnect */\r
682 } /* end if SD */\r
683 else if (!(tfnc && CHM_SGNL) || /* IORx or IOSP TOP? */\r
684 (chan_flag[ch] & CHF_TOP))\r
685 dev_wreor (ch, dev); /* R: write EOR */\r
686 chan_flag[ch] = chan_flag[ch] & ~CHF_TOP;\r
687 } /* end else comp */\r
688 } /* end if wcr */\r
689 } /* end if ilce */\r
690 else if (chan_flag[ch] & CHF_TOP) { /* off, TOP pending? */\r
691 chan_flag[ch] = chan_flag[ch] & ~CHF_TOP; /* clear TOP */\r
692 dev_wreor (ch, dev); /* write EOR */\r
693 }\r
694 else if (ion) int_req = int_req | int_zc[ch]; /* no TOP, EOW intr */\r
695 } /* end if cnt */\r
696 } /* end if xfr */\r
697if (TST_EOR (ch)) return chan_eor (ch); /* eor rcvd? */\r
698return r;\r
699}\r
700\r
701/* MAR increment */\r
702\r
703uint32 chan_mar_inc (int32 ch)\r
704{\r
705uint32 t = (chan_mar[ch] + 1) & PAMASK; /* incr mar */\r
706\r
707if ((chan_flag[ch] & CHF_DCHN) && ((t & VA_POFF) == 0)) { /* chain? */\r
708 chan_flag[ch] = chan_flag[ch] & ~CHF_DCHN; /* clr flag */\r
709 if (chan_dcr[ch] & CHD_INT) /* if armed, intr */\r
710 int_req = int_req | int_zc[ch];\r
711 t = (chan_dcr[ch] & CHD_PAGE) << VA_V_PN; /* new mar */\r
712 }\r
713return t;\r
714}\r
715\r
716/* End of record action */\r
717\r
718t_stat chan_eor (int32 ch)\r
719{\r
720uint32 tfnc = CHM_GETFNC (chan_mode[ch]);\r
721uint32 dev = chan_uar[ch] & DEV_MASK;\r
722\r
723chan_flag[ch] = chan_flag[ch] & ~(CHF_EOR | CHF_ILCE); /* clr eor, ilce */\r
724if (((tfnc == CHM_COMP) && ion) || (chan_mode[ch] & CHM_ER))\r
725 int_req = int_req | int_er[ch]; /* EOT/EOR? */\r
726if (dev && (tfnc & CHM_PROC)) /* P, still conn? */\r
727 chan_flag[ch] = chan_flag[ch] | CHF_IREC; /* interrecord */\r
728else return dev_disc (ch, dev); /* disconnect */\r
729return SCPE_OK;\r
730}\r
731\r
732/* Utility routines */\r
733\r
734t_stat dev_disc (uint32 ch, uint32 dev)\r
735{\r
736chan_uar[ch] = 0; /* disconnect */\r
737if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_DISC, dev, NULL);\r
738return SCPE_OK;\r
739}\r
740\r
741t_stat dev_wreor (uint32 ch, uint32 dev)\r
742{\r
743if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_WREOR, dev, NULL);\r
744chan_flag[ch] = chan_flag[ch] | CHF_EOR; /* set eor */\r
745return SCPE_OK;\r
746}\r
747\r
748/* Externally visible routines */\r
749/* Channel driver */\r
750\r
751t_stat chan_process (void)\r
752{\r
753int32 i, dev;\r
754t_stat r;\r
755\r
756for (i = 0; i < NUM_CHAN; i++) { /* loop thru */\r
757 dev = chan_uar[i] & DEV_MASK; /* get dev */\r
758 if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) { /* chan active? */\r
759 if (dev & DEV_OUT) r = chan_write (i); /* write */\r
760 else r = chan_read (i); /* read */\r
761 if (r) return r;\r
762 }\r
763 }\r
764return SCPE_OK;\r
765}\r
766\r
767/* Test for channel active */\r
768\r
769t_bool chan_testact (void)\r
770{\r
771int32 i, dev;\r
772\r
773for (i = 0; i < NUM_CHAN; i++) {\r
774 dev = chan_uar[i] & DEV_MASK;\r
775 if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) return 1;\r
776 }\r
777return 0;\r
778}\r
779\r
780/* Async output device ready for more data */\r
781\r
782void chan_set_ordy (int32 ch)\r
783{\r
784if ((ch >= 0) && (ch < NUM_CHAN)) {\r
785 int32 dev = chan_uar[ch] & DEV_MASK; /* get dev */\r
786 if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* buf or ilce? */\r
787 SET_XFR (dev, ch); /* set xfr flg */\r
788 else chan_flag[ch] = chan_flag[ch] | CHF_OWAK; /* need wakeup */\r
789 }\r
790return;\r
791}\r
792\r
793/* Set flag in channel */\r
794\r
795void chan_set_flag (int32 ch, uint32 fl)\r
796{\r
797if ((ch >= 0) && (ch < NUM_CHAN)) chan_flag[ch] = chan_flag[ch] | fl;\r
798return;\r
799}\r
800\r
801/* Set UAR in channel */\r
802\r
803void chan_set_uar (int32 ch, uint32 dev)\r
804{\r
805if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = dev & DEV_MASK;\r
806return;\r
807}\r
808\r
809/* Disconnect channel */\r
810\r
811void chan_disc (int32 ch)\r
812{\r
813if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = 0;\r
814return;\r
815}\r
816\r
817/* Reset channels */\r
818\r
819t_stat chan_reset (DEVICE *dptr)\r
820{\r
821int32 i;\r
822\r
823xfr_req = 0;\r
824for (i = 0; i < NUM_CHAN; i++) {\r
825 chan_uar[i] = 0;\r
826 chan_wcr[i] = 0;\r
827 chan_mar[i] = 0;\r
828 chan_dcr[i] = 0;\r
829 chan_war[i] = 0;\r
830 chan_cpw[i] = 0;\r
831 chan_cnt[i] = 0;\r
832 chan_mode[i] = 0;\r
833 chan_flag[i] = 0;\r
834 }\r
835return SCPE_OK;\r
836}\r
837\r
838/* Channel assignment routines */\r
839\r
840t_stat set_chan (UNIT *uptr, int32 val, char *sptr, void *desc)\r
841{\r
842DEVICE *dptr;\r
843DIB *dibp;\r
844int32 i;\r
845\r
846if (sptr == NULL) return SCPE_ARG; /* valid args? */\r
847if (uptr == NULL) return SCPE_IERR;\r
848dptr = find_dev_from_unit (uptr);\r
849if (dptr == NULL) return SCPE_IERR;\r
850dibp = (DIB *) dptr->ctxt;\r
851if (dibp == NULL) return SCPE_IERR;\r
852for (i = 0; i < NUM_CHAN; i++) { /* match input */\r
853 if (strcmp (sptr, chname[i]) == 0) { /* find string */\r
854 if (val && !(val & (1 << i))) return SCPE_ARG; /* legal? */\r
855 dibp->chan = i; /* store new */\r
856 return SCPE_OK;\r
857 }\r
858 }\r
859return SCPE_ARG;\r
860}\r
861\r
862t_stat show_chan (FILE *st, UNIT *uptr, int32 val, void *desc)\r
863{\r
864DEVICE *dptr;\r
865DIB *dibp;\r
866\r
867if (uptr == NULL) return SCPE_IERR;\r
868dptr = find_dev_from_unit (uptr);\r
869if (dptr == NULL) return SCPE_IERR;\r
870dibp = (DIB *) dptr->ctxt;\r
871if (dibp == NULL) return SCPE_IERR;\r
872fprintf (st, "channel=%s", chname[dibp->chan]);\r
873return SCPE_OK;\r
874}\r
875\r
876/* Init device tables */\r
877\r
878t_bool io_init (void)\r
879{\r
880DEVICE *dptr;\r
881DIB *dibp;\r
882DSPT *tplp;\r
883int32 ch;\r
884uint32 i, j, dev, doff;\r
885\r
886/* Clear dispatch table, device map */\r
887\r
888for (i = 0; i < NUM_CHAN; i++) {\r
889 for (j = 0; j < (DEV_MASK + 1); j++) {\r
890 dev_dsp[j][i] = NULL;\r
891 dev_map[j][i] = 0;\r
892 }\r
893 }\r
894\r
895/* Test each device for conflict; add to map; init tables */\r
896\r
897for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru devices */\r
898 dibp = (DIB *) dptr->ctxt; /* get DIB */\r
899 if ((dibp == NULL) || (dptr->flags & DEV_DIS)) continue; /* exist, enabled? */\r
900 ch = dibp->chan; /* get channel */\r
901 dev = dibp->dev; /* get device num */\r
902 if (ch < 0) dev3_dsp[dev] = dibp->iop; /* special device */\r
903 else {\r
904 if (dibp->tplt == NULL) return TRUE; /* must have template */\r
905 for (tplp = dibp->tplt; tplp->num; tplp++) { /* loop thru templates */\r
906 for (j = 0; j < tplp->num; j++) { /* repeat as needed */\r
907 doff = dev + tplp->off + j; /* get offset dnum */\r
908 if (dev_map[doff][ch]) { /* slot in use? */\r
909 printf ("Device number conflict, chan = %s, devno = %02o\n",\r
910 chname[ch], doff);\r
911 if (sim_log) fprintf (sim_log,\r
912 "Device number conflict, chan = %s, dev = %02o\n",\r
913 chname[ch], doff);\r
914 return TRUE;\r
915 }\r
916 dev_map[doff][ch] = dibp->xfr; /* set xfr flag */\r
917 dev_dsp[doff][ch] = dibp->iop; /* set dispatch */\r
918 } /* end for j */\r
919 } /* end for tplt */\r
920 } /* end else */\r
921 } /* end for i */\r
922return FALSE;\r
923}\r
924\r
925/* Display channel state */\r
926\r
927t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc)\r
928{\r
929if ((val < 0) || (val >= NUM_CHAN)) return SCPE_IERR;\r
930fprintf (st, "UAR: %02o\n", chan_uar[val]);\r
931fprintf (st, "WCR: %05o\n", chan_wcr[val]);\r
932fprintf (st, "MAR: %06o\n", chan_mar[val]);\r
933fprintf (st, "DCR: %02o\n", chan_dcr[val]);\r
934fprintf (st, "WAR: %08o\n", chan_war[val]);\r
935fprintf (st, "CPW: %o\n", chan_cpw[val]);\r
936fprintf (st, "CNT: %o\n", chan_cnt[val]);\r
937fprintf (st, "MODE: %03o\n", chan_mode[val]);\r
938fprintf (st, "FLAG: %04o\n", chan_flag[val]);\r
939return SCPE_OK;\r
940}\r