diff --git a/Core/gb.c b/Core/gb.c index 00c4e7b..401eba8 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -640,6 +640,8 @@ void GB_reset(GB_gameboy_t *gb) gb->accessed_oam_row = -1; + gb->sgb_player_count = 1; + /* Todo: Ugly, fixme, see comment in the timer state machine */ gb->div_state = 3; diff --git a/Core/gb.h b/Core/gb.h index a422d5e..65b377a 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -474,6 +474,8 @@ struct GB_gameboy_internal_s { bool sgb_ready_for_pulse; bool sgb_ready_for_write; 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 */ @@ -500,7 +502,7 @@ struct GB_gameboy_internal_s { uint32_t background_palettes_rgb[0x20]; uint32_t sprite_palettes_rgb[0x20]; GB_color_correction_mode_t color_correction_mode; - bool keys[GB_KEY_MAX]; + bool keys[4][GB_KEY_MAX]; /* Timing */ uint64_t last_sync; diff --git a/Core/joypad.c b/Core/joypad.c index edee8c3..b4e0f76 100644 --- a/Core/joypad.c +++ b/Core/joypad.c @@ -13,14 +13,19 @@ void GB_update_joyp(GB_gameboy_t *gb) gb->io_registers[GB_IO_JOYP] &= 0xF0; switch (key_selection) { case 3: - /* Nothing is wired, all up */ - gb->io_registers[GB_IO_JOYP] |= 0x0F; + if (gb->sgb_player_count > 1) { + 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; case 2: /* Direction keys */ 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. */ if (!(gb->io_registers[GB_IO_JOYP] & 1)) { @@ -34,13 +39,13 @@ void GB_update_joyp(GB_gameboy_t *gb) case 1: /* Other keys */ 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; case 0: 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; @@ -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) { 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; } diff --git a/Core/joypad.h b/Core/joypad.h index 83d3734..768d685 100644 --- a/Core/joypad.h +++ b/Core/joypad.h @@ -16,6 +16,7 @@ typedef enum { } GB_key_t; 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 void GB_update_joyp(GB_gameboy_t *gb); diff --git a/Core/memory.c b/Core/memory.c index cf5a0c0..3a646ae 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -730,10 +730,10 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) return; case GB_IO_JOYP: + GB_sgb_write(gb, value); gb->io_registers[GB_IO_JOYP] &= 0x0F; gb->io_registers[GB_IO_JOYP] |= value & 0xF0; GB_update_joyp(gb); - GB_sgb_write(gb, value); return; case GB_IO_BIOS: diff --git a/Core/sgb.c b/Core/sgb.c index 71fccfe..a755016 100644 --- a/Core/sgb.c +++ b/Core/sgb.c @@ -1,5 +1,9 @@ #include "sgb.h" +enum { + MLT_REQ = 0x11, +}; + static void command_ready(GB_gameboy_t *gb) { /* 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; } } + + 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) { if (!GB_is_sgb(gb)) return; if (gb->sgb_disable_commands) return; - switch ((value >> 4) & 3 ) { + + switch ((value >> 4) & 3) { case 3: gb->sgb_ready_for_pulse = true; break; @@ -73,6 +85,10 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value) gb->sgb_ready_for_write = true; gb->sgb_command_write_index = 0; 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; default: