Virtual and real timers
This commit is contained in:
parent
b61339f0ea
commit
6aafb25dee
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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");
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue