Rework RTC and break it into GPIO, with RTC and Rumble (GPIO3)

This adds Rumble support for GPIO3-enabled games (Drill Dozer).
This commit is contained in:
David Guillen Fandos 2023-09-20 22:46:06 +02:00
parent f0bacff91a
commit ae048beb9c
8 changed files with 422 additions and 270 deletions

View File

@ -1179,16 +1179,24 @@ typedef enum
#define RTC_WRITE_TIME_FULL 1 #define RTC_WRITE_TIME_FULL 1
#define RTC_WRITE_STATUS 2 #define RTC_WRITE_STATUS 2
static bool rtc_enabled = false, rumble_enabled = false;
// I/O registers (for RTC, rumble, etc)
u8 gpio_regs[3];
// RTC tracking variables
u32 rtc_state = RTC_DISABLED; u32 rtc_state = RTC_DISABLED;
u32 rtc_write_mode; u32 rtc_write_mode;
u8 rtc_registers[3];
u32 rtc_command; u32 rtc_command;
u32 rtc_data[12]; u64 rtc_data;
u32 rtc_data_bits;
u32 rtc_status = 0x40; u32 rtc_status = 0x40;
u32 rtc_data_bytes;
s32 rtc_bit_count; s32 rtc_bit_count;
static u32 encode_bcd(u8 value) // Rumble trackin vars, not really preserved (it's just aproximate)
static u32 rumble_enable_tick, rumble_ticks;
static u8 encode_bcd(u8 value)
{ {
int l = 0; int l = 0;
int h = 0; int h = 0;
@ -1200,224 +1208,189 @@ static u32 encode_bcd(u8 value)
return h * 16 + l; return h * 16 + l;
} }
// RTC writes need to reflect in the bytes [0xC4..0xC9] of the gamepak void update_gpio_romregs() {
#define write_rtc_register(index, _value) \ if (rtc_enabled || rumble_enabled) {
update_address = 0x80000C4 + (index * 2); \ // Update the registers in the ROM mapped buffer.
rtc_registers[index] = _value; \ u8 *map = memory_map_read[0x8000000 >> 15];
rtc_page_index = update_address >> 15; \ if (map) {
map = memory_map_read[rtc_page_index]; \ if (gpio_regs[2]) {
\ // Registers are visible, readable:
if(map) { \ address16(map, 0xC4) = eswap16(gpio_regs[0]);
address16(map, update_address & 0x7FFF) = eswap16(_value); \ address16(map, 0xC6) = eswap16(gpio_regs[1]);
} \ address16(map, 0xC8) = eswap16(gpio_regs[2]);
} else {
void function_cc write_rtc(u32 address, u32 value) // Registers are write-only, just read out zero
{ address16(map, 0xC4) = 0;
u32 rtc_page_index; address16(map, 0xC6) = 0;
u32 update_address; address16(map, 0xC8) = 0;
u8 *map;
value &= 0xFFFF;
switch(address)
{
// RTC command
// Bit 0: SCHK, perform action
// Bit 1: IO, input/output command data
// Bit 2: CS, select input/output? If high make I/O write only
case 0xC4:
if(rtc_state == RTC_DISABLED)
rtc_state = RTC_IDLE;
if(!(rtc_registers[0] & 0x04))
value = (rtc_registers[0] & 0x02) | (value & ~0x02);
if(rtc_registers[2] & 0x01)
{
// To begin writing a command 1, 5 must be written to the command
// registers.
if((rtc_state == RTC_IDLE) && (rtc_registers[0] == 0x01) &&
(value == 0x05))
{
// We're now ready to begin receiving a command.
write_rtc_register(0, value);
rtc_state = RTC_COMMAND;
rtc_command = 0;
rtc_bit_count = 7;
}
else
{
write_rtc_register(0, value);
switch(rtc_state)
{
// Accumulate RTC command by receiving the next bit, and if we
// have accumulated enough bits to form a complete command
// execute it.
case RTC_COMMAND:
if(rtc_registers[0] & 0x01)
{
rtc_command |= ((value & 0x02) >> 1) << rtc_bit_count;
rtc_bit_count--;
}
// Have we received a full RTC command? If so execute it.
if(rtc_bit_count < 0)
{
switch(rtc_command)
{
// Resets RTC
case RTC_COMMAND_RESET:
rtc_state = RTC_IDLE;
memset(rtc_registers, 0, sizeof(rtc_registers));
break;
// Sets status of RTC
case RTC_COMMAND_WRITE_STATUS:
rtc_state = RTC_INPUT_DATA;
rtc_data_bytes = 1;
rtc_write_mode = RTC_WRITE_STATUS;
break;
// Outputs current status of RTC
case RTC_COMMAND_READ_STATUS:
rtc_state = RTC_OUTPUT_DATA;
rtc_data_bytes = 1;
rtc_data[0] = rtc_status;
break;
// Actually outputs the time, all of it
case RTC_COMMAND_OUTPUT_TIME_FULL:
{
struct tm *current_time;
time_t current_time_flat;
time(&current_time_flat);
current_time = localtime(&current_time_flat);
rtc_state = RTC_OUTPUT_DATA;
rtc_data_bytes = 7;
rtc_data[0] = encode_bcd(current_time->tm_year);
rtc_data[1] = encode_bcd(current_time->tm_mon + 1);
rtc_data[2] = encode_bcd(current_time->tm_mday);
rtc_data[3] = encode_bcd(current_time->tm_wday);
rtc_data[4] = encode_bcd(current_time->tm_hour);
rtc_data[5] = encode_bcd(current_time->tm_min);
rtc_data[6] = encode_bcd(current_time->tm_sec);
break;
}
// Only outputs the current time of day.
case RTC_COMMAND_OUTPUT_TIME:
{
struct tm *current_time;
time_t current_time_flat;
time(&current_time_flat);
current_time = localtime(&current_time_flat);
rtc_state = RTC_OUTPUT_DATA;
rtc_data_bytes = 3;
rtc_data[0] = encode_bcd(current_time->tm_hour);
rtc_data[1] = encode_bcd(current_time->tm_min);
rtc_data[2] = encode_bcd(current_time->tm_sec);
break;
}
}
rtc_bit_count = 0;
}
break;
// Receive parameters from the game as input to the RTC
// for a given command. Read one bit at a time.
case RTC_INPUT_DATA:
// Bit 1 of parameter A must be high for input
if(rtc_registers[1] & 0x02)
{
// Read next bit for input
if(!(value & 0x01))
{
rtc_data[rtc_bit_count >> 3] |=
((value & 0x01) << (7 - (rtc_bit_count & 0x07)));
}
else
{
rtc_bit_count++;
if(rtc_bit_count == (signed)(rtc_data_bytes * 8))
{
rtc_state = RTC_IDLE;
switch(rtc_write_mode)
{
case RTC_WRITE_STATUS:
rtc_status = rtc_data[0];
break;
default:
break;
}
}
}
}
break;
case RTC_OUTPUT_DATA:
// Bit 1 of parameter A must be low for output
if(!(rtc_registers[1] & 0x02))
{
// Write next bit to output, on bit 1 of parameter B
if(!(value & 0x01))
{
u8 current_output_byte = rtc_registers[2];
current_output_byte =
(current_output_byte & ~0x02) |
(((rtc_data[rtc_bit_count >> 3] >>
(rtc_bit_count & 0x07)) & 0x01) << 1);
write_rtc_register(0, current_output_byte);
}
else
{
rtc_bit_count++;
if(rtc_bit_count == (signed)(rtc_data_bytes * 8))
{
rtc_state = RTC_IDLE;
memset(rtc_registers, 0, sizeof(rtc_registers));
}
}
}
break;
default:
break;
}
}
} }
else }
{
write_rtc_register(2, value);
}
break;
// Write parameter A
case 0xC6:
write_rtc_register(1, value);
break;
// Write parameter B
case 0xC8:
write_rtc_register(2, value);
break;
} }
} }
#define write_rtc8() \ #define GPIO_RTC_CLK 0x1
#define GPIO_RTC_DAT 0x2
#define GPIO_RTC_CSS 0x4
#define write_rtc16() \ static void write_rtc(u8 old, u8 new)
write_rtc(address & 0xFF, value) \ {
// RTC works using a high CS and falling edge capture for the clock signal.
if (!(new & GPIO_RTC_CSS)) {
// Chip select is down, reset the RTC protocol. And do not process input.
rtc_state = RTC_IDLE;
rtc_command = 0;
rtc_bit_count = 0;
return;
}
#define write_rtc32() \ // CS low to high transition!
if (!(old & GPIO_RTC_CSS))
rtc_state = RTC_COMMAND;
if ((old & GPIO_RTC_CLK) && !(new & GPIO_RTC_CLK)) {
// Advance clock state, input/ouput data.
switch (rtc_state) {
case RTC_COMMAND:
rtc_command <<= 1;
rtc_command |= ((new >> 1) & 1);
// 8 bit command read, process:
if (++rtc_bit_count == 8) {
switch (rtc_command) {
case RTC_COMMAND_RESET:
case RTC_COMMAND_WRITE_STATUS:
rtc_state = RTC_INPUT_DATA;
rtc_data = 0;
rtc_data_bits = 8;
rtc_write_mode = RTC_WRITE_STATUS;
break;
case RTC_COMMAND_READ_STATUS:
rtc_state = RTC_OUTPUT_DATA;
rtc_data_bits = 8;
rtc_data = rtc_status;
break;
case RTC_COMMAND_OUTPUT_TIME_FULL:
{
struct tm *current_time;
time_t current_time_flat;
time(&current_time_flat);
current_time = localtime(&current_time_flat);
rtc_state = RTC_OUTPUT_DATA;
rtc_data_bits = 56;
rtc_data = ((u64)encode_bcd(current_time->tm_year)) |
((u64)encode_bcd(current_time->tm_mon+1)<< 8) |
((u64)encode_bcd(current_time->tm_mday) << 16) |
((u64)encode_bcd(current_time->tm_wday) << 24) |
((u64)encode_bcd(current_time->tm_hour) << 32) |
((u64)encode_bcd(current_time->tm_min) << 40) |
((u64)encode_bcd(current_time->tm_sec) << 48);
}
break;
case RTC_COMMAND_OUTPUT_TIME:
{
struct tm *current_time;
time_t current_time_flat;
time(&current_time_flat);
current_time = localtime(&current_time_flat);
rtc_state = RTC_OUTPUT_DATA;
rtc_data_bits = 24;
rtc_data = (encode_bcd(current_time->tm_hour)) |
(encode_bcd(current_time->tm_min) << 8) |
(encode_bcd(current_time->tm_sec) << 16);
}
break;
};
rtc_bit_count = 0;
}
break;
case RTC_INPUT_DATA:
rtc_data <<= 1;
rtc_data |= ((new >> 1) & 1);
rtc_data_bits--;
if (!rtc_data_bits) {
rtc_status = rtc_data; // HACK: assuming write status here.
rtc_state = RTC_IDLE;
}
break;
case RTC_OUTPUT_DATA:
// Output the next bit from rtc_data
if (!(gpio_regs[1] & 0x2)) {
// Only output if the port is set to OUT!
u32 bit = rtc_data & 1;
gpio_regs[0] = (new & ~0x2) | ((bit) << 1);
}
rtc_data >>= 1;
rtc_data_bits--;
if (!rtc_data_bits)
rtc_state = RTC_IDLE; // Finish transmission!
break;
};
}
}
static void write_rumble(u8 old, u8 new) {
if (new && !old)
rumble_enable_tick = cpu_ticks;
else if (!new && old) {
rumble_ticks += (cpu_ticks - rumble_enable_tick);
rumble_enable_tick = 0;
}
}
void rumble_frame_reset() {
// Reset the tick initial value to frame start (only if active)
rumble_ticks = 0;
if (rumble_enable_tick)
rumble_enable_tick = cpu_ticks;
}
float rumble_active_pct() {
// Calculate the percentage of Rumble active for this frame.
u32 active_ticks = rumble_ticks;
// If the rumble is still active, account for the due cycles
if (rumble_enable_tick)
active_ticks += (cpu_ticks - rumble_enable_tick);
return active_ticks / (GBC_BASE_RATE / 60);
}
void function_cc write_gpio(u32 address, u32 value) {
u8 prev_value = gpio_regs[0];
switch(address) {
case 0xC4:
// Any writes do not affect input pins:
gpio_regs[0] = (gpio_regs[0] & ~gpio_regs[1]) | (value & gpio_regs[1]);
break;
case 0xC6:
gpio_regs[1] = value & 0xF;
break;
case 0xC8: /* I/O port control */
gpio_regs[2] = value & 1;
break;
};
// If the game has an RTC, ensure it gets the data
if (rtc_enabled && (prev_value & 0x7) != (gpio_regs[0] & 0x7))
write_rtc(prev_value & 0x7, gpio_regs[0] & 0x7);
if (rumble_enabled && (prev_value & 0x8) != (gpio_regs[0] & 0x8))
write_rumble(prev_value & 0x8, gpio_regs[0] & 0x8);
// Reflect the values
update_gpio_romregs();
}
#define write_gpio8() \
#define write_gpio16() \
write_gpio(address & 0xFF, value) \
#define write_gpio32() \
#define write_memory(type) \ #define write_memory(type) \
switch(address >> 24) \ switch(address >> 24) \
@ -1460,7 +1433,7 @@ void function_cc write_rtc(u32 address, u32 value)
\ \
case 0x08: \ case 0x08: \
/* gamepak ROM or RTC */ \ /* gamepak ROM or RTC */ \
write_rtc##type(); \ write_gpio##type(); \
break; \ break; \
\ \
case 0x09: \ case 0x09: \
@ -1571,6 +1544,7 @@ typedef struct
#define FLAGS_FLASH_128KB 0x0001 #define FLAGS_FLASH_128KB 0x0001
#define FLAGS_RUMBLE 0x0002 #define FLAGS_RUMBLE 0x0002
#define FLAGS_RTC 0x0004
#include "gba_over.h" #include "gba_over.h"
@ -1602,6 +1576,12 @@ static void load_game_config_over(gamepak_info_t *gpinfo)
flash_bank_cnt = FLASH_SIZE_128KB; flash_bank_cnt = FLASH_SIZE_128KB;
} }
if (gbaover[i].flags & FLAGS_RTC)
rtc_enabled = true;
if (gbaover[i].flags & FLAGS_RUMBLE)
rumble_enabled = true;
if (gbaover[i].translation_gate_target_1 != 0) if (gbaover[i].translation_gate_target_1 != 0)
{ {
translation_gate_target_pc[translation_gate_targets] = gbaover[i].translation_gate_target_1; translation_gate_target_pc[translation_gate_targets] = gbaover[i].translation_gate_target_1;
@ -2192,12 +2172,9 @@ u8 *load_gamepak_page(u32 physical_index)
// Map it to the read handlers now // Map it to the read handlers now
map_rom_entry(read, physical_index, swap_location, gamepak_size >> 15); map_rom_entry(read, physical_index, swap_location, gamepak_size >> 15);
// If RTC is active page the RTC register bytes so they can be read // When mapping page 0, we might need to reflect the GPIO regs.
if ((rtc_state != RTC_DISABLED) && (physical_index == 0)) { if (physical_index == 0)
address16(swap_location, 0xC4) = eswap16(rtc_registers[0]); update_gpio_romregs();
address16(swap_location, 0xC6) = eswap16(rtc_registers[1]);
address16(swap_location, 0xC8) = eswap16(rtc_registers[2]);
}
return swap_location; return swap_location;
} }
@ -2288,11 +2265,13 @@ void init_memory(void)
eeprom_mode = EEPROM_BASE_MODE; eeprom_mode = EEPROM_BASE_MODE;
eeprom_address = 0; eeprom_address = 0;
eeprom_counter = 0; eeprom_counter = 0;
rumble_enable_tick = 0;
rumble_ticks = 0;
flash_mode = FLASH_BASE_MODE; flash_mode = FLASH_BASE_MODE;
rtc_state = RTC_DISABLED; rtc_state = RTC_DISABLED;
memset(rtc_registers, 0, sizeof(rtc_registers)); memset(gpio_regs, 0, sizeof(gpio_regs));
reg[REG_BUS_VALUE] = 0xe129f000; reg[REG_BUS_VALUE] = 0xe129f000;
} }
@ -2315,7 +2294,7 @@ bool memory_check_savestate(const u8 *src)
static const char *vars32[] = { static const char *vars32[] = {
"backup-type","flash-mode", "flash-cmd-pos", "flash-bank-num", "flash-dev-id", "backup-type","flash-mode", "flash-cmd-pos", "flash-bank-num", "flash-dev-id",
"flash-size", "eeprom-size", "eeprom-mode", "eeprom-addr", "eeprom-counter", "flash-size", "eeprom-size", "eeprom-mode", "eeprom-addr", "eeprom-counter",
"rtc-state", "rtc-write-mode", "rtc-cmd", "rtc-status", "rtc-data-byte-cnt", "rtc-bit-cnt", "rtc-state", "rtc-write-mode", "rtc-cmd", "rtc-status", "rtc-data-bit-cnt", "rtc-bit-cnt",
}; };
static const char *dmavars32[] = { static const char *dmavars32[] = {
"src-addr", "dst-addr", "src-dir", "dst-dir", "src-addr", "dst-addr", "src-dir", "dst-dir",
@ -2342,7 +2321,7 @@ bool memory_check_savestate(const u8 *src)
if (!bson_contains_key(bakdoc, vars32[i], BSON_TYPE_INT32)) if (!bson_contains_key(bakdoc, vars32[i], BSON_TYPE_INT32))
return false; return false;
if (!bson_contains_key(bakdoc, "rtc-regs", BSON_TYPE_BIN) || if (!bson_contains_key(bakdoc, "gpio-regs", BSON_TYPE_BIN) ||
!bson_contains_key(bakdoc, "rtc-data-words", BSON_TYPE_ARR)) !bson_contains_key(bakdoc, "rtc-data-words", BSON_TYPE_ARR))
return false; return false;
@ -2364,6 +2343,7 @@ bool memory_check_savestate(const u8 *src)
bool memory_read_savestate(const u8 *src) bool memory_read_savestate(const u8 *src)
{ {
int i; int i;
u32 rtc_data_array[2];
const u8 *memdoc = bson_find_key(src, "memory"); const u8 *memdoc = bson_find_key(src, "memory");
const u8 *bakdoc = bson_find_key(src, "backup"); const u8 *bakdoc = bson_find_key(src, "backup");
const u8 *dmadoc = bson_find_key(src, "dma"); const u8 *dmadoc = bson_find_key(src, "dma");
@ -2391,15 +2371,15 @@ bool memory_read_savestate(const u8 *src)
bson_read_int32(bakdoc, "eeprom-addr", &eeprom_address) && bson_read_int32(bakdoc, "eeprom-addr", &eeprom_address) &&
bson_read_int32(bakdoc, "eeprom-counter", &eeprom_counter) && bson_read_int32(bakdoc, "eeprom-counter", &eeprom_counter) &&
bson_read_bytes(bakdoc, "gpio-regs", gpio_regs, sizeof(gpio_regs)) &&
bson_read_int32(bakdoc, "rtc-state", &rtc_state) && bson_read_int32(bakdoc, "rtc-state", &rtc_state) &&
bson_read_int32(bakdoc, "rtc-write-mode", &rtc_write_mode) && bson_read_int32(bakdoc, "rtc-write-mode", &rtc_write_mode) &&
bson_read_int32(bakdoc, "rtc-cmd", &rtc_command) && bson_read_int32(bakdoc, "rtc-cmd", &rtc_command) &&
bson_read_int32(bakdoc, "rtc-status", &rtc_status) && bson_read_int32(bakdoc, "rtc-status", &rtc_status) &&
bson_read_int32(bakdoc, "rtc-data-byte-cnt", &rtc_data_bytes) && bson_read_int32(bakdoc, "rtc-data-bit-cnt", &rtc_data_bits) &&
bson_read_int32(bakdoc, "rtc-bit-cnt", (u32*)&rtc_bit_count) && bson_read_int32(bakdoc, "rtc-bit-cnt", (u32*)&rtc_bit_count) &&
bson_read_bytes(bakdoc, "rtc-regs", rtc_registers, sizeof(rtc_registers)) && bson_read_int32_array(bakdoc, "rtc-data-words", rtc_data_array, 2)))
bson_read_int32_array(bakdoc, "rtc-data-words", rtc_data,
sizeof(rtc_data)/sizeof(rtc_data[0]))))
return false; return false;
for (i = 0; i < DMA_CHAN_CNT; i++) for (i = 0; i < DMA_CHAN_CNT; i++)
@ -2420,6 +2400,8 @@ bool memory_read_savestate(const u8 *src)
return false; return false;
} }
rtc_data = rtc_data_array[0] | (((u64)rtc_data_array[1]) << 32);
return true; return true;
} }
@ -2427,6 +2409,8 @@ unsigned memory_write_savestate(u8 *dst)
{ {
int i; int i;
u8 *wbptr, *wbptr2, *startp = dst; u8 *wbptr, *wbptr2, *startp = dst;
u32 rtc_data_array[2] = { (u32)rtc_data, (u32)(rtc_data >> 32) };
bson_start_document(dst, "memory", wbptr); bson_start_document(dst, "memory", wbptr);
bson_write_bytes(dst, "iwram", &iwram[0x8000], 0x8000); bson_write_bytes(dst, "iwram", &iwram[0x8000], 0x8000);
bson_write_bytes(dst, "ewram", ewram, 0x40000); bson_write_bytes(dst, "ewram", ewram, 0x40000);
@ -2450,15 +2434,14 @@ unsigned memory_write_savestate(u8 *dst)
bson_write_int32(dst, "eeprom-addr", eeprom_address); bson_write_int32(dst, "eeprom-addr", eeprom_address);
bson_write_int32(dst, "eeprom-counter", eeprom_counter); bson_write_int32(dst, "eeprom-counter", eeprom_counter);
bson_write_bytes(dst, "gpio-regs", gpio_regs, sizeof(gpio_regs));
bson_write_int32(dst, "rtc-state", rtc_state); bson_write_int32(dst, "rtc-state", rtc_state);
bson_write_int32(dst, "rtc-write-mode", rtc_write_mode); bson_write_int32(dst, "rtc-write-mode", rtc_write_mode);
bson_write_int32(dst, "rtc-cmd", rtc_command); bson_write_int32(dst, "rtc-cmd", rtc_command);
bson_write_int32(dst, "rtc-status", rtc_status); bson_write_int32(dst, "rtc-status", rtc_status);
bson_write_int32(dst, "rtc-data-byte-cnt", rtc_data_bytes); bson_write_int32(dst, "rtc-data-bit-cnt", rtc_data_bits);
bson_write_int32(dst, "rtc-bit-cnt", rtc_bit_count); bson_write_int32(dst, "rtc-bit-cnt", rtc_bit_count);
bson_write_bytes(dst, "rtc-regs", rtc_registers, sizeof(rtc_registers)); bson_write_int32array(dst, "rtc-data-words", rtc_data_array, 2);
bson_write_int32array(dst, "rtc-data-words", rtc_data,
sizeof(rtc_data)/sizeof(rtc_data[0]));
bson_finish_document(dst, wbptr); bson_finish_document(dst, wbptr);
bson_start_document(dst, "dma", wbptr); bson_start_document(dst, "dma", wbptr);
@ -2525,7 +2508,8 @@ static s32 load_gamepak_raw(const char *name)
return -1; return -1;
} }
u32 load_gamepak(const struct retro_game_info* info, const char *name) u32 load_gamepak(const struct retro_game_info* info, const char *name,
int force_rtc, int force_rumble)
{ {
gamepak_info_t gpinfo; gamepak_info_t gpinfo;
@ -2542,9 +2526,17 @@ u32 load_gamepak(const struct retro_game_info* info, const char *name)
translation_gate_targets = 0; translation_gate_targets = 0;
flash_device_id = FLASH_DEVICE_MACRONIX_64KB; flash_device_id = FLASH_DEVICE_MACRONIX_64KB;
flash_bank_cnt = FLASH_SIZE_64KB; flash_bank_cnt = FLASH_SIZE_64KB;
rtc_enabled = false;
rumble_enabled = false;
load_game_config_over(&gpinfo); load_game_config_over(&gpinfo);
// Forced RTC / Rumble modes, override the autodetect logic.
if (force_rtc != FEAT_AUTODETECT)
rtc_enabled = (force_rtc == FEAT_ENABLE);
if (force_rumble != FEAT_AUTODETECT)
rumble_enabled = (force_rumble == FEAT_ENABLE);
return 0; return 0;
} }

View File

@ -22,6 +22,10 @@
#include "libretro.h" #include "libretro.h"
#define FEAT_AUTODETECT -1
#define FEAT_DISABLE 0
#define FEAT_ENABLE 1
#define DMA_CHAN_CNT 4 #define DMA_CHAN_CNT 4
#define DMA_START_IMMEDIATELY 0 #define DMA_START_IMMEDIATELY 0
@ -221,7 +225,10 @@ u32 function_cc read_eeprom(void);
void function_cc write_eeprom(u32 address, u32 value); void function_cc write_eeprom(u32 address, u32 value);
u8 read_backup(u32 address); u8 read_backup(u32 address);
void function_cc write_backup(u32 address, u32 value); void function_cc write_backup(u32 address, u32 value);
void function_cc write_rtc(u32 address, u32 value); void function_cc write_gpio(u32 address, u32 value);
void rumble_frame_reset();
float rumble_active_pct();
/* EDIT: Shouldn't this be extern ?! */ /* EDIT: Shouldn't this be extern ?! */
extern const u32 def_seq_cycles[16][2]; extern const u32 def_seq_cycles[16][2];
@ -237,7 +244,8 @@ extern char gamepak_filename[512];
cpu_alert_type dma_transfer(unsigned dma_chan, int *cycles); cpu_alert_type dma_transfer(unsigned dma_chan, int *cycles);
u8 *memory_region(u32 address, u32 *memory_limit); u8 *memory_region(u32 address, u32 *memory_limit);
u32 load_gamepak(const struct retro_game_info* info, const char *name); u32 load_gamepak(const struct retro_game_info* info, const char *name,
int force_rtc, int force_rumble);
s32 load_bios(char *name); s32 load_bios(char *name);
void init_memory(void); void init_memory(void);
void init_gamepak_buffer(void); void init_gamepak_buffer(void);

View File

@ -109,6 +109,17 @@ static const ini_t gbaover[] = {
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */ 0, /* translation_gate_target_3 */
}, },
{
// Shin Bokura no Taiyou: Gyakushuu no Sabata (J)
"BOKTAI3", /* gamepak_title */
"U33J", /* gamepak_code */
"A4", /* gamepak_maker */
FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */
},
{ {
// Bomberman Jetters Game Collection (J) // Bomberman Jetters Game Collection (J)
"BOMBERMANJGC", /* gamepak_title */ "BOMBERMANJGC", /* gamepak_title */
@ -498,6 +509,39 @@ static const ini_t gbaover[] = {
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */ 0, /* translation_gate_target_3 */
}, },
{
// Legendz - Sign of Nekuromu (J)
"LEGENDZSON__", /* gamepak_title */
"BLVJ", /* gamepak_code */
"B2", /* gamepak_maker */
FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */
},
{
// Legendz - Yomigaeru Shiren no Shima (J)
"LEGENDZSHIMA", /* gamepak_title */
"BLJJ", /* gamepak_code */
"B2", /* gamepak_maker */
FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */
},
{
// Legendz - Buhwarhaneun Siryeonyi Seom (K)
"LEGENDZSHIMA", /* gamepak_title */
"BLJK", /* gamepak_code */
"B2", /* gamepak_maker */
FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */
},
{ {
// Magical Houshin (J) // Magical Houshin (J)
"M HOUSHIN", /* gamepak_title */ "M HOUSHIN", /* gamepak_title */
@ -976,7 +1020,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */ "POKEMON EMER", /* gamepak_title */
"BPEE", /* gamepak_code */ "BPEE", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */ 0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -987,7 +1031,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */ "POKEMON EMER", /* gamepak_title */
"BPEJ", /* gamepak_code */ "BPEJ", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */ 0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -998,7 +1042,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */ "POKEMON EMER", /* gamepak_title */
"BPED", /* gamepak_code */ "BPED", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */ 0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1009,7 +1053,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */ "POKEMON EMER", /* gamepak_title */
"BPEF", /* gamepak_code */ "BPEF", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */ 0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1020,7 +1064,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */ "POKEMON EMER", /* gamepak_title */
"BPES", /* gamepak_code */ "BPES", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */ 0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1031,7 +1075,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */ "POKEMON EMER", /* gamepak_title */
"BPEI", /* gamepak_code */ "BPEI", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */ 0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1042,7 +1086,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */ "POKEMON SAPP", /* gamepak_title */
"AXPE", /* gamepak_code */ "AXPE", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1053,7 +1097,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */ "POKEMON SAPP", /* gamepak_title */
"AXPJ", /* gamepak_code */ "AXPJ", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1064,7 +1108,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */ "POKEMON SAPP", /* gamepak_title */
"AXPD", /* gamepak_code */ "AXPD", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1075,7 +1119,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */ "POKEMON SAPP", /* gamepak_title */
"AXPI", /* gamepak_code */ "AXPI", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1086,7 +1130,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */ "POKEMON SAPP", /* gamepak_title */
"AXPS", /* gamepak_code */ "AXPS", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1097,7 +1141,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */ "POKEMON SAPP", /* gamepak_title */
"AXPF", /* gamepak_code */ "AXPF", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1108,7 +1152,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */ "POKEMON RUBY", /* gamepak_title */
"AXVE", /* gamepak_code */ "AXVE", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1119,7 +1163,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */ "POKEMON RUBY", /* gamepak_title */
"AXVJ", /* gamepak_code */ "AXVJ", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1130,7 +1174,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */ "POKEMON RUBY", /* gamepak_title */
"AXVD", /* gamepak_code */ "AXVD", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1141,7 +1185,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */ "POKEMON RUBY", /* gamepak_title */
"AXVI", /* gamepak_code */ "AXVI", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1152,7 +1196,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */ "POKEMON RUBY", /* gamepak_title */
"AXVS", /* gamepak_code */ "AXVS", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1163,7 +1207,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */ "POKEMON RUBY", /* gamepak_title */
"AXVF", /* gamepak_code */ "AXVF", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1452,12 +1496,23 @@ static const ini_t gbaover[] = {
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */ 0, /* translation_gate_target_3 */
}, },
{
// RockMan EXE 4.5 - Real Operation (J)
"ROCKEXE4.5RO", /* gamepak_title */
"BR4J", /* gamepak_code */
"08", /* gamepak_maker */
FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */
},
{ {
// Sennen Kazoku (J) // Sennen Kazoku (J)
"SENNENKAZOKU", /* gamepak_title */ "SENNENKAZOKU", /* gamepak_title */
"BKAJ", /* gamepak_code */ "BKAJ", /* gamepak_code */
"01", /* gamepak_maker */ "01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */ FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */ 0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */ 0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
@ -1782,6 +1837,39 @@ static const ini_t gbaover[] = {
0, /* translation_gate_target_2 */ 0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */ 0, /* translation_gate_target_3 */
}, },
{
// Wario Ware, Twisted (U)
"WARIOTWISTED", /* gamepak_title */
"RZWE", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_RUMBLE, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */
},
{
// Wario Ware, Twisted (E)
"WARIOTWISTED", /* gamepak_title */
"RZWP", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_RUMBLE, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */
},
{
// Wario Ware, Twisted (J)
"MAWARUWARIO", /* gamepak_title */
"RZWJ", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_RUMBLE, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
0, /* translation_gate_target_3 */
},
{ {
// Yu-Gi-Oh! - Dungeon Dice Monsters (U) // Yu-Gi-Oh! - Dungeon Dice Monsters (U)
"YU-GI-OH DDM", /* gamepak_title */ "YU-GI-OH DDM", /* gamepak_title */

View File

@ -84,6 +84,7 @@ static retro_video_refresh_t video_cb;
static retro_audio_sample_batch_t audio_batch_cb; static retro_audio_sample_batch_t audio_batch_cb;
static retro_input_poll_t input_poll_cb; static retro_input_poll_t input_poll_cb;
static retro_environment_t environ_cb; static retro_environment_t environ_cb;
static retro_set_rumble_state_t rumble_cb;
struct retro_perf_callback perf_cb; struct retro_perf_callback perf_cb;
@ -91,6 +92,8 @@ int dynarec_enable;
boot_mode selected_boot_mode = boot_game; boot_mode selected_boot_mode = boot_game;
int sprite_limit = 1; int sprite_limit = 1;
static int rtc_mode = FEAT_AUTODETECT, rumble_mode = FEAT_AUTODETECT;
u32 idle_loop_target_pc = 0xFFFFFFFF; u32 idle_loop_target_pc = 0xFFFFFFFF;
u32 translation_gate_target_pc[MAX_TRANSLATION_GATES]; u32 translation_gate_target_pc[MAX_TRANSLATION_GATES];
u32 translation_gate_targets = 0; u32 translation_gate_targets = 0;
@ -744,7 +747,7 @@ static void extract_directory(char* buf, const char* path, size_t size)
strncpy(buf, ".", size); strncpy(buf, ".", size);
} }
static void check_variables(int started_from_load) static void check_variables(bool started_from_load)
{ {
struct retro_variable var; struct retro_variable var;
bool frameskip_type_prev; bool frameskip_type_prev;
@ -776,7 +779,6 @@ static void check_variables(int started_from_load)
if (started_from_load) { if (started_from_load) {
var.key = "gpsp_bios"; var.key = "gpsp_bios";
var.value = 0; var.value = 0;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{ {
if (!strcmp(var.value, "auto")) if (!strcmp(var.value, "auto"))
@ -789,7 +791,6 @@ static void check_variables(int started_from_load)
var.key = "gpsp_boot_mode"; var.key = "gpsp_boot_mode";
var.value = 0; var.value = 0;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{ {
if (!strcmp(var.value, "game")) if (!strcmp(var.value, "game"))
@ -797,6 +798,30 @@ static void check_variables(int started_from_load)
else if (!strcmp(var.value, "bios")) else if (!strcmp(var.value, "bios"))
selected_boot_mode = boot_bios; selected_boot_mode = boot_bios;
} }
var.key = "gpsp_rtc";
var.value = 0;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "disabled"))
rtc_mode = FEAT_DISABLE;
else if (!strcmp(var.value, "enabled"))
rtc_mode = FEAT_ENABLE;
else
rtc_mode = FEAT_AUTODETECT;
}
var.key = "gpsp_rumble";
var.value = 0;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "disabled"))
rumble_mode = FEAT_DISABLE;
else if (!strcmp(var.value, "enabled"))
rumble_mode = FEAT_ENABLE;
else
rumble_mode = FEAT_AUTODETECT;
}
} }
var.key = "gpsp_sprlim"; var.key = "gpsp_sprlim";
@ -956,7 +981,7 @@ bool retro_load_game(const struct retro_game_info* info)
if (!info) if (!info)
return false; return false;
check_variables(1); check_variables(true);
set_input_descriptors(); set_input_descriptors();
char filename_bios[MAX_PATH]; char filename_bios[MAX_PATH];
@ -1000,12 +1025,18 @@ bool retro_load_game(const struct retro_game_info* info)
} }
memset(gamepak_backup, 0xff, sizeof(gamepak_backup)); memset(gamepak_backup, 0xff, sizeof(gamepak_backup));
if (load_gamepak(info, info->path) != 0) if (load_gamepak(info, info->path, rtc_mode, rumble_mode) != 0)
{ {
error_msg("Could not load the game file."); error_msg("Could not load the game file.");
return false; return false;
} }
struct retro_rumble_interface rumbleif;
if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumbleif))
rumble_cb = rumbleif.set_rumble_state;
else
rumble_cb = NULL;
reset_gba(); reset_gba();
set_memory_descriptors(); set_memory_descriptors();
@ -1063,6 +1094,8 @@ void retro_run(void)
input_poll_cb(); input_poll_cb();
update_input(); update_input();
rumble_frame_reset();
/* Check whether current frame should /* Check whether current frame should
* be skipped */ * be skipped */
skip_next_frame = 0; skip_next_frame = 0;
@ -1145,11 +1178,18 @@ void retro_run(void)
execute_arm(execute_cycles); execute_arm(execute_cycles);
} }
if (rumble_cb) {
// TODO: Add some user-option to select a rumble policy
u32 strength = 0xffff * rumble_active_pct();
rumble_cb(0, RETRO_RUMBLE_WEAK, MIN(strength, 0xffff));
rumble_cb(0, RETRO_RUMBLE_STRONG, MIN(strength, 0xffff) / 2);
}
audio_run(); audio_run();
video_run(); video_run();
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
check_variables(0); check_variables(false);
} }
unsigned retro_api_version(void) unsigned retro_api_version(void)

View File

@ -87,6 +87,30 @@ struct retro_core_option_definition option_defs_us[] = {
}, },
"disabled" "disabled"
}, },
{
"gpsp_rtc",
"RTC support",
"Sets the RTC support for the emulated cartridge. Autodetect uses a ROM database that works with most commercial titles. You might need to force RTC when using homebrew or ROM hacks.",
{
{ "auto", NULL },
{ "enabled", NULL },
{ "disabled", NULL },
{ NULL, NULL },
},
"auto"
},
{
"gpsp_rumble",
"Rumble support",
"Sets the Rumble support for the emulated cartridge. Autodetect uses a ROM database that works with most commercial titles. You might want to force Rumble when using homebrew or ROM hacks that support it. You can also force-disable it if you don't like it.",
{
{ "auto", NULL },
{ "enabled", NULL },
{ "disabled", NULL },
{ NULL, NULL },
},
"auto"
},
{ {
"gpsp_frameskip", "gpsp_frameskip",
"Frameskip", "Frameskip",

View File

@ -2471,7 +2471,7 @@ static void emit_saveaccess_stub(u8 **tr_ptr) {
// Writes to region 8 are directed to RTC (only 16 bit ones though) // Writes to region 8 are directed to RTC (only 16 bit ones though)
tmemld[1][8] = (u32)translation_ptr; tmemld[1][8] = (u32)translation_ptr;
emit_mem_call(&write_rtc, 0xFE); emit_mem_call(&write_gpio, 0xFE);
// These are for region 0xD where EEPROM is mapped. Addr is ignored // These are for region 0xD where EEPROM is mapped. Addr is ignored
// Value is limited to one bit (both reading and writing!) // Value is limited to one bit (both reading and writing!)
@ -2524,7 +2524,7 @@ static void emit_saveaccess_stub(u8 **tr_ptr) {
mips_emit_xori(reg_rv, reg_temp, 0x08); mips_emit_xori(reg_rv, reg_temp, 0x08);
mips_emit_b(bne, reg_rv, reg_zero, st_phndlr_branch(strop)); mips_emit_b(bne, reg_rv, reg_zero, st_phndlr_branch(strop));
if (strop == 1) { if (strop == 1) {
emit_mem_call(&write_rtc, 0xFF); // Addr emit_mem_call(&write_gpio, 0xFF); // Addr
} else { } else {
mips_emit_nop(); mips_emit_nop();
mips_emit_jr(mips_reg_ra); // Do nothing mips_emit_jr(mips_reg_ra); // Do nothing

View File

@ -92,7 +92,7 @@ bool bson_read_bytes(const u8 *srcp, const char *key, void* buffer, unsigned cnt
/* this is an upper limit, leave room for future (?) stuff */ /* this is an upper limit, leave room for future (?) stuff */
#define GBA_STATE_MEM_SIZE (416*1024) #define GBA_STATE_MEM_SIZE (416*1024)
#define GBA_STATE_MAGIC 0x06BAC0DE #define GBA_STATE_MAGIC 0x06BAC0DE
#define GBA_STATE_VERSION 0x00010002 #define GBA_STATE_VERSION 0x00010003
bool gba_load_state(const void *src); bool gba_load_state(const void *src);
void gba_save_state(void *dst); void gba_save_state(void *dst);

View File

@ -219,8 +219,8 @@ defsymbl(x86_indirect_branch_dual)
# General ext memory routines # General ext memory routines
ext_store_rtc8: # No RTC writes on byte or word access ext_store_gpio8: # No GPIO/RTC writes on byte or word access
ext_store_rtc32: ext_store_gpio32:
ext_store_backup16: # Backup (flash) accessed via byte writes ext_store_backup16: # Backup (flash) accessed via byte writes
ext_store_backup32: ext_store_backup32:
ext_store_eeprom8: # EEPROM accesses are performed using 16 bit DMA ext_store_eeprom8: # EEPROM accesses are performed using 16 bit DMA
@ -228,11 +228,11 @@ ext_store_eeprom32:
ext_store_ignore: ext_store_ignore:
ret # ignore these writes ret # ignore these writes
ext_store_rtc16: ext_store_gpio16:
and $0xFFFF, %edx # make value 16bit and $0xFFFF, %edx # make value 16bit
and $0xFF, %eax # mask address and $0xFF, %eax # mask address
SETUP_ARGS # Setup addr, value SETUP_ARGS # Setup addr, value
CALL_FUNC(write_rtc) # write out RTC register CALL_FUNC(write_gpio) # write out RTC register
ret ret
ext_store_backup8: ext_store_backup8:
@ -565,7 +565,7 @@ return_to_main:
ADDR_TYPE ext_store_palette##asize /* 0x05 Palette RAM */;\ ADDR_TYPE ext_store_palette##asize /* 0x05 Palette RAM */;\
ADDR_TYPE ext_store_vram##asize /* 0x06 VRAM */;\ ADDR_TYPE ext_store_vram##asize /* 0x06 VRAM */;\
ADDR_TYPE ext_store_oam##asize /* 0x07 OAM RAM */;\ ADDR_TYPE ext_store_oam##asize /* 0x07 OAM RAM */;\
ADDR_TYPE ext_store_rtc##asize /* 0x08 gamepak (RTC or ignore) */;\ ADDR_TYPE ext_store_gpio##asize /* 0x08 gamepak (RTC or ignore) */;\
ADDR_TYPE ext_store_ignore /* 0x09 gamepak, ignore */;\ ADDR_TYPE ext_store_ignore /* 0x09 gamepak, ignore */;\
ADDR_TYPE ext_store_ignore /* 0x0A gamepak, ignore */;\ ADDR_TYPE ext_store_ignore /* 0x0A gamepak, ignore */;\
ADDR_TYPE ext_store_ignore /* 0x0B gamepak, ignore */;\ ADDR_TYPE ext_store_ignore /* 0x0B gamepak, ignore */;\