// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. // Copyright (C) 1999-2003 Forgotten // Copyright (C) 2004 Forgotten and the VBA development team // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2, or(at your option) // any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include #include #include #include #include "AutoBuild.h" #include "SDL.h" #include "GBA.h" #include "agbprint.h" #include "Flash.h" #include "Port.h" #include "debugger.h" #include "RTC.h" #include "Sound.h" #include "Text.h" #include "unzip.h" #include "Util.h" #include "gb/GB.h" #include "gb/gbGlobals.h" #ifndef WIN32 # include # define GETCWD getcwd #else // WIN32 # include # define GETCWD _getcwd #endif // WIN32 #ifndef __GNUC__ # define HAVE_DECL_GETOPT 0 # define __STDC__ 1 # include "getopt.h" #else // ! __GNUC__ # define HAVE_DECL_GETOPT 1 # include "getopt.h" #endif // ! __GNUC__ #ifdef MMX extern "C" bool cpu_mmx; #endif extern bool soundEcho; extern bool soundLowPass; extern bool soundReverse; extern int Init_2xSaI(u32); extern void _2xSaI(u8*,u32,u8*,u8*,u32,int,int); extern void _2xSaI32(u8*,u32,u8*,u8*,u32,int,int); extern void Super2xSaI(u8*,u32,u8*,u8*,u32,int,int); extern void Super2xSaI32(u8*,u32,u8*,u8*,u32,int,int); extern void SuperEagle(u8*,u32,u8*,u8*,u32,int,int); extern void SuperEagle32(u8*,u32,u8*,u8*,u32,int,int); extern void Pixelate(u8*,u32,u8*,u8*,u32,int,int); extern void Pixelate32(u8*,u32,u8*,u8*,u32,int,int); extern void MotionBlur(u8*,u32,u8*,u8*,u32,int,int); extern void MotionBlur32(u8*,u32,u8*,u8*,u32,int,int); extern void AdMame2x(u8*,u32,u8*,u8*,u32,int,int); extern void AdMame2x32(u8*,u32,u8*,u8*,u32,int,int); extern void Simple2x(u8*,u32,u8*,u8*,u32,int,int); extern void Simple2x32(u8*,u32,u8*,u8*,u32,int,int); extern void Bilinear(u8*,u32,u8*,u8*,u32,int,int); extern void Bilinear32(u8*,u32,u8*,u8*,u32,int,int); extern void BilinearPlus(u8*,u32,u8*,u8*,u32,int,int); extern void BilinearPlus32(u8*,u32,u8*,u8*,u32,int,int); extern void Scanlines(u8*,u32,u8*,u8*,u32,int,int); extern void Scanlines32(u8*,u32,u8*,u8*,u32,int,int); extern void ScanlinesTV(u8*,u32,u8*,u8*,u32,int,int); extern void ScanlinesTV32(u8*,u32,u8*,u8*,u32,int,int); extern void hq2x(u8*,u32,u8*,u8*,u32,int,int); extern void hq2x32(u8*,u32,u8*,u8*,u32,int,int); extern void lq2x(u8*,u32,u8*,u8*,u32,int,int); extern void lq2x32(u8*,u32,u8*,u8*,u32,int,int); extern void SmartIB(u8*,u32,int,int); extern void SmartIB32(u8*,u32,int,int); extern void MotionBlurIB(u8*,u32,int,int); extern void MotionBlurIB32(u8*,u32,int,int); void Init_Overlay(SDL_Surface *surface, int overlaytype); void Quit_Overlay(void); void Draw_Overlay(SDL_Surface *surface, int size); extern void remoteInit(); extern void remoteCleanUp(); extern void remoteStubMain(); extern void remoteStubSignal(int,int); extern void remoteOutput(char *, u32); extern void remoteSetProtocol(int); extern void remoteSetPort(int); extern void debuggerOutput(char *, u32); extern void CPUUpdateRenderBuffers(bool); struct EmulatedSystem emulator = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, false, 0 }; SDL_Surface *surface = NULL; SDL_Overlay *overlay = NULL; SDL_Rect overlay_rect; int systemSpeed = 0; int systemRedShift = 0; int systemBlueShift = 0; int systemGreenShift = 0; int systemColorDepth = 0; int systemDebug = 0; int systemVerbose = 0; int systemFrameSkip = 0; int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; int srcPitch = 0; int srcWidth = 0; int srcHeight = 0; int destWidth = 0; int destHeight = 0; int sensorX = 2047; int sensorY = 2047; int filter = 0; u8 *delta = NULL; int sdlPrintUsage = 0; int disableMMX = 0; int cartridgeType = 3; int sizeOption = 0; int captureFormat = 0; int pauseWhenInactive = 0; int active = 1; int emulating = 0; int RGB_LOW_BITS_MASK=0x821; u32 systemColorMap32[0x10000]; u16 systemColorMap16[0x10000]; u16 systemGbPalette[24]; void (*filterFunction)(u8*,u32,u8*,u8*,u32,int,int) = NULL; void (*ifbFunction)(u8*,u32,int,int) = NULL; int ifbType = 0; char filename[2048]; char ipsname[2048]; char biosFileName[2048]; char captureDir[2048]; char saveDir[2048]; char batteryDir[2048]; static char *rewindMemory = NULL; static int rewindPos = 0; static int rewindTopPos = 0; static int rewindCounter = 0; static int rewindCount = 0; static bool rewindSaveNeeded = false; static int rewindTimer = 0; #define REWIND_SIZE 400000 #define _stricmp strcasecmp bool sdlButtons[4][12] = { { false, false, false, false, false, false, false, false, false, false, false, false }, { false, false, false, false, false, false, false, false, false, false, false, false }, { false, false, false, false, false, false, false, false, false, false, false, false }, { false, false, false, false, false, false, false, false, false, false, false, false } }; bool sdlMotionButtons[4] = { false, false, false, false }; int sdlNumDevices = 0; SDL_Joystick **sdlDevices = NULL; bool wasPaused = false; int autoFrameSkip = 0; int frameskipadjust = 0; int showRenderedFrames = 0; int renderedFrames = 0; int throttle = 0; u32 throttleLastTime = 0; u32 autoFrameSkipLastTime = 0; int showSpeed = 1; int showSpeedTransparent = 1; bool disableStatusMessages = false; bool paused = false; bool pauseNextFrame = false; bool debugger = false; bool debuggerStub = false; int fullscreen = 0; bool systemSoundOn = false; bool yuv = false; int yuvType = 0; bool removeIntros = false; int sdlFlashSize = 0; int sdlAutoIPS = 1; int sdlRtcEnable = 0; int sdlAgbPrint = 0; int sdlDefaultJoypad = 0; extern void debuggerSignal(int,int); void (*dbgMain)() = debuggerMain; void (*dbgSignal)(int,int) = debuggerSignal; void (*dbgOutput)(char *, u32) = debuggerOutput; int mouseCounter = 0; int autoFire = 0; bool autoFireToggle = false; bool screenMessage = false; char screenMessageBuffer[21]; u32 screenMessageTime = 0; SDL_cond *cond = NULL; SDL_mutex *mutex = NULL; u8 sdlBuffer[4096]; int sdlSoundLen = 0; char *arg0; #ifndef C_CORE u8 sdlStretcher[16384]; int sdlStretcherPos; #else void (*sdlStretcher)(u8 *, u8*) = NULL; #endif enum { KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_BUTTON_A, KEY_BUTTON_B, KEY_BUTTON_START, KEY_BUTTON_SELECT, KEY_BUTTON_L, KEY_BUTTON_R, KEY_BUTTON_SPEED, KEY_BUTTON_CAPTURE }; u16 joypad[4][12] = { { SDLK_LEFT, SDLK_RIGHT, SDLK_UP, SDLK_DOWN, SDLK_z, SDLK_x, SDLK_RETURN,SDLK_BACKSPACE, SDLK_a, SDLK_s, SDLK_SPACE, SDLK_F12 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; u16 defaultJoypad[12] = { SDLK_LEFT, SDLK_RIGHT, SDLK_UP, SDLK_DOWN, SDLK_z, SDLK_x, SDLK_RETURN,SDLK_BACKSPACE, SDLK_a, SDLK_s, SDLK_SPACE, SDLK_F12 }; u16 motion[4] = { SDLK_KP4, SDLK_KP6, SDLK_KP8, SDLK_KP2 }; u16 defaultMotion[4] = { SDLK_KP4, SDLK_KP6, SDLK_KP8, SDLK_KP2 }; struct option sdlOptions[] = { { "agb-print", no_argument, &sdlAgbPrint, 1 }, { "auto-frameskip", no_argument, &autoFrameSkip, 1 }, { "bios", required_argument, 0, 'b' }, { "config", required_argument, 0, 'c' }, { "debug", no_argument, 0, 'd' }, { "filter", required_argument, 0, 'f' }, { "filter-normal", no_argument, &filter, 0 }, { "filter-tv-mode", no_argument, &filter, 1 }, { "filter-2xsai", no_argument, &filter, 2 }, { "filter-super-2xsai", no_argument, &filter, 3 }, { "filter-super-eagle", no_argument, &filter, 4 }, { "filter-pixelate", no_argument, &filter, 5 }, { "filter-motion-blur", no_argument, &filter, 6 }, { "filter-advmame", no_argument, &filter, 7 }, { "filter-simple2x", no_argument, &filter, 8 }, { "filter-bilinear", no_argument, &filter, 9 }, { "filter-bilinear+", no_argument, &filter, 10 }, { "filter-scanlines", no_argument, &filter, 11 }, { "filter-hq2x", no_argument, &filter, 12 }, { "filter-lq2x", no_argument, &filter, 13 }, { "flash-size", required_argument, 0, 'S' }, { "flash-64k", no_argument, &sdlFlashSize, 0 }, { "flash-128k", no_argument, &sdlFlashSize, 1 }, { "frameskip", required_argument, 0, 's' }, { "fullscreen", no_argument, &fullscreen, 1 }, { "gdb", required_argument, 0, 'G' }, { "help", no_argument, &sdlPrintUsage, 1 }, { "ifb-none", no_argument, &ifbType, 0 }, { "ifb-motion-blur", no_argument, &ifbType, 1 }, { "ifb-smart", no_argument, &ifbType, 2 }, { "ips", required_argument, 0, 'i' }, { "no-agb-print", no_argument, &sdlAgbPrint, 0 }, { "no-auto-frameskip", no_argument, &autoFrameSkip, 0 }, { "no-debug", no_argument, 0, 'N' }, { "no-ips", no_argument, &sdlAutoIPS, 0 }, { "no-mmx", no_argument, &disableMMX, 1 }, { "no-pause-when-inactive", no_argument, &pauseWhenInactive, 0 }, { "no-rtc", no_argument, &sdlRtcEnable, 0 }, { "no-show-speed", no_argument, &showSpeed, 0 }, { "no-throttle", no_argument, &throttle, 0 }, { "pause-when-inactive", no_argument, &pauseWhenInactive, 1 }, { "profile", optional_argument, 0, 'p' }, { "rtc", no_argument, &sdlRtcEnable, 1 }, { "save-type", required_argument, 0, 't' }, { "save-auto", no_argument, &cpuSaveType, 0 }, { "save-eeprom", no_argument, &cpuSaveType, 1 }, { "save-sram", no_argument, &cpuSaveType, 2 }, { "save-flash", no_argument, &cpuSaveType, 3 }, { "save-sensor", no_argument, &cpuSaveType, 4 }, { "save-none", no_argument, &cpuSaveType, 5 }, { "show-speed-normal", no_argument, &showSpeed, 1 }, { "show-speed-detailed", no_argument, &showSpeed, 2 }, { "throttle", required_argument, 0, 'T' }, { "verbose", required_argument, 0, 'v' }, { "video-1x", no_argument, &sizeOption, 0 }, { "video-2x", no_argument, &sizeOption, 1 }, { "video-3x", no_argument, &sizeOption, 2 }, { "video-4x", no_argument, &sizeOption, 3 }, { "yuv", required_argument, 0, 'Y' }, { NULL, no_argument, NULL, 0 } }; extern bool CPUIsGBAImage(char *); extern bool gbIsGameboyRom(char *); #ifndef C_CORE #define SDL_LONG(val) \ *((u32 *)&sdlStretcher[sdlStretcherPos]) = val;\ sdlStretcherPos+=4; #define SDL_AND_EAX(val) \ sdlStretcher[sdlStretcherPos++] = 0x25;\ SDL_LONG(val); #define SDL_AND_EBX(val) \ sdlStretcher[sdlStretcherPos++] = 0x81;\ sdlStretcher[sdlStretcherPos++] = 0xe3;\ SDL_LONG(val); #define SDL_OR_EAX_EBX \ sdlStretcher[sdlStretcherPos++] = 0x09;\ sdlStretcher[sdlStretcherPos++] = 0xd8; #define SDL_LOADL_EBX \ sdlStretcher[sdlStretcherPos++] = 0x8b;\ sdlStretcher[sdlStretcherPos++] = 0x1f; #define SDL_LOADW \ sdlStretcher[sdlStretcherPos++] = 0x66;\ sdlStretcher[sdlStretcherPos++] = 0x8b;\ sdlStretcher[sdlStretcherPos++] = 0x06;\ sdlStretcher[sdlStretcherPos++] = 0x83;\ sdlStretcher[sdlStretcherPos++] = 0xc6;\ sdlStretcher[sdlStretcherPos++] = 0x02; #define SDL_LOADL \ sdlStretcher[sdlStretcherPos++] = 0x8b;\ sdlStretcher[sdlStretcherPos++] = 0x06;\ sdlStretcher[sdlStretcherPos++] = 0x83;\ sdlStretcher[sdlStretcherPos++] = 0xc6;\ sdlStretcher[sdlStretcherPos++] = 0x04; #define SDL_LOADL2 \ sdlStretcher[sdlStretcherPos++] = 0x8b;\ sdlStretcher[sdlStretcherPos++] = 0x06;\ sdlStretcher[sdlStretcherPos++] = 0x83;\ sdlStretcher[sdlStretcherPos++] = 0xc6;\ sdlStretcher[sdlStretcherPos++] = 0x03; #define SDL_STOREW \ sdlStretcher[sdlStretcherPos++] = 0x66;\ sdlStretcher[sdlStretcherPos++] = 0x89;\ sdlStretcher[sdlStretcherPos++] = 0x07;\ sdlStretcher[sdlStretcherPos++] = 0x83;\ sdlStretcher[sdlStretcherPos++] = 0xc7;\ sdlStretcher[sdlStretcherPos++] = 0x02; #define SDL_STOREL \ sdlStretcher[sdlStretcherPos++] = 0x89;\ sdlStretcher[sdlStretcherPos++] = 0x07;\ sdlStretcher[sdlStretcherPos++] = 0x83;\ sdlStretcher[sdlStretcherPos++] = 0xc7;\ sdlStretcher[sdlStretcherPos++] = 0x04; #define SDL_STOREL2 \ sdlStretcher[sdlStretcherPos++] = 0x89;\ sdlStretcher[sdlStretcherPos++] = 0x07;\ sdlStretcher[sdlStretcherPos++] = 0x83;\ sdlStretcher[sdlStretcherPos++] = 0xc7;\ sdlStretcher[sdlStretcherPos++] = 0x03; #define SDL_RET \ sdlStretcher[sdlStretcherPos++] = 0xc3; #define SDL_PUSH_EAX \ sdlStretcher[sdlStretcherPos++] = 0x50; #define SDL_PUSH_ECX \ sdlStretcher[sdlStretcherPos++] = 0x51; #define SDL_PUSH_EBX \ sdlStretcher[sdlStretcherPos++] = 0x53; #define SDL_PUSH_ESI \ sdlStretcher[sdlStretcherPos++] = 0x56; #define SDL_PUSH_EDI \ sdlStretcher[sdlStretcherPos++] = 0x57; #define SDL_POP_EAX \ sdlStretcher[sdlStretcherPos++] = 0x58; #define SDL_POP_ECX \ sdlStretcher[sdlStretcherPos++] = 0x59; #define SDL_POP_EBX \ sdlStretcher[sdlStretcherPos++] = 0x5b; #define SDL_POP_ESI \ sdlStretcher[sdlStretcherPos++] = 0x5e; #define SDL_POP_EDI \ sdlStretcher[sdlStretcherPos++] = 0x5f; #define SDL_MOV_ECX(val) \ sdlStretcher[sdlStretcherPos++] = 0xb9;\ SDL_LONG(val); #define SDL_REP_MOVSB \ sdlStretcher[sdlStretcherPos++] = 0xf3;\ sdlStretcher[sdlStretcherPos++] = 0xa4; #define SDL_REP_MOVSW \ sdlStretcher[sdlStretcherPos++] = 0xf3;\ sdlStretcher[sdlStretcherPos++] = 0x66;\ sdlStretcher[sdlStretcherPos++] = 0xa5; #define SDL_REP_MOVSL \ sdlStretcher[sdlStretcherPos++] = 0xf3;\ sdlStretcher[sdlStretcherPos++] = 0xa5; void sdlMakeStretcher(int width) { sdlStretcherPos = 0; switch(systemColorDepth) { case 16: if(sizeOption) { SDL_PUSH_EAX; SDL_PUSH_ESI; SDL_PUSH_EDI; for(int i = 0; i < width; i++) { SDL_LOADW; SDL_STOREW; SDL_STOREW; if(sizeOption > 1) { SDL_STOREW; } if(sizeOption > 2) { SDL_STOREW; } } SDL_POP_EDI; SDL_POP_ESI; SDL_POP_EAX; SDL_RET; } else { SDL_PUSH_ESI; SDL_PUSH_EDI; SDL_PUSH_ECX; SDL_MOV_ECX(width); SDL_REP_MOVSW; SDL_POP_ECX; SDL_POP_EDI; SDL_POP_ESI; SDL_RET; } break; case 24: if(sizeOption) { SDL_PUSH_EAX; SDL_PUSH_ESI; SDL_PUSH_EDI; int w = width - 1; for(int i = 0; i < w; i++) { SDL_LOADL2; SDL_STOREL2; SDL_STOREL2; if(sizeOption > 1) { SDL_STOREL2; } if(sizeOption > 2) { SDL_STOREL2; } } // need to write the last one SDL_LOADL2; SDL_STOREL2; if(sizeOption > 1) { SDL_STOREL2; } if(sizeOption > 2) { SDL_STOREL2; } SDL_AND_EAX(0x00ffffff); SDL_PUSH_EBX; SDL_LOADL_EBX; SDL_AND_EBX(0xff000000); SDL_OR_EAX_EBX; SDL_POP_EBX; SDL_STOREL2; SDL_POP_EDI; SDL_POP_ESI; SDL_POP_EAX; SDL_RET; } else { SDL_PUSH_ESI; SDL_PUSH_EDI; SDL_PUSH_ECX; SDL_MOV_ECX(3*width); SDL_REP_MOVSB; SDL_POP_ECX; SDL_POP_EDI; SDL_POP_ESI; SDL_RET; } break; case 32: if(sizeOption) { SDL_PUSH_EAX; SDL_PUSH_ESI; SDL_PUSH_EDI; for(int i = 0; i < width; i++) { SDL_LOADL; SDL_STOREL; SDL_STOREL; if(sizeOption > 1) { SDL_STOREL; } if(sizeOption > 2) { SDL_STOREL; } } SDL_POP_EDI; SDL_POP_ESI; SDL_POP_EAX; SDL_RET; } else { SDL_PUSH_ESI; SDL_PUSH_EDI; SDL_PUSH_ECX; SDL_MOV_ECX(width); SDL_REP_MOVSL; SDL_POP_ECX; SDL_POP_EDI; SDL_POP_ESI; SDL_RET; } break; } } #ifdef _MSC_VER #define SDL_CALL_STRETCHER \ {\ __asm mov eax, stretcher\ __asm mov edi, dest\ __asm mov esi, src\ __asm call eax\ } #else #define SDL_CALL_STRETCHER \ asm volatile("call *%%eax"::"a" (stretcher),"S" (src),"D" (dest)) #endif #else #define SDL_CALL_STRETCHER \ sdlStretcher(src, dest) void sdlStretch16x1(u8 *src, u8 *dest) { u16 *s = (u16 *)src; u16 *d = (u16 *)dest; for(int i = 0; i < srcWidth; i++) *d++ = *s++; } void sdlStretch16x2(u8 *src, u8 *dest) { u16 *s = (u16 *)src; u16 *d = (u16 *)dest; for(int i = 0; i < srcWidth; i++) { *d++ = *s; *d++ = *s++; } } void sdlStretch16x3(u8 *src, u8 *dest) { u16 *s = (u16 *)src; u16 *d = (u16 *)dest; for(int i = 0; i < srcWidth; i++) { *d++ = *s; *d++ = *s; *d++ = *s++; } } void sdlStretch16x4(u8 *src, u8 *dest) { u16 *s = (u16 *)src; u16 *d = (u16 *)dest; for(int i = 0; i < srcWidth; i++) { *d++ = *s; *d++ = *s; *d++ = *s; *d++ = *s++; } } void (*sdlStretcher16[4])(u8 *, u8 *) = { sdlStretch16x1, sdlStretch16x2, sdlStretch16x3, sdlStretch16x4 }; void sdlStretch32x1(u8 *src, u8 *dest) { u32 *s = (u32 *)src; u32 *d = (u32 *)dest; for(int i = 0; i < srcWidth; i++) *d++ = *s++; } void sdlStretch32x2(u8 *src, u8 *dest) { u32 *s = (u32 *)src; u32 *d = (u32 *)dest; for(int i = 0; i < srcWidth; i++) { *d++ = *s; *d++ = *s++; } } void sdlStretch32x3(u8 *src, u8 *dest) { u32 *s = (u32 *)src; u32 *d = (u32 *)dest; for(int i = 0; i < srcWidth; i++) { *d++ = *s; *d++ = *s; *d++ = *s++; } } void sdlStretch32x4(u8 *src, u8 *dest) { u32 *s = (u32 *)src; u32 *d = (u32 *)dest; for(int i = 0; i < srcWidth; i++) { *d++ = *s; *d++ = *s; *d++ = *s; *d++ = *s++; } } void (*sdlStretcher32[4])(u8 *, u8 *) = { sdlStretch32x1, sdlStretch32x2, sdlStretch32x3, sdlStretch32x4 }; void sdlStretch24x1(u8 *src, u8 *dest) { u8 *s = src; u8 *d = dest; for(int i = 0; i < srcWidth; i++) { *d++ = *s++; *d++ = *s++; *d++ = *s++; } } void sdlStretch24x2(u8 *src, u8 *dest) { u8 *s = (u8 *)src; u8 *d = (u8 *)dest; for(int i = 0; i < srcWidth; i++) { *d++ = *s; *d++ = *(s+1); *d++ = *(s+2); s += 3; *d++ = *s; *d++ = *(s+1); *d++ = *(s+2); s += 3; } } void sdlStretch24x3(u8 *src, u8 *dest) { u8 *s = (u8 *)src; u8 *d = (u8 *)dest; for(int i = 0; i < srcWidth; i++) { *d++ = *s; *d++ = *(s+1); *d++ = *(s+2); s += 3; *d++ = *s; *d++ = *(s+1); *d++ = *(s+2); s += 3; *d++ = *s; *d++ = *(s+1); *d++ = *(s+2); s += 3; } } void sdlStretch24x4(u8 *src, u8 *dest) { u8 *s = (u8 *)src; u8 *d = (u8 *)dest; for(int i = 0; i < srcWidth; i++) { *d++ = *s; *d++ = *(s+1); *d++ = *(s+2); s += 3; *d++ = *s; *d++ = *(s+1); *d++ = *(s+2); s += 3; *d++ = *s; *d++ = *(s+1); *d++ = *(s+2); s += 3; *d++ = *s; *d++ = *(s+1); *d++ = *(s+2); s += 3; } } void (*sdlStretcher24[4])(u8 *, u8 *) = { sdlStretch24x1, sdlStretch24x2, sdlStretch24x3, sdlStretch24x4 }; #endif u32 sdlFromHex(char *s) { u32 value; sscanf(s, "%x", &value); return value; } #ifdef __MSC__ #define stat _stat #define S_IFDIR _S_IFDIR #endif void sdlCheckDirectory(char *dir) { struct stat buf; int len = strlen(dir); char *p = dir + len - 1; if(*p == '/' || *p == '\\') *p = 0; if(stat(dir, &buf) == 0) { if(!(buf.st_mode & S_IFDIR)) { fprintf(stderr, "Error: %s is not a directory\n", dir); dir[0] = 0; } } else { fprintf(stderr, "Error: %s does not exist\n", dir); dir[0] = 0; } } char *sdlGetFilename(char *name) { static char filebuffer[2048]; int len = strlen(name); char *p = name + len - 1; while(true) { if(*p == '/' || *p == '\\') { p++; break; } len--; p--; if(len == 0) break; } if(len == 0) strcpy(filebuffer, name); else strcpy(filebuffer, p); return filebuffer; } FILE *sdlFindFile(const char *name) { char buffer[4096]; char path[2048]; #ifdef WIN32 #define PATH_SEP ";" #define FILE_SEP '\\' #define EXE_NAME "VisualBoyAdvance-SDL.exe" #else // ! WIN32 #define PATH_SEP ":" #define FILE_SEP '/' #define EXE_NAME "VisualBoyAdvance" #endif // ! WIN32 fprintf(stderr, "Searching for file %s\n", name); if(GETCWD(buffer, 2048)) { fprintf(stderr, "Searching current directory: %s\n", buffer); } FILE *f = fopen(name, "r"); if(f != NULL) { return f; } char *home = getenv("HOME"); if(home != NULL) { fprintf(stderr, "Searching home directory: %s\n", home); sprintf(path, "%s%c%s", home, FILE_SEP, name); f = fopen(path, "r"); if(f != NULL) return f; } #ifdef WIN32 home = getenv("USERPROFILE"); if(home != NULL) { fprintf(stderr, "Searching user profile directory: %s\n", home); sprintf(path, "%s%c%s", home, FILE_SEP, name); f = fopen(path, "r"); if(f != NULL) return f; } #else // ! WIN32 fprintf(stderr, "Searching system config directory: %s\n", SYSCONFDIR); sprintf(path, "%s%c%s", SYSCONFDIR, FILE_SEP, name); f = fopen(path, "r"); if(f != NULL) return f; #endif // ! WIN32 if(!strchr(arg0, '/') && !strchr(arg0, '\\')) { char *path = getenv("PATH"); if(path != NULL) { fprintf(stderr, "Searching PATH\n"); strncpy(buffer, path, 4096); buffer[4095] = 0; char *tok = strtok(buffer, PATH_SEP); while(tok) { sprintf(path, "%s%c%s", tok, FILE_SEP, EXE_NAME); f = fopen(path, "r"); if(f != NULL) { char path2[2048]; fclose(f); sprintf(path2, "%s%c%s", tok, FILE_SEP, name); f = fopen(path2, "r"); if(f != NULL) { fprintf(stderr, "Found at %s\n", path2); return f; } } tok = strtok(NULL, PATH_SEP); } } } else { // executable is relative to some directory fprintf(stderr, "Searching executable directory\n"); strcpy(buffer, arg0); char *p = strrchr(buffer, FILE_SEP); if(p) { *p = 0; sprintf(path, "%s%c%s", buffer, FILE_SEP, name); f = fopen(path, "r"); if(f != NULL) return f; } } return NULL; } void sdlReadPreferences(FILE *f) { char buffer[2048]; while(1) { char *s = fgets(buffer, 2048, f); if(s == NULL) break; char *p = strchr(s, '#'); if(p) *p = 0; char *token = strtok(s, " \t\n\r="); if(!token) continue; if(strlen(token) == 0) continue; char *key = token; char *value = strtok(NULL, "\t\n\r"); if(value == NULL) { fprintf(stderr, "Empty value for key %s\n", key); continue; } if(!strcmp(key,"Joy0_Left")) { joypad[0][KEY_LEFT] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_Right")) { joypad[0][KEY_RIGHT] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_Up")) { joypad[0][KEY_UP] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_Down")) { joypad[0][KEY_DOWN] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_A")) { joypad[0][KEY_BUTTON_A] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_B")) { joypad[0][KEY_BUTTON_B] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_L")) { joypad[0][KEY_BUTTON_L] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_R")) { joypad[0][KEY_BUTTON_R] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_Start")) { joypad[0][KEY_BUTTON_START] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_Select")) { joypad[0][KEY_BUTTON_SELECT] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_Speed")) { joypad[0][KEY_BUTTON_SPEED] = sdlFromHex(value); } else if(!strcmp(key, "Joy0_Capture")) { joypad[0][KEY_BUTTON_CAPTURE] = sdlFromHex(value); } else if(!strcmp(key,"Joy1_Left")) { joypad[1][KEY_LEFT] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_Right")) { joypad[1][KEY_RIGHT] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_Up")) { joypad[1][KEY_UP] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_Down")) { joypad[1][KEY_DOWN] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_A")) { joypad[1][KEY_BUTTON_A] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_B")) { joypad[1][KEY_BUTTON_B] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_L")) { joypad[1][KEY_BUTTON_L] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_R")) { joypad[1][KEY_BUTTON_R] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_Start")) { joypad[1][KEY_BUTTON_START] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_Select")) { joypad[1][KEY_BUTTON_SELECT] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_Speed")) { joypad[1][KEY_BUTTON_SPEED] = sdlFromHex(value); } else if(!strcmp(key, "Joy1_Capture")) { joypad[1][KEY_BUTTON_CAPTURE] = sdlFromHex(value); } else if(!strcmp(key,"Joy2_Left")) { joypad[2][KEY_LEFT] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_Right")) { joypad[2][KEY_RIGHT] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_Up")) { joypad[2][KEY_UP] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_Down")) { joypad[2][KEY_DOWN] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_A")) { joypad[2][KEY_BUTTON_A] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_B")) { joypad[2][KEY_BUTTON_B] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_L")) { joypad[2][KEY_BUTTON_L] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_R")) { joypad[2][KEY_BUTTON_R] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_Start")) { joypad[2][KEY_BUTTON_START] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_Select")) { joypad[2][KEY_BUTTON_SELECT] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_Speed")) { joypad[2][KEY_BUTTON_SPEED] = sdlFromHex(value); } else if(!strcmp(key, "Joy2_Capture")) { joypad[2][KEY_BUTTON_CAPTURE] = sdlFromHex(value); } else if(!strcmp(key,"Joy4_Left")) { joypad[4][KEY_LEFT] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_Right")) { joypad[4][KEY_RIGHT] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_Up")) { joypad[4][KEY_UP] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_Down")) { joypad[4][KEY_DOWN] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_A")) { joypad[4][KEY_BUTTON_A] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_B")) { joypad[4][KEY_BUTTON_B] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_L")) { joypad[4][KEY_BUTTON_L] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_R")) { joypad[4][KEY_BUTTON_R] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_Start")) { joypad[4][KEY_BUTTON_START] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_Select")) { joypad[4][KEY_BUTTON_SELECT] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_Speed")) { joypad[4][KEY_BUTTON_SPEED] = sdlFromHex(value); } else if(!strcmp(key, "Joy4_Capture")) { joypad[4][KEY_BUTTON_CAPTURE] = sdlFromHex(value); } else if(!strcmp(key, "Motion_Left")) { motion[KEY_LEFT] = sdlFromHex(value); } else if(!strcmp(key, "Motion_Right")) { motion[KEY_RIGHT] = sdlFromHex(value); } else if(!strcmp(key, "Motion_Up")) { motion[KEY_UP] = sdlFromHex(value); } else if(!strcmp(key, "Motion_Down")) { motion[KEY_DOWN] = sdlFromHex(value); } else if(!strcmp(key, "frameSkip")) { frameSkip = sdlFromHex(value); if(frameSkip < 0 || frameSkip > 9) frameSkip = 2; } else if(!strcmp(key, "gbFrameSkip")) { gbFrameSkip = sdlFromHex(value); if(gbFrameSkip < 0 || gbFrameSkip > 9) gbFrameSkip = 0; } else if(!strcmp(key, "video")) { sizeOption = sdlFromHex(value); if(sizeOption < 0 || sizeOption > 3) sizeOption = 1; } else if(!strcmp(key, "fullScreen")) { fullscreen = sdlFromHex(value) ? 1 : 0; } else if(!strcmp(key, "useBios")) { useBios = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "skipBios")) { skipBios = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "biosFile")) { strcpy(biosFileName, value); } else if(!strcmp(key, "filter")) { filter = sdlFromHex(value); if(filter < 0 || filter > 13) filter = 0; } else if(!strcmp(key, "disableStatus")) { disableStatusMessages = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "borderOn")) { gbBorderOn = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "borderAutomatic")) { gbBorderAutomatic = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "emulatorType")) { gbEmulatorType = sdlFromHex(value); if(gbEmulatorType < 0 || gbEmulatorType > 5) gbEmulatorType = 1; } else if(!strcmp(key, "colorOption")) { gbColorOption = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "captureDir")) { sdlCheckDirectory(value); strcpy(captureDir, value); } else if(!strcmp(key, "saveDir")) { sdlCheckDirectory(value); strcpy(saveDir, value); } else if(!strcmp(key, "batteryDir")) { sdlCheckDirectory(value); strcpy(batteryDir, value); } else if(!strcmp(key, "captureFormat")) { captureFormat = sdlFromHex(value); } else if(!strcmp(key, "soundQuality")) { soundQuality = sdlFromHex(value); switch(soundQuality) { case 1: case 2: case 4: break; default: fprintf(stderr, "Unknown sound quality %d. Defaulting to 22Khz\n", soundQuality); soundQuality = 2; break; } } else if(!strcmp(key, "soundOff")) { soundOffFlag = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "soundEnable")) { int res = sdlFromHex(value) & 0x30f; soundEnable(res); soundDisable(~res); } else if(!strcmp(key, "soundEcho")) { soundEcho = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "soundLowPass")) { soundLowPass = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "soundReverse")) { soundReverse = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "soundVolume")) { soundVolume = sdlFromHex(value); if(soundVolume < 0 || soundVolume > 3) soundVolume = 0; } else if(!strcmp(key, "removeIntros")) { removeIntros = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "saveType")) { cpuSaveType = sdlFromHex(value); if(cpuSaveType < 0 || cpuSaveType > 5) cpuSaveType = 0; } else if(!strcmp(key, "flashSize")) { sdlFlashSize = sdlFromHex(value); if(sdlFlashSize != 0 && sdlFlashSize != 1) sdlFlashSize = 0; } else if(!strcmp(key, "ifbType")) { ifbType = sdlFromHex(value); if(ifbType < 0 || ifbType > 2) ifbType = 0; } else if(!strcmp(key, "showSpeed")) { showSpeed = sdlFromHex(value); if(showSpeed < 0 || showSpeed > 2) showSpeed = 1; } else if(!strcmp(key, "showSpeedTransparent")) { showSpeedTransparent = sdlFromHex(value); } else if(!strcmp(key, "autoFrameSkip")) { autoFrameSkip = sdlFromHex(value); } else if(!strcmp(key, "throttle")) { throttle = sdlFromHex(value); if(throttle != 0 && (throttle < 5 || throttle > 1000)) throttle = 0; } else if(!strcmp(key, "disableMMX")) { #ifdef MMX cpu_mmx = sdlFromHex(value) ? false : true; #endif } else if(!strcmp(key, "pauseWhenInactive")) { pauseWhenInactive = sdlFromHex(value) ? true : false; } else if(!strcmp(key, "agbPrint")) { sdlAgbPrint = sdlFromHex(value); } else if(!strcmp(key, "rtcEnabled")) { sdlRtcEnable = sdlFromHex(value); } else if(!strcmp(key, "rewindTimer")) { rewindTimer = sdlFromHex(value); if(rewindTimer < 0 || rewindTimer > 600) rewindTimer = 0; rewindTimer *= 6; // convert value to 10 frames multiple } else if(!strcmp(key, "enhancedDetection")) { cpuEnhancedDetection = sdlFromHex(value) ? true : false; } else { fprintf(stderr, "Unknown configuration key %s\n", key); } } } void sdlReadPreferences() { FILE *f = sdlFindFile("VisualBoyAdvance.cfg"); if(f == NULL) { fprintf(stderr, "Configuration file NOT FOUND (using defaults)\n"); return; } else fprintf(stderr, "Reading configuration file.\n"); sdlReadPreferences(f); fclose(f); } static void sdlApplyPerImagePreferences() { FILE *f = sdlFindFile("vba-over.ini"); if(!f) { fprintf(stderr, "vba-over.ini NOT FOUND (using emulator settings)\n"); return; } else fprintf(stderr, "Reading vba-over.ini\n"); char buffer[7]; buffer[0] = '['; buffer[1] = rom[0xac]; buffer[2] = rom[0xad]; buffer[3] = rom[0xae]; buffer[4] = rom[0xaf]; buffer[5] = ']'; buffer[6] = 0; char readBuffer[2048]; bool found = false; while(1) { char *s = fgets(readBuffer, 2048, f); if(s == NULL) break; char *p = strchr(s, ';'); if(p) *p = 0; char *token = strtok(s, " \t\n\r="); if(!token) continue; if(strlen(token) == 0) continue; if(!strcmp(token, buffer)) { found = true; break; } } if(found) { while(1) { char *s = fgets(readBuffer, 2048, f); if(s == NULL) break; char *p = strchr(s, ';'); if(p) *p = 0; char *token = strtok(s, " \t\n\r="); if(!token) continue; if(strlen(token) == 0) continue; if(token[0] == '[') // starting another image settings break; char *value = strtok(NULL, "\t\n\r="); if(value == NULL) continue; if(!strcmp(token, "rtcEnabled")) rtcEnable(atoi(value) == 0 ? false : true); else if(!strcmp(token, "flashSize")) { int size = atoi(value); if(size == 0x10000 || size == 0x20000) flashSetSize(size); } else if(!strcmp(token, "saveType")) { int save = atoi(value); if(save >= 0 && save <= 5) cpuSaveType = save; } } } fclose(f); } static int sdlCalculateShift(u32 mask) { int m = 0; while(mask) { m++; mask >>= 1; } return m-5; } static int sdlCalculateMaskWidth(u32 mask) { int m = 0; int mask2 = mask; while(mask2) { m++; mask2 >>= 1; } int m2 = 0; mask2 = mask; while(!(mask2 & 1)) { m2++; mask2 >>= 1; } return m - m2; } void sdlWriteState(int num) { char stateName[2048]; if(saveDir[0]) sprintf(stateName, "%s/%s%d.sgm", saveDir, sdlGetFilename(filename), num+1); else sprintf(stateName,"%s%d.sgm", filename, num+1); if(emulator.emuWriteState) emulator.emuWriteState(stateName); sprintf(stateName, "Wrote state %d", num+1); systemScreenMessage(stateName); } void sdlReadState(int num) { char stateName[2048]; if(saveDir[0]) sprintf(stateName, "%s/%s%d.sgm", saveDir, sdlGetFilename(filename), num+1); else sprintf(stateName,"%s%d.sgm", filename, num+1); if(emulator.emuReadState) emulator.emuReadState(stateName); sprintf(stateName, "Loaded state %d", num+1); systemScreenMessage(stateName); } void sdlWriteBattery() { char buffer[1048]; if(batteryDir[0]) sprintf(buffer, "%s/%s.sav", batteryDir, sdlGetFilename(filename)); else sprintf(buffer, "%s.sav", filename); emulator.emuWriteBattery(buffer); systemScreenMessage("Wrote battery"); } void sdlReadBattery() { char buffer[1048]; if(batteryDir[0]) sprintf(buffer, "%s/%s.sav", batteryDir, sdlGetFilename(filename)); else sprintf(buffer, "%s.sav", filename); bool res = false; res = emulator.emuReadBattery(buffer); if(res) systemScreenMessage("Loaded battery"); } #define MOD_KEYS (KMOD_CTRL|KMOD_SHIFT|KMOD_ALT|KMOD_META) #define MOD_NOCTRL (KMOD_SHIFT|KMOD_ALT|KMOD_META) #define MOD_NOALT (KMOD_CTRL|KMOD_SHIFT|KMOD_META) #define MOD_NOSHIFT (KMOD_CTRL|KMOD_ALT|KMOD_META) void sdlUpdateKey(int key, bool down) { int i; for(int j = 0; j < 4; j++) { for(i = 0 ; i < 12; i++) { if((joypad[j][i] & 0xf000) == 0) { if(key == joypad[j][i]) sdlButtons[j][i] = down; } } } for(i = 0 ; i < 4; i++) { if((motion[i] & 0xf000) == 0) { if(key == motion[i]) sdlMotionButtons[i] = down; } } } void sdlUpdateJoyButton(int which, int button, bool pressed) { int i; for(int j = 0; j < 4; j++) { for(i = 0; i < 12; i++) { int dev = (joypad[j][i] >> 12); int b = joypad[j][i] & 0xfff; if(dev) { dev--; if((dev == which) && (b >= 128) && (b == (button+128))) { sdlButtons[j][i] = pressed; } } } } for(i = 0; i < 4; i++) { int dev = (motion[i] >> 12); int b = motion[i] & 0xfff; if(dev) { dev--; if((dev == which) && (b >= 128) && (b == (button+128))) { sdlMotionButtons[i] = pressed; } } } } void sdlUpdateJoyHat(int which, int hat, int value) { int i; for(int j = 0; j < 4; j++) { for(i = 0; i < 12; i++) { int dev = (joypad[j][i] >> 12); int a = joypad[j][i] & 0xfff; if(dev) { dev--; if((dev == which) && (a>=32) && (a < 48) && (((a&15)>>2) == hat)) { int dir = a & 3; int v = 0; switch(dir) { case 0: v = value & SDL_HAT_UP; break; case 1: v = value & SDL_HAT_DOWN; break; case 2: v = value & SDL_HAT_RIGHT; break; case 3: v = value & SDL_HAT_LEFT; break; } sdlButtons[j][i] = (v ? true : false); } } } } for(i = 0; i < 4; i++) { int dev = (motion[i] >> 12); int a = motion[i] & 0xfff; if(dev) { dev--; if((dev == which) && (a>=32) && (a < 48) && (((a&15)>>2) == hat)) { int dir = a & 3; int v = 0; switch(dir) { case 0: v = value & SDL_HAT_UP; break; case 1: v = value & SDL_HAT_DOWN; break; case 2: v = value & SDL_HAT_RIGHT; break; case 3: v = value & SDL_HAT_LEFT; break; } sdlMotionButtons[i] = (v ? true : false); } } } } void sdlUpdateJoyAxis(int which, int axis, int value) { int i; for(int j = 0; j < 4; j++) { for(i = 0; i < 12; i++) { int dev = (joypad[j][i] >> 12); int a = joypad[j][i] & 0xfff; if(dev) { dev--; if((dev == which) && (a < 32) && ((a>>1) == axis)) { sdlButtons[j][i] = (a & 1) ? (value > 16384) : (value < -16384); } } } } for(i = 0; i < 4; i++) { int dev = (motion[i] >> 12); int a = motion[i] & 0xfff; if(dev) { dev--; if((dev == which) && (a < 32) && ((a>>1) == axis)) { sdlMotionButtons[i] = (a & 1) ? (value > 16384) : (value < -16384); } } } } bool sdlCheckJoyKey(int key) { int dev = (key >> 12) - 1; int what = key & 0xfff; if(what >= 128) { // joystick button int button = what - 128; if(button >= SDL_JoystickNumButtons(sdlDevices[dev])) return false; } else if (what < 0x20) { // joystick axis what >>= 1; if(what >= SDL_JoystickNumAxes(sdlDevices[dev])) return false; } else if (what < 0x30) { // joystick hat what = (what & 15); what >>= 2; if(what >= SDL_JoystickNumHats(sdlDevices[dev])) return false; } // no problem found return true; } void sdlCheckKeys() { sdlNumDevices = SDL_NumJoysticks(); if(sdlNumDevices) sdlDevices = (SDL_Joystick **)calloc(1,sdlNumDevices * sizeof(SDL_Joystick **)); int i; bool usesJoy = false; for(int j = 0; j < 4; j++) { for(i = 0; i < 12; i++) { int dev = joypad[j][i] >> 12; if(dev) { dev--; bool ok = false; if(sdlDevices) { if(dev < sdlNumDevices) { if(sdlDevices[dev] == NULL) { sdlDevices[dev] = SDL_JoystickOpen(dev); } ok = sdlCheckJoyKey(joypad[j][i]); } else ok = false; } if(!ok) joypad[j][i] = defaultJoypad[i]; else usesJoy = true; } } } for(i = 0; i < 4; i++) { int dev = motion[i] >> 12; if(dev) { dev--; bool ok = false; if(sdlDevices) { if(dev < sdlNumDevices) { if(sdlDevices[dev] == NULL) { sdlDevices[dev] = SDL_JoystickOpen(dev); } ok = sdlCheckJoyKey(motion[i]); } else ok = false; } if(!ok) motion[i] = defaultMotion[i]; else usesJoy = true; } } if(usesJoy) SDL_JoystickEventState(SDL_ENABLE); } void sdlPollEvents() { SDL_Event event; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: emulating = 0; break; case SDL_ACTIVEEVENT: if(pauseWhenInactive && (event.active.state & SDL_APPINPUTFOCUS)) { active = event.active.gain; if(active) { if(!paused) { if(emulating) soundResume(); } } else { wasPaused = true; if(pauseWhenInactive) { if(emulating) soundPause(); } memset(delta,255,sizeof(delta)); } } break; case SDL_MOUSEMOTION: case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONDOWN: if(fullscreen) { SDL_ShowCursor(SDL_ENABLE); mouseCounter = 120; } break; case SDL_JOYHATMOTION: sdlUpdateJoyHat(event.jhat.which, event.jhat.hat, event.jhat.value); break; case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: sdlUpdateJoyButton(event.jbutton.which, event.jbutton.button, event.jbutton.state == SDL_PRESSED); break; case SDL_JOYAXISMOTION: sdlUpdateJoyAxis(event.jaxis.which, event.jaxis.axis, event.jaxis.value); break; case SDL_KEYDOWN: sdlUpdateKey(event.key.keysym.sym, true); break; case SDL_KEYUP: switch(event.key.keysym.sym) { case SDLK_r: if(!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { if(emulating) { emulator.emuReset(); systemScreenMessage("Reset"); } } break; case SDLK_b: if(!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { if(emulating && emulator.emuReadMemState && rewindMemory && rewindCount) { rewindPos = --rewindPos & 7; emulator.emuReadMemState(&rewindMemory[REWIND_SIZE*rewindPos], REWIND_SIZE); rewindCount--; rewindCounter = 0; systemScreenMessage("Rewind"); } } break; case SDLK_p: if(!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { paused = !paused; SDL_PauseAudio(paused); if(paused) wasPaused = true; } break; case SDLK_ESCAPE: emulating = 0; break; case SDLK_f: if(!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { int flags = 0; fullscreen = !fullscreen; if(fullscreen) flags |= SDL_FULLSCREEN; SDL_SetVideoMode(destWidth, destHeight, systemColorDepth, flags); // if(SDL_WM_ToggleFullScreen(surface)) // fullscreen = !fullscreen; } break; case SDLK_F11: if(dbgMain != debuggerMain) { if(armState) { armNextPC -= 4; reg[15].I -= 4; } else { armNextPC -= 2; reg[15].I -= 2; } } debugger = true; break; case SDLK_F1: case SDLK_F2: case SDLK_F3: case SDLK_F4: case SDLK_F5: case SDLK_F6: case SDLK_F7: case SDLK_F8: case SDLK_F9: case SDLK_F10: if(!(event.key.keysym.mod & MOD_NOSHIFT) && (event.key.keysym.mod & KMOD_SHIFT)) { sdlWriteState(event.key.keysym.sym-SDLK_F1); } else if(!(event.key.keysym.mod & MOD_KEYS)) { sdlReadState(event.key.keysym.sym-SDLK_F1); } break; case SDLK_1: case SDLK_2: case SDLK_3: case SDLK_4: if(!(event.key.keysym.mod & MOD_NOALT) && (event.key.keysym.mod & KMOD_ALT)) { char *disableMessages[4] = { "autofire A disabled", "autofire B disabled", "autofire R disabled", "autofire L disabled"}; char *enableMessages[4] = { "autofire A", "autofire B", "autofire R", "autofire L"}; int mask = 1 << (event.key.keysym.sym - SDLK_1); if(event.key.keysym.sym > SDLK_2) mask <<= 6; if(autoFire & mask) { autoFire &= ~mask; systemScreenMessage(disableMessages[event.key.keysym.sym - SDLK_1]); } else { autoFire |= mask; systemScreenMessage(enableMessages[event.key.keysym.sym - SDLK_1]); } } if(!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { int mask = 0x0100 << (event.key.keysym.sym - SDLK_1); layerSettings ^= mask; layerEnable = DISPCNT & layerSettings; CPUUpdateRenderBuffers(false); } break; case SDLK_5: case SDLK_6: case SDLK_7: case SDLK_8: if(!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { int mask = 0x0100 << (event.key.keysym.sym - SDLK_1); layerSettings ^= mask; layerEnable = DISPCNT & layerSettings; } break; case SDLK_n: if(!(event.key.keysym.mod & MOD_NOCTRL) && (event.key.keysym.mod & KMOD_CTRL)) { if(paused) paused = false; pauseNextFrame = true; } break; default: break; } sdlUpdateKey(event.key.keysym.sym, false); break; } } } void usage(char *cmd) { printf("%s [option ...] file\n", cmd); printf("\ \n\ Options:\n\ -1, --video-1x 1x\n\ -2, --video-2x 2x\n\ -3, --video-3x 3x\n\ -4, --video-4x 4x\n\ -F, --fullscreen Full screen\n\ -G, --gdb=PROTOCOL GNU Remote Stub mode:\n\ tcp - use TCP at port 55555\n\ tcp:PORT - use TCP at port PORT\n\ pipe - use pipe transport\n\ -N, --no-debug Don't parse debug information\n\ -S, --flash-size=SIZE Set the Flash size\n\ --flash-64k 0 - 64K Flash\n\ --flash-128k 1 - 128K Flash\n\ -T, --throttle=THROTTLE Set the desired throttle (5...1000)\n\ -Y, --yuv=TYPE Use YUV overlay for drawing:\n\ 0 - YV12\n\ 1 - UYVY\n\ 2 - YVYU\n\ 3 - YUY2\n\ 4 - IYUV\n\ -b, --bios=BIOS Use given bios file\n\ -c, --config=FILE Read the given configuration file\n\ -d, --debug Enter debugger\n\ -f, --filter=FILTER Select filter:\n\ --filter-normal 0 - normal mode\n\ --filter-tv-mode 1 - TV Mode\n\ --filter-2xsai 2 - 2xSaI\n\ --filter-super-2xsai 3 - Super 2xSaI\n\ --filter-super-eagle 4 - Super Eagle\n\ --filter-pixelate 5 - Pixelate\n\ --filter-motion-blur 6 - Motion Blur\n\ --filter-advmame 7 - AdvanceMAME Scale2x\n\ --filter-simple2x 8 - Simple2x\n\ --filter-bilinear 9 - Bilinear\n\ --filter-bilinear+ 10 - Bilinear Plus\n\ --filter-scanlines 11 - Scanlines\n\ --filter-hq2x 12 - hq2x\n\ --filter-lq2x 13 - lq2x\n\ -h, --help Print this help\n\ -i, --ips=PATCH Apply given IPS patch\n\ -p, --profile=[HERTZ] Enable profiling\n\ -s, --frameskip=FRAMESKIP Set frame skip (0...9)\n\ "); printf("\ -t, --save-type=TYPE Set the available save type\n\ --save-auto 0 - Automatic (EEPROM, SRAM, FLASH)\n\ --save-eeprom 1 - EEPROM\n\ --save-sram 2 - SRAM\n\ --save-flash 3 - FLASH\n\ --save-sensor 4 - EEPROM+Sensor\n\ --save-none 5 - NONE\n\ -v, --verbose=VERBOSE Set verbose logging (trace.log)\n\ 1 - SWI\n\ 2 - Unaligned memory access\n\ 4 - Illegal memory write\n\ 8 - Illegal memory read\n\ 16 - DMA 0\n\ 32 - DMA 1\n\ 64 - DMA 2\n\ 128 - DMA 3\n\ 256 - Undefined instruction\n\ 512 - AGBPrint messages\n\ \n\ Long options only:\n\ --agb-print Enable AGBPrint support\n\ --auto-frameskip Enable auto frameskipping\n\ --ifb-none No interframe blending\n\ --ifb-motion-blur Interframe motion blur\n\ --ifb-smart Smart interframe blending\n\ --no-agb-print Disable AGBPrint support\n\ --no-auto-frameskip Disable auto frameskipping\n\ --no-ips Do not apply IPS patch\n\ --no-mmx Disable MMX support\n\ --no-pause-when-inactive Don't pause when inactive\n\ --no-rtc Disable RTC support\n\ --no-show-speed Don't show emulation speed\n\ --no-throttle Disable thrrotle\n\ --pause-when-inactive Pause when inactive\n\ --rtc Enable RTC support\n\ --show-speed-normal Show emulation speed\n\ --show-speed-detailed Show detailed speed data\n\ "); } int main(int argc, char **argv) { fprintf(stderr, "VisualBoyAdvance version %s [SDL]\n", VERSION); arg0 = argv[0]; captureDir[0] = 0; saveDir[0] = 0; batteryDir[0] = 0; ipsname[0] = 0; int op = -1; frameSkip = 2; gbBorderOn = 0; parseDebug = true; sdlReadPreferences(); sdlPrintUsage = 0; while((op = getopt_long(argc, argv, "FNT:Y:G:D:b:c:df:hi:p::s:t:v:1234", sdlOptions, NULL)) != -1) { switch(op) { case 0: // long option already processed by getopt_long break; case 'b': useBios = true; if(optarg == NULL) { fprintf(stderr, "Missing BIOS file name\n"); exit(-1); } strcpy(biosFileName, optarg); break; case 'c': { if(optarg == NULL) { fprintf(stderr, "Missing config file name\n"); exit(-1); } FILE *f = fopen(optarg, "r"); if(f == NULL) { fprintf(stderr, "File not found %s\n", optarg); exit(-1); } sdlReadPreferences(f); fclose(f); } break; case 'd': debugger = true; break; case 'h': sdlPrintUsage = 1; break; case 'i': if(optarg == NULL) { fprintf(stderr, "Missing IPS name\n"); exit(-1); strcpy(ipsname, optarg); } break; case 'Y': yuv = true; if(optarg) { yuvType = atoi(optarg); switch(yuvType) { case 0: yuvType = SDL_YV12_OVERLAY; break; case 1: yuvType = SDL_UYVY_OVERLAY; break; case 2: yuvType = SDL_YVYU_OVERLAY; break; case 3: yuvType = SDL_YUY2_OVERLAY; break; case 4: yuvType = SDL_IYUV_OVERLAY; break; default: yuvType = SDL_YV12_OVERLAY; } } else yuvType = SDL_YV12_OVERLAY; break; case 'G': dbgMain = remoteStubMain; dbgSignal = remoteStubSignal; dbgOutput = remoteOutput; debugger = true; debuggerStub = true; if(optarg) { char *s = optarg; if(strncmp(s,"tcp:", 4) == 0) { s+=4; int port = atoi(s); remoteSetProtocol(0); remoteSetPort(port); } else if(strcmp(s,"tcp") == 0) { remoteSetProtocol(0); } else if(strcmp(s, "pipe") == 0) { remoteSetProtocol(1); } else { fprintf(stderr, "Unknown protocol %s\n", s); exit(-1); } } else { remoteSetProtocol(0); } break; case 'N': parseDebug = false; break; case 'D': if(optarg) { systemDebug = atoi(optarg); } else { systemDebug = 1; } break; case 'F': fullscreen = 1; mouseCounter = 120; break; case 'f': if(optarg) { filter = atoi(optarg); } else { filter = 0; } break; case 'p': #ifdef PROFILING if(optarg) { cpuEnableProfiling(atoi(optarg)); } else cpuEnableProfiling(100); #endif break; case 'S': sdlFlashSize = atoi(optarg); if(sdlFlashSize < 0 || sdlFlashSize > 1) sdlFlashSize = 0; break; case 's': if(optarg) { int a = atoi(optarg); if(a >= 0 && a <= 9) { gbFrameSkip = a; frameSkip = a; } } else { frameSkip = 2; gbFrameSkip = 0; } break; case 't': if(optarg) { int a = atoi(optarg); if(a < 0 || a > 5) a = 0; cpuSaveType = a; } break; case 'T': if(optarg) { int t = atoi(optarg); if(t < 5 || t > 1000) t = 0; throttle = t; } break; case 'v': if(optarg) { systemVerbose = atoi(optarg); } else systemVerbose = 0; break; case '1': sizeOption = 0; break; case '2': sizeOption = 1; break; case '3': sizeOption = 2; break; case '4': sizeOption = 3; break; case '?': sdlPrintUsage = 1; break; } } if(sdlPrintUsage) { usage(argv[0]); exit(-1); } #ifdef MMX if(disableMMX) cpu_mmx = 0; #endif if(rewindTimer) rewindMemory = (char *)malloc(8*REWIND_SIZE); if(sdlFlashSize == 0) flashSetSize(0x10000); else flashSetSize(0x20000); rtcEnable(sdlRtcEnable ? true : false); agbPrintEnable(sdlAgbPrint ? true : false); if(!debuggerStub) { if(optind >= argc) { systemMessage(0,"Missing image name"); usage(argv[0]); exit(-1); } } if(filter) { sizeOption = 1; } for(int i = 0; i < 24;) { systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10); systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10); systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10); systemGbPalette[i++] = 0; } systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; if(optind < argc) { char *szFile = argv[optind]; utilGetBaseName(szFile, filename); char *p = strrchr(filename, '.'); if(p) *p = 0; if(ipsname[0] == 0) sprintf(ipsname, "%s.ips", filename); bool failed = false; IMAGE_TYPE type = utilFindType(szFile); if(type == IMAGE_UNKNOWN) { systemMessage(0, "Unknown file type %s", szFile); exit(-1); } cartridgeType = (int)type; if(type == IMAGE_GB) { failed = !gbLoadRom(szFile); if(!failed) { cartridgeType = 1; emulator = GBSystem; if(sdlAutoIPS) { int size = gbRomSize; utilApplyIPS(ipsname, &gbRom, &size); if(size != gbRomSize) { extern bool gbUpdateSizes(); gbUpdateSizes(); gbReset(); } } } } else if(type == IMAGE_GBA) { int size = CPULoadRom(szFile); failed = (size == 0); if(!failed) { // if(cpuEnhancedDetection && cpuSaveType == 0) { // utilGBAFindSave(rom, size); // } sdlApplyPerImagePreferences(); cartridgeType = 0; emulator = GBASystem; /* disabled due to problems if(removeIntros && rom != NULL) { WRITE32LE(&rom[0], 0xea00002e); } */ CPUInit(biosFileName, useBios); CPUReset(); if(sdlAutoIPS) { int size = 0x2000000; utilApplyIPS(ipsname, &rom, &size); if(size != 0x2000000) { CPUReset(); } } } } if(failed) { systemMessage(0, "Failed to load file %s", szFile); exit(-1); } } else { cartridgeType = 0; strcpy(filename, "gnu_stub"); rom = (u8 *)malloc(0x2000000); workRAM = (u8 *)calloc(1, 0x40000); bios = (u8 *)calloc(1,0x4000); internalRAM = (u8 *)calloc(1,0x8000); paletteRAM = (u8 *)calloc(1,0x400); vram = (u8 *)calloc(1, 0x20000); oam = (u8 *)calloc(1, 0x400); pix = (u8 *)calloc(1, 4 * 240 * 160); ioMem = (u8 *)calloc(1, 0x400); emulator = GBASystem; CPUInit(biosFileName, useBios); CPUReset(); } sdlReadBattery(); if(debuggerStub) remoteInit(); int flags = SDL_INIT_VIDEO|SDL_INIT_AUDIO| SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE; if(soundOffFlag) flags ^= SDL_INIT_AUDIO; if(SDL_Init(flags)) { systemMessage(0, "Failed to init SDL: %s", SDL_GetError()); exit(-1); } if(SDL_InitSubSystem(SDL_INIT_JOYSTICK)) { systemMessage(0, "Failed to init joystick support: %s", SDL_GetError()); } sdlCheckKeys(); if(cartridgeType == 0) { srcWidth = 240; srcHeight = 160; systemFrameSkip = frameSkip; } else if (cartridgeType == 1) { if(gbBorderOn) { srcWidth = 256; srcHeight = 224; gbBorderLineSkip = 256; gbBorderColumnSkip = 48; gbBorderRowSkip = 40; } else { srcWidth = 160; srcHeight = 144; gbBorderLineSkip = 160; gbBorderColumnSkip = 0; gbBorderRowSkip = 0; } systemFrameSkip = gbFrameSkip; } else { srcWidth = 320; srcHeight = 240; } destWidth = (sizeOption+1)*srcWidth; destHeight = (sizeOption+1)*srcHeight; surface = SDL_SetVideoMode(destWidth, destHeight, 16, SDL_ANYFORMAT|SDL_HWSURFACE|SDL_DOUBLEBUF| (fullscreen ? SDL_FULLSCREEN : 0)); if(surface == NULL) { systemMessage(0, "Failed to set video mode"); SDL_Quit(); exit(-1); } systemRedShift = sdlCalculateShift(surface->format->Rmask); systemGreenShift = sdlCalculateShift(surface->format->Gmask); systemBlueShift = sdlCalculateShift(surface->format->Bmask); systemColorDepth = surface->format->BitsPerPixel; if(systemColorDepth == 15) systemColorDepth = 16; if(yuv) { Init_Overlay(surface, yuvType); systemColorDepth = 32; systemRedShift = 3; systemGreenShift = 11; systemBlueShift = 19; } if(systemColorDepth != 16 && systemColorDepth != 24 && systemColorDepth != 32) { fprintf(stderr,"Unsupported color depth '%d'.\nOnly 16, 24 and 32 bit color depths are supported\n", systemColorDepth); exit(-1); } #ifndef C_CORE sdlMakeStretcher(srcWidth); #else switch(systemColorDepth) { case 16: sdlStretcher = sdlStretcher16[sizeOption]; break; case 24: sdlStretcher = sdlStretcher24[sizeOption]; break; case 32: sdlStretcher = sdlStretcher32[sizeOption]; break; default: fprintf(stderr, "Unsupported resolution: %d\n", systemColorDepth); exit(-1); } #endif fprintf(stderr,"Color depth: %d\n", systemColorDepth); if(systemColorDepth == 16) { if(sdlCalculateMaskWidth(surface->format->Gmask) == 6) { Init_2xSaI(565); RGB_LOW_BITS_MASK = 0x821; } else { Init_2xSaI(555); RGB_LOW_BITS_MASK = 0x421; } if(cartridgeType == 2) { for(int i = 0; i < 0x10000; i++) { systemColorMap16[i] = (((i >> 1) & 0x1f) << systemBlueShift) | (((i & 0x7c0) >> 6) << systemGreenShift) | (((i & 0xf800) >> 11) << systemRedShift); } } else { for(int i = 0; i < 0x10000; i++) { systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | (((i & 0x3e0) >> 5) << systemGreenShift) | (((i & 0x7c00) >> 10) << systemBlueShift); } } srcPitch = srcWidth * 2+4; } else { if(systemColorDepth != 32) filterFunction = NULL; RGB_LOW_BITS_MASK = 0x010101; if(systemColorDepth == 32) { Init_2xSaI(32); } for(int i = 0; i < 0x10000; i++) { systemColorMap32[i] = ((i & 0x1f) << systemRedShift) | (((i & 0x3e0) >> 5) << systemGreenShift) | (((i & 0x7c00) >> 10) << systemBlueShift); } if(systemColorDepth == 32) srcPitch = srcWidth*4 + 4; else srcPitch = srcWidth*3; } if(systemColorDepth != 32) { switch(filter) { case 0: filterFunction = NULL; break; case 1: filterFunction = ScanlinesTV; break; case 2: filterFunction = _2xSaI; break; case 3: filterFunction = Super2xSaI; break; case 4: filterFunction = SuperEagle; break; case 5: filterFunction = Pixelate; break; case 6: filterFunction = MotionBlur; break; case 7: filterFunction = AdMame2x; break; case 8: filterFunction = Simple2x; break; case 9: filterFunction = Bilinear; break; case 10: filterFunction = BilinearPlus; break; case 11: filterFunction = Scanlines; break; case 12: filterFunction = hq2x; break; case 13: filterFunction = lq2x; break; default: filterFunction = NULL; break; } } else { switch(filter) { case 0: filterFunction = NULL; break; case 1: filterFunction = ScanlinesTV32; break; case 2: filterFunction = _2xSaI32; break; case 3: filterFunction = Super2xSaI32; break; case 4: filterFunction = SuperEagle32; break; case 5: filterFunction = Pixelate32; break; case 6: filterFunction = MotionBlur32; break; case 7: filterFunction = AdMame2x32; break; case 8: filterFunction = Simple2x32; break; case 9: filterFunction = Bilinear32; break; case 10: filterFunction = BilinearPlus32; break; case 11: filterFunction = Scanlines32; break; case 12: filterFunction = hq2x32; break; case 13: filterFunction = lq2x32; break; default: filterFunction = NULL; break; } } if(systemColorDepth == 16) { switch(ifbType) { case 0: default: ifbFunction = NULL; break; case 1: ifbFunction = MotionBlurIB; break; case 2: ifbFunction = SmartIB; break; } } else if(systemColorDepth == 32) { switch(ifbType) { case 0: default: ifbFunction = NULL; break; case 1: ifbFunction = MotionBlurIB32; break; case 2: ifbFunction = SmartIB32; break; } } else ifbFunction = NULL; if(delta == NULL) { delta = (u8*)malloc(322*242*4); memset(delta, 255, 322*242*4); } emulating = 1; renderedFrames = 0; if(!soundOffFlag) soundInit(); autoFrameSkipLastTime = throttleLastTime = systemGetClock(); SDL_WM_SetCaption("VisualBoyAdvance", NULL); while(emulating) { if(!paused && active) { if(debugger && emulator.emuHasDebugger) dbgMain(); else { emulator.emuMain(emulator.emuCount); if(rewindSaveNeeded && rewindMemory && emulator.emuWriteMemState) { rewindCount++; if(rewindCount > 8) rewindCount = 8; if(emulator.emuWriteMemState && emulator.emuWriteMemState(&rewindMemory[rewindPos*REWIND_SIZE], REWIND_SIZE)) { rewindPos = ++rewindPos & 7; if(rewindCount == 8) rewindTopPos = ++rewindTopPos & 7; } } rewindSaveNeeded = false; } } else { SDL_Delay(500); } sdlPollEvents(); if(mouseCounter) { mouseCounter--; if(mouseCounter == 0) SDL_ShowCursor(SDL_DISABLE); } } emulating = 0; fprintf(stderr,"Shutting down\n"); remoteCleanUp(); soundShutdown(); if(gbRom != NULL || rom != NULL) { sdlWriteBattery(); emulator.emuCleanUp(); } if(delta) { free(delta); delta = NULL; } SDL_Quit(); return 0; } void systemMessage(int num, const char *msg, ...) { char buffer[2048]; va_list valist; va_start(valist, msg); vsprintf(buffer, msg, valist); fprintf(stderr, "%s\n", buffer); va_end(valist); } void systemDrawScreen() { renderedFrames++; if(yuv) { Draw_Overlay(surface, sizeOption+1); return; } SDL_LockSurface(surface); if(screenMessage) { if(cartridgeType == 1 && gbBorderOn) { gbSgbRenderBorder(); } if(((systemGetClock() - screenMessageTime) < 3000) && !disableStatusMessages) { drawText(pix, srcPitch, 10, srcHeight - 20, screenMessageBuffer); } else { screenMessage = false; } } if(ifbFunction) { if(systemColorDepth == 16) ifbFunction(pix+destWidth+4, destWidth+4, srcWidth, srcHeight); else ifbFunction(pix+destWidth*2+4, destWidth*2+4, srcWidth, srcHeight); } if(filterFunction) { if(systemColorDepth == 16) filterFunction(pix+destWidth+4,destWidth+4, delta, (u8*)surface->pixels,surface->pitch, srcWidth, srcHeight); else filterFunction(pix+destWidth*2+4, destWidth*2+4, delta, (u8*)surface->pixels, surface->pitch, srcWidth, srcHeight); } else { int destPitch = surface->pitch; u8 *src = pix; u8 *dest = (u8*)surface->pixels; int i; u32 *stretcher = (u32 *)sdlStretcher; if(systemColorDepth == 16) src += srcPitch; int option = sizeOption; if(yuv) option = 0; switch(sizeOption) { case 0: for(i = 0; i < srcHeight; i++) { SDL_CALL_STRETCHER; src += srcPitch; dest += destPitch; } break; case 1: for(i = 0; i < srcHeight; i++) { SDL_CALL_STRETCHER; dest += destPitch; SDL_CALL_STRETCHER; src += srcPitch; dest += destPitch; } break; case 2: for(i = 0; i < srcHeight; i++) { SDL_CALL_STRETCHER; dest += destPitch; SDL_CALL_STRETCHER; dest += destPitch; SDL_CALL_STRETCHER; src += srcPitch; dest += destPitch; } break; case 3: for(i = 0; i < srcHeight; i++) { SDL_CALL_STRETCHER; dest += destPitch; SDL_CALL_STRETCHER; dest += destPitch; SDL_CALL_STRETCHER; dest += destPitch; SDL_CALL_STRETCHER; src += srcPitch; dest += destPitch; } break; } } if(showSpeed && fullscreen) { char buffer[50]; if(showSpeed == 1) sprintf(buffer, "%d%%", systemSpeed); else sprintf(buffer, "%3d%%(%d, %d fps)", systemSpeed, systemFrameSkip, showRenderedFrames); if(showSpeedTransparent) drawTextTransp((u8*)surface->pixels, surface->pitch, 10, surface->h-20, buffer); else drawText((u8*)surface->pixels, surface->pitch, 10, surface->h-20, buffer); } SDL_UnlockSurface(surface); // SDL_UpdateRect(surface, 0, 0, destWidth, destHeight); SDL_Flip(surface); } bool systemReadJoypads() { return true; } u32 systemReadJoypad(int which) { if(which < 0 || which > 3) which = sdlDefaultJoypad; u32 res = 0; if(sdlButtons[which][KEY_BUTTON_A]) res |= 1; if(sdlButtons[which][KEY_BUTTON_B]) res |= 2; if(sdlButtons[which][KEY_BUTTON_SELECT]) res |= 4; if(sdlButtons[which][KEY_BUTTON_START]) res |= 8; if(sdlButtons[which][KEY_RIGHT]) res |= 16; if(sdlButtons[which][KEY_LEFT]) res |= 32; if(sdlButtons[which][KEY_UP]) res |= 64; if(sdlButtons[which][KEY_DOWN]) res |= 128; if(sdlButtons[which][KEY_BUTTON_R]) res |= 256; if(sdlButtons[which][KEY_BUTTON_L]) res |= 512; // disallow L+R or U+D of being pressed at the same time if((res & 48) == 48) res &= ~16; if((res & 192) == 192) res &= ~128; if(sdlButtons[which][KEY_BUTTON_SPEED]) res |= 1024; if(sdlButtons[which][KEY_BUTTON_CAPTURE]) res |= 2048; if(autoFire) { res &= (~autoFire); if(autoFireToggle) res |= autoFire; autoFireToggle = !autoFireToggle; } return res; } void systemSetTitle(const char *title) { SDL_WM_SetCaption(title, NULL); } void systemShowSpeed(int speed) { systemSpeed = speed; showRenderedFrames = renderedFrames; renderedFrames = 0; if(!fullscreen && showSpeed) { char buffer[80]; if(showSpeed == 1) sprintf(buffer, "VisualBoyAdvance-%3d%%", systemSpeed); else sprintf(buffer, "VisualBoyAdvance-%3d%%(%d, %d fps)", systemSpeed, systemFrameSkip, showRenderedFrames); systemSetTitle(buffer); } } void systemFrame() { } void system10Frames(int rate) { u32 time = systemGetClock(); if(!wasPaused && autoFrameSkip && !throttle) { u32 diff = time - autoFrameSkipLastTime; int speed = 100; if(diff) speed = (1000000/rate)/diff; if(speed >= 98) { frameskipadjust++; if(frameskipadjust >= 3) { frameskipadjust=0; if(systemFrameSkip > 0) systemFrameSkip--; } } else { if(speed < 80) frameskipadjust -= (90 - speed)/5; else if(systemFrameSkip < 9) frameskipadjust--; if(frameskipadjust <= -2) { frameskipadjust += 2; if(systemFrameSkip < 9) systemFrameSkip++; } } } if(!wasPaused && throttle) { if(!speedup) { u32 diff = time - throttleLastTime; int target = (1000000/(rate*throttle)); int d = (target - diff); if(d > 0) { SDL_Delay(d); } } throttleLastTime = systemGetClock(); } if(rewindMemory) { if(++rewindCounter >= rewindTimer) { rewindSaveNeeded = true; rewindCounter = 0; } } if(systemSaveUpdateCounter) { if(--systemSaveUpdateCounter <= SYSTEM_SAVE_NOT_UPDATED) { sdlWriteBattery(); systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; } } wasPaused = false; autoFrameSkipLastTime = time; } void systemScreenCapture(int a) { char buffer[2048]; if(captureFormat) { if(captureDir[0]) sprintf(buffer, "%s/%s%02d.bmp", captureDir, sdlGetFilename(filename), a); else sprintf(buffer, "%s%02d.bmp", filename, a); emulator.emuWriteBMP(buffer); } else { if(captureDir[0]) sprintf(buffer, "%s/%s%02d.png", captureDir, sdlGetFilename(filename), a); else sprintf(buffer, "%s%02d.png", filename, a); emulator.emuWritePNG(buffer); } systemScreenMessage("Screen capture"); } void soundCallback(void *,u8 *stream,int len) { if(!emulating) return; SDL_mutexP(mutex); // printf("Locked mutex\n"); if(!speedup && !throttle) { while(sdlSoundLen < 2048*2) { if(emulating) SDL_CondWait(cond, mutex); else break; } } if(emulating) { // printf("Copying data\n"); memcpy(stream, sdlBuffer, len); } sdlSoundLen = 0; if(mutex) SDL_mutexV(mutex); } void systemWriteDataToSoundBuffer() { if(SDL_GetAudioStatus() != SDL_AUDIO_PLAYING) SDL_PauseAudio(0); bool cont = true; while(cont && !speedup && !throttle) { SDL_mutexP(mutex); // printf("Waiting for len < 2048 (speed up %d)\n", speedup); if(sdlSoundLen < 2048*2) cont = false; SDL_mutexV(mutex); } int len = soundBufferLen; int copied = 0; if((sdlSoundLen+len) >= 2048*2) { // printf("Case 1\n"); memcpy(&sdlBuffer[sdlSoundLen],soundFinalWave, 2048*2-sdlSoundLen); copied = 2048*2 - sdlSoundLen; sdlSoundLen = 2048*2; SDL_CondSignal(cond); cont = true; if(!speedup && !throttle) { while(cont) { SDL_mutexP(mutex); if(sdlSoundLen < 2048*2) cont = false; SDL_mutexV(mutex); } memcpy(&sdlBuffer[0],&(((u8 *)soundFinalWave)[copied]), soundBufferLen-copied); sdlSoundLen = soundBufferLen-copied; } else { memcpy(&sdlBuffer[0], &(((u8 *)soundFinalWave)[copied]), soundBufferLen); } } else { // printf("case 2\n"); memcpy(&sdlBuffer[sdlSoundLen], soundFinalWave, soundBufferLen); sdlSoundLen += soundBufferLen; } } bool systemSoundInit() { SDL_AudioSpec audio; switch(soundQuality) { case 1: audio.freq = 44100; soundBufferLen = 1470*2; break; case 2: audio.freq = 22050; soundBufferLen = 736*2; break; case 4: audio.freq = 11025; soundBufferLen = 368*2; break; } audio.format=AUDIO_S16SYS; audio.channels = 2; audio.samples = 1024; audio.callback = soundCallback; audio.userdata = NULL; if(SDL_OpenAudio(&audio, NULL)) { fprintf(stderr,"Failed to open audio: %s\n", SDL_GetError()); return false; } soundBufferTotalLen = soundBufferLen*10; cond = SDL_CreateCond(); mutex = SDL_CreateMutex(); sdlSoundLen = 0; systemSoundOn = true; return true; } void systemSoundShutdown() { SDL_mutexP(mutex); SDL_CondSignal(cond); SDL_mutexV(mutex); SDL_DestroyCond(cond); cond = NULL; SDL_DestroyMutex(mutex); mutex = NULL; SDL_CloseAudio(); } void systemSoundPause() { SDL_PauseAudio(1); } void systemSoundResume() { SDL_PauseAudio(0); } void systemSoundReset() { } u32 systemGetClock() { return SDL_GetTicks(); } void systemUpdateMotionSensor() { if(sdlMotionButtons[KEY_LEFT]) { sensorX += 3; if(sensorX > 2197) sensorX = 2197; if(sensorX < 2047) sensorX = 2057; } else if(sdlMotionButtons[KEY_RIGHT]) { sensorX -= 3; if(sensorX < 1897) sensorX = 1897; if(sensorX > 2047) sensorX = 2037; } else if(sensorX > 2047) { sensorX -= 2; if(sensorX < 2047) sensorX = 2047; } else { sensorX += 2; if(sensorX > 2047) sensorX = 2047; } if(sdlMotionButtons[KEY_UP]) { sensorY += 3; if(sensorY > 2197) sensorY = 2197; if(sensorY < 2047) sensorY = 2057; } else if(sdlMotionButtons[KEY_DOWN]) { sensorY -= 3; if(sensorY < 1897) sensorY = 1897; if(sensorY > 2047) sensorY = 2037; } else if(sensorY > 2047) { sensorY -= 2; if(sensorY < 2047) sensorY = 2047; } else { sensorY += 2; if(sensorY > 2047) sensorY = 2047; } } int systemGetSensorX() { return sensorX; } int systemGetSensorY() { return sensorY; } void systemGbPrint(u8 *data,int pages,int feed,int palette, int contrast) { } void systemScreenMessage(const char *msg) { screenMessage = true; screenMessageTime = systemGetClock(); if(strlen(msg) > 20) { strncpy(screenMessageBuffer, msg, 20); screenMessageBuffer[20] = 0; } else strcpy(screenMessageBuffer, msg); } bool systemCanChangeSoundQuality() { return false; } bool systemPauseOnFrame() { if(pauseNextFrame) { paused = true; pauseNextFrame = false; return true; } return false; } // Code donated by Niels Wagenaar (BoycottAdvance) // GBA screensize. #define GBA_WIDTH 240 #define GBA_HEIGHT 160 void Init_Overlay(SDL_Surface *gbascreen, int overlaytype) { overlay = SDL_CreateYUVOverlay( GBA_WIDTH, GBA_HEIGHT, overlaytype, gbascreen); fprintf(stderr, "Created %dx%dx%d %s %s overlay\n", overlay->w,overlay->h,overlay->planes, overlay->hw_overlay?"hardware":"software", overlay->format==SDL_YV12_OVERLAY?"YV12": overlay->format==SDL_IYUV_OVERLAY?"IYUV": overlay->format==SDL_YUY2_OVERLAY?"YUY2": overlay->format==SDL_UYVY_OVERLAY?"UYVY": overlay->format==SDL_YVYU_OVERLAY?"YVYU": "Unknown"); } void Quit_Overlay(void) { SDL_FreeYUVOverlay(overlay); } /* NOTE: These RGB conversion functions are not intended for speed, only as examples. */ inline void RGBtoYUV(Uint8 *rgb, int *yuv) { yuv[0] = (int)((0.257 * rgb[0]) + (0.504 * rgb[1]) + (0.098 * rgb[2]) + 16); yuv[1] = (int)(128 - (0.148 * rgb[0]) - (0.291 * rgb[1]) + (0.439 * rgb[2])); yuv[2] = (int)(128 + (0.439 * rgb[0]) - (0.368 * rgb[1]) - (0.071 * rgb[2])); } inline void ConvertRGBtoYV12(SDL_Overlay *o) { int x,y; int yuv[3]; Uint8 *p,*op[3]; SDL_LockYUVOverlay(o); /* Black initialization */ /* memset(o->pixels[0],0,o->pitches[0]*o->h); memset(o->pixels[1],128,o->pitches[1]*((o->h+1)/2)); memset(o->pixels[2],128,o->pitches[2]*((o->h+1)/2)); */ /* Convert */ for(y=0; y<160 && yh; y++) { p=(Uint8 *)pix+srcPitch*y; op[0]=o->pixels[0]+o->pitches[0]*y; op[1]=o->pixels[1]+o->pitches[1]*(y/2); op[2]=o->pixels[2]+o->pitches[2]*(y/2); for(x=0; x<240 && xw; x++) { RGBtoYUV(p,yuv); *(op[0]++)=yuv[0]; if(x%2==0 && y%2==0) { *(op[1]++)=yuv[2]; *(op[2]++)=yuv[1]; } p+=4;//s->format->BytesPerPixel; } } SDL_UnlockYUVOverlay(o); } inline void ConvertRGBtoIYUV(SDL_Overlay *o) { int x,y; int yuv[3]; Uint8 *p,*op[3]; SDL_LockYUVOverlay(o); /* Black initialization */ /* memset(o->pixels[0],0,o->pitches[0]*o->h); memset(o->pixels[1],128,o->pitches[1]*((o->h+1)/2)); memset(o->pixels[2],128,o->pitches[2]*((o->h+1)/2)); */ /* Convert */ for(y=0; y<160 && yh; y++) { p=(Uint8 *)pix+srcPitch*y; op[0]=o->pixels[0]+o->pitches[0]*y; op[1]=o->pixels[1]+o->pitches[1]*(y/2); op[2]=o->pixels[2]+o->pitches[2]*(y/2); for(x=0; x<240 && xw; x++) { RGBtoYUV(p,yuv); *(op[0]++)=yuv[0]; if(x%2==0 && y%2==0) { *(op[1]++)=yuv[1]; *(op[2]++)=yuv[2]; } p+=4; //s->format->BytesPerPixel; } } SDL_UnlockYUVOverlay(o); } inline void ConvertRGBtoUYVY(SDL_Overlay *o) { int x,y; int yuv[3]; Uint8 *p,*op; SDL_LockYUVOverlay(o); for(y=0; y<160 && yh; y++) { p=(Uint8 *)pix+srcPitch*y; op=o->pixels[0]+o->pitches[0]*y; for(x=0; x<240 && xw; x++) { RGBtoYUV(p,yuv); if(x%2==0) { *(op++)=yuv[1]; *(op++)=yuv[0]; *(op++)=yuv[2]; } else *(op++)=yuv[0]; p+=4; //s->format->BytesPerPixel; } } SDL_UnlockYUVOverlay(o); } inline void ConvertRGBtoYVYU(SDL_Overlay *o) { int x,y; int yuv[3]; Uint8 *p,*op; SDL_LockYUVOverlay(o); for(y=0; y<160 && yh; y++) { p=(Uint8 *)pix+srcPitch*y; op=o->pixels[0]+o->pitches[0]*y; for(x=0; x<240 && xw; x++) { RGBtoYUV(p,yuv); if(x%2==0) { *(op++)=yuv[0]; *(op++)=yuv[2]; op[1]=yuv[1]; } else { *op=yuv[0]; op+=2; } p+=4; //s->format->BytesPerPixel; } } SDL_UnlockYUVOverlay(o); } inline void ConvertRGBtoYUY2(SDL_Overlay *o) { int x,y; int yuv[3]; Uint8 *p,*op; SDL_LockYUVOverlay(o); for(y=0; y<160 && yh; y++) { p=(Uint8 *)pix+srcPitch*y; op=o->pixels[0]+o->pitches[0]*y; for(x=0; x<240 && xw; x++) { RGBtoYUV(p,yuv); if(x%2==0) { *(op++)=yuv[0]; *(op++)=yuv[1]; op[1]=yuv[2]; } else { *op=yuv[0]; op+=2; } p+=4; //s->format->BytesPerPixel; } } SDL_UnlockYUVOverlay(o); } inline void Convert32bit(SDL_Surface *display) { switch(overlay->format) { case SDL_YV12_OVERLAY: ConvertRGBtoYV12(overlay); break; case SDL_UYVY_OVERLAY: ConvertRGBtoUYVY(overlay); break; case SDL_YVYU_OVERLAY: ConvertRGBtoYVYU(overlay); break; case SDL_YUY2_OVERLAY: ConvertRGBtoYUY2(overlay); break; case SDL_IYUV_OVERLAY: ConvertRGBtoIYUV(overlay); break; default: fprintf(stderr, "cannot convert RGB picture to obtained YUV format!\n"); exit(1); break; } } inline void Draw_Overlay(SDL_Surface *display, int size) { SDL_LockYUVOverlay(overlay); Convert32bit(display); overlay_rect.x = 0; overlay_rect.y = 0; overlay_rect.w = GBA_WIDTH * size; overlay_rect.h = GBA_HEIGHT * size; SDL_DisplayYUVOverlay(overlay, &overlay_rect); SDL_UnlockYUVOverlay(overlay); } void systemGbBorderOn() { srcWidth = 256; srcHeight = 224; gbBorderLineSkip = 256; gbBorderColumnSkip = 48; gbBorderRowSkip = 40; destWidth = (sizeOption+1)*srcWidth; destHeight = (sizeOption+1)*srcHeight; surface = SDL_SetVideoMode(destWidth, destHeight, 16, SDL_ANYFORMAT|SDL_HWSURFACE|SDL_DOUBLEBUF| (fullscreen ? SDL_FULLSCREEN : 0)); #ifndef C_CORE sdlMakeStretcher(srcWidth); #else switch(systemColorDepth) { case 16: sdlStretcher = sdlStretcher16[sizeOption]; break; case 24: sdlStretcher = sdlStretcher24[sizeOption]; break; case 32: sdlStretcher = sdlStretcher32[sizeOption]; break; default: fprintf(stderr, "Unsupported resolution: %d\n", systemColorDepth); exit(-1); } #endif if(systemColorDepth == 16) { if(sdlCalculateMaskWidth(surface->format->Gmask) == 6) { Init_2xSaI(565); RGB_LOW_BITS_MASK = 0x821; } else { Init_2xSaI(555); RGB_LOW_BITS_MASK = 0x421; } if(cartridgeType == 2) { for(int i = 0; i < 0x10000; i++) { systemColorMap16[i] = (((i >> 1) & 0x1f) << systemBlueShift) | (((i & 0x7c0) >> 6) << systemGreenShift) | (((i & 0xf800) >> 11) << systemRedShift); } } else { for(int i = 0; i < 0x10000; i++) { systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | (((i & 0x3e0) >> 5) << systemGreenShift) | (((i & 0x7c00) >> 10) << systemBlueShift); } } srcPitch = srcWidth * 2+4; } else { if(systemColorDepth != 32) filterFunction = NULL; RGB_LOW_BITS_MASK = 0x010101; if(systemColorDepth == 32) { Init_2xSaI(32); } for(int i = 0; i < 0x10000; i++) { systemColorMap32[i] = ((i & 0x1f) << systemRedShift) | (((i & 0x3e0) >> 5) << systemGreenShift) | (((i & 0x7c00) >> 10) << systemBlueShift); } if(systemColorDepth == 32) srcPitch = srcWidth*4 + 4; else srcPitch = srcWidth*3; } }