1 /* vax780_stddev.c: VAX 11/780 standard I/O devices
2 
3    Copyright (c) 1998-2012, Robert M Supnik
4 
5    Permission is hereby granted, free of charge, to any person obtaining a
6    copy of this software and associated documentation files (the "Software"),
7    to deal in the Software without restriction, including without limitation
8    the rights to use, copy, modify, merge, publish, distribute, sublicense,
9    and/or sell copies of the Software, and to permit persons to whom the
10    Software is furnished to do so, subject to the following conditions:
11 
12    The above copyright notice and this permission notice shall be included in
13    all copies or substantial portions of the Software.
14 
15    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18    ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22    Except as contained in this notice, the name of Robert M Supnik shall not be
23    used in advertising or otherwise to promote the sale, use or other dealings
24    in this Software without prior written authorization from Robert M Supnik.
25 
26    tti          console input
27    tto          console output
28    rx           console floppy
29    todr         TODR clock
30    tmr          interval timer
31 
32    18-Apr-12    RMS     Revised to use clock coscheduling
33    21-Mar-11    RMS     Added reboot capability
34    17-Aug-08    RMS     Resync TODR on any clock reset
35    18-Jun-07    RMS     Added UNIT_IDLE flag to console input, clock
36    29-Oct-06    RMS     Added clock coscheduler function
37                         Synced keyboard to clock for idling
38    11-May-06    RMS     Revised timer logic for EVKAE
39    22-Nov-05    RMS     Revised for new terminal processing routines
40    10-Mar-05    RMS     Fixed bug in timer schedule routine (Mark Hittinger)
41    08-Sep-04    RMS     Cloned from vax_stddev.c, vax_sysdev.c, and pdp11_rx.c
42 
43    The console floppy protocol is based on the description in the 1982 VAX
44    Architecture Reference Manual:
45 
46    TXDB<11:8> = 0       ->      normal console output
47    TXDB<11:8> = 1       ->      data output to floppy
48    TXDB<11:8> = 3       ->      read communications region
49    TXDB<11:8> = 9       ->      command output to floppy
50    TXDB<11:8> = F       ->      flag output (e.g., reboot)
51 
52    RXDB<11:8> = 0       ->      normal terminal input
53    RXDB<11:8> = 1       ->      data input from floppy
54    RXDB<11:8> = 3       ->      communications region data
55    RXDB<11:8> = 2       ->      status input from floppy
56    RXDB<11:8> = 9       ->      "command" input from floppy (protocol error)
57 */
58 
59 #include "vax_defs.h"
60 #include <time.h>
61 
62 /* Terminal definitions */
63 
64 #define RXCS_RD         (CSR_DONE + CSR_IE)             /* terminal input */
65 #define RXCS_WR         (CSR_IE)
66 #define RXDB_ERR        0x8000                          /* error */
67 #define RXDB_OVR        0x4000                          /* overrun */
68 #define RXDB_FRM        0x2000                          /* framing error */
69 #define TXCS_RD         (CSR_DONE + CSR_IE)             /* terminal output */
70 #define TXCS_WR         (CSR_IE)
71 #define TXDB_V_SEL      8                               /* unit select */
72 #define TXDB_M_SEL      0xF
73 #define  TXDB_FDAT      0x1                             /* floppy data */
74 #define  TXDB_COMM      0x3                             /* console mem read */
75 #define  TXDB_FCMD      0x9                             /* floppy cmd */
76 #define  TXDB_MISC      0xF                             /* console misc */
77 #define COMM_LNT        0200                            /* comm region lnt */
78 #define COMM_MASK       (COMM_LNT - 1)                  /* comm region mask */
79 #define  COMM_GH        0144                            /* GH flag */
80 #define  COMM_WRMS      0145                            /* warm start */
81 #define  COMM_CLDS      0146                            /* cold start */
82 #define  COMM_APTL      0147                            /* APT load */
83 #define  COMM_LAST      0150                            /* last position */
84 #define  COMM_AUTO      0151                            /* auto restart */
85 #define  COMM_PCSV      0152                            /* PCS version */
86 #define  COMM_WCSV      0153                            /* WCS version */
87 #define  COMM_WCSS      0154                            /* WCS secondary */
88 #define  COMM_FPLV      0155                            /* FPLA version */
89 #define COMM_DATA       0x300                           /* comm data return */
90 #define MISC_MASK        0xFF                           /* console data mask */
91 #define  MISC_SWDN       0x1                            /* software done */
92 #define  MISC_BOOT       0x2                            /* reboot */
93 #define  MISC_CLWS       0x3                            /* clear warm start */
94 #define  MISC_CLCS       0x4                            /* clear cold start */
95 #define TXDB_SEL        (TXDB_M_SEL << TXDB_V_SEL)      /* non-terminal */
96 #define TXDB_GETSEL(x)  (((x) >> TXDB_V_SEL) & TXDB_M_SEL)
97 
98 /* Clock definitions */
99 
100 #define TMR_CSR_ERR     0x80000000                      /* error W1C */
101 #define TMR_CSR_DON     0x00000080                      /* done W1C */
102 #define TMR_CSR_IE      0x00000040                      /* int enb RW */
103 #define TMR_CSR_SGL     0x00000020                      /* single WO */
104 #define TMR_CSR_XFR     0x00000010                      /* xfer WO */
105 #define TMR_CSR_RUN     0x00000001                      /* run RW */
106 #define TMR_CSR_RD      (TMR_CSR_W1C | TMR_CSR_WR)
107 #define TMR_CSR_W1C     (TMR_CSR_ERR | TMR_CSR_DON)
108 #define TMR_CSR_WR      (TMR_CSR_IE | TMR_CSR_RUN)
109 #define TMR_INC         10000                           /* usec/interval */
110 #define CLK_DELAY       5000                            /* 100 Hz */
111 #define TMXR_MULT       1                               /* 100 Hz */
112 
113 /* Floppy definitions */
114 
115 #define FL_NUMTR        77                              /* tracks/disk */
116 #define FL_M_TRACK      0377
117 #define FL_NUMSC        26                              /* sectors/track */
118 #define FL_M_SECTOR     0177
119 #define FL_NUMBY        128                             /* bytes/sector */
120 #define FL_SIZE         (FL_NUMTR * FL_NUMSC * FL_NUMBY)        /* bytes/disk */
121 #define UNIT_V_WLK      (UNIT_V_UF)                     /* write locked */
122 #define UNIT_WLK        (1u << UNIT_V_UF)
123 #define UNIT_WPRT       (UNIT_WLK | UNIT_RO)            /* write protect */
124 
125 #define FL_IDLE         0                               /* idle state */
126 #define FL_RWDS         1                               /* rw, sect next */
127 #define FL_RWDT         2                               /* rw, track next */
128 #define FL_READ         3                               /* read */
129 #define FL_READ1        4
130 #define FL_WRITE        5                               /* write */
131 #define FL_WRITE1       6
132 #define FL_FILL         7                               /* fill buffer */
133 #define FL_EMPTY        8                               /* empty buffer */
134 #define FL_READSTA      9                               /* read status */
135 #define FL_DONE         10                              /* cmd done */
136 
137 #define FL_V_FNC        0                               /* floppy function */
138 #define FL_M_FNC        0xFF
139 #define  FL_FNCRD       0x0                             /* read */
140 #define  FL_FNCWR       0x1                             /* write */
141 #define  FL_FNCRS       0x2                             /* read status */
142 #define  FL_FNCWD       0x3                             /* write del data */
143 #define  FL_FNCCA       0x4                             /* cancel */
144 #define FL_CDATA        0x100                           /* returned data */
145 #define FL_CDONE        0x200                           /* completion code */
146 #define  FL_STACRC      0x001                           /* status bits */
147 #define  FL_STAPAR      0x002
148 #define  FL_STAINC      0x004
149 #define  FL_STADDA      0x040
150 #define  FL_STAERR      0x080
151 #define FL_CPROT        0x905                           /* protocol error */
152 #define FL_GETFNC(x)    (((x) >> FL_V_FNC) & FL_M_FNC)
153 
154 #define TRACK u3                                        /* current track */
155 #define CALC_DA(t,s) (((t) * FL_NUMSC) + ((s) - 1)) * FL_NUMBY
156 
157 int32 tti_csr = 0;                                      /* control/status */
158 int32 tti_buf = 0;                                      /* buffer */
159 int32 tti_int = 0;                                      /* interrupt */
160 int32 tto_csr = 0;                                      /* control/status */
161 int32 tto_buf = 0;                                      /* buffer */
162 int32 tto_int = 0;                                      /* interrupt */
163 
164 int32 tmr_iccs = 0;                                     /* interval timer csr */
165 uint32 tmr_icr = 0;                                     /* curr interval */
166 uint32 tmr_nicr = 0;                                    /* next interval */
167 uint32 tmr_inc = 0;                                     /* timer increment */
168 int32 tmr_sav = 0;                                      /* timer save */
169 int32 tmr_int = 0;                                      /* interrupt */
170 int32 tmr_use_100hz = 1;                                /* use 100Hz for timer */
171 int32 clk_tps = 100;                                    /* ticks/second */
172 int32 tmxr_poll = CLK_DELAY * TMXR_MULT;                /* term mux poll */
173 int32 tmr_poll = CLK_DELAY;                             /* pgm timer poll */
174 int32 todr_reg = 0;                                     /* TODR register */
175 
176 int32 fl_fnc = 0;                                       /* function */
177 int32 fl_esr = 0;                                       /* error status */
178 int32 fl_ecode = 0;                                     /* error code */
179 int32 fl_track = 0;                                     /* desired track */
180 int32 fl_sector = 0;                                    /* desired sector */
181 int32 fl_state = FL_IDLE;                               /* controller state */
182 int32 fl_stopioe = 1;                                   /* stop on error */
183 int32 fl_swait = 100;                                   /* seek, per track */
184 int32 fl_cwait = 50;                                    /* command time */
185 int32 fl_xwait = 20;                                    /* tr set time */
186 uint8 fl_buf[FL_NUMBY] = { 0 };                         /* sector buffer */
187 int32 fl_bptr = 0;                                      /* buffer pointer */
188 
189 uint8 comm_region[COMM_LNT] = { 0 };                    /* comm region */
190 
191 extern int32 sim_switches;
192 extern jmp_buf save_env;
193 
194 t_stat tti_svc (UNIT *uptr);
195 t_stat tto_svc (UNIT *uptr);
196 t_stat clk_svc (UNIT *uptr);
197 t_stat tmr_svc (UNIT *uptr);
198 t_stat tti_reset (DEVICE *dptr);
199 t_stat tto_reset (DEVICE *dptr);
200 t_stat clk_reset (DEVICE *dptr);
201 t_stat tmr_reset (DEVICE *dptr);
202 t_stat fl_svc (UNIT *uptr);
203 t_stat fl_reset (DEVICE *dptr);
204 int32 icr_rd (t_bool interp);
205 void tmr_incr (uint32 inc);
206 void tmr_sched (void);
207 t_stat todr_resync (void);
208 t_stat fl_wr_txdb (int32 data);
209 t_bool fl_test_xfr (UNIT *uptr, t_bool wr);
210 void fl_protocol_error (void);
211 
212 extern int32 con_halt (int32 code, int32 cc);
213 
214 /* TTI data structures
215 
216    tti_dev      TTI device descriptor
217    tti_unit     TTI unit descriptor
218    tti_reg      TTI register list
219 */
220 
221 UNIT tti_unit = { UDATA (&tti_svc, TT_MODE_8B, 0), 0 };
222 
223 REG tti_reg[] = {
224     { HRDATA (RXDB, tti_buf, 16) },
225     { HRDATA (RXCS, tti_csr, 16) },
226     { FLDATA (INT, tti_int, 0) },
227     { FLDATA (DONE, tti_csr, CSR_V_DONE) },
228     { FLDATA (IE, tti_csr, CSR_V_IE) },
229     { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT },
230     { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT },
231     { NULL }
232     };
233 
234 MTAB tti_mod[] = {
235     { TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
236     { TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
237     { 0 }
238     };
239 
240 DEVICE tti_dev = {
241     "TTI", &tti_unit, tti_reg, tti_mod,
242     1, 10, 31, 1, 16, 8,
243     NULL, NULL, &tti_reset,
244     NULL, NULL, NULL,
245     NULL, 0
246     };
247 
248 /* TTO data structures
249 
250    tto_dev      TTO device descriptor
251    tto_unit     TTO unit descriptor
252    tto_reg      TTO register list
253 */
254 
255 UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_8B, 0), SERIAL_OUT_WAIT };
256 
257 REG tto_reg[] = {
258     { HRDATA (TXDB, tto_buf, 16) },
259     { HRDATA (TXCS, tto_csr, 16) },
260     { FLDATA (INT, tto_int, 0) },
261     { FLDATA (DONE, tto_csr, CSR_V_DONE) },
262     { FLDATA (IE, tto_csr, CSR_V_IE) },
263     { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT },
264     { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT + REG_NZ },
265     { NULL }
266     };
267 
268 MTAB tto_mod[] = {
269     { TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
270     { TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
271     { TT_MODE, TT_MODE_7P, "7p", "7P", NULL },
272     { 0 }
273     };
274 
275 DEVICE tto_dev = {
276     "TTO", &tto_unit, tto_reg, tto_mod,
277     1, 10, 31, 1, 16, 8,
278     NULL, NULL, &tto_reset,
279     NULL, NULL, NULL,
280     NULL, 0
281     };
282 
283 /* TODR and TMR data structures */
284 
285 UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY };          /* 100Hz */
286 
287 REG clk_reg[] = {
288     { DRDATA (TODR, todr_reg, 32), PV_LEFT },
289     { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT },
290     { DRDATA (TPS, clk_tps, 8), REG_HIDDEN + REG_NZ + PV_LEFT },
291     { NULL }
292     };
293 
294 DEVICE clk_dev = {
295     "TODR", &clk_unit, clk_reg, NULL,
296     1, 0, 0, 0, 0, 0,
297     NULL, NULL, &clk_reset,
298     NULL, NULL, NULL,
299     NULL, 0
300     };
301 
302 UNIT tmr_unit = { UDATA (&tmr_svc, 0, 0) };                     /* timer */
303 
304 REG tmr_reg[] = {
305     { HRDATA (ICCS, tmr_iccs, 32) },
306     { HRDATA (ICR, tmr_icr, 32) },
307     { HRDATA (NICR, tmr_nicr, 32) },
308     { HRDATA (INCR, tmr_inc, 32), REG_HIDDEN },
309     { HRDATA (SAVE, tmr_sav, 32), REG_HIDDEN },
310     { FLDATA (USE100HZ, tmr_use_100hz, 0), REG_HIDDEN },
311     { FLDATA (INT, tmr_int, 0) },
312     { NULL }
313     };
314 
315 DEVICE tmr_dev = {
316     "TMR", &tmr_unit, tmr_reg, NULL,
317     1, 0, 0, 0, 0, 0,
318     NULL, NULL, &tmr_reset,
319     NULL, NULL, NULL,
320     NULL, 0
321     };
322 
323 /* RX01 data structures
324 
325    fl_dev       RX device descriptor
326    fl_unit      RX unit list
327    fl_reg       RX register list
328    fl_mod       RX modifier list
329 */
330 
331 UNIT fl_unit = { UDATA (&fl_svc,
332       UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, FL_SIZE) };
333 
334 REG fl_reg[] = {
335     { HRDATA (FNC, fl_fnc, 8) },
336     { HRDATA (ES, fl_esr, 8) },
337     { HRDATA (ECODE, fl_ecode, 8) },
338     { HRDATA (TA, fl_track, 8) },
339     { HRDATA (SA, fl_sector, 8) },
340     { DRDATA (STATE, fl_state, 4), REG_RO },
341     { DRDATA (BPTR, fl_bptr, 7)  },
342     { DRDATA (CTIME, fl_cwait, 24), PV_LEFT },
343     { DRDATA (STIME, fl_swait, 24), PV_LEFT },
344     { DRDATA (XTIME, fl_xwait, 24), PV_LEFT },
345     { FLDATA (STOP_IOE, fl_stopioe, 0) },
346     { BRDATA (DBUF, fl_buf, 16, 8, FL_NUMBY) },
347     { BRDATA (COMM, comm_region, 16, 8, COMM_LNT) },
348     { NULL }
349     };
350 
351 MTAB fl_mod[] = {
352     { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
353     { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
354     { 0 }
355     };
356 
357 DEVICE fl_dev = {
358     "RX", &fl_unit, fl_reg, fl_mod,
359     1, DEV_RDX, 20, 1, DEV_RDX, 8,
360     NULL, NULL, &fl_reset,
361     NULL, NULL, NULL,
362     NULL, 0
363     };
364 
365 /* Terminal MxPR routines
366 
367    rxcs_rd/wr   input control/status
368    rxdb_rd      input buffer
369    txcs_rd/wr   output control/status
370    txdb_wr      output buffer
371 */
372 
rxcs_rd(void)373 int32 rxcs_rd (void)
374 {
375 return (tti_csr & RXCS_RD);
376 }
377 
rxcs_wr(int32 data)378 void rxcs_wr (int32 data)
379 {
380 if ((data & CSR_IE) == 0)
381     tto_int = 0;
382 else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
383     tti_int = 1;
384 tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR);
385 return;
386 }
387 
rxdb_rd(void)388 int32 rxdb_rd (void)
389 {
390 int32 t = tti_buf;                                      /* char + error */
391 
392 tti_csr = tti_csr & ~CSR_DONE;                          /* clr done */
393 tti_buf = tti_buf & BMASK;                              /* clr errors */
394 tti_int = 0;
395 return t;
396 }
397 
txcs_rd(void)398 int32 txcs_rd (void)
399 {
400 return (tto_csr & TXCS_RD);
401 }
402 
txcs_wr(int32 data)403 void txcs_wr (int32 data)
404 {
405 if ((data & CSR_IE) == 0)
406     tto_int = 0;
407 else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
408     tto_int = 1;
409 tto_csr = (tto_csr & ~TXCS_WR) | (data & TXCS_WR);
410 return;
411 }
412 
txdb_wr(int32 data)413 void txdb_wr (int32 data)
414 {
415 tto_buf = data & WMASK;                                 /* save data */
416 tto_csr = tto_csr & ~CSR_DONE;                          /* clear flag */
417 tto_int = 0;                                            /* clear int */
418 if (tto_buf & TXDB_SEL)                                 /* floppy? */
419     fl_wr_txdb (tto_buf);
420 else sim_activate (&tto_unit, tto_unit.wait);           /* no, console */
421 return;
422 }
423 
424 /* Terminal input service (poll for character) */
425 
tti_svc(UNIT * uptr)426 t_stat tti_svc (UNIT *uptr)
427 {
428 int32 c;
429 
430 sim_activate (uptr, KBD_WAIT (uptr->wait, clk_cosched (tmr_poll)));
431                                                         /* continue poll */
432 if ((c = sim_poll_kbd ()) < SCPE_KFLAG)                 /* no char or error? */
433     return c;
434 if (c & SCPE_BREAK)                                     /* break? */
435     tti_buf = RXDB_ERR | RXDB_FRM;
436 else tti_buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags));
437 uptr->pos = uptr->pos + 1;
438 tti_csr = tti_csr | CSR_DONE;
439 if (tti_csr & CSR_IE)
440     tti_int = 1;
441 return SCPE_OK;
442 }
443 
444 /* Terminal input reset */
445 
tti_reset(DEVICE * dptr)446 t_stat tti_reset (DEVICE *dptr)
447 {
448 tti_buf = 0;
449 tti_csr = 0;
450 tti_int = 0;
451 sim_activate (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll));
452 return SCPE_OK;
453 }
454 
455 /* Terminal output service (output character) */
456 
tto_svc(UNIT * uptr)457 t_stat tto_svc (UNIT *uptr)
458 {
459 int32 c;
460 t_stat r;
461 
462 if ((tto_buf & TXDB_SEL) == 0) {                        /* for console? */
463     c = sim_tt_outcvt (tto_buf, TT_GET_MODE (uptr->flags));
464     if (c >= 0) {
465         if ((r = sim_putchar_s (c)) != SCPE_OK) {       /* output; error? */
466             sim_activate (uptr, uptr->wait);            /* retry */
467             return ((r == SCPE_STALL)? SCPE_OK: r);     /* !stall? report */
468             }
469         }
470     uptr->pos = uptr->pos + 1;
471     }
472 tto_csr = tto_csr | CSR_DONE;
473 if (tto_csr & CSR_IE)
474     tto_int = 1;
475 return SCPE_OK;
476 }
477 
478 /* Terminal output reset */
479 
tto_reset(DEVICE * dptr)480 t_stat tto_reset (DEVICE *dptr)
481 {
482 tto_buf = 0;
483 tto_csr = CSR_DONE;
484 tto_int = 0;
485 sim_cancel (&tto_unit);                                 /* deactivate unit */
486 return SCPE_OK;
487 }
488 
489 /* Programmable timer
490 
491    The architected VAX timer, which increments at 1Mhz, cannot be
492    accurately simulated due to the overhead that would be required
493    for 1M clock events per second.  Instead, a hidden calibrated
494    100Hz timer is run (because that's what VMS expects), and a
495    hack is used for the interval timer.
496 
497    When the timer is started, the timer interval is inspected.
498 
499    if the interval is >= 10msec, then the 100Hz timer drives the
500         next interval
501    if the interval is < 10mec, then count instructions
502 
503    If the interval register is read, then its value between events
504    is interpolated using the current instruction count versus the
505    count when the most recent event started, the result is scaled
506    to the calibrated system clock, unless the interval being timed
507    is less than a calibrated system clock tick (or the calibrated
508    clock is running very slowly) at which time the result will be
509    the elapsed instruction count.
510 */
511 
iccs_rd(void)512 int32 iccs_rd (void)
513 {
514 return tmr_iccs & TMR_CSR_RD;
515 }
516 
iccs_wr(int32 val)517 void iccs_wr (int32 val)
518 {
519 if ((val & TMR_CSR_RUN) == 0) {                         /* clearing run? */
520     sim_cancel (&tmr_unit);                             /* cancel timer */
521     tmr_use_100hz = 0;
522     if (tmr_iccs & TMR_CSR_RUN)                         /* run 1 -> 0? */
523         tmr_icr = icr_rd (TRUE);                        /* update itr */
524     }
525 tmr_iccs = tmr_iccs & ~(val & TMR_CSR_W1C);             /* W1C csr */
526 tmr_iccs = (tmr_iccs & ~TMR_CSR_WR) |                   /* new r/w */
527     (val & TMR_CSR_WR);
528 if (val & TMR_CSR_XFR) tmr_icr = tmr_nicr;              /* xfr set? */
529 if (val & TMR_CSR_RUN)  {                               /* run? */
530     if (val & TMR_CSR_XFR)                              /* new tir? */
531         sim_cancel (&tmr_unit);                         /* stop prev */
532     if (!sim_is_active (&tmr_unit))                     /* not running? */
533         tmr_sched ();                                   /* activate */
534     }
535 else if (val & TMR_CSR_SGL) {                           /* single step? */
536     tmr_incr (1);                                       /* incr tmr */
537     if (tmr_icr == 0)                                   /* if ovflo, */
538         tmr_icr = tmr_nicr;                             /* reload tir */
539     }
540 if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) !=          /* update int */
541     (TMR_CSR_DON | TMR_CSR_IE))
542     tmr_int = 0;
543 return;
544 }
545 
icr_rd(t_bool interp)546 int32 icr_rd (t_bool interp)
547 {
548 uint32 delta;
549 
550 if (interp || (tmr_iccs & TMR_CSR_RUN)) {               /* interp, running? */
551     delta = sim_grtime () - tmr_sav;                    /* delta inst */
552     if (tmr_use_100hz && (tmr_poll > TMR_INC))          /* scale large int */
553         delta = (uint32) ((((double) delta) * TMR_INC) / tmr_poll);
554     if (delta >= tmr_inc)
555         delta = tmr_inc - 1;
556     return tmr_icr + delta;
557     }
558 return tmr_icr;
559 }
560 
nicr_rd()561 int32 nicr_rd ()
562 {
563 return tmr_nicr;
564 }
565 
nicr_wr(int32 val)566 void nicr_wr (int32 val)
567 {
568 tmr_nicr = val;
569 }
570 
571 /* 100Hz base clock unit service */
572 
clk_svc(UNIT * uptr)573 t_stat clk_svc (UNIT *uptr)
574 {
575 tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK);            /* calibrate clock */
576 sim_activate (&clk_unit, tmr_poll);                     /* reactivate unit */
577 tmxr_poll = tmr_poll * TMXR_MULT;                       /* set mux poll */
578 todr_reg = todr_reg + 1;                                /* incr TODR */
579 if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz)          /* timer on, std intvl? */
580     tmr_incr (TMR_INC);                                 /* do timer service */
581 return SCPE_OK;
582 }
583 
584 /* Interval timer unit service */
585 
tmr_svc(UNIT * uptr)586 t_stat tmr_svc (UNIT *uptr)
587 {
588 tmr_incr (tmr_inc);                                     /* incr timer */
589 return SCPE_OK;
590 }
591 
592 /* Timer increment */
593 
tmr_incr(uint32 inc)594 void tmr_incr (uint32 inc)
595 {
596 uint32 new_icr = (tmr_icr + inc) & LMASK;               /* add incr */
597 
598 if (new_icr < tmr_icr) {                                /* ovflo? */
599     tmr_icr = 0;                                        /* now 0 */
600     if (tmr_iccs & TMR_CSR_DON)                         /* done? set err */
601         tmr_iccs = tmr_iccs | TMR_CSR_ERR;
602     else tmr_iccs = tmr_iccs | TMR_CSR_DON;             /* set done */
603     if (tmr_iccs & TMR_CSR_RUN) {                       /* run? */
604         tmr_icr = tmr_nicr;                             /* reload */
605         tmr_sched ();                                   /* reactivate */
606         }
607     if (tmr_iccs & TMR_CSR_IE)                          /* ie? set int req */
608         tmr_int = 1;
609     else tmr_int = 0;
610     }
611 else {
612     tmr_icr = new_icr;                                  /* no, update icr */
613     if (tmr_iccs & TMR_CSR_RUN)                         /* still running? */
614         tmr_sched ();                                   /* reactivate */
615     }
616 return;
617 }
618 
619 /* Timer scheduling */
620 
tmr_sched(void)621 void tmr_sched (void)
622 {
623 tmr_sav = sim_grtime ();                                /* save intvl base */
624 tmr_inc = (~tmr_icr + 1);                               /* inc = interval */
625 if (tmr_inc == 0) tmr_inc = 1;
626 if (tmr_inc < TMR_INC) {                                /* 100Hz multiple? */
627     sim_activate (&tmr_unit, tmr_inc);                  /* schedule timer */
628     tmr_use_100hz = 0;
629     }
630 else tmr_use_100hz = 1;                                 /* let clk handle */
631 return;
632 }
633 
634 /* Clock coscheduling routine */
635 
clk_cosched(int32 wait)636 int32 clk_cosched (int32 wait)
637 {
638 int32 t;
639 
640 t = sim_is_active (&clk_unit);
641 return (t? t - 1: wait);
642 }
643 
644 /* 100Hz clock reset */
645 
clk_reset(DEVICE * dptr)646 t_stat clk_reset (DEVICE *dptr)
647 {
648 tmr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK);      /* init 100Hz timer */
649 sim_activate (&clk_unit, tmr_poll);                     /* activate 100Hz unit */
650 tmxr_poll = tmr_poll * TMXR_MULT;                       /* set mux poll */
651 return SCPE_OK;
652 }
653 
654 /* Interval timer reset */
655 
tmr_reset(DEVICE * dptr)656 t_stat tmr_reset (DEVICE *dptr)
657 {
658 tmr_iccs = 0;
659 tmr_icr = 0;
660 tmr_nicr = 0;
661 tmr_int = 0;
662 tmr_use_100hz = 1;
663 sim_cancel (&tmr_unit);                                 /* cancel timer */
664 todr_resync ();                                         /* resync TODR */
665 return SCPE_OK;
666 }
667 
668 /* TODR routines */
669 
todr_rd(void)670 int32 todr_rd (void)
671 {
672 return todr_reg;
673 }
674 
todr_wr(int32 data)675 void todr_wr (int32 data)
676 {
677 todr_reg = data;
678 return;
679 }
680 
todr_resync(void)681 t_stat todr_resync (void)
682 {
683 uint32 base;
684 time_t curr;
685 struct tm *ctm;
686 
687 curr = time (NULL);                                     /* get curr time */
688 if (curr == (time_t) -1)                                /* error? */
689     return SCPE_NOFNC;
690 ctm = localtime (&curr);                                /* decompose */
691 if (ctm == NULL)                                        /* error? */
692     return SCPE_NOFNC;
693 base = (((((ctm->tm_yday * 24) +                        /* sec since 1-Jan */
694         ctm->tm_hour) * 60) +
695         ctm->tm_min) * 60) +
696         ctm->tm_sec;
697 todr_reg = (base * 100) + 0x10000000;                   /* cvt to VAX form */
698 return SCPE_OK;
699 }
700 
701 /* Console write, txdb<11:8> != 0 (console unit) */
702 
fl_wr_txdb(int32 data)703 t_stat fl_wr_txdb (int32 data)
704 {
705 int32 sel = TXDB_GETSEL (data);                         /* get selection */
706 
707 if (sel == TXDB_FCMD) {                                 /* floppy command? */
708     fl_fnc = FL_GETFNC (data);                          /* get function */
709     if (fl_state != FL_IDLE)                            /* cmd in prog? */
710         switch (fl_fnc) {
711 
712         case FL_FNCCA:                                  /* cancel? */
713             sim_cancel (&fl_unit);                      /* stop op */
714             fl_state = FL_DONE;
715             break;
716 
717         default:                                        /* all others */
718             fl_protocol_error ();
719             return SCPE_OK;
720             }
721 
722         else switch (fl_fnc) {                          /* idle, case */
723 
724         case FL_FNCRS:                                  /* read status */
725             fl_state = FL_READSTA;
726             break;
727 
728         case FL_FNCCA:                                  /* cancel, nop */
729             fl_state = FL_DONE;
730             break;
731 
732         case FL_FNCRD: case FL_FNCWR:                   /* data xfer */
733         case FL_FNCWD:
734             fl_esr = 0;                                 /* clear errors */
735             fl_ecode = 0;
736             fl_bptr = 0;                                /* init buffer */
737             fl_state = FL_RWDS;                         /* sector next */
738             break;
739 
740         default:                                        /* all others */
741             fl_protocol_error ();
742             return SCPE_OK;
743             }
744 
745     sim_activate (&fl_unit, fl_cwait);                  /* sched command */
746     }                                                   /* end command */
747 else if (sel == TXDB_FDAT) {                            /* floppy data? */
748     switch (fl_state) {                                 /* data */
749 
750         case FL_RWDS:                                   /* expecting sector */
751             fl_sector = data & FL_M_SECTOR;
752             fl_state = FL_RWDT;
753             break;
754 
755         case FL_RWDT:                                   /* expecting track */
756             fl_track = data & FL_M_TRACK;
757             if (fl_fnc == FL_FNCRD)
758                 fl_state = FL_READ;
759             else fl_state = FL_FILL;
760             break;
761 
762         case FL_FILL:                                   /* expecting wr data */
763             fl_buf[fl_bptr++] = data & BMASK;
764             if (fl_bptr >= FL_NUMBY)
765                 fl_state = FL_WRITE;
766             break;
767 
768         default:
769             fl_protocol_error ();
770             return SCPE_OK;
771             }
772 
773     sim_activate (&fl_unit, fl_xwait);                  /* schedule xfer */
774     }                                                   /* end else data */
775 else {
776     sim_activate (&tto_unit, tto_unit.wait);            /* set up timeout */
777     if (sel == TXDB_COMM) {                             /* read comm region? */
778         data = data & COMM_MASK;                        /* byte to select */
779         tti_buf = comm_region[data] | COMM_DATA;
780         tti_csr = tti_csr | CSR_DONE;                   /* set input flag */
781         if (tti_csr & CSR_IE)
782             tti_int = 1;
783         }
784     else if (sel == TXDB_MISC) {                        /* misc function? */
785         switch (data & MISC_MASK) {                     /* case on function */
786 
787         case MISC_CLWS:
788             comm_region[COMM_WRMS] = 0;
789 
790         case MISC_CLCS:
791             comm_region[COMM_CLDS] = 0;
792             break;
793 
794         case MISC_SWDN:
795             ABORT (STOP_SWDN);
796             break;
797 
798         case MISC_BOOT:
799             con_halt (0, 0);                            /* set up reboot */
800             break;
801             }
802         }
803     }
804 return SCPE_OK;
805 }
806 
807 /* Unit service; the action to be taken depends on the transfer state:
808 
809    FL_IDLE              Should never get here
810    FL_RWDS              Set TXCS<done> (driver sends sector, sets FL_RWDT)
811    FL_RWDT              Set TXCS<done> (driver sends track, sets FL_READ/FL_FILL)
812    FL_READ              Set TXCS<done>, schedule FL_READ1
813    FL_READ1             Read sector, schedule FL_EMPTY
814    FL_EMPTY             Copy data to RXDB, set RXCS<done>
815                         if fl_bptr >= max, schedule completion, else continue
816    FL_FILL              Set TXCS<done> (driver sends next byte, sets FL_WRITE)
817    FL_WRITE             Set TXCS<done>, schedule FL_WRITE1
818    FL_WRITE1            Write sector, schedule FL_DONE
819    FL_DONE              Copy requested data to TXDB, set FL_IDLE
820 */
821 
fl_svc(UNIT * uptr)822 t_stat fl_svc (UNIT *uptr)
823 {
824 int32 i, t;
825 uint32 da;
826 int8 *fbuf = uptr->filebuf;
827 
828 switch (fl_state) {                                     /* case on state */
829 
830     case FL_IDLE:                                       /* idle */
831         return SCPE_IERR;                               /* done */
832 
833     case FL_READ: case FL_WRITE:                        /* read, write */
834         fl_state = fl_state + 1;                        /* set next state */
835         t = abs (fl_track - uptr->TRACK);               /* # tracks to seek */
836         if (t == 0)                                     /* minimum 1 */
837             t = 1;
838         sim_activate (uptr, fl_swait * t);              /* schedule seek */
839                                                         /* fall thru, set flag */
840     case FL_RWDS: case FL_RWDT: case FL_FILL:           /* rwds, rwdt, fill */
841         tto_csr = tto_csr | CSR_DONE;                   /* set output done */
842         if (tto_csr & CSR_IE)
843             tto_int = 1;
844         break;
845 
846     case FL_READ1:                                      /* read, seek done */
847         if (fl_test_xfr (uptr, FALSE)) {                /* transfer ok? */
848             da = CALC_DA (fl_track, fl_sector);         /* get disk address */
849             for (i = 0; i < FL_NUMBY; i++)              /* copy sector to buf */
850                 fl_buf[i] = fbuf[da + i];
851             tti_buf = fl_esr | FL_CDONE;                /* completion code */
852             tti_csr = tti_csr | CSR_DONE;               /* set input flag */
853             if (tti_csr & CSR_IE)
854                 tti_int = 1;
855             fl_state = FL_EMPTY;                        /* go empty */
856             }
857         else fl_state = FL_DONE;                        /* error? cmd done */
858         sim_activate (uptr, fl_xwait);                  /* schedule next */
859         break;
860 
861     case FL_EMPTY:                                      /* empty buffer */
862         if ((tti_csr & CSR_DONE) == 0) {                /* prev data taken? */
863             tti_buf = FL_CDATA | fl_buf[fl_bptr++];     /* get next byte */
864             tti_csr = tti_csr | CSR_DONE;               /* set input flag */
865             if (tti_csr & CSR_IE)
866                 tti_int = 1;
867             if (fl_bptr >= FL_NUMBY) {                  /* buffer empty? */
868                 fl_state = FL_IDLE;                     /* cmd done */
869                 break;
870                 }
871             }
872         sim_activate (uptr, fl_xwait);                  /* schedule next */
873         break;
874 
875     case FL_WRITE1:                                     /* write, seek done */
876         if (fl_test_xfr (uptr, TRUE)) {                 /* transfer ok? */
877             da = CALC_DA (fl_track, fl_sector);         /* get disk address */
878             for (i = 0; i < FL_NUMBY; i++)              /* copy buf to sector */
879                 fbuf[da + i] = fl_buf[i];
880             da = da + FL_NUMBY;
881             if (da > uptr->hwmark)                      /* update hwmark */
882                 uptr->hwmark = da;
883             }
884         if (fl_fnc == FL_FNCWD)                         /* wrdel? set status */
885             fl_esr |= FL_STADDA;
886         fl_state = FL_DONE;                             /* command done */
887         sim_activate (uptr, fl_xwait);                  /* schedule */
888         break;
889 
890     case FL_DONE:                                       /* command done */
891         if (tti_csr & CSR_DONE)                         /* input buf empty? */
892             sim_activate (uptr, fl_xwait);              /* no, wait */
893         else {                                          /* yes */
894             tti_buf = fl_esr | FL_CDONE;                /* completion code */
895             tti_csr = tti_csr | CSR_DONE;               /* set input flag */
896             if (tti_csr & CSR_IE)
897                 tti_int = 1;
898             fl_state = FL_IDLE;                         /* floppy idle */
899             }
900         break;
901 
902     case FL_READSTA:                                    /* read status */
903         if ((tti_csr & CSR_DONE) == 0) {                /* input buf empty? */
904             tti_buf = fl_ecode;                         /* return err code */
905             tti_csr = tti_csr | CSR_DONE;               /* set input flag */
906             if (tti_csr & CSR_IE)
907                 tti_int = 1;
908             fl_state = FL_DONE;                         /* command done */
909             }
910         sim_activate (uptr, fl_xwait);
911         break;
912         }
913 return SCPE_OK;
914 }
915 
916 /* Test for data transfer okay */
917 
fl_test_xfr(UNIT * uptr,t_bool wr)918 t_bool fl_test_xfr (UNIT *uptr, t_bool wr)
919 {
920 if ((uptr->flags & UNIT_BUF) == 0)                      /* not buffered? */
921     fl_ecode = 0110;
922 else if (fl_track >= FL_NUMTR)                          /* bad track? */
923     fl_ecode = 0040;                                    /* done, error */
924 else if ((fl_sector == 0) || (fl_sector > FL_NUMSC))    /* bad sect? */
925     fl_ecode = 0070;                                    /* done, error */
926 else if (wr && (uptr->flags & UNIT_WPRT))               /* write and locked? */
927     fl_ecode = 0100;                                    /* done, error */
928 else {
929     uptr->TRACK = fl_track;                             /* now on track */
930     return TRUE;
931     }
932 fl_esr = fl_esr | FL_STAERR;                            /* set error */
933 return FALSE;
934 }
935 
936 /* Set protocol error */
937 
fl_protocol_error(void)938 void fl_protocol_error (void)
939 {
940 if ((tto_csr & CSR_DONE) == 0) {                        /* output busy? */
941     tto_csr = tto_csr | CSR_DONE;                       /* set done */
942     if (tto_csr & CSR_IE)
943         tto_int = 1;
944     }
945 if ((tti_csr & CSR_DONE) == 0) {                        /* input idle? */
946     tti_csr = tti_csr | CSR_DONE;                       /* set done */
947     if (tti_csr & CSR_IE)
948         tti_int = 1;
949     }
950 tti_buf = FL_CPROT;                                     /* status */
951 fl_state = FL_IDLE;                                     /* floppy idle */
952 return;
953 }
954 
955 /* Reset */
956 
fl_reset(DEVICE * dptr)957 t_stat fl_reset (DEVICE *dptr)
958 {
959 uint32 i;
960 
961 fl_esr = FL_STAINC;
962 fl_ecode = 0;                                           /* clear error */
963 fl_sector = 0;                                          /* clear addr */
964 fl_track = 0;
965 fl_state = FL_IDLE;                                     /* ctrl idle */
966 fl_bptr = 0;
967 sim_cancel (&fl_unit);                                  /* cancel drive */
968 fl_unit.TRACK = 0;
969 for (i = 0; i < COMM_LNT; i++)
970     comm_region[i] = 0;
971 comm_region[COMM_FPLV] = VER_FPLA;
972 comm_region[COMM_PCSV] = VER_PCS;
973 comm_region[COMM_WCSV] = VER_WCSP;
974 comm_region[COMM_WCSS] = VER_WCSS;
975 comm_region[COMM_GH] = 1;
976 return SCPE_OK;
977 }
978