Mercurial > vba-clojure
comparison src/win32/System.cpp @ 1:f9f4f1b99eed
importing src directory
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 03 Mar 2012 10:31:27 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:8ced16adf2e1 | 1:f9f4f1b99eed |
---|---|
1 // System.cpp : Defines the system behaviors for the emulator. | |
2 // | |
3 #include "stdafx.h" | |
4 #include "Sound.h" | |
5 #include "Input.h" | |
6 #include "IUpdate.h" | |
7 #include "ram_search.h" | |
8 #include "WinMiscUtil.h" | |
9 #include "WinResUtil.h" | |
10 #include "resource.h" | |
11 #include "VBA.h" | |
12 #include "../gba/GBA.h" | |
13 #include "../gba/GBAGlobals.h" | |
14 #include "../gba/GBASound.h" | |
15 #include "../gb/GB.h" | |
16 #include "../gb/gbGlobals.h" | |
17 //#include "../common/System.h" | |
18 #include "../common/movie.h" | |
19 #include "../common/vbalua.h" | |
20 #include "../common/Text.h" | |
21 #include "../common/Util.h" | |
22 #include "../common/nesvideos-piece.h" | |
23 #include "../version.h" | |
24 #include <cassert> | |
25 | |
26 struct EmulatedSystem theEmulator; | |
27 | |
28 u32 RGB_LOW_BITS_MASK = 0; | |
29 int emulating = 0; | |
30 int systemCartridgeType = 0; | |
31 int systemSpeed = 0; | |
32 bool systemSoundOn = false; | |
33 u32 systemColorMap32[0x10000]; | |
34 u16 systemColorMap16[0x10000]; | |
35 u16 systemGbPalette[24]; | |
36 int systemRedShift = 0; | |
37 int systemBlueShift = 0; | |
38 int systemGreenShift = 0; | |
39 int systemColorDepth = 16; | |
40 int systemDebug = 0; | |
41 int systemVerbose = 0; | |
42 int systemFrameSkip = 0; | |
43 int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; | |
44 | |
45 const int32 INITIAL_SENSOR_VALUE = 2047; | |
46 | |
47 int32 sensorX = INITIAL_SENSOR_VALUE; | |
48 int32 sensorY = INITIAL_SENSOR_VALUE; | |
49 u16 currentButtons [4] = { 0, 0, 0, 0 }; // constrain: never contains hacked buttons, only the lower 16 bits of each are used | |
50 u16 lastKeys = 0; | |
51 | |
52 // static_assertion that BUTTON_REGULAR_RECORDING_MASK should be an u16 constant | |
53 namespace { const void * const s_STATIC_ASSERTION_(static_cast<void *>(BUTTON_REGULAR_RECORDING_MASK & 0xFFFF0000)); } | |
54 | |
55 #define BMP_BUFFER_MAX_WIDTH (256) | |
56 #define BMP_BUFFER_MAX_HEIGHT (224) | |
57 #define BMP_BUFFER_MAX_DEPTH (4) | |
58 static u8 bmpBuffer[BMP_BUFFER_MAX_WIDTH * BMP_BUFFER_MAX_HEIGHT * BMP_BUFFER_MAX_DEPTH]; | |
59 | |
60 static int s_stockThrottleValues[] = { | |
61 6, 15, 25, 25, 37, 50, 75, 87, 100, 112, 125, 150, 200, 300, 400, 600, 800, 1000 | |
62 }; | |
63 | |
64 // systemXYZ: Win32 stuff | |
65 | |
66 // input | |
67 | |
68 void systemSetSensorX(int32 x) | |
69 { | |
70 sensorX = x; | |
71 } | |
72 | |
73 void systemSetSensorY(int32 y) | |
74 { | |
75 sensorY = y; | |
76 } | |
77 | |
78 void systemResetSensor() | |
79 { | |
80 sensorX = sensorY = INITIAL_SENSOR_VALUE; | |
81 } | |
82 | |
83 int32 systemGetSensorX() | |
84 { | |
85 return sensorX; | |
86 } | |
87 | |
88 int32 systemGetSensorY() | |
89 { | |
90 return sensorY; | |
91 } | |
92 | |
93 // handles motion sensor input | |
94 void systemUpdateMotionSensor(int i) | |
95 { | |
96 if (i < 0 || i > 3) | |
97 i = 0; | |
98 | |
99 if (currentButtons[i] & BUTTON_MASK_LEFT_MOTION) | |
100 { | |
101 sensorX += 3; | |
102 if (sensorX > 2197) | |
103 sensorX = 2197; | |
104 if (sensorX < 2047) | |
105 sensorX = 2057; | |
106 } | |
107 else if (currentButtons[i] & BUTTON_MASK_RIGHT_MOTION) | |
108 { | |
109 sensorX -= 3; | |
110 if (sensorX < 1897) | |
111 sensorX = 1897; | |
112 if (sensorX > 2047) | |
113 sensorX = 2037; | |
114 } | |
115 else if (sensorX > 2047) | |
116 { | |
117 sensorX -= 2; | |
118 if (sensorX < 2047) | |
119 sensorX = 2047; | |
120 } | |
121 else | |
122 { | |
123 sensorX += 2; | |
124 if (sensorX > 2047) | |
125 sensorX = 2047; | |
126 } | |
127 | |
128 if (currentButtons[i] & BUTTON_MASK_UP_MOTION) | |
129 { | |
130 sensorY += 3; | |
131 if (sensorY > 2197) | |
132 sensorY = 2197; | |
133 if (sensorY < 2047) | |
134 sensorY = 2057; | |
135 } | |
136 else if (currentButtons[i] & BUTTON_MASK_DOWN_MOTION) | |
137 { | |
138 sensorY -= 3; | |
139 if (sensorY < 1897) | |
140 sensorY = 1897; | |
141 if (sensorY > 2047) | |
142 sensorY = 2037; | |
143 } | |
144 else if (sensorY > 2047) | |
145 { | |
146 sensorY -= 2; | |
147 if (sensorY < 2047) | |
148 sensorY = 2047; | |
149 } | |
150 else | |
151 { | |
152 sensorY += 2; | |
153 if (sensorY > 2047) | |
154 sensorY = 2047; | |
155 } | |
156 } | |
157 | |
158 int systemGetDefaultJoypad() | |
159 { | |
160 return theApp.joypadDefault; | |
161 } | |
162 | |
163 void systemSetDefaultJoypad(int which) | |
164 { | |
165 theApp.joypadDefault = which; | |
166 } | |
167 | |
168 bool systemReadJoypads() | |
169 { | |
170 // this function is called at every frame, even if vba is fast-forwarded. | |
171 // so we try to limit the input frequency here just in case. | |
172 static u32 lastTime = systemGetClock(); | |
173 if ((u32)(systemGetClock() - lastTime) < 10) | |
174 return false; | |
175 lastTime = systemGetClock(); | |
176 | |
177 if (theApp.input) | |
178 return theApp.input->readDevices(); | |
179 return false; | |
180 } | |
181 | |
182 u32 systemGetOriginalJoypad(int i, bool sensor) | |
183 { | |
184 if (i < 0 || i > 3) | |
185 i = 0; | |
186 | |
187 u32 res = 0; | |
188 if (theApp.input) | |
189 res = theApp.input->readDevice(i, sensor); | |
190 | |
191 // +auto input, XOR | |
192 // maybe these should be moved into DirectInput.cpp | |
193 if (theApp.autoFire || theApp.autoFire2) | |
194 { | |
195 res ^= (theApp.autoFireToggle ? theApp.autoFire : theApp.autoFire2); | |
196 if (!theApp.autofireAccountForLag || !systemCounters.laggedLast) | |
197 { | |
198 theApp.autoFireToggle = !theApp.autoFireToggle; | |
199 } | |
200 } | |
201 if (theApp.autoHold) | |
202 { | |
203 res ^= theApp.autoHold; | |
204 } | |
205 | |
206 // filter buttons | |
207 // maybe better elsewhere? | |
208 if (!theApp.allowLeftRight) | |
209 { | |
210 // disallow L+R or U+D to being pressed at the same time | |
211 if ((res & (BUTTON_MASK_RIGHT | BUTTON_MASK_LEFT)) == (BUTTON_MASK_RIGHT | BUTTON_MASK_LEFT)) | |
212 res &= ~BUTTON_MASK_RIGHT; // leave only LEFT on | |
213 if ((res & (BUTTON_MASK_DOWN | BUTTON_MASK_UP)) == (BUTTON_MASK_DOWN | BUTTON_MASK_UP)) | |
214 res &= ~BUTTON_MASK_DOWN; // leave only UP on | |
215 } | |
216 | |
217 if (!sensor) | |
218 { | |
219 if (res & BUTTON_MOTION_MASK) | |
220 res &= ~BUTTON_MOTION_MASK; | |
221 } | |
222 | |
223 if (systemCartridgeType != 0 && !gbSgbMode) // regular GB has no L/R buttons | |
224 { | |
225 if (res & (BUTTON_GBA_ONLY)) | |
226 res &= ~BUTTON_GBA_ONLY; | |
227 } | |
228 | |
229 currentButtons[i] = res & BUTTON_REGULAR_RECORDING_MASK; | |
230 | |
231 return res; | |
232 } | |
233 | |
234 u32 systemGetJoypad(int i, bool sensor) | |
235 { | |
236 if (i < 0 || i > 3) | |
237 i = 0; | |
238 | |
239 // input priority: original+auto < Lua < frame search < movie, correct this if wrong | |
240 | |
241 // get original+auto input | |
242 u32 hackedButtons = systemGetOriginalJoypad(i, sensor) & BUTTON_NONRECORDINGONLY_MASK; | |
243 u32 res = currentButtons[i]; | |
244 | |
245 // since movie input has the highest priority, there's no point to read from other input | |
246 if (VBAMoviePlaying()) | |
247 { | |
248 // VBAMovieRead() overwrites currentButtons[i] | |
249 VBAMovieRead(i, sensor); | |
250 res = currentButtons[i]; | |
251 } | |
252 else | |
253 { | |
254 // Lua input, shouldn't have any side effect within them | |
255 if (VBALuaUsingJoypad(i)) | |
256 res = VBALuaReadJoypad(i); | |
257 | |
258 // override input above | |
259 if (theApp.frameSearchSkipping) | |
260 res = theApp.frameSearchOldInput[i]; | |
261 | |
262 // flush non-hack buttons into the "current buttons" input buffer, which will be read by the movie routine | |
263 currentButtons[i] = res & BUTTON_REGULAR_RECORDING_MASK; | |
264 VBAMovieWrite(i, sensor); | |
265 } | |
266 | |
267 return res | hackedButtons; | |
268 } | |
269 | |
270 void systemSetJoypad(int which, u32 buttons) | |
271 { | |
272 if (which < 0 || which > 3) | |
273 which = 0; | |
274 | |
275 currentButtons[which] = buttons; | |
276 | |
277 lastKeys = 0; | |
278 } | |
279 | |
280 void systemClearJoypads() | |
281 { | |
282 for (int i = 0; i < 3; ++i) | |
283 currentButtons[i] = 0; | |
284 | |
285 lastKeys = 0; | |
286 } | |
287 | |
288 // screen | |
289 | |
290 // delayed repaint | |
291 void systemRefreshScreen() | |
292 { | |
293 if (theApp.m_pMainWnd) | |
294 { | |
295 theApp.m_pMainWnd->PostMessage(WM_PAINT, NULL, NULL); | |
296 } | |
297 } | |
298 | |
299 extern bool vbaShuttingDown; | |
300 | |
301 void systemRenderFrame() | |
302 { | |
303 extern long linearSoundFrameCount; | |
304 extern long linearFrameCount; | |
305 | |
306 if (vbaShuttingDown) | |
307 return; | |
308 | |
309 ++theApp.renderedFrames; | |
310 | |
311 VBAUpdateFrameCountDisplay(); | |
312 VBAUpdateButtonPressDisplay(); | |
313 | |
314 // "in-game" text rendering | |
315 if (textMethod == 0) // transparent text can only be painted once, so timed messages will not be updated | |
316 { | |
317 extern void DrawLuaGui(); | |
318 DrawLuaGui(); | |
319 | |
320 int copyX = 240, copyY = 160; | |
321 if (systemCartridgeType == 1) | |
322 if (gbBorderOn) | |
323 copyX = 256, copyY = 224; | |
324 else | |
325 copyX = 160, copyY = 144; | |
326 int pitch = copyX * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); // FIXME: sure? | |
327 | |
328 DrawTextMessages((u8 *)pix, pitch, 0, copyY); | |
329 } | |
330 | |
331 ++linearFrameCount; | |
332 if (!theApp.sound) | |
333 { | |
334 if (linearFrameCount > 10000) | |
335 linearFrameCount -= 10000; | |
336 linearSoundFrameCount = linearFrameCount; | |
337 } | |
338 | |
339 // record avi | |
340 int width = 240; | |
341 int height = 160; | |
342 switch (systemCartridgeType) | |
343 { | |
344 case 0: | |
345 width = 240; | |
346 height = 160; | |
347 break; | |
348 case 1: | |
349 if (gbBorderOn) | |
350 { | |
351 width = 256; | |
352 height = 224; | |
353 } | |
354 else | |
355 { | |
356 width = 160; | |
357 height = 144; | |
358 } | |
359 break; | |
360 } | |
361 | |
362 bool firstFrameLogged = false; | |
363 --linearFrameCount; | |
364 do | |
365 { | |
366 ++linearFrameCount; | |
367 | |
368 if (theApp.aviRecording && (!theApp.altAviRecordMethod || (theApp.altAviRecordMethod && !firstFrameLogged))) | |
369 { | |
370 // usually aviRecorder is created when vba starts avi recording, though | |
371 if (theApp.aviRecorder == NULL) | |
372 { | |
373 theApp.aviRecorder = new AVIWrite(); | |
374 | |
375 theApp.aviRecorder->SetFPS(60); | |
376 | |
377 BITMAPINFOHEADER bi; | |
378 memset(&bi, 0, sizeof(bi)); | |
379 bi.biSize = 0x28; | |
380 bi.biPlanes = 1; | |
381 bi.biBitCount = 24; | |
382 bi.biWidth = width; | |
383 bi.biHeight = height; | |
384 bi.biSizeImage = 3 * width * height; | |
385 theApp.aviRecorder->SetVideoFormat(&bi); | |
386 if (!theApp.aviRecorder->Open(theApp.aviRecordName)) | |
387 { | |
388 delete theApp.aviRecorder; | |
389 theApp.aviRecorder = NULL; | |
390 theApp.aviRecording = false; | |
391 } | |
392 } | |
393 | |
394 if (theApp.aviRecorder != NULL && !theApp.aviRecorder->IsPaused()) | |
395 { | |
396 assert( | |
397 width <= BMP_BUFFER_MAX_WIDTH && height <= BMP_BUFFER_MAX_HEIGHT && systemColorDepth <= | |
398 BMP_BUFFER_MAX_DEPTH * 8); | |
399 utilWriteBMP(bmpBuffer, width, height, systemColorDepth, pix); | |
400 theApp.aviRecorder->AddFrame(bmpBuffer); | |
401 } | |
402 } | |
403 | |
404 if (theApp.nvVideoLog) | |
405 { | |
406 // convert from whatever bit depth to 16-bit, while stripping away extra pixels | |
407 assert(width <= BMP_BUFFER_MAX_WIDTH && height <= BMP_BUFFER_MAX_HEIGHT && 16 <= BMP_BUFFER_MAX_DEPTH * 8); | |
408 utilWriteBMP(bmpBuffer, width, -height, 16, pix); | |
409 NESVideoLoggingVideo((u8 *)bmpBuffer, width, height, 0x1000000 * 60); | |
410 } | |
411 | |
412 firstFrameLogged = true; | |
413 } | |
414 while (linearFrameCount < linearSoundFrameCount); // compensate for frames lost due to frame skip being nonzero, etc. | |
415 | |
416 if (textMethod != 0) // do not draw Lua HUD to a video dump | |
417 { | |
418 extern void DrawLuaGui(); | |
419 DrawLuaGui(); | |
420 } | |
421 | |
422 // interframe blending | |
423 if (theApp.ifbFunction) | |
424 { | |
425 if (systemColorDepth == 16) | |
426 theApp.ifbFunction(pix + theApp.filterWidth * 2 + 4, theApp.filterWidth * 2 + 4, | |
427 theApp.filterWidth, theApp.filterHeight); | |
428 else | |
429 theApp.ifbFunction(pix + theApp.filterWidth * 4 + 4, theApp.filterWidth * 4 + 4, | |
430 theApp.filterWidth, theApp.filterHeight); | |
431 } | |
432 | |
433 systemRedrawScreen(); | |
434 } | |
435 | |
436 void systemRedrawScreen() | |
437 { | |
438 if (vbaShuttingDown) | |
439 return; | |
440 | |
441 if (theApp.display) | |
442 theApp.display->render(); | |
443 | |
444 systemUpdateListeners(); | |
445 } | |
446 | |
447 void systemUpdateListeners() | |
448 { | |
449 if (vbaShuttingDown) | |
450 return; | |
451 | |
452 Update_RAM_Search(); // updates RAM search and RAM watch | |
453 | |
454 // update viewers etc. | |
455 if (theApp.updateCount) | |
456 { | |
457 POSITION pos = theApp.updateList.GetHeadPosition(); | |
458 while (pos) | |
459 { | |
460 IUpdateListener *up = theApp.updateList.GetNext(pos); | |
461 if (up) | |
462 up->update(); | |
463 } | |
464 } | |
465 } | |
466 | |
467 int systemScreenCapture(int captureNumber) | |
468 { | |
469 return winScreenCapture(captureNumber); | |
470 } | |
471 | |
472 void systemMessage(int number, const char *defaultMsg, ...) | |
473 { | |
474 CString buffer; | |
475 va_list valist; | |
476 CString msg = defaultMsg; | |
477 if (number) | |
478 msg = winResLoadString(number); | |
479 | |
480 va_start(valist, defaultMsg); | |
481 buffer.FormatV(msg, valist); | |
482 | |
483 theApp.winCheckFullscreen(); | |
484 systemSoundClearBuffer(); | |
485 AfxGetApp()->m_pMainWnd->MessageBox(buffer, winResLoadString(IDS_ERROR), MB_OK | MB_ICONERROR); | |
486 | |
487 va_end(valist); | |
488 } | |
489 | |
490 void systemScreenMessage(const char *msg, int slot, int duration, const char *colorList) | |
491 { | |
492 if (slot < 0 || slot > SCREEN_MESSAGE_SLOTS) | |
493 return; | |
494 | |
495 theApp.screenMessage[slot] = true; | |
496 theApp.screenMessageTime[slot] = GetTickCount(); | |
497 theApp.screenMessageDuration[slot] = duration; | |
498 theApp.screenMessageBuffer[slot] = msg; | |
499 theApp.screenMessageColorBuffer[slot] = colorList ? colorList : ""; | |
500 | |
501 if (theApp.screenMessageBuffer[slot].GetLength() > 40) | |
502 theApp.screenMessageBuffer[slot] = theApp.screenMessageBuffer[slot].Left(40); | |
503 | |
504 // update the display when a main slot message appears while the game is paused | |
505 if (slot == 0 && (theApp.paused || (theApp.frameSearching))) | |
506 systemRefreshScreen(); | |
507 } | |
508 | |
509 void systemShowSpeed(int speed) | |
510 { | |
511 systemSpeed = speed; | |
512 theApp.showRenderedFrames = theApp.renderedFrames; | |
513 theApp.renderedFrames = 0; | |
514 if (theApp.videoOption <= VIDEO_4X && theApp.showSpeed) | |
515 { | |
516 CString buffer; | |
517 if (theApp.showSpeed == 1) | |
518 buffer.Format(VBA_NAME_AND_VERSION " %3d%%", systemSpeed); | |
519 else | |
520 buffer.Format(VBA_NAME_AND_VERSION " %3d%% (%d fps | %d skipped)", | |
521 systemSpeed, | |
522 theApp.showRenderedFrames, | |
523 systemFrameSkip); | |
524 | |
525 systemSetTitle(buffer); | |
526 } | |
527 } | |
528 | |
529 void systemSetTitle(const char *title) | |
530 { | |
531 if (theApp.m_pMainWnd != NULL) | |
532 { | |
533 AfxGetApp()->m_pMainWnd->SetWindowText(title); | |
534 } | |
535 } | |
536 | |
537 // timing/speed | |
538 | |
539 u32 systemGetClock() | |
540 { | |
541 return timeGetTime(); | |
542 } | |
543 | |
544 void systemIncreaseThrottle() | |
545 { | |
546 int throttle = theApp.throttle; | |
547 | |
548 if (throttle < 6) | |
549 ++throttle; | |
550 else if (throttle < s_stockThrottleValues[_countof(s_stockThrottleValues) - 1]) | |
551 { | |
552 int i = 0; | |
553 while (throttle >= s_stockThrottleValues[i]) | |
554 { | |
555 ++i; | |
556 } | |
557 throttle = s_stockThrottleValues[i]; | |
558 } | |
559 | |
560 systemSetThrottle(throttle); | |
561 } | |
562 | |
563 void systemDecreaseThrottle() | |
564 { | |
565 int throttle = theApp.throttle; | |
566 | |
567 if (throttle > 6) | |
568 { | |
569 int i = _countof(s_stockThrottleValues) - 1; | |
570 while (throttle <= s_stockThrottleValues[i]) | |
571 { | |
572 --i; | |
573 } | |
574 throttle = s_stockThrottleValues[i]; | |
575 } | |
576 else if (throttle > 1) | |
577 --throttle; | |
578 | |
579 systemSetThrottle(throttle); | |
580 } | |
581 | |
582 void systemSetThrottle(int throttle) | |
583 { | |
584 theApp.throttle = throttle; | |
585 char str[256]; | |
586 sprintf(str, "%d%% throttle speed", theApp.throttle); | |
587 systemScreenMessage(str); | |
588 } | |
589 | |
590 int systemGetThrottle() | |
591 { | |
592 return theApp.throttle; | |
593 } | |
594 | |
595 void systemFrame() | |
596 { | |
597 if (theApp.altAviRecordMethod && theApp.aviRecording) | |
598 { | |
599 if (theApp.aviRecorder) | |
600 { | |
601 if (!theApp.aviRecorder->IsSoundAdded()) | |
602 { | |
603 WAVEFORMATEX wfx; | |
604 memset(&wfx, 0, sizeof(wfx)); | |
605 wfx.wFormatTag = WAVE_FORMAT_PCM; | |
606 wfx.nChannels = 2; | |
607 wfx.nSamplesPerSec = 44100 / soundQuality; | |
608 wfx.wBitsPerSample = 16; | |
609 wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels; | |
610 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; | |
611 wfx.cbSize = 0; | |
612 theApp.aviRecorder->SetSoundFormat(&wfx); | |
613 } | |
614 theApp.aviRecorder->AddSound((u8 *)soundFrameSound, soundFrameSoundWritten * 2); | |
615 } | |
616 } | |
617 | |
618 soundFrameSoundWritten = 0; | |
619 | |
620 // no more stupid updates :) | |
621 | |
622 extern int quitAfterTime; // from VBA.cpp | |
623 void VBAMovieStop(bool8 suppress_message); // from ../movie.cpp | |
624 if (quitAfterTime >= 0 && systemCounters.frameCount == quitAfterTime) | |
625 { | |
626 VBAMovieStop(true); | |
627 AfxPostQuitMessage(0); | |
628 } | |
629 | |
630 // change the sound speed, or set it to normal - must always do this or it won't get reset after a change, but that's OK | |
631 // because it's inexpensive | |
632 if (theApp.sound) | |
633 { | |
634 theApp.sound->setSpeed( | |
635 speedup || theApp.winPauseNextFrame || !synchronize || theApp.accuratePitchThrottle || theApp.useOldSync | |
636 ? 1.0f : (float)theApp.throttle / 100.0f); | |
637 } | |
638 | |
639 // if a throttle speed is set and we're not allowed to change the sound frequency to achieve it, | |
640 // sleep for a certain amount each time we get here to approximate the necessary slowdown | |
641 if (synchronize && (theApp.accuratePitchThrottle || !theApp.sound || theApp.throttle < 6) /*&& !theApp.winPauseNextFrame*/) | |
642 { | |
643 /// FIXME: this is still a horrible way of achieving a certain frame time | |
644 /// (look at what Snes9x does - it's complicated but much much better) | |
645 | |
646 static float sleepAmt = 0.0f; // variable to smooth out the sleeping amount so it doesn't oscillate so fast | |
647 // if(!theApp.wasPaused) { | |
648 if (!speedup) | |
649 { | |
650 u32 time = systemGetClock(); | |
651 u32 diff = time - theApp.throttleLastTime; | |
652 if (theApp.wasPaused) | |
653 diff = 0; | |
654 | |
655 int target = (100000 / (60 * theApp.throttle)); | |
656 int d = (target - diff); | |
657 | |
658 if (d > 1000) // added to avoid 500-day waits for vba to start emulating. | |
659 d = 1000; // I suspect most users aren't that patient, and would find 1 second to be a more reasonable delay. | |
660 | |
661 sleepAmt = 0.8f * sleepAmt + 0.2f * (float)d; | |
662 if (d - sleepAmt <= 1.5f && d - sleepAmt >= -1.5f) | |
663 d = (int)(sleepAmt); | |
664 | |
665 if (d > 0) | |
666 { | |
667 Sleep(d); | |
668 } | |
669 } | |
670 theApp.throttleLastTime = systemGetClock(); | |
671 //} | |
672 //else | |
673 //{ | |
674 // Sleep(100); | |
675 //} | |
676 } | |
677 | |
678 if (systemCounters.frameCount % 10 == 0) | |
679 { | |
680 if (theApp.rewindMemory) | |
681 { | |
682 if (++theApp.rewindCounter >= (theApp.rewindTimer)) | |
683 { | |
684 theApp.rewindSaveNeeded = true; | |
685 theApp.rewindCounter = 0; | |
686 } | |
687 } | |
688 if (systemSaveUpdateCounter) | |
689 { | |
690 if (--systemSaveUpdateCounter <= SYSTEM_SAVE_NOT_UPDATED) | |
691 { | |
692 winWriteBatteryFile(); | |
693 systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; | |
694 } | |
695 } | |
696 } | |
697 | |
698 theApp.wasPaused = false; | |
699 /// theApp.autoFrameSkipLastTime = time; | |
700 } | |
701 | |
702 int systemFramesToSkip() | |
703 { | |
704 int framesToSkip = systemFrameSkip; | |
705 | |
706 bool fastForward = speedup; | |
707 | |
708 #if (defined(WIN32) && !defined(SDL)) | |
709 fastForward = (fastForward || theApp.frameSearchSkipping); | |
710 int throttle = theApp.throttle; | |
711 if (theApp.frameSearching && throttle < 100) | |
712 throttle = 100; | |
713 #else | |
714 extern int throttle; | |
715 #endif | |
716 | |
717 #if (defined(WIN32) && !defined(SDL)) | |
718 if (theApp.aviRecording || theApp.nvVideoLog) | |
719 { | |
720 framesToSkip = 0; // render all frames | |
721 } | |
722 else | |
723 { | |
724 if (fastForward) | |
725 framesToSkip = 9; // try 6 FPS during speedup | |
726 else if (throttle != 100) | |
727 framesToSkip = (framesToSkip * throttle) / 100; | |
728 } | |
729 #endif | |
730 | |
731 return framesToSkip; | |
732 } | |
733 | |
734 // sound | |
735 | |
736 bool systemSoundInit() | |
737 { | |
738 if (theApp.sound) | |
739 delete theApp.sound; | |
740 | |
741 extern ISound *newDirectSound(); | |
742 theApp.sound = newDirectSound(); | |
743 return theApp.sound->init(); | |
744 } | |
745 | |
746 void systemSoundShutdown() | |
747 { | |
748 if (theApp.sound) | |
749 delete theApp.sound; | |
750 theApp.sound = NULL; | |
751 } | |
752 | |
753 void systemSoundPause() | |
754 { | |
755 if (theApp.sound) | |
756 theApp.sound->pause(); | |
757 soundPaused = 1; | |
758 } | |
759 | |
760 void systemSoundResume() | |
761 { | |
762 if (theApp.sound) | |
763 theApp.sound->resume(); | |
764 soundPaused = 0; | |
765 } | |
766 | |
767 bool systemSoundIsPaused() | |
768 { | |
769 // return soundPaused; | |
770 return !(theApp.sound && theApp.sound->isPlaying()); | |
771 } | |
772 | |
773 void systemSoundClearBuffer() | |
774 { | |
775 if (theApp.sound) | |
776 theApp.sound->clearAudioBuffer(); | |
777 } | |
778 | |
779 void systemSoundReset() | |
780 { | |
781 if (theApp.sound) | |
782 theApp.sound->reset(); | |
783 } | |
784 | |
785 void systemSoundWriteToBuffer() | |
786 { | |
787 if (theApp.sound) | |
788 theApp.sound->write(); | |
789 } | |
790 | |
791 bool systemSoundCanChangeQuality() | |
792 { | |
793 return true; | |
794 } | |
795 | |
796 bool systemSoundSetQuality(int quality) | |
797 { | |
798 if (systemCartridgeType == 0) | |
799 soundSetQuality(quality); | |
800 else | |
801 gbSoundSetQuality(quality); | |
802 | |
803 return true; | |
804 } | |
805 | |
806 // emulation | |
807 | |
808 bool systemIsEmulating() | |
809 { | |
810 return emulating != 0; | |
811 } | |
812 | |
813 void systemGbBorderOn() | |
814 { | |
815 if (vbaShuttingDown) | |
816 return; | |
817 | |
818 if (emulating && systemCartridgeType == 1) | |
819 { | |
820 theApp.updateWindowSize(theApp.videoOption); | |
821 } | |
822 } | |
823 | |
824 bool systemIsRunningGBA() | |
825 { | |
826 return (systemCartridgeType == 0); | |
827 } | |
828 | |
829 bool systemIsSpedUp() | |
830 { | |
831 return theApp.speedupToggle; | |
832 } | |
833 | |
834 bool systemIsPaused() | |
835 { | |
836 return theApp.paused; | |
837 } | |
838 | |
839 void systemSetPause(bool pause) | |
840 { | |
841 if (pause) | |
842 { | |
843 capturePrevious = false; | |
844 theApp.wasPaused = true; | |
845 theApp.paused = true; | |
846 theApp.speedupToggle = false; | |
847 theApp.winPauseNextFrame = false; | |
848 systemSoundPause(); | |
849 systemRefreshScreen();; | |
850 } | |
851 else | |
852 { | |
853 theApp.paused = false; | |
854 systemSoundResume(); | |
855 } | |
856 } | |
857 | |
858 // aka. frame advance | |
859 bool systemPauseOnFrame() | |
860 { | |
861 if (theApp.winPauseNextFrame) | |
862 { | |
863 if (!theApp.nextframeAccountForLag || !systemCounters.laggedLast) | |
864 { | |
865 theApp.winPauseNextFrame = false; | |
866 return true; | |
867 } | |
868 } | |
869 | |
870 return false; | |
871 } | |
872 | |
873 bool systemLoadBIOS(const char *biosFileName, bool useBiosFile) | |
874 { | |
875 bool use = false; | |
876 if (systemCartridgeType == 0) | |
877 use = CPULoadBios(biosFileName, useBiosFile); | |
878 else | |
879 use = false; | |
880 return use; | |
881 } | |
882 | |
883 // FIXME: now platform-independant stuff | |
884 // it should be admitted that the naming schema/code organization is a whole mess | |
885 // these things should be moved somewhere else | |
886 | |
887 EmulatedSystemCounters systemCounters = | |
888 { | |
889 // frameCount | |
890 0, | |
891 // lagCount | |
892 0, | |
893 // extraCount | |
894 0, | |
895 // lagged | |
896 true, | |
897 // laggedLast | |
898 true, | |
899 }; | |
900 | |
901 // VBAxyz stuff are not part of the core. | |
902 | |
903 void VBAOnEnteringFrameBoundary() | |
904 { | |
905 CallRegisteredLuaFunctions(LUACALL_AFTEREMULATION); | |
906 | |
907 if (VBALuaRunning()) | |
908 { | |
909 VBALuaFrameBoundary(); | |
910 } | |
911 | |
912 VBAMovieUpdateState(); | |
913 } | |
914 | |
915 void VBAOnExitingFrameBoundary() | |
916 { | |
917 ; | |
918 } | |
919 | |
920 ////////////////////////////////////////////// | |
921 // ultility | |
922 | |
923 extern void toolsLog(const char *); | |
924 | |
925 void log(const char *msg, ...) | |
926 { | |
927 CString buffer; | |
928 va_list valist; | |
929 | |
930 va_start(valist, msg); | |
931 buffer.FormatV(msg, valist); | |
932 | |
933 toolsLog(buffer); | |
934 | |
935 va_end(valist); | |
936 } |