Preliminary update to the GameCube to GBA link cable emulation. Fixes Zelda Wind Waker's Tingle Tuner connection, Pac-Man Vs, Final Fantasy: Crystal Chronicles multiplayer, and most other Gamecube to GBA link cable games.
* Added a second socket at port 49420 (0xc10c) which sends clock information * Handled disconnections from the GBA and GC * Made the transfers asynchronous * Blocks the socket before the connection times out Requires Dolphin 4.0-5899 or later. git-svn-id: https://svn.code.sf.net/p/vbam/code/trunk@1235 a31d4220-a93d-0410-bf67-fe4944624d44
This commit is contained in:
parent
1ac06a6e66
commit
8b0b049230
|
@ -3016,10 +3016,11 @@ void CPUUpdateRegister(u32 address, u16 value)
|
||||||
break;
|
break;
|
||||||
case COMM_JOY_TRANS_H:
|
case COMM_JOY_TRANS_H:
|
||||||
UPDATE_REG(COMM_JOY_TRANS_H, value);
|
UPDATE_REG(COMM_JOY_TRANS_H, value);
|
||||||
|
UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) | JOYSTAT_SEND);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMM_JOYSTAT:
|
case COMM_JOYSTAT:
|
||||||
UPDATE_REG(COMM_JOYSTAT, (READ16LE(&ioMem[COMM_JOYSTAT]) & 0xf) | (value & 0xf0));
|
UPDATE_REG(COMM_JOYSTAT, (READ16LE(&ioMem[COMM_JOYSTAT]) & 0x0a) | (value & ~0x0a));
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -3590,8 +3591,8 @@ void CPULoop(int ticks)
|
||||||
|
|
||||||
#ifndef NO_LINK
|
#ifndef NO_LINK
|
||||||
// shuffle2: what's the purpose?
|
// shuffle2: what's the purpose?
|
||||||
if(gba_link_enabled)
|
//if(gba_link_enabled)
|
||||||
cpuNextEvent = 1;
|
//cpuNextEvent = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cpuBreakLoop = false;
|
cpuBreakLoop = false;
|
||||||
|
@ -4040,12 +4041,12 @@ void CPULoop(int ticks)
|
||||||
|
|
||||||
ticks -= clockTicks;
|
ticks -= clockTicks;
|
||||||
|
|
||||||
if (gba_joybus_enabled)
|
|
||||||
JoyBusUpdate(clockTicks);
|
|
||||||
|
|
||||||
#ifndef NO_LINK
|
#ifndef NO_LINK
|
||||||
if (gba_link_enabled)
|
if (gba_link_enabled)
|
||||||
LinkUpdate(clockTicks);
|
LinkUpdate(clockTicks);
|
||||||
|
|
||||||
|
if (gba_joybus_enabled)
|
||||||
|
JoyBusUpdate(clockTicks);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cpuNextEvent = CPUUpdateTicks();
|
cpuNextEvent = CPUUpdateTicks();
|
||||||
|
@ -4063,7 +4064,7 @@ void CPULoop(int ticks)
|
||||||
|
|
||||||
#ifndef NO_LINK
|
#ifndef NO_LINK
|
||||||
// shuffle2: what's the purpose?
|
// shuffle2: what's the purpose?
|
||||||
if(gba_link_enabled)
|
if(gba_link_enabled || gba_joybus_active)
|
||||||
cpuNextEvent = 1;
|
cpuNextEvent = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ const char *MakeInstanceFilename(const char *Input)
|
||||||
|
|
||||||
// Joybus
|
// Joybus
|
||||||
bool gba_joybus_enabled = false;
|
bool gba_joybus_enabled = false;
|
||||||
|
bool gba_joybus_active = false;
|
||||||
|
|
||||||
// If disabled, gba core won't call any (non-joybus) link functions
|
// If disabled, gba core won't call any (non-joybus) link functions
|
||||||
bool gba_link_enabled = false;
|
bool gba_link_enabled = false;
|
||||||
|
@ -388,30 +389,84 @@ void JoyBusShutdown()
|
||||||
dol = NULL;
|
dol = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const u64 TICKS_PER_FRAME = 16777216 / 60;
|
||||||
|
const u64 TICKS_PER_SECOND = 16777216;
|
||||||
|
const u64 BITS_PER_SECOND = 115200;
|
||||||
|
const u64 BYTES_PER_SECOND = BITS_PER_SECOND / 8;
|
||||||
|
|
||||||
|
static u32 lastjoybusupdate = 0;
|
||||||
|
static u32 nextjoybusupdate = 0;
|
||||||
|
static u32 lastcommand = 0;
|
||||||
|
static bool booted = false;
|
||||||
|
|
||||||
void JoyBusUpdate(int ticks)
|
void JoyBusUpdate(int ticks)
|
||||||
{
|
{
|
||||||
linktime += ticks;
|
lastjoybusupdate += ticks;
|
||||||
static int lastjoybusupdate = 0;
|
lastcommand += ticks;
|
||||||
|
|
||||||
// Kinda ugly hack to update joybus stuff intermittently
|
bool joybus_activated = ((READ16LE(&ioMem[COMM_RCNT])) >> 14) == 3;
|
||||||
if (linktime > lastjoybusupdate + 0x3000)
|
gba_joybus_active = dol && gba_joybus_enabled && joybus_activated;
|
||||||
|
|
||||||
|
if ((lastjoybusupdate > nextjoybusupdate))
|
||||||
{
|
{
|
||||||
lastjoybusupdate = linktime;
|
if (!joybus_activated)
|
||||||
|
{
|
||||||
|
if (dol && booted)
|
||||||
|
{
|
||||||
|
JoyBusShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastjoybusupdate = 0;
|
||||||
|
nextjoybusupdate = 0;
|
||||||
|
lastcommand = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dol)
|
||||||
|
{
|
||||||
|
booted = false;
|
||||||
|
JoyBusConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
dol->ReceiveClock(false);
|
||||||
|
|
||||||
|
if (dol->IsDisconnected())
|
||||||
|
{
|
||||||
|
JoyBusShutdown();
|
||||||
|
nextjoybusupdate = TICKS_PER_SECOND * 2; // try to connect after 2 seconds
|
||||||
|
lastjoybusupdate = 0;
|
||||||
|
lastcommand = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dol->ClockSync(lastjoybusupdate);
|
||||||
|
|
||||||
char data[5] = { 0x10, 0, 0, 0, 0 }; // init with invalid cmd
|
char data[5] = { 0x10, 0, 0, 0, 0 }; // init with invalid cmd
|
||||||
std::vector<char> resp;
|
std::vector<char> resp;
|
||||||
|
u8 cmd = 0x10;
|
||||||
|
|
||||||
if (!dol)
|
if (lastcommand > (TICKS_PER_FRAME * 4))
|
||||||
JoyBusConnect();
|
{
|
||||||
|
cmd = dol->ReceiveCmd(data, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmd = dol->ReceiveCmd(data, false);
|
||||||
|
}
|
||||||
|
|
||||||
u8 cmd = dol->ReceiveCmd(data);
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case JOY_CMD_RESET:
|
case JOY_CMD_RESET:
|
||||||
UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_RESET);
|
UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_RESET);
|
||||||
|
resp.push_back(0x00); // GBA device ID
|
||||||
|
resp.push_back(0x04);
|
||||||
|
nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND;
|
||||||
|
break;
|
||||||
|
|
||||||
case JOY_CMD_STATUS:
|
case JOY_CMD_STATUS:
|
||||||
resp.push_back(0x00); // GBA device ID
|
resp.push_back(0x00); // GBA device ID
|
||||||
resp.push_back(0x04);
|
resp.push_back(0x04);
|
||||||
|
|
||||||
|
nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JOY_CMD_READ:
|
case JOY_CMD_READ:
|
||||||
|
@ -419,8 +474,10 @@ void JoyBusUpdate(int ticks)
|
||||||
resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_L]) >> 8));
|
resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_L]) >> 8));
|
||||||
resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_H]) & 0xff));
|
resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_H]) & 0xff));
|
||||||
resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_H]) >> 8));
|
resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_H]) >> 8));
|
||||||
UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) & ~JOYSTAT_SEND);
|
|
||||||
UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_SEND_COMPLETE);
|
UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_SEND_COMPLETE);
|
||||||
|
nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND;
|
||||||
|
booted = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JOY_CMD_WRITE:
|
case JOY_CMD_WRITE:
|
||||||
|
@ -428,13 +485,24 @@ void JoyBusUpdate(int ticks)
|
||||||
UPDATE_REG(COMM_JOY_RECV_H, (u16)((u16)data[4] << 8) | (u8)data[3]);
|
UPDATE_REG(COMM_JOY_RECV_H, (u16)((u16)data[4] << 8) | (u8)data[3]);
|
||||||
UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) | JOYSTAT_RECV);
|
UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) | JOYSTAT_RECV);
|
||||||
UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_RECV_COMPLETE);
|
UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_RECV_COMPLETE);
|
||||||
|
nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND;
|
||||||
|
booted = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
nextjoybusupdate = TICKS_PER_SECOND / 40000;
|
||||||
|
lastjoybusupdate = 0;
|
||||||
return; // ignore
|
return; // ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastjoybusupdate = 0;
|
||||||
resp.push_back((u8)READ16LE(&ioMem[COMM_JOYSTAT]));
|
resp.push_back((u8)READ16LE(&ioMem[COMM_JOYSTAT]));
|
||||||
|
|
||||||
|
if (cmd == JOY_CMD_READ)
|
||||||
|
{
|
||||||
|
UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) & ~JOYSTAT_SEND);
|
||||||
|
}
|
||||||
|
|
||||||
dol->Send(resp);
|
dol->Send(resp);
|
||||||
|
|
||||||
// Generate SIO interrupt if we can
|
// Generate SIO interrupt if we can
|
||||||
|
@ -444,6 +512,8 @@ void JoyBusUpdate(int ticks)
|
||||||
IF |= 0x80;
|
IF |= 0x80;
|
||||||
UPDATE_REG(0x202, IF);
|
UPDATE_REG(0x202, IF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastcommand = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,6 +521,9 @@ static void ReInitLink();
|
||||||
|
|
||||||
void LinkUpdate(int ticks)
|
void LinkUpdate(int ticks)
|
||||||
{
|
{
|
||||||
|
if (((READ16LE(&ioMem[COMM_RCNT])) >> 14) == 3)
|
||||||
|
return;
|
||||||
|
|
||||||
// this actually gets called every single instruction, so keep default
|
// this actually gets called every single instruction, so keep default
|
||||||
// path as short as possible
|
// path as short as possible
|
||||||
|
|
||||||
|
|
|
@ -145,6 +145,7 @@ typedef struct {
|
||||||
} LANLINKDATA;
|
} LANLINKDATA;
|
||||||
|
|
||||||
extern bool gba_joybus_enabled;
|
extern bool gba_joybus_enabled;
|
||||||
|
extern bool gba_joybus_active;
|
||||||
extern bool gba_link_enabled;
|
extern bool gba_link_enabled;
|
||||||
|
|
||||||
extern sf::IPAddress joybusHostAddr;
|
extern sf::IPAddress joybusHostAddr;
|
||||||
|
|
|
@ -12,14 +12,23 @@ GBASockClient::GBASockClient(sf::IPAddress _server_addr)
|
||||||
server_addr = _server_addr;
|
server_addr = _server_addr;
|
||||||
|
|
||||||
client.Connect(0xd6ba, server_addr);
|
client.Connect(0xd6ba, server_addr);
|
||||||
//client.SetBlocking(false);
|
client.SetBlocking(false);
|
||||||
|
|
||||||
|
clock_client.Connect(0xc10c, server_addr);
|
||||||
|
clock_client.SetBlocking(false);
|
||||||
|
|
||||||
|
clock_sync = 0;
|
||||||
|
is_disconnected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GBASockClient::~GBASockClient()
|
GBASockClient::~GBASockClient()
|
||||||
{
|
{
|
||||||
client.Close();
|
client.Close();
|
||||||
|
clock_client.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 clock_sync_ticks = 0;
|
||||||
|
|
||||||
void GBASockClient::Send(std::vector<char> data)
|
void GBASockClient::Send(std::vector<char> data)
|
||||||
{
|
{
|
||||||
char* plain_data = new char[data.size()];
|
char* plain_data = new char[data.size()];
|
||||||
|
@ -31,12 +40,60 @@ void GBASockClient::Send(std::vector<char> data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns cmd for convenience
|
// Returns cmd for convenience
|
||||||
char GBASockClient::ReceiveCmd(char* data_in)
|
char GBASockClient::ReceiveCmd(char* data_in, bool block)
|
||||||
{
|
{
|
||||||
std::size_t num_received;
|
if (IsDisconnected())
|
||||||
client.Receive(data_in, 5, num_received);
|
return data_in[0];
|
||||||
|
|
||||||
|
std::size_t num_received = 0;
|
||||||
|
if (block || clock_sync == 0)
|
||||||
|
{
|
||||||
|
sf::SelectorTCP Selector;
|
||||||
|
Selector.Add(client);
|
||||||
|
Selector.Wait(6);
|
||||||
|
}
|
||||||
|
if (client.Receive(data_in, 5, num_received) == sf::Socket::Disconnected)
|
||||||
|
Disconnect();
|
||||||
|
|
||||||
return data_in[0];
|
return data_in[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GBASockClient::ReceiveClock(bool block)
|
||||||
|
{
|
||||||
|
if (IsDisconnected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
char sync_ticks[4] = { 0, 0, 0, 0 };
|
||||||
|
std::size_t num_received = 0;
|
||||||
|
if (clock_client.Receive(sync_ticks, 4, num_received) == sf::Socket::Disconnected)
|
||||||
|
Disconnect();
|
||||||
|
|
||||||
|
if (num_received == 4)
|
||||||
|
{
|
||||||
|
clock_sync_ticks = 0;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
clock_sync_ticks |= (u8)(sync_ticks[i]) << ((3 - i) * 8);
|
||||||
|
clock_sync += clock_sync_ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBASockClient::ClockSync(u32 ticks)
|
||||||
|
{
|
||||||
|
if (clock_sync > (s32)ticks)
|
||||||
|
clock_sync -= (s32)ticks;
|
||||||
|
else
|
||||||
|
clock_sync = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBASockClient::Disconnect()
|
||||||
|
{
|
||||||
|
is_disconnected = true;
|
||||||
|
client.Close();
|
||||||
|
clock_client.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GBASockClient::IsDisconnected()
|
||||||
|
{
|
||||||
|
return !client.IsValid() || !clock_client.IsValid() || is_disconnected;
|
||||||
|
}
|
||||||
#endif // NO_LINK
|
#endif // NO_LINK
|
||||||
|
|
|
@ -10,9 +10,18 @@ public:
|
||||||
~GBASockClient();
|
~GBASockClient();
|
||||||
|
|
||||||
void Send(std::vector<char> data);
|
void Send(std::vector<char> data);
|
||||||
char ReceiveCmd(char* data_in);
|
char ReceiveCmd(char* data_in, bool block);
|
||||||
|
void ReceiveClock(bool block);
|
||||||
|
|
||||||
|
void ClockSync(u32 ticks);
|
||||||
|
void Disconnect();
|
||||||
|
bool IsDisconnected();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sf::IPAddress server_addr;
|
sf::IPAddress server_addr;
|
||||||
sf::SocketTCP client;
|
sf::SocketTCP client;
|
||||||
|
sf::SocketTCP clock_client;
|
||||||
|
|
||||||
|
s32 clock_sync;
|
||||||
|
bool is_disconnected;
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,6 +33,8 @@ extern bool skipBios;
|
||||||
extern int frameSkip;
|
extern int frameSkip;
|
||||||
extern bool speedup;
|
extern bool speedup;
|
||||||
extern bool synchronize;
|
extern bool synchronize;
|
||||||
|
extern bool gba_joybus_enabled;
|
||||||
|
extern bool gba_joybus_active;
|
||||||
extern bool cpuDisableSfx;
|
extern bool cpuDisableSfx;
|
||||||
extern bool cpuIsMultiBoot;
|
extern bool cpuIsMultiBoot;
|
||||||
extern bool parseDebug;
|
extern bool parseDebug;
|
||||||
|
|
|
@ -180,7 +180,7 @@ void Direct3DDisplay::prepareDisplayMode()
|
||||||
dpp.hDeviceWindow = theApp.m_pMainWnd->GetSafeHwnd();
|
dpp.hDeviceWindow = theApp.m_pMainWnd->GetSafeHwnd();
|
||||||
dpp.FullScreen_RefreshRateInHz = ( dpp.Windowed == TRUE ) ? 0 : theApp.fsFrequency;
|
dpp.FullScreen_RefreshRateInHz = ( dpp.Windowed == TRUE ) ? 0 : theApp.fsFrequency;
|
||||||
dpp.Flags = 0;
|
dpp.Flags = 0;
|
||||||
dpp.PresentationInterval = theApp.vsync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
|
dpp.PresentationInterval = (theApp.vsync && !gba_joybus_active) ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
|
||||||
// D3DPRESENT_INTERVAL_ONE means VSync ON
|
// D3DPRESENT_INTERVAL_ONE means VSync ON
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -240,7 +240,7 @@ void DirectSound::write(u16 * finalWave, int length)
|
||||||
LPVOID lpvPtr2;
|
LPVOID lpvPtr2;
|
||||||
DWORD dwBytes2 = 0;
|
DWORD dwBytes2 = 0;
|
||||||
|
|
||||||
if( !speedup && synchronize && !theApp.throttle ) {
|
if( !speedup && synchronize && !theApp.throttle && !gba_joybus_active) {
|
||||||
hr = dsbSecondary->GetStatus(&status);
|
hr = dsbSecondary->GetStatus(&status);
|
||||||
if( status & DSBSTATUS_PLAYING ) {
|
if( status & DSBSTATUS_PLAYING ) {
|
||||||
if( !soundPaused ) {
|
if( !soundPaused ) {
|
||||||
|
|
|
@ -1218,7 +1218,7 @@ void MainWnd::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
theApp.wasPaused = true;
|
theApp.wasPaused = true;
|
||||||
if(theApp.pauseWhenInactive) {
|
if(theApp.pauseWhenInactive && !gba_joybus_active) {
|
||||||
if(emulating) {
|
if(emulating) {
|
||||||
soundPause();
|
soundPause();
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,7 +281,7 @@ void OpenAL::write(u16 * finalWave, int length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !speedup && synchronize && !theApp.throttle ) {
|
if( !speedup && synchronize && !theApp.throttle && !gba_joybus_active) {
|
||||||
// wait until at least one buffer has finished
|
// wait until at least one buffer has finished
|
||||||
while( nBuffersProcessed == 0 ) {
|
while( nBuffersProcessed == 0 ) {
|
||||||
winlog( " waiting...\n" );
|
winlog( " waiting...\n" );
|
||||||
|
|
|
@ -336,7 +336,7 @@ bool OpenGLDisplay::initialize()
|
||||||
|
|
||||||
initializeMatrices( theApp.surfaceSizeX, theApp.surfaceSizeY );
|
initializeMatrices( theApp.surfaceSizeX, theApp.surfaceSizeY );
|
||||||
|
|
||||||
setVSync( theApp.vsync );
|
setVSync( theApp.vsync && !gba_joybus_active );
|
||||||
|
|
||||||
#ifdef MMX
|
#ifdef MMX
|
||||||
if(!theApp.disableMMX)
|
if(!theApp.disableMMX)
|
||||||
|
|
|
@ -2706,7 +2706,7 @@ void Sm60FPS_Sleep()
|
||||||
if( theApp.autoFrameSkip ) {
|
if( theApp.autoFrameSkip ) {
|
||||||
u32 dwTimePass = Sm60FPS::dwTimeElapse + (GetTickCount() - Sm60FPS::dwTime0);
|
u32 dwTimePass = Sm60FPS::dwTimeElapse + (GetTickCount() - Sm60FPS::dwTime0);
|
||||||
u32 dwTimeShould = (u32)(Sm60FPS::nFrameCnt * Sm60FPS::K_fDT);
|
u32 dwTimeShould = (u32)(Sm60FPS::nFrameCnt * Sm60FPS::K_fDT);
|
||||||
if( dwTimeShould > dwTimePass ) {
|
if (dwTimeShould > dwTimePass && !gba_joybus_active) {
|
||||||
Sleep(dwTimeShould - dwTimePass);
|
Sleep(dwTimeShould - dwTimePass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -451,7 +451,7 @@ void XAudio2_Output::write(u16 * finalWave, int length)
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// the maximum number of buffers is currently queued
|
// the maximum number of buffers is currently queued
|
||||||
if( synchronize && !speedup && !theApp.throttle ) {
|
if( synchronize && !speedup && !theApp.throttle && !gba_joybus_active ) {
|
||||||
// wait for one buffer to finish playing
|
// wait for one buffer to finish playing
|
||||||
if (WaitForSingleObject( notify.hBufferEndEvent, 10000 ) == WAIT_TIMEOUT) {
|
if (WaitForSingleObject( notify.hBufferEndEvent, 10000 ) == WAIT_TIMEOUT) {
|
||||||
device_changed = true;
|
device_changed = true;
|
||||||
|
|
Loading…
Reference in New Issue