rlm@1: // System.cpp : Defines the system behaviors for the emulator. rlm@1: // rlm@1: #include "stdafx.h" rlm@1: #include "Sound.h" rlm@1: #include "Input.h" rlm@1: #include "IUpdate.h" rlm@1: #include "ram_search.h" rlm@1: #include "WinMiscUtil.h" rlm@1: #include "WinResUtil.h" rlm@1: #include "resource.h" rlm@1: #include "VBA.h" rlm@1: #include "../gba/GBA.h" rlm@1: #include "../gba/GBAGlobals.h" rlm@1: #include "../gba/GBASound.h" rlm@1: #include "../gb/GB.h" rlm@1: #include "../gb/gbGlobals.h" rlm@1: //#include "../common/System.h" rlm@1: #include "../common/movie.h" rlm@1: #include "../common/vbalua.h" rlm@1: #include "../common/Text.h" rlm@1: #include "../common/Util.h" rlm@1: #include "../common/nesvideos-piece.h" rlm@1: #include "../version.h" rlm@1: #include rlm@1: rlm@1: struct EmulatedSystem theEmulator; rlm@1: rlm@1: u32 RGB_LOW_BITS_MASK = 0; rlm@1: int emulating = 0; rlm@1: int systemCartridgeType = 0; rlm@1: int systemSpeed = 0; rlm@1: bool systemSoundOn = false; rlm@1: u32 systemColorMap32[0x10000]; rlm@1: u16 systemColorMap16[0x10000]; rlm@1: u16 systemGbPalette[24]; rlm@1: int systemRedShift = 0; rlm@1: int systemBlueShift = 0; rlm@1: int systemGreenShift = 0; rlm@1: int systemColorDepth = 16; rlm@1: int systemDebug = 0; rlm@1: int systemVerbose = 0; rlm@1: int systemFrameSkip = 0; rlm@1: int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; rlm@1: rlm@1: const int32 INITIAL_SENSOR_VALUE = 2047; rlm@1: rlm@1: int32 sensorX = INITIAL_SENSOR_VALUE; rlm@1: int32 sensorY = INITIAL_SENSOR_VALUE; rlm@1: u16 currentButtons [4] = { 0, 0, 0, 0 }; // constrain: never contains hacked buttons, only the lower 16 bits of each are used rlm@1: u16 lastKeys = 0; rlm@1: rlm@1: // static_assertion that BUTTON_REGULAR_RECORDING_MASK should be an u16 constant rlm@1: namespace { const void * const s_STATIC_ASSERTION_(static_cast(BUTTON_REGULAR_RECORDING_MASK & 0xFFFF0000)); } rlm@1: rlm@1: #define BMP_BUFFER_MAX_WIDTH (256) rlm@1: #define BMP_BUFFER_MAX_HEIGHT (224) rlm@1: #define BMP_BUFFER_MAX_DEPTH (4) rlm@1: static u8 bmpBuffer[BMP_BUFFER_MAX_WIDTH * BMP_BUFFER_MAX_HEIGHT * BMP_BUFFER_MAX_DEPTH]; rlm@1: rlm@1: static int s_stockThrottleValues[] = { rlm@1: 6, 15, 25, 25, 37, 50, 75, 87, 100, 112, 125, 150, 200, 300, 400, 600, 800, 1000 rlm@1: }; rlm@1: rlm@1: // systemXYZ: Win32 stuff rlm@1: rlm@1: // input rlm@1: rlm@1: void systemSetSensorX(int32 x) rlm@1: { rlm@1: sensorX = x; rlm@1: } rlm@1: rlm@1: void systemSetSensorY(int32 y) rlm@1: { rlm@1: sensorY = y; rlm@1: } rlm@1: rlm@1: void systemResetSensor() rlm@1: { rlm@1: sensorX = sensorY = INITIAL_SENSOR_VALUE; rlm@1: } rlm@1: rlm@1: int32 systemGetSensorX() rlm@1: { rlm@1: return sensorX; rlm@1: } rlm@1: rlm@1: int32 systemGetSensorY() rlm@1: { rlm@1: return sensorY; rlm@1: } rlm@1: rlm@1: // handles motion sensor input rlm@1: void systemUpdateMotionSensor(int i) rlm@1: { rlm@1: if (i < 0 || i > 3) rlm@1: i = 0; rlm@1: rlm@1: if (currentButtons[i] & BUTTON_MASK_LEFT_MOTION) rlm@1: { rlm@1: sensorX += 3; rlm@1: if (sensorX > 2197) rlm@1: sensorX = 2197; rlm@1: if (sensorX < 2047) rlm@1: sensorX = 2057; rlm@1: } rlm@1: else if (currentButtons[i] & BUTTON_MASK_RIGHT_MOTION) rlm@1: { rlm@1: sensorX -= 3; rlm@1: if (sensorX < 1897) rlm@1: sensorX = 1897; rlm@1: if (sensorX > 2047) rlm@1: sensorX = 2037; rlm@1: } rlm@1: else if (sensorX > 2047) rlm@1: { rlm@1: sensorX -= 2; rlm@1: if (sensorX < 2047) rlm@1: sensorX = 2047; rlm@1: } rlm@1: else rlm@1: { rlm@1: sensorX += 2; rlm@1: if (sensorX > 2047) rlm@1: sensorX = 2047; rlm@1: } rlm@1: rlm@1: if (currentButtons[i] & BUTTON_MASK_UP_MOTION) rlm@1: { rlm@1: sensorY += 3; rlm@1: if (sensorY > 2197) rlm@1: sensorY = 2197; rlm@1: if (sensorY < 2047) rlm@1: sensorY = 2057; rlm@1: } rlm@1: else if (currentButtons[i] & BUTTON_MASK_DOWN_MOTION) rlm@1: { rlm@1: sensorY -= 3; rlm@1: if (sensorY < 1897) rlm@1: sensorY = 1897; rlm@1: if (sensorY > 2047) rlm@1: sensorY = 2037; rlm@1: } rlm@1: else if (sensorY > 2047) rlm@1: { rlm@1: sensorY -= 2; rlm@1: if (sensorY < 2047) rlm@1: sensorY = 2047; rlm@1: } rlm@1: else rlm@1: { rlm@1: sensorY += 2; rlm@1: if (sensorY > 2047) rlm@1: sensorY = 2047; rlm@1: } rlm@1: } rlm@1: rlm@1: int systemGetDefaultJoypad() rlm@1: { rlm@1: return theApp.joypadDefault; rlm@1: } rlm@1: rlm@1: void systemSetDefaultJoypad(int which) rlm@1: { rlm@1: theApp.joypadDefault = which; rlm@1: } rlm@1: rlm@1: bool systemReadJoypads() rlm@1: { rlm@1: // this function is called at every frame, even if vba is fast-forwarded. rlm@1: // so we try to limit the input frequency here just in case. rlm@1: static u32 lastTime = systemGetClock(); rlm@1: if ((u32)(systemGetClock() - lastTime) < 10) rlm@1: return false; rlm@1: lastTime = systemGetClock(); rlm@1: rlm@1: if (theApp.input) rlm@1: return theApp.input->readDevices(); rlm@1: return false; rlm@1: } rlm@1: rlm@1: u32 systemGetOriginalJoypad(int i, bool sensor) rlm@1: { rlm@1: if (i < 0 || i > 3) rlm@1: i = 0; rlm@1: rlm@1: u32 res = 0; rlm@1: if (theApp.input) rlm@1: res = theApp.input->readDevice(i, sensor); rlm@1: rlm@1: // +auto input, XOR rlm@1: // maybe these should be moved into DirectInput.cpp rlm@1: if (theApp.autoFire || theApp.autoFire2) rlm@1: { rlm@1: res ^= (theApp.autoFireToggle ? theApp.autoFire : theApp.autoFire2); rlm@1: if (!theApp.autofireAccountForLag || !systemCounters.laggedLast) rlm@1: { rlm@1: theApp.autoFireToggle = !theApp.autoFireToggle; rlm@1: } rlm@1: } rlm@1: if (theApp.autoHold) rlm@1: { rlm@1: res ^= theApp.autoHold; rlm@1: } rlm@1: rlm@1: // filter buttons rlm@1: // maybe better elsewhere? rlm@1: if (!theApp.allowLeftRight) rlm@1: { rlm@1: // disallow L+R or U+D to being pressed at the same time rlm@1: if ((res & (BUTTON_MASK_RIGHT | BUTTON_MASK_LEFT)) == (BUTTON_MASK_RIGHT | BUTTON_MASK_LEFT)) rlm@1: res &= ~BUTTON_MASK_RIGHT; // leave only LEFT on rlm@1: if ((res & (BUTTON_MASK_DOWN | BUTTON_MASK_UP)) == (BUTTON_MASK_DOWN | BUTTON_MASK_UP)) rlm@1: res &= ~BUTTON_MASK_DOWN; // leave only UP on rlm@1: } rlm@1: rlm@1: if (!sensor) rlm@1: { rlm@1: if (res & BUTTON_MOTION_MASK) rlm@1: res &= ~BUTTON_MOTION_MASK; rlm@1: } rlm@1: rlm@1: if (systemCartridgeType != 0 && !gbSgbMode) // regular GB has no L/R buttons rlm@1: { rlm@1: if (res & (BUTTON_GBA_ONLY)) rlm@1: res &= ~BUTTON_GBA_ONLY; rlm@1: } rlm@1: rlm@1: currentButtons[i] = res & BUTTON_REGULAR_RECORDING_MASK; rlm@1: rlm@1: return res; rlm@1: } rlm@1: rlm@1: u32 systemGetJoypad(int i, bool sensor) rlm@1: { rlm@1: if (i < 0 || i > 3) rlm@1: i = 0; rlm@1: rlm@1: // input priority: original+auto < Lua < frame search < movie, correct this if wrong rlm@1: rlm@1: // get original+auto input rlm@1: u32 hackedButtons = systemGetOriginalJoypad(i, sensor) & BUTTON_NONRECORDINGONLY_MASK; rlm@1: u32 res = currentButtons[i]; rlm@1: rlm@1: // since movie input has the highest priority, there's no point to read from other input rlm@1: if (VBAMoviePlaying()) rlm@1: { rlm@1: // VBAMovieRead() overwrites currentButtons[i] rlm@1: VBAMovieRead(i, sensor); rlm@1: res = currentButtons[i]; rlm@1: } rlm@1: else rlm@1: { rlm@1: // Lua input, shouldn't have any side effect within them rlm@1: if (VBALuaUsingJoypad(i)) rlm@1: res = VBALuaReadJoypad(i); rlm@1: rlm@1: // override input above rlm@1: if (theApp.frameSearchSkipping) rlm@1: res = theApp.frameSearchOldInput[i]; rlm@1: rlm@1: // flush non-hack buttons into the "current buttons" input buffer, which will be read by the movie routine rlm@1: currentButtons[i] = res & BUTTON_REGULAR_RECORDING_MASK; rlm@1: VBAMovieWrite(i, sensor); rlm@1: } rlm@1: rlm@1: return res | hackedButtons; rlm@1: } rlm@1: rlm@1: void systemSetJoypad(int which, u32 buttons) rlm@1: { rlm@1: if (which < 0 || which > 3) rlm@1: which = 0; rlm@1: rlm@1: currentButtons[which] = buttons; rlm@1: rlm@1: lastKeys = 0; rlm@1: } rlm@1: rlm@1: void systemClearJoypads() rlm@1: { rlm@1: for (int i = 0; i < 3; ++i) rlm@1: currentButtons[i] = 0; rlm@1: rlm@1: lastKeys = 0; rlm@1: } rlm@1: rlm@1: // screen rlm@1: rlm@1: // delayed repaint rlm@1: void systemRefreshScreen() rlm@1: { rlm@1: if (theApp.m_pMainWnd) rlm@1: { rlm@1: theApp.m_pMainWnd->PostMessage(WM_PAINT, NULL, NULL); rlm@1: } rlm@1: } rlm@1: rlm@1: extern bool vbaShuttingDown; rlm@1: rlm@1: void systemRenderFrame() rlm@1: { rlm@1: extern long linearSoundFrameCount; rlm@1: extern long linearFrameCount; rlm@1: rlm@1: if (vbaShuttingDown) rlm@1: return; rlm@1: rlm@1: ++theApp.renderedFrames; rlm@1: rlm@1: VBAUpdateFrameCountDisplay(); rlm@1: VBAUpdateButtonPressDisplay(); rlm@1: rlm@1: // "in-game" text rendering rlm@1: if (textMethod == 0) // transparent text can only be painted once, so timed messages will not be updated rlm@1: { rlm@1: extern void DrawLuaGui(); rlm@1: DrawLuaGui(); rlm@1: rlm@1: int copyX = 240, copyY = 160; rlm@1: if (systemCartridgeType == 1) rlm@1: if (gbBorderOn) rlm@1: copyX = 256, copyY = 224; rlm@1: else rlm@1: copyX = 160, copyY = 144; rlm@1: int pitch = copyX * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); // FIXME: sure? rlm@1: rlm@1: DrawTextMessages((u8 *)pix, pitch, 0, copyY); rlm@1: } rlm@1: rlm@1: ++linearFrameCount; rlm@1: if (!theApp.sound) rlm@1: { rlm@1: if (linearFrameCount > 10000) rlm@1: linearFrameCount -= 10000; rlm@1: linearSoundFrameCount = linearFrameCount; rlm@1: } rlm@1: rlm@1: // record avi rlm@1: int width = 240; rlm@1: int height = 160; rlm@1: switch (systemCartridgeType) rlm@1: { rlm@1: case 0: rlm@1: width = 240; rlm@1: height = 160; rlm@1: break; rlm@1: case 1: rlm@1: if (gbBorderOn) rlm@1: { rlm@1: width = 256; rlm@1: height = 224; rlm@1: } rlm@1: else rlm@1: { rlm@1: width = 160; rlm@1: height = 144; rlm@1: } rlm@1: break; rlm@1: } rlm@1: rlm@1: bool firstFrameLogged = false; rlm@1: --linearFrameCount; rlm@1: do rlm@1: { rlm@1: ++linearFrameCount; rlm@1: rlm@1: if (theApp.aviRecording && (!theApp.altAviRecordMethod || (theApp.altAviRecordMethod && !firstFrameLogged))) rlm@1: { rlm@1: // usually aviRecorder is created when vba starts avi recording, though rlm@1: if (theApp.aviRecorder == NULL) rlm@1: { rlm@1: theApp.aviRecorder = new AVIWrite(); rlm@1: rlm@1: theApp.aviRecorder->SetFPS(60); rlm@1: rlm@1: BITMAPINFOHEADER bi; rlm@1: memset(&bi, 0, sizeof(bi)); rlm@1: bi.biSize = 0x28; rlm@1: bi.biPlanes = 1; rlm@1: bi.biBitCount = 24; rlm@1: bi.biWidth = width; rlm@1: bi.biHeight = height; rlm@1: bi.biSizeImage = 3 * width * height; rlm@1: theApp.aviRecorder->SetVideoFormat(&bi); rlm@1: if (!theApp.aviRecorder->Open(theApp.aviRecordName)) rlm@1: { rlm@1: delete theApp.aviRecorder; rlm@1: theApp.aviRecorder = NULL; rlm@1: theApp.aviRecording = false; rlm@1: } rlm@1: } rlm@1: rlm@1: if (theApp.aviRecorder != NULL && !theApp.aviRecorder->IsPaused()) rlm@1: { rlm@1: assert( rlm@1: width <= BMP_BUFFER_MAX_WIDTH && height <= BMP_BUFFER_MAX_HEIGHT && systemColorDepth <= rlm@1: BMP_BUFFER_MAX_DEPTH * 8); rlm@1: utilWriteBMP(bmpBuffer, width, height, systemColorDepth, pix); rlm@1: theApp.aviRecorder->AddFrame(bmpBuffer); rlm@1: } rlm@1: } rlm@1: rlm@1: if (theApp.nvVideoLog) rlm@1: { rlm@1: // convert from whatever bit depth to 16-bit, while stripping away extra pixels rlm@1: assert(width <= BMP_BUFFER_MAX_WIDTH && height <= BMP_BUFFER_MAX_HEIGHT && 16 <= BMP_BUFFER_MAX_DEPTH * 8); rlm@1: utilWriteBMP(bmpBuffer, width, -height, 16, pix); rlm@1: NESVideoLoggingVideo((u8 *)bmpBuffer, width, height, 0x1000000 * 60); rlm@1: } rlm@1: rlm@1: firstFrameLogged = true; rlm@1: } rlm@1: while (linearFrameCount < linearSoundFrameCount); // compensate for frames lost due to frame skip being nonzero, etc. rlm@1: rlm@1: if (textMethod != 0) // do not draw Lua HUD to a video dump rlm@1: { rlm@1: extern void DrawLuaGui(); rlm@1: DrawLuaGui(); rlm@1: } rlm@1: rlm@1: // interframe blending rlm@1: if (theApp.ifbFunction) rlm@1: { rlm@1: if (systemColorDepth == 16) rlm@1: theApp.ifbFunction(pix + theApp.filterWidth * 2 + 4, theApp.filterWidth * 2 + 4, rlm@1: theApp.filterWidth, theApp.filterHeight); rlm@1: else rlm@1: theApp.ifbFunction(pix + theApp.filterWidth * 4 + 4, theApp.filterWidth * 4 + 4, rlm@1: theApp.filterWidth, theApp.filterHeight); rlm@1: } rlm@1: rlm@1: systemRedrawScreen(); rlm@1: } rlm@1: rlm@1: void systemRedrawScreen() rlm@1: { rlm@1: if (vbaShuttingDown) rlm@1: return; rlm@1: rlm@1: if (theApp.display) rlm@1: theApp.display->render(); rlm@1: rlm@1: systemUpdateListeners(); rlm@1: } rlm@1: rlm@1: void systemUpdateListeners() rlm@1: { rlm@1: if (vbaShuttingDown) rlm@1: return; rlm@1: rlm@1: Update_RAM_Search(); // updates RAM search and RAM watch rlm@1: rlm@1: // update viewers etc. rlm@1: if (theApp.updateCount) rlm@1: { rlm@1: POSITION pos = theApp.updateList.GetHeadPosition(); rlm@1: while (pos) rlm@1: { rlm@1: IUpdateListener *up = theApp.updateList.GetNext(pos); rlm@1: if (up) rlm@1: up->update(); rlm@1: } rlm@1: } rlm@1: } rlm@1: rlm@1: int systemScreenCapture(int captureNumber) rlm@1: { rlm@1: return winScreenCapture(captureNumber); rlm@1: } rlm@1: rlm@1: void systemMessage(int number, const char *defaultMsg, ...) rlm@1: { rlm@1: CString buffer; rlm@1: va_list valist; rlm@1: CString msg = defaultMsg; rlm@1: if (number) rlm@1: msg = winResLoadString(number); rlm@1: rlm@1: va_start(valist, defaultMsg); rlm@1: buffer.FormatV(msg, valist); rlm@1: rlm@1: theApp.winCheckFullscreen(); rlm@1: systemSoundClearBuffer(); rlm@1: AfxGetApp()->m_pMainWnd->MessageBox(buffer, winResLoadString(IDS_ERROR), MB_OK | MB_ICONERROR); rlm@1: rlm@1: va_end(valist); rlm@1: } rlm@1: rlm@1: void systemScreenMessage(const char *msg, int slot, int duration, const char *colorList) rlm@1: { rlm@1: if (slot < 0 || slot > SCREEN_MESSAGE_SLOTS) rlm@1: return; rlm@1: rlm@1: theApp.screenMessage[slot] = true; rlm@1: theApp.screenMessageTime[slot] = GetTickCount(); rlm@1: theApp.screenMessageDuration[slot] = duration; rlm@1: theApp.screenMessageBuffer[slot] = msg; rlm@1: theApp.screenMessageColorBuffer[slot] = colorList ? colorList : ""; rlm@1: rlm@1: if (theApp.screenMessageBuffer[slot].GetLength() > 40) rlm@1: theApp.screenMessageBuffer[slot] = theApp.screenMessageBuffer[slot].Left(40); rlm@1: rlm@1: // update the display when a main slot message appears while the game is paused rlm@1: if (slot == 0 && (theApp.paused || (theApp.frameSearching))) rlm@1: systemRefreshScreen(); rlm@1: } rlm@1: rlm@1: void systemShowSpeed(int speed) rlm@1: { rlm@1: systemSpeed = speed; rlm@1: theApp.showRenderedFrames = theApp.renderedFrames; rlm@1: theApp.renderedFrames = 0; rlm@1: if (theApp.videoOption <= VIDEO_4X && theApp.showSpeed) rlm@1: { rlm@1: CString buffer; rlm@1: if (theApp.showSpeed == 1) rlm@1: buffer.Format(VBA_NAME_AND_VERSION " %3d%%", systemSpeed); rlm@1: else rlm@1: buffer.Format(VBA_NAME_AND_VERSION " %3d%% (%d fps | %d skipped)", rlm@1: systemSpeed, rlm@1: theApp.showRenderedFrames, rlm@1: systemFrameSkip); rlm@1: rlm@1: systemSetTitle(buffer); rlm@1: } rlm@1: } rlm@1: rlm@1: void systemSetTitle(const char *title) rlm@1: { rlm@1: if (theApp.m_pMainWnd != NULL) rlm@1: { rlm@1: AfxGetApp()->m_pMainWnd->SetWindowText(title); rlm@1: } rlm@1: } rlm@1: rlm@1: // timing/speed rlm@1: rlm@1: u32 systemGetClock() rlm@1: { rlm@1: return timeGetTime(); rlm@1: } rlm@1: rlm@1: void systemIncreaseThrottle() rlm@1: { rlm@1: int throttle = theApp.throttle; rlm@1: rlm@1: if (throttle < 6) rlm@1: ++throttle; rlm@1: else if (throttle < s_stockThrottleValues[_countof(s_stockThrottleValues) - 1]) rlm@1: { rlm@1: int i = 0; rlm@1: while (throttle >= s_stockThrottleValues[i]) rlm@1: { rlm@1: ++i; rlm@1: } rlm@1: throttle = s_stockThrottleValues[i]; rlm@1: } rlm@1: rlm@1: systemSetThrottle(throttle); rlm@1: } rlm@1: rlm@1: void systemDecreaseThrottle() rlm@1: { rlm@1: int throttle = theApp.throttle; rlm@1: rlm@1: if (throttle > 6) rlm@1: { rlm@1: int i = _countof(s_stockThrottleValues) - 1; rlm@1: while (throttle <= s_stockThrottleValues[i]) rlm@1: { rlm@1: --i; rlm@1: } rlm@1: throttle = s_stockThrottleValues[i]; rlm@1: } rlm@1: else if (throttle > 1) rlm@1: --throttle; rlm@1: rlm@1: systemSetThrottle(throttle); rlm@1: } rlm@1: rlm@1: void systemSetThrottle(int throttle) rlm@1: { rlm@1: theApp.throttle = throttle; rlm@1: char str[256]; rlm@1: sprintf(str, "%d%% throttle speed", theApp.throttle); rlm@1: systemScreenMessage(str); rlm@1: } rlm@1: rlm@1: int systemGetThrottle() rlm@1: { rlm@1: return theApp.throttle; rlm@1: } rlm@1: rlm@1: void systemFrame() rlm@1: { rlm@1: if (theApp.altAviRecordMethod && theApp.aviRecording) rlm@1: { rlm@1: if (theApp.aviRecorder) rlm@1: { rlm@1: if (!theApp.aviRecorder->IsSoundAdded()) rlm@1: { rlm@1: WAVEFORMATEX wfx; rlm@1: memset(&wfx, 0, sizeof(wfx)); rlm@1: wfx.wFormatTag = WAVE_FORMAT_PCM; rlm@1: wfx.nChannels = 2; rlm@1: wfx.nSamplesPerSec = 44100 / soundQuality; rlm@1: wfx.wBitsPerSample = 16; rlm@1: wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels; rlm@1: wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; rlm@1: wfx.cbSize = 0; rlm@1: theApp.aviRecorder->SetSoundFormat(&wfx); rlm@1: } rlm@1: theApp.aviRecorder->AddSound((u8 *)soundFrameSound, soundFrameSoundWritten * 2); rlm@1: } rlm@1: } rlm@1: rlm@1: soundFrameSoundWritten = 0; rlm@1: rlm@1: // no more stupid updates :) rlm@1: rlm@1: extern int quitAfterTime; // from VBA.cpp rlm@1: void VBAMovieStop(bool8 suppress_message); // from ../movie.cpp rlm@1: if (quitAfterTime >= 0 && systemCounters.frameCount == quitAfterTime) rlm@1: { rlm@1: VBAMovieStop(true); rlm@1: AfxPostQuitMessage(0); rlm@1: } rlm@1: rlm@1: // 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 rlm@1: // because it's inexpensive rlm@1: if (theApp.sound) rlm@1: { rlm@1: theApp.sound->setSpeed( rlm@1: speedup || theApp.winPauseNextFrame || !synchronize || theApp.accuratePitchThrottle || theApp.useOldSync rlm@1: ? 1.0f : (float)theApp.throttle / 100.0f); rlm@1: } rlm@1: rlm@1: // if a throttle speed is set and we're not allowed to change the sound frequency to achieve it, rlm@1: // sleep for a certain amount each time we get here to approximate the necessary slowdown rlm@1: if (synchronize && (theApp.accuratePitchThrottle || !theApp.sound || theApp.throttle < 6) /*&& !theApp.winPauseNextFrame*/) rlm@1: { rlm@1: /// FIXME: this is still a horrible way of achieving a certain frame time rlm@1: /// (look at what Snes9x does - it's complicated but much much better) rlm@1: rlm@1: static float sleepAmt = 0.0f; // variable to smooth out the sleeping amount so it doesn't oscillate so fast rlm@1: // if(!theApp.wasPaused) { rlm@1: if (!speedup) rlm@1: { rlm@1: u32 time = systemGetClock(); rlm@1: u32 diff = time - theApp.throttleLastTime; rlm@1: if (theApp.wasPaused) rlm@1: diff = 0; rlm@1: rlm@1: int target = (100000 / (60 * theApp.throttle)); rlm@1: int d = (target - diff); rlm@1: rlm@1: if (d > 1000) // added to avoid 500-day waits for vba to start emulating. rlm@1: d = 1000; // I suspect most users aren't that patient, and would find 1 second to be a more reasonable delay. rlm@1: rlm@1: sleepAmt = 0.8f * sleepAmt + 0.2f * (float)d; rlm@1: if (d - sleepAmt <= 1.5f && d - sleepAmt >= -1.5f) rlm@1: d = (int)(sleepAmt); rlm@1: rlm@1: if (d > 0) rlm@1: { rlm@1: Sleep(d); rlm@1: } rlm@1: } rlm@1: theApp.throttleLastTime = systemGetClock(); rlm@1: //} rlm@1: //else rlm@1: //{ rlm@1: // Sleep(100); rlm@1: //} rlm@1: } rlm@1: rlm@1: if (systemCounters.frameCount % 10 == 0) rlm@1: { rlm@1: if (theApp.rewindMemory) rlm@1: { rlm@1: if (++theApp.rewindCounter >= (theApp.rewindTimer)) rlm@1: { rlm@1: theApp.rewindSaveNeeded = true; rlm@1: theApp.rewindCounter = 0; rlm@1: } rlm@1: } rlm@1: if (systemSaveUpdateCounter) rlm@1: { rlm@1: if (--systemSaveUpdateCounter <= SYSTEM_SAVE_NOT_UPDATED) rlm@1: { rlm@1: winWriteBatteryFile(); rlm@1: systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; rlm@1: } rlm@1: } rlm@1: } rlm@1: rlm@1: theApp.wasPaused = false; rlm@1: /// theApp.autoFrameSkipLastTime = time; rlm@1: } rlm@1: rlm@1: int systemFramesToSkip() rlm@1: { rlm@1: int framesToSkip = systemFrameSkip; rlm@1: rlm@1: bool fastForward = speedup; rlm@1: rlm@1: #if (defined(WIN32) && !defined(SDL)) rlm@1: fastForward = (fastForward || theApp.frameSearchSkipping); rlm@1: int throttle = theApp.throttle; rlm@1: if (theApp.frameSearching && throttle < 100) rlm@1: throttle = 100; rlm@1: #else rlm@1: extern int throttle; rlm@1: #endif rlm@1: rlm@1: #if (defined(WIN32) && !defined(SDL)) rlm@1: if (theApp.aviRecording || theApp.nvVideoLog) rlm@1: { rlm@1: framesToSkip = 0; // render all frames rlm@1: } rlm@1: else rlm@1: { rlm@1: if (fastForward) rlm@1: framesToSkip = 9; // try 6 FPS during speedup rlm@1: else if (throttle != 100) rlm@1: framesToSkip = (framesToSkip * throttle) / 100; rlm@1: } rlm@1: #endif rlm@1: rlm@1: return framesToSkip; rlm@1: } rlm@1: rlm@1: // sound rlm@1: rlm@1: bool systemSoundInit() rlm@1: { rlm@1: if (theApp.sound) rlm@1: delete theApp.sound; rlm@1: rlm@1: extern ISound *newDirectSound(); rlm@1: theApp.sound = newDirectSound(); rlm@1: return theApp.sound->init(); rlm@1: } rlm@1: rlm@1: void systemSoundShutdown() rlm@1: { rlm@1: if (theApp.sound) rlm@1: delete theApp.sound; rlm@1: theApp.sound = NULL; rlm@1: } rlm@1: rlm@1: void systemSoundPause() rlm@1: { rlm@1: if (theApp.sound) rlm@1: theApp.sound->pause(); rlm@1: soundPaused = 1; rlm@1: } rlm@1: rlm@1: void systemSoundResume() rlm@1: { rlm@1: if (theApp.sound) rlm@1: theApp.sound->resume(); rlm@1: soundPaused = 0; rlm@1: } rlm@1: rlm@1: bool systemSoundIsPaused() rlm@1: { rlm@1: // return soundPaused; rlm@1: return !(theApp.sound && theApp.sound->isPlaying()); rlm@1: } rlm@1: rlm@1: void systemSoundClearBuffer() rlm@1: { rlm@1: if (theApp.sound) rlm@1: theApp.sound->clearAudioBuffer(); rlm@1: } rlm@1: rlm@1: void systemSoundReset() rlm@1: { rlm@1: if (theApp.sound) rlm@1: theApp.sound->reset(); rlm@1: } rlm@1: rlm@1: void systemSoundWriteToBuffer() rlm@1: { rlm@1: if (theApp.sound) rlm@1: theApp.sound->write(); rlm@1: } rlm@1: rlm@1: bool systemSoundCanChangeQuality() rlm@1: { rlm@1: return true; rlm@1: } rlm@1: rlm@1: bool systemSoundSetQuality(int quality) rlm@1: { rlm@1: if (systemCartridgeType == 0) rlm@1: soundSetQuality(quality); rlm@1: else rlm@1: gbSoundSetQuality(quality); rlm@1: rlm@1: return true; rlm@1: } rlm@1: rlm@1: // emulation rlm@1: rlm@1: bool systemIsEmulating() rlm@1: { rlm@1: return emulating != 0; rlm@1: } rlm@1: rlm@1: void systemGbBorderOn() rlm@1: { rlm@1: if (vbaShuttingDown) rlm@1: return; rlm@1: rlm@1: if (emulating && systemCartridgeType == 1) rlm@1: { rlm@1: theApp.updateWindowSize(theApp.videoOption); rlm@1: } rlm@1: } rlm@1: rlm@1: bool systemIsRunningGBA() rlm@1: { rlm@1: return (systemCartridgeType == 0); rlm@1: } rlm@1: rlm@1: bool systemIsSpedUp() rlm@1: { rlm@1: return theApp.speedupToggle; rlm@1: } rlm@1: rlm@1: bool systemIsPaused() rlm@1: { rlm@1: return theApp.paused; rlm@1: } rlm@1: rlm@1: void systemSetPause(bool pause) rlm@1: { rlm@1: if (pause) rlm@1: { rlm@1: capturePrevious = false; rlm@1: theApp.wasPaused = true; rlm@1: theApp.paused = true; rlm@1: theApp.speedupToggle = false; rlm@1: theApp.winPauseNextFrame = false; rlm@1: systemSoundPause(); rlm@1: systemRefreshScreen();; rlm@1: } rlm@1: else rlm@1: { rlm@1: theApp.paused = false; rlm@1: systemSoundResume(); rlm@1: } rlm@1: } rlm@1: rlm@1: // aka. frame advance rlm@1: bool systemPauseOnFrame() rlm@1: { rlm@1: if (theApp.winPauseNextFrame) rlm@1: { rlm@1: if (!theApp.nextframeAccountForLag || !systemCounters.laggedLast) rlm@1: { rlm@1: theApp.winPauseNextFrame = false; rlm@1: return true; rlm@1: } rlm@1: } rlm@1: rlm@1: return false; rlm@1: } rlm@1: rlm@1: bool systemLoadBIOS(const char *biosFileName, bool useBiosFile) rlm@1: { rlm@1: bool use = false; rlm@1: if (systemCartridgeType == 0) rlm@1: use = CPULoadBios(biosFileName, useBiosFile); rlm@1: else rlm@1: use = false; rlm@1: return use; rlm@1: } rlm@1: rlm@1: // FIXME: now platform-independant stuff rlm@1: // it should be admitted that the naming schema/code organization is a whole mess rlm@1: // these things should be moved somewhere else rlm@1: rlm@1: EmulatedSystemCounters systemCounters = rlm@1: { rlm@1: // frameCount rlm@1: 0, rlm@1: // lagCount rlm@1: 0, rlm@1: // extraCount rlm@1: 0, rlm@1: // lagged rlm@1: true, rlm@1: // laggedLast rlm@1: true, rlm@1: }; rlm@1: rlm@1: // VBAxyz stuff are not part of the core. rlm@1: rlm@1: void VBAOnEnteringFrameBoundary() rlm@1: { rlm@1: CallRegisteredLuaFunctions(LUACALL_AFTEREMULATION); rlm@1: rlm@1: if (VBALuaRunning()) rlm@1: { rlm@1: VBALuaFrameBoundary(); rlm@1: } rlm@1: rlm@1: VBAMovieUpdateState(); rlm@1: } rlm@1: rlm@1: void VBAOnExitingFrameBoundary() rlm@1: { rlm@1: ; rlm@1: } rlm@1: rlm@1: ////////////////////////////////////////////// rlm@1: // ultility rlm@1: rlm@1: extern void toolsLog(const char *); rlm@1: rlm@1: void log(const char *msg, ...) rlm@1: { rlm@1: CString buffer; rlm@1: va_list valist; rlm@1: rlm@1: va_start(valist, msg); rlm@1: buffer.FormatV(msg, valist); rlm@1: rlm@1: toolsLog(buffer); rlm@1: rlm@1: va_end(valist); rlm@1: }