mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-12-28 12:11:50 +01:00
ab4638144c
--------------- * added support for Master System compatibility mode (Z80 ports access mode), incl. Mode 5 rendering. * added Mode 4 rendering for both Genesis & Master System modes. * added alternate BG planes rendering functions (should be faster on PPC architectures). [Core/IO] --------------- * added support for Master System compatibility mode (Z80 ports access mode). * added Master System peripherals emulation (Control Pad, Paddle, Sports Pad & Light Phaser). * added XE-1AP (analog controller) emulation. * added Activator emulation. [Core/Extra] --------------- * added support for all known Master System cartridge mappers. * added copy-protection hardware emulation for a few MD unlicensed games: fixes 777 Casino (crash when talking to bunny girls). (NB: most of those unlicensed games seem to have been already patched by ROM dumpers, main purpose is documenting them) * added support for Top Shooter arcade board controller. (A=Shoot, B=Bet, C/RIGHT=Coins, START=Start, hold UP on startup to enter service mode) * improved King of Fighters 98 mapper emulation (registers address decoding is now 100% accurate) * fixed Game Genie when several codes affect same ROM address. * fixed EEPROM types for Brian Lara Cricket & NBA Jam TE (verified on real cartridges) [Core/General] --------------- * added Master System compatibility mode emulation (automatically enabled when loading ROM file with .sms extension). * improved savestate stability & compatibility (support for old 1.4.x savestates is preserved) * various code cleanup & comments. [Gamecube/Wii] --------------- * fixed cheat codes handling when several codes affect same ROM address. * improved input controller detection on menu exit. * improved key remapping dialog box to match emulated device * changed Menu key for Gamecube controller to allow MODE button mapping * fixed DVD not being unmounted on swap (memory leak) [Wii only] --------------- * added USB mouse support for Sega Mouse emulation * compiled with latest libogc: improves USB compatibility & fixes stability issues with Wiimotes.
451 lines
10 KiB
C
451 lines
10 KiB
C
/***************************************************************************************
|
|
* Genesis Plus
|
|
* I/O controller (MD & MS compatibility modes)
|
|
*
|
|
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
|
|
* Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
****************************************************************************************/
|
|
|
|
#include "shared.h"
|
|
#include "gamepad.h"
|
|
#include "lightgun.h"
|
|
#include "mouse.h"
|
|
#include "activator.h"
|
|
#include "xe_a1p.h"
|
|
#include "teamplayer.h"
|
|
#include "paddle.h"
|
|
#include "sportspad.h"
|
|
|
|
uint8 io_reg[0x10];
|
|
|
|
uint8 region_code = REGION_USA;
|
|
|
|
static struct port_t
|
|
{
|
|
void (*data_w)(unsigned char data, unsigned char mask);
|
|
unsigned char (*data_r)(void);
|
|
} port[3];
|
|
|
|
static void dummy_write(unsigned char data, unsigned char mask)
|
|
{
|
|
}
|
|
|
|
static unsigned char dummy_read(void)
|
|
{
|
|
return 0x7F;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* I/O chip functions *
|
|
* *
|
|
*****************************************************************************/
|
|
void io_init(void)
|
|
{
|
|
/* Initialize connected peripherals */
|
|
input_init();
|
|
|
|
/* Initialize IO Ports handlers & connected peripherals */
|
|
switch (input.system[0])
|
|
{
|
|
case SYSTEM_MS_GAMEPAD:
|
|
{
|
|
port[0].data_w = dummy_write;
|
|
port[0].data_r = gamepad_1_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_MD_GAMEPAD:
|
|
{
|
|
port[0].data_w = gamepad_1_write;
|
|
port[0].data_r = gamepad_1_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_MOUSE:
|
|
{
|
|
port[0].data_w = mouse_write;
|
|
port[0].data_r = mouse_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_ACTIVATOR:
|
|
{
|
|
port[0].data_w = activator_1_write;
|
|
port[0].data_r = activator_1_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_XE_A1P:
|
|
{
|
|
port[0].data_w = xe_a1p_write;
|
|
port[0].data_r = xe_a1p_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_WAYPLAY:
|
|
{
|
|
port[0].data_w = wayplay_1_write;
|
|
port[0].data_r = wayplay_1_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_TEAMPLAYER:
|
|
{
|
|
port[0].data_w = teamplayer_1_write;
|
|
port[0].data_r = teamplayer_1_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_LIGHTPHASER:
|
|
{
|
|
port[0].data_w = dummy_write;
|
|
port[0].data_r = phaser_1_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_PADDLE:
|
|
{
|
|
port[0].data_w = paddle_1_write;
|
|
port[0].data_r = paddle_1_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_SPORTSPAD:
|
|
{
|
|
port[0].data_w = sportspad_1_write;
|
|
port[0].data_r = sportspad_1_read;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
port[0].data_w = dummy_write;
|
|
port[0].data_r = dummy_read;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (input.system[1])
|
|
{
|
|
case SYSTEM_MS_GAMEPAD:
|
|
{
|
|
port[1].data_w = dummy_write;
|
|
port[1].data_r = gamepad_2_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_MD_GAMEPAD:
|
|
{
|
|
port[1].data_w = gamepad_2_write;
|
|
port[1].data_r = gamepad_2_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_MOUSE:
|
|
{
|
|
port[1].data_w = mouse_write;
|
|
port[1].data_r = mouse_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_ACTIVATOR:
|
|
{
|
|
port[1].data_w = activator_2_write;
|
|
port[1].data_r = activator_2_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_MENACER:
|
|
{
|
|
port[1].data_w = dummy_write;
|
|
port[1].data_r = menacer_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_JUSTIFIER:
|
|
{
|
|
port[1].data_w = justifier_write;
|
|
port[1].data_r = justifier_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_WAYPLAY:
|
|
{
|
|
port[1].data_w = wayplay_2_write;
|
|
port[1].data_r = wayplay_2_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_TEAMPLAYER:
|
|
{
|
|
port[1].data_w = teamplayer_2_write;
|
|
port[1].data_r = teamplayer_2_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_LIGHTPHASER:
|
|
{
|
|
port[1].data_w = dummy_write;
|
|
port[1].data_r = phaser_2_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_PADDLE:
|
|
{
|
|
port[1].data_w = paddle_2_write;
|
|
port[1].data_r = paddle_2_read;
|
|
break;
|
|
}
|
|
|
|
case SYSTEM_SPORTSPAD:
|
|
{
|
|
port[1].data_w = sportspad_2_write;
|
|
port[1].data_r = sportspad_2_read;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
port[1].data_w = dummy_write;
|
|
port[1].data_r = dummy_read;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* External Port (unconnected) */
|
|
port[2].data_w = dummy_write;
|
|
port[2].data_r = dummy_read;
|
|
}
|
|
|
|
|
|
void io_reset(void)
|
|
{
|
|
/* Reset I/O registers */
|
|
if (system_hw == SYSTEM_PBC)
|
|
{
|
|
/* SMS compatibility mode control register */
|
|
io_reg[0x00] = 0xFF;
|
|
}
|
|
else
|
|
{
|
|
/* Genesis mode registers */
|
|
io_reg[0x00] = region_code | 0x20 | (config.tmss & 1);
|
|
io_reg[0x01] = 0x00;
|
|
io_reg[0x02] = 0x00;
|
|
io_reg[0x03] = 0x00;
|
|
io_reg[0x04] = 0x00;
|
|
io_reg[0x05] = 0x00;
|
|
io_reg[0x06] = 0x00;
|
|
io_reg[0x07] = 0xFF;
|
|
io_reg[0x08] = 0x00;
|
|
io_reg[0x09] = 0x00;
|
|
io_reg[0x0A] = 0xFF;
|
|
io_reg[0x0B] = 0x00;
|
|
io_reg[0x0C] = 0x00;
|
|
io_reg[0x0D] = 0xFB;
|
|
io_reg[0x0E] = 0x00;
|
|
io_reg[0x0F] = 0x00;
|
|
}
|
|
|
|
/* Reset connected peripherals */
|
|
input_reset();
|
|
}
|
|
|
|
void io_68k_write(unsigned int offset, unsigned int data)
|
|
{
|
|
switch (offset)
|
|
{
|
|
case 0x01: /* Port A Data */
|
|
case 0x02: /* Port B Data */
|
|
case 0x03: /* Port C Data */
|
|
{
|
|
io_reg[offset] = data;
|
|
port[offset-1].data_w(data, io_reg[offset + 3]);
|
|
return;
|
|
}
|
|
|
|
case 0x04: /* Port A Ctrl */
|
|
case 0x05: /* Port B Ctrl */
|
|
case 0x06: /* Port C Ctrl */
|
|
{
|
|
if (data != io_reg[offset])
|
|
{
|
|
io_reg[offset] = data;
|
|
port[offset-4].data_w(io_reg[offset-3], data);
|
|
}
|
|
return;
|
|
}
|
|
|
|
case 0x07: /* Port A TxData */
|
|
case 0x0A: /* Port B TxData */
|
|
case 0x0D: /* Port C TxData */
|
|
{
|
|
io_reg[offset] = data;
|
|
return;
|
|
}
|
|
|
|
case 0x09: /* Port A S-Ctrl */
|
|
case 0x0C: /* Port B S-Ctrl */
|
|
case 0x0F: /* Port C S-Ctrl */
|
|
{
|
|
io_reg[offset] = data & 0xF8;
|
|
return;
|
|
}
|
|
|
|
default: /* Read-only ports */
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned int io_68k_read(unsigned int offset)
|
|
{
|
|
switch(offset)
|
|
{
|
|
case 0x01: /* Port A Data */
|
|
case 0x02: /* Port B Data */
|
|
case 0x03: /* Port C Data */
|
|
{
|
|
unsigned int mask = 0x80 | io_reg[offset + 3];
|
|
unsigned int data = port[offset-1].data_r();
|
|
return (io_reg[offset] & mask) | (data & ~mask);
|
|
}
|
|
|
|
default: /* return register value */
|
|
{
|
|
return io_reg[offset];
|
|
}
|
|
}
|
|
}
|
|
|
|
void io_z80_write(unsigned int data)
|
|
{
|
|
/* pins can't be configured as output on japanese models */
|
|
if (region_code & REGION_USA)
|
|
{
|
|
/*
|
|
Bit Function
|
|
--------------
|
|
D7 : Port B TH pin output level (1=high, 0=low)
|
|
D6 : Port B TR pin output level (1=high, 0=low)
|
|
D5 : Port A TH pin output level (1=high, 0=low)
|
|
D4 : Port A TR pin output level (1=high, 0=low)
|
|
D3 : Port B TH pin direction (1=input, 0=output)
|
|
D2 : Port B TR pin direction (1=input, 0=output)
|
|
D1 : Port A TH pin direction (1=input, 0=output)
|
|
D0 : Port A TR pin direction (1=input, 0=output)
|
|
*/
|
|
|
|
/* Send TR/TH state to connected peripherals */
|
|
port[0].data_w((data << 1) & 0x60, (~io_reg[0] << 5) & 0x60);
|
|
port[1].data_w((data >> 1) & 0x60, (~io_reg[0] << 3) & 0x60);
|
|
|
|
|
|
/* Check for TH low-to-high transitions on both ports */
|
|
if ((!(io_reg[0] & 0x80) && (data & 0x80)) ||
|
|
(!(io_reg[0] & 0x20) && (data & 0x20)))
|
|
{
|
|
/* Latch new HVC */
|
|
hvc_latch = hctab[(mcycles_z80 + Z80_CYCLE_OFFSET) % MCYCLES_PER_LINE] | 0x10000;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* outputs return fixed value */
|
|
data &= 0x0F;
|
|
}
|
|
|
|
/* Update control register */
|
|
io_reg[0] = data;
|
|
}
|
|
|
|
unsigned int io_z80_read(unsigned int offset)
|
|
{
|
|
/* Read port A & port B input data */
|
|
unsigned int data = (port[0].data_r()) | (port[1].data_r() << 8);
|
|
|
|
/* Read control register value */
|
|
unsigned int ctrl = io_reg[0];
|
|
|
|
/* I/O ports */
|
|
if (offset)
|
|
{
|
|
/*
|
|
Bit Function
|
|
--------------
|
|
D7 : Port B TH pin input
|
|
D6 : Port A TH pin input
|
|
D5 : Unused (0 on Mega Drive, 1 otherwise)
|
|
D4 : RESET button (always 1 on Mega Drive)
|
|
D3 : Port B TR pin input
|
|
D2 : Port B TL pin input
|
|
D1 : Port B Right pin input
|
|
D0 : Port B Left pin input
|
|
*/
|
|
data = ((data >> 10) & 0x0F) | (data & 0x40) | ((data >> 7) & 0x80) | 0x10;
|
|
|
|
/* Adjust port B TH state if configured as output */
|
|
if (!(ctrl & 0x08))
|
|
{
|
|
data &= ~0x80;
|
|
data |= (ctrl & 0x80);
|
|
}
|
|
|
|
/* Adjust port A TH state if configured as output */
|
|
if (!(ctrl & 0x02))
|
|
{
|
|
data &= ~0x40;
|
|
data |= ((ctrl & 0x20) << 1);
|
|
}
|
|
|
|
/* Adjust port B TR state if configured as output */
|
|
if (!(ctrl & 0x04))
|
|
{
|
|
data &= ~0x08;
|
|
data |= ((ctrl & 0x40) >> 3);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Bit Function
|
|
--------------
|
|
D7 : Port B Down pin input
|
|
D6 : Port B Up pin input
|
|
D5 : Port A TR pin input
|
|
D4 : Port A TL pin input
|
|
D3 : Port A Right pin input
|
|
D2 : Port A Left pin input
|
|
D1 : Port A Down pin input
|
|
D0 : Port A Up pin input
|
|
*/
|
|
data = (data & 0x3F) | ((data >> 2) & 0xC0);
|
|
|
|
/* Adjust port A TR state if configured as output */
|
|
if (!(ctrl & 0x01))
|
|
{
|
|
data &= ~0x20;
|
|
data |= ((ctrl & 0x10) << 1);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|