First Commit of my working state
[simh.git] / Interdata / id_uvc.c
1 /* id_uvc.c: Interdata universal clock
2
3 Copyright (c) 2001-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 pic precision incremental clock
27 lfc line frequency clock
28
29 18-Jun-07 RMS Added UNIT_IDLE flag
30 18-Oct-06 RMS Changed LFC to be free running, export tmr_poll
31 23-Jul-05 RMS Fixed {} error in OC
32 01-Mar-03 RMS Added SET/SHOW LFC FREQ support
33 Changed precision clock algorithm for V7 UNIX
34 */
35
36 #include "id_defs.h"
37 #include <ctype.h>
38
39 /* Device definitions */
40
41 #define UNIT_V_DIAG (UNIT_V_UF + 0) /* diag mode */
42 #define UNIT_DIAG (1 << UNIT_V_DIAG)
43
44 #define STA_OVF 0x08 /* PIC overflow */
45 #define CMD_STRT 0x20 /* start */
46 #define PIC_V_RATE 12 /* rate */
47 #define PIC_M_RATE 0xF
48 #define PIC_RATE (PIC_M_RATE << PIC_V_RATE)
49 #define PIC_CTR 0x0FFF /* PIC counters */
50 #define GET_RATE(x) (((x) >> PIC_V_RATE) & PIC_M_RATE)
51 #define GET_CTR(x) ((x) & PIC_CTR)
52 #define PIC_TPS 1000
53
54 extern uint32 int_req[INTSZ], int_enb[INTSZ];
55
56 int32 pic_db = 0; /* output buf */
57 int32 pic_ric = 0; /* reset count */
58 int32 pic_cic = 0; /* current count */
59 uint32 pic_save = 0; /* saved time */
60 uint32 pic_ovf = 0; /* overflow */
61 uint32 pic_rdp = 0;
62 uint32 pic_wdp = 0;
63 uint32 pic_cnti = 0; /* instr/timer */
64 uint32 pic_arm = 0; /* int arm */
65 uint32 pic_decr = 1; /* decrement */
66 uint16 pic_time[4] = { 1, 10, 100, 1000 }; /* delays */
67 uint16 pic_usec[4] = { 1, 10, 100, 1000 }; /* usec per tick */
68 static int32 pic_map[16] = { /* map rate to delay */
69 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
70 };
71
72 DEVICE pic_dev;
73 uint32 pic (uint32 dev, uint32 op, uint32 dat);
74 t_stat pic_svc (UNIT *uptr);
75 t_stat pic_reset (DEVICE *dptr);
76 void pic_sched (t_bool strt);
77 uint32 pic_rd_cic (void);
78
79 int32 lfc_tps = 120; /* ticks per */
80 int32 lfc_poll = 8000;
81 uint32 lfc_arm = 0; /* int arm */
82
83 DEVICE lfc_dev;
84 uint32 lfc (uint32 dev, uint32 op, uint32 dat);
85 t_stat lfc_svc (UNIT *uptr);
86 t_stat lfc_reset (DEVICE *dptr);
87 t_stat lfc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc);
88 t_stat lfc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc);
89
90 /* PIC data structures
91
92 pic_dev PIC device descriptor
93 pic_unit PIC unit descriptor
94 pic_reg PIC register list
95 */
96
97 DIB pic_dib = { d_PIC, -1, v_PIC, NULL, &pic, NULL };
98
99 UNIT pic_unit = { UDATA (&pic_svc, UNIT_IDLE, 0), 1000 };
100
101 REG pic_reg[] = {
102 { HRDATA (BUF, pic_db, 16) },
103 { HRDATA (RIC, pic_ric, 16) },
104 { HRDATA (CIC, pic_cic, 12) },
105 { FLDATA (RDP, pic_rdp, 0) },
106 { FLDATA (WDP, pic_wdp, 0) },
107 { FLDATA (OVF, pic_ovf, 0) },
108 { FLDATA (IREQ, int_req[l_PIC], i_PIC) },
109 { FLDATA (IENB, int_enb[l_PIC], i_PIC) },
110 { FLDATA (IARM, pic_arm, 0) },
111 { BRDATA (TIME, pic_time, 10, 16, 4), REG_NZ + PV_LEFT },
112 { DRDATA (SAVE, pic_save, 32), REG_HRO + PV_LEFT },
113 { DRDATA (DECR, pic_decr, 16), REG_HRO + PV_LEFT },
114 { FLDATA (MODE, pic_cnti, 0), REG_HRO },
115 { HRDATA (DEVNO, pic_dib.dno, 8), REG_HRO },
116 { NULL }
117 };
118
119 MTAB pic_mod[] = {
120 { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL },
121 { UNIT_DIAG, 0, NULL, "NORMAL", NULL },
122 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
123 &set_dev, &show_dev, NULL },
124 { 0 }
125 };
126
127 DEVICE pic_dev = {
128 "PIC", &pic_unit, pic_reg, pic_mod,
129 1, 0, 0, 0, 0, 0,
130 NULL, NULL, &pic_reset,
131 NULL, NULL, NULL,
132 &pic_dib, DEV_DISABLE
133 };
134
135 /* LFC data structures
136
137 lfc_dev LFC device descriptor
138 lfc_unit LFC unit descriptor
139 lfc_reg LFC register list
140 */
141
142 DIB lfc_dib = { d_LFC, -1, v_LFC, NULL, &lfc, NULL };
143
144 UNIT lfc_unit = { UDATA (&lfc_svc, UNIT_IDLE, 0), 8333 };
145
146 REG lfc_reg[] = {
147 { FLDATA (IREQ, int_req[l_LFC], i_LFC) },
148 { FLDATA (IENB, int_enb[l_LFC], i_LFC) },
149 { FLDATA (IARM, lfc_arm, 0) },
150 { DRDATA (TIME, lfc_unit.wait, 24), REG_NZ + PV_LEFT },
151 { DRDATA (TPS, lfc_tps, 8), PV_LEFT + REG_HRO },
152 { HRDATA (DEVNO, lfc_dib.dno, 8), REG_HRO },
153 { NULL }
154 };
155
156 MTAB lfc_mod[] = {
157 { MTAB_XTD|MTAB_VDV, 100, NULL, "50HZ",
158 &lfc_set_freq, NULL, NULL },
159 { MTAB_XTD|MTAB_VDV, 120, NULL, "60HZ",
160 &lfc_set_freq, NULL, NULL },
161 { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL,
162 NULL, &lfc_show_freq, NULL },
163 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
164 &set_dev, &show_dev, NULL },
165 { 0 }
166 };
167
168 DEVICE lfc_dev = {
169 "LFC", &lfc_unit, lfc_reg, lfc_mod,
170 1, 0, 0, 0, 0, 0,
171 NULL, NULL, &lfc_reset,
172 NULL, NULL, NULL,
173 &lfc_dib, DEV_DISABLE
174 };
175
176 /* Precision clock: IO routine */
177
178 uint32 pic (uint32 dev, uint32 op, uint32 dat)
179 {
180 int32 t;
181
182 switch (op) { /* case IO op */
183
184 case IO_ADR: /* select */
185 return HW; /* HW capable */
186
187 case IO_RH: /* read halfword */
188 pic_rdp = 0; /* clr ptr */
189 return pic_rd_cic ();
190
191 case IO_RD: /* read */
192 t = pic_rd_cic (); /* get cic */
193 if (pic_rdp) t = t & DMASK8; /* 2nd? get lo */
194 else t = (t >> 8) & DMASK8; /* 1st? get hi */
195 pic_rdp = pic_rdp ^ 1; /* flip byte ptr */
196 return t;
197
198 case IO_WH: /* write halfword */
199 pic_wdp = 0; /* clr ptr */
200 pic_db = dat;
201 break;
202
203 case IO_WD: /* write */
204 if (pic_wdp) pic_db = (pic_db & 0xFF00) | dat;
205 else pic_db = (pic_db & 0xFF) | (dat << 8);
206 pic_wdp = pic_wdp ^ 1; /* flip byte ptr */
207 break;
208
209 case IO_SS: /* sense status */
210 if (pic_ovf) { /* overflow? */
211 pic_ovf = 0; /* clear flag */
212 CLR_INT (v_PIC); /* clear intr */
213 return STA_OVF;
214 }
215 return 0;
216
217 case IO_OC: /* output cmd */
218 pic_arm = int_chg (v_PIC, dat, pic_arm); /* upd int ctrl */
219 if (dat & CMD_STRT) { /* start? */
220 pic_ric = pic_db; /* new ric */
221 pic_cic = GET_CTR (pic_ric); /* new cic */
222 pic_ovf = 0; /* clear flag */
223 sim_cancel (&pic_unit); /* stop clock */
224 pic_rdp = pic_wdp = 0; /* init ptrs */
225 if (pic_ric & PIC_RATE) pic_sched (TRUE); /* any rate? */
226 } /* end if start */
227 break;
228 } /* end case */
229
230 return 0;
231 }
232
233 /* Unit service */
234
235 t_stat pic_svc (UNIT *uptr)
236 {
237 t_bool rate_chg = FALSE;
238
239 if (pic_cnti) pic_cic = 0; /* one shot? */
240 pic_cic = pic_cic - pic_decr; /* decrement */
241 if (pic_cic <= 0) { /* overflow? */
242 if (pic_wdp) pic_ovf = 1; /* broken wr? set flag */
243 if (pic_arm) SET_INT (v_PIC); /* if armed, intr */
244 if (GET_RATE (pic_ric) != GET_RATE (pic_db)) /* rate change? */
245 rate_chg = TRUE;
246 pic_ric = pic_db; /* new ric */
247 pic_cic = GET_CTR (pic_ric); /* new cic */
248 if ((pic_ric & PIC_RATE) == 0) return SCPE_OK;
249 }
250 pic_sched (rate_chg);
251 return SCPE_OK;
252 }
253
254 /* Schedule next interval
255
256 If eff rate < 1ms, or diagnostic mode, count instructions
257 If eff rate = 1ms, and not diagnostic mode, use timer
258 */
259
260 void pic_sched (t_bool strt)
261 {
262 int32 r, t, intv, intv_usec;
263
264 pic_save = sim_grtime (); /* save start */
265 r = pic_map[GET_RATE (pic_ric)]; /* get mapped rate */
266 intv = pic_cic? pic_cic: 1; /* get cntr */
267 intv_usec = intv * pic_usec[r]; /* cvt to usec */
268 if (!(pic_unit.flags & UNIT_DIAG) && /* not diag? */
269 ((intv_usec % 1000) == 0)) { /* 1ms multiple? */
270 pic_cnti = 0; /* clr mode */
271 pic_decr = pic_usec[3 - r]; /* set decrement */
272 if (strt) t = sim_rtcn_init (pic_time[3], TMR_PIC); /* init or */
273 else t = sim_rtcn_calb (PIC_TPS, TMR_PIC); /* calibrate */
274 }
275 else {
276 pic_cnti = 1; /* set mode */
277 pic_decr = 1; /* decr = 1 */
278 t = pic_time[r] * intv; /* interval */
279 if (t == 1) t++; /* for diagn */
280 }
281 sim_activate (&pic_unit, t); /* activate */
282 return;
283 }
284
285 /* Read (interpolated) current interval */
286
287 uint32 pic_rd_cic (void)
288 {
289 if (sim_is_active (&pic_unit) && pic_cnti) { /* running, one shot? */
290 uint32 delta = sim_grtime () - pic_save; /* interval */
291 uint32 tm = pic_time[pic_map[GET_RATE (pic_ric)]]; /* ticks/intv */
292 delta = delta / tm; /* ticks elapsed */
293 if (delta >= ((uint32) pic_cic)) return 0; /* cap value */
294 return pic_cic - delta;
295 }
296 return pic_cic;
297 }
298
299 /* Reset routine */
300
301 t_stat pic_reset (DEVICE *dptr)
302 {
303 sim_cancel (&pic_unit); /* cancel unit */
304 pic_ric = pic_cic = 0;
305 pic_db = 0;
306 pic_ovf = 0; /* clear state */
307 pic_cnti = 0;
308 pic_decr = 1;
309 pic_rdp = pic_wdp = 0;
310 CLR_INT (v_PIC); /* clear int */
311 CLR_ENB (v_PIC); /* disable int */
312 pic_arm = 0; /* disarm int */
313 return SCPE_OK;
314 }
315
316 /* Line clock: IO routine */
317
318 uint32 lfc (uint32 dev, uint32 op, uint32 dat)
319 {
320 switch (op) { /* case IO op */
321
322 case IO_ADR: /* select */
323 return BY; /* byte only */
324
325 case IO_OC: /* command */
326 lfc_arm = int_chg (v_LFC, dat, lfc_arm); /* upd int ctrl */
327 break;
328 }
329 return 0;
330 }
331
332 /* Unit service */
333
334 t_stat lfc_svc (UNIT *uptr)
335 {
336 lfc_poll = sim_rtcn_calb (lfc_tps, TMR_LFC); /* calibrate */
337 sim_activate (uptr, lfc_poll); /* reactivate */
338 if (lfc_arm) { /* armed? */
339 SET_INT (v_LFC); /* req intr */
340 }
341 return SCPE_OK;
342 }
343
344 /* Reset routine */
345
346 t_stat lfc_reset (DEVICE *dptr)
347 {
348 lfc_poll = sim_rtcn_init (lfc_unit.wait, TMR_LFC);
349 sim_activate_abs (&lfc_unit, lfc_poll); /* init clock */
350 CLR_INT (v_LFC); /* clear int */
351 CLR_ENB (v_LFC); /* disable int */
352 lfc_arm = 0; /* disarm int */
353 return SCPE_OK;
354 }
355
356 /* Set frequency */
357
358 t_stat lfc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc)
359 {
360 if (cptr) return SCPE_ARG;
361 if ((val != 100) && (val != 120)) return SCPE_IERR;
362 lfc_tps = val;
363 return SCPE_OK;
364 }
365
366 /* Show frequency */
367
368 t_stat lfc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc)
369 {
370 fprintf (st, (lfc_tps == 100)? "50Hz": "60Hz");
371 return SCPE_OK;
372 }
373