Multiplayer SGB APIs/SGB detection
This commit is contained in:
parent
5c581651ce
commit
7735d638c6
@ -640,6 +640,8 @@ void GB_reset(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
gb->accessed_oam_row = -1;
|
gb->accessed_oam_row = -1;
|
||||||
|
|
||||||
|
gb->sgb_player_count = 1;
|
||||||
|
|
||||||
/* Todo: Ugly, fixme, see comment in the timer state machine */
|
/* Todo: Ugly, fixme, see comment in the timer state machine */
|
||||||
gb->div_state = 3;
|
gb->div_state = 3;
|
||||||
|
|
||||||
|
@ -474,6 +474,8 @@ struct GB_gameboy_internal_s {
|
|||||||
bool sgb_ready_for_pulse;
|
bool sgb_ready_for_pulse;
|
||||||
bool sgb_ready_for_write;
|
bool sgb_ready_for_write;
|
||||||
bool sgb_disable_commands;
|
bool sgb_disable_commands;
|
||||||
|
/* Multiplayer Input */
|
||||||
|
uint8_t sgb_player_count, sgb_current_player;
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
|
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
|
||||||
@ -500,7 +502,7 @@ struct GB_gameboy_internal_s {
|
|||||||
uint32_t background_palettes_rgb[0x20];
|
uint32_t background_palettes_rgb[0x20];
|
||||||
uint32_t sprite_palettes_rgb[0x20];
|
uint32_t sprite_palettes_rgb[0x20];
|
||||||
GB_color_correction_mode_t color_correction_mode;
|
GB_color_correction_mode_t color_correction_mode;
|
||||||
bool keys[GB_KEY_MAX];
|
bool keys[4][GB_KEY_MAX];
|
||||||
|
|
||||||
/* Timing */
|
/* Timing */
|
||||||
uint64_t last_sync;
|
uint64_t last_sync;
|
||||||
|
@ -13,14 +13,19 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
|||||||
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
||||||
switch (key_selection) {
|
switch (key_selection) {
|
||||||
case 3:
|
case 3:
|
||||||
/* Nothing is wired, all up */
|
if (gb->sgb_player_count > 1) {
|
||||||
gb->io_registers[GB_IO_JOYP] |= 0x0F;
|
gb->io_registers[GB_IO_JOYP] |= 0xF - gb->sgb_current_player;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Nothing is wired, all up */
|
||||||
|
gb->io_registers[GB_IO_JOYP] |= 0x0F;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
/* Direction keys */
|
/* Direction keys */
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[i]) << i;
|
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[gb->sgb_current_player][i]) << i;
|
||||||
}
|
}
|
||||||
/* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */
|
/* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */
|
||||||
if (!(gb->io_registers[GB_IO_JOYP] & 1)) {
|
if (!(gb->io_registers[GB_IO_JOYP] & 1)) {
|
||||||
@ -34,13 +39,13 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
|||||||
case 1:
|
case 1:
|
||||||
/* Other keys */
|
/* Other keys */
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[i + 4]) << i;
|
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[gb->sgb_current_player][i + 4]) << i;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
gb->io_registers[GB_IO_JOYP] |= (!(gb->keys[i] || gb->keys[i + 4])) << i;
|
gb->io_registers[GB_IO_JOYP] |= (!(gb->keys[gb->sgb_current_player][i] || gb->keys[gb->sgb_current_player][i + 4])) << i;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -60,5 +65,12 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
|||||||
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed)
|
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed)
|
||||||
{
|
{
|
||||||
assert(index >= 0 && index < GB_KEY_MAX);
|
assert(index >= 0 && index < GB_KEY_MAX);
|
||||||
gb->keys[index] = pressed;
|
gb->keys[0][index] = pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed)
|
||||||
|
{
|
||||||
|
assert(index >= 0 && index < GB_KEY_MAX);
|
||||||
|
assert(player < 4);
|
||||||
|
gb->keys[player][index] = pressed;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ typedef enum {
|
|||||||
} GB_key_t;
|
} GB_key_t;
|
||||||
|
|
||||||
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed);
|
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed);
|
||||||
|
void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed);
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
void GB_update_joyp(GB_gameboy_t *gb);
|
void GB_update_joyp(GB_gameboy_t *gb);
|
||||||
|
@ -730,10 +730,10 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case GB_IO_JOYP:
|
case GB_IO_JOYP:
|
||||||
|
GB_sgb_write(gb, value);
|
||||||
gb->io_registers[GB_IO_JOYP] &= 0x0F;
|
gb->io_registers[GB_IO_JOYP] &= 0x0F;
|
||||||
gb->io_registers[GB_IO_JOYP] |= value & 0xF0;
|
gb->io_registers[GB_IO_JOYP] |= value & 0xF0;
|
||||||
GB_update_joyp(gb);
|
GB_update_joyp(gb);
|
||||||
GB_sgb_write(gb, value);
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case GB_IO_BIOS:
|
case GB_IO_BIOS:
|
||||||
|
18
Core/sgb.c
18
Core/sgb.c
@ -1,5 +1,9 @@
|
|||||||
#include "sgb.h"
|
#include "sgb.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MLT_REQ = 0x11,
|
||||||
|
};
|
||||||
|
|
||||||
static void command_ready(GB_gameboy_t *gb)
|
static void command_ready(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
/* SGB header commands are used to send the contents of the header to the SNES CPU.
|
/* SGB header commands are used to send the contents of the header to the SNES CPU.
|
||||||
@ -31,13 +35,21 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
gb->sgb_disable_commands = true;
|
gb->sgb_disable_commands = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (gb->sgb_command[0] >> 3) {
|
||||||
|
case MLT_REQ:
|
||||||
|
gb->sgb_player_count = (uint8_t[]){1, 2, 1, 4}[gb->sgb_command[1] & 3];
|
||||||
|
gb->sgb_current_player = gb->sgb_player_count - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
||||||
{
|
{
|
||||||
if (!GB_is_sgb(gb)) return;
|
if (!GB_is_sgb(gb)) return;
|
||||||
if (gb->sgb_disable_commands) return;
|
if (gb->sgb_disable_commands) return;
|
||||||
switch ((value >> 4) & 3 ) {
|
|
||||||
|
switch ((value >> 4) & 3) {
|
||||||
case 3:
|
case 3:
|
||||||
gb->sgb_ready_for_pulse = true;
|
gb->sgb_ready_for_pulse = true;
|
||||||
break;
|
break;
|
||||||
@ -73,6 +85,10 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
|||||||
gb->sgb_ready_for_write = true;
|
gb->sgb_ready_for_write = true;
|
||||||
gb->sgb_command_write_index = 0;
|
gb->sgb_command_write_index = 0;
|
||||||
memset(gb->sgb_command, 0, sizeof(gb->sgb_command));
|
memset(gb->sgb_command, 0, sizeof(gb->sgb_command));
|
||||||
|
if (gb->sgb_player_count > 1 && (value & 0x30) != (gb->io_registers[GB_IO_JOYP] & 0x30)) {
|
||||||
|
gb->sgb_current_player++;
|
||||||
|
gb->sgb_current_player &= gb->sgb_player_count - 1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
Loading…
Reference in New Issue
Block a user