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