mirror of
https://github.com/libretro/smsplus-gx.git
synced 2024-12-04 23:56:29 +00:00
431 lines
8.9 KiB
C
431 lines
8.9 KiB
C
/******************************************************************************
|
|
* Sega Master System / GameGear Emulator
|
|
* Copyright (C) 1998-2007 Charles MacDonald
|
|
*
|
|
* additionnal code by Eke-Eke (SMS Plus GX)
|
|
*
|
|
* 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
|
|
*
|
|
* Z80 memory handlers
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include "shared.h"
|
|
|
|
/* Pull-up resistors on data bus */
|
|
uint8_t data_bus_pullup = 0x00;
|
|
uint8_t data_bus_pulldown = 0x00;
|
|
|
|
/* Read unmapped memory */
|
|
uint8_t z80_read_unmapped(void)
|
|
{
|
|
int32_t pc = Z80.pc.w.l;
|
|
uint8_t data;
|
|
pc = (pc - 1) & 0xFFFF;
|
|
data = cpu_readmap[pc >> 13][pc & 0x03FF];
|
|
return ((data | data_bus_pullup) & ~data_bus_pulldown);
|
|
}
|
|
|
|
/* Port $3E (Memory Control Port) */
|
|
void memctrl_w(uint8_t data)
|
|
{
|
|
/* detect CARTRIDGE/BIOS enabled/disabled */
|
|
if (IS_SMS)
|
|
{
|
|
/* autodetect loaded BIOS ROM */
|
|
if (!(bios.enabled & 2) && ((data & 0xE8) == 0xE8))
|
|
{
|
|
bios.enabled = option.use_bios | 2;
|
|
memcpy(bios.rom, cart.rom, cart.size);
|
|
memcpy(bios.fcr, cart.fcr, 4);
|
|
bios.pages = cart.pages;
|
|
cart.loaded = 0;
|
|
}
|
|
|
|
/* disables CART & BIOS by default */
|
|
slot.rom = NULL;
|
|
slot.mapper = MAPPER_NONE;
|
|
|
|
switch (data & 0x48)
|
|
{
|
|
case 0x00: /* BIOS & CART enabled */
|
|
case 0x08: /* BIOS disabled, CART enabled */
|
|
if (cart.loaded)
|
|
{
|
|
slot.rom = cart.rom;
|
|
slot.pages = cart.pages;
|
|
slot.mapper = cart.mapper;
|
|
slot.fcr = &cart.fcr[0];
|
|
}
|
|
break;
|
|
case 0x40: /* BIOS enabled, CART disabled */
|
|
slot.rom = bios.rom;
|
|
slot.pages = bios.pages;
|
|
slot.mapper = MAPPER_SEGA;
|
|
slot.fcr = &bios.fcr[0];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
mapper_reset();
|
|
|
|
/* reset SLOT mapping */
|
|
if (slot.rom)
|
|
{
|
|
cpu_readmap[0] = &slot.rom[0];
|
|
if (slot.mapper != MAPPER_KOREA_MSX)
|
|
{
|
|
mapper_16k_w(0,slot.fcr[0]);
|
|
mapper_16k_w(1,slot.fcr[1]);
|
|
mapper_16k_w(2,slot.fcr[2]);
|
|
mapper_16k_w(3,slot.fcr[3]);
|
|
}
|
|
else
|
|
{
|
|
mapper_8k_w(0,slot.fcr[0]);
|
|
mapper_8k_w(1,slot.fcr[1]);
|
|
mapper_8k_w(2,slot.fcr[2]);
|
|
mapper_8k_w(3,slot.fcr[3]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint8_t i;
|
|
for(i = 0x00; i <= 0x2F; i++)
|
|
{
|
|
cpu_readmap[i] = dummy_read;
|
|
cpu_writemap[i] = dummy_write;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* update register value */
|
|
sms.memctrl = data;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* Sega Master System port handlers */
|
|
/*--------------------------------------------------------------------------*/
|
|
void sms_port_w(uint16_t port, uint8_t data)
|
|
{
|
|
port &= 0xFF;
|
|
/* access FM unit */
|
|
if(port >= 0xF0)
|
|
{
|
|
switch(port)
|
|
{
|
|
case 0xF0:
|
|
fmunit_write(0, data);
|
|
return;
|
|
case 0xF1:
|
|
fmunit_write(1, data);
|
|
return;
|
|
case 0xF2:
|
|
fmunit_detect_w(data);
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch(port & 0xC1)
|
|
{
|
|
case 0x00:
|
|
memctrl_w(data);
|
|
return;
|
|
case 0x01:
|
|
pio_ctrl_w(data);
|
|
return;
|
|
case 0x40:
|
|
case 0x41:
|
|
psg_write(data);
|
|
return;
|
|
case 0x80:
|
|
case 0x81:
|
|
vdp_write(port, data);
|
|
return;
|
|
case 0xC0:
|
|
case 0xC1:
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint8_t sms_port_r(uint16_t port)
|
|
{
|
|
port &= 0xFF;
|
|
/* FM unit */
|
|
if (port == 0xF2)
|
|
return fmunit_detect_r() & pio_port_r(port);
|
|
|
|
switch(port & 0xC0)
|
|
{
|
|
case 0x00:
|
|
return z80_read_unmapped();
|
|
case 0x40:
|
|
return vdp_counter_r(port);
|
|
case 0x80:
|
|
return vdp_read(port);
|
|
case 0xC0:
|
|
return pio_port_r(port);
|
|
}
|
|
/* Just to please the compiler */
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* Game Gear port handlers */
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
void gg_port_w(uint16_t port, uint8_t data)
|
|
{
|
|
port &= 0xFF;
|
|
if(port <= 0x20) {
|
|
sio_w(port, data);
|
|
return;
|
|
}
|
|
switch(port & 0xC1)
|
|
{
|
|
case 0x00:
|
|
memctrl_w(data);
|
|
return;
|
|
case 0x01:
|
|
pio_ctrl_w(data);
|
|
return;
|
|
case 0x40:
|
|
case 0x41:
|
|
psg_write(data);
|
|
return;
|
|
case 0x80:
|
|
case 0x81:
|
|
gg_vdp_write(port, data);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
uint8_t gg_port_r(uint16_t port)
|
|
{
|
|
port &= 0xFF;
|
|
if(port <= 0x20)
|
|
return sio_r(port);
|
|
|
|
switch(port & 0xC0)
|
|
{
|
|
case 0x00:
|
|
return z80_read_unmapped();
|
|
case 0x40:
|
|
return vdp_counter_r(port);
|
|
case 0x80:
|
|
return vdp_read(port);
|
|
|
|
case 0xC0:
|
|
switch(port)
|
|
{
|
|
case 0xC0:
|
|
case 0xC1:
|
|
case 0xDC:
|
|
case 0xDD:
|
|
return pio_port_r(port);
|
|
}
|
|
return z80_read_unmapped();
|
|
}
|
|
|
|
/* Just to please the compiler */
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* Game Gear (MS) port handlers */
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
void ggms_port_w(uint16_t port, uint8_t data)
|
|
{
|
|
port &= 0xFF;
|
|
if(port <= 0x20)
|
|
{
|
|
sio_w(port, data);
|
|
return;
|
|
}
|
|
|
|
switch(port & 0xC1)
|
|
{
|
|
case 0x00:
|
|
memctrl_w(data);
|
|
return;
|
|
case 0x01:
|
|
pio_ctrl_w(data);
|
|
return;
|
|
case 0x40:
|
|
case 0x41:
|
|
psg_write(data);
|
|
return;
|
|
case 0x80:
|
|
case 0x81:
|
|
vdp_write(port, data); /* fixed */
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint8_t ggms_port_r(uint16_t port)
|
|
{
|
|
port &= 0xFF;
|
|
if(port <= 0x20)
|
|
return sio_r(port);
|
|
switch(port & 0xC0)
|
|
{
|
|
case 0x00:
|
|
return z80_read_unmapped();
|
|
case 0x40:
|
|
return vdp_counter_r(port);
|
|
case 0x80:
|
|
return vdp_read(port);
|
|
case 0xC0:
|
|
switch(port)
|
|
{
|
|
case 0xC0:
|
|
case 0xC1:
|
|
case 0xDC:
|
|
case 0xDD:
|
|
return pio_port_r(port);
|
|
}
|
|
return z80_read_unmapped();
|
|
}
|
|
/* Just to please the compiler */
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* MegaDrive / Genesis port handlers */
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
void md_port_w(uint16_t port, uint8_t data)
|
|
{
|
|
switch(port & 0xC1)
|
|
{
|
|
case 0x00:
|
|
/* No memory control register */
|
|
return;
|
|
case 0x01:
|
|
pio_ctrl_w(data);
|
|
return;
|
|
case 0x40:
|
|
case 0x41:
|
|
psg_write(data);
|
|
return;
|
|
case 0x80:
|
|
case 0x81:
|
|
md_vdp_write(port, data);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
uint8_t md_port_r(uint16_t port)
|
|
{
|
|
switch(port & 0xC0)
|
|
{
|
|
case 0x00:
|
|
return z80_read_unmapped();
|
|
case 0x40:
|
|
return vdp_counter_r(port);
|
|
case 0x80:
|
|
return vdp_read(port);
|
|
case 0xC0:
|
|
switch(port)
|
|
{
|
|
case 0xC0:
|
|
case 0xC1:
|
|
case 0xDC:
|
|
case 0xDD:
|
|
return pio_port_r(port);
|
|
}
|
|
return z80_read_unmapped();
|
|
}
|
|
/* Just to please the compiler */
|
|
return 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* SG1000,SC3000,SF7000 port handlers */
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
void tms_port_w(uint16_t port, uint8_t data)
|
|
{
|
|
switch(port & 0xC0)
|
|
{
|
|
case 0x40:
|
|
psg_write(data);
|
|
return;
|
|
case 0x80:
|
|
tms_write(port, data);
|
|
return;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint8_t tms_port_r(uint16_t port)
|
|
{
|
|
switch(port & 0xC0)
|
|
{
|
|
case 0x80:
|
|
return vdp_read(port);
|
|
case 0xC0:
|
|
return pio_port_r(port);
|
|
default:
|
|
return 0xff;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
/* Colecovision port handlers */
|
|
/*--------------------------------------------------------------------------*/
|
|
void coleco_port_w(uint16_t port, uint8_t data)
|
|
{
|
|
/* A7 is used as enable input */
|
|
/* A6 & A5 are used to decode the address */
|
|
switch(port & 0xE0)
|
|
{
|
|
case 0x80:
|
|
coleco.pio_mode = 0;
|
|
return;
|
|
case 0xa0:
|
|
tms_write(port,data);
|
|
return;
|
|
case 0xc0:
|
|
coleco.pio_mode = 1;
|
|
return;
|
|
case 0xe0:
|
|
psg_write(data);
|
|
return;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint8_t coleco_port_r(uint16_t port)
|
|
{
|
|
/* A7 is used as enable input */
|
|
/* A6 & A5 are used to decode the address */
|
|
switch(port & 0xE0)
|
|
{
|
|
case 0xa0:
|
|
return vdp_read(port);
|
|
case 0xe0:
|
|
return coleco_pio_r((port>>1)&1);
|
|
default:
|
|
return 0xff;
|
|
}
|
|
}
|