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