UIQ3 update, some makefile unification, rm old configs, stuff
[picodrive.git] / platform / uiq3 / Engine.cpp
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