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 |
32 | extern TInt machineUid;\r |
33 | extern int gamestate, gamestate_next;\r |
ca482e5d |
34 | extern char *loadrom_fname;\r |
35 | extern int loadrom_result;\r |
cc68a136 |
36 | extern const char *actionNames[];\r |
cc68a136 |
37 | RSemaphore initSemaphore;\r |
ca482e5d |
38 | RSemaphore pauseSemaphore;\r |
39 | RSemaphore loadWaitSemaphore;\r |
cc68a136 |
40 | static CPicolAppView *appView = 0;\r |
41 | \r |
42 | \r |
43 | TInt 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 |
94 | TInt EmuThreadFunction(TAny* anArg);\r |
95 | \r |
96 | TInt 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 |
148 | TInt 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 |
165 | TInt 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 |
208 | TInt 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 |
232 | extern RMutex logMutex;\r |
233 | #endif\r |
234 | \r |
235 | void 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 |
290 | TBool CPicoGameSession::iEmuRunning = EFalse;\r |
291 | CThreadWatcher *CPicoGameSession::iThreadWatcher = 0;\r |
ca482e5d |
292 | TBuf<150> CPicoGameSession::iRomInternalName;\r |
cc68a136 |
293 | \r |
294 | \r |
295 | // CThreadWatcher\r |
296 | CThreadWatcher::~CThreadWatcher()\r |
297 | {\r |
298 | Cancel();\r |
299 | DEBUGPRINT(_L("after CThreadWatcher::Cancel();"));\r |
300 | }\r |
301 | \r |
302 | CThreadWatcher::CThreadWatcher(const TThreadId& aTid)\r |
303 | : CActive(CActive::EPriorityStandard), iTid(aTid)\r |
304 | {\r |
305 | }\r |
306 | \r |
307 | \r |
308 | CThreadWatcher* 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 |
317 | void 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 |
328 | void 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 |
336 | void 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 |
347 | extern "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 |