1 /*
2  *  This file is part of x48, an emulator of the HP-48sx Calculator.
3  *  Copyright (C) 1994  Eddie C. Dost  (ecd@dressler.de)
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 /* $Log: timer.c,v $
21  * Revision 1.7  1995/01/11  18:20:01  ecd
22  * major update to support HP48 G/GX
23  *
24  * Revision 1.6  1994/12/07  20:20:50  ecd
25  * minor fix
26  *
27  * Revision 1.6  1994/12/07  20:20:50  ecd
28  * minor fix
29  *
30  * Revision 1.5  1994/11/28  02:00:51  ecd
31  * removed stupid bug that caused negative time on call
32  * to adjtime()
33  *
34  * Revision 1.4  1994/11/02  14:44:28  ecd
35  * real time support completed
36  *
37  *
38  * $Id: timer.c,v 1.7 1995/01/11 18:20:01 ecd Exp ecd $
39  */
40 
41 #include "global.h"
42 
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <unistd.h>
46 #include <time.h>
47 
48 #include "timer.h"
49 #include "debugger.h"
50 #include "romio.h"
51 
52 #include <assert.h>
53 
54 /* #define DEBUG_TIMER 1 */
55 /* #define DEBUG_TIMER_ADJUST 1 */
56 
57 #ifdef SOLARIS
58 extern int gettimeofday __ProtoType__((struct timeval *tp));
59 #endif
60 #ifdef SUNOS
61 extern int gettimeofday __ProtoType__((struct timeval *, struct timezone *));
62 #endif
63 
64 typedef struct x48_timer_t {
65   word_1  run;
66   word_64 start;
67   word_64 stop;
68   word_64 value;
69 } x48_timer_t;
70 
71 static x48_timer_t timers[NR_TIMERS];
72 
73 static long systime_offset = 0;
74 
75 /*
76  * Ticks for THU 01.01.1970 00:00:00
77  */
78 word_64 unix_0_time  = 0x1CF2E8F800000L;
79 word_64 ticks_10_min = 0x00b40000L;
80 
81 /*
82  * Will be in saturn_t in the future
83  */
84 word_64 set_0_time = 0x0;
85 
86 /*
87  * Calculated as (unix_0_time + set_0_time)
88  */
89 word_64 time_offset = 0x0;
90 
91 #define RAM_BASE_SX	0x70000
92 #define ACCESSTIME_SX	(0x70052 - RAM_BASE_SX)
93 #define ACCESSCRC_SX	(0x7005F - RAM_BASE_SX)
94 #define TIMEOUT_SX	(0x70063 - RAM_BASE_SX)
95 #define TIMEOUTCLK_SX	(0x70070 - RAM_BASE_SX)
96 
97 #define RAM_BASE_GX	0x80000
98 #define ACCESSTIME_GX	(0x80058 - RAM_BASE_GX)
99 #define ACCESSCRC_GX	(0x80065 - RAM_BASE_GX)
100 #define TIMEOUT_GX	(0x80069 - RAM_BASE_GX)
101 #define TIMEOUTCLK_GX	(0x80076 - RAM_BASE_GX)
102 
103 #define calc_crc(nib) (crc = (crc >> 4) ^ (((crc ^ (nib)) & 0xf) * 0x1081))
104 
105 /*
106  * Set ACCESSTIME: (on startup)
107  *
108  * 1. TICKS = 8192 * gettimeofday()  (UNIX System Time)
109  * 2. TICKS += unix_0_time           (TICKS for 1.1.1970, 0:00)
110  * 3. TICKS += set_0_time            (Time adjustment from User)
111  * 4. TICKS += saturn.timer2         (Timer 2 from last run)
112  * 5. Write this into ACCESSTIME
113  * 6. Calculate CRC for 13 Nibbles
114  * 7. Write this into ACCESSCRC
115  * 8. Prevent AutoOff by setting TIMEOUT
116  *
117  */
118 void
119 #ifdef __FunctionProto__
set_accesstime(void)120 set_accesstime(void)
121 #else
122 set_accesstime()
123 #endif
124 {
125   struct timeval  tv;
126 #ifndef SOLARIS
127   struct timezone tz;
128 #endif
129   word_64	  ticks, timeout, timer2;
130   word_20	  accesstime_loc, timeout_loc;
131   word_20	  accesscrc_loc, timeoutclk_loc;
132   word_16	  crc;
133   word_4	  val;
134   int		  i;
135   time_t	  gmt;
136   struct tm	 *ltm;
137 
138   /*
139    * This is done to set the variable 'timezone' on SYSV systems
140    */
141   (void)time(&gmt);
142   ltm = localtime(&gmt);
143 #if defined(SYSV_TIME) || defined(__sgi)
144   systime_offset = timezone;
145   if( ltm->tm_isdst )
146     systime_offset -= 3600;
147 #else
148   systime_offset = -ltm->tm_gmtoff;
149 #endif
150 
151 #ifdef SOLARIS
152   gettimeofday(&tv);
153 #else
154   gettimeofday(&tv, &tz);
155 #endif
156   tv.tv_sec -= systime_offset;
157 
158   ticks = tv.tv_sec;
159   ticks <<= 13;
160   ticks += (tv.tv_usec << 7) / 15625;
161 
162   time_offset = unix_0_time + set_0_time;
163   ticks += time_offset;
164 
165   timer2 = saturn.timer2;
166   if (saturn.timer2 & 0x80000000)
167     {
168       assert(timer2 < 0);
169     }
170 
171   ticks += timer2;
172 
173   timeout = ticks;
174 
175   crc = 0x0;
176 
177   if (opt_gx)
178     {
179       accesstime_loc = ACCESSTIME_GX;
180       accesscrc_loc = ACCESSCRC_GX;
181       timeout_loc = TIMEOUT_GX;
182       timeoutclk_loc = TIMEOUTCLK_GX;
183     }
184   else
185     {
186       accesstime_loc = ACCESSTIME_SX;
187       accesscrc_loc = ACCESSCRC_SX;
188       timeout_loc = TIMEOUT_SX;
189       timeoutclk_loc = TIMEOUTCLK_SX;
190     }
191 
192   for (i = 0; i < 13; i++)
193     {
194       val = ticks & 0xf;
195       calc_crc(val);
196       saturn.ram[accesstime_loc + i] = val;
197       ticks >>= 4;
198     }
199 
200   for (i = 0; i < 4; i++)
201     {
202       saturn.ram[accesscrc_loc + i] = crc & 0xf;
203       crc >>= 4;
204     }
205 
206   timeout += ticks_10_min;
207 
208   for (i = 0; i < 13; i++)
209     {
210       val = timeout & 0xf;
211       calc_crc(val);
212       saturn.ram[timeout_loc + i] = val;
213       timeout >>= 4;
214     }
215 
216   saturn.ram[timeoutclk_loc] = 0xf;
217 }
218 
219 void
220 #ifdef __FunctionProto__
start_timer(int timer)221 start_timer(int timer)
222 #else
223 start_timer(timer)
224 int timer;
225 #endif
226 {
227   struct timeval  tv;
228 #ifndef SOLARIS
229   struct timezone tz;
230 #endif
231   assert(timer <= NR_TIMERS);
232 
233   if (timers[timer].run == 1)
234     return;
235 
236 #ifdef SOLARIS
237   gettimeofday(&tv);
238 #else
239   gettimeofday(&tv, &tz);
240 #endif
241   tv.tv_sec -= systime_offset;
242 
243   timers[timer].run = 1;
244   if (timer == T1_TIMER) {
245     timers[timer].start = (tv.tv_sec << 9);
246     timers[timer].start += (tv.tv_usec / 15625) >> 3;
247   } else {
248     timers[timer].start = tv.tv_sec;
249     timers[timer].start <<= 13;
250     timers[timer].start += (tv.tv_usec << 7) / 15625;
251   }
252 #ifdef DEBUG_TIMER
253   fprintf(stderr, "Timer%c[%d] start at 0x%lx\n", timer == T1_TIMER?'*':' ', timer, timers[timer].start);
254 #endif
255 }
256 
257 void
258 #ifdef __FunctionProto__
restart_timer(int timer)259 restart_timer(int timer)
260 #else
261 restart_timer(timer)
262 int timer;
263 #endif
264 {
265   struct timeval  tv;
266 #ifndef SOLARIS
267   struct timezone tz;
268 #endif
269 
270   if (timer > NR_TIMERS)
271     return;
272 
273   timers[timer].start = 0;
274   timers[timer].stop = 0;
275   timers[timer].value = 0;
276 
277 #ifdef SOLARIS
278   gettimeofday(&tv);
279 #else
280   gettimeofday(&tv, &tz);
281 #endif
282   tv.tv_sec -= systime_offset;
283 
284   timers[timer].run = 1;
285   if (timer == T1_TIMER) {
286     timers[timer].start = (tv.tv_sec << 9);
287     timers[timer].start += (tv.tv_usec / 15625) >> 3;
288   } else {
289     timers[timer].start = tv.tv_sec;
290     timers[timer].start <<= 13;
291     timers[timer].start += (tv.tv_usec << 7) / 15625;
292   }
293 #ifdef DEBUG_TIMER
294   fprintf(stderr, "Timer[%d] restart at 0x%lx\n", timer,
295           timers[timer].start);
296 #endif
297 }
298 
299 void
300 #ifdef __FunctionProto__
stop_timer(int timer)301 stop_timer(int timer)
302 #else
303 stop_timer(timer)
304 int timer;
305 #endif
306 {
307   struct timeval  tv;
308 #ifndef SOLARIS
309   struct timezone tz;
310 #endif
311 
312   if (timer > NR_TIMERS)
313     return;
314 
315   if (timers[timer].run == 0)
316     return;
317 
318 #ifdef SOLARIS
319   gettimeofday(&tv);
320 #else
321   gettimeofday(&tv, &tz);
322 #endif
323   tv.tv_sec -= systime_offset;
324 
325   timers[timer].run = 0;
326   if (timer == T1_TIMER) {
327     timers[timer].stop = (tv.tv_sec << 9);
328     timers[timer].stop += (tv.tv_usec / 15625) >> 3;
329   } else {
330     timers[timer].stop = tv.tv_sec;
331     timers[timer].stop <<= 13;
332     timers[timer].stop += (tv.tv_usec << 7) / 15625;
333   }
334 
335   timers[timer].value += timers[timer].stop - timers[timer].start;
336 //  add_sub_64(&timers[timer].stop, &timers[timer].start, &timers[timer].value);
337 
338 #ifdef DEBUG_TIMER
339   fprintf(stderr, "Timer[%d] stop at 0x%llx, value 0x%llx\n",
340           timer, timers[timer].stop, timers[timer].value);
341 #endif
342 }
343 
344 void
345 #ifdef __FunctionProto__
reset_timer(int timer)346 reset_timer(int timer)
347 #else
348 reset_timer(timer)
349 int timer;
350 #endif
351 {
352   if (timer > NR_TIMERS)
353     return;
354   timers[timer].run = 0;
355   timers[timer].start = 0;
356   timers[timer].stop = 0;
357   timers[timer].value = 0;
358 #ifdef DEBUG_TIMER
359   fprintf(stderr, "Timer[%d] reset\n", timer);
360 #endif
361 }
362 
363 static word_64 zero = 0;
364 
365 word_64
366 #ifdef __FunctionProto__
get_timer(int timer)367 get_timer(int timer)
368 #else
369 get_timer(timer)
370 int timer;
371 #endif
372 {
373   struct timeval  tv;
374 #ifndef SOLARIS
375   struct timezone tz;
376 #endif
377   word_64         stop;
378 
379   if (timer > NR_TIMERS)
380     return zero;
381 
382   if (timers[timer].run) {
383 
384 #ifdef SOLARIS
385     gettimeofday(&tv);
386 #else
387     gettimeofday(&tv, &tz);
388 #endif
389     tv.tv_sec -= systime_offset;
390 
391     if (timer == T1_TIMER) {
392       stop = (tv.tv_sec << 9);
393       stop += (tv.tv_usec / 15625) >> 3;
394     } else {
395       stop = tv.tv_sec;
396       stop <<= 13;
397       stop += (tv.tv_usec << 7) / 15625;
398     }
399     timers[timer].value += stop - timers[timer].start;
400   }
401 
402   return timers[timer].value;
403 }
404 
405 /*
406  * Calculate TIMER 2 Ticks:
407  *
408  * 1. TICKS = 8192 * gettimeofday()  (UNIX System Time)
409  * 2. TICKS += unix_0_time           (TICKS for 1.1.1970, 0:00)
410  * 3. TICKS += set_0_time            (Time adjustment from User)
411  * 4. Get value of ACCESSTIME
412  * 5. Return (ACCESSTIME - TICKS)
413  *
414  */
415 
416 t1_t2_ticks
417 #ifdef __FunctionProto__
get_t1_t2(void)418 get_t1_t2(void)
419 #else
420 get_t1_t2()
421 #endif
422 {
423   struct timeval  tv;
424 #ifndef SOLARIS
425   struct timezone tz;
426 #endif
427   word_64         stop;
428   t1_t2_ticks	  ticks;
429   word_64	  access_time;
430   word_64	  adj_time;
431   word_64	  diff_time;
432   word_64	  delta;
433   word_20	  accesstime_loc;
434   int             i;
435 
436 #ifdef SOLARIS
437   gettimeofday(&tv);
438 #else
439   gettimeofday(&tv, &tz);
440 #endif
441   tv.tv_sec -= systime_offset;
442 
443   if (timers[T1_TIMER].run)
444     {
445       stop = (tv.tv_sec << 9);
446       stop += (tv.tv_usec / 15625) >> 3;
447       if (timers[T1_TIMER].start <=  stop)
448         {
449           timers[T1_TIMER].value += stop - timers[T1_TIMER].start;
450         } else {
451 	  fprintf(stderr, "clock running backwards\n");
452 	}
453     }
454   ticks.t1_ticks = timers[T1_TIMER].value;
455 
456   stop = tv.tv_sec;
457   stop <<= 13;
458   stop += (tv.tv_usec << 7) / 15625;
459 
460   stop += time_offset;
461 
462   accesstime_loc = opt_gx ? ACCESSTIME_GX : ACCESSTIME_SX;
463 
464   access_time = 0x0;
465 
466   for (i = 13 - 1; i >= 0; i--)
467     {
468       access_time <<= 4;
469       access_time |= ((int)saturn.ram[accesstime_loc + i] & 0xf);
470     }
471 
472   access_time -= stop;
473 
474   if (adj_time_pending || in_debugger)
475     {
476       /*
477        * We have been inside an interrupt for very long, maybe
478        * or we are sleeping in the debugger.
479        * Don't adjust the time, can't come from user, anyhow.
480        */
481 
482       if ((saturn.timer2 >= 0 && access_time < 0)
483           || ((unsigned long)saturn.timer2 > access_time))
484         {
485           /*
486            * check OK, return calculated time
487            */
488           ticks.t2_ticks = access_time;
489         }
490       else
491         {
492           /*
493            * Don't increment timer2, return old value and
494            * slow down timer2.
495            */
496           ticks.t2_ticks = saturn.timer2;
497           saturn.t2_tick++;
498         }
499 
500       return ticks;
501     }
502 
503   diff_time = saturn.timer2;
504 
505   adj_time = access_time - diff_time;
506   delta = abs(adj_time);
507 
508   if (delta > 0x3c000)	/* Half a minute */
509     {
510       set_0_time += adj_time;
511       time_offset += adj_time;
512       access_time -= adj_time;
513 
514 #ifdef DEBUG_TIMER_ADJUST
515       fprintf(stderr, "Time adjusted by ");
516       fprintf(stderr, "%lX", adj_time);
517       fprintf(stderr, " TICKS, Total offset ");
518       fprintf(stderr, "%lX", set_0_time);
519       fprintf(stderr, " TICKS\n");
520 #endif
521     }
522 
523   if ((saturn.timer2 >= 0 && (access_time < 0))
524       || ((unsigned long)saturn.timer2 > access_time))
525     {
526       /*
527        * check OK, return calculated time
528        */
529       ticks.t2_ticks = access_time;
530     }
531   else
532     {
533       /*
534        * Don't increment timer2, return old value and
535        * slow down timer2.
536        */
537       ticks.t2_ticks = saturn.timer2;
538       saturn.t2_tick++;
539     }
540 
541   return ticks;
542 }
543 
544