/***************************************************************************************1* Genesis Plus2* I/O controller (Genesis & Master System modes)3*4* Support for Master System (315-5216, 315-5237 & 315-5297), Game Gear & Mega Drive I/O chips5*6* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)7* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)8*9* Redistribution and use of this code or any derivative works are permitted10* provided that the following conditions are met:11*12* - Redistributions may not be sold, nor may they be used in a commercial13* product or activity.14*15* - Redistributions that are modified from the original source must include the16* complete source code, including the source code for all components used by a17* binary built from the modified sources. However, as a special exception, the18* source code distributed need not include anything that is normally distributed19* (in either source or binary form) with the major components (compiler, kernel,20* and so on) of the operating system on which the executable runs, unless that21* component itself accompanies the executable.22*23* - Redistributions must reproduce the above copyright notice, this list of24* conditions and the following disclaimer in the documentation and/or other25* materials provided with the distribution.26*27* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"28* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE29* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE30* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE31* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR32* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF33* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS34* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN35* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)36* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE37* POSSIBILITY OF SUCH DAMAGE.38*39****************************************************************************************/4041#include "shared.h"42#include "gamepad.h"43#include "lightgun.h"44#include "mouse.h"45#include "activator.h"46#include "xe_a1p.h"47#include "teamplayer.h"48#include "paddle.h"49#include "sportspad.h"5051uint8 io_reg[0x10];5253uint8 region_code = REGION_USA;5455static struct port_t56{57void (*data_w)(unsigned char data, unsigned char mask);58unsigned char (*data_r)(void);59} port[3];6061static void dummy_write(unsigned char data, unsigned char mask)62{63}6465static unsigned char dummy_read(void)66{67return 0x7F;68}6970/*****************************************************************************71* I/O chip initialization *72* *73*****************************************************************************/74void io_init(void)75{76/* Initialize connected peripherals */77input_init();7879/* Initialize IO Ports handlers & connected peripherals */80switch (input.system[0])81{82case SYSTEM_MS_GAMEPAD:83{84port[0].data_w = dummy_write;85port[0].data_r = gamepad_1_read;86break;87}8889case SYSTEM_MD_GAMEPAD:90{91port[0].data_w = gamepad_1_write;92port[0].data_r = gamepad_1_read;93break;94}9596case SYSTEM_MOUSE:97{98port[0].data_w = mouse_write;99port[0].data_r = mouse_read;100break;101}102103case SYSTEM_ACTIVATOR:104{105port[0].data_w = activator_1_write;106port[0].data_r = activator_1_read;107break;108}109110case SYSTEM_XE_A1P:111{112port[0].data_w = xe_a1p_1_write;113port[0].data_r = xe_a1p_1_read;114break;115}116117case SYSTEM_WAYPLAY:118{119port[0].data_w = wayplay_1_write;120port[0].data_r = wayplay_1_read;121break;122}123124case SYSTEM_TEAMPLAYER:125{126port[0].data_w = teamplayer_1_write;127port[0].data_r = teamplayer_1_read;128break;129}130131case SYSTEM_LIGHTPHASER:132{133port[0].data_w = dummy_write;134port[0].data_r = phaser_1_read;135break;136}137138case SYSTEM_PADDLE:139{140port[0].data_w = paddle_1_write;141port[0].data_r = paddle_1_read;142break;143}144145case SYSTEM_SPORTSPAD:146{147port[0].data_w = sportspad_1_write;148port[0].data_r = sportspad_1_read;149break;150}151152default:153{154port[0].data_w = dummy_write;155port[0].data_r = dummy_read;156break;157}158}159160switch (input.system[1])161{162case SYSTEM_MS_GAMEPAD:163{164port[1].data_w = dummy_write;165port[1].data_r = gamepad_2_read;166break;167}168169case SYSTEM_MD_GAMEPAD:170{171port[1].data_w = gamepad_2_write;172port[1].data_r = gamepad_2_read;173break;174}175176case SYSTEM_MOUSE:177{178port[1].data_w = mouse_write;179port[1].data_r = mouse_read;180break;181}182183case SYSTEM_XE_A1P:184{185port[1].data_w = xe_a1p_2_write;186port[1].data_r = xe_a1p_2_read;187break;188}189190case SYSTEM_ACTIVATOR:191{192port[1].data_w = activator_2_write;193port[1].data_r = activator_2_read;194break;195}196197case SYSTEM_MENACER:198{199port[1].data_w = dummy_write;200port[1].data_r = menacer_read;201break;202}203204case SYSTEM_JUSTIFIER:205{206port[1].data_w = justifier_write;207port[1].data_r = justifier_read;208break;209}210211case SYSTEM_WAYPLAY:212{213port[1].data_w = wayplay_2_write;214port[1].data_r = wayplay_2_read;215break;216}217218case SYSTEM_TEAMPLAYER:219{220port[1].data_w = teamplayer_2_write;221port[1].data_r = teamplayer_2_read;222break;223}224225case SYSTEM_LIGHTPHASER:226{227port[1].data_w = dummy_write;228port[1].data_r = phaser_2_read;229break;230}231232case SYSTEM_PADDLE:233{234port[1].data_w = paddle_2_write;235port[1].data_r = paddle_2_read;236break;237}238239case SYSTEM_SPORTSPAD:240{241port[1].data_w = sportspad_2_write;242port[1].data_r = sportspad_2_read;243break;244}245246default:247{248port[1].data_w = dummy_write;249port[1].data_r = dummy_read;250break;251}252}253254/* External Port (unconnected) */255port[2].data_w = dummy_write;256port[2].data_r = dummy_read;257}258259260void io_reset(void)261{262/* Reset I/O registers */263if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)264{265io_reg[0x00] = region_code | (config.bios & 1);266io_reg[0x01] = 0x00;267io_reg[0x02] = 0x00;268io_reg[0x03] = 0x00;269io_reg[0x04] = 0x00;270io_reg[0x05] = 0x00;271io_reg[0x06] = 0x00;272io_reg[0x07] = 0xFF;273io_reg[0x08] = 0x00;274io_reg[0x09] = 0x00;275io_reg[0x0A] = 0xFF;276io_reg[0x0B] = 0x00;277io_reg[0x0C] = 0x00;278io_reg[0x0D] = 0xFB;279io_reg[0x0E] = 0x00;280io_reg[0x0F] = 0x00;281282/* CD unit detection */283if (system_hw != SYSTEM_MCD)284{285io_reg[0x00] |= 0x20;286}287}288else289{290/* Game Gear specific registers */291io_reg[0x00] = 0x80 | (region_code >> 1);292io_reg[0x01] = 0x00;293io_reg[0x02] = 0xFF;294io_reg[0x03] = 0x00;295io_reg[0x04] = 0xFF;296io_reg[0x05] = 0x00;297io_reg[0x06] = 0xFF;298299/* initial !RESET input */300io_reg[0x0D] = IO_RESET_HI;301302/* default !CONT input */303if (system_hw != SYSTEM_PBC)304{305io_reg[0x0D] |= IO_CONT1_HI;306}307308/* Control registers */309io_reg[0x0E] = 0x00;310io_reg[0x0F] = 0xFF;311}312313/* Reset connected peripherals */314input_reset();315}316317318/*****************************************************************************319* I/O ports access from 68k (Genesis mode) *320* *321*****************************************************************************/322323void io_68k_write(unsigned int offset, unsigned int data)324{325switch (offset)326{327case 0x01: /* Port A Data */328case 0x02: /* Port B Data */329case 0x03: /* Port C Data */330{331io_reg[offset] = data;332port[offset-1].data_w(data, io_reg[offset + 3]);333return;334}335336case 0x04: /* Port A Ctrl */337case 0x05: /* Port B Ctrl */338case 0x06: /* Port C Ctrl */339{340if (data != io_reg[offset])341{342io_reg[offset] = data;343port[offset-4].data_w(io_reg[offset-3], data);344}345return;346}347348case 0x07: /* Port A TxData */349case 0x0A: /* Port B TxData */350case 0x0D: /* Port C TxData */351{352io_reg[offset] = data;353return;354}355356case 0x09: /* Port A S-Ctrl */357case 0x0C: /* Port B S-Ctrl */358case 0x0F: /* Port C S-Ctrl */359{360io_reg[offset] = data & 0xF8;361return;362}363364default: /* Read-only ports */365{366return;367}368}369}370371unsigned int io_68k_read(unsigned int offset)372{373switch(offset)374{375case 0x01: /* Port A Data */376case 0x02: /* Port B Data */377case 0x03: /* Port C Data */378{379unsigned int mask, data;380real_input_callback();381mask = 0x80 | io_reg[offset + 3];382data = port[offset-1].data_r();383return (io_reg[offset] & mask) | (data & ~mask);384}385386default: /* return register value */387{388return io_reg[offset];389}390}391}392393394/*****************************************************************************395* I/O ports access from Z80 *396* *397*****************************************************************************/398399void io_z80_write(unsigned int offset, unsigned int data, unsigned int cycles)400{401if (offset)402{403/* I/O Control register */404if (region_code & REGION_USA)405{406/*407Bit Function408--------------409D7 : Port B TH pin output level (1=high, 0=low)410D6 : Port B TR pin output level (1=high, 0=low)411D5 : Port A TH pin output level (1=high, 0=low)412D4 : Port A TR pin output level (1=high, 0=low)413D3 : Port B TH pin direction (1=input, 0=output)414D2 : Port B TR pin direction (1=input, 0=output)415D1 : Port A TH pin direction (1=input, 0=output)416D0 : Port A TR pin direction (1=input, 0=output)417*/418419/* Send TR/TH state to connected peripherals */420port[0].data_w((data << 1) & 0x60, (~io_reg[0x0F] << 5) & 0x60);421port[1].data_w((data >> 1) & 0x60, (~io_reg[0x0F] << 3) & 0x60);422423424/* Check for TH low-to-high transitions on both ports */425if ((!(io_reg[0x0F] & 0x80) && (data & 0x80)) ||426(!(io_reg[0x0F] & 0x20) && (data & 0x20)))427{428/* Latch new HVC */429hvc_latch = hctab[cycles % MCYCLES_PER_LINE] | 0x10000;430}431432/* Update I/O Control register */433io_reg[0x0F] = data;434}435else436{437/* TH output is fixed to 0 & TR is always an input on japanese hardware */438io_reg[0x0F] = (data | 0x05) & 0x5F;439440/* Port $DD bits D4-D5 return D0-D2 (cf. http://www2.odn.ne.jp/~haf09260/Sms/EnrSms.htm) */441io_reg[0x0D] = ((data & 0x01) << 4) | ((data & 0x04) << 3);442}443}444else445{446/* Update Memory Control register */447io_reg[0x0E] = data;448449/* Switch cartridge & BIOS ROM */450sms_cart_switch(~data);451}452}453454unsigned int io_z80_read(unsigned int offset)455{456unsigned int data, ctrl;457real_input_callback();458/* Read port A & port B input data */459data = (port[0].data_r()) | (port[1].data_r() << 8);460461/* I/O control register value */462ctrl = io_reg[0x0F];463464/* I/O ports */465if (offset)466{467/*468Bit Function469--------------470D7 : Port B TH pin input471D6 : Port A TH pin input472D5 : CONT input (0 on Mega Drive hardware, 1 otherwise)473D4 : RESET button (1: default, 0: pressed, only on Master System hardware)474D3 : Port B TR pin input475D2 : Port B TL pin input476D1 : Port B Right pin input477D0 : Port B Left pin input478*/479data = ((data >> 10) & 0x0F) | (data & 0x40) | ((data >> 7) & 0x80) | io_reg[0x0D];480481/* clear !RESET input */482io_reg[0x0D] |= IO_RESET_HI;483484/* Adjust port B TH state if configured as output */485if (!(ctrl & 0x08))486{487data &= ~0x80;488data |= (ctrl & 0x80);489}490491/* Adjust port A TH state if configured as output */492if (!(ctrl & 0x02))493{494data &= ~0x40;495data |= ((ctrl & 0x20) << 1);496}497498/* Adjust port B TR state if configured as output */499if (!(ctrl & 0x04))500{501data &= ~0x08;502data |= ((ctrl & 0x40) >> 3);503}504}505else506{507/*508Bit Function509--------------510D7 : Port B Down pin input511D6 : Port B Up pin input512D5 : Port A TR pin input513D4 : Port A TL pin input514D3 : Port A Right pin input515D2 : Port A Left pin input516D1 : Port A Down pin input517D0 : Port A Up pin input518*/519data = (data & 0x3F) | ((data >> 2) & 0xC0);520521/* Adjust port A TR state if configured as output */522if (!(ctrl & 0x01))523{524data &= ~0x20;525data |= ((ctrl & 0x10) << 1);526}527}528529return data;530}531532533/*****************************************************************************534* Game Gear communication ports access *535* *536*****************************************************************************/537538void io_gg_write(unsigned int offset, unsigned int data)539{540switch (offset)541{542case 1: /* Parallel data register */543io_reg[1] = data;544return;545546case 2: /* Data direction register and NMI enable */547io_reg[2] = data;548return;549550case 3: /* Transmit data buffer */551io_reg[3] = data;552return;553554case 5: /* Serial control (bits 0-2 are read-only) */555io_reg[5] = data & 0xF8;556return;557558case 6: /* PSG Stereo output control */559io_reg[6] = data;560SN76489_Config(Z80.cycles, config.psg_preamp, config.psgBoostNoise, data);561return;562563default: /* Read-only */564return;565}566}567568unsigned int io_gg_read(unsigned int offset)569{570switch (offset)571{572case 0: /* Mode Register */573return (io_reg[0] & ~(input.pad[0] & INPUT_START));574575case 1: /* Parallel data register (not connected) */576return ((io_reg[1] & ~(io_reg[2] & 0x7F)) | (io_reg[2] & 0x7F));577578case 2: /* Data direction register and NMI enable */579return io_reg[2];580581case 3: /* Transmit data buffer */582return io_reg[3];583584case 4: /* Receive data buffer */585return io_reg[4];586587case 5: /* Serial control */588return io_reg[5];589590default: /* Write-Only */591return 0xFF;592}593}594595596597