[Core/SCD]

---------------
* added support for CUE files
* added CD-DA tracks emulation (needs CUE+BIN or ISO+WAV images)
* added CD fader emulation
* added CDD "Fast FW" & "Fast RW" commands emulation
* improved CDD TOC emulation (random freezes in Sonic CD, Switch/Panic, Final Fight CD and probably many others)
* improved PCM chip synchronization with SUB-CPU (missing speeches in Willy Beamish)
* fixed PCM chip emulation (random hangs in Snatcher, missing sound effects in Switch/Panic, Final Fight CD, Wonderdog...)
* fixed Word-RAM memory mode on soft-reset (missing logo gfx effects)
* fixed SUB-CPU access to unused areas when using PC-relative instructions (Final Fight CD first boss random crash)
* fixed CPU idle loop detection on memory mode register access (Pugsy CD first boss slowdown)
* fixed Mode 1 emulation (cartridge boot mode)

[Core/Sound]
---------------
* replaced FIR resampler by Blip Buffer for FM resampling
* modified SN76489 core for use of Blip Buffer
* improved PSG & FM chips synchronization using Blip Buffer
* added Game Gear PSG stereo support
* fixed SG-1000 specific PSG noise
* fixed YM2612 LFO AM waveform (California Games surfing event)
* fixed YM2612 phase precision
* minor optimizations to YM2612 core

[Core/Game Gear]
---------------
* added support for CJ Elephant Fugitive (recently released by SMS Power)
* added Game Gear extended screen option

[Core/Genesis]
---------------
* added support for a few recently dumped (but unreleased) games

[Core/General]
---------------
* improved ROM & CD image file loading
* various code cleanup

[Gamecube/Wii]
---------------
* added automatic disc swap feature
* removed automatic frameskipping (no use)
* improved general audio/video sync
* various code cleanup & bugfixes
This commit is contained in:
EkeEke 2012-10-13 19:01:31 +02:00
parent c8d4bb4f91
commit f2a7b4cb8a
74 changed files with 5270 additions and 4003 deletions

View File

@ -114,21 +114,21 @@ LIBRETRO_SRC := $(GENPLUS_SRC_DIR)/genesis.c \
$(GENPLUS_SRC_DIR)/cart_hw/areplay.c \
$(GENPLUS_SRC_DIR)/cart_hw/md_cart.c \
$(GENPLUS_SRC_DIR)/cart_hw/sms_cart.c \
$(GENPLUS_SRC_DIR)/cart_hw/gg_eeprom.c \
$(GENPLUS_SRC_DIR)/cart_hw/md_eeprom.c \
$(GENPLUS_SRC_DIR)/cart_hw/eeprom_93c.c \
$(GENPLUS_SRC_DIR)/cart_hw/eeprom_i2c.c \
$(GENPLUS_SRC_DIR)/cart_hw/eeprom_spi.c \
$(GENPLUS_SRC_DIR)/cart_hw/ggenie.c \
$(GENPLUS_SRC_DIR)/cart_hw/sram.c \
$(GENPLUS_SRC_DIR)/cart_hw/svp/ssp16.c \
$(GENPLUS_SRC_DIR)/cart_hw/svp/svp.c \
$(GENPLUS_SRC_DIR)/ntsc/md_ntsc.c \
$(GENPLUS_SRC_DIR)/ntsc/sms_ntsc.c \
$(GENPLUS_SRC_DIR)/sound/Fir_Resampler.c \
$(GENPLUS_SRC_DIR)/sound/eq.c \
= $(GENPLUS_SRC_DIR)/sound/eq.c \
$(GENPLUS_SRC_DIR)/sound/sound.c \
$(GENPLUS_SRC_DIR)/sound/ym2612.c \
$(GENPLUS_SRC_DIR)/sound/ym2413.c \
$(GENPLUS_SRC_DIR)/sound/sn76489.c \
$(GENPLUS_SRC_DIR)/sound/blip.c \
$(GENPLUS_SRC_DIR)/sound/blip_buf.c \
$(GENPLUS_SRC_DIR)/z80/z80.c \
$(GENPLUS_SRC_DIR)/m68k/m68kcpu.c \
$(GENPLUS_SRC_DIR)/m68k/s68kcpu.c \

View File

@ -27,7 +27,7 @@ CFLAGS = `sdl-config --cflags` -march=i686 -O6 -fomit-frame-pointer -Wall -Wn
#-g -ggdb -pg
#-fomit-frame-pointer
LDFLAGS =
DEFINES = -DLSB_FIRST -DUSE_16BPP_RENDERING -DLOGERROR -DLOG_CDC -DLOG_CDD -DLOG_SCD -DLOGVDP -DLOG_PCM
DEFINES = -DLSB_FIRST -DUSE_16BPP_RENDERING
INCLUDES = -I. -I.. -I../z80 -I../m68k -I../sound -I../input_hw -I../cart_hw -I../cd_hw -I../cart_hw/svp -I../ntsc
LIBS = `sdl-config --libs` -lz -lm
@ -61,22 +61,22 @@ OBJECTS += obj/input.o \
OBJECTS += obj/sound.o \
obj/sn76489.o \
obj/ym2413.o \
obj/ym2413.o \
obj/ym2612.o
OBJECTS += obj/Fir_Resampler.o
OBJECTS += obj/blip.o
OBJECTS += obj/blip_buf.o
OBJECTS += obj/eq.o \
OBJECTS += obj/sram.o \
obj/svp.o \
obj/ssp16.o \
obj/ggenie.o \
obj/areplay.o \
obj/gg_eeprom.o \
obj/md_eeprom.o \
obj/md_cart.o \
OBJECTS += obj/sram.o \
obj/svp.o \
obj/ssp16.o \
obj/ggenie.o \
obj/areplay.o \
obj/eeprom_93c.o \
obj/eeprom_i2c.o \
obj/eeprom_spi.o \
obj/md_cart.o \
obj/sms_cart.o
OBJECTS += obj/scd.o \

View File

@ -65,9 +65,9 @@ void areplay_init(void)
memset(&action_replay,0,sizeof(action_replay));
/* store Action replay ROM (max. 128k) & RAM (64k) above cartridge ROM + SRAM area */
if (cart.romsize > 0x600000) return;
action_replay.rom = cart.rom + 0x600000;
action_replay.ram = cart.rom + 0x620000;
if (cart.romsize > 0x810000) return;
action_replay.rom = cart.rom + 0x810000;
action_replay.ram = cart.rom + 0x830000;
/* Open Action Replay ROM */
f = fopen(AR_ROM,"rb");

View File

@ -1,260 +1,249 @@
/****************************************************************************
* Genesis Plus
* Microwire Serial EEPROM (93C46) support
*
* Copyright (C) 2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "gg_eeprom.h"
#define BIT_DATA (0)
#define BIT_CLK (1)
#define BIT_CS (2)
T_EEPROM_93C gg_eeprom;
void gg_eeprom_init()
{
/* default eeprom state */
memset(&gg_eeprom, 0, sizeof(T_EEPROM_93C));
gg_eeprom.data = 1;
gg_eeprom.state = WAIT_START;
}
void gg_eeprom_ctrl(unsigned char data)
{
/* Reset EEPROM */
if (data & 0x80)
{
gg_eeprom_init();
return;
}
/* Enable EEPROM */
gg_eeprom.enabled = data & 0x08;
}
void gg_eeprom_write(unsigned char data)
{
/* Make sure CS is HIGH */
if (data & (1 << BIT_CS))
{
/* Data latched on CLK postive edge */
if ((data & (1 << BIT_CLK)) && !gg_eeprom.clk)
{
/* Current EEPROM state */
switch (gg_eeprom.state)
{
case WAIT_START:
{
/* Wait for START bit */
if (data & (1 << BIT_DATA))
{
gg_eeprom.opcode = 0;
gg_eeprom.cycles = 0;
gg_eeprom.state = GET_OPCODE;
}
break;
}
case GET_OPCODE:
{
/* 8-bit buffer (opcode + address) */
gg_eeprom.opcode |= ((data >> BIT_DATA) & 1) << (7 - gg_eeprom.cycles);
gg_eeprom.cycles++;
if (gg_eeprom.cycles == 8)
{
/* Decode instruction */
switch ((gg_eeprom.opcode >> 6) & 3)
{
case 1:
{
/* WRITE */
gg_eeprom.buffer = 0;
gg_eeprom.cycles = 0;
gg_eeprom.state = WRITE_WORD;
break;
}
case 2:
{
/* READ */
gg_eeprom.buffer = *(uint16 *)(sram.sram + ((gg_eeprom.opcode & 0x3F) << 1));
gg_eeprom.cycles = 0;
gg_eeprom.state = READ_WORD;
/* Force DATA OUT */
gg_eeprom.data = 0;
break;
}
case 3:
{
/* ERASE */
if (gg_eeprom.we)
{
*(uint16 *)(sram.sram + ((gg_eeprom.opcode & 0x3F) << 1)) = 0xFFFF;
}
/* wait for next command */
gg_eeprom.state = WAIT_STANDBY;
break;
}
default:
{
/* special command */
switch ((gg_eeprom.opcode >> 4) & 3)
{
case 1:
{
/* WRITE ALL */
gg_eeprom.buffer = 0;
gg_eeprom.cycles = 0;
gg_eeprom.state = WRITE_WORD;
break;
}
case 2:
{
/* ERASE ALL */
if (gg_eeprom.we)
{
memset(sram.sram, 0xFF, 128);
}
/* wait for next command */
gg_eeprom.state = WAIT_STANDBY;
break;
}
default:
{
/* WRITE ENABLE/DISABLE */
gg_eeprom.we = (gg_eeprom.opcode >> 4) & 1;
/* wait for next command */
gg_eeprom.state = WAIT_STANDBY;
break;
}
}
break;
}
}
}
break;
}
case WRITE_WORD:
{
/* 16-bit data buffer */
gg_eeprom.buffer |= ((data >> BIT_DATA) & 1) << (15 - gg_eeprom.cycles);
gg_eeprom.cycles++;
if (gg_eeprom.cycles == 16)
{
/* check EEPROM write protection */
if (gg_eeprom.we)
{
if (gg_eeprom.opcode & 0x40)
{
/* write one word */
*(uint16 *)(sram.sram + ((gg_eeprom.opcode & 0x3F) << 1)) = gg_eeprom.buffer;
}
else
{
/* write 64 words */
int i;
for (i=0; i<64; i++)
{
*(uint16 *)(sram.sram + (i << 1)) = gg_eeprom.buffer;
}
}
}
/* wait for next command */
gg_eeprom.state = WAIT_STANDBY;
}
break;
}
case READ_WORD:
{
/* set DATA OUT */
gg_eeprom.data = ((gg_eeprom.buffer >> (15 - gg_eeprom.cycles)) & 1);
gg_eeprom.cycles++;
if (gg_eeprom.cycles == 16)
{
/* read next word (93C46B) */
gg_eeprom.opcode++;
gg_eeprom.cycles = 0;
gg_eeprom.buffer = *(uint16 *)(sram.sram + ((gg_eeprom.opcode & 0x3F) << 1));
}
break;
}
default:
{
/* wait for STANDBY mode */
break;
}
}
}
}
else
{
/* CS HIGH->LOW transition */
if (gg_eeprom.cs)
{
/* standby mode */
gg_eeprom.data = 1;
gg_eeprom.state = WAIT_START;
}
}
/* Update input lines */
gg_eeprom.cs = (data >> BIT_CS) & 1;
gg_eeprom.clk = (data >> BIT_CLK) & 1;
}
unsigned char gg_eeprom_read(void)
{
return ((gg_eeprom.cs << BIT_CS) | (gg_eeprom.data << BIT_DATA) | (1 << BIT_CLK));
}
/****************************************************************************
* Genesis Plus
* Microwire Serial EEPROM (93C46 only) support
*
* Copyright (C) 2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "eeprom_93c.h"
/* fixed board implementation */
#define BIT_DATA (0)
#define BIT_CLK (1)
#define BIT_CS (2)
T_EEPROM_93C eeprom_93c;
void eeprom_93c_init()
{
/* default eeprom state */
memset(&eeprom_93c, 0, sizeof(T_EEPROM_93C));
eeprom_93c.data = 1;
eeprom_93c.state = WAIT_START;
sram.custom = 3;
}
void eeprom_93c_write(unsigned char data)
{
/* Make sure CS is HIGH */
if (data & (1 << BIT_CS))
{
/* Data latched on CLK postive edge */
if ((data & (1 << BIT_CLK)) && !eeprom_93c.clk)
{
/* Current EEPROM state */
switch (eeprom_93c.state)
{
case WAIT_START:
{
/* Wait for START bit */
if (data & (1 << BIT_DATA))
{
eeprom_93c.opcode = 0;
eeprom_93c.cycles = 0;
eeprom_93c.state = GET_OPCODE;
}
break;
}
case GET_OPCODE:
{
/* 8-bit buffer (opcode + address) */
eeprom_93c.opcode |= ((data >> BIT_DATA) & 1) << (7 - eeprom_93c.cycles);
eeprom_93c.cycles++;
if (eeprom_93c.cycles == 8)
{
/* Decode instruction */
switch ((eeprom_93c.opcode >> 6) & 3)
{
case 1:
{
/* WRITE */
eeprom_93c.buffer = 0;
eeprom_93c.cycles = 0;
eeprom_93c.state = WRITE_WORD;
break;
}
case 2:
{
/* READ */
eeprom_93c.buffer = *(uint16 *)(sram.sram + ((eeprom_93c.opcode & 0x3F) << 1));
eeprom_93c.cycles = 0;
eeprom_93c.state = READ_WORD;
/* Force DATA OUT */
eeprom_93c.data = 0;
break;
}
case 3:
{
/* ERASE */
if (eeprom_93c.we)
{
*(uint16 *)(sram.sram + ((eeprom_93c.opcode & 0x3F) << 1)) = 0xFFFF;
}
/* wait for next command */
eeprom_93c.state = WAIT_STANDBY;
break;
}
default:
{
/* special command */
switch ((eeprom_93c.opcode >> 4) & 3)
{
case 1:
{
/* WRITE ALL */
eeprom_93c.buffer = 0;
eeprom_93c.cycles = 0;
eeprom_93c.state = WRITE_WORD;
break;
}
case 2:
{
/* ERASE ALL */
if (eeprom_93c.we)
{
memset(sram.sram, 0xFF, 128);
}
/* wait for next command */
eeprom_93c.state = WAIT_STANDBY;
break;
}
default:
{
/* WRITE ENABLE/DISABLE */
eeprom_93c.we = (eeprom_93c.opcode >> 4) & 1;
/* wait for next command */
eeprom_93c.state = WAIT_STANDBY;
break;
}
}
break;
}
}
}
break;
}
case WRITE_WORD:
{
/* 16-bit data buffer */
eeprom_93c.buffer |= ((data >> BIT_DATA) & 1) << (15 - eeprom_93c.cycles);
eeprom_93c.cycles++;
if (eeprom_93c.cycles == 16)
{
/* check EEPROM write protection */
if (eeprom_93c.we)
{
if (eeprom_93c.opcode & 0x40)
{
/* write one word */
*(uint16 *)(sram.sram + ((eeprom_93c.opcode & 0x3F) << 1)) = eeprom_93c.buffer;
}
else
{
/* write 64 words */
int i;
for (i=0; i<64; i++)
{
*(uint16 *)(sram.sram + (i << 1)) = eeprom_93c.buffer;
}
}
}
/* wait for next command */
eeprom_93c.state = WAIT_STANDBY;
}
break;
}
case READ_WORD:
{
/* set DATA OUT */
eeprom_93c.data = ((eeprom_93c.buffer >> (15 - eeprom_93c.cycles)) & 1);
eeprom_93c.cycles++;
if (eeprom_93c.cycles == 16)
{
/* read next word (93C46B) */
eeprom_93c.opcode++;
eeprom_93c.cycles = 0;
eeprom_93c.buffer = *(uint16 *)(sram.sram + ((eeprom_93c.opcode & 0x3F) << 1));
}
break;
}
default:
{
/* wait for STANDBY mode */
break;
}
}
}
}
else
{
/* CS HIGH->LOW transition */
if (eeprom_93c.cs)
{
/* standby mode */
eeprom_93c.data = 1;
eeprom_93c.state = WAIT_START;
}
}
/* Update input lines */
eeprom_93c.cs = (data >> BIT_CS) & 1;
eeprom_93c.clk = (data >> BIT_CLK) & 1;
}
unsigned char eeprom_93c_read(void)
{
return ((eeprom_93c.cs << BIT_CS) | (eeprom_93c.data << BIT_DATA) | (1 << BIT_CLK));
}

View File

@ -1,75 +1,72 @@
/****************************************************************************
* Genesis Plus
* Microwire Serial EEPROM (93C46) support
*
* Copyright (C) 2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _GG_EEPROM_H_
#define _GG_EEPROM_H_
typedef enum
{
WAIT_STANDBY,
WAIT_START,
GET_OPCODE,
WRITE_WORD,
READ_WORD
} T_STATE;
typedef struct
{
uint8 enabled; /* 1: chip enabled */
uint8 cs; /* CHIP SELECT line state */
uint8 clk; /* CLK line state */
uint8 data; /* DATA OUT line state */
uint8 cycles; /* current operation cycle */
uint8 we; /* 1: write enabled */
uint8 opcode; /* 8-bit opcode + address */
uint16 buffer; /* 16-bit data buffer */
T_STATE state; /* current operation state */
} T_EEPROM_93C;
/* global variables */
extern T_EEPROM_93C gg_eeprom;
/* Function prototypes */
extern void gg_eeprom_init();
extern void gg_eeprom_ctrl(unsigned char data);
extern void gg_eeprom_write(unsigned char data);
extern unsigned char gg_eeprom_read(void);
#endif
/****************************************************************************
* Genesis Plus
* Microwire Serial EEPROM (93C46 only) support
*
* Copyright (C) 2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _EEPROM_93C_H_
#define _EEPROM_93C_H_
typedef enum
{
WAIT_STANDBY,
WAIT_START,
GET_OPCODE,
WRITE_WORD,
READ_WORD
} T_STATE_93C;
typedef struct
{
uint8 enabled; /* 1: chip enabled */
uint8 cs; /* CHIP SELECT line state */
uint8 clk; /* CLK line state */
uint8 data; /* DATA OUT line state */
uint8 cycles; /* current operation cycle */
uint8 we; /* 1: write enabled */
uint8 opcode; /* 8-bit opcode + address */
uint16 buffer; /* 16-bit data buffer */
T_STATE_93C state; /* current operation state */
} T_EEPROM_93C;
/* global variables */
extern T_EEPROM_93C eeprom_93c;
/* Function prototypes */
extern void eeprom_93c_init();
extern void eeprom_93c_write(unsigned char data);
extern unsigned char eeprom_93c_read(void);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
/****************************************************************************
* Genesis Plus
* I2C Serial EEPROM (24Cxx) support
*
* Copyright (C) 2007-2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _EEPROM_I2C_H_
#define _EEPROM_I2C_H_
/* Function prototypes */
extern void eeprom_i2c_init();
#endif

358
source/cart_hw/eeprom_spi.c Normal file
View File

@ -0,0 +1,358 @@
/****************************************************************************
* Genesis Plus
* SPI Serial EEPROM (25xxx/95xxx) support
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
/* max supported size 64KB (25x512/95x512) */
#define SIZE_MASK 0xffff
#define PAGE_MASK 0x7f
/* hard-coded board implementation (!WP pin not used) */
#define BIT_DATA (0)
#define BIT_CLK (1)
#define BIT_HOLD (2)
#define BIT_CS (3)
typedef enum
{
STANDBY,
GET_OPCODE,
GET_ADDRESS,
WRITE_BYTE,
READ_BYTE
} T_STATE_SPI;
typedef struct
{
uint8 cs; /* !CS line state */
uint8 clk; /* SCLK line state */
uint8 out; /* SO line state */
uint8 status; /* status register */
uint8 opcode; /* 8-bit opcode */
uint8 buffer; /* 8-bit data buffer */
uint16 addr; /* 16-bit address */
uint32 cycles; /* current operation cycle */
T_STATE_SPI state; /* current operation state */
} T_EEPROM_SPI;
static T_EEPROM_SPI spi_eeprom;
void eeprom_spi_init()
{
/* reset eeprom state */
memset(&spi_eeprom, 0, sizeof(T_EEPROM_SPI));
spi_eeprom.out = 1;
spi_eeprom.state = GET_OPCODE;
/* enable backup RAM */
sram.custom = 2;
sram.on = 1;
}
void eeprom_spi_write(unsigned char data)
{
/* Make sure !HOLD is high */
if (data & (1 << BIT_HOLD))
{
/* Check !CS state */
if (data & (1 << BIT_CS))
{
/* !CS high -> end of current operation */
spi_eeprom.cycles = 0;
spi_eeprom.out = 1;
spi_eeprom.opcode = 0;
spi_eeprom.state = GET_OPCODE;
}
else
{
/* !CS low -> process current operation */
switch (spi_eeprom.state)
{
case GET_OPCODE:
{
/* latch data on CLK positive edge */
if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
{
/* 8-bit opcode buffer */
spi_eeprom.opcode |= ((data >> BIT_DATA) & 1);
spi_eeprom.cycles++;
/* last bit ? */
if (spi_eeprom.cycles == 8)
{
/* reset cycles count */
spi_eeprom.cycles = 0;
/* Decode instruction */
switch (spi_eeprom.opcode)
{
case 0x01:
{
/* WRITE STATUS */
spi_eeprom.buffer = 0;
spi_eeprom.state = WRITE_BYTE;
break;
}
case 0x02:
{
/* WRITE BYTE */
spi_eeprom.addr = 0;
spi_eeprom.state = GET_ADDRESS;
break;
}
case 0x03:
{
/* READ BYTE */
spi_eeprom.addr = 0;
spi_eeprom.state = GET_ADDRESS;
break;
}
case 0x04:
{
/* WRITE DISABLE */
spi_eeprom.status &= ~0x02;
spi_eeprom.state = STANDBY;
break;
}
case 0x05:
{
/* READ STATUS */
spi_eeprom.buffer = spi_eeprom.status;
spi_eeprom.state = READ_BYTE;
break;
}
case 0x06:
{
/* WRITE ENABLE */
spi_eeprom.status |= 0x02;
spi_eeprom.state = STANDBY;
break;
}
default:
{
/* specific instructions (not supported) */
spi_eeprom.state = STANDBY;
break;
}
}
}
else
{
/* shift opcode value */
spi_eeprom.opcode = spi_eeprom.opcode << 1;
}
}
break;
}
case GET_ADDRESS:
{
/* latch data on CLK positive edge */
if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
{
/* 16-bit address */
spi_eeprom.addr |= ((data >> BIT_DATA) & 1);
spi_eeprom.cycles++;
/* last bit ? */
if (spi_eeprom.cycles == 16)
{
/* reset cycles count */
spi_eeprom.cycles = 0;
/* mask unused address bits */
spi_eeprom.addr &= SIZE_MASK;
/* operation type */
if (spi_eeprom.opcode & 0x01)
{
/* READ operation */
spi_eeprom.buffer = sram.sram[spi_eeprom.addr];
spi_eeprom.state = READ_BYTE;
}
else
{
/* WRITE operation */
spi_eeprom.buffer = 0;
spi_eeprom.state = WRITE_BYTE;
}
}
else
{
/* shift address value */
spi_eeprom.addr = spi_eeprom.addr << 1;
}
}
break;
}
case WRITE_BYTE:
{
/* latch data on CLK positive edge */
if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
{
/* 8-bit data buffer */
spi_eeprom.buffer |= ((data >> BIT_DATA) & 1);
spi_eeprom.cycles++;
/* last bit ? */
if (spi_eeprom.cycles == 8)
{
/* reset cycles count */
spi_eeprom.cycles = 0;
/* write data to destination */
if (spi_eeprom.opcode & 0x01)
{
/* update status register */
spi_eeprom.status = (spi_eeprom.status & 0x02) | (spi_eeprom.buffer & 0x0c);
/* wait for operation end */
spi_eeprom.state = STANDBY;
}
else
{
/* Memory Array (write-protected) */
if (spi_eeprom.status & 2)
{
/* check array protection bits (BP0, BP1) */
switch ((spi_eeprom.status >> 2) & 0x03)
{
case 0x01:
{
/* $C000-$FFFF (sector #3) is protected */
if (spi_eeprom.addr < 0xC000)
{
sram.sram[spi_eeprom.addr] = spi_eeprom.buffer;
}
break;
}
case 0x02:
{
/* $8000-$FFFF (sectors #2 and #3) is protected */
if (spi_eeprom.addr < 0x8000)
{
sram.sram[spi_eeprom.addr] = spi_eeprom.buffer;
}
break;
}
case 0x03:
{
/* $0000-$FFFF (all sectors) is protected */
break;
}
default:
{
/* no sectors protected */
sram.sram[spi_eeprom.addr] = spi_eeprom.buffer;
break;
}
}
}
/* reset data buffer */
spi_eeprom.buffer = 0;
/* increase array address (sequential writes are limited within the same page) */
spi_eeprom.addr = (spi_eeprom.addr & ~PAGE_MASK) | ((spi_eeprom.addr + 1) & PAGE_MASK);
}
}
else
{
/* shift data buffer value */
spi_eeprom.buffer = spi_eeprom.buffer << 1;
}
}
break;
}
case READ_BYTE:
{
/* output data on CLK positive edge */
if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
{
/* read out bits */
spi_eeprom.out = (spi_eeprom.buffer >> (7 - spi_eeprom.cycles)) & 1;
spi_eeprom.cycles++;
/* last bit ? */
if (spi_eeprom.cycles == 8)
{
/* reset cycles count */
spi_eeprom.cycles = 0;
/* read from memory array ? */
if (spi_eeprom.opcode == 0x03)
{
/* read next array byte */
spi_eeprom.addr = (spi_eeprom.addr + 1) & SIZE_MASK;
spi_eeprom.buffer = sram.sram[spi_eeprom.addr];
}
}
}
break;
}
default:
{
/* wait for !CS low->high transition */
break;
}
}
}
}
/* update input lines */
spi_eeprom.cs = (data >> BIT_CS) & 1;
spi_eeprom.clk = (data >> BIT_CLK) & 1;
}
unsigned int eeprom_spi_read(unsigned int address)
{
return (spi_eeprom.out << BIT_DATA);
}

View File

@ -0,0 +1,47 @@
/****************************************************************************
* Genesis Plus
* SPI Serial EEPROM (25XX512 only) support
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _EEPROM_SPI_H_
#define _EEPROM_SPI_H_
/* Function prototypes */
extern void eeprom_spi_init();
extern void eeprom_spi_write(unsigned char data);
extern unsigned int eeprom_spi_read(unsigned int address);
#endif

View File

@ -65,8 +65,8 @@ void ggenie_init(void)
memset(&ggenie,0,sizeof(ggenie));
/* Store Game Genie ROM (32k) above cartridge ROM + SRAM area */
if (cart.romsize > 0x600000) return;
ggenie.rom = cart.rom + 0x600000;
if (cart.romsize > 0x810000) return;
ggenie.rom = cart.rom + 0x810000;
/* Open Game Genie ROM file */
f = fopen(GG_ROM,"rb");

View File

@ -42,7 +42,8 @@
****************************************************************************************/
#include "shared.h"
#include "md_eeprom.h"
#include "eeprom_i2c.h"
#include "eeprom_spi.h"
#include "gamepad.h"
#define CART_CNT (48)
@ -60,7 +61,12 @@ typedef struct
/* Function prototypes */
static void mapper_sega_w(uint32 data);
static void mapper_ssf2_w(uint32 address, uint32 data);
static void mapper_wukong_w(uint32 address, uint32 data);
static void mapper_sf001_w(uint32 address, uint32 data);
static void mapper_sf002_w(uint32 address, uint32 data);
static void mapper_sf004_w(uint32 address, uint32 data);
static uint32 mapper_sf004_r(uint32 address);
static void mapper_t5740_w(uint32 address, uint32 data);
static uint32 mapper_t5740_r(uint32 address);
static void mapper_realtec_w(uint32 address, uint32 data);
static void mapper_seganet_w(uint32 address, uint32 data);
static void mapper_32k_w(uint32 data);
@ -242,11 +248,10 @@ void md_cart_init(void)
memset(cart.rom + cart.romsize, 0xff, size - cart.romsize);
}
/* special case: Sonic & Knuckles */
/* $200000-$3fffff is mapped to external cartridge */
/* Sonic & Knuckles */
if (strstr(rominfo.international,"SONIC & KNUCKLES") != NULL)
{
/* disable ROM mirroring */
/* disable ROM mirroring at $200000-$3fffff (normally mapped to external cartridge) */
size = 0x400000;
}
@ -280,29 +285,36 @@ void md_cart_init(void)
zbank_memory_map[i].write = zbank_unused_w;
}
/* support for Quackshot REV 01 (real) dump */
if ((strstr(rominfo.product,"00004054-01") != NULL) && (cart.romsize == 0x80000))
{
/* $000000-$0fffff: first 256K mirrored (A18 not connected to ROM chip, A19 not decoded) */
for (i=0x00; i<0x10; i++)
{
/* $200000-$3fffff: mirror of $000000-$1fffff (A21 not decoded) */
m68k.memory_map[i].base = m68k.memory_map[i + 0x20].base = cart.rom + ((i & 0x03) << 16);
}
/* $100000-$1fffff: second 256K mirrored (A20 connected to ROM chip A18) */
for (i=0x10; i<0x20; i++)
{
/* $200000-$3fffff: mirror of $000000-$1fffff (A21 not decoded) */
m68k.memory_map[i].base = m68k.memory_map[i + 0x20].base = cart.rom + 0x40000 + ((i & 0x03) << 16);
}
}
/**********************************************
BACKUP MEMORY
***********************************************/
sram_init();
md_eeprom_init();
eeprom_i2c_init();
if (sram.on)
{
if (sram.custom)
/* static RAM only (64KB max.) */
if (!sram.custom)
{
/* Serial EEPROM */
m68k.memory_map[md_eeprom.type.sda_out_adr >> 16].read8 = md_eeprom_read_byte;
m68k.memory_map[md_eeprom.type.sda_out_adr >> 16].read16 = md_eeprom_read_word;
m68k.memory_map[md_eeprom.type.sda_in_adr >> 16].read8 = md_eeprom_read_byte;
m68k.memory_map[md_eeprom.type.sda_in_adr >> 16].read16 = md_eeprom_read_word;
m68k.memory_map[md_eeprom.type.scl_adr >> 16].write8 = md_eeprom_write_byte;
m68k.memory_map[md_eeprom.type.scl_adr >> 16].write16 = md_eeprom_write_word;
zbank_memory_map[md_eeprom.type.sda_out_adr >> 16].read = md_eeprom_read_byte;
zbank_memory_map[md_eeprom.type.sda_in_adr >> 16].read = md_eeprom_read_byte;
zbank_memory_map[md_eeprom.type.scl_adr >> 16].write = md_eeprom_write_byte;
}
else
{
/* Static RAM (64k max.) - disabled on reset if ROM is mapped in same area */
/* disabled on startup if ROM is mapped in same area */
if (cart.romsize <= sram.start)
{
m68k.memory_map[sram.start >> 16].base = sram.sram;
@ -438,7 +450,7 @@ void md_cart_init(void)
/**********************************************
J-CART
***********************************************/
if (((strstr(rominfo.product,"00000000") != NULL) && (rominfo.checksum == 0x168b)) || /* Super Skidmarks, Micro Machines Military*/
if (((strstr(rominfo.product,"00000000") != NULL) && (rominfo.checksum == 0x168b)) || /* Super Skidmarks, Micro Machines Military */
((strstr(rominfo.product,"00000000") != NULL) && (rominfo.checksum == 0x165e)) || /* Pete Sampras Tennis (1991), Micro Machines 96 */
((strstr(rominfo.product,"00000000") != NULL) && (rominfo.checksum == 0xcee0)) || /* Micro Machines Military (bad) */
((strstr(rominfo.product,"00000000") != NULL) && (rominfo.checksum == 0x2c41)) || /* Micro Machines 96 (bad) */
@ -493,11 +505,11 @@ void md_cart_init(void)
{
FILE *f;
/* store S&K ROM above cartridge ROM + SRAM */
/* store S&K ROM above cartridge ROM (and before backup memory) */
if (cart.romsize > 0x600000) break;
/* load Sonic & Knuckles ROM (2 MBytes) */
f = fopen(SK_ROM,"r+b");
/* load Sonic & Knuckles ROM (2 MB) */
f = fopen(SK_ROM,"rb");
if (!f) break;
for (i=0; i<0x200000; i+=0x1000)
{
@ -505,23 +517,31 @@ void md_cart_init(void)
}
fclose(f);
/* load Sonic 2 UPMEM ROM (256 KBytes) */
f = fopen(SK_UPMEM,"r+b");
/* load Sonic 2 UPMEM ROM (256 KB) */
f = fopen(SK_UPMEM,"rb");
if (!f) break;
for (i=0; i<0x40000; i+=0x1000)
{
fread(cart.rom + 0x800000 + i, 0x1000, 1, f);
fread(cart.rom + 0x900000 + i, 0x1000, 1, f);
}
fclose(f);
#ifdef LSB_FIRST
for (i=0; i<0x240000; i+=2)
for (i=0; i<0x200000; i+=2)
{
/* Byteswap ROM */
uint8 temp = cart.rom[i + 0x600000];
cart.rom[i + 0x600000] = cart.rom[i + 0x600000 + 1];
cart.rom[i + 0x600000 + 1] = temp;
}
for (i=0; i<0x40000; i+=2)
{
/* Byteswap ROM */
uint8 temp = cart.rom[i + 0x900000];
cart.rom[i + 0x900000] = cart.rom[i + 0x900000 + 1];
cart.rom[i + 0x900000 + 1] = temp;
}
#endif
/* $000000-$1FFFFF is mapped to S&K ROM */
@ -601,10 +621,92 @@ void md_cart_init(void)
m68k.aerr_enabled = config.addr_error;
#endif
/* detect special cartridges */
if (cart.romsize > 0x800000)
/* detect specific mappers */
if (strstr(rominfo.domestic,"SUPER STREET FIGHTER2") != NULL)
{
/* Ultimate MK3 (hack) */
/* SSF2 mapper */
cart.hw.bankshift = 1;
/* specific !TIME handler */
cart.hw.time_w = mapper_ssf2_w;
}
else if (strstr(rominfo.product,"T-5740") != NULL)
{
/* T-5740XX-XX mapper */
cart.hw.bankshift = 1;
m68k.memory_map[0x01].read8 = mapper_t5740_r;
zbank_memory_map[0x01].read = mapper_t5740_r;
/* specific !TIME handlers */
cart.hw.time_w = mapper_t5740_w;
cart.hw.time_r = eeprom_spi_read;
/* initialize SPI EEPROM board */
eeprom_spi_init();
}
else if ((strstr(rominfo.ROMType,"SF") != NULL) && (strstr(rominfo.product,"001") != NULL))
{
/* SF-001 mapper */
m68k.memory_map[0x00].write8 = mapper_sf001_w;
m68k.memory_map[0x00].write16 = mapper_sf001_w;
zbank_memory_map[0x00].write = mapper_sf001_w;
/* no !TIME handler */
cart.hw.time_w = m68k_unused_8_w;
}
else if ((strstr(rominfo.ROMType,"SF") != NULL) && (strstr(rominfo.product,"002") != NULL))
{
/* SF-002 mapper */
m68k.memory_map[0x00].write8 = mapper_sf002_w;
m68k.memory_map[0x00].write16 = mapper_sf002_w;
zbank_memory_map[0x00].write = mapper_sf002_w;
/* no !TIME handler */
cart.hw.time_w = m68k_unused_8_w;
}
else if ((strstr(rominfo.ROMType,"SF") != NULL) && (strstr(rominfo.product,"004") != NULL))
{
/* SF-004 mapper */
m68k.memory_map[0x00].write8 = mapper_sf004_w;
m68k.memory_map[0x00].write16 = mapper_sf004_w;
zbank_memory_map[0x00].write = mapper_sf004_w;
/* specific !TIME handlers */
cart.hw.time_r = mapper_sf004_r;
cart.hw.time_w = m68k_unused_8_w;
/* first 256K ROM bank is initially mirrored into $000000-$1FFFFF */
for (i=0x00; i<0x20; i++)
{
m68k.memory_map[i].base = cart.rom + ((i & 0x03) << 16);
}
/* 32K SRAM is initially disabled at $200000-$2FFFFF */
for (i=0x20; i<0x30; i++)
{
m68k.memory_map[i].base = sram.sram;
m68k.memory_map[i].read8 = m68k_read_bus_8;
m68k.memory_map[i].read16 = m68k_read_bus_16;
m68k.memory_map[i].write8 = m68k_unused_8_w;
m68k.memory_map[i].write16 = m68k_unused_16_w;
zbank_memory_map[i].read = m68k_read_bus_8;
zbank_memory_map[i].write = zbank_unused_w;
}
/* $300000-$3FFFFF is not mapped */
for (i=0x30; i<0x40; i++)
{
m68k.memory_map[i].read8 = m68k_read_bus_8;
m68k.memory_map[i].read16 = m68k_read_bus_16;
m68k.memory_map[i].write8 = m68k_unused_8_w;
m68k.memory_map[i].write16 = m68k_unused_16_w;
zbank_memory_map[i].read = m68k_read_bus_8;
zbank_memory_map[i].write = zbank_unused_w;
}
}
else if (cart.romsize > 0x400000)
{
/* assume linear ROM mapper without bankswitching (max. 10MB) */
for (i=0x40; i<0xA0; i++)
{
m68k.memory_map[i].base = cart.rom + (i<<16);
@ -612,34 +714,6 @@ void md_cart_init(void)
m68k.memory_map[i].read16 = NULL;
zbank_memory_map[i].read = NULL;
}
#if M68K_EMULATE_ADDRESS_ERROR
/* this game does not work properly on real hardware */
m68k.aerr_enabled = 0;
#endif
}
else if (cart.romsize > 0x400000)
{
/* assume SSF2 mapper */
cart.hw.bankshift = 1;
cart.hw.time_w = mapper_ssf2_w;
}
/* Legend of Wukong mapper */
if (strstr(rominfo.international,"LEGEND OF WUKONG") != NULL)
{
/* 128K ROM Bankswitch */
m68k.memory_map[0].write8 = mapper_wukong_w;
zbank_memory_map[0].write = mapper_wukong_w;
/* 8K SRAM */
m68k.memory_map[0x3C].base = sram.sram;
m68k.memory_map[0x3C].read8 = NULL;
m68k.memory_map[0x3C].read16 = NULL;
m68k.memory_map[0x3C].write8 = NULL;
m68k.memory_map[0x3C].write16 = NULL;
zbank_memory_map[0x3C].read = NULL;
zbank_memory_map[0x3C].write = NULL;
}
/* default write handler for !TIME range ($A130xx)*/
@ -664,7 +738,10 @@ void md_cart_reset(int hard_reset)
}
/* SVP chip */
if (svp) svp_reset();
if (svp)
{
svp_reset();
}
/* Lock-ON */
switch (config.lock_on)
@ -782,7 +859,7 @@ int md_cart_context_load(uint8 *state)
*************************************************************/
/*
ROM/SRAM Bankswitch (Phantasy Star IV, Story of Thor/Beyond Oasis, Sonic 3 & Knuckles)
"official" ROM/SRAM bankswitch (Phantasy Star IV, Story of Thor/Beyond Oasis, Sonic 3 & Knuckles)
*/
static void mapper_sega_w(uint32 data)
{
@ -813,7 +890,7 @@ static void mapper_sega_w(uint32 data)
/* S2K upmem chip mapped to $300000-$3fffff (256K mirrored) */
for (i=0x30; i<0x40; i++)
{
m68k.memory_map[i].base = (cart.rom + 0x800000) + ((i & 3) << 16);
m68k.memory_map[i].base = (cart.rom + 0x900000) + ((i & 3) << 16);
}
}
}
@ -831,7 +908,7 @@ static void mapper_sega_w(uint32 data)
}
/*
Super Street Fighter 2 ROM Bankswitch
Super Street Fighter 2 ROM bankswitch
documented by Bart Trzynadlowski (http://www.trzy.org/files/ssf2.txt)
*/
static void mapper_ssf2_w(uint32 address, uint32 data)
@ -853,17 +930,112 @@ static void mapper_ssf2_w(uint32 address, uint32 data)
}
/*
Legend of Wukong ROM Bankswitch
SF-001 mapper
*/
static void mapper_wukong_w(uint32 address, uint32 data)
static void mapper_sf001_w(uint32 address, uint32 data)
{
switch ((address >> 8) & 0xf)
{
case 0xe:
{
int i;
/* bit 6: enable / disable cartridge access */
if (data & 0x40)
{
/* $000000-$3FFFFF is not mapped */
for (i=0x00; i<0x40; i++)
{
m68k.memory_map[i].base = cart.rom + (i << 16);
m68k.memory_map[i].read8 = m68k_read_bus_8;
m68k.memory_map[i].read16 = m68k_read_bus_16;
m68k.memory_map[i].write8 = m68k_unused_8_w;
m68k.memory_map[i].write16 = m68k_unused_16_w;
zbank_memory_map[i].read = zbank_unused_r;
zbank_memory_map[i].write = m68k_unused_8_w;
}
}
/* bit 7: enable / disable SRAM & ROM bankswitching */
else if (data & 0x80)
{
/* 256K ROM bank #15 mapped to $000000-$03FFFF */
for (i=0x00; i<0x04; i++)
{
m68k.memory_map[i].base = cart.rom + ((0x38 + i) << 16);
m68k.memory_map[i].read8 = NULL;
m68k.memory_map[i].read16 = NULL;
zbank_memory_map[i].read = NULL;
}
/* 256K ROM banks #2 to #15 mapped to $040000-$3BFFFF */
for (i=0x04; i<0x3c; i++)
{
m68k.memory_map[i].base = cart.rom + (i << 16);
m68k.memory_map[i].read8 = NULL;
m68k.memory_map[i].read16 = NULL;
zbank_memory_map[i].read = NULL;
}
/* 32K static RAM mirrored into $3C0000-$3FFFFF (odd bytes only) */
for (i=0x3c; i<0x40; i++)
{
m68k.memory_map[i].base = sram.sram;
m68k.memory_map[i].read8 = NULL;
m68k.memory_map[i].read16 = NULL;
m68k.memory_map[i].write8 = NULL;
m68k.memory_map[i].write16 = NULL;
zbank_memory_map[i].read = NULL;
zbank_memory_map[i].write = NULL;
}
}
else
{
/* 256K ROM banks #1 to #16 mapped to $000000-$3FFFFF */
for (i=0x00; i<0x40; i++)
{
m68k.memory_map[i].base = cart.rom + (i << 16);
m68k.memory_map[i].read8 = NULL;
m68k.memory_map[i].read16 = NULL;
m68k.memory_map[i].write8 = m68k_unused_8_w;
m68k.memory_map[i].write16 = m68k_unused_16_w;
zbank_memory_map[i].read = NULL;
zbank_memory_map[i].write = m68k_unused_8_w;
}
}
/* bit 5: lock bankswitch hardware when set */
if (data & 0x20)
{
/* disable bankswitch hardware access until hard reset */
m68k.memory_map[0x00].write8 = m68k_unused_8_w;
m68k.memory_map[0x00].write16 = m68k_unused_16_w;
zbank_memory_map[0x00].write = m68k_unused_8_w;
}
return;
}
default:
{
m68k_unused_8_w(address, data);
return;
}
}
}
/*
SF-002 mapper
*/
static void mapper_sf002_w(uint32 address, uint32 data)
{
int i;
if (data & 0x80)
{
/* $200000-$3BFFFF mapped to $000000-$1BFFFF */
/* $000000-$1BFFFF mapped to $200000-$3BFFFF */
for (i=0x20; i<0x3C; i++)
{
m68k.memory_map[i].base = cart.rom + (((i&0x1F)<<16) & cart.mask);
m68k.memory_map[i].base = cart.rom + ((i & 0x1F) << 16);
}
}
else
@ -871,13 +1043,236 @@ static void mapper_wukong_w(uint32 address, uint32 data)
/* $200000-$3BFFFF mapped to $200000-$3BFFFF */
for (i=0x20; i<0x3C; i++)
{
m68k.memory_map[i].base = cart.rom + ((i<<16) & cart.mask);
m68k.memory_map[i].base = cart.rom + (i << 16);
}
}
}
/*
Realtec ROM Bankswitch (Earth Defend, Balloon Boy & Funny World, Whac-A-Critter)
SF-004 mapper
*/
static void mapper_sf004_w(uint32 address, uint32 data)
{
int i;
switch ((address >> 8) & 0xf)
{
case 0xd:
{
/* bit 7: enable/disable static RAM access */
if (data & 0x80)
{
/* 32KB static RAM mirrored into $200000-$2FFFFF (odd bytes only) */
for (i=0x20; i<0x30; i++)
{
m68k.memory_map[i].read8 = NULL;
m68k.memory_map[i].read16 = NULL;
m68k.memory_map[i].write8 = NULL;
m68k.memory_map[i].write16 = NULL;
zbank_memory_map[i].read = NULL;
zbank_memory_map[i].write = NULL;
}
}
else
{
/* 32KB static RAM disabled at $200000-$2FFFFF */
for (i=0x20; i<0x30; i++)
{
m68k.memory_map[i].read8 = m68k_read_bus_8;
m68k.memory_map[i].read16 = m68k_read_bus_16;
m68k.memory_map[i].write8 = m68k_unused_8_w;
m68k.memory_map[i].write16 = m68k_unused_16_w;
zbank_memory_map[i].read = m68k_read_bus_8;
zbank_memory_map[i].write = m68k_unused_8_w;
}
}
return;
}
case 0x0e:
{
/* bit 5: enable / disable cartridge ROM access */
if (data & 0x20)
{
/* $000000-$1FFFFF is not mapped */
for (i=0x00; i<0x20; i++)
{
m68k.memory_map[i].read8 = m68k_read_bus_8;
m68k.memory_map[i].read16 = m68k_read_bus_16;
zbank_memory_map[i].read = m68k_read_bus_8;
}
}
/* bit 6: enable / disable first page mirroring */
else if (data & 0x40)
{
/* first page ROM bank */
uint8 base = (m68k.memory_map[0x00].base - cart.rom) >> 16;
/* 5 x 256K ROM banks mapped to $000000-$13FFFF, starting from first page ROM bank */
for (i=0x00; i<0x14; i++)
{
m68k.memory_map[i].base = cart.rom + (((base + i) & 0x1f) << 16);
m68k.memory_map[i].read8 = NULL;
m68k.memory_map[i].read16 = NULL;
zbank_memory_map[i].read = NULL;
}
/* $140000-$1FFFFF is not mapped */
for (i=0x14; i<0x20; i++)
{
m68k.memory_map[i].read8 = m68k_read_bus_8;
m68k.memory_map[i].read16 = m68k_read_bus_16;
zbank_memory_map[i].read = m68k_read_bus_8;
}
}
else
{
/* first page 256K ROM bank mirrored into $000000-$1FFFFF */
for (i=0x00; i<0x20; i++)
{
m68k.memory_map[i].base = m68k.memory_map[0].base + ((i & 0x03) << 16);
m68k.memory_map[i].read8 = NULL;
m68k.memory_map[i].read16 = NULL;
zbank_memory_map[i].read = NULL;
}
}
/* bit 7: lock ROM bankswitching hardware when cleared */
if (!(data & 0x80))
{
/* disable bankswitch hardware access */
m68k.memory_map[0x00].write8 = m68k_unused_8_w;
m68k.memory_map[0x00].write16 = m68k_unused_16_w;
zbank_memory_map[0x00].write = m68k_unused_8_w;
}
return;
}
case 0x0f:
{
/* bits 6-4: select first page ROM bank (8 x 256K ROM banks) */
uint8 base = ((data >> 4) & 7) << 2;
if (m68k.memory_map[0].base == m68k.memory_map[4].base)
{
/* selected 256K ROM bank mirrored into $000000-$1FFFFF */
for (i=0x00; i<0x20; i++)
{
m68k.memory_map[i].base = cart.rom + ((base + (i & 0x03)) << 16);
}
}
else
{
/* 5 x 256K ROM banks mapped to $000000-$13FFFF, starting from selected bank */
for (i=0x00; i<0x14; i++)
{
m68k.memory_map[i].base = cart.rom + (((base + i) & 0x1f) << 16);
}
}
return;
}
default:
{
m68k_unused_8_w(address, data);
return;
}
}
}
static uint32 mapper_sf004_r(uint32 address)
{
/* return first page 256K bank index ($00,$10,$20,...,$70) */
return (((m68k.memory_map[0x00].base - cart.rom) >> 18) << 4);
}
/*
T-5740xx-xx mapper
*/
static void mapper_t5740_w(uint32 address, uint32 data)
{
int i;
uint8 *base;
switch (address & 0xff)
{
case 0x01: /* mode register */
{
/* bits 7-4: unused ? */
/* bit 3: enable SPI registers access ? */
/* bit 2: not used ? */
/* bit 1: enable bankswitch registers access ? */
/* bit 0: always set, enable hardware access ? */
return;
}
case 0x03: /* page #5 register */
{
/* map any of 16 x 512K ROM banks to $280000-$2FFFFF */
base = cart.rom + ((data & 0x0f) << 19);
for (i=0x28; i<0x30; i++)
{
m68k.memory_map[i].base = base + ((i & 0x07) << 16);
}
return;
}
case 0x05: /* page #6 register */
{
/* map any of 16 x 512K ROM banks to $300000-$37FFFF */
base = cart.rom + ((data & 0x0f) << 19);
for (i=0x30; i<0x38; i++)
{
m68k.memory_map[i].base = base + ((i & 0x07) << 16);
}
return;
}
case 0x07: /* page #7 register */
{
/* map any of 16 x 512K ROM banks to $380000-$3FFFFF */
base = cart.rom + ((data & 0x0f) << 19);
for (i=0x38; i<0x40; i++)
{
m68k.memory_map[i].base = base + ((i & 0x07) << 16);
}
return;
}
case 0x09: /* serial EEPROM SPI board support */
{
eeprom_spi_write(data);
return;
}
default:
{
/* unknown registers */
m68k_unused_8_w(address, data);
return;
}
}
}
static uint32 mapper_t5740_r(uint32 address)
{
/* By default, first 32K of each eight 512K pages mapped in $000000-$3FFFFF are mirrored in the 512K page */
/* mirroring is disabled/enabled when a specific number of words is being read from specific ROM addresses */
/* Exact decoding isn't known but mirrored data is expected on startup when reading a few times from $181xx */
/* this area doesn't seem to be accessed as byte later so it seems safe to always return mirrored data here */
if ((address & 0xff00) == 0x8100)
{
return READ_BYTE(cart.rom , (address & 0x7fff));
}
return READ_BYTE(cart.rom, address);
}
/*
Realtec ROM bankswitch (Earth Defend, Balloon Boy & Funny World, Whac-A-Critter)
(Note: register usage is inverted in TascoDlx documentation)
*/
static void mapper_realtec_w(uint32 address, uint32 data)

View File

@ -1,111 +0,0 @@
/****************************************************************************
* Genesis Plus
* I2C Serial EEPROM (24Cxx) support
*
* Copyright (C) 2007-2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _MD_EEPROM_H_
#define _MD_EEPROM_H_
typedef enum
{
STAND_BY = 0,
WAIT_STOP,
GET_SLAVE_ADR,
GET_WORD_ADR_7BITS,
GET_WORD_ADR_HIGH,
GET_WORD_ADR_LOW,
WRITE_DATA,
READ_DATA
} T_EEPROM_STATE;
/* this defines the type of EEPROM inside the game cartridge as Backup RAM
*
* Here are some notes from 8BitWizard (http://www.spritesmind.net/_GenDev/forum):
*
* Mode 1 (7-bit) - the chip takes a single byte with a 7-bit memory address and a R/W bit (24C01)
* Mode 2 (8-bit) - the chip takes a 7-bit device address and R/W bit followed by an 8-bit memory address;
* the device address may contain up to three more memory address bits (24C01 - 24C16).
* You can also string eight 24C01, four 24C02, two 24C08, or various combinations, set their address config lines correctly,
* and the result appears exactly the same as a 24C16
* Mode 3 (16-bit) - the chip takes a 7-bit device address and R/W bit followed by a 16-bit memory address (24C32 and larger)
*
* Also, while most 24Cxx are addressed at 200000-2FFFFF, I have found two different ways of mapping the control lines.
* EA uses SDA on D7 (read/write) and SCL on D6 (write only), and I have found boards using different mapping (I think Accolade)
* which uses D1-read=SDA, D0-write=SDA, D1-write=SCL. Accolade also has a custom-chip mapper which may even use a third method.
*/
typedef struct
{
uint8 address_bits; /* number of bits needed to address memory: 7, 8 or 16 */
uint16 size_mask; /* depends on the max size of the memory (in bytes) */
uint16 pagewrite_mask; /* depends on the maximal number of bytes that can be written in a single write cycle */
uint32 sda_in_adr; /* 68000 memory address mapped to SDA_IN */
uint32 sda_out_adr; /* 68000 memory address mapped to SDA_OUT */
uint32 scl_adr; /* 68000 memory address mapped to SCL */
uint8 sda_in_bit; /* bit offset for SDA_IN */
uint8 sda_out_bit; /* bit offset for SDA_OUT */
uint8 scl_bit; /* bit offset for SCL */
} T_EEPROM_TYPE;
typedef struct
{
uint8 sda; /* current /SDA line state */
uint8 scl; /* current /SCL line state */
uint8 old_sda; /* previous /SDA line state */
uint8 old_scl; /* previous /SCL line state */
uint8 cycles; /* current operation cycle number (0-9) */
uint8 rw; /* operation type (1:READ, 0:WRITE) */
uint16 slave_mask; /* device address (shifted by the memory address width)*/
uint16 word_address; /* memory address */
T_EEPROM_STATE state; /* current operation state */
T_EEPROM_TYPE type; /* EEPROM characteristics for this game */
} T_EEPROM_24C;
/* global variables */
extern T_EEPROM_24C md_eeprom;
/* Function prototypes */
extern void md_eeprom_init();
extern unsigned int md_eeprom_read_byte(unsigned int address);
extern unsigned int md_eeprom_read_word(unsigned int address);
extern void md_eeprom_write_byte(unsigned int address, unsigned int data);
extern void md_eeprom_write_word(unsigned int address, unsigned int data);
#endif

View File

@ -37,7 +37,7 @@
****************************************************************************************/
#include "shared.h"
#include "gg_eeprom.h"
#include "eeprom_93c.h"
#include "terebi_oekaki.h"
#define MAPPER_NONE (0x00)
@ -446,7 +446,7 @@ void sms_cart_init(void)
if (cart_rom.mapper == MAPPER_93C46)
{
/* 93C46 eeprom */
gg_eeprom_init();
eeprom_93c_init();
}
else if (cart_rom.mapper == MAPPER_TEREBI)
{
@ -457,6 +457,9 @@ void sms_cart_init(void)
/* initialize SRAM */
sram_init();
/* enable cartridge backup memory by default */
sram.on = 1;
/* save current settings */
if (old_system[0] == -1)
{
@ -1230,15 +1233,24 @@ static void write_mapper_korea_16k(unsigned int address, unsigned char data)
static void write_mapper_93c46(unsigned int address, unsigned char data)
{
if ((address == 0x8000) && gg_eeprom.enabled)
/* EEPROM serial input */
if ((address == 0x8000) && eeprom_93c.enabled)
{
gg_eeprom_write(data);
eeprom_93c_write(data);
return;
}
/* EEPROM ctrl */
if (address == 0xFFFC)
{
gg_eeprom_ctrl(data);
/* enable/disable EEPROM */
eeprom_93c.enabled = data & 0x08;
if (data & 0x80)
{
/* reset EEPROM */
eeprom_93c_init();
}
}
/* SEGA mapper compatibility */
@ -1263,9 +1275,9 @@ static void write_mapper_terebi(unsigned int address, unsigned char data)
static unsigned char read_mapper_93c46(unsigned int address)
{
if ((address == 0x8000) && gg_eeprom.enabled)
if ((address == 0x8000) && eeprom_93c.enabled)
{
return gg_eeprom_read();
return eeprom_93c_read();
}
return z80_readmap[address >> 10][address & 0x03FF];

View File

@ -57,13 +57,13 @@ T_SRAM sram;
****************************************************************************/
void sram_init()
{
memset (&sram, 0, sizeof (T_SRAM));
memset(&sram, 0, sizeof (T_SRAM));
/* SRAM data is stored above cartridge ROM area, at $500000-$50FFFF (max. 64K) */
if (cart.romsize > 0x500000) return;
sram.sram = cart.rom + 0x500000;
/* backup RAM data is stored above cartridge ROM area, at $800000-$80FFFF (max. 64K) */
if (cart.romsize > 0x800000) return;
sram.sram = cart.rom + 0x800000;
/* initialize SRAM */
/* initialize Backup RAM */
memset(sram.sram, 0xFF, 0x10000);
sram.crc = crc32(0, sram.sram, 0x10000);
@ -73,22 +73,100 @@ void sram_init()
sram.start = READ_WORD_LONG(cart.rom, 0x1b4);
sram.end = READ_WORD_LONG(cart.rom, 0x1b8);
/* fixe some bad header informations */
if ((sram.start > sram.end) || ((sram.end - sram.start) >= 0x10000))
/* autodetect games with wrong header infos */
if (strstr(rominfo.product,"T-26013") != NULL)
{
/* Psy-O-Blade (wrong header) */
sram.start = 0x200001;
sram.end = 0x203fff;
}
/* fixe other bad header informations */
else if ((sram.start > sram.end) || ((sram.end - sram.start) >= 0x10000))
{
sram.end = sram.start + 0xffff;
}
sram.start &= 0xfffffffe;
sram.start &= 0xfffffe;
sram.end |= 1;
/* enable SRAM */
/* enable backup RAM */
sram.on = 1;
sram.detected = 1;
}
else
{
/* by default, enable SRAM only for ROM <= 2MB */
if (cart.romsize <= 0x200000)
/* autodetect games with missing header infos */
if (strstr(rominfo.product,"T-50086") != NULL)
{
/* PGA Tour Golf */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x203fff;
}
else if (strstr(rominfo.product,"ACLD007") != NULL)
{
/* Winter Challenge */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x200fff;
}
else if (strstr(rominfo.product,"T-50286") != NULL)
{
/* Buck Rogers - Countdown to Doomsday */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x203fff;
}
else if (((rominfo.realchecksum == 0xaeaa) || (rominfo.realchecksum == 0x8dba)) &&
(rominfo.checksum == 0x8104))
{
/* Xin Qigai Wangzi (use uncommon area) */
sram.on = 1;
sram.start = 0x400000;
sram.end = 0x40ffff;
}
else if ((strstr(rominfo.ROMType,"SF") != NULL) && (strstr(rominfo.product,"001") != NULL))
{
/* SF-001 (use bankswitching) */
sram.on = 1;
sram.start = 0x3c0001;
sram.end = 0x3cffff;
}
else if ((strstr(rominfo.ROMType,"SF") != NULL) && (strstr(rominfo.product,"004") != NULL))
{
/* SF-004 (use bankswitching) */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x203fff;
}
else if (strstr(rominfo.international,"SONIC & KNUCKLES") != NULL)
{
/* standalone Sonic & Knuckle does not use backup RAM */
if (cart.romsize == 0x400000)
{
/* Sonic 3 & Knuckles combined ROM */
/* it shows S&K header but should obviously use FRAM from Sonic 3 */
sram.on = 1;
sram.start = 0x200000;
sram.end = 0x203fff;
}
}
/* auto-detect games which need disabled backup RAM */
else if (strstr(rominfo.product,"T-113016") != NULL)
{
/* Pugsy (try writing outside ROM area as copy protection) */
sram.on = 0;
}
else if (strstr(rominfo.international,"SONIC THE HEDGEHOG 2") != NULL)
{
/* Sonic the Hedgehog 2 (does not use backup RAM) */
/* this prevents backup RAM from being mapped in place of mirrored ROM when using S&K LOCK-ON feature */
sram.on = 0;
}
/* by default, enable backup RAM for ROM smaller than 2MB */
else if (cart.romsize <= 0x200000)
{
/* SRAM mapped to $200000-$20ffff */
sram.start = 0x200000;
@ -96,62 +174,4 @@ void sram_init()
sram.on = 1;
}
}
/* autodetect some games with bad header or specific configuration */
if (strstr(rominfo.product,"T-113016") != NULL)
{
/* Pugsy (try accessing unmapped area for copy protection) */
sram.on = 0;
}
else if (strstr(rominfo.international,"SONIC THE HEDGEHOG 2") != NULL)
{
/* Sonic the Hedgehog 2 does not use SRAM */
/* this prevents SRAM activation when using Sonic & Knuckles LOCK-ON feature */
sram.on = 0;
}
else if (strstr(rominfo.international,"SONIC & KNUCKLES") != NULL)
{
if (cart.romsize == 0x400000)
{
/* Sonic 3 & Knuckles */
/* the combined ROM has S&K header but should obviously use FRAM from Sonic the Hedgehog 3 */
sram.on = 1;
}
}
else if (strstr(rominfo.product,"T-26013") != NULL)
{
/* Psy-O-Blade (bad header) */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x203fff;
}
else if (strstr(rominfo.product,"T-50086") != NULL)
{
/* PGA Tour Golf (no header) */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x203fff;
}
else if (strstr(rominfo.product,"ACLD007") != NULL)
{
/* Winter Challenge (no header) */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x200fff;
}
else if (strstr(rominfo.product,"T-50286") != NULL)
{
/* Buck Rogers - Countdown to Doomsday (no header) */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x203fff;
}
else if (((rominfo.realchecksum == 0xaeaa) || (rominfo.realchecksum == 0x8dba)) &&
(rominfo.checksum == 0x8104))
{
/* Xin Qigai Wangzi, aka Beggar Prince (no header, use uncommon area) */
sram.on = 1;
sram.start = 0x400000;
sram.end = 0x40ffff;
}
}

View File

@ -1,6 +1,6 @@
/***************************************************************************************
* Genesis Plus
* CD cartridge (external RAM or ROM)
* CD compatible ROM/RAM cartridge support
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
@ -40,7 +40,7 @@
/*--------------------------------------------------------------------------*/
/* cartridge backup RAM (max. 512KB) */
/* backup RAM cartridge (max. 512KB) */
/*--------------------------------------------------------------------------*/
static unsigned int cart_ram_read_byte(unsigned int address)
{
@ -74,7 +74,7 @@ static void cart_ram_write_word(unsigned int address, unsigned int data)
/*--------------------------------------------------------------------------*/
/* cartridge RAM ID */
/* backup RAM cartridge ID */
/*--------------------------------------------------------------------------*/
static unsigned int cart_id_read_byte(unsigned int address)
@ -95,7 +95,7 @@ static unsigned int cart_id_read_word(unsigned int address)
/*--------------------------------------------------------------------------*/
/* cartridge RAM write protection */
/* backup RAM cartridge write protection */
/*--------------------------------------------------------------------------*/
static unsigned int cart_prot_read_byte(unsigned int address)
@ -175,7 +175,7 @@ static void cart_prot_write_word(unsigned int address, unsigned int data)
}
/*--------------------------------------------------------------------------*/
/* cartridge hardware initialization */
/* ROM/RAM cartridge initialization */
/*--------------------------------------------------------------------------*/
void cd_cart_init(void)
{
@ -184,25 +184,31 @@ void cd_cart_init(void)
/* System boot mode */
if (scd.cartridge.boot)
{
/* disable backup RAM Cart when booting from cartridge (Mode 1) */
/* disable backup RAM cartridge when booting from cartridge (Mode 1) */
scd.cartridge.id = 0;
}
else
{
/* enable 512K backup RAM Cart when booting from CD (Mode 2) */
/* enable 512K backup RAM cartridge when booting from CD (Mode 2) */
scd.cartridge.id = 6;
}
/* RAM cart enabled ? */
/* RAM cartridge enabled ? */
if (scd.cartridge.id)
{
/* cartridge RAM size mask */
/* disable cartridge backup memory */
memset(&sram, 0, sizeof (T_SRAM));
/* clear backup RAM */
memset(scd.cartridge.area, 0x00, sizeof(scd.cartridge.area));
/* backup RAM size mask */
scd.cartridge.mask = (1 << (scd.cartridge.id + 13)) - 1;
/* enable cartridge RAM write access */
/* enable RAM cartridge write access */
scd.cartridge.prot = 1;
/* cartridge ID register (read-only) */
/* RAM cartridge ID register (read-only) */
for (i=0x40; i<0x60; i++)
{
m68k.memory_map[i].base = NULL;
@ -214,7 +220,7 @@ void cd_cart_init(void)
zbank_memory_map[i].write = zbank_unused_w;
}
/* cartridge RAM */
/* RAM cartridge memory */
for (i=0x60; i<0x70; i++)
{
m68k.memory_map[i].base = NULL;
@ -226,7 +232,7 @@ void cd_cart_init(void)
zbank_memory_map[i].write = cart_ram_write_byte;
}
/* cartridge write protection register */
/* RAM cartridge write protection register */
for (i=0x70; i<0x80; i++)
{
m68k.memory_map[i].base = NULL;
@ -240,19 +246,22 @@ void cd_cart_init(void)
}
else
{
/* $000000-$3FFFFF (boot from cartridge) or $400000-$7FFFFF (boot from CD) */
uint8 base = scd.cartridge.boot ^ 0x40;
/* initialize ROM cartridge */
md_cart_init();
/* cartridge ROM */
for (i=base; i<base+0x40; i++)
/* when booting from CD, cartridge is mapped to $400000-$7FFFFF */
if (!scd.cartridge.boot)
{
m68k.memory_map[i].base = scd.cartridge.area + ((i & 0x3f) << 16);
m68k.memory_map[i].read8 = NULL;
m68k.memory_map[i].read16 = NULL;
m68k.memory_map[i].write8 = m68k_unused_8_w;
m68k.memory_map[i].write16 = m68k_unused_16_w;
zbank_memory_map[i].read = NULL;
zbank_memory_map[i].write = zbank_unused_w;
for (i=0; i<0x40; i++)
{
m68k.memory_map[i+0x40].base = m68k.memory_map[i].base;
m68k.memory_map[i+0x40].read8 = m68k.memory_map[i].read8;
m68k.memory_map[i+0x40].read16 = m68k.memory_map[i].read16;
m68k.memory_map[i+0x40].write8 = m68k.memory_map[i].write8;
m68k.memory_map[i+0x40].write16 = m68k.memory_map[i].write16;
zbank_memory_map[i+0x40].read = zbank_memory_map[i].read;
zbank_memory_map[i+0x40].write = zbank_memory_map[i].write;
}
}
}
}
}

View File

@ -1,6 +1,6 @@
/***************************************************************************************
* Genesis Plus
* CD cartridge (external RAM or ROM)
* CD compatible ROM/RAM cartridge support
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
@ -36,14 +36,15 @@
*
****************************************************************************************/
/* CD cartridge (backup RAM or game) */
/* CD compatible ROM/RAM cartridge */
typedef struct
{
uint8 boot; /* boot mode (0x00: boot from CD, 0x40: boot from cartridge) */
uint8 id; /* cartridge ID (indicates RAM size, 0 if disabled) */
uint8 prot; /* cartridge RAM write protection */
uint32 mask; /* cartridge RAM size mask */
uint8 area[0x400000]; /* cartridge ROM/RAM area (4MB) */
uint8 area[0x810000]; /* cartridge ROM/RAM area (max. 8MB + 64KB backup) */
uint8 boot; /* cartridge boot mode (0x00: boot from CD with ROM/RAM cartridge enabled, 0x40: boot from ROM cartridge with CD enabled) */
uint8 id; /* RAM cartridge ID (related to RAM size, 0 if disabled) */
uint8 prot; /* RAM cartridge write protection */
uint32 mask; /* RAM cartridge size mask */
} cd_cart_t;
/* Function prototypes */

View File

@ -111,6 +111,75 @@ void cdc_reset(void)
}
}
int cdc_context_save(uint8 *state)
{
uint8 tmp8;
int bufferptr = 0;
if (cdc.dma_w == pcm_ram_dma_w)
{
tmp8 = 1;
}
else if (cdc.dma_w == prg_ram_dma_w)
{
tmp8 = 2;
}
else if (cdc.dma_w == word_ram_0_dma_w)
{
tmp8 = 3;
}
else if (cdc.dma_w == word_ram_1_dma_w)
{
tmp8 = 4;
}
else if (cdc.dma_w == word_ram_2M_dma_w)
{
tmp8 = 5;
}
else
{
tmp8 = 0;
}
save_param(&cdc, sizeof(cdc));
save_param(&tmp8, 1);
return bufferptr;
}
int cdc_context_load(uint8 *state)
{
uint8 tmp8;
int bufferptr = 0;
load_param(&cdc, sizeof(cdc));
load_param(&tmp8, 1);
switch (tmp8)
{
case 1:
cdc.dma_w = pcm_ram_dma_w;
break;
case 2:
cdc.dma_w = prg_ram_dma_w;
break;
case 3:
cdc.dma_w = word_ram_0_dma_w;
break;
case 4:
cdc.dma_w = word_ram_1_dma_w;
break;
case 5:
cdc.dma_w = word_ram_2M_dma_w;
break;
default:
cdc.dma_w = 0;
break;
}
return bufferptr;
}
void cdc_dma_update(void)
{
/* maximal transfer length */
@ -207,7 +276,7 @@ int cdc_decoder_update(uint32 header)
*(uint32 *)(cdc.ram + offset) = header;
/* write CDD block data (2048 bytes) */
cdd_read(cdc.ram + 4 + offset);
cdd_read_data(cdc.ram + 4 + offset);
/* take care of buffer overrun */
if (offset > (0x4000 - 2048 - 4))

View File

@ -60,6 +60,8 @@ typedef struct
/* Function prototypes */
extern void cdc_init(void);
extern void cdc_reset(void);
extern int cdc_context_save(uint8 *state);
extern int cdc_context_load(uint8 *state);
extern void cdc_dma_update(void);
extern int cdc_decoder_update(uint32 header);
extern void cdc_reg_w(unsigned char data);

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/***************************************************************************************
* Genesis Plus
* CD drive processor
* CD drive processor & CD-DA fader
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
@ -38,15 +38,29 @@
#ifndef _HW_CDD_
#define _HW_CDD_
#include "blip_buf.h"
#define cdd scd.cdd_hw
#define CD_MAX_TRACKS 99
/* CDD status */
#define NO_DISC 0x00
#define CD_PLAY 0x01
#define CD_SEEK 0x02
#define CD_SCAN 0x03
#define CD_READY 0x04
#define CD_OPEN 0x05
#define CD_STOP 0x09
/* CD blocks scanning speed */
#define CD_SCAN_SPEED 10
#define CD_MAX_TRACKS 100
/* CD track */
typedef struct
{
FILE *fd;
int offset;
int start;
int end;
} track_t;
@ -68,17 +82,22 @@ typedef struct
int index;
int lba;
int scanOffset;
int volume;
uint8 status;
uint16 sectorSize;
toc_t toc;
int16 audio[2];
} cdd_t;
/* Function prototypes */
extern void cdd_init(void);
extern void cdd_init(blip_t* left, blip_t* right);
extern void cdd_reset(void);
extern void cdd_load(char *filename, int type_bin);
extern int cdd_context_save(uint8 *state);
extern int cdd_context_load(uint8 *state);
extern int cdd_load(char *filename, char *header);
extern void cdd_unload(void);
extern void cdd_read(uint8 *dst);
extern void cdd_read_data(uint8 *dst);
extern void cdd_read_audio(unsigned int samples);
extern void cdd_update(void);
extern void cdd_process(void);

View File

@ -425,6 +425,50 @@ void gfx_reset(void)
gfx.cycles = 0;
}
int gfx_context_save(uint8 *state)
{
uint32 tmp32;
int bufferptr = 0;
save_param(&gfx.cycles, sizeof(gfx.cycles));
save_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
save_param(&gfx.dotMask, sizeof(gfx.dotMask));
save_param(&gfx.stampShift, sizeof(gfx.stampShift));
save_param(&gfx.mapShift, sizeof(gfx.mapShift));
save_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
save_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
tmp32 = (uint8 *)(gfx.tracePtr) - scd.word_ram_2M;
save_param(&tmp32, 4);
tmp32 = (uint8 *)(gfx.mapPtr) - scd.word_ram_2M;
save_param(&tmp32, 4);
return bufferptr;
}
int gfx_context_load(uint8 *state)
{
uint32 tmp32;
int bufferptr = 0;
load_param(&gfx.cycles, sizeof(gfx.cycles));
load_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
load_param(&gfx.dotMask, sizeof(gfx.dotMask));
load_param(&gfx.stampShift, sizeof(gfx.stampShift));
load_param(&gfx.mapShift, sizeof(gfx.mapShift));
load_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
load_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
load_param(&tmp32, 4);
gfx.tracePtr = (uint16 *)(scd.word_ram_2M + tmp32);
load_param(&tmp32, 4);
gfx.mapPtr = (uint16 *)(scd.word_ram_2M + tmp32);
return bufferptr;
}
INLINE void gfx_render(uint32 bufferIndex, uint32 width)
{
uint8 pixel_in, pixel_out;

View File

@ -108,6 +108,8 @@ extern void cell_ram_1_write8(unsigned int address, unsigned int data);
/***************************************************************/
extern void gfx_init(void);
extern void gfx_reset(void);
extern int gfx_context_save(uint8 *state);
extern int gfx_context_load(uint8 *state);
extern void gfx_start(unsigned int base, int cycles);
extern void gfx_update(int cycles);

View File

@ -36,29 +36,24 @@
*
****************************************************************************************/
#include "shared.h"
#include "blip.h"
#define PCM_MCLOCKS_PER_SAMPLE (384 * 4)
#define PCM_SCYCLES_RATIO (384 * 4)
#define pcm scd.pcm_hw
static struct blip_buffer_t* blip[2];
static blip_t* blip[2];
void pcm_init(double clock, double samplerate)
void pcm_init(blip_t* left, blip_t* right)
{
pcm_shutdown();
/* number of SCD master clocks run per second */
double mclk = snd.frame_rate ? (SCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * snd.frame_rate) : SCD_CLOCK;
/* allocate blip buffers */
blip[0] = blip_alloc(clock, samplerate * PCM_MCLOCKS_PER_SAMPLE, samplerate / 4);
blip[1] = blip_alloc(clock, samplerate * PCM_MCLOCKS_PER_SAMPLE, samplerate / 4);
}
void pcm_shutdown(void)
{
/* desallocate blip buffers */
if (blip[0]) blip_free(blip[0]);
if (blip[1]) blip_free(blip[1]);
blip[0] = blip[1] = 0;
/* PCM chips is running at original rate and is synchronized with SUB-CPU */
/* Chip output is resampled to desired rate using Blip Buffer. */
blip[0] = left;
blip[1] = right;
blip_set_rates(left, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);
blip_set_rates(right, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);
}
void pcm_reset(void)
@ -72,11 +67,48 @@ void pcm_reset(void)
/* reset master clocks counter */
pcm.cycles = 0;
/* clear blip delta buffers */
/* clear blip buffers */
blip_clear(blip[0]);
blip_clear(blip[1]);
}
int pcm_context_save(uint8 *state)
{
uint8 tmp8;
int bufferptr = 0;
tmp8 = (scd.pcm_hw.bank - scd.pcm_hw.ram) >> 12;
save_param(scd.pcm_hw.chan, sizeof(scd.pcm_hw.chan));
save_param(scd.pcm_hw.out, sizeof(scd.pcm_hw.out));
save_param(&tmp8, 1);
save_param(&scd.pcm_hw.enabled, sizeof(scd.pcm_hw.enabled));
save_param(&scd.pcm_hw.status, sizeof(scd.pcm_hw.status));
save_param(&scd.pcm_hw.index, sizeof(scd.pcm_hw.index));
save_param(scd.pcm_hw.ram, sizeof(scd.pcm_hw.ram));
return bufferptr;
}
int pcm_context_load(uint8 *state)
{
uint8 tmp8;
int bufferptr = 0;
load_param(scd.pcm_hw.chan, sizeof(scd.pcm_hw.chan));
load_param(scd.pcm_hw.out, sizeof(scd.pcm_hw.out));
load_param(&tmp8, 1);
scd.pcm_hw.bank = &scd.pcm_hw.ram[(tmp8 & 0x0f) << 12];
load_param(&scd.pcm_hw.enabled, sizeof(scd.pcm_hw.enabled));
load_param(&scd.pcm_hw.status, sizeof(scd.pcm_hw.status));
load_param(&scd.pcm_hw.index, sizeof(scd.pcm_hw.index));
load_param(scd.pcm_hw.ram, sizeof(scd.pcm_hw.ram));
return bufferptr;
}
void pcm_run(unsigned int length)
{
#ifdef LOG_PCM
@ -148,14 +180,14 @@ void pcm_run(unsigned int length)
/* check if PCM left output changed */
if (pcm.out[0] != l)
{
blip_add(blip[0], i, l-pcm.out[0]);
blip_add_delta_fast(blip[0], i, l-pcm.out[0]);
pcm.out[0] = l;
}
/* check if PCM right output changed */
if (pcm.out[1] != r)
{
blip_add(blip[1], i, r-pcm.out[1]);
blip_add_delta_fast(blip[1], i, r-pcm.out[1]);
pcm.out[1] = r;
}
}
@ -165,14 +197,14 @@ void pcm_run(unsigned int length)
/* check if PCM left output changed */
if (pcm.out[0])
{
blip_add(blip[0], 0, -pcm.out[0]);
blip_add_delta_fast(blip[0], 0, -pcm.out[0]);
pcm.out[0] = 0;
}
/* check if PCM right output changed */
if (pcm.out[1])
{
blip_add(blip[1], 0, -pcm.out[1]);
blip_add_delta_fast(blip[1], 0, -pcm.out[1]);
pcm.out[1] = 0;
}
}
@ -182,20 +214,19 @@ void pcm_run(unsigned int length)
blip_end_frame(blip[1], length);
/* update PCM master clock counter */
pcm.cycles += length * PCM_MCLOCKS_PER_SAMPLE;
pcm.cycles += length * PCM_SCYCLES_RATIO;
}
void pcm_update(short *buffer, int length)
void pcm_update(unsigned int samples)
{
/* get number of internal clocks (samples) needed */
unsigned int clocks = blip_clocks_needed(blip[0], length);
unsigned int clocks = blip_clocks_needed(blip[0], samples);
/* run PCM chip */
pcm_run(clocks);
/* resample to output stereo buffer */
blip_read_samples(blip[0], buffer, 1);
blip_read_samples(blip[1], buffer + 1, 1);
if (clocks > 0)
{
pcm_run(clocks);
}
/* reset PCM master clocks counter */
pcm.cycles = 0;
@ -208,7 +239,7 @@ void pcm_write(unsigned int address, unsigned char data)
if (clocks > 0)
{
/* number of internal clocks (samples) to run */
clocks = (clocks + PCM_MCLOCKS_PER_SAMPLE - 1) / PCM_MCLOCKS_PER_SAMPLE;
clocks = (clocks + PCM_SCYCLES_RATIO - 1) / PCM_SCYCLES_RATIO;
pcm_run(clocks);
}
@ -332,7 +363,7 @@ unsigned char pcm_read(unsigned int address)
if (clocks > 0)
{
/* number of internal clocks (samples) to run */
clocks = (clocks + PCM_MCLOCKS_PER_SAMPLE - 1) / PCM_MCLOCKS_PER_SAMPLE;
clocks = (clocks + PCM_SCYCLES_RATIO - 1) / PCM_SCYCLES_RATIO;
pcm_run(clocks);
}

View File

@ -38,6 +38,8 @@
#ifndef _CD_PCM_
#define _CD_PCM_
#include "blip_buf.h"
/* PCM channel */
typedef struct
{
@ -63,10 +65,11 @@ typedef struct
} pcm_t;
/* Function prototypes */
extern void pcm_init(double clock, double samplerate);
extern void pcm_shutdown(void);
extern void pcm_init(blip_t* left, blip_t* right);
extern void pcm_reset(void);
extern void pcm_update(short *buffer, int length);
extern int pcm_context_save(uint8 *state);
extern int pcm_context_load(uint8 *state);
extern void pcm_update(unsigned int samples);
extern void pcm_write(unsigned int address, unsigned char data);
extern unsigned char pcm_read(unsigned int address);
extern void pcm_ram_dma_w(unsigned int words);

View File

@ -276,7 +276,7 @@ static unsigned int scd_read_byte(unsigned int address)
{
unsigned int data = cdc_reg_r();
#ifdef LOG_CDC
error("CDC register %X read 0x%02X (%X) ", scd.regs[0x04>>1].byte.l & 0x0F, data, s68k.pc);
error("CDC register %X read 0x%02X (%X)\n", scd.regs[0x04>>1].byte.l & 0x0F, data, s68k.pc);
#endif
return data;
}
@ -1037,6 +1037,9 @@ void scd_init(void)
/* 0x00: boot from CD (Mode 2), 0x40: boot from cartridge (Mode 1) */
uint8 base = scd.cartridge.boot;
/* $400000-$7FFFFF (resp. $000000-$3FFFFF): cartridge area (4MB) */
cd_cart_init();
/* $000000-$1FFFFF (resp. $400000-$5FFFFF): CD memory area */
for (i=base; i<base+0x20; i++)
{
@ -1088,9 +1091,6 @@ void scd_init(void)
zbank_memory_map[i].read = zbank_unused_r;
zbank_memory_map[i].write = zbank_unused_w;
}
/* $400000-$7FFFFF (resp. $000000-$3FFFFF): cartridge area (4MB) */
cd_cart_init();
/****************************************************************/
/* SUB-CPU memory map ($000000-$FFFFFF) */
@ -1147,20 +1147,14 @@ void scd_init(void)
s68k.memory_map[0xff].write16 = scd_write_word;
/* Initialize CD hardware */
cdd_init();
cdc_init();
gfx_init();
pcm_init(SCD_CLOCK, snd.sample_rate);
/* Clear RAM */
memset(scd.prg_ram, 0x00, sizeof(scd.prg_ram));
memset(scd.word_ram, 0x00, sizeof(scd.word_ram));
memset(scd.word_ram_2M, 0x00, sizeof(scd.word_ram_2M));
}
void scd_shutdown(void)
{
pcm_shutdown();
memset(scd.bram, 0x00, sizeof(scd.bram));
}
void scd_reset(int hard)
@ -1251,7 +1245,7 @@ void scd_update(unsigned int cycles)
/* reload CDD cycle counter */
cdd.cycles -= (500000 * 4);
/* update CDD */
/* update CDD sector */
cdd_update();
/* check if a new CDD command has been processed */
@ -1307,7 +1301,6 @@ void scd_update(unsigned int cycles)
int scd_context_save(uint8 *state)
{
uint8 tmp8;
uint16 tmp16;
uint32 tmp32;
int bufferptr = 0;
@ -1320,62 +1313,16 @@ int scd_context_save(uint8 *state)
save_param(&scd.dmna, sizeof(scd.dmna));
/* GFX processor */
save_param(&gfx.cycles, sizeof(gfx.cycles));
save_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
save_param(&gfx.dotMask, sizeof(gfx.dotMask));
save_param(&gfx.stampShift, sizeof(gfx.stampShift));
save_param(&gfx.mapShift, sizeof(gfx.mapShift));
save_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
save_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
tmp32 = (uint8 *)(gfx.tracePtr) - scd.word_ram_2M;
save_param(&tmp32, 4);
tmp32 = (uint8 *)(gfx.mapPtr) - scd.word_ram_2M;
save_param(&tmp32, 4);
bufferptr += gfx_context_save(&state[bufferptr]);
/* CD Data controller */
save_param(&cdc, sizeof(cdc));
if (cdc.dma_w == pcm_ram_dma_w)
{
tmp8 = 1;
}
else if (cdc.dma_w == prg_ram_dma_w)
{
tmp8 = 2;
}
else if (cdc.dma_w == word_ram_0_dma_w)
{
tmp8 = 3;
}
else if (cdc.dma_w == word_ram_1_dma_w)
{
tmp8 = 4;
}
else if (cdc.dma_w == word_ram_2M_dma_w)
{
tmp8 = 5;
}
else
{
tmp8 = 0;
}
save_param(&tmp8, 1);
bufferptr += cdc_context_save(&state[bufferptr]);
/* CD Drive processor */
save_param(&cdd.cycles, sizeof(cdd.cycles));
save_param(&cdd.latency, sizeof(cdd.latency));
save_param(&cdd.index, sizeof(cdd.index));
save_param(&cdd.lba, sizeof(cdd.lba));
save_param(&cdd.status, sizeof(cdd.status));
bufferptr += cdd_context_save(&state[bufferptr]);
/* PCM chip */
save_param(scd.pcm_hw.chan, sizeof(scd.pcm_hw.chan));
save_param(scd.pcm_hw.out, sizeof(scd.pcm_hw.out));
tmp8 = (scd.pcm_hw.bank - scd.pcm_hw.ram) >> 12;
save_param(&tmp8, sizeof(tmp8));
save_param(&scd.pcm_hw.enabled, sizeof(scd.pcm_hw.enabled));
save_param(&scd.pcm_hw.status, sizeof(scd.pcm_hw.status));
save_param(&scd.pcm_hw.index, sizeof(scd.pcm_hw.index));
save_param(scd.pcm_hw.ram, sizeof(scd.pcm_hw.ram));
bufferptr += pcm_context_save(&state[bufferptr]);
/* PRG-RAM */
save_param(scd.prg_ram, sizeof(scd.prg_ram));
@ -1427,13 +1374,18 @@ int scd_context_save(uint8 *state)
tmp32 = s68k_get_reg(M68K_REG_USP); save_param(&tmp32, 4);
tmp32 = s68k_get_reg(M68K_REG_ISP); save_param(&tmp32, 4);
/* bootable MD cartridge */
if (scd.cartridge.boot)
{
bufferptr += md_cart_context_save(&state[bufferptr]);
}
return bufferptr;
}
int scd_context_load(uint8 *state)
{
int i;
uint8 tmp8;
uint16 tmp16;
uint32 tmp32;
int bufferptr = 0;
@ -1446,70 +1398,16 @@ int scd_context_load(uint8 *state)
load_param(&scd.dmna, sizeof(scd.dmna));
/* GFX processor */
load_param(&gfx.cycles, sizeof(gfx.cycles));
load_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
load_param(&gfx.dotMask, sizeof(gfx.dotMask));
load_param(&gfx.stampShift, sizeof(gfx.stampShift));
load_param(&gfx.mapShift, sizeof(gfx.mapShift));
load_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
load_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
load_param(&tmp32, 4);
gfx.tracePtr = (uint16 *)(scd.word_ram_2M + tmp32);
load_param(&tmp32, 4);
gfx.mapPtr = (uint16 *)(scd.word_ram_2M + tmp32);
bufferptr += gfx_context_load(&state[bufferptr]);
/* CD Data controller */
load_param(&cdc, sizeof(cdc));
load_param(&tmp8, 1);
switch (tmp8)
{
case 1:
cdc.dma_w = pcm_ram_dma_w;
break;
case 2:
cdc.dma_w = prg_ram_dma_w;
break;
case 3:
cdc.dma_w = word_ram_0_dma_w;
break;
case 4:
cdc.dma_w = word_ram_1_dma_w;
break;
case 5:
cdc.dma_w = word_ram_2M_dma_w;
break;
default:
cdc.dma_w = 0;
break;
}
bufferptr += cdc_context_load(&state[bufferptr]);
/* CD Drive processor */
load_param(&cdd.cycles, sizeof(cdd.cycles));
load_param(&cdd.latency, sizeof(cdd.latency));
load_param(&cdd.index, sizeof(cdd.index));
load_param(&cdd.lba, sizeof(cdd.lba));
load_param(&cdd.status, sizeof(cdd.status));
if (!cdd.index)
{
if (cdd.lba < 0)
{
fseek(cdd.toc.tracks[0].fd, 0, SEEK_SET);
}
else
{
fseek(cdd.toc.tracks[0].fd, cdd.lba * cdd.sectorSize, SEEK_SET);
}
}
bufferptr += cdd_context_load(&state[bufferptr]);
/* PCM chip */
load_param(scd.pcm_hw.chan, sizeof(scd.pcm_hw.chan));
load_param(scd.pcm_hw.out, sizeof(scd.pcm_hw.out));
load_param(&tmp8, sizeof(tmp8));
scd.pcm_hw.bank = &scd.pcm_hw.ram[(tmp8 & 0x0f) << 12];
load_param(&scd.pcm_hw.enabled, sizeof(scd.pcm_hw.enabled));
load_param(&scd.pcm_hw.status, sizeof(scd.pcm_hw.status));
load_param(&scd.pcm_hw.index, sizeof(scd.pcm_hw.index));
load_param(scd.pcm_hw.ram, sizeof(scd.pcm_hw.ram));
bufferptr += pcm_context_load(&state[bufferptr]);
/* PRG-RAM */
load_param(scd.prg_ram, sizeof(scd.prg_ram));
@ -1676,6 +1574,12 @@ int scd_context_load(uint8 *state)
load_param(&tmp32, 4); s68k_set_reg(M68K_REG_USP,tmp32);
load_param(&tmp32, 4); s68k_set_reg(M68K_REG_ISP,tmp32);
/* bootable MD cartridge hardware */
if (scd.cartridge.boot)
{
bufferptr += md_cart_context_load(&state[bufferptr]);
}
return bufferptr;
}

View File

@ -55,7 +55,8 @@
/* CD hardware */
typedef struct
{
uint8 bootrom[0x20000]; /* 128K internal BOOTROM */
cd_cart_t cartridge; /* ROM/RAM Cartridge */
uint8 bootrom[0x20000]; /* 128K internal BOOT ROM */
uint8 prg_ram[0x80000]; /* 512K PRG-RAM */
uint8 word_ram[2][0x20000]; /* 2 x 128K Word RAM (1M mode) */
uint8 word_ram_2M[0x40000]; /* 256K Word RAM (2M mode) */
@ -69,12 +70,10 @@ typedef struct
cdc_t cdc_hw; /* CD data controller */
cdd_t cdd_hw; /* CD drive processor */
pcm_t pcm_hw; /* PCM chip */
cd_cart_t cartridge; /* Cartridge hardware */
} cd_hw_t;
/* Function prototypes */
extern void scd_init(void);
extern void scd_shutdown(void);
extern void scd_reset(int hard);
extern void scd_update(unsigned int cycles);
extern int scd_context_load(uint8 *state);

View File

@ -223,14 +223,6 @@ void gen_init(void)
}
}
void gen_shutdown(void)
{
if (system_hw == SYSTEM_MCD)
{
scd_shutdown();
}
}
void gen_reset(int hard_reset)
{
/* System Reset */

View File

@ -65,7 +65,6 @@ extern uint8 pico_current;
/* Function prototypes */
extern void gen_init(void);
extern void gen_reset(int hard_reset);
extern void gen_shutdown(void);
extern void gen_tmss_w(unsigned int offset, unsigned int data);
extern void gen_bankswitch_w(unsigned int data);
extern unsigned int gen_bankswitch_r(void);

View File

@ -107,7 +107,6 @@ void config_default(void)
config.lg = 1.0;
config.mg = 1.0;
config.hg = 1.0;
config.rolloff = 0.990;
config.dac_bits = 14;
config.ym2413 = 2; /* AUTO */
@ -129,6 +128,7 @@ void config_default(void)
config.yscale = 0;
config.aspect = 1;
config.overscan = 3; /* FULL */
config.gg_extra = 0;
config.ntsc = 0;
config.vsync = 1; /* AUTO */

View File

@ -40,7 +40,7 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
#define CONFIG_VERSION "GENPLUS-GX 1.7.0"
#define CONFIG_VERSION "GENPLUS-GX 1.7.1"
/****************************************************************************
* Config Option
@ -62,7 +62,6 @@ typedef struct
int16 lg;
int16 mg;
int16 hg;
float rolloff;
uint8 system;
uint8 region_detect;
uint8 master_clock;
@ -76,6 +75,7 @@ typedef struct
uint8 invert_mouse;
uint8 gun_cursor[2];
uint8 overscan;
uint8 gg_extra;
uint8 ntsc;
uint8 vsync;
uint8 render;

View File

@ -51,6 +51,8 @@
#include <ogc/dvd.h>
#endif
char rom_filename[256];
/* device root directories */
#ifdef HW_RVL
static const char rootdir[TYPE_RECENT][10] = {"sd:/","usb:/","dvd:/"};
@ -208,7 +210,10 @@ int ParseDirectory(void)
/* list entries */
while ((entry != NULL)&& (nbfiles < MAXFILES))
{
if (entry->d_name[0] != '.')
/* filter entries */
if ((entry->d_name[0] != '.')
&& strncasecmp(".wav", &entry->d_name[strlen(entry->d_name) - 4], 4)
&& strncasecmp(".mp3", &entry->d_name[strlen(entry->d_name) - 4], 4))
{
memset(&filelist[nbfiles], 0, sizeof (FILEENTRIES));
sprintf(filelist[nbfiles].filename,"%s",entry->d_name);
@ -235,13 +240,13 @@ int ParseDirectory(void)
/****************************************************************************
* LoadFile
*
* This function will load a BIN, SMD or ZIP file into the ROM buffer.
* This function will load a game file into the ROM buffer.
* This functions return the actual size of data copied into the buffer
*
****************************************************************************/
int LoadFile(int selection)
{
int filetype;
int size = 0, filetype;
char filename[MAXPATHLEN];
/* file path */
@ -262,16 +267,36 @@ int LoadFile(int selection)
}
}
/* try to load file */
int size = load_rom(filename);
/* open message box */
GUI_MsgBoxOpen("Information", "Loading game...", 1);
/* check if virtual CD tray was open */
if ((system_hw == SYSTEM_MCD) && (cdd.status == CD_OPEN))
{
/* swap CD image file */
size = cdd_load(filename, (char *)(cdc.ram));
/* update CD header informations */
if (!scd.cartridge.boot)
{
getrominfo((char *)(cdc.ram));
}
}
/* no CD image file loaded */
if (!size)
{
/* close CD tray to force system reset */
cdd.status = CD_STOP;
/* load game file */
size = load_rom(filename);
}
if (size > 0)
{
/* auto-save previous game state */
if (config.s_auto & 2)
{
slot_autosave(config.s_default,config.s_device);
}
slot_autosave(config.s_default,config.s_device);
/* update pathname for screenshot, save & cheat files */
if (romtype & SYSTEM_SMS)
@ -316,10 +341,14 @@ int LoadFile(int selection)
/* recent file list may have changed */
if (deviceType == TYPE_RECENT) deviceType = -1;
/* close message box */
GUI_MsgBoxClose();
/* valid image has been loaded */
return 1;
}
GUI_WaitPrompt("Error", "Unable to load game");
return 0;
}

View File

@ -69,4 +69,6 @@ extern int UpdateDirectory(bool go_up, char *filename);
extern int ParseDirectory(void);
extern int LoadFile(int selection);
extern char rom_filename[256];
#endif

View File

@ -175,10 +175,14 @@ void slot_autoload(int slot, int device)
memcpy(scd.cartridge.area + scd.cartridge.mask + 1 - 0x40, brm_format, 0x40);
}
}
}
/* configurable SRAM & State auto-saving */
if ((slot && !(config.s_auto & 2)) || (!slot && !(config.s_auto & 1)))
{
return;
}
if (strlen(rom_filename))
{
SILENT = 1;
@ -244,7 +248,11 @@ void slot_autosave(int slot, int device)
}
}
}
}
/* configurable SRAM & State auto-saving */
if ((slot && !(config.s_auto & 2)) || (!slot && !(config.s_auto & 1)))
{
return;
}
@ -396,13 +404,13 @@ int slot_load(int slot, int device)
}
else
{
if (!sram.on || (system_hw == SYSTEM_MCD))
if (!sram.on)
{
GUI_WaitPrompt("Error","SRAM is disabled !");
GUI_WaitPrompt("Error","Backup RAM is disabled !");
return 0;
}
GUI_MsgBoxOpen("Information","Loading SRAM ...",1);
GUI_MsgBoxOpen("Information","Loading Backup RAM ...",1);
}
/* Device Type */
@ -595,20 +603,20 @@ int slot_save(int slot, int device)
else
{
/* only save if SRAM is enabled */
if (!sram.on || (system_hw == SYSTEM_MCD))
if (!sram.on)
{
GUI_WaitPrompt("Error","SRAM disabled !");
GUI_WaitPrompt("Error","Backup RAM disabled !");
return 0;
}
/* only save if SRAM has been modified */
if (crc32(0, &sram.sram[0], 0x10000) == sram.crc)
{
GUI_WaitPrompt("Warning","SRAM not modified !");
GUI_WaitPrompt("Warning","Backup RAM not modified !");
return 0;
}
GUI_MsgBoxOpen("Information","Saving SRAM ...",1);
GUI_MsgBoxOpen("Information","Saving Backup RAM ...",1);
/* allocate buffer */
buffer = (u8 *)memalign(32, 0x10000);
@ -816,7 +824,7 @@ int slot_save(int slot, int device)
/* Close message box */
GUI_MsgBoxClose();
}
}
return 1;
}

View File

@ -137,7 +137,7 @@ int load_archive(char *filename, unsigned char *buffer, int maxsize, char *exten
}
sprintf (msg, "Unzipping %d bytes ...", size);
GUI_MsgBoxOpen("Information",msg,1);
GUI_MsgBoxUpdate("Information",msg);
/* Initialize zip stream */
z_stream zs;
@ -227,7 +227,7 @@ int load_archive(char *filename, unsigned char *buffer, int maxsize, char *exten
}
sprintf((char *)msg,"Loading %d bytes ...", size);
GUI_MsgBoxOpen("Information", (char *)msg, 1);
GUI_MsgBoxUpdate("Information", (char *)msg);
/* filename extension */
if (extension)
@ -253,7 +253,6 @@ int load_archive(char *filename, unsigned char *buffer, int maxsize, char *exten
fclose(fd);
/* Return loaded ROM size */
GUI_MsgBoxClose();
SILENT = 0;
return size;
}

View File

@ -38,6 +38,7 @@
****************************************************************************************/
#include "shared.h"
#include "file_load.h"
#include "cheats.h"
#include "font.h"
#include "gui.h"

View File

@ -495,28 +495,40 @@ int FileSelector(int type)
/* go back one page */
else if (p & (PAD_TRIGGER_L | PAD_BUTTON_LEFT))
{
selection -= 10;
if (selection < 0)
selection = offset = 0;
if (selection < offset)
offset -= 10;
if (offset < 0)
offset = 0;
}
if (maxfiles >= 10)
{
selection -= 10;
if (selection < 0)
{
selection = offset = 0;
}
else if (selection < offset)
{
offset -= 10;
if (offset < 0) offset = 0;
}
}
}
/* go forward one page */
else if (p & (PAD_TRIGGER_R | PAD_BUTTON_RIGHT))
{
selection += 10;
if (selection > maxfiles - 1)
if (maxfiles >= 10)
{
selection = maxfiles - 1;
offset = selection - 10 + 1;
selection += 10;
if (selection > maxfiles - 1)
{
/* last page */
selection = maxfiles - 1;
offset = maxfiles - 10;
}
else if (selection >= (offset + 10))
{
/* next page */
offset += 10;
if (offset > (maxfiles - 10)) offset = maxfiles - 10;
}
}
if ((selection - offset) >= 10)
offset += 10;
if (offset > maxfiles - 10)
offset = maxfiles - 10;
}
/* quit */

View File

@ -44,7 +44,6 @@
#include "cheats.h"
#include "file_load.h"
#include "file_slot.h"
#include "md_eeprom.h"
#ifdef HW_RVL
#include <ogc/usbmouse.h>
@ -333,21 +332,20 @@ static gui_item items_options[5] =
};
/* Audio options */
static gui_item items_audio[13] =
static gui_item items_audio[12] =
{
{NULL,NULL,"Master System FM: AUTO", "Enable/disable YM2413 chip", 56,132,276,48},
{NULL,NULL,"High-Quality FM: ON", "Enable/disable YM2612/YM2413 resampling", 56,132,276,48},
{NULL,NULL,"FM Roll-off: 0.999", "Adjust FIR low-pass filtering", 56,132,276,48},
{NULL,NULL,"FM Resolution: MAX", "Adjust YM2612 DAC precision", 56,132,276,48},
{NULL,NULL,"FM Volume: 1.00", "Adjust YM2612/YM2413 output level", 56,132,276,48},
{NULL,NULL,"PSG Volume: 2.50", "Adjust SN76489 output level", 56,132,276,48},
{NULL,NULL,"PSG Noise Boost: OFF", "Boost SN76489 Noise Channel", 56,132,276,48},
{NULL,NULL,"Filtering: 3-BAND EQ", "Setup Audio filtering", 56,132,276,48},
{NULL,NULL,"Low Gain: 1.00", "Adjust EQ Low Band Gain", 56,132,276,48},
{NULL,NULL,"Mid Gain: 1.00", "Adjust EQ Mid Band Gain", 56,132,276,48},
{NULL,NULL,"High Gain: 1.00", "Adjust EQ High BandGain", 56,132,276,48},
{NULL,NULL,"Low Freq: 200 Hz", "Adjust EQ Lowest Frequency", 56,132,276,48},
{NULL,NULL,"High Freq: 20000 Hz", "Adjust EQ Highest Frequency", 56,132,276,48}
{NULL,NULL,"Master System FM: AUTO", "Enable/disable YM2413 chip", 56,132,276,48},
{NULL,NULL,"High-Quality FM: ON", "Adjust YM2612/YM2413 resampling quality", 56,132,276,48},
{NULL,NULL,"FM Resolution: MAX", "Adjust YM2612 DAC precision", 56,132,276,48},
{NULL,NULL,"FM Volume: 1.00", "Adjust YM2612/YM2413 output level", 56,132,276,48},
{NULL,NULL,"PSG Volume: 2.50", "Adjust SN76489 output level", 56,132,276,48},
{NULL,NULL,"PSG Noise Boost: OFF", "Boost SN76489 Noise Channel", 56,132,276,48},
{NULL,NULL,"Filtering: 3-BAND EQ", "Setup Audio filtering", 56,132,276,48},
{NULL,NULL,"Low Gain: 1.00", "Adjust EQ Low Band Gain", 56,132,276,48},
{NULL,NULL,"Mid Gain: 1.00", "Adjust EQ Mid Band Gain", 56,132,276,48},
{NULL,NULL,"High Gain: 1.00", "Adjust EQ High Band Gain", 56,132,276,48},
{NULL,NULL,"Low Freq: 200 Hz", "Adjust EQ Lowest Frequency", 56,132,276,48},
{NULL,NULL,"High Freq: 20000 Hz", "Adjust EQ Highest Frequency", 56,132,276,48}
};
/* System options */
@ -367,14 +365,14 @@ static gui_item items_system[10] =
/* Video options */
#ifdef HW_RVL
static gui_item items_video[11] =
static gui_item items_video[12] =
#else
static gui_item items_video[9] =
static gui_item items_video[10] =
#endif
{
{NULL,NULL,"Display: PROGRESSIVE", "Select video mode", 56,132,276,48},
{NULL,NULL,"TV mode: 50/60Hz", "Select video refresh rate", 56,132,276,48},
{NULL,NULL,"VSYNC: AUTO", "Enable/disable synchronization with Video Hardware", 56,132,276,48},
{NULL,NULL,"VSYNC: AUTO", "Enable/disable sync with Video Hardware", 56,132,276,48},
{NULL,NULL,"GX Bilinear Filter: OFF", "Enable/disable texture hardware filtering", 56,132,276,48},
#ifdef HW_RVL
{NULL,NULL,"VI Trap Filter: ON", "Enable/disable video hardware filtering", 56,132,276,48},
@ -382,6 +380,7 @@ static gui_item items_video[9] =
#endif
{NULL,NULL,"NTSC Filter: COMPOSITE", "Enable/disable NTSC software filtering", 56,132,276,48},
{NULL,NULL,"Borders: OFF", "Enable/disable overscan emulation", 56,132,276,48},
{NULL,NULL,"GG screen: ORIGINAL", "Enable/disable Game Gear extended screen", 56,132,276,48},
{NULL,NULL,"Aspect: ORIGINAL (4:3)", "Select display aspect ratio", 56,132,276,48},
{NULL,NULL,"Screen Position (+0,+0)", "Adjust display position", 56,132,276,48},
{NULL,NULL,"Screen Scaling (+0,+0)", "Adjust display scaling", 56,132,276,48}
@ -579,7 +578,7 @@ static gui_menu menu_video =
{
"Video Settings",
0,0,
9,4,6,0,
10,4,6,0,
items_video,
buttons_list,
bg_list,
@ -792,97 +791,56 @@ static void prefmenu ()
* Audio Settings menu
*
****************************************************************************/
static int update_snd_items(void)
{
gui_menu *m = &menu_audio;
gui_item *items = m->items;
int offset;
float fm_volume = (float)config.fm_preamp/100.0;
float psg_volume = (float)config.psg_preamp/100.0;
float rolloff = config.rolloff * 100.0;
float lg = (float)config.lg/100.0;
float mg = (float)config.mg/100.0;
float hg = (float)config.hg/100.0;
if (config.hq_fm)
{
sprintf (items[1].text, "High-Quality FM: ON");
sprintf (items[2].text, "FM Roll-off: %1.2f %%",rolloff);
strcpy (items[2].comment, "Adjust FIR low-pass filtering");
offset = 3;
}
else
{
sprintf (items[1].text, "High-Quality FM: OFF");
offset = 2;
}
strcpy(items[offset].comment, "Adjust YM2612 DAC precision");
strcpy(items[offset+1].comment, "Adjust YM2612/YM2413 output level");
strcpy(items[offset+2].comment, "Adjust SN76489 output level");
strcpy(items[offset+3].comment, "Boost SN76489 Noise Channel");
strcpy(items[offset+4].comment, "Configure Audio filtering");
if (config.dac_bits < 14)
sprintf (items[offset].text, "FM Resolution: %d bits", config.dac_bits);
else
sprintf (items[offset].text, "FM Resolution: MAX");
sprintf (items[offset+1].text, "FM Volume: %1.2f", fm_volume);
sprintf (items[offset+2].text, "PSG Volume: %1.2f", psg_volume);
sprintf (items[offset+3].text, "PSG Noise Boost: %s", config.psgBoostNoise ? "ON":"OFF");
if (config.filter == 2)
{
sprintf (items[offset+4].text, "Filtering: 3-BAND EQ");
sprintf (items[offset+5].text, "Low Gain: %1.2f", lg);
m->max_items = offset + 10;
}
else if (config.filter == 1)
{
sprintf (items[offset+4].text, "Filtering: LOW-PASS");
sprintf (items[offset+5].text, "Low-Pass Rate: %d %%", config.lp_range);
strcpy (items[offset+5].comment, "Adjust Low Pass filter");
m->max_items = offset + 6;
}
else
{
sprintf (items[offset+4].text, "Filtering: OFF");
m->max_items = offset + 5;
}
sprintf (items[offset+6].text, "Middle Gain: %1.2f", mg);
sprintf (items[offset+7].text, "High Gain: %1.2f", hg);
sprintf (items[offset+8].text, "Low Freq: %d", config.low_freq);
sprintf (items[offset+9].text, "High Freq: %d", config.high_freq);
strcpy (items[offset+5].comment, "Adjust EQ Low Band Gain");
strcpy (items[offset+6].comment, "Adjust EQ Mid Band Gain");
strcpy (items[offset+7].comment, "Adjust EQ High Band Gain");
strcpy (items[offset+8].comment, "Adjust EQ Lowest Frequency");
strcpy (items[offset+9].comment, "Adjust EQ Highest Frequency");
return offset;
}
static void soundmenu ()
{
int ret, quit = 0;
int reinit = 0;
gui_menu *m = &menu_audio;
gui_item *items = m->items;
float fm_volume = (float)config.fm_preamp/100.0;
float psg_volume = (float)config.psg_preamp/100.0;
float rolloff = config.rolloff * 100.0;
float lg = (float)config.lg/100.0;
float mg = (float)config.mg/100.0;
float hg = (float)config.hg/100.0;
int offset = update_snd_items();
gui_menu *m = &menu_audio;
gui_item *items = m->items;
if (config.ym2413 == 0) sprintf (items[0].text, "Master System FM: OFF");
else if (config.ym2413 == 1) sprintf (items[0].text, "Master System FM: ON");
else sprintf (items[0].text, "Master System FM: AUTO");
if (config.hq_fm) sprintf (items[1].text, "High-Quality FM: ON");
else sprintf (items[1].text, "High-Quality FM: OFF");
if (config.dac_bits < 14) sprintf (items[2].text, "FM Resolution: %d bits", config.dac_bits);
else sprintf (items[2].text, "FM Resolution: MAX");
sprintf (items[3].text, "FM Volume: %1.2f", fm_volume);
sprintf (items[4].text, "PSG Volume: %1.2f", psg_volume);
sprintf (items[5].text, "PSG Noise Boost: %s", config.psgBoostNoise ? "ON":"OFF");
if (config.filter == 2)
{
float lg = (float)config.lg/100.0;
float mg = (float)config.mg/100.0;
float hg = (float)config.hg/100.0;
sprintf(items[6].text, "Filtering: 3-BAND EQ");
sprintf(items[7].text, "Low Gain: %1.2f", lg);
strcpy(items[7].comment, "Adjust EQ Low Band Gain");
sprintf(items[8].text, "Middle Gain: %1.2f", mg);
sprintf(items[9].text, "High Gain: %1.2f", hg);
sprintf(items[10].text, "Low Freq: %d", config.low_freq);
sprintf(items[11].text, "High Freq: %d", config.high_freq);
m->max_items = 12;
}
else if (config.filter == 1)
{
sprintf (items[6].text, "Filtering: LOW-PASS");
sprintf (items[7].text, "Low-Pass Rate: %d %%", config.lp_range);
strcpy (items[7].comment, "Adjust Low Pass filter");
m->max_items = 8;
}
else
{
sprintf (items[6].text, "Filtering: OFF");
m->max_items = 7;
}
GUI_InitMenu(m);
GUI_SlideMenuTitle(m,strlen("Audio "));
@ -890,23 +848,6 @@ static void soundmenu ()
{
ret = GUI_RunMenu(m);
/* special case */
if (config.hq_fm)
{
if (ret == 2)
{
GUI_OptionBox(m,0,"FM Roll-off",(void *)&rolloff,0.1,95.0,99.9,0);
sprintf (items[2].text, "FM Roll-off: %1.2f %%",rolloff);
config.rolloff = rolloff / 100.0;
reinit = 1;
ret = 255;
}
else if (ret > 2)
{
ret--;
}
}
switch (ret)
{
case 0:
@ -924,10 +865,7 @@ static void soundmenu ()
sms_cart_init();
/* restore SRAM */
if (config.s_auto & 1)
{
slot_autoload(0,config.s_device);
}
slot_autoload(0,config.s_device);
}
break;
}
@ -935,8 +873,8 @@ static void soundmenu ()
case 1:
{
config.hq_fm ^= 1;
reinit = 1;
offset = update_snd_items();
if (config.hq_fm) sprintf (items[1].text, "High-Quality FM: ON");
else sprintf (items[1].text, "High-Quality FM: OFF");
break;
}
@ -944,16 +882,16 @@ static void soundmenu ()
{
config.dac_bits++;
if (config.dac_bits > 14) config.dac_bits = 7;
if (config.dac_bits < 14) sprintf (items[offset].text, "FM Resolution: %d bits", config.dac_bits);
else sprintf (items[offset].text, "FM Resolution: MAX");
reinit = 1;
if (config.dac_bits < 14) sprintf (items[2].text, "FM Resolution: %d bits", config.dac_bits);
else sprintf (items[2].text, "FM Resolution: MAX");
YM2612Config(config.dac_bits);
break;
}
case 3:
{
GUI_OptionBox(m,0,"FM Volume",(void *)&fm_volume,0.01,0.0,5.0,0);
sprintf (items[offset+1].text, "FM Volume: %1.2f", fm_volume);
sprintf (items[3].text, "FM Volume: %1.2f", fm_volume);
config.fm_preamp = (int)(fm_volume * 100.0 + 0.5);
break;
}
@ -961,16 +899,31 @@ static void soundmenu ()
case 4:
{
GUI_OptionBox(m,0,"PSG Volume",(void *)&psg_volume,0.01,0.0,5.0,0);
sprintf (items[offset+2].text, "PSG Volume: %1.2f", psg_volume);
sprintf (items[4].text, "PSG Volume: %1.2f", psg_volume);
config.psg_preamp = (int)(psg_volume * 100.0 + 0.5);
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
SN76489_Config(config.psg_preamp, config.psgBoostNoise, 0xff);
}
else
{
SN76489_Config(config.psg_preamp, config.psgBoostNoise, io_reg[6]);
}
break;
}
case 5:
{
config.psgBoostNoise ^= 1;
sprintf (items[offset+3].text, "PSG Noise Boost: %s", config.psgBoostNoise ? "ON":"OFF");
SN76489_BoostNoise(config.psgBoostNoise);
sprintf (items[5].text, "PSG Noise Boost: %s", config.psgBoostNoise ? "ON":"OFF");
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
SN76489_Config(config.psg_preamp, config.psgBoostNoise, 0xff);
}
else
{
SN76489_Config(config.psg_preamp, config.psgBoostNoise, io_reg[6]);
}
break;
}
@ -979,23 +932,24 @@ static void soundmenu ()
config.filter = (config.filter + 1) % 3;
if (config.filter == 2)
{
sprintf (items[offset+4].text, "Filtering: 3-BAND EQ");
sprintf (items[offset+5].text, "Low Gain: %1.2f", lg);
strcpy (items[offset+5].comment, "Adjust EQ Low Band Gain");
m->max_items = offset + 10;
float lg = (float)config.lg/100.0;
sprintf (items[6].text, "Filtering: 3-BAND EQ");
sprintf (items[7].text, "Low Gain: %1.2f", lg);
strcpy (items[7].comment, "Adjust EQ Low Band Gain");
m->max_items = 12;
audio_set_equalizer();
}
else if (config.filter == 1)
{
sprintf (items[offset+4].text, "Filtering: LOW-PASS");
sprintf (items[offset+5].text, "Low-Pass Rate: %d %%", config.lp_range);
strcpy (items[offset+5].comment, "Adjust Low Pass filter");
m->max_items = offset + 6;
sprintf (items[6].text, "Filtering: LOW-PASS");
sprintf (items[7].text, "Low-Pass Rate: %d %%", config.lp_range);
strcpy (items[7].comment, "Adjust Low Pass filter");
m->max_items = 8;
}
else
{
sprintf (items[offset+4].text, "Filtering: OFF");
m->max_items = offset + 5;
sprintf (items[6].text, "Filtering: OFF");
m->max_items = 7;
}
while ((m->offset + 4) > m->max_items)
@ -1011,12 +965,13 @@ static void soundmenu ()
if (config.filter == 1)
{
GUI_OptionBox(m,0,"Low-Pass Rate",(void *)&config.lp_range,1,0,100,1);
sprintf (items[offset+5].text, "Low-Pass Rate: %d %%", config.lp_range);
sprintf (items[7].text, "Low-Pass Rate: %d %%", config.lp_range);
}
else
{
float lg = (float)config.lg/100.0;
GUI_OptionBox(m,0,"Low Gain",(void *)&lg,0.01,0.0,2.0,0);
sprintf (items[offset+5].text, "Low Gain: %1.2f", lg);
sprintf (items[7].text, "Low Gain: %1.2f", lg);
config.lg = (int)(lg * 100.0);
audio_set_equalizer();
}
@ -1025,8 +980,9 @@ static void soundmenu ()
case 8:
{
GUI_OptionBox(m,0,"Middle Gain",(void *)&mg,0.01,0.0,2.0,0);
sprintf (items[offset+6].text, "Middle Gain: %1.2f", mg);
float mg = (float)config.mg/100.0;
GUI_OptionBox(m,0,"Middle Gain",(void *)&mg,0.01,0.0,2.0,0);
sprintf (items[8].text, "Middle Gain: %1.2f", mg);
config.mg = (int)(mg * 100.0);
audio_set_equalizer();
break;
@ -1034,8 +990,9 @@ static void soundmenu ()
case 9:
{
float hg = (float)config.hg/100.0;
GUI_OptionBox(m,0,"High Gain",(void *)&hg,0.01,0.0,2.0,0);
sprintf (items[offset+7].text, "High Gain: %1.2f", hg);
sprintf (items[9].text, "High Gain: %1.2f", hg);
config.hg = (int)(hg * 100.0);
audio_set_equalizer();
break;
@ -1044,7 +1001,7 @@ static void soundmenu ()
case 10:
{
GUI_OptionBox(m,0,"Low Frequency",(void *)&config.low_freq,10,0,config.high_freq,1);
sprintf (items[offset+8].text, "Low Freq: %d", config.low_freq);
sprintf (items[10].text, "Low Freq: %d", config.low_freq);
audio_set_equalizer();
break;
}
@ -1052,7 +1009,7 @@ static void soundmenu ()
case 11:
{
GUI_OptionBox(m,0,"High Frequency",(void *)&config.high_freq,100,config.low_freq,30000,1);
sprintf (items[offset+9].text, "High Freq: %d", config.high_freq);
sprintf (items[11].text, "High Freq: %d", config.high_freq);
audio_set_equalizer();
break;
}
@ -1065,12 +1022,6 @@ static void soundmenu ()
}
}
if (reinit && system_hw)
{
audio_init(snd.sample_rate, snd.frame_rate);
sound_restore();
}
GUI_DeleteMenu(m);
}
@ -1095,26 +1046,26 @@ static void systemmenu ()
gui_item *items = m->items;
if (config.system == 0)
sprintf (items[0].text, "Console Hardware: AUTO");
sprintf (items[0].text, "Console Type: AUTO");
else if (config.system == SYSTEM_SG)
sprintf (items[0].text, "Console Hardware: SG-1000");
sprintf (items[0].text, "Console Type: SG-1000");
else if (config.system == SYSTEM_MARKIII)
sprintf (items[0].text, "Console Hardware: MARK-III");
sprintf (items[0].text, "Console Type: MARK-III");
else if (config.system == SYSTEM_SMS)
sprintf (items[0].text, "Console Hardware: SMS");
sprintf (items[0].text, "Console Type: SMS");
else if (config.system == SYSTEM_SMS2)
sprintf (items[0].text, "Console Hardware: SMS-II");
sprintf (items[0].text, "Console Type: SMS II");
else if (config.system == SYSTEM_GG)
sprintf (items[0].text, "Console Hardware: GG");
sprintf (items[0].text, "Console Type: GG");
else if (config.system == SYSTEM_MD)
sprintf (items[0].text, "Console Hardware: MD");
sprintf (items[0].text, "Console Type: MD");
if (config.region_detect == 0)
sprintf (items[1].text, "Console Region: AUTO");
else if (config.region_detect == 1)
sprintf (items[1].text, "Console Region: USA");
else if (config.region_detect == 2)
sprintf (items[1].text, "Console Region: EUR");
sprintf (items[1].text, "Console Region: EUROPE");
else if (config.region_detect == 3)
sprintf (items[1].text, "Console Region: JAPAN");
@ -1171,81 +1122,91 @@ static void systemmenu ()
if (config.system == SYSTEM_MD)
{
config.system = 0;
sprintf (items[0].text, "Console Hardware: AUTO");
sprintf (items[0].text, "Console Type: AUTO");
/* Default system hardware (auto) */
system_hw = romtype;
if (system_hw) system_hw = romtype;
}
else if (config.system == 0)
{
config.system = SYSTEM_SG;
sprintf (items[0].text, "Console Hardware: SG-1000");
system_hw = SYSTEM_SG;
sprintf (items[0].text, "Console Type: SG-1000");
if (system_hw) system_hw = SYSTEM_SG;
}
else if (config.system == SYSTEM_SG)
{
config.system = SYSTEM_MARKIII;
sprintf (items[0].text, "Console Hardware: MARK-III");
system_hw = SYSTEM_MARKIII;
sprintf (items[0].text, "Console Type: MARK-III");
if (system_hw) system_hw = SYSTEM_MARKIII;
}
else if (config.system == SYSTEM_MARKIII)
{
config.system = SYSTEM_SMS;
sprintf (items[0].text, "Console Hardware: SMS");
system_hw = SYSTEM_SMS;
sprintf (items[0].text, "Console Type: SMS");
if (system_hw) system_hw = SYSTEM_SMS;
}
else if (config.system == SYSTEM_SMS)
{
config.system = SYSTEM_SMS2;
sprintf (items[0].text, "Console Hardware: SMS-II");
system_hw = SYSTEM_SMS2;
sprintf (items[0].text, "Console Type: SMS II");
if (system_hw) system_hw = SYSTEM_SMS2;
}
else if (config.system == SYSTEM_SMS2)
{
config.system = SYSTEM_GG;
sprintf (items[0].text, "Console Hardware: GG");
sprintf (items[0].text, "Console Type: GG");
if (romtype == SYSTEM_GG)
{
/* Game Gear mode */
system_hw = SYSTEM_GG;
if (system_hw) system_hw = SYSTEM_GG;
}
else
{
/* Game Gear in MS compatibility mode */
system_hw = SYSTEM_GGMS;
if (system_hw) system_hw = SYSTEM_GGMS;
}
}
else if (config.system == SYSTEM_GG)
{
config.system = SYSTEM_MD;
sprintf (items[0].text, "Console Hardware: MD");
sprintf (items[0].text, "Console Type: MD");
if (romtype & SYSTEM_MD)
{
/* Default mode */
system_hw = romtype;
if (system_hw) system_hw = romtype;
}
else
{
/* Mega Drive in MS compatibility mode */
system_hw = SYSTEM_PBC;
if (system_hw) system_hw = SYSTEM_PBC;
}
}
/* reinitialize audio streams */
audio_init(snd.sample_rate, snd.frame_rate);
/* force hard reset */
system_init();
system_reset();
/* restore SRAM */
if (config.s_auto & 1)
if (system_hw)
{
/* restore previous input settings */
if (old_system[0] != -1)
{
input.system[0] = old_system[0];
}
if (old_system[1] != -1)
{
input.system[1] = old_system[1];
}
/* reinitialize audio streams */
audio_init(snd.sample_rate, snd.frame_rate);
/* force hard reset */
system_init();
system_reset();
/* restore SRAM */
slot_autoload(0,config.s_device);
}
break;
}
@ -1261,7 +1222,7 @@ static void systemmenu ()
else if (config.region_detect == 3)
sprintf (items[1].text, "Console Region: JAPAN");
/* force system reinitialization + region BIOS */
/* force system reinitialization + region BIOS */
reinit = 2;
break;
}
@ -1309,10 +1270,7 @@ static void systemmenu ()
system_reset();
/* restore SRAM */
if (config.s_auto & 1)
{
slot_autoload(0,config.s_device);
}
slot_autoload(0,config.s_device);
}
break;
}
@ -1333,10 +1291,7 @@ static void systemmenu ()
md_cart_init();
/* restore SRAM */
if (config.s_auto & 1)
{
slot_autoload(0,config.s_device);
}
slot_autoload(0,config.s_device);
}
sprintf (items[6].text, "68k Address Error: %s", config.addr_error ? "ON" : "OFF");
break;
@ -1361,10 +1316,7 @@ static void systemmenu ()
system_reset();
/* restore SRAM */
if (config.s_auto & 1)
{
slot_autoload(0,config.s_device);
}
slot_autoload(0,config.s_device);
/* Action Replay switch */
if (areplay_get_status() < 0)
@ -1414,9 +1366,9 @@ static void systemmenu ()
if (reinit && system_hw)
{
/* reinitialize console region */
get_region(0);
get_region(NULL);
/* framerate has changed, reinitialize audio timings */
/* framerate might have changed, reinitialize audio timings */
audio_init(snd.sample_rate, get_framerate());
/* system with region BIOS should be reinitialized if region code has changed */
@ -1426,10 +1378,7 @@ static void systemmenu ()
system_reset();
/* restore SRAM */
if (config.s_auto & 1)
{
slot_autoload(0,config.s_device);
}
slot_autoload(0,config.s_device);
}
else
{
@ -1472,9 +1421,6 @@ static void systemmenu ()
vc_max = vc_table[3][vdp_pal];
break;
}
/* reinitialize sound emulation */
sound_restore();
}
}
@ -1548,25 +1494,27 @@ static void videomenu ()
else
sprintf (items[VI_OFFSET+1].text, "Borders: NONE");
if (config.aspect == 1)
sprintf (items[VI_OFFSET+2].text,"Aspect: ORIGINAL (4:3)");
else if (config.aspect == 2)
sprintf (items[VI_OFFSET+2].text, "Aspect: ORIGINAL (16:9)");
else
sprintf (items[VI_OFFSET+2].text, "Aspect: SCALED");
sprintf(items[VI_OFFSET+2].text, "GG Screen: %s", config.gg_extra ? "EXTENDED":"ORIGINAL");
sprintf (items[VI_OFFSET+3].text, "Screen Position: (%s%02d,%s%02d)",
if (config.aspect == 1)
sprintf (items[VI_OFFSET+3].text,"Aspect: ORIGINAL (4:3)");
else if (config.aspect == 2)
sprintf (items[VI_OFFSET+3].text, "Aspect: ORIGINAL (16:9)");
else
sprintf (items[VI_OFFSET+3].text, "Aspect: SCALED");
sprintf (items[VI_OFFSET+4].text, "Screen Position: (%s%02d,%s%02d)",
(config.xshift < 0) ? "":"+", config.xshift,
(config.yshift < 0) ? "":"+", config.yshift);
sprintf (items[VI_OFFSET+4].text, "Screen Scaling: (%s%02d,%s%02d)",
sprintf (items[VI_OFFSET+5].text, "Screen Scaling: (%s%02d,%s%02d)",
(config.xscale < 0) ? "":"+", config.xscale,
(config.yscale < 0) ? "":"+", config.yscale);
if (config.aspect)
m->max_items = VI_OFFSET+4;
else
m->max_items = VI_OFFSET+5;
else
m->max_items = VI_OFFSET+6;
GUI_InitMenu(m);
GUI_SlideMenuTitle(m,strlen("Video "));
@ -1695,36 +1643,41 @@ static void videomenu ()
sprintf (items[VI_OFFSET+1].text, "Borders: NONE");
break;
case VI_OFFSET+2: /*** aspect ratio ***/
case VI_OFFSET+2: /*** Game Gear extended screen */
config.gg_extra ^= 1;
sprintf(items[VI_OFFSET+2].text, "GG Screen: %s", config.gg_extra ? "EXTENDED":"ORIGINAL");
break;
case VI_OFFSET+3: /*** aspect ratio ***/
config.aspect = (config.aspect + 1) % 3;
if (config.aspect == 1)
sprintf (items[VI_OFFSET+2].text,"Aspect: ORIGINAL (4:3)");
sprintf (items[VI_OFFSET+3].text,"Aspect: ORIGINAL (4:3)");
else if (config.aspect == 2)
sprintf (items[VI_OFFSET+2].text, "Aspect: ORIGINAL (16:9)");
sprintf (items[VI_OFFSET+3].text, "Aspect: ORIGINAL (16:9)");
else
sprintf (items[VI_OFFSET+2].text, "Aspect: SCALED");
sprintf (items[VI_OFFSET+3].text, "Aspect: SCALED");
if (config.aspect)
{
/* disable items */
m->max_items = VI_OFFSET+4;
m->max_items = VI_OFFSET+5;
/* reset menu selection */
if (m->offset > VI_OFFSET)
{
m->offset = VI_OFFSET;
m->selected = 2;
m->selected = 3;
}
}
else
{
/* enable items */
m->max_items = VI_OFFSET+5;
m->max_items = VI_OFFSET+6;
}
break;
case VI_OFFSET+3: /*** screen position ***/
case VI_OFFSET+4: /*** screen position ***/
if (system_hw)
{
state[0] = m->arrows[0]->state;
@ -1742,9 +1695,9 @@ static void videomenu ()
m->arrows[1]->state = state[1];
m->screenshot = 0;
strcpy(m->title,"Video Settings");
sprintf (items[VI_OFFSET+3].text, "Screen Position: (%s%02d,%s%02d)",
(config.xshift < 0) ? "":"+", config.xshift,
(config.yshift < 0) ? "":"+", config.yshift);
sprintf (items[VI_OFFSET+4].text, "Screen Position: (%s%02d,%s%02d)",
(config.xshift < 0) ? "":"+", config.xshift,
(config.yshift < 0) ? "":"+", config.yshift);
}
else
{
@ -1752,7 +1705,7 @@ static void videomenu ()
}
break;
case VI_OFFSET+4: /*** screen scaling ***/
case VI_OFFSET+5: /*** screen scaling ***/
if (system_hw)
{
state[0] = m->arrows[0]->state;
@ -1770,9 +1723,9 @@ static void videomenu ()
m->arrows[1]->state = state[1];
m->screenshot = 0;
strcpy(m->title,"Video Settings");
sprintf (items[VI_OFFSET+4].text, "Screen Scaling: (%s%02d,%s%02d)",
(config.xscale < 0) ? "":"+", config.xscale,
(config.yscale < 0) ? "":"+", config.yscale);
sprintf (items[VI_OFFSET+5].text, "Screen Scaling: (%s%02d,%s%02d)",
(config.xscale < 0) ? "":"+", config.xscale,
(config.yscale < 0) ? "":"+", config.yscale);
}
else
{
@ -1788,11 +1741,8 @@ static void videomenu ()
if (reinit && system_hw)
{
/* framerate has changed, reinitialize audio timings */
/* framerate might have changed, reinitialize audio timings */
audio_init(snd.sample_rate, get_framerate());
/* reinitialize sound chips */
sound_restore();
}
GUI_DeleteMenu(m);
@ -3222,14 +3172,18 @@ static void showrominfo (void)
sprintf (items[11], "ROM end: $%06X", rominfo.romend);
if (sram.custom)
sprintf (items[12], "EEPROM(%dK): $%06X", ((md_eeprom.type.size_mask+1)* 8) /1024, md_eeprom.type.sda_in_adr);
sprintf (items[12], "Serial EEPROM");
else if (sram.detected)
sprintf (items[12], "SRAM Start: $%06X", sram.start);
else
sprintf (items[12], "No Backup Memory specified");
if (sram.custom)
sprintf (items[13], "EEPROM(%dK): $%06X", ((md_eeprom.type.size_mask+1)* 8) /1024, md_eeprom.type.sda_out_adr);
if (sram.custom == 1)
sprintf (items[13], "Type: I2C (24Cxx)");
else if (sram.custom == 2)
sprintf (items[13], "Type: SPI (25x512/95x512)");
else if (sram.custom == 3)
sprintf (items[13], "Type: I2C (93C46)");
else if (sram.detected)
sprintf (items[13], "SRAM End: $%06X", sram.end);
else if (sram.on)
@ -3292,9 +3246,9 @@ static void showcredits(void)
FONT_writeCenter("original Z80 core by Juergen Buchmueller", 18, 0, 640, 552 - offset, (GXColor)WHITE);
FONT_writeCenter("original 68k core (Musashi) by Karl Stenerud", 18, 0, 640, 570 - offset, (GXColor)WHITE);
FONT_writeCenter("original YM2612/2413 cores by Jarek Burczynski, Tatsuyuki Satoh", 18, 0, 640, 588 - offset, (GXColor)WHITE);
FONT_writeCenter("SN76489 core by Maxim", 18, 0, 640, 606 - offset, (GXColor)WHITE);
FONT_writeCenter("original SN76489 core by Maxim", 18, 0, 640, 606 - offset, (GXColor)WHITE);
FONT_writeCenter("SVP core by Gravydas Ignotas (Notaz)", 18, 0, 640, 624 - offset, (GXColor)WHITE);
FONT_writeCenter("FIR Resampler & NTSC Video Filter by Shay Green (Blargg)", 18, 0, 640, 642 - offset, (GXColor)WHITE);
FONT_writeCenter("Blip Buffer Library & NTSC Video Filter by Shay Green (Blargg)", 18, 0, 640, 642 - offset, (GXColor)WHITE);
FONT_writeCenter("3-Band EQ implementation by Neil C", 18, 0, 640, 660 - offset, (GXColor)WHITE);
FONT_writeCenter("Special thanks to ...", 20, 0, 640, 700 - offset, (GXColor)LIGHT_GREEN);
@ -3306,9 +3260,9 @@ static void showcredits(void)
FONT_writeCenter("porting code, GUI engine & design by Eke-Eke", 18, 0, 640, 866 - offset, (GXColor)WHITE);
FONT_writeCenter("original Gamecube port by Softdev, Honkeykong & Markcube", 18, 0, 640, 884 - offset, (GXColor)WHITE);
FONT_writeCenter("original icons, logo & button design by Low Lines", 18, 0, 640, 906 - offset, (GXColor)WHITE);
FONT_writeCenter("credit illustration by Orioto (from Deviant Art)", 18, 0, 640, 924 - offset, (GXColor)WHITE);
FONT_writeCenter("credit illustration by Orioto (Deviant Art)", 18, 0, 640, 924 - offset, (GXColor)WHITE);
FONT_writeCenter("memory card icon design by Brakken", 18, 0, 640, 942 - offset, (GXColor)WHITE);
FONT_writeCenter("libogc by Shagkur & other contibutors", 18, 0, 640, 960 - offset, (GXColor)WHITE);
FONT_writeCenter("libogc by Shagkur & various other contibutors", 18, 0, 640, 960 - offset, (GXColor)WHITE);
FONT_writeCenter("libfat by Chism", 18, 0, 640, 978 - offset, (GXColor)WHITE);
FONT_writeCenter("wiiuse by Michael Laforest (Para)", 18, 0, 640, 996 - offset, (GXColor)WHITE);
FONT_writeCenter("asndlib & OGG player by Francisco Muñoz (Hermes)", 18, 0, 640, 1014 - offset, (GXColor)WHITE);
@ -3317,7 +3271,7 @@ static void showcredits(void)
FONT_writeCenter("Special thanks to ...", 20, 0, 640, 1090 - offset, (GXColor)LIGHT_GREEN);
FONT_writeCenter("Softdev, Tmbinc, Costis, Emukiddid, Team Twiizer", 18, 0, 640, 1126 - offset, (GXColor)WHITE);
FONT_writeCenter("Brakken & Tehskeen members for their support", 18, 0, 640, 1144 - offset, (GXColor)WHITE);
FONT_writeCenter("Brakken & former Tehskeen members for their support", 18, 0, 640, 1144 - offset, (GXColor)WHITE);
FONT_writeCenter("Anca, my wife, for her patience & various ideas", 18, 0, 640, 1162 - offset, (GXColor)WHITE);
gxSetScreen();
@ -3430,11 +3384,8 @@ void mainmenu(void)
char filename[MAXPATHLEN];
int status, quit = 0;
if ((config.s_auto & 1) || (system_hw == SYSTEM_MCD))
{
/* Autosave Backup RAM */
slot_autosave(0, config.s_device);
}
/* Autosave Backup RAM */
slot_autosave(0, config.s_device);
#ifdef HW_RVL
/* Wiimote shutdown */
@ -3619,10 +3570,7 @@ void mainmenu(void)
system_reset();
/* restore SRAM */
if (config.s_auto & 1)
{
slot_autoload(0,config.s_device);
}
slot_autoload(0,config.s_device);
}
/* exit to game */

View File

@ -39,25 +39,28 @@
#include "shared.h"
/* Length is dimensionned for at least one frame of emulation */
#define SOUND_BUFFER_LEN 4096
/* DMA soundbuffers (required to be 32-bytes aligned)
To prevent audio clashes, we use double buffering technique:
one buffer is the active DMA buffer
the other one is the current work buffer (updated during frame emulation)
We do not need more since frame emulation and DMA operation are synchronized
*/
u8 soundbuffer[2][SOUND_BUFFER_MAX_SIZE] ATTRIBUTE_ALIGN(32);
/* Current work soundbuffer */
u32 mixbuffer;
/* Number of sound buffers */
#define SOUND_BUFFER_NUM 3
/* audio DMA status */
u32 audioStarted;
/* DMA soundbuffers (required to be 32-bytes aligned) */
static u8 soundbuffer[SOUND_BUFFER_NUM][SOUND_BUFFER_LEN] ATTRIBUTE_ALIGN(32);
/* Current work soundbuffer */
static u8 mixbuffer;
/* Background music */
static u8 *Bg_music_ogg = NULL;
static u32 Bg_music_ogg_size = 0;
/* Frame Sync */
static u8 audio_sync;
/***************************************************************************************/
/* Audio engine */
/***************************************************************************************/
@ -74,7 +77,8 @@ static void ai_callback(void)
}
prevtime = current;
#endif
frameticker ++;
audio_sync = 1;
}
/* AUDIO engine initialization */
@ -122,47 +126,52 @@ void gx_audio_Shutdown(void)
This function retrieves samples for the frame then set the next DMA parameters
Parameters will be taken in account only when current DMA operation is over
***/
void gx_audio_Update(void)
int gx_audio_Update(void)
{
/* Current available soundbuffer */
s16 *sb = (s16 *)(soundbuffer[mixbuffer]);
/* Retrieve audio samples (size must be multiple of 32 bytes) */
int size = audio_update(sb) * 4;
#ifdef LOG_TIMING
if (prevtime && (frame_cnt < LOGSIZE - 1))
if (audio_sync)
{
delta_samp[frame_cnt + 1] = size;
}
else
{
delta_samp[0] = size;
}
#endif
/* Current available soundbuffer */
s16 *sb = (s16 *)(soundbuffer[mixbuffer]);
/* Update DMA settings */
DCFlushRange((void *)sb, size);
AUDIO_InitDMA((u32) sb, size);
mixbuffer ^= 1;
/* Retrieve audio samples (size must be multiple of 32 bytes) */
int size = audio_update(sb) * 4;
/* Start Audio DMA */
/* this is called once to kick-off DMA from external memory to audio interface */
/* DMA operation is automatically restarted when all samples have been sent. */
/* If DMA settings are not updated at that time, previous sound buffer will be used. */
/* Therefore we need to make sure frame emulation is completed before current DMA is */
/* completed, either by synchronizing frame emulation with DMA start or by syncing it */
/* with Vertical Interrupt and outputing a suitable number of samples per frame. */
if (!audioStarted)
{
/* restart audio DMA */
AUDIO_StopDMA();
AUDIO_StartDMA();
audioStarted = 1;
#ifdef LOG_TIMING
if (prevtime && (frame_cnt < LOGSIZE - 1))
{
delta_samp[frame_cnt + 1] = size;
}
else
{
delta_samp[0] = size;
}
#endif
/* resynchronize emulation */
frameticker = 1;
/* Update DMA settings */
DCFlushRange((void *)sb, size);
AUDIO_InitDMA((u32) sb, size);
mixbuffer = (mixbuffer + 1) % SOUND_BUFFER_NUM;
audio_sync = 0;
/* Start Audio DMA */
/* this is called once to kick-off DMA from external memory to audio interface */
/* DMA operation is automatically restarted when all samples have been sent. */
/* If DMA settings are not updated at that time, previous sound buffer will be used. */
/* Therefore we need to make sure frame emulation is completed before current DMA is */
/* completed, by synchronizing frame emulation with DMA start and also by syncing it */
/* with Video Interrupt and outputing a suitable number of samples per frame. */
if (!audioStarted)
{
/* restart audio DMA */
AUDIO_StopDMA();
AUDIO_StartDMA();
audioStarted = 1;
}
return SYNC_AUDIO;
}
return NO_SYNC;
}
/***
@ -184,17 +193,14 @@ void gx_audio_Start(void)
AUDIO_RegisterDMACallback(NULL);
DSP_Halt();
/* when VSYNC is forced OFF or AUTO with TV mode not matching emulated video mode, emulation is synchronized with Audio Hardware */
if (!config.vsync || (config.tv_mode == !vdp_pal))
{
/* DMA Interrupt callback */
AUDIO_RegisterDMACallback(ai_callback);
}
/* DMA Interrupt callback */
AUDIO_RegisterDMACallback(ai_callback);
/* reset emulation audio processing */
memset(soundbuffer, 0, 2 * SOUND_BUFFER_MAX_SIZE);
memset(soundbuffer, 0, 3 * SOUND_BUFFER_LEN);
audioStarted = 0;
mixbuffer = 0;
audio_sync = 1;
}
/***

View File

@ -40,17 +40,12 @@
#ifndef _GC_AUDIO_H_
#define _GC_AUDIO_H_
/* Length is dimensionned for at least one frame of emulation */
#define SOUND_BUFFER_MAX_SIZE 4096
extern u8 soundbuffer[2][SOUND_BUFFER_MAX_SIZE];
extern u32 mixbuffer;
extern u32 audioStarted;
extern void gx_audio_Init(void);
extern void gx_audio_Shutdown(void);
extern void gx_audio_Start(void);
extern void gx_audio_Stop(void);
extern void gx_audio_Update(void);
extern int gx_audio_Update(void);
#endif

View File

@ -1216,7 +1216,7 @@ void gx_input_SetDefault(void)
#ifdef HW_RVL
for (i=0; i<4; i++)
{
/* Wiimote */
/* Wiimote (horizontal) */
config.wpad_keymap[i*3 + WPAD_EXP_NONE][KEY_BUTTONA] = WPAD_BUTTON_A;
config.wpad_keymap[i*3 + WPAD_EXP_NONE][KEY_BUTTONB] = WPAD_BUTTON_1;
config.wpad_keymap[i*3 + WPAD_EXP_NONE][KEY_BUTTONC] = WPAD_BUTTON_2;
@ -1594,8 +1594,6 @@ void gx_input_UpdateMenu(void)
else if (pw & WPAD_BUTTON_DOWN) pp |= PAD_BUTTON_DOWN;
else if (pw & WPAD_BUTTON_LEFT) pp |= PAD_BUTTON_LEFT;
else if (pw & WPAD_BUTTON_RIGHT) pp |= PAD_BUTTON_RIGHT;
if (pw & WPAD_BUTTON_MINUS) pp |= PAD_TRIGGER_L;
if (pw & WPAD_BUTTON_PLUS) pp |= PAD_TRIGGER_R;
}
else
{
@ -1604,8 +1602,6 @@ void gx_input_UpdateMenu(void)
else if (pw & WPAD_BUTTON_DOWN) pp |= PAD_BUTTON_RIGHT;
else if (pw & WPAD_BUTTON_LEFT) pp |= PAD_BUTTON_DOWN;
else if (pw & WPAD_BUTTON_RIGHT) pp |= PAD_BUTTON_UP;
if (pw & WPAD_BUTTON_MINUS) pp |= PAD_TRIGGER_R;
if (pw & WPAD_BUTTON_PLUS) pp |= PAD_TRIGGER_L;
}
/* Classic Controller direction keys */
@ -1620,6 +1616,8 @@ void gx_input_UpdateMenu(void)
if (pw & WPAD_BUTTON_2) pp |= PAD_BUTTON_A;
if (pw & WPAD_BUTTON_1) pp |= PAD_BUTTON_B;
if (pw & WPAD_BUTTON_HOME) pp |= PAD_TRIGGER_Z;
if (pw & WPAD_BUTTON_PLUS) pp |= PAD_TRIGGER_L;
if (pw & WPAD_BUTTON_MINUS) pp |= PAD_TRIGGER_R;
if (pw & WPAD_CLASSIC_BUTTON_FULL_L) pp |= PAD_TRIGGER_L;
if (pw & WPAD_CLASSIC_BUTTON_FULL_R) pp |= PAD_TRIGGER_R;
if (pw & WPAD_CLASSIC_BUTTON_A) pp |= PAD_BUTTON_A;

View File

@ -85,6 +85,9 @@ static gx_texture *crosshair[2];
static u32 *xfb[2];
static u32 whichfb = 0;
/*** Frame Sync ***/
static u8 video_sync;
/***************************************************************************************/
/* Emulation video modes */
/***************************************************************************************/
@ -314,7 +317,6 @@ static GXRModeObj *tvmodes[6] =
&TV50hz_576i
};
/***************************************************************************************/
/* GX rendering engine */
/***************************************************************************************/
@ -360,7 +362,8 @@ static void vi_callback(u32 cnt)
}
prevtime = current;
#endif
frameticker ++;
video_sync = 1;
}
/* Vertex Rendering */
@ -491,7 +494,7 @@ static void gxSetAspectRatio(int *xscale, int *yscale)
}
/* Horizontal Scaling */
/* Wii/NGC pixel clock = 13.5 Mhz */
/* Wii/Gamecube pixel clock = 13.5 Mhz */
/* "H32" pixel clock = Master Clock / 10 = 5.3693175 Mhz (NTSC) or 5.3203424 (PAL) */
/* "H40" pixel clock = Master Clock / 8 = 6,711646875 Mhz (NTSC) or 6,650428 Mhz (PAL) */
if (config.overscan & 2)
@ -499,26 +502,26 @@ static void gxSetAspectRatio(int *xscale, int *yscale)
/* Horizontal borders are emulated */
if (reg[12] & 1)
{
/* 348 "H40" pixels = 348 * Wii pixel clock / "H40" pixel clock = approx. 700 (NTSC) or 707 (PAL) Wii/NGC pixels */
/* 348 "H40" pixels = 348 * Wii/GC pixel clock / "H40" pixel clock = approx. 700 (NTSC) or 707 (PAL) Wii/GC pixels */
*xscale = (system_clock == MCLOCK_NTSC) ? 350 : 354;
}
else
{
/* 284 "H32" pixels = 284 * Wii pixel clock / "H40" pixel clock = approx. 714 (NTSC) or 721 (PAL) Wii/NGC pixels */
/* 284 "H32" pixels = 284 * Wii/GC pixel clock / "H40" pixel clock = approx. 714 (NTSC) or 721 (PAL) Wii/GC pixels */
*xscale = (system_clock == MCLOCK_NTSC) ? 357 : 361;
}
}
else
{
/* Horizontal borders are simulated */
if (system_hw == SYSTEM_GG)
if ((system_hw == SYSTEM_GG) && !config.gg_extra)
{
/* 160 "H32" pixels = 160 * Wii pixel clock / "H32" pixel clock = approx. 403 Wii/NGC pixels (NTSC only) */
/* 160 "H32" pixels = 160 * Wii/GC pixel clock / "H32" pixel clock = approx. 403 Wii/GC pixels (NTSC only) */
*xscale = 202;
}
else
{
/* 320 "H40" pixels = 256 "H32" pixels = 256 * Wii pixel clock / "H32" pixel clock = approx. 644 (NTSC) or 650 (PAL) Wii/NGC pixels */
/* 320 "H40" pixels = 256 "H32" pixels = 256 * Wii/GC pixel clock / "H32" pixel clock = approx. 644 (NTSC) or 650 (PAL) Wii/GC pixels */
*xscale = (system_clock == MCLOCK_NTSC) ? 322 : 325;
}
}
@ -544,7 +547,7 @@ static void gxSetAspectRatio(int *xscale, int *yscale)
}
/* Game Gear specific: if borders are disabled, upscale to fullscreen */
if (system_hw == SYSTEM_GG)
if ((system_hw == SYSTEM_GG) && !config.gg_extra)
{
if (!(config.overscan & 1))
{
@ -1359,6 +1362,9 @@ void gxTextureClose(gx_texture **p_texture)
/* Emulation mode -> Menu mode */
void gx_video_Stop(void)
{
/* wait for next VBLANK */
VIDEO_WaitVSync ();
/* unallocate NTSC filters */
if (sms_ntsc) free(sms_ntsc);
if (md_ntsc) free(md_ntsc);
@ -1373,12 +1379,12 @@ void gx_video_Stop(void)
gxResetRendering(1);
gxResetMode(vmode);
/* display game snapshot */
/* render game snapshot */
gxClearScreen((GXColor)BLACK);
gxDrawScreenshot(0xff);
/* default VI settings */
VIDEO_SetPreRetraceCallback(NULL);
VIDEO_SetPostRetraceCallback(NULL);
#ifdef HW_RVL
VIDEO_SetTrapFilter(1);
VIDEO_SetGamma(VI_GM_1_0);
@ -1413,11 +1419,11 @@ void gx_video_Start(void)
gc_pal = 0;
}
/* When VSYNC is forced ON or AUTO with TV mode matching emulated video mode, emulation is synchronized with video hardware */
/* When VSYNC is set to AUTO & console TV mode matches emulated video mode, emulation is synchronized with video hardware as well */
if (config.vsync && (gc_pal == vdp_pal))
{
/* VSYNC callback */
VIDEO_SetPreRetraceCallback(vi_callback);
VIDEO_SetPostRetraceCallback(vi_callback);
VIDEO_Flush();
}
@ -1434,7 +1440,7 @@ void gx_video_Start(void)
}
/* update horizontal border width */
if (system_hw == SYSTEM_GG)
if ((system_hw == SYSTEM_GG) && !config.gg_extra)
{
bitmap.viewport.x = (config.overscan & 2) ? 14 : -48;
}
@ -1522,12 +1528,14 @@ void gx_video_Start(void)
}
/* GX render update */
void gx_video_Update(void)
int gx_video_Update(void)
{
int update = bitmap.viewport.changed & 1;
if (!video_sync && config.vsync && (gc_pal == vdp_pal)) return NO_SYNC;
video_sync = 0;
/* check if display has changed during frame */
if (update)
if (bitmap.viewport.changed & 1)
{
/* update texture size */
vwidth = bitmap.viewport.w + (2 * bitmap.viewport.x);
@ -1621,14 +1629,14 @@ void gx_video_Update(void)
/* copy EFB to XFB */
GX_DrawDone();
GX_CopyDisp(xfb[whichfb], GX_TRUE);
GX_CopyDisp(xfb[whichfb], GX_FALSE);
GX_Flush();
/* XFB is ready to be displayed */
VIDEO_SetNextFramebuffer(xfb[whichfb]);
VIDEO_Flush();
if (update)
if (bitmap.viewport.changed & 1)
{
/* clear update flags */
bitmap.viewport.changed &= ~1;
@ -1647,6 +1655,8 @@ void gx_video_Update(void)
/* Audio DMA need to be resynchronized with VSYNC */
audioStarted = 0;
}
return SYNC_VIDEO;
}
/* Initialize VIDEO subsystem */

View File

@ -99,6 +99,6 @@ extern void gx_video_Init(void);
extern void gx_video_Shutdown(void);
extern void gx_video_Start(void);
extern void gx_video_Stop(void);
extern void gx_video_Update(void);
extern int gx_video_Update(void);
#endif

View File

@ -49,16 +49,11 @@
#include <fat.h>
/* audio "exact" samplerate, measured on real hardware */
#ifdef HW_RVL
#define SAMPLERATE_48KHZ 48000
#else
#define SAMPLERATE_48KHZ 48044
#endif
/* output samplerate, adjusted to take resampler precision in account */
#define SAMPLERATE_48KHZ 47992
u32 Shutdown = 0;
u32 ConfigRequested = 1;
u32 frameticker;
#ifdef LOG_TIMING
u64 prevtime;
@ -139,6 +134,8 @@ static void init_machine(void)
static void run_emulation(void)
{
int sync;
/* main emulation loop */
while (1)
{
@ -148,37 +145,28 @@ static void run_emulation(void)
/* 16-bit hardware + CD */
while (!ConfigRequested)
{
/* automatic frame skipping */
if (frameticker > 1)
{
/* skip frame */
system_frame_scd(1);
frameticker = 1;
}
else
{
/* render frame */
frameticker = 0;
system_frame_scd(0);
/* render frame */
system_frame_scd(0);
/* audio/video sync */
sync = NO_SYNC;
while (sync != (SYNC_VIDEO | SYNC_AUDIO))
{
/* update video */
gx_video_Update();
}
sync |= gx_video_Update();
/* update audio */
gx_audio_Update();
/* update audio */
sync |= gx_audio_Update();
}
/* check interlaced mode change */
if (bitmap.viewport.changed & 4)
{
/* VSYNC "original" mode */
if (!config.render && (gc_pal == vdp_pal))
if (!config.render && config.vsync && (gc_pal == vdp_pal))
{
/* framerate has changed, reinitialize audio timings */
audio_init(SAMPLERATE_48KHZ, get_framerate());
/* reinitialize sound chips */
sound_restore();
}
/* clear flag */
@ -189,9 +177,6 @@ static void run_emulation(void)
/* use Wii DVD light to simulate CD Drive access led */
*(u32*)0xcd0000c0 = (*(u32*)0xcd0000c0 & ~0x20) | ((scd.regs[0x06>>1].byte.h & 0x01) << 5);
#endif
/* wait for next frame */
while (frameticker < 1) usleep(1);
}
}
else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
@ -199,45 +184,33 @@ static void run_emulation(void)
/* 16-bit hardware */
while (!ConfigRequested)
{
/* automatic frame skipping (only needed when running Virtua Racing on Gamecube) */
if (frameticker > 1)
{
/* skip frame */
system_frame_gen(1);
frameticker = 1;
}
else
{
/* render frame */
frameticker = 0;
system_frame_gen(0);
/* render frame */
system_frame_gen(0);
/* audio/video sync */
sync = NO_SYNC;
while (sync != (SYNC_VIDEO | SYNC_AUDIO))
{
/* update video */
gx_video_Update();
}
sync |= gx_video_Update();
/* update audio */
gx_audio_Update();
/* update audio */
sync |= gx_audio_Update();
}
/* check interlaced mode change */
if (bitmap.viewport.changed & 4)
{
/* VSYNC "original" mode */
if (!config.render && (gc_pal == vdp_pal))
if (!config.render && config.vsync && (gc_pal == vdp_pal))
{
/* framerate has changed, reinitialize audio timings */
audio_init(SAMPLERATE_48KHZ, get_framerate());
/* reinitialize sound chips */
sound_restore();
}
/* clear flag */
bitmap.viewport.changed &= ~4;
}
/* wait for next frame */
while (frameticker < 1) usleep(1);
}
}
else
@ -245,35 +218,33 @@ static void run_emulation(void)
/* 8-bit hardware */
while (!ConfigRequested)
{
/* render frame (no frame skipping needed) */
frameticker = 0;
/* render frame */
system_frame_sms(0);
/* update video */
gx_video_Update();
/* audio/video sync */
sync = NO_SYNC;
while (sync != (SYNC_VIDEO | SYNC_AUDIO))
{
/* update video */
sync |= gx_video_Update();
/* update audio */
gx_audio_Update();
/* update audio */
sync |= gx_audio_Update();
}
/* check interlaced mode change (PBC mode only) */
if (bitmap.viewport.changed & 4)
{
/* "original" mode */
if (!config.render)
if (!config.render && config.vsync && (gc_pal == vdp_pal))
{
/* framerate has changed, reinitialize audio timings */
audio_init(SAMPLERATE_48KHZ, get_framerate());
/* reinitialize sound chips */
sound_restore();
}
/* clear flag */
bitmap.viewport.changed &= ~4;
}
/* wait for next frame */
while (frameticker < 1) usleep(1);
}
}
@ -374,24 +345,21 @@ static void run_emulation(void)
/* restart video & audio */
gx_video_Start();
gx_audio_Start();
frameticker = 1;
}
}
/*********************************************************************************************************************************************************
Output exact framerate (used for sound chips emulation -- see sound.c)
Get emulator input framerate (actually used by audio emulation to approximate number of samples rendered on each frame, see audio_init in system.c)
*********************************************************************************************************************************************************/
double get_framerate(void)
{
/* VSYNC is forced OFF or AUTO with TV mode not matching emulated video mode: emulation is synchronized with audio hardware (DMA interrupt) */
/* Run emulator at original VDP framerate if console TV mode does not match emulated TV mode or VSYNC is disabled */
if (!config.vsync || (config.tv_mode == !vdp_pal))
{
/* emulator will use original VDP framerate, which (theorically) provides more accurate frequencies but occasional video desynchronization */
return 0.0;
}
/* VSYNC is forced ON or AUTO with TV mode matching emulated video mode: emulation is synchronized with video hardware (VSYNC) */
/* emulator use exact output framerate for perfect video synchronization and (theorically) no sound skipping */
/* Otherwise, run emulator at Wii/Gamecube framerate to ensure perfect video synchronization */
if (vdp_pal)
{
/* 288p -> 13500000 pixels/sec, 864 pixels/line, 312 lines/field -> fps = 13500000/864/312 = 50.08 hz */
@ -412,35 +380,28 @@ double get_framerate(void)
********************************************/
void reloadrom(void)
{
/* restore previous input settings */
if (old_system[0] != -1)
/* Cartridge "Hot Swap" support (make sure system has already been inited once and use cartridges) */
if ((config.hot_swap == 3) && ((system_hw != SYSTEM_MCD) || scd.cartridge.boot))
{
input.system[0] = old_system[0];
}
if (old_system[1] != -1)
{
input.system[1] = old_system[1];
}
/* Cartridge Hot Swap (make sure system has already been inited once) */
if ((config.hot_swap == 3) && (system_hw != SYSTEM_MCD))
{
/* Initialize cartridge hardware only */
/* Only initialize cartridge hardware */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
/* 16-bit cartridge */
md_cart_init();
md_cart_reset(1);
}
else
{
/* 8-bit cartridge */
sms_cart_init();
sms_cart_reset();
}
}
else
/* Disc Swap support (automatically enabled if CD tray is open) */
else if ((system_hw != SYSTEM_MCD) || (cdd.status != CD_OPEN))
{
/* Initialize audio emulation */
/* To prevent any sound skipping, sound chips must run at the exact same speed as the rest of emulation (see sound.c) */
interlaced = 0;
audio_init(SAMPLERATE_48KHZ, get_framerate());
@ -452,17 +413,11 @@ void reloadrom(void)
config.hot_swap |= 2;
}
if ((config.s_auto & 1) || (system_hw == SYSTEM_MCD))
{
/* Auto-Load Backup RAM */
slot_autoload(0,config.s_device);
}
/* Auto-Load Backup RAM */
slot_autoload(0,config.s_device);
/* Auto-Load State */
if (config.s_auto & 2)
{
slot_autoload(config.s_default,config.s_device);
}
slot_autoload(config.s_default,config.s_device);
/* Load Cheat file */
CheatLoad();
@ -476,14 +431,10 @@ void shutdown(void)
/* save current config */
config_save();
if (config.s_auto & 2)
{
/* Auto-Save State file */
slot_autosave(config.s_default,config.s_device);
}
/* auto-save State file */
slot_autosave(config.s_default,config.s_device);
/* shutdown emulation */
system_shutdown();
audio_shutdown();
gx_audio_Shutdown();
gx_video_Shutdown();
@ -649,7 +600,6 @@ int main (int argc, char *argv[])
reloadrom();
gx_video_Start();
gx_audio_Start();
frameticker = 1;
ConfigRequested = 0;
}
}

View File

@ -59,17 +59,20 @@
/*************************************************/
#ifdef HW_RVL
#define VERSION "Genesis Plus GX 1.7.0 (WII)"
#define VERSION "Genesis Plus GX 1.7.1 (WII)"
#else
#define VERSION "Genesis Plus GX 1.7.0 (GCN)"
#define VERSION "Genesis Plus GX 1.7.1 (GCN)"
#endif
#define NO_SYNC 0
#define SYNC_VIDEO 1
#define SYNC_AUDIO 2
/* globals */
extern void legal(void);
extern double get_framerate(void);
extern void reloadrom(void);
extern void shutdown(void);
extern u32 frameticker;
extern u32 Shutdown;
extern u32 ConfigRequested;

View File

@ -545,8 +545,9 @@ void io_gg_write(unsigned int offset, unsigned int data)
io_reg[5] = data & 0xF8;
return;
case 6: /* PSG Stereo output control (unsupported) */
case 6: /* PSG Stereo output control */
io_reg[6] = data;
SN76489_Config(config.psg_preamp, config.psgBoostNoise, data);
return;
default: /* Read-only */

View File

@ -90,7 +90,6 @@ typedef struct
ROMINFO rominfo;
char rom_filename[256];
uint8 romtype;
static uint8 rom_region;
@ -210,11 +209,29 @@ static uint16 getchecksum(uint8 *rom, int length)
return checksum;
}
/***************************************************************************
* deinterleave_block
*
* Convert interleaved (.smd) ROM files.
***************************************************************************/
static void deinterleave_block(uint8 * src)
{
int i;
uint8 block[0x4000];
memcpy (block, src, 0x4000);
for (i = 0; i < 0x2000; i += 1)
{
src[i * 2 + 0] = block[0x2000 + (i)];
src[i * 2 + 1] = block[0x0000 + (i)];
}
}
/***************************************************************************
*
* Pass a pointer to the ROM base address.
***************************************************************************/
static void getrominfo(char *romheader)
void getrominfo(char *romheader)
{
/* Clear ROM info structure */
memset (&rominfo, 0, sizeof (ROMINFO));
@ -363,23 +380,6 @@ static void getrominfo(char *romheader)
}
}
/***************************************************************************
* deinterleave_block
*
* Convert interleaved (.smd) ROM files.
***************************************************************************/
static void deinterleave_block(uint8 * src)
{
int i;
uint8 block[0x4000];
memcpy (block, src, 0x4000);
for (i = 0; i < 0x2000; i += 1)
{
src[i * 2 + 0] = block[0x2000 + (i)];
src[i * 2 + 1] = block[0x0000 + (i)];
}
}
/***************************************************************************
* load_bios
*
@ -396,10 +396,10 @@ int load_bios(void)
{
case SYSTEM_MCD:
{
/* check if CD BOOT ROM is already loaded */
/* check if CD BOOTROM is already loaded */
if (!(system_bios & 0x10) || ((system_bios & 0x0c) != (region_code >> 4)))
{
/* load CD BOOT ROM */
/* load CD BOOTROM (fixed 128KB size) */
switch (region_code)
{
case REGION_USA:
@ -413,7 +413,7 @@ int load_bios(void)
break;
}
/* CD BOOT ROM loaded ? */
/* CD BOOTROM loaded ? */
if (size > 0)
{
#ifdef LSB_FIRST
@ -442,21 +442,21 @@ int load_bios(void)
case SYSTEM_GG:
case SYSTEM_GGMS:
{
/* check if Game Gear "BIOS" is already loaded */
/* check if Game Gear BOOTROM is already loaded */
if (!(system_bios & SYSTEM_GG))
{
/* mark Master System & Game Gear "BIOS" as unloaded */
/* mark both Master System & Game Gear BOOTROM as unloaded */
system_bios &= ~(SYSTEM_SMS | SYSTEM_GG);
/* "BIOS" ROM is stored above cartridge ROM area, into $400000-$4FFFFF (max. 1MB) */
/* BOOTROM is stored above cartridge ROM area (max. 4MB) */
if (cart.romsize <= 0x400000)
{
/* load Game Gear "BIOS" file */
/* load Game Gear BOOTROM file */
size = load_archive(GG_BIOS, cart.rom + 0x400000, 0x100000, 0);
if (size > 0)
{
/* mark Game Gear "BIOS" as loaded */
/* mark Game Gear BOOTROM as loaded */
system_bios |= SYSTEM_GG;
}
}
@ -470,35 +470,35 @@ int load_bios(void)
case SYSTEM_SMS:
case SYSTEM_SMS2:
{
/* check if Master System "BIOS" is already loaded */
/* check if Master System BOOTROM is already loaded */
if (!(system_bios & SYSTEM_SMS) || ((system_bios & 0x0c) != (region_code >> 4)))
{
/* mark Master System & Game Gear "BIOS" as unloaded */
/* mark both Master System & Game Gear BOOTROM as unloaded */
system_bios &= ~(SYSTEM_SMS | SYSTEM_GG);
/* "BIOS" ROM is stored above cartridge ROM area, into $400000-$4FFFFF (max. 1MB) */
/* BOOTROM is stored above cartridge ROM area (max. 4MB) */
if (cart.romsize <= 0x400000)
{
/* load Master System "BIOS" file */
/* load Master System BOOTROM file */
switch (region_code)
{
case REGION_USA:
size = load_archive(MS_BIOS_US, cart.rom + 0x400000, 0x100000, 0);
size = load_archive(MS_BIOS_US, cart.rom + 0x400000, 0x400000, 0);
break;
case REGION_EUROPE:
size = load_archive(MS_BIOS_EU, cart.rom + 0x400000, 0x100000, 0);
size = load_archive(MS_BIOS_EU, cart.rom + 0x400000, 0x400000, 0);
break;
default:
size = load_archive(MS_BIOS_JP, cart.rom + 0x400000, 0x100000, 0);
size = load_archive(MS_BIOS_JP, cart.rom + 0x400000, 0x400000, 0);
break;
}
if (size > 0)
{
/* mark Master System "BIOS" as loaded */
/* mark Master System BOOTROM as loaded */
system_bios |= SYSTEM_SMS;
/* loaded "BIOS" region */
/* loaded BOOTROM region */
system_bios = (system_bios & 0xf0) | (region_code >> 4);
}
}
@ -526,75 +526,60 @@ int load_bios(void)
***************************************************************************/
int load_rom(char *filename)
{
FILE *fd;
char buf[0x220];
int i;
/* default ROM header */
char *header = (char *)(cart.rom);
int i, size;
/* clear any existing patches */
ggenie_shutdown();
areplay_shutdown();
if (romtype == SYSTEM_MCD)
/* check previous loaded ROM size */
if (cart.romsize > 0x800000)
{
/* unload CD image */
cdd_unload();
}
else
{
/* no CD image loaded */
/* assume no CD is currently loaded */
cdd.loaded = 0;
}
/* .cue file support */
if (!memcmp(".cue", &filename[strlen(filename) - 4], 4))
/* auto-detect CD image files */
size = cdd_load(filename, (char *)(cart.rom));
if (size < 0)
{
/* open associated .bin file */
strncpy(&filename[strlen(filename) - 4], ".bin", 4);
/* error opening file */
return 0;
}
/* file header */
fd = fopen(filename, "rb");
if (fd)
if (size > 0)
{
fread(buf, 0x220, 1, fd);
fclose(fd);
}
/* auto-detect CD image file */
if (!memcmp("SEGADISCSYSTEM", buf + 0x10, 14))
{
/* file header pointer (BIN format) */
header = buf + 0x10;
/* enable CD hardware */
system_hw = SYSTEM_MCD;
}
else if (!memcmp("SEGADISCSYSTEM", buf, 14))
{
/* file header pointer (ISO format) */
header = buf;
/* enable CD hardware */
/* CD image file loaded */
system_hw = SYSTEM_MCD;
}
else
{
/* load file into ROM buffer */
char extension[4];
int size = load_archive(filename, cart.rom, sizeof(cart.rom), extension);
size = load_archive(filename, cart.rom, sizeof(cart.rom), extension);
if (!size)
{
/* mark all BOOTROM as unloaded since they could have been overwritten */
system_bios &= ~(0x10 | SYSTEM_SMS | SYSTEM_GG);
return 0;
}
/* mark CD BIOS as unloaded */
system_bios &= ~0x10;
if (!size) return(0);
/* mark BOOTROM as unloaded if they have been overwritten by cartridge ROM */
if (size > 0x800000)
{
/* CD BIOS ROM are loaded at the start of CD area */
system_bios &= ~0x10;
}
else if (size > 0x400000)
{
/* Master System or Game Gear BIOS ROM are loaded within $400000-$4FFFFF area */
system_bios &= ~(SYSTEM_SMS | SYSTEM_GG);
}
/* convert lower case to upper case */
*(uint32 *)(extension) &= 0xdfdfdfdf;
/* Auto-detect system hardware from ROM file extension */
/* auto-detect system hardware from ROM file extension */
if (!memcmp("SMS", &extension[0], 3))
{
/* Master System II hardware */
@ -627,13 +612,13 @@ int load_rom(char *filename)
}
/* auto-detect 512 byte extra header */
if (memcmp((char *)(cart.rom + 0x100), "SEGA", 4) && ((size / 512) & 1))
if (memcmp((char *)(cart.rom + 0x100), "SEGA", 4) && ((size / 512) & 1) && !(size % 512))
{
/* remove header */
size -= 512;
memcpy (cart.rom, cart.rom + 512, size);
/* interleaved ROM format (.smd) */
/* assume interleaved Genesis ROM format (.smd) */
if (system_hw == SYSTEM_MD)
{
for (i = 0; i < (size / 0x4000); i++)
@ -642,25 +627,29 @@ int load_rom(char *filename)
}
}
}
/* initialize ROM size */
cart.romsize = size;
}
/* initialize ROM size */
cart.romsize = size;
/* get infos from ROM header */
getrominfo(header);
getrominfo((char *)(cart.rom));
/* set console region */
get_region(header);
get_region((char *)(cart.rom));
/* CD image file */
if (system_hw == SYSTEM_MCD)
{
/* load CD BOOT ROM */
if (!load_bios()) return (0);
if (!load_bios())
{
/* unmount CD image */
cdd_unload();
/* load CD image */
cdd_load(filename, header - buf);
/* error loading CD BOOT ROM */
return (0);
}
/* boot from CD */
scd.cartridge.boot = 0x00;
@ -692,7 +681,7 @@ int load_rom(char *filename)
}
}
/* auto-detected system hardware */
/* Save auto-detected system hardware */
romtype = system_hw;
/* PICO ROM */
@ -702,56 +691,51 @@ int load_rom(char *filename)
system_hw = SYSTEM_PICO;
}
/* CD BOOT ROM */
/* CD BOOTROM */
else if (strstr(rominfo.ROMType, "BR") != NULL)
{
/* enable CD hardware */
system_hw = SYSTEM_MCD;
/* boot from CD */
scd.cartridge.boot = 0x00;
/* copy ROM to BOOTROM area */
memcpy(scd.bootrom, cart.rom, sizeof(scd.bootrom));
/* mark CD BIOS as being loaded */
system_bios = system_bios | 0x10;
/* loaded CD BIOS region */
system_bios = (system_bios & 0xf0) | (region_code >> 4);
/* boot from CD */
scd.cartridge.boot = 0x00;
/* no CD image loaded */
cdd.loaded = 0;
/* clear CD TOC */
cdd_unload();
}
/* special ROM cartridges that use CD hardware */
else if ((strstr(rominfo.domestic,"FLUX") != NULL) || (strstr(rominfo.domestic,"WONDER LIBRARY") != NULL))
/* ROM cartridges with CD support */
else if ((strstr(rominfo.domestic,"FLUX") != NULL) ||
(strstr(rominfo.domestic,"WONDER LIBRARY") != NULL) ||
(strstr(rominfo.product,"T-5740") != NULL))
{
/* enable CD hardware */
system_hw = SYSTEM_MCD;
/* copy ROM to CD cartridge area */
memcpy(scd.cartridge.area, cart.rom, sizeof(scd.cartridge.area));
/* load CD BOOT ROM */
if (load_bios())
/* check if console hardware is set to AUTO */
if (config.system == 0x00)
{
/* no CD image loaded */
cdd.loaded = 0;
/* auto-enable CD hardware */
system_hw = SYSTEM_MCD;
/* clear CD TOC */
cdd_unload();
/* try to load CD BOOTROM */
if (load_bios())
{
/* boot from cartridge */
scd.cartridge.boot = 0x40;
/* boot from cartridge */
scd.cartridge.boot = 0x40;
}
else
{
/* assume Mega Drive hardware */
system_hw = SYSTEM_MD;
/* copy back to cartridge ROM area */
memcpy(cart.rom, scd.cartridge.area, sizeof(scd.cartridge.area));
/* automatically load associated .iso image */
strncpy(&filename[strlen(filename) - 4], ".iso", 4);
cdd_load(filename, (char *)cdc.ram);
}
else
{
/* if not found, disable CD hardware */
system_hw = SYSTEM_MD;
}
}
}
@ -777,11 +761,14 @@ int load_rom(char *filename)
system_hw = config.system;
}
/* Master System or Game Gear BIOS ROM are loaded within $400000-$4FFFFF area */
if ((cart.romsize > 0x400000) || (system_hw == SYSTEM_MCD))
/* restore previous input settings */
if (old_system[0] != -1)
{
/* Mark both BIOS as unloaded if loaded ROM is overwriting them */
system_bios &= ~(SYSTEM_SMS | SYSTEM_GG);
input.system[0] = old_system[0];
}
if (old_system[1] != -1)
{
input.system[1] = old_system[1];
}
return(1);

View File

@ -61,7 +61,6 @@ typedef struct
/* Global variables */
extern ROMINFO rominfo;
extern char rom_filename[256];
extern uint8 romtype;
/* Function prototypes */
@ -70,6 +69,7 @@ extern int load_rom(char *filename);
extern void get_region(char *romheader);
extern char *get_company(void);
extern char *get_peripheral(int index);
extern void getrominfo(char *romheader);
#endif /* _LOADROM_H_ */

View File

@ -320,41 +320,42 @@ unsigned int ctrl_io_read_byte(unsigned int address)
#ifdef LOG_SCD
error("[%d][%d]read byte CD register %X (%X)\n", v_counter, m68k.cycles, address, m68k.pc);
#endif
/* Memory Mode */
if (address == 0xa12003)
if (system_hw == SYSTEM_MCD)
{
m68k_poll_detect(0x03);
return scd.regs[0x03>>1].byte.l;
}
/* SUB-CPU communication flags */
if (address == 0xa1200f)
{
m68k_poll_detect(0x0f);
return scd.regs[0x0f>>1].byte.l;
}
/* default registers */
if (address < 0xa12030)
{
/* SUB-CPU communication words */
if (address >= 0xa12020)
/* Memory Mode */
if (address == 0xa12003)
{
m68k_poll_detect((address - 0x10) & 0x1f);
m68k_poll_detect(0x03);
return scd.regs[0x03>>1].byte.l;
}
/* register LSB */
if (address & 1)
/* SUB-CPU communication flags */
if (address == 0xa1200f)
{
return scd.regs[(address >> 1) & 0xff].byte.l;
m68k_poll_detect(0x0f);
return scd.regs[0x0f>>1].byte.l;
}
/* default registers */
if (address < 0xa12030)
{
/* SUB-CPU communication words */
if (address >= 0xa12020)
{
m68k_poll_detect((address - 0x10) & 0x1f);
}
/* register LSB */
if (address & 1)
{
return scd.regs[(address >> 1) & 0xff].byte.l;
}
/* register MSB */
return scd.regs[(address >> 1) & 0xff].byte.h;
}
/* register MSB */
return scd.regs[(address >> 1) & 0xff].byte.h;
}
/* invalid address */
return m68k_read_bus_8(address);
}
@ -438,35 +439,38 @@ unsigned int ctrl_io_read_word(unsigned int address)
#ifdef LOG_SCD
error("[%d][%d]read word CD register %X (%X)\n", v_counter, m68k.cycles, address, m68k.pc);
#endif
/* Memory Mode */
if (address == 0xa12002)
if (system_hw == SYSTEM_MCD)
{
m68k_poll_detect(0x03);
return scd.regs[0x03>>1].w;
}
/* CDC host data (word access only ?) */
if (address == 0xa12008)
{
return cdc_host_r();
}
/* H-INT vector (word access only ?) */
if (address == 0xa12006)
{
return *(uint16 *)(m68k.memory_map[0].base + 0x72);
}
/* default registers */
if (address < 0xa12030)
{
/* SUB-CPU communication words */
if (address >= 0xa12020)
/* Memory Mode */
if (address == 0xa12002)
{
m68k_poll_detect((address - 0x10) & 0x1e);
m68k_poll_detect(0x03);
return scd.regs[0x03>>1].w;
}
/* CDC host data (word access only ?) */
if (address == 0xa12008)
{
return cdc_host_r();
}
/* H-INT vector (word access only ?) */
if (address == 0xa12006)
{
return *(uint16 *)(m68k.memory_map[0].base + 0x72);
}
/* default registers */
if (address < 0xa12030)
{
/* SUB-CPU communication words */
if (address >= 0xa12020)
{
m68k_poll_detect((address - 0x10) & 0x1e);
}
return scd.regs[(address >> 1) & 0xff].w;
}
return scd.regs[(address >> 1) & 0xff].w;
}
/* invalid address */
@ -559,161 +563,167 @@ void ctrl_io_write_byte(unsigned int address, unsigned int data)
#ifdef LOG_SCD
error("[%d][%d]write byte CD register %X -> 0x%02X (%X)\n", v_counter, m68k.cycles, address, data, m68k.pc);
#endif
switch (address & 0xff)
if (system_hw == SYSTEM_MCD)
{
case 0x00: /* SUB-CPU interrupt */
switch (address & 0xff)
{
/* IFL2 bit */
if (data & 0x01)
case 0x00: /* SUB-CPU interrupt */
{
/* level 2 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x04)
/* IFL2 bit */
if (data & 0x01)
{
/* relative SUB-CPU cycle counter */
unsigned int cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE;
/* sync SUB-CPU with MAIN-CPU */
if (!s68k.stopped && (s68k.cycles < cycles))
/* level 2 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x04)
{
s68k_run(cycles);
/* relative SUB-CPU cycle counter */
unsigned int cycles = (m68k.cycles * SCYCLES_PER_LINE) / MCYCLES_PER_LINE;
/* sync SUB-CPU with MAIN-CPU */
if (!s68k.stopped && (s68k.cycles < cycles))
{
s68k_run(cycles);
}
/* set IFL2 flag */
scd.regs[0x00].byte.h |= 0x01;
/* trigger level 2 interrupt */
scd.pending |= (1 << 2);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
/* set IFL2 flag */
scd.regs[0x00].byte.h |= 0x01;
/* trigger level 2 interrupt */
scd.pending |= (1 << 2);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* writing 0 does nothing */
return;
}
case 0x01: /* SUB-CPU control */
{
/* RESET bit */
if (data & 0x01)
{
/* trigger reset on 0->1 transition */
if (!(scd.regs[0x00].byte.l & 0x01))
{
/* reset SUB-CPU */
s68k_pulse_reset();
}
/* BUSREQ bit */
if (data & 0x02)
{
/* SUB-CPU bus requested */
s68k_pulse_halt();
}
else
{
/* SUB-CPU bus released */
s68k_clear_halt();
}
}
else
{
/* SUB-CPU is halted while !RESET is asserted */
s68k_pulse_halt();
}
scd.regs[0x00].byte.l = data;
return;
}
case 0x03: /* Memory mode */
{
m68k_poll_sync(0x02);
/* PRG-RAM 128k bank mapped to $020000-$03FFFF (resp. $420000-$43FFFF) */
m68k.memory_map[scd.cartridge.boot + 0x02].base = scd.prg_ram + ((data & 0xc0) << 11);
m68k.memory_map[scd.cartridge.boot + 0x03].base = m68k.memory_map[scd.cartridge.boot + 0x02].base + 0x10000;
/* check current mode */
if (scd.regs[0x03>>1].byte.l & 0x04)
{
/* DMNA bit */
if (data & 0x02)
{
/* writing 1 to DMNA in 1M mode will return Word-RAM to SUB-CPU in 2M mode */
scd.dmna = 1;
}
else
{
/* writing 0 to DMNA in 1M mode actually set DMNA bit */
data |= 0x02;
/* update BK0-1 & DMNA bits */
scd.regs[0x03>>1].byte.l = (scd.regs[0x03>>1].byte.l & ~0xc2) | (data & 0xc2);
return;
}
}
else
{
/* writing 0 in 2M mode does nothing */
if (data & 0x02)
{
/* Word-RAM is assigned to SUB-CPU */
scd.dmna = 1;
/* clear RET bit */
scd.regs[0x03>>1].byte.l = (scd.regs[0x03>>1].byte.l & ~0xc3) | (data & 0xc2);
return;
}
}
/* update BK0-1 bits */
scd.regs[0x03>>1].byte.l = (scd.regs[0x02>>1].byte.l & ~0xc0) | (data & 0xc0);
return;
}
case 0x0f: /* SUB-CPU communication flags, normally read-only (Space Ace, Dragon's Lair) */
{
/* ROL8 operation */
data = (data << 1) | ((data >> 7) & 1);
}
case 0x0e: /* MAIN-CPU communication flags */
{
m68k_poll_sync(0x0e);
scd.regs[0x0e>>1].byte.h = data;
return;
}
default:
{
/* default registers */
if (address < 0xa12020)
{
/* MAIN-CPU communication words */
if (address >= 0xa12010)
{
m68k_poll_sync(address & 0x1e);
}
/* register LSB */
if (address & 1)
{
scd.regs[(address >> 1) & 0xff].byte.l = data;
return;
}
/* register MSB */
scd.regs[(address >> 1) & 0xff].byte.h = data;
/* writing 0 does nothing */
return;
}
case 0x01: /* SUB-CPU control */
{
/* RESET bit */
if (data & 0x01)
{
/* trigger reset on 0->1 transition */
if (!(scd.regs[0x00].byte.l & 0x01))
{
/* reset SUB-CPU */
s68k_pulse_reset();
}
/* BUSREQ bit */
if (data & 0x02)
{
/* SUB-CPU bus requested */
s68k_pulse_halt();
}
else
{
/* SUB-CPU bus released */
s68k_clear_halt();
}
}
else
{
/* SUB-CPU is halted while !RESET is asserted */
s68k_pulse_halt();
}
scd.regs[0x00].byte.l = data;
return;
}
case 0x03: /* Memory mode */
{
m68k_poll_sync(0x02);
/* PRG-RAM 128k bank mapped to $020000-$03FFFF (resp. $420000-$43FFFF) */
m68k.memory_map[scd.cartridge.boot + 0x02].base = scd.prg_ram + ((data & 0xc0) << 11);
m68k.memory_map[scd.cartridge.boot + 0x03].base = m68k.memory_map[scd.cartridge.boot + 0x02].base + 0x10000;
/* check current mode */
if (scd.regs[0x03>>1].byte.l & 0x04)
{
/* DMNA bit */
if (data & 0x02)
{
/* writing 1 to DMNA in 1M mode will return Word-RAM to SUB-CPU in 2M mode */
scd.dmna = 1;
}
else
{
/* writing 0 to DMNA in 1M mode actually set DMNA bit */
data |= 0x02;
/* update BK0-1 & DMNA bits */
scd.regs[0x03>>1].byte.l = (scd.regs[0x03>>1].byte.l & ~0xc2) | (data & 0xc2);
return;
}
}
else
{
/* writing 0 in 2M mode does nothing */
if (data & 0x02)
{
/* Word-RAM is assigned to SUB-CPU */
scd.dmna = 1;
/* clear RET bit */
scd.regs[0x03>>1].byte.l = (scd.regs[0x03>>1].byte.l & ~0xc3) | (data & 0xc2);
return;
}
}
/* update BK0-1 bits */
scd.regs[0x03>>1].byte.l = (scd.regs[0x02>>1].byte.l & ~0xc0) | (data & 0xc0);
return;
}
case 0x0f: /* SUB-CPU communication flags, normally read-only (Space Ace, Dragon's Lair) */
{
/* ROL8 operation */
data = (data << 1) | ((data >> 7) & 1);
}
case 0x0e: /* MAIN-CPU communication flags */
{
m68k_poll_sync(0x0e);
scd.regs[0x0e>>1].byte.h = data;
return;
}
default:
{
/* default registers */
if (address < 0xa12020)
{
/* MAIN-CPU communication words */
if (address >= 0xa12010)
{
m68k_poll_sync(address & 0x1e);
}
/* register LSB */
if (address & 1)
{
scd.regs[(address >> 1) & 0xff].byte.l = data;
return;
}
/* register MSB */
scd.regs[(address >> 1) & 0xff].byte.h = data;
return;
}
/* invalid address */
m68k_unused_8_w(address, data);
return;
}
/* invalid address */
m68k_unused_8_w(address, data);
return;
}
}
m68k_unused_8_w(address, data);
return;
}
case 0x30: /* TIME */
@ -783,141 +793,147 @@ void ctrl_io_write_word(unsigned int address, unsigned int data)
#ifdef LOG_SCD
error("[%d][%d]write word CD register %X -> 0x%04X (%X)\n", v_counter, m68k.cycles, address, data, m68k.pc);
#endif
switch (address & 0xfe)
if (system_hw == SYSTEM_MCD)
{
case 0x00: /* SUB-CPU interrupt & control */
switch (address & 0xfe)
{
/* RESET bit */
if (data & 0x01)
case 0x00: /* SUB-CPU interrupt & control */
{
/* trigger reset on 0->1 transition */
if (!(scd.regs[0x00].byte.l & 0x01))
/* RESET bit */
if (data & 0x01)
{
/* reset SUB-CPU */
s68k_pulse_reset();
}
/* trigger reset on 0->1 transition */
if (!(scd.regs[0x00].byte.l & 0x01))
{
/* reset SUB-CPU */
s68k_pulse_reset();
}
/* BUSREQ bit */
if (data & 0x02)
/* BUSREQ bit */
if (data & 0x02)
{
/* SUB-CPU bus requested */
s68k_pulse_halt();
}
else
{
/* SUB-CPU bus released */
s68k_clear_halt();
}
}
else
{
/* SUB-CPU bus requested */
/* SUB-CPU is halted while !RESET is asserted */
s68k_pulse_halt();
}
else
/* IFL2 bit */
if (data & 0x100)
{
/* SUB-CPU bus released */
s68k_clear_halt();
}
}
else
{
/* SUB-CPU is halted while !RESET is asserted */
s68k_pulse_halt();
}
/* level 2 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x04)
{
/* set IFL2 flag */
scd.regs[0x00].byte.h |= 0x01;
/* IFL2 bit */
if (data & 0x100)
{
/* level 2 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x04)
{
/* set IFL2 flag */
scd.regs[0x00].byte.h |= 0x01;
/* trigger level 2 interrupt */
scd.pending |= (1 << 2);
/* trigger level 2 interrupt */
scd.pending |= (1 << 2);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* update LSB only */
scd.regs[0x00].byte.l = data & 0xff;
return;
}
case 0x02: /* Memory Mode */
{
m68k_poll_sync(0x02);
/* PRG-RAM 128k bank mapped to $020000-$03FFFF (resp. $420000-$43FFFF) */
m68k.memory_map[scd.cartridge.boot + 0x02].base = scd.prg_ram + ((data & 0xc0) << 11);
m68k.memory_map[scd.cartridge.boot + 0x03].base = m68k.memory_map[scd.cartridge.boot + 0x02].base + 0x10000;
/* check current mode */
if (scd.regs[0x03>>1].byte.l & 0x04)
{
/* DMNA bit */
if (data & 0x02)
{
/* writing 1 to DMNA in 1M mode will return Word-RAM to SUB-CPU in 2M mode */
scd.dmna = 1;
}
else
{
/* writing 0 to DMNA in 1M mode actually set DMNA bit */
data |= 0x02;
/* update WP0-7, BK0-1 & DMNA bits */
scd.regs[0x02>>1].w = (scd.regs[0x02>>1].w & ~0xffc2) | (data & 0xffc2);
return;
}
}
else
{
/* writing 0 in 2M mode does nothing */
if (data & 0x02)
{
/* Word-RAM is assigned to SUB-CPU */
scd.dmna = 1;
/* clear RET bit */
scd.regs[0x02>>1].w = (scd.regs[0x02>>1].w & ~0xffc3) | (data & 0xffc2);
return;
}
}
/* update WP0-7 & BK0-1 bits */
scd.regs[0x02>>1].w = (scd.regs[0x02>>1].w & ~0xffc0) | (data & 0xffc0);
return;
}
case 0x06: /* H-INT vector (word access only ?) */
{
*(uint16 *)(m68k.memory_map[0].base + 0x72) = data;
return;
}
case 0x0e: /* MAIN-CPU communication flags */
{
m68k_poll_sync(0x0e);
/* LSB is read-only (Mortal Kombat) */
scd.regs[0x0e>>1].byte.h = data;
return;
}
default:
{
if (address < 0xa12020)
{
/* MAIN-CPU communication words */
if (address >= 0xa12010)
{
m68k_poll_sync(address & 0x1e);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* default registers */
scd.regs[(address >> 1) & 0xff].w = data;
/* update LSB only */
scd.regs[0x00].byte.l = data & 0xff;
return;
}
/* invalid address */
m68k_unused_16_w (address, data);
return;
case 0x02: /* Memory Mode */
{
m68k_poll_sync(0x02);
/* PRG-RAM 128k bank mapped to $020000-$03FFFF (resp. $420000-$43FFFF) */
m68k.memory_map[scd.cartridge.boot + 0x02].base = scd.prg_ram + ((data & 0xc0) << 11);
m68k.memory_map[scd.cartridge.boot + 0x03].base = m68k.memory_map[scd.cartridge.boot + 0x02].base + 0x10000;
/* check current mode */
if (scd.regs[0x03>>1].byte.l & 0x04)
{
/* DMNA bit */
if (data & 0x02)
{
/* writing 1 to DMNA in 1M mode will return Word-RAM to SUB-CPU in 2M mode */
scd.dmna = 1;
}
else
{
/* writing 0 to DMNA in 1M mode actually set DMNA bit */
data |= 0x02;
/* update WP0-7, BK0-1 & DMNA bits */
scd.regs[0x02>>1].w = (scd.regs[0x02>>1].w & ~0xffc2) | (data & 0xffc2);
return;
}
}
else
{
/* writing 0 in 2M mode does nothing */
if (data & 0x02)
{
/* Word-RAM is assigned to SUB-CPU */
scd.dmna = 1;
/* clear RET bit */
scd.regs[0x02>>1].w = (scd.regs[0x02>>1].w & ~0xffc3) | (data & 0xffc2);
return;
}
}
/* update WP0-7 & BK0-1 bits */
scd.regs[0x02>>1].w = (scd.regs[0x02>>1].w & ~0xffc0) | (data & 0xffc0);
return;
}
case 0x06: /* H-INT vector (word access only ?) */
{
*(uint16 *)(m68k.memory_map[0].base + 0x72) = data;
return;
}
case 0x0e: /* MAIN-CPU communication flags */
{
m68k_poll_sync(0x0e);
/* LSB is read-only (Mortal Kombat) */
scd.regs[0x0e>>1].byte.h = data;
return;
}
default:
{
if (address < 0xa12020)
{
/* MAIN-CPU communication words */
if (address >= 0xa12010)
{
m68k_poll_sync(address & 0x1e);
}
/* default registers */
scd.regs[(address >> 1) & 0xff].w = data;
return;
}
/* invalid address */
m68k_unused_16_w (address, data);
return;
}
}
}
m68k_unused_16_w (address, data);
return;
}
case 0x30: /* TIME */
@ -1089,7 +1105,7 @@ void vdp_write_byte(unsigned int address, unsigned int data)
{
if (address & 1)
{
psg_write(m68k.cycles, data);
SN76489_Write(m68k.cycles, data);
return;
}
m68k_unused_8_w(address, data);
@ -1135,7 +1151,7 @@ void vdp_write_word(unsigned int address, unsigned int data)
case 0x10: /* PSG */
case 0x14:
{
psg_write(m68k.cycles, data & 0xFF);
SN76489_Write(m68k.cycles, data & 0xFF);
return;
}

View File

@ -294,7 +294,7 @@ void zbank_write_vdp(unsigned int address, unsigned int data)
{
if (address & 1)
{
psg_write(Z80.cycles, data);
SN76489_Write(Z80.cycles, data);
return;
}
zbank_unused_w(address, data);

View File

@ -231,7 +231,7 @@ void z80_md_port_w(unsigned int port, unsigned char data)
case 0x40:
case 0x41:
{
psg_write(Z80.cycles, data);
SN76489_Write(Z80.cycles, data);
return;
}
@ -340,7 +340,7 @@ void z80_gg_port_w(unsigned int port, unsigned char data)
case 0x40:
case 0x41:
{
psg_write(Z80.cycles, data);
SN76489_Write(Z80.cycles, data);
return;
}
@ -437,7 +437,7 @@ void z80_ms_port_w(unsigned int port, unsigned char data)
case 0x40:
case 0x41:
{
psg_write(Z80.cycles, data);
SN76489_Write(Z80.cycles, data);
return;
}
@ -543,7 +543,7 @@ void z80_m3_port_w(unsigned int port, unsigned char data)
case 0x40:
case 0x41:
{
psg_write(Z80.cycles, data);
SN76489_Write(Z80.cycles, data);
return;
}
@ -630,7 +630,7 @@ void z80_sg_port_w(unsigned int port, unsigned char data)
case 0x40:
case 0x41:
{
psg_write(Z80.cycles, data);
SN76489_Write(Z80.cycles, data);
return;
}

View File

@ -1,354 +0,0 @@
/* Finite impulse response (FIR) resampler with adjustable FIR size */
/* Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
/* C Conversion by Eke-Eke for use in Genesis Plus GX (2009). */
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "Fir_Resampler.h"
#include "macros.h"
/* sound buffer */
static sample_t *buffer = NULL;
static int buffer_size = 0;
static sample_t impulses[MAX_RES][WIDTH];
static sample_t* write_pos = NULL;
static int res = 1;
static int imp_phase = 0;
static unsigned long skip_bits = 0;
static int step = STEREO;
static int input_per_cycle;
static double ratio = 1.0;
static void gen_sinc(double rolloff, int width, double offset, double spacing, double scale, int count, sample_t *out )
{
double w, rolloff_cos_a, num, den, sinc;
double const maxh = 256;
double const fstep = M_PI / maxh * spacing;
double const to_w = maxh * 2 / width;
double const pow_a_n = pow( rolloff, maxh );
double angle = (count / 2 - 1 + offset) * -fstep;
scale /= maxh * 2;
do
{
*out++ = 0;
w = angle * to_w;
if ( fabs( w ) < M_PI )
{
rolloff_cos_a = rolloff * cos( angle );
num = 1 - rolloff_cos_a -
pow_a_n * cos( maxh * angle ) +
pow_a_n * rolloff * cos( (maxh - 1) * angle );
den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
sinc = scale * num / den - scale;
out [-1] = (short) (cos( w ) * sinc + sinc);
}
angle += fstep;
}
while(--count);
}
/*static int available( long input_count )
{
int cycle_count = input_count / input_per_cycle;
int output_count = cycle_count * res * STEREO;
input_count -= cycle_count * input_per_cycle;
unsigned long skip = skip_bits >> imp_phase;
int remain = res - imp_phase;
while ( input_count >= 0 )
{
input_count -= step + (skip & 1) * STEREO;
skip >>= 1;
if ( !--remain )
{
skip = skip_bits;
remain = res;
}
output_count += 2;
}
return output_count;
}
*/
int Fir_Resampler_avail()
{
long count = 0;
sample_t* in = buffer;
sample_t* end_pos = write_pos;
unsigned long skip = skip_bits >> imp_phase;
int remain = res - imp_phase;
if ( end_pos - in >= WIDTH * STEREO )
{
end_pos -= WIDTH * STEREO;
do
{
count++;
remain--;
in += (skip * STEREO) & STEREO;
skip >>= 1;
in += step;
if ( !remain )
{
skip = skip_bits;
remain = res;
}
}
while ( in <= end_pos );
}
return count;
}
int Fir_Resampler_initialize( int new_size )
{
res = 1;
skip_bits = 0;
imp_phase = 0;
step = STEREO;
ratio = 1.0;
buffer = (sample_t *) realloc( buffer, (new_size + WRITE_OFFSET) * sizeof (sample_t) );
write_pos = 0;
if ( !buffer ) return 0;
buffer_size = new_size + WRITE_OFFSET;
Fir_Resampler_clear();
return 1;
}
void Fir_Resampler_shutdown( void )
{
if (buffer) free(buffer);
buffer = 0;
buffer_size = 0;
write_pos = 0;
}
void Fir_Resampler_clear()
{
imp_phase = 0;
if ( buffer_size )
{
write_pos = &buffer [WRITE_OFFSET];
memset( buffer, 0, buffer_size * sizeof (sample_t) );
}
}
double Fir_Resampler_time_ratio( double new_factor, double rolloff )
{
int i, r;
double nearest, error, filter;
double fstep = 0.0;
double least_error = 2;
double pos = 0.0;
res = -1;
for ( r = 1; r <= MAX_RES; r++ )
{
pos += new_factor;
nearest = floor( pos + 0.5 );
error = fabs( pos - nearest );
if ( error < least_error )
{
res = r;
fstep = nearest / res;
least_error = error;
}
}
skip_bits = 0;
step = STEREO * (int) floor( fstep );
ratio = fstep;
fstep = fmod( fstep, 1.0 );
filter = (ratio < 1.0) ? 1.0 : 1.0 / ratio;
pos = 0.0;
input_per_cycle = 0;
memset(impulses, 0, MAX_RES*WIDTH*sizeof(sample_t));
for ( i = 0; i < res; i++ )
{
gen_sinc( rolloff, (int) (WIDTH * filter + 1) & ~1, pos, filter,
(double) (0x7FFF * GAIN * filter),
(int) WIDTH, impulses[i] );
pos += fstep;
input_per_cycle += step;
if ( pos >= 0.9999999 )
{
pos -= 1.0;
skip_bits |= 1 << i;
input_per_cycle++;
}
}
Fir_Resampler_clear();
return ratio;
}
/* Current ratio */
double Fir_Resampler_ratio( void )
{
return ratio;
}
/* Number of input samples that can be written */
int Fir_Resampler_max_write( void )
{
return buffer + buffer_size - write_pos;
}
/* Pointer to place to write input samples */
sample_t* Fir_Resampler_buffer( void )
{
return write_pos;
}
/* Number of input samples in buffer */
int Fir_Resampler_written( void )
{
return write_pos - &buffer [WRITE_OFFSET];
}
/* Number of output samples available */
/*int Fir_Resampler_avail( void )
{
return available( write_pos - &buffer [WIDTH * STEREO] );
}*/
void Fir_Resampler_write( long count )
{
write_pos += count;
}
int Fir_Resampler_read( sample_t* out, long count )
{
sample_t* out_ = out;
sample_t* in = buffer;
sample_t* end_pos = write_pos;
unsigned long skip = skip_bits >> imp_phase;
sample_t const* imp = impulses [imp_phase];
int remain = res - imp_phase;
int n;
int pt0,pt1;
sample_t* i;
long l,r;
if ( end_pos - in >= WIDTH * STEREO )
{
end_pos -= WIDTH * STEREO;
do
{
count--;
if ( count < 0 )
break;
/* accumulate in extended precision */
l = 0;
r = 0;
i = in;
for ( n = WIDTH / 2; n; --n )
{
pt0 = imp [0];
l += pt0 * i [0];
r += pt0 * i [1];
pt1 = imp [1];
imp += 2;
l += pt1 * i [2];
r += pt1 * i [3];
i += 4;
}
remain--;
l >>= 15;
r >>= 15;
in += (skip * STEREO) & STEREO;
skip >>= 1;
in += step;
if ( !remain )
{
imp = impulses [0];
skip = skip_bits;
remain = res;
}
*out++ = (sample_t) l;
*out++ = (sample_t) r;
}
while ( in <= end_pos );
}
imp_phase = res - remain;
n = write_pos - in;
write_pos = &buffer [n];
memmove( buffer, in, n * sizeof *in );
return out - out_;
}
/* fixed (Eke_Eke) */
int Fir_Resampler_input_needed( long output_count )
{
long input_count = 0;
unsigned long skip = skip_bits >> imp_phase;
int remain = res - imp_phase;
while ( (output_count) > 0 )
{
input_count += step + (skip & 1) * STEREO;
skip >>= 1;
if ( !--remain )
{
skip = skip_bits;
remain = res;
}
output_count --;
}
input_count -= (write_pos - &buffer [WRITE_OFFSET]);
if ( input_count < 0 )
input_count = 0;
return (input_count >> 1);
}
int Fir_Resampler_skip_input( long count )
{
int remain = write_pos - buffer;
int max_count = remain - WIDTH * STEREO;
if ( count > max_count )
count = max_count;
remain -= count;
write_pos = &buffer [remain];
memmove( buffer, &buffer [count], remain * sizeof buffer [0] );
return count;
}

View File

@ -1,43 +0,0 @@
/* Finite impulse response (FIR) resampler with adjustable FIR size */
/* Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
/* C Conversion by Eke-Eke for use in Genesis Plus GX (2009). */
#ifndef FIR_RESAMPLER_H
#define FIR_RESAMPLER_H
#define STEREO 2
#define MAX_RES 32
#define WIDTH 16
#define WRITE_OFFSET (WIDTH * STEREO) - STEREO
#define GAIN 1.0
typedef signed int sample_t;
extern int Fir_Resampler_initialize( int new_size );
extern void Fir_Resampler_shutdown( void );
extern void Fir_Resampler_clear( void );
extern double Fir_Resampler_time_ratio( double new_factor, double rolloff );
extern double Fir_Resampler_ratio( void );
extern int Fir_Resampler_max_write( void );
extern sample_t* Fir_Resampler_buffer( void );
extern int Fir_Resampler_written( void );
extern int Fir_Resampler_avail( void );
extern void Fir_Resampler_write( long count );
extern int Fir_Resampler_read( sample_t* out, long count );
extern int Fir_Resampler_input_needed( long output_count );
extern int Fir_Resampler_skip_input( long count );
#endif

View File

@ -1,139 +0,0 @@
/* http://www.slack.net/~ant/ */
#include "blip.h"
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
enum { buf_extra = 2 }; /* extra samples to save past end */
enum { time_bits = 16 }; /* bits in fraction of fixed-point sample counts */
enum { time_unit = 1 << time_bits };
enum { phase_bits = 15 }; /* bits in fraction of deltas in buffer */
enum { phase_count = 1 << phase_bits };
enum { phase_shift = time_bits - phase_bits };
typedef int buf_t; /* type of element in delta buffer */
struct blip_buffer_t
{
int factor; /* clocks to samples conversion factor */
int offset; /* fractional position of clock 0 in delta buffer */
int amp; /* current output amplitude (sum of all deltas up to now) */
int size; /* size of delta buffer */
buf_t buf [65536]; /* delta buffer, only size elements actually allocated */
};
blip_buffer_t* blip_alloc( double clock_rate, double sample_rate, int size )
{
/* Allocate space for structure and delta buffer */
blip_buffer_t* s = (blip_buffer_t*) malloc(
offsetof (blip_buffer_t, buf) + (size + buf_extra) * sizeof (buf_t) );
if ( s != NULL )
{
/* Calculate output:input ratio and convert to fixed-point */
double ratio = sample_rate / clock_rate;
s->factor = (int) (ratio * time_unit + 0.5);
s->size = size;
blip_clear( s );
}
return s;
}
void blip_free( blip_buffer_t* s )
{
free( s );
}
void blip_clear( blip_buffer_t* s )
{
s->offset = 0;
s->amp = 0;
memset( s->buf, 0, (s->size + buf_extra) * sizeof (buf_t) );
}
void blip_add( blip_buffer_t* s, int clocks, int delta )
{
/* Convert to fixed-point time in terms of output samples */
int fixed_time = clocks * s->factor + s->offset;
/* Extract whole and fractional parts */
int index = fixed_time >> time_bits; /* whole */
int phase = fixed_time >> phase_shift & (phase_count - 1); /* fraction */
/* Split delta between first and second samples */
int second = delta * phase;
int first = delta * phase_count - second;
/* Add deltas to buffer */
s->buf [index ] += first;
s->buf [index+1] += second;
}
int blip_clocks_needed( const blip_buffer_t* s, int samples )
{
/* Fixed-point number of samples needed in addition to those in buffer */
int fixed_needed = samples * time_unit - s->offset;
/* If more are needed, convert to clocks and round up */
return (fixed_needed <= 0) ? 0 : (fixed_needed - 1) / s->factor + 1;
}
void blip_end_frame( blip_buffer_t* s, int clocks )
{
s->offset += clocks * s->factor;
}
int blip_samples_avail( const blip_buffer_t* s )
{
return s->offset >> time_bits;
}
int blip_read_samples( blip_buffer_t* s, short out[], int stereo)
{
int count = s->offset >> time_bits;
if ( count )
{
/* Sum deltas and write out */
int i, sample;
for ( i = 0; i < count; ++i )
{
/* Apply slight high-pass filter */
s->amp -= s->amp >> 9;
/* Add next delta */
s->amp += s->buf [i];
/* Calculate output sample */
sample = s->amp >> phase_bits;
/* Keep within 16-bit sample range */
if ( sample < -32768 ) sample = -32768;
if ( sample > +32767 ) sample = +32767;
out [i << stereo] = sample;
}
/* Copy remaining samples to beginning of buffer and clear the rest */
memmove( s->buf, &s->buf [count], buf_extra * sizeof (buf_t) );
memset( &s->buf [buf_extra], 0, count * sizeof (buf_t) );
/* Remove samples */
s->offset -= count * time_unit;
}
return count;
}

View File

@ -1,49 +0,0 @@
/* Fast sound synthesis buffer for use in real-time emulators of electronic
sound generator chips like those in early video game consoles. Uses linear
interpolation. Higher-quality versions are available that use sinc-based
band-limited synthesis. */
#ifndef BLIP_H
#define BLIP_H
#ifdef __cplusplus
extern "C" {
#endif
/* Creates a new blip_buffer with specified input clock rate, output
sample rate, and size (in samples), or returns NULL if out of memory. */
typedef struct blip_buffer_t blip_buffer_t;
blip_buffer_t* blip_alloc( double clock_rate, double sample_rate, int size );
/* Frees memory used by a blip_buffer. No effect if NULL is passed. */
void blip_free( blip_buffer_t* );
/* Removes all samples and clears buffer. */
void blip_clear( blip_buffer_t* );
/* Adds an amplitude transition of delta at specified time in source clocks.
Delta can be negative. */
void blip_add( blip_buffer_t*, int time, int delta );
/* Number of additional clocks needed until n samples will be available.
If buffer cannot even hold n samples, returns number of clocks until buffer
becomes full. */
int blip_clocks_needed( const blip_buffer_t*, int samples_needed );
/* Ends current time frame of specified duration and make its samples available
(along with any still-unread samples) for reading with read_samples(), then
begins a new time frame at the end of the current frame. */
void blip_end_frame( blip_buffer_t*, int duration );
/* Number of samples available for reading with read(). */
int blip_samples_avail( const blip_buffer_t* );
/* Reads at most n samples out of buffer into out, removing them from from
the buffer. */
int blip_read_samples( blip_buffer_t*, short out [], int stereo);
#ifdef __cplusplus
}
#endif
#endif

409
source/sound/blip_buf.c Normal file
View File

@ -0,0 +1,409 @@
/* blip_buf $vers. http://www.slack.net/~ant/ */
/* Modified for Genesis Plus GX by EkeEke (01/09/12) */
/* - disabled assertions checks (define #BLIP_ASSERT to re-enable) */
/* - fixed multiple time-frames support & removed m->avail */
/* - modified blip_read_samples to always output to stereo streams */
/* - added blip_mix_samples function (see blip_buf.h) */
#include "blip_buf.h"
#define BLIP_ASSERT 1
#ifdef BLIP_ASSERT
#include <assert.h>
#endif
#include <limits.h>
#include <string.h>
#include <stdlib.h>
/* Library Copyright (C) 2003-2009 Shay Green. This library is free software;
you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
library 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#if defined (BLARGG_TEST) && BLARGG_TEST
#include "blargg_test.h"
#endif
/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000.
Avoids constants that don't fit in 32 bits. */
#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF
typedef unsigned long fixed_t;
enum { pre_shift = 32 };
#elif defined(ULLONG_MAX)
typedef unsigned long long fixed_t;
enum { pre_shift = 32 };
#else
typedef unsigned fixed_t;
enum { pre_shift = 0 };
#endif
enum { time_bits = pre_shift + 20 };
static fixed_t const time_unit = (fixed_t) 1 << time_bits;
enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */
enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */
enum { half_width = 8 };
enum { buf_extra = half_width*2 + end_frame_extra };
enum { phase_bits = 5 };
enum { phase_count = 1 << phase_bits };
enum { delta_bits = 15 };
enum { delta_unit = 1 << delta_bits };
enum { frac_bits = time_bits - pre_shift };
/* We could eliminate avail and encode whole samples in offset, but that would
limit the total buffered samples to blip_max_frame. That could only be
increased by decreasing time_bits, which would reduce resample ratio accuracy.
*/
struct blip_t
{
fixed_t factor;
fixed_t offset;
int size;
int integrator;
};
typedef int buf_t;
/* probably not totally portable */
#define SAMPLES( buf ) ((buf_t*) ((buf) + 1))
/* Arithmetic (sign-preserving) right shift */
#define ARITH_SHIFT( n, shift ) \
((n) >> (shift))
enum { max_sample = +32767 };
enum { min_sample = -32768 };
#define CLAMP( n ) \
{\
if ( n > max_sample ) n = max_sample;\
else if ( n < min_sample) n = min_sample;\
}
#ifdef BLIP_ASSERT
static void check_assumptions( void )
{
int n;
#if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF
#error "int must be at least 32 bits"
#endif
assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */
n = max_sample * 2;
CLAMP( n );
assert( n == max_sample );
n = min_sample * 2;
CLAMP( n );
assert( n == min_sample );
assert( blip_max_ratio <= time_unit );
assert( blip_max_frame <= (fixed_t) -1 >> time_bits );
}
#endif
blip_t* blip_new( int size )
{
blip_t* m;
#ifdef BLIP_ASSERT
assert( size >= 0 );
#endif
m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) );
if ( m )
{
m->factor = time_unit / blip_max_ratio;
m->size = size;
blip_clear( m );
#ifdef BLIP_ASSERT
check_assumptions();
#endif
}
return m;
}
void blip_delete( blip_t* m )
{
if ( m != NULL )
{
/* Clear fields in case user tries to use after freeing */
memset( m, 0, sizeof *m );
free( m );
}
}
void blip_set_rates( blip_t* m, double clock_rate, double sample_rate )
{
double factor = time_unit * sample_rate / clock_rate;
m->factor = (fixed_t) factor;
#ifdef BLIP_ASSERT
/* Fails if clock_rate exceeds maximum, relative to sample_rate */
assert( 0 <= factor - m->factor && factor - m->factor < 1 );
#endif
/* Avoid requiring math.h. Equivalent to
m->factor = (int) ceil( factor ) */
if ( m->factor < factor )
m->factor++;
/* At this point, factor is most likely rounded up, but could still
have been rounded down in the floating-point calculation. */
}
void blip_clear( blip_t* m )
{
/* We could set offset to 0, factor/2, or factor-1. 0 is suitable if
factor is rounded up. factor-1 is suitable if factor is rounded down.
Since we don't know rounding direction, factor/2 accommodates either,
with the slight loss of showing an error in half the time. Since for
a 64-bit factor this is years, the halving isn't a problem. */
m->offset = m->factor / 2;
m->integrator = 0;
memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) );
}
int blip_clocks_needed( const blip_t* m, int samples )
{
fixed_t needed;
#ifdef BLIP_ASSERT
/* Fails if buffer can't hold that many more samples */
assert( (samples >= 0) && (((m->offset >> time_bits) + samples) <= m->size) );
#endif
needed = (fixed_t) samples * time_unit;
if ( needed < m->offset )
return 0;
return (needed - m->offset + m->factor - 1) / m->factor;
}
void blip_end_frame( blip_t* m, unsigned t )
{
m->offset += t * m->factor;
#ifdef BLIP_ASSERT
/* Fails if buffer size was exceeded */
assert( (m->offset >> time_bits) <= m->size );
#endif
}
int blip_samples_avail( const blip_t* m )
{
return (m->offset >> time_bits);
}
static void remove_samples( blip_t* m, int count )
{
buf_t* buf = SAMPLES( m );
int remain = (m->offset >> time_bits) + buf_extra - count;
m->offset -= count * time_unit;
memmove( &buf [0], &buf [count], remain * sizeof buf [0] );
memset( &buf [remain], 0, count * sizeof buf [0] );
}
int blip_read_samples( blip_t* m, short out [], int count)
{
#ifdef BLIP_ASSERT
assert( count >= 0 );
if ( count > (m->offset >> time_bits) )
count = m->offset >> time_bits;
if ( count )
#endif
{
buf_t const* in = SAMPLES( m );
buf_t const* end = in + count;
int sum = m->integrator;
int s;
do
{
/* Eliminate fraction */
s = ARITH_SHIFT( sum, delta_bits );
sum += *in++;
CLAMP( s );
*out = s;
out += 2;
/* High-pass filter */
sum -= s << (delta_bits - bass_shift);
}
while ( in != end );
m->integrator = sum;
remove_samples( m, count );
}
return count;
}
int blip_mix_samples( blip_t* m, short out [], int count)
{
#ifdef BLIP_ASSERT
assert( count >= 0 );
if ( count > (m->offset >> time_bits) )
count = m->offset >> time_bits;
if ( count )
#endif
{
buf_t const* in = SAMPLES( m );
buf_t const* end = in + count;
int sum = m->integrator;
int s, temp;
do
{
/* Eliminate fraction */
s = ARITH_SHIFT( sum, delta_bits );
sum += *in++;
/* Add to current buffer */
temp = s + *out;
CLAMP( temp );
*out = temp;
out += 2;
/* High-pass filter */
sum -= s << (delta_bits - bass_shift);
}
while ( in != end );
m->integrator = sum;
remove_samples( m, count );
}
return count;
}
/* Things that didn't help performance on x86:
__attribute__((aligned(128)))
#define short int
restrict
*/
/* Sinc_Generator( 0.9, 0.55, 4.5 ) */
static short const bl_step [phase_count + 1] [half_width] =
{
{ 43, -115, 350, -488, 1136, -914, 5861,21022},
{ 44, -118, 348, -473, 1076, -799, 5274,21001},
{ 45, -121, 344, -454, 1011, -677, 4706,20936},
{ 46, -122, 336, -431, 942, -549, 4156,20829},
{ 47, -123, 327, -404, 868, -418, 3629,20679},
{ 47, -122, 316, -375, 792, -285, 3124,20488},
{ 47, -120, 303, -344, 714, -151, 2644,20256},
{ 46, -117, 289, -310, 634, -17, 2188,19985},
{ 46, -114, 273, -275, 553, 117, 1758,19675},
{ 44, -108, 255, -237, 471, 247, 1356,19327},
{ 43, -103, 237, -199, 390, 373, 981,18944},
{ 42, -98, 218, -160, 310, 495, 633,18527},
{ 40, -91, 198, -121, 231, 611, 314,18078},
{ 38, -84, 178, -81, 153, 722, 22,17599},
{ 36, -76, 157, -43, 80, 824, -241,17092},
{ 34, -68, 135, -3, 8, 919, -476,16558},
{ 32, -61, 115, 34, -60, 1006, -683,16001},
{ 29, -52, 94, 70, -123, 1083, -862,15422},
{ 27, -44, 73, 106, -184, 1152,-1015,14824},
{ 25, -36, 53, 139, -239, 1211,-1142,14210},
{ 22, -27, 34, 170, -290, 1261,-1244,13582},
{ 20, -20, 16, 199, -335, 1301,-1322,12942},
{ 18, -12, -3, 226, -375, 1331,-1376,12293},
{ 15, -4, -19, 250, -410, 1351,-1408,11638},
{ 13, 3, -35, 272, -439, 1361,-1419,10979},
{ 11, 9, -49, 292, -464, 1362,-1410,10319},
{ 9, 16, -63, 309, -483, 1354,-1383, 9660},
{ 7, 22, -75, 322, -496, 1337,-1339, 9005},
{ 6, 26, -85, 333, -504, 1312,-1280, 8355},
{ 4, 31, -94, 341, -507, 1278,-1205, 7713},
{ 3, 35, -102, 347, -506, 1238,-1119, 7082},
{ 1, 40, -110, 350, -499, 1190,-1021, 6464},
{ 0, 43, -115, 350, -488, 1136, -914, 5861}
};
/* Shifting by pre_shift allows calculation using unsigned int rather than
possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient.
And by having pre_shift 32, a 32-bit platform can easily do the shift by
simply ignoring the low half. */
void blip_add_delta( blip_t* m, unsigned time, int delta )
{
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
buf_t* out = SAMPLES( m ) + (fixed >> frac_bits);
int const phase_shift = frac_bits - phase_bits;
int phase = fixed >> phase_shift & (phase_count - 1);
short const* in = bl_step [phase];
short const* rev = bl_step [phase_count - phase];
int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1);
int delta2 = (delta * interp) >> delta_bits;
delta -= delta2;
#ifdef BLIP_ASSERT
/* Fails if buffer size was exceeded */
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
#endif
out [0] += in[0]*delta + in[half_width+0]*delta2;
out [1] += in[1]*delta + in[half_width+1]*delta2;
out [2] += in[2]*delta + in[half_width+2]*delta2;
out [3] += in[3]*delta + in[half_width+3]*delta2;
out [4] += in[4]*delta + in[half_width+4]*delta2;
out [5] += in[5]*delta + in[half_width+5]*delta2;
out [6] += in[6]*delta + in[half_width+6]*delta2;
out [7] += in[7]*delta + in[half_width+7]*delta2;
in = rev;
out [ 8] += in[7]*delta + in[7-half_width]*delta2;
out [ 9] += in[6]*delta + in[6-half_width]*delta2;
out [10] += in[5]*delta + in[5-half_width]*delta2;
out [11] += in[4]*delta + in[4-half_width]*delta2;
out [12] += in[3]*delta + in[3-half_width]*delta2;
out [13] += in[2]*delta + in[2-half_width]*delta2;
out [14] += in[1]*delta + in[1-half_width]*delta2;
out [15] += in[0]*delta + in[0-half_width]*delta2;
}
void blip_add_delta_fast( blip_t* m, unsigned time, int delta )
{
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
buf_t* out = SAMPLES( m ) + (fixed >> frac_bits);
int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1);
int delta2 = delta * interp;
#ifdef BLIP_ASSERT
/* Fails if buffer size was exceeded */
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
#endif
out [7] += delta * delta_unit - delta2;
out [8] += delta2;
}

74
source/sound/blip_buf.h Normal file
View File

@ -0,0 +1,74 @@
/** Sample buffer that resamples from input clock rate to output sample rate \file */
/* blip_buf $vers */
#ifndef BLIP_BUF_H
#define BLIP_BUF_H
#ifdef __cplusplus
extern "C" {
#endif
/** First parameter of most functions is blip_t*, or const blip_t* if nothing
is changed. */
typedef struct blip_t blip_t;
/** Creates new buffer that can hold at most sample_count samples. Sets rates
so that there are blip_max_ratio clocks per sample. Returns pointer to new
buffer, or NULL if insufficient memory. */
blip_t* blip_new( int sample_count );
/** Sets approximate input clock rate and output sample rate. For every
clock_rate input clocks, approximately sample_rate samples are generated. */
void blip_set_rates( blip_t*, double clock_rate, double sample_rate );
enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
clock_rate must not be greater than sample_rate*blip_max_ratio. */
blip_max_ratio = 1 << 20 };
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
void blip_clear( blip_t* );
/** Adds positive/negative delta into buffer at specified clock time. */
void blip_add_delta( blip_t*, unsigned int clock_time, int delta );
/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta );
/** Length of time frame, in clocks, needed to make sample_count additional
samples available. */
int blip_clocks_needed( const blip_t*, int sample_count );
enum { /** Maximum number of samples that can be generated from one time frame. */
blip_max_frame = 4000 };
/** Makes input clocks before clock_duration available for reading as output
samples. Also begins new time frame at clock_duration, so that clock time 0 in
the new time frame specifies the same clock as clock_duration in the old time
frame specified. Deltas can have been added slightly past clock_duration (up to
however many clocks there are in two output samples). */
void blip_end_frame( blip_t*, unsigned int clock_duration );
/** Number of buffered samples available for reading. */
int blip_samples_avail( const blip_t* );
/** Reads and removes at most 'count' samples and writes them to to every other
element of 'out', allowing easy interleaving of two buffers into a stereo sample
stream. Outputs 16-bit signed samples. Returns number of samples actually read. */
int blip_read_samples( blip_t*, short out [], int count);
/* Same as above function except sample is added to output buffer previous value */
/* This allows easy mixing of different blip buffers into a single output stream */
int blip_mix_samples( blip_t* m, short out [], int count);
/** Frees buffer. No effect if NULL is passed. */
void blip_delete( blip_t* );
/* Deprecated */
typedef blip_t blip_buffer_t;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -32,10 +32,17 @@
- Removed alternate volume table, panning & mute support (unused)
- Removed configurable Feedback and Shift Register Width (always use Sega ones)
- Added linear resampling using Blip Buffer (based on Blargg's implementation: http://www.smspower.org/forums/viewtopic.php?t=11376)
01/09/12 Eke-Eke (Genesis Plus GX)
- Added generic Blip-Buffer support internally, using common Master Clock as timebase
- Re-added stereo GG support
- Re-added configurable Feedback and Shift Register Width
- Rewrote core with various optimizations
*/
#include "shared.h"
#include "blip.h"
#define PSG_MCYCLES_RATIO (16 * 15)
/* Initial state of shift register */
#define NoiseInitialState 0x8000
@ -44,14 +51,20 @@
/*#define PSG_CUTOFF 0x6*/
#define PSG_CUTOFF 0x1
/* SN76489 clone in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */
#define FB_SEGAVDP 0x0009
/* original Texas Instruments TMS SN76489AN (rev. A) used in SG-1000, SC-3000H & SF-7000 computers */
#define FB_DISCRETE 0x0006
#define SRW_DISCRETE 15
/* SN76489AN clone integrated in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */
#define FB_SEGAVDP 0x0009
#define SRW_SEGAVDP 16
typedef struct
{
/* Configuration */
int BoostNoise; /* double noise volume when non-zero */
int PreAmp[4][2]; /* stereo channels pre-amplification ratio (%) */
int NoiseFeedback;
int SRWidth;
/* PSG registers: */
int Registers[8]; /* Tone, vol x4 */
@ -62,10 +75,12 @@ typedef struct
/* Output calculation variables */
int ToneFreqVals[4]; /* Frequency register values (counters) */
int ToneFreqPos[4]; /* Frequency channel flip-flops */
int Channels[4]; /* Value of each channel, before stereo is applied */
int Channel[4][2]; /* current amplitude of each (stereo) channel */
int ChanOut[4][2]; /* current output value of each (stereo) channel */
/* Internal M-clock counter */
unsigned long clocks;
/* Blip-Buffer variables */
int chan_amp[4]; /* current channel amplitudes in delta buffers */
} SN76489_Context;
static const uint16 PSGVolumeValues[16] =
@ -77,24 +92,33 @@ static const uint16 PSGVolumeValues[16] =
1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0
};
static struct blip_buffer_t* blip; /* delta resampler */
static SN76489_Context SN76489;
void SN76489_Init(double PSGClockValue, int SamplingRate)
{
SN76489_Shutdown();
/* SamplingRate*16 instead of PSGClockValue/16 since division would lose some
precision. blip_alloc doesn't care about the absolute sampling rate, just the
ratio to clock rate. */
blip = blip_alloc(PSGClockValue, SamplingRate * 16.0, SamplingRate / 4);
}
static blip_t* blip[2];
void SN76489_Shutdown(void)
void SN76489_Init(blip_t* left, blip_t* right, int type)
{
if (blip) blip_free(blip);
blip = NULL;
int i;
blip[0] = left;
blip[1] = right;
for (i=0; i<4; i++)
{
SN76489.PreAmp[i][0] = 100;
SN76489.PreAmp[i][1] = 100;
}
if (type == SN_DISCRETE)
{
SN76489.NoiseFeedback = FB_DISCRETE;
SN76489.SRWidth = SRW_DISCRETE;
}
else
{
SN76489.NoiseFeedback = FB_SEGAVDP;
SN76489.SRWidth = SRW_SEGAVDP;
}
}
void SN76489_Reset()
@ -104,50 +128,59 @@ void SN76489_Reset()
for(i = 0; i <= 3; i++)
{
/* Initialise PSG state */
SN76489.Registers[2*i] = 1; /* tone freq=1 */
SN76489.Registers[2*i+1] = 0xf; /* vol=off */
SN76489.Registers[2*i] = 1; /* tone freq=1 */
SN76489.Registers[2*i+1] = 0xf; /* vol=off */
/* Set counters to 0 */
/* Set counters to 0 */
SN76489.ToneFreqVals[i] = 0;
/* Set flip-flops to 1 */
/* Set flip-flops to 1 */
SN76489.ToneFreqPos[i] = 1;
/* Clear channels output */
SN76489.Channels[i] = 0;
/* Clear stereo channels amplitude */
SN76489.Channel[i][0] = 0;
SN76489.Channel[i][1] = 0;
/* Clear current amplitudes in delta buffer */
SN76489.chan_amp[i] = 0;
/* Clear stereo channel outputs in delta buffer */
SN76489.ChanOut[i][0] = 0;
SN76489.ChanOut[i][1] = 0;
}
SN76489.LatchedRegister=0;
/* Initialise latched register index */
SN76489.LatchedRegister = 0;
/* Initialise noise generator */
SN76489.NoiseShiftRegister=NoiseInitialState;
SN76489.NoiseFreq = 0x10;
SN76489.BoostNoise = config.psgBoostNoise;
/* Clear Blip delta buffer */
if (blip) blip_clear(blip);
/* Reset internal M-cycle counter */
SN76489.clocks = 0;
}
void SN76489_BoostNoise(int boost)
void SN76489_Config(int preAmp, int boostNoise, int stereo)
{
SN76489.BoostNoise = boost;
SN76489.Channels[3]= PSGVolumeValues[SN76489.Registers[7]] << boost;
int i;
for (i=0; i<4; i++)
{
/* stereo channel pre-amplification */
SN76489.PreAmp[i][0] = preAmp * ((stereo >> (i*2)) & 1);
SN76489.PreAmp[i][1] = preAmp * ((stereo >> (i*2 + 1)) & 1);
/* noise channel boost */
if (i == 3)
{
SN76489.PreAmp[3][0] = SN76489.PreAmp[3][0] << boostNoise;
SN76489.PreAmp[3][1] = SN76489.PreAmp[3][1] << boostNoise;
}
/* update stereo channel amplitude */
SN76489.Channel[i][0]= (PSGVolumeValues[SN76489.Registers[i*2 + 1]] * SN76489.PreAmp[i][0]) / 100;
SN76489.Channel[i][1]= (PSGVolumeValues[SN76489.Registers[i*2 + 1]] * SN76489.PreAmp[i][1]) / 100;
}
}
void SN76489_SetContext(uint8 *data)
{
memcpy(&SN76489, data, sizeof(SN76489_Context));
}
void SN76489_GetContext(uint8 *data)
{
memcpy(data, &SN76489, sizeof(SN76489_Context));
}
uint8 *SN76489_GetContextPtr(void)
void *SN76489_GetContextPtr(void)
{
return (uint8 *)&SN76489;
}
@ -157,88 +190,63 @@ int SN76489_GetContextSize(void)
return sizeof(SN76489_Context);
}
void SN76489_Write(int data)
{
if (data & 0x80)
{
/* Latch byte %1 cc t dddd */
SN76489.LatchedRegister = (data >> 4) & 0x07;
}
switch (SN76489.LatchedRegister)
{
case 0:
case 2:
case 4: /* Tone channels */
if (data & 0x80)
{
/* Data byte %1 cc t dddd */
SN76489.Registers[SN76489.LatchedRegister] = (SN76489.Registers[SN76489.LatchedRegister] & 0x3f0) | (data & 0xf);
}
else
{
/* Data byte %0 - dddddd */
SN76489.Registers[SN76489.LatchedRegister] = (SN76489.Registers[SN76489.LatchedRegister] & 0x00f) | ((data & 0x3f) << 4);
}
/* Zero frequency changed to 1 to avoid div/0 */
if (SN76489.Registers[SN76489.LatchedRegister] == 0) SN76489.Registers[SN76489.LatchedRegister] = 1;
break;
case 1:
case 3:
case 5: /* Channel attenuation */
SN76489.Registers[SN76489.LatchedRegister] = data & 0x0f;
SN76489.Channels[SN76489.LatchedRegister>>1] = PSGVolumeValues[data&0x0f];
break;
case 6: /* Noise control */
SN76489.Registers[6] = data & 0x0f;
SN76489.NoiseShiftRegister = NoiseInitialState; /* reset shift register */
SN76489.NoiseFreq = 0x10 << (data&0x3); /* set noise signal generator frequency */
break;
case 7: /* Noise attenuation */
SN76489.Registers[7] = data & 0x0f;
SN76489.Channels[3] = PSGVolumeValues[data&0x0f] << SN76489.BoostNoise;
break;
}
}
/* Updates tone amplitude in delta buffer. Call whenever amplitude might have changed. */
static void UpdateToneAmplitude(int i, int time)
INLINE void UpdateToneAmplitude(int i, int time)
{
int delta = (SN76489.Channels[i] * SN76489.ToneFreqPos[i]) - SN76489.chan_amp[i];
int delta;
/* left output */
delta = (SN76489.Channel[i][0] * SN76489.ToneFreqPos[i]) - SN76489.ChanOut[i][0];
if (delta != 0)
{
SN76489.chan_amp[i] += delta;
blip_add(blip, time, delta);
SN76489.ChanOut[i][0] += delta;
blip_add_delta_fast(blip[0], time, delta);
}
/* right output */
delta = (SN76489.Channel[i][1] * SN76489.ToneFreqPos[i]) - SN76489.ChanOut[i][1];
if (delta != 0)
{
SN76489.ChanOut[i][1] += delta;
blip_add_delta_fast(blip[1], time, delta);
}
}
/* Updates noise amplitude in delta buffer. Call whenever amplitude might have changed. */
static void UpdateNoiseAmplitude(int time)
INLINE void UpdateNoiseAmplitude(int time)
{
int delta = (SN76489.Channels[3] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.chan_amp[3];
int delta;
/* left output */
delta = (SN76489.Channel[3][0] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.ChanOut[3][0];
if (delta != 0)
{
SN76489.chan_amp[3] += delta;
blip_add(blip, time, delta);
SN76489.ChanOut[3][0] += delta;
blip_add_delta_fast(blip[0], time, delta);
}
/* right output */
delta = (SN76489.Channel[3][1] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.ChanOut[3][1];
if (delta != 0)
{
SN76489.ChanOut[3][1] += delta;
blip_add_delta_fast(blip[1], time, delta);
}
}
/* Runs tone channel for clock_length clocks */
static void RunTone(int i, int clock_length)
static void RunTone(int i, int clocks)
{
int time;
/* Update in case a register changed etc. */
UpdateToneAmplitude(i, 0);
UpdateToneAmplitude(i, SN76489.clocks);
/* Time of next transition */
time = SN76489.ToneFreqVals[i];
/* Process any transitions that occur within clocks we're running */
while (time < clock_length)
while (time < clocks)
{
if (SN76489.Registers[i*2]>PSG_CUTOFF) {
/* Flip the flip-flop */
@ -250,15 +258,15 @@ static void RunTone(int i, int clock_length)
UpdateToneAmplitude(i, time);
/* Advance to time of next transition */
time += SN76489.Registers[i*2];
time += SN76489.Registers[i*2] * PSG_MCYCLES_RATIO;
}
/* Calculate new value for register, now that next transition is past number of clocks we're running */
SN76489.ToneFreqVals[i] = time - clock_length;
/* Update channel tone counter */
SN76489.ToneFreqVals[i] = time;
}
/* Runs noise channel for clock_length clocks */
static void RunNoise(int clock_length)
static void RunNoise(int clocks)
{
int time;
@ -271,13 +279,13 @@ static void RunNoise(int clock_length)
}
/* Update in case a register changed etc. */
UpdateNoiseAmplitude(0);
UpdateNoiseAmplitude(SN76489.clocks);
/* Time of next transition */
time = SN76489.ToneFreqVals[3];
/* Process any transitions that occur within clocks we're running */
while ( time < clock_length )
while (time < clocks)
{
/* Flip the flip-flop */
SN76489.ToneFreqPos[3] = -SN76489.ToneFreqPos[3];
@ -292,40 +300,142 @@ static void RunNoise(int clock_length)
/* Do some optimised calculations for common (known) feedback values */
/* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */
/* since that's (one or more bits set) && (not all bits set) */
Feedback = ((Feedback & FB_SEGAVDP) && ((Feedback & FB_SEGAVDP) ^ FB_SEGAVDP));
Feedback = ((Feedback & SN76489.NoiseFeedback) && ((Feedback & SN76489.NoiseFeedback) ^ SN76489.NoiseFeedback));
}
else /* Periodic noise */
Feedback = Feedback & 1;
SN76489.NoiseShiftRegister = (SN76489.NoiseShiftRegister >> 1) | (Feedback << (SRW_SEGAVDP - 1));
SN76489.NoiseShiftRegister = (SN76489.NoiseShiftRegister >> 1) | (Feedback << (SN76489.SRWidth - 1));
UpdateNoiseAmplitude(time);
}
/* Advance to time of next transition */
time += NoiseFreq;
time += NoiseFreq * PSG_MCYCLES_RATIO;
}
/* Calculate new value for register, now that next transition is past number of clocks we're running */
SN76489.ToneFreqVals[3] = time - clock_length;
/* Update channel tone counter */
SN76489.ToneFreqVals[3] = time;
}
int SN76489_Update(INT16 *buffer, int clock_length)
static void SN76489_RunUntil(unsigned int clocks)
{
int i;
/* Run noise first, since it might use current value of third tone frequency counter */
RunNoise(clock_length);
RunNoise(clocks);
/* Run tone channels */
for( i = 0; i <= 2; ++i )
RunTone(i, clock_length);
/* Read samples into output buffer */
blip_end_frame(blip, clock_length);
return blip_read_samples(blip, buffer, 0);
for (i=0; i<3; ++i)
{
RunTone(i, clocks);
}
}
int SN76489_Clocks(int length)
void SN76489_Update(unsigned int clocks)
{
return blip_clocks_needed(blip, length);
}
int i;
if (clocks > SN76489.clocks)
{
/* Run chip until current timestamp */
SN76489_RunUntil(clocks);
/* Update internal M-cycle counter */
SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
}
/* Adjust internal M-cycle counter for next frame */
SN76489.clocks -= clocks;
/* Adjust channel time counters for new frame */
for (i=0; i<4; ++i)
{
SN76489.ToneFreqVals[i] -= clocks;
}
}
void SN76489_Write(unsigned int clocks, unsigned int data)
{
unsigned int index;
if (clocks > SN76489.clocks)
{
/* run chip until current timestamp */
SN76489_RunUntil(clocks);
/* update internal M-cycle counter */
SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;
}
if (data & 0x80)
{
/* latch byte %1 cc t dddd */
SN76489.LatchedRegister = index = (data >> 4) & 0x07;
}
else
{
/* restore latched register index */
index = SN76489.LatchedRegister;
}
switch (index)
{
case 0:
case 2:
case 4: /* Tone Channels frequency */
{
if (data & 0x80)
{
/* Data byte %1 cc t dddd */
SN76489.Registers[index] = (SN76489.Registers[index] & 0x3f0) | (data & 0xf);
}
else
{
/* Data byte %0 - dddddd */
SN76489.Registers[index] = (SN76489.Registers[index] & 0x00f) | ((data & 0x3f) << 4);
}
/* zero frequency behaves the same as a value of 1 */
if (SN76489.Registers[index] == 0)
{
SN76489.Registers[index] = 1;
}
break;
}
case 1:
case 3:
case 5: /* Tone Channels attenuation */
{
data &= 0x0f;
SN76489.Registers[index] = data;
data = PSGVolumeValues[data];
index >>= 1;
SN76489.Channel[index][0] = (data * SN76489.PreAmp[index][0]) / 100;
SN76489.Channel[index][1] = (data * SN76489.PreAmp[index][1]) / 100;
break;
}
case 6: /* Noise control */
{
SN76489.Registers[6] = data & 0x0f;
/* reset shift register */
SN76489.NoiseShiftRegister = NoiseInitialState;
/* set noise signal generator frequency */
SN76489.NoiseFreq = (0x10 << (data&0x3)) * PSG_MCYCLES_RATIO;
break;
}
case 7: /* Noise attenuation */
{
data &= 0x0f;
SN76489.Registers[7] = data;
data = PSGVolumeValues[data];
SN76489.Channel[3][0] = (data * SN76489.PreAmp[3][0]) / 100;
SN76489.Channel[3][1] = (data * SN76489.PreAmp[3][1]) / 100;
break;
}
}
}

View File

@ -6,18 +6,18 @@
#ifndef _SN76489_H_
#define _SN76489_H_
/* Function prototypes */
#include "blip_buf.h"
extern void SN76489_Init(double PSGClockValue, int SamplingRate);
#define SN_DISCRETE 0
#define SN_INTEGRATED 1
/* Function prototypes */
extern void SN76489_Init(blip_t* left, blip_t* right, int type);
extern void SN76489_Reset(void);
extern void SN76489_Shutdown(void);
extern void SN76489_SetContext(uint8 *data);
extern void SN76489_GetContext(uint8 *data);
extern uint8 *SN76489_GetContextPtr(void);
extern void SN76489_Config(int preAmp, int boostNoise, int stereo);
extern void SN76489_Write(unsigned int clocks, unsigned int data);
extern void SN76489_Update(unsigned int cycles);
extern void *SN76489_GetContextPtr(void);
extern int SN76489_GetContextSize(void);
extern void SN76489_Write(int data);
extern int SN76489_Update(INT16 *buffer, int clock_length);
extern int SN76489_Clocks(int length);
extern void SN76489_BoostNoise(int boost);
#endif /* _SN76489_H_ */

View File

@ -38,199 +38,168 @@
****************************************************************************************/
#include "shared.h"
#include "Fir_Resampler.h"
#include "blip_buf.h"
/* Cycle-accurate samples */
static unsigned int psg_cycles_ratio;
static unsigned int psg_cycles_count;
static unsigned int fm_cycles_ratio;
static unsigned int fm_cycles_count;
/* FM output buffer (large enough to hold a whole frame at original chips rate) */
static int fm_buffer[1080 * 2];
static int fm_last[2];
static int *fm_ptr;
/* Cycle-accurate FM samples */
static uint32 fm_cycles_ratio;
static uint32 fm_cycles_start;
static uint32 fm_cycles_count;
/* YM chip function pointers */
static void (*YM_Reset)(void);
static void (*YM_Update)(int *buffer, int length);
static void (*YM_Write)(unsigned int a, unsigned int v);
/* Run FM chip for required M-cycles */
/* Run FM chip until required M-cycles */
INLINE void fm_update(unsigned int cycles)
{
if (cycles > fm_cycles_count)
{
int32 *buffer;
/* samples to run */
/* number of samples to run */
unsigned int samples = (cycles - fm_cycles_count + fm_cycles_ratio - 1) / fm_cycles_ratio;
/* update cycle count */
/* run FM chip to sample buffer */
YM_Update(fm_ptr, samples);
/* update FM buffer pointer */
fm_ptr += (samples << 1);
/* update FM cycle counter */
fm_cycles_count += samples * fm_cycles_ratio;
/* select input sample buffer */
buffer = Fir_Resampler_buffer();
if (buffer)
{
Fir_Resampler_write(samples << 1);
}
else
{
buffer = snd.fm.pos;
snd.fm.pos += (samples << 1);
}
/* run FM chip & get samples */
YM_Update(buffer, samples);
}
}
/* Run PSG chip for required M-cycles */
INLINE void psg_update(unsigned int cycles)
void sound_init( void )
{
if (cycles > psg_cycles_count)
{
/* clocks to run */
unsigned int clocks = (cycles - psg_cycles_count + psg_cycles_ratio - 1) / psg_cycles_ratio;
/* update cycle count */
psg_cycles_count += clocks * psg_cycles_ratio;
/* run PSG chip & get samples */
snd.psg.pos += SN76489_Update(snd.psg.pos, clocks);
}
}
/* Initialize sound chips emulation */
void sound_init(void)
{
/* Number of M-cycles executed per second. */
/* */
/* All emulated chips are kept in sync by using a common oscillator (MCLOCK) */
/* */
/* The original console would run exactly 53693175 M-cycles (53203424 for PAL), with */
/* 3420 M-cycles per line and 262 (313 for PAL) lines per frame, which gives an exact */
/* framerate of 59.92 (49.70 for PAL) fps. */
/* */
/* Since audio samples are generated at the end of the frame, to prevent audio skipping */
/* or lag between emulated frames, number of samples rendered per frame must be set to */
/* output samplerate (number of samples played per second) divided by output framerate */
/* (number of frames emulated per seconds). */
/* */
/* On some systems, we may want to achieve 100% smooth video rendering by synchronizing */
/* frame emulation with VSYNC, which frequency is generally not exactly those values. */
/* In that case, number of frames emulated per seconds is the same as the number of */
/* frames rendered per seconds by the host system video hardware. */
/* */
/* When no framerate is specified, base clock is original master clock value. */
/* Otherwise, it is based on the output framerate. */
/* */
double mclk = snd.frame_rate ? (MCYCLES_PER_LINE * lines_per_frame * snd.frame_rate) : system_clock;
/* For maximal accuracy, sound chips run in synchronization with 68k and Z80 cpus */
/* These values give the exact number of M-cycles executed per internal sample clock: */
/* . PSG chip runs at original rate and audio is resampled internally after each update */
/* . FM chips run by default (if HQ mode disabled) at the output rate directly */
/* We use 21.11 fixed point precision (max. mcycle value is 3420*313 i.e 21 bits max) */
psg_cycles_ratio = 16 * 15 * (1 << 11);
fm_cycles_ratio = (unsigned int)(mclk / (double) snd.sample_rate * 2048.0);
psg_cycles_count = 0;
fm_cycles_count = 0;
/* Initialize PSG core (input clock should be based on emulated system clock) */
SN76489_Init(mclk/15.0,snd.sample_rate);
/* Initialize FM cores (input clock and samplerate are only used when HQ mode is disabled) */
/* Initialize FM chip */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
/* YM2612 */
YM2612Init(mclk/7.0,snd.sample_rate);
YM2612Init();
YM2612Config(config.dac_bits);
YM_Reset = YM2612ResetChip;
YM_Update = YM2612Update;
YM_Write = YM2612Write;
/* In HQ mode, YM2612 is running at original rate (one sample each 144*7 M-cycles) */
/* Audio is resampled externally at the end of a frame. */
if (config.hq_fm)
{
fm_cycles_ratio = 144 * 7 * (1 << 11);
Fir_Resampler_time_ratio(mclk / (double)snd.sample_rate / (144.0 * 7.0), config.rolloff);
}
/* chip is running a VCLK / 144 = MCLK / 7 / 144 */
fm_cycles_ratio = 144 * 7;
}
else
{
/* YM2413 */
YM2413Init(mclk/15.0,snd.sample_rate);
YM2413Init();
YM_Reset = YM2413ResetChip;
YM_Update = YM2413Update;
YM_Write = YM2413Write;
/* In HQ mode, YM2413 is running at original rate (one sample each 72*15 M-cycles) */
/* Audio is resampled externally at the end of a frame. */
if (config.hq_fm)
{
fm_cycles_ratio = 72 * 15 * (1 << 11);
Fir_Resampler_time_ratio(mclk / (double)snd.sample_rate / (72.0 * 15.0), config.rolloff);
}
/* chip is running a ZCLK / 72 = MCLK / 15 / 72 */
fm_cycles_ratio = 72 * 15;
}
#ifdef LOGSOUND
error("%f mcycles per second\n", mclk);
error("%d mcycles per PSG sample\n", psg_cycles_ratio);
error("%d mcycles per FM sample\n", fm_cycles_ratio);
#endif
/* Initialize PSG chip */
SN76489_Config(config.psg_preamp, config.psgBoostNoise, 0xff);
}
void sound_shutdown(void)
{
Fir_Resampler_shutdown();
SN76489_Shutdown();
}
/* Reset sound chips emulation */
void sound_reset(void)
{
/* reset sound chips */
YM_Reset();
SN76489_Reset();
fm_cycles_count = 0;
psg_cycles_count = 0;
/* reset FM buffer ouput */
fm_last[0] = fm_last[1] = 0;
/* reset FM buffer pointer */
fm_ptr = fm_buffer;
/* reset FM cycle counters */
fm_cycles_start = fm_cycles_count = 0;
}
void sound_restore()
int sound_update(unsigned int cycles)
{
int size;
uint8 *ptr, *temp;
int delta, preamp, time, l, r, *ptr;
/* save YM context */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
/* Run PSG & FM chips until end of frame */
SN76489_Update(cycles);
fm_update(cycles);
/* FM output pre-amplification */
preamp = config.fm_preamp;
/* FM frame initial timestamp */
time = fm_cycles_start;
/* Restore last FM outputs from previous frame */
l = fm_last[0];
r = fm_last[1];
/* FM buffer start pointer */
ptr = fm_buffer;
/* flush FM samples */
if (config.hq_fm)
{
size = YM2612GetContextSize();
ptr = YM2612GetContextPtr();
/* high-quality Band-Limited synthesis */
do
{
/* left channel */
delta = ((*ptr++ * preamp) / 100) - l;
l += delta;
blip_add_delta(snd.blips[0][0], time, delta);
/* right channel */
delta = ((*ptr++ * preamp) / 100) - r;
r += delta;
blip_add_delta(snd.blips[0][1], time, delta);
/* increment time counter */
time += fm_cycles_ratio;
}
while (time < cycles);
}
else
{
size = YM2413GetContextSize();
ptr = YM2413GetContextPtr();
}
temp = malloc(size);
if (temp)
{
memcpy(temp, ptr, size);
/* faster Linear Interpolation */
do
{
/* left channel */
delta = ((*ptr++ * preamp) / 100) - l;
l += delta;
blip_add_delta_fast(snd.blips[0][0], time, delta);
/* right channel */
delta = ((*ptr++ * preamp) / 100) - r;
r += delta;
blip_add_delta_fast(snd.blips[0][1], time, delta);
/* increment time counter */
time += fm_cycles_ratio;
}
while (time < cycles);
}
/* reinitialize sound chips */
sound_init();
/* reset FM buffer pointer */
fm_ptr = fm_buffer;
/* restore YM context */
if (temp)
{
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
YM2612Restore(temp);
}
else
{
YM2413Restore(temp);
}
free(temp);
}
/* save last FM output for next frame */
fm_last[0] = l;
fm_last[1] = r;
/* adjust FM cycle counters for next frame */
fm_cycles_count = fm_cycles_start = time - cycles;
/* end of blip buffers time frame */
blip_end_frame(snd.blips[0][0], cycles);
blip_end_frame(snd.blips[0][1], cycles);
/* return number of available samples */
return blip_samples_avail(snd.blips[0][0]);
}
int sound_context_save(uint8 *state)
@ -247,8 +216,8 @@ int sound_context_save(uint8 *state)
}
save_param(SN76489_GetContextPtr(),SN76489_GetContextSize());
save_param(&fm_cycles_count,sizeof(fm_cycles_count));
save_param(&psg_cycles_count,sizeof(psg_cycles_count));
save_param(&fm_cycles_start,sizeof(fm_cycles_start));
return bufferptr;
}
@ -268,98 +237,38 @@ int sound_context_load(uint8 *state)
load_param(SN76489_GetContextPtr(),SN76489_GetContextSize());
load_param(&fm_cycles_count,sizeof(fm_cycles_count));
load_param(&psg_cycles_count,sizeof(psg_cycles_count));
fm_cycles_count = psg_cycles_count;
load_param(&fm_cycles_start,sizeof(fm_cycles_start));
fm_cycles_count = fm_cycles_start;
return bufferptr;
}
/* End of frame update, return the number of samples run so far. */
int sound_update(unsigned int cycles)
{
int size, avail;
/* run PSG & FM chips until end of frame */
cycles <<= 11;
psg_update(cycles);
fm_update(cycles);
/* check number of available FM samples */
if (config.hq_fm)
{
size = Fir_Resampler_avail();
}
else
{
size = (snd.fm.pos - snd.fm.buffer) >> 1;
}
#ifdef LOGSOUND
error("%d FM samples available\n",size);
#endif
/* check number of available PSG samples */
avail = snd.psg.pos - snd.psg.buffer;
#ifdef LOGSOUND
error("%d PSG samples available\n", avail);
#endif
/* resynchronize FM & PSG chips */
if (size > avail)
{
/* FM chip is ahead */
fm_cycles_count += SN76489_Clocks(size - avail) * psg_cycles_ratio;
/* return number of available samples */
size = avail;
}
else
{
/* PSG chip is ahead */
psg_cycles_count += SN76489_Clocks(avail - size) * psg_cycles_ratio;
}
#ifdef LOGSOUND
error("%lu PSG cycles run\n",psg_cycles_count);
error("%lu FM cycles run \n",fm_cycles_count);
#endif
/* adjust PSG & FM cycle counts for next frame */
psg_cycles_count -= cycles;
fm_cycles_count -= cycles;
#ifdef LOGSOUND
error("%lu PSG cycles left\n",psg_cycles_count);
error("%lu FM cycles left\n",fm_cycles_count);
#endif
return size;
}
/* Reset FM chip */
void fm_reset(unsigned int cycles)
{
fm_update(cycles << 11);
/* synchronize FM chip with CPU */
fm_update(cycles);
/* reset FM chip */
YM_Reset();
}
/* Write FM chip */
void fm_write(unsigned int cycles, unsigned int address, unsigned int data)
{
if (address & 1) fm_update(cycles << 11);
/* synchronize FM chip with CPU (on data port write only) */
if (address & 1)
{
fm_update(cycles);
}
/* write FM register */
YM_Write(address, data);
}
/* Read FM status (YM2612 only) */
unsigned int fm_read(unsigned int cycles, unsigned int address)
{
fm_update(cycles << 11);
/* synchronize FM chip with CPU */
fm_update(cycles);
/* read FM status (YM2612 only) */
return YM2612Read();
}
/* Write PSG chip */
void psg_write(unsigned int cycles, unsigned int data)
{
psg_update(cycles << 11);
SN76489_Write(data);
}

View File

@ -42,15 +42,12 @@
/* Function prototypes */
extern void sound_init(void);
extern void sound_shutdown(void);
extern void sound_reset(void);
extern void sound_restore(void);
extern int sound_context_save(uint8 *state);
extern int sound_context_load(uint8 *state);
extern int sound_update(unsigned int cycles);
extern void fm_reset(unsigned int cycles);
extern void fm_write(unsigned int cycles, unsigned int address, unsigned int data);
extern unsigned int fm_read(unsigned int cycles, unsigned int address);
extern void psg_write(unsigned int cycles, unsigned int data);
#endif /* _SOUND_H_ */

View File

@ -1076,11 +1076,8 @@ static void OPLL_initalize(void)
{
int i;
/* frequency base */
double freqbase = (ym2413.clock / 72.0) / (double)ym2413.rate;
/* YM2413 running at original frequency */
if (config.hq_fm) freqbase = 1.0;
/* YM2413 always running at original frequency */
double freqbase = 1.0;
/* make fnumber -> increment counter table */
for( i = 0 ; i < 1024; i++ )
@ -1592,16 +1589,13 @@ static void OPLLWriteReg(int r, int v)
}
void YM2413Init(double clock, int rate)
void YM2413Init(void)
{
init_tables();
/* clear */
memset(&ym2413,0,sizeof(YM2413));
ym2413.clock = clock;
ym2413.rate = rate;
/* init global tables */
OPLL_initalize();
}
@ -1725,18 +1719,3 @@ unsigned int YM2413GetContextSize(void)
{
return sizeof(YM2413);
}
void YM2413Restore(unsigned char *buffer)
{
/* save current timings */
double clock = ym2413.clock;
int rate = ym2413.rate;
/* restore internal state */
memcpy(&ym2413, buffer, sizeof(YM2413));
/* keep current timings */
ym2413.clock = clock;
ym2413.rate = rate;
OPLL_initalize();
}

View File

@ -12,14 +12,12 @@
#ifndef _H_YM2413_
#define _H_YM2413_
extern void YM2413Init(double clock, int rate);
extern void YM2413Init(void);
extern void YM2413ResetChip(void);
extern void YM2413Update(int *buffer, int length);
extern void YM2413Write(unsigned int a, unsigned int v);
extern unsigned int YM2413Read(unsigned int a);
extern unsigned char *YM2413GetContextPtr(void);
extern unsigned int YM2413GetContextSize(void);
extern void YM2413Restore(unsigned char *buffer);
#endif /*_H_YM2413_*/

View File

@ -22,7 +22,11 @@
/*
** CHANGELOG:
**
** 2006~2011 Eke-Eke (Genesis Plus GX):
** 01-09-2012 Eke-Eke (Genesis Plus GX):
** - removed input clock / output samplerate frequency ratio, chip now always run at (original) internal sample frequency
** - removed now uneeded extra bits of precision
**
** 2006~2012 Eke-Eke (Genesis Plus GX):
** - removed unused multichip support
** - added YM2612 Context external access functions
** - fixed LFO implementation:
@ -42,7 +46,6 @@
** - implemented accurate address/data ports behavior
** - added preliminar support for DAC precision
**
**
** 03-08-2003 Jarek Burczynski:
** - fixed YM2608 initial values (after the reset)
** - fixed flag and irqmask handling (YM2608)
@ -127,15 +130,6 @@
#include "shared.h"
/* globals */
#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */
#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
#define FREQ_MASK ((1<<FREQ_SH)-1)
/* envelope generator */
#define ENV_BITS 10
#define ENV_LEN (1<<ENV_BITS)
@ -150,6 +144,11 @@
#define EG_REL 1
#define EG_OFF 0
/* phase generator (detune mask) */
#define DT_BITS 17
#define DT_LEN (1 << DT_BITS)
#define DT_MASK (DT_LEN - 1)
/* operator unit */
#define SIN_BITS 10
#define SIN_LEN (1<<SIN_BITS)
@ -157,6 +156,7 @@
#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
#define TL_BITS 14 /* channel output */
/* TL_TAB_LEN is calculated as:
* 13 - sinus amplitude bits (Y axis)
@ -536,26 +536,23 @@ typedef struct
INT32 pms; /* channel PMS */
UINT8 ams; /* channel AMS */
UINT32 fc; /* fnum,blk:adjusted to sample rate */
UINT32 fc; /* fnum,blk */
UINT8 kcode; /* key code */
UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
UINT32 block_fnum; /* blk/fnum value (for LFO PM calculations) */
} FM_CH;
typedef struct
{
double clock; /* master clock (Hz) */
UINT32 rate; /* sampling rate (Hz) */
UINT16 address; /* address register */
UINT8 status; /* status flag */
UINT32 mode; /* mode CSM / 3SLOT */
UINT8 fn_h; /* freq latch */
INT32 TimerBase; /* Timer base time */
INT32 TA; /* timer a value */
INT32 TAL; /* timer a base */
INT32 TAL; /* timer a base */
INT32 TAC; /* timer a counter */
INT32 TB; /* timer b value */
INT32 TBL; /* timer b base */
INT32 TBL; /* timer b base */
INT32 TBC; /* timer b counter */
INT32 dt_tab[8][32]; /* DeTune table */
@ -584,20 +581,13 @@ typedef struct
FM_3SLOT SL3; /* 3 slot mode state */
unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */
/* EG */
UINT32 eg_cnt; /* global envelope generator counter */
UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */
UINT32 eg_timer_add; /* step of eg_timer */
UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 3 samples (on real chip) */
/* there are 2048 FNUMs that can be generated using FNUM/BLK registers
but LFO works with one more bit of a precision so we really need 4096 elements */
UINT32 fn_table[4096]; /* fnumber->increment counter */
UINT32 fn_max; /* max increment (required for calculating phase overflow) */
/* LFO */
UINT8 lfo_cnt; /* current LFO phase (out of 128) */
UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */
UINT32 lfo_timer_add; /* step of lfo_timer */
UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */
UINT32 LFO_AM; /* current LFO AM step */
UINT32 LFO_PM; /* current LFO PM step */
@ -613,6 +603,7 @@ typedef struct
UINT8 dacen; /* DAC mode */
INT32 dacout; /* DAC output */
FM_OPN OPN; /* OPN state */
} YM2612;
/* emulated chip */
@ -623,12 +614,6 @@ static INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */
static INT32 mem; /* one sample delay memory */
static INT32 out_fm[8]; /* outputs of working channels */
/* limiter */
#define Limit(val, max,min) { \
if ( val > max ) val = max; \
else if ( val < min ) val = min; \
}
INLINE void FM_KEYON(FM_CH *CH , int s )
{
FM_SLOT *SLOT = &CH->SLOT[s];
@ -775,17 +760,15 @@ INLINE void INTERNAL_TIMER_A()
{
if (ym2612.OPN.ST.mode & 0x01)
{
if ((ym2612.OPN.ST.TAC -= ym2612.OPN.ST.TimerBase) <= 0)
ym2612.OPN.ST.TAC--;
if (ym2612.OPN.ST.TAC <= 0)
{
/* set status (if enabled) */
if (ym2612.OPN.ST.mode & 0x04)
ym2612.OPN.ST.status |= 0x01;
/* reload the counter */
if (ym2612.OPN.ST.TAL)
ym2612.OPN.ST.TAC += ym2612.OPN.ST.TAL;
else
ym2612.OPN.ST.TAC = ym2612.OPN.ST.TAL;
ym2612.OPN.ST.TAC = ym2612.OPN.ST.TAL;
/* CSM mode auto key on */
if ((ym2612.OPN.ST.mode & 0xC0) == 0x80)
@ -798,7 +781,8 @@ INLINE void INTERNAL_TIMER_B(int step)
{
if (ym2612.OPN.ST.mode & 0x02)
{
if ((ym2612.OPN.ST.TBC -= (ym2612.OPN.ST.TimerBase * step)) <= 0)
ym2612.OPN.ST.TBC-=step;
if (ym2612.OPN.ST.TBC <= 0)
{
/* set status (if enabled) */
if (ym2612.OPN.ST.mode & 0x08)
@ -1030,13 +1014,13 @@ INLINE void advance_lfo()
{
if (ym2612.OPN.lfo_timer_overflow) /* LFO enabled ? */
{
/* increment LFO timer */
ym2612.OPN.lfo_timer += ym2612.OPN.lfo_timer_add;
/* increment LFO timer (every samples) */
ym2612.OPN.lfo_timer ++;
/* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */
while (ym2612.OPN.lfo_timer >= ym2612.OPN.lfo_timer_overflow)
if (ym2612.OPN.lfo_timer >= ym2612.OPN.lfo_timer_overflow)
{
ym2612.OPN.lfo_timer -= ym2612.OPN.lfo_timer_overflow;
ym2612.OPN.lfo_timer = 0;
/* There are 128 LFO steps */
ym2612.OPN.lfo_cnt = ( ym2612.OPN.lfo_cnt + 1 ) & 127;
@ -1055,16 +1039,15 @@ INLINE void advance_lfo()
}
INLINE void advance_eg_channels(void)
INLINE void advance_eg_channels(FM_CH *CH, unsigned int eg_cnt)
{
unsigned int eg_cnt = ym2612.OPN.eg_cnt;
unsigned int i = 0;
unsigned int i = 6; /* six channels */
unsigned int j;
FM_SLOT *SLOT;
do
{
SLOT = &ym2612.CH[i].SLOT[SLOT1];
SLOT = &CH->SLOT[SLOT1];
j = 4; /* four operators per channel */
do
{
@ -1202,83 +1185,98 @@ INLINE void advance_eg_channels(void)
break;
}
}
/* next slot */
SLOT++;
j--;
} while (j);
i++;
} while (i < 6); /* 6 channels */
} while (--j);
/* next channel */
CH++;
} while (--i);
}
/* SSG-EG update process */
/* The behavior is based upon Nemesis tests on real hardware */
/* This is actually executed before each samples */
INLINE void update_ssg_eg_channel(FM_SLOT *SLOT)
INLINE void update_ssg_eg_channels(FM_CH *CH)
{
unsigned int i = 4; /* four operators per channel */
unsigned int i = 6; /* six channels */
unsigned int j;
FM_SLOT *SLOT;
do
{
/* detect SSG-EG transition */
/* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */
/* if an Attack Phase is programmed, inversion can occur on each sample */
if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL))
j = 4; /* four operators per channel */
SLOT = &CH->SLOT[SLOT1];
do
{
if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */
/* detect SSG-EG transition */
/* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */
/* if an Attack Phase is programmed, inversion can occur on each sample */
if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL))
{
/* set inversion flag */
if (SLOT->ssg & 0x02)
SLOT->ssgn = 4;
/* force attenuation level during decay phases */
if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04)))
SLOT->volume = MAX_ATT_INDEX;
}
else /* loop SSG-EG */
{
/* toggle output inversion flag or reset Phase Generator */
if (SLOT->ssg & 0x02)
SLOT->ssgn ^= 4;
else
SLOT->phase = 0;
/* same as Key ON */
if (SLOT->state != EG_ATT)
if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */
{
if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
{
SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
}
/* set inversion flag */
if (SLOT->ssg & 0x02)
SLOT->ssgn = 4;
/* force attenuation level during decay phases */
if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04)))
SLOT->volume = MAX_ATT_INDEX;
}
else /* loop SSG-EG */
{
/* toggle output inversion flag or reset Phase Generator */
if (SLOT->ssg & 0x02)
SLOT->ssgn ^= 4;
else
SLOT->phase = 0;
/* same as Key ON */
if (SLOT->state != EG_ATT)
{
/* Attack Rate is maximal: directly switch to Decay or Substain */
SLOT->volume = MIN_ATT_INDEX;
SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
{
SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
}
else
{
/* Attack Rate is maximal: directly switch to Decay or Substain */
SLOT->volume = MIN_ATT_INDEX;
SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
}
}
}
/* recalculate EG output */
if (SLOT->ssgn ^ (SLOT->ssg&0x04))
SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
else
SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
}
/* recalculate EG output */
if (SLOT->ssgn ^ (SLOT->ssg&0x04))
SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
else
SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
}
/* next slot */
SLOT++;
} while (--j);
/* next slot */
SLOT++;
i--;
} while (i);
/* next channel */
CH++;
} while (--i);
}
INLINE void update_phase_lfo_slot(FM_SLOT *SLOT , INT32 pms, UINT32 block_fnum)
INLINE void update_phase_lfo_slot(FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum)
{
INT32 lfo_fn_table_index_offset = lfo_pm_table[ (((block_fnum & 0x7f0) >> 4) << 8) + pms + ym2612.OPN.LFO_PM ];
INT32 lfo_fn_table_index_offset = lfo_pm_table[(((block_fnum & 0x7f0) >> 4) << 8) + pms + ym2612.OPN.LFO_PM];
if (lfo_fn_table_index_offset) /* LFO phase modulation active */
{
UINT8 blk;
int kc, fc;
unsigned int kc, fc;
/* there are 2048 FNUMs that can be generated using FNUM/BLK registers
but LFO works with one more bit of a precision so we really need 4096 elements */
block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
blk = (block_fnum&0x7000) >> 12;
block_fnum = block_fnum & 0xfff;
@ -1287,10 +1285,7 @@ INLINE void update_phase_lfo_slot(FM_SLOT *SLOT , INT32 pms, UINT32 block_fnum)
kc = (blk<<2) | opn_fktable[block_fnum >> 8];
/* (frequency) phase increment counter */
fc = (ym2612.OPN.fn_table[block_fnum]>>(7-blk)) + SLOT->DT[kc];
/* (frequency) phase overflow (credits to Nemesis) */
if (fc < 0) fc += ym2612.OPN.fn_max;
fc = (((block_fnum << 5) >> (7 - blk)) + SLOT->DT[kc]) & DT_MASK;
/* update phase */
SLOT->phase += (fc * SLOT->mul) >> 1;
@ -1305,13 +1300,15 @@ INLINE void update_phase_lfo_channel(FM_CH *CH)
{
UINT32 block_fnum = CH->block_fnum;
INT32 lfo_fn_table_index_offset = lfo_pm_table[ (((block_fnum & 0x7f0) >> 4) << 8) + CH->pms + ym2612.OPN.LFO_PM ];
INT32 lfo_fn_table_index_offset = lfo_pm_table[(((block_fnum & 0x7f0) >> 4) << 8) + CH->pms + ym2612.OPN.LFO_PM];
if (lfo_fn_table_index_offset) /* LFO phase modulation active */
{
UINT8 blk;
int kc, fc, finc;
unsigned int kc, fc, finc;
/* there are 2048 FNUMs that can be generated using FNUM/BLK registers
but LFO works with one more bit of a precision so we really need 4096 elements */
block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
blk = (block_fnum&0x7000) >> 12;
block_fnum = block_fnum & 0xfff;
@ -1319,24 +1316,20 @@ INLINE void update_phase_lfo_channel(FM_CH *CH)
/* keyscale code */
kc = (blk<<2) | opn_fktable[block_fnum >> 8];
/* (frequency) phase increment counter */
fc = (ym2612.OPN.fn_table[block_fnum]>>(7-blk));
/* (frequency) phase increment counter */
fc = (block_fnum << 5) >> (7 - blk);
/* (frequency) phase overflow (credits to Nemesis) */
finc = fc + CH->SLOT[SLOT1].DT[kc];
if (finc < 0) finc += ym2612.OPN.fn_max;
/* apply DETUNE & MUL operator specific values */
finc = (fc + CH->SLOT[SLOT1].DT[kc]) & DT_MASK;
CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1;
finc = fc + CH->SLOT[SLOT2].DT[kc];
if (finc < 0) finc += ym2612.OPN.fn_max;
finc = (fc + CH->SLOT[SLOT2].DT[kc]) & DT_MASK;
CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1;
finc = fc + CH->SLOT[SLOT3].DT[kc];
if (finc < 0) finc += ym2612.OPN.fn_max;
finc = (fc + CH->SLOT[SLOT3].DT[kc]) & DT_MASK;
CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1;
finc = fc + CH->SLOT[SLOT4].DT[kc];
if (finc < 0) finc += ym2612.OPN.fn_max;
finc = (fc + CH->SLOT[SLOT4].DT[kc]) & DT_MASK;
CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1;
}
else /* LFO phase modulation = zero */
@ -1349,13 +1342,13 @@ INLINE void update_phase_lfo_channel(FM_CH *CH)
}
/* update phase increment and envelope generator */
INLINE void refresh_fc_eg_slot(FM_SLOT *SLOT , int fc , int kc )
INLINE void refresh_fc_eg_slot(FM_SLOT *SLOT , unsigned int fc , unsigned int kc )
{
/* add detune value */
fc += SLOT->DT[kc];
/* (frequency) phase overflow (credits to Nemesis) */
if (fc < 0) fc += ym2612.OPN.fn_max;
fc &= DT_MASK;
/* (frequency) phase increment counter */
SLOT->Incr = (fc * SLOT->mul) >> 1;
@ -1407,90 +1400,99 @@ INLINE void refresh_fc_eg_chan(FM_CH *CH )
#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask))
INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm)
INLINE signed int op_calc(UINT32 phase, unsigned int env, unsigned int pm)
{
UINT32 p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ];
UINT32 p = (env<<3) + sin_tab[ ( (phase >> SIN_BITS) + (pm >> 1) ) & SIN_MASK ];
if (p >= TL_TAB_LEN)
return 0;
return tl_tab[p];
}
INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm)
INLINE signed int op_calc1(UINT32 phase, unsigned int env, unsigned int pm)
{
UINT32 p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ];
UINT32 p = (env<<3) + sin_tab[ ( (phase + pm ) >> SIN_BITS ) & SIN_MASK ];
if (p >= TL_TAB_LEN)
return 0;
return tl_tab[p];
}
INLINE void chan_calc(FM_CH *CH)
INLINE void chan_calc(FM_CH *CH, int num)
{
UINT32 AM = ym2612.OPN.LFO_AM >> CH->ams;
unsigned int eg_out = volume_calc(&CH->SLOT[SLOT1]);
m2 = c1 = c2 = mem = 0;
*CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
do
{
INT32 out = CH->op1_out[0] + CH->op1_out[1];
CH->op1_out[0] = CH->op1_out[1];
UINT32 AM = ym2612.OPN.LFO_AM >> CH->ams;
unsigned int eg_out = volume_calc(&CH->SLOT[SLOT1]);
if( !CH->connect1 ){
/* algorithm 5 */
mem = c1 = c2 = CH->op1_out[0];
}else{
/* other algorithms */
*CH->connect1 += CH->op1_out[0];
}
m2 = c1 = c2 = mem = 0;
CH->op1_out[1] = 0;
if( eg_out < ENV_QUIET ) /* SLOT 1 */
*CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
{
if (!CH->FB)
out=0;
INT32 out = CH->op1_out[0] + CH->op1_out[1];
CH->op1_out[0] = CH->op1_out[1];
CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<<CH->FB) );
if( !CH->connect1 ){
/* algorithm 5 */
mem = c1 = c2 = CH->op1_out[0];
}else{
/* other algorithms */
*CH->connect1 += CH->op1_out[0];
}
CH->op1_out[1] = 0;
if( eg_out < ENV_QUIET ) /* SLOT 1 */
{
if (!CH->FB)
out=0;
CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<<CH->FB) );
}
}
}
eg_out = volume_calc(&CH->SLOT[SLOT3]);
if( eg_out < ENV_QUIET ) /* SLOT 3 */
*CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, m2);
eg_out = volume_calc(&CH->SLOT[SLOT3]);
if( eg_out < ENV_QUIET ) /* SLOT 3 */
*CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, m2);
eg_out = volume_calc(&CH->SLOT[SLOT2]);
if( eg_out < ENV_QUIET ) /* SLOT 2 */
eg_out = volume_calc(&CH->SLOT[SLOT2]);
if( eg_out < ENV_QUIET ) /* SLOT 2 */
*CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, c1);
eg_out = volume_calc(&CH->SLOT[SLOT4]);
if( eg_out < ENV_QUIET ) /* SLOT 4 */
*CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, c2);
eg_out = volume_calc(&CH->SLOT[SLOT4]);
if( eg_out < ENV_QUIET ) /* SLOT 4 */
*CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, c2);
/* store current MEM */
CH->mem_value = mem;
/* store current MEM */
CH->mem_value = mem;
/* update phase counters AFTER output calculations */
if(CH->pms)
{
/* add support for 3 slot mode */
if ((ym2612.OPN.ST.mode & 0xC0) && (CH == &ym2612.CH[2]))
/* update phase counters AFTER output calculations */
if(CH->pms)
{
update_phase_lfo_slot(&CH->SLOT[SLOT1], CH->pms, ym2612.OPN.SL3.block_fnum[1]);
update_phase_lfo_slot(&CH->SLOT[SLOT2], CH->pms, ym2612.OPN.SL3.block_fnum[2]);
update_phase_lfo_slot(&CH->SLOT[SLOT3], CH->pms, ym2612.OPN.SL3.block_fnum[0]);
update_phase_lfo_slot(&CH->SLOT[SLOT4], CH->pms, CH->block_fnum);
/* add support for 3 slot mode */
if ((ym2612.OPN.ST.mode & 0xC0) && (CH == &ym2612.CH[2]))
{
update_phase_lfo_slot(&CH->SLOT[SLOT1], CH->pms, ym2612.OPN.SL3.block_fnum[1]);
update_phase_lfo_slot(&CH->SLOT[SLOT2], CH->pms, ym2612.OPN.SL3.block_fnum[2]);
update_phase_lfo_slot(&CH->SLOT[SLOT3], CH->pms, ym2612.OPN.SL3.block_fnum[0]);
update_phase_lfo_slot(&CH->SLOT[SLOT4], CH->pms, CH->block_fnum);
}
else
{
update_phase_lfo_channel(CH);
}
}
else update_phase_lfo_channel(CH);
}
else /* no LFO phase modulation */
{
CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
}
else /* no LFO phase modulation */
{
CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
}
/* next channel */
CH++;
} while (--num);
}
/* write a OPN mode register 0x20-0x2f */
@ -1506,7 +1508,7 @@ INLINE void OPNWriteMode(int r, int v)
case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/ym2612) */
if (v&8) /* LFO enabled ? */
{
ym2612.OPN.lfo_timer_overflow = lfo_samples_per_step[v&7] << LFO_SH;
ym2612.OPN.lfo_timer_overflow = lfo_samples_per_step[v&7];
}
else
{
@ -1520,15 +1522,15 @@ INLINE void OPNWriteMode(int r, int v)
break;
case 0x24: /* timer A High 8*/
ym2612.OPN.ST.TA = (ym2612.OPN.ST.TA & 0x03)|(((int)v)<<2);
ym2612.OPN.ST.TAL = (1024 - ym2612.OPN.ST.TA) << TIMER_SH;
ym2612.OPN.ST.TAL = 1024 - ym2612.OPN.ST.TA;
break;
case 0x25: /* timer A Low 2*/
ym2612.OPN.ST.TA = (ym2612.OPN.ST.TA & 0x3fc)|(v&3);
ym2612.OPN.ST.TAL = (1024 - ym2612.OPN.ST.TA) << TIMER_SH;
ym2612.OPN.ST.TAL = 1024 - ym2612.OPN.ST.TA;
break;
case 0x26: /* timer B */
ym2612.OPN.ST.TB = v;
ym2612.OPN.ST.TBL = (256 - ym2612.OPN.ST.TB) << (TIMER_SH + 4);
ym2612.OPN.ST.TBL = (256 - v) << 4;
break;
case 0x27: /* mode, timer control */
set_timers(v);
@ -1687,7 +1689,7 @@ INLINE void OPNWriteReg(int r, int v)
/* keyscale code */
CH->kcode = (blk<<2) | opn_fktable[fn >> 7];
/* phase increment counter */
CH->fc = ym2612.OPN.fn_table[fn*2]>>(7-blk);
CH->fc = (fn << 6) >> (7 - blk);
/* store fnum in clear form for LFO PM calculations */
CH->block_fnum = (blk<<11) | fn;
@ -1706,7 +1708,7 @@ INLINE void OPNWriteReg(int r, int v)
/* keyscale code */
ym2612.OPN.SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7];
/* phase increment counter */
ym2612.OPN.SL3.fc[c] = ym2612.OPN.fn_table[fn*2]>>(7-blk);
ym2612.OPN.SL3.fc[c] = (fn << 6) >> (7 - blk);
ym2612.OPN.SL3.block_fnum[c] = (blk<<11) | fn;
ym2612.CH[2].SLOT[SLOT1].Incr=-1;
}
@ -1722,9 +1724,8 @@ INLINE void OPNWriteReg(int r, int v)
switch( OPN_SLOT(r) ){
case 0: /* 0xb0-0xb2 : FB,ALGO */
{
int feedback = (v>>3)&7;
CH->ALGO = v&7;
CH->FB = feedback ? feedback+6 : 0;
CH->FB = (v>>3)&7;
setup_connection( CH, c );
break;
}
@ -1744,65 +1745,6 @@ INLINE void OPNWriteReg(int r, int v)
}
}
/* initialize time tables */
static void init_timetables(double freqbase)
{
int i,d;
double rate;
/* DeTune table */
for (d = 0;d <= 3;d++)
{
for (i = 0;i <= 31;i++)
{
rate = ((double)dt_tab[d*32 + i]) * freqbase * (1<<(FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
ym2612.OPN.ST.dt_tab[d][i] = (INT32) rate;
ym2612.OPN.ST.dt_tab[d+4][i] = -ym2612.OPN.ST.dt_tab[d][i];
}
}
/* there are 2048 FNUMs that can be generated using FNUM/BLK registers
but LFO works with one more bit of a precision so we really need 4096 elements */
/* calculate fnumber -> increment counter table */
for(i = 0; i < 4096; i++)
{
/* freq table for octave 7 */
/* OPN phase increment counter = 20bit */
/* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */
/* where sample clock is M/144 */
/* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */
/* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */
ym2612.OPN.fn_table[i] = (UINT32)( (double)i * 32 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
}
/* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */
ym2612.OPN.fn_max = (UINT32)( (double)0x20000 * freqbase * (1<<(FREQ_SH-10)) );
}
/* prescaler set (and make time tables) */
static void OPNSetPres(int pres)
{
/* frequency base (ratio between FM original samplerate & desired output samplerate)*/
double freqbase = ym2612.OPN.ST.clock / ym2612.OPN.ST.rate / pres;
/* YM2612 running at original frequency (~53267 Hz) */
if (config.hq_fm) freqbase = 1.0;
/* EG is updated every 3 samples */
ym2612.OPN.eg_timer_add = (UINT32)((1<<EG_SH) * freqbase);
ym2612.OPN.eg_timer_overflow = ( 3 ) * (1<<EG_SH);
/* LFO timer increment (every samples) */
ym2612.OPN.lfo_timer_add = (UINT32)((1<<LFO_SH) * freqbase);
/* Timers increment (every samples) */
ym2612.OPN.ST.TimerBase = (int) ((1 << TIMER_SH) * freqbase);
/* make time tables */
init_timetables(freqbase);
}
static void reset_channels(FM_CH *CH , int num )
{
int c,s;
@ -1826,14 +1768,14 @@ static void reset_channels(FM_CH *CH , int num )
}
/* initialize generic tables */
static void init_tables(void)
static void init_tables(unsigned char dac_bits)
{
signed int i,x;
signed int d,i,x;
signed int n;
double o,m;
/* DAC precision */
unsigned int mask = ~((1 << (14 - config.dac_bits)) - 1);
unsigned int mask = ~((1 << (14 - dac_bits)) - 1);
/* build Linear Power Table */
for (x=0; x<TL_RES_LEN; x++)
@ -1924,18 +1866,26 @@ static void init_tables(void)
}
}
}
/* build DETUNE table */
for (d = 0;d <= 3;d++)
{
for (i = 0;i <= 31;i++)
{
ym2612.OPN.ST.dt_tab[d][i] = (INT32) dt_tab[d*32 + i];
ym2612.OPN.ST.dt_tab[d+4][i] = -ym2612.OPN.ST.dt_tab[d][i];
}
}
}
/* initialize ym2612 emulator(s) */
void YM2612Init(double clock, int rate)
/* initialize ym2612 emulator */
void YM2612Init(void)
{
memset(&ym2612,0,sizeof(YM2612));
init_tables();
ym2612.OPN.ST.clock = clock;
ym2612.OPN.ST.rate = rate;
OPNSetPres(6*24); /* YM2612 prescaler is fixed to 1/6, one sample (6 mixed channels) is output for each 24 FM clocks */
init_tables(TL_BITS);
}
/* reset OPN registers */
@ -1962,9 +1912,9 @@ void YM2612ResetChip(void)
set_timers(0x30);
ym2612.OPN.ST.TB = 0;
ym2612.OPN.ST.TBL = 256 << (TIMER_SH + 4);
ym2612.OPN.ST.TBL = 256 << 4;
ym2612.OPN.ST.TA = 0;
ym2612.OPN.ST.TAL = 1024 << TIMER_SH;
ym2612.OPN.ST.TAL = 1024;
reset_channels(&ym2612.CH[0] , 6 );
@ -2074,39 +2024,32 @@ void YM2612Update(int *buffer, int length)
out_fm[5] = 0;
/* update SSG-EG output */
update_ssg_eg_channel(&ym2612.CH[0].SLOT[SLOT1]);
update_ssg_eg_channel(&ym2612.CH[1].SLOT[SLOT1]);
update_ssg_eg_channel(&ym2612.CH[2].SLOT[SLOT1]);
update_ssg_eg_channel(&ym2612.CH[3].SLOT[SLOT1]);
update_ssg_eg_channel(&ym2612.CH[4].SLOT[SLOT1]);
update_ssg_eg_channel(&ym2612.CH[5].SLOT[SLOT1]);
update_ssg_eg_channels(&ym2612.CH[0]);
/* calculate FM */
chan_calc(&ym2612.CH[0]);
chan_calc(&ym2612.CH[1]);
chan_calc(&ym2612.CH[2]);
chan_calc(&ym2612.CH[3]);
chan_calc(&ym2612.CH[4]);
if (!ym2612.dacen)
{
chan_calc(&ym2612.CH[5]);
chan_calc(&ym2612.CH[0],6);
}
else
{
/* DAC Mode */
out_fm[5] = ym2612.dacout;
chan_calc(&ym2612.CH[0],5);
}
/* advance LFO */
advance_lfo();
/* advance envelope generator */
ym2612.OPN.eg_timer += ym2612.OPN.eg_timer_add;
while (ym2612.OPN.eg_timer >= ym2612.OPN.eg_timer_overflow)
ym2612.OPN.eg_timer ++;
/* EG is updated every 3 samples */
if (ym2612.OPN.eg_timer >= 3)
{
ym2612.OPN.eg_timer -= ym2612.OPN.eg_timer_overflow;
ym2612.OPN.eg_timer = 0;
ym2612.OPN.eg_cnt++;
advance_eg_channels();
advance_eg_channels(&ym2612.CH[0], ym2612.OPN.eg_cnt);
}
/* 14-bit DAC inputs (range is -8192;+8192) */
@ -2164,29 +2107,31 @@ void YM2612Update(int *buffer, int length)
INTERNAL_TIMER_B(length);
}
unsigned char *YM2612GetContextPtr(void)
void YM2612Config(unsigned char dac_bits)
{
return (unsigned char *)&ym2612;
/* reinitialize TL table */
init_tables(dac_bits);
}
unsigned int YM2612GetContextSize(void)
int YM2612LoadContext(unsigned char *state)
{
return sizeof(YM2612);
}
int c,s;
uint8 index;
int bufferptr = 0;
void YM2612Restore(unsigned char *buffer)
{
/* save current timings */
double clock = ym2612.OPN.ST.clock;
int rate = ym2612.OPN.ST.rate;
/* restore YM2612 context */
load_param(&ym2612, sizeof(ym2612));
/* restore internal state */
memcpy(&ym2612, buffer, sizeof(YM2612));
/* keep current timings */
ym2612.OPN.ST.clock = clock;
ym2612.OPN.ST.rate = rate;
OPNSetPres(6*24);
/* restore DT table address pointer for each channel slots */
for (c=0; c<6; c++)
{
for (s=0; s<4; s++)
{
load_param(&index,sizeof(index));
bufferptr += sizeof(index);
ym2612.CH[c].SLOT[s].DT = ym2612.OPN.ST.dt_tab[index&7];
}
}
/* restore outputs connections */
setup_connection(&ym2612.CH[0],0);
@ -2196,30 +2141,6 @@ void YM2612Restore(unsigned char *buffer)
setup_connection(&ym2612.CH[4],4);
setup_connection(&ym2612.CH[5],5);
/* restore TL table (DAC resolution might have been modified) */
init_tables();
}
int YM2612LoadContext(unsigned char *state)
{
int c,s;
uint8 index;
int bufferptr = sizeof(YM2612);
/* restore YM2612 context */
YM2612Restore(state);
/* restore DT table address pointer for each channel slots */
for( c = 0 ; c < 6 ; c++ )
{
for(s = 0 ; s < 4 ; s++ )
{
load_param(&index,sizeof(index));
bufferptr += sizeof(index);
ym2612.CH[c].SLOT[s].DT = ym2612.OPN.ST.dt_tab[index&7];
}
}
return bufferptr;
}
@ -2227,15 +2148,15 @@ int YM2612SaveContext(unsigned char *state)
{
int c,s;
uint8 index;
int bufferptr = sizeof(YM2612);
int bufferptr = 0;
/* save YM2612 context */
memcpy(state, &ym2612, sizeof(YM2612));
save_param(&ym2612, sizeof(ym2612));
/* save DT table index for each channel slots */
for( c = 0 ; c < 6 ; c++ )
for (c=0; c<6; c++)
{
for(s = 0 ; s < 4 ; s++ )
for (s=0; s<4; s++)
{
index = (ym2612.CH[c].SLOT[s].DT - ym2612.OPN.ST.dt_tab[0]) >> 5;
save_param(&index,sizeof(index));

View File

@ -16,14 +16,12 @@
#ifndef _H_YM2612_
#define _H_YM2612_
extern void YM2612Init(double clock, int rate);
extern void YM2612Init(void);
extern void YM2612Config(unsigned char dac_bits);
extern void YM2612ResetChip(void);
extern void YM2612Update(int *buffer, int length);
extern void YM2612Write(unsigned int a, unsigned int v);
extern unsigned int YM2612Read(void);
extern unsigned char *YM2612GetContextPtr(void);
extern unsigned int YM2612GetContextSize(void);
extern void YM2612Restore(unsigned char *buffer);
extern int YM2612LoadContext(unsigned char *state);
extern int YM2612SaveContext(unsigned char *state);

View File

@ -48,13 +48,13 @@ int state_load(unsigned char *state)
version[16] = 0;
if (memcmp(version,STATE_VERSION,11))
{
return -1;
return 0;
}
/* version check (support from previous 1.6.x state format) */
if ((version[11] < 0x31) || (version[13] < 0x36))
/* version check (1.7.1 and above only) */
if ((version[11] < 0x31) || (version[13] < 0x37) || (version[15] < 0x31))
{
return -1;
return 0;
}
/* reset system */
@ -98,32 +98,32 @@ int state_load(unsigned char *state)
load_param(work_ram, 0x2000);
}
/* CPU cycles */
load_param(&m68k.cycles, sizeof(m68k.cycles));
load_param(&Z80.cycles, sizeof(Z80.cycles));
/* IO */
load_param(io_reg, sizeof(io_reg));
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
load_param(io_reg, sizeof(io_reg));
io_reg[0] = region_code | 0x20 | (config.bios & 1);
}
else
{
/* 1.6.1 or 1.7.x specific */
if ((version[15] == 0x31) || (version[13] == 0x37))
{
load_param(&io_reg[0x0E], 1);
}
load_param(&io_reg[0x0F], 1);
io_reg[0] = 0x80 | (region_code >> 1);
}
/* VDP */
bufferptr += vdp_context_load(&state[bufferptr], version);
bufferptr += vdp_context_load(&state[bufferptr]);
/* SOUND */
bufferptr += sound_context_load(&state[bufferptr]);
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
SN76489_Init(snd.blips[0][0], snd.blips[0][1], SN_INTEGRATED);
SN76489_Config(config.psg_preamp, config.psgBoostNoise, 0xff);
}
else
{
SN76489_Init(snd.blips[0][0], snd.blips[0][1], (system_hw < SYSTEM_MARKIII) ? SN_DISCRETE : SN_INTEGRATED);
SN76489_Config(config.psg_preamp, config.psgBoostNoise, io_reg[6]);
}
/* 68000 */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
@ -149,19 +149,11 @@ int state_load(unsigned char *state)
load_param(&tmp32, 4); m68k_set_reg(M68K_REG_PC, tmp32);
load_param(&tmp16, 2); m68k_set_reg(M68K_REG_SR, tmp16);
load_param(&tmp32, 4); m68k_set_reg(M68K_REG_USP,tmp32);
load_param(&tmp32, 4); m68k_set_reg(M68K_REG_ISP,tmp32);
/* 1.6.1 or 1.7.x specific */
if ((version[15] == 0x31) || (version[13] == 0x37))
{
load_param(&tmp32, 4); m68k_set_reg(M68K_REG_ISP,tmp32);
/* 1.7.x specific */
if (version[13] == 0x37)
{
load_param(&m68k.int_level, sizeof(m68k.int_level));
load_param(&m68k.stopped, sizeof(m68k.stopped));
}
}
load_param(&m68k.cycles, sizeof(m68k.cycles));
load_param(&m68k.int_level, sizeof(m68k.int_level));
load_param(&m68k.stopped, sizeof(m68k.stopped));
}
/* Z80 */
@ -171,6 +163,17 @@ int state_load(unsigned char *state)
/* Extra HW */
if (system_hw == SYSTEM_MCD)
{
/* handle case of MD cartridge using or not CD hardware */
char id[5];
load_param(id,4);
id[4] = 0;
/* check if CD hardware was enabled before attempting to restore */
if (memcmp(id,"SCD!",4))
{
return 0;
}
/* CD hardware */
bufferptr += scd_context_load(&state[bufferptr]);
}
@ -183,15 +186,10 @@ int state_load(unsigned char *state)
{
/* MS cartridge hardware */
bufferptr += sms_cart_context_load(&state[bufferptr]);
/* 1.6.1 or 1.7.x specific */
if ((version[15] == 0x31) || (version[13] == 0x37))
{
sms_cart_switch(~io_reg[0x0E]);
}
sms_cart_switch(~io_reg[0x0E]);
}
return 1;
return bufferptr;
}
int state_save(unsigned char *state)
@ -217,20 +215,8 @@ int state_save(unsigned char *state)
save_param(work_ram, 0x2000);
}
/* CPU cycles */
save_param(&m68k.cycles, sizeof(m68k.cycles));
save_param(&Z80.cycles, sizeof(Z80.cycles));
/* IO */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
save_param(io_reg, sizeof(io_reg));
}
else
{
save_param(&io_reg[0x0E], 1);
save_param(&io_reg[0x0F], 1);
}
save_param(io_reg, sizeof(io_reg));
/* VDP */
bufferptr += vdp_context_save(&state[bufferptr]);
@ -264,6 +250,7 @@ int state_save(unsigned char *state)
tmp32 = m68k_get_reg(M68K_REG_USP); save_param(&tmp32, 4);
tmp32 = m68k_get_reg(M68K_REG_ISP); save_param(&tmp32, 4);
save_param(&m68k.cycles, sizeof(m68k.cycles));
save_param(&m68k.int_level, sizeof(m68k.int_level));
save_param(&m68k.stopped, sizeof(m68k.stopped));
}
@ -271,9 +258,14 @@ int state_save(unsigned char *state)
/* Z80 */
save_param(&Z80, sizeof(Z80_Regs));
/* Extra HW */
/* External HW */
if (system_hw == SYSTEM_MCD)
{
/* CD hardware ID flag */
char id[5];
strncpy(id,"SCD!",4);
save_param(id, 4);
/* CD hardware */
bufferptr += scd_context_save(&state[bufferptr]);
}

View File

@ -39,8 +39,8 @@
#ifndef _STATE_H_
#define _STATE_H_
#define STATE_SIZE 0xfc080
#define STATE_VERSION "GENPLUS-GX 1.7.0"
#define STATE_SIZE 0xfd000
#define STATE_VERSION "GENPLUS-GX 1.7.1"
#define load_param(param, size) \
memcpy(param, &state[bufferptr], size); \

View File

@ -40,7 +40,6 @@
****************************************************************************************/
#include "shared.h"
#include "Fir_Resampler.h"
#include "eq.h"
/* Global variables */
@ -56,59 +55,84 @@ static uint8 pause_b;
static EQSTATE eq;
static int32 llp,rrp;
/****************************************************************
* Audio subsystem
****************************************************************/
/******************************************************************************************/
/* Audio subsystem */
/******************************************************************************************/
int audio_init(int samplerate, double framerate)
{
/* Number of M-cycles executed per second. */
/* All emulated chips are kept in sync by using a common oscillator (MCLOCK) */
/* */
/* The original console would run exactly 53693175 M-cycles per sec (53203424 for PAL), */
/* 3420 M-cycles per line and 262 (313 for PAL) lines per frame, which gives an exact */
/* framerate of 59.92 (49.70 for PAL) frames per second. */
/* */
/* Since audio samples are generated at the end of the frame, to prevent audio skipping */
/* or lag between emulated frames, number of samples rendered per frame must be set to */
/* output samplerate (number of samples played per second) divided by input framerate */
/* (number of frames emulated per seconds). */
/* */
/* On some systems, we may want to achieve 100% smooth video rendering by synchronizing */
/* frame emulation with VSYNC, which frequency is generally not exactly those values. */
/* In that case, input framerate (number of frames emulated per seconds) is the same as */
/* output framerate (number of frames rendered per seconds) by the host video hardware. */
/* */
/* When no framerate is specified, base clock is set to original master clock value. */
/* Otherwise, it is set to number of M-cycles emulated per line (fixed) multiplied by */
/* number of lines per frame (VDP mode specific) multiplied by input framerate. */
/* */
double mclk = framerate ? (MCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * framerate) : system_clock;
/* Shutdown first */
audio_shutdown();
/* Clear the sound data context */
memset(&snd, 0, sizeof (snd));
/* Default settings */
/* Initialize audio rates */
snd.sample_rate = samplerate;
snd.frame_rate = framerate;
/* If no framerate is specified, assume emulator is running at the original frequency */
if (!framerate)
/* Initialize Blip Buffers */
snd.blips[0][0] = blip_new(samplerate / 10);
snd.blips[0][1] = blip_new(samplerate / 10);
if (!snd.blips[0][0] || !snd.blips[0][1])
{
if (vdp_pal)
{
/* PAL mode -> MCLK cycles/sec, 3420 cycles/line, 313 lines/field */
/* fps = MCLK/3420/313 = 49.70 hz (PAL console) or 50.16 hz (NTSC console w/ 50 hz switch) */
framerate = (double)system_clock / (double)MCYCLES_PER_LINE / 313.0;
}
else
{
/* NTSC mode -> MCLK cycles/sec, 3420 cycles/line, 262 lines/field */
/* fps = MCLK/3420/262 = 59.92 hz (NTSC console) or 59.38 hz (PAL console w/ 60 hz switch) */
framerate = (double)system_clock / (double)MCYCLES_PER_LINE / 262.0;
}
audio_shutdown();
return -1;
}
/* Sound buffer maximal size (for at least one frame) */
snd.buffer_size = (int)((double)samplerate / framerate) + 32;
/* For maximal accuracy, sound chips are running at their original rate using common */
/* master clock timebase so they remain perfectly synchronized together, while still */
/* being synchronized with 68K and Z80 CPUs as well. Mixed sound chip output is then */
/* resampled to desired rate at the end of each frame, using Blip Buffer. */
blip_set_rates(snd.blips[0][0], mclk, samplerate);
blip_set_rates(snd.blips[0][1], mclk, samplerate);
/* SN76489 stream buffer */
snd.psg.buffer = (int16 *) malloc(snd.buffer_size * sizeof(int16));
if (!snd.psg.buffer) return (-1);
/* Initialize PSG core */
SN76489_Init(snd.blips[0][0], snd.blips[0][1], (system_hw < SYSTEM_MARKIII) ? SN_DISCRETE : SN_INTEGRATED);
/* YM2612 stream buffer */
snd.fm.buffer = (int32 *) malloc(snd.buffer_size * sizeof(int32) * 2);
if (!snd.fm.buffer) return (-1);
/* PCM stream buffer */
/* Mega CD sound hardware */
if (system_hw == SYSTEM_MCD)
{
snd.pcm.buffer = (int16 *) malloc(snd.buffer_size * sizeof(int16) * 2);
if (!snd.pcm.buffer) return (-1);
/* allocate blip buffers */
snd.blips[1][0] = blip_new(samplerate / 10);
snd.blips[1][1] = blip_new(samplerate / 10);
snd.blips[2][0] = blip_new(samplerate / 10);
snd.blips[2][1] = blip_new(samplerate / 10);
if (!snd.blips[1][0] || !snd.blips[1][1] || !snd.blips[2][0] || !snd.blips[2][1])
{
audio_shutdown();
return -1;
}
/* Initialize PCM core */
pcm_init(snd.blips[1][0], snd.blips[1][1]);
/* Initialize CDD core */
cdd_init(snd.blips[2][0], snd.blips[2][1]);
}
/* Resampling buffer */
if (config.hq_fm && !Fir_Resampler_initialize(4096)) return (-1);
/* Set audio enable flag */
snd.enabled = 1;
@ -121,23 +145,26 @@ int audio_init(int samplerate, double framerate)
void audio_reset(void)
{
int i,j;
/* Clear blip buffers */
for (i=0; i<3; i++)
{
for (j=0; j<2; j++)
{
if (snd.blips[i][j])
{
blip_clear(snd.blips[i][j]);
}
}
}
/* Low-Pass filter */
llp = 0;
rrp = 0;
/* 3 band EQ */
audio_set_equalizer();
/* Resampling buffer */
Fir_Resampler_clear();
/* Audio buffers */
snd.psg.pos = snd.psg.buffer;
snd.fm.pos = snd.fm.buffer;
snd.pcm.pos = snd.pcm.buffer;
if (snd.psg.buffer) memset (snd.psg.buffer, 0, snd.buffer_size * sizeof(int16));
if (snd.fm.buffer) memset (snd.fm.buffer, 0, snd.buffer_size * sizeof(int32) * 2);
if (snd.pcm.buffer) memset (snd.pcm.buffer, 0, snd.buffer_size * sizeof(int16) * 2);
}
void audio_set_equalizer(void)
@ -150,129 +177,122 @@ void audio_set_equalizer(void)
void audio_shutdown(void)
{
/* Sound buffers */
if (snd.fm.buffer) free(snd.fm.buffer);
if (snd.psg.buffer) free(snd.psg.buffer);
if (snd.pcm.buffer) free(snd.pcm.buffer);
/* Resampling buffer */
Fir_Resampler_shutdown();
int i,j;
/* Delete blip buffers */
for (i=0; i<3; i++)
{
for (j=0; j<2; j++)
{
blip_delete(snd.blips[i][j]);
snd.blips[i][j] = 0;
}
}
}
int audio_update(int16 *buffer)
{
int32 i, l, r;
int32 ll = llp;
int32 rr = rrp;
int psg_preamp = config.psg_preamp;
int fm_preamp = config.fm_preamp;
int filter = config.filter;
uint32 factora = (config.lp_range << 16) / 100;
uint32 factorb = 0x10000 - factora;
int32 *fm = snd.fm.buffer;
int16 *psg = snd.psg.buffer;
int16 *pcm = snd.pcm.buffer;
/* get number of available samples */
/* run sound chips until end of frame */
int size = sound_update(mcycles_vdp);
/* Mega CD specific */
if (system_hw == SYSTEM_MCD)
{
/* sync PCM chip with other sound chips */
pcm_update(size);
/* read CDDA samples */
cdd_read_audio(size);
}
#ifdef ALIGN_SND
/* return an aligned number of samples if necessary*/
/* return an aligned number of samples if required */
size &= ALIGN_SND;
#endif
if (config.hq_fm)
{
/* resample into FM output buffer */
Fir_Resampler_read(fm, size);
#ifdef LOGSOUND
error("%d FM samples remaining\n",Fir_Resampler_written() >> 1);
/* resample FM & PSG mixed stream to output buffer */
#ifdef LSB_FIRST
blip_read_samples(snd.blips[0][0], buffer, size);
blip_read_samples(snd.blips[0][1], buffer + 1, size);
#else
blip_read_samples(snd.blips[0][0], buffer + 1, size);
blip_read_samples(snd.blips[0][1], buffer, size);
#endif
}
else
{
/* adjust remaining samples in FM output buffer*/
snd.fm.pos -= (size * 2);
#ifdef LOGSOUND
error("%d FM samples remaining\n",(snd.fm.pos - snd.fm.buffer)>>1);
/* Mega CD specific */
if (system_hw == SYSTEM_MCD)
{
/* resample PCM & CD-DA streams to output buffer */
#ifdef LSB_FIRST
blip_mix_samples(snd.blips[1][0], buffer, size);
blip_mix_samples(snd.blips[1][1], buffer + 1, size);
blip_mix_samples(snd.blips[2][0], buffer, size);
blip_mix_samples(snd.blips[2][1], buffer + 1, size);
#else
blip_mix_samples(snd.blips[1][0], buffer + 1, size);
blip_mix_samples(snd.blips[1][1], buffer, size);
blip_mix_samples(snd.blips[2][0], buffer + 1, size);
blip_mix_samples(snd.blips[2][1], buffer, size);
#endif
}
/* adjust remaining samples in PSG output buffer*/
snd.psg.pos -= size;
#ifdef LOGSOUND
error("%d PSG samples remaining\n",snd.psg.pos - snd.psg.buffer);
#endif
/* PCM sound chip */
if (pcm)
/* Audio filtering */
if (config.filter)
{
/* get needed samples */
pcm_update(pcm, size);
}
int32 i, l, r;
/* mix samples */
for (i = 0; i < size; i ++)
{
/* PSG samples (mono) */
l = r = (((*psg++) * psg_preamp) / 100);
/* FM samples (stereo) */
l += ((*fm++ * fm_preamp) / 100);
r += ((*fm++ * fm_preamp) / 100);
/* PCM samples (stereo) */
if (pcm)
{
l += *pcm++;
r += *pcm++;
}
/* filtering */
if (filter & 1)
if (config.filter & 1)
{
/* single-pole low-pass filter (6 dB/octave) */
ll = (ll>>16)*factora + l*factorb;
rr = (rr>>16)*factora + r*factorb;
l = ll >> 16;
r = rr >> 16;
uint32 factora = (config.lp_range << 16) / 100;
uint32 factorb = 0x10000 - factora;
int32 ll = llp;
int32 rr = rrp;
for (i = 0; i < size; i ++)
{
/* apply low-pass filter */
ll = (ll>>16)*factora + buffer[0]*factorb;
rr = (rr>>16)*factora + buffer[1]*factorb;
l = ll >> 16;
r = rr >> 16;
/* clipping (16-bit samples) */
if (l > 32767) l = 32767;
else if (l < -32768) l = -32768;
if (r > 32767) r = 32767;
else if (r < -32768) r = -32768;
/* update sound buffer */
*buffer++ = l;
*buffer++ = r;
}
/* save last samples for next frame */
llp = ll;
rrp = rr;
}
else if (filter & 2)
else if (config.filter & 2)
{
/* 3 Band EQ */
l = do_3band(&eq,l);
r = do_3band(&eq,r);
for (i = 0; i < size; i ++)
{
/* 3 Band EQ */
l = do_3band(&eq,buffer[0]);
r = do_3band(&eq,buffer[1]);
/* clipping (16-bit samples) */
if (l > 32767) l = 32767;
else if (l < -32768) l = -32768;
if (r > 32767) r = 32767;
else if (r < -32768) r = -32768;
/* update sound buffer */
*buffer++ = l;
*buffer++ = r;
}
}
/* clipping (16-bit samples) */
if (l > 32767) l = 32767;
else if (l < -32768) l = -32768;
if (r > 32767) r = 32767;
else if (r < -32768) r = -32768;
/* update sound buffer */
#ifdef LSB_FIRST
*buffer++ = l;
*buffer++ = r;
#else
*buffer++ = r;
*buffer++ = l;
#endif
}
/* save filtered samples for next frame */
llp = ll;
rrp = rr;
/* keep remaining samples for next frame */
memcpy(snd.fm.buffer, fm, (snd.fm.pos - snd.fm.buffer) * 4);
memcpy(snd.psg.buffer, psg, (snd.psg.pos - snd.psg.buffer) * 2);
#ifdef LOGSOUND
error("%d samples returned\n\n",size);
#endif
@ -292,12 +312,6 @@ void system_init(void)
sound_init();
}
void system_shutdown (void)
{
gen_shutdown();
sound_shutdown();
}
void system_reset(void)
{
gen_reset(1);
@ -1084,7 +1098,7 @@ void system_frame_sms(int do_skip)
}
else
{
if (system_hw == SYSTEM_GG)
if ((system_hw == SYSTEM_GG) && !config.gg_extra)
{
/* Display area reduced to 160x144 */
bitmap.viewport.y = (144 - bitmap.viewport.h) / 2;

View File

@ -42,6 +42,8 @@
#ifndef _SYSTEM_H_
#define _SYSTEM_H_
#include "blip_buf.h"
/* Supported hardware models */
#define SYSTEM_SG 0x10
#define SYSTEM_MARKIII 0x11
@ -85,25 +87,10 @@ typedef struct
typedef struct
{
int sample_rate; /* Output Sample rate (8000-48000) */
double frame_rate; /* Output Frame rate (usually 50 or 60 frames per second) */
int enabled; /* 1= sound emulation is enabled */
int buffer_size; /* Size of sound buffer (in bytes) */
struct
{
int32 *pos;
int32 *buffer;
} fm;
struct
{
int16 *pos;
int16 *buffer;
} psg;
struct
{
int16 *pos;
int16 *buffer;
} pcm;
int sample_rate; /* Output Sample rate (8000-48000) */
double frame_rate; /* Output Frame rate (usually 50 or 60 frames per second) */
int enabled; /* 1= sound emulation is enabled */
blip_t* blips[3][2]; /* Blip Buffer resampling */
} t_snd;
@ -124,7 +111,6 @@ extern int audio_update(int16 *buffer);
extern void audio_set_equalizer(void);
extern void system_init(void);
extern void system_reset(void);
extern void system_shutdown(void);
extern void system_frame_gen(int do_skip);
extern void system_frame_scd(int do_skip);
extern void system_frame_sms(int do_skip);

View File

@ -279,7 +279,7 @@ void vdp_reset(void)
bitmap.viewport.oh = 192;
/* default overscan area */
if (system_hw == SYSTEM_GG)
if ((system_hw == SYSTEM_GG) && !config.gg_extra)
{
/* Display area reduced to 160x144 if overscan is disabled */
bitmap.viewport.x = (config.overscan & 2) ? 14 : -48;
@ -423,7 +423,7 @@ int vdp_context_save(uint8 *state)
return bufferptr;
}
int vdp_context_load(uint8 *state, char *version)
int vdp_context_load(uint8 *state)
{
int i, bufferptr = 0;
uint8 temp_reg[0x20];
@ -474,12 +474,7 @@ int vdp_context_load(uint8 *state, char *version)
load_param(&vint_pending, sizeof(vint_pending));
load_param(&dma_length, sizeof(dma_length));
load_param(&dma_type, sizeof(dma_type));
/* 1.7.x specific */
if (version[13] == 0x37)
{
load_param(&dma_src, sizeof(dma_src));
}
load_param(&dma_src, sizeof(dma_src));
load_param(&cached_write, sizeof(cached_write));
@ -750,11 +745,12 @@ void vdp_68k_ctrl_w(unsigned int data)
/* DMA source address */
dma_src = (reg[22] << 8) | reg[21];
/* SVP RAM or CD Word-RAM transfer */
if (((system_hw == SYSTEM_MCD) || svp) && ((reg[23] & 0x70) == 0x10))
/* Transfer from SVP DRAM ($300000-$31ffff) or CD Word-RAM ($200000-$3fffff/$600000-$7fffff) */
if (((system_hw == SYSTEM_MCD) && ((reg[23] & 0x70) == ((scd.cartridge.boot >> 1) + 0x10))) || (svp && ((reg[23] & 0x70) == 0x10)))
{
/* source data is available with one cycle delay, which means the first word written by VDP is previous data being held */
/* on 68k bus at that time, then source words are written normally to VDP RAM, with only last source word being ignored */
/* source data is available with one cycle delay, i.e first word written by VDP is */
/* previous data being held on 68k bus at that time, then source words are written */
/* normally to VDP RAM, with only last source word being ignored */
addr += reg[15];
dma_length--;
}
@ -990,7 +986,7 @@ void vdp_sms_ctrl_w(unsigned int data)
}
else
{
if (system_hw == SYSTEM_GG)
if ((system_hw == SYSTEM_GG) && !config.gg_extra)
{
/* Display area reduced to 160x144 */
bitmap.viewport.y = (144 - height) / 2;

View File

@ -89,7 +89,7 @@ extern unsigned int (*vdp_z80_data_r)(void);
extern void vdp_init(void);
extern void vdp_reset(void);
extern int vdp_context_save(uint8 *state);
extern int vdp_context_load(uint8 *state, char *version);
extern int vdp_context_load(uint8 *state);
extern void vdp_dma_update(unsigned int cycles);
extern void vdp_68k_ctrl_w(unsigned int data);
extern void vdp_z80_ctrl_w(unsigned int data);

View File

@ -2989,7 +2989,7 @@ void render_obj_tms(int max_width)
}
/* handle Game Gear reduced screen (160x144) */
if ((system_hw == SYSTEM_GG) && (v_counter < bitmap.viewport.h))
if ((system_hw == SYSTEM_GG) && !config.gg_extra && (v_counter < bitmap.viewport.h))
{
int line = v_counter - (bitmap.viewport.h - 144) / 2;
if ((line < 0) || (line >= 144))
@ -3094,7 +3094,7 @@ void render_obj_m4(int max_width)
}
/* handle Game Gear reduced screen (160x144) */
if ((system_hw == SYSTEM_GG) && (v_counter < bitmap.viewport.h))
if ((system_hw == SYSTEM_GG) && !config.gg_extra && (v_counter < bitmap.viewport.h))
{
int line = v_counter - (bitmap.viewport.h - 144) / 2;
if ((line < 0) || (line >= 144))

View File

@ -20,7 +20,6 @@ void set_config_defaults(void)
config.mg = 1.0;
config.hg = 1.0;
config.lp_range = 60;
config.rolloff = 0.995;
config.dac_bits = 14;
config.ym2413 = 2; /* = AUTO (0 = always OFF, 1 = always ON) */
@ -35,7 +34,8 @@ void set_config_defaults(void)
config.lock_on = 0; /* = OFF (can be TYPE_SK, TYPE_GG & TYPE_AR) */
/* display options */
config.overscan = 3; /* = both ON (0 = no borders , 1 = vertical borders only, 2 = horizontal borders only) */
config.overscan = 0; /* = both ON (0 = no borders , 1 = vertical borders only, 2 = horizontal borders only) */
config.gg_extra = 0; /* 1 = show extended Game Gear screen (256x192) */
config.render = 0; /* 1 = double resolution output (only when interlaced mode 2 is enabled) */
/* controllers options */

View File

@ -26,7 +26,6 @@ typedef struct
int16 lg;
int16 mg;
int16 hg;
float rolloff;
uint8 system;
uint8 region_detect;
uint8 vdp_mode;
@ -40,6 +39,7 @@ typedef struct
uint8 invert_mouse;
uint8 gun_cursor[2];
uint8 overscan;
uint8 gg_extra;
uint8 ntsc;
uint8 render;
t_input_config input[MAX_INPUTS];

View File

@ -86,7 +86,7 @@ static int sdl_sound_init()
}
sdl_sound.current_emulated_samples = 0;
n = SOUND_SAMPLES_SIZE * 2 * sizeof(short) * 11;
n = SOUND_SAMPLES_SIZE * 2 * sizeof(short) * 20;
sdl_sound.buffer = (char*)malloc(n);
if(!sdl_sound.buffer) {
MessageBox(NULL, "Can't allocate audio buffer", "Error", 0);
@ -308,7 +308,6 @@ static int sdl_control_update(SDLKey keystate)
{
case SDLK_TAB:
{
system_init();
system_reset();
break;
}
@ -369,8 +368,8 @@ static int sdl_control_update(SDLKey keystate)
if (f)
{
uint8 buf[STATE_SIZE];
state_save(buf);
fwrite(&buf, STATE_SIZE, 1, f);
int len = state_save(buf);
fwrite(&buf, len, 1, f);
fclose(f);
}
break;
@ -427,9 +426,6 @@ static int sdl_control_update(SDLKey keystate)
vc_max = vc_table[3][vdp_pal];
break;
}
/* reinitialize sound emulation */
sound_restore();
}
break;
}
@ -443,7 +439,7 @@ static int sdl_control_update(SDLKey keystate)
case SDLK_F11:
{
config.overscan = (config.overscan + 1) & 3;
if (system_hw == SYSTEM_GG)
if ((system_hw == SYSTEM_GG) && !config.gg_extra)
{
bitmap.viewport.x = (config.overscan & 2) ? 14 : -48;
}
@ -809,7 +805,8 @@ int main (int argc, char **argv)
}
}
}
else
if (sram.on)
{
/* load SRAM */
fp = fopen("./game.srm", "rb");
@ -883,7 +880,8 @@ int main (int argc, char **argv)
}
}
}
else
if (sram.on)
{
/* save SRAM */
fp = fopen("./game.srm", "wb");