smsplus-gx/source/sms.c
gameblabla 8a9c66e9bf
Bunch of fixes
One of them is a fix for a really odd bug with the proprietary z80 core :
Pause button would not work.

For some reasons, executing the irq line functions will not work if
put in a function so what i did was a bunch of macros instead.
This now works and reduces the complexity of the code kinda.
(Also inlining)

There's also a fix for the ALSA code. On the bittboy,
it woud sometimes underrun. When it does so, it becomes mute.
Fixed this by handling underruns and removing non blocking.
(Useless anyway here)
2019-02-19 10:38:00 +01:00

558 lines
14 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
*
* Sega Master System console emulation
*
******************************************************************************/
#include "shared.h"
/* SMS context */
sms_t sms;
/* BIOS/CART ROM */
bios_t bios;
slot_t slot;
/* Colecovision support */
t_coleco coleco;
uint8_t dummy_write[0x400];
uint8_t dummy_read[0x400];
static void writemem_mapper_none(uint16_t offset, uint8_t data)
{
cpu_writemap[offset >> 10][offset & 0x03FF] = data;
}
static void writemem_mapper_sega(uint16_t offset, uint8_t data)
{
if(offset >= 0xFFFC)
{
mapper_16k_w(offset & 3, data);
}
cpu_writemap[offset >> 10][offset & 0x03FF] = data;
}
static void writemem_mapper_codies(uint16_t offset, uint8_t data)
{
if (offset == 0x0000)
{
mapper_16k_w(1,data);
return;
}
if (offset == 0x4000)
{
mapper_16k_w(2,data);
return;
}
if (offset == 0x8000)
{
mapper_16k_w(3,data);
return;
}
cpu_writemap[offset >> 10][offset & 0x03FF] = data;
}
static void writemem_mapper_korea_msx(uint16_t offset, uint8_t data)
{
if (offset <= 0x0003)
{
mapper_8k_w(offset,data);
return;
}
cpu_writemap[offset >> 10][offset & 0x03FF] = data;
}
static void writemem_mapper_korea(uint16_t offset, uint8_t data)
{
if (offset == 0xA000)
{
mapper_16k_w(3,data);
return;
}
cpu_writemap[offset >> 10][offset & 0x03FF] = data;
}
void mapper_reset(void)
{
switch(slot.mapper)
{
case MAPPER_NONE:
cpu_writemem16 = writemem_mapper_none;
break;
case MAPPER_CODIES:
cpu_writemem16 = writemem_mapper_codies;
break;
case MAPPER_KOREA:
cpu_writemem16 = writemem_mapper_korea;
break;
case MAPPER_KOREA_MSX:
cpu_writemem16 = writemem_mapper_korea_msx;
break;
default:
cpu_writemem16 = writemem_mapper_sega;
break;
}
}
void sms_init(void)
{
CPUZ80_Init();
/* Default: open bus */
data_bus_pullup = 0x00;
data_bus_pulldown = 0x00;
/* Initialize port handlers */
switch(sms.console)
{
case CONSOLE_COLECO:
cpu_writeport16 = coleco_port_w;
cpu_readport16 = coleco_port_r;
data_bus_pullup = 0xFF;
break;
case CONSOLE_SG1000:
case CONSOLE_SC3000:
case CONSOLE_SF7000:
cpu_writeport16 = tms_port_w;
cpu_readport16 = tms_port_r;
data_bus_pullup = 0xFF;
break;
case CONSOLE_SMS:
cpu_writeport16 = sms_port_w;
cpu_readport16 = sms_port_r;
break;
case CONSOLE_SMS2:
cpu_writeport16 = sms_port_w;
cpu_readport16 = sms_port_r;
data_bus_pullup = 0xFF;
break;
case CONSOLE_GG:
cpu_writeport16 = gg_port_w;
cpu_readport16 = gg_port_r;
data_bus_pullup = 0xFF;
break;
case CONSOLE_GGMS:
cpu_writeport16 = ggms_port_w;
cpu_readport16 = ggms_port_r;
data_bus_pullup = 0xFF;
break;
case CONSOLE_GEN:
case CONSOLE_MD:
cpu_writeport16 = md_port_w;
cpu_readport16 = md_port_r;
break;
case CONSOLE_GENPBC:
case CONSOLE_MDPBC:
cpu_writeport16 = md_port_w;
cpu_readport16 = md_port_r;
data_bus_pullup = 0xFF;
break;
}
}
void sms_shutdown(void)
{
/* Nothing to do */
}
void sms_reset(void)
{
int i;
/* reset Z80 state */
CPUZ80_Reset();
/* clear SMS context */
memset(dummy_write,data_bus_pullup, sizeof(dummy_write));
memset(dummy_read,data_bus_pullup, sizeof(dummy_read));
memset(sms.wram,0, sizeof(sms.wram));
sms.paused = 0x00;
sms.save = 0x00;
sms.fm_detect = 0x00;
sms.ioctrl = 0xFF;
sms.hlatch = 0x00;
sms.memctrl = 0xAB;
/* enable Cartridge ROM by default*/
slot.rom = cart.rom;
slot.pages = cart.pages;
slot.mapper = cart.mapper;
slot.fcr = &cart.fcr[0];
/* reset Memory Mapping */
switch(sms.console)
{
case CONSOLE_COLECO:
{
/* $0000-$1FFF mapped to internal ROM (8K) */
for(i = 0x00; i < 0x08; i++)
{
cpu_readmap[i] = &coleco.rom[i << 10];
cpu_writemap[i] = dummy_write;
}
/* $2000-$5FFF mapped to expansion */
for(i = 0x08; i < 0x18; i++)
{
cpu_readmap[i] = dummy_read;
cpu_writemap[i] = dummy_write;
}
/* $6000-$7FFF mapped to RAM (1K mirrored) */
for(i = 0x18; i < 0x20; i++)
{
cpu_readmap[i] = &sms.wram[0];
cpu_writemap[i] = &sms.wram[0];
}
/* $8000-$FFFF mapped to Cartridge ROM (max. 32K) */
for(i = 0x20; i < 0x40; i++)
{
cpu_readmap[i] = &cart.rom[(i&0x1F) << 10];
cpu_writemap[i] = dummy_write;
}
/* reset I/O */
coleco.keypad[0] = 0xf0;
coleco.keypad[1] = 0xf0;
coleco.pio_mode = 0x00;
break;
}
case CONSOLE_SG1000:
case CONSOLE_SC3000:
case CONSOLE_SF7000:
{
/* $0000-$7FFF mapped to cartridge ROM (max. 32K) */
for(i = 0x00; i < 0x20; i++)
{
cpu_readmap[i] = &cart.rom[i << 10];
cpu_writemap[i] = dummy_write;
}
/* $8000-$BFFF mapped to external RAM (lower 16K) */
for(i = 0x20; i < 0x30; i++)
{
cpu_readmap[i] = &cart.sram[(i & 0x0F) << 10];
cpu_writemap[i] = &cart.sram[(i & 0x0F) << 10];
}
/* $C000-$FFFF mapped to internal RAM (2K) or external RAM (upper 16K) */
for(i = 0x30; i < 0x40; i++)
{
cpu_readmap[i] = &cart.sram[0x4000 + ((i & 0x0F) << 10)];
cpu_writemap[i] = &cart.sram[0x4000 + ((i & 0x0F) << 10)];
}
break;
}
default:
{
/* SMS BIOS support */
if (IS_SMS)
{
if (bios.enabled == 3)
{
/* reset BIOS paging */
bios.fcr[0] = 0;
bios.fcr[1] = 0;
bios.fcr[2] = 1;
bios.fcr[3] = 2;
/* enable BIOS ROM */
slot.rom = bios.rom;
slot.pages = bios.pages;
slot.mapper = MAPPER_SEGA;
slot.fcr = &bios.fcr[0];
sms.memctrl = 0xE0;
}
else
{
/* save Memory Control register value in RAM */
sms.wram[0] = sms.memctrl;
}
}
/* default cartridge ROM mapping at $0000-$BFFF (first 32k mirrored) */
for(i = 0x00; i <= 0x2F; i++)
{
cpu_readmap[i] = &slot.rom[(i & 0x1F) << 10];
cpu_writemap[i] = dummy_write;
}
/* enable internal RAM at $C000-$FFFF (8k mirrored) */
for(i = 0x30; i <= 0x3F; i++)
{
cpu_readmap[i] = &sms.wram[(i & 0x07) << 10];
cpu_writemap[i] = &sms.wram[(i & 0x07) << 10];
}
/* reset cartridge paging registers */
switch(cart.mapper)
{
case MAPPER_NONE:
case MAPPER_SEGA:
cart.fcr[0] = 0;
cart.fcr[1] = 0;
cart.fcr[2] = 1;
cart.fcr[3] = 2;
break;
default:
cart.fcr[0] = 0;
cart.fcr[1] = 0;
cart.fcr[2] = 1;
cart.fcr[3] = 0;
break;
}
/* reset memory map */
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]);
}
break;
}
}
/* reset SLOT mapper */
mapper_reset();
}
void mapper_8k_w(uint16_t address, uint8_t data)
{
int i;
/* cartridge ROM page (8k) index */
uint8 page = data % (slot.pages << 1);
/* Save frame control register data */
slot.fcr[address] = data;
switch (address & 3)
{
case 0: /* cartridge ROM bank (16k) at $8000-$9FFF */
{
for(i = 0x20; i <= 0x27; i++)
{
cpu_readmap[i] = &slot.rom[(page << 13) | ((i & 0x07) << 10)];
}
break;
}
case 1: /* cartridge ROM bank (16k) at $A000-$BFFF */
{
for(i = 0x28; i <= 0x2F; i++)
{
cpu_readmap[i] = &slot.rom[(page << 13) | ((i & 0x07) << 10)];
}
break;
}
case 2: /* cartridge ROM bank (16k) at $4000-$5FFF */
{
for(i = 0x10; i <= 0x17; i++)
{
cpu_readmap[i] = &slot.rom[(page << 13) | ((i & 0x07) << 10)];
}
break;
}
case 3: /* cartridge ROM bank (16k) at $6000-$7FFF */
{
for(i = 0x18; i <= 0x1F; i++)
{
cpu_readmap[i] = &slot.rom[(page << 13) | ((i & 0x07) << 10)];
}
break;
}
}
}
void mapper_16k_w(uint16_t address, uint8_t data)
{
int i;
/* cartridge ROM page (16k) index */
uint8 page = data % slot.pages;
/* page index increment (SEGA mapper) */
if (slot.fcr[0] & 0x03)
{
page = (page + ((4 - (slot.fcr[0] & 0x03)) << 3)) % slot.pages;
}
/* save frame control register data */
slot.fcr[address] = data;
switch (address)
{
case 0: /* control register (SEGA mapper) */
{
if(data & 0x08)
{
/* external RAM (upper or lower 16K) mapped at $8000-$BFFF */
int offset = (data & 0x04) ? 0x4000 : 0x0000;
for(i = 0x20; i <= 0x2F; i++)
{
cpu_readmap[i] = cpu_writemap[i] = &cart.sram[offset + ((i & 0x0F) << 10)];
}
sms.save = 1;
}
else
{
page = slot.fcr[3] % slot.pages;
/* page index increment (SEGA mapper) */
if (slot.fcr[0] & 0x03)
{
page = (page + ((4 - (slot.fcr[0] & 0x03)) << 3)) % slot.pages;
}
/* cartridge ROM mapped at $8000-$BFFF */
for(i = 0x20; i <= 0x2F; i++)
{
cpu_readmap[i] = &slot.rom[(page << 14) | ((i & 0x0F) << 10)];
cpu_writemap[i] = dummy_write;
}
}
if(data & 0x10)
{
/* external RAM (lower 16K) mapped at $C000-$FFFF */
for(i = 0x30; i <= 0x3F; i++)
{
cpu_writemap[i] = cpu_readmap[i] = &cart.sram[(i & 0x0F) << 10];
}
sms.save = 1;
}
else
{
/* internal RAM (8K mirrorred) mapped at $C000-$FFFF */
for(i = 0x30; i <= 0x3F; i++)
{
cpu_writemap[i] = cpu_readmap[i] = &sms.wram[(i & 0x07) << 10];
}
}
break;
}
case 1: /* cartridge ROM bank (16k) at $0000-$3FFF */
{
/* first 1k is not fixed (CODEMASTER mapper) */
if (slot.mapper == MAPPER_CODIES)
{
cpu_readmap[0] = &slot.rom[(page << 14)];
}
for(i = 0x01; i <= 0x0F; i++)
{
cpu_readmap[i] = &slot.rom[(page << 14) | ((i & 0x0F) << 10)];
}
break;
}
case 2: /* cartridge ROM bank (16k) at $4000-$7FFF */
{
for(i = 0x10; i <= 0x1F; i++)
{
cpu_readmap[i] = &slot.rom[(page << 14) | ((i & 0x0F) << 10)];
}
/* Ernie Elf's Golf external RAM switch */
if (slot.mapper == MAPPER_CODIES)
{
if (data & 0x80)
{
/* external RAM (8k) mapped at $A000-$BFFF */
for(i = 0x28; i <= 0x2F; i++)
{
cpu_writemap[i] = cpu_readmap[i] = &cart.sram[(i & 0x0F) << 10];
}
sms.save = 1;
}
else
{
/* cartridge ROM mapped at $A000-$BFFF */
for(i = 0x28; i <= 0x2F; i++)
{
cpu_readmap[i] = &slot.rom[((slot.fcr[3] % slot.pages) << 14) | ((i & 0x0F) << 10)];
cpu_writemap[i] = dummy_write;
}
}
}
break;
}
case 3: /* cartridge ROM bank (16k) at $8000-$BFFF */
{
/* check that external RAM (16k) is not mapped at $8000-$BFFF (SEGA mapper) */
if ((slot.fcr[0] & 0x08)) break;
/* first 8k */
for(i = 0x20; i <= 0x27; i++)
{
cpu_readmap[i] = &slot.rom[(page << 14) | ((i & 0x0F) << 10)];
}
/* check that external RAM (8k) is not mapped at $A000-$BFFF (CODEMASTER mapper) */
if ((slot.mapper == MAPPER_CODIES) && (slot.fcr[2] & 0x80)) break;
/* last 8k */
for(i = 0x28; i <= 0x2F; i++)
{
cpu_readmap[i] = &slot.rom[(page << 14) | ((i & 0x0F) << 10)];
}
break;
}
}
}
int32_t sms_irq_callback(int32_t param)
{
return 0xFF;
}