From a70465323471f78cf8b7de12d0bf9741066c18b4 Mon Sep 17 00:00:00 2001 From: skidau Date: Mon, 23 Mar 2015 12:17:26 +0000 Subject: [PATCH] Ported AdamN's Game Link (Game Boy link) code from 1030e. Seems to work better with a link timeout of 1. --- src/gb/GB.cpp | 198 +++++++++++++++++++++++------------ src/gba/GBALink.cpp | 114 +++++++++++++++++++- src/gba/GBALink.h | 11 +- src/win32/LinkOptions.cpp | 1 + src/win32/MainWndOptions.cpp | 6 +- src/win32/VBA.cpp | 6 +- 6 files changed, 266 insertions(+), 70 deletions(-) diff --git a/src/gb/GB.cpp b/src/gb/GB.cpp index c7176869..cc351f96 100644 --- a/src/gb/GB.cpp +++ b/src/gb/GB.cpp @@ -14,6 +14,7 @@ #include "gbSGB.h" #include "gbSound.h" #include "../Util.h" +#include "../gba/GBALink.h" #ifdef __GNUC__ #define _stricmp strcasecmp @@ -796,25 +797,46 @@ void gbWriteMemory(register u16 address, register u8 value) return; } - // serial control - case 0x02: { - gbSerialOn = (value & 0x80); - gbMemory[0xff02] = value; - if(gbSerialOn) { - gbSerialTicks = GBSERIAL_CLOCK_TICKS; -#ifdef OLD_GB_LINK - if(linkConnected) { - if(value & 1) { - linkSendByte(0x100|gbMemory[0xFF01]); - Sleep(5); - } - } -#endif - } + // serial control + case 0x02: { + gbSerialOn = (value & 0x80); +#ifndef NO_LINK + 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; + } + EmuReseted = false; + gbMemory[0xff02] = value; + if (gbSerialOn) { + gbSerialTicks = GBSERIAL_CLOCK_TICKS; - gbSerialBits = 0; - return; - } + 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) ) { //internal clock + if (gbSerialFunction) { + gbSIO_SC = value; + gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); //gbSerialFunction/gbStartLink/gbPrinter + } + else gbMemory[0xff01] = 0xff; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + } +#ifdef OLD_GB_LINK + if (linkConnected) { + if (value & 1) { + linkSendByte(0x100 | gbMemory[0xFF01]); + Sleep(5); + } + } +#endif + } + + gbSerialBits = 0; + return; +#endif + } case 0x04: { // DIV register resets on any write @@ -2176,6 +2198,11 @@ void gbGetHardwareType() void gbReset() { +#ifndef NO_LINK + EmuReseted = true; + gbLinkReset(); +#endif + gbGetHardwareType(); oldRegister_WY = 146; @@ -5204,59 +5231,100 @@ void gbEmulate(int ticksToStop) gbMemory[0xff41] = register_STAT; - // serial emulation - if(gbSerialOn) { +#ifndef NO_LINK + // serial emulation + gbSerialOn = (gbMemory[0xff02] & 0x80); + static int SIOctr = 0; + SIOctr++; + if (SIOctr % 5) + if (gbSerialOn) { //Transfer Started #ifdef OLD_GB_LINK - if(linkConnected) { - gbSerialTicks -= clockTicks; + if (linkConnected) { + gbSerialTicks -= clockTicks; - while(gbSerialTicks <= 0) { - // increment number of shifted bits - gbSerialBits++; - linkProc(); - if(gbSerialOn && (gbMemory[0xff02] & 1)) { - if(gbSerialBits == 8) { - gbSerialBits = 0; - gbMemory[0xff01] = 0xff; - gbMemory[0xff02] &= 0x7f; - gbSerialOn = 0; - gbMemory[0xff0f] = register_IF |= 8; - gbSerialTicks = 0; - } - } - gbSerialTicks += GBSERIAL_CLOCK_TICKS; - } - } else { + while (gbSerialTicks <= 0) { + // increment number of shifted bits + gbSerialBits++; + linkProc(); + if (gbSerialOn && (gbMemory[0xff02] & 1)) { + if (gbSerialBits == 8) { + gbSerialBits = 0; + gbMemory[0xff01] = 0xff; + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + gbSerialTicks = 0; + } + } + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } + else { #endif - if(gbMemory[0xff02] & 1) { - gbSerialTicks -= clockTicks; + if (gbMemory[0xff02] & 1) { //internal clocks (master) + 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 - if(gbSerialFunction) // external device - gbMemory[0xff01] = gbSerialFunction(gbMemory[0xff01]); - else - gbMemory[0xff01] = 0xff; - gbSerialTicks = 0; - gbMemory[0xff02] &= 0x7f; - gbSerialOn = 0; - gbMemory[0xff0f] = register_IF |= 8; - gbSerialBits = 0; - } else - gbSerialTicks += GBSERIAL_CLOCK_TICKS; - } - } + // 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 + gbSerialTicks = 0; + 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 (gbSerialFunction) { // external device + gbSIO_SC = gbMemory[0xff02]; + 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 + { + if (gbMemory) + gbSerialOn = (gbMemory[0xff02] & 0x80); + dat = gbLinkUpdate(gbMemory[0xff01], gbSerialOn); + } + gbMemory[0xff01] = (dat >> 8); + } //else + gbSerialTicks = 0; + if ((dat & 1) && (gbMemory[0xff02] & 0x80)) //(dat & 1)==1 when reply data received + { + gbMemory[0xff02] &= 0x7f; + gbSerialOn = 0; + gbMemory[0xff0f] = register_IF |= 8; + } + gbSerialBits = 0; + } + else + gbSerialTicks += GBSERIAL_CLOCK_TICKS; + } + } #ifdef OLD_GB_LINK - } + } +#endif + } #endif - } - soundTicks -= clockTicks; if ( !gbSpeed ) diff --git a/src/gba/GBALink.cpp b/src/gba/GBALink.cpp index 4a3722fa..156a5133 100644 --- a/src/gba/GBALink.cpp +++ b/src/gba/GBALink.cpp @@ -244,7 +244,8 @@ static const LinkDriver linkDrivers[] = { LINK_CABLE_IPC, InitIPC, NULL, StartCableIPC, UpdateCableIPC, CloseIPC }, { LINK_CABLE_SOCKET, InitSocket, ConnectUpdateSocket, StartCableSocket, UpdateSocket, CloseSocket }, { LINK_RFU_IPC, InitIPC, NULL, StartRFU, UpdateRFUIPC, CloseIPC }, - { LINK_GAMECUBE_DOLPHIN, JoyBusConnect, NULL, NULL, JoyBusUpdate, JoyBusShutdown } + { LINK_GAMECUBE_DOLPHIN, JoyBusConnect, NULL, NULL, JoyBusUpdate, JoyBusShutdown }, + { LINK_GAMEBOY, InitIPC, NULL, NULL, NULL, CloseIPC } }; @@ -435,6 +436,11 @@ static const int trtimeend[3][4] = { static int GetSIOMode(u16, u16); +u8 gbSIO_SC = 0; +bool LinkIsWaiting = false; +bool LinkFirstTime = true; +bool EmuReseted = true; + // The GBA wireless RFU (see adapter3.txt) static void StartRFU(u16 value) { @@ -1906,7 +1912,7 @@ bool LinkRFUUpdate() if (((rfu_cmd == 0x11 || rfu_cmd == 0x1a || rfu_cmd == 0x26) && (GetTickCount() - rfu_lasttime) < 16) || ((rfu_cmd == 0xa5 || rfu_cmd == 0xb5) && (GetTickCount() - rfu_lasttime) < 16) || - ((rfu_cmd == 0xa7 || rfu_cmd == 0xb7) && (GetTickCount() - rfu_lasttime) < linktimeout)) + ((rfu_cmd == 0xa7 || rfu_cmd == 0xb7) && (GetTickCount() - rfu_lasttime) < (DWORD)linktimeout)) { //c_s.Lock(); ok = (linkmem->rfu_listfront[vbaid] != linkmem->rfu_listback[vbaid]); @@ -2691,6 +2697,110 @@ void lclient::Send() { lanlink.tcpsocket.Send(outbuffer, 4); return; } + + +void gbLinkReset() +{ + LinkIsWaiting = false; + LinkFirstTime = true; + linkmem->linkcmd[linkid] = 0; + linkmem->linkdata[linkid] = 0xff; + return; +} + +u8 gbStartLink(u8 b) //used on internal clock +{ + 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; + //int gbSerialOn = (gbMemory[0xff02] & 0x80); //not needed? + gba_link_enabled = true; //(gbMemory[0xff02]!=0); //not needed? + rfu_enabled = false; + + if (!gba_link_enabled) return 0xff; + + //Single Computer + if (!lanlink.active) + { + u32 tm = GetTickCount(); + do { + WaitForSingleObject(linksync[linkid], 1); + ResetEvent(linksync[linkid]); + } while (linkmem->linkcmd[linkid] && (GetTickCount() - tm)<(u32)linktimeout); + linkmem->linkdata[linkid] = b; + linkmem->linkcmd[linkid] = 1; + SetEvent(linksync[linkid]); + + LinkIsWaiting = false; + tm = GetTickCount(); + do { + WaitForSingleObject(linksync[1 - linkid], 1); + ResetEvent(linksync[1 - linkid]); + } while (!linkmem->linkcmd[1 - linkid] && (GetTickCount() - tm)<(u32)linktimeout); + if (linkmem->linkcmd[1 - linkid]) { + dat = (u8)linkmem->linkdata[1 - linkid]; + linkmem->linkcmd[1 - linkid] = 0; + } //else LinkIsWaiting = true; + SetEvent(linksync[1 - linkid]); + + LinkFirstTime = true; + if (dat != 0xff/*||b==0x00||dat==0x00*/) + LinkFirstTime = false; + + return dat; + } + return dat; +} + +u16 gbLinkUpdate(u8 b, int gbSerialOn) //used on external clock +{ + u8 dat = b; //0xff; //slave (w/ external clocks) won't be getting 0xff if master turned off + BOOL recvd = false; + int idx = 0; + + gba_link_enabled = true; //(gbMemory[0xff02]!=0); + rfu_enabled = false; + + if (gbSerialOn) { + if (gba_link_enabled) + //Single Computer + if (!lanlink.active) + { + u32 tm;// = GetTickCount(); + //do { + WaitForSingleObject(linksync[1 - linkid], linktimeout); + ResetEvent(linksync[1 - linkid]); + //} while (!linkmem->linkcmd[1-linkid] && (GetTickCount()-tm)<(u32)linktimeout); + if (linkmem->linkcmd[1 - linkid]) { + dat = (u8)linkmem->linkdata[1 - linkid]; + linkmem->linkcmd[1 - linkid] = 0; + recvd = true; + LinkIsWaiting = false; + } + else LinkIsWaiting = true; + SetEvent(linksync[1 - linkid]); + + if (!LinkIsWaiting) { + tm = GetTickCount(); + do { + WaitForSingleObject(linksync[linkid], 1); + ResetEvent(linksync[linkid]); + } while (linkmem->linkcmd[1 - linkid] && (GetTickCount() - tm)<(u32)linktimeout); + if (!linkmem->linkcmd[linkid]) { + linkmem->linkdata[linkid] = b; + linkmem->linkcmd[linkid] = 1; + } + SetEvent(linksync[linkid]); + } + + } + + if (dat == 0xff/*||dat==0x00||b==0x00*/) //dat==0xff||dat==0x00 + LinkFirstTime = true; + } + return ((dat << 8) | (recvd & (u8)0xff)); +} + #else bool gba_joybus_active = false; #endif \ No newline at end of file diff --git a/src/gba/GBALink.h b/src/gba/GBALink.h index 96dfc5ae..748e5b95 100644 --- a/src/gba/GBALink.h +++ b/src/gba/GBALink.h @@ -10,7 +10,8 @@ enum LinkMode LINK_CABLE_IPC, LINK_CABLE_SOCKET, LINK_RFU_IPC, - LINK_GAMECUBE_DOLPHIN + LINK_GAMECUBE_DOLPHIN, + LINK_GAMEBOY }; /** @@ -193,4 +194,12 @@ typedef struct { u32 data[255]; } rfu_datarec; +extern u8 gbSIO_SC; +extern bool LinkIsWaiting; +extern bool LinkFirstTime; +extern bool EmuReseted; +extern void gbLinkReset(); +extern u8 gbStartLink(u8 b); +extern u16 gbLinkUpdate(u8 b, int gbSerialOn); + #endif /* GBA_GBALINK_H */ diff --git a/src/win32/LinkOptions.cpp b/src/win32/LinkOptions.cpp index 595e60be..2ff4112a 100644 --- a/src/win32/LinkOptions.cpp +++ b/src/win32/LinkOptions.cpp @@ -73,6 +73,7 @@ BOOL LinkOptions::OnInitDialog(){ AddMode("Cable - Network", LINK_CABLE_SOCKET); AddMode("GameCube - Dolphin", LINK_GAMECUBE_DOLPHIN); AddMode("Wireless adapter - Single Computer", LINK_RFU_IPC); + AddMode("Game Link (Game Boy) - Single Computer", LINK_GAMEBOY); sprintf(timeout, "%d", theApp.linkTimeout); diff --git a/src/win32/MainWndOptions.cpp b/src/win32/MainWndOptions.cpp index 4bb7eb8f..14070623 100644 --- a/src/win32/MainWndOptions.cpp +++ b/src/win32/MainWndOptions.cpp @@ -1025,7 +1025,11 @@ void MainWnd::OnOptionsGameboyPrinter() if(theApp.winGbPrinterEnabled) gbSerialFunction = gbPrinterSend; else - gbSerialFunction = NULL; +#ifndef NO_LINK + gbSerialFunction = gbStartLink; +#else + gbSerialFunction = NULL; +#endif } void MainWnd::OnUpdateOptionsGameboyPrinter(CCmdUI* pCmdUI) diff --git a/src/win32/VBA.cpp b/src/win32/VBA.cpp index 0a64016e..513df39c 100644 --- a/src/win32/VBA.cpp +++ b/src/win32/VBA.cpp @@ -1517,7 +1517,11 @@ void VBA::loadSettings() if(winGbPrinterEnabled) gbSerialFunction = gbPrinterSend; else - gbSerialFunction = NULL; +#ifndef NO_LINK + gbSerialFunction = gbStartLink; +#else + gbSerialFunction = NULL; +#endif pauseWhenInactive = regQueryDwordValue("pauseWhenInactive", 1) ? true : false;