Virtual and real timers

This commit is contained in:
ergo720 2018-05-17 01:07:40 +02:00
parent b61339f0ea
commit 6aafb25dee
7 changed files with 255 additions and 12 deletions

View File

@ -332,6 +332,7 @@
<ClInclude Include="..\..\src\CxbxKrnl\PoolManager.h" />
<ClInclude Include="..\..\src\CxbxKrnl\ReservedMemory.h" />
<ClInclude Include="..\..\src\CxbxKrnl\ResourceTracker.h" />
<ClInclude Include="..\..\src\CxbxKrnl\Timer.h" />
<ClInclude Include="..\..\src\CxbxKrnl\VMManager.h" />
<ClInclude Include="..\..\src\CxbxVersion.h" />
<ClInclude Include="..\..\src\Cxbx\CxbxXbdm.h" />
@ -636,6 +637,7 @@
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug_Direct3D9|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="..\..\src\CxbxKrnl\Timer.cpp" />
<ClCompile Include="..\..\src\CxbxKrnl\VMManager.cpp" />
<ClCompile Include="..\..\src\Cxbx\CxbxXbdm.cpp" />
<ClCompile Include="..\..\src\Cxbx\DlgAbout.cpp" />

View File

@ -286,7 +286,11 @@
</ClCompile>
<ClCompile Include="..\..\src\devices\USBController\OHCI.cpp">
<Filter>Hardware</Filter>
</ClCompile> <ClCompile Include="..\..\src\Cxbx\DlgXboxControllerPortMapping.cpp">
</ClCompile>
<ClCompile Include="..\..\src\CxbxKrnl\Timer.cpp">
<Filter>Emulator</Filter>
</ClCompile>
<ClCompile Include="..\..\src\Cxbx\DlgXboxControllerPortMapping.cpp">
<Filter>GUI</Filter>
</ClCompile>
<ClCompile Include="..\..\src\Common\Win32\XBPortMapping.cpp">
@ -575,6 +579,9 @@
</ClInclude>
<ClInclude Include="..\..\src\devices\USBController\OHCI.h">
<Filter>Hardware</Filter>
</ClInclude>
<ClInclude Include="..\..\src\CxbxKrnl\Timer.h">
<Filter>Emulator</Filter>
</ClInclude>
<ClInclude Include="..\..\src\Cxbx\DlgXboxControllerPortMapping.h">
<Filter>GUI</Filter>

170
src/CxbxKrnl/Timer.cpp Normal file
View File

@ -0,0 +1,170 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * .,-::::: .,:: .::::::::. .,:: .:
// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;;
// * [[[ '[[,,[[' [[[__[[\. '[[,,[['
// * $$$ Y$$$P $$""""Y$$ Y$$$P
// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo,
// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm,
// *
// * Cxbx->CxbxKrnl->Timer.cpp
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2018 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#include <windows.h>
#include <thread>
#include <vector>
#include "Timer.h"
#define CLOCK_REALTIME 0
#define CLOCK_VIRTUALTIME 1
#define SCALE_S 1000000000ULL
#define SCALE_MS 1000000ULL
#define SCALE_US 1000ULL
#define SCALE_NS 1ULL
// Vector storing all the timers created
static std::vector<TimerObject*> TimerList;
// The frequency of the high resolution timer of the host
static uint64_t ClockFrequency;
// Disable a compiler warning relative to uint64_t -> uint32_t conversions in Muldiv64. This function is taken from
// XQEMU so it should be safe regardless
#pragma warning(push)
#pragma warning(disable: 4244)
// Compute (a*b)/c with a 96 bit intermediate result
static inline uint64_t Muldiv64(uint64_t a, uint32_t b, uint32_t c)
{
union {
uint64_t ll;
struct {
uint32_t low, high;
} l;
} u, res;
uint64_t rl, rh;
u.ll = a;
rl = (uint64_t)u.l.low * (uint64_t)b;
rh = (uint64_t)u.l.high * (uint64_t)b;
rh += (rl >> 32);
res.l.high = rh / c;
res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
return res.ll;
}
// Returns the current time of the timer
static inline uint64_t GetTime_NS(TimerObject* Timer)
{
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
uint64_t Ret = Muldiv64(li.QuadPart, SCALE_S, ClockFrequency);
return Timer->Type == CLOCK_REALTIME ? Ret : Ret / Timer->SlowdownFactor;
}
#pragma warning(pop)
// Calculates the next expire time of the timer
static inline uint64_t GetNextExpireTime(TimerObject* Timer)
{
return GetTime_NS(Timer) + Timer->ExpireTime_MS.load() * SCALE_MS;
}
// Deallocates the memory of the timer
void Timer_Destroy(TimerObject* Timer)
{
unsigned int index;
for (unsigned int i = 0; i < TimerList.size(); i++) {
if (Timer == TimerList[i]) {
index = i;
}
}
delete Timer;
TimerList.erase(TimerList.begin() + index);
}
// Thread that runs the timer
void ClockThread(TimerObject* Timer)
{
uint64_t NewExpireTime = GetNextExpireTime(Timer);
while (true) {
if (GetTime_NS(Timer) > NewExpireTime) {
Timer->Callback(Timer->Opaque);
NewExpireTime = GetNextExpireTime(Timer);
}
if (Timer->Exit.load()) {
Timer_Destroy(Timer);
return;
}
}
}
// Changes the expire time of a timer
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms)
{
Timer->ExpireTime_MS.store(Expire_ms);
}
// Destroys the timer
void Timer_Exit(TimerObject* Timer)
{
Timer->Exit.store(true);
}
// Allocates the memory for the timer object
TimerObject* Timer_Create(pTimerCB Callback, void* Arg, unsigned int Factor)
{
TimerObject* pTimer = new TimerObject;
pTimer->Type = Factor <= 1 ? CLOCK_REALTIME : CLOCK_VIRTUALTIME;
pTimer->Callback = Callback;
pTimer->ExpireTime_MS.store(0);
pTimer->Exit.store(false);
pTimer->Opaque = Arg;
pTimer->SlowdownFactor = Factor < 1 ? 1 : Factor;
TimerList.emplace_back(pTimer);
return pTimer;
}
// Starts the timer
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS)
{
Timer->ExpireTime_MS.store(Expire_MS);
std::thread(ClockThread, Timer).detach();
}
// Retrives the frequency of the high resolution timer of the host
void Timer_Init()
{
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
ClockFrequency = freq.QuadPart;
}

63
src/CxbxKrnl/Timer.h Normal file
View File

@ -0,0 +1,63 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * .,-::::: .,:: .::::::::. .,:: .:
// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;;
// * [[[ '[[,,[[' [[[__[[\. '[[,,[['
// * $$$ Y$$$P $$""""Y$$ Y$$$P
// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo,
// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm,
// *
// * Cxbx->CxbxKrnl->Timer.h
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2018 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#ifndef TIMER_H
#define TIMER_H
#include <atomic>
/* typedef of the timer object and the callback function*/
typedef void(*pTimerCB)(void*);
typedef struct _TimerObject
{
int Type; // timer type (virtual or real)
std::atomic_uint64_t ExpireTime_MS; // when the timer expires (ms)
std::atomic_bool Exit; // indicates that the timer should be destroyed
pTimerCB Callback; // function to call when the timer expires
void* Opaque; // opaque argument to pass to the callback
unsigned int SlowdownFactor; // how much the time is slowed down (virtual clocks only)
}
TimerObject;
/* Timer exported functions */
TimerObject* Timer_Create(pTimerCB callback, void* arg, unsigned int Factor);
void Timer_Start(TimerObject* timer, uint64_t Expire_MS);
void Timer_Exit(TimerObject* Timer);
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms);
void Timer_Init();
#endif

View File

@ -42,7 +42,7 @@ OHCI_State* g_pHostController1 = nullptr;
OHCI_State* g_pHostController2 = nullptr;
void OHCI_State::HC_Reset(USB_State new_state)
void OHCI_State::HC_ChangeState(USB_State new_state)
{
// The usb state can be USB_Suspend if it is a software reset, and USB_Reset if it is a hardware
// reset or cold boot
@ -59,8 +59,8 @@ void OHCI_State::HC_Reset(USB_State new_state)
else {
HC_Registers.HcControl &= (OHCI_CTL_IR | OHCI_CTL_RWC);
}
HC_Registers.HcControl = ~OHCI_CTL_HCFS;
HC_Registers.HcControl = new_state;
HC_Registers.HcControl &= ~OHCI_CTL_HCFS;
HC_Registers.HcControl |= new_state;
HC_Registers.HcCommandStatus = 0;
HC_Registers.HcInterruptStatus = 0;
HC_Registers.HcInterruptEnable = OHCI_INTR_MASTER_INTERRUPT_ENABLED; // enable interrupts
@ -73,7 +73,6 @@ void OHCI_State::HC_Reset(USB_State new_state)
HC_Registers.HcFmInterval = 0;
HC_Registers.HcFmInterval |= (0x2778 << 16); // TBD according to the standard, using what XQEMU sets (FSLargestDataPacket)
HC_Registers.HcFmInterval |= (0 << 31); // redundant, but we'll do it for the sake of completeness (FrameIntervalToggle)
HC_Registers.HcFmInterval |= 0x2EDF; // bit-time of a frame. 1 frame = 1 ms (FrameInterval)
HC_Registers.HcFmRemaining = 0;
HC_Registers.HcFmNumber = 0;
@ -90,5 +89,5 @@ void OHCI_State::HC_Reset(USB_State new_state)
//}
}
DbgPrintf("usb-ohci: Reset\n");
DbgPrintf("Usb-Ohci: Reset\n");
}

View File

@ -140,7 +140,7 @@ class OHCI_State
// write a register
void HC_WriteRegister(xbaddr addr, uint32_t value);
// reset the HC to the default state
void HC_Reset(USB_State new_state);
void HC_ChangeState(USB_State new_state);
private:
// all the registers available on the OHCI standard

View File

@ -51,12 +51,14 @@ void USBDevice::Init(unsigned int address)
m_DeviceId = 0x01C2;
m_VendorId = PCI_VENDOR_ID_NVIDIA;
g_pHostController1 = new OHCI_State;
g_pHostController2 = new OHCI_State;
if (address == USB0_BASE) {
g_pHostController1 = new OHCI_State;
g_pHostController1->HC_ChangeState(USB_Reset);
return;
}
// We can use the USB_Reset state to also cold boot the HC during initialization
g_pHostController1->HC_Reset(USB_Reset);
g_pHostController2->HC_Reset(USB_Reset);
g_pHostController2 = new OHCI_State;
g_pHostController2->HC_ChangeState(USB_Reset);
}
uint32_t USBDevice::MMIORead(int barIndex, uint32_t addr, unsigned size)