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