emu48-mirror/sources/Emu48/TIMER.C

404 lines
13 KiB
C
Raw Normal View History

2024-03-19 22:24:30 +01:00
/*
* 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;
}