pandora: use hw scaler (resolves 32x+sms), frontend refactoring
[picodrive.git] / platform / uiq3 / Engine.cpp
CommitLineData
cc68a136 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
efcba75f 26#include <pico/pico_int.h>\r
ca482e5d 27#include "../common/emu.h"\r
cc68a136 28#include "engine/debug.h"\r
ca482e5d 29#include "App.h"\r
cc68a136 30\r
31// this is where we start to break a bunch of symbian rules\r
32extern TInt machineUid;\r
33extern int gamestate, gamestate_next;\r
ca482e5d 34extern char *loadrom_fname;\r
35extern int loadrom_result;\r
cc68a136 36extern const char *actionNames[];\r
cc68a136 37RSemaphore initSemaphore;\r
ca482e5d 38RSemaphore pauseSemaphore;\r
39RSemaphore loadWaitSemaphore;\r
cc68a136 40static CPicolAppView *appView = 0;\r
41\r
42\r
43TInt CPicoGameSession::Do(const TPicoServRqst what, TAny *param)\r
44{\r
ca482e5d 45 switch (what)\r
46 {\r
cc68a136 47 case PicoMsgLoadState: \r
ca482e5d 48 if(!rom_loaded) return -1; // no ROM\r
a47dd663 49 return emu_save_load_game(1, 0);\r
cc68a136 50\r
51 case PicoMsgSaveState:\r
ca482e5d 52 if(!rom_loaded) return -1;\r
a47dd663 53 return emu_save_load_game(0, 0);\r
cc68a136 54\r
55 case PicoMsgLoadROM:\r
56 return loadROM((TPtrC16 *)param);\r
57 \r
58 case PicoMsgResume:\r
ca482e5d 59 DEBUGPRINT(_L("resume"));\r
60 if(rom_loaded) {\r
cc68a136 61 return ChangeRunState(PGS_Running);\r
62 }\r
63 return 1;\r
64\r
65 case PicoMsgReset: \r
ca482e5d 66 if(rom_loaded) {\r
f8af9634 67 return ChangeRunState(PGS_Reset);\r
cc68a136 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
94TInt EmuThreadFunction(TAny* anArg);\r
95\r
96TInt 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
ca482e5d 106 if (loadWaitSemaphore.Handle() <= 0)\r
107 loadWaitSemaphore.CreateLocal(0);\r
cc68a136 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
cc68a136 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
ca482e5d 137 res = initSemaphore.Wait(3*1000*1000); // wait until it's initialized\r
cc68a136 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
148TInt 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
165TInt CPicoGameSession::loadROM(TPtrC16 *pptr)\r
166{\r
ca482e5d 167 TInt ret;\r
168 char buff[150];\r
cc68a136 169\r
ca482e5d 170 // make sure emu thread is ok\r
171 ret = ChangeRunState(PGS_Paused);\r
172 if(ret) return ret;\r
cc68a136 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
ca482e5d 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
f8af9634 182 loadWaitSemaphore.Wait(1); // make sure sem is not set\r
ca482e5d 183 ret = ChangeRunState(PGS_ReloadRom);\r
184 if(ret) return ret;\r
cc68a136 185\r
f8af9634 186 loadWaitSemaphore.Wait(60*1000*1000);\r
cc68a136 187\r
ca482e5d 188 if (loadrom_result == 0)\r
f8af9634 189 return PicoErrRomOpenFailed;\r
cc68a136 190\r
a47dd663 191 emu_get_game_name(buff);\r
cc68a136 192 TPtrC8 buff8((TUint8*) buff);\r
193 iRomInternalName.Copy(buff8);\r
194\r
ca482e5d 195 DEBUGPRINT(_L("done waiting for ROM load"));\r
cc68a136 196\r
197 // debug\r
198 #ifdef __DEBUG_PRINT\r
f8af9634 199 TInt mem, cells = User::CountAllocCells();\r
cc68a136 200 User::AllocSize(mem);\r
201 DEBUGPRINT(_L("comm: cels=%d, size=%d KB"), cells, mem/1024);\r
cc68a136 202 #endif\r
203\r
204 return 0;\r
205}\r
206\r
207\r
208TInt CPicoGameSession::changeConfig(TPicoConfig *aConfig)\r
209{\r
cc68a136 210 // 6 button pad, enable XYZM config if needed\r
ca482e5d 211 if (PicoOpt & POPT_6BTN_PAD)\r
212 {\r
cc68a136 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
ca482e5d 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
cc68a136 226\r
227 return 0;\r
228}\r
229\r
230\r
cc68a136 231#ifdef __DEBUG_PRINT_FILE\r
232extern RMutex logMutex;\r
233#endif\r
234\r
235void 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
ca482e5d 269 if (iThreadWatcher != NULL)\r
cc68a136 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
cc68a136 277 if (initSemaphore.Handle() > 0)\r
278 initSemaphore.Close();\r
279 if (pauseSemaphore.Handle() > 0)\r
280 pauseSemaphore.Close();\r
ca482e5d 281 if (loadWaitSemaphore.Handle() > 0)\r
282 loadWaitSemaphore.Close();\r
283 DEBUGPRINT(_L("freeResources() returning"));\r
cc68a136 284#ifdef __DEBUG_PRINT_FILE\r
285 if (logMutex.Handle() > 0)\r
286 logMutex.Close();\r
287#endif\r
288}\r
289\r
290TBool CPicoGameSession::iEmuRunning = EFalse;\r
291CThreadWatcher *CPicoGameSession::iThreadWatcher = 0;\r
ca482e5d 292TBuf<150> CPicoGameSession::iRomInternalName;\r
cc68a136 293\r
294\r
295// CThreadWatcher\r
296CThreadWatcher::~CThreadWatcher()\r
297{\r
298 Cancel();\r
299 DEBUGPRINT(_L("after CThreadWatcher::Cancel();"));\r
300}\r
301\r
302CThreadWatcher::CThreadWatcher(const TThreadId& aTid)\r
303: CActive(CActive::EPriorityStandard), iTid(aTid)\r
304{\r
305}\r
306\r
307\r
308CThreadWatcher* 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
317void 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
328void 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
336void 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
ca482e5d 346\r
347extern "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