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