mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-09-10 16:53:15 +02:00
[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:
parent
c8d4bb4f91
commit
f2a7b4cb8a
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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");
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
45
source/cart_hw/eeprom_i2c.h
Normal file
45
source/cart_hw/eeprom_i2c.h
Normal 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
358
source/cart_hw/eeprom_spi.c
Normal 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);
|
||||
}
|
||||
|
47
source/cart_hw/eeprom_spi.h
Normal file
47
source/cart_hw/eeprom_spi.h
Normal 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
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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
|
@ -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];
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 */
|
||||
|
@ -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))
|
||||
|
@ -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
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
****************************************************************************************/
|
||||
|
||||
#include "shared.h"
|
||||
#include "file_load.h"
|
||||
#include "cheats.h"
|
||||
#include "font.h"
|
||||
#include "gui.h"
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
156
source/gx/main.c
156
source/gx/main.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
247
source/loadrom.c
247
source/loadrom.c
@ -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);
|
||||
|
@ -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_ */
|
||||
|
||||
|
646
source/mem68k.c
646
source/mem68k.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
409
source/sound/blip_buf.c
Normal 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
74
source/sound/blip_buf.h
Normal 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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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_ */
|
@ -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);
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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_*/
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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); \
|
||||
|
316
source/system.c
316
source/system.c
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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 */
|
||||
|
@ -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];
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user