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