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