404 lines
13 KiB
C
404 lines
13 KiB
C
|
/*
|
||
|
* timer.c
|
||
|
*
|
||
|
* This file is part of Emu48
|
||
|
*
|
||
|
* Copyright (C) 1995 Sebastien Carlier
|
||
|
*
|
||
|
*/
|
||
|
#include "pch.h"
|
||
|
#include "Emu48.h"
|
||
|
|
||
|
#define AUTO_OFF 10 // 25.02.98 cg, new, Time in minutes for 'auto off'
|
||
|
|
||
|
// 25.02.98 cg, new, Ticks for 01.01.1970 00:00:00
|
||
|
#define UNIX_0_TIME 0x0001cf2e8f800000
|
||
|
|
||
|
// 25.02.98 cg, new, Ticks for 'auto off'
|
||
|
#define OFF_TIME ((LONGLONG) (AUTO_OFF * 60) << 13)
|
||
|
|
||
|
// 30.09.98 cg, new, memory address for clock and auto off
|
||
|
// S(X) = 0x70052-0x70070, G(X) = 0x80058-0x80076
|
||
|
#define RPLTIME ((cCurrentRomType=='S')?0x52:0x58)
|
||
|
|
||
|
#define T1_FREQ 62 // 10.11.98 cg, new, Timer1 1/frequency in ms
|
||
|
#define T2_FREQ 8192 // 06.04.98 cg, new, Timer2 frequency
|
||
|
|
||
|
// 27.02.98 cg, new, Timer definitions
|
||
|
|
||
|
// Timer addresses without mapping offset
|
||
|
#define TIMER1_CTRL 0x2e // Timer1 Control
|
||
|
#define TIMER2_CTRL 0x2f // Timer2 Control
|
||
|
#define TIMER1 0x37 // Timer1 (4 bit)
|
||
|
#define TIMER2 0x3f // Timer2 (32 bit, LSB first)
|
||
|
|
||
|
// 0x2e Timer1 Control [SRQ WKE INT XTRA]
|
||
|
#define SRQ 0x08 // Service request
|
||
|
#define WKE 0x04 // Wake up
|
||
|
#define INT 0x02 // Interrupt
|
||
|
#define XTRA 0x01 // Extra function
|
||
|
|
||
|
// 0x2f Timer2 Control [SRQ WKE INT RUN]
|
||
|
#define SRQ 0x08 // Service request
|
||
|
#define WKE 0x04 // Wake up
|
||
|
#define INT 0x02 // Interrupt
|
||
|
#define RUN 0x01 // Timer run
|
||
|
|
||
|
// BOOL bAccurateTimer = TRUE; // 10.11.98 cg, removed, not adjustable any more
|
||
|
// UINT uT1Period = 62; // 10.11.98 cg, removed, not adjustable any more
|
||
|
|
||
|
static BOOL bStarted = FALSE;
|
||
|
static BOOL bOutRange = FALSE; // 21.04.98 cg, new, flag if timer value out of range
|
||
|
static UINT uT1TimerId = 0;
|
||
|
static UINT uT2TimerId = 0;
|
||
|
// static DWORD dwT1Ticks = 0; // 19.02.98 cg, removed, not used
|
||
|
// static DWORD dwT2Init = 0; // 06.04.98 cg, removed, not used any more
|
||
|
// static DWORD dwT2Step = 0; // 06.04.98 cg, removed, not used any more
|
||
|
// static DWORD dwT2Ticks = 0; // 06.04.98 cg, removed, not used any more
|
||
|
|
||
|
static BOOL bAccurateTimer; // 10.11.98 cg, new, flag if accurate timer is used
|
||
|
static LARGE_INTEGER lT2Ref; // 06.04.98 cg, new, counter value at timer2 start
|
||
|
static TIMECAPS tc; // 21.04.98 cg, new, timer information
|
||
|
|
||
|
static __inline MAX(int a, int b) {return (a>b)?a:b;}
|
||
|
|
||
|
static void CALLBACK TimeProc(UINT uEventId, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2);
|
||
|
|
||
|
static VOID SetAccesstime(VOID) // 06.10.97 cg, new, set date and time
|
||
|
{
|
||
|
SYSTEMTIME ts;
|
||
|
LONGLONG ticks, time;
|
||
|
DWORD dw;
|
||
|
WORD crc, i;
|
||
|
BYTE p[4];
|
||
|
|
||
|
_ASSERT(sizeof(LONGLONG) == 8); // check size of datatype
|
||
|
|
||
|
GetLocalTime(&ts); // local time, _ftime() cause memory/resource leaks
|
||
|
|
||
|
// calculate days until 01.01.1970
|
||
|
dw = (DWORD) ts.wMonth;
|
||
|
if (dw > 2)
|
||
|
dw -= 3L;
|
||
|
else
|
||
|
{
|
||
|
dw += 9L;
|
||
|
--ts.wYear;
|
||
|
}
|
||
|
dw = (DWORD) ts.wDay + (153L * dw + 2L) / 5L;
|
||
|
dw += (146097L * (((DWORD) ts.wYear) / 100L)) / 4L;
|
||
|
dw += (1461L * (((DWORD) ts.wYear) % 100L)) / 4L;
|
||
|
dw -= 719469L;
|
||
|
|
||
|
// convert into seconds and add time
|
||
|
dw = dw * 24L + (DWORD) ts.wHour;
|
||
|
dw = dw * 60L + (DWORD) ts.wMinute;
|
||
|
dw = dw * 60L + (DWORD) ts.wSecond;
|
||
|
|
||
|
// create timerticks = (s + ms) * 8192
|
||
|
ticks = ((LONGLONG) dw << 13) | (((LONGLONG) ts.wMilliseconds << 10) / 125);
|
||
|
|
||
|
ticks += UNIX_0_TIME; // add offset ticks from year 0
|
||
|
ticks += (LONG) Chipset.t2; // add actual timer2 value
|
||
|
|
||
|
time = ticks; // save for calc. timeout
|
||
|
time += OFF_TIME; // add 10 min for auto off
|
||
|
|
||
|
dw = RPLTIME; // 30.09.98, bugfix, HP addresses for clock in port0
|
||
|
|
||
|
crc = 0x0; // reset crc value
|
||
|
for (i = 0; i < 13; ++i, ++dw) // write date and time
|
||
|
{
|
||
|
*p = (BYTE) ticks & 0xf;
|
||
|
crc = (crc >> 4) ^ (((crc ^ ((WORD) *p)) & 0xf) * 0x1081);
|
||
|
Chipset.Port0[dw] = *p; // 30.09.98, bugfix, always store in port0
|
||
|
ticks >>= 4;
|
||
|
}
|
||
|
|
||
|
Nunpack(p,crc,4); // write crc
|
||
|
memcpy(Chipset.Port0+dw,p,4); // 30.09.98, bugfix, always store in port0
|
||
|
|
||
|
dw += 4; // HP addresses for timeout
|
||
|
|
||
|
for (i = 0; i < 13; ++i, ++dw) // write time for auto off
|
||
|
{
|
||
|
// 30.09.98, bugfix, always store in port0
|
||
|
Chipset.Port0[dw] = (BYTE) time & 0xf;
|
||
|
time >>= 4;
|
||
|
}
|
||
|
|
||
|
Chipset.Port0[dw] = 0xf; // 30.09.98, bugfix, always store in port0
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static DWORD CalcT2(VOID) // 21.04.98 cg, new, calculate timer2 value
|
||
|
{
|
||
|
DWORD dwT2 = Chipset.t2; // get value from chipset
|
||
|
if (bStarted) // timer2 running
|
||
|
{
|
||
|
LARGE_INTEGER lT2Act;
|
||
|
QueryPerformanceCounter(&lT2Act); // actual time
|
||
|
// calculate ticks since reference point
|
||
|
dwT2 -= (DWORD)
|
||
|
(((lT2Act.QuadPart - lT2Ref.QuadPart) * T2_FREQ)
|
||
|
/ lFreq.QuadPart);
|
||
|
}
|
||
|
return dwT2;
|
||
|
}
|
||
|
|
||
|
static VOID CheckT1(BYTE nT1) // 25.11.98 cg, bugfix, changed implementation
|
||
|
{
|
||
|
if ((nT1&8) == 0) // timer1 MSB not set
|
||
|
{
|
||
|
Chipset.IORam[TIMER1_CTRL] &= ~SRQ; // clear SRQ bit
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
_ASSERT((nT1&8) != 0); // timer1 MSB set
|
||
|
|
||
|
// timer MSB is one and either INT or WAKE is set
|
||
|
if ( (Chipset.IORam[TIMER1_CTRL]&WKE)
|
||
|
|| (Chipset.IORam[TIMER1_CTRL]&INT))
|
||
|
Chipset.IORam[TIMER1_CTRL] |= SRQ; // set SRQ
|
||
|
// cpu not sleeping and T1 -> Interrupt
|
||
|
if ( (!Chipset.Shutdn || (Chipset.IORam[TIMER1_CTRL]&WKE))
|
||
|
&& (Chipset.IORam[TIMER1_CTRL]&INT))
|
||
|
{
|
||
|
Chipset.SoftInt = TRUE;
|
||
|
bInterrupt = TRUE;
|
||
|
}
|
||
|
// cpu sleeping and T1 -> Wake Up
|
||
|
if (Chipset.Shutdn && (Chipset.IORam[TIMER1_CTRL]&WKE))
|
||
|
{
|
||
|
Chipset.IORam[TIMER1_CTRL] &= ~WKE; // clear WKE bit
|
||
|
ResumeThread(hThread); // wake up emulation thread
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static VOID CheckT2(DWORD dwT2) // 25.11.98 cg, bugfix, changed implementation
|
||
|
{
|
||
|
if ((dwT2&0x80000000) == 0) // timer2 MSB not set
|
||
|
{
|
||
|
Chipset.IORam[TIMER2_CTRL] &= ~SRQ; // clear SRQ bit
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
_ASSERT((dwT2&0x80000000) != 0); // timer2 MSB set
|
||
|
|
||
|
// timer MSB is one and either INT or WAKE is set
|
||
|
if ( (Chipset.IORam[TIMER2_CTRL]&WKE)
|
||
|
|| (Chipset.IORam[TIMER2_CTRL]&INT))
|
||
|
Chipset.IORam[TIMER2_CTRL] |= SRQ; // set SRQ
|
||
|
// cpu not sleeping and T2 -> Interrupt
|
||
|
if ( (!Chipset.Shutdn || (Chipset.IORam[TIMER2_CTRL]&WKE))
|
||
|
&& (Chipset.IORam[TIMER2_CTRL]&INT))
|
||
|
{
|
||
|
Chipset.SoftInt = TRUE;
|
||
|
bInterrupt = TRUE;
|
||
|
}
|
||
|
// cpu sleeping and T2 -> Wake Up
|
||
|
if (Chipset.Shutdn && (Chipset.IORam[TIMER2_CTRL]&WKE))
|
||
|
{
|
||
|
Chipset.IORam[TIMER2_CTRL] &= ~WKE; // clear WKE bit
|
||
|
ResumeThread(hThread); // wake up emulation thread
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static VOID RescheduleT2(BOOL bRefPoint) // 21.04.98 cg, add function parameter
|
||
|
{
|
||
|
UINT uDelay;
|
||
|
_ASSERT(uT2TimerId == 0); // timer2 must stopped
|
||
|
// 29.09.98 cg, bugfix, changed implementation
|
||
|
if (bRefPoint) // save reference time
|
||
|
{
|
||
|
QueryPerformanceCounter(&lT2Ref); // time of corresponding Chipset.t2 value
|
||
|
uDelay = Chipset.t2; // timer value for delay
|
||
|
}
|
||
|
else // called without new refpoint, restart t2 with actual value
|
||
|
{
|
||
|
uDelay = CalcT2(); // actual timer value for delay
|
||
|
}
|
||
|
uDelay &= 0x7FFFFFFF; // 24.11.98 cg, bugfix, execute timer2 event when MSB change
|
||
|
uDelay >>= 3; // timer delay in ms
|
||
|
uDelay = MAX(tc.wPeriodMin,uDelay); // wait minimum delay of timer
|
||
|
if (bOutRange = uDelay > tc.wPeriodMax) // delay greater maximum delay
|
||
|
uDelay = tc.wPeriodMax; // wait maximum delay time
|
||
|
// 29.09.98 cg, end of changed implementation
|
||
|
// dwT2Init = timeGetTime(); // 06.04.98 cg, not used any more
|
||
|
// dwT2Ticks = dwT2Init; // 06.04.98 cg, not used any more
|
||
|
// start timer2; schedule event, when Chipset.t2 will be zero (Chipset.t2 / 8 = time in ms)
|
||
|
uT2TimerId = timeSetEvent(uDelay,0,(LPTIMECALLBACK)&TimeProc,2,TIME_ONESHOT);
|
||
|
_ASSERT(uT2TimerId); // 21.04.98 cg, new, test if timer2 started
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static VOID AbortT2(VOID)
|
||
|
{
|
||
|
_ASSERT(uT2TimerId);
|
||
|
timeKillEvent(uT2TimerId); // 09.10.97 cg, bugfix, first kill event
|
||
|
uT2TimerId = 0; // 09.10.97 cg, bugfix, then reset var
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static void CALLBACK TimeProc(UINT uEventId, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
|
||
|
{
|
||
|
_ASSERT(uEventId); // 16.03.98 cg, new, illegal EventId
|
||
|
if (!bStarted) // timer stopped
|
||
|
return; // -> quit
|
||
|
// called from timer1 event (default period 16 Hz)
|
||
|
// if ((uT1TimerId!=0) && (uEventId == uT1TimerId))
|
||
|
if (uEventId == uT1TimerId) // 16.03.98 cg, removed useless part
|
||
|
{
|
||
|
EnterCriticalSection(&csT1Lock); // 21.04.98 cg, bugfix, synchronize
|
||
|
{
|
||
|
Chipset.t1 = (Chipset.t1-1)&0xF;// decrement timer value
|
||
|
CheckT1(Chipset.t1); // test timer1 control bits
|
||
|
}
|
||
|
LeaveCriticalSection(&csT1Lock); // 21.04.98 cg
|
||
|
return;
|
||
|
}
|
||
|
// called from timer2 event, Chipset.t2 should be zero
|
||
|
// if ((uT2TimerId!=0) && (uEventId == uT2TimerId))
|
||
|
if (uEventId == uT2TimerId) // 16.03.98 cg, removed useless part
|
||
|
{
|
||
|
EnterCriticalSection(&csT2Lock); // 21.04.98 cg, bugfix, synchronize
|
||
|
{
|
||
|
uT2TimerId = 0; // 30.11.98 cg, bugfix, single shot timer timer2 stopped
|
||
|
if (!bOutRange) // 21.04.98 cg, timer event elapsed
|
||
|
{
|
||
|
// timer2 overrun, test timer2 control bits else restart timer2
|
||
|
// 16.11.98 cg, bugfix, don't wait for timer2 overrun any more
|
||
|
Chipset.t2 = CalcT2(); // calculate new timer2 value
|
||
|
CheckT2(Chipset.t2); // test timer2 control bits
|
||
|
}
|
||
|
RescheduleT2(!bOutRange); // restart timer2
|
||
|
}
|
||
|
LeaveCriticalSection(&csT2Lock); // 21.04.98 cg
|
||
|
return;
|
||
|
}
|
||
|
return;
|
||
|
UNREFERENCED_PARAMETER(uMsg);
|
||
|
UNREFERENCED_PARAMETER(dwUser); // 19.02.98 cg, added unreferenced parameter declarations
|
||
|
UNREFERENCED_PARAMETER(dw1);
|
||
|
UNREFERENCED_PARAMETER(dw2);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID StartTimers(VOID)
|
||
|
{
|
||
|
if (bStarted) // timer running
|
||
|
return; // -> quit
|
||
|
if (Chipset.IORam[TIMER2_CTRL]&RUN) // start timer1 and timer2 ?
|
||
|
{
|
||
|
bStarted = TRUE; // flag timer running
|
||
|
SetAccesstime(); // 06.10.97 cg, set date and time
|
||
|
// 10.11.98 cg, changed, use always accurate timer
|
||
|
// if (bAccurateTimer) // box "Accurate timer" checked
|
||
|
// {
|
||
|
// 21.04.98 cg, optimized, set timer resolution to 1 ms, if failed don't use "Accurate timer"
|
||
|
bAccurateTimer = (timeBeginPeriod(1) == TIMERR_NOERROR);
|
||
|
// }
|
||
|
timeGetDevCaps(&tc,sizeof(tc)); // 21.04.98 cg, get timer resolution
|
||
|
// set timer1 with given period
|
||
|
// 10.11.98 cg, changed, use fix event time
|
||
|
uT1TimerId = timeSetEvent(T1_FREQ,0,(LPTIMECALLBACK)&TimeProc,1,TIME_PERIODIC);
|
||
|
_ASSERT(uT1TimerId); // 16.03.98 cg, new, test if timer1 started
|
||
|
RescheduleT2(TRUE); // start timer2
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VOID StopTimers(VOID)
|
||
|
{
|
||
|
if (!bStarted) // timer stopped
|
||
|
return; // -> quit
|
||
|
// Chipset.t2 = ReadT2(); // 21.04.98 cg, removed, read timer2 value later
|
||
|
if (uT1TimerId != 0) // timer1 running
|
||
|
{
|
||
|
// 02.12.98 cg, bugfix, Critical Section handler may cause a dead lock
|
||
|
timeKillEvent(uT1TimerId); // stop timer1
|
||
|
uT1TimerId = 0; // set flag timer1 stopped
|
||
|
// 02.12.98 cg, end of bugfix
|
||
|
}
|
||
|
if (uT2TimerId != 0) // timer2 running
|
||
|
{
|
||
|
EnterCriticalSection(&csT2Lock); // 21.04.98 cg, bugfix, synchronize
|
||
|
{
|
||
|
Chipset.t2 = CalcT2(); // 21.04.98 cg, update chipset timer2 value
|
||
|
// AbortT2(); // 02.12.98 cg, removed, stop timer2 later
|
||
|
}
|
||
|
LeaveCriticalSection(&csT2Lock); // 21.04.98 cg
|
||
|
AbortT2(); // 02.12.98 cg, bugfix, stop timer2 here
|
||
|
}
|
||
|
bStarted = FALSE;
|
||
|
if (bAccurateTimer) // "Accurate timer" running
|
||
|
{
|
||
|
timeEndPeriod(1); // finish service
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
DWORD ReadT2(VOID)
|
||
|
{
|
||
|
// 21.04.98 cg, changed implementation
|
||
|
DWORD dwT2;
|
||
|
EnterCriticalSection(&csT2Lock);
|
||
|
{
|
||
|
// 16.12.98 cg, bugfix, calculate timer2 value or if stopped last timer value
|
||
|
dwT2 = bStarted ? CalcT2() : Chipset.t2;
|
||
|
CheckT2(dwT2); // 25.11.98 cg, new, update timer2 control bits
|
||
|
}
|
||
|
LeaveCriticalSection(&csT2Lock);
|
||
|
return dwT2;
|
||
|
}
|
||
|
|
||
|
VOID SetT2(DWORD dwValue)
|
||
|
{
|
||
|
// calling AbortT2() inside Critical Section handler may cause a dead lock
|
||
|
if (uT2TimerId != 0) // 02.12.98 cg, bugfix, timer2 running
|
||
|
AbortT2(); // 02.12.98 cg, bugfix, stop timer2
|
||
|
// 21.04.98 cg, changed implementation
|
||
|
EnterCriticalSection(&csT2Lock);
|
||
|
{
|
||
|
// if (uT2TimerId != 0) // 02.12.98 cg, removed, done before
|
||
|
// AbortT2(); // 02.12.98 cg, removed, done before
|
||
|
Chipset.t2 = dwValue; // set new value
|
||
|
CheckT2(Chipset.t2); // 25.11.98 cg, readded, test timer2 control bits
|
||
|
if (bStarted) // timer running
|
||
|
RescheduleT2(TRUE); // restart timer2
|
||
|
}
|
||
|
LeaveCriticalSection(&csT2Lock);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
BYTE ReadT1(VOID)
|
||
|
{
|
||
|
BYTE nT1;
|
||
|
EnterCriticalSection(&csT1Lock); // 21.04.98 cg, bugfix, synchronize
|
||
|
{
|
||
|
nT1 = Chipset.t1; // read timer1 value
|
||
|
CheckT1(nT1); // 25.11.98 cg, new, update timer1 control bits
|
||
|
}
|
||
|
LeaveCriticalSection(&csT1Lock); // 21.04.98 cg
|
||
|
return nT1;
|
||
|
}
|
||
|
|
||
|
VOID SetT1(BYTE byValue)
|
||
|
{
|
||
|
timeKillEvent(uT1TimerId); // 11.06.98 cg, bugfix, stop timer1
|
||
|
uT1TimerId = 0; // 11.06.98 cg, new, set flag timer1 stopped
|
||
|
EnterCriticalSection(&csT1Lock); // 21.04.98 cg, bugfix, synchronize
|
||
|
{
|
||
|
Chipset.t1 = byValue&0xF; // set new timer1 value
|
||
|
CheckT1(Chipset.t1); // 25.11.98 cg, readded, test timer1 control bits
|
||
|
}
|
||
|
LeaveCriticalSection(&csT1Lock); // 21.04.98 cg
|
||
|
// 11.06.98 cg, bugfix, restart timer1
|
||
|
// 10.11.98 cg, changed, use fix event time
|
||
|
uT1TimerId = timeSetEvent(T1_FREQ,0,(LPTIMECALLBACK)&TimeProc,1,TIME_PERIODIC);
|
||
|
_ASSERT(uT1TimerId); // 11.06.98 cg, new, test if timer1 started
|
||
|
return;
|
||
|
}
|