1796 lines
52 KiB
C++
1796 lines
52 KiB
C++
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "SoundRetro.h"
|
|
#include "libretro.h"
|
|
|
|
#include "../System.h"
|
|
#include "../Util.h"
|
|
#include "../apu/Blip_Buffer.h"
|
|
#include "../apu/Gb_Apu.h"
|
|
#include "../apu/Gb_Oscs.h"
|
|
#include "../common/Port.h"
|
|
#include "../common/ConfigManager.h"
|
|
#include "../gba/Cheats.h"
|
|
#include "../gba/EEprom.h"
|
|
#include "../gba/Flash.h"
|
|
#include "../gba/GBAGfx.h"
|
|
#include "../gba/Globals.h"
|
|
#include "../gba/RTC.h"
|
|
#include "../gba/Sound.h"
|
|
#include "../gba/bios.h"
|
|
|
|
#include "../gb/gb.h"
|
|
#include "../gb/gbCheats.h"
|
|
#include "../gb/gbGlobals.h"
|
|
#include "../gb/gbMemory.h"
|
|
#include "../gb/gbSGB.h"
|
|
#include "../gb/gbSound.h"
|
|
|
|
static retro_log_printf_t log_cb;
|
|
static retro_video_refresh_t video_cb;
|
|
static retro_input_poll_t poll_cb;
|
|
static retro_input_state_t input_cb;
|
|
static retro_environment_t environ_cb;
|
|
retro_audio_sample_batch_t audio_batch_cb;
|
|
static retro_set_rumble_state_t rumble_cb;
|
|
|
|
static char retro_system_directory[4096];
|
|
static char biosfile[4096];
|
|
static float sndFiltering = 0.5f;
|
|
static bool sndInterpolation = true;
|
|
static bool can_dupe = false;
|
|
static bool usebios = false;
|
|
static unsigned retropad_device[4] = {0};
|
|
|
|
static const double FramesPerSecond = (16777216.0 / 280896.0); // 59.73
|
|
static const long SampleRate = 32768;
|
|
static const int GBWidth = 160;
|
|
static const int GBHeight = 144;
|
|
static const int SGBWidth = 256;
|
|
static const int SGBHeight = 224;
|
|
static const int GBAWidth = 240;
|
|
static const int GBAHeight = 160;
|
|
static unsigned width = 240;
|
|
static unsigned height = 160;
|
|
static EmulatedSystem* core = NULL;
|
|
static IMAGE_TYPE type = IMAGE_UNKNOWN;
|
|
static unsigned current_gbPalette;
|
|
|
|
uint16_t systemColorMap16[0x10000];
|
|
uint32_t systemColorMap32[0x10000];
|
|
int RGB_LOW_BITS_MASK = 0;
|
|
int systemRedShift = 0;
|
|
int systemBlueShift = 0;
|
|
int systemGreenShift = 0;
|
|
int systemColorDepth = 32;
|
|
int systemDebug = 0;
|
|
int systemVerbose = 0;
|
|
int systemFrameSkip = 0;
|
|
int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
|
|
int systemSpeed = 0;
|
|
int emulating = 0;
|
|
|
|
void (*dbgOutput)(const char* s, uint32_t addr);
|
|
void (*dbgSignal)(int sig, int number);
|
|
|
|
// Dummy vars/funcs for serial io emulation without LINK communication related stuff
|
|
#ifndef NO_LINK
|
|
#include "../gba/GBALink.h"
|
|
uint8_t gbSIO_SC;
|
|
bool LinkIsWaiting;
|
|
bool LinkFirstTime;
|
|
bool EmuReseted;
|
|
int winGbPrinterEnabled;
|
|
bool gba_joybus_active = false;
|
|
|
|
LinkMode GetLinkMode()
|
|
{
|
|
return LINK_DISCONNECTED;
|
|
}
|
|
|
|
void StartGPLink(uint16_t value)
|
|
{
|
|
}
|
|
|
|
void LinkUpdate(int ticks)
|
|
{
|
|
}
|
|
|
|
void StartLink(uint16_t siocnt)
|
|
{
|
|
}
|
|
|
|
void CheckLinkConnection()
|
|
{
|
|
}
|
|
|
|
void gbInitLink()
|
|
{
|
|
LinkIsWaiting = false;
|
|
LinkFirstTime = true;
|
|
}
|
|
|
|
uint16_t gbLinkUpdate(uint8_t b, int gbSerialOn) //used on external clock
|
|
{
|
|
return (b << 8);
|
|
}
|
|
#endif
|
|
|
|
#define GS555(x) (x | (x << 5) | (x << 10))
|
|
uint16_t systemGbPalette[24] = {
|
|
GS555(0x1f), GS555(0x15), GS555(0x0c), 0,
|
|
GS555(0x1f), GS555(0x15), GS555(0x0c), 0,
|
|
GS555(0x1f), GS555(0x15), GS555(0x0c), 0,
|
|
GS555(0x1f), GS555(0x15), GS555(0x0c), 0,
|
|
GS555(0x1f), GS555(0x15), GS555(0x0c), 0,
|
|
GS555(0x1f), GS555(0x15), GS555(0x0c), 0
|
|
};
|
|
|
|
static const uint16_t defaultGBPalettes[][8] = {
|
|
{
|
|
// Standard
|
|
0x7FFF, 0x56B5, 0x318C, 0x0000, 0x7FFF, 0x56B5, 0x318C, 0x0000,
|
|
},
|
|
{
|
|
// Blue Sea
|
|
0x6200, 0x7E10, 0x7C10, 0x5000, 0x6200, 0x7E10, 0x7C10, 0x5000,
|
|
},
|
|
{
|
|
// Dark Night
|
|
0x4008, 0x4000, 0x2000, 0x2008, 0x4008, 0x4000, 0x2000, 0x2008,
|
|
},
|
|
{
|
|
// Green Forest
|
|
0x43F0, 0x03E0, 0x4200, 0x2200, 0x43F0, 0x03E0, 0x4200, 0x2200,
|
|
},
|
|
{
|
|
// Hot Desert
|
|
0x43FF, 0x03FF, 0x221F, 0x021F, 0x43FF, 0x03FF, 0x221F, 0x021F,
|
|
},
|
|
{
|
|
// Pink Dreams
|
|
0x621F, 0x7E1F, 0x7C1F, 0x2010, 0x621F, 0x7E1F, 0x7C1F, 0x2010,
|
|
},
|
|
{
|
|
// Weird Colors
|
|
0x621F, 0x401F, 0x001F, 0x2010, 0x621F, 0x401F, 0x001F, 0x2010,
|
|
},
|
|
{
|
|
// Real GB Colors
|
|
0x1B8E, 0x02C0, 0x0DA0, 0x1140, 0x1B8E, 0x02C0, 0x0DA0, 0x1140,
|
|
},
|
|
{
|
|
// Real 'GB on GBASP' Colors
|
|
0x7BDE, /*0x23F0*/ 0x5778, /*0x5DC0*/ 0x5640, 0x0000, 0x7BDE, /*0x3678*/ 0x529C, /*0x0980*/ 0x2990, 0x0000,
|
|
}
|
|
};
|
|
|
|
static void set_gbPalette(void)
|
|
{
|
|
const uint16_t *pal = defaultGBPalettes[current_gbPalette];
|
|
|
|
if (type != IMAGE_GB)
|
|
return;
|
|
|
|
if (gbCgbMode || gbSgbMode)
|
|
return;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
gbPalette[i] = pal[i];
|
|
}
|
|
|
|
extern int gbRomType; // gets type from header 0x147
|
|
extern int gbBattery; // enabled when gbRamSize != 0
|
|
|
|
static bool gb_hasrtc(void)
|
|
{
|
|
switch (gbRomType) {
|
|
case 0x0f:
|
|
case 0x10: // MBC3 + extended
|
|
case 0x13:
|
|
case 0xfd: // TAMA5 + extended
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void* gb_rtcdata_prt(void)
|
|
{
|
|
if (gb_hasrtc()) {
|
|
switch (gbRomType) {
|
|
case 0x0f:
|
|
case 0x10: // MBC3 + extended
|
|
return &gbDataMBC3.mapperSeconds;
|
|
case 0x13:
|
|
case 0xfd: // TAMA5 + extended
|
|
return &gbDataTAMA5.mapperSeconds;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static size_t gb_rtcdata_size(void)
|
|
{
|
|
if (gb_hasrtc()) {
|
|
switch (gbRomType) {
|
|
case 0x0f:
|
|
case 0x10: // MBC3 + extended
|
|
return (10 * sizeof(int) + sizeof(time_t));
|
|
break;
|
|
case 0x13:
|
|
case 0xfd: // TAMA5 + extended
|
|
return (14 * sizeof(int) + sizeof(time_t));
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void gbUpdateRTC(void)
|
|
{
|
|
if (gb_hasrtc()) {
|
|
struct tm* lt;
|
|
time_t rawtime;
|
|
time(&rawtime);
|
|
lt = localtime(&rawtime);
|
|
|
|
switch (gbRomType) {
|
|
case 0x0f:
|
|
case 0x10: {
|
|
gbDataMBC3.mapperSeconds = lt->tm_sec;
|
|
gbDataMBC3.mapperMinutes = lt->tm_min;
|
|
gbDataMBC3.mapperHours = lt->tm_hour;
|
|
gbDataMBC3.mapperDays = lt->tm_yday & 255;
|
|
gbDataMBC3.mapperControl = (gbDataMBC3.mapperControl & 0xfe) | (lt->tm_yday > 255 ? 1 : 0);
|
|
gbDataMBC3.mapperLastTime = rawtime;
|
|
}
|
|
break;
|
|
case 0xfd: {
|
|
uint8_t gbDaysinMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
|
gbDataTAMA5.mapperSeconds = lt->tm_sec;
|
|
gbDataTAMA5.mapperMinutes = lt->tm_min;
|
|
gbDataTAMA5.mapperHours = lt->tm_hour;
|
|
gbDataTAMA5.mapperDays = 1;
|
|
gbDataTAMA5.mapperMonths = 1;
|
|
gbDataTAMA5.mapperYears = 1970;
|
|
gbDataTAMA5.mapperLastTime = rawtime;
|
|
int days = lt->tm_yday + 365 * 3;
|
|
while (days) {
|
|
gbDataTAMA5.mapperDays++;
|
|
days--;
|
|
if (gbDataTAMA5.mapperDays > gbDaysinMonth[gbDataTAMA5.mapperMonths - 1]) {
|
|
gbDataTAMA5.mapperDays = 1;
|
|
gbDataTAMA5.mapperMonths++;
|
|
if (gbDataTAMA5.mapperMonths > 12) {
|
|
gbDataTAMA5.mapperMonths = 1;
|
|
gbDataTAMA5.mapperYears++;
|
|
if ((gbDataTAMA5.mapperYears & 3) == 0)
|
|
gbDaysinMonth[1] = 29;
|
|
else
|
|
gbDaysinMonth[1] = 28;
|
|
}
|
|
}
|
|
}
|
|
gbDataTAMA5.mapperControl = (gbDataTAMA5.mapperControl & 0xfe) | (lt->tm_yday > 255 ? 1 : 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void* retro_get_memory_data(unsigned id)
|
|
{
|
|
if (type == IMAGE_GBA) {
|
|
switch (id) {
|
|
case RETRO_MEMORY_SAVE_RAM:
|
|
if ((saveType == GBA_SAVE_EEPROM) | (saveType == GBA_SAVE_EEPROM_SENSOR))
|
|
return eepromData;
|
|
if ((saveType == GBA_SAVE_SRAM) | (saveType == GBA_SAVE_FLASH))
|
|
return flashSaveMemory;
|
|
return NULL;
|
|
case RETRO_MEMORY_SYSTEM_RAM:
|
|
return workRAM;
|
|
case RETRO_MEMORY_VIDEO_RAM:
|
|
return vram;
|
|
}
|
|
}
|
|
else if (type == IMAGE_GB) {
|
|
switch (id) {
|
|
case RETRO_MEMORY_SAVE_RAM:
|
|
if (gbBattery)
|
|
return gbRam;
|
|
return NULL;
|
|
case RETRO_MEMORY_SYSTEM_RAM:
|
|
return gbMemoryMap[0x0c];
|
|
case RETRO_MEMORY_VIDEO_RAM:
|
|
return gbMemoryMap[0x08] ;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
size_t retro_get_memory_size(unsigned id)
|
|
{
|
|
if (type == IMAGE_GBA) {
|
|
switch (id) {
|
|
case RETRO_MEMORY_SAVE_RAM:
|
|
if ((saveType == GBA_SAVE_EEPROM) | (saveType == GBA_SAVE_EEPROM_SENSOR))
|
|
return eepromSize;
|
|
if (saveType == GBA_SAVE_FLASH)
|
|
return flashSize;
|
|
if (saveType == GBA_SAVE_SRAM)
|
|
return SIZE_SRAM;
|
|
return 0;
|
|
case RETRO_MEMORY_SYSTEM_RAM:
|
|
return SIZE_WRAM;
|
|
case RETRO_MEMORY_VIDEO_RAM:
|
|
return SIZE_VRAM - 0x2000; // usuable vram is only 0x18000
|
|
}
|
|
}
|
|
else if (type == IMAGE_GB) {
|
|
switch (id) {
|
|
case RETRO_MEMORY_SAVE_RAM:
|
|
if (gbBattery)
|
|
return gbRamSize;
|
|
return 0;
|
|
case RETRO_MEMORY_SYSTEM_RAM:
|
|
return 0x2000;
|
|
case RETRO_MEMORY_VIDEO_RAM:
|
|
return 0x2000;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned retro_api_version(void)
|
|
{
|
|
return RETRO_API_VERSION;
|
|
}
|
|
|
|
void retro_set_video_refresh(retro_video_refresh_t cb)
|
|
{
|
|
video_cb = cb;
|
|
}
|
|
|
|
void retro_set_audio_sample(retro_audio_sample_t cb)
|
|
{
|
|
}
|
|
|
|
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
|
|
{
|
|
audio_batch_cb = cb;
|
|
}
|
|
|
|
void retro_set_input_poll(retro_input_poll_t cb)
|
|
{
|
|
poll_cb = cb;
|
|
}
|
|
|
|
void retro_set_input_state(retro_input_state_t cb)
|
|
{
|
|
input_cb = cb;
|
|
}
|
|
|
|
static void update_input_descriptors(void);
|
|
|
|
static struct retro_input_descriptor input_gba[] = {
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Turbo B" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Turbo A" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "Solar Sensor (Darker)" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "Solar Sensor (Lighter)" },
|
|
{ 0, 0, 0, 0, NULL },
|
|
};
|
|
|
|
static struct retro_input_descriptor input_gb[] = {
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "B" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "A" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Turbo B" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Turbo A" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 0, 0, 0, 0, NULL },
|
|
};
|
|
|
|
static struct retro_input_descriptor input_sgb[] = {
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "B" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "A" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Turbo B" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Turbo A" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "B" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "A" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Turbo B" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Turbo A" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "B" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "A" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Turbo B" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Turbo A" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "B" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "A" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Turbo B" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Turbo A" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 0, 0, 0, 0, NULL },
|
|
};
|
|
|
|
static const struct retro_controller_description port_gba[] = {
|
|
{ "GBA Joypad", RETRO_DEVICE_JOYPAD },
|
|
{ NULL, 0 },
|
|
};
|
|
|
|
static const struct retro_controller_description port_gb[] = {
|
|
{ "GB Joypad", RETRO_DEVICE_JOYPAD },
|
|
{ NULL, 0 },
|
|
};
|
|
|
|
static const struct retro_controller_info ports_gba[] = {
|
|
{ port_gba, 1},
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
static const struct retro_controller_info ports_gb[] = {
|
|
{ port_gb, 1},
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
static const struct retro_controller_info ports_sgb[] = {
|
|
{ port_gb, 1},
|
|
{ port_gb, 1},
|
|
{ port_gb, 1},
|
|
{ port_gb, 1},
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
void retro_set_controller_port_device(unsigned port, unsigned device)
|
|
{
|
|
if (port > 3) return;
|
|
|
|
retropad_device[port] = device;
|
|
log_cb(RETRO_LOG_INFO, "Controller %d device: %d\n", port + 1, device);
|
|
}
|
|
|
|
void retro_set_environment(retro_environment_t cb)
|
|
{
|
|
environ_cb = cb;
|
|
|
|
struct retro_variable variables[] = {
|
|
{ "vbam_solarsensor", "Solar Sensor Level; 0|1|2|3|4|5|6|7|8|9|10" },
|
|
{ "vbam_usebios", "Use BIOS file (Restart); disabled|enabled" },
|
|
{ "vbam_soundinterpolation", "Sound Interpolation; enabled|disabled" },
|
|
{ "vbam_soundfiltering", "Sound Filtering; 5|6|7|8|9|10|0|1|2|3|4" },
|
|
{ "vbam_gbHardware", "(GB) Emulated Hardware; Game Boy Color|Automatic|Super Game Boy|Game Boy|Game Boy Advance|Super Game Boy 2" },
|
|
{ "vbam_palettes", "(GB) Color Palette; Standard|Blue Sea|Dark Knight|Green Forest|Hot Desert|Pink Dreams|Wierd Colors|Original|GBA SP" },
|
|
{ "vbam_showborders", "(GB) Show Borders; disabled|enabled|auto" },
|
|
{ "vbam_turboenable", "Enable Turbo Buttons; disabled|enabled" },
|
|
{ "vbam_turbodelay", "Turbo Delay (in frames); 3|4|5|6|7|8|9|10|11|12|13|14|15|1|2" },
|
|
{ "vbam_astick_deadzone", "Sensors Deadzone (%); 15|20|25|30|0|5|10"},
|
|
{ "vbam_gyro_sensitivity", "Sensor Sensitivity (Gyroscope) (%); 100|105|110|115|120|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95"},
|
|
{ "vbam_tilt_sensitivity", "Sensor Sensitivity (Tilt) (%); 100|105|110|115|120|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95"},
|
|
{ "vbam_swap_astick", "Swap Left/Right Analog; disabled|enabled" },
|
|
{ "vbam_layer_1", "Show layer 1; enabled|disabled" },
|
|
{ "vbam_layer_2", "Show layer 2; enabled|disabled" },
|
|
{ "vbam_layer_3", "Show layer 3; enabled|disabled" },
|
|
{ "vbam_layer_4", "Show layer 4; enabled|disabled" },
|
|
{ "vbam_layer_5", "Show sprite layer; enabled|disabled" },
|
|
{ "vbam_layer_6", "Show window layer 1; enabled|disabled" },
|
|
{ "vbam_layer_7", "Show window layer 2; enabled|disabled" },
|
|
{ "vbam_layer_8", "Show sprite window layer; enabled|disabled" },
|
|
{ "vbam_sound_1", "Sound channel 1; enabled|disabled" },
|
|
{ "vbam_sound_2", "Sound channel 2; enabled|disabled" },
|
|
{ "vbam_sound_3", "Sound channel 3; enabled|disabled" },
|
|
{ "vbam_sound_4", "Sound channel 4; enabled|disabled" },
|
|
{ "vbam_sound_5", "Direct Sound A; enabled|disabled" },
|
|
{ "vbam_sound_6", "Direct Sound B; enabled|disabled" },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables);
|
|
}
|
|
|
|
void retro_get_system_info(struct retro_system_info *info)
|
|
{
|
|
info->need_fullpath = false;
|
|
info->valid_extensions = "dmg|gb|gbc|cgb|sgb|gba";
|
|
#ifdef GIT_VERSION
|
|
info->library_version = "2.1.0" GIT_VERSION;
|
|
#else
|
|
info->library_version = "2.1.0-GIT";
|
|
#endif
|
|
info->library_name = "VBA-M";
|
|
info->block_extract = false;
|
|
}
|
|
|
|
void retro_get_system_av_info(struct retro_system_av_info *info)
|
|
{
|
|
float aspect = (3.0f / 2.0f);
|
|
if (type == IMAGE_GB)
|
|
aspect = !gbBorderOn ? (10.0 / 9.0) : (8.0 / 7.0);
|
|
|
|
info->geometry.base_width = width;
|
|
info->geometry.base_height = height;
|
|
info->geometry.max_width = width;
|
|
info->geometry.max_height = height;
|
|
info->geometry.aspect_ratio = aspect;
|
|
info->timing.fps = FramesPerSecond;
|
|
info->timing.sample_rate = (double)SampleRate;
|
|
}
|
|
|
|
void retro_init(void)
|
|
{
|
|
struct retro_log_callback log;
|
|
struct retro_rumble_interface rumble;
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_GET_CAN_DUPE, &can_dupe);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
|
|
log_cb = log.log;
|
|
else
|
|
log_cb = NULL;
|
|
|
|
const char* dir = NULL;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir)
|
|
snprintf(retro_system_directory, sizeof(retro_system_directory), "%s", dir);
|
|
|
|
#ifdef FRONTEND_SUPPORTS_RGB565
|
|
enum retro_pixel_format rgb565 = RETRO_PIXEL_FORMAT_RGB565;
|
|
if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb565) && log_cb)
|
|
log_cb(RETRO_LOG_INFO, "Frontend supports RGB565 - will use that instead of XRGB1555.\n");
|
|
#else
|
|
enum retro_pixel_format rgb8888 = RETRO_PIXEL_FORMAT_XRGB8888;
|
|
if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb8888) && log_cb)
|
|
log_cb(RETRO_LOG_INFO, "Frontend supports XRGB8888 - will use that instead of XRGB1555.\n");
|
|
#endif
|
|
|
|
bool yes = true;
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS, &yes);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble)) {
|
|
rumble_cb = rumble.set_rumble_state;
|
|
} else
|
|
rumble_cb = NULL;
|
|
|
|
}
|
|
|
|
static const char *gbGetCartridgeType(void)
|
|
{
|
|
const char *type = "Unknown";
|
|
|
|
switch (gbRom[0x147]) {
|
|
case 0x00:
|
|
type = "ROM";
|
|
break;
|
|
case 0x01:
|
|
type = "ROM+MBC1";
|
|
break;
|
|
case 0x02:
|
|
type = "ROM+MBC1+RAM";
|
|
break;
|
|
case 0x03:
|
|
type = "ROM+MBC1+RAM+BATTERY";
|
|
break;
|
|
case 0x05:
|
|
type = "ROM+MBC2";
|
|
break;
|
|
case 0x06:
|
|
type = "ROM+MBC2+BATTERY";
|
|
break;
|
|
case 0x0b:
|
|
type = "ROM+MMM01";
|
|
break;
|
|
case 0x0c:
|
|
type = "ROM+MMM01+RAM";
|
|
break;
|
|
case 0x0d:
|
|
type = "ROM+MMM01+RAM+BATTERY";
|
|
break;
|
|
case 0x0f:
|
|
type = "ROM+MBC3+TIMER+BATTERY";
|
|
break;
|
|
case 0x10:
|
|
type = "ROM+MBC3+TIMER+RAM+BATTERY";
|
|
break;
|
|
case 0x11:
|
|
type = "ROM+MBC3";
|
|
break;
|
|
case 0x12:
|
|
type = "ROM+MBC3+RAM";
|
|
break;
|
|
case 0x13:
|
|
type = "ROM+MBC3+RAM+BATTERY";
|
|
break;
|
|
case 0x19:
|
|
type = "ROM+MBC5";
|
|
break;
|
|
case 0x1a:
|
|
type = "ROM+MBC5+RAM";
|
|
break;
|
|
case 0x1b:
|
|
type = "ROM+MBC5+RAM+BATTERY";
|
|
break;
|
|
case 0x1c:
|
|
type = "ROM+MBC5+RUMBLE";
|
|
break;
|
|
case 0x1d:
|
|
type = "ROM+MBC5+RUMBLE+RAM";
|
|
break;
|
|
case 0x1e:
|
|
type = "ROM+MBC5+RUMBLE+RAM+BATTERY";
|
|
break;
|
|
case 0x22:
|
|
type = "ROM+MBC7+BATTERY";
|
|
break;
|
|
case 0x55:
|
|
type = "GameGenie";
|
|
break;
|
|
case 0x56:
|
|
type = "GameShark V3.0";
|
|
break;
|
|
case 0xfc:
|
|
type = "ROM+POCKET CAMERA";
|
|
break;
|
|
case 0xfd:
|
|
type = "ROM+BANDAI TAMA5";
|
|
break;
|
|
case 0xfe:
|
|
type = "ROM+HuC-3";
|
|
break;
|
|
case 0xff:
|
|
type = "ROM+HuC-1";
|
|
break;
|
|
}
|
|
|
|
return (type);
|
|
}
|
|
|
|
static const char *gbGetSaveRamSize(void)
|
|
{
|
|
const char *type = "Unknown";
|
|
|
|
switch (gbRom[0x149]) {
|
|
case 0:
|
|
type = "None";
|
|
break;
|
|
case 1:
|
|
type = "2K";
|
|
break;
|
|
case 2:
|
|
type = "8K";
|
|
break;
|
|
case 3:
|
|
type = "32K";
|
|
break;
|
|
case 4:
|
|
type = "128K";
|
|
break;
|
|
case 5:
|
|
type = "64K";
|
|
break;
|
|
}
|
|
|
|
return (type);
|
|
}
|
|
|
|
typedef struct {
|
|
char romtitle[256];
|
|
char romid[5];
|
|
int saveSize; // also can override eeprom size
|
|
int saveType; // 0auto 1eeprom 2sram 3flash 4sensor+eeprom 5none
|
|
int rtcEnabled;
|
|
int mirroringEnabled;
|
|
int useBios;
|
|
} ini_t;
|
|
|
|
static const ini_t gbaover[512] = {
|
|
#include "gba-over.inc"
|
|
};
|
|
|
|
static int romSize = 0;
|
|
|
|
static void load_image_preferences(void)
|
|
{
|
|
const char *savetype[] = {
|
|
"AUTO",
|
|
"EEPROM",
|
|
"SRAM",
|
|
"FLASH",
|
|
"SENSOR + EEPROM",
|
|
"NONE"
|
|
};
|
|
|
|
char buffer[5];
|
|
|
|
buffer[0] = rom[0xac];
|
|
buffer[1] = rom[0xad];
|
|
buffer[2] = rom[0xae];
|
|
buffer[3] = rom[0xaf];
|
|
buffer[4] = 0;
|
|
|
|
cpuSaveType = GBA_SAVE_AUTO;
|
|
flashSize = SIZE_FLASH512;
|
|
eepromSize = SIZE_EEPROM_512;
|
|
rtcEnabled = false;
|
|
mirroringEnable = false;
|
|
|
|
log("GameID in ROM is: %s\n", buffer);
|
|
|
|
bool found = false;
|
|
int found_no = 0;
|
|
|
|
for (int i = 0; i < 512; i++) {
|
|
if (!strcmp(gbaover[i].romid, buffer)) {
|
|
found = true;
|
|
found_no = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
log("Found ROM in vba-over list.\n");
|
|
log("Name : %s\n", gbaover[found_no].romtitle);
|
|
|
|
rtcEnabled = gbaover[found_no].rtcEnabled;
|
|
cpuSaveType = gbaover[found_no].saveType;
|
|
|
|
unsigned size = gbaover[found_no].saveSize;
|
|
if (cpuSaveType == GBA_SAVE_SRAM)
|
|
flashSize = SIZE_SRAM;
|
|
else if (cpuSaveType == GBA_SAVE_FLASH)
|
|
flashSize = (size == SIZE_FLASH1M) ? SIZE_FLASH1M : SIZE_FLASH512;
|
|
else if ((cpuSaveType == GBA_SAVE_EEPROM) || (cpuSaveType == GBA_SAVE_EEPROM_SENSOR))
|
|
eepromSize = (size == SIZE_EEPROM_8K) ? SIZE_EEPROM_8K : SIZE_EEPROM_512;
|
|
}
|
|
|
|
// gameID that starts with 'F' are classic/famicom games
|
|
mirroringEnable = (buffer[0] == 0x46) ? true : false;
|
|
|
|
if (!cpuSaveType) {
|
|
log("Scrapping ROM for save type.\n");
|
|
utilGBAFindSave(romSize);
|
|
}
|
|
|
|
saveType = cpuSaveType;
|
|
|
|
if (flashSize == SIZE_FLASH512 || flashSize == SIZE_FLASH1M)
|
|
flashSetSize(flashSize);
|
|
|
|
rtcEnable(rtcEnabled);
|
|
rtcEnableRumble(!rtcEnabled);
|
|
|
|
doMirroring(mirroringEnable);
|
|
|
|
log("romSize : %dKB\n", (romSize + 1023) / 1024);
|
|
log("has RTC : %s.\n", rtcEnabled ? "Yes" : "No");
|
|
log("cpuSaveType : %s.\n", savetype[cpuSaveType]);
|
|
if (cpuSaveType == 3)
|
|
log("flashSize : %d.\n", flashSize);
|
|
if (cpuSaveType == 1)
|
|
log("eepromSize : %d.\n", eepromSize);
|
|
log("mirroringEnable : %s.\n", mirroringEnable ? "Yes" : "No");
|
|
}
|
|
|
|
static void update_colormaps(void)
|
|
{
|
|
#ifdef FRONTEND_SUPPORTS_RGB565
|
|
systemColorDepth = 16;
|
|
systemRedShift = 11;
|
|
systemGreenShift = 6;
|
|
systemBlueShift = 0;
|
|
#else
|
|
systemColorDepth = 32;
|
|
systemRedShift = 19;
|
|
systemGreenShift = 11;
|
|
systemBlueShift = 3;
|
|
#endif
|
|
|
|
utilUpdateSystemColorMaps(false);
|
|
|
|
log("Color Depth = %d\n", systemColorDepth);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
static const char SLASH = '\\';
|
|
#else
|
|
static const char SLASH = '/';
|
|
#endif
|
|
|
|
static void gba_init(void)
|
|
{
|
|
log("Loading VBA-M Core (GBA)...\n");
|
|
|
|
load_image_preferences();
|
|
soundSetSampleRate(SampleRate);
|
|
|
|
if (usebios) {
|
|
snprintf(biosfile, sizeof(biosfile), "%s%c%s", retro_system_directory, SLASH, "gba_bios.bin");
|
|
log("Loading bios: %s\n", biosfile);
|
|
}
|
|
CPUInit(biosfile, usebios);
|
|
|
|
width = GBAWidth;
|
|
height = GBAHeight;
|
|
|
|
CPUReset();
|
|
}
|
|
|
|
static void gb_init(void)
|
|
{
|
|
const char *biosname[] = {"gb_bios.bin", "gbc_bios.bin"};
|
|
|
|
log("Loading VBA-M Core (GB/GBC)...\n");
|
|
|
|
gbGetHardwareType();
|
|
|
|
if (usebios) {
|
|
snprintf(biosfile, sizeof(biosfile), "%s%c%s",
|
|
retro_system_directory, SLASH, biosname[gbCgbMode]);
|
|
log("Loading bios: %s\n", biosfile);
|
|
}
|
|
|
|
gbCPUInit(biosfile, usebios);
|
|
|
|
if (gbBorderOn) {
|
|
width = gbBorderLineSkip = SGBWidth;
|
|
height = SGBHeight;
|
|
gbBorderColumnSkip = (SGBWidth - GBWidth) >> 1;
|
|
gbBorderRowSkip = (SGBHeight - GBHeight) >> 1;
|
|
} else {
|
|
width = gbBorderLineSkip = GBWidth;
|
|
height = GBHeight;
|
|
gbBorderColumnSkip = gbBorderRowSkip = 0;
|
|
}
|
|
|
|
gbSoundSetSampleRate(SampleRate);
|
|
gbSoundSetDeclicking(1);
|
|
|
|
gbReset(); // also resets sound;
|
|
|
|
// VBA-M always updates time based on current time and not in-game time.
|
|
// No need to add RTC data to RETRO_MEMORY_RTC, so its safe to place this here.
|
|
gbUpdateRTC();
|
|
|
|
log("Rom size : %02x (%dK)\n", gbRom[0x148], (romSize + 1023) / 1024);
|
|
log("Cartridge type : %02x (%s)\n", gbRom[0x147], gbGetCartridgeType());
|
|
log("Ram size : %02x (%s)\n", gbRom[0x149], gbGetSaveRamSize());
|
|
|
|
int i = 0;
|
|
uint8_t crc = 25;
|
|
|
|
for (i = 0x134; i < 0x14d; i++)
|
|
crc += gbRom[i];
|
|
crc = 256 - crc;
|
|
log("CRC : %02x (%02x)\n", crc, gbRom[0x14d]);
|
|
|
|
uint16_t crc16 = 0;
|
|
|
|
for (i = 0; i < gbRomSize; i++)
|
|
crc16 += gbRom[i];
|
|
|
|
crc16 -= gbRom[0x14e] + gbRom[0x14f];
|
|
log("Checksum : %04x (%04x)\n", crc16, gbRom[0x14e] * 256 + gbRom[0x14f]);
|
|
|
|
if (gbBattery)
|
|
log("Game supports battery save ram.\n");
|
|
if (gbRom[0x143] == 0xc0)
|
|
log("Game works on CGB only\n");
|
|
else if (gbRom[0x143] == 0x80)
|
|
log("Game supports GBC functions, GB compatible.\n");
|
|
if (gbRom[0x146] == 0x03)
|
|
log("Game supports SGB functions\n");
|
|
}
|
|
|
|
static void gba_soundchanged(void)
|
|
{
|
|
soundInterpolation = sndInterpolation;
|
|
soundFiltering = sndFiltering;
|
|
}
|
|
|
|
void retro_deinit(void)
|
|
{
|
|
emulating = 0;
|
|
core->emuCleanUp();
|
|
soundShutdown();
|
|
}
|
|
|
|
void retro_reset(void)
|
|
{
|
|
core->emuReset();
|
|
set_gbPalette();
|
|
}
|
|
|
|
#define MAX_PLAYERS 4
|
|
#define MAX_BUTTONS 10
|
|
#define TURBO_BUTTONS 2
|
|
static bool turbo_enable = false;
|
|
static unsigned turbo_delay = 3;
|
|
static unsigned turbo_delay_counter[MAX_PLAYERS][TURBO_BUTTONS] = {{0}, {0}};
|
|
static const unsigned binds[MAX_BUTTONS] = {
|
|
RETRO_DEVICE_ID_JOYPAD_A,
|
|
RETRO_DEVICE_ID_JOYPAD_B,
|
|
RETRO_DEVICE_ID_JOYPAD_SELECT,
|
|
RETRO_DEVICE_ID_JOYPAD_START,
|
|
RETRO_DEVICE_ID_JOYPAD_RIGHT,
|
|
RETRO_DEVICE_ID_JOYPAD_LEFT,
|
|
RETRO_DEVICE_ID_JOYPAD_UP,
|
|
RETRO_DEVICE_ID_JOYPAD_DOWN,
|
|
RETRO_DEVICE_ID_JOYPAD_R,
|
|
RETRO_DEVICE_ID_JOYPAD_L
|
|
};
|
|
|
|
static const unsigned turbo_binds[TURBO_BUTTONS] = {
|
|
RETRO_DEVICE_ID_JOYPAD_X,
|
|
RETRO_DEVICE_ID_JOYPAD_Y
|
|
};
|
|
|
|
static void systemGbBorderOff(void);
|
|
static void systemUpdateSolarSensor(int level);
|
|
static uint8_t sensorDarkness = 0xE8;
|
|
static uint8_t sensorDarknessLevel = 0; // so we can adjust sensor from gamepad
|
|
static int astick_deadzone;
|
|
static int gyro_sensitivity, tilt_sensitivity;
|
|
static bool swap_astick;
|
|
|
|
static void update_variables(bool startup)
|
|
{
|
|
bool sound_changed = false;
|
|
char key[256];
|
|
struct retro_variable var;
|
|
var.key = key;
|
|
|
|
int disabled_layers=0;
|
|
|
|
strcpy(key, "vbam_layer_x");
|
|
for (int i = 0; i < 8; i++) {
|
|
key[strlen("vbam_layer_")] = '1' + i;
|
|
var.value = NULL;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && var.value[0] == 'd')
|
|
disabled_layers |= 0x100 << i;
|
|
}
|
|
|
|
layerSettings = 0xFF00 ^ disabled_layers;
|
|
layerEnable = DISPCNT & layerSettings;
|
|
CPUUpdateRenderBuffers(false);
|
|
|
|
int sound_enabled = 0x30F;
|
|
strcpy(key, "vbam_sound_x");
|
|
for (unsigned i = 0; i < 6; i++) {
|
|
key[strlen("vbam_sound_")] = '1' + i;
|
|
var.value = NULL;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && var.value[0] == 'd') {
|
|
int which = (i < 4) ? (1 << i) : (0x100 << (i - 4));
|
|
sound_enabled &= ~(which);
|
|
}
|
|
}
|
|
|
|
if (soundGetEnable() != sound_enabled)
|
|
soundSetEnable(sound_enabled & 0x30F);
|
|
|
|
var.key = "vbam_soundinterpolation";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
|
|
bool newval = (strcmp(var.value, "enabled") == 0);
|
|
if (sndInterpolation != newval) {
|
|
sndInterpolation = newval;
|
|
sound_changed = true;
|
|
}
|
|
}
|
|
|
|
var.key = "vbam_soundfiltering";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
|
|
float newval = atof(var.value) * 0.1f;
|
|
if (sndFiltering != newval) {
|
|
sndFiltering = newval;
|
|
sound_changed = true;
|
|
}
|
|
}
|
|
|
|
if (sound_changed) {
|
|
//Update interpolation and filtering values
|
|
gba_soundchanged();
|
|
}
|
|
|
|
var.key = "vbam_usebios";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
|
|
bool newval = (strcmp(var.value, "enabled") == 0);
|
|
usebios = newval;
|
|
}
|
|
|
|
var.key = "vbam_solarsensor";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
|
|
sensorDarknessLevel = atoi(var.value);
|
|
systemUpdateSolarSensor(sensorDarknessLevel);
|
|
}
|
|
|
|
var.key = "vbam_showborders";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
|
|
int oldval = (gbBorderOn << 1) | gbBorderAutomatic;
|
|
if (strcmp(var.value, "auto") == 0) {
|
|
gbBorderOn = 0;
|
|
gbBorderAutomatic = 1;
|
|
}
|
|
else if (strcmp(var.value, "enabled") == 0) {
|
|
gbBorderAutomatic = 0;
|
|
gbBorderOn = 1;
|
|
}
|
|
else { // disabled
|
|
gbBorderOn = 0;
|
|
gbBorderAutomatic = 0;
|
|
}
|
|
|
|
if ((type == IMAGE_GB) &&
|
|
(oldval != ((gbBorderOn << 1) | gbBorderAutomatic)) && !startup) {
|
|
if (gbBorderOn) {
|
|
systemGbBorderOn();
|
|
gbSgbRenderBorder();
|
|
}
|
|
else
|
|
systemGbBorderOff();
|
|
}
|
|
}
|
|
|
|
var.key = "vbam_gbHardware";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
|
|
if (strcmp(var.value, "Automatic") == 0)
|
|
gbEmulatorType = 0;
|
|
else if (strcmp(var.value, "Game Boy Color") == 0)
|
|
gbEmulatorType = 1;
|
|
else if (strcmp(var.value, "Super Game Boy") == 0)
|
|
gbEmulatorType = 2;
|
|
else if (strcmp(var.value, "Game Boy") == 0)
|
|
gbEmulatorType = 3;
|
|
else if (strcmp(var.value, "Game Boy Advance") == 0)
|
|
gbEmulatorType = 4;
|
|
else if (strcmp(var.value, "Super Game Boy 2") == 0)
|
|
gbEmulatorType = 5;
|
|
}
|
|
|
|
var.key = "vbam_turboenable";
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
bool val = !strcmp(var.value, "enabled");
|
|
turbo_enable = val;
|
|
}
|
|
|
|
var.key = "vbam_turbodelay";
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
turbo_delay = atoi(var.value);
|
|
}
|
|
|
|
var.key = "vbam_astick_deadzone";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
astick_deadzone = (int)(atoi(var.value) * 0.01f * 0x8000);
|
|
}
|
|
|
|
var.key = "vbam_tilt_sensitivity";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
tilt_sensitivity = atoi(var.value);
|
|
}
|
|
|
|
var.key = "vbam_gyro_sensitivity";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
gyro_sensitivity = atoi(var.value);
|
|
}
|
|
|
|
var.key = "vbam_swap_astick";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
swap_astick = (bool)(!strcmp(var.value, "enabled"));
|
|
}
|
|
|
|
var.key = "vbam_palettes";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
int lastpal = current_gbPalette;
|
|
|
|
if (!strcmp(var.value, "Standard"))
|
|
current_gbPalette = 0;
|
|
else if (!strcmp(var.value, "Blue Sea"))
|
|
current_gbPalette = 1;
|
|
else if (!strcmp(var.value, "Dark Knight"))
|
|
current_gbPalette = 2;
|
|
else if (!strcmp(var.value, "Green Forest"))
|
|
current_gbPalette = 3;
|
|
else if (!strcmp(var.value, "Hot Desert"))
|
|
current_gbPalette = 4;
|
|
else if (!strcmp(var.value, "Pink Dreams"))
|
|
current_gbPalette = 5;
|
|
else if (!strcmp(var.value, "Wierd Colors"))
|
|
current_gbPalette = 6;
|
|
else if (!strcmp(var.value, "Original"))
|
|
current_gbPalette = 7;
|
|
else if (!strcmp(var.value, "GBA SP"))
|
|
current_gbPalette = 8;
|
|
|
|
if (lastpal != current_gbPalette)
|
|
set_gbPalette();
|
|
}
|
|
}
|
|
|
|
// System analog stick range is -0x7fff to 0x7fff
|
|
// Implementation from mupen64plus-libretro
|
|
#include <math.h>
|
|
#define ROUND(x) floor((x) + 0.5)
|
|
#define ASTICK_MAX 0x8000
|
|
static int analog_x, analog_y, analog_z;
|
|
|
|
static void updateInput_MotionSensors(void)
|
|
{
|
|
int16_t analog[3], astick_data[3];
|
|
double scaled_range, radius, angle;
|
|
unsigned tilt_retro_device_index =
|
|
swap_astick ? RETRO_DEVICE_INDEX_ANALOG_LEFT : RETRO_DEVICE_INDEX_ANALOG_RIGHT;
|
|
unsigned gyro_retro_device_index =
|
|
swap_astick ? RETRO_DEVICE_INDEX_ANALOG_RIGHT : RETRO_DEVICE_INDEX_ANALOG_LEFT;
|
|
|
|
// Tilt sensor section
|
|
analog[0] = input_cb(0, RETRO_DEVICE_ANALOG,
|
|
tilt_retro_device_index, RETRO_DEVICE_ID_ANALOG_X);
|
|
analog[1] = input_cb(0, RETRO_DEVICE_ANALOG,
|
|
tilt_retro_device_index, RETRO_DEVICE_ID_ANALOG_Y);
|
|
|
|
// Convert cartesian coordinate analog stick to polar coordinates
|
|
radius = sqrt(analog[0] * analog[0] + analog[1] * analog[1]);
|
|
angle = atan2(analog[1], analog[0]);
|
|
|
|
if (radius > astick_deadzone) {
|
|
// Re-scale analog stick range to negate deadzone (makes slow movements possible)
|
|
radius = (radius - astick_deadzone) *
|
|
((float)ASTICK_MAX/(ASTICK_MAX - astick_deadzone));
|
|
// Tilt sensor range is from from 1897 to 2197
|
|
radius *= 150.0 / ASTICK_MAX * (tilt_sensitivity / 100.0);
|
|
// Convert back to cartesian coordinates
|
|
astick_data[0] = +(int16_t)ROUND(radius * cos(angle));
|
|
astick_data[1] = -(int16_t)ROUND(radius * sin(angle));
|
|
} else
|
|
astick_data[0] = astick_data[1] = 0;
|
|
|
|
analog_x = astick_data[0];
|
|
analog_y = astick_data[1];
|
|
|
|
// Gyro sensor section
|
|
analog[3] = input_cb(0, RETRO_DEVICE_ANALOG,
|
|
gyro_retro_device_index, RETRO_DEVICE_ID_ANALOG_X);
|
|
|
|
if ( analog[3] < -astick_deadzone ) {
|
|
// Re-scale analog stick range
|
|
scaled_range = (-analog[3] - astick_deadzone) *
|
|
((float)ASTICK_MAX / (ASTICK_MAX - astick_deadzone));
|
|
// Gyro sensor range is +/- 1800
|
|
scaled_range *= 1800.0 / ASTICK_MAX * (gyro_sensitivity / 100.0);
|
|
astick_data[3] = -(int16_t)ROUND(scaled_range);
|
|
} else if ( analog[3] > astick_deadzone ) {
|
|
scaled_range = (analog[3] - astick_deadzone) *
|
|
((float)ASTICK_MAX / (ASTICK_MAX - astick_deadzone));
|
|
scaled_range *= (1800.0 / ASTICK_MAX * (gyro_sensitivity / 100.0));
|
|
astick_data[3] = +(int16_t)ROUND(scaled_range);
|
|
} else
|
|
astick_data[3] = 0;
|
|
|
|
analog_z = astick_data[3];
|
|
}
|
|
|
|
// Update solar sensor level by gamepad buttons, default L2/R2
|
|
void updateInput_SolarSensor(void)
|
|
{
|
|
static bool buttonpressed = false;
|
|
|
|
if (buttonpressed) {
|
|
buttonpressed = input_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2) ||
|
|
input_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2);
|
|
} else {
|
|
if (input_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2)) {
|
|
sensorDarknessLevel++;
|
|
if (sensorDarknessLevel > 10)
|
|
sensorDarknessLevel = 10;
|
|
systemUpdateSolarSensor(sensorDarknessLevel);
|
|
buttonpressed = true;
|
|
} else if (input_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2)) {
|
|
if (sensorDarknessLevel)
|
|
sensorDarknessLevel--;
|
|
systemUpdateSolarSensor(sensorDarknessLevel);
|
|
buttonpressed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static unsigned has_frame;
|
|
|
|
void retro_run(void)
|
|
{
|
|
bool updated = false;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
|
|
update_variables(false);
|
|
|
|
poll_cb();
|
|
|
|
updateInput_SolarSensor();
|
|
updateInput_MotionSensors();
|
|
|
|
has_frame = 0;
|
|
|
|
do {
|
|
core->emuMain(core->emuCount);
|
|
} while (!has_frame);
|
|
}
|
|
|
|
static unsigned serialize_size = 0;
|
|
|
|
size_t retro_serialize_size(void)
|
|
{
|
|
return serialize_size;
|
|
}
|
|
|
|
bool retro_serialize(void* data, size_t size)
|
|
{
|
|
if (size == serialize_size)
|
|
return core->emuWriteState((uint8_t*)data, size);
|
|
return false;
|
|
}
|
|
|
|
bool retro_unserialize(const void* data, size_t size)
|
|
{
|
|
if (size == serialize_size)
|
|
return core->emuReadState((uint8_t*)data, size);
|
|
return false;
|
|
}
|
|
|
|
void retro_cheat_reset(void)
|
|
{
|
|
cheatsEnabled = 1;
|
|
if (type == IMAGE_GBA)
|
|
cheatsDeleteAll(false);
|
|
else if (type == IMAGE_GB)
|
|
gbCheatRemoveAll();
|
|
}
|
|
|
|
#define ISHEXDEC \
|
|
((code[cursor] >= '0') && (code[cursor] <= '9')) || \
|
|
((code[cursor] >= 'a') && (code[cursor] <= 'f')) || \
|
|
((code[cursor] >= 'A') && (code[cursor] <= 'F')) || \
|
|
(code[cursor] == '-') \
|
|
|
|
void retro_cheat_set(unsigned index, bool enabled, const char* code)
|
|
{
|
|
// 2018-11-30 - retrowertz
|
|
// added support GB/GBC multiline 6/9 digit Game Genie codes and Game Shark
|
|
|
|
char name[128] = {0};
|
|
unsigned cursor = 0;
|
|
char *codeLine = NULL;
|
|
int codeLineSize = strlen(code) + 5;
|
|
int codePos = 0;
|
|
int i = 0;
|
|
|
|
codeLine = (char *)calloc(codeLineSize, sizeof(char));
|
|
sprintf(name, "cheat_%d", index);
|
|
for (cursor = 0;; cursor++) {
|
|
if (ISHEXDEC) {
|
|
codeLine[codePos++] = toupper(code[cursor]);
|
|
} else {
|
|
switch (type) {
|
|
case IMAGE_GB:
|
|
if (codePos >= 7) {
|
|
if (codePos == 7 || codePos == 11) {
|
|
codeLine[codePos] = '\0';
|
|
if (gbAddGgCheat(codeLine, name))
|
|
log("Cheat code added: '%s'\n", codeLine);
|
|
} else if (codePos == 8) {
|
|
codeLine[codePos] = '\0';
|
|
if (gbAddGsCheat(codeLine, name))
|
|
log("Cheat code added: '%s'\n", codeLine);
|
|
} else {
|
|
codeLine[codePos] = '\0';
|
|
log("Invalid cheat code '%s'\n", codeLine);
|
|
}
|
|
codePos = 0;
|
|
memset(codeLine, 0, codeLineSize);
|
|
}
|
|
break;
|
|
case IMAGE_GBA:
|
|
if (codePos >= 12) {
|
|
if (codePos == 12 ) {
|
|
for (i = 0; i < 4 ; i++)
|
|
codeLine[codePos - i] = codeLine[(codePos - i) - 1];
|
|
codeLine[8] = ' ';
|
|
codeLine[13] = '\0';
|
|
cheatsAddCBACode(codeLine, name);
|
|
log("Cheat code added: '%s'\n", codeLine);
|
|
} else if (codePos == 16) {
|
|
codeLine[16] = '\0';
|
|
cheatsAddGSACode(codeLine, name, true);
|
|
log("Cheat code added: '%s'\n", codeLine);
|
|
} else {
|
|
codeLine[codePos] = '\0';
|
|
log("Invalid cheat code '%s'\n", codeLine);
|
|
}
|
|
codePos = 0;
|
|
memset(codeLine, 0, codeLineSize);
|
|
}
|
|
break;
|
|
}
|
|
if (!code[cursor])
|
|
break;
|
|
}
|
|
}
|
|
free(codeLine);
|
|
}
|
|
|
|
static void update_input_descriptors(void)
|
|
{
|
|
if (type == IMAGE_GB) {
|
|
if (gbSgbMode) {
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports_sgb);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, input_sgb);
|
|
} else {
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports_gb);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, input_gb);
|
|
}
|
|
} else if (type == IMAGE_GBA) {
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports_gba);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, input_gba);
|
|
}
|
|
}
|
|
|
|
bool retro_load_game(const struct retro_game_info *game)
|
|
{
|
|
type = utilFindType((const char *)game->path);
|
|
|
|
if (type == IMAGE_UNKNOWN) {
|
|
log("Unsupported image %s!\n", game->path);
|
|
return false;
|
|
}
|
|
|
|
update_variables(true);
|
|
update_colormaps();
|
|
soundInit();
|
|
|
|
if (type == IMAGE_GBA) {
|
|
romSize = CPULoadRomData((const char*)game->data, game->size);
|
|
|
|
if (!romSize)
|
|
return false;
|
|
|
|
core = &GBASystem;
|
|
|
|
gba_init();
|
|
|
|
struct retro_memory_descriptor desc[11];
|
|
memset(desc, 0, sizeof(desc));
|
|
|
|
desc[0].start=0x03000000; desc[0].select=0xFF000000; desc[0].len=0x8000; desc[0].ptr=internalRAM;//fast WRAM
|
|
desc[1].start=0x02000000; desc[1].select=0xFF000000; desc[1].len=0x40000; desc[1].ptr=workRAM;//slow WRAM
|
|
/* TODO: if SRAM is flash, use start=0 addrspace="S" instead */
|
|
desc[2].start=0x0E000000; desc[2].select=0; desc[2].len=flashSize; desc[2].ptr=flashSaveMemory;//SRAM
|
|
desc[3].start=0x08000000; desc[3].select=0; desc[3].len=romSize; desc[3].ptr=rom;//ROM
|
|
desc[3].flags=RETRO_MEMDESC_CONST;
|
|
desc[4].start=0x0A000000; desc[4].select=0; desc[4].len=romSize; desc[4].ptr=rom;//ROM mirror 1
|
|
desc[4].flags=RETRO_MEMDESC_CONST;
|
|
desc[5].start=0x0C000000; desc[5].select=0; desc[5].len=romSize; desc[5].ptr=rom;//ROM mirror 2
|
|
desc[5].flags=RETRO_MEMDESC_CONST;
|
|
desc[6].start=0x00000000; desc[6].select=0; desc[6].len=0x4000; desc[6].ptr=bios;//BIOS
|
|
desc[6].flags=RETRO_MEMDESC_CONST;
|
|
desc[7].start=0x06000000; desc[7].select=0xFF000000; desc[7].len=0x18000; desc[7].ptr=vram;//VRAM
|
|
desc[8].start=0x05000000; desc[8].select=0xFF000000; desc[8].len=0x400; desc[8].ptr=paletteRAM;//palettes
|
|
desc[9].start=0x07000000; desc[9].select=0xFF000000; desc[9].len=0x400; desc[9].ptr=oam;//OAM
|
|
desc[10].start=0x04000000;desc[10].select=0; desc[10].len=0x400; desc[10].ptr=ioMem;//bunch of registers
|
|
|
|
struct retro_memory_map retromap = {
|
|
desc,
|
|
sizeof(desc)/sizeof(desc[0])
|
|
};
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &retromap);
|
|
}
|
|
|
|
else if (type == IMAGE_GB) {
|
|
if (!gbLoadRomData((const char *)game->data, game->size))
|
|
return false;
|
|
|
|
romSize = game->size;
|
|
|
|
core = &GBSystem;
|
|
|
|
gb_init();
|
|
|
|
unsigned addr, i;
|
|
struct retro_memory_descriptor desc[16];
|
|
struct retro_memory_map retromap;
|
|
|
|
memset(desc, 0, sizeof(desc));
|
|
// the GB's memory is divided into 16 parts with 4096 bytes each == 65536
|
|
// $FFFF Interrupt Enable Flag
|
|
// $FF80-$FFFE Zero Page - 127 bytes
|
|
// $FF00-$FF7F Hardware I/O Registers
|
|
// $FEA0-$FEFF Unusable Memory
|
|
// $FE00-$FE9F OAM - Object Attribute Memory
|
|
// $E000-$FDFF Echo RAM - Reserved, Do Not Use
|
|
// $D000-$DFFF Internal RAM - Bank 1-7 (switchable - CGB only)
|
|
// $C000-$CFFF Internal RAM - Bank 0 (fixed)
|
|
// $A000-$BFFF Cartridge RAM (If Available)
|
|
// $9C00-$9FFF BG Map Data 2
|
|
// $9800-$9BFF BG Map Data 1
|
|
// $8000-$97FF Character RAM
|
|
// $4000-$7FFF Cartridge ROM - Switchable Banks 1-xx
|
|
// $0150-$3FFF Cartridge ROM - Bank 0 (fixed)
|
|
// $0100-$014F Cartridge Header Area
|
|
// $0000-$00FF Restart and Interrupt Vectors
|
|
// http://gameboy.mongenel.com/dmg/asmmemmap.html
|
|
for (addr = 0, i = 0; addr < 16; addr++) {
|
|
if (gbMemoryMap[addr] != NULL) {
|
|
desc[i].ptr = gbMemoryMap[addr];
|
|
desc[i].start = addr * 0x1000;
|
|
desc[i].len = 4096;
|
|
if (addr < 4) desc[i].flags = RETRO_MEMDESC_CONST;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
retromap.descriptors = desc;
|
|
retromap.num_descriptors = i;
|
|
environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &retromap);
|
|
}
|
|
|
|
if (!core)
|
|
return false;
|
|
|
|
update_input_descriptors(); // Initialize input descriptors and info
|
|
update_variables(false);
|
|
uint8_t* state_buf = (uint8_t*)malloc(2000000);
|
|
serialize_size = core->emuWriteState(state_buf, 2000000);
|
|
free(state_buf);
|
|
|
|
emulating = 1;
|
|
|
|
return emulating;
|
|
}
|
|
|
|
bool retro_load_game_special(unsigned, const struct retro_game_info *, size_t)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void retro_unload_game(void)
|
|
{
|
|
}
|
|
|
|
unsigned retro_get_region(void)
|
|
{
|
|
return RETRO_REGION_NTSC;
|
|
}
|
|
|
|
void systemOnWriteDataToSoundBuffer(const uint16_t*, int)
|
|
{
|
|
}
|
|
|
|
void systemOnSoundShutdown(void)
|
|
{
|
|
}
|
|
|
|
bool systemCanChangeSoundQuality(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void systemDrawScreen(void)
|
|
{
|
|
unsigned pitch = width * (systemColorDepth >> 3);
|
|
video_cb(pix, width, height, pitch);
|
|
}
|
|
|
|
void systemFrame(void)
|
|
{
|
|
has_frame = 1;
|
|
}
|
|
|
|
void systemGbBorderOn(void)
|
|
{
|
|
bool changed = ((width != SGBWidth) || (height != SGBHeight));
|
|
width = gbBorderLineSkip = SGBWidth;
|
|
height = SGBHeight;
|
|
gbBorderColumnSkip = (SGBWidth - GBWidth) >> 1;
|
|
gbBorderRowSkip = (SGBHeight - GBHeight) >> 1;
|
|
|
|
struct retro_system_av_info avinfo;
|
|
retro_get_system_av_info(&avinfo);
|
|
|
|
if (!changed)
|
|
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &avinfo);
|
|
else
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avinfo);
|
|
}
|
|
|
|
static void systemGbBorderOff(void)
|
|
{
|
|
bool changed = ((width != GBWidth) || (height != GBHeight));
|
|
width = gbBorderLineSkip = GBWidth;
|
|
height = GBHeight;
|
|
gbBorderColumnSkip = gbBorderRowSkip = 0;
|
|
|
|
struct retro_system_av_info avinfo;
|
|
retro_get_system_av_info(&avinfo);
|
|
|
|
if (!changed)
|
|
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &avinfo);
|
|
else
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avinfo);
|
|
}
|
|
|
|
void systemMessage(const char* fmt, ...)
|
|
{
|
|
char buffer[256];
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vsprintf(buffer, fmt, ap);
|
|
if (log_cb)
|
|
log_cb(RETRO_LOG_INFO, "%s\n", buffer);
|
|
va_end(ap);
|
|
}
|
|
|
|
void systemMessage(int, const char* fmt, ...)
|
|
{
|
|
char buffer[256];
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vsprintf(buffer, fmt, ap);
|
|
if (log_cb)
|
|
log_cb(RETRO_LOG_INFO, "%s\n", buffer);
|
|
va_end(ap);
|
|
}
|
|
|
|
static int rumble_state, rumble_down;
|
|
|
|
uint32_t systemReadJoypad(int which)
|
|
{
|
|
uint32_t J = 0;
|
|
unsigned i, buttons = MAX_BUTTONS - ((type == IMAGE_GB) ? 2 : 0); // gb only has 8 buttons
|
|
|
|
if (which == -1)
|
|
which = 0;
|
|
|
|
if (retropad_device[which] == RETRO_DEVICE_JOYPAD) {
|
|
for (i = 0; i < buttons; i++)
|
|
J |= input_cb(which, RETRO_DEVICE_JOYPAD, 0, binds[i]) << i;
|
|
|
|
if (turbo_enable) {
|
|
/* Handle Turbo A & B buttons */
|
|
for (i = 0; i < TURBO_BUTTONS; i++) {
|
|
if (input_cb(which, RETRO_DEVICE_JOYPAD, 0, turbo_binds[i])) {
|
|
if (!turbo_delay_counter[which][i])
|
|
J |= 1 << i;
|
|
turbo_delay_counter[which][i]++;
|
|
if (turbo_delay_counter[which][i] > turbo_delay)
|
|
/* Reset the toggle if delay value is reached */
|
|
turbo_delay_counter[which][i] = 0;
|
|
} else
|
|
/* If the button is not pressed, just reset the toggle */
|
|
turbo_delay_counter[which][i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do not allow opposing directions
|
|
if ((J & 0x30) == 0x30)
|
|
J &= ~(0x30);
|
|
else if ((J & 0xC0) == 0xC0)
|
|
J &= ~(0xC0);
|
|
|
|
return J;
|
|
}
|
|
|
|
static void systemUpdateSolarSensor(int v)
|
|
{
|
|
int value = 0;
|
|
switch (v) {
|
|
case 1: value = 0x06; break;
|
|
case 2: value = 0x0E; break;
|
|
case 3: value = 0x18; break;
|
|
case 4: value = 0x20; break;
|
|
case 5: value = 0x28; break;
|
|
case 6: value = 0x38; break;
|
|
case 7: value = 0x48; break;
|
|
case 8: value = 0x60; break;
|
|
case 9: value = 0x78; break;
|
|
case 10: value = 0x98; break;
|
|
default: break;
|
|
}
|
|
|
|
sensorDarkness = 0xE8 - value;
|
|
}
|
|
|
|
bool systemReadJoypads(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static int sensor_tilt[2], sensor_gyro;
|
|
|
|
int systemGetSensorX()
|
|
{
|
|
return sensor_tilt[0];
|
|
}
|
|
|
|
int systemGetSensorY()
|
|
{
|
|
return sensor_tilt[1];
|
|
}
|
|
|
|
int systemGetSensorZ()
|
|
{
|
|
return sensor_gyro / 10;
|
|
}
|
|
|
|
uint8_t systemGetSensorDarkness(void)
|
|
{
|
|
return sensorDarkness;
|
|
}
|
|
|
|
void systemUpdateMotionSensor(void)
|
|
{
|
|
// Max ranges as set in VBAM
|
|
static const int tilt_max = 2197;
|
|
static const int tilt_min = 1897;
|
|
static const int tilt_center = 2047;
|
|
static const int gyro_thresh = 1800;
|
|
|
|
if (!sensor_tilt[0])
|
|
sensor_tilt[0] = tilt_center;
|
|
|
|
if (!sensor_tilt[1])
|
|
sensor_tilt[1] = tilt_center;
|
|
|
|
sensor_tilt[0] = (-analog_x) + tilt_center;
|
|
if (sensor_tilt[0] > tilt_max)
|
|
sensor_tilt[0] = tilt_max;
|
|
else if (sensor_tilt[0] < tilt_min)
|
|
sensor_tilt[0] = tilt_min;
|
|
|
|
sensor_tilt[1] = analog_y + tilt_center;
|
|
if (sensor_tilt[1] > tilt_max)
|
|
sensor_tilt[1] = tilt_max;
|
|
else if (sensor_tilt[1] < tilt_min)
|
|
sensor_tilt[1] = tilt_min;
|
|
|
|
sensor_gyro = analog_z;
|
|
if (sensor_gyro > gyro_thresh)
|
|
sensor_gyro = gyro_thresh;
|
|
else if (sensor_gyro < -gyro_thresh)
|
|
sensor_gyro = -gyro_thresh;
|
|
}
|
|
|
|
void systemCartridgeRumble(bool e)
|
|
{
|
|
if (!rumble_cb)
|
|
return;
|
|
|
|
if (e) {
|
|
rumble_cb(0, RETRO_RUMBLE_WEAK, 0xffff);
|
|
rumble_cb(0, RETRO_RUMBLE_STRONG, 0xffff);
|
|
} else {
|
|
rumble_cb(0, RETRO_RUMBLE_WEAK, 0);
|
|
rumble_cb(0, RETRO_RUMBLE_STRONG, 0);
|
|
}
|
|
}
|
|
|
|
bool systemPauseOnFrame(void)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void systemGbPrint(uint8_t*, int, int, int, int)
|
|
{
|
|
}
|
|
|
|
void systemScreenCapture(int)
|
|
{
|
|
}
|
|
|
|
void systemScreenMessage(const char* msg)
|
|
{
|
|
if (log_cb)
|
|
log_cb(RETRO_LOG_INFO, "%s\n", msg);
|
|
}
|
|
|
|
void systemSetTitle(const char*)
|
|
{
|
|
}
|
|
|
|
void systemShowSpeed(int)
|
|
{
|
|
}
|
|
|
|
void system10Frames(int)
|
|
{
|
|
}
|
|
|
|
uint32_t systemGetClock(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
SoundDriver* systemSoundInit(void)
|
|
{
|
|
soundShutdown();
|
|
return new SoundRetro();
|
|
}
|
|
|
|
void log(const char* defaultMsg, ...)
|
|
{
|
|
va_list valist;
|
|
char buf[2048];
|
|
va_start(valist, defaultMsg);
|
|
vsnprintf(buf, 2048, defaultMsg, valist);
|
|
va_end(valist);
|
|
|
|
if (log_cb)
|
|
log_cb(RETRO_LOG_INFO, "%s", buf);
|
|
}
|