First Commit of my working state
[simh.git] / PDP11 / pdp11_vh.c
CommitLineData
196ba1fc
PH
1/* pdp11_vh.c: DHQ11 asynchronous terminal multiplexor simulator\r
2\r
3 Copyright (c) 2004-2006, John A. Dundas III\r
4 Portions derived from work by Robert M Supnik\r
5\r
6 Permission is hereby granted, free of charge, to any person obtaining a\r
7 copy of this software and associated documentation files (the "Software"),\r
8 to deal in the Software without restriction, including without limitation\r
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
10 and/or sell copies of the Software, and to permit persons to whom the\r
11 Software is furnished to do so, subject to the following conditions:\r
12\r
13 The above copyright notice and this permission notice shall be included in\r
14 all copies or substantial portions of the Software.\r
15\r
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
19 THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
22\r
23 Except as contained in this notice, the name of the Author shall not be\r
24 used in advertising or otherwise to promote the sale, use or other dealings\r
25 in this Software without prior written authorization from the Author.\r
26\r
27 vh DHQ11 asynch multiplexor for SIMH\r
28\r
29 18-Jun-07 RMS Added UNIT_IDLE flag\r
30 29-Oct-06 RMS Synced poll and clock\r
31 07-Jul-05 RMS Removed extraneous externs\r
32 15-Jun-05 RMS Revised for new autoconfigure interface\r
33 Fixed bug in vector display routine\r
34 12-Jun-04 RMS Repair MS2SIMH macro to avoid divide by 0 bug\r
35 08-Jun-04 JAD Repair vh_dev initialization; remove unused\r
36 variables, cast to avoid conversion confusion\r
37 07-Jun-04 JAD Complete function prototypes of forward declarations.\r
38 Repair broken prototypes of vh_rd() and vh_wr()\r
39 Explicitly size integer declarations\r
40 4-Jun-04 JAD Preliminary code: If operating in a PDP-11 Unibus\r
41 environment, force DHU mode\r
42 29-May-04 JAD Make certain RX.TIMER is within allowable range\r
43 25-May-04 JAD All time-based operations are scaled by tmxr_poll units\r
44 23-May-04 JAD Change to fifo_get() and dq_tx_report() to avoid\r
45 gratuitous stack manipulation\r
46 20-May-04 JAD Made modem control and auto-hangup unit flags\r
47 19-May-04 JAD Fix problem with modem status where the line number\r
48 was not being included\r
49 12-May-04 JAD Revised for updated tmxr interfaces\r
50 28-Jan-04 JAD Original creation and testing\r
51\r
52I/O Page Registers\r
53\r
54CSR 17 760 440 (float)\r
55\r
56Vector: 300 (float)\r
57\r
58Priority: BR4\r
59\r
60Rank: 32\r
61\r
62*/\r
63/* MANY constants needed! */\r
64\r
65#if defined (VM_VAX)\r
66#include "vax_defs.h"\r
67extern int32 int_req[IPL_HLVL];\r
68#endif\r
69\r
70#if defined (VM_PDP11)\r
71#include "pdp11_defs.h"\r
72extern int32 int_req[IPL_HLVL];\r
73extern int32 cpu_opt;\r
74#endif\r
75\r
76#include "sim_sock.h"\r
77#include "sim_tmxr.h"\r
78\r
79/* imports from pdp11_stddev.c: */\r
80extern int32 tmxr_poll, clk_tps;\r
81/* convert ms to SIMH time units based on tmxr_poll polls per second */\r
82#define MS2SIMH(ms) (((ms) * clk_tps) / 1000)\r
83extern FILE *sim_log;\r
84\r
85#ifndef VH_MUXES\r
86#define VH_MUXES (4)\r
87#endif\r
88#define VH_MNOMASK (VH_MUXES - 1)\r
89\r
90#define VH_LINES (8)\r
91\r
92#define UNIT_V_MODEDHU (UNIT_V_UF + 0)\r
93#define UNIT_V_FASTDMA (UNIT_V_UF + 1)\r
94#define UNIT_V_MODEM (UNIT_V_UF + 2)\r
95#define UNIT_V_HANGUP (UNIT_V_UF + 3)\r
96#define UNIT_MODEDHU (1 << UNIT_V_MODEDHU)\r
97#define UNIT_FASTDMA (1 << UNIT_V_FASTDMA)\r
98#define UNIT_MODEM (1 << UNIT_V_MODEM)\r
99#define UNIT_HANGUP (1 << UNIT_V_HANGUP)\r
100\r
101/* VHCSR - 160440 - Control and Status Register */\r
102\r
103#define CSR_M_IND_ADDR (017)\r
104#define CSR_SKIP (1 << 4)\r
105#define CSR_MASTER_RESET (1 << 5)\r
106#define CSR_RXIE (1 << 6)\r
107#define CSR_RX_DATA_AVAIL (1 << 7)\r
108#define CSR_M_TX_LINE (017)\r
109#define CSR_V_TX_LINE (8)\r
110#define CSR_TX_DMA_ERR (1 << 12)\r
111#define CSR_DIAG_FAIL (1 << 13)\r
112#define CSR_TXIE (1 << 14)\r
113#define CSR_TX_ACTION (1 << 15)\r
114#define CSR_GETCHAN(x) ((x) & CSR_M_IND_ADDR)\r
115#define CSR_RW \\r
116 (CSR_TXIE|CSR_RXIE|CSR_SKIP|CSR_M_IND_ADDR|CSR_MASTER_RESET)\r
117#define RESET_ABORT (052525)\r
118\r
119/* Receive Buffer (RBUF) */\r
120\r
121#define FIFO_SIZE (256)\r
122#define FIFO_ALARM (191)\r
123#define FIFO_HALF (FIFO_SIZE / 2)\r
124#define RBUF_M_RX_CHAR (0377)\r
125#define RBUF_M_RX_LINE (07)\r
126#define RBUF_V_RX_LINE (8)\r
127#define RBUF_PARITY_ERR (1 << 12)\r
128#define RBUF_FRAME_ERR (1 << 13)\r
129#define RBUF_OVERRUN_ERR (1 << 14)\r
130#define RBUF_DATA_VALID (1 << 15)\r
131#define RBUF_GETLINE(x) (((x) >> RBUF_V_RX_LINE) & RBUF_M_RX_LINE)\r
132#define RBUF_PUTLINE(x) ((x) << RBUF_V_RX_LINE)\r
133#define RBUF_DIAG \\r
134 (RBUF_PARITY_ERR|RBUF_FRAME_ERR|RBUF_OVERRUN_ERR)\r
135#define XON (021)\r
136#define XOFF (023)\r
137\r
138/* Transmit Character Register (TXCHAR) */\r
139\r
140#define TXCHAR_M_CHAR (0377)\r
141#define TXCHAR_TX_DATA_VALID (1 << 15)\r
142\r
143/* Receive Timer Register (RXTIMER) */\r
144\r
145#define RXTIMER_M_RX_TIMER (0377)\r
146\r
147/* Line-Parameter Register (LPR) */\r
148\r
149#define LPR_DISAB_XRPT (1 << 0) /* not impl. in real DHU */\r
150#define LPR_V_DIAG (1)\r
151#define LPR_M_DIAG (03)\r
152#define LPR_V_CHAR_LGTH (3)\r
153#define LPR_M_CHAR_LGTH (03)\r
154#define LPR_PARITY_ENAB (1 << 5)\r
155#define LPR_EVEN_PARITY (1 << 6)\r
156#define LPR_STOP_CODE (1 << 7)\r
157#define LPR_V_RX_SPEED (8)\r
158#define LPR_M_RX_SPEED (017)\r
159#define LPR_V_TX_SPEED (12)\r
160#define LPR_M_TX_SPEED (017)\r
161\r
162#define RATE_50 (0)\r
163#define RATE_75 (1)\r
164#define RATE_110 (2)\r
165#define RATE_134 (3)\r
166#define RATE_150 (4)\r
167#define RATE_300 (5)\r
168#define RATE_600 (6)\r
169#define RATE_1200 (7)\r
170#define RATE_1800 (8)\r
171#define RATE_2000 (9)\r
172#define RATE_2400 (10)\r
173#define RATE_4800 (11)\r
174#define RATE_7200 (12)\r
175#define RATE_9600 (13)\r
176#define RATE_19200 (14)\r
177#define RATE_38400 (15)\r
178\r
179/* Line-Status Register (STAT) */\r
180\r
181#define STAT_DHUID (1 << 8) /* mode: 0=DHV, 1=DHU */\r
182#define STAT_MDL (1 << 9) /* always 0, has modem support */\r
183#define STAT_CTS (1 << 11) /* CTS from modem */\r
184#define STAT_DCD (1 << 12) /* DCD from modem */\r
185#define STAT_RI (1 << 13) /* RI from modem */\r
186#define STAT_DSR (1 << 15) /* DSR from modem */\r
187\r
188/* FIFO Size Register (FIFOSIZE) */\r
189\r
190#define FIFOSIZE_M_SIZE (0377)\r
191\r
192/* FIFO Data Register (FIFODATA) */\r
193\r
194#define FIFODATA_W0 (0377)\r
195#define FIFODATA_V_W1 (8)\r
196#define FIFODATA_M_W1 (0377)\r
197\r
198/* Line-Control Register (LNCTRL) */\r
199\r
200#define LNCTRL_TX_ABORT (1 << 0)\r
201#define LNCTRL_IAUTO (1 << 1)\r
202#define LNCTRL_RX_ENA (1 << 2)\r
203#define LNCTRL_BREAK (1 << 3)\r
204#define LNCTRL_OAUTO (1 << 4)\r
205#define LNCTRL_FORCE_XOFF (1 << 5)\r
206#define LNCTRL_V_MAINT (6)\r
207#define LNCTRL_M_MAINT (03)\r
208#define LNCTRL_LINK_TYPE (1 << 8) /* 0=data leads only, 1=modem */\r
209#define LNCTRL_DTR (1 << 9) /* DTR to modem */\r
210#define LNCTRL_RTS (1 << 12) /* RTS to modem */\r
211\r
212/* Transmit Buffer Address Register Number 1 (TBUFFAD1) */\r
213\r
214/* Transmit Buffer Address Register Number 2 (TBUFFAD2) */\r
215\r
216#define TB2_M_TBUFFAD (077)\r
217#define TB2_TX_DMA_START (1 << 7)\r
218#define TB2_TX_ENA (1 << 15)\r
219\r
220/* Transmit DMA Buffer Counter (TBUFFCT) */\r
221\r
222/* Self-Test Error Codes */\r
223\r
224#define SELF_NULL (0201)\r
225#define SELF_SKIP (0203)\r
226#define SELF_OCT (0211)\r
227#define SELF_RAM (0225)\r
228#define SELF_RCD (0231)\r
229#define SELF_DRD (0235)\r
230\r
231#define BMP_OK (0305)\r
232#define BMP_BAD (0307)\r
233\r
234/* Loopback types */\r
235\r
236#define LOOP_NONE (0)\r
237#define LOOP_H325 (1)\r
238#define LOOP_H3101 (2) /* p.2-13 DHQ manual */\r
239/* Local storage */\r
240\r
241static uint16 vh_csr[VH_MUXES] = { 0 }; /* CSRs */\r
242static uint16 vh_timer[VH_MUXES] = { 1 }; /* controller timeout */\r
243static uint16 vh_mcount[VH_MUXES] = { 0 };\r
244static uint32 vh_timeo[VH_MUXES] = { 0 };\r
245static uint32 vh_ovrrun[VH_MUXES] = { 0 }; /* line overrun bits */\r
246/* XOFF'd channels, one bit/channel */\r
247static uint32 vh_stall[VH_MUXES] = { 0 };\r
248static uint16 vh_loop[VH_MUXES] = { 0 }; /* loopback status */\r
249\r
250/* One bit per controller: */\r
251static uint32 vh_rxi = 0; /* rcv interrupts */\r
252static uint32 vh_txi = 0; /* xmt interrupts */\r
253static uint32 vh_crit = 0; /* FIFO.CRIT */\r
254\r
255static const int32 bitmask[4] = { 037, 077, 0177, 0377 };\r
256\r
257/* RX FIFO state */\r
258\r
259static int32 rbuf_idx[VH_MUXES] = { 0 };/* index into vh_rbuf */\r
260static uint32 vh_rbuf[VH_MUXES][FIFO_SIZE] = { 0 };\r
261\r
262/* TXQ state */\r
263\r
264#define TXQ_SIZE (16)\r
265static int32 txq_idx[VH_MUXES] = { 0 };\r
266static uint32 vh_txq[VH_MUXES][TXQ_SIZE] = { 0 };\r
267\r
268/* Need to extend the TMLN structure */\r
269\r
270typedef struct {\r
271 TMLN *tmln;\r
272 uint16 lpr; /* line parameters */\r
273 uint16 lnctrl; /* line control */\r
274 uint16 lstat; /* line modem status */\r
275 uint16 tbuffct; /* remaining character count */\r
276 uint16 tbuf1;\r
277 uint16 tbuf2;\r
278 uint16 txchar; /* single character I/O */\r
279} TMLX;\r
280\r
281static TMLN vh_ldsc[VH_MUXES * VH_LINES] = { 0 };\r
282static TMXR vh_desc = { VH_MUXES * VH_LINES, 0, 0, vh_ldsc };\r
283static TMLX vh_parm[VH_MUXES * VH_LINES] = { 0 };\r
284\r
285/* Forward references */\r
286static t_stat vh_rd (int32 *data, int32 PA, int32 access);\r
287static t_stat vh_wr (int32 data, int32 PA, int32 access);\r
288static t_stat vh_svc (UNIT *uptr);\r
289static int32 vh_rxinta (void);\r
290static int32 vh_txinta (void);\r
291static t_stat vh_clear (int32 vh, t_bool flag);\r
292static t_stat vh_reset (DEVICE *dptr);\r
293static t_stat vh_attach (UNIT *uptr, char *cptr);\r
294static t_stat vh_detach (UNIT *uptr); \r
295static t_stat vh_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); \r
296static t_stat vh_show (FILE *st, UNIT *uptr, int32 val, void *desc);\r
297static t_stat vh_show_debug (FILE *st, UNIT *uptr, int32 val, void *desc);\r
298static t_stat vh_show_rbuf (FILE *st, UNIT *uptr, int32 val, void *desc);\r
299static t_stat vh_show_txq (FILE *st, UNIT *uptr, int32 val, void *desc);\r
300static t_stat vh_putc (int32 vh, TMLX *lp, int32 chan, int32 data);\r
301static void doDMA (int32 vh, int32 chan);\r
302static t_stat vh_summ (FILE *st, UNIT *uptr, int32 val, void *desc);\r
303\r
304int32 tmxr_send_buffered_data (TMLN *lp);\r
305\r
306/* SIMH I/O Structures */\r
307\r
308static DIB vh_dib = {\r
309 IOBA_VH,\r
310 IOLN_VH * VH_MUXES,\r
311 &vh_rd, /* read */\r
312 &vh_wr, /* write */\r
313 2, /* # of vectors */\r
314 IVCL (VHRX),\r
315 VEC_VHRX,\r
316 { &vh_rxinta, &vh_txinta } /* int. ack. routines */\r
317};\r
318\r
319static UNIT vh_unit[VH_MUXES] = {\r
320 { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) },\r
321 { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) },\r
322 { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) },\r
323 { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) },\r
324};\r
325\r
326static const REG vh_nlreg = { DRDATA (NLINES, vh_desc.lines, 6), PV_LEFT };\r
327\r
328static const REG vh_reg[] = {\r
329 { BRDATA (CSR, vh_csr, DEV_RDX, 16, VH_MUXES) },\r
330 { GRDATA (DEVADDR, vh_dib.ba, DEV_RDX, 32, 0), REG_HRO },\r
331 { GRDATA (DEVVEC, vh_dib.vec, DEV_RDX, 16, 0), REG_HRO },\r
332 { NULL }\r
333};\r
334\r
335static const MTAB vh_mod[] = {\r
336 { UNIT_MODEDHU, 0, "DHV mode", "DHV", NULL },\r
337 { UNIT_MODEDHU, UNIT_MODEDHU, "DHU mode", "DHU", NULL },\r
338 { UNIT_FASTDMA, 0, NULL, "NORMAL", NULL },\r
339 { UNIT_FASTDMA, UNIT_FASTDMA, "fast DMA", "FASTDMA", NULL },\r
340 { UNIT_MODEM, 0, NULL, "NOMODEM", NULL },\r
341 { UNIT_MODEM, UNIT_MODEM, "modem", "MODEM", NULL },\r
342 { UNIT_HANGUP, 0, NULL, "NOHANGUP", NULL },\r
343 { UNIT_HANGUP, UNIT_HANGUP, "hangup", "HANGUP", NULL },\r
344 { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS",\r
345 &set_addr, &show_addr, NULL },\r
346 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",\r
347 &set_vec, &vh_show_vec, NULL },\r
348 { MTAB_XTD|MTAB_VDV, 0, NULL, "AUTOCONFIGURE",\r
349 &set_addr_flt, NULL, NULL },\r
350 /* this one is dangerous, don't use yet */\r
351 { MTAB_XTD|MTAB_VDV|MTAB_VAL, 0, "lines", "LINES",\r
352 NULL, NULL, (REG *)&vh_nlreg },\r
353 { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &vh_summ },\r
354 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,\r
355 NULL, &vh_show, NULL },\r
356 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,\r
357 NULL, &vh_show, NULL },\r
358 { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",\r
359 &tmxr_dscln, NULL, &vh_desc },\r
360 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEBUG", NULL,\r
361 NULL, &vh_show_debug, NULL },\r
362 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "RBUF", NULL,\r
363 NULL, &vh_show_rbuf, NULL },\r
364 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "TXQ", NULL,\r
365 NULL, &vh_show_txq, NULL },\r
366 { 0 }\r
367};\r
368\r
369DEVICE vh_dev = {\r
370 "VH", /* name */\r
371 vh_unit, /* units */\r
372 (REG *)vh_reg, /* registers */\r
373 (MTAB *)vh_mod, /* modifiers */\r
374 VH_MUXES, /* # units */\r
375 DEV_RDX, /* address radix */\r
376 8, /* address width */\r
377 1, /* address increment */\r
378 DEV_RDX, /* data radix */\r
379 8, /* data width */\r
380 NULL, /* examine routine */\r
381 NULL, /* deposit routine */\r
382 &vh_reset, /* reset routine */\r
383 NULL, /* boot routine */\r
384 &vh_attach, /* attach routine */\r
385 &vh_detach, /* detach routine */\r
386 (void *)&vh_dib, /* context */\r
387 DEV_FLTA | DEV_DISABLE | DEV_DIS |DEV_NET | DEV_QBUS | DEV_UBUS, /* flags */\r
388};\r
389\r
390/* Interrupt routines */\r
391\r
392static void vh_clr_rxint ( int32 vh )\r
393{\r
394 vh_rxi &= ~(1 << vh);\r
395 if (vh_rxi == 0)\r
396 CLR_INT (VHRX);\r
397 else\r
398 SET_INT (VHRX);\r
399}\r
400\r
401static void vh_set_rxint ( int32 vh )\r
402{\r
403 vh_rxi |= (1 << vh);\r
404 SET_INT (VHRX);\r
405}\r
406\r
407/* RX interrupt ack. (bus cycle) */\r
408\r
409static int32 vh_rxinta (void)\r
410{\r
411 int32 vh;\r
412\r
413 for (vh = 0; vh < VH_MUXES; vh++) {\r
414 if (vh_rxi & (1 << vh)) {\r
415 vh_clr_rxint (vh);\r
416 return (vh_dib.vec + (vh * 010));\r
417 }\r
418 }\r
419 return (0);\r
420}\r
421\r
422static void vh_clr_txint ( int32 vh )\r
423{\r
424 vh_txi &= ~(1 << vh);\r
425 if (vh_txi == 0)\r
426 CLR_INT (VHTX);\r
427 else\r
428 SET_INT (VHTX);\r
429}\r
430\r
431static void vh_set_txint ( int32 vh )\r
432{\r
433 vh_txi |= (1 << vh);\r
434 SET_INT (VHTX);\r
435}\r
436\r
437/* TX interrupt ack. (bus cycle) */\r
438\r
439static int32 vh_txinta (void)\r
440{\r
441 int32 vh;\r
442\r
443 for (vh = 0; vh < VH_MUXES; vh++) {\r
444 if (vh_txi & (1 << vh)) {\r
445 vh_clr_txint (vh);\r
446 return (vh_dib.vec + 4 + (vh * 010));\r
447 }\r
448 }\r
449 return (0);\r
450}\r
451/* RX FIFO get/put routines */\r
452\r
453/* return 0 on success, -1 on FIFO overflow */\r
454\r
455static int32 fifo_put ( int32 vh,\r
456 TMLX *lp,\r
457 int32 data )\r
458{\r
459 int32 status = 0;\r
460\r
461 if (lp == NULL)\r
462 goto override;\r
463 /* this might have to move to vh_getc() */\r
464 if ((lp->lnctrl & LNCTRL_OAUTO) && ((data & RBUF_DIAG) == 0)) {\r
465 TMLX *l0p;\r
466 /* implement transmitted data flow control */\r
467 switch (data & 0377) {\r
468 case XON:\r
469 lp->tbuf2 |= TB2_TX_ENA;\r
470 goto common;\r
471 case XOFF:\r
472 lp->tbuf2 &= ~TB2_TX_ENA;\r
473 common:\r
474 /* find line 0 for this controller */\r
475 l0p = &vh_parm[vh * VH_LINES];\r
476 if (l0p->lpr & LPR_DISAB_XRPT)\r
477 return (0);\r
478 break;\r
479 default:\r
480 break;\r
481 }\r
482 }\r
483/* BUG: which of the following 2 is correct? */\r
484 /* if ((data & RBUF_DIAG) == RBUF_DIAG) */\r
485 if (data & RBUF_DIAG)\r
486 goto override;\r
487 if (((lp->lnctrl >> LNCTRL_V_MAINT) & LNCTRL_M_MAINT) == 2)\r
488 goto override;\r
489 if (!(lp->lnctrl & LNCTRL_RX_ENA))\r
490 return (0); \r
491override:\r
492 vh_csr[vh] |= CSR_RX_DATA_AVAIL;\r
493 if (rbuf_idx[vh] < FIFO_SIZE) {\r
494 vh_rbuf[vh][rbuf_idx[vh]] = data;\r
495 rbuf_idx[vh] += 1;\r
496 } else {\r
497 vh_ovrrun[vh] |= (1 << RBUF_GETLINE (data));\r
498 status = -1;\r
499 }\r
500 if (vh_csr[vh] & CSR_RXIE) {\r
501 if (vh_unit[vh].flags & UNIT_MODEDHU) {\r
502 /* was it a modem status change? */\r
503 if ((data & RBUF_DIAG) == RBUF_DIAG)\r
504 vh_set_rxint (vh);\r
505 /* look for FIFO alarm @ 3/4 full */\r
506 else if (rbuf_idx[vh] == FIFO_ALARM)\r
507 vh_set_rxint (vh);\r
508 else if (vh_timer[vh] == 0)\r
509 ; /* nothing, infinite timeout */\r
510 else if (vh_timer[vh] == 1)\r
511 vh_set_rxint (vh);\r
512 else if (vh_timeo[vh] == 0)\r
513 vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1;\r
514 } else {\r
515 /* Interrupt on transition _from_ an empty FIFO */\r
516 if (rbuf_idx[vh] == 1)\r
517 vh_set_rxint (vh);\r
518 }\r
519 }\r
520 if (rbuf_idx[vh] > FIFO_ALARM)\r
521 vh_crit |= (1 << vh);\r
522 /* Implement RX FIFO-level flow control */\r
523 if (lp != NULL) {\r
524 if ((lp->lnctrl & LNCTRL_FORCE_XOFF) || \r
525 ((vh_crit & (1 << vh)) && (lp->lnctrl & LNCTRL_IAUTO))) {\r
526 int32 chan = RBUF_GETLINE(data);\r
527 vh_stall[vh] ^= (1 << chan);\r
528 /* send XOFF every other character received */\r
529 if (vh_stall[vh] & (1 << chan))\r
530 vh_putc (vh, lp, chan, XOFF);\r
531 }\r
532 }\r
533 return (status);\r
534}\r
535\r
536static int32 fifo_get ( int32 vh )\r
537{\r
538 int32 data, i;\r
539\r
540 if (rbuf_idx[vh] == 0) {\r
541 vh_csr[vh] &= ~CSR_RX_DATA_AVAIL;\r
542 return (0);\r
543 }\r
544 /* pick off the first character, mark valid */\r
545 data = vh_rbuf[vh][0] | RBUF_DATA_VALID;\r
546 /* move the remainder up */\r
547 rbuf_idx[vh] -= 1;\r
548 for (i = 0; i < rbuf_idx[vh]; i++)\r
549 vh_rbuf[vh][i] = vh_rbuf[vh][i + 1];\r
550 /* rbuf_idx[vh] -= 1; */\r
551 /* look for any previous overruns */\r
552 if (vh_ovrrun[vh]) {\r
553 for (i = 0; i < VH_LINES; i++) {\r
554 if (vh_ovrrun[vh] & (1 << i)) {\r
555 fifo_put (vh, NULL, RBUF_OVERRUN_ERR |\r
556 RBUF_PUTLINE (i));\r
557 vh_ovrrun[vh] &= ~(1 << i);\r
558 break;\r
559 }\r
560 }\r
561 }\r
562 /* recompute FIFO alarm condition */\r
563 if ((rbuf_idx[vh] < FIFO_HALF) && (vh_crit & (1 << vh))) {\r
564 vh_crit &= ~(1 << vh);\r
565 /* send XON to all XOFF'd channels on this controller */\r
566 for (i = 0; i < VH_LINES; i++) {\r
567 TMLX *lp = &vh_parm[(vh * VH_LINES) + i];\r
568 if (lp->lnctrl & LNCTRL_FORCE_XOFF)\r
569 continue;\r
570 if (vh_stall[vh] & (1 << i)) {\r
571 vh_putc (vh, NULL, i, XON);\r
572 vh_stall[vh] &= ~(1 << i);\r
573 }\r
574 }\r
575 }\r
576 return (data & 0177777);\r
577}\r
578/* TX Q manipulation */\r
579\r
580static int32 dq_tx_report ( int32 vh )\r
581{\r
582 int32 data, i;\r
583\r
584 if (txq_idx[vh] == 0)\r
585 return (0);\r
586 data = vh_txq[vh][0];\r
587 txq_idx[vh] -= 1;\r
588 for (i = 0; i < txq_idx[vh]; i++)\r
589 vh_txq[vh][i] = vh_txq[vh][i + 1];\r
590 /* txq_idx[vh] -= 1; */\r
591 return (data & 0177777);\r
592}\r
593\r
594static void q_tx_report ( int32 vh,\r
595 int32 data )\r
596{\r
597 if (vh_csr[vh] & CSR_TXIE)\r
598 vh_set_txint (vh);\r
599 if (txq_idx[vh] >= TXQ_SIZE) {\r
600/* BUG: which of the following 2 is correct? */\r
601 dq_tx_report (vh);\r
602 /* return; */\r
603 }\r
604 vh_txq[vh][txq_idx[vh]] = CSR_TX_ACTION | data;\r
605 txq_idx[vh] += 1;\r
606}\r
607/* Channel get/put routines */\r
608\r
609static void HangupModem ( int32 vh,\r
610 TMLX *lp,\r
611 int32 chan )\r
612{\r
613 if (vh_unit[vh].flags & UNIT_MODEM)\r
614 lp->lstat &= ~(STAT_DCD|STAT_DSR|STAT_CTS|STAT_RI);\r
615 if (lp->lnctrl & LNCTRL_LINK_TYPE)\r
616 /* RBUF<0> = 0 for modem status */\r
617 fifo_put (vh, lp, RBUF_DIAG |\r
618 RBUF_PUTLINE (chan) |\r
619 ((lp->lstat >> 8) & 0376));\r
620 /* BUG: check for overflow above */\r
621}\r
622\r
623/* TX a character on a line, regardless of the TX enable state */\r
624\r
625static t_stat vh_putc ( int32 vh,\r
626 TMLX *lp,\r
627 int32 chan,\r
628 int32 data )\r
629{\r
630 int32 val;\r
631 t_stat status = SCPE_OK;\r
632\r
633 /* truncate to desired character length */\r
634 data &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) & LPR_M_CHAR_LGTH];\r
635 switch ((lp->lnctrl >> LNCTRL_V_MAINT) & LNCTRL_M_MAINT) {\r
636 case 0: /* normal */\r
637#if 0\r
638 /* check for (external) loopback setting */\r
639 switch (vh_loop[vh]) {\r
640 default:\r
641 case LOOP_NONE:\r
642 break;\r
643 }\r
644#endif\r
645 status = tmxr_putc_ln (lp->tmln, data);\r
646 if (status == SCPE_LOST) {\r
647 tmxr_reset_ln (lp->tmln);\r
648 HangupModem (vh, lp, chan);\r
649 } else if (status == SCPE_STALL) {\r
650 /* let's flush and try again */\r
651 tmxr_send_buffered_data (lp->tmln);\r
652 status = tmxr_putc_ln (lp->tmln, data);\r
653 }\r
654 break;\r
655 case 1: /* auto echo */\r
656 break;\r
657 case 2: /* local loopback */\r
658 if (lp->lnctrl & LNCTRL_BREAK)\r
659 val = fifo_put (vh, lp,\r
660 RBUF_FRAME_ERR | RBUF_PUTLINE (chan));\r
661 else\r
662 val = fifo_put (vh, lp,\r
663 RBUF_PUTLINE (chan) | data);\r
664 status = (val < 0) ? SCPE_TTMO : SCPE_OK;\r
665 break;\r
666 default: /* remote loopback */\r
667 break;\r
668 }\r
669 return (status);\r
670}\r
671\r
672/* Retrieve all stored input from TMXR and place in RX FIFO */\r
673\r
674static void vh_getc ( int32 vh )\r
675{\r
676 uint32 i, c;\r
677 TMLX *lp;\r
678\r
679 for (i = 0; i < VH_LINES; i++) {\r
680 lp = &vh_parm[(vh * VH_LINES) + i];\r
681 while (c = tmxr_getc_ln (lp->tmln)) {\r
682 if (c & SCPE_BREAK) {\r
683 fifo_put (vh, lp,\r
684 RBUF_FRAME_ERR | RBUF_PUTLINE (i));\r
685 /* BUG: check for overflow above */\r
686 } else {\r
687 c &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) &\r
688 LPR_M_CHAR_LGTH];\r
689 fifo_put (vh, lp, RBUF_PUTLINE (i) | c);\r
690 /* BUG: check for overflow above */\r
691 }\r
692 }\r
693 }\r
694}\r
695\r
696/* I/O dispatch routines */\r
697\r
698static t_stat vh_rd ( int32 *data,\r
699 int32 PA,\r
700 int32 access )\r
701{\r
702 int32 vh = ((PA - vh_dib.ba) >> 4) & VH_MNOMASK, line;\r
703 TMLX *lp;\r
704\r
705 switch ((PA >> 1) & 7) {\r
706 case 0: /* CSR */\r
707 *data = vh_csr[vh] | dq_tx_report (vh);\r
708 vh_csr[vh] &= ~0117400; /* clear the read-once bits */\r
709 break;\r
710 case 1: /* RBUF */\r
711 *data = fifo_get (vh);\r
712 break;\r
713 case 2: /* LPR */\r
714 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {\r
715 *data = 0;\r
716 break;\r
717 }\r
718 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
719 lp = &vh_parm[line];\r
720 *data = lp->lpr;\r
721 break;\r
722 case 3: /* STAT/FIFOSIZE */\r
723 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {\r
724 *data = 0;\r
725 break;\r
726 }\r
727 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
728 lp = &vh_parm[line];\r
729 *data = (lp->lstat & ~0377) | /* modem status */\r
730#if 0\r
731 (64 - tmxr_tqln (lp->tmln));\r
732fprintf (stderr, "\rtqln %d\n", 64 - tmxr_tqln (lp->tmln));\r
733#else\r
734 64;\r
735#endif\r
736 break;\r
737 case 4: /* LNCTRL */\r
738 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {\r
739 *data = 0;\r
740 break;\r
741 }\r
742 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
743 lp = &vh_parm[line];\r
744 *data = lp->lnctrl;\r
745 break;\r
746 case 5: /* TBUFFAD1 */\r
747 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {\r
748 *data = 0;\r
749 break;\r
750 }\r
751 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
752 lp = &vh_parm[line];\r
753 *data = lp->tbuf1;\r
754 break;\r
755 case 6: /* TBUFFAD2 */\r
756 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {\r
757 *data = 0;\r
758 break;\r
759 }\r
760 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
761 lp = &vh_parm[line];\r
762 *data = lp->tbuf2;\r
763 break;\r
764 case 7: /* TBUFFCT */\r
765 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) {\r
766 *data = 0;\r
767 break;\r
768 }\r
769 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
770 lp = &vh_parm[line];\r
771 *data = lp->tbuffct;\r
772 break;\r
773 default:\r
774 /* can't happen */\r
775 break;\r
776 }\r
777 return (SCPE_OK);\r
778}\r
779\r
780static t_stat vh_wr ( int32 data,\r
781 int32 PA,\r
782 int32 access )\r
783{\r
784 int32 vh = ((PA - vh_dib.ba) >> 4) & VH_MNOMASK, line;\r
785 TMLX *lp;\r
786\r
787 switch ((PA >> 1) & 7) { \r
788 case 0: /* CSR, but no read-modify-write */\r
789 if (access == WRITEB)\r
790 data = (PA & 1) ?\r
791 (vh_csr[vh] & 0377) | (data << 8) :\r
792 (vh_csr[vh] & ~0377) | data & 0377;\r
793 if (data & CSR_MASTER_RESET) {\r
794 if ((vh_unit[vh].flags & UNIT_MODEDHU) && (data & CSR_SKIP))\r
795 data &= ~CSR_MASTER_RESET;\r
796 sim_activate (&vh_unit[vh], clk_cosched (tmxr_poll));\r
797 /* vh_mcount[vh] = 72; */ /* 1.2 seconds */\r
798 vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */\r
799 }\r
800 if ((data & CSR_RXIE) == 0)\r
801 vh_clr_rxint (vh);\r
802 /* catch the RXIE transition if the FIFO is not empty */\r
803 else if (((vh_csr[vh] & CSR_RXIE) == 0) &&\r
804 (rbuf_idx[vh] != 0)) {\r
805 if (vh_unit[vh].flags & UNIT_MODEDHU) {\r
806 if (rbuf_idx[vh] > FIFO_ALARM)\r
807 vh_set_rxint (vh);\r
808 else if (vh_timer[vh] == 0)\r
809 ;\r
810 else if (vh_timer[vh] == 1)\r
811 vh_set_rxint (vh);\r
812 else if (vh_timeo[vh] == 0)\r
813 vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1;\r
814 } else {\r
815 vh_set_rxint (vh);\r
816 }\r
817 }\r
818 if ((data & CSR_TXIE) == 0)\r
819 vh_clr_txint (vh);\r
820 else if (((vh_csr[vh] & CSR_TXIE) == 0) &&\r
821 (txq_idx[vh] != 0))\r
822 vh_set_txint (vh);\r
823 vh_csr[vh] = (vh_csr[vh] & ~((uint16) CSR_RW)) | (data & (uint16) CSR_RW);\r
824 break;\r
825 case 1: /* TXCHAR/RXTIMER */\r
826 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)\r
827 break;\r
828 if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {\r
829 vh_mcount[vh] = 1;\r
830 break;\r
831 }\r
832 if (vh_unit[vh].flags & UNIT_MODEDHU) {\r
833 if (CSR_GETCHAN (vh_csr[vh]) != 0)\r
834 break;\r
835 if (access == WRITEB)\r
836 data = (PA & 1) ?\r
837 (vh_timer[vh] & 0377) | (data << 8) :\r
838 (vh_timer[vh] & ~0377) | (data & 0377);\r
839 vh_timer[vh] = data & 0377;\r
840#if 0\r
841 if (vh_csr[vh] & CSR_RXIE) {\r
842 if (rbuf_idx[vh] > FIFO_ALARM)\r
843 vh_set_rxint (vh);\r
844 else if (vh_timer[vh] == 0)\r
845 ;\r
846 else if (vh_timer[vh] == 1)\r
847 vh_set_rxint (vh);\r
848 else if (vh_timeo[vh] == 0)\r
849 vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1;\r
850 }\r
851#endif\r
852 } else {\r
853 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
854 lp = &vh_parm[line];\r
855 if (access == WRITEB)\r
856 data = (PA & 1) ? \r
857 (lp->txchar & 0377) | (data << 8) :\r
858 (lp->txchar & ~0377) | (data & 0377);\r
859 lp->txchar = data; /* TXCHAR */\r
860 if (lp->txchar & TXCHAR_TX_DATA_VALID) {\r
861 if (lp->tbuf2 & TB2_TX_ENA)\r
862 vh_putc (vh, lp,\r
863 CSR_GETCHAN (vh_csr[vh]),\r
864 lp->txchar);\r
865 q_tx_report (vh,\r
866 CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE);\r
867 lp->txchar &= ~TXCHAR_TX_DATA_VALID;\r
868 }\r
869 }\r
870 break;\r
871 case 2: /* LPR */\r
872 if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {\r
873 vh_mcount[vh] = 1;\r
874 break;\r
875 }\r
876 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)\r
877 break;\r
878 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
879 lp = &vh_parm[line];\r
880 if (access == WRITEB)\r
881 data = (PA & 1) ?\r
882 (lp->lpr & 0377) | (data << 8) :\r
883 (lp->lpr & ~0377) | data & 0377;\r
884 /* Modify only if CSR<3:0> == 0 */\r
885 if (CSR_GETCHAN (vh_csr[vh]) != 0)\r
886 data &= ~LPR_DISAB_XRPT;\r
887 lp->lpr = data;\r
888 if (((lp->lpr >> LPR_V_DIAG) & LPR_M_DIAG) == 1) {\r
889 fifo_put (vh, lp,\r
890 RBUF_DIAG |\r
891 RBUF_PUTLINE (CSR_GETCHAN (vh_csr[vh])) |\r
892 BMP_OK);\r
893 /* BUG: check for overflow above */\r
894 lp->lpr &= ~(LPR_M_DIAG << LPR_V_DIAG);\r
895 }\r
896 break;\r
897 case 3: /* STAT/FIFODATA */\r
898 if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {\r
899 vh_mcount[vh] = 1;\r
900 break;\r
901 }\r
902 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)\r
903 break;\r
904 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
905 lp = &vh_parm[line];\r
906 if (vh_unit[vh].flags & UNIT_MODEDHU) {\r
907 /* high byte writes not allowed */\r
908 if (PA & 1)\r
909 break;\r
910 /* transmit 1 or 2 characters */\r
911 if (!(lp->tbuf2 & TB2_TX_ENA))\r
912 break;\r
913 vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), data);\r
914 q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE);\r
915 if (access != WRITEB)\r
916 vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]),\r
917 data >> 8);\r
918 }\r
919 break;\r
920 case 4: /* LNCTRL */\r
921 if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {\r
922 vh_mcount[vh] = 1;\r
923 break;\r
924 }\r
925 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) \r
926 break;\r
927 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
928 lp = &vh_parm[line];\r
929 if (access == WRITEB)\r
930 data = (PA & 1) ?\r
931 (lp->lnctrl & 0377) | (data << 8) :\r
932 (lp->lnctrl & ~0377) | data & 0377;\r
933 /* catch the abort TX transition */\r
934 if (!(lp->lnctrl & LNCTRL_TX_ABORT) &&\r
935 (data & LNCTRL_TX_ABORT)) {\r
936 if ((lp->tbuf2 & TB2_TX_ENA) &&\r
937 (lp->tbuf2 & TB2_TX_DMA_START)) {\r
938 lp->tbuf2 &= ~TB2_TX_DMA_START;\r
939 q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE);\r
940 }\r
941 }\r
942 /* Implement program-initiated flow control */\r
943 if ( (data & LNCTRL_FORCE_XOFF) &&\r
944 !(lp->lnctrl & LNCTRL_FORCE_XOFF) ) {\r
945 if (!(lp->lnctrl & LNCTRL_IAUTO))\r
946 vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XOFF);\r
947 } else if ( !(data & LNCTRL_FORCE_XOFF) &&\r
948 (lp->lnctrl & LNCTRL_FORCE_XOFF) ) {\r
949 if (!(lp->lnctrl & LNCTRL_IAUTO))\r
950 vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON);\r
951 else if (!(vh_crit & (1 << vh)) &&\r
952 (vh_stall[vh] & (1 << CSR_GETCHAN (vh_csr[vh]))))\r
953 vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON);\r
954 }\r
955 if ( (data & LNCTRL_IAUTO) && /* IAUTO 0->1 */\r
956 !(lp->lnctrl & LNCTRL_IAUTO) ) {\r
957 if (!(lp->lnctrl & LNCTRL_FORCE_XOFF)) {\r
958 if (vh_crit & (1 << vh)) {\r
959 vh_putc (vh, lp,\r
960 CSR_GETCHAN (vh_csr[vh]), XOFF);\r
961 vh_stall[vh] |= (1 << CSR_GETCHAN (vh_csr[vh]));\r
962 }\r
963 } else {\r
964 /* vh_stall[vh] |= (1 << CSR_GETCHAN (vh_csr[vh])) */;\r
965 }\r
966 } else if ( !(data & LNCTRL_IAUTO) &&\r
967 (lp->lnctrl & LNCTRL_IAUTO) ) {\r
968 if (!(lp->lnctrl & LNCTRL_FORCE_XOFF))\r
969 vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON);\r
970 }\r
971 /* check modem control bits */\r
972 if ( !(data & LNCTRL_DTR) && /* DTR 1->0 */\r
973 (lp->lnctrl & LNCTRL_DTR)) {\r
974 if ((lp->tmln->conn) && (vh_unit[vh].flags & UNIT_HANGUP)) {\r
975 tmxr_linemsg (lp->tmln, "\r\nLine hangup\r\n");\r
976 tmxr_reset_ln (lp->tmln);\r
977 }\r
978 HangupModem (vh, lp, CSR_GETCHAN (vh_csr[vh]));\r
979 }\r
980 lp->lnctrl = data;\r
981 lp->tmln->rcve = (data & LNCTRL_RX_ENA) ? 1 : 0;\r
982 tmxr_poll_rx (&vh_desc);\r
983 vh_getc (vh);\r
984 if (lp->lnctrl & LNCTRL_BREAK)\r
985 vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), 0);\r
986 break;\r
987 case 5: /* TBUFFAD1 */\r
988 if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {\r
989 vh_mcount[vh] = 1;\r
990 break;\r
991 }\r
992 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) \r
993 break;\r
994 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
995 lp = &vh_parm[line];\r
996 if (access == WRITEB)\r
997 data = (PA & 1) ?\r
998 (lp->tbuf1 & 0377) | (data << 8) :\r
999 (lp->tbuf1 & ~0377) | data & 0377;\r
1000 lp->tbuf1 = data;\r
1001 break;\r
1002 case 6: /* TBUFFAD2 */\r
1003 if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {\r
1004 vh_mcount[vh] = 1;\r
1005 break;\r
1006 }\r
1007 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)\r
1008 break;\r
1009 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
1010 lp = &vh_parm[line];\r
1011 if (access == WRITEB)\r
1012 data = (PA & 1) ?\r
1013 (lp->tbuf2 & 0377) | (data << 8) :\r
1014 (lp->tbuf2 & ~0377) | data & 0377;\r
1015 lp->tbuf2 = data;\r
1016 /* if starting a DMA, clear DMA_ERR */\r
1017 if (vh_unit[vh].flags & UNIT_FASTDMA) {\r
1018 doDMA (vh, CSR_GETCHAN (vh_csr[vh]));\r
1019 tmxr_send_buffered_data (lp->tmln);\r
1020 }\r
1021 break;\r
1022 case 7: /* TBUFFCT */\r
1023 if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {\r
1024 vh_mcount[vh] = 1;\r
1025 break;\r
1026 }\r
1027 if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)\r
1028 break;\r
1029 line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]);\r
1030 lp = &vh_parm[line];\r
1031 if (access == WRITEB)\r
1032 data = (PA & 1) ?\r
1033 (lp->tbuffct & 0377) | (data << 8) :\r
1034 (lp->tbuffct & ~0377) | data & 0377;\r
1035 lp->tbuffct = data;\r
1036 break;\r
1037 default:\r
1038 /* can't happen */\r
1039 break;\r
1040 }\r
1041 return (SCPE_OK);\r
1042}\r
1043\r
1044static void doDMA ( int32 vh,\r
1045 int32 chan )\r
1046{\r
1047 int32 line, status;\r
1048 uint32 pa;\r
1049 TMLX *lp;\r
1050\r
1051 line = (vh * VH_LINES) + chan;\r
1052 lp = &vh_parm[line];\r
1053 if ((lp->tbuf2 & TB2_TX_ENA) && (lp->tbuf2 & TB2_TX_DMA_START)) {\r
1054/* BUG: should compare against available xmit buffer space */\r
1055 pa = lp->tbuf1;\r
1056 pa |= (lp->tbuf2 & TB2_M_TBUFFAD) << 16;\r
1057 status = chan << CSR_V_TX_LINE;\r
1058 while (lp->tbuffct) {\r
1059 uint8 buf;\r
1060 if (Map_ReadB (pa, 1, &buf)) {\r
1061 status |= CSR_TX_DMA_ERR;\r
1062 lp->tbuffct = 0;\r
1063 break;\r
1064 }\r
1065 if (vh_putc (vh, lp, chan, buf) != SCPE_OK)\r
1066 break;\r
1067 /* pa = (pa + 1) & PAMASK; */\r
1068 pa = (pa + 1) & ((1 << 22) - 1);\r
1069 lp->tbuffct--;\r
1070 }\r
1071 lp->tbuf1 = pa & 0177777;\r
1072 lp->tbuf2 = (lp->tbuf2 & ~TB2_M_TBUFFAD) |\r
1073 ((pa >> 16) & TB2_M_TBUFFAD);\r
1074 if (lp->tbuffct == 0) {\r
1075 lp->tbuf2 &= ~TB2_TX_DMA_START;\r
1076 q_tx_report (vh, status);\r
1077 }\r
1078 }\r
1079}\r
1080\r
1081/* Perform many of the functions of PROC2 */\r
1082\r
1083static t_stat vh_svc ( UNIT *uptr )\r
1084{\r
1085 int32 vh, newln, i;\r
1086\r
1087 /* scan all muxes for countdown reset */\r
1088 for (vh = 0; vh < VH_MUXES; vh++) {\r
1089 if (vh_csr[vh] & CSR_MASTER_RESET) {\r
1090 if (vh_mcount[vh] != 0)\r
1091 vh_mcount[vh] -= 1;\r
1092 else\r
1093 vh_clear (vh, FALSE);\r
1094 }\r
1095 }\r
1096 /* sample every 10ms for modem changes (new connections) */\r
1097 newln = tmxr_poll_conn (&vh_desc);\r
1098 if (newln >= 0) {\r
1099 TMLX *lp;\r
1100 int32 line;\r
1101 vh = newln / VH_LINES; /* determine which mux */\r
1102 line = newln - (vh * VH_LINES);\r
1103 lp = &vh_parm[newln];\r
1104 lp->lstat |= STAT_DSR | STAT_DCD | STAT_CTS;\r
1105 if (!(lp->lnctrl & LNCTRL_DTR))\r
1106 lp->lstat |= STAT_RI;\r
1107 if (lp->lnctrl & LNCTRL_LINK_TYPE)\r
1108 fifo_put (vh, lp, RBUF_DIAG |\r
1109 RBUF_PUTLINE (line) |\r
1110 ((lp->lstat >> 8) & 0376));\r
1111 /* BUG: should check for overflow above */\r
1112 }\r
1113 /* scan all muxes, lines for DMA to complete; start every 3.12ms */\r
1114 for (vh = 0; vh < VH_MUXES; vh++) {\r
1115 for (i = 0; i < VH_LINES; i++)\r
1116 doDMA (vh, i);\r
1117 }\r
1118 /* interrupt driven in a real DHQ */\r
1119 tmxr_poll_rx (&vh_desc);\r
1120 for (vh = 0; vh < VH_MUXES; vh++)\r
1121 vh_getc (vh);\r
1122 tmxr_poll_tx (&vh_desc);\r
1123 /* scan all DHU-mode muxes for RX FIFO timeout */\r
1124 for (vh = 0; vh < VH_MUXES; vh++) {\r
1125 if (vh_unit[vh].flags & UNIT_MODEDHU) {\r
1126 if (vh_timeo[vh] && (vh_csr[vh] & CSR_RXIE)) {\r
1127 vh_timeo[vh] -= 1;\r
1128 if ((vh_timeo[vh] == 0) && rbuf_idx[vh])\r
1129 vh_set_rxint (vh);\r
1130 }\r
1131 }\r
1132 }\r
1133 sim_activate (uptr, tmxr_poll); /* requeue ourselves */\r
1134 return (SCPE_OK);\r
1135}\r
1136\r
1137/* init a channel on a controller */\r
1138\r
1139/* set for:\r
1140send/receive 9600\r
11418 data bits\r
11421 stop bit\r
1143no parity\r
1144parity odd\r
1145auto-flow off\r
1146RX disabled\r
1147TX enabled\r
1148no break on line\r
1149no loopback\r
1150link type set to data-leads only\r
1151DTR & RTS off\r
1152DMA character counter 0\r
1153DMA start address registers 0\r
1154TX_DMA_START 0\r
1155TX_ABORT 0\r
1156auto-flow reports enabled\r
1157FIFO size set to 64\r
1158*/\r
1159\r
1160static void vh_init_chan ( int32 vh,\r
1161 int32 chan )\r
1162{\r
1163 int32 line;\r
1164 TMLX *lp;\r
1165\r
1166 line = (vh * VH_LINES) + chan;\r
1167 lp = &vh_parm[line];\r
1168 lp->lpr = (RATE_9600 << LPR_V_TX_SPEED) |\r
1169 (RATE_9600 << LPR_V_RX_SPEED) |\r
1170 (03 << LPR_V_CHAR_LGTH);\r
1171 lp->lnctrl = 0;\r
1172 lp->lstat &= ~(STAT_MDL | STAT_DHUID | STAT_RI);\r
1173 if (vh_unit[vh].flags & UNIT_MODEDHU)\r
1174 lp->lstat |= STAT_DHUID | 64;\r
1175 if (!(vh_unit[vh].flags & UNIT_MODEM))\r
1176 lp->lstat |= STAT_DSR | STAT_DCD | STAT_CTS;\r
1177 lp->tmln->xmte = 1;\r
1178 lp->tmln->rcve = 0;\r
1179 lp->tbuffct = 0;\r
1180 lp->tbuf1 = 0;\r
1181 lp->tbuf2 = TB2_TX_ENA;\r
1182 lp->txchar = 0;\r
1183}\r
1184\r
1185/* init a controller; flag true if BINIT, false if master.reset */\r
1186\r
1187static t_stat vh_clear ( int32 vh,\r
1188 t_bool flag )\r
1189{\r
1190 int32 i;\r
1191\r
1192 txq_idx[vh] = 0;\r
1193 rbuf_idx[vh] = 0;\r
1194 /* put 8 diag bytes in FIFO: 6 SELF_x, 2 circuit revision codes */\r
1195 if (vh_csr[vh] & CSR_SKIP) {\r
1196 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(0) | SELF_SKIP);\r
1197 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(1) | SELF_SKIP);\r
1198 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(2) | SELF_SKIP);\r
1199 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(3) | SELF_SKIP);\r
1200 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(4) | SELF_SKIP);\r
1201 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(5) | SELF_SKIP);\r
1202 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(6) | 0107);\r
1203 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(7) | 0105);\r
1204 } else {\r
1205 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(0) | SELF_NULL);\r
1206 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(1) | SELF_NULL);\r
1207 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(2) | SELF_NULL);\r
1208 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(3) | SELF_NULL);\r
1209 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(4) | SELF_NULL);\r
1210 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(5) | SELF_NULL);\r
1211 /* PROC2 ver. 1 */\r
1212 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(6) | 0107);\r
1213 /* PROC1 ver. 1 */\r
1214 fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(7) | 0105);\r
1215 }\r
1216 vh_csr[vh] &= ~(CSR_TX_ACTION|CSR_DIAG_FAIL|CSR_MASTER_RESET);\r
1217 if (flag)\r
1218 vh_csr[vh] &= ~(CSR_TXIE|CSR_RXIE|CSR_SKIP);\r
1219 vh_csr[vh] |= CSR_TX_DMA_ERR | (CSR_M_TX_LINE << CSR_V_TX_LINE);\r
1220 vh_clr_rxint (vh);\r
1221 vh_clr_txint (vh);\r
1222 vh_timer[vh] = 1;\r
1223 vh_timeo[vh] = 0;\r
1224 vh_ovrrun[vh] = 0;\r
1225 for (i = 0; i < VH_LINES; i++)\r
1226 vh_init_chan (vh, i);\r
1227 vh_crit &= ~(1 << vh);\r
1228 vh_stall[vh] = 0;\r
1229 vh_loop[vh] = LOOP_NONE;\r
1230 return (SCPE_OK);\r
1231}\r
1232\r
1233/* Reset all controllers. Used by BINIT and RESET. */\r
1234\r
1235static t_stat vh_reset ( DEVICE *dptr )\r
1236{\r
1237 int32 i;\r
1238\r
1239 for (i = 0; i < (VH_MUXES * VH_LINES); i++)\r
1240 vh_parm[i].tmln = &vh_ldsc[i];\r
1241 for (i = 0; i < VH_MUXES; i++) {\r
1242#if defined (VM_PDP11)\r
1243 /* if Unibus, force DHU mode */\r
1244 if (UNIBUS)\r
1245 vh_unit[i].flags |= UNIT_MODEDHU;\r
1246#endif\r
1247 vh_clear (i, TRUE);\r
1248 }\r
1249 vh_rxi = vh_txi = 0;\r
1250 CLR_INT (VHRX);\r
1251 CLR_INT (VHTX);\r
1252 for (i = 0; i < VH_MUXES; i++)\r
1253 sim_cancel (&vh_unit[i]);\r
1254 return (auto_config (dptr->name, (dptr->flags & DEV_DIS) ? 0 : VH_MUXES));\r
1255}\r
1256\r
1257\r
1258static t_stat vh_attach ( UNIT *uptr,\r
1259 char *cptr )\r
1260{\r
1261 if (uptr == &vh_unit[0])\r
1262 return (tmxr_attach (&vh_desc, uptr, cptr));\r
1263 return (SCPE_NOATT);\r
1264}\r
1265\r
1266static t_stat vh_detach ( UNIT *uptr )\r
1267{\r
1268 return (tmxr_detach (&vh_desc, uptr));\r
1269}\r
1270\r
1271static t_stat vh_summ ( FILE *st,\r
1272 UNIT *uptr,\r
1273 int32 val,\r
1274 void *desc )\r
1275{\r
1276int32 i, t;\r
1277\r
1278for (i = t = 0; i < vh_desc.lines; i++) { /* get num conn */\r
1279 if (vh_ldsc[i].conn) t = t + 1; }\r
1280fprintf (st, "%d %s", t, (t == 1) ? "connection" : "connections");\r
1281return SCPE_OK;\r
1282}\r
1283\r
1284static t_stat vh_show (FILE *st, UNIT *uptr, int32 val, void *desc)\r
1285{\r
1286int32 i, t;\r
1287\r
1288for (i = t = 0; i < vh_desc.lines; i++) { /* loop thru conn */\r
1289 if (vh_ldsc[i].conn) {\r
1290 t = 1;\r
1291 if (val) tmxr_fconns (st, &vh_ldsc[i], i);\r
1292 else tmxr_fstats (st, &vh_ldsc[i], i); } }\r
1293if (t == 0) fprintf (st, "all disconnected\n");\r
1294return SCPE_OK;\r
1295}\r
1296\r
1297static t_stat vh_show_vec ( FILE *st,\r
1298 UNIT *uptr,\r
1299 int32 val,\r
1300 void *desc )\r
1301{\r
1302 return (show_vec (st, uptr, ((vh_desc.lines * 2) / VH_LINES), desc));\r
1303}\r
1304\r
1305static void debug_line ( FILE *st,\r
1306 int32 vh,\r
1307 int32 chan )\r
1308{\r
1309 int32 line;\r
1310 TMLX *lp;\r
1311\r
1312 line = (vh * VH_LINES) + chan;\r
1313 lp = &vh_parm[line];\r
1314 fprintf (st, "\tline %d\tlpr %06o, lnctrl %06o, lstat %06o\n",\r
1315 chan, lp->lpr, lp->lnctrl, lp->lstat);\r
1316 fprintf (st, "\t\ttbuffct %06o, tbuf1 %06o, tbuf2 %06o, txchar %06o\n",\r
1317 lp->tbuffct, lp->tbuf1, lp->tbuf2, lp->txchar);\r
1318 fprintf (st, "\t\ttmln rcve %d xmte %d\n",\r
1319 lp->tmln->rcve, lp->tmln->xmte);\r
1320}\r
1321\r
1322static t_stat vh_show_debug ( FILE *st,\r
1323 UNIT *uptr,\r
1324 int32 val,\r
1325 void *desc )\r
1326{\r
1327 int32 i, j;\r
1328\r
1329 fprintf (st, "VH:\trxi %d, txi %d\n", vh_rxi, vh_txi);\r
1330 for (i = 0; i < VH_MUXES; i++) {\r
1331 fprintf (st, "VH%d:\tmode %s, crit %d\n", i,\r
1332 vh_unit[i].flags & UNIT_MODEDHU ? "DHU" : "DHV",\r
1333 vh_crit & (1 << i));\r
1334 fprintf (st, "\tCSR %06o, mcount %d, rbuf_idx %d, txq_idx %d\n",\r
1335 vh_csr[i], vh_mcount[i], rbuf_idx[i], txq_idx[i]);\r
1336 for (j = 0; j < VH_LINES; j++)\r
1337 debug_line (st, i, j);\r
1338 }\r
1339 return (SCPE_OK);\r
1340}\r
1341\r
1342static t_stat vh_show_rbuf ( FILE *st,\r
1343 UNIT *uptr,\r
1344 int32 val,\r
1345 void *desc )\r
1346{\r
1347 int32 i;\r
1348\r
1349 for (i = 0; i < rbuf_idx[0]; i++)\r
1350 fprintf (st, "%03d: %06o\n", i, vh_rbuf[0][i]);\r
1351 return (SCPE_OK);\r
1352}\r
1353\r
1354static t_stat vh_show_txq ( FILE *st,\r
1355 UNIT *uptr,\r
1356 int32 val,\r
1357 void *desc )\r
1358{\r
1359 int32 i;\r
1360\r
1361 for (i = 0; i < txq_idx[0]; i++)\r
1362 fprintf (st, "%02d: %06o\n\r", i, vh_txq[0][i]);\r
1363 return (SCPE_OK);\r
1364}\r
1365\r