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