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