emu48-mirror/sources/Emu48/TIMER.C

380 lines
11 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"
2024-03-19 22:25:45 +01:00
#include "io.h" // 24.10.99 cg, new, I/O definitions
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
#define AUTO_OFF 10 // Time in minutes for 'auto off'
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
// Ticks for 01.01.1970 00:00:00
2024-03-19 22:24:30 +01:00
#define UNIX_0_TIME 0x0001cf2e8f800000
2024-03-19 22:25:45 +01:00
// Ticks for 'auto off'
2024-03-19 22:24:30 +01:00
#define OFF_TIME ((LONGLONG) (AUTO_OFF * 60) << 13)
2024-03-19 22:25:45 +01:00
// memory address for clock and auto off
// S(X) = 0x70052-0x70070, G(X) = 0x80058-0x80076, 49G = 0x80058-0x80076
2024-03-19 22:24:30 +01:00
#define RPLTIME ((cCurrentRomType=='S')?0x52:0x58)
2024-03-19 22:25:45 +01:00
#define T1_FREQ 62 // Timer1 1/frequency in ms
#define T2_FREQ 8192 // Timer2 frequency
2024-03-19 22:24:30 +01:00
static BOOL bStarted = FALSE;
2024-03-19 22:25:45 +01:00
static BOOL bOutRange = FALSE; // flag if timer value out of range
2024-03-19 22:24:30 +01:00
static UINT uT1TimerId = 0;
static UINT uT2TimerId = 0;
2024-03-19 22:25:45 +01:00
static BOOL bNINT2T1 = FALSE; // 24.10.99 cg, new, state of NINT2 affected form Timer1
static BOOL bNINT2T2 = FALSE; // 24.10.99 cg, new, state of NINT2 affected form Timer2
static BOOL bAccurateTimer; // flag if accurate timer is used
static LARGE_INTEGER lT2Ref; // counter value at timer2 start
static TIMECAPS tc; // timer information
2024-03-19 22:24:30 +01:00
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);
2024-03-19 22:25:45 +01:00
static DWORD CalcT2(VOID) // calculate timer2 value
2024-03-19 22:24:30 +01:00
{
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;
}
2024-03-19 22:25:45 +01:00
static VOID CheckT1(BYTE nT1)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
// 24.10.99 cg, bugfix, implementation of TSRQ
bNINT2T1 = (Chipset.IORam[TIMER1_CTRL]&INTR) != 0 && (nT1&8) != 0;
IOBit(SRQ1,TSRQ,bNINT2T1 || bNINT2T2);
// 24.10.99 cg, end of implementation
2024-03-19 22:24:30 +01:00
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)
2024-03-19 22:25:45 +01:00
|| (Chipset.IORam[TIMER1_CTRL]&INTR))
2024-03-19 22:24:30 +01:00
Chipset.IORam[TIMER1_CTRL] |= SRQ; // set SRQ
// cpu not sleeping and T1 -> Interrupt
if ( (!Chipset.Shutdn || (Chipset.IORam[TIMER1_CTRL]&WKE))
2024-03-19 22:25:45 +01:00
&& (Chipset.IORam[TIMER1_CTRL]&INTR))
2024-03-19 22:24:30 +01:00
{
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
2024-03-19 22:25:45 +01:00
Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode
SetEvent(hEventShutdn); // wake up emulation thread
2024-03-19 22:24:30 +01:00
}
return;
}
2024-03-19 22:25:45 +01:00
static VOID CheckT2(DWORD dwT2)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
// 24.10.99 cg, bugfix, implementation of TSRQ
bNINT2T2 = (Chipset.IORam[TIMER2_CTRL]&INTR) != 0 && (dwT2&0x80000000) != 0;
IOBit(SRQ1,TSRQ,bNINT2T1 || bNINT2T2);
// 24.10.99 cg, end of implementation
2024-03-19 22:24:30 +01:00
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)
2024-03-19 22:25:45 +01:00
|| (Chipset.IORam[TIMER2_CTRL]&INTR))
2024-03-19 22:24:30 +01:00
Chipset.IORam[TIMER2_CTRL] |= SRQ; // set SRQ
// cpu not sleeping and T2 -> Interrupt
if ( (!Chipset.Shutdn || (Chipset.IORam[TIMER2_CTRL]&WKE))
2024-03-19 22:25:45 +01:00
&& (Chipset.IORam[TIMER2_CTRL]&INTR))
2024-03-19 22:24:30 +01:00
{
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
2024-03-19 22:25:45 +01:00
Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode
SetEvent(hEventShutdn); // wake up emulation thread
2024-03-19 22:24:30 +01:00
}
return;
}
2024-03-19 22:25:45 +01:00
static VOID RescheduleT2(BOOL bRefPoint)
2024-03-19 22:24:30 +01:00
{
UINT uDelay;
_ASSERT(uT2TimerId == 0); // timer2 must stopped
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
}
2024-03-19 22:25:45 +01:00
uDelay &= 0x7FFFFFFF; // execute timer2 event when MSB change
2024-03-19 22:24:30 +01:00
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
// 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);
2024-03-19 22:25:45 +01:00
_ASSERT(uT2TimerId); // test if timer2 started
2024-03-19 22:24:30 +01:00
return;
}
static VOID AbortT2(VOID)
{
_ASSERT(uT2TimerId);
2024-03-19 22:25:45 +01:00
timeKillEvent(uT2TimerId); // kill event
uT2TimerId = 0; // then reset var
2024-03-19 22:24:30 +01:00
return;
}
static void CALLBACK TimeProc(UINT uEventId, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
2024-03-19 22:25:45 +01:00
_ASSERT(uEventId); // illegal EventId
2024-03-19 22:24:30 +01:00
if (!bStarted) // timer stopped
return; // -> quit
2024-03-19 22:25:45 +01:00
if (uEventId == uT1TimerId) // called from timer1 event (default period 16 Hz)
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
EnterCriticalSection(&csT1Lock);
2024-03-19 22:24:30 +01:00
{
Chipset.t1 = (Chipset.t1-1)&0xF;// decrement timer value
CheckT1(Chipset.t1); // test timer1 control bits
}
2024-03-19 22:25:45 +01:00
LeaveCriticalSection(&csT1Lock);
2024-03-19 22:24:30 +01:00
return;
}
2024-03-19 22:25:45 +01:00
if (uEventId == uT2TimerId) // called from timer2 event, Chipset.t2 should be zero
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
EnterCriticalSection(&csT2Lock);
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
uT2TimerId = 0; // single shot timer timer2 stopped
if (!bOutRange) // timer event elapsed
2024-03-19 22:24:30 +01:00
{
// timer2 overrun, test timer2 control bits else restart timer2
Chipset.t2 = CalcT2(); // calculate new timer2 value
CheckT2(Chipset.t2); // test timer2 control bits
}
RescheduleT2(!bOutRange); // restart timer2
}
2024-03-19 22:25:45 +01:00
LeaveCriticalSection(&csT2Lock);
2024-03-19 22:24:30 +01:00
return;
}
return;
UNREFERENCED_PARAMETER(uMsg);
2024-03-19 22:25:45 +01:00
UNREFERENCED_PARAMETER(dwUser);
2024-03-19 22:24:30 +01:00
UNREFERENCED_PARAMETER(dw1);
UNREFERENCED_PARAMETER(dw2);
}
2024-03-19 22:25:45 +01:00
VOID SetHP48Time(VOID) // 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; // 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; // always store in port0
ticks >>= 4;
}
Nunpack(p,crc,4); // write crc
memcpy(Chipset.Port0+dw,p,4); // always store in port0
dw += 4; // HP addresses for timeout
for (i = 0; i < 13; ++i, ++dw) // write time for auto off
{
// always store in port0
Chipset.Port0[dw] = (BYTE) time & 0xf;
time >>= 4;
}
Chipset.Port0[dw] = 0xf; // always store in port0
return;
}
2024-03-19 22:24:30 +01:00
VOID StartTimers(VOID)
{
if (bStarted) // timer running
return; // -> quit
if (Chipset.IORam[TIMER2_CTRL]&RUN) // start timer1 and timer2 ?
{
bStarted = TRUE; // flag timer running
2024-03-19 22:25:45 +01:00
// 24.10.99 cg, new, initialisation of NINT2 lines
bNINT2T1 = (Chipset.IORam[TIMER1_CTRL]&INTR) != 0 && (Chipset.t1 & 8) != 0;
bNINT2T2 = (Chipset.IORam[TIMER2_CTRL]&INTR) != 0 && (Chipset.t2 & 0x80000000) != 0;
// 24.10.99 cg, end of initialisation
// set timer resolution to 1 ms, if failed don't use "Accurate timer"
2024-03-19 22:24:30 +01:00
bAccurateTimer = (timeBeginPeriod(1) == TIMERR_NOERROR);
2024-03-19 22:25:45 +01:00
timeGetDevCaps(&tc,sizeof(tc)); // get timer resolution
2024-03-19 22:24:30 +01:00
// set timer1 with given period
uT1TimerId = timeSetEvent(T1_FREQ,0,(LPTIMECALLBACK)&TimeProc,1,TIME_PERIODIC);
2024-03-19 22:25:45 +01:00
_ASSERT(uT1TimerId); // test if timer1 started
2024-03-19 22:24:30 +01:00
RescheduleT2(TRUE); // start timer2
}
return;
}
VOID StopTimers(VOID)
{
if (!bStarted) // timer stopped
return; // -> quit
if (uT1TimerId != 0) // timer1 running
{
2024-03-19 22:25:45 +01:00
// Critical Section handler may cause a dead lock
2024-03-19 22:24:30 +01:00
timeKillEvent(uT1TimerId); // stop timer1
uT1TimerId = 0; // set flag timer1 stopped
}
if (uT2TimerId != 0) // timer2 running
{
2024-03-19 22:25:45 +01:00
EnterCriticalSection(&csT2Lock);
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
Chipset.t2 = CalcT2(); // update chipset timer2 value
// AbortT2(); // removed, stop timer2 later
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
LeaveCriticalSection(&csT2Lock);
AbortT2(); // stop timer2 here
2024-03-19 22:24:30 +01:00
}
bStarted = FALSE;
if (bAccurateTimer) // "Accurate timer" running
{
timeEndPeriod(1); // finish service
}
return;
}
DWORD ReadT2(VOID)
{
DWORD dwT2;
EnterCriticalSection(&csT2Lock);
{
2024-03-19 22:25:45 +01:00
// calculate timer2 value or if stopped last timer value
2024-03-19 22:24:30 +01:00
dwT2 = bStarted ? CalcT2() : Chipset.t2;
2024-03-19 22:25:45 +01:00
CheckT2(dwT2); // update timer2 control bits
2024-03-19 22:24:30 +01:00
}
LeaveCriticalSection(&csT2Lock);
return dwT2;
}
VOID SetT2(DWORD dwValue)
{
// calling AbortT2() inside Critical Section handler may cause a dead lock
2024-03-19 22:25:45 +01:00
if (uT2TimerId != 0) // timer2 running
AbortT2(); // stop timer2
2024-03-19 22:24:30 +01:00
EnterCriticalSection(&csT2Lock);
{
Chipset.t2 = dwValue; // set new value
2024-03-19 22:25:45 +01:00
CheckT2(Chipset.t2); // test timer2 control bits
2024-03-19 22:24:30 +01:00
if (bStarted) // timer running
RescheduleT2(TRUE); // restart timer2
}
LeaveCriticalSection(&csT2Lock);
return;
}
BYTE ReadT1(VOID)
{
BYTE nT1;
2024-03-19 22:25:45 +01:00
EnterCriticalSection(&csT1Lock);
2024-03-19 22:24:30 +01:00
{
nT1 = Chipset.t1; // read timer1 value
2024-03-19 22:25:45 +01:00
CheckT1(nT1); // update timer1 control bits
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
LeaveCriticalSection(&csT1Lock);
2024-03-19 22:24:30 +01:00
return nT1;
}
VOID SetT1(BYTE byValue)
{
2024-03-19 22:25:45 +01:00
_ASSERT(byValue < 0x10); // timer1 is only a 4bit counter
if (Chipset.t1 == byValue) // same value doesn't restart timer period
return;
timeKillEvent(uT1TimerId); // stop timer1
uT1TimerId = 0; // set flag timer1 stopped
EnterCriticalSection(&csT1Lock);
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
Chipset.t1 = byValue; // set new timer1 value
CheckT1(Chipset.t1); // test timer1 control bits
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
LeaveCriticalSection(&csT1Lock);
// restart timer1 to get full period of frequency
2024-03-19 22:24:30 +01:00
uT1TimerId = timeSetEvent(T1_FREQ,0,(LPTIMECALLBACK)&TimeProc,1,TIME_PERIODIC);
2024-03-19 22:25:45 +01:00
_ASSERT(uT1TimerId); // test if timer1 started
2024-03-19 22:24:30 +01:00
return;
}