| 1 | /*******************************************************************\r |
| 2 | *\r |
| 3 | * File: Engine.cpp\r |
| 4 | *\r |
| 5 | * Author: Peter van Sebille (peter@yipton.net)\r |
| 6 | *\r |
| 7 | * Modified/adapted for picodriveN by notaz, 2006\r |
| 8 | *\r |
| 9 | * (c) Copyright 2006, notaz\r |
| 10 | * (c) Copyright 2002, Peter van Sebille\r |
| 11 | * All Rights Reserved\r |
| 12 | *\r |
| 13 | *******************************************************************/\r |
| 14 | \r |
| 15 | \r |
| 16 | #include "Engine.h"\r |
| 17 | #include <w32std.h>\r |
| 18 | #include <eikenv.h>\r |
| 19 | #include <e32svr.h>\r |
| 20 | #include <e32math.h>\r |
| 21 | #include <e32uid.h>\r |
| 22 | \r |
| 23 | #include <string.h>\r |
| 24 | \r |
| 25 | #include "version.h"\r |
| 26 | #include <pico/pico_int.h>\r |
| 27 | #include "../common/emu.h"\r |
| 28 | #include "engine/debug.h"\r |
| 29 | #include "App.h"\r |
| 30 | \r |
| 31 | // this is where we start to break a bunch of symbian rules\r |
| 32 | extern TInt machineUid;\r |
| 33 | extern int gamestate, gamestate_next;\r |
| 34 | extern char *loadrom_fname;\r |
| 35 | extern int loadrom_result;\r |
| 36 | extern const char *actionNames[];\r |
| 37 | RSemaphore initSemaphore;\r |
| 38 | RSemaphore pauseSemaphore;\r |
| 39 | RSemaphore loadWaitSemaphore;\r |
| 40 | static CPicolAppView *appView = 0;\r |
| 41 | \r |
| 42 | \r |
| 43 | TInt CPicoGameSession::Do(const TPicoServRqst what, TAny *param)\r |
| 44 | {\r |
| 45 | switch (what)\r |
| 46 | {\r |
| 47 | case PicoMsgLoadState: \r |
| 48 | if(!rom_loaded) return -1; // no ROM\r |
| 49 | return emu_save_load_game(1, 0);\r |
| 50 | \r |
| 51 | case PicoMsgSaveState:\r |
| 52 | if(!rom_loaded) return -1;\r |
| 53 | return emu_save_load_game(0, 0);\r |
| 54 | \r |
| 55 | case PicoMsgLoadROM:\r |
| 56 | return loadROM((TPtrC16 *)param);\r |
| 57 | \r |
| 58 | case PicoMsgResume:\r |
| 59 | DEBUGPRINT(_L("resume"));\r |
| 60 | if(rom_loaded) {\r |
| 61 | return ChangeRunState(PGS_Running);\r |
| 62 | }\r |
| 63 | return 1;\r |
| 64 | \r |
| 65 | case PicoMsgReset: \r |
| 66 | if(rom_loaded) {\r |
| 67 | return ChangeRunState(PGS_Reset);\r |
| 68 | }\r |
| 69 | return 1;\r |
| 70 | \r |
| 71 | case PicoMsgKeys:\r |
| 72 | return ChangeRunState(PGS_KeyConfig);\r |
| 73 | \r |
| 74 | case PicoMsgPause:\r |
| 75 | return ChangeRunState(PGS_Paused);\r |
| 76 | \r |
| 77 | case PicoMsgQuit:\r |
| 78 | DEBUGPRINT(_L("got quit msg."));\r |
| 79 | return ChangeRunState(PGS_Quit);\r |
| 80 | \r |
| 81 | // config change\r |
| 82 | case PicoMsgConfigChange:\r |
| 83 | return changeConfig((TPicoConfig *)param);\r |
| 84 | \r |
| 85 | case PicoMsgSetAppView:\r |
| 86 | appView = (CPicolAppView *)param;\r |
| 87 | return 1;\r |
| 88 | \r |
| 89 | default:\r |
| 90 | return 1;\r |
| 91 | }\r |
| 92 | }\r |
| 93 | \r |
| 94 | TInt EmuThreadFunction(TAny* anArg);\r |
| 95 | \r |
| 96 | TInt CPicoGameSession::StartEmuThread()\r |
| 97 | {\r |
| 98 | TInt res=KErrNone;\r |
| 99 | iEmuRunning = EFalse;\r |
| 100 | \r |
| 101 | if (initSemaphore.Handle() > 0)\r |
| 102 | initSemaphore.Close();\r |
| 103 | initSemaphore.CreateLocal(0);\r |
| 104 | if (pauseSemaphore.Handle() <= 0)\r |
| 105 | pauseSemaphore.CreateLocal(0);\r |
| 106 | if (loadWaitSemaphore.Handle() <= 0)\r |
| 107 | loadWaitSemaphore.CreateLocal(0);\r |
| 108 | \r |
| 109 | RThread thread;\r |
| 110 | if(iThreadWatcher && (res = thread.Open(iThreadWatcher->iTid)) == KErrNone) {\r |
| 111 | // should be a dead thread in some strange state.\r |
| 112 | DEBUGPRINT(_L("found thread with the same id (id=%i, RequestCount=%i), killing.."),\r |
| 113 | (TInt32)thread.Id(), thread.RequestCount());\r |
| 114 | // what can we do in this situation? Nothing seems to help, it just stays in this state.\r |
| 115 | delete iThreadWatcher;\r |
| 116 | iThreadWatcher = 0;\r |
| 117 | thread.Kill(1);\r |
| 118 | thread.Terminate(1);\r |
| 119 | thread.Close();\r |
| 120 | }\r |
| 121 | \r |
| 122 | res=thread.Create(_L("PicoEmuThread"), // create new server thread\r |
| 123 | EmuThreadFunction, // thread's main function\r |
| 124 | KDefaultStackSize,\r |
| 125 | KMinHeapSize,\r |
| 126 | KPicoMaxHeapSize,\r |
| 127 | 0 // &semaphore // passed as TAny* argument to thread function\r |
| 128 | );\r |
| 129 | \r |
| 130 | if(res == KErrNone) { // thread created ok - now start it going\r |
| 131 | thread.SetPriority(EPriorityMore);\r |
| 132 | iEmuRunning = ETrue;\r |
| 133 | if (iThreadWatcher) delete iThreadWatcher;\r |
| 134 | iThreadWatcher = CThreadWatcher::NewL(thread.Id());\r |
| 135 | thread.Resume(); // start it going\r |
| 136 | DEBUGPRINT(_L("initSemaphore.Wait()"));\r |
| 137 | res = initSemaphore.Wait(3*1000*1000); // wait until it's initialized\r |
| 138 | DEBUGPRINT(_L("initSemaphore resume, ExitReason() == %i"), thread.ExitReason());\r |
| 139 | res |= thread.ExitReason();\r |
| 140 | thread.Close(); // we're no longer interested in the other thread\r |
| 141 | if(res != KErrNone) iEmuRunning = EFalse;\r |
| 142 | return res;\r |
| 143 | }\r |
| 144 | \r |
| 145 | return res;\r |
| 146 | }\r |
| 147 | \r |
| 148 | TInt CPicoGameSession::ChangeRunState(TPicoGameState newstate, TPicoGameState newstate_next)\r |
| 149 | {\r |
| 150 | if (!iEmuRunning) {\r |
| 151 | gamestate = PGS_Paused;\r |
| 152 | TInt res = StartEmuThread();\r |
| 153 | if(res != KErrNone) DEBUGPRINT(_L("StartEmuThread() returned %i"), res);\r |
| 154 | if (!iEmuRunning) return PicoErrEmuThread;\r |
| 155 | }\r |
| 156 | \r |
| 157 | int oldstate = gamestate;\r |
| 158 | gamestate = newstate;\r |
| 159 | gamestate_next = newstate_next ? newstate_next : PGS_Paused;\r |
| 160 | if (oldstate == PGS_Paused) pauseSemaphore.Signal();\r |
| 161 | return 0;\r |
| 162 | }\r |
| 163 | \r |
| 164 | \r |
| 165 | TInt CPicoGameSession::loadROM(TPtrC16 *pptr)\r |
| 166 | {\r |
| 167 | TInt ret;\r |
| 168 | char buff[150];\r |
| 169 | \r |
| 170 | // make sure emu thread is ok\r |
| 171 | ret = ChangeRunState(PGS_Paused);\r |
| 172 | if(ret) return ret;\r |
| 173 | \r |
| 174 | // read the contents of the client pointer into a TPtr.\r |
| 175 | static TBuf8<KMaxFileName> writeBuf;\r |
| 176 | writeBuf.Copy(*pptr);\r |
| 177 | \r |
| 178 | // push the emu thead to a load state. This is done so that it owns all file handles.\r |
| 179 | // If successful, in will enter PGS_Running state by itself.\r |
| 180 | loadrom_fname = (char *)writeBuf.PtrZ();\r |
| 181 | loadrom_result = 0;\r |
| 182 | loadWaitSemaphore.Wait(1); // make sure sem is not set\r |
| 183 | ret = ChangeRunState(PGS_ReloadRom);\r |
| 184 | if(ret) return ret;\r |
| 185 | \r |
| 186 | loadWaitSemaphore.Wait(60*1000*1000);\r |
| 187 | \r |
| 188 | if (loadrom_result == 0)\r |
| 189 | return PicoErrRomOpenFailed;\r |
| 190 | \r |
| 191 | emu_get_game_name(buff);\r |
| 192 | TPtrC8 buff8((TUint8*) buff);\r |
| 193 | iRomInternalName.Copy(buff8);\r |
| 194 | \r |
| 195 | DEBUGPRINT(_L("done waiting for ROM load"));\r |
| 196 | \r |
| 197 | // debug\r |
| 198 | #ifdef __DEBUG_PRINT\r |
| 199 | TInt mem, cells = User::CountAllocCells();\r |
| 200 | User::AllocSize(mem);\r |
| 201 | DEBUGPRINT(_L("comm: cels=%d, size=%d KB"), cells, mem/1024);\r |
| 202 | #endif\r |
| 203 | \r |
| 204 | return 0;\r |
| 205 | }\r |
| 206 | \r |
| 207 | \r |
| 208 | TInt CPicoGameSession::changeConfig(TPicoConfig *aConfig)\r |
| 209 | {\r |
| 210 | // 6 button pad, enable XYZM config if needed\r |
| 211 | if (PicoOpt & POPT_6BTN_PAD)\r |
| 212 | {\r |
| 213 | actionNames[8] = "Z";\r |
| 214 | actionNames[9] = "Y";\r |
| 215 | actionNames[10] = "X";\r |
| 216 | actionNames[11] = "MODE";\r |
| 217 | } else {\r |
| 218 | actionNames[8] = actionNames[9] = actionNames[10] = actionNames[11] = 0;\r |
| 219 | }\r |
| 220 | \r |
| 221 | // if we are in center 90||270 modes, we can bind renderer switcher\r |
| 222 | if (currentConfig.scaling == TPicoConfig::PMFit &&\r |
| 223 | (currentConfig.rotation == TPicoConfig::PRot0 || currentConfig.rotation == TPicoConfig::PRot180))\r |
| 224 | actionNames[25] = 0;\r |
| 225 | else actionNames[25] = "RENDERER";\r |
| 226 | \r |
| 227 | return 0;\r |
| 228 | }\r |
| 229 | \r |
| 230 | \r |
| 231 | #ifdef __DEBUG_PRINT_FILE\r |
| 232 | extern RMutex logMutex;\r |
| 233 | #endif\r |
| 234 | \r |
| 235 | void CPicoGameSession::freeResources()\r |
| 236 | {\r |
| 237 | RThread thread;\r |
| 238 | TInt i;\r |
| 239 | \r |
| 240 | DEBUGPRINT(_L("CPicoGameSession::freeResources()"));\r |
| 241 | \r |
| 242 | if(iThreadWatcher && thread.Open(iThreadWatcher->iTid) == KErrNone)\r |
| 243 | {\r |
| 244 | // try to stop our emu thread\r |
| 245 | gamestate = PGS_Quit;\r |
| 246 | if(pauseSemaphore.Handle() > 0)\r |
| 247 | pauseSemaphore.Signal();\r |
| 248 | \r |
| 249 | if(thread.Handle() > 0)\r |
| 250 | {\r |
| 251 | // tried reopening thread handle here over time intervals to detect if thread is alive,\r |
| 252 | // but would run into handle panics.\r |
| 253 | \r |
| 254 | for(i = 0; i < 8; i++) {\r |
| 255 | User::After(100 * 1000);\r |
| 256 | if(thread.ExitReason() != 0) break;\r |
| 257 | }\r |
| 258 | \r |
| 259 | if(thread.ExitReason() == 0) {\r |
| 260 | // too late, time to die\r |
| 261 | DEBUGPRINT(_L("thread %i not responding, killing.."), (TInt32) thread.Id());\r |
| 262 | thread.Terminate(1);\r |
| 263 | }\r |
| 264 | thread.Close();\r |
| 265 | }\r |
| 266 | \r |
| 267 | }\r |
| 268 | \r |
| 269 | if (iThreadWatcher != NULL)\r |
| 270 | {\r |
| 271 | DEBUGPRINT(_L("delete iThreadWatcher"));\r |
| 272 | delete iThreadWatcher;\r |
| 273 | DEBUGPRINT(_L("after delete iThreadWatcher"));\r |
| 274 | iThreadWatcher = NULL;\r |
| 275 | }\r |
| 276 | \r |
| 277 | if (initSemaphore.Handle() > 0)\r |
| 278 | initSemaphore.Close();\r |
| 279 | if (pauseSemaphore.Handle() > 0)\r |
| 280 | pauseSemaphore.Close();\r |
| 281 | if (loadWaitSemaphore.Handle() > 0)\r |
| 282 | loadWaitSemaphore.Close();\r |
| 283 | DEBUGPRINT(_L("freeResources() returning"));\r |
| 284 | #ifdef __DEBUG_PRINT_FILE\r |
| 285 | if (logMutex.Handle() > 0)\r |
| 286 | logMutex.Close();\r |
| 287 | #endif\r |
| 288 | }\r |
| 289 | \r |
| 290 | TBool CPicoGameSession::iEmuRunning = EFalse;\r |
| 291 | CThreadWatcher *CPicoGameSession::iThreadWatcher = 0;\r |
| 292 | TBuf<150> CPicoGameSession::iRomInternalName;\r |
| 293 | \r |
| 294 | \r |
| 295 | // CThreadWatcher\r |
| 296 | CThreadWatcher::~CThreadWatcher()\r |
| 297 | {\r |
| 298 | Cancel();\r |
| 299 | DEBUGPRINT(_L("after CThreadWatcher::Cancel();"));\r |
| 300 | }\r |
| 301 | \r |
| 302 | CThreadWatcher::CThreadWatcher(const TThreadId& aTid)\r |
| 303 | : CActive(CActive::EPriorityStandard), iTid(aTid)\r |
| 304 | {\r |
| 305 | }\r |
| 306 | \r |
| 307 | \r |
| 308 | CThreadWatcher* CThreadWatcher::NewL(const TThreadId& aTid)\r |
| 309 | {\r |
| 310 | CThreadWatcher* self = new(ELeave) CThreadWatcher(aTid);\r |
| 311 | CleanupStack::PushL(self);\r |
| 312 | self->ConstructL();\r |
| 313 | CleanupStack::Pop(); // self\r |
| 314 | return self;\r |
| 315 | }\r |
| 316 | \r |
| 317 | void CThreadWatcher::ConstructL()\r |
| 318 | {\r |
| 319 | CActiveScheduler::Add(this);\r |
| 320 | RThread thread;\r |
| 321 | if(thread.Open(iTid) == KErrNone) {\r |
| 322 | thread.Logon(iStatus);\r |
| 323 | thread.Close();\r |
| 324 | SetActive();\r |
| 325 | }\r |
| 326 | }\r |
| 327 | \r |
| 328 | void CThreadWatcher::RunL()\r |
| 329 | {\r |
| 330 | DEBUGPRINT(_L("CThreadWatcher::RunL()"));\r |
| 331 | CPicoGameSession::iEmuRunning = EFalse;\r |
| 332 | if(appView) appView->UpdateCommandList();\r |
| 333 | //initSemaphore.Signal(); // no point to do that here, AS can't get here if it is waiting\r |
| 334 | }\r |
| 335 | \r |
| 336 | void CThreadWatcher::DoCancel()\r |
| 337 | {\r |
| 338 | RThread thread;\r |
| 339 | DEBUGPRINT(_L("CThreadWatcher::DoCancel()"));\r |
| 340 | if(thread.Open(iTid) == KErrNone) {\r |
| 341 | DEBUGPRINT(_L("thread.LogonCancel(iStatus);"));\r |
| 342 | thread.LogonCancel(iStatus);\r |
| 343 | thread.Close();\r |
| 344 | }\r |
| 345 | }\r |
| 346 | \r |
| 347 | extern "C" void cache_flush_d_inval_i(const void *start_addr, const void *end_addr)\r |
| 348 | {\r |
| 349 | // TODO\r |
| 350 | User::IMB_Range((TAny *)start_addr, (TAny *)end_addr);\r |
| 351 | }\r |
| 352 | \r |