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/PicoInt.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 int pico_was_reset = 0;
\r
41 static CPicolAppView *appView = 0;
\r
44 TInt CPicoGameSession::Do(const TPicoServRqst what, TAny *param)
\r
48 case PicoMsgLoadState:
\r
49 if(!rom_loaded) return -1; // no ROM
\r
50 return emu_SaveLoadGame(1, 0);
\r
52 case PicoMsgSaveState:
\r
53 if(!rom_loaded) return -1;
\r
54 return emu_SaveLoadGame(0, 0);
\r
56 case PicoMsgLoadROM:
\r
57 return loadROM((TPtrC16 *)param);
\r
60 DEBUGPRINT(_L("resume"));
\r
62 return ChangeRunState(PGS_Running);
\r
70 return ChangeRunState(PGS_Running);
\r
75 return ChangeRunState(PGS_KeyConfig);
\r
78 return ChangeRunState(PGS_Paused);
\r
81 DEBUGPRINT(_L("got quit msg."));
\r
82 return ChangeRunState(PGS_Quit);
\r
85 case PicoMsgConfigChange:
\r
86 return changeConfig((TPicoConfig *)param);
\r
88 case PicoMsgSetAppView:
\r
89 appView = (CPicolAppView *)param;
\r
97 TInt EmuThreadFunction(TAny* anArg);
\r
99 TInt CPicoGameSession::StartEmuThread()
\r
102 iEmuRunning = EFalse;
\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
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
121 thread.Terminate(1);
\r
125 res=thread.Create(_L("PicoEmuThread"), // create new server thread
\r
126 EmuThreadFunction, // thread's main function
\r
130 0 // &semaphore // passed as TAny* argument to thread function
\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
151 TInt CPicoGameSession::ChangeRunState(TPicoGameState newstate, TPicoGameState newstate_next)
\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
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
168 TInt CPicoGameSession::loadROM(TPtrC16 *pptr)
\r
173 // make sure emu thread is ok
\r
174 ret = ChangeRunState(PGS_Paused);
\r
175 if(ret) return ret;
\r
177 // read the contents of the client pointer into a TPtr.
\r
178 static TBuf8<KMaxFileName> writeBuf;
\r
179 writeBuf.Copy(*pptr);
\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
188 loadWaitSemaphore.Wait(20*1000*1000);
\r
190 if (loadrom_result == 0)
\r
191 return PicoErrNotRom;
\r
193 emu_getGameName(buff);
\r
194 TPtrC8 buff8((TUint8*) buff);
\r
195 iRomInternalName.Copy(buff8);
\r
197 DEBUGPRINT(_L("done waiting for ROM load"));
\r
200 #ifdef __DEBUG_PRINT
\r
201 TInt cells = User::CountAllocCells();
\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
212 TInt CPicoGameSession::changeConfig(TPicoConfig *aConfig)
\r
214 // 6 button pad, enable XYZM config if needed
\r
215 if (PicoOpt & POPT_6BTN_PAD)
\r
217 actionNames[8] = "Z";
\r
218 actionNames[9] = "Y";
\r
219 actionNames[10] = "X";
\r
220 actionNames[11] = "MODE";
\r
222 actionNames[8] = actionNames[9] = actionNames[10] = actionNames[11] = 0;
\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
235 #ifdef __DEBUG_PRINT_FILE
\r
236 extern RMutex logMutex;
\r
239 void CPicoGameSession::freeResources()
\r
244 DEBUGPRINT(_L("CPicoGameSession::freeResources()"));
\r
246 if(iThreadWatcher && thread.Open(iThreadWatcher->iTid) == KErrNone)
\r
248 // try to stop our emu thread
\r
249 gamestate = PGS_Quit;
\r
250 if(pauseSemaphore.Handle() > 0)
\r
251 pauseSemaphore.Signal();
\r
253 if(thread.Handle() > 0)
\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
258 for(i = 0; i < 8; i++) {
\r
259 User::After(100 * 1000);
\r
260 if(thread.ExitReason() != 0) break;
\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
273 if (iThreadWatcher != NULL)
\r
275 DEBUGPRINT(_L("delete iThreadWatcher"));
\r
276 delete iThreadWatcher;
\r
277 DEBUGPRINT(_L("after delete iThreadWatcher"));
\r
278 iThreadWatcher = NULL;
\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
294 TBool CPicoGameSession::iEmuRunning = EFalse;
\r
295 CThreadWatcher *CPicoGameSession::iThreadWatcher = 0;
\r
296 TBuf<150> CPicoGameSession::iRomInternalName;
\r
300 CThreadWatcher::~CThreadWatcher()
\r
303 DEBUGPRINT(_L("after CThreadWatcher::Cancel();"));
\r
306 CThreadWatcher::CThreadWatcher(const TThreadId& aTid)
\r
307 : CActive(CActive::EPriorityStandard), iTid(aTid)
\r
312 CThreadWatcher* CThreadWatcher::NewL(const TThreadId& aTid)
\r
314 CThreadWatcher* self = new(ELeave) CThreadWatcher(aTid);
\r
315 CleanupStack::PushL(self);
\r
316 self->ConstructL();
\r
317 CleanupStack::Pop(); // self
\r
321 void CThreadWatcher::ConstructL()
\r
323 CActiveScheduler::Add(this);
\r
325 if(thread.Open(iTid) == KErrNone) {
\r
326 thread.Logon(iStatus);
\r
332 void CThreadWatcher::RunL()
\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
340 void CThreadWatcher::DoCancel()
\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
351 extern "C" void cache_flush_d_inval_i(const void *start_addr, const void *end_addr)
\r
354 User::IMB_Range((TAny *)start_addr, (TAny *)end_addr);
\r