rlm@1
|
1 // VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
rlm@1
|
2 // Copyright (C) 1999-2003 Forgotten
|
rlm@1
|
3 // Copyright (C) 2004 Forgotten and the VBA development team
|
rlm@1
|
4
|
rlm@1
|
5 // This program is free software; you can redistribute it and/or modify
|
rlm@1
|
6 // it under the terms of the GNU General Public License as published by
|
rlm@1
|
7 // the Free Software Foundation; either version 2, or(at your option)
|
rlm@1
|
8 // any later version.
|
rlm@1
|
9 //
|
rlm@1
|
10 // This program is distributed in the hope that it will be useful,
|
rlm@1
|
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
rlm@1
|
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
rlm@1
|
13 // GNU General Public License for more details.
|
rlm@1
|
14 //
|
rlm@1
|
15 // You should have received a copy of the GNU General Public License
|
rlm@1
|
16 // along with this program; if not, write to the Free Software Foundation,
|
rlm@1
|
17 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
rlm@1
|
18
|
rlm@1
|
19 #include <stdio.h>
|
rlm@1
|
20 #include <stdarg.h>
|
rlm@1
|
21 #include <string.h>
|
rlm@1
|
22
|
rlm@1
|
23 #include <SDL.h>
|
rlm@1
|
24 #include <SDL_thread.h>
|
rlm@1
|
25
|
rlm@1
|
26 #include "../common/System.h"
|
rlm@1
|
27 #include "../common/Util.h"
|
rlm@1
|
28 #include "../gba/GBAGlobals.h"
|
rlm@1
|
29 #include "../gb/gbGlobals.h"
|
rlm@1
|
30 #include "../gba/GBASound.h"
|
rlm@1
|
31
|
rlm@1
|
32 #include "window.h"
|
rlm@1
|
33 #include "intl.h"
|
rlm@1
|
34
|
rlm@1
|
35 // Required vars, used by the emulator core
|
rlm@1
|
36 //
|
rlm@1
|
37 int systemRedShift;
|
rlm@1
|
38 int systemGreenShift;
|
rlm@1
|
39 int systemBlueShift;
|
rlm@1
|
40 int systemColorDepth;
|
rlm@1
|
41 int systemDebug;
|
rlm@1
|
42 int systemVerbose;
|
rlm@1
|
43 int systemSaveUpdateCounter;
|
rlm@1
|
44 int systemFrameSkip;
|
rlm@1
|
45 u32 systemColorMap32[0x10000];
|
rlm@1
|
46 u16 systemColorMap16[0x10000];
|
rlm@1
|
47 u16 systemGbPalette[24];
|
rlm@1
|
48 bool systemSoundOn;
|
rlm@1
|
49
|
rlm@1
|
50 char filename[2048];
|
rlm@1
|
51 char biosFileName[2048];
|
rlm@1
|
52 //char captureDir[2048];
|
rlm@1
|
53 char saveDir[2048];
|
rlm@1
|
54 char batteryDir[2048];
|
rlm@1
|
55
|
rlm@1
|
56 int sensorX = 2047;
|
rlm@1
|
57 int sensorY = 2047;
|
rlm@1
|
58 bool sensorOn = false;
|
rlm@1
|
59
|
rlm@1
|
60 int emulating;
|
rlm@1
|
61 bool debugger;
|
rlm@1
|
62 int RGB_LOW_BITS_MASK;
|
rlm@1
|
63
|
rlm@1
|
64 int cartridgeType = 3;
|
rlm@1
|
65 int sizeOption = 0;
|
rlm@1
|
66 int captureFormat = 0;
|
rlm@1
|
67 int throttle = 0;
|
rlm@1
|
68 bool paused = false;
|
rlm@1
|
69 bool removeIntros = false;
|
rlm@1
|
70 int sdlFlashSize = 0;
|
rlm@1
|
71 int sdlRtcEnable = 0;
|
rlm@1
|
72
|
rlm@1
|
73 int sdlDefaultJoypad = 0;
|
rlm@1
|
74
|
rlm@1
|
75 SDL_Joystick **sdlDevices = NULL;
|
rlm@1
|
76
|
rlm@1
|
77 u16 motion[4] = {
|
rlm@1
|
78 SDLK_KP4, SDLK_KP6, SDLK_KP8, SDLK_KP2
|
rlm@1
|
79 };
|
rlm@1
|
80
|
rlm@1
|
81 struct EmulatedSystem emulator = {
|
rlm@1
|
82 NULL,
|
rlm@1
|
83 NULL,
|
rlm@1
|
84 NULL,
|
rlm@1
|
85 NULL,
|
rlm@1
|
86 NULL,
|
rlm@1
|
87 NULL,
|
rlm@1
|
88 NULL,
|
rlm@1
|
89 NULL,
|
rlm@1
|
90 NULL,
|
rlm@1
|
91 NULL,
|
rlm@1
|
92 NULL,
|
rlm@1
|
93 NULL,
|
rlm@1
|
94 false,
|
rlm@1
|
95 0
|
rlm@1
|
96 };
|
rlm@1
|
97
|
rlm@1
|
98 // Extra vars, only used for the GUI
|
rlm@1
|
99 //
|
rlm@1
|
100 int systemRenderedFrames;
|
rlm@1
|
101 int systemFPS;
|
rlm@1
|
102
|
rlm@1
|
103 // Sound stuff
|
rlm@1
|
104 //
|
rlm@1
|
105 const int iSoundSamples = 2048;
|
rlm@1
|
106 const int iSoundTotalLen = iSoundSamples * 4;
|
rlm@1
|
107 static u8 auiSoundBuffer[iSoundTotalLen];
|
rlm@1
|
108 static int iSoundLen;
|
rlm@1
|
109 static SDL_cond * pstSoundCond;
|
rlm@1
|
110 static SDL_mutex * pstSoundMutex;
|
rlm@1
|
111
|
rlm@1
|
112 inline VBA::Window * GUI()
|
rlm@1
|
113 {
|
rlm@1
|
114 return VBA::Window::poGetInstance();
|
rlm@1
|
115 }
|
rlm@1
|
116
|
rlm@1
|
117 void systemMessage(int _iId, const char * _csFormat, ...)
|
rlm@1
|
118 {
|
rlm@1
|
119 va_list args;
|
rlm@1
|
120 va_start(args, _csFormat);
|
rlm@1
|
121
|
rlm@1
|
122 GUI()->vPopupErrorV(_(_csFormat), args);
|
rlm@1
|
123
|
rlm@1
|
124 va_end(args);
|
rlm@1
|
125 }
|
rlm@1
|
126
|
rlm@1
|
127 void systemDrawScreen()
|
rlm@1
|
128 {
|
rlm@1
|
129 GUI()->vDrawScreen();
|
rlm@1
|
130 systemRenderedFrames++;
|
rlm@1
|
131 }
|
rlm@1
|
132
|
rlm@1
|
133 bool systemReadJoypads()
|
rlm@1
|
134 {
|
rlm@1
|
135 return true;
|
rlm@1
|
136 }
|
rlm@1
|
137
|
rlm@1
|
138 u32 systemReadJoypad(int,bool)
|
rlm@1
|
139 {
|
rlm@1
|
140 return GUI()->uiReadJoypad();
|
rlm@1
|
141 }
|
rlm@1
|
142
|
rlm@1
|
143 void systemShowSpeed(int _iSpeed)
|
rlm@1
|
144 {
|
rlm@1
|
145 systemFPS = systemRenderedFrames;
|
rlm@1
|
146 systemRenderedFrames = 0;
|
rlm@1
|
147
|
rlm@1
|
148 GUI()->vShowSpeed(_iSpeed);
|
rlm@1
|
149 }
|
rlm@1
|
150
|
rlm@1
|
151 void system10Frames(int _iRate)
|
rlm@1
|
152 {
|
rlm@1
|
153 GUI()->vComputeFrameskip(_iRate);
|
rlm@1
|
154 }
|
rlm@1
|
155
|
rlm@1
|
156 void systemFrame(int)
|
rlm@1
|
157 {
|
rlm@1
|
158 }
|
rlm@1
|
159
|
rlm@1
|
160 void systemSetTitle(const char * _csTitle)
|
rlm@1
|
161 {
|
rlm@1
|
162 GUI()->set_title(_csTitle);
|
rlm@1
|
163 }
|
rlm@1
|
164
|
rlm@1
|
165 int systemScreenCapture(int _iNum)
|
rlm@1
|
166 {
|
rlm@1
|
167 GUI()->vCaptureScreen(_iNum);
|
rlm@1
|
168 }
|
rlm@1
|
169
|
rlm@1
|
170 void systemSoundWriteToBuffer()
|
rlm@1
|
171 {
|
rlm@1
|
172 if (SDL_GetAudioStatus() != SDL_AUDIO_PLAYING)
|
rlm@1
|
173 {
|
rlm@1
|
174 SDL_PauseAudio(0);
|
rlm@1
|
175 }
|
rlm@1
|
176
|
rlm@1
|
177 bool bWait = true;
|
rlm@1
|
178 while (bWait && ! speedup && GUI()->iGetThrottle() == 0)
|
rlm@1
|
179 {
|
rlm@1
|
180 SDL_mutexP(pstSoundMutex);
|
rlm@1
|
181 if (iSoundLen < iSoundTotalLen)
|
rlm@1
|
182 {
|
rlm@1
|
183 bWait = false;
|
rlm@1
|
184 }
|
rlm@1
|
185 SDL_mutexV(pstSoundMutex);
|
rlm@1
|
186 }
|
rlm@1
|
187
|
rlm@1
|
188 int iLen = soundBufferLen;
|
rlm@1
|
189 int iCopied = 0;
|
rlm@1
|
190 if (iSoundLen + iLen >= iSoundTotalLen)
|
rlm@1
|
191 {
|
rlm@1
|
192 iCopied = iSoundTotalLen - iSoundLen;
|
rlm@1
|
193 memcpy(&auiSoundBuffer[iSoundLen], soundFinalWave, iCopied);
|
rlm@1
|
194
|
rlm@1
|
195 iSoundLen = iSoundTotalLen;
|
rlm@1
|
196 SDL_CondSignal(pstSoundCond);
|
rlm@1
|
197
|
rlm@1
|
198 bWait = true;
|
rlm@1
|
199 if (! speedup && GUI()->iGetThrottle() == 0)
|
rlm@1
|
200 {
|
rlm@1
|
201 while(bWait)
|
rlm@1
|
202 {
|
rlm@1
|
203 SDL_mutexP(pstSoundMutex);
|
rlm@1
|
204 if (iSoundLen < iSoundTotalLen)
|
rlm@1
|
205 {
|
rlm@1
|
206 bWait = false;
|
rlm@1
|
207 }
|
rlm@1
|
208 SDL_mutexV(pstSoundMutex);
|
rlm@1
|
209 }
|
rlm@1
|
210
|
rlm@1
|
211 memcpy(auiSoundBuffer, ((u8 *)soundFinalWave) + iCopied,
|
rlm@1
|
212 soundBufferLen - iCopied);
|
rlm@1
|
213
|
rlm@1
|
214 iSoundLen = soundBufferLen - iCopied;
|
rlm@1
|
215 }
|
rlm@1
|
216 else
|
rlm@1
|
217 {
|
rlm@1
|
218 memcpy(auiSoundBuffer, ((u8 *)soundFinalWave) + iCopied,
|
rlm@1
|
219 soundBufferLen);
|
rlm@1
|
220 }
|
rlm@1
|
221 }
|
rlm@1
|
222 else
|
rlm@1
|
223 {
|
rlm@1
|
224 memcpy(&auiSoundBuffer[iSoundLen], soundFinalWave, soundBufferLen);
|
rlm@1
|
225 iSoundLen += soundBufferLen;
|
rlm@1
|
226 }
|
rlm@1
|
227 }
|
rlm@1
|
228
|
rlm@1
|
229 static void vSoundCallback(void * _pvUserData, u8 * _puiStream, int _iLen)
|
rlm@1
|
230 {
|
rlm@1
|
231 if (! emulating)
|
rlm@1
|
232 {
|
rlm@1
|
233 return;
|
rlm@1
|
234 }
|
rlm@1
|
235
|
rlm@1
|
236 SDL_mutexP(pstSoundMutex);
|
rlm@1
|
237 if (! speedup && GUI()->iGetThrottle() == 0)
|
rlm@1
|
238 {
|
rlm@1
|
239 while (iSoundLen < iSoundTotalLen && emulating)
|
rlm@1
|
240 {
|
rlm@1
|
241 SDL_CondWait(pstSoundCond, pstSoundMutex);
|
rlm@1
|
242 }
|
rlm@1
|
243 }
|
rlm@1
|
244 if (emulating)
|
rlm@1
|
245 {
|
rlm@1
|
246 memcpy(_puiStream, auiSoundBuffer, _iLen);
|
rlm@1
|
247 }
|
rlm@1
|
248 iSoundLen = 0;
|
rlm@1
|
249 SDL_mutexV(pstSoundMutex);
|
rlm@1
|
250 }
|
rlm@1
|
251
|
rlm@1
|
252 bool systemSoundInit()
|
rlm@1
|
253 {
|
rlm@1
|
254 SDL_AudioSpec stAudio;
|
rlm@1
|
255
|
rlm@1
|
256 switch (soundQuality)
|
rlm@1
|
257 {
|
rlm@1
|
258 case 1:
|
rlm@1
|
259 stAudio.freq = 44100;
|
rlm@1
|
260 soundBufferLen = 1470 * 2;
|
rlm@1
|
261 break;
|
rlm@1
|
262 case 2:
|
rlm@1
|
263 stAudio.freq = 22050;
|
rlm@1
|
264 soundBufferLen = 736 * 2;
|
rlm@1
|
265 break;
|
rlm@1
|
266 case 4:
|
rlm@1
|
267 stAudio.freq = 11025;
|
rlm@1
|
268 soundBufferLen = 368 * 2;
|
rlm@1
|
269 break;
|
rlm@1
|
270 }
|
rlm@1
|
271
|
rlm@1
|
272 stAudio.format = AUDIO_S16SYS;
|
rlm@1
|
273 stAudio.channels = 2;
|
rlm@1
|
274 stAudio.samples = iSoundSamples;
|
rlm@1
|
275 stAudio.callback = vSoundCallback;
|
rlm@1
|
276 stAudio.userdata = NULL;
|
rlm@1
|
277
|
rlm@1
|
278 if (SDL_OpenAudio(&stAudio, NULL) < 0)
|
rlm@1
|
279 {
|
rlm@1
|
280 fprintf(stderr, "Failed to open audio: %s\n", SDL_GetError());
|
rlm@1
|
281 return false;
|
rlm@1
|
282 }
|
rlm@1
|
283
|
rlm@1
|
284 pstSoundCond = SDL_CreateCond();
|
rlm@1
|
285 pstSoundMutex = SDL_CreateMutex();
|
rlm@1
|
286
|
rlm@1
|
287 soundBufferTotalLen = soundBufferLen * 10;
|
rlm@1
|
288 iSoundLen = 0;
|
rlm@1
|
289 systemSoundOn = true;
|
rlm@1
|
290
|
rlm@1
|
291 return true;
|
rlm@1
|
292 }
|
rlm@1
|
293
|
rlm@1
|
294 void systemSoundShutdown()
|
rlm@1
|
295 {
|
rlm@1
|
296 SDL_mutexP(pstSoundMutex);
|
rlm@1
|
297 int iSave = emulating;
|
rlm@1
|
298 emulating = 0;
|
rlm@1
|
299 SDL_CondSignal(pstSoundCond);
|
rlm@1
|
300 SDL_mutexV(pstSoundMutex);
|
rlm@1
|
301
|
rlm@1
|
302 SDL_DestroyCond(pstSoundCond);
|
rlm@1
|
303 pstSoundCond = NULL;
|
rlm@1
|
304
|
rlm@1
|
305 SDL_DestroyMutex(pstSoundMutex);
|
rlm@1
|
306 pstSoundMutex = NULL;
|
rlm@1
|
307
|
rlm@1
|
308 SDL_CloseAudio();
|
rlm@1
|
309
|
rlm@1
|
310 emulating = iSave;
|
rlm@1
|
311 systemSoundOn = false;
|
rlm@1
|
312 }
|
rlm@1
|
313
|
rlm@1
|
314 void systemSoundPause()
|
rlm@1
|
315 {
|
rlm@1
|
316 SDL_PauseAudio(1);
|
rlm@1
|
317 }
|
rlm@1
|
318
|
rlm@1
|
319 void systemSoundResume()
|
rlm@1
|
320 {
|
rlm@1
|
321 SDL_PauseAudio(0);
|
rlm@1
|
322 }
|
rlm@1
|
323
|
rlm@1
|
324 void systemSoundReset()
|
rlm@1
|
325 {
|
rlm@1
|
326 }
|
rlm@1
|
327
|
rlm@1
|
328 u32 systemGetClock()
|
rlm@1
|
329 {
|
rlm@1
|
330 return SDL_GetTicks();
|
rlm@1
|
331 }
|
rlm@1
|
332
|
rlm@1
|
333 void systemUpdateMotionSensor()
|
rlm@1
|
334 {
|
rlm@1
|
335 }
|
rlm@1
|
336
|
rlm@1
|
337 int systemGetSensorX()
|
rlm@1
|
338 {
|
rlm@1
|
339 return 0;
|
rlm@1
|
340 }
|
rlm@1
|
341
|
rlm@1
|
342 int systemGetSensorY()
|
rlm@1
|
343 {
|
rlm@1
|
344 return 0;
|
rlm@1
|
345 }
|
rlm@1
|
346
|
rlm@1
|
347 void systemGbPrint(u8 * _puiData,
|
rlm@1
|
348 int _iPages,
|
rlm@1
|
349 int _iFeed,
|
rlm@1
|
350 int _iPalette,
|
rlm@1
|
351 int _iContrast)
|
rlm@1
|
352 {
|
rlm@1
|
353 }
|
rlm@1
|
354
|
rlm@1
|
355 void systemScreenMessage(const char * _csMsg, int slot, int duration, const char *colorList)
|
rlm@1
|
356 {
|
rlm@1
|
357 }
|
rlm@1
|
358
|
rlm@1
|
359 bool systemCanChangeSoundQuality()
|
rlm@1
|
360 {
|
rlm@1
|
361 return true;
|
rlm@1
|
362 }
|
rlm@1
|
363
|
rlm@1
|
364 bool systemPauseOnFrame()
|
rlm@1
|
365 {
|
rlm@1
|
366 return false;
|
rlm@1
|
367 }
|
rlm@1
|
368
|
rlm@1
|
369 void systemGbBorderOn()
|
rlm@1
|
370 {
|
rlm@1
|
371 }
|
rlm@1
|
372
|
rlm@1
|
373 void debuggerMain()
|
rlm@1
|
374 {
|
rlm@1
|
375 }
|
rlm@1
|
376
|
rlm@1
|
377 void debuggerSignal(int, int)
|
rlm@1
|
378 {
|
rlm@1
|
379 }
|
rlm@1
|
380
|
rlm@1
|
381 void debuggerOutput(char *, u32)
|
rlm@1
|
382 {
|
rlm@1
|
383 }
|
rlm@1
|
384
|
rlm@1
|
385 char *sdlGetFilename(char *name)
|
rlm@1
|
386 {
|
rlm@1
|
387 static char filebuffer[2048];
|
rlm@1
|
388
|
rlm@1
|
389 int len = strlen(name);
|
rlm@1
|
390
|
rlm@1
|
391 char *p = name + len - 1;
|
rlm@1
|
392
|
rlm@1
|
393 while(true) {
|
rlm@1
|
394 if(*p == '/' ||
|
rlm@1
|
395 *p == '\\') {
|
rlm@1
|
396 p++;
|
rlm@1
|
397 break;
|
rlm@1
|
398 }
|
rlm@1
|
399 len--;
|
rlm@1
|
400 p--;
|
rlm@1
|
401 if(len == 0)
|
rlm@1
|
402 break;
|
rlm@1
|
403 }
|
rlm@1
|
404
|
rlm@1
|
405 if(len == 0)
|
rlm@1
|
406 strcpy(filebuffer, name);
|
rlm@1
|
407 else
|
rlm@1
|
408 strcpy(filebuffer, p);
|
rlm@1
|
409 return filebuffer;
|
rlm@1
|
410 }
|
rlm@1
|
411
|
rlm@1
|
412 bool sdlCheckJoyKey(int key)
|
rlm@1
|
413 {
|
rlm@1
|
414 int dev = (key >> 12) - 1;
|
rlm@1
|
415 int what = key & 0xfff;
|
rlm@1
|
416
|
rlm@1
|
417 if(what >= 128) {
|
rlm@1
|
418 // joystick button
|
rlm@1
|
419 int button = what - 128;
|
rlm@1
|
420
|
rlm@1
|
421 if(button >= SDL_JoystickNumButtons(sdlDevices[dev]))
|
rlm@1
|
422 return false;
|
rlm@1
|
423 } else if (what < 0x20) {
|
rlm@1
|
424 // joystick axis
|
rlm@1
|
425 what >>= 1;
|
rlm@1
|
426 if(what >= SDL_JoystickNumAxes(sdlDevices[dev]))
|
rlm@1
|
427 return false;
|
rlm@1
|
428 } else if (what < 0x30) {
|
rlm@1
|
429 // joystick hat
|
rlm@1
|
430 what = (what & 15);
|
rlm@1
|
431 what >>= 2;
|
rlm@1
|
432 if(what >= SDL_JoystickNumHats(sdlDevices[dev]))
|
rlm@1
|
433 return false;
|
rlm@1
|
434 }
|
rlm@1
|
435
|
rlm@1
|
436 // no problem found
|
rlm@1
|
437 return true;
|
rlm@1
|
438 }
|
rlm@1
|
439
|
rlm@1
|
440 u16 checksumBIOS()
|
rlm@1
|
441 {
|
rlm@1
|
442 bool hasBIOS = false;
|
rlm@1
|
443 u8 * tempBIOS;
|
rlm@1
|
444 if(useBios)
|
rlm@1
|
445 {
|
rlm@1
|
446 tempBIOS = (u8 *)malloc(0x4000);
|
rlm@1
|
447 int size = 0x4000;
|
rlm@1
|
448 if(utilLoad(biosFileName,
|
rlm@1
|
449 utilIsGBABios,
|
rlm@1
|
450 tempBIOS,
|
rlm@1
|
451 size)) {
|
rlm@1
|
452 if(size == 0x4000)
|
rlm@1
|
453 hasBIOS = true;
|
rlm@1
|
454 }
|
rlm@1
|
455 }
|
rlm@1
|
456
|
rlm@1
|
457 u16 biosCheck = 0;
|
rlm@1
|
458 if(hasBIOS) {
|
rlm@1
|
459 for(int i = 0; i < 0x4000; i += 4)
|
rlm@1
|
460 biosCheck += *((u32 *)&tempBIOS[i]);
|
rlm@1
|
461 free(tempBIOS);
|
rlm@1
|
462 }
|
rlm@1
|
463
|
rlm@1
|
464 return biosCheck;
|
rlm@1
|
465 }
|
rlm@1
|
466
|
rlm@1
|
467 void (*dbgMain)() = debuggerMain;
|
rlm@1
|
468 void (*dbgSignal)(int, int) = debuggerSignal;
|
rlm@1
|
469 void (*dbgOutput)(char *, u32) = debuggerOutput;
|