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_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_write_mode;
u8 rtc_registers[3];
u32 rtc_command;
u32 rtc_data[12];
u64 rtc_data;
u32 rtc_data_bits;
u32 rtc_status = 0x40;
u32 rtc_data_bytes;
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 h = 0;
@ -1200,224 +1208,189 @@ static u32 encode_bcd(u8 value)
return h * 16 + l;
}
// RTC writes need to reflect in the bytes [0xC4..0xC9] of the gamepak
#define write_rtc_register(index, _value) \
update_address = 0x80000C4 + (index * 2); \
rtc_registers[index] = _value; \
rtc_page_index = update_address >> 15; \
map = memory_map_read[rtc_page_index]; \
\
if(map) { \
address16(map, update_address & 0x7FFF) = eswap16(_value); \
} \
void update_gpio_romregs() {
if (rtc_enabled || rumble_enabled) {
// Update the registers in the ROM mapped buffer.
u8 *map = memory_map_read[0x8000000 >> 15];
if (map) {
if (gpio_regs[2]) {
// Registers are visible, readable:
address16(map, 0xC4) = eswap16(gpio_regs[0]);
address16(map, 0xC6) = eswap16(gpio_regs[1]);
address16(map, 0xC8) = eswap16(gpio_regs[2]);
} else {
// Registers are write-only, just read out zero
address16(map, 0xC4) = 0;
address16(map, 0xC6) = 0;
address16(map, 0xC8) = 0;
}
}
}
}
void function_cc write_rtc(u32 address, u32 value)
#define GPIO_RTC_CLK 0x1
#define GPIO_RTC_DAT 0x2
#define GPIO_RTC_CSS 0x4
static void write_rtc(u8 old, u8 new)
{
u32 rtc_page_index;
u32 update_address;
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 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;
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;
rtc_bit_count = 0;
return;
}
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.
// 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:
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
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:
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_data = 0;
rtc_data_bits = 8;
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;
rtc_data_bits = 8;
rtc_data = 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;
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);
}
// Only outputs the current time of day.
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_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);
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;
// 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_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;
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);
// 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);
}
else
{
rtc_bit_count++;
rtc_data >>= 1;
rtc_data_bits--;
if (!rtc_data_bits)
rtc_state = RTC_IDLE; // Finish transmission!
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() \
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;
}
}
#define write_rtc16() \
write_rtc(address & 0xFF, value) \
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;
}
#define write_rtc32() \
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) \
switch(address >> 24) \
@ -1460,7 +1433,7 @@ void function_cc write_rtc(u32 address, u32 value)
\
case 0x08: \
/* gamepak ROM or RTC */ \
write_rtc##type(); \
write_gpio##type(); \
break; \
\
case 0x09: \
@ -1571,6 +1544,7 @@ typedef struct
#define FLAGS_FLASH_128KB 0x0001
#define FLAGS_RUMBLE 0x0002
#define FLAGS_RTC 0x0004
#include "gba_over.h"
@ -1602,6 +1576,12 @@ static void load_game_config_over(gamepak_info_t *gpinfo)
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)
{
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_rom_entry(read, physical_index, swap_location, gamepak_size >> 15);
// If RTC is active page the RTC register bytes so they can be read
if ((rtc_state != RTC_DISABLED) && (physical_index == 0)) {
address16(swap_location, 0xC4) = eswap16(rtc_registers[0]);
address16(swap_location, 0xC6) = eswap16(rtc_registers[1]);
address16(swap_location, 0xC8) = eswap16(rtc_registers[2]);
}
// When mapping page 0, we might need to reflect the GPIO regs.
if (physical_index == 0)
update_gpio_romregs();
return swap_location;
}
@ -2288,11 +2265,13 @@ void init_memory(void)
eeprom_mode = EEPROM_BASE_MODE;
eeprom_address = 0;
eeprom_counter = 0;
rumble_enable_tick = 0;
rumble_ticks = 0;
flash_mode = FLASH_BASE_MODE;
rtc_state = RTC_DISABLED;
memset(rtc_registers, 0, sizeof(rtc_registers));
memset(gpio_regs, 0, sizeof(gpio_regs));
reg[REG_BUS_VALUE] = 0xe129f000;
}
@ -2315,7 +2294,7 @@ bool memory_check_savestate(const u8 *src)
static const char *vars32[] = {
"backup-type","flash-mode", "flash-cmd-pos", "flash-bank-num", "flash-dev-id",
"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[] = {
"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))
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))
return false;
@ -2364,6 +2343,7 @@ bool memory_check_savestate(const u8 *src)
bool memory_read_savestate(const u8 *src)
{
int i;
u32 rtc_data_array[2];
const u8 *memdoc = bson_find_key(src, "memory");
const u8 *bakdoc = bson_find_key(src, "backup");
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-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-write-mode", &rtc_write_mode) &&
bson_read_int32(bakdoc, "rtc-cmd", &rtc_command) &&
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_bytes(bakdoc, "rtc-regs", rtc_registers, sizeof(rtc_registers)) &&
bson_read_int32_array(bakdoc, "rtc-data-words", rtc_data,
sizeof(rtc_data)/sizeof(rtc_data[0]))))
bson_read_int32_array(bakdoc, "rtc-data-words", rtc_data_array, 2)))
return false;
for (i = 0; i < DMA_CHAN_CNT; i++)
@ -2420,6 +2400,8 @@ bool memory_read_savestate(const u8 *src)
return false;
}
rtc_data = rtc_data_array[0] | (((u64)rtc_data_array[1]) << 32);
return true;
}
@ -2427,6 +2409,8 @@ unsigned memory_write_savestate(u8 *dst)
{
int i;
u8 *wbptr, *wbptr2, *startp = dst;
u32 rtc_data_array[2] = { (u32)rtc_data, (u32)(rtc_data >> 32) };
bson_start_document(dst, "memory", wbptr);
bson_write_bytes(dst, "iwram", &iwram[0x8000], 0x8000);
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-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-write-mode", rtc_write_mode);
bson_write_int32(dst, "rtc-cmd", rtc_command);
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_bytes(dst, "rtc-regs", rtc_registers, sizeof(rtc_registers));
bson_write_int32array(dst, "rtc-data-words", rtc_data,
sizeof(rtc_data)/sizeof(rtc_data[0]));
bson_write_int32array(dst, "rtc-data-words", rtc_data_array, 2);
bson_finish_document(dst, wbptr);
bson_start_document(dst, "dma", wbptr);
@ -2525,7 +2508,8 @@ static s32 load_gamepak_raw(const char *name)
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;
@ -2542,9 +2526,17 @@ u32 load_gamepak(const struct retro_game_info* info, const char *name)
translation_gate_targets = 0;
flash_device_id = FLASH_DEVICE_MACRONIX_64KB;
flash_bank_cnt = FLASH_SIZE_64KB;
rtc_enabled = false;
rumble_enabled = false;
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;
}

View File

@ -22,6 +22,10 @@
#include "libretro.h"
#define FEAT_AUTODETECT -1
#define FEAT_DISABLE 0
#define FEAT_ENABLE 1
#define DMA_CHAN_CNT 4
#define DMA_START_IMMEDIATELY 0
@ -221,7 +225,10 @@ u32 function_cc read_eeprom(void);
void function_cc write_eeprom(u32 address, u32 value);
u8 read_backup(u32 address);
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 ?! */
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);
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);
void init_memory(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_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)
"BOMBERMANJGC", /* gamepak_title */
@ -498,6 +509,39 @@ static const ini_t gbaover[] = {
0, /* translation_gate_target_2 */
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)
"M HOUSHIN", /* gamepak_title */
@ -976,7 +1020,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */
"BPEE", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -987,7 +1031,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */
"BPEJ", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -998,7 +1042,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */
"BPED", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1009,7 +1053,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */
"BPEF", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1020,7 +1064,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */
"BPES", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1031,7 +1075,7 @@ static const ini_t gbaover[] = {
"POKEMON EMER", /* gamepak_title */
"BPEI", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0x80008ce, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1042,7 +1086,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */
"AXPE", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1053,7 +1097,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */
"AXPJ", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1064,7 +1108,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */
"AXPD", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1075,7 +1119,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */
"AXPI", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1086,7 +1130,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */
"AXPS", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1097,7 +1141,7 @@ static const ini_t gbaover[] = {
"POKEMON SAPP", /* gamepak_title */
"AXPF", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1108,7 +1152,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */
"AXVE", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1119,7 +1163,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */
"AXVJ", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1130,7 +1174,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */
"AXVD", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1141,7 +1185,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */
"AXVI", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1152,7 +1196,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */
"AXVS", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1163,7 +1207,7 @@ static const ini_t gbaover[] = {
"POKEMON RUBY", /* gamepak_title */
"AXVF", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1452,12 +1496,23 @@ static const ini_t gbaover[] = {
0, /* translation_gate_target_2 */
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)
"SENNENKAZOKU", /* gamepak_title */
"BKAJ", /* gamepak_code */
"01", /* gamepak_maker */
FLAGS_FLASH_128KB, /* flags */
FLAGS_FLASH_128KB | FLAGS_RTC, /* flags */
0, /* idle_loop_target_pc */
0, /* translation_gate_target_1 */
0, /* translation_gate_target_2 */
@ -1782,6 +1837,39 @@ static const ini_t gbaover[] = {
0, /* translation_gate_target_2 */
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 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_input_poll_t input_poll_cb;
static retro_environment_t environ_cb;
static retro_set_rumble_state_t rumble_cb;
struct retro_perf_callback perf_cb;
@ -91,6 +92,8 @@ int dynarec_enable;
boot_mode selected_boot_mode = boot_game;
int sprite_limit = 1;
static int rtc_mode = FEAT_AUTODETECT, rumble_mode = FEAT_AUTODETECT;
u32 idle_loop_target_pc = 0xFFFFFFFF;
u32 translation_gate_target_pc[MAX_TRANSLATION_GATES];
u32 translation_gate_targets = 0;
@ -744,7 +747,7 @@ static void extract_directory(char* buf, const char* path, size_t size)
strncpy(buf, ".", size);
}
static void check_variables(int started_from_load)
static void check_variables(bool started_from_load)
{
struct retro_variable var;
bool frameskip_type_prev;
@ -776,7 +779,6 @@ static void check_variables(int started_from_load)
if (started_from_load) {
var.key = "gpsp_bios";
var.value = 0;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "auto"))
@ -789,7 +791,6 @@ static void check_variables(int started_from_load)
var.key = "gpsp_boot_mode";
var.value = 0;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
if (!strcmp(var.value, "game"))
@ -797,6 +798,30 @@ static void check_variables(int started_from_load)
else if (!strcmp(var.value, "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";
@ -956,7 +981,7 @@ bool retro_load_game(const struct retro_game_info* info)
if (!info)
return false;
check_variables(1);
check_variables(true);
set_input_descriptors();
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));
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.");
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();
set_memory_descriptors();
@ -1063,6 +1094,8 @@ void retro_run(void)
input_poll_cb();
update_input();
rumble_frame_reset();
/* Check whether current frame should
* be skipped */
skip_next_frame = 0;
@ -1145,11 +1178,18 @@ void retro_run(void)
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();
video_run();
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
check_variables(0);
check_variables(false);
}
unsigned retro_api_version(void)

View File

@ -87,6 +87,30 @@ struct retro_core_option_definition option_defs_us[] = {
},
"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",
"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)
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
// 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_b(bne, reg_rv, reg_zero, st_phndlr_branch(strop));
if (strop == 1) {
emit_mem_call(&write_rtc, 0xFF); // Addr
emit_mem_call(&write_gpio, 0xFF); // Addr
} else {
mips_emit_nop();
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 */
#define GBA_STATE_MEM_SIZE (416*1024)
#define GBA_STATE_MAGIC 0x06BAC0DE
#define GBA_STATE_VERSION 0x00010002
#define GBA_STATE_VERSION 0x00010003
bool gba_load_state(const void *src);
void gba_save_state(void *dst);

View File

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