1 /*******************************************************************
\r
5 * Author: Peter van Sebille (peter@yipton.net)
\r
7 * Modified/adapted for picodriveN by notaz, 2006
\r
9 * (c) Copyright 2006, notaz
\r
10 * (c) Copyright 2002, Peter van Sebille
\r
11 * All Rights Reserved
\r
13 *******************************************************************/
\r
20 #include <e32math.h>
\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
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
43 TInt CPicoGameSession::Do(const TPicoServRqst what, TAny *param)
\r
47 case PicoMsgLoadState:
\r
48 if(!rom_loaded) return -1; // no ROM
\r
49 return emu_SaveLoadGame(1, 0);
\r
51 case PicoMsgSaveState:
\r
52 if(!rom_loaded) return -1;
\r
53 return emu_SaveLoadGame(0, 0);
\r
55 case PicoMsgLoadROM:
\r
56 return loadROM((TPtrC16 *)param);
\r
59 DEBUGPRINT(_L("resume"));
\r
61 return ChangeRunState(PGS_Running);
\r
67 return ChangeRunState(PGS_Reset);
\r
72 return ChangeRunState(PGS_KeyConfig);
\r
75 return ChangeRunState(PGS_Paused);
\r
78 DEBUGPRINT(_L("got quit msg."));
\r
79 return ChangeRunState(PGS_Quit);
\r
82 case PicoMsgConfigChange:
\r
83 return changeConfig((TPicoConfig *)param);
\r
85 case PicoMsgSetAppView:
\r
86 appView = (CPicolAppView *)param;
\r
94 TInt EmuThreadFunction(TAny* anArg);
\r
96 TInt CPicoGameSession::StartEmuThread()
\r
99 iEmuRunning = EFalse;
\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
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
118 thread.Terminate(1);
\r
122 res=thread.Create(_L("PicoEmuThread"), // create new server thread
\r
123 EmuThreadFunction, // thread's main function
\r
127 0 // &semaphore // passed as TAny* argument to thread function
\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
148 TInt CPicoGameSession::ChangeRunState(TPicoGameState newstate, TPicoGameState newstate_next)
\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
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
165 TInt CPicoGameSession::loadROM(TPtrC16 *pptr)
\r
170 // make sure emu thread is ok
\r
171 ret = ChangeRunState(PGS_Paused);
\r
172 if(ret) return ret;
\r
174 // read the contents of the client pointer into a TPtr.
\r
175 static TBuf8<KMaxFileName> writeBuf;
\r
176 writeBuf.Copy(*pptr);
\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
186 loadWaitSemaphore.Wait(60*1000*1000);
\r
188 if (loadrom_result == 0)
\r
189 return PicoErrRomOpenFailed;
\r
191 emu_getGameName(buff);
\r
192 TPtrC8 buff8((TUint8*) buff);
\r
193 iRomInternalName.Copy(buff8);
\r
195 DEBUGPRINT(_L("done waiting for ROM load"));
\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
208 TInt CPicoGameSession::changeConfig(TPicoConfig *aConfig)
\r
210 // 6 button pad, enable XYZM config if needed
\r
211 if (PicoOpt & POPT_6BTN_PAD)
\r
213 actionNames[8] = "Z";
\r
214 actionNames[9] = "Y";
\r
215 actionNames[10] = "X";
\r
216 actionNames[11] = "MODE";
\r
218 actionNames[8] = actionNames[9] = actionNames[10] = actionNames[11] = 0;
\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
231 #ifdef __DEBUG_PRINT_FILE
\r
232 extern RMutex logMutex;
\r
235 void CPicoGameSession::freeResources()
\r
240 DEBUGPRINT(_L("CPicoGameSession::freeResources()"));
\r
242 if(iThreadWatcher && thread.Open(iThreadWatcher->iTid) == KErrNone)
\r
244 // try to stop our emu thread
\r
245 gamestate = PGS_Quit;
\r
246 if(pauseSemaphore.Handle() > 0)
\r
247 pauseSemaphore.Signal();
\r
249 if(thread.Handle() > 0)
\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
254 for(i = 0; i < 8; i++) {
\r
255 User::After(100 * 1000);
\r
256 if(thread.ExitReason() != 0) break;
\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
269 if (iThreadWatcher != NULL)
\r
271 DEBUGPRINT(_L("delete iThreadWatcher"));
\r
272 delete iThreadWatcher;
\r
273 DEBUGPRINT(_L("after delete iThreadWatcher"));
\r
274 iThreadWatcher = NULL;
\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
290 TBool CPicoGameSession::iEmuRunning = EFalse;
\r
291 CThreadWatcher *CPicoGameSession::iThreadWatcher = 0;
\r
292 TBuf<150> CPicoGameSession::iRomInternalName;
\r
296 CThreadWatcher::~CThreadWatcher()
\r
299 DEBUGPRINT(_L("after CThreadWatcher::Cancel();"));
\r
302 CThreadWatcher::CThreadWatcher(const TThreadId& aTid)
\r
303 : CActive(CActive::EPriorityStandard), iTid(aTid)
\r
308 CThreadWatcher* CThreadWatcher::NewL(const TThreadId& aTid)
\r
310 CThreadWatcher* self = new(ELeave) CThreadWatcher(aTid);
\r
311 CleanupStack::PushL(self);
\r
312 self->ConstructL();
\r
313 CleanupStack::Pop(); // self
\r
317 void CThreadWatcher::ConstructL()
\r
319 CActiveScheduler::Add(this);
\r
321 if(thread.Open(iTid) == KErrNone) {
\r
322 thread.Logon(iStatus);
\r
328 void CThreadWatcher::RunL()
\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
336 void CThreadWatcher::DoCancel()
\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
347 extern "C" void cache_flush_d_inval_i(const void *start_addr, const void *end_addr)
\r
350 User::IMB_Range((TAny *)start_addr, (TAny *)end_addr);
\r