/* * This file is part of x48, an emulator of the HP-48sx Calculator. * Copyright (C) 1994 Eddie C. Dost (ecd@dressler.de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Log: timer.c,v $ * Revision 1.7 1995/01/11 18:20:01 ecd * major update to support HP48 G/GX * * Revision 1.6 1994/12/07 20:20:50 ecd * minor fix * * Revision 1.6 1994/12/07 20:20:50 ecd * minor fix * * Revision 1.5 1994/11/28 02:00:51 ecd * removed stupid bug that caused negative time on call * to adjtime() * * Revision 1.4 1994/11/02 14:44:28 ecd * real time support completed * * * $Id: timer.c,v 1.7 1995/01/11 18:20:01 ecd Exp ecd $ */ #include "global.h" #include #include #include #include #include "timer.h" #include "debugger.h" #include "romio.h" /* #define DEBUG_TIMER 1 */ /* #define DEBUG_TIMER_ADJUST 1 */ #ifdef SOLARIS extern int gettimeofday __ProtoType__((struct timeval *tp)); #endif #ifdef SUNOS extern int gettimeofday __ProtoType__((struct timeval *, struct timezone *)); #endif typedef struct x48_timer_t { word_1 run; word_64 start; word_64 stop; word_64 value; } x48_timer_t; static x48_timer_t timers[NR_TIMERS]; static long systime_offset = 0; /* * Ticks for THU 01.01.1970 00:00:00 */ word_64 unix_0_time = { 0x0001cf2e, 0x8f800000 }; word_64 ticks_10_min = { 0x0, 0x00b40000 }; /* * Will be in saturn_t in the future */ word_64 set_0_time = { 0x0, 0x0 }; /* * Calculated as (unix_0_time + set_0_time) */ word_64 time_offset = { 0x0, 0x0 }; #define RAM_BASE_SX 0x70000 #define ACCESSTIME_SX (0x70052 - RAM_BASE_SX) #define ACCESSCRC_SX (0x7005F - RAM_BASE_SX) #define TIMEOUT_SX (0x70063 - RAM_BASE_SX) #define TIMEOUTCLK_SX (0x70070 - RAM_BASE_SX) #define RAM_BASE_GX 0x80000 #define ACCESSTIME_GX (0x80058 - RAM_BASE_GX) #define ACCESSCRC_GX (0x80065 - RAM_BASE_GX) #define TIMEOUT_GX (0x80069 - RAM_BASE_GX) #define TIMEOUTCLK_GX (0x80076 - RAM_BASE_GX) #define calc_crc(nib) (crc = (crc >> 4) ^ (((crc ^ (nib)) & 0xf) * 0x1081)) static inline void #ifdef __FunctionProto__ add_64(word_64 r1, word_64 *r2) #else add_64(r1, r2) word_64 r1; word_64 *r2; #endif { unsigned short s1[4], s2[4]; unsigned long r[4]; s1[0] = (unsigned short)(r1.lo & 0xffff); s1[1] = (unsigned short)((r1.lo >> 16) & 0xffff); s1[2] = (unsigned short)(r1.hi & 0xffff); s1[3] = (unsigned short)((r1.hi >> 16) & 0xffff); s2[0] = (unsigned short)(r2->lo & 0xffff); s2[1] = (unsigned short)((r2->lo >> 16) & 0xffff); s2[2] = (unsigned short)(r2->hi & 0xffff); s2[3] = (unsigned short)((r2->hi >> 16) & 0xffff); r[0] = (unsigned long)s1[0] + (unsigned long)s2[0]; r[1] = (unsigned long)s1[1] + (unsigned long)s2[1]; r[2] = (unsigned long)s1[2] + (unsigned long)s2[2]; r[3] = (unsigned long)s1[3] + (unsigned long)s2[3]; if (r[0] >> 16) r[1]++; if (r[1] >> 16) r[2]++; if (r[2] >> 16) r[3]++; r2->lo = ((r[1] << 16) | (r[0] & 0xffff)); r2->hi = ((r[3] << 16) | (r[2] & 0xffff)); } static inline void #ifdef __FunctionProto__ sub_64(word_64 r1, word_64 *r2) #else sub_64(r1, r2) word_64 r1; word_64 *r2; #endif { unsigned short s1[4], s2[4]; unsigned long r[4]; s1[0] = (unsigned short)(r1.lo & 0xffff); s1[1] = (unsigned short)((r1.lo >> 16) & 0xffff); s1[2] = (unsigned short)(r1.hi & 0xffff); s1[3] = (unsigned short)((r1.hi >> 16) & 0xffff); s2[0] = (unsigned short)(r2->lo & 0xffff); s2[1] = (unsigned short)((r2->lo >> 16) & 0xffff); s2[2] = (unsigned short)(r2->hi & 0xffff); s2[3] = (unsigned short)((r2->hi >> 16) & 0xffff); r[0] = (unsigned long)s2[0] - (unsigned long)s1[0]; r[1] = (unsigned long)s2[1] - (unsigned long)s1[1]; r[2] = (unsigned long)s2[2] - (unsigned long)s1[2]; r[3] = (unsigned long)s2[3] - (unsigned long)s1[3]; if (r[0] >> 16) r[1]--; if (r[1] >> 16) r[2]--; if (r[2] >> 16) r[3]--; r2->lo = ((r[1] << 16) | (r[0] & 0xffff)); r2->hi = ((r[3] << 16) | (r[2] & 0xffff)); } /* * Set ACCESSTIME: (on startup) * * 1. TICKS = 8192 * gettimeofday() (UNIX System Time) * 2. TICKS += unix_0_time (TICKS for 1.1.1970, 0:00) * 3. TICKS += set_0_time (Time adjustment from User) * 4. TICKS += saturn.timer2 (Timer 2 from last run) * 5. Write this into ACCESSTIME * 6. Calculate CRC for 13 Nibbles * 7. Write this into ACCESSCRC * 8. Prevent AutoOff by setting TIMEOUT * */ void #ifdef __FunctionProto__ set_accesstime(void) #else set_accesstime() #endif { struct timeval tv; #ifndef SOLARIS struct timezone tz; #endif word_64 ticks, timeout, timer2; word_20 accesstime_loc, timeout_loc; word_20 accesscrc_loc, timeoutclk_loc; word_16 crc; word_4 val; int i; time_t gmt; struct tm *ltm; /* * This is done to set the variable 'timezone' on SYSV systems */ (void)time(&gmt); ltm = localtime(&gmt); #ifdef SYSV_TIME // shagrath: the timezone field is not available on 2.1 devices :\ so it's crashing // revert the timezone fix for the time being //systime_offset = timezone; if( ltm->tm_isdst ) systime_offset -= 3600; #else systime_offset = -ltm->tm_gmtoff; #endif #ifdef SOLARIS gettimeofday(&tv); #else gettimeofday(&tv, &tz); #endif tv.tv_sec -= systime_offset; ticks.hi = (tv.tv_sec >> 19); ticks.lo = (tv.tv_sec << 13); ticks.lo |= (tv.tv_usec << 7) / 15625; time_offset.lo = unix_0_time.lo; time_offset.hi = unix_0_time.hi; add_64(set_0_time, &time_offset); add_64(time_offset, &ticks); timer2.lo = saturn.timer2; if (saturn.timer2 & 0x80000000) timer2.hi = 0xffffffff; else timer2.hi = 0x0; add_64(timer2, &ticks); timeout.lo = ticks.lo; timeout.hi = ticks.hi; crc = 0x0; if (opt_gx) { accesstime_loc = ACCESSTIME_GX; accesscrc_loc = ACCESSCRC_GX; timeout_loc = TIMEOUT_GX; timeoutclk_loc = TIMEOUTCLK_GX; } else { accesstime_loc = ACCESSTIME_SX; accesscrc_loc = ACCESSCRC_SX; timeout_loc = TIMEOUT_SX; timeoutclk_loc = TIMEOUTCLK_SX; } for (i = 0; i < 13; i++) { val = ticks.lo & 0xf; calc_crc(val); saturn.ram[accesstime_loc + i] = val; ticks.lo >>= 4; ticks.lo |= (ticks.hi & 0xf) << 28; ticks.hi >>= 4; } for (i = 0; i < 4; i++) { saturn.ram[accesscrc_loc + i] = crc & 0xf; crc >>= 4; } add_64(ticks_10_min, &timeout); for (i = 0; i < 13; i++) { val = timeout.lo & 0xf; calc_crc(val); saturn.ram[timeout_loc + i] = val; timeout.lo >>= 4; timeout.lo |= (timeout.hi & 0xf) << 28; timeout.hi >>= 4; } saturn.ram[timeoutclk_loc] = 0xf; } long #ifdef __FunctionProto__ diff_timer(word_64 *t1, word_64 *t2) #else diff_timer(t1, t2) word_64 *t1; word_64 *t2; #endif { return (t1->lo - t2->lo); } static inline void #ifdef __FunctionProto__ add_sub_64(word_64 *t1, word_64 *t2, word_64 *diff) #else add_sub_64(t1, t2, diff) word_64 *t1; word_64 *t2; word_64 *diff; #endif { unsigned short s1[4], s2[4]; unsigned long r[4]; s1[0] = (unsigned short)(t1->lo & 0xffff); s1[1] = (unsigned short)((t1->lo >> 16) & 0xffff); s1[2] = (unsigned short)(t1->hi & 0xffff); s1[3] = (unsigned short)((t1->hi >> 16) & 0xffff); s2[0] = (unsigned short)(t2->lo & 0xffff); s2[1] = (unsigned short)((t2->lo >> 16) & 0xffff); s2[2] = (unsigned short)(t2->hi & 0xffff); s2[3] = (unsigned short)((t2->hi >> 16) & 0xffff); r[0] = (unsigned long)s1[0] - (unsigned long)s2[0]; r[1] = (unsigned long)s1[1] - (unsigned long)s2[1]; r[2] = (unsigned long)s1[2] - (unsigned long)s2[2]; r[3] = (unsigned long)s1[3] - (unsigned long)s2[3]; if (r[0] >> 16) r[1]--; if (r[1] >> 16) r[2]--; if (r[2] >> 16) r[3]--; s1[0] = (unsigned short)(diff->lo & 0xffff); s1[1] = (unsigned short)((diff->lo >> 16) & 0xffff); s1[2] = (unsigned short)(diff->hi & 0xffff); s1[3] = (unsigned short)((diff->hi >> 16) & 0xffff); s2[0] = (unsigned short)r[0]; s2[1] = (unsigned short)r[1]; s2[2] = (unsigned short)r[2]; s2[3] = (unsigned short)r[3]; r[0] = (unsigned long)s1[0] + (unsigned long)s2[0]; r[1] = (unsigned long)s1[1] + (unsigned long)s2[1]; r[2] = (unsigned long)s1[2] + (unsigned long)s2[2]; r[3] = (unsigned long)s1[3] + (unsigned long)s2[3]; if (r[0] >> 16) r[1]++; if (r[1] >> 16) r[2]++; if (r[2] >> 16) r[3]++; t2->lo = t1->lo; t2->hi = t1->hi; diff->lo = ((r[1] << 16) | (r[0] & 0xffff)); diff->hi = ((r[3] << 16) | (r[2] & 0xffff)); } void #ifdef __FunctionProto__ start_timer(int timer) #else start_timer(timer) int timer; #endif { struct timeval tv; #ifndef SOLARIS struct timezone tz; #endif if (timer > NR_TIMERS) return; if (timers[timer].run == 1) return; #ifdef SOLARIS gettimeofday(&tv); #else gettimeofday(&tv, &tz); #endif tv.tv_sec -= systime_offset; timers[timer].run = 1; if (timer == T1_TIMER) { timers[timer].start.hi = (tv.tv_sec >> 23); timers[timer].start.lo = (tv.tv_sec << 9); timers[timer].start.lo |= tv.tv_usec / 62500; } else { timers[timer].start.hi = (tv.tv_sec >> 19); timers[timer].start.lo = (tv.tv_sec << 13); timers[timer].start.lo |= (tv.tv_usec << 7) / 15625; } #ifdef DEBUG_TIMER fprintf(stderr, "Timer[%d] start at 0x%.8lx%.8lx\n", timer, timers[timer].start.hi, timers[timer].start.lo); #endif } void #ifdef __FunctionProto__ restart_timer(int timer) #else restart_timer(timer) int timer; #endif { struct timeval tv; #ifndef SOLARIS struct timezone tz; #endif if (timer > NR_TIMERS) return; timers[timer].start.lo = timers[timer].start.hi = 0; timers[timer].stop.lo = timers[timer].stop.hi = 0; timers[timer].value.lo = timers[timer].value.hi = 0; #ifdef SOLARIS gettimeofday(&tv); #else gettimeofday(&tv, &tz); #endif tv.tv_sec -= systime_offset; timers[timer].run = 1; if (timer == T1_TIMER) { timers[timer].start.hi = (tv.tv_sec >> 23); timers[timer].start.lo = (tv.tv_sec << 9); timers[timer].start.lo |= tv.tv_usec / 62500; } else { timers[timer].start.hi = (tv.tv_sec >> 19); timers[timer].start.lo = (tv.tv_sec << 13); timers[timer].start.lo |= (tv.tv_usec << 7) / 15625; } #ifdef DEBUG_TIMER fprintf(stderr, "Timer[%d] restart at 0x%.8lx%.8lx\n", timer, timers[timer].start.hi, timers[timer].start.lo); #endif } void #ifdef __FunctionProto__ stop_timer(int timer) #else stop_timer(timer) int timer; #endif { struct timeval tv; #ifndef SOLARIS struct timezone tz; #endif if (timer > NR_TIMERS) return; if (timers[timer].run == 0) return; #ifdef SOLARIS gettimeofday(&tv); #else gettimeofday(&tv, &tz); #endif tv.tv_sec -= systime_offset; timers[timer].run = 0; if (timer == T1_TIMER) { timers[timer].stop.hi = (tv.tv_sec >> 23); timers[timer].stop.lo = (tv.tv_sec << 9); timers[timer].stop.lo |= tv.tv_usec / 62500; } else { timers[timer].stop.hi = (tv.tv_sec >> 19); timers[timer].stop.lo = (tv.tv_sec << 13); timers[timer].stop.lo |= (tv.tv_usec << 7) / 15625; } add_sub_64(&timers[timer].stop, &timers[timer].start, &timers[timer].value); #ifdef DEBUG_TIMER fprintf(stderr, "Timer[%d] stop at 0x%.8lx%.8lx, value 0x%.8lx%.8lx\n", timer, timers[timer].stop.hi, timers[timer].stop.lo, timers[timer].value.hi, timers[timer].value.lo); #endif } void #ifdef __FunctionProto__ reset_timer(int timer) #else reset_timer(timer) int timer; #endif { if (timer > NR_TIMERS) return; timers[timer].run = 0; timers[timer].start.lo = timers[timer].start.hi = 0; timers[timer].stop.lo = timers[timer].stop.hi = 0; timers[timer].value.lo = timers[timer].value.hi = 0; #ifdef DEBUG_TIMER fprintf(stderr, "Timer[%d] reset\n", timer); #endif } static word_64 zero = { 0, 0 }; word_64 #ifdef __FunctionProto__ get_timer(int timer) #else get_timer(timer) int timer; #endif { struct timeval tv; #ifndef SOLARIS struct timezone tz; #endif word_64 stop; if (timer > NR_TIMERS) return zero; if (timers[timer].run) { #ifdef SOLARIS gettimeofday(&tv); #else gettimeofday(&tv, &tz); #endif tv.tv_sec -= systime_offset; if (timer == T1_TIMER) { stop.hi = (tv.tv_sec >> 23); stop.lo = (tv.tv_sec << 9); stop.lo |= tv.tv_usec / 62500; } else { stop.hi = (tv.tv_sec >> 19); stop.lo = (tv.tv_sec << 13); stop.lo |= (tv.tv_usec << 7) / 15625; } add_sub_64(&stop, &timers[timer].start, &timers[timer].value); } return timers[timer].value; } /* * Calculate TIMER 2 Ticks: * * 1. TICKS = 8192 * gettimeofday() (UNIX System Time) * 2. TICKS += unix_0_time (TICKS for 1.1.1970, 0:00) * 3. TICKS += set_0_time (Time adjustment from User) * 4. Get value of ACCESSTIME * 5. Return (ACCESSTIME - TICKS) * */ t1_t2_ticks #ifdef __FunctionProto__ get_t1_t2(void) #else get_t1_t2() #endif { struct timeval tv; #ifndef SOLARIS struct timezone tz; #endif word_64 stop; t1_t2_ticks ticks; word_64 access_time; word_64 adj_time; word_64 diff_time; word_64 delta; word_20 accesstime_loc; int i; #ifdef SOLARIS gettimeofday(&tv); #else gettimeofday(&tv, &tz); #endif tv.tv_sec -= systime_offset; if (timers[T1_TIMER].run) { stop.hi = (tv.tv_sec >> 23); stop.lo = (tv.tv_sec << 9); stop.lo |= tv.tv_usec / 62500; add_sub_64(&stop, &timers[T1_TIMER].start, &timers[T1_TIMER].value); } ticks.t1_ticks = timers[T1_TIMER].value.lo; stop.hi = (tv.tv_sec >> 19); stop.lo = (tv.tv_sec << 13); stop.lo |= (tv.tv_usec << 7) / 15625; add_64(time_offset, &stop); if (opt_gx) accesstime_loc = ACCESSTIME_GX; else accesstime_loc = ACCESSTIME_SX; access_time.lo = 0x0; access_time.hi = 0x0; for (i = 13 - 1; i >= 0; i--) { access_time.hi <<= 4; access_time.hi |= ((access_time.lo >> 28) & 0xf); access_time.lo <<= 4; access_time.lo |= ((int)saturn.ram[accesstime_loc + i] & 0xf); } sub_64(stop, &access_time); if (adj_time_pending || in_debugger) { /* * We have been inside an interrupt for very long, maybe * or we are sleeping in the debugger. * Don't adjust the time, can't come from user, anyhow. */ if ((saturn.timer2 >= 0 && (access_time.lo & 0x80000000)) || ((unsigned long)saturn.timer2 > access_time.lo)) { /* * check OK, return calculated time */ ticks.t2_ticks = access_time.lo; } else { /* * Don't increment timer2, return old value and * slow down timer2. */ ticks.t2_ticks = saturn.timer2; saturn.t2_tick++; } return ticks; } diff_time.lo = saturn.timer2; if (saturn.timer2 & 0x80000000) diff_time.hi = 0xffffffff; else diff_time.hi = 0x0; adj_time.lo = access_time.lo; adj_time.hi = access_time.hi; sub_64(diff_time, &adj_time); if (adj_time.hi & 0x8000000) { delta.lo = 0x0; delta.hi = 0x0; sub_64(adj_time, &delta); } else { delta.lo = adj_time.lo; delta.hi = adj_time.hi; } if (delta.hi != 0 || (delta.lo > 0x3c000)) /* Half a minute */ { add_64(adj_time, &set_0_time); add_64(adj_time, &time_offset); sub_64(adj_time, &access_time); #ifdef DEBUG_TIMER_ADJUST fprintf(stderr, "Time adjusted by "); if (adj_time.hi) fprintf(stderr, "%lX%.8lX", adj_time.hi, adj_time.lo); else fprintf(stderr, "%lX", adj_time.lo); fprintf(stderr, " TICKS, Total offset "); if (set_0_time.hi) fprintf(stderr, "%lX%.8lX", set_0_time.hi, set_0_time.lo); else fprintf(stderr, "%lX", set_0_time.lo); fprintf(stderr, " TICKS\n"); #endif } if ((saturn.timer2 >= 0 && (access_time.lo & 0x80000000)) || ((unsigned long)saturn.timer2 > access_time.lo)) { /* * check OK, return calculated time */ ticks.t2_ticks = access_time.lo; } else { /* * Don't increment timer2, return old value and * slow down timer2. */ ticks.t2_ticks = saturn.timer2; saturn.t2_tick++; } return ticks; }