From ed7e1247b1f7f7b7895cd2ace79aa32ecee30d88 Mon Sep 17 00:00:00 2001 From: bgk Date: Mon, 1 Oct 2012 16:55:36 +0000 Subject: [PATCH] LINK: Add AdaMN's work for reference git-svn-id: https://svn.code.sf.net/p/vbam/code/branches/adamn-link@1140 a31d4220-a93d-0410-bf67-fe4944624d44 --- project/vs2010_mfc/VBA2010.suo | Bin 20992 -> 77312 bytes project/vs2010_mfc/VBA2010.vcxproj | 16 +- src/System.h | 4 + src/apu/Gb_Apu.cpp | 4 + src/apu/Gb_Oscs.cpp | 25 +- src/apu/Gb_Oscs.h | 4 +- src/gb/GB.cpp | 170 +- src/gb/gbGlobals.h | 2 + src/gba/GBA-arm.cpp | 2 +- src/gba/GBA.cpp | 387 +++- src/gba/GBA.h | 6 + src/gba/GBALink.cpp | 2962 ++++++++++++++++++++++++++-- src/gba/GBALink.h | 110 +- src/gba/Globals.h | 2 + src/gba/Sound.cpp | 4 +- src/gba/bios.cpp | 32 +- src/win32/DirectSound.cpp | 18 +- src/win32/Disassemble.cpp | 47 +- src/win32/Disassemble.h | 11 +- src/win32/LinkOptions.cpp | 39 +- src/win32/Logging.cpp | 22 + src/win32/Logging.h | 4 + src/win32/MainWndOptions.cpp | 53 +- src/win32/OpenAL.cpp | 19 +- src/win32/Throttle.cpp | 4 +- src/win32/VBA.cpp | 176 +- src/win32/VBA.h | 23 + src/win32/VBA.rc | 79 +- src/win32/XAudio2.cpp | 4 +- src/win32/resource.h | 7 + 30 files changed, 3834 insertions(+), 402 deletions(-) diff --git a/project/vs2010_mfc/VBA2010.suo b/project/vs2010_mfc/VBA2010.suo index 279d74938e69b33a74ef08036f1f4ce37ab25bc4..d40db4c038291951819fcf6612fcfa56e37f80a2 100644 GIT binary patch literal 77312 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 delta 2347 zcmbuAeN0nV6u{r5ujTPwO2H1RRw+=lU$g~^GNH88FQB;S(1k=xDS`t*XbmVzF>F(} zDfFn_)QMBa5;mQtE>Sm=60^9?CDY8r#4O8}>?1DIm<9ikMA=G8CDHDO4_cSmeE?Ys5 zNku~%LNXykIRlX+G&u{Ig^-UgA=wDcQ^tCd_d>7aGwCX{R3pb@BVdhJvJXJ&DykZ32duX<;JK1y}h(aWWd@(3`X}bXCJl zNvI7X)PN;ez><6eq@4l;d`eIWieL&aLeGGW+u>xX1!RIA&GKNXk4eFw+AmJWg|+Y~ z^}0Y`20v)@?c&)etO3_nGtO4V^QZ-I#Owi+1*<}Z&aYP-h{Nt~_FjfjTEdkBQ?3qwt+X&0oA+w? zmARe#(}?7(6CL52;N~tHT-cWlqd5-Ixo32%`Zdwmw4Tt!dGU05xh>)T+&K(3K32pZ z3xWSs`o1|=*NO9V=2O>8_oVN9mdFo@1>fQ?Rd zJjY3JZ1i7;5f2@t+7+dW^{Z=S2;ntggtN4ufPyUp9y(dq3nw0Af9Ticop zCZo{|0|Pl=yd8jmr9u>76zjF{=_N(Z3T_L=(~k+R=R7ENau1d%G}#YLqilig%9nRbp$A5Pd+w6C8Ez#w}1214>%*zbwS`>=l-8A9Gg zFkPZ^Xx=c&K?IP4$RXq~5<=cX-baogN0DR52MDbjiS-|1f08QUDHKj4A0ea2Sp<$1 ztJsracaHQDYL^jON(~+q8;6^I4q5}L;zGSqUx;Iz+vjNsrAwJsay+^-h*%@g=vFe$rfZdXzMb*nTz*5U$KZN zc^atqwRAOkI=Z~kH^H?>Da9|AL>~KTfBnmuJMRr@YqQf!$Wa+w?Y=#@wU7)`s!D`87p1UBjDJDCP!_B+7rvjBM z$&H!f!OF9NtAUZydd$vfgEQl$l&zS#68PoT0P|t~y1=zW4M%P_gQ`?dPiGvYIrD?g z2q^C;pvR}is?S4UWj*{fJ{SDUe&F-)X;db{zU6Cpo^JSPp_-_!6SSz%HAM#T4irYA zw`P@4fD#q%>3KDE0#&Qj8H2C3j$cSh^h&LDO;>dCIy&FyLJQWgXo(>U+YJw0wb6@r a49(xtZB18Vh-sNEdQYi@dCw{Ntn43^8?$Ht diff --git a/project/vs2010_mfc/VBA2010.vcxproj b/project/vs2010_mfc/VBA2010.vcxproj index ca192463..926eab79 100644 --- a/project/vs2010_mfc/VBA2010.vcxproj +++ b/project/vs2010_mfc/VBA2010.vcxproj @@ -24,7 +24,7 @@ Application Static - NotSet + MultiByte true @@ -49,13 +49,13 @@ <_ProjectFileVersion>10.0.30319.1 $(ProjectDir)$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)_temp\ - true + false false true false $(ProjectDir)$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)_temp\ - true + false AllRules.ruleset @@ -66,9 +66,9 @@ AllRules.ruleset - C:\Program Files (x86)\OpenAL 1.1 SDK\libs\Win32;C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86;$(LibraryPath) + %programfiles%\OpenAL 1.1 SDK\libs\Win32;$(DXSDK_DIR)\lib\x86;$(LibraryPath) C:\Program Files (x86)\OpenAL 1.1 SDK\libs\Win32;C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86;$(LibraryPath) - C:\Program Files (x86)\OpenAL 1.1 SDK\include;C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Include;$(IncludePath) + %programfiles%\OpenAL 1.1 SDK\include;$(DXSDK_DIR)\Include;$(IncludePath) C:\Program Files (x86)\OpenAL 1.1 SDK\include;C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Include;$(IncludePath) %programfiles%\OpenAL 1.1 SDK\include;$(DXSDK_DIR)\Include;$(IncludePath) %programfiles%\OpenAL 1.1 SDK\libs\Win32;$(DXSDK_DIR)\lib\x86;$(LibraryPath) @@ -100,7 +100,7 @@ false - $(IntDir)$(ProjectName).pdb + $(IntDir)vc$(PlatformToolsetVersion).pdb Level3 EditAndContinue @@ -124,6 +124,7 @@ 5.0 + $(OutDir)$(TargetName).pdb @@ -177,6 +178,7 @@ true true false + $(OutDir)$(TargetName).pdb 1 @@ -709,7 +711,7 @@ - + \ No newline at end of file diff --git a/src/System.h b/src/System.h index 43e6a200..5ee133f5 100644 --- a/src/System.h +++ b/src/System.h @@ -61,6 +61,7 @@ extern int systemGetSensorY(); extern bool systemCanChangeSoundQuality(); extern void systemShowSpeed(int); extern void system10Frames(int); +extern void system1Frames(int rate, int count); extern void systemFrame(); extern void systemGbBorderOn(); @@ -86,6 +87,9 @@ extern int systemFrameSkip; extern int systemSaveUpdateCounter; extern int systemSpeed; +extern int lastSA; +extern int lastSR; + #define SYSTEM_SAVE_UPDATED 30 #define SYSTEM_SAVE_NOT_UPDATED 0 diff --git a/src/apu/Gb_Apu.cpp b/src/apu/Gb_Apu.cpp index f86b29fb..e96f76bc 100644 --- a/src/apu/Gb_Apu.cpp +++ b/src/apu/Gb_Apu.cpp @@ -14,6 +14,7 @@ License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" +#include "../system.h" unsigned const vol_reg = 0xFF24; unsigned const stereo_reg = 0xFF25; @@ -317,6 +318,9 @@ void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) { int old_data = regs [reg]; regs [reg] = data; + + //if(lastSA>=0x70 && lastSA<=0x75) + //log("CH3 Addr:%04X = %04X Regs[%d]:%02X->%02X\n",lastSA,lastSR,reg,old_data,data); if ( addr < vol_reg ) { diff --git a/src/apu/Gb_Oscs.cpp b/src/apu/Gb_Oscs.cpp index b77cbad2..5e6853ed 100644 --- a/src/apu/Gb_Oscs.cpp +++ b/src/apu/Gb_Oscs.cpp @@ -38,6 +38,7 @@ inline void Gb_Osc::update_amp( blip_time_t time, int new_amp ) { last_amp = new_amp; med_synth->offset( time, delta, output ); + //good_synth->offset( time, delta, output ); } } @@ -280,12 +281,12 @@ inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int switch ( reg ) { - case 0: + case 0: //NR30 if ( !dac_enabled() ) enabled = false; break; - case 1: + case 1: //NR31 length_ctr = max_len - data; break; @@ -571,9 +572,9 @@ void Gb_Noise::run( blip_time_t time, blip_time_t end_time ) void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) { // Calc volume - static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; + static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; //4 = 100% int const volume_shift = 2; - int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB + int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB //AdamN: osc.regs[2]=apu.regs[12] (NR32) int const volume_mul = volumes [volume_idx]; // Determine what will be generated @@ -585,10 +586,10 @@ void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) if ( dac_enabled() ) { // Play inaudible frequencies as constant amplitude - amp = 8 << 4; // really depends on average of all samples in wave + amp = 8 << 4; // really depends on average of all samples in wave //AdamN: can we use this->last_amp instead? // if delay is larger, constant amplitude won't start yet - if ( frequency() <= 0x7FB || delay > 15 * clk_mul ) + if ( frequency() <= 0x7FB || delay > 15 * clk_mul ) //2043 { if ( volume_mul ) playing = (int) enabled; @@ -609,13 +610,14 @@ void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) // wave size and bank int const size20_mask = 0x20; - int const flags = regs [0] & agb_mask; - int const wave_mask = (flags & size20_mask) | 0x1F; + int const flags = regs [0] & agb_mask; //AdamN: osc.regs[0]=apu.regs[10] (NR30) + int const wave_mask = (flags & size20_mask) | 0x1F; //AdamN: Bank Dimension int swap_banks = 0; - if ( flags & bank40_mask ) + if ( flags & bank40_mask ) //AdamN: Bank 1 { - swap_banks = flags & size20_mask; + swap_banks = flags & size20_mask; //AdamN: Bank Dimension, swap_banks = Use 2 Banks (64 digits) wave += bank_size/2 - (swap_banks >> 1); + //wave = &this->wave_ram[bank_size/2 - (swap_banks >> 1)]; } int ph = this->phase ^ swap_banks; @@ -640,13 +642,14 @@ void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) ph = (ph + 1) & wave_mask; // Scale by volume - int amp = (nybble * volume_mul) >> (volume_shift + 4); + int amp = (nybble * volume_mul) >> (volume_shift + 4); //AdamN: should we use +dac_bias in here? int delta = amp - lamp; if ( delta ) { lamp = amp; med_synth->offset_inline( time, delta, out ); + //good_synth->offset_inline( time, delta, out ); } time += per; } diff --git a/src/apu/Gb_Oscs.h b/src/apu/Gb_Oscs.h index ba8daa89..126f9fa7 100644 --- a/src/apu/Gb_Oscs.h +++ b/src/apu/Gb_Oscs.h @@ -165,11 +165,11 @@ private: int period() const { return (2048 - frequency()) * (2 * clk_mul); } // Non-zero if DAC is enabled - int dac_enabled() const { return regs [0] & 0x80; } + int dac_enabled() const { return regs [0] & 0x80; } //AdamN: osc.regs[0]=apu.regs[10] void corrupt_wave(); - BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; } + BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; } //AdamN: osc.regs[0]=apu.regs[10] // Wave index that would be accessed, or -1 if no access would occur int access( unsigned addr ) const; diff --git a/src/gb/GB.cpp b/src/gb/GB.cpp index 1543953e..6c91b1d2 100644 --- a/src/gb/GB.cpp +++ b/src/gb/GB.cpp @@ -14,6 +14,8 @@ #include "gbSGB.h" #include "gbSound.h" #include "../Util.h" +#include "../gba/GBALink.h" +//#include "../gba/Globals.h" #ifdef __GNUC__ #define _stricmp strcasecmp @@ -790,16 +792,81 @@ void gbWriteMemory(register u16 address, register u8 value) } case 0x01: { + #ifdef GBA_LOGGING + if(gbMemory[0xff01]!=value) + if(systemVerbose & VERBOSE_SIO) { + log("SioSB(%04X) : %02X %02X->%02X %d\n", PC.W-2, gbMemory[0xff02], gbMemory[0xff01], value, GetTickCount() ); //register_LY + } + #endif gbMemory[0xff01] = value; + //if(value==0xff/*0x00*/) LinkFirstTime = true; return; } // serial control case 0x02: { + #ifdef GBA_LOGGING + if(gbMemory[0xff02]!=value) + if(systemVerbose & VERBOSE_SIO) { + log("SioSC(%04X) : %02X->%02X %02X %d\n", PC.W-2, gbMemory[0xff02], value, gbMemory[0xff01], GetTickCount() ); //register_LY + } + #endif gbSerialOn = (value & 0x80); + //if(gba_link_enabled && lanlink.connected) + if( EmuReseted ||(gbMemory[0xff02] & 0x7c)||(value & 0x7c)||(!(value & 0x81)) ) { //trying to detect whether the game has exited multiplay mode, pokemon blue start w/ 0x7e while pocket racing start w/ 0x7c + LinkFirstTime = true; + if( EmuReseted ||(gbMemory[0xff02] & 0x7c)||(value & 0x7c) ) + if(lanlink.connected) + if(linkid) lc.DiscardData(); + else ls.DiscardData(1); + } + EmuReseted = false; gbMemory[0xff02] = value; if(gbSerialOn) { gbSerialTicks = GBSERIAL_CLOCK_TICKS; + + LinkIsWaiting = true; + + //Do data exchange, master initiate the transfer + //may cause visual artifact if not processed immediately, is it due to IRQ stuff or invalid data being exchanged? + if( (value & 1) /*|| (!LinkFirstTime)*/ ) { //&& !linkid //internal clock + if(gbSerialFunction) { + gbSIO_SC = value; + LogStrPush("gbWrite"); + //if(linkid /*&& (value & 1)*/) {gbMemory[0xff01] = 0xff; LinkFirstTime = true;} else + gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); //gbSerialFunction/gbStartLink/gbPrinter + LogStrPop(7); + } else gbMemory[0xff01]=0xff; + //gbSerialTicks = GBSERIAL_CLOCK_TICKS; //1; //0; //wrong value in gbSerialTicks could cause visual artifact + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + //if(register_IE & 8) + gbMemory[0xff0f] = register_IF |= 8; + //gbSerialBits = 0; + //LinkFirstTime = false; + //if(!(value & 1)) LinkFirstTime = true; + } /*else //external clock + { + u16 dat = 0; + //if(gbSerialFunction) + { + gbSIO_SC = value; + LogStrPush("gbWrite"); + dat = gbLinkUpdate(gbMemory[0xff01]); + gbMemory[0xff01] = (dat >> 8); //gbSerialFunction/gbStartLink/gbPrinter + LogStrPop(7); + } + if(dat & 1) { + //gbSerialTicks = GBSERIAL_CLOCK_TICKS; //1; //0; //wrong value in gbSerialTicks could cause visual artifact + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + //if(register_IE & 8) + gbMemory[0xff0f] = register_IF |= 8; + //gbSerialBits = 0; + //LinkFirstTime = false; + //if(!(value & 1)) LinkFirstTime = true; + } + }*/ #ifdef OLD_GB_LINK if(linkConnected) { if(value & 1) { @@ -2174,6 +2241,8 @@ void gbGetHardwareType() void gbReset() { + EmuReseted = true; + LinkFirstTime = true; gbGetHardwareType(); oldRegister_WY = 146; @@ -4487,6 +4556,8 @@ void gbDrawLine() void gbEmulate(int ticksToStop) { + if(EmuCtr>0) {log("Emu inside Emu:%d\n",EmuCtr);return;} + gbRegister tempRegister; u8 tempValue; s8 offset; @@ -4916,8 +4987,9 @@ void gbEmulate(int ticksToStop) gbFrameCount++; systemFrame(); - if((gbFrameCount % 10) == 0) - system10Frames(60); + /*if((gbFrameCount % 10) == 0) + system10Frames(60);*/ + system1Frames(60, gbFrameCount); if(gbFrameCount >= 60) { u32 currentTime = systemGetClock(); @@ -5186,8 +5258,9 @@ void gbEmulate(int ticksToStop) systemFrame(); - if((gbFrameCount % 10) == 0) - system10Frames(60); + /*if((gbFrameCount % 10) == 0) + system10Frames(60);*/ + system1Frames(60, gbFrameCount); if(gbFrameCount >= 60) { u32 currentTime = systemGetClock(); @@ -5206,7 +5279,33 @@ void gbEmulate(int ticksToStop) gbMemory[0xff41] = register_STAT; // serial emulation - if(gbSerialOn) { + gbSerialOn = (gbMemory[0xff02] & 0x80); + /*if(gba_link_enabled && lanlink.connected && (register_LY>=144)) { + //if(gbSerialOn) + { + u16 dat = 0; + if(gbSerialFunction) { // external device + gbSIO_SC = gbMemory[0xff02]; + LogStrPush("gbEmu"); + if(!LinkFirstTime) dat = (gbSerialFunction(gbMemory[0xff01]) << 8) | 1; else + dat = gbLinkUpdate(gbMemory[0xff01]); + gbMemory[0xff01] = (dat >> 8); + LogStrPop(5); + if(gbSerialOn && (dat & 1)) { + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + //if(register_IE & 8) + gbMemory[0xff0f] = register_IF |= 8; + } + } //else gbMemory[0xff01] = 0xff; + } + gbSerialTicks = 1; //GBSERIAL_CLOCK_TICKS; //0; //seems to cause varies visual artifact if gbSerialTicks value isn't right + gbSerialBits = 0; + }*/ + static int SIOctr = 0; + SIOctr++; + if(!lanlink.speed || (SIOctr % 5)) + if(gbSerialOn) { //Transfer Started #ifdef OLD_GB_LINK if(linkConnected) { gbSerialTicks -= clockTicks; @@ -5229,7 +5328,7 @@ void gbEmulate(int ticksToStop) } } else { #endif - if(gbMemory[0xff02] & 1) { + if(gbMemory[0xff02] & 1) { //internal clocks (master) gbSerialTicks -= clockTicks; // overflow @@ -5238,21 +5337,66 @@ void gbEmulate(int ticksToStop) // gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1); // increment number of shifted bits gbSerialBits++; - if(gbSerialBits == 8) { + if(gbSerialBits >= 8) { // end of transmission - if(gbSerialFunction) // external device - gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); - else - gbMemory[0xff01] = 0xff; - gbSerialTicks = 0; + /*if(gbSerialFunction) { // external device + gbSIO_SC = gbMemory[0xff02]; + LogStrPush("gbEmu"); + gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); //gbStartLik/gbPrinter + LogStrPop(5); + LinkFirstTime = false; + } else + gbMemory[0xff01] = 0xff;*/ + gbSerialTicks = 0; + //if(!linkid) + /*{ gbMemory[0xff02] &= 0x7f; gbSerialOn = 0; gbMemory[0xff0f] = register_IF |= 8; + }*/ gbSerialBits = 0; } else gbSerialTicks += GBSERIAL_CLOCK_TICKS; } - } + } else //external clocks (slave) + { + gbSerialTicks -= clockTicks; + + // overflow + while(gbSerialTicks <= 0) { + // shift serial byte to right and put a 1 bit in its place + // gbMemory[0xff01] = 0x80 | (gbMemory[0xff01]>>1); + // increment number of shifted bits + gbSerialBits++; + if(gbSerialBits >= 8) { + // end of transmission + u16 dat = 0; + if(!LinkIsWaiting) { + if(lanlink.connected) + if(linkid) lc.DiscardData(); + else ls.DiscardData(1); + } else + if(gbSerialFunction) { // external device + gbSIO_SC = gbMemory[0xff02]; + LogStrPush("gbEmu"); + if(!LinkFirstTime) dat = (gbSerialFunction(gbMemory[0xff01]) << 8) | 1; else //external clock not suppose to start a transfer, but there are time where both side using external clock and couldn't communicate properly + dat = gbLinkUpdate(gbMemory[0xff01]); + gbMemory[0xff01] = (dat >> 8); + LogStrPop(5); + } //else + //gbMemory[0xff01] = 0xff; + gbSerialTicks = 0; + if(dat & 1) //if(linkid) + { + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + } + gbSerialBits = 0; + } else + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } #ifdef OLD_GB_LINK } #endif diff --git a/src/gb/gbGlobals.h b/src/gb/gbGlobals.h index df3df866..dd06c16e 100644 --- a/src/gb/gbGlobals.h +++ b/src/gb/gbGlobals.h @@ -1,6 +1,8 @@ #ifndef GBGLOBALS_H #define GBGLOBALS_H +#define VERBOSE_SIO 2048 + extern int gbRomSizeMask; extern int gbRomSize; extern int gbRamSize; diff --git a/src/gba/GBA-arm.cpp b/src/gba/GBA-arm.cpp index 1ff88340..f0c33c88 100644 --- a/src/gba/GBA-arm.cpp +++ b/src/gba/GBA-arm.cpp @@ -657,7 +657,7 @@ static void count(u32 opcode, int cond_res) #define OP_MVN \ EMIT1(not, eax) \ EMIT2(mov, eax, REGREF1(esi)) -#define OP_MVNS CHECK_PC(OP_MVN EMIT2(test,eax,eax), SETCOND_LOGICAL) +#define OP_MVNS CHECK_PC(OP_MVN EMIT2(test,eax,eax), SETCOND_LOGICAL) //AdamN: NOT eax doesn't affect any flags in x86 so need to add TEST for SETCOND_LOGICAL to works properly // ALU cleanup macro #define ALU_FINISH ALU_TRAILER diff --git a/src/gba/GBA.cpp b/src/gba/GBA.cpp index b4512b23..9e891739 100644 --- a/src/gba/GBA.cpp +++ b/src/gba/GBA.cpp @@ -22,6 +22,10 @@ #include "agbprint.h" #include "GBALink.h" +#ifdef _MSC_VER //#ifdef _WIN32 +#include "../Win32/WinHelper.h" // AdamN: for CCriticalSection +#endif + #ifdef PROFILING #include "prof/prof.h" #endif @@ -32,6 +36,8 @@ extern int emulating; +extern bool AppTerminated = false; + int SWITicks = 0; int IRQTicks = 0; @@ -59,6 +65,9 @@ bool cpuFlashEnabled = true; bool cpuEEPROMEnabled = true; bool cpuEEPROMSensorEnabled = false; +bool breakpt = false; +u32 breakaddr = 0xffffffff; + u32 cpuPrefetch[2]; int cpuTotalTicks = 0; @@ -815,7 +824,7 @@ static bool CPUReadState(gzFile gzFile) THUMB_PREFETCH; } - CPUUpdateRegister(0x204, CPUReadHalfWordQuick(0x4000204)); + CPUUpdateRegister(0x204, CPUReadHalfWordQuick(0x4000204)); //WAITCNT return true; } @@ -1779,7 +1788,8 @@ void CPUSoftwareInterrupt(int comment) #endif if(useBios) { #ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_SWI) { + //if(comment>0x2A) //AdamN: what is the idea of flooding the log with all interrupts?? having a stats showing how many times each interrupt being called might be more usefull than flooding + if(systemVerbose & VERBOSE_SWI) { //AdamN: trying to capture only the impossible (should be logged even when logging not enabled tho) log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, armState ? armNextPC - 4: armNextPC -2, reg[0].I, @@ -1827,31 +1837,31 @@ void CPUSoftwareInterrupt(int comment) stopState = true; cpuNextEvent = cpuTotalTicks; break; - case 0x04: + case 0x04: //AdamN: IntrWait #ifdef GBA_LOGGING if(systemVerbose & VERBOSE_SWI) { log("IntrWait: 0x%08x,0x%08x (VCOUNT = %2d)\n", reg[0].I, - reg[1].I, + reg[1].I, //AdamN: Interrupt flag(s) to wait for (same format as IE/IF registers) VCOUNT); } #endif - CPUSoftwareInterrupt(); + CPUSoftwareInterrupt(); //AdamN: Forcefully sets IME=1 and wait in Halt state until one (or more) of the specified interrupt(s) occurs. break; - case 0x05: + case 0x05: //AdamN: this VBlankIntrWait oftenly called (every 1/60 sec) so it should be handled properly #ifdef GBA_LOGGING if(systemVerbose & VERBOSE_SWI) { log("VBlankIntrWait: (VCOUNT = %2d)\n", VCOUNT); } #endif - CPUSoftwareInterrupt(); + CPUSoftwareInterrupt(); //AdamN: set r0=1,r1=1 then execute IntrWait break; - case 0x06: - CPUSoftwareInterrupt(); + case 0x06: //AdamN: Div oftenly called so it should be handled properly + BIOS_Div(); //CPUSoftwareInterrupt(); break; - case 0x07: - CPUSoftwareInterrupt(); + case 0x07: //AdamN: DivArm + BIOS_DivARM(); //CPUSoftwareInterrupt(); break; case 0x08: BIOS_Sqrt(); @@ -1862,7 +1872,7 @@ void CPUSoftwareInterrupt(int comment) case 0x0A: BIOS_ArcTan2(); break; - case 0x0B: + case 0x0B: //AdamN: CpuSet oftenly called so it should be handled properly { int len = (reg[2].I & 0x1FFFFF) >>1; if (!(((reg[0].I & 0xe000000) == 0) || @@ -1888,7 +1898,7 @@ void CPUSoftwareInterrupt(int comment) } BIOS_CpuSet(); break; - case 0x0C: + case 0x0C: //AdamN: CpuFastSet oftenly called so it should be handled properly { int len = (reg[2].I & 0x1FFFFF) >>5; if (!(((reg[0].I & 0xe000000) == 0) || @@ -2005,7 +2015,7 @@ void CPUSoftwareInterrupt(int comment) #ifdef GBA_LOGGING if(systemVerbose & VERBOSE_SWI) { log("SoundBiasSet: 0x%08x (VCOUNT = %2d)\n", - reg[0].I, + reg[0].I, //AdamN: r0=BIAS Level (0=Level 000h, any other value=Level 200h), r1=Delay Count on NDS (GBA uses a fixed delay count of 8) VCOUNT); } #endif @@ -2014,15 +2024,152 @@ void CPUSoftwareInterrupt(int comment) else soundResume(); break; + /*case 0x1A: //AdamN: SoundDriverInit, calling sequences after SoundDriverInit then SoundChannelClear then SoundDriverMode, then repeated SoundDriverVSync+SoundDriverMain +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SoundDriverInit: 0x%08x (VCOUNT = %2d)\n", + reg[0].I, //AdamN: Pointer to SoundArea structure + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break;*/ + case 0x1B: //AdamN: SoundDriverMode +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SoundDriverMode: 0x%08x (VCOUNT = %2d)\n", + reg[0].I, //AdamN: Sound driver operation mode + VCOUNT); + } +#endif + //CPUSoftwareInterrupt(); + break; + case 0x1C: //AdamN: SoundDriverMain (no param) + //CPUSoftwareInterrupt(); //AdamN: Call (using/after SoundDriverVSync) every 1/60 of a second, immediately after the V-Blank interrupt. After that, this routine is called after BG and OBJ processing is executed. + break; + case 0x1D: //AdamN: SoundDriverVSync + //CPUSoftwareInterrupt(); //AdamN: Resets the sound DMA (no param) to sync the BIOS sound driver + break; + case 0x1E: //AdamN: SoundChannelClear + //CPUSoftwareInterrupt(); //AdamN: Clears all direct sound channels and stops the sound (no param) + break; case 0x1F: BIOS_MidiKey2Freq(); break; - case 0x2A: + case 0x20: //AdamN: SoundWhatever0/MusicPlayerOpen +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("MusicPlayerOpen: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + //CPUSoftwareInterrupt(); + break; + case 0x21: //AdamN: SoundWhatever1/MusicPlayerStart +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("MusicPlayerStart: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + //CPUSoftwareInterrupt(); + break; + case 0x22: //AdamN: SoundWhatever2/MusicPlayerStop +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("MusicPlayerStop: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + //CPUSoftwareInterrupt(); + break; + case 0x23: //AdamN: SoundWhatever3/MusicPlayerContinue +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("MusicPlayerContinue: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + //CPUSoftwareInterrupt(); + break; + case 0x24: //AdamN: SoundWhatever4/MusicPlayerFadeOut +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("MusicPlayerFadeOut: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4: armNextPC -2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + //CPUSoftwareInterrupt(); + break; + case 0x25: //AdamN: MultiBoot +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("MultiBoot: 0x%08x,0x%08x (VCOUNT = %2d)\n", + reg[0].I, //AdamN: Pointer to MultiBootParam structure + reg[1].I, //AdamN: Transfer Mode + VCOUNT); + } +#endif + //CPUSoftwareInterrupt(); + break; + case 0x26: //AdamN: HardReset +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("HardReset: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); //Fully reset to Nintendo intro (no param) + break; + case 0x27: //AdamN: CustomHalt +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("CustomHalt: 0x%08x (VCOUNT = %2d)\n", + reg[2].I, //AdamN: 8bit param (00h=Halt, 80h=Stop) + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x28: //AdamN: SoundDriverVSyncOff + //CPUSoftwareInterrupt(); //AdamN: Stop sound DMA (no param) to prevent sound DMA from overflowing and playing noise when the game loading (as SoundDriverVSync might missed the 1/60sec interval) + break; + case 0x29: //AdamN: SoundDriverVSyncOn + //CPUSoftwareInterrupt(); //AdamN: Restarts the sound DMA stopped with SoundDriverVSyncOff (no param) to restore sound driver operation + break; + case 0x2A: //AdamN: SoundGetJumpList +#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SWI) { + log("SoundGetJumpList: 0x%08x (VCOUNT = %2d)\n", + reg[0].I, //AdamN: Destination address (must be aligned by 4) (120h bytes buffer) + VCOUNT); + } +#endif BIOS_SndDriverJmpTableCopy(); // let it go, because we don't really emulate this function default: #ifdef GBA_LOGGING - if(systemVerbose & VERBOSE_SWI) { + if(systemVerbose & VERBOSE_SWI) { log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, armState ? armNextPC - 4: armNextPC -2, reg[0].I, @@ -2034,11 +2181,12 @@ void CPUSoftwareInterrupt(int comment) if(!disableMessage) { systemMessage(MSG_UNSUPPORTED_BIOS_FUNCTION, - N_("Unsupported BIOS function %02x called from %08x. A BIOS file is needed in order to get correct behaviour."), + N_("Unsupported BIOS Function %02X called from %08x. A BIOS file is needed in order to get correct behaviour."), comment, armMode ? armNextPC - 4: armNextPC - 2); disableMessage = true; } + //CPUSoftwareInterrupt(); //AdamN: not needed as it could cause the game to stop when there are no bios file break; } } @@ -2405,7 +2553,10 @@ void CPUUpdateRegister(u32 address, u16 value) { case 0x00: { // we need to place the following code in { } because we declare & initialize variables in a case statement - if((value & 7) > 5) { + /*if((value & 4)&&((DISPCNT & 0x0010)!=(value & 0x0010))) { + log("DISPCNT(%08x) : %04X->%04X %04X R0=%08X R1=%08X R2=%08X R14=%08X Z=%d (VCOUNT = %d)\n", armState ? armNextPC - 4: armNextPC -2, DISPCNT, value, DISPSTAT, reg[0].I, reg[1].I, reg[2].I, reg[14].I, Z_FLAG, VCOUNT); + }*/ + if((value & 7) > 5) { // display modes above 0-5 are prohibited DISPCNT = (value & 7); } @@ -2626,7 +2777,11 @@ void CPUUpdateRegister(u32 address, u16 value) case 0x7c: case 0x80: case 0x84: + lastSR = value & 0xFF; + lastSA = address&0xFF; soundEvent(address&0xFF, (u8)(value & 0xFF)); + lastSR = value >> 8; + lastSA = (address&0xFF)+1; soundEvent((address&0xFF)+1, (u8)(value>>8)); break; case 0x82: @@ -2643,6 +2798,8 @@ void CPUUpdateRegister(u32 address, u16 value) case 0x9a: case 0x9c: case 0x9e: + lastSR = value; + lastSA = address&0xFF; soundEvent(address&0xFF, value); break; case 0xB0: @@ -2822,16 +2979,79 @@ void CPUUpdateRegister(u32 address, u16 value) timerOnOffDelay|=8; cpuNextEvent = cpuTotalTicks; break; - - + case COMM_SIODATA32_L: + if(READ16LE(&ioMem[COMM_SIODATA32_L]) != value) { + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + log("SIODATAL(%d) : %04X %04X %04X->%04X %04X %04X %04X %04X (VCOUNT = %d)\n", GetTickCount(), READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), READ16LE(&ioMem[COMM_SIOMULTI0]), value, READ16LE(&ioMem[COMM_SIOMULTI1]), READ16LE(&ioMem[COMM_SIOMULTI2]), READ16LE(&ioMem[COMM_SIOMULTI3]), READ16LE(&ioMem[COMM_SIOMLT_SEND]), VCOUNT ); + } + #endif + } + UPDATE_REG(COMM_SIODATA32_L, value); + break; + case COMM_SIODATA32_H: + if(READ16LE(&ioMem[COMM_SIODATA32_H]) != value) { + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + log("SIODATAH(%d) : %04X %04X %04X %04X->%04X %04X %04X %04X (VCOUNT = %d)\n", GetTickCount(), READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), READ16LE(&ioMem[COMM_SIOMULTI0]), READ16LE(&ioMem[COMM_SIOMULTI1]), value, READ16LE(&ioMem[COMM_SIOMULTI2]), READ16LE(&ioMem[COMM_SIOMULTI3]), READ16LE(&ioMem[COMM_SIOMLT_SEND]), VCOUNT ); + } + #endif + } + UPDATE_REG(COMM_SIODATA32_H, value); + break; case COMM_SIOCNT: + if(READ16LE(&ioMem[COMM_SIOCNT]) != (/*READ16LE(&ioMem[COMM_SIOCNT]) &*/ value)) { + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + if(value & 0x4000) + log("SIOCNT(%08x) : %04X %04X->%04X %04X %04X %04X %04X %04X (VCOUNT = %d)\n", armState ? armNextPC - 4: armNextPC -2, READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), value, READ16LE(&ioMem[COMM_SIOMULTI0]), READ16LE(&ioMem[COMM_SIOMULTI1]), READ16LE(&ioMem[COMM_SIOMULTI2]), READ16LE(&ioMem[COMM_SIOMULTI3]), READ16LE(&ioMem[COMM_SIOMLT_SEND]), VCOUNT ); + } + #endif + } + if( EmuReseted ) { //trying to detect whether the game has exited multiplay mode (ie. game restarted) + EmuReseted = false; + LinkFirstTime = true; + if(lanlink.connected) + if(linkid) lc.DiscardData(); + else ls.DiscardData(1); + } + //UPDATE_REG(COMM_SIOCNT, value); + lanlink.mode = GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT])); + if(lanlink.mode!=GetSIOMode(READ16LE(&ioMem[COMM_SIOCNT]), READ16LE(&ioMem[COMM_RCNT]))) LinkFirstTime = true; StartLink(value); + /*switch (GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT]))) { + case MULTIPLAYER: + if(LinkHandlerActive) { + if (value & 0x80) { //AdamN: Start/Busy Bit.7=1 (Active/Transfering) + LinkCmdQueue(1,value); + c_s.Lock(); //AdamN: Locking resource to prevent deadlock + //UPDATE_REG(COMM_SIOCNT, value); //AdamN: when using socket handler SIOCNT need to be updated as soon as possible because program might be reading it and ORing it for the next update + LinkParam1=value; + LinkCommand|=1; //AdamN: StartLink command + c_s.Unlock(); //AdamN: Locking resource to prevent deadlock + } else UPDATE_REG(COMM_SIOCNT, value); + } else { + LogStrPush("CPUUpdReg"); + //UPDATE_REG(COMM_SIOCNT, value); //just for testing the effect + if (value & 0x80) { //AdamN: Start/Busy Bit.7=1 (Active/Transfering) + StartLink(value); //AdamN: using blocking sockets here can cause noticibly delays + } else UPDATE_REG(COMM_SIOCNT, value); + LogStrPop(9); + } + break; + case NORMAL8: + case NORMAL32: + case UART: + default: + UPDATE_REG(COMM_SIOCNT, value); + break; + } */ /* // old code path for no linking... { - if (value & 0x80) { + if (value & 0x80) { //Start bit.7 value &= 0xff7f; - if ((value & 1) && (value & 0x4000)) { + if ((value & 1) && (value & 0x4000)) { //IRQ Enable bit.14 UPDATE_REG(COMM_SIODATA8, 0xFF); IF |= 0x80; UPDATE_REG(0x202, IF); @@ -2843,24 +3063,65 @@ void CPUUpdateRegister(u32 address, u16 value) */ break; - case COMM_SIODATA8: - if (gba_link_enabled) - LinkSSend(value); - UPDATE_REG(COMM_RCNT, value); + case COMM_SIODATA8: //AdamN: 8bit(SIODATA8) on Normal/UART(up to 4x8bit with FIFO), 16bit(SIOMLT_SEND) on Multiplayer mode + if(READ16LE(&ioMem[COMM_SIOMLT_SEND]) != (/*READ16LE(&ioMem[COMM_SIOMLT_SEND]) &*/ value)) { + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + log("SIODATA(%d) : %04X %04X %04X %04X %04X %04X %04X->%04X (VCOUNT = %d)\n", GetTickCount(), READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), READ16LE(&ioMem[COMM_SIOMULTI0]), READ16LE(&ioMem[COMM_SIOMULTI1]), READ16LE(&ioMem[COMM_SIOMULTI2]), READ16LE(&ioMem[COMM_SIOMULTI3]), READ16LE(&ioMem[COMM_SIOMLT_SEND]), value, VCOUNT ); + } + #endif + } + UPDATE_REG(COMM_SIODATA8, value); //COMM_SIOMLT_SEND + /*if (gba_link_enabled && lanlink.connected) //AdamN: added active connection checking, don't send any packet if there are no connection + LinkSSend(value); //AdamN: Does sending packet really needed in this part?(as transfers are initiated by SIOCNT) + UPDATE_REG(COMM_RCNT, value); //AdamN: is this really necessary?? seems to break connection + */ + /*if(linkid && (READ16LE(&ioMem[COMM_SIOMLT_SEND]) & 0x3f)==0x0f) { + lc.WaitForData(-1); + LinkUpdate2(0); + }*/ break; - case 0x130: + case 0x130: //AdamN: KEYINPUT P1 |= (value & 0x3FF); UPDATE_REG(0x130, P1); break; - case 0x132: + case 0x132: //AdamN: KEYCNT UPDATE_REG(0x132, value & 0xC3FF); break; - case COMM_RCNT: - StartGPLink(value); + case COMM_RCNT: //AdamN: often reach here even when no connection yet + /*if(lanlink.connected) + if(linkid) { + lc.DiscardData(); //AdamN: discarding may cause server to wait for infinity + } else { + //ls.DiscardData(); + }*/ + lanlink.mode = GetSIOMode(READ16LE(&ioMem[COMM_SIOCNT]), value); + if(READ16LE(&ioMem[COMM_RCNT]) != (/*READ16LE(&ioMem[COMM_RCNT]) &*/ value)) { + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + log("RCNT(%d) : %04X->%04X %04X %04X %04X %04X %04X (VCOUNT = %d)\n", GetTickCount(), READ16LE(&ioMem[COMM_RCNT]), value, READ16LE(&ioMem[COMM_SIOCNT]), READ16LE(&ioMem[COMM_SIOMULTI0]), READ16LE(&ioMem[COMM_SIOMULTI1]), READ16LE(&ioMem[COMM_SIOMULTI2]), READ16LE(&ioMem[COMM_SIOMULTI3]), VCOUNT ); + } + #endif + } + /*if(LinkHandlerActive) { + LinkCmdQueue(2,value); + c_s.Lock(); //AdamN: Locking resource to prevent deadlock + LinkParam2=value; + LinkCommand|=2; //AdamN: StartGPLink command + c_s.Unlock(); //AdamN: Locking resource to prevent deadlock + } else { + LogStrPush("CPUUpdReg");*/ + StartGPLink(value); //AdamN: doesn't need to be put in different thread as it doesn't send/recv data + /*LogStrPop(9); + }*/ break; + + /*case COMM_IR: + StartIRLink(value); //AdamN: not made yet tho, probably not useful + break;*/ case COMM_JOYCNT: { @@ -3128,6 +3389,8 @@ void CPUInit(const char *biosFileName, bool useBiosFile) void CPUReset() { + EmuReseted = true; + LinkFirstTime = true; if(gbaSaveType == 0) { if(eepromInUse) gbaSaveType = 3; @@ -3475,21 +3738,27 @@ extern void winlog(const char *, ...); void CPULoop(int ticks) { + if(EmuCtr>0) {log("Emu inside Emu:%d\n",EmuCtr);return;} + int clockTicks; int timerOverflow = 0; // variable used by the CPU core cpuTotalTicks = 0; + cpuBreakLoop = false; // // shuffle2: what's the purpose? - if(gba_link_enabled) - cpuNextEvent = 1; + /*if(gba_link_enabled && lanlink.connected && lanlink.mode==MULTIPLAYER && frameCount < systemFrameSkip) + //if(linkid) + cpuNextEvent = 159; //224; //else; //AdamN: this is to prevent CPU from executing too many opcodes when frame being skipped which cause Linking instability (ie. in-game timeout reached on client) + else */ + cpuNextEvent = CPUUpdateTicks(); //cpuNextEvent = 1; //224 //AdamN: this will cause slowdown?*/ + bool NewFrame = false; - cpuBreakLoop = false; - cpuNextEvent = CPUUpdateTicks(); + //cpuBreakLoop = false; + //cpuNextEvent = CPUUpdateTicks(); // if(cpuNextEvent > ticks) cpuNextEvent = ticks; - for(;;) { #ifndef FINAL_VERSION if(systemDebug) { @@ -3526,6 +3795,7 @@ void CPULoop(int ticks) } #endif /* FINAL_VERSION */ + if(breakpt && armNextPC==breakaddr) holdState=true; //AdamN: checking for breakpoint if(!holdState && !SWITicks) { if(armState) { if (!armExecute()) @@ -3555,7 +3825,7 @@ void CPULoop(int ticks) cpuTotalTicks = 0; cpuDmaHack = false; - updateLoop: +updateLoop: if (IRQTicks) { @@ -3581,7 +3851,7 @@ void CPULoop(int ticks) lcdTicks += 224; DISPSTAT |= 2; UPDATE_REG(0x04, DISPSTAT); - if(DISPSTAT & 16) { + if(DISPSTAT & 16) { // entering H-Blank IF |= 2; UPDATE_REG(0x202, IF); } @@ -3608,12 +3878,15 @@ void CPULoop(int ticks) DISPSTAT &= 0xFFFD; if(VCOUNT == 160) { count++; + NewFrame = true; systemFrame(); - if((count % 10) == 0) { + /*if((count % 10) == 0) { system10Frames(60); - } - if(count == 60) { + }*/ + system1Frames(60, count); + + if(count >= 60) { u32 time = systemGetClock(); if(time != lastTime) { u32 t = 100000/(time - lastTime); @@ -3623,6 +3896,7 @@ void CPULoop(int ticks) lastTime = time; count = 0; } + u32 joy = 0; // update joystick information if(systemReadJoypads()) @@ -3667,11 +3941,12 @@ void CPULoop(int ticks) DISPSTAT |= 1; DISPSTAT &= 0xFFFD; UPDATE_REG(0x04, DISPSTAT); - if(DISPSTAT & 0x0008) { + if(DISPSTAT & 0x0008) { //entering V-Blank IF |= 1; UPDATE_REG(0x202, IF); } CPUCheckDMA(1, 0x0f); + if(frameCount >= framesToSkip) { systemDrawScreen(); frameCount = 0; @@ -3786,7 +4061,10 @@ void CPULoop(int ticks) } break; } - } + } //else //AdamN: this seems to fix the instability of linking when frame being skipped, but it causes artifact at the top lines when connection established in game, does cpu executes more opcodes when frameskipped? + //frameCount++; //AdamN: shouldn't increase frameCount here, instead do something useless just to delay before calling LinkUpdate to maintain stability + //if(lanlink.connected) if(linkid) cpuNextEvent = cpuTotalTicks;/*lc.WaitForData(0); else ls.WaitForData(0);*/ + // entering H-Blank DISPSTAT |= 2; UPDATE_REG(0x04, DISPSTAT); @@ -3796,6 +4074,7 @@ void CPULoop(int ticks) IF |= 2; UPDATE_REG(0x202, IF); } + // } } } @@ -3942,8 +4221,21 @@ void CPULoop(int ticks) if (gba_joybus_enabled) JoyBusUpdate(clockTicks); - if (gba_link_enabled) - LinkUpdate(clockTicks); + if (gba_link_enabled && (lanlink.connected || !lanlink.active)) { //AdamN: don't update if there are no connection + LinkUpdate(clockTicks); //AdamN: when frame being skipped LinkUpdate might not be called(?) thus causing instability + NewFrame = false; + //if(LinkHandlerActive) { + //LinkCmdQueue(8,clockTicks); + //c_s.Lock(); //AdamN: Locking resource to prevent deadlock + //LinkParam8=clockTicks; + //LinkCommand|=8; //AdamN: LinkUpdate command + //c_s.Unlock(); //AdamN: Locking resource to prevent deadlock + //} else if(/*(clockTicks & 1)&&*/(lanlink.connected)) { //AdamN: it seems LinkUpdate need to be called every cycle, occasionally doesn't works + //LogStrPush("CPULoop"); + //LinkUpdate2(clockTicks); //AdamN: using blocking sockets here can cause noticibly delays + //LogStrPop(7); + //} + } cpuNextEvent = CPUUpdateTicks(); @@ -3960,8 +4252,11 @@ void CPULoop(int ticks) } // shuffle2: what's the purpose? - if(gba_link_enabled) - cpuNextEvent = 1; + /*if(gba_link_enabled && lanlink.connected) + cpuNextEvent = 1; //AdamN: this will cause great slowdown*/ + /*if(gba_link_enabled && lanlink.connected && lanlink.mode==MULTIPLAYER && frameCount < systemFrameSkip) + //if(linkid) + cpuNextEvent = 159; //224;*/ //cpuTotalTicks+224; //AdamN: this is to prevent CPU from executing too many opcodes when frame being skipped which cause Linking instability (ie. in-game timeout reached on client) if(IF && (IME & 1) && armIrqEnable) { int res = IF & IE; diff --git a/src/gba/GBA.h b/src/gba/GBA.h index 83041f52..5909329d 100644 --- a/src/gba/GBA.h +++ b/src/gba/GBA.h @@ -66,6 +66,12 @@ extern bool armState; extern int armMode; extern void (*cpuSaveGameFunc)(u32,u8); +extern int cpuNextEvent; +extern int cpuTotalTicks; +extern bool holdState; +extern bool breakpt; +extern u32 breakaddr; + #ifdef BKPT_SUPPORT extern u8 freezeWorkRAM[0x40000]; extern u8 freezeInternalRAM[0x8000]; diff --git a/src/gba/GBALink.cpp b/src/gba/GBALink.cpp index e78258b9..611b9aad 100644 --- a/src/gba/GBALink.cpp +++ b/src/gba/GBALink.cpp @@ -20,10 +20,12 @@ bool gba_link_enabled = false; #include "GBA.h" #include "GBALink.h" #include "GBASockClient.h" +#include "../gb/gbGlobals.h" #define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value) int linktime = 0; +int tmpctr = 0; GBASockClient* dol = NULL; sf::IPAddress joybusHostAddr = sf::IPAddress::LocalHost; @@ -33,27 +35,45 @@ sf::IPAddress joybusHostAddr = sf::IPAddress::LocalHost; u8 tspeed = 3; u8 transfer = 0; LINKDATA *linkmem = NULL; +LINKDATA rfudata; int linkid = 0, vbaid = 0; -HANDLE linksync[4]; +HANDLE linksync[5]; int savedlinktime = 0; HANDLE mmf = NULL; char linkevent[] = "VBA link event "; static int i, j; -int linktimeout = 1000; +int linktimeout = 1000; // LANLINKDATA lanlink; u16 linkdata[4]; +bool linkdatarecvd[4]; +u8 gbSIO_SC = 0; lserver ls; lclient lc; bool oncewait = false, after = false; +bool LinkIsWaiting = false; +bool LinkFirstTime = true; +bool EmuReseted = true; +int EmuCtr = 0; +WinHelper::CCriticalSection c_s; //AdamN: critical section object to lock shared resource on multithread as CWnd is not thread-safe +int LinkCommand = 0; +int LinkParam1 = 0; +int LinkParam2 = 0; +int LinkParam4 = 0; +int LinkParam8 = 0; +extern bool LinkHandlerActive = false; +CPtrList LinkCmdList; +CString LogStr = _T(""); // RFU crap (except for numtransfers note...should probably check that out) bool rfu_enabled = false; -u8 rfu_cmd, rfu_qsend, rfu_qrecv; -int rfu_state, rfu_polarity, linktime2, rfu_counter, rfu_masterq; +bool rfu_initialized = false; +u8 rfu_cmd, rfu_qsend, rfu_qrecv, rfu_lastcmd; +u16 rfu_id, rfu_thisid; +int rfu_state, rfu_polarity, linktime2, rfu_counter, rfu_masterq;//, rfu_cacheq; // numtransfers seems to be used interchangeably with linkmem->numtransfers // probably a bug? int rfu_transfer_end, numtransfers = 0; -u32 rfu_masterdata[32]; +u32 rfu_masterdata[5*35];//, rfu_cachedata[35]; //for 0x24-0x26, temp buffer before data actually sent to other gbas or cache after read // ??? int trtimedata[4][4] = { @@ -75,10 +95,54 @@ int GetSIOMode(u16, u16); DWORD WINAPI LinkClientThread(void *); DWORD WINAPI LinkServerThread(void *); +DWORD WINAPI LinkHandlerThread(void *); int StartServer(void); u16 StartRFU(u16); +u16 StartRFU2(u16 value); +u16 StartRFU3(u16 value); +BOOL LinkSendData(const char *buf, int size, int nretry = 0); +BOOL LinkIsDataReady(void); +BOOL LinkWaitForData(int ms); + +void LogStrPush(const char *str) { + c_s.Lock(); + LogStr.AppendFormat(_T("%s(%08x)>"),str,GetTickCount()); + c_s.Unlock(); + return; +} + +void LogStrPop(int len) { + c_s.Lock(); + LogStr.Delete(LogStr.GetLength()-(len+11),(len+11)); + c_s.Unlock(); + return; +} + +void LinkCmdQueue(u16 Cmd, u16 Prm) { + LINKCMDPRM cmdprm; + ULONG tmp, tmp2; + //POSITION pos; + cmdprm.Command = Cmd; + cmdprm.Param = Prm; + tmp = Prm; + tmp = tmp << 16; + tmp |= Cmd; + tmp2 = 0; + c_s.Lock(); + if((lanlink.connected /*|| !lanlink.active*/) && LinkCmdList.GetCount()<255) { //as Client GetCount usually no more than 4, as Server it can be alot more than 256 (flooded with LinkUpdate) + if(LinkCmdList.GetCount()>0) + tmp2 = (ULONG)*&LinkCmdList.GetTail(); //AdamN: check last command that hasn't been processed yet + if((Cmd==8) && ((tmp2 & 0xffff)==Cmd)) { //AdamN: LinkUpdate could flood the command queue + LinkCmdList.SetAt(LinkCmdList.GetTailPosition(),(void*)tmp); //AdamN: replace the value, doesn't need to delete the old value isn't? since it's not really a pointer + //log("Add: %04X %04X\n",cmdprm.Command,cmdprm.Param); + } else LinkCmdList.AddTail((void*)tmp); + //if(LinkCmdList.GetCount()>1) log("Count: %d\n",LinkCmdList.GetCount()); + } + c_s.Unlock(); + return; +} char *MakeInstanceFilename(const char *Input) { @@ -95,41 +159,327 @@ char *MakeInstanceFilename(const char *Input) return result; } +u8 gbStartLink(u8 b) +{ + u8 dat = 0xff; //master (w/ internal clock) will gets 0xff if slave is turned off (or not ready yet also?) + //if(linkid) return 0xff; //b; //Slave shouldn't be sending from here + BOOL sent = false; + if(gba_link_enabled && lanlink.connected) { + LogStrPush("gbStartLink"); + //Send Data (Master/Slave) + if(linkid) { //Client + lc.outbuffer[0] = b; + sent = lc.SendData(1, 1); + } else //Server + { + ls.outbuffer[0] = b; + sent = ls.SendData(1, 1); + } + //if(linkid) return b; + //Receive Data (Master) + if(sent) + //if(gbMemory[0xff02] & 1) + if(linkid) { //Client + if(lc.WaitForData(linktimeout)) { //-1 might not be getting infinity as expected :( + if(lc.RecvData(1)) + dat = lc.inbuffer[0]; + //dat = b; + } + } else //Server + { + if(ls.WaitForData(linktimeout)) { //-1 might not be getting infinity as expected :( + if(ls.RecvData(1, 1)) + dat = ls.inbuffer[0]; + //dat = b; + } + } + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + log("sSIO : %02X %02X->%02X %d\n", gbSIO_SC, b, dat, GetTickCount() ); //register_LY + } + #endif + LinkFirstTime = true; + if(dat==0xff/*||b==0x00||dat==0x00*/) { //dat==0xff + //LinkFirstTime = true; + //if(dat==0xff) + if(linkid) lc.DiscardData(); + else ls.DiscardData(1); + } else // + //if(!(gbMemory[0xff02] & 2)) //((gbMemory[0xff02] & 3) == 1) + LinkFirstTime = false; //it seems internal clocks can send 1(w/ speed=0) following data w/ external clock, does the speed declare how many followers that can be send? + //if( /*(gbMemory[0xff02] & 2) ||*/ !(gbMemory[0xff02] & 1) ) LinkFirstTime = true; + LinkIsWaiting = false; + LogStrPop(11); + } + return dat; +} -void StartLink(u16 value) +u16 gbLinkUpdate(u8 b) +{ + u8 dat = b; //0xff; //slave (w/ external clocks) won't be getting 0xff if master turned off + BOOL recvd = false; + int gbSerialOn = (gbMemory[0xff02] & 0x80); + if(gbSerialOn) + if(gba_link_enabled && lanlink.connected) { + //if(!gbSerialOn && !linkid) return (b<<8); + LogStrPush("gbLinkUpd"); + //Receive Data (Slave) + //if(!(gbMemory[0xff02] & 1)) + if(linkid) { //Client + if((/*LinkFirstTime &&*/ lc.IsDataReady()) || ( !lanlink.speed && !LinkFirstTime && lc.WaitForData(linktimeout)) ) { + recvd = lc.RecvData(1); + if(recvd /*&& gbSerialOn*/) { //don't update if not ready? + dat = lc.inbuffer[0]; + //LinkFirstTime = false; + //LinkFirstTime = true; + } else LinkIsWaiting = true; + } else LinkIsWaiting = true; + } else //Server + { + if((/*LinkFirstTime &&*/ ls.IsDataReady()) || ( !lanlink.speed && !LinkFirstTime && ls.WaitForData(linktimeout)) ) { + recvd = ls.RecvData(1, 1); + if(recvd /*&& gbSerialOn*/) { //don't update if not ready? + dat = ls.inbuffer[0]; + //LinkFirstTime = false; + //LinkFirstTime = true; + } else LinkIsWaiting = true; + } else LinkIsWaiting = true; + } + /*if(!linkid) //Master shouldn't be initiate a transfer from here + { + LogStrPop(9); + return b; //returning b seems to make it as Player1 + }*/ + //Send Data (Master/Slave), slave should replies 0xff if it's not ready yet? + //if(!LinkFirstTime) LinkFirstTime = true; else + if(recvd) + if(linkid) { //Client + if(gbSerialOn) + lc.outbuffer[0] = b; else lc.outbuffer[0] = (u8)0xff; + if(gbSerialOn) //don't reply if not ready? + lc.SendData(1, 1); + LinkIsWaiting = false; + //LinkFirstTime = false; + } else //Server + { + if(gbSerialOn) + ls.outbuffer[0] = b; else ls.outbuffer[0] = (u8)0xff; + if(gbSerialOn) //don't reply if not ready? + ls.SendData(1, 1); + LinkIsWaiting = false; + //LinkFirstTime = false; + } + #ifdef GBA_LOGGING + if(recvd && gbSerialOn) + if(systemVerbose & VERBOSE_SIO) { + log("cSIO : %02X %02X->%02X %d\n", gbSIO_SC, b, dat, GetTickCount() ); //register_LY + } + #endif + if(dat==0xff||dat==0x00||b==0x00) { //dat==0xff||dat==0x00 + LinkFirstTime = true; + if(dat==0xff) + if(linkid) lc.DiscardData(); + else ls.DiscardData(1); + } + LogStrPop(9); + } + return ((dat << 8) | (recvd & (u8)0xff)); +} + +void StartLink2(u16 value) //Called when COMM_SIOCNT written +{ + char inbuffer[256], outbuffer[256]; + u16 *u16inbuffer = (u16*)inbuffer; + u16 *u16outbuffer = (u16*)outbuffer; + BOOL disable = true; + BOOL sent = false; + unsigned long notblock = 1; //AdamN: 0=blocking, non-zero=non-blocking + //unsigned long arg = 0; + //fd_set fdset; + //timeval wsocktimeout; + + if (ioMem == NULL) + return; + + /*if (rfu_enabled) { + UPDATE_REG(COMM_SIOCNT, StartRFU(value)); + return; + }*/ + u16 rcnt = READ16LE(&ioMem[COMM_RCNT]); + u16 siocnt = READ16LE(&ioMem[COMM_SIOCNT]); + int commmode = GetSIOMode(value, rcnt); + if(!linkid && (((siocnt&3) != (value&3)) || (GetSIOMode(siocnt, rcnt) != commmode))) linkdatarecvd[/*0*/linkid]=false; //AdamN: reset if clock/mode changed from the last time + switch (commmode) { + case MULTIPLAYER: + if (value & 0x08) { //AdamN: SD Bit.3=1 (All GBAs Ready) + if(lanlink.active && lanlink.connected) + if (/*!(value & 0x04)*/ !linkid) { //Parent/Master, AdamN: SI Bit.2=0 (0=Parent/Master/Server,1=Child/Slave/Client) + if (value & 0x80) //AdamN: Start/Busy Bit.7=1 (Active/Transfering) + if(!linkdatarecvd[0]) { //AdamN: cycle not started yet + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) | 0x0080); //AdamN: Activate bit.7 (Start/Busy) since value hasn't been updated yet at this point + UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) & 0xfffe); //AdamN: LOWering bit.0 (SC) + linkdata[0] = READ16LE(&ioMem[COMM_SIOMLT_SEND]); + linkdata[1] = 0xffff; + WRITE32LE(&linkdata[2], 0xffffffff); + WRITE32LE(&ioMem[COMM_SIOMULTI0], 0xffffffff); //AdamN: data from SIOMULTI0 & SIOMULTI1 + WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); //AdamN: data from SIOMULTI2 & SIOMULTI3 + outbuffer[0] = 3; //AdamN: size of packet + outbuffer[1] = linkid; //AdamN: Sender ID (0) + u16outbuffer[1] = linkdata[0]; //AdamN: u16outbuffer[1] points to outbuffer[2] + for(int i=1; i<=lanlink.numgbas; i++) { + notblock = 0; //1; + ioctlsocket(ls.tcpsocket[i], FIONBIO, ¬block); //AdamN: temporarily use non-blocking for sending multiple data at once + int ern=WSAGetLastError(); + if(ern!=0) log("IOCTL1 Error: %d\n",ern); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSSend to : %d Size : %d %s\n", (LPCTSTR)LogStr, i, 4, (LPCTSTR)DataHex(outbuffer,4)); + } + #endif + /*outbuffer[0] = 0; + for(int c=0;c<8;c++) //AdamN: sending 8 bytes dummies + send(ls.tcpsocket[i], outbuffer, 1, 0); //send a dummies, it seems the first packet won't arrive on the other side for some reason, like need to fill the internal buffer before it actually get sent + outbuffer[0] = 3;*/ + setsockopt(ls.tcpsocket[i], IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + sent|=(send(ls.tcpsocket[i], outbuffer, 4, 0)>=0); + ern=WSAGetLastError(); + if(ern!=0) log("Send%d Error: %d\n",i,ern); + } + /*notblock = 0; + ioctlsocket(ls.tcpsocket[i], FIONBIO, ¬block); //AdamN: back to blocking, might not be needed + ern=WSAGetLastError(); + if(ern!=0) log("IOCTL2 Error: %d\n",ern);*/ + if(sent) { + UPDATE_REG(COMM_SIOMULTI0, linkdata[0]); //AdamN: SIOMULTI0 (16bit data received from master/server) + UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) & 0xfff7); //AdamN: LOWering SO bit.3 (RCNT or SIOCNT?) + for(int i=1; i<=lanlink.numgbas; i++) linkdatarecvd[i] = false; //AdamN: new cycle begins + linkdatarecvd[0] = true; + //value &= 0xff7f; //AdamN: Deactivate bit.7 (Start/Busy), Busy bit should be Deactivated when all SIOMULTI has been filled with data from active GBAs + //value |= (sent != 0) << 7; + } + } + } else { //Slave/Client? + //todo: ignoring ReadOnly bits on Slaves, is this needed? + if (value & 0x80) log("Slave wants to Send"); + } + /*value &= 0xff8b; //AdamN: bit.2(SI),4-5(ID),6(ERR) masked out + //if(sent) + value |= (linkid ? 0xc : 8); //AdamN: master=0x08 bit.3(SD), slave=0x0c bit.2-3(SI,SD) + value |= linkid << 4; //AdamN: setting bit.4-5 (ID) after a successful transfer*/ + } + value &= 0xff8b; //AdamN: bit.2(SI),4-5(ID),6(ERR) masked out + value |= 8; //AdamN: need to be set as soon as possible for Mario Kart + /*if(sent)*/ { + value |= (linkid ? 0xc : 8); //AdamN: master=0x08 bit.3(SD), slave=0x0c bit.2-3(SI,SD) + value |= linkid << 4; //AdamN: setting bit.4-5 (ID) after a successful transfer, need to be set as soon as possible otherwise Client may think it's a Server(when getting in-game timeout) and tried to initiate a transfer on next retry + } + UPDATE_REG(COMM_SIOCNT, value); + if(linkid && (value & 0x3f)==0x0f) lc.WaitForData(-1); else + if(sent) { + ls.WaitForData(-1); + //MSG msg; + //int ready = 0; + //do { //Waiting for incomming data before continuing CPU execution + // SleepEx(0,true); //SleepEx(0,true); //to give time for incoming data + // if(PeekMessage(&msg, 0/*theApp.GetMainWnd()->m_hWnd*/, 0, 0, PM_NOREMOVE)) + /* theApp.PumpMessage(); //seems to be processing message only if it has message otherwise it halt the program + + //fdset.fd_count = lanlink.numgbas; + notblock = 0; + for(int i=1; i<=lanlink.numgbas; i++) { + //fdset.fd_array[i-1] = ls.tcpsocket[i]; + ioctlsocket(ls.tcpsocket[i], FIONBIO, ¬block); //AdamN: temporarily use blocking for reading + int ern=WSAGetLastError(); + if(ern!=0) { + log("slIOCTL Error: %d\n",ern); + //if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065) lanlink.connected = false; + } + arg = 1; //0; + if(ioctlsocket(ls.tcpsocket[i], FIONREAD, &arg)!=0) { //AdamN: Alternative(Faster) to Select(Slower) + int ern=WSAGetLastError(); + if(ern!=0) { + log("%sSC Error: %d\n",(LPCTSTR)LogStr,ern); + char message[40]; + lanlink.connected = false; + sprintf(message, _T("Player %d disconnected."), i+1); + MessageBox(NULL, message, _T("Link"), MB_OK); + break; + } + } + if(arg>0) ready++; + } + //wsocktimeout.tv_sec = linktimeout / 1000; + //wsocktimeout.tv_usec = linktimeout % 1000; //0; //AdamN: remainder should be set also isn't? + //ready = select(0, &fdset, NULL, NULL, &wsocktimeout); + //int ern=WSAGetLastError(); + //if(ern!=0) { + // log("slCC Error: %d\n",ern); + // if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065) lanlink.connected = false; + //} + } while (lanlink.connected && ready==0);*/ + } + + //AdamN: doesn't seems to be needed here + if (linkid) //Slave + UPDATE_REG(COMM_RCNT, 7); //AdamN: Bit.0-2 (SC,SD,SI) as for GP + else //Master + UPDATE_REG(COMM_RCNT, 3); //AdamN: Bit.0-1 (SC,SD) as for GP //not needed + if(sent || (linkid && (value & 0x3f)==0x0f)) + LinkUpdate2(0,0); + break; + case NORMAL8: + case NORMAL32: //AdamN: Wireless mode also use NORMAL32 mode for transreceiving the data + case UART: + default: + UPDATE_REG(COMM_SIOCNT, value); + break; + } +} + +void StartLink(u16 value) //Called when COMM_SIOCNT written { if (ioMem == NULL) return; if (rfu_enabled) { - UPDATE_REG(COMM_SIOCNT, StartRFU(value)); + //if(lanlink.connected) + UPDATE_REG(COMM_SIOCNT, StartRFU3(value)); //RF use NORMAL32 mode to communicate + //else UPDATE_REG(COMM_SIOCNT, StartRFU(value)); //RF use NORMAL32 mode to communicate + return; } + BOOL sent = false; + //u16 prvSIOCNT = READ16LE(&ioMem[COMM_SIOCNT]); + switch (GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT]))) { - case MULTIPLAYER: - if (value & 0x80) { - if (!linkid) { - if (!transfer) { - if (lanlink.active) + case MULTIPLAYER: + if (value & 0x80) { //AdamN: Start Bit=Start/Active (transfer initiated) + if (!linkid) { //master/server? + if (!transfer) { //not transfered yet? + if (lanlink.active) //On Networks { if (lanlink.connected) { - linkdata[0] = READ16LE(&ioMem[COMM_SIODATA8]); + linkdata[0] = READ16LE(&ioMem[COMM_SIODATA8]); //AdamN: SIOMLT_SEND(16bit data to be sent) on MultiPlayer mode savedlinktime = linktime; tspeed = value & 3; - ls.Send(); + LogStrPush("StartLink1"); + sent = ls.Send(); //AdamN: Server need to send this often + LogStrPop(10); + if(sent) { transfer = 1; linktime = 0; - UPDATE_REG(COMM_SIODATA32_L, linkdata[0]); - UPDATE_REG(COMM_SIODATA32_H, 0xffff); - WRITE32LE(&ioMem[0x124], 0xffffffff); - if (lanlink.speed&&oncewait == false) + UPDATE_REG(COMM_SIODATA32_L, linkdata[0]); //AdamN: SIOMULTI0 (16bit data received from master/server) + UPDATE_REG(COMM_SIODATA32_H, 0xffff); //AdamN: SIOMULTI1 (16bit data received from slave1, reset to FFFFh upon transfer start) + WRITE32LE(&ioMem[0x124], 0xffffffff); //AdamN: data from SIOMULTI2 & SIOMULTI3 + if (lanlink.speed && oncewait == false) //lanlink.speed = speedhack ls.howmanytimes++; after = false; + } } } - else if (linkmem->numgbas > 1) + else if (linkmem->numgbas > 1) //On Single Computer { ResetEvent(linksync[0]); linkmem->linkcmd[0] = ('M' << 8) + (value & 3); @@ -146,21 +496,30 @@ void StartLink(u16 value) linktime = 0; tspeed = value & 3; WRITE32LE(&ioMem[COMM_SIODATA32_L], 0xffffffff); - WRITE32LE(&ioMem[0x124], 0xffffffff); + WRITE32LE(&ioMem[0x124], 0xffffffff); //COMM_SIOMULTI2 + sent = true; } } } + if(sent) { value &= 0xff7f; value |= (transfer != 0) << 7; + } } + /*if(sent)*/ { //AdamN: no need to check for sent here value &= 0xff8b; value |= (linkid ? 0xc : 8); value |= linkid << 4; - UPDATE_REG(COMM_SIOCNT, value); + + //AdamN: doesn't seems to be needed if (linkid) UPDATE_REG(COMM_RCNT, 7); else UPDATE_REG(COMM_RCNT, 3); + } + UPDATE_REG(COMM_SIOCNT, value); + if(sent || (linkid && (value & 0x3f)==0x0f)) + LinkUpdate(0); break; case NORMAL8: @@ -172,25 +531,33 @@ void StartLink(u16 value) } } -void StartGPLink(u16 value) +void StartGPLink(u16 value) //Called when COMM_RCNT written { UPDATE_REG(COMM_RCNT, value); if (!value) - return; + return; //if value=0(bit.15=1 & bit.14=0 for GP) then it's not possible for GP mode - switch (GetSIOMode(READ16LE(&ioMem[COMM_SIOCNT]), value)) { - case MULTIPLAYER: + switch (GetSIOMode(READ16LE(&ioMem[COMM_SIOCNT]), value)) { //bit.15=0 & bit.14=any for MP/Normal/UART + case MULTIPLAYER: value &= 0xc0f0; value |= 3; if (linkid) value |= 4; UPDATE_REG(COMM_SIOCNT, ((READ16LE(&ioMem[COMM_SIOCNT])&0xff8b)|(linkid ? 0xc : 8)|(linkid<<4))); break; - - case GP: - if (rfu_enabled) - rfu_state = RFU_INIT; + + case GP: //Only bit.0-3&14-15 of RCNT used for GP, JoyBus use bit.15=1 & bit.14=1 + //default: //Normal mode, may cause problem w/ RF if default also reset the RF + if (rfu_enabled) { + rfu_state = RFU_INIT; //reset wireless + rfu_polarity = 0; + #ifdef GBA_LOGGING + if(systemVerbose & (VERBOSE_SIO | VERBOSE_LINK)) { + log("RFU Reset : %04X %04X %d\n", READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), GetTickCount() ); + } + #endif + } break; } } @@ -272,15 +639,460 @@ void JoyBusUpdate(int ticks) #ifdef _MSC_VER +void LinkUpdate2(int ticks, int FrameCnt) //It seems Frameskipping on Client side causes instability, Client may need to execute the CPU slower than Server to maintain stability for some games (such as Mario Kart) +{ + char inbuffer[256], outbuffer[256]; + u16 *u16inbuffer = (u16*)inbuffer; + u16 *u16outbuffer = (u16*)outbuffer; + unsigned long notblock = 0; //AdamN: 0=blocking, non-zero=non-blocking + BOOL disable = true; + unsigned long arg = 0; + BOOL recvd = false; + BOOL sent = false; + fd_set fdset; + timeval wsocktimeout; + + if (ioMem == NULL) + return; + + linktime += ticks; + + int missed = 0; + int stacked = 0; + static int misctr = 0; + + //if(ticks!=0 && linktime<1008) return; + + /*if (rfu_enabled) + { + linktime2 += ticks; // linktime2 is unused! + rfu_transfer_end -= ticks; + if (transfer && rfu_transfer_end <= 0) + { + transfer = 0; + if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) //IRQ Enable + { + IF |= 0x80; //Serial Communication + UPDATE_REG(0x202, IF); //Interrupt Request Flags / IRQ Acknowledge + } + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff7f); + } + return; + }*/ + + if (lanlink.active) //On Networks + { + if (lanlink.connected) + { + int numbytes = 0; + u16 siocnt = READ16LE(&ioMem[COMM_SIOCNT]); + u16 rcnt = READ16LE(&ioMem[COMM_RCNT]); + if(GetSIOMode(siocnt, rcnt)!=MULTIPLAYER) {/*UPDATE_REG(0x06, VCOUNT);*/ return;} //AdamN: skip if it's not MP mode, default mode at startup is Normal8bit + if (linkid) { //Slave/Client? + UPDATE_REG(COMM_SIOCNT, siocnt | 0x000c); //AdamN: set Bit.2(SI) and Bit.3(SD) to mark it as Ready + //if(linktime<159/*1008*/) return; //AdamN: checking scanlines? giving delays to prevent too much slowdown, could cause instability? + //linktime=0; + //if(VCOUNT>160) UPDATE_REG(0x06, 160); //AdamN: forcing VCOUNT value to trick games that expect data exchange when VCOUNT=160 + //UPDATE_REG(COMM_SIOCNT, siocnt | 0x000c); + //if(GetSIOMode(siocnt, rcnt)!=MULTIPLAYER) return; //AdamN: skip if it's not MP mode + /*u16outbuffer[0] = 0; + u16outbuffer[1] = 0; + notblock = 1; + ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: temporarily use non-blocking for sending + setsockopt(lanlink.tcpsocket, IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + sent=(send(lanlink.tcpsocket, outbuffer, 4, 0)>=0);*/ //AdamN: sending dummies packet to prevent timeout when server tried to read socket w/ an empty buffer + notblock = 0; + ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: temporarily use blocking for reading + int ern=WSAGetLastError(); + if(ern!=0) log("IOCTL1 Error: %d\n",ern); + if(linkdatarecvd[0] /*|| VCOUNT>=159 || !LinkFirstTime*/) { + bool last = true; + for(int i=1;i<=lanlink.numgbas;i++) last&=linkdatarecvd[i]; + if(!last) { + /*fdset.fd_count = 1; + fdset.fd_array[0] = lanlink.tcpsocket; + wsocktimeout.tv_sec = linktimeout / 1000; + wsocktimeout.tv_usec = linktimeout % 1000; //0; //AdamN: remainder should be set also isn't? + if(select(0, &fdset, NULL, NULL, &wsocktimeout)<=0) { //AdamN: this will blocks executions but allows Server to be paused w/o causing disconnection on Client + int ern=WSAGetLastError(); + if(ern!=0) { + lanlink.connected = false; + log("%sCR[%d]\n",(LPCTSTR)LogStr,ern); + } + return; + }*/ + if(!lc.WaitForData(-1)) return; + } + } + do { + arg = lc.IsDataReady(); //0; + /*if(ioctlsocket(lanlink.tcpsocket, FIONREAD, &arg)!=0) { //AdamN: Alternative(Faster) to Select(Slower) + int ern=WSAGetLastError(); + if(ern!=0) { + log("%sCC Error: %d\n",(LPCTSTR)LogStr,ern); + lanlink.connected = false; + MessageBox(NULL, _T("Player 1 disconnected."), _T("Link"), MB_OK); + } + }*/ + if(arg>0) { + stacked++; + //UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) | 0x000c); //AdamN: set Bit.2(SI) and Bit.3(SD) to mark it as Ready + numbytes = 0; + int sz=recv(lanlink.tcpsocket, inbuffer, 1, 0); //AdamN: read the size of packet + int ern=WSAGetLastError(); + if(ern!=0) { + if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065) lanlink.connected = false; + #ifdef GBA_LOGGING + if(ern!=10060) + if(systemVerbose & VERBOSE_LINK) { + log("%sCRecv1 Error: %d\n",(LPCTSTR)LogStr,ern); //oftenly gets access violation if App closed down, could be due to LogStr="" + } + #endif + } + if(sz > 0) { + sz = inbuffer[0]; + /*if(sz==0) + tmpctr++;*/ + UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) & 0xfffe); //AdamN: LOWering bit.0 (SC) + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) | 0x0080); //AdamN: Activate bit.7 (Start/Busy) + } + numbytes++; + while(numbytes<=sz) { + int cnt=recv(lanlink.tcpsocket, inbuffer+numbytes, (sz-numbytes)+1, 0); + int ern=WSAGetLastError(); //AdamN: it seems WSAGetLastError can still gets 0 even when server no longer existed + if(ern!=0 || cnt<=0) { + log("%sCRecv2 Error: %d\n",(LPCTSTR)LogStr,ern); + if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065 || cnt<=0) lanlink.connected = false; + break; + } + numbytes+=cnt; + } + recvd=(numbytes>1); + //done: check sender id and update linkdata+register + if(recvd) { + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sCRecv2 Size : %d %s\n", (LPCTSTR)LogStr, numbytes, (LPCTSTR)DataHex(inbuffer,numbytes)); + } + #endif + int sid = inbuffer[1]; //AdamN: sender ID + if(!sid) { //AdamN: if sender is parent then it's new cycle + for(int i=1; i<=lanlink.numgbas; i++) linkdatarecvd[i] = false; + WRITE32LE(&linkdata[0], 0xffffffff); + WRITE32LE(&linkdata[2], 0xffffffff); + WRITE32LE(&ioMem[COMM_SIOMULTI0], 0xffffffff); //AdamN: data from SIOMULTI0 & SIOMULTI1 + WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); //AdamN: data from SIOMULTI2 & SIOMULTI3 + } + linkdata[sid] = u16inbuffer[1]; //AdamN: received data, u16inbuffer[1] points to inbuffer[2] + linkdatarecvd[sid] = true; + LinkFirstTime = false; + //UPDATE_REG(0x06, VCOUNT); //AdamN: restore to the real VCOUNT, may be it shouldn't be restored until no longer in MP mode? + UPDATE_REG((sid << 1)+COMM_SIOMULTI0, linkdata[sid]); //AdamN: SIOMULTI0 (16bit data received from master/server) + UPDATE_REG(COMM_SIOCNT, (READ16LE(&ioMem[COMM_SIOCNT]) & 0xffcf) | (linkid << 4)); //AdamN: Set ID bit.4-5 + //UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff7f); //AdamN: Deactivate bit.7 (Start/Busy), should be done after a cycle finished + //value &= 0xff7f; + //value |= (sent != 0) << 7; + //done: if (sid=0); + ern=WSAGetLastError(); + if(ern!=0) { + log("Send-0 Error: %d\n",ern); //AdamN: might be getting err 10035(would block) when used on non-blocking socket + if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065) lanlink.connected = false; + } + if(sent) { + UPDATE_REG((linkid << 1)+COMM_SIOMULTI0, linkdata[linkid]); + //UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) & 0xfff7); //AdamN: LOWering SO bit.3 (RCNT or SIOCNT?) + linkdatarecvd[linkid] = true; + } + } + } + bool last = true; + for(int i=0; i<=lanlink.numgbas; i++) last &= linkdatarecvd[i]; + if(last) { //AdamN: cycle ends + //UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) | 0x0001); //AdamN: HIGHering bit.0 (SC), only needed for Master/Server? + //UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) | 0x0008); //AdamN: HIGHering SO bit.3 (RCNT or SIOCNT?) + UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) | 0x0007/*f*/); //AdamN: HIGHering SC,SD,SI,SO (apparently SD and SI need to be High also for Slave), Slave might be checking SC when not using IRQ + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff7f); //AdamN: Deactivate bit.7 (Start/Busy) + //AdamN: Trigger Interrupt if Enabled + if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) { //IRQ Enable + IF |= 0x80; //Serial Communication + UPDATE_REG(0x202, IF); //Interrupt Request Flags / IRQ Acknowledge + } + for(int i=0; i<=lanlink.numgbas; i++) linkdatarecvd[i]=false; //AdamN: does it need to reset these? + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + //if(((READ16LE(&ioMem[COMM_SIOCNT]) >> 4) & 3) != linkid) + //if(READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) + log("SIO : %04X %04X %04X %04X %04X %04X (VCOUNT = %d) %d\n", READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), READ16LE(&ioMem[COMM_SIOMULTI0]), READ16LE(&ioMem[COMM_SIOMULTI1]), READ16LE(&ioMem[COMM_SIOMULTI2]), READ16LE(&ioMem[COMM_SIOMULTI3]), VCOUNT, GetTickCount() ); + } + #endif + break; //to exit while(arg>0 && lanlink.connected); if it's last + } else if(sent) UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) & 0xfff7); //AdamN: LOWering SO bit.3 (RCNT or SIOCNT?) + //if(sent) UPDATE_REG(COMM_RCNT, 7); //AdamN: Bit.0-2 (SC,SD,SI) as for GP + } + } else missed++; + } while(arg>0 && lanlink.connected); + UPDATE_REG(COMM_SIOCNT, (READ16LE(&ioMem[COMM_SIOCNT]) & 0xffcf) | (linkid << 4)); //AdamN: Set ID bit.4-5, needed for Mario Kart + //UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff77); //AdamN: making it not ready so slave doesn't start counting the in-game timeout counter + //if(!LinkFirstTime) { + //lc.WaitForData(1); + //if(misctr++<400) + // log("Frame:%d Tick:%d Missed:%d Stacked:%d\n",FrameCnt,/*GetTickCount()*/ticks,missed,stacked); + //} + /*notblock = 1; + ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: back to non-blocking, might not be needed + int ern=WSAGetLastError(); + if(ern!=0) log("IOCTL2 Error: %d\n",ern);*/ + + } else { //Server/Master? + UPDATE_REG(COMM_SIOCNT, siocnt | 0x0008); //AdamN: set Bit.2(SI) and Bit.3(SD) to mark it as Ready + //if(linktime<2) return; //AdamN: giving delays to prevent too much slowdown, but could cause instability + //linktime=0; + //if(GetSIOMode(siocnt, rcnt)!=MULTIPLAYER) return; //AdamN: skip if it's not MP mode + /*sent = false; + u16outbuffer[0] = 0; + u16outbuffer[1] = 0; + notblock = 1; + for(int i=1;i<=lanlink.numgbas;i++) { + ioctlsocket(ls.tcpsocket[i], FIONBIO, ¬block); //AdamN: temporarily use non-blocking for sending multiple data at once + setsockopt(ls.tcpsocket[i], IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + sent|=(send(ls.tcpsocket[i], outbuffer, 1, 0)>=0); //AdamN: sending dummies packet to prevent timeout when client tried to read socket w/ an empty buffer + }*/ + if(!linkdatarecvd[0]) return; //AdamN: don't read if it's not starting a cycle yet + bool last = true; + int ready = 0; + for(int i=1;i<=lanlink.numgbas;i++) last&=linkdatarecvd[i]; + if(!last) { + fdset.fd_count = lanlink.numgbas; + notblock = 0; + for(int i=1; i<=lanlink.numgbas; i++) { + fdset.fd_array[i-1] = ls.tcpsocket[i]; + ioctlsocket(ls.tcpsocket[i], FIONBIO, ¬block); //AdamN: temporarily use blocking for reading + int ern=WSAGetLastError(); + if(ern!=0) log("IOCTL30 Error: %d\n",ern); + } + wsocktimeout.tv_sec = linktimeout / 1000; + wsocktimeout.tv_usec = linktimeout % 1000; //0; //AdamN: remainder should be set also isn't? + ready = select(0, &fdset, NULL, NULL, &wsocktimeout); //AdamN: this will blocks executions but allows Client to be paused w/o causing disconnection on Server + if(ready<=0){ //AdamN: may cause noticible delay, result can also be SOCKET_ERROR, Select seems to be needed to maintain stability + int ern=WSAGetLastError(); + if(ern!=0) { + lanlink.connected = false; + log("%sCR[%d]\n",(LPCTSTR)LogStr,ern); + } + return; + } + } + if(ready>0) + do { + for(int i=1; i<=lanlink.numgbas; i++) { + notblock = 0; + ioctlsocket(ls.tcpsocket[i], FIONBIO, ¬block); //AdamN: temporarily use blocking for reading + int ern=WSAGetLastError(); + if(ern!=0) log("IOCTL3 Error: %d\n",ern); + /*fdset.fd_count = 1; + fdset.fd_array[0] = ls.tcpsocket[i]; + wsocktimeout.tv_sec = linktimeout / 1000; + wsocktimeout.tv_usec = linktimeout % 1000; //0; //AdamN: remainder should be set also isn't? + if(select(0, &fdset, NULL, NULL, &wsocktimeout)<=0){ //AdamN: may cause noticible delay, result can also be SOCKET_ERROR, Select seems to be needed to maintain stability + int ern=WSAGetLastError(); + if(ern!=0) { + lanlink.connected = false; + log("%sCR%d[%d]\n",(LPCTSTR)LogStr,i,ern); + } + return; + }*/ + arg = 0; + if(ioctlsocket(ls.tcpsocket[i], FIONREAD, &arg)!=0) { + int ern=WSAGetLastError(); //AdamN: this seems to get ern=10038(invalid socket handle) often + if(ern!=0) { + log("%sSR-%d[%d]\n",(LPCTSTR)LogStr,i,ern); + char message[40]; + lanlink.connected = false; + sprintf(message, _T("Player %d disconnected."), i+1); + MessageBox(NULL, message, _T("Link"), MB_OK); + break; + } + continue; //break; + } + if(arg>0) { + numbytes = 0; + int sz=recv(ls.tcpsocket[i], inbuffer, 1, 0); //AdamN: read the size of packet + int ern=WSAGetLastError(); + if(ern!=0) { + if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065) lanlink.connected = false; + #ifdef GBA_LOGGING + if(ern!=10060) + if(systemVerbose & VERBOSE_LINK) { + log("%sSRecv1-%d Error: %d\n",(LPCTSTR)LogStr,i,ern); + } + #endif + } + if(sz > 0) { + sz = inbuffer[0]; + UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) & 0xfffe); //AdamN: LOWering bit.0 (SC), not really needed here + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) | 0x0080); //AdamN: Activate bit.7 (Start/Busy), not really needed here + } + numbytes++; + while(numbytes<=sz) { + int cnt=recv(ls.tcpsocket[i], inbuffer+numbytes, (sz-numbytes)+1, 0); + int ern=WSAGetLastError(); + if(ern!=0 || cnt<=0) { + log("%sSRecv2-%d Error: %d\n",(LPCTSTR)LogStr,i,ern); + if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065 || cnt<=0) lanlink.connected = false; + break; + } + numbytes+=cnt; + } + recvd=(numbytes>1); + //done: check sender id and update linkdata+register + if(recvd) { + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSRecv2 Size : %d %s\n", (LPCTSTR)LogStr, numbytes, (LPCTSTR)DataHex(inbuffer,numbytes)); + } + #endif + int sid = inbuffer[1]; //AdamN: sender ID + linkdata[sid] = u16inbuffer[1]; //AdamN: received data, u16inbuffer[1] points to inbuffer[2] + //if(!sid) //AdamN: if sender is parent then it's new cycle, not possible here + // for(int i=0; i<=lanlink.numgbas; i++) linkdatarecvd[i] = false; + linkdatarecvd[sid] = true; + UPDATE_REG((sid << 1)+COMM_SIOMULTI0, linkdata[sid]); //AdamN: SIOMULTI0 (16bit data received from master/server) + //UPDATE_REG(COMM_SIOCNT, (READ16LE(&ioMem[COMM_SIOCNT]) & 0xffcf) | (linkid << 4)); //AdamN: Set ID bit.4-5 not needed for server as it's already set in StartLink + //UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff7f); //AdamN: Deactivate bit.7 (Start/Busy), should be done after a cycle finished + //value &= 0xff7f; + //value |= (sent != 0) << 7; + // + sent = false; + //todo: Master: broadcast received data to other GBAs + outbuffer[0] = 3; //AdamN: size of packet + outbuffer[1] = sid; //AdamN: Sender ID + u16outbuffer[1] = linkdata[sid]; + for(int j=1; j<=lanlink.numgbas; j++) + if(j!=i && j!=sid) { + notblock = 0; //1 + ioctlsocket(ls.tcpsocket[j], FIONBIO, ¬block); //AdamN: temporarily use non-blocking for sending multiple data at once + int ern=WSAGetLastError(); + if(ern!=0) log("IOCTL31 Error: %d\n",ern); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSSend1 to : %d Size : %d %s\n", (LPCTSTR)LogStr, j, 4, (LPCTSTR)DataHex(outbuffer,4)); + } + #endif + setsockopt(ls.tcpsocket[j], IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + sent|=(send(ls.tcpsocket[j], outbuffer, 4, 0)>=0); + ern=WSAGetLastError(); + if(ern!=0) { + log("Send-%d Error: %d\n",j,ern); + if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065) lanlink.connected = false; + if(!lanlink.connected) break; + } + if(sent) { + //UPDATE_REG((linkid << 1)+COMM_SIOMULTI0, linkdata[linkid]); + //UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) & 0xfff7); //AdamN: LOWering SO bit.3 (RCNT or SIOCNT?) + linkdatarecvd[j] = true; + } + } + } + } + } + last = true; + for(int i=0; i<=lanlink.numgbas; i++) last &= linkdatarecvd[i]; + if(last) { //AdamN: cycle ends + //UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) | 0x0001); //AdamN: HIGHering bit.0 (SC), only needed for Master/Server? + //UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) | 0x0009 /*8*/); //AdamN: HIGHering SO bit.3 (RCNT or SIOCNT?) + UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) | 0x0003/*b*/); //AdamN: HIGHering SC,SD,SO (apparently SD need to be High also for Master), Does Master checking SC when not using IRQ also? + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff7f); //AdamN: Deactivate bit.7 (Start/Busy) + //AdamN: Trigger Interrupt if Enabled + if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) { //IRQ Enable + IF |= 0x80; //Serial Communication + UPDATE_REG(0x202, IF); //Interrupt Request Flags / IRQ Acknowledge + } + for(int i=0; i<=lanlink.numgbas; i++) linkdatarecvd[i]=false; //AdamN: does it need to reset these? + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + //if(READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) + log("SIO : %04X %04X %04X %04X %04X %04X (VCOUNT = %d) %d\n", READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), READ16LE(&ioMem[COMM_SIOMULTI0]), READ16LE(&ioMem[COMM_SIOMULTI1]), READ16LE(&ioMem[COMM_SIOMULTI2]), READ16LE(&ioMem[COMM_SIOMULTI3]), VCOUNT, GetTickCount() ); + } + #endif + } else if(sent) UPDATE_REG(COMM_RCNT, READ16LE(&ioMem[COMM_RCNT]) & 0xfff7); //AdamN: LOWering SO bit.3 (RCNT or SIOCNT?) + } while(!last && lanlink.connected); + UPDATE_REG(COMM_SIOCNT, (READ16LE(&ioMem[COMM_SIOCNT]) & 0xffcf) | (linkid << 4)); //AdamN: Set ID bit.4-5, needed for Mario Kart + } + } + return; + } + return; +} + +void LinkRFUUpdate() +{ + if(!lanlink.active) { //Single Comp + if ((rfu_cmd==0xa5||rfu_cmd==0xa7||rfu_cmd==0x28) && (READ16LE(&ioMem[COMM_SIOCNT]) & 0x80) && !transfer) { + bool ok = false; + if(rfu_cmd==0xa5 && rfu_transfer_end <= 0) ok=true; //AdamN: workaround for single comp, to prevent the game getting timeout due to both side waiting for incoming data (as incoming data aren't queued) + else + for(int i=0; inumgbas; i++) + if(i!=vbaid) { + WaitForSingleObject(linksync[i], linktimeout); + ResetEvent(linksync[i]); + if(linkmem->rfu_q[i]>0 && ((linkmem->rfu_qid[i]&(1<rfu_qid[i]==(vbaid<<3)+0x61f1))) {ok=true;SetEvent(linksync[i]);break;} + SetEvent(linksync[i]); + } + + if(ok) { + rfu_cmd = 0x28; + transfer = 1; + rfu_qrecv = 0; + rfu_transfer_end = 0; + /*u16 value = READ16LE(&ioMem[COMM_SIOCNT]); + if (value & 8) //Transfer Enable Flag Send (bit.3, 1=Disable Transfer/Not Ready) + value &= 0xfffb; //Transfer enable flag receive (0=Enable Transfer/Ready, bit.2=bit.3 of otherside) // A kind of acknowledge procedure + else //(Bit.3, 0=Enable Transfer/Ready) + value |= 4; //bit.2=1 (otherside is Not Ready) + UPDATE_REG(COMM_SIOCNT, value);*/ + UPDATE_REG(COMM_SIODATA32_H, 0x9966); + UPDATE_REG(COMM_SIODATA32_L, (rfu_qrecv<<8) | rfu_cmd); + } + } + } else //Network + if(lanlink.connected) { + + } +} + // Windows threading is within! void LinkUpdate(int ticks) { + BOOL recvd = false; + BOOL sent = false; linktime += ticks; if (rfu_enabled) { linktime2 += ticks; // linktime2 is unused! rfu_transfer_end -= ticks; + + LinkRFUUpdate(); + if (transfer && rfu_transfer_end <= 0) { transfer = 0; @@ -289,19 +1101,26 @@ void LinkUpdate(int ticks) IF |= 0x80; UPDATE_REG(0x202, IF); } - UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff7f); + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff7f); //Start bit.7 reset + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + log("SIOn32 : %04X %04X %08X (VCOUNT = %d) %d %d\n", READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), READ32LE(&ioMem[COMM_SIODATA32_L]), VCOUNT, GetTickCount(), linktime2 ); + } + #endif } return; } - if (lanlink.active) + if (lanlink.active) //On Networks { if (lanlink.connected) { if (after) { - if (linkid && linktime > 6044) { - lc.Recv(); + if (linkid && linktime > 6044) { + LogStrPush("LinkUpd1"); + recvd = lc.Recv(); //AdamN: not sure who need this, never reach here? + LogStrPop(8); oncewait = true; } else @@ -312,25 +1131,29 @@ void LinkUpdate(int ticks) { linkdata[linkid] = READ16LE(&ioMem[COMM_SIODATA8]); - if (!lc.oncesend) - lc.Send(); - + if (!lc.oncesend) { + LogStrPush("LinkUpd2"); + sent = lc.Send(); //AdamN: Client need to send this often + LogStrPop(8); + } lc.oncesend = false; - UPDATE_REG(COMM_SIODATA32_L, linkdata[0]); + if(sent) { //AdamN: no need to check sent here ? + UPDATE_REG(COMM_SIODATA32_L, linkdata[0]); //AdamN: linkdata[0] instead of linkdata[linkid]? UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) | 0x80); transfer = 1; if (lc.numtransfers==1) linktime = 0; else linktime -= savedlinktime; + } } if (transfer && linktime >= trtimeend[lanlink.numgbas-1][tspeed]) { - if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) + if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) //IRQ Enable { - IF |= 0x80; - UPDATE_REG(0x202, IF); + IF |= 0x80; //Serial Communication + UPDATE_REG(0x202, IF); //Interrupt Request Flags / IRQ Acknowledge } UPDATE_REG(COMM_SIOCNT, (READ16LE(&ioMem[COMM_SIOCNT]) & 0xff0f) | (linkid << 4)); @@ -338,35 +1161,57 @@ void LinkUpdate(int ticks) linktime -= trtimeend[lanlink.numgbas-1][tspeed]; oncewait = false; - if (!lanlink.speed) + if (!lanlink.speed) //lanlink.speed = speedhack { - if (linkid) - lc.Recv(); - else - ls.Recv(); // WTF is the point of this? - + if (linkid) { //Slave/Client + LogStrPush("LinkUpd3"); + recvd = lc.Recv(); //AdamN: Client need to read this often + LogStrPop(8); + } else { //Master/Server + LogStrPush("LinkUpd4"); + recvd = ls.Recv(); // WTF is the point of this? + //AdamN: This is often Needed for Server/Master to works properly, This seems also to be the cause of stop responding on server when a client closed down after connection established, but the infinite loop was occuring inside ls.Recv(); (fixed tho) however, timeouts inside ls.Recv() can still cause lag + LogStrPop(8); + } + if(recvd) { UPDATE_REG(COMM_SIODATA32_H, linkdata[1]); UPDATE_REG(0x124, linkdata[2]); UPDATE_REG(0x126, linkdata[3]); oncewait = true; + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + //if(((READ16LE(&ioMem[COMM_SIOCNT]) >> 4) & 3) != linkid) + //if(READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) + log("SIOm16 : %04X %04X %04X %04X %04X %04X (VCOUNT = %d) %d %d\n", READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), READ16LE(&ioMem[COMM_SIOMULTI0]), READ16LE(&ioMem[COMM_SIOMULTI1]), READ16LE(&ioMem[COMM_SIOMULTI2]), READ16LE(&ioMem[COMM_SIOMULTI3]), VCOUNT, GetTickCount(), savedlinktime ); + } + #endif + } } else { - + if(recvd) { //AdamN: should this be checked for linkdata consistency? after = true; - if (lanlink.numgbas == 1) + if (lanlink.numgbas == 1) //AdamN: only for 2 players? { UPDATE_REG(COMM_SIODATA32_H, linkdata[1]); UPDATE_REG(0x124, linkdata[2]); UPDATE_REG(0x126, linkdata[3]); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_SIO) { + //if(((READ16LE(&ioMem[COMM_SIOCNT]) >> 4) & 3) != linkid) + //if(READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) + log("SIOm16 : %04X %04X %04X %04X %04X %04X (VCOUNT = %d) %d %d\n", READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), READ16LE(&ioMem[COMM_SIOMULTI0]), READ16LE(&ioMem[COMM_SIOMULTI1]), READ16LE(&ioMem[COMM_SIOMULTI2]), READ16LE(&ioMem[COMM_SIOMULTI3]), VCOUNT, GetTickCount(), savedlinktime ); + } + #endif + } } } } } return; - } + } // ** CRASH ** linkmem is NULL, todo investigate why, added null check - if (linkid && !transfer && linkmem && linktime >= linkmem->lastlinktime && linkmem->numtransfers) + if (linkid && !transfer && linkmem && linktime >= linkmem->lastlinktime && linkmem->numtransfers) //On Single Computer (link.active=false) { linkmem->linkdata[linkid] = READ16LE(&ioMem[COMM_SIODATA8]); @@ -385,7 +1230,7 @@ void LinkUpdate(int ticks) tspeed = (linkmem->linkcmd[0]) & 3; transfer = 1; WRITE32LE(&ioMem[COMM_SIODATA32_L], 0xffffffff); - WRITE32LE(&ioMem[0x124], 0xffffffff); + WRITE32LE(&ioMem[0x124], 0xffffffff); //COMM_SIOMULTI2 UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) | 0x80); break; } @@ -441,15 +1286,875 @@ inline int GetSIOMode(u16 siocnt, u16 rcnt) switch (siocnt & 0x3000) { case 0x0000: return NORMAL8; case 0x1000: return NORMAL32; - case 0x2000: return MULTIPLAYER; - case 0x3000: return UART; + case 0x2000: return MULTIPLAYER; //AdamN: 16bit + case 0x3000: return UART; //AdamN: 8bit } - } + }else //AdamN: added ELSE to make sure bit.15 is 1 as there is no default handler above - if (rcnt & 0x4000) + if (rcnt & 0x4000) //AdamN: Joybus is (rcnt & 0xC000)==0xC000 return JOYBUS; - return GP; + return GP; //AdamN: GeneralPurpose is (rcnt & 0xC000)==0x8000 +} + +u16 StartRFU2(u16 value) +{ + static char inbuffer[1028], outbuffer[1028]; + u16 *u16inbuffer = (u16*)inbuffer; + u16 *u16outbuffer = (u16*)outbuffer; + u32 *u32inbuffer = (u32*)inbuffer; + u32 *u32outbuffer = (u32*)outbuffer; + static int outsize, insize; + static int gbaid = 0; + int initid = 0; + int ngbas = 0; + BOOL recvd = false; + bool ok = false; + + switch (GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT]))) { + case NORMAL8: + rfu_polarity = 0; + return value; + break; + + case NORMAL32: + /*if (value & 8) //Transfer Enable Flag Send (bit.3, 1=Disable Transfer/Not Ready) + value &= 0xfffb; //Transfer enable flag receive (0=Enable Transfer/Ready, bit.2=bit.3 of otherside) // A kind of acknowledge procedure + else //(Bit.3, 0=Enable Transfer/Ready) + value |= 4; //bit.2=1 (otherside is Not Ready)*/ + + if (value & 0x80) //start/busy bit + { + value |= 0x02; //wireless always use 2Mhz speed right? + + if ((value & 3) == 1) //internal clock w/ 256KHz speed + rfu_transfer_end = 2048; + else //external clock or any clock w/ 2MHz speed + rfu_transfer_end = 0; //256; //0 + + u16 a = READ16LE(&ioMem[COMM_SIODATA32_H]); + + switch (rfu_state) { + case RFU_INIT: + if (READ32LE(&ioMem[COMM_SIODATA32_L]) == 0xb0bb8001) { + rfu_state = RFU_COMM; // end of startup + //WaitForSingleObject(linksync[vbaid], linktimeout); + //ResetEvent(linksync[vbaid]); + rfudata.rfu_q[vbaid] = 0; + rfudata.rfu_qid[vbaid] = 0; + rfudata.rfu_request[vbaid] = 0; + rfudata.rfu_reqid[vbaid] = 0; + rfudata.rfu_bdata[vbaid][0] = 0; + numtransfers = 0; + rfu_masterq = 0; + //SetEvent(linksync[vbaid]); //vbaid + } + UPDATE_REG(COMM_SIODATA32_H, READ16LE(&ioMem[COMM_SIODATA32_L])); + UPDATE_REG(COMM_SIODATA32_L, a); + break; + + case RFU_COMM: + if (a == 0x9966) //initialize command + { + rfu_cmd = ioMem[COMM_SIODATA32_L]; + if ((rfu_qsend=ioMem[COMM_SIODATA32_L+1]) != 0) { //COMM_SIODATA32_L+1, following word size + rfu_state = RFU_SEND; + rfu_counter = 0; + } + if (rfu_cmd == 0x25 || rfu_cmd == 0x24) { //send data + rfu_masterq = rfu_qsend; + } else + if(rfu_cmd == 0xa8) { + rfu_id = (gbaid<<3)+0x61f1; + } + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("CMD1 : %08X %d\n", READ32LE(&ioMem[COMM_SIODATA32_L]), GetTickCount()); + } + #endif + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + } + else if (a == 0x8000) //finalize command + { + switch (rfu_cmd) { + case 0x1a: // check if someone joined + rfu_qrecv = 0; + ok = false; + initid = gbaid; + do { + gbaid++; + gbaid %= linkmem->numgbas; + WaitForSingleObject(linksync[gbaid], linktimeout); //wait until this gba allowed to move + ResetEvent(linksync[gbaid]); + ok = (gbaid!=initid && (gbaid==vbaid || (linkmem->rfu_request[gbaid]==0 || linkmem->rfu_reqid[gbaid]!=(vbaid<<3)+0x61f1))); + SetEvent(linksync[gbaid]); + } while(ok); //only include connected gbas + //rfu_id = (gbaid<<3)+0x61f1; + WaitForSingleObject(linksync[gbaid], linktimeout); //wait until this gba allowed to move + ResetEvent(linksync[gbaid]); + if(linkmem->rfu_reqid[gbaid]==(vbaid<<3)+0x61f1 && linkmem->rfu_request[gbaid]) { + rfu_masterdata[rfu_qrecv] = (gbaid<<3)+0x61f1; + rfu_qrecv++; + //linkmem->rfu_request[gbaid] = 0; //to prevent receiving the same request, it seems the same request need to be received more than once + } + SetEvent(linksync[gbaid]); + + if(rfu_qrecv!=0) { + rfu_state = RFU_RECV; + rfu_counter = 0; + + rfu_id = rfu_masterdata[rfu_qrecv-1]; + gbaid = (rfu_id-0x61f1)>>3; + } + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x1e: // receive broadcast data + case 0x1d: // no visible difference + rfu_polarity = 0; + rfu_state = RFU_RECV; + rfu_counter = 0; + rfu_qrecv = 0; //7*(linkmem->numgbas-1); + //int ctr = 0; + for(int i=0; inumgbas; i++) { + WaitForSingleObject(linksync[i], linktimeout); //wait until this gba allowed to move + ResetEvent(linksync[i]); //don't allow this gba to move (send data) + if(i!=vbaid && linkmem->rfu_bdata[i][0]!=0) + //if(linkmem->rfu_gdata[i]==linkmem->rfu_gdata[vbaid]) //only matching game id will be shown + { + memcpy(&rfu_masterdata[rfu_qrecv],linkmem->rfu_bdata[i],sizeof(linkmem->rfu_bdata[i])); + rfu_qrecv+=sizeof(linkmem->rfu_bdata[i]); + } + SetEvent(linksync[i]); + } + rfu_qrecv = rfu_qrecv >> 2; + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x11: // ? always receives 0xff - I suspect it's something for 3+ players + rfu_qrecv = 0; + if(rfu_qrecv==0) { //not the host + rfu_qrecv++; + } + if(rfu_qrecv!=0) + rfu_state = RFU_RECV; + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x13: // unknown + case 0x14: // unknown, seems to have something to do with 0x11 + case 0x20: // this has something to do with 0x1f + case 0x21: // this too + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + rfu_polarity = 0; + rfu_state = RFU_RECV; + rfu_qrecv = 1; + break; + + case 0x24: // send data (broadcast to all connected adapters?) + if((numtransfers++)==0) linktime = 1; //numtransfers doesn't seems to be used? + WaitForSingleObject(linksync[vbaid], linktimeout); //wait until the last sent data has been received/read + ResetEvent(linksync[vbaid]); //mark it as unread/unreceived data + linkmem->rfu_linktime[vbaid] = linktime; + linkmem->rfu_q[vbaid] = rfu_masterq; //linkmem->rfu_q[vbaid]; + memcpy(linkmem->rfu_data[vbaid],rfu_masterdata,rfu_masterq<<2); + linkmem->rfu_qid[vbaid] = 0; //rfu_id; //mark to whom the data for, 0=to all connected gbas + SetEvent(linksync[vbaid]); + //log("Send%02X[%d] : %02X %0s\n", rfu_cmd, GetTickCount(), rfu_masterq*4, (LPCTSTR)DataHex((char*)rfu_masterdata,rfu_masterq*4) ); + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + linktime = 0; + break; + + case 0x25: // send (to currently connected adapter) & wait for data reply + if((numtransfers++)==0) linktime = 1; //numtransfers doesn't seems to be used? + WaitForSingleObject(linksync[vbaid], linktimeout); //wait until last sent data received + ResetEvent(linksync[vbaid]); //mark it as unread/unreceived + linkmem->rfu_linktime[vbaid] = linktime; + linkmem->rfu_q[vbaid] = rfu_masterq; //linkmem->rfu_q[vbaid]; + memcpy(linkmem->rfu_data[vbaid],rfu_masterdata,rfu_masterq<<2); //(rfu_counter+1)<<2 //linkmem->rfu_q[vbaid]<<2 //rfu_qsend<<2 + linkmem->rfu_qid[vbaid] = rfu_id; //mark to whom the data for + SetEvent(linksync[vbaid]); + //log("Send%02X[%d] : %02X %0s\n", rfu_cmd, GetTickCount(), rfu_masterq*4, (LPCTSTR)DataHex((char*)rfu_masterdata,rfu_masterq*4) ); + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x26: //receive data + //read data from currently connected adapter + WaitForSingleObject(linksync[gbaid], linktimeout); + ResetEvent(linksync[gbaid]); //to prevent data being changed while still being read + if((linkmem->rfu_qid[gbaid]==(vbaid<<3)+0x61f1)||(linkmem->rfu_qid[gbaid]<0x61f1 && !(linkmem->rfu_qid[gbaid] & (1<rfu_q[gbaid]; + memcpy(rfu_masterdata, linkmem->rfu_data[gbaid], rfu_masterq<<2); //128 //sizeof(rfu_masterdata) + } else rfu_masterq = 0; + SetEvent(linksync[gbaid]); //mark it as received + rfu_qrecv = rfu_masterq; //rfu_cacheq; + + if(rfu_qrecv!=0) + { + rfu_state = RFU_RECV; + rfu_counter = 0; + + WaitForSingleObject(linksync[gbaid], linktimeout); + ResetEvent(linksync[gbaid]); + if(linkmem->rfu_qid[gbaid]>=0x61f1) //only when the data intended for this gba + linkmem->rfu_q[gbaid] = 0; else linkmem->rfu_qid[gbaid] |= (1<rfu_q[vbaid] = 0; + linkmem->rfu_qid[vbaid] = 0; + linkmem->rfu_request[vbaid] = 0; + linkmem->rfu_reqid[vbaid] = 0; + linkmem->rfu_bdata[vbaid][0] = 0; + SetEvent(linksync[vbaid]); //vbaid + numtransfers = 0; + rfu_masterq = 0; + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x16: // send broadcast data (ie. broadcast room name) + //copy bdata to all gbas + { + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); + linkmem->rfu_bdata[vbaid][0] = (vbaid<<3)+0x61f1; + memcpy(&linkmem->rfu_bdata[vbaid][1],rfu_masterdata,sizeof(linkmem->rfu_bdata[vbaid])-4); + SetEvent(linksync[vbaid]); + } + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x17: // setup or something ? (broadcast game id?) + //copy gdata to all gbas + { + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); + linkmem->rfu_gdata[vbaid] = rfu_masterdata[0]; + SetEvent(linksync[vbaid]); + } + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x1f: // pick/join a server + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x10: // init? + case 0x3d: // init? + rfu_qrecv = 1; + rfu_state = RFU_RECV; + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x19: //? + case 0x1c: //?? + case 0x27: // wait for data ? + default: + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0xa5: // 2nd part of send&wait function 0x25 + case 0xa7: // 2nd part of wait function 0x27 + case 0x28: + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); //to make sure nobody changing it until it finishes reading it + rfu_transfer_end = linkmem->rfu_linktime[vbaid] - linktime + 256; + SetEvent(linksync[vbaid]); //mark it as received + + //if (rfu_transfer_end < 256) + // rfu_transfer_end = 256; + + linktime = -rfu_transfer_end; + rfu_transfer_end = 2048; //256 //0 //might fasten the response (but slowdown performance), but if incoming data isn't ready the game might get timeout faster + rfu_polarity = 1; + //check if there is incoming data for this gba + ok = false; + for(int i=0; inumgbas; i++) + if(i!=vbaid) { + WaitForSingleObject(linksync[i], linktimeout); + ResetEvent(linksync[i]); + if(linkmem->rfu_q[i]>0 && ((linkmem->rfu_qid[i]&(1<rfu_qid[i]==(vbaid<<3)+0x61f1))) {ok=true;SetEvent(linksync[i]);break;} + SetEvent(linksync[i]); + } + if(!ok) return value; //don't change anything yet if there is no incoming data + rfu_cmd = 0x28; + break; + } + UPDATE_REG(COMM_SIODATA32_H, 0x9966); + UPDATE_REG(COMM_SIODATA32_L, (rfu_qrecv<<8) | rfu_cmd); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + static CString st = _T(""); + if(rfu_masterq>0 && (rfu_cmd>=0xa4 && rfu_cmd<=0xa6)) + st = DataHex((char*)rfu_masterdata, rfu_masterq<<2); else st = _T(""); + log("CMD2 : %08X %d %s\n", READ32LE(&ioMem[COMM_SIODATA32_L]), GetTickCount(), (LPCTSTR)st); + st = _T(""); + } + #endif + + } else { //unknown command word + + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + } + break; + + case RFU_SEND: + if(--rfu_qsend == 0) { + rfu_state = RFU_COMM; + } + + switch (rfu_cmd) { + case 0x16: //broadcast room data + rfu_masterdata[rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + + case 0x17: //set game id? + rfu_masterdata[0] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + + case 0x1f: //join a room + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); + linkmem->rfu_reqid[vbaid] = READ16LE(&ioMem[COMM_SIODATA32_L]); //(rfu_id-0x61f1)>>3 + linkmem->rfu_request[vbaid] = 1; + rfu_id = READ16LE(&ioMem[COMM_SIODATA32_L]); + gbaid = (rfu_id-0x61f1)>>3; + SetEvent(linksync[vbaid]); + break; + + case 0x24: //send data + case 0x25: //send & wait + rfu_masterdata[rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + } + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + break; + + case RFU_RECV: + if (--rfu_qrecv == 0) { + rfu_state = RFU_COMM; + } + + switch (rfu_cmd) { + case 0x9d: + case 0x9e: + UPDATE_REG(COMM_SIODATA32_L, rfu_masterdata[rfu_counter]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, rfu_masterdata[rfu_counter++]>>16); + break; + + case 0xa6: + UPDATE_REG(COMM_SIODATA32_L, rfu_masterdata[rfu_counter]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, rfu_masterdata[rfu_counter++]>>16); + break; + + case 0x90: + case 0xbd: + UPDATE_REG(COMM_SIODATA32_L, (vbaid<<3)+0x61f1); //this adapter id? + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + case 0x93: // it seems like the game doesn't care about this value //vendor id? + UPDATE_REG(COMM_SIODATA32_L, 0x1234); // put anything in here + UPDATE_REG(COMM_SIODATA32_H, 0x0200); // also here, but it should be 0200 + break; + + case 0xa0: + case 0xa1: + UPDATE_REG(COMM_SIODATA32_L, (vbaid<<3)+0x61f1/*0x641b*/); //this adapter id? + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + case 0x9a: + UPDATE_REG(COMM_SIODATA32_L, rfu_masterdata[rfu_counter]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, rfu_masterdata[rfu_counter++]>>16); + break; + + case 0x91: + UPDATE_REG(COMM_SIODATA32_L, 0x00ff); //signal strength, more than 0xff seems to be invalid signal + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + case 0x94: + UPDATE_REG(COMM_SIODATA32_L, rfu_id); //current active connected adapter ID? + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + default: + UPDATE_REG(COMM_SIODATA32_L, 0x0001/*0x0173*/); //not 0x0000 as default? + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + } + break; + } + transfer = 1; + } + + //if(transfer) //&& rfu_state!=RFU_INIT + if (value & 8) //Transfer Enable Flag Send (bit.3, 1=Disable Transfer/Not Ready) + value &= 0xfffb; //Transfer enable flag receive (0=Enable Transfer/Ready, bit.2=bit.3 of otherside) // A kind of acknowledge procedure + else //(Bit.3, 0=Enable Transfer/Ready) + value |= 4; //bit.2=1 (otherside is Not Ready) + + if (rfu_polarity) + value ^= 4; // sometimes it's the other way around + + default: //other SIO modes + return value; + } +} + +u16 StartRFU3(u16 value) +{ + static char inbuffer[1028], outbuffer[1028]; + u16 *u16inbuffer = (u16*)inbuffer; + u16 *u16outbuffer = (u16*)outbuffer; + u32 *u32inbuffer = (u32*)inbuffer; + u32 *u32outbuffer = (u32*)outbuffer; + static int outsize, insize; + static int gbaid = 0; + int initid = 0; + int ngbas = 0; + BOOL recvd = false; + bool ok = false; + + switch (GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT]))) { + case NORMAL8: + rfu_polarity = 0; + return value; + break; + + case NORMAL32: + /*if (value & 8) //Transfer Enable Flag Send (bit.3, 1=Disable Transfer/Not Ready) + value &= 0xfffb; //Transfer enable flag receive (0=Enable Transfer/Ready, bit.2=bit.3 of otherside) // A kind of acknowledge procedure + else //(Bit.3, 0=Enable Transfer/Ready) + value |= 4; //bit.2=1 (otherside is Not Ready)*/ + + if (value & 0x80) //start/busy bit + { + value |= 0x02; //wireless always use 2Mhz speed right? + + if ((value & 3) == 1) //internal clock w/ 256KHz speed + rfu_transfer_end = 2048; + else //external clock or any clock w/ 2MHz speed + rfu_transfer_end = 0; //256; //0 + + u16 a = READ16LE(&ioMem[COMM_SIODATA32_H]); + + switch (rfu_state) { + case RFU_INIT: + if (READ32LE(&ioMem[COMM_SIODATA32_L]) == 0xb0bb8001) { + rfu_state = RFU_COMM; // end of startup + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); + linkmem->rfu_q[vbaid] = 0; + linkmem->rfu_qid[vbaid] = 0; + linkmem->rfu_request[vbaid] = 0; + linkmem->rfu_reqid[vbaid] = 0; + linkmem->rfu_bdata[vbaid][0] = 0; + numtransfers = 0; + rfu_masterq = 0; + SetEvent(linksync[vbaid]); //vbaid + } + /*if((value & 0x1081)==0x1080) { //Pre-initialization (used to detect whether wireless adapter is available or not) + UPDATE_REG(COMM_SIODATA32_L, 0x494e); //0x494e + UPDATE_REG(COMM_SIODATA32_H, 0x0000); //0x0000 + rfu_transfer_end = 256; + transfer = 1; + if (value & 8) //Transfer Enable Flag Send (bit.3, 1=Disable Transfer/Not Ready) + value &= 0xfffb; //Transfer enable flag receive (0=Enable Transfer/Ready, bit.2=bit.3 of otherside) // A kind of acknowledge procedure + else //(Bit.3, 0=Enable Transfer/Ready) + value |= 4; //bit.2=1 (otherside is Not Ready) + value |= 0x02; // (value & 0xff7f)|0x02; //bit.1 need to be Set(2Mhz) otherwise some games might not be able to detect wireless adapter existance + return value; + } else*/ { + UPDATE_REG(COMM_SIODATA32_H, READ16LE(&ioMem[COMM_SIODATA32_L])); + UPDATE_REG(COMM_SIODATA32_L, a); + } + break; + + case RFU_COMM: + if (a == 0x9966) //initialize command + { + rfu_cmd = ioMem[COMM_SIODATA32_L]; + if ((rfu_qsend=ioMem[COMM_SIODATA32_L+1]) != 0) { //COMM_SIODATA32_L+1, following word size + rfu_state = RFU_SEND; + rfu_counter = 0; + } + if (rfu_cmd == 0x25 || rfu_cmd == 0x24) { //send data + rfu_masterq = rfu_qsend; + } else + if(rfu_cmd == 0xa8) { + WaitForSingleObject(linksync[vbaid], linktimeout); //wait until this gba allowed to move + ResetEvent(linksync[vbaid]); + if(linkmem->rfu_reqid[vbaid]==0) { //the host + SetEvent(linksync[vbaid]); + ok = false; + initid = gbaid; + do { + gbaid++; + gbaid %= linkmem->numgbas; + WaitForSingleObject(linksync[gbaid], linktimeout); //wait until this gba allowed to move + ResetEvent(linksync[gbaid]); + ok = (gbaid!=initid && (gbaid==vbaid || linkmem->rfu_reqid[gbaid]!=(vbaid<<3)+0x61f1)); + SetEvent(linksync[gbaid]); + } while (ok); //only include connected gbas + rfu_id = (gbaid<<3)+0x61f1; //(u16)linkmem->rfu_bdata[gbaid][0]; + } else { //not the host + rfu_id = linkmem->rfu_reqid[vbaid]; + gbaid = (rfu_id-0x61f1)>>3; + } + SetEvent(linksync[vbaid]); + } + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("CMD1 : %08X %d\n", READ32LE(&ioMem[COMM_SIODATA32_L]), GetTickCount()); + } + #endif + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + } + else if (a == 0x8000) //finalize command + { + switch (rfu_cmd) { + case 0x1a: // check if someone joined + rfu_qrecv = 0; + ok = false; + initid = gbaid; + do { + gbaid++; + gbaid %= linkmem->numgbas; + WaitForSingleObject(linksync[gbaid], linktimeout); //wait until this gba allowed to move + ResetEvent(linksync[gbaid]); + ok = (gbaid!=initid && (gbaid==vbaid || (linkmem->rfu_request[gbaid]==0 || linkmem->rfu_reqid[gbaid]!=(vbaid<<3)+0x61f1))); + SetEvent(linksync[gbaid]); + } while(ok); //only include connected gbas + //rfu_id = (gbaid<<3)+0x61f1; + WaitForSingleObject(linksync[gbaid], linktimeout); //wait until this gba allowed to move + ResetEvent(linksync[gbaid]); + if(linkmem->rfu_reqid[gbaid]==(vbaid<<3)+0x61f1 && linkmem->rfu_request[gbaid]) { + rfu_masterdata[rfu_qrecv] = (gbaid<<3)+0x61f1; + rfu_qrecv++; + //linkmem->rfu_request[gbaid] = 0; //to prevent receiving the same request, it seems the same request need to be received more than once + } + SetEvent(linksync[gbaid]); + + if(rfu_qrecv!=0) { + rfu_state = RFU_RECV; + rfu_counter = 0; + + rfu_id = rfu_masterdata[rfu_qrecv-1]; + gbaid = (rfu_id-0x61f1)>>3; + } + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x1e: // receive broadcast data + case 0x1d: // no visible difference + rfu_polarity = 0; + rfu_state = RFU_RECV; + rfu_counter = 0; + rfu_qrecv = 0; //7*(linkmem->numgbas-1); + //int ctr = 0; + for(int i=0; inumgbas; i++) { + WaitForSingleObject(linksync[i], linktimeout); //wait until this gba allowed to move + ResetEvent(linksync[i]); //don't allow this gba to move (send data) + if(i!=vbaid && linkmem->rfu_bdata[i][0]!=0) + //if(linkmem->rfu_gdata[i]==linkmem->rfu_gdata[vbaid]) //only matching game id will be shown + { + memcpy(&rfu_masterdata[rfu_qrecv],linkmem->rfu_bdata[i],sizeof(linkmem->rfu_bdata[i])); + rfu_qrecv+=sizeof(linkmem->rfu_bdata[i]); + } + SetEvent(linksync[i]); + } + rfu_qrecv = rfu_qrecv >> 2; + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x11: // ? always receives 0xff - I suspect it's something for 3+ players + rfu_qrecv = 0; + if(rfu_qrecv==0) { //not the host + rfu_qrecv++; + } + if(rfu_qrecv!=0) + rfu_state = RFU_RECV; + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x13: // unknown + case 0x14: // unknown, seems to have something to do with 0x11 + case 0x20: // this has something to do with 0x1f + case 0x21: // this too + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + rfu_polarity = 0; + rfu_state = RFU_RECV; + rfu_qrecv = 1; + break; + + case 0x24: // send data (broadcast to all connected adapters?) + if((numtransfers++)==0) linktime = 1; //numtransfers doesn't seems to be used? + WaitForSingleObject(linksync[vbaid], linktimeout); //wait until the last sent data has been received/read + ResetEvent(linksync[vbaid]); //mark it as unread/unreceived data + linkmem->rfu_linktime[vbaid] = linktime; + linkmem->rfu_q[vbaid] = rfu_masterq; //linkmem->rfu_q[vbaid]; + memcpy(linkmem->rfu_data[vbaid],rfu_masterdata,rfu_masterq<<2); + linkmem->rfu_qid[vbaid] = 0; //rfu_id; //mark to whom the data for, 0=to all connected gbas + SetEvent(linksync[vbaid]); + //log("Send%02X[%d] : %02X %0s\n", rfu_cmd, GetTickCount(), rfu_masterq*4, (LPCTSTR)DataHex((char*)rfu_masterdata,rfu_masterq*4) ); + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + linktime = 0; + break; + + case 0x25: // send (to currently connected adapter) & wait for data reply + if((numtransfers++)==0) linktime = 1; //numtransfers doesn't seems to be used? + WaitForSingleObject(linksync[vbaid], linktimeout); //wait until last sent data received + ResetEvent(linksync[vbaid]); //mark it as unread/unreceived + linkmem->rfu_linktime[vbaid] = linktime; + linkmem->rfu_q[vbaid] = rfu_masterq; //linkmem->rfu_q[vbaid]; + memcpy(linkmem->rfu_data[vbaid],rfu_masterdata,rfu_masterq<<2); //(rfu_counter+1)<<2 //linkmem->rfu_q[vbaid]<<2 //rfu_qsend<<2 + linkmem->rfu_qid[vbaid] = rfu_id; //mark to whom the data for + SetEvent(linksync[vbaid]); + //log("Send%02X[%d] : %02X %0s\n", rfu_cmd, GetTickCount(), rfu_masterq*4, (LPCTSTR)DataHex((char*)rfu_masterdata,rfu_masterq*4) ); + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x26: //receive data + //read data from currently connected adapter + WaitForSingleObject(linksync[gbaid], linktimeout); + ResetEvent(linksync[gbaid]); //to prevent data being changed while still being read + if((linkmem->rfu_qid[gbaid]==(vbaid<<3)+0x61f1)||(linkmem->rfu_qid[gbaid]<0x61f1 && !(linkmem->rfu_qid[gbaid] & (1<rfu_q[gbaid]; + memcpy(rfu_masterdata, linkmem->rfu_data[gbaid], rfu_masterq<<2); //128 //sizeof(rfu_masterdata) + } else rfu_masterq = 0; + SetEvent(linksync[gbaid]); //mark it as received + rfu_qrecv = rfu_masterq; //rfu_cacheq; + + if(rfu_qrecv!=0) + { + rfu_state = RFU_RECV; + rfu_counter = 0; + + WaitForSingleObject(linksync[gbaid], linktimeout); + ResetEvent(linksync[gbaid]); + if(linkmem->rfu_qid[gbaid]>=0x61f1) //only when the data intended for this gba + linkmem->rfu_q[gbaid] = 0; else linkmem->rfu_qid[gbaid] |= (1<rfu_q[vbaid] = 0; + linkmem->rfu_qid[vbaid] = 0; + linkmem->rfu_request[vbaid] = 0; + linkmem->rfu_reqid[vbaid] = 0; + linkmem->rfu_bdata[vbaid][0] = 0; + SetEvent(linksync[vbaid]); //vbaid + numtransfers = 0; + rfu_masterq = 0; + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x16: // send broadcast data (ie. broadcast room name) + //copy bdata to all gbas + { + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); + linkmem->rfu_bdata[vbaid][0] = (vbaid<<3)+0x61f1; + memcpy(&linkmem->rfu_bdata[vbaid][1],rfu_masterdata,sizeof(linkmem->rfu_bdata[vbaid])-4); + SetEvent(linksync[vbaid]); + } + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x17: // setup or something ? (broadcast game id?) + //copy gdata to all gbas + { + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); + linkmem->rfu_gdata[vbaid] = rfu_masterdata[0]; + SetEvent(linksync[vbaid]); + } + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x1f: // pick/join a server + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x10: // init? + case 0x3d: // init? + rfu_qrecv = 1; + rfu_state = RFU_RECV; + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0x19: //? + case 0x1c: //?? + case 0x27: // wait for data ? + default: + rfu_cmd |= 0x80; //rfu_cmd ^= 0x80 + break; + + case 0xa5: // 2nd part of send&wait function 0x25 + case 0xa7: // 2nd part of wait function 0x27 + case 0x28: + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); //to make sure nobody changing it until it finishes reading it + rfu_transfer_end = linkmem->rfu_linktime[vbaid] - linktime + 256; + SetEvent(linksync[vbaid]); //mark it as received + + //if (rfu_transfer_end < 256) + // rfu_transfer_end = 256; + + linktime = -rfu_transfer_end; + rfu_transfer_end = 2048; //256 //0 //might fasten the response (but slowdown performance), but if incoming data isn't ready the game might get timeout faster + rfu_polarity = 1; + //check if there is incoming data for this gba + ok = false; + for(int i=0; inumgbas; i++) + if(i!=vbaid) { + WaitForSingleObject(linksync[i], linktimeout); + ResetEvent(linksync[i]); + if(linkmem->rfu_q[i]>0 && ((linkmem->rfu_qid[i]&(1<rfu_qid[i]==(vbaid<<3)+0x61f1))) {ok=true;SetEvent(linksync[i]);break;} + SetEvent(linksync[i]); + } + if(!ok) return value; //don't change anything yet if there is no incoming data + rfu_cmd = 0x28; + break; + } + UPDATE_REG(COMM_SIODATA32_H, 0x9966); + UPDATE_REG(COMM_SIODATA32_L, (rfu_qrecv<<8) | rfu_cmd); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + static CString st = _T(""); + if(rfu_masterq>0 && (rfu_cmd>=0xa4 && rfu_cmd<=0xa6)) + st = DataHex((char*)rfu_masterdata, rfu_masterq<<2); else st = _T(""); + log("CMD2 : %08X %d %s\n", READ32LE(&ioMem[COMM_SIODATA32_L]), GetTickCount(), (LPCTSTR)st); + st = _T(""); + } + #endif + + } else { //unknown command word + + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + } + break; + + case RFU_SEND: + if(--rfu_qsend == 0) { + rfu_state = RFU_COMM; + } + + switch (rfu_cmd) { + case 0x16: //broadcast room data + rfu_masterdata[rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + + case 0x17: //set game id? + rfu_masterdata[0] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + + case 0x1f: //join a room + WaitForSingleObject(linksync[vbaid], linktimeout); + ResetEvent(linksync[vbaid]); + linkmem->rfu_reqid[vbaid] = READ16LE(&ioMem[COMM_SIODATA32_L]); //(rfu_id-0x61f1)>>3 + linkmem->rfu_request[vbaid] = 1; + rfu_id = READ16LE(&ioMem[COMM_SIODATA32_L]); + gbaid = (rfu_id-0x61f1)>>3; + SetEvent(linksync[vbaid]); + break; + + case 0x24: //send data + case 0x25: //send & wait + rfu_masterdata[rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + } + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + break; + + case RFU_RECV: + if (--rfu_qrecv == 0) { + rfu_state = RFU_COMM; + } + + switch (rfu_cmd) { + case 0x9d: + case 0x9e: + UPDATE_REG(COMM_SIODATA32_L, rfu_masterdata[rfu_counter]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, rfu_masterdata[rfu_counter++]>>16); + break; + + case 0xa6: + UPDATE_REG(COMM_SIODATA32_L, rfu_masterdata[rfu_counter]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, rfu_masterdata[rfu_counter++]>>16); + break; + + case 0x90: + case 0xbd: + UPDATE_REG(COMM_SIODATA32_L, (vbaid<<3)+0x61f1); //this adapter id? + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + case 0x93: // it seems like the game doesn't care about this value //vendor id? + UPDATE_REG(COMM_SIODATA32_L, 0x1234); // put anything in here + UPDATE_REG(COMM_SIODATA32_H, 0x0200); // also here, but it should be 0200 + break; + + case 0xa0: + case 0xa1: + UPDATE_REG(COMM_SIODATA32_L, (vbaid<<3)+0x61f1/*0x641b*/); //this adapter id? + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + case 0x9a: + UPDATE_REG(COMM_SIODATA32_L, rfu_masterdata[rfu_counter]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, rfu_masterdata[rfu_counter++]>>16); + break; + + case 0x91: + UPDATE_REG(COMM_SIODATA32_L, 0x00ff); //signal strength, more than 0xff seems to be invalid signal + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + case 0x94: + UPDATE_REG(COMM_SIODATA32_L, rfu_id); //current active connected adapter ID? + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + default: + UPDATE_REG(COMM_SIODATA32_L, 0x0001/*0x0173*/); //not 0x0000 as default? + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + } + break; + } + transfer = 1; + } + + //if(transfer) //&& rfu_state!=RFU_INIT + if (value & 8) //Transfer Enable Flag Send (bit.3, 1=Disable Transfer/Not Ready) + value &= 0xfffb; //Transfer enable flag receive (0=Enable Transfer/Ready, bit.2=bit.3 of otherside) // A kind of acknowledge procedure + else //(Bit.3, 0=Enable Transfer/Ready) + value |= 4; //bit.2=1 (otherside is Not Ready) + + if (rfu_polarity) + value ^= 4; // sometimes it's the other way around + + default: //other SIO modes + return value; + } } // The GBA wireless RFU (see adapter3.txt) @@ -463,16 +2168,18 @@ u16 StartRFU(u16 value) break; case NORMAL32: - if (value & 8) - value &= 0xfffb; // A kind of acknowledge procedure - else - value |= 4; + if (value & 8) //Transfer Enable Flag Send (bit.3, 1=Disable Transfer/Not Ready) + value &= 0xfffb; //Transfer enable flag receive (0=Enable Transfer/Ready, bit.2=bit.3 of otherside) // A kind of acknowledge procedure + else //(Bit.3, 0=Enable Transfer/Ready) + value |= 4; //bit.2=1 (otherside is Not Ready) - if (value & 0x80) + if (value & 0x80) //start/busy bit { - if ((value&3) == 1) + value |= 0x02; //wireless always use 2Mhz speed right? + + if ((value&3) == 1) //internal clock w/ 256KHz speed rfu_transfer_end = 2048; - else + else //external clock or any clock w/ 2MHz speed rfu_transfer_end = 256; u16 a = READ16LE(&ioMem[COMM_SIODATA32_H]); @@ -490,7 +2197,7 @@ u16 StartRFU(u16 value) if (a == 0x9966) { rfu_cmd = ioMem[COMM_SIODATA32_L]; - if ((rfu_qsend=ioMem[0x121]) != 0) { + if ((rfu_qsend=ioMem[0x121]) != 0) { //COMM_SIODATA32_L+1, following data rfu_state = RFU_SEND; rfu_counter = 0; } @@ -528,7 +2235,7 @@ u16 StartRFU(u16 value) numtransfers = 0; rfu_cmd |= 0x80; if (linkmem->numgbas == 2) - SetEvent(linksync[1-vbaid]); + SetEvent(linksync[1-vbaid]); //allow other gba to move break; case 0x11: // ? always receives 0xff - I suspect it's something for 3+ players @@ -542,6 +2249,10 @@ u16 StartRFU(u16 value) break; case 0x26: + if ( linkid > 0) { //may not be needed + memcpy(rfu_masterdata, linkmem->rfu_data[1-vbaid], 128); + rfu_masterq = linkmem->rfu_q[1-vbaid]; + } if(linkid>0){ rfu_qrecv = rfu_masterq; } @@ -549,26 +2260,34 @@ u16 StartRFU(u16 value) rfu_state = RFU_RECV; rfu_counter = 0; } + //if(rfu_state == RFU_RECV) + //if(linkid>0) + //log("Recv26[%d] : %02X %0s\n", GetTickCount(), rfu_qrecv*4, (LPCTSTR)DataHex((char*)rfu_masterdata,rfu_qrecv*4) ); else + //log("Recv26[%d] : %02X %0s\n", GetTickCount(), rfu_qrecv*4, (LPCTSTR)DataHex((char*)linkmem->rfu_data[1-vbaid],rfu_qrecv*4) ); rfu_cmd |= 0x80; break; case 0x24: // send data - if((numtransfers++)==0) linktime = 1; + if((numtransfers++)==0) linktime = 1; //numtransfers doesn't seems to be used? linkmem->rfu_linktime[vbaid] = linktime; if(linkmem->numgbas==2){ - SetEvent(linksync[1-vbaid]); - WaitForSingleObject(linksync[vbaid], linktimeout); - ResetEvent(linksync[vbaid]); + SetEvent(linksync[1-vbaid]); //allow other gba to move (read data) + WaitForSingleObject(linksync[vbaid], linktimeout); //wait until this gba allowed to move + ResetEvent(linksync[vbaid]); //don't allow this gba to move (send data) } + //log("Send%02X[%d] : %02X %0s\n", rfu_cmd, GetTickCount(), linkmem->rfu_q[vbaid]*4, (LPCTSTR)DataHex((char*)linkmem->rfu_data[vbaid],linkmem->rfu_q[vbaid]*4) ); rfu_cmd |= 0x80; linktime = 0; linkid = -1; break; case 0x25: // send & wait for data - case 0x1f: // pick a server + //log("Send%02X[%d] : %02X %0s\n", rfu_cmd, GetTickCount(), linkmem->rfu_q[vbaid]*4, (LPCTSTR)DataHex((char*)linkmem->rfu_data[vbaid],linkmem->rfu_q[vbaid]*4) ); + rfu_cmd |= 0x80; + break; + case 0x1f: // pick/join a server case 0x10: // init - case 0x16: // send broadcast data + case 0x16: // send broadcast data (ie. room name) case 0x17: // setup or something ? case 0x27: // wait for data ? case 0x3d: // init @@ -595,9 +2314,9 @@ u16 StartRFU(u16 value) linkmem->rfu_linktime[vbaid] = linktime; if (linkmem->numgbas == 2) { if (!linkid || (linkid && numtransfers)) - SetEvent(linksync[1-vbaid]); - WaitForSingleObject(linksync[vbaid], linktimeout); - ResetEvent(linksync[vbaid]); + SetEvent(linksync[1-vbaid]); //allow other gba to move (send data) + WaitForSingleObject(linksync[vbaid], linktimeout); //wait until this gba allowed to move + ResetEvent(linksync[vbaid]); //don't allow this gba to move(read data) } if ( linkid > 0) { memcpy(rfu_masterdata, linkmem->rfu_data[1-vbaid], 128); @@ -624,8 +2343,9 @@ u16 StartRFU(u16 value) break; case RFU_SEND: - if(--rfu_qsend == 0) + if(--rfu_qsend == 0) { rfu_state = RFU_COMM; + } switch (rfu_cmd) { case 0x16: @@ -723,6 +2443,10 @@ int InitLink() { WSADATA wsadata; BOOL disable = true; + DWORD timeout = linktimeout; + int sz = 32768; + //int len = sizeof(int); + unsigned long notblock = 1; //AdamN: 0=blocking, non-zero=non-blocking linkid = 0; @@ -732,17 +2456,29 @@ int InitLink() } if((lanlink.tcpsocket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET){ - MessageBox(NULL, "Couldn't create socket.", "Error!", MB_OK); + MessageBox(NULL, _T("Couldn't create socket."), _T("Error!"), MB_OK); WSACleanup(); return 0; } - setsockopt(lanlink.tcpsocket, IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); + setsockopt(lanlink.tcpsocket, IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately - if((mmf=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(LINKDATA), "VBA link memory"))==NULL){ + setsockopt(lanlink.tcpsocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(DWORD)); //setting recv timeout + setsockopt(lanlink.tcpsocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(DWORD)); //setting send timeout + //getsockopt(lanlink.tcpsocket, SOL_SOCKET, SO_RCVBUF, (char*)&sz, (int*)&len); //setting recv buffer + setsockopt(lanlink.tcpsocket, SOL_SOCKET, SO_RCVBUF, (char*)&sz, sizeof(int)); //setting recv buffer + + /*if(ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block)==SOCKET_ERROR) { //AdamN: activate non-blocking mode (by default sockets are using blocking mode after created) + MessageBox(NULL, _T("Couldn't enable non-blocking socket."), _T("Error!"), MB_OK); closesocket(lanlink.tcpsocket); WSACleanup(); - MessageBox(NULL, "Error creating file mapping", "Error", MB_OK|MB_ICONEXCLAMATION); + return 0; + }*/ + + if((mmf=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(LINKDATA), _T("VBA link memory")))==NULL){ + closesocket(lanlink.tcpsocket); + WSACleanup(); + MessageBox(NULL, _T("Error creating file mapping"), _T("Error"), MB_OK|MB_ICONEXCLAMATION); return 0; } @@ -755,23 +2491,23 @@ int InitLink() closesocket(lanlink.tcpsocket); WSACleanup(); CloseHandle(mmf); - MessageBox(NULL, "Error mapping file", "Error", MB_OK|MB_ICONEXCLAMATION); + MessageBox(NULL, _T("Error mapping file"), _T("Error"), MB_OK|MB_ICONEXCLAMATION); return 0; } - if(linkmem->linkflags&LINK_PARENTLOST) + if(linkmem->linkflags & LINK_PARENTLOST) vbaid = 0; if(vbaid==0){ linkid = 0; - if(linkmem->linkflags&LINK_PARENTLOST){ + if(linkmem->linkflags & LINK_PARENTLOST){ linkmem->numgbas++; linkmem->linkflags &= ~LINK_PARENTLOST; } else linkmem->numgbas=1; - for(i=0;i<4;i++){ + for(i=0;i<5;i++){ linkevent[15]=(char)i+'1'; if((linksync[i]=CreateEvent(NULL, true, false, linkevent))==NULL){ closesocket(lanlink.tcpsocket); @@ -781,7 +2517,7 @@ int InitLink() for(j=0;jnumgbas++; - if(linkmem->numgbas>4){ - linkmem->numgbas=4; + if(linkmem->numgbas>5){ + linkmem->numgbas=5; closesocket(lanlink.tcpsocket); WSACleanup(); - MessageBox(NULL, "5 or more GBAs not supported.", "Error!", MB_OK|MB_ICONEXCLAMATION); + MessageBox(NULL, _T("6 or more GBAs not supported."), _T("Error!"), MB_OK|MB_ICONEXCLAMATION); UnmapViewOfFile(linkmem); CloseHandle(mmf); return 0; } - for(i=0;i<4;i++){ + for(i=0;i<5;i++){ linkevent[15]=(char)i+'1'; if((linksync[i]=OpenEvent(EVENT_ALL_ACCESS, false, linkevent))==NULL){ closesocket(lanlink.tcpsocket); @@ -809,12 +2545,12 @@ int InitLink() for(j=0;jlastlinktime=0xffffffff; linkmem->numtransfers=0; linkmem->linkflags=0; @@ -824,12 +2560,13 @@ int InitLink() for(i=0;i<4;i++){ linkmem->linkdata[i] = 0xffff; linkdata[i] = 0xffff; + linkdatarecvd[i] = false; } - return 1; + return 1; //1=sucessful? } void CloseLink(void){ - if(lanlink.connected){ + if(/*lanlink.active &&*/ lanlink.connected){ if(linkid){ char outbuffer[4]; outbuffer[0] = 4; @@ -870,8 +2607,10 @@ void CloseLink(void){ lserver::lserver(void){ intinbuffer = (int*)inbuffer; u16inbuffer = (u16*)inbuffer; + u32inbuffer = (u32*)inbuffer; intoutbuffer = (int*)outbuffer; u16outbuffer = (u16*)outbuffer; + u32outbuffer = (u32*)outbuffer; oncewait = false; } @@ -879,11 +2618,32 @@ int lserver::Init(void *serverdlg){ SOCKADDR_IN info; DWORD nothing; char str[100]; + unsigned long notblock = 1; //0=blocking, non-zero=non-blocking info.sin_family = AF_INET; info.sin_addr.S_un.S_addr = INADDR_ANY; info.sin_port = htons(5738); + ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //use non-blocking mode? + + if(lanlink.thread!=NULL){ + c_s.Lock(); //AdamN: Locking resource to prevent deadlock + lanlink.terminate = true; + c_s.Unlock(); //AdamN: Unlock it after use + WaitForSingleObject(linksync[vbaid], 2000); //500 + lanlink.thread = NULL; + ResetEvent(linksync[vbaid]); //should it be reset? + } + lanlink.terminate = false; + + CloseLink(); //AdamN: close connections gracefully + InitLink(); //AdamN: reinit sockets + outsize = 0; + insize = 0; + + //notblock = 1; //0; + //ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: need to be blocking mode? It seems Server Need to be in Non-blocking mode + if(bind(lanlink.tcpsocket, (LPSOCKADDR)&info, sizeof(SOCKADDR_IN))==SOCKET_ERROR){ closesocket(lanlink.tcpsocket); if((lanlink.tcpsocket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET) @@ -895,83 +2655,237 @@ int lserver::Init(void *serverdlg){ if(listen(lanlink.tcpsocket, lanlink.numgbas)==SOCKET_ERROR) return WSAGetLastError(); - if(lanlink.thread!=NULL){ - lanlink.terminate = true; - WaitForSingleObject(linksync[vbaid], 500); - lanlink.thread = NULL; - } - lanlink.terminate = false; linkid = 0; gethostname(str, 100); - ((ServerWait*)serverdlg)->m_serveraddress.Format("Server IP address is: %s", inet_ntoa(*(LPIN_ADDR)(gethostbyname(str)->h_addr_list[0]))); - + int i = 0; + CString stringb = _T(""); + if (gethostbyname(str)->h_addrtype == AF_INET) { + while (gethostbyname(str)->h_addr_list[i] != 0) { + stringb.AppendFormat(_T("%s/"), inet_ntoa(*(LPIN_ADDR)(gethostbyname(str)->h_addr_list[i++]))); //AdamN: trying to shows all IP that was binded + } + if(stringb.GetLength()>0) + stringb.Delete(stringb.GetLength()-1); + } + ((ServerWait*)serverdlg)->m_serveraddress.Format(_T("Server IP address is: %s"), (LPCTSTR)stringb /*inet_ntoa(*(LPIN_ADDR)(gethostbyname(str)->h_addr_list[0]))*/); + lanlink.thread = CreateThread(NULL, 0, LinkServerThread, serverdlg, 0, ¬hing); return 0; } -DWORD WINAPI LinkServerThread(void *serverdlg){ +DWORD WINAPI LinkServerThread(void *serverdlg){ fd_set fdset; timeval wsocktimeout; + SOCKADDR_IN info; + int infolen; char inbuffer[256], outbuffer[256]; int *intinbuffer = (int*)inbuffer; u16 *u16inbuffer = (u16*)inbuffer; int *intoutbuffer = (int*)outbuffer; u16 *u16outbuffer = (u16*)outbuffer; BOOL disable = true; + DWORD timeout = linktimeout; + int sz = 32768; + //DWORD nothing; + unsigned long notblock = 1; //0=blocking, non-zero=non-blocking - wsocktimeout.tv_sec = 1; - wsocktimeout.tv_usec = 0; + wsocktimeout.tv_sec = linktimeout / 1000; //1; + wsocktimeout.tv_usec = linktimeout % 1000; //0; i = 0; + BOOL shown = true; - while(im_plconn[i].Format("Player %d connected", i+1); - ((ServerWait*)serverdlg)->UpdateData(false); + DWORD lat = GetTickCount(); + send(ls.tcpsocket[i+1], outbuffer, 4, 0); //Sending index and #gba to client + lat = GetTickCount() - lat; + infolen = sizeof(SOCKADDR_IN); + getpeername(ls.tcpsocket[i+1], (LPSOCKADDR)&info , &infolen); + ((ServerWait*)serverdlg)->m_plconn[i].Format(_T("Player %d connected (IP: %s, Latency: %dms)"), i+1, inet_ntoa(info.sin_addr), lat); + if(IsWindow(((ServerWait*)serverdlg)->m_hWnd)) { //AdamN: not really useful for UpdateData + //((ServerWait*)serverdlg)->UpdateData(false); //AdamN: this seems to cause 2 assertion failed errors when a player connected, seems to be not thread-safe + ((ServerWait*)serverdlg)->GetDlgItem(IDC_STATIC1)->SetWindowText((LPCTSTR)((ServerWait*)serverdlg)->m_serveraddress); //((ServerWait*)serverdlg)->Invalidate(); //((ServerWait*)serverdlg)->SendMessage(WM_PAINT, 0, 0); //AdamN: using message might be safer than UpdateData but might not works + ((ServerWait*)serverdlg)->GetDlgItem(IDC_STATIC2)->SetWindowText((LPCTSTR)((ServerWait*)serverdlg)->m_plconn[0]); //m_plconn[0] + ((ServerWait*)serverdlg)->GetDlgItem(IDC_STATIC3)->SetWindowText((LPCTSTR)((ServerWait*)serverdlg)->m_plconn[1]); //m_plconn[1] + ((ServerWait*)serverdlg)->GetDlgItem(IDC_STATIC4)->SetWindowText((LPCTSTR)((ServerWait*)serverdlg)->m_plconn[2]); //m_plconn[2] + //((ServerWait*)serverdlg)->GetDlgItem(IDC_SERVERWAIT)->Invalidate(); //m_prgctrl + ((ServerWait*)serverdlg)->Invalidate(); + } i++; } } - ((ServerWait*)serverdlg)->m_prgctrl.StepIt(); + shown = IsWindow(((ServerWait*)serverdlg)->m_hWnd); //AdamN: trying to detect when Cancel button pressed (which cause Waiting for Player window to be closed) + if(shown) { + ((ServerWait*)serverdlg)->m_prgctrl.StepIt(); //AdamN: this will cause assertion failed if the Waiting for Player window is Canceled + } + c_s.Lock(); //AdamN: Locking resource to prevent deadlock + bool canceled=lanlink.terminate; //AdamN: w/o locking might not be thread-safe + c_s.Unlock(); //AdamN: Unlock it after use + if(canceled) break; + } + + if(i>0) { //AdamN: if canceled after 1 or more player has been connected link will stil be marked as connected + MessageBox(NULL, _T("All players connected"), _T("Link"), MB_OK); + c_s.Lock(); //AdamN: Locking resource to prevent deadlock + lanlink.numgbas = i; //AdamN: update # of GBAs according to connected players before server got canceled + c_s.Unlock(); //AdamN: Unlock it after use + } + if(shown) { + ((ServerWait*)serverdlg)->SendMessage(WM_CLOSE, 0, 0); //AdamN: this will also cause assertion failed if the Waiting for Player window was Canceled/no longer existed } - MessageBox(NULL, "All players connected", "Link", MB_OK); - ((ServerWait*)serverdlg)->SendMessage(WM_CLOSE, 0, 0); - for(i=1;i<=lanlink.numgbas;i++){ + shown = (i>0); //AdamN: if canceled after 1 or more player has been connected connecteion will still be established + for(i=1;i<=lanlink.numgbas;i++){ //AdamN: this should be i0) { + tmp = (ULONG)*&LinkCmdList.GetHead(); + cmdprm.Command = tmp & 0xffff; + cmdprm.Param = tmp >> 16; + //log("Rem: %04X %04X\n",cmdprm.Command,cmdprm.Param); //AdamN: calling "log" in here seems to cause deadlock + LinkCmdList.RemoveHead(); + } + c_s.Unlock(); //AdamN: Locking resource to prevent deadlock + LinkCmd = cmdprm.Command; + if(LinkCmd & 1) { //StartLink + int prm = cmdprm.Param; //LinkParam1; + StartLink2(prm); //AdamN: Might not be thread-safe w/o proper locking inside StartLink + c_s.Lock(); + LinkCommand&=0xfffffffe; + c_s.Unlock(); + } + if(LinkCmd & 2) { //StartGPLink, might not be needed as it doesn't use socket + int prm = cmdprm.Param; //LinkParam2; + StartGPLink(prm); //AdamN: Might not be thread-safe w/o proper locking inside StartLink + c_s.Lock(); + LinkCommand&=0xfffffffd; + c_s.Unlock(); + } + if(LinkCmd & 4) { //StartRFU, might not be needed as it doesn't use socket currently + int prm = cmdprm.Param; //LinkParam4; + StartRFU(prm); //AdamN: Might not be thread-safe w/o proper locking inside StartLink + c_s.Lock(); + LinkCommand&=0xfffffffb; + c_s.Unlock(); + } + if(LinkCmd & 8) { //LinkUpdate + int prm = cmdprm.Param; //LinkParam8; + LinkUpdate2(prm, 0); //AdamN: Might not be thread-safe w/o proper locking inside StartLink + c_s.Lock(); + LinkCommand&=0xfffffff7; + c_s.Unlock(); + } + SleepEx(1,true); + + /*c_s.Lock(); + done=(lanlink.connected && linkid&&lc.numtransfers==0); + c_s.Unlock(); + if(done) lc.CheckConn();*/ + + c_s.Lock(); + done=(/*lanlink.terminate &&*/ AppTerminated || !lanlink.connected); + c_s.Unlock(); + } while (!done); + //SetEvent(linksync[vbaid]); + c_s.Lock(); + LinkHandlerActive = false; + c_s.Unlock(); + return 0; +} + +BOOL lserver::Send(void){ + //return false; + BOOL sent = false; + BOOL disable = true; if(lanlink.type==0){ // TCP if(savedlinktime==-1){ outbuffer[0] = 4; - outbuffer[1] = -32; //0xe0 + outbuffer[1] = -32; //0xe0 //Closing mark? for(i=1;i<=lanlink.numgbas;i++){ - send(tcpsocket[i], outbuffer, 4, 0); - recv(tcpsocket[i], inbuffer, 4, 0); + unsigned long notblock = 1; //0=blocking, non-zero=non-blocking + ioctlsocket(tcpsocket[i], FIONBIO, ¬block); //AdamN: use non-blocking for sending + setsockopt(tcpsocket[i], IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSS1\n",(LPCTSTR)LogStr); + log("SSend to : %d Size : %d %s\n", i, 4, (LPCTSTR)DataHex(outbuffer,4)); + } + #endif + if(send(tcpsocket[i], outbuffer, 4, 0)!=SOCKET_ERROR) { //AdamN: should check for socket error to reduce the lag + sent = true; + unsigned long notblock = 0; //0=blocking, non-zero=non-blocking + ioctlsocket(tcpsocket[i], FIONBIO, ¬block); //AdamN: use blocking for receiving + int cnt=recv(tcpsocket[i], inbuffer, 4, 0); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSS2\n",(LPCTSTR)LogStr); + log("Srecv from : %d Size : %d %s\n", i, cnt, (LPCTSTR)DataHex(inbuffer,cnt)); + } + #endif + } } + //return sent; } outbuffer[1] = tspeed; u16outbuffer[1] = linkdata[0]; @@ -979,169 +2893,588 @@ void lserver::Send(void){ if(lanlink.numgbas==1){ if(lanlink.type==0){ outbuffer[0] = 8; - send(tcpsocket[1], outbuffer, 8, 0); + unsigned long notblock = 1; //0=blocking, non-zero=non-blocking + ioctlsocket(tcpsocket[1], FIONBIO, ¬block); //AdamN: use non-blocking for sending + setsockopt(tcpsocket[1], IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSS3\n",(LPCTSTR)LogStr); + log("SSend to : %d Size : %d %s\n", 1, 8, (LPCTSTR)DataHex(outbuffer,8)); + } + #endif + sent=(send(tcpsocket[1], outbuffer, 8, 0)>=0); + int ern=WSAGetLastError(); + if(ern!=0) { + log("WSAError = %d\n",ern); + lanlink.connected = false; + } } } else if(lanlink.numgbas==2){ u16outbuffer[4] = linkdata[2]; if(lanlink.type==0){ outbuffer[0] = 10; - send(tcpsocket[1], outbuffer, 10, 0); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSS4\n",(LPCTSTR)LogStr); + log("SSend to : %d Size : %d %s\n", 1, 10, (LPCTSTR)DataHex(outbuffer,10)); + } + #endif + sent=(send(tcpsocket[1], outbuffer, 10, 0)>=0); u16outbuffer[4] = linkdata[1]; - send(tcpsocket[2], outbuffer, 10, 0); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSS5\n",(LPCTSTR)LogStr); + log("SSend to : %d Size : %d %s\n", 2, 10, (LPCTSTR)DataHex(outbuffer,10)); + } + #endif + sent=(send(tcpsocket[2], outbuffer, 10, 0)>=0); } } else { if(lanlink.type==0){ outbuffer[0] = 12; u16outbuffer[4] = linkdata[2]; u16outbuffer[5] = linkdata[3]; - send(tcpsocket[1], outbuffer, 12, 0); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSS6\n",(LPCTSTR)LogStr); + log("SSend to : %d Size : %d %s\n", 1, 12, (LPCTSTR)DataHex(outbuffer,12)); + } + #endif + sent=(send(tcpsocket[1], outbuffer, 12, 0)>=0); u16outbuffer[4] = linkdata[1]; - send(tcpsocket[2], outbuffer, 12, 0); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSS7\n",(LPCTSTR)LogStr); + log("SSend to : %d Size : %d %s\n", 2, 12, (LPCTSTR)DataHex(outbuffer,12)); + } + #endif + sent=(send(tcpsocket[2], outbuffer, 12, 0)>=0); u16outbuffer[5] = linkdata[2]; - send(tcpsocket[3], outbuffer, 12, 0); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sSS8\n",(LPCTSTR)LogStr); + log("SSend to : %d Size : %d %s\n", 3, 12, (LPCTSTR)DataHex(outbuffer,12)); + } + #endif + sent=(send(tcpsocket[3], outbuffer, 12, 0)>=0); } } } - return; + return sent; } -void lserver::Recv(void){ +BOOL lserver::Recv(void){ + //return false; + BOOL recvd = false; + BOOL disable = true; + unsigned long arg = 0; + int numbytes; if(lanlink.type==0){ // TCP - wsocktimeout.tv_usec = 0; - wsocktimeout.tv_sec = linktimeout / 1000; + wsocktimeout.tv_sec = linktimeout / 1000; //AdamN: setting this too small may cause disconnection in game, too large will cause great lag + wsocktimeout.tv_usec = linktimeout % 1000; //0; //AdamN: remainder should be set also isn't? fdset.fd_count = lanlink.numgbas; - for(i=0;i0) cnt++; + } + if(cnt<=0) return recvd;*/ howmanytimes++; for(i=0;i1) memcpy(inbuffer, inbuffer+inbuffer[0]*(howmanytimes-1), inbuffer[0]); - if(inbuffer[1]==-32){ + if(inbuffer[1]==-32 /*|| WSAGetLastError()!=0*/){ //AdamN: Should be checking for possible of socket error isn't? char message[30]; lanlink.connected = false; - sprintf(message, "Player %d disconnected.", i+2); - MessageBox(NULL, message, "Link", MB_OK); + sprintf(message, _T("Player %d disconnected."), i+2); + MessageBox(NULL, message, _T("Link"), MB_OK); outbuffer[0] = 4; outbuffer[1] = -32; for(i=1;i=0); + latency[i] = GetTickCount() - lat; + int ern = WSAGetLastError(); + if(ern!=0) lanlink.connected = false; + if(!lanlink.connected) { + char message[40]; + sprintf(message, _T("Player %d disconnected."), i+1); + MessageBox(NULL, message, _T("Link"), MB_OK); + } + j--; + } while (j!=0 && lanlink.connected && !sent2); + sent|=sent2; + } + return sent; +} + +BOOL lserver::SendData(const char *buf, int size, int nretry){ + //return false; + BOOL sent = false; + BOOL disable = true; + unsigned long notblock = 0; //1; //0=blocking, non-zero=non-blocking + for(int i=1; i<=lanlink.numgbas; i++) { + ioctlsocket(tcpsocket[i], FIONBIO, ¬block); //AdamN: use non-blocking for sending + setsockopt(tcpsocket[i], IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + //log("%sSS0%d\n",(LPCTSTR)LogStr, i); + log("%sSSend0%d Size : %d %s\n", (LPCTSTR)LogStr, i, size, (LPCTSTR)DataHex(buf,size)); + } + #endif + BOOL sent2 = false; + int j = nretry+1; + do { + DWORD lat = GetTickCount(); + sent2=(send(tcpsocket[i], buf, size, 0)>=0); + latency[i] = GetTickCount() - lat; + int ern = WSAGetLastError(); + if(ern!=0) lanlink.connected = false; + if(!lanlink.connected) { + char message[40]; + sprintf(message, _T("Player %d disconnected."), i+1); + MessageBox(NULL, message, _T("Link"), MB_OK); + } + j--; + } while (j!=0 && lanlink.connected && !sent2); + sent|=sent2; + } + return sent; +} + +BOOL lserver::RecvData(int size, int idx, bool peek){ + //return false; + BOOL recvd = false; + BOOL disable = true; + unsigned long notblock = 0; //0=blocking, non-zero=non-blocking + ioctlsocket(tcpsocket[idx], FIONBIO, ¬block); //AdamN: use blocking for receiving + setsockopt(tcpsocket[idx], IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + int flg = 0; + if(peek) flg = MSG_PEEK; + int rsz = size; + do { + int cnt=recv(tcpsocket[idx], inbuffer+(size-rsz), rsz, flg); + if(cnt>=0) rsz-=cnt; else lanlink.connected = false; + int ern = WSAGetLastError(); + if(ern!=0) lanlink.connected = false; + if(!lanlink.connected) { + char message[40]; + sprintf(message, _T("Player %d disconnected."), idx+1); + MessageBox(NULL, message, _T("Link"), MB_OK); + } + } while (rsz>0 && lanlink.connected); + insize = size-rsz; + recvd = (rsz<=0); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + //log("%sSR0%d\n",(LPCTSTR)LogStr, idx); + log("%sSRecv%d(%d) Size : %d/%d %s\n", (LPCTSTR)LogStr, idx, peek, insize, size, (LPCTSTR)DataHex(inbuffer,size)); + } + #endif + return recvd; +} + +BOOL lserver::WaitForData(int ms) { + unsigned long notblock = 1; //AdamN: 0=blocking, non-zero=non-blocking + unsigned long arg = 0; + //fd_set fdset; + //timeval wsocktimeout; + MSG msg; + int ready = 0; + EmuCtr++; + //DWORD needms = ms; + if(EmuReseted) + for(int i=1; i<=lanlink.numgbas; i++) DiscardData(i); + EmuReseted = false; + DWORD starttm = GetTickCount(); + do { //Waiting for incomming data before continuing CPU execution + SleepEx(0,true); //SleepEx(0,true); //to give time for incoming data + if(PeekMessage(&msg, 0/*theApp.GetMainWnd()->m_hWnd*/, 0, 0, PM_NOREMOVE)) { + if(msg.message==WM_CLOSE) AppTerminated=true; else theApp.PumpMessage(); //seems to be processing message only if it has message otherwise it halt the program + } + + //fdset.fd_count = lanlink.numgbas; + notblock = 0; + for(int i=1; i<=lanlink.numgbas; i++) { + //fdset.fd_array[i-1] = tcpsocket[i]; + ioctlsocket(tcpsocket[i], FIONBIO, ¬block); //AdamN: temporarily use blocking for reading + int ern=WSAGetLastError(); + if(ern!=0) { + log("slIOCTL Error: %d\n",ern); + //if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065) lanlink.connected = false; + } + arg = 0; //1; + if(ioctlsocket(tcpsocket[i], FIONREAD, &arg)!=0) { //AdamN: Alternative(Faster) to Select(Slower) + int ern=WSAGetLastError(); + if(ern!=0) { + log("%sSC Error: %d\n",(LPCTSTR)LogStr,ern); + char message[40]; + lanlink.connected = false; + sprintf(message, _T("Player %d disconnected."), i+1); + MessageBox(NULL, message, _T("Link"), MB_OK); + break; + } + } + if(arg>0) ready++; + } + //wsocktimeout.tv_sec = linktimeout / 1000; + //wsocktimeout.tv_usec = linktimeout % 1000; //0; //AdamN: remainder should be set also isn't? + //ready = select(0, &fdset, NULL, NULL, &wsocktimeout); + //int ern=WSAGetLastError(); + //if(ern!=0) { + // log("slCC Error: %d\n",ern); + // if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065) lanlink.connected = false; + //} + } while (lanlink.connected && ready==0 && /*(int)*/(GetTickCount()-starttm)<(DWORD)ms && !AppTerminated && !EmuReseted); //with ms<0 might not gets a proper result? + //if((GetTickCount()-starttm)>=(DWORD)ms) log("TimeOut:%d\n",ms); + EmuCtr--; + return (ready>0); +} + +BOOL lserver::IsDataReady(void) { + unsigned long arg;// = 0; + int ready = 0; + for(int i=1; i<=lanlink.numgbas; i++) { + if(EmuReseted) DiscardData(i); + arg = 0; + if(ioctlsocket(tcpsocket[i], FIONREAD, &arg)!=0) { //AdamN: Alternative(Faster) to Select(Slower) + int ern=WSAGetLastError(); + if(ern!=0) { + log("%sSC Error: %d\n",(LPCTSTR)LogStr,ern); + char message[40]; + lanlink.connected = false; + sprintf(message, _T("Player %d disconnected."), i+1); + MessageBox(NULL, message, _T("Link"), MB_OK); + break; + } + } + if(arg>0) ready++; + } + EmuReseted = false; + return(arg>0); +} + +int lserver::DiscardData(int idx) { + char buff[256]; + unsigned long arg; + int sz = 0; + do { + arg = 0; + if(ioctlsocket(tcpsocket[idx], FIONREAD, &arg)!=0) { //AdamN: Alternative(Faster) to Select(Slower) + int ern=WSAGetLastError(); + if(ern!=0) { + log("%sCC Error: %d\n",(LPCTSTR)LogStr,ern); + char message[40]; + lanlink.connected = false; + sprintf(message, _T("Player %d disconnected."), idx+1); + MessageBox(NULL, message, _T("Link"), MB_OK); + } + } + if(arg>0) { + int cnt=recv(tcpsocket[idx], buff, min(arg,256), 0); + if(cnt>0) sz+=cnt; + } + } while (arg>0 && lanlink.connected); + return(sz); +} // Client lclient::lclient(void){ intinbuffer = (int*)inbuffer; u16inbuffer = (u16*)inbuffer; + u32inbuffer = (u32*)inbuffer; intoutbuffer = (int*)outbuffer; u16outbuffer = (u16*)outbuffer; + u32outbuffer = (u32*)outbuffer; numtransfers = 0; oncesend = false; return; } int lclient::Init(LPHOSTENT hostentry, void *waitdlg){ - unsigned long notblock = 1; + unsigned long notblock = 1; //0=blocking, non-zero=non-blocking DWORD nothing; - + serverinfo.sin_family = AF_INET; serverinfo.sin_port = htons(5738); serverinfo.sin_addr = *((LPIN_ADDR)*hostentry->h_addr_list); - if(ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block)==SOCKET_ERROR) + if(ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block)==SOCKET_ERROR) //use non-blocking mode return WSAGetLastError(); - + if(lanlink.thread!=NULL){ + c_s.Lock(); //AdamN: Locking resource to prevent deadlock lanlink.terminate = true; - WaitForSingleObject(linksync[vbaid], 500); + c_s.Unlock(); //AdamN: Unlock it after use + WaitForSingleObject(linksync[vbaid], 2000); //500 + lanlink.thread = NULL; + ResetEvent(linksync[vbaid]); //should it be reset? } - ((ServerWait*)waitdlg)->SetWindowText("Connecting..."); + CloseLink(); //AdamN: close connections gracefully + InitLink(); //AdamN: reinit sockets + outsize = 0; + insize = 0; + + //notblock = 0; + //ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: need to be in blocking mode? It seems Client Need to be in Blocking mode otherwise Select will generate error 10038, But Select on blocking socket will cause delays + + //((ServerWait*)waitdlg)->SetWindowText("Connecting..."); //AdamN: SetWindowText seems to cause problem on client connect + ((ServerWait*)waitdlg)->m_serveraddress.Format(_T("Connecting to %s"), inet_ntoa(*(LPIN_ADDR)hostentry->h_addr_list[0])); + lanlink.terminate = false; + lanlink.thread = CreateThread(NULL, 0, LinkClientThread, waitdlg, 0, ¬hing); + return 0; } DWORD WINAPI LinkClientThread(void *waitdlg){ fd_set fdset; timeval wsocktimeout; - int numbytes; + int numbytes, cnt; char inbuffer[16]; u16 *u16inbuffer = (u16*)inbuffer; + unsigned long block = 0; + BOOL shown = true; + //DWORD nothing; if(connect(lanlink.tcpsocket, (LPSOCKADDR)&lc.serverinfo, sizeof(SOCKADDR_IN))==SOCKET_ERROR){ if(WSAGetLastError()!=WSAEWOULDBLOCK){ - MessageBox(NULL, "Couldn't connect to server.", "Link", MB_OK); + MessageBox(NULL, _T("Couldn't connect to server."), _T("Link"), MB_OK); return 1; } - wsocktimeout.tv_sec = 1; - wsocktimeout.tv_usec = 0; + wsocktimeout.tv_sec = linktimeout / 1000; //1; + wsocktimeout.tv_usec = linktimeout % 1000; //0; do{ - if(lanlink.terminate) return 0; + //WinHelper::CCriticalSection::CLock lock(&c_s); + c_s.Lock(); //AdamN: Locking resource to prevent deadlock + bool canceled=lanlink.terminate; //AdamN: w/o locking might not be thread-safe + c_s.Unlock(); //AdamN: Unlock it after use + if(canceled) return 0; fdset.fd_count = 1; fdset.fd_array[0] = lanlink.tcpsocket; - ((ServerWait*)waitdlg)->m_prgctrl.StepIt(); - } while(select(0, NULL, &fdset, NULL, &wsocktimeout)!=1&&connect(lanlink.tcpsocket, (LPSOCKADDR)&lc.serverinfo, sizeof(SOCKADDR_IN))!=0); + shown = IsWindow(((ServerWait*)waitdlg)->m_hWnd); //AdamN: trying to detect when Cancel button pressed (which cause Waiting for Player window to be closed) + if(shown) + ((ServerWait*)waitdlg)->m_prgctrl.StepIt(); + } while(select(0, NULL, &fdset, NULL, &wsocktimeout)!=1/*&&connect(lanlink.tcpsocket, (LPSOCKADDR)&lc.serverinfo, sizeof(SOCKADDR_IN))!=0*/); } - ioctlsocket(lanlink.tcpsocket, FIONBIO, &block); + ioctlsocket(lanlink.tcpsocket, FIONBIO, &block); //AdamN: temporary using blocking mode numbytes = 0; - while(numbytes<4) - numbytes += recv(lanlink.tcpsocket, inbuffer+numbytes, 16, 0); + DWORD lat = GetTickCount(); + while(numbytes<4) { + cnt = recv(lanlink.tcpsocket, inbuffer+numbytes, 16, 0); //AdamN: receiving index and #of gbas + if((cnt<=0/*SOCKET_ERROR*/)||(WSAGetLastError()!=0)) break; //AdamN: to prevent stop responding due to infinite loop on socket error + numbytes += cnt; + //if(IsWindow(((ServerWait*)waitdlg)->m_hWnd)) + // ((ServerWait*)waitdlg)->m_prgctrl.StepIt(); //AdamN: update progressbar so it won't look stucked + } + lat = GetTickCount() - lat; linkid = (int)u16inbuffer[0]; lanlink.numgbas = (int)u16inbuffer[1]; - ((ServerWait*)waitdlg)->m_serveraddress.Format("Connected as #%d", linkid+1); - if(lanlink.numgbas!=linkid) ((ServerWait*)waitdlg)->m_plconn[0].Format("Waiting for %d players to join", lanlink.numgbas-linkid); - else ((ServerWait*)waitdlg)->m_plconn[0].Format("All players joined."); - + ((ServerWait*)waitdlg)->m_serveraddress.Format(_T("Connected as #%d (Latency: %dms)"), linkid+1, lat); + if(lanlink.numgbas!=linkid) ((ServerWait*)waitdlg)->m_plconn[0].Format(_T("Waiting for %d more players to join"), lanlink.numgbas-linkid); + else ((ServerWait*)waitdlg)->m_plconn[0].Format(_T("All players joined.")); + if(IsWindow(((ServerWait*)waitdlg)->m_hWnd)) { //AdamN: not really useful for UpdateData + //((ServerWait*)waitdlg)->UpdateData(false); //AdamN: refreshing static text after being modified above, may not be thread-safe (causing assertion failed) + ((ServerWait*)waitdlg)->GetDlgItem(IDC_STATIC1)->SetWindowText((LPCTSTR)((ServerWait*)waitdlg)->m_serveraddress); //((ServerWait*)waitdlg)->Invalidate(); //((ServerWait*)waitdlg)->SendMessage(WM_PAINT, 0, 0); //AdamN: using message might be safer than UpdateData but might not works + ((ServerWait*)waitdlg)->GetDlgItem(IDC_STATIC2)->SetWindowText((LPCTSTR)((ServerWait*)waitdlg)->m_plconn[0]); //m_plconn[0] + //((ServerWait*)waitdlg)->GetDlgItem(IDC_SERVERWAIT)->Invalidate(); //m_prgctrl + ((ServerWait*)waitdlg)->Invalidate(); + } numbytes = 0; inbuffer[0] = 1; - while(numbytesm_hWnd)) + ((ServerWait*)waitdlg)->m_prgctrl.StepIt(); //AdamN: update progressbar so it won't look stucked + } + MessageBox(NULL, _T("Connected."), _T("Link"), MB_OK); //AdamN: shown when the game initialize multiplayer mode (on VBALink it's shown after players connected to server), is it really needed to show this thing? + if(IsWindow(((ServerWait*)waitdlg)->m_hWnd)) + ((ServerWait*)waitdlg)->SendMessage(WM_CLOSE, 0, 0); //AdamN: may cause assertion failed when window no longer existed - MessageBox(NULL, "Connected.", "Link", MB_OK); - ((ServerWait*)waitdlg)->SendMessage(WM_CLOSE, 0, 0); - - block = 1; - - ioctlsocket(lanlink.tcpsocket, FIONBIO, &block); + /*block = 1; //AdamN: 1=non-blocking + ioctlsocket(lanlink.tcpsocket, FIONBIO, &block); //AdamN: back to non-blocking mode?*/ lanlink.connected = true; + + //SetEvent(linksync[vbaid]); //AdamN: saying the lanlink.thread is exiting? might cause thread to stuck when app exited + + c_s.Lock(); + if(lanlink.connected) { + lanlink.terminate = false; + //lanlink.thread = CreateThread(NULL, 0, LinkHandlerThread, NULL, 0, ¬hing); //AdamN: trying to reduce the lag by handling sockets in a different thread, not working properly yet + } + c_s.Unlock(); + return 0; } -void lclient::CheckConn(void){ - if((numbytes=recv(lanlink.tcpsocket, inbuffer, 256, 0))>0){ - while(numbytes0) + if((numbytes=recv(lanlink.tcpsocket, inbuffer, 256, 0))>=0){ //>0 //AdamN: this socket need to be in non-blocking mode otherwise it will wait forever until server's game entering multiplayer mode + /*#ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sCC1\n",(LPCTSTR)LogStr); + log("CCrecv Size : %d %s\n", numbytes, (LPCTSTR)DataHex(inbuffer,numbytes)); + } + #endif*/ + if(numbytes>=0) //AdamN: otherwise socket error + while(numbytes0); + if(inbuffer[1]==-32){ //AdamN: only true if server was closed gracefully outbuffer[0] = 4; + notblock = 1; //0=blocking, non-zero=non-blocking + ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: use non-blocking for sending + setsockopt(lanlink.tcpsocket, IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + log("%sCC3\n",(LPCTSTR)LogStr); + log("CCsend Size : %d %s\n", 4, (LPCTSTR)DataHex(outbuffer,4)); + } + #endif send(lanlink.tcpsocket, outbuffer, 4, 0); lanlink.connected = false; - MessageBox(NULL, "Server disconnected.", "Link", MB_OK); + MessageBox(NULL, _T("Server disconnected."), _T("Link"), MB_OK); return; } + if(recvd) { numtransfers = 1; savedlinktime = 0; linkdata[0] = u16inbuffer[1]; @@ -1151,29 +3484,68 @@ void lclient::CheckConn(void){ after = false; oncewait = true; oncesend = true; + } } return; } -void lclient::Recv(void){ +BOOL lclient::Recv(void){ + //return false; + BOOL recvd = false; + BOOL disable = true; + unsigned long arg = 0; + unsigned long notblock = 0; //0=blocking, non-zero=non-blocking + ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: use blocking for receiving fdset.fd_count = 1; fdset.fd_array[0] = lanlink.tcpsocket; wsocktimeout.tv_sec = linktimeout / 1000; - wsocktimeout.tv_usec = 0; - if(select(0, &fdset, NULL, NULL, &wsocktimeout)==0){ + wsocktimeout.tv_usec = linktimeout % 1000; //0; //AdamN: remainder should be set also isn't? + if(select(0, &fdset, NULL, NULL, &wsocktimeout)<=0){ //AdamN: may cause noticible delay, result can also be SOCKET_ERROR, Select seems to be needed to maintain stability numtransfers = 0; - return; + int ern=WSAGetLastError(); + if(ern!=0) + log("%sCR1[%d]\n",(LPCTSTR)LogStr,ern); + return recvd; } + /*arg = 0; + if(ioctlsocket(lanlink.tcpsocket, FIONREAD, &arg)!=0) { + int ern=WSAGetLastError(); //AdamN: this seems to get ern=10038(invalid socket handle) often + numtransfers = 0; + if(ern!=0) + log("%sCC0[%d]\n",(LPCTSTR)LogStr,ern); + return recvd; + } + if(arg==0) return recvd;*/ numbytes = 0; inbuffer[0] = 1; - while(numbytes=0); + return sent; +} + +BOOL lclient::SendData(int size, int nretry){ + //return false; + BOOL sent = false; + BOOL disable = true; + unsigned long notblock = 0; //1; //0=blocking, non-zero=non-blocking + ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: use non-blocking for sending + setsockopt(lanlink.tcpsocket, IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + //log("%sCS01\n",(LPCTSTR)LogStr); + log("%sCSend00 Size : %d %s\n", (LPCTSTR)LogStr, size, (LPCTSTR)DataHex(outbuffer,size)); + } + #endif + int i = nretry+1; + do { + DWORD lat = GetTickCount(); + sent=(send(lanlink.tcpsocket, outbuffer, size, 0)>=0); + lanlink.latency = GetTickCount() - lat; + int ern = WSAGetLastError(); + if(ern!=0) lanlink.connected = false; + if(!lanlink.connected) MessageBox(NULL, _T("Player 1 disconnected."), _T("Link"), MB_OK); + i--; + } while (i!=0 && lanlink.connected && !sent); + return sent; +} + +BOOL lclient::SendData(const char *buf, int size, int nretry){ + //return false; + BOOL sent = false; + BOOL disable = true; + unsigned long notblock = 0; //1; //0=blocking, non-zero=non-blocking + ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: use non-blocking for sending + setsockopt(lanlink.tcpsocket, IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + //log("%sCS01\n",(LPCTSTR)LogStr); + log("%sCSend00 Size : %d %s\n", (LPCTSTR)LogStr, size, (LPCTSTR)DataHex(buf,size)); + } + #endif + int i = nretry+1; + do { + DWORD lat = GetTickCount(); + sent=(send(lanlink.tcpsocket, buf, size, 0)>=0); + lanlink.latency = GetTickCount() - lat; + int ern = WSAGetLastError(); + if(ern!=0) lanlink.connected = false; + if(!lanlink.connected) MessageBox(NULL, _T("Player 1 disconnected."), _T("Link"), MB_OK); + i--; + } while (i!=0 && lanlink.connected && !sent); + return sent; +} + +BOOL lclient::RecvData(int size, bool peek){ + //return false; + BOOL recvd = false; + BOOL disable = true; + unsigned long notblock = 0; //0=blocking, non-zero=non-blocking + ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: use blocking for receiving + setsockopt(lanlink.tcpsocket, IPPROTO_TCP, TCP_NODELAY, (char*)&disable, sizeof(BOOL)); //true=send packet immediately + int flg = 0; + if(peek) flg = MSG_PEEK; + int rsz = size; + do { + int cnt=recv(lanlink.tcpsocket, inbuffer+(size-rsz), rsz, flg); + if(cnt>=0) rsz-=cnt; else lanlink.connected = false; + int ern = WSAGetLastError(); + if(ern!=0) lanlink.connected = false; + if(!lanlink.connected) MessageBox(NULL, _T("Player 1 disconnected."), _T("Link"), MB_OK); + } while (rsz>0 && lanlink.connected); + insize = size-rsz; + recvd = (rsz<=0); + #ifdef GBA_LOGGING + if(systemVerbose & VERBOSE_LINK) { + //log("%sCR01\n",(LPCTSTR)LogStr); + log("%sCRecv(%d) Size : %d/%d %s\n", (LPCTSTR)LogStr, peek, insize, size, (LPCTSTR)DataHex(inbuffer,size)); + } + #endif + return recvd; +} + +BOOL lclient::WaitForData(int ms) { + unsigned long notblock = 1; //AdamN: 0=blocking, non-zero=non-blocking + unsigned long arg = 0; + //fd_set fdset; + //timeval wsocktimeout; + MSG msg; + int ready = 0; + //DWORD needms = ms; + EmuCtr++; + if(EmuReseted) DiscardData(); + EmuReseted = false; + DWORD starttm = GetTickCount(); + do { //Waiting for incomming data before continuing CPU execution + SleepEx(0,true); //SleepEx(0,true); //to give time for incoming data + if(PeekMessage(&msg, 0/*theApp.GetMainWnd()->m_hWnd*/, 0, 0, PM_NOREMOVE)) { + if(msg.message==WM_CLOSE) AppTerminated=true; else theApp.PumpMessage(); //seems to be processing message only if it has message otherwise it halt the program + } + //fdset.fd_count = 1; + notblock = 0; + //fdset.fd_array[0] = lanlink.tcpsocket; + ioctlsocket(lanlink.tcpsocket, FIONBIO, ¬block); //AdamN: temporarily use blocking for reading + int ern=WSAGetLastError(); + if(ern!=0) { + log("clIOCTL Error: %d\n",ern); + //if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065) lanlink.connected = false; + } + arg = 0; //1; + if(ioctlsocket(lanlink.tcpsocket, FIONREAD, &arg)!=0) { //AdamN: Alternative(Faster) to Select(Slower) + int ern=WSAGetLastError(); + if(ern!=0) { + log("%sCC Error: %d\n",(LPCTSTR)LogStr,ern); + lanlink.connected = false; + MessageBox(NULL, _T("Player 1 disconnected."), _T("Link"), MB_OK); + break; + } + } + if(arg>0) ready++; + //wsocktimeout.tv_sec = linktimeout / 1000; + //wsocktimeout.tv_usec = linktimeout % 1000; //0; //AdamN: remainder should be set also isn't? + //ready = select(0, &fdset, NULL, NULL, &wsocktimeout); + //int ern=WSAGetLastError(); + //if(ern!=0) { + // log("slCC Error: %d\n",ern); + // if(ern==10054 || ern==10053 || ern==10057 || ern==10051 || ern==10050 || ern==10065) lanlink.connected = false; + //} + } while (lanlink.connected && ready==0 && /*(int)*/(GetTickCount()-starttm)<(DWORD)ms && !AppTerminated && !EmuReseted); //with ms<0 might not gets a proper result? + //if((GetTickCount()-starttm)>=(DWORD)ms) log("TimeOut:%d\n",ms); + EmuCtr--; + return (ready>0); +} + +BOOL lclient::IsDataReady(void) { + unsigned long arg = 0; + if(EmuReseted) DiscardData(); + EmuReseted = false; + if(ioctlsocket(lanlink.tcpsocket, FIONREAD, &arg)!=0) { //AdamN: Alternative(Faster) to Select(Slower) + int ern=WSAGetLastError(); + if(ern!=0) { + log("%sCC Error: %d\n",(LPCTSTR)LogStr,ern); + lanlink.connected = false; + MessageBox(NULL, _T("Player 1 disconnected."), _T("Link"), MB_OK); + } + } + return(arg>0); +} + +int lclient::DiscardData(void) { + char buff[256]; + unsigned long arg; + int sz = 0; + do { + arg = 0; + if(ioctlsocket(lanlink.tcpsocket, FIONREAD, &arg)!=0) { //AdamN: Alternative(Faster) to Select(Slower) + int ern=WSAGetLastError(); + if(ern!=0) { + log("%sCC Error: %d\n",(LPCTSTR)LogStr,ern); + lanlink.connected = false; + MessageBox(NULL, _T("Player 1 disconnected."), _T("Link"), MB_OK); + } + } + if(arg>0) { + int cnt=recv(lanlink.tcpsocket, buff, min(arg,256), 0); + if(cnt>0) sz+=cnt; + } + } while (arg>0 && lanlink.connected); + return(sz); +} + +BOOL LinkSendData(const char *buf, int size, int nretry) { + BOOL sent = false; + if(linkid) //client + sent = lc.SendData(buf, size, nretry); + else //server + sent = ls.SendData(buf, size, nretry); + return(sent); +} + +BOOL LinkIsDataReady(void) { + BOOL rdy = false; + if(linkid) //client + rdy = lc.IsDataReady(); + else //server + rdy = ls.IsDataReady(); + return(rdy); +} + +BOOL LinkWaitForData(int ms) { + BOOL rdy = false; + if(linkid) //client + rdy = lc.WaitForData(ms); + else //server + rdy = ls.WaitForData(ms); + return(rdy); } diff --git a/src/gba/GBALink.h b/src/gba/GBALink.h index 353f07f0..914a7b38 100644 --- a/src/gba/GBALink.h +++ b/src/gba/GBALink.h @@ -4,30 +4,41 @@ #include #endif +#ifdef _MSC_VER //#ifdef _WIN32 +#include "../Win32/WinHelper.h" // AdamN: for CCriticalSection +#endif + #define LINK_PARENTLOST 0x80 #define UNSUPPORTED -1 #define MULTIPLAYER 0 #define NORMAL8 1 -#define NORMAL32 2 +#define NORMAL32 2 //AdamN: wireless use normal32 also #define UART 3 #define JOYBUS 4 #define GP 5 +#define INFRARED 6 //AdamN: Infrared Register at 4000136h #define RFU_INIT 0 #define RFU_COMM 1 #define RFU_SEND 2 #define RFU_RECV 3 -#define COMM_SIODATA32_L 0x120 -#define COMM_SIODATA32_H 0x122 +#define COMM_SIODATA32_L 0x120 //AdamN: Lower 16bit on Normal mode +#define COMM_SIODATA32_H 0x122 //AdamN: Higher 16bit on Normal mode +#define COMM_SIOMULTI0 0x120 //AdamN: SIOMULTI0 (16bit) on MultiPlayer mode (Parent/Master) +#define COMM_SIOMULTI1 0x122 //AdamN: SIOMULTI1 (16bit) on MultiPlayer mode (Child1/Slave1) +#define COMM_SIOMULTI2 0x124 //AdamN: SIOMULTI2 (16bit) on MultiPlayer mode (Child2/Slave2) +#define COMM_SIOMULTI3 0x126 //AdamN: SIOMULTI3 (16bit) on MultiPlayer mode (Child3/Slave3) #define COMM_SIOCNT 0x128 -#define COMM_SIODATA8 0x12a -#define COMM_RCNT 0x134 +#define COMM_SIODATA8 0x12a //AdamN: 8bit on Normal/UART mode, (up to 4x8bit with FIFO) +#define COMM_SIOMLT_SEND 0x12a //AdamN: SIOMLT_SEND (16bit R/W) on MultiPlayer mode (local outgoing) +#define COMM_RCNT 0x134 //AdamN: SIO Mode (4bit data) on GeneralPurpose mode +#define COMM_IR 0x136 //AdamN: Infrared Register (16bit) 1bit data at a time(LED On/Off)? #define COMM_JOYCNT 0x140 -#define COMM_JOY_RECV_L 0x150 +#define COMM_JOY_RECV_L 0x150 //AdamN: Send/Receive 8bit Lower first then 8bit Higher #define COMM_JOY_RECV_H 0x152 -#define COMM_JOY_TRANS_L 0x154 +#define COMM_JOY_TRANS_L 0x154 //AdamN: Send/Receive 8bit Lower first then 8bit Higher #define COMM_JOY_TRANS_H 0x156 -#define COMM_JOYSTAT 0x158 +#define COMM_JOYSTAT 0x158 //AdamN: Send/Receive 8bit lower only #define JOYSTAT_RECV 2 #define JOYSTAT_SEND 8 @@ -53,11 +64,14 @@ typedef struct { int lastlinktime; u8 numgbas; u8 linkflags; - int rfu_q[4]; - u8 rfu_request[4]; - int rfu_linktime[4]; - u32 rfu_bdata[4][7]; - u32 rfu_data[4][32]; + int rfu_q[5]; + u16 rfu_qid[5]; + u8 rfu_request[5]; + u16 rfu_reqid[5]; + int rfu_linktime[5]; + u32 rfu_bdata[5][7]; //for 0x16/0x1d/0x1e? + u32 rfu_gdata[5]; //for 0x17/0x19?/0x1e? + u32 rfu_data[5][32]; //for 0x24-0x26 } LINKDATA; class lserver{ @@ -65,56 +79,81 @@ class lserver{ fd_set fdset; timeval wsocktimeout; //timeval udptimeout; +public: char inbuffer[256], outbuffer[256]; int *intinbuffer; u16 *u16inbuffer; + u32 *u32inbuffer; int *intoutbuffer; u16 *u16outbuffer; + u32 *u32outbuffer; + int insize, outsize; int counter; int done; -public: int howmanytimes; - SOCKET tcpsocket[4]; - SOCKADDR_IN udpaddr[4]; + SOCKET tcpsocket[5]; + SOCKADDR_IN udpaddr[5]; + DWORD latency[5]; lserver(void); int Init(void*); - void Send(void); - void Recv(void); + BOOL Send(void); + BOOL Recv(void); + BOOL WaitForData(int ms); + BOOL SendData(int size, int nretry = 0); + BOOL SendData(const char *buf, int size, int nretry = 0); + BOOL RecvData(int size, int idx, bool peek = false); + BOOL IsDataReady(void); + int DiscardData(int idx); }; class lclient{ fd_set fdset; timeval wsocktimeout; +public: char inbuffer[256], outbuffer[256]; int *intinbuffer; u16 *u16inbuffer; + u32 *u32inbuffer; int *intoutbuffer; u16 *u16outbuffer; - int numbytes; -public: + u32 *u32outbuffer; + int numbytes, insize, outsize; bool oncesend; SOCKADDR_IN serverinfo; SOCKET noblock; int numtransfers; lclient(void); int Init(LPHOSTENT, void*); - void Send(void); - void Recv(void); + BOOL Send(void); + BOOL Recv(void); void CheckConn(void); + BOOL SendData(int size, int nretry = 0); + BOOL SendData(const char *buf, int size, int nretry = 0); + BOOL RecvData(int size, bool peek = false); + BOOL WaitForData(int ms); + BOOL IsDataReady(void); + int DiscardData(void); }; typedef struct { SOCKET tcpsocket; //SOCKET udpsocket; + DWORD latency; int numgbas; HANDLE thread; u8 type; u8 server; bool terminate; bool connected; - bool speed; + bool speed; //speedhack bool active; + int mode; } LANLINKDATA; + +typedef struct { + u16 Command; + u16 Param; +} LINKCMDPRM; #endif extern bool gba_joybus_enabled; @@ -124,24 +163,47 @@ extern sf::IPAddress joybusHostAddr; extern void JoyBusConnect(); extern void JoyBusShutdown(); extern void JoyBusUpdate(int ticks); +extern inline int GetSIOMode(u16 siocnt, u16 rcnt); extern bool gba_link_enabled; #ifdef _MSC_VER +extern void LogStrPush(const char *str); +extern void LogStrPop(int len); +extern void LinkCmdQueue(u16 Cmd, u16 Prm); +extern u8 gbStartLink(u8 b); +extern u16 gbLinkUpdate(u8 b); extern void StartLink(u16); +extern void StartLink2(u16); extern void StartGPLink(u16); extern void LinkSSend(u16); extern void LinkUpdate(int); +extern void LinkUpdate2(int ticks, int FrameCnt); extern void LinkChildStop(); extern void LinkChildSend(u16); -extern void CloseLanLink(); +extern void CloseLink(); //CloseLanLink(); //AdamN: this should be CloseLink isn't? extern char *MakeInstanceFilename(const char *Input); extern LANLINKDATA lanlink; extern int vbaid; extern bool rfu_enabled; extern int linktimeout; +extern u8 gbSIO_SC; extern lclient lc; +extern lserver ls; extern int linkid; +extern bool linkdatarecvd[4]; +extern bool LinkIsWaiting; +extern bool LinkFirstTime; +extern bool EmuReseted; +extern int EmuCtr; +extern WinHelper::CCriticalSection c_s; //AdamN: critical section object to lock shared resource on multithread as CWnd is not thread-safe +extern int LinkCommand; +extern int LinkParam1; +extern int LinkParam2; +extern int LinkParam4; +extern int LinkParam8; +extern bool LinkHandlerActive; +//extern CPtrList LinkCmdList; #else // These are stubbed for now inline void StartLink(u16){} inline void StartGPLink(u16){} diff --git a/src/gba/Globals.h b/src/gba/Globals.h index 3d0781df..24e0e2ed 100644 --- a/src/gba/Globals.h +++ b/src/gba/Globals.h @@ -15,6 +15,8 @@ #define VERBOSE_UNDEFINED 256 #define VERBOSE_AGBPRINT 512 #define VERBOSE_SOUNDOUTPUT 1024 +#define VERBOSE_SIO 2048 +#define VERBOSE_LINK 4096 extern reg_pair reg[45]; extern bool ioReadable[0x400]; diff --git a/src/gba/Sound.cpp b/src/gba/Sound.cpp index 025ec03a..cee4cb27 100644 --- a/src/gba/Sound.cpp +++ b/src/gba/Sound.cpp @@ -303,7 +303,7 @@ void soundEvent(u32 address, u16 data) { switch ( address ) { - case SGCNT0_H: + case SGCNT0_H: //SOUNDCNT_H write_SGCNT0_H( data ); break; @@ -319,7 +319,7 @@ void soundEvent(u32 address, u16 data) WRITE16LE( &ioMem[address], data ); break; - case 0x88: + case 0x88: //SOUNDBIAS data &= 0xC3FF; WRITE16LE( &ioMem[address], data ); break; diff --git a/src/gba/bios.cpp b/src/gba/bios.cpp index bde18386..f0d72d88 100644 --- a/src/gba/bios.cpp +++ b/src/gba/bios.cpp @@ -856,7 +856,7 @@ void BIOS_RegisterRamReset(u32 flags) // no need to trace here. this is only called directly from GBA.cpp // to emulate bios initialization - CPUUpdateRegister(0x0, 0x80); + CPUUpdateRegister(0x0, 0x80); //DISPCNT if(flags) { if(flags & 0x01) { @@ -894,35 +894,35 @@ void BIOS_RegisterRamReset(u32 flags) for(i = 0; i < 0x18; i++) CPUUpdateRegister(0xb0+i*2, 0); - CPUUpdateRegister(0x130, 0); - CPUUpdateRegister(0x20, 0x100); - CPUUpdateRegister(0x30, 0x100); - CPUUpdateRegister(0x26, 0x100); - CPUUpdateRegister(0x36, 0x100); + CPUUpdateRegister(0x130, 0); //KEYINPUT + CPUUpdateRegister(0x20, 0x100); //BG2PA + CPUUpdateRegister(0x30, 0x100); //BG3PA + CPUUpdateRegister(0x26, 0x100); //BG2PD + CPUUpdateRegister(0x36, 0x100); //BG3PD } if(flags & 0x20) { int i; for(i = 0; i < 8; i++) - CPUUpdateRegister(0x110+i*2, 0); - CPUUpdateRegister(0x134, 0x8000); + CPUUpdateRegister(0x110+i*2, 0); //Unused Registers after Timer Register? + CPUUpdateRegister(0x134, 0x8000); //COMM_RCNT for GP/JOYBUS? for(i = 0; i < 7; i++) - CPUUpdateRegister(0x140+i*2, 0); + CPUUpdateRegister(0x140+i*2, 0); //Unused Registers after COMM_JOYCNT? } if(flags & 0x40) { int i; - CPUWriteByte(0x4000084, 0); - CPUWriteByte(0x4000084, 0x80); - CPUWriteMemory(0x4000080, 0x880e0000); - CPUUpdateRegister(0x88, CPUReadHalfWord(0x4000088)&0x3ff); - CPUWriteByte(0x4000070, 0x70); + CPUWriteByte(0x4000084, 0); //SOUNDCNT_X, Is this realy needed as below it's rewritten right? + CPUWriteByte(0x4000084, 0x80); //SOUNDCNT_X, Rewritten above? + CPUWriteMemory(0x4000080, 0x880e0000); //SOUNDCNT_L + CPUUpdateRegister(0x88, CPUReadHalfWord(0x4000088)&0x3ff); //SOUNDBIAS + CPUWriteByte(0x4000070, 0x70); //SOUND3CNT_L for(i = 0; i < 8; i++) CPUUpdateRegister(0x90+i*2, 0); - CPUWriteByte(0x4000070, 0); + CPUWriteByte(0x4000070, 0); //SOUND3CNT_L, Rewritten again? for(i = 0; i < 8; i++) CPUUpdateRegister(0x90+i*2, 0); - CPUWriteByte(0x4000084, 0); + CPUWriteByte(0x4000084, 0); //SOUNDCNT_X, Another Rewriten?? } } } diff --git a/src/win32/DirectSound.cpp b/src/win32/DirectSound.cpp index b0293067..bb3a6623 100644 --- a/src/win32/DirectSound.cpp +++ b/src/win32/DirectSound.cpp @@ -43,6 +43,8 @@ public: void reset(); // stop and reset the secondary sound buffer void resume(); // resume the secondary sound buffer void write(u16 * finalWave, int length); // write the emulated sound to the secondary sound buffer + // Configuration Changes + void setThrottle( unsigned short throttle ); }; @@ -191,6 +193,8 @@ bool DirectSound::init(long sampleRate) return false; } + setThrottle(theApp.throttle); //AdamN: setting sound pitch to current throttle + return true; } @@ -240,7 +244,7 @@ void DirectSound::write(u16 * finalWave, int length) LPVOID lpvPtr2; DWORD dwBytes2 = 0; - if( !speedup && synchronize && !theApp.throttle ) { + if( !speedup && synchronize /*&& !theApp.throttle*/ ) { hr = dsbSecondary->GetStatus(&status); if( status & DSBSTATUS_PLAYING ) { if( !soundPaused ) { @@ -310,6 +314,18 @@ void DirectSound::write(u16 * finalWave, int length) } } +void DirectSound::setThrottle( unsigned short throttle ) //AdamN: doesn't works quite right yet +{ + if(!pDirectSound) return; + if( dsbSecondary == NULL ) return; + + if( throttle == 0 ) throttle = 100; + //HRESULT hr = dsbPrimary->SetFrequency( (DWORD)(soundGetSampleRate() * throttle / 100.0f) ); //AdamN: may not be valid for primary buffer, also need to check CAPS whether frequency is supported or not + //ASSERT( hr == S_OK ); + HRESULT hr = dsbSecondary->SetFrequency( (DWORD)(soundGetSampleRate() * throttle / 100.0f) ); //AdamN: may need to use IDirectMusicSegment8::SetLength to adjust the inverval for looping to prevent silence when pitch is higher than normal pitch or restarted/relooped when pitch is lower than normal before it finished the whole stream + ASSERT( hr == S_OK ); +} + SoundDriver *newDirectSound() { return new DirectSound(); diff --git a/src/win32/Disassemble.cpp b/src/win32/Disassemble.cpp index 0cb36156..75e49b53 100644 --- a/src/win32/Disassemble.cpp +++ b/src/win32/Disassemble.cpp @@ -33,10 +33,15 @@ Disassemble::Disassemble(CWnd* pParent /*=NULL*/) m_t = FALSE; m_v = FALSE; m_z = FALSE; + m_breakpt = FALSE; + m_autostep = FALSE; mode = -1; //}}AFX_DATA_INIT mode = 0; address = 0; + //breakaddr = 0xffffffff; + //breakpt = false; + autostep = false; autoUpdate = false; count = 1; } @@ -56,6 +61,9 @@ void Disassemble::DoDataExchange(CDataExchange* pDX) DDX_Check(pDX, IDC_V, m_v); DDX_Check(pDX, IDC_Z, m_z); DDX_Radio(pDX, IDC_AUTOMATIC, mode); + DDX_Check(pDX, IDC_BREAK_AT, m_breakpt); + DDX_Control(pDX, IDC_ADDRESS2, m_address2); + DDX_Check(pDX, IDC_AUTO_STEP, m_autostep); //}}AFX_DATA_MAP } @@ -73,7 +81,9 @@ BEGIN_MESSAGE_MAP(Disassemble, CDialog) ON_BN_CLICKED(IDC_THUMB, OnThumb) ON_WM_VSCROLL() //}}AFX_MSG_MAP - END_MESSAGE_MAP() + ON_BN_CLICKED(IDC_BREAK_AT, &Disassemble::OnBnClickedBreakAt) + ON_BN_CLICKED(IDC_STEPINTO, &Disassemble::OnBnClickedStepinto) +END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // Disassemble message handlers @@ -338,3 +348,38 @@ void Disassemble::PostNcDestroy() { delete this; } + + +void Disassemble::OnBnClickedBreakAt() +{ + // TODO: Add your control notification handler code here + CString buffer; + m_address2.GetWindowText(buffer); + sscanf(buffer, "%x", &breakaddr); + //if (mode==1) + breakaddr&=0xfffffffc; + //else if (mode==2) + // breakaddr&=0xfffffffe; + m_breakpt = !m_breakpt; + breakpt = (bool)m_breakpt; + refresh(); +} + + +void Disassemble::OnBnClickedStepinto() +{ + // TODO: Add your control notification handler code here + if(rom != NULL) + { + if(armState) + breakaddr = breakaddr + 4; + else + breakaddr = breakaddr + 2; + char buffer[20]; + sprintf(buffer, _T("%08x"), breakaddr); + m_address2.SetWindowText(buffer); + cpuNextEvent = 1; //cpuTotalTicks; + holdState = false; + refresh(); + } +} diff --git a/src/win32/Disassemble.h b/src/win32/Disassemble.h index 4bf78e50..ceae4e5a 100644 --- a/src/win32/Disassemble.h +++ b/src/win32/Disassemble.h @@ -22,13 +22,17 @@ class Disassemble : public ResizeDlg, IUpdateListener void refresh(); int count; bool autoUpdate; + //bool breakpt; + bool autostep; u32 address; + //u32 breakaddr; Disassemble(CWnd* pParent = NULL); // standard constructor // Dialog Data //{{AFX_DATA(Disassemble) enum { IDD = IDD_DISASSEMBLE }; CEdit m_address; + CEdit m_address2; CListBox m_list; BOOL m_c; BOOL m_f; @@ -37,6 +41,8 @@ class Disassemble : public ResizeDlg, IUpdateListener BOOL m_t; BOOL m_v; BOOL m_z; + BOOL m_breakpt; + BOOL m_autostep; int mode; //}}AFX_DATA @@ -67,7 +73,10 @@ class Disassemble : public ResizeDlg, IUpdateListener afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); //}}AFX_MSG DECLARE_MESSAGE_MAP() - }; +public: + afx_msg void OnBnClickedBreakAt(); + afx_msg void OnBnClickedStepinto(); +}; //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. diff --git a/src/win32/LinkOptions.cpp b/src/win32/LinkOptions.cpp index a5c22d43..0124af03 100644 --- a/src/win32/LinkOptions.cpp +++ b/src/win32/LinkOptions.cpp @@ -1,8 +1,13 @@ #include "stdafx.h" +//#include "afxctl.h" #include "vba.h" #include "LinkOptions.h" #include "../gba/GBALink.h" +/*#ifdef _MSC_VER //#ifdef _WIN32 +#include "WinHelper.h" // AdamN: MFC MultiThreading +#endif*/ + extern lserver ls; #ifdef _DEBUG @@ -49,7 +54,7 @@ void LinkOptions::DoDataExchange(CDataExchange* pDX) BOOL LinkOptions::OnInitDialog(){ TCITEM tabitem; - char tabtext[3][8] = {"General", "Server", "Client"}; + char tabtext[3][8] = {_T("General"), _T("Server"), _T("Client")}; int i; CDialog::OnInitDialog(); @@ -145,6 +150,11 @@ void LinkServer::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(LinkServer) + BOOL ok = (theApp.cartridgeType != IMAGE_GB); + GetDlgItem(IDC_LINK3P)->EnableWindow(ok); + GetDlgItem(IDC_LINK4P)->EnableWindow(ok); + GetDlgItem(IDC_LINK5P)->EnableWindow(rfu_enabled && ok); + DDX_Radio(pDX, IDC_LINK2P, m_numplayers); DDX_Radio(pDX, IDC_LINKTCP, m_prottype); DDX_Check(pDX, IDC_SSPEED, m_speed); @@ -195,7 +205,7 @@ BOOL LinkServer::OnInitDialog() { CDialog::OnInitDialog(); - m_numplayers = lanlink.numgbas; + m_numplayers = max(lanlink.numgbas-1,0); m_prottype = lanlink.type; m_speed = lanlink.speed; @@ -455,14 +465,14 @@ void LinkOptions::OnOk() void LinkGeneral::OnRadio1() { m_type = 0; - lanlink.active = 0; + lanlink.active = 0; //Single Computer GetParent()->Invalidate(); } void LinkGeneral::OnRadio2() { m_type = 1; - lanlink.active = 1; + lanlink.active = 1; //Networks GetParent()->Invalidate(); } @@ -473,7 +483,7 @@ BOOL LinkGeneral::OnInitDialog(){ CDialog::OnInitDialog(); m_timeout.LimitText(5); - sprintf(timeout, "%d", linktimeout); + sprintf(timeout, _T("%d"), linktimeout); m_timeout.SetWindowText(timeout); m_type = lanlink.active; @@ -504,8 +514,8 @@ void LinkServer::OnServerStart() if((errorcode=ls.Init(&dlg))!=0){ char message[50]; - sprintf(message, "Error %d occured.\nPlease try again.", errorcode); - MessageBox(message, "Error", MB_OK); + sprintf(message, _T("Error %d occured.\nPlease try again."), errorcode); + MessageBox(message, _T("Error"), MB_OK); return; } @@ -520,6 +530,7 @@ BOOL LinkClient::OnInitDialog() m_prottype = lanlink.type; m_hacks = lanlink.speed; + m_serverip.SetWindowText(_T("localhost")); UpdateData(FALSE); @@ -539,11 +550,12 @@ void LinkClient::OnLinkConnect() lanlink.speed = m_hacks==1 ? true : false; m_serverip.GetWindowText(ipaddress, 30); - + if((errorcode=lc.Init(gethostbyname(ipaddress), &dlg))!=0){ + char message[50]; - sprintf(message, "Error %d occured.\nPlease try again.", errorcode); - MessageBox(message, "Error", MB_OK); + sprintf(message, _T("Error %d occured.\nPlease try again."), errorcode); + MessageBox(message, _T("Error"), MB_OK); return; } dlg.DoModal(); @@ -557,7 +569,7 @@ void LinkOptions::GetAllData(LinkGeneral *src) src->UpdateData(true); src->m_timeout.GetWindowText(timeout, 5); - sscanf(timeout, "%d", &linktimeout); + sscanf(timeout, _T("%d"), &linktimeout); if(src->m_type==0){ lanlink.speed = 0; @@ -593,6 +605,7 @@ void ServerWait::DoDataExchange(CDataExchange* pDX) //}}AFX_DATA_MAP } + BEGIN_MESSAGE_MAP(ServerWait, CDialog) //{{AFX_MSG_MAP(ServerWait) ON_BN_CLICKED(ID_CANCEL, OnCancel) @@ -604,7 +617,9 @@ END_MESSAGE_MAP() void ServerWait::OnCancel() { - lanlink.terminate = true; + c_s.Lock(); //AdamN: Locking resource to prevent deadlock + lanlink.terminate = true; //AdamN: accessing this might not be thread-safe w/o locking + c_s.Unlock(); //AdamN: Unlock it after use CDialog::OnCancel(); } diff --git a/src/win32/Logging.cpp b/src/win32/Logging.cpp index 6a61ec20..4f01bab7 100644 --- a/src/win32/Logging.cpp +++ b/src/win32/Logging.cpp @@ -33,6 +33,7 @@ Logging::Logging(CWnd* pParent /*=NULL*/) m_dma3 = FALSE; m_agbprint = FALSE; m_undefined = FALSE; + m_sio = FALSE; //}}AFX_DATA_INIT } @@ -52,6 +53,8 @@ void Logging::DoDataExchange(CDataExchange* pDX) DDX_Check(pDX, IDC_VERBOSE_AGBPRINT, m_agbprint); DDX_Check(pDX, IDC_VERBOSE_UNDEFINED, m_undefined); DDX_Check(pDX, IDC_VERBOSE_SOUNDOUTPUT, m_sound_output); + DDX_Check(pDX, IDC_VERBOSE_SIO, m_sio); + DDX_Check(pDX, IDC_VERBOSE_LINK, m_link); } @@ -69,10 +72,13 @@ BEGIN_MESSAGE_MAP(Logging, CDialog) ON_BN_CLICKED(IDC_VERBOSE_UNALIGNED_ACCESS, OnVerboseUnalignedAccess) ON_BN_CLICKED(IDC_VERBOSE_UNDEFINED, OnVerboseUndefined) ON_BN_CLICKED(IDC_VERBOSE_SOUNDOUTPUT, OnVerboseSoundoutput) + ON_BN_CLICKED(IDC_VERBOSE_SIO, OnVerboseSIO) ON_BN_CLICKED(IDC_SAVE, OnSave) ON_EN_ERRSPACE(IDC_LOG, OnErrspaceLog) ON_EN_MAXTEXT(IDC_LOG, OnMaxtextLog) ON_WM_CLOSE() +// ON_BN_CLICKED(IDC_VERBOSE_LINK, &Logging::OnBnClickedVerboseLink) +ON_BN_CLICKED(IDC_VERBOSE_LINK, &Logging::OnVerboseLink) END_MESSAGE_MAP() @@ -147,6 +153,16 @@ void Logging::OnVerboseSoundoutput() systemVerbose ^= VERBOSE_SOUNDOUTPUT; } +void Logging::OnVerboseSIO() +{ + systemVerbose ^= VERBOSE_SIO; +} + +void Logging::OnVerboseLink() +{ + systemVerbose ^= VERBOSE_LINK; +} + void Logging::OnSave() { int len = m_log.GetWindowTextLength(); @@ -213,6 +229,8 @@ BOOL Logging::OnInitDialog() m_undefined = (systemVerbose & VERBOSE_UNDEFINED) != 0; m_agbprint = (systemVerbose & VERBOSE_AGBPRINT) != 0; m_sound_output = (systemVerbose & VERBOSE_SOUNDOUTPUT) != 0; + m_sio = (systemVerbose & VERBOSE_SIO) != 0; + m_link = (systemVerbose & VERBOSE_LINK) != 0; UpdateData(FALSE); m_log.LimitText(-1); @@ -293,3 +311,7 @@ void toolsClearLog() Logging::instance->clearLog(); } } + + + + diff --git a/src/win32/Logging.h b/src/win32/Logging.h index e9382afe..6a4a9593 100644 --- a/src/win32/Logging.h +++ b/src/win32/Logging.h @@ -27,6 +27,8 @@ public: BOOL m_agbprint; BOOL m_undefined; BOOL m_sound_output; + BOOL m_sio; + BOOL m_link; // Overrides protected: @@ -47,6 +49,8 @@ protected: afx_msg void OnVerboseUnalignedAccess(); afx_msg void OnVerboseUndefined(); afx_msg void OnVerboseSoundoutput(); + afx_msg void OnVerboseSIO(); + afx_msg void OnVerboseLink(); afx_msg void OnSave(); afx_msg void OnClose(); afx_msg void OnErrspaceLog(); diff --git a/src/win32/MainWndOptions.cpp b/src/win32/MainWndOptions.cpp index ca8c82c6..4e02de74 100644 --- a/src/win32/MainWndOptions.cpp +++ b/src/win32/MainWndOptions.cpp @@ -40,7 +40,12 @@ void MainWnd::OnOptionsFrameskipThrottleNothrottle() { theApp.updateThrottle( 0 ); // disable - theApp.autoFrameSkip = false; + /*if(theApp.autoFrameSkip) { + frameSkip = 0; + gbFrameSkip = 0; + systemFrameSkip = 0; + } + theApp.autoFrameSkip = false;*/ } void MainWnd::OnUpdateOptionsFrameskipThrottleNothrottle(CCmdUI* pCmdUI) @@ -52,7 +57,7 @@ void MainWnd::OnUpdateOptionsFrameskipThrottleNothrottle(CCmdUI* pCmdUI) void MainWnd::OnOptionsFrameskipThrottle25() { theApp.updateThrottle( 25 ); - theApp.autoFrameSkip = false; + //theApp.autoFrameSkip = false; } void MainWnd::OnUpdateOptionsFrameskipThrottle25(CCmdUI* pCmdUI) @@ -64,7 +69,7 @@ void MainWnd::OnUpdateOptionsFrameskipThrottle25(CCmdUI* pCmdUI) void MainWnd::OnOptionsFrameskipThrottle50() { theApp.updateThrottle( 50 ); - theApp.autoFrameSkip = false; + //theApp.autoFrameSkip = false; } void MainWnd::OnUpdateOptionsFrameskipThrottle50(CCmdUI* pCmdUI) @@ -76,7 +81,7 @@ void MainWnd::OnUpdateOptionsFrameskipThrottle50(CCmdUI* pCmdUI) void MainWnd::OnOptionsFrameskipThrottle100() { theApp.updateThrottle( 100 ); - theApp.autoFrameSkip = false; + //theApp.autoFrameSkip = false; } void MainWnd::OnUpdateOptionsFrameskipThrottle100(CCmdUI* pCmdUI) @@ -88,7 +93,7 @@ void MainWnd::OnUpdateOptionsFrameskipThrottle100(CCmdUI* pCmdUI) void MainWnd::OnOptionsFrameskipThrottle150() { theApp.updateThrottle( 150 ); -theApp.autoFrameSkip = false; +//theApp.autoFrameSkip = false; } void MainWnd::OnUpdateOptionsFrameskipThrottle150(CCmdUI* pCmdUI) @@ -100,7 +105,7 @@ void MainWnd::OnUpdateOptionsFrameskipThrottle150(CCmdUI* pCmdUI) void MainWnd::OnOptionsFrameskipThrottle200() { theApp.updateThrottle( 200 ); - theApp.autoFrameSkip = false; + //theApp.autoFrameSkip = false; } void MainWnd::OnUpdateOptionsFrameskipThrottle200(CCmdUI* pCmdUI) @@ -116,7 +121,7 @@ void MainWnd::OnOptionsFrameskipThrottleOther() if( v ) { theApp.updateThrottle( v ); - theApp.autoFrameSkip = false; + //theApp.autoFrameSkip = false; } } @@ -137,12 +142,16 @@ void MainWnd::OnOptionsFrameskipAutomatic() theApp.autoFrameSkip = !theApp.autoFrameSkip; if(!theApp.autoFrameSkip && emulating) theApp.updateFrameSkip(); - else + else if(theApp.autoFrameSkip) { - theApp.throttle = false; - frameSkip = 0; + //theApp.throttle = false; + frameSkip = 9; + gbFrameSkip = 9; systemFrameSkip = 0; } + //frameSkip = 0; + //gbFrameSkip = 0; + //systemFrameSkip = 0; } void MainWnd::OnUpdateOptionsFrameskipAutomatic(CCmdUI* pCmdUI) @@ -152,6 +161,7 @@ void MainWnd::OnUpdateOptionsFrameskipAutomatic(CCmdUI* pCmdUI) BOOL MainWnd::OnOptionsFrameskip(UINT nID) { + //theApp.autoFrameSkip = false; switch(nID) { case ID_OPTIONS_VIDEO_FRAMESKIP_0: case ID_OPTIONS_VIDEO_FRAMESKIP_1: @@ -166,7 +176,7 @@ BOOL MainWnd::OnOptionsFrameskip(UINT nID) } if(emulating) theApp.updateFrameSkip(); - theApp.updateThrottle( 0 ); + //theApp.updateThrottle( 0 ); return TRUE; break; case ID_OPTIONS_VIDEO_FRAMESKIP_6: @@ -180,7 +190,7 @@ BOOL MainWnd::OnOptionsFrameskip(UINT nID) } if(emulating) theApp.updateFrameSkip(); - theApp.updateThrottle( 0 ); + //theApp.updateThrottle( 0 ); return TRUE; break; } @@ -585,8 +595,13 @@ void MainWnd::OnUpdateOptionsEmulatorDisablestatusmessages(CCmdUI* pCmdUI) void MainWnd::OnOptionsEmulatorSynchronize() { synchronize = !synchronize; - if (synchronize) - theApp.throttle = false; + if (synchronize) { + //theApp.throttle = false; + //theApp.autoFrameSkip = false; + frameSkip = 0; + gbFrameSkip = 0; + systemFrameSkip = 0; + } } void MainWnd::OnUpdateOptionsEmulatorSynchronize(CCmdUI* pCmdUI) @@ -1023,6 +1038,8 @@ void MainWnd::OnUpdateOptionsGameboyBorder(CCmdUI* pCmdUI) void MainWnd::OnOptionsGameboyPrinter() { theApp.winGbPrinterEnabled = !theApp.winGbPrinterEnabled; + if(gba_link_enabled) + gbSerialFunction = gbStartLink; else if(theApp.winGbPrinterEnabled) gbSerialFunction = gbPrinterSend; else @@ -1560,7 +1577,7 @@ void MainWnd::OnOptionsLinkRFU() if(rfu_enabled) rfu_enabled = false; else { rfu_enabled = true; - MessageBox("Please note this is the first version\nof RFU emulation code and it's not 100% bug free.\nAlso only 2 players single computer are supported at this time.", "Warning", MB_OK); + //MessageBox("Please note this is the first version\nof RFU emulation code and it's not 100% bug free.\nAlso only 2 players single computer are supported at this time.", "Warning", MB_OK); } } @@ -1572,6 +1589,12 @@ void MainWnd::OnUpdateOptionsLinkEnable(CCmdUI* pCmdUI) void MainWnd::OnOptionsLinkEnable() { gba_link_enabled = !gba_link_enabled; + if(gba_link_enabled) + gbSerialFunction = gbStartLink; else + if(theApp.winGbPrinterEnabled) + gbSerialFunction = gbPrinterSend; + else + gbSerialFunction = NULL; } void MainWnd::OnUpdateOptionsLinkRFU(CCmdUI* pCmdUI) diff --git a/src/win32/OpenAL.cpp b/src/win32/OpenAL.cpp index 7d836035..076f714c 100644 --- a/src/win32/OpenAL.cpp +++ b/src/win32/OpenAL.cpp @@ -42,6 +42,7 @@ public: void reset(); // stop and reset the secondary sound buffer void resume(); // play/resume the secondary sound buffer void write(u16 * finalWave, int length); // write the emulated sound to a sound buffer + void setThrottle( unsigned short throttle ); //pitch private: OPENALFNTABLE ALFunction; @@ -181,6 +182,8 @@ bool OpenAL::init(long sampleRate) initialized = true; + + setThrottle(theApp.throttle); //setting pitch to current throttle return true; } @@ -281,7 +284,7 @@ void OpenAL::write(u16 * finalWave, int length) } } - if( !speedup && synchronize && !theApp.throttle ) { + if( !speedup && synchronize /*&& !theApp.throttle*/ ) { // wait until at least one buffer has finished while( nBuffersProcessed == 0 ) { winlog( " waiting...\n" ); @@ -320,6 +323,20 @@ void OpenAL::write(u16 * finalWave, int length) } } +void OpenAL::setThrottle( unsigned short throttle ) +{ + if( !initialized ) return; + winlog( "OpenAL::setThrottle\n" ); + + debugState(); + + if( throttle == 0 ) throttle = 100; + ALFunction.alSourcef(source, AL_PITCH, (float)throttle / 100.0f ); + ASSERT_SUCCESS; + + debugState(); //AdamN: is this needed? +} + SoundDriver *newOpenAL() { winlog( "newOpenAL\n" ); diff --git a/src/win32/Throttle.cpp b/src/win32/Throttle.cpp index 58d0b9a1..85fc1e97 100644 --- a/src/win32/Throttle.cpp +++ b/src/win32/Throttle.cpp @@ -59,8 +59,8 @@ void Throttle::OnOk() { UpdateData(); - if(m_throttle < 5 || m_throttle > 1000) - systemMessage(IDS_INVALID_THROTTLE_VALUE, "Invalid throttle value. Please enter a number between 5 and 1000"); + if(m_throttle < 5 || m_throttle > 2000) + systemMessage(IDS_INVALID_THROTTLE_VALUE, "Invalid throttle value. Please enter a number between 5 and 2000"); else EndDialog(m_throttle); } diff --git a/src/win32/VBA.cpp b/src/win32/VBA.cpp index efa49950..45339264 100644 --- a/src/win32/VBA.cpp +++ b/src/win32/VBA.cpp @@ -149,6 +149,9 @@ void winOutput(const char *, u32); void (*dbgSignal)(int,int) = winSignal; void (*dbgOutput)(const char *, u32) = winOutput; +int lastSA = 0; +int lastSR = 0; + #ifdef MMX extern "C" bool cpu_mmx; #endif @@ -156,7 +159,7 @@ extern "C" bool cpu_mmx; namespace Sm60FPS { float K_fCpuSpeed = 100.0f; // was 98.0f before, but why? - float K_fTargetFps = 60.0f * K_fCpuSpeed / 100; + float K_fTargetFps = 60.0f * K_fCpuSpeed / 100.0f; float K_fDT = 1000.0f / K_fTargetFps; u32 dwTimeElapse; @@ -167,6 +170,7 @@ namespace Sm60FPS float fCurFPS; bool bLastSkip; int nCurSpeed; + float nPrvSpeed; int bSaveMoreCPU; }; @@ -336,8 +340,21 @@ VBA::VBA() } } +CString DataHex(const char *buf, int len) { + CString dat = _T(""); + if(buf!=NULL && len>0) { + for(int i=0; i100 && synchronize) throttle = 100; //max speed when audio sync enabled is 100% + this->throttle = throttle; if( throttle ) { Sm60FPS::K_fCpuSpeed = (float)throttle; Sm60FPS::K_fTargetFps = 60.0f * Sm60FPS::K_fCpuSpeed / 100; Sm60FPS::K_fDT = 1000.0f / Sm60FPS::K_fTargetFps; - autoFrameSkip = false; + /*autoFrameSkip = true; frameSkip = 0; - systemFrameSkip = 0; + gbFrameSkip = 0; + systemFrameSkip = 0;*/ } - soundSetThrottle(throttle); + soundSetThrottle(throttle); //AdamN: only XAudio2 that have this feature? (added for OpenAL) } @@ -1065,14 +1090,13 @@ void systemFrame() void system10Frames(int rate) { - if( theApp.autoFrameSkip ) { u32 time = systemGetClock(); u32 diff = time - theApp.autoFrameSkipLastTime; - theApp.autoFrameSkipLastTime = time; if( diff ) { // countermeasure against div/0 when debugging + theApp.autoFrameSkipLastTime = time; Sm60FPS::nCurSpeed = (1000000/rate)/diff; } else { Sm60FPS::nCurSpeed = 100; @@ -1112,6 +1136,96 @@ void system10Frames(int rate) #endif } +void system1Frames(int rate, int count) //AdamN: update skippedframes faster to make sure it didn't cause too many frames or too little frames being skipped +{ + + if( theApp.autoFrameSkip ) //AdamN: CurSpeed might need to be updated regardless of autoframeskip for throttle to works properly w/o autoframeskip + { + theApp.autoFrameSkipCount++; + u32 time = systemGetClock(); + u32 diff = time - theApp.autoFrameSkipLastTime; + if( diff) { + // countermeasure against div/0 when debugging + int tm = (diff/theApp.autoFrameSkipCount); + float cspd = (100000.0f/rate/*60.0f*/)/tm; //calc current speed smoothly (frame 1-10,2-11,3-12,so on) + Sm60FPS::nCurSpeed = (int)cspd; //(500000/rate)/diff; + if(theApp.autoFrameSkipCount >= 10) { //60 + theApp.autoFrameSkipCount--; //= 0; //to maintain 10 frames per update + theApp.autoFrameSkipLastTime += tm ; //theApp.autoFrameSkipLastTime2; + //theApp.autoFrameSkipLastTime2 += tm; //rough prediction of nextframe's time + }/* else if(theApp.autoFrameSkipCount == 2) + theApp.autoFrameSkipLastTime2 = time;*/ + //if(theApp.autoFrameSkip) + { + float tgtspd = Sm60FPS::K_fCpuSpeed; + int maxfs = gbFrameSkip; + if(theApp.cartridgeType == IMAGE_GBA) maxfs = frameSkip; + //if(speedup && (count % 10)==0) theApp.updateThrottle(Sm60FPS::nCurSpeed); //update throttle on turbo, may slows down things + //if(synchronize && tgtspd>100.0f) float tgtspd = 100.0f; //max speed when audio sync enabled is 100%, this is to prevent skippedframe piling up since CurSpeed never goes beyond 100 + if((systemFrameSkip < maxfs) && (Sm60FPS::nCurSpeed < tgtspd*0.94f/**0.89f *0.845f*/)) { //*0.7f + /*if(maxfs==9) //special feature for auto-frameskip-cap=9 + if(Sm60FPS::nCurSpeed <= 1.01f*Sm60FPS::nPrvSpeed && systemFrameSkip>3) { //if frameskipping no longer effective, don't skip more + systemFrameSkip--; + //Sm60FPS::nPrvSpeed = 0.0f; //0.99f*Sm60FPS::nPrvSpeed; + } else + Sm60FPS::nPrvSpeed = (float)Sm60FPS::nCurSpeed;*/ + //frameSkip += 1; + //gbFrameSkip += 1; + systemFrameSkip += 1; //frames to skip for the next n frames + } else + if((systemFrameSkip > 0) && (Sm60FPS::nCurSpeed > tgtspd/**1.02f*//**1.1f *1.4f*/)) { //*1.07f + //frameSkip -= 1; + //gbFrameSkip -= 1; + systemFrameSkip -= 1; //frames to skip for the next n frames + } + //Sm60FPS::nPrvSpeed = Sm60FPS::nCurSpeed; + } + //theApp.autoFrameSkipCount = 0; + } else { + /*if(theApp.autoFrameSkipCount == 2) + theApp.autoFrameSkipLastTime2 = time;*/ + if(!Sm60FPS::nCurSpeed) + Sm60FPS::nCurSpeed = (int)Sm60FPS::K_fCpuSpeed; //100; + //frameSkip = 0; + //gbFrameSkip = 0; + //systemFrameSkip = 0; + } + } + + if((count % 10)!=0) return; + + if(theApp.rewindMemory) { + if(++theApp.rewindCounter >= (theApp.rewindTimer)) { + theApp.rewindSaveNeeded = true; + theApp.rewindCounter = 0; + } + } + if(systemSaveUpdateCounter) { + if(--systemSaveUpdateCounter <= SYSTEM_SAVE_NOT_UPDATED) { + ((MainWnd *)theApp.m_pMainWnd)->writeBatteryFile(); + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + } + } + + theApp.wasPaused = false; + +// Old autoframeskip crap... might be useful later. autoframeskip Ifdef above might be useless as well now +// theApp.autoFrameSkipLastTime = time; + +#ifdef LOG_PERFORMANCE + if( systemSpeedCounter >= PERFORMANCE_INTERVAL ) { + // log performance every PERFORMANCE_INTERVAL frames + float a = 0.0f; + for( unsigned short i = 0 ; i < PERFORMANCE_INTERVAL ; i++ ) { + a += (float)systemSpeedTable[i]; + } + a /= (float)PERFORMANCE_INTERVAL; + log( _T("Speed: %f\n"), a ); + systemSpeedCounter = 0; + } +#endif +} + void systemScreenMessage(const char *msg) { theApp.screenMessage = true; @@ -1163,7 +1277,7 @@ SoundDriver * systemSoundInit() if( drv ) { if (theApp.throttle) - drv->setThrottle( theApp.throttle ); + drv->setThrottle( theApp.throttle ); //AdamN: XAudio2 seems the only one who has this feature? } return drv; @@ -1251,10 +1365,13 @@ BOOL VBA::OnIdle(LONG lCount) if(debugger) return TRUE; // continue loop return !::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE); - } else if(emulating && active && !paused) { + } else if(emulating && (gba_link_enabled || active) && !paused) { for(int i = 0; i < 2; i++) { - emulator.emuMain(emulator.emuCount); - if(lanlink.connected&&linkid&&lc.numtransfers==0) lc.CheckConn(); + emulator.emuMain(emulator.emuCount); //emuMain=CPULoop,emuCount=250000 + if(/*gba_link_enabled &&*/ lanlink.connected && linkid && lc.numtransfers==0 && (theApp.cartridgeType == IMAGE_GBA) && !rfu_enabled) { + if(/*(i & 1) &&*/ !LinkHandlerActive) + lc.CheckConn(); //AdamN: This is Needed for Client/Slave to works properly, but does it need to be called twice in the FOR loop? + } if(rewindSaveNeeded && rewindMemory && emulator.emuWriteMemState) { rewindCount++; @@ -1622,6 +1739,8 @@ void VBA::loadSettings() rfu_enabled = regQueryDwordValue("RFU", false) ? true : false; gba_link_enabled = regQueryDwordValue("linkEnabled", false) ? true : false; + if(gba_link_enabled) + gbSerialFunction = gbStartLink; gba_joybus_enabled = regQueryDwordValue("joybusEnabled", false) ? true : false; buffer = regQueryStringValue("joybusHostAddr", ""); @@ -2620,11 +2739,11 @@ void Sm60FPS_Init() } -bool Sm60FPS_CanSkipFrame() +bool Sm60FPS_CanSkipFrame() //AdamN: autoframeskipping here doesn't seems to give much effect, more like only giving up to 1 frame skipped(per 10 frames) instead of up to 9 frames { - if( theApp.autoFrameSkip ) { + /*if( theApp.autoFrameSkip )*/ { //AdamN: this is the reason why throttle doesn't works properly w/o autoframeskip if( Sm60FPS::nFrameCnt == 0 ) { - Sm60FPS::nFrameCnt = 0; + //Sm60FPS::nFrameCnt = 0; //AdamN: is this really necessary? Sm60FPS::dwTimeElapse = 0; Sm60FPS::dwTime0 = GetTickCount(); } else { @@ -2634,14 +2753,18 @@ bool Sm60FPS_CanSkipFrame() if( Sm60FPS::nCurSpeed > Sm60FPS::K_fCpuSpeed ) { Sm60FPS::fWantFPS += 1; - if( Sm60FPS::fWantFPS > Sm60FPS::K_fTargetFps ){ - Sm60FPS::fWantFPS = Sm60FPS::K_fTargetFps; + //frameSkip = 0; + //systemFrameSkip = 0; + if( Sm60FPS::fWantFPS > Sm60FPS::K_fTargetFps/**(systemFrameSkip+1)*/ ){ + Sm60FPS::fWantFPS = Sm60FPS::K_fTargetFps/**(systemFrameSkip+1)*/; } } else { - if( Sm60FPS::nCurSpeed < (Sm60FPS::K_fCpuSpeed - 5) ) { + if( Sm60FPS::nCurSpeed < (Sm60FPS::K_fCpuSpeed/**0.94f*/ /*- 5*/) ) { Sm60FPS::fWantFPS -= 1; - if( Sm60FPS::fWantFPS < 30.f ) { - Sm60FPS::fWantFPS = 30.f; + //frameSkip += 1; + //systemFrameSkip += 1; + if( Sm60FPS::fWantFPS < 6.f/*30.f*/ ) { + Sm60FPS::fWantFPS = 6.f/*30.f*/; } } } @@ -2649,12 +2772,15 @@ bool Sm60FPS_CanSkipFrame() Sm60FPS::dwTime1 = GetTickCount(); Sm60FPS::dwTimeElapse += (Sm60FPS::dwTime1 - Sm60FPS::dwTime0); Sm60FPS::dwTime0 = Sm60FPS::dwTime1; + if( !Sm60FPS::bLastSkip && - ( (Sm60FPS::fWantFPS < Sm60FPS::K_fTargetFps) || Sm60FPS::bSaveMoreCPU) ) { + ( (Sm60FPS::fWantFPS < Sm60FPS::K_fTargetFps/**(systemFrameSkip+1)*/) || Sm60FPS::bSaveMoreCPU) ) { Sm60FPS::fCurFPS = (float)Sm60FPS::nFrameCnt * 1000 / Sm60FPS::dwTimeElapse; - if( (Sm60FPS::fCurFPS < Sm60FPS::K_fTargetFps) || Sm60FPS::bSaveMoreCPU ) { + if( (Sm60FPS::fCurFPS < Sm60FPS::K_fTargetFps/**(systemFrameSkip+1)*/) || Sm60FPS::bSaveMoreCPU ) { Sm60FPS::bLastSkip = true; Sm60FPS::nFrameCnt++; + //frameSkip += 1; + //systemFrameSkip += 1; return true; } } @@ -2662,16 +2788,20 @@ bool Sm60FPS_CanSkipFrame() } Sm60FPS::bLastSkip = false; Sm60FPS::nFrameCnt++; - } + //frameSkip = 0; + //systemFrameSkip = 0; + } return false; } void Sm60FPS_Sleep() { - if( theApp.autoFrameSkip ) { + if(!speedup) /*if( theApp.autoFrameSkip )*/ { //AdamN: this is the reason why throttle doesn't works properly w/o autoframeskip u32 dwTimePass = Sm60FPS::dwTimeElapse + (GetTickCount() - Sm60FPS::dwTime0); - u32 dwTimeShould = (u32)(Sm60FPS::nFrameCnt * Sm60FPS::K_fDT); + float kfdt = Sm60FPS::K_fDT; + if(!theApp.autoFrameSkip) kfdt*=(systemFrameSkip+1); + u32 dwTimeShould = (u32)(Sm60FPS::nFrameCnt * kfdt); if( dwTimeShould > dwTimePass ) { Sleep(dwTimeShould - dwTimePass); } diff --git a/src/win32/VBA.h b/src/win32/VBA.h index 6cc88ed0..8ed44721 100644 --- a/src/win32/VBA.h +++ b/src/win32/VBA.h @@ -124,6 +124,8 @@ class VBA : public CWinApp bool tripleBuffering; unsigned short throttle; u32 autoFrameSkipLastTime; + u32 autoFrameSkipLastTime2; + int autoFrameSkipCount; bool autoFrameSkip; bool vsync; bool changingVideoSize; @@ -255,6 +257,27 @@ class VBA : public CWinApp extern VBA theApp; extern int emulating; + extern bool AppTerminated; //AdamN: to mark Application is exiting for other threads to check + extern CString DataHex(const char *buf, int len); //AdamN: buffer to hex string + +namespace Sm60FPS +{ + extern float K_fCpuSpeed; // was 98.0f before, but why? + extern float K_fTargetFps; + extern float K_fDT; + + extern u32 dwTimeElapse; + extern u32 dwTime0; + extern u32 dwTime1; + extern u32 nFrameCnt; + extern float fWantFPS; + extern float fCurFPS; + extern bool bLastSkip; + extern int nCurSpeed; + extern float nPrvSpeed; + extern int bSaveMoreCPU; +}; + #ifdef MMX extern "C" bool cpu_mmx; #endif diff --git a/src/win32/VBA.rc b/src/win32/VBA.rc index e6738f57..44b6abbb 100644 --- a/src/win32/VBA.rc +++ b/src/win32/VBA.rc @@ -132,13 +132,14 @@ BEGIN CONTROL "Network",IDC_LINK_LAN,"Button",BS_AUTORADIOBUTTON,17,43,70,16 END -IDD_LINKTAB2 DIALOG 0, 0, 210, 113 +IDD_LINKTAB2 DIALOGEX 0, 0, 210, 113 STYLE DS_SETFONT | WS_CHILD -FONT 8, "MS Sans Serif" +FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN - CONTROL "2",IDC_LINK2P,"Button",BS_AUTORADIOBUTTON | WS_GROUP,46,16,21,13 - CONTROL "3",IDC_LINK3P,"Button",BS_AUTORADIOBUTTON,94,16,21,13 - CONTROL "4",IDC_LINK4P,"Button",BS_AUTORADIOBUTTON,142,16,21,13 + CONTROL "2",IDC_LINK2P,"Button",BS_AUTORADIOBUTTON | WS_GROUP,33,17,21,13 + CONTROL "3",IDC_LINK3P,"Button",BS_AUTORADIOBUTTON,73,17,21,13 + CONTROL "4",IDC_LINK4P,"Button",BS_AUTORADIOBUTTON,115,17,21,13 + CONTROL "5",IDC_LINK5P,"Button",BS_AUTORADIOBUTTON,156,17,21,13 CONTROL "TCP/IP",IDC_LINKTCP,"Button",BS_AUTORADIOBUTTON | WS_GROUP,54,47,42,14 CONTROL "UDP",IDC_LINKUDP,"Button",BS_AUTORADIOBUTTON | WS_DISABLED,121,47,33,14 PUSHBUTTON "Start!",IDC_SERVERSTART,79,89,50,17 @@ -162,17 +163,17 @@ BEGIN CONTROL "On (fast)",IDC_SPEEDON,"Button",BS_AUTORADIOBUTTON,128,63,48,12 END -IDD_SERVERWAIT DIALOG 0, 0, 186, 90 +IDD_SERVERWAIT DIALOGEX 0, 0, 186, 90 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Waiting for players" -FONT 8, "MS Sans Serif" +FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN PUSHBUTTON "Cancel",IDCANCEL,63,69,50,14 CONTROL "Progress1",IDC_SERVERWAIT,"msctls_progress32",WS_BORDER,33,50,120,13 - LTEXT "",IDC_STATIC1,7,7,154,8 - LTEXT "",IDC_STATIC2,7,17,105,8 - LTEXT "",IDC_STATIC3,7,25,105,8 - LTEXT "",IDC_STATIC4,7,33,105,8 + LTEXT "",IDC_STATIC1,7,7,178,8,SS_ENDELLIPSIS + LTEXT "",IDC_STATIC2,7,17,175,8 + LTEXT "",IDC_STATIC3,7,25,175,8 + LTEXT "",IDC_STATIC4,7,33,175,8 END IDD_OPENDLG DIALOG 36, 24, 202, 117 @@ -740,10 +741,10 @@ BEGIN GROUPBOX "Sprite",IDC_STATIC,8,67,154,30 END -IDD_DISASSEMBLE DIALOG 0, 0, 402, 225 +IDD_DISASSEMBLE DIALOGEX 0, 0, 402, 250 STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "Disassemble" -FONT 8, "MS Sans Serif" +FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN CONTROL "Automatic",IDC_AUTOMATIC,"Button",BS_AUTORADIOBUTTON | WS_GROUP,7,9,47,10 CONTROL "ARM",IDC_ARM,"Button",BS_AUTORADIOBUTTON,62,9,32,10 @@ -800,6 +801,10 @@ BEGIN LTEXT "",IDC_MODE,376,176,20,8,SS_NOPREFIX SCROLLBAR IDC_VSCROLL,283,25,10,161,SBS_VERT PUSHBUTTON "Goto R15",IDC_GOPC,7,204,50,14 + CONTROL "Break at",IDC_BREAK_AT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,225,41,10 + EDITTEXT IDC_ADDRESS2,53,222,65,14,ES_UPPERCASE | ES_AUTOHSCROLL | WS_GROUP + PUSHBUTTON "Step Into",IDC_STEPINTO,124,222,50,14 + CONTROL "Auto Step",IDC_AUTO_STEP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,179,225,47,10 END IDD_GDB_PORT DIALOG 0, 0, 186, 51 @@ -823,13 +828,12 @@ BEGIN LTEXT "",IDC_PORT,143,7,36,8,SS_NOPREFIX END -IDD_LOGGING DIALOGEX 0, 0, 382, 220 +IDD_LOGGING DIALOGEX 0, 0, 382, 257 STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME EXSTYLE WS_EX_TOOLWINDOW CAPTION "Logging" FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN - CONTROL "SWI",IDC_VERBOSE_SWI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,18,90,12 CONTROL "Unaligned memory",IDC_VERBOSE_UNALIGNED_ACCESS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,36,90,12 CONTROL "Illegal write",IDC_VERBOSE_ILLEGAL_WRITE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,54,90,12 CONTROL "Illegal read",IDC_VERBOSE_ILLEGAL_READ,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,72,90,12 @@ -839,12 +843,15 @@ BEGIN CONTROL "DMA 3",IDC_VERBOSE_DMA3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,144,90,12 CONTROL "Undefined instruction",IDC_VERBOSE_UNDEFINED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,162,90,12 CONTROL "AGBPrint",IDC_VERBOSE_AGBPRINT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,180,90,12 - EDITTEXT IDC_LOG,114,6,258,192,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL - PUSHBUTTON "Save...",IDC_SAVE,114,204,48,12 - PUSHBUTTON "Clear",IDC_CLEAR,168,204,48,12 - DEFPUSHBUTTON "OK",ID_OK,324,204,48,12 - GROUPBOX "Verbose",IDC_STATIC,6,6,102,210 + EDITTEXT IDC_LOG,114,6,258,226,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL + PUSHBUTTON "Save...",IDC_SAVE,113,238,48,12 + PUSHBUTTON "Clear",IDC_CLEAR,171,238,48,12 + DEFPUSHBUTTON "OK",ID_OK,327,238,48,12 + GROUPBOX "Verbose",IDC_STATIC,6,7,102,243 CONTROL "Sound output",IDC_VERBOSE_SOUNDOUTPUT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,198,90,12 + CONTROL "SIO",IDC_VERBOSE_SIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,216,90,12 + CONTROL "Link",IDC_VERBOSE_LINK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,233,90,12 + CONTROL "SWI",IDC_VERBOSE_SWI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,19,90,12 END IDD_EXPORT_SPS DIALOGEX 0, 0, 248, 148 @@ -1238,6 +1245,14 @@ BEGIN BOTTOMMARGIN, 107 END + IDD_LINKTAB2, DIALOG + BEGIN + END + + IDD_SERVERWAIT, DIALOG + BEGIN + END + IDD_OPENDLG, DIALOG BEGIN RIGHTMARGIN, 165 @@ -1424,7 +1439,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 396 TOPMARGIN, 7 - BOTTOMMARGIN, 218 + BOTTOMMARGIN, 243 END IDD_GDB_PORT, DIALOG @@ -1448,7 +1463,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 375 TOPMARGIN, 7 - BOTTOMMARGIN, 213 + BOTTOMMARGIN, 250 END IDD_EXPORT_SPS, DIALOG @@ -1687,7 +1702,7 @@ BEGIN BEGIN MENUITEM "&Battery file...", ID_FILE_IMPORT_BATTERYFILE MENUITEM "Gameshark &code file...", ID_FILE_IMPORT_GAMESHARKCODEFILE - MENUITEM "&Gameshark/Pro Action Replay Snapshot...", ID_FILE_IMPORT_GAMESHARKSNAPSHOT + MENUITEM "&Gameshark/Pro Action Replay Snapshot...", ID_FILE_IMPORT_GAMESHARKSNAPSHOT END POPUP "Export" BEGIN @@ -2225,13 +2240,21 @@ BEGIN IDS_LOADED_CHEATS "Loaded cheats" IDS_ERROR_DISP_COLOR "Unsupported display setting for color depth: %d bits. \nWindows desktop must be in either 16-bit, 24-bit or 32-bit mode for this program to work in window mode." IDS_ADD_GSA_CODE "Add GameSharkAdvance code" - IDS_FILTER_GSVSPS "GS & PAC Snapshots (*.SPS;*.XPS)_*.SPS;*.XPS_GameShark SP Snapshots (*.GSV)_*.gsv__" IDS_FILTER_SPS "Gameshark Snapshot_*.SPS__" IDS_SELECT_SNAPSHOT_FILE "Select snapshot file" IDS_FILTER_SAV "Battery file_*.SAV_Flash save_*.DAT__" IDS_SELECT_BATTERY_FILE "Select battery file" END +STRINGTABLE +BEGIN + IDS_INVALID_INTERVAL_VALUE + "Invalid rewind interval value. Please enter a number between 0 and 600 seconds." + IDS_REGISTRY "VisualBoyAdvance no longer uses the registry to store its settings. Your previous settings have been exported into the file: %s" + IDS_MOVIE_PLAY "Playing a movie will load a save state which may erase your previous battery saves. Please be sure to have a saved state if you don't want to loose any previous data." + IDS_FILTER_GSVSPS "GS & PAC Snapshots (*.SPS;*.XPS)_*.SPS;*.XPS_GameShark SP Snapshots (*.GSV)_*.gsv__" +END + STRINGTABLE BEGIN IDS_UNSUPPORTED_CHEAT_LIST_TYPE "Unsupported cheat list type %d" @@ -2292,14 +2315,6 @@ BEGIN IDS_END_OF_MOVIE "end of movie" END -STRINGTABLE -BEGIN - IDS_INVALID_INTERVAL_VALUE - "Invalid rewind interval value. Please enter a number between 0 and 600 seconds." - IDS_REGISTRY "VisualBoyAdvance no longer uses the registry to store its settings. Your previous settings have been exported into the file: %s" - IDS_MOVIE_PLAY "Playing a movie will load a save state which may erase your previous battery saves. Please be sure to have a saved state if you don't want to loose any previous data." -END - STRINGTABLE BEGIN IDS_OAL_NODEVICE "There are no sound devices present on this system." diff --git a/src/win32/XAudio2.cpp b/src/win32/XAudio2.cpp index 73de71c5..967820e4 100644 --- a/src/win32/XAudio2.cpp +++ b/src/win32/XAudio2.cpp @@ -278,6 +278,8 @@ bool XAudio2_Output::init(long sampleRate) initialized = true; + + setThrottle(theApp.throttle); //AdamN: setting sound pitch to current throttle return true; } @@ -303,7 +305,7 @@ void XAudio2_Output::write(u16 * finalWave, int length) break; } else { // the maximum number of buffers is currently queued - if( synchronize && !speedup && !theApp.throttle ) { + if( synchronize && !speedup /*&& !theApp.throttle*/ ) { // wait for one buffer to finish playing WaitForSingleObject( notify.hBufferEndEvent, INFINITE ); } else { diff --git a/src/win32/resource.h b/src/win32/resource.h index ffd9a9ce..78487cb7 100644 --- a/src/win32/resource.h +++ b/src/win32/resource.h @@ -150,6 +150,7 @@ #define IDC_C_FLAG 1020 #define IDC_CONTINUE 1020 #define IDC_SAVE_DIR 1020 +#define IDC_STEPINTO 1020 #define IDC_V_FLAG 1021 #define IDC_CAPTURE_DIR 1021 #define IDC_CHEAT_LIST 1021 @@ -215,6 +216,7 @@ #define IDS_VALUE_CANNOT_BE_EMPTY 1042 #define IDS_ERROR_ON_STARTDOC 1043 #define IDC_R 1043 +#define IDC_ADDRESS2 1043 #define IDS_ERROR_ON_STARTPAGE 1044 #define IDC_G 1044 #define IDS_ERROR_PRINTING_ON_STRETCH 1045 @@ -459,6 +461,8 @@ #define IDC_ARM 1200 #define IDC_THUMB 1201 #define IDC_AUTO_UPDATE 1204 +#define IDC_BREAK_AT 1205 +#define IDC_AUTO_STEP 1206 #define IDC_N 1210 #define IDC_Z 1211 #define IDC_C 1212 @@ -491,6 +495,8 @@ #define IDC_VERBOSE_SOUNDOUTPUT 1235 #define IDC_NOTES 1236 #define IDC_CURRENT_ADDRESS_LABEL 1236 +#define IDC_VERBOSE_SIO 1236 +#define IDC_VERBOSE_LINK 1237 #define IDC_LOAD 1238 #define IDC_SIZE_CONTROL 1240 #define IDC_MODES 1240 @@ -841,6 +847,7 @@ #define IDC_LINK3P 40325 #define IDC_LINK4P 40326 #define IDC_CLINKUDP 40327 +#define IDC_LINK5P 40327 #define IDC_SPEEDON 40328 #define ID_OPTIONS_EMULATOR_REMOVEINTROSGBA 40331 #define ID_Menu 40332