1 /* pro_clk.c: real time clock
2
3 Copyright (c) 1997-2004, Tarik Isani (xhomer@isani.org)
4
5 This file is part of Xhomer.
6
7 Xhomer is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2
9 as published by the Free Software Foundation.
10
11 Xhomer is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Xhomer; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21
22 /* TBD:
23 -some bits should not be cleared by reset?
24 -BCD mode not implemented
25 -12 hour mode not implemented
26 -interrupt throttle mode needs to be validated
27 -alarm int could generate extra int, or none at all
28 */
29
30 /* When enabled, generates interrupts at the following rates
31 in "real" PRO time (or actual realtime if RTC is defined):
32
33 RS Frequency (Hz)
34 -- --------------
35 0 none
36 1 256
37 2 128
38 3 8192
39 4 4096
40 5 2048
41 6 1024
42 7 512
43 8 256
44 9 128
45 10 64
46 11 32
47 12 16
48 13 8
49 14 4
50 15 2
51 */
52
53 #ifdef PRO
54 #include "pdp11_defs.h"
55 #include "sim_defs.h" /* For sim_gtime() */
56
57 #include <time.h>
58 #include <sys/time.h>
59 #include <unistd.h>
60
61
62 /* Data type for >32-bit time value, implemented in three
63 32-bit integers. This is used for tracking real time
64 in 10's of uS.
65
66 The range of mid and lo is restricted, to allow multiplication
67 by 8192 (the highest timer frequency), with enough guard-band
68 to detect overflows */
69
70 typedef struct
71 {
72 int hi; /* 0-(2G-1) */
73 int mid; /* 0-99,999 */
74 int lo; /* 0-99,999 */
75 } bigtime;
76
77
78 int pro_force_year = -1; /* when > -1, will override host's RTC year value */
79 int pro_int_throttle = 0; /* when 1, will throttle timer interrupts */
80
81 LOCAL int pro_clk_csr0;
82 LOCAL int pro_clk_csr1;
83 LOCAL int pro_clk_csr2;
84 LOCAL int pro_clk_csr3;
85
86 LOCAL int pro_clk_al_sec; /* alarm registers */
87 LOCAL int pro_clk_al_min;
88 LOCAL int pro_clk_al_hour;
89
90 LOCAL int pro_clk_rs; /* freq. # for previous periodic int cycle */
91 LOCAL int pro_clk_realtime; /* indicates whether in realtime mode */
92 LOCAL int pro_clk_numint; /* number of outstanding interrupts */
93 LOCAL int pro_clk_freq; /* periodic int frequency in Hz */
94 LOCAL int pro_clk_int_cnt; /* interrupt counter used for update cycles */
95
96 LOCAL struct tm *pro_time;
97
98 LOCAL int pro_time_sec;
99 LOCAL int pro_time_min;
100 LOCAL int pro_time_hour;
101
102 /* Periodic interrupt frequencies (in Hz) */
103
104 LOCAL int pro_clk_intf[16] = {8192, 256, 128, 8192, 4096, 2048, 1024, 512,
105 256, 128, 64, 32, 16, 8, 4, 2};
106
107
108 #ifdef RTC
109 LOCAL bigtime pro_time_start, pro_time_current, pro_time_tmp;
110
111
112 /* Subtract two bigtime operands (op1-op2)
113
114 It is assumed that both operands are positive and that op1
115 is greater than or equal to op2 */
116
bigtime_sub(bigtime op1,bigtime op2)117 LOCAL bigtime bigtime_sub(bigtime op1, bigtime op2)
118 {
119 bigtime res;
120
121
122 /* Subtract each segment individually, and carry any underflows */
123
124 res.hi = op1.hi - op2.hi;
125
126 if (op1.mid >= op2.mid)
127 res.mid = op1.mid - op2.mid;
128 else
129 {
130 res.mid = 100000 - op2.mid + op1.mid;
131 res.hi--;
132 }
133
134 if (op1.lo >= op2.lo)
135 res.lo = op1.lo - op2.lo;
136 else
137 {
138 res.lo = 100000 - op2.lo + op1.lo;
139
140 if (res.mid > 0)
141 res.mid--;
142 else
143 {
144 res.mid = 99999;
145 res.hi--;
146 }
147 }
148
149 return res;
150 }
151
152
153 /* Add a bigtime operand to an int (op1+op2)
154
155 op2 must be non-negative and may not exceed (2G-100,000) */
156
bigtime_add(bigtime op1,int op2)157 LOCAL bigtime bigtime_add(bigtime op1, int op2)
158 {
159 int tmp_res;
160 bigtime res;
161
162
163 tmp_res = op1.lo + op2;
164
165 res.lo = tmp_res % 100000;
166 res.mid = ((tmp_res / 100000) + op1.mid) % 100000;
167 res.hi = ((tmp_res / 100000) + op1.mid) / 100000 + op1.hi;
168
169 return res;
170 }
171
172
173 /* Multiply a bigtime operand by an int (op1*op2)
174
175 op2 must be non-negative and may not exceed 21,474 (2G/100,000) */
176
bigtime_mul(bigtime op1,int op2)177 LOCAL bigtime bigtime_mul(bigtime op1, int op2)
178 {
179 bigtime res, tmp;
180
181
182 tmp.hi = op1.hi * op2;
183 tmp.mid = op1.mid * op2;
184 tmp.lo = op1.lo * op2;
185
186 res.lo = tmp.lo % 100000;
187 res.mid = ((tmp.lo / 100000) + tmp.mid) % 100000;
188 res.hi = ((tmp.lo / 100000) + tmp.mid) / 100000 + tmp.hi;
189
190 return res;
191 }
192
193
194 /* Return time in 10's of microseconds */
195
pro_time_get()196 LOCAL bigtime pro_time_get()
197 {
198 struct timeval tv;
199 bigtime res;
200
201
202 gettimeofday(&tv, NULL);
203
204 /* Calculate 10's of microseconds */
205
206 res.lo = tv.tv_usec/10;
207
208 /* Calculate seconds */
209
210 res.mid = tv.tv_sec % 100000;
211 res.hi = tv.tv_sec / 100000;
212
213 return res;
214 }
215 #endif
216
217
218 /* Update pro_time variable */
219
pro_time_update()220 LOCAL void pro_time_update()
221 {
222 time_t t;
223
224 t = time(NULL);
225 pro_time = localtime(&t);
226
227 pro_time_sec = pro_time->tm_sec;
228 pro_time_min = pro_time->tm_min;
229 pro_time_hour = pro_time->tm_hour;
230
231 /* Ignore leap seconds */
232
233 if (pro_time_sec > 59)
234 pro_time_sec = 59;
235 }
236
237
238 /* Event scheduler */
239
pro_clk_sched()240 LOCAL void pro_clk_sched ()
241 {
242 int interval, rs;
243
244 /* Recalculate frequency and start time if RS has changed */
245
246 rs = pro_clk_csr0 & PRO_CLK_RS;
247
248 if (rs != pro_clk_rs)
249 {
250 pro_clk_rs = rs;
251
252 /* Update pro_clk_freq */
253
254 pro_clk_freq = pro_clk_intf[pro_clk_rs];
255 #ifdef RTC
256 /* Initialize time register */
257
258 pro_time_start = bigtime_mul(pro_time_get(), pro_clk_freq);
259 #endif
260 /* Clear number of outstanding interrupts */
261
262 pro_clk_numint = 0;
263
264 /* Clear interrupt counter */
265
266 pro_clk_int_cnt = 0;
267 }
268
269 /* Determine delay interval until next interrupt */
270
271 interval = PRO_EQ_CLK_IPS / pro_clk_freq;
272
273 #ifdef RTC
274 /* Use shorter interval if more than 2 interrupts are outstanding.
275 This will allow the RTC to catch up. (Wait until the emulator
276 is in real-time mode - i.e. the ROM diagnostics have completed) */
277
278 /* (this is only done when in interrupt throttle mode) */
279
280 /* XXX This might cause a problem with emulated operating systems
281 - needs to be validated */
282
283 if (pro_int_throttle && pro_clk_realtime && (pro_clk_numint > 2))
284 interval = PRO_CLK_RTC_CATCHUP;
285 #endif
286 /* Schedule event */
287
288 pro_eq_sched(PRO_EVENT_CLK, interval);
289 }
290
291 /* Update-ended event scheduler */
292
pro_clk_uf_sched()293 LOCAL void pro_clk_uf_sched ()
294 {
295 pro_eq_sched(PRO_EVENT_CLK_UF, PRO_EQ_CLK_UF);
296 }
297
298 /* Update-ended event handler */
299
pro_clk_uf_eq()300 void pro_clk_uf_eq ()
301 {
302 /* Update time and date */
303
304 pro_time_update();
305
306 /* Clear update-in-progress bit */
307
308 pro_clk_csr0 = pro_clk_csr0 & ~PRO_CLK_UIP;
309
310 /* Set UF bit */
311
312 pro_clk_csr2 = pro_clk_csr2 | PRO_CLK_UF;
313
314 /* Trigger update-ended interrupt, if enabled */
315
316 if ((pro_clk_csr1 & PRO_CLK_UIE) != 0)
317 {
318 pro_clk_csr2 = pro_clk_csr2 | PRO_CLK_IRQF;
319 pro_int_set(PRO_INT_CLK);
320 }
321
322 /* Compare against alarm registers and trigger
323 alarm interrupt if appropriate */
324
325 if (((pro_time_sec == pro_clk_al_sec)
326 || ((pro_clk_al_sec & PRO_CLK_AL_X) == PRO_CLK_AL_X))
327 && ((pro_time_min == pro_clk_al_min)
328 || ((pro_clk_al_min & PRO_CLK_AL_X) == PRO_CLK_AL_X))
329 && ((pro_time_hour == pro_clk_al_hour)
330 || ((pro_clk_al_hour & PRO_CLK_AL_X) == PRO_CLK_AL_X)))
331 {
332 /* Set AF bit */
333
334 pro_clk_csr2 = pro_clk_csr2 | PRO_CLK_AF;
335
336 /* Trigger alarm interrupt, if enabled */
337
338 if ((pro_clk_csr1 & PRO_CLK_AIE) != 0)
339 {
340 pro_clk_csr2 = pro_clk_csr2 | PRO_CLK_IRQF;
341 pro_int_set(PRO_INT_CLK);
342 }
343 }
344 }
345
346 /* Periodic interrupt event queue handler */
347
pro_clk_eq()348 void pro_clk_eq ()
349 {
350 #ifdef RTC
351 int set_int;
352 #endif
353 #ifdef LIMIT
354 unsigned long num_us; /* number uS to sleep for realtime operation */
355 #endif
356
357 /* Set interrupt and schedule next interrupt, if enabled */
358
359 #ifdef RTC
360 /* Check if enough instructions have been simulated
361 to switch to realtime mode */
362
363 if (pro_clk_realtime == 0)
364 {
365 set_int = 1;
366
367 if (sim_gtime() > PRO_CLK_RTC_SWITCH)
368 pro_clk_realtime = 1;
369 }
370 else
371 {
372 /* Calculate number of outstanding interrupts, based on real time */
373
374 /* Get current time */
375
376 pro_time_current = bigtime_mul(pro_time_get(), pro_clk_freq);
377
378 /* Calculate number of outstanding interrupts (as 32-bit int) */
379
380 /* Perform the following: pro_clk_numint = (pro_time_current - pro_time_start)/100000; */
381
382 pro_time_tmp = bigtime_sub(pro_time_current, pro_time_start);
383
384 /* (There is an implicit division by 100000 here, as pro_time_tmp.lo is dropped) */
385
386 pro_clk_numint = pro_time_tmp.hi * 100000 + pro_time_tmp.mid;
387
388 /* Trigger int only if the previous one has been seen
389 * (and only if in interrupt throttle mode) */
390
391 /* This is required for interrupt throttle mode, but makes the
392 * emulator implementation slightly inaccurate. If both the
393 * clock interrupt bit in the interrupt controller's interrupt
394 * request register and the periodic interrupt flag in the CSR
395 * are set, the OS is determined not to have seen the previous
396 * interrupt yet. In this case, further interrupts are delayed.
397 * (Note that this is an experimental option!)
398 */
399
400 if (pro_int_throttle && ((pro_clk_csr2 & PRO_CLK_PF) != 0)
401 && ((pro_int_irr[(PRO_INT_CLK >> 8) & 0377] & (PRO_INT_CLK & 0377)) != 0))
402 set_int = 0;
403 else
404
405 /* Check number of outstanding interrupts */
406
407 #ifndef LIMIT
408 if (pro_clk_numint < 1)
409 set_int = 0;
410 else
411 #endif
412 {
413 #ifdef LIMIT
414 if (pro_clk_numint < 1)
415 {
416 /* Perform the following: num_us = (pro_time_current - pro_time_start)/pro_clk_freq; */
417
418 pro_time_tmp = bigtime_sub(pro_time_current, pro_time_start);
419
420 /* Since pro_clk_numint == 0, we are gauranteed that pro_time_tmp.hi and .mid are 0 */
421
422 num_us = (pro_time_tmp.lo * 10)/pro_clk_freq;
423 usleep(num_us);
424 }
425 #endif
426 set_int = 1;
427
428 /* Advance start time */
429
430 pro_time_start = bigtime_add(pro_time_start, 100000);
431 }
432 }
433
434 if (set_int == 1)
435 #endif
436 {
437 /* Trigger interrupt only if frequency is not disabled */
438
439 if (pro_clk_rs != 0)
440 {
441 /* Trigger interrupt */
442
443 pro_clk_csr2 = pro_clk_csr2 | PRO_CLK_PF;
444
445 if ((pro_clk_csr1 & PRO_CLK_PIE) != 0) /* if enabled */
446 {
447 pro_clk_csr2 = pro_clk_csr2 | PRO_CLK_IRQF;
448 pro_int_set(PRO_INT_CLK);
449 }
450 }
451
452 /* Advance interrupt counter and update time, if required */
453
454 pro_clk_int_cnt++;
455
456 if (pro_clk_int_cnt >= pro_clk_freq)
457 {
458 pro_clk_int_cnt = 0;
459
460 /* Inhibit update cycles if SET bit is set */
461
462 if ((pro_clk_csr1 & PRO_CLK_SET) == 0)
463 {
464 /* Set update-in-progress bit */
465
466 pro_clk_csr0 = pro_clk_csr0 | PRO_CLK_UIP;
467
468 /* Schedule update-ended event */
469
470 pro_clk_uf_sched ();
471 }
472 }
473 }
474
475 /* Schedule next interrupt */
476
477 pro_clk_sched();
478 }
479
480 /* Clock registers */
481
pro_clk_rd(int pa)482 int pro_clk_rd (int pa)
483 {
484 int data;
485
486 switch (pa & 017777776)
487 {
488 case 017773000:
489 data = pro_time_sec; /* seconds */
490 break;
491
492 case 017773002:
493 data = pro_clk_al_sec; /* seconds alarm */
494 break;
495
496 case 017773004:
497 data = pro_time_min; /* minutes */
498 break;
499
500 case 017773006:
501 data = pro_clk_al_min; /* minutes alarm */
502 break;
503
504 case 017773010:
505 data = pro_time_hour; /* hours */
506 break;
507
508 case 017773012:
509 data = pro_clk_al_hour; /* hours alarm */
510 break;
511
512 case 017773014:
513 data = pro_time->tm_wday + 1; /* day */ /* XXX correct? */
514 break;
515
516 case 017773016:
517 data = pro_time->tm_mday; /* date */
518 break;
519
520 case 017773020:
521 data = pro_time->tm_mon + 1; /* month */
522 break;
523
524 case 017773022:
525 /* Venix is unhappy with (70 < year > 99).
526 The following workaround allows the year to
527 be forced from the configuration file. */
528
529 if (pro_force_year > -1)
530 data = pro_force_year;
531 else
532 data = pro_time->tm_year % 100; /* year */
533 break;
534
535 case 017773024:
536 data = pro_clk_csr0;
537 break;
538
539 case 017773026:
540 data = pro_clk_csr1;
541 break;
542
543 case 017773030:
544 data = pro_clk_csr2;
545
546 /* clear read-once bits */
547
548 pro_clk_csr2 = pro_clk_csr2 & ~(PRO_CLK_IRQF | PRO_CLK_PF
549 | PRO_CLK_AF | PRO_CLK_UF);
550
551 break;
552
553 case 017773032:
554 data = pro_clk_csr3;
555
556 /* set read-once bit */
557
558 pro_clk_csr3 = pro_clk_csr3 | PRO_CLK_VRT;
559
560 break;
561
562 default:
563 data = 0;
564 break;
565 }
566
567 return data;
568 }
569
pro_clk_wr(int data,int pa,int access)570 void pro_clk_wr (int data, int pa, int access)
571 {
572 switch (pa & 017777776)
573 {
574 case 017773002:
575 WRITE_WB(pro_clk_al_sec, PRO_CLK_AL_SEC_W, access); /* seconds alarm */
576 break;
577
578 case 017773006:
579 WRITE_WB(pro_clk_al_min, PRO_CLK_AL_MIN_W, access); /* minutes alarm */
580 break;
581
582 case 017773012:
583 WRITE_WB(pro_clk_al_hour, PRO_CLK_AL_HOUR_W, access); /* hours alarm */
584 break;
585
586 case 017773000:
587 case 017773004:
588 case 017773010:
589 case 017773014:
590 case 017773016:
591 case 017773020:
592 case 017773022:
593
594 /* Writes to time registers are not supported */
595 /* XXX
596 printf("%10.0lf Realtime clock write\r\n", sim_gtime());
597 */
598 break;
599
600 case 017773024:
601 WRITE_WB(pro_clk_csr0, PRO_CLK_CSR0_W, access);
602 break;
603
604 case 017773026:
605 WRITE_WB(pro_clk_csr1, PRO_CLK_CSR1_W, access);
606 break;
607
608 case 017773030:
609 case 017773032:
610 /* read-only */
611 break;
612
613 default:
614 break;
615 }
616 }
617
618
619 /* Reset */
620
pro_clk_reset()621 void pro_clk_reset ()
622 {
623 pro_clk_csr0 = 0000;
624 pro_clk_csr1 = PRO_CLK_DM | PRO_CLK_24H;
625 pro_clk_csr2 = 0000;
626 pro_clk_csr3 = PRO_CLK_VRT; /* this indicates clock has not lost power */
627
628 pro_clk_al_sec = 0;
629 pro_clk_al_min = 0;
630 pro_clk_al_hour = 0;
631
632 pro_clk_realtime = 0;
633 pro_clk_numint = 0;
634 pro_clk_freq = 0;
635 pro_clk_int_cnt = 0;
636
637 #ifdef RTC
638 pro_time_start.hi = 0;
639 pro_time_start.mid = 0;
640 pro_time_start.lo = 0;
641
642 pro_time_current.hi = 0;
643 pro_time_current.mid = 0;
644 pro_time_current.lo = 0;
645 #endif
646
647 /* Initialize time */
648
649 pro_time_update();
650
651 /* Force initialization of pro_clk_freq and pro_time_start */
652
653 pro_clk_rs = -1;
654
655 /* Schedule first periodic event */
656
657 pro_clk_sched();
658 }
659 #endif
660