From f2a7b4cb8af3fb23722b64602f3c8f892966cf7c Mon Sep 17 00:00:00 2001 From: EkeEke Date: Sat, 13 Oct 2012 19:01:31 +0200 Subject: [PATCH] [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 --- Makefile.libretro | 10 +- Makefile.win32 | 24 +- source/cart_hw/areplay.c | 6 +- source/cart_hw/{gg_eeprom.c => eeprom_93c.c} | 509 ++++---- source/cart_hw/{gg_eeprom.h => eeprom_93c.h} | 147 ++- source/cart_hw/{md_eeprom.c => eeprom_i2c.c} | 1174 +++++++++--------- source/cart_hw/eeprom_i2c.h | 45 + source/cart_hw/eeprom_spi.c | 358 ++++++ source/cart_hw/eeprom_spi.h | 47 + source/cart_hw/ggenie.c | 4 +- source/cart_hw/md_cart.c | 535 ++++++-- source/cart_hw/md_eeprom.h | 111 -- source/cart_hw/sms_cart.c | 26 +- source/cart_hw/sram.c | 158 ++- source/cd_hw/cd_cart.c | 59 +- source/cd_hw/cd_cart.h | 15 +- source/cd_hw/cdc.c | 71 +- source/cd_hw/cdc.h | 2 + source/cd_hw/cdd.c | 926 +++++++++++--- source/cd_hw/cdd.h | 29 +- source/cd_hw/gfx.c | 44 + source/cd_hw/gfx.h | 2 + source/cd_hw/pcm.c | 93 +- source/cd_hw/pcm.h | 9 +- source/cd_hw/scd.c | 148 +-- source/cd_hw/scd.h | 5 +- source/genesis.c | 8 - source/genesis.h | 1 - source/gx/config.c | 2 +- source/gx/config.h | 4 +- source/gx/fileio/file_load.c | 47 +- source/gx/fileio/file_load.h | 2 + source/gx/fileio/file_slot.c | 26 +- source/gx/fileio/fileio.c | 5 +- source/gx/gui/cheats.c | 1 + source/gx/gui/filesel.c | 44 +- source/gx/gui/menu.c | 458 +++---- source/gx/gx_audio.c | 112 +- source/gx/gx_audio.h | 7 +- source/gx/gx_input.c | 8 +- source/gx/gx_video.c | 48 +- source/gx/gx_video.h | 2 +- source/gx/main.c | 156 +-- source/gx/osd.h | 9 +- source/io_ctrl.c | 3 +- source/loadrom.c | 247 ++-- source/loadrom.h | 2 +- source/mem68k.c | 646 +++++----- source/membnk.c | 2 +- source/memz80.c | 10 +- source/sound/Fir_Resampler.c | 354 ------ source/sound/Fir_Resampler.h | 43 - source/sound/blip.c | 139 --- source/sound/blip.h | 49 - source/sound/blip_buf.c | 409 ++++++ source/sound/blip_buf.h | 74 ++ source/sound/sn76489.c | 364 ++++-- source/sound/sn76489.h | 20 +- source/sound/sound.c | 349 ++---- source/sound/sound.h | 3 - source/sound/ym2413.c | 27 +- source/sound/ym2413.h | 4 +- source/sound/ym2612.c | 535 ++++---- source/sound/ym2612.h | 6 +- source/state.c | 92 +- source/state.h | 4 +- source/system.c | 316 ++--- source/system.h | 26 +- source/vdp_ctrl.c | 22 +- source/vdp_ctrl.h | 2 +- source/vdp_render.c | 4 +- source/win/config.c | 4 +- source/win/config.h | 2 +- source/win/main.c | 18 +- 74 files changed, 5270 insertions(+), 4003 deletions(-) rename source/cart_hw/{gg_eeprom.c => eeprom_93c.c} (61%) rename source/cart_hw/{gg_eeprom.h => eeprom_93c.h} (85%) rename source/cart_hw/{md_eeprom.c => eeprom_i2c.c} (51%) create mode 100644 source/cart_hw/eeprom_i2c.h create mode 100644 source/cart_hw/eeprom_spi.c create mode 100644 source/cart_hw/eeprom_spi.h delete mode 100644 source/cart_hw/md_eeprom.h delete mode 100644 source/sound/Fir_Resampler.c delete mode 100644 source/sound/Fir_Resampler.h delete mode 100644 source/sound/blip.c delete mode 100644 source/sound/blip.h create mode 100644 source/sound/blip_buf.c create mode 100644 source/sound/blip_buf.h diff --git a/Makefile.libretro b/Makefile.libretro index c0a287b..77a06be 100644 --- a/Makefile.libretro +++ b/Makefile.libretro @@ -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 \ diff --git a/Makefile.win32 b/Makefile.win32 index 21bf59a..2358232 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -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 \ diff --git a/source/cart_hw/areplay.c b/source/cart_hw/areplay.c index 941f823..c27f8d8 100644 --- a/source/cart_hw/areplay.c +++ b/source/cart_hw/areplay.c @@ -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"); diff --git a/source/cart_hw/gg_eeprom.c b/source/cart_hw/eeprom_93c.c similarity index 61% rename from source/cart_hw/gg_eeprom.c rename to source/cart_hw/eeprom_93c.c index 9fb88be..4892400 100644 --- a/source/cart_hw/gg_eeprom.c +++ b/source/cart_hw/eeprom_93c.c @@ -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)); +} + diff --git a/source/cart_hw/gg_eeprom.h b/source/cart_hw/eeprom_93c.h similarity index 85% rename from source/cart_hw/gg_eeprom.h rename to source/cart_hw/eeprom_93c.h index f818d83..ee262c0 100644 --- a/source/cart_hw/gg_eeprom.h +++ b/source/cart_hw/eeprom_93c.h @@ -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 diff --git a/source/cart_hw/md_eeprom.c b/source/cart_hw/eeprom_i2c.c similarity index 51% rename from source/cart_hw/md_eeprom.c rename to source/cart_hw/eeprom_i2c.c index a72b1d5..6019d68 100644 --- a/source/cart_hw/md_eeprom.c +++ b/source/cart_hw/eeprom_i2c.c @@ -1,553 +1,621 @@ -/**************************************************************************** - * 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. - * - ****************************************************************************************/ - -#include "shared.h" -#include "md_eeprom.h" - -#define GAME_CNT 28 - -T_EEPROM_24C md_eeprom; - -typedef struct -{ - char game_id[16]; - uint16 chk; - T_EEPROM_TYPE type; -} T_GAME_ENTRY; - -static const T_GAME_ENTRY database[GAME_CNT] = -{ - /* ACCLAIM mappers */ - /* 24C02 (old mapper) */ - {{"T-081326" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200001, 0, 1, 1}}, /* NBA Jam (UE) */ - {{"T-81033" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200001, 0, 1, 1}}, /* NBA Jam (J) */ - /* 24C02 */ - {{"T-081276" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NFL Quarterback Club */ - /* 24C04 */ - {{"T-81406" }, 0, {8, 0x1FF, 0x1FF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NBA Jam TE */ - /* 24C16 */ - {{"T-081586" }, 0, {8, 0x7FF, 0x7FF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NFL Quarterback Club '96 */ - /* 24C65 */ - {{"T-81576" }, 0, {16, 0x1FFF, 0x1FFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* College Slam */ - {{"T-81476" }, 0, {16, 0x1FFF, 0x1FFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* Frank Thomas Big Hurt Baseball */ - - /* EA mapper (24C01 only) */ - {{"T-50176" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* Rings of Power */ - {{"T-50396" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* NHLPA Hockey 93 */ - {{"T-50446" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* John Madden Football 93 */ - {{"T-50516" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* John Madden Football 93 (Championship Ed.) */ - {{"T-50606" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* Bill Walsh College Football */ - - /* SEGA mapper (24C01 only) */ - {{"T-12046" }, 0xAD23, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Megaman - The Wily Wars */ - {{"T-12053" }, 0xEA80, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Rockman Mega World [Alt] */ - {{"MK-1215" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Evander 'Real Deal' Holyfield's Boxing */ - {{"MK-1228" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Greatest Heavyweights of the Ring (U) */ - {{"G-5538" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Greatest Heavyweights of the Ring (J) */ - {{"PR-1993" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Greatest Heavyweights of the Ring (E) */ - {{"G-4060" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Wonderboy in Monster World */ - {{"00001211-00"}, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Sports Talk Baseball */ - {{"00004076-00"}, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Honoo no Toukyuuji Dodge Danpei */ - {{"G-4524" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Ninja Burai Densetsu */ - {{"00054503-00"}, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Game Toshokan */ - - /* CODEMASTERS mapper */ - /* 24C08 */ - {{"T-120106" }, 0, {8, 0x3FF, 0x3FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Brian Lara Cricket */ - {{"00000000-00"}, 0xCEE0, {8, 0x3FF, 0x3FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines Military */ - /* 24C16 */ - {{"T-120096" }, 0, {8, 0x7FF, 0x7FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines 2 - Turbo Tournament */ - {{"00000000-00"}, 0x2C41, {8, 0x7FF, 0x7FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines Turbo Tournament 96 */ - /* 24C65 */ - {{"T-120146-50"}, 0, {16, 0x1FFF, 0x1FFF, 0x300000, 0x380001, 0x300000, 0, 7, 1}} /* Brian Lara Cricket 96, Shane Warne Cricket */ -}; - - -static void eeprom_update(void); -static unsigned char eeprom_out(void); - -void md_eeprom_init() -{ - int i = 0; - - /* initialize eeprom */ - memset(&md_eeprom, 0, sizeof(T_EEPROM_24C)); - md_eeprom.sda = md_eeprom.old_sda = 1; - md_eeprom.scl = md_eeprom.old_scl = 1; - md_eeprom.state = STAND_BY; - - /* no eeprom by default */ - sram.custom = 0; - - /* look into game database */ - while (i> md_eeprom.type.sda_in_bit) & 1; - do_update = 1; - } - - if (address == md_eeprom.type.scl_adr) - { - md_eeprom.scl = (data >> md_eeprom.type.scl_bit) & 1; - do_update = 1; - } - - if (do_update) - { - eeprom_update(); - return; - } - - m68k_unused_8_w(address, data); -} - -void md_eeprom_write_word(unsigned int address, unsigned int data) -{ - int do_update = 0; - - if (address == md_eeprom.type.sda_in_adr) - { - md_eeprom.sda = (data >> (8 + md_eeprom.type.sda_in_bit)) & 1; - do_update = 1; - } - else if (address == (md_eeprom.type.sda_in_adr ^1)) - { - md_eeprom.sda = (data >> md_eeprom.type.sda_in_bit) & 1; - do_update = 1; - } - - if (address == md_eeprom.type.scl_adr) - { - md_eeprom.scl = (data >> (8 + md_eeprom.type.scl_bit)) & 1; - do_update = 1; - } - else if (address == (md_eeprom.type.scl_adr ^1)) - { - md_eeprom.scl = (data >> md_eeprom.type.scl_bit) & 1; - do_update = 1; - } - - if (do_update) - { - eeprom_update(); - return; - } - - m68k_unused_16_w(address, data); -} - - -INLINE void Detect_START() -{ - if (md_eeprom.old_scl && md_eeprom.scl) - { - if (md_eeprom.old_sda && !md_eeprom.sda) - { - md_eeprom.cycles = 0; - md_eeprom.slave_mask = 0; - if (md_eeprom.type.address_bits == 7) - { - md_eeprom.word_address = 0; - md_eeprom.state = GET_WORD_ADR_7BITS; - } - else md_eeprom.state = GET_SLAVE_ADR; - } - } -} - -INLINE void Detect_STOP() -{ - if (md_eeprom.old_scl && md_eeprom.scl) - { - if (!md_eeprom.old_sda && md_eeprom.sda) - { - md_eeprom.state = STAND_BY; - } - } -} - -static void eeprom_update(void) -{ - /* EEPROM current state */ - switch (md_eeprom.state) - { - /* Standby Mode */ - case STAND_BY: - { - Detect_START(); - Detect_STOP(); - break; - } - - /* Suspended Mode */ - case WAIT_STOP: - { - Detect_STOP(); - break; - } - - /* Get Word Address 7 bits: MODE-1 only (24C01) - * and R/W bit - */ - case GET_WORD_ADR_7BITS: - { - Detect_START(); - Detect_STOP(); - - /* look for SCL LOW to HIGH transition */ - if (!md_eeprom.old_scl && md_eeprom.scl) - { - if (md_eeprom.cycles == 0) md_eeprom.cycles ++; - } - - - /* look for SCL HIGH to LOW transition */ - if (md_eeprom.old_scl && !md_eeprom.scl && (md_eeprom.cycles > 0)) - { - if (md_eeprom.cycles < 8) - { - md_eeprom.word_address |= (md_eeprom.old_sda << (7 - md_eeprom.cycles)); - } - else if (md_eeprom.cycles == 8) - { - md_eeprom.rw = md_eeprom.old_sda; - } - else - { /* ACK CYCLE */ - md_eeprom.cycles = 0; - md_eeprom.word_address &= md_eeprom.type.size_mask; - md_eeprom.state = md_eeprom.rw ? READ_DATA : WRITE_DATA; - } - - md_eeprom.cycles ++; - } - break; - } - - /* Get Slave Address (3bits) : MODE-2 & MODE-3 only (24C01 - 24C512) (0-3bits, depending on the array size) - * or/and Word Address MSB: MODE-2 only (24C04 - 24C16) (0-3bits, depending on the array size) - * and R/W bit - */ - case GET_SLAVE_ADR: - { - Detect_START(); - Detect_STOP(); - - /* look for SCL LOW to HIGH transition */ - if (!md_eeprom.old_scl && md_eeprom.scl) - { - if (md_eeprom.cycles == 0) md_eeprom.cycles ++; - } - - /* look for SCL HIGH to LOW transition */ - if (md_eeprom.old_scl && !md_eeprom.scl && (md_eeprom.cycles > 0)) - { - if ((md_eeprom.cycles > 4) && (md_eeprom.cycles <8)) - { - if ((md_eeprom.type.address_bits == 16) || - (md_eeprom.type.size_mask < (1 << (15 - md_eeprom.cycles)))) - { - /* this is a SLAVE ADDRESS bit */ - md_eeprom.slave_mask |= (md_eeprom.old_sda << (7 - md_eeprom.cycles)); - } - else - { - /* this is a WORD ADDRESS high bit */ - if (md_eeprom.old_sda) md_eeprom.word_address |= (1 << (15 - md_eeprom.cycles)); - else md_eeprom.word_address &= ~(1 << (15 - md_eeprom.cycles)); - } - } - else if (md_eeprom.cycles == 8) md_eeprom.rw = md_eeprom.old_sda; - else if (md_eeprom.cycles > 8) - { - /* ACK CYCLE */ - md_eeprom.cycles = 0; - if (md_eeprom.type.address_bits == 16) - { - /* two ADDRESS bytes */ - md_eeprom.state = md_eeprom.rw ? READ_DATA : GET_WORD_ADR_HIGH; - md_eeprom.slave_mask <<= 16; - } - else - { - /* one ADDRESS byte */ - md_eeprom.state = md_eeprom.rw ? READ_DATA : GET_WORD_ADR_LOW; - md_eeprom.slave_mask <<= 8; - } - } - - md_eeprom.cycles ++; - } - break; - } - - /* Get Word Address MSB (4-8bits depending on the array size) - * MODE-3 only (24C32 - 24C512) - */ - case GET_WORD_ADR_HIGH: - { - Detect_START(); - Detect_STOP(); - - /* look for SCL HIGH to LOW transition */ - if (md_eeprom.old_scl && !md_eeprom.scl) - { - if (md_eeprom.cycles < 9) - { - if ((md_eeprom.type.size_mask + 1) < (1 << (17 - md_eeprom.cycles))) - { - /* ignored bit: slave mask should be right-shifted by one */ - md_eeprom.slave_mask >>= 1; - } - else - { - /* this is a WORD ADDRESS high bit */ - if (md_eeprom.old_sda) md_eeprom.word_address |= (1 << (16 - md_eeprom.cycles)); - else md_eeprom.word_address &= ~(1 << (16 - md_eeprom.cycles)); - } - - md_eeprom.cycles ++; - } - else - { - /* ACK CYCLE */ - md_eeprom.cycles = 1; - md_eeprom.state = GET_WORD_ADR_LOW; - } - } - break; - } - - /* Get Word Address LSB: 7bits (24C01) or 8bits (24C02-24C512) - * MODE-2 and MODE-3 only (24C01 - 24C512) - */ - case GET_WORD_ADR_LOW: - { - Detect_START(); - Detect_STOP(); - - /* look for SCL HIGH to LOW transition */ - if (md_eeprom.old_scl && !md_eeprom.scl) - { - if (md_eeprom.cycles < 9) - { - if ((md_eeprom.type.size_mask + 1) < (1 << (9 - md_eeprom.cycles))) - { - /* ignored bit (X24C01): slave mask should be right-shifted by one */ - md_eeprom.slave_mask >>= 1; - } - else - { - /* this is a WORD ADDRESS high bit */ - if (md_eeprom.old_sda) md_eeprom.word_address |= (1 << (8 - md_eeprom.cycles)); - else md_eeprom.word_address &= ~(1 << (8 - md_eeprom.cycles)); - } - - md_eeprom.cycles ++; - } - else - { - /* ACK CYCLE */ - md_eeprom.cycles = 1; - md_eeprom.word_address &= md_eeprom.type.size_mask; - md_eeprom.state = WRITE_DATA; - } - } - break; - } - - /* - * Read Cycle - */ - case READ_DATA: - { - Detect_START(); - Detect_STOP(); - - /* look for SCL HIGH to LOW transition */ - if (md_eeprom.old_scl && !md_eeprom.scl) - { - if (md_eeprom.cycles < 9) md_eeprom.cycles ++; - else - { - md_eeprom.cycles = 1; - - /* ACK not received */ - if (md_eeprom.old_sda) md_eeprom.state = WAIT_STOP; - } - } - break; - } - - /* - * Write Cycle - */ - case WRITE_DATA: - { - Detect_START(); - Detect_STOP(); - - /* look for SCL HIGH to LOW transition */ - if (md_eeprom.old_scl && !md_eeprom.scl) - { - if (md_eeprom.cycles < 9) - { - /* Write DATA bits (max 64kBytes) */ - uint16 sram_address = (md_eeprom.slave_mask | md_eeprom.word_address) & 0xFFFF; - if (md_eeprom.old_sda) sram.sram[sram_address] |= (1 << (8 - md_eeprom.cycles)); - else sram.sram[sram_address] &= ~(1 << (8 - md_eeprom.cycles)); - - if (md_eeprom.cycles == 8) - { - /* WORD ADDRESS is incremented (roll up at maximum pagesize) */ - md_eeprom.word_address = (md_eeprom.word_address & (0xFFFF - md_eeprom.type.pagewrite_mask)) | - ((md_eeprom.word_address + 1) & md_eeprom.type.pagewrite_mask); - } - - md_eeprom.cycles ++; - } - else md_eeprom.cycles = 1; /* ACK cycle */ - } - break; - } - } - - md_eeprom.old_scl = md_eeprom.scl; - md_eeprom.old_sda = md_eeprom.sda; -} - -static unsigned char eeprom_out(void) -{ - uint8 sda_out = md_eeprom.sda; - - /* EEPROM state */ - switch (md_eeprom.state) - { - case READ_DATA: - { - if (md_eeprom.cycles < 9) - { - /* Return DATA bits (max 64kBytes) */ - uint16 sram_address = (md_eeprom.slave_mask | md_eeprom.word_address) & 0xffff; - sda_out = (sram.sram[sram_address] >> (8 - md_eeprom.cycles)) & 1; - - if (md_eeprom.cycles == 8) - { - /* WORD ADDRESS is incremented (roll up at maximum array size) */ - md_eeprom.word_address ++; - md_eeprom.word_address &= md_eeprom.type.size_mask; - } - } - break; - } - - case GET_WORD_ADR_7BITS: - case GET_SLAVE_ADR: - case GET_WORD_ADR_HIGH: - case GET_WORD_ADR_LOW: - case WRITE_DATA: - { - if (md_eeprom.cycles == 9) sda_out = 0; - break; - } - - default: - { - break; - } - } - - return (sda_out << md_eeprom.type.sda_out_bit); -} +/**************************************************************************** + * 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. + * + ****************************************************************************************/ + +#include "shared.h" + +#define GAME_CNT 28 + +/* 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_CONFIG_I2C; + +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_STATE_I2C; + +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_STATE_I2C state; /* current operation state */ + T_CONFIG_I2C config; /* EEPROM characteristics for this game */ +} T_EEPROM_I2C; + +typedef struct +{ + char game_id[16]; + uint16 chk; + T_CONFIG_I2C config; +} T_GAME_ENTRY; + +static const T_GAME_ENTRY database[GAME_CNT] = +{ + /* ACCLAIM mappers */ + /* 24C02 (old mapper) */ + {{"T-081326" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200001, 0, 1, 1}}, /* NBA Jam (UE) */ + {{"T-81033" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200001, 0, 1, 1}}, /* NBA Jam (J) */ + /* 24C02 */ + {{"T-081276" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NFL Quarterback Club */ + /* 24C04 */ + {{"T-81406" }, 0, {8, 0x1FF, 0x1FF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NBA Jam TE */ + /* 24C16 */ + {{"T-081586" }, 0, {8, 0x7FF, 0x7FF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NFL Quarterback Club '96 */ + /* 24C65 */ + {{"T-81576" }, 0, {16, 0x1FFF, 0x1FFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* College Slam */ + {{"T-81476" }, 0, {16, 0x1FFF, 0x1FFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* Frank Thomas Big Hurt Baseball */ + + /* EA mapper (X24C01 only) */ + {{"T-50176" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* Rings of Power */ + {{"T-50396" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* NHLPA Hockey 93 */ + {{"T-50446" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* John Madden Football 93 */ + {{"T-50516" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* John Madden Football 93 (Championship Ed.) */ + {{"T-50606" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* Bill Walsh College Football */ + + /* SEGA mapper (X24C01 only) */ + {{"T-12046" }, 0xAD23, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Megaman - The Wily Wars */ + {{"T-12053" }, 0xEA80, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Rockman Mega World [Alt] */ + {{"MK-1215" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Evander 'Real Deal' Holyfield's Boxing */ + {{"MK-1228" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Greatest Heavyweights of the Ring (U) */ + {{"G-5538" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Greatest Heavyweights of the Ring (J) */ + {{"PR-1993" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Greatest Heavyweights of the Ring (E) */ + {{"G-4060" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Wonderboy in Monster World */ + {{"00001211-00"}, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Sports Talk Baseball */ + {{"00004076-00"}, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Honoo no Toukyuuji Dodge Danpei */ + {{"G-4524" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Ninja Burai Densetsu */ + {{"00054503-00"}, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Game Toshokan */ + + /* CODEMASTERS mapper */ + /* 24C08 */ + {{"T-120106" }, 0, {8, 0x3FF, 0x3FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Brian Lara Cricket */ + {{"00000000-00"}, 0xCEE0, {8, 0x3FF, 0x3FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines Military */ + /* 24C16 */ + {{"T-120096" }, 0, {8, 0x7FF, 0x7FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines 2 - Turbo Tournament */ + {{"00000000-00"}, 0x2C41, {8, 0x7FF, 0x7FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines Turbo Tournament 96 */ + /* 24C65 */ + {{"T-120146-50"}, 0, {16, 0x1FFF, 0x1FFF, 0x300000, 0x380001, 0x300000, 0, 7, 1}} /* Brian Lara Cricket 96, Shane Warne Cricket */ +}; + +static T_EEPROM_I2C eeprom_i2c; + +static unsigned int eeprom_i2c_read_byte(unsigned int address); +static unsigned int eeprom_i2c_read_word(unsigned int address); +static void eeprom_i2c_write_byte(unsigned int address, unsigned int data); +static void eeprom_i2c_write_word(unsigned int address, unsigned int data); + +void eeprom_i2c_init() +{ + int i = 0; + + /* initialize eeprom */ + memset(&eeprom_i2c, 0, sizeof(T_EEPROM_I2C)); + eeprom_i2c.sda = eeprom_i2c.old_sda = 1; + eeprom_i2c.scl = eeprom_i2c.old_scl = 1; + eeprom_i2c.state = STAND_BY; + + /* no eeprom by default */ + sram.custom = 0; + + /* look into game database */ + while (i> 16].read8 = eeprom_i2c_read_byte; + m68k.memory_map[eeprom_i2c.config.sda_out_adr >> 16].read16 = eeprom_i2c_read_word; + m68k.memory_map[eeprom_i2c.config.sda_in_adr >> 16].read8 = eeprom_i2c_read_byte; + m68k.memory_map[eeprom_i2c.config.sda_in_adr >> 16].read16 = eeprom_i2c_read_word; + m68k.memory_map[eeprom_i2c.config.scl_adr >> 16].write8 = eeprom_i2c_write_byte; + m68k.memory_map[eeprom_i2c.config.scl_adr >> 16].write16 = eeprom_i2c_write_word; + zbank_memory_map[eeprom_i2c.config.sda_out_adr >> 16].read = eeprom_i2c_read_byte; + zbank_memory_map[eeprom_i2c.config.sda_in_adr >> 16].read = eeprom_i2c_read_byte; + zbank_memory_map[eeprom_i2c.config.scl_adr >> 16].write = eeprom_i2c_write_byte; + } +} + +INLINE void Detect_START() +{ + if (eeprom_i2c.old_scl && eeprom_i2c.scl) + { + if (eeprom_i2c.old_sda && !eeprom_i2c.sda) + { + eeprom_i2c.cycles = 0; + eeprom_i2c.slave_mask = 0; + if (eeprom_i2c.config.address_bits == 7) + { + eeprom_i2c.word_address = 0; + eeprom_i2c.state = GET_WORD_ADR_7BITS; + } + else eeprom_i2c.state = GET_SLAVE_ADR; + } + } +} + +INLINE void Detect_STOP() +{ + if (eeprom_i2c.old_scl && eeprom_i2c.scl) + { + if (!eeprom_i2c.old_sda && eeprom_i2c.sda) + { + eeprom_i2c.state = STAND_BY; + } + } +} + +static void eeprom_i2c_update(void) +{ + /* EEPROM current state */ + switch (eeprom_i2c.state) + { + /* Standby Mode */ + case STAND_BY: + { + Detect_START(); + Detect_STOP(); + break; + } + + /* Suspended Mode */ + case WAIT_STOP: + { + Detect_STOP(); + break; + } + + /* Get Word Address 7 bits: MODE-1 only (24C01) + * and R/W bit + */ + case GET_WORD_ADR_7BITS: + { + Detect_START(); + Detect_STOP(); + + /* look for SCL LOW to HIGH transition */ + if (!eeprom_i2c.old_scl && eeprom_i2c.scl) + { + if (eeprom_i2c.cycles == 0) eeprom_i2c.cycles ++; + } + + + /* look for SCL HIGH to LOW transition */ + if (eeprom_i2c.old_scl && !eeprom_i2c.scl && (eeprom_i2c.cycles > 0)) + { + if (eeprom_i2c.cycles < 8) + { + eeprom_i2c.word_address |= (eeprom_i2c.old_sda << (7 - eeprom_i2c.cycles)); + } + else if (eeprom_i2c.cycles == 8) + { + eeprom_i2c.rw = eeprom_i2c.old_sda; + } + else + { /* ACK CYCLE */ + eeprom_i2c.cycles = 0; + eeprom_i2c.word_address &= eeprom_i2c.config.size_mask; + eeprom_i2c.state = eeprom_i2c.rw ? READ_DATA : WRITE_DATA; + } + + eeprom_i2c.cycles ++; + } + break; + } + + /* Get Slave Address (3bits) : MODE-2 & MODE-3 only (24C01 - 24C512) (0-3bits, depending on the array size) + * or/and Word Address MSB: MODE-2 only (24C04 - 24C16) (0-3bits, depending on the array size) + * and R/W bit + */ + case GET_SLAVE_ADR: + { + Detect_START(); + Detect_STOP(); + + /* look for SCL LOW to HIGH transition */ + if (!eeprom_i2c.old_scl && eeprom_i2c.scl) + { + if (eeprom_i2c.cycles == 0) eeprom_i2c.cycles ++; + } + + /* look for SCL HIGH to LOW transition */ + if (eeprom_i2c.old_scl && !eeprom_i2c.scl && (eeprom_i2c.cycles > 0)) + { + if ((eeprom_i2c.cycles > 4) && (eeprom_i2c.cycles <8)) + { + if ((eeprom_i2c.config.address_bits == 16) || + (eeprom_i2c.config.size_mask < (1 << (15 - eeprom_i2c.cycles)))) + { + /* this is a SLAVE ADDRESS bit */ + eeprom_i2c.slave_mask |= (eeprom_i2c.old_sda << (7 - eeprom_i2c.cycles)); + } + else + { + /* this is a WORD ADDRESS high bit */ + if (eeprom_i2c.old_sda) eeprom_i2c.word_address |= (1 << (15 - eeprom_i2c.cycles)); + else eeprom_i2c.word_address &= ~(1 << (15 - eeprom_i2c.cycles)); + } + } + else if (eeprom_i2c.cycles == 8) eeprom_i2c.rw = eeprom_i2c.old_sda; + else if (eeprom_i2c.cycles > 8) + { + /* ACK CYCLE */ + eeprom_i2c.cycles = 0; + if (eeprom_i2c.config.address_bits == 16) + { + /* two ADDRESS bytes */ + eeprom_i2c.state = eeprom_i2c.rw ? READ_DATA : GET_WORD_ADR_HIGH; + eeprom_i2c.slave_mask <<= 16; + } + else + { + /* one ADDRESS byte */ + eeprom_i2c.state = eeprom_i2c.rw ? READ_DATA : GET_WORD_ADR_LOW; + eeprom_i2c.slave_mask <<= 8; + } + } + + eeprom_i2c.cycles ++; + } + break; + } + + /* Get Word Address MSB (4-8bits depending on the array size) + * MODE-3 only (24C32 - 24C512) + */ + case GET_WORD_ADR_HIGH: + { + Detect_START(); + Detect_STOP(); + + /* look for SCL HIGH to LOW transition */ + if (eeprom_i2c.old_scl && !eeprom_i2c.scl) + { + if (eeprom_i2c.cycles < 9) + { + if ((eeprom_i2c.config.size_mask + 1) < (1 << (17 - eeprom_i2c.cycles))) + { + /* ignored bit: slave mask should be right-shifted by one */ + eeprom_i2c.slave_mask >>= 1; + } + else + { + /* this is a WORD ADDRESS high bit */ + if (eeprom_i2c.old_sda) eeprom_i2c.word_address |= (1 << (16 - eeprom_i2c.cycles)); + else eeprom_i2c.word_address &= ~(1 << (16 - eeprom_i2c.cycles)); + } + + eeprom_i2c.cycles ++; + } + else + { + /* ACK CYCLE */ + eeprom_i2c.cycles = 1; + eeprom_i2c.state = GET_WORD_ADR_LOW; + } + } + break; + } + + /* Get Word Address LSB: 7bits (24C01) or 8bits (24C02-24C512) + * MODE-2 and MODE-3 only (24C01 - 24C512) + */ + case GET_WORD_ADR_LOW: + { + Detect_START(); + Detect_STOP(); + + /* look for SCL HIGH to LOW transition */ + if (eeprom_i2c.old_scl && !eeprom_i2c.scl) + { + if (eeprom_i2c.cycles < 9) + { + if ((eeprom_i2c.config.size_mask + 1) < (1 << (9 - eeprom_i2c.cycles))) + { + /* ignored bit (X24C01): slave mask should be right-shifted by one */ + eeprom_i2c.slave_mask >>= 1; + } + else + { + /* this is a WORD ADDRESS high bit */ + if (eeprom_i2c.old_sda) eeprom_i2c.word_address |= (1 << (8 - eeprom_i2c.cycles)); + else eeprom_i2c.word_address &= ~(1 << (8 - eeprom_i2c.cycles)); + } + + eeprom_i2c.cycles ++; + } + else + { + /* ACK CYCLE */ + eeprom_i2c.cycles = 1; + eeprom_i2c.word_address &= eeprom_i2c.config.size_mask; + eeprom_i2c.state = WRITE_DATA; + } + } + break; + } + + /* + * Read Cycle + */ + case READ_DATA: + { + Detect_START(); + Detect_STOP(); + + /* look for SCL HIGH to LOW transition */ + if (eeprom_i2c.old_scl && !eeprom_i2c.scl) + { + if (eeprom_i2c.cycles < 9) eeprom_i2c.cycles ++; + else + { + eeprom_i2c.cycles = 1; + + /* ACK not received */ + if (eeprom_i2c.old_sda) eeprom_i2c.state = WAIT_STOP; + } + } + break; + } + + /* + * Write Cycle + */ + case WRITE_DATA: + { + Detect_START(); + Detect_STOP(); + + /* look for SCL HIGH to LOW transition */ + if (eeprom_i2c.old_scl && !eeprom_i2c.scl) + { + if (eeprom_i2c.cycles < 9) + { + /* Write DATA bits (max 64kBytes) */ + uint16 sram_address = (eeprom_i2c.slave_mask | eeprom_i2c.word_address) & 0xFFFF; + if (eeprom_i2c.old_sda) sram.sram[sram_address] |= (1 << (8 - eeprom_i2c.cycles)); + else sram.sram[sram_address] &= ~(1 << (8 - eeprom_i2c.cycles)); + + if (eeprom_i2c.cycles == 8) + { + /* WORD ADDRESS is incremented (roll up at maximum pagesize) */ + eeprom_i2c.word_address = (eeprom_i2c.word_address & (0xFFFF - eeprom_i2c.config.pagewrite_mask)) | + ((eeprom_i2c.word_address + 1) & eeprom_i2c.config.pagewrite_mask); + } + + eeprom_i2c.cycles ++; + } + else eeprom_i2c.cycles = 1; /* ACK cycle */ + } + break; + } + } + + eeprom_i2c.old_scl = eeprom_i2c.scl; + eeprom_i2c.old_sda = eeprom_i2c.sda; +} + +static unsigned char eeprom_i2c_out(void) +{ + uint8 sda_out = eeprom_i2c.sda; + + /* EEPROM state */ + switch (eeprom_i2c.state) + { + case READ_DATA: + { + if (eeprom_i2c.cycles < 9) + { + /* Return DATA bits (max 64kBytes) */ + uint16 sram_address = (eeprom_i2c.slave_mask | eeprom_i2c.word_address) & 0xffff; + sda_out = (sram.sram[sram_address] >> (8 - eeprom_i2c.cycles)) & 1; + + if (eeprom_i2c.cycles == 8) + { + /* WORD ADDRESS is incremented (roll up at maximum array size) */ + eeprom_i2c.word_address ++; + eeprom_i2c.word_address &= eeprom_i2c.config.size_mask; + } + } + break; + } + + case GET_WORD_ADR_7BITS: + case GET_SLAVE_ADR: + case GET_WORD_ADR_HIGH: + case GET_WORD_ADR_LOW: + case WRITE_DATA: + { + if (eeprom_i2c.cycles == 9) sda_out = 0; + break; + } + + default: + { + break; + } + } + + return (sda_out << eeprom_i2c.config.sda_out_bit); +} + +static unsigned int eeprom_i2c_read_byte(unsigned int address) +{ + if (address == eeprom_i2c.config.sda_out_adr) + { + return eeprom_i2c_out(); + } + + return READ_BYTE(cart.rom, address); +} + +static unsigned int eeprom_i2c_read_word(unsigned int address) +{ + if (address == eeprom_i2c.config.sda_out_adr) + { + return (eeprom_i2c_out() << 8); + } + + if (address == (eeprom_i2c.config.sda_out_adr ^ 1)) + { + return eeprom_i2c_out(); + } + + return *(uint16 *)(cart.rom + address); +} + +static void eeprom_i2c_write_byte(unsigned int address, unsigned int data) +{ + int do_update = 0; + + if (address == eeprom_i2c.config.sda_in_adr) + { + eeprom_i2c.sda = (data >> eeprom_i2c.config.sda_in_bit) & 1; + do_update = 1; + } + + if (address == eeprom_i2c.config.scl_adr) + { + eeprom_i2c.scl = (data >> eeprom_i2c.config.scl_bit) & 1; + do_update = 1; + } + + if (do_update) + { + eeprom_i2c_update(); + return; + } + + m68k_unused_8_w(address, data); +} + +static void eeprom_i2c_write_word(unsigned int address, unsigned int data) +{ + int do_update = 0; + + if (address == eeprom_i2c.config.sda_in_adr) + { + eeprom_i2c.sda = (data >> (8 + eeprom_i2c.config.sda_in_bit)) & 1; + do_update = 1; + } + else if (address == (eeprom_i2c.config.sda_in_adr ^1)) + { + eeprom_i2c.sda = (data >> eeprom_i2c.config.sda_in_bit) & 1; + do_update = 1; + } + + if (address == eeprom_i2c.config.scl_adr) + { + eeprom_i2c.scl = (data >> (8 + eeprom_i2c.config.scl_bit)) & 1; + do_update = 1; + } + else if (address == (eeprom_i2c.config.scl_adr ^1)) + { + eeprom_i2c.scl = (data >> eeprom_i2c.config.scl_bit) & 1; + do_update = 1; + } + + if (do_update) + { + eeprom_i2c_update(); + return; + } + + m68k_unused_16_w(address, data); +} diff --git a/source/cart_hw/eeprom_i2c.h b/source/cart_hw/eeprom_i2c.h new file mode 100644 index 0000000..b47ce02 --- /dev/null +++ b/source/cart_hw/eeprom_i2c.h @@ -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 diff --git a/source/cart_hw/eeprom_spi.c b/source/cart_hw/eeprom_spi.c new file mode 100644 index 0000000..921eb6a --- /dev/null +++ b/source/cart_hw/eeprom_spi.c @@ -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); +} + diff --git a/source/cart_hw/eeprom_spi.h b/source/cart_hw/eeprom_spi.h new file mode 100644 index 0000000..1001e6e --- /dev/null +++ b/source/cart_hw/eeprom_spi.h @@ -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 diff --git a/source/cart_hw/ggenie.c b/source/cart_hw/ggenie.c index feae975..e6d82c1 100644 --- a/source/cart_hw/ggenie.c +++ b/source/cart_hw/ggenie.c @@ -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"); diff --git a/source/cart_hw/md_cart.c b/source/cart_hw/md_cart.c index efe9c9b..8a2be24 100644 --- a/source/cart_hw/md_cart.c +++ b/source/cart_hw/md_cart.c @@ -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) diff --git a/source/cart_hw/md_eeprom.h b/source/cart_hw/md_eeprom.h deleted file mode 100644 index a0cd997..0000000 --- a/source/cart_hw/md_eeprom.h +++ /dev/null @@ -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 diff --git a/source/cart_hw/sms_cart.c b/source/cart_hw/sms_cart.c index 74aa67c..1d3728e 100644 --- a/source/cart_hw/sms_cart.c +++ b/source/cart_hw/sms_cart.c @@ -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]; diff --git a/source/cart_hw/sram.c b/source/cart_hw/sram.c index c7e3b12..ae83e01 100644 --- a/source/cart_hw/sram.c +++ b/source/cart_hw/sram.c @@ -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; - } } diff --git a/source/cd_hw/cd_cart.c b/source/cd_hw/cd_cart.c index 068dfea..7cfd306 100644 --- a/source/cd_hw/cd_cart.c +++ b/source/cd_hw/cd_cart.c @@ -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 (0x4000 - 2048 - 4)) diff --git a/source/cd_hw/cdc.h b/source/cd_hw/cdc.h index 225114a..463a8f0 100644 --- a/source/cd_hw/cdc.h +++ b/source/cd_hw/cdc.h @@ -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); diff --git a/source/cd_hw/cdd.c b/source/cd_hw/cdd.c index d99d5f3..5973c2d 100644 --- a/source/cd_hw/cdd.c +++ b/source/cd_hw/cdd.c @@ -1,6 +1,6 @@ /*************************************************************************************** * Genesis Plus - * CD drive processor + * CD drive processor & CD-DA fader * * Copyright (C) 2012 Eke-Eke (Genesis Plus GX) * @@ -37,14 +37,6 @@ ****************************************************************************************/ #include "shared.h" -#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 - /* BCD conversion lookup tables */ static const uint8 lut_BCD_8[100] = { @@ -74,17 +66,56 @@ static const uint16 lut_BCD_16[100] = 0x0900, 0x0901, 0x0902, 0x0903, 0x0904, 0x0905, 0x0906, 0x0907, 0x0908, 0x0909, }; - +/* pre-build TOC */ static const uint16 toc_snatcher[21] = { - 56164, 495, 10120, 20555, 1580, 5417, 12502, 16090, 6553, 9681, - 8148, 20228, 8622, 6142, 5858, 1287, 7424, 3535, 31697, 2485, + 56014, 495, 10120, 20555, 1580, 5417, 12502, 16090, 6553, 9681, + 8148, 20228, 8622, 6142, 5858, 1287, 7424, 3535, 31697, 2485, 31380 }; - -void cdd_init(void) +static const uint16 toc_lunar[52] = { + 5422, 1057, 7932, 5401, 6380, 6592, 5862, 5937, 5478, 5870, + 6673, 6613, 6429, 4996, 4977, 5657, 3720, 5892, 3140, 3263, + 6351, 5187, 3249, 1464, 1596, 1750, 1751, 6599, 4578, 5205, + 1550, 1827, 2328, 1346, 1569, 1613, 7199, 4928, 1656, 2549, + 1875, 3901, 1850, 2399, 2028, 1724, 4889, 14551, 1184, 2132, + 685, 3167 +}; + +/* supported WAVE file header (16-bit stereo samples @44.1kHz) */ +static const unsigned char waveHeader[32] = +{ + 0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00, + 0x44,0xac,0x00,0x00,0x10,0xb1,0x02,0x00,0x04,0x00,0x10,0x00,0x64,0x61,0x74,0x61 +}; + +/* supported file extensions */ +static const char extensions[10][16] = +{ + "%02d.wav", + " %02d.wav", + "-%02d.wav", + "_%02d.wav", + " - %02d.wav", + "%d.wav", + " %d.wav", + "-%d.wav", + "_%d.wav", + " - %d.wav" +}; + +static blip_t* blip[2]; + +void cdd_init(blip_t* left, blip_t* right) +{ + /* CD-DA is running by default at 44100 Hz */ + /* Audio stream is resampled to desired rate using Blip Buffer */ + blip[0] = left; + blip[1] = right; + blip_set_rates(left, 44100, snd.sample_rate); + blip_set_rates(right, 44100, snd.sample_rate); } void cdd_reset(void) @@ -92,7 +123,7 @@ void cdd_reset(void) /* reset cycle counter */ cdd.cycles = 0; - /* reset disc read latency */ + /* reset drive access latency */ cdd.latency = 0; /* reset track index */ @@ -102,103 +133,512 @@ void cdd_reset(void) cdd.lba = 0; /* reset status */ - cdd.status = cdd.loaded ? CD_READY : NO_DISC; + cdd.status = cdd.loaded ? CD_STOP : NO_DISC; + + /* reset CD-DA fader (full volume) */ + cdd.volume = 0x400; + + /* clear CD-DA output */ + cdd.audio[0] = cdd.audio[1] = 0; } -void cdd_load(char *filename, int type_bin) +int cdd_context_save(uint8 *state) { - /* unload any disc first */ - cdd_unload(); + int bufferptr = 0; - /* CD image file format */ - if (type_bin) + 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.scanOffset, sizeof(cdd.scanOffset)); + save_param(&cdd.volume, sizeof(cdd.volume)); + save_param(&cdd.status, sizeof(cdd.status)); + + return bufferptr; +} + +int cdd_context_load(uint8 *state) +{ + int lba; + int bufferptr = 0; + + 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.scanOffset, sizeof(cdd.scanOffset)); + load_param(&cdd.volume, sizeof(cdd.volume)); + load_param(&cdd.status, sizeof(cdd.status)); + + /* adjust current LBA within track limit */ + lba = cdd.lba; + if (lba < cdd.toc.tracks[cdd.index].start) { - /* BIN format (2352 bytes block data) */ - cdd.sectorSize = 2352; - } - else - { - /* ISO format (2048 bytes block data) */ - cdd.sectorSize = 2048; + lba = cdd.toc.tracks[cdd.index].start; } - /* load DATA track */ - cdd.toc.tracks[0].fd = fopen(filename,"rb"); - if (cdd.toc.tracks[0].fd) + /* seek to current track position */ + if (!cdd.index) { - /* DATA track start (logical block 0) */ - cdd.toc.tracks[0].start = 0; - - /* DATA track length */ - fseek(cdd.toc.tracks[0].fd, 0, SEEK_END); - cdd.toc.tracks[0].end = ftell(cdd.toc.tracks[0].fd) / cdd.sectorSize; - fseek(cdd.toc.tracks[0].fd, 0, SEEK_SET); - - /* initialize TOC */ - cdd.toc.end = cdd.toc.tracks[0].end; - cdd.toc.last = 1; - - /* TODO: add audio track support from BIN/CUE, ISO/WAV, MP3, OGG ? */ - - /* Simulated audio tracks if none found */ - if (cdd.toc.last == 1) + /* DATA track */ + if (cdd.toc.tracks[0].fd) { - /* Some games require specific TOC infos */ - if (strstr(rominfo.product,"T-95035") != NULL) + fseek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET); + } + } + else if (cdd.toc.tracks[cdd.index].fd) + { + /* AUDIO track */ + fseek(cdd.toc.tracks[cdd.index].fd, (lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); + } + + return bufferptr; +} + +int cdd_load(char *filename, char *header) +{ + char fname[256]; + char line[128]; + char *ptr = 0; + char *lptr; + FILE *fd; + + /* first unmount any loaded disc */ + cdd_unload(); + + /* open file */ + fd = fopen(filename, "rb"); + + /* save a copy of base filename */ + if (strlen(filename) < 256) + { + strncpy(fname, filename, strlen(filename)); + fname[strlen(filename)] = 0; + } + + /* autodetect .cue file */ + if (!memcmp(".cue", &filename[strlen(filename) - 4], 4) || !memcmp(".CUE", &filename[strlen(filename) - 4], 4)) + { + if (fd) + { + /* find first FILE command */ + lptr = strstr(line, "FILE"); + while (!lptr) { - /* Snatcher */ - cdd.toc.last = cdd.toc.end = 0; - do + if (fgets(line, 128, fd) == NULL) { - cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; - cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + toc_snatcher[cdd.toc.last]; - cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; - cdd.toc.last++; + break; } - while (cdd.toc.last < 21); + lptr = strstr(line, "FILE"); } - else if (strstr(rominfo.product,"T-45074") != NULL) + + /* get BINARY file name */ + if (lptr && strstr(line, " BINARY")) { - /* Lunar - Eternal Blue (J) */ - cdd.toc.tracks[1].start = cdd.toc.end + 2*75; - cdd.toc.tracks[1].end = cdd.toc.tracks[1].start + 21654; - cdd.toc.end = cdd.toc.tracks[1].end; - cdd.toc.tracks[2].start = cdd.toc.end + 2*75; - cdd.toc.tracks[2].end = cdd.toc.tracks[2].start + 5004; - cdd.toc.end = cdd.toc.tracks[2].end; - cdd.toc.tracks[3].start = cdd.toc.end + 2*75; - cdd.toc.tracks[3].end = cdd.toc.tracks[3].start + 684; - cdd.toc.end = cdd.toc.tracks[3].end; - cdd.toc.last = 4; - } - else if (strstr(rominfo.product,"T-127045") != NULL) - { - /* Lunar - Eternal Blue (U) */ - cdd.toc.tracks[1].start = cdd.toc.end + 2*75; - cdd.toc.tracks[1].end = cdd.toc.tracks[1].start + 21735; - cdd.toc.end = cdd.toc.tracks[1].end; - cdd.toc.tracks[2].start = cdd.toc.end + 2*75; - cdd.toc.tracks[2].end = cdd.toc.tracks[2].start + 27131; - cdd.toc.end = cdd.toc.tracks[2].end; - cdd.toc.last = 3; + /* skip "FILE" attribute */ + lptr += 4; + + /* skip DOUBLE QUOTE or SPACE characters */ + while ((*lptr == 0x20) || (*lptr == '\"')) lptr++; + + /* set pointer at the end of filepath */ + ptr = fname + strlen(fname) - 1; + while ((ptr - fname) && (*ptr != '/') && (*ptr != '\\')) ptr--; + if (ptr - fname) ptr++; + + /* append filename characters after filepath */ + while ((*lptr != '\"') && memcmp(lptr, " BINARY", 7)) + { + *ptr++ = *lptr++; + } + *ptr = 0; + + /* open file & initialize DATA track file descriptor */ + cdd.toc.tracks[0].fd = fopen(fname, "rb"); } else { - /* default TOC (99 x 2s) */ - do - { - cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end + 2*75; - cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + 2*75; - cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; - cdd.toc.last++; - } - while ((cdd.toc.last < 99) && (cdd.toc.end < 56*60*75)); + /* close .cue file */ + fclose(fd); + + /* invalid .cue file */ + return -1; + } + } + } + else + { + /* initialize DATA track file descriptor */ + cdd.toc.tracks[0].fd = fd; + + /* automatically try to open associated .cue file */ + strncpy(&fname[strlen(fname) - 4], ".cue", 4); + fd = fopen(fname, "rb"); + } + + if (!cdd.toc.tracks[0].fd) + { + /* close any opened .cue file */ + if (fd) fclose(fd); + + /* error opening file */ + return -1; + } + + /* read first 16 bytes */ + fread(header, 0x10, 1, cdd.toc.tracks[0].fd); + + /* look for valid CD image ID string */ + if (memcmp("SEGADISCSYSTEM", header, 14)) + { + /* if not found, read next 16 bytes */ + fread(header, 0x10, 1, cdd.toc.tracks[0].fd); + + /* look again for valid CD image ID string */ + if (memcmp("SEGADISCSYSTEM", header, 14)) + { + /* close any opened .cue file */ + if (fd) fclose(fd); + + /* close binary file */ + fclose(cdd.toc.tracks[0].fd); + + /* not a CD image file */ + return 0; + } + + /* BIN format (2352 bytes data blocks) */ + cdd.sectorSize = 2352; + } + else + { + /* ISO format (2048 bytes data blocks) */ + cdd.sectorSize = 2048; + } + + /* read CD image header + security code */ + fread(header + 0x10, 0x200, 1, cdd.toc.tracks[0].fd); + + /* DATA track length (default) */ + fseek(cdd.toc.tracks[0].fd, 0, SEEK_END); + cdd.toc.tracks[0].end = ftell(cdd.toc.tracks[0].fd) / cdd.sectorSize; + + /* DATA track start (logical block 0) */ + fseek(cdd.toc.tracks[0].fd, 0, SEEK_SET); + cdd.toc.tracks[0].start = 0; + + /* initialize TOC */ + cdd.toc.end = cdd.toc.tracks[0].end; + cdd.toc.last = 1; + + /* automatically retrieve audio tracks infos from .cue file */ + if (fd) + { + int pregap = 0; + int mm, ss, bb; + + /* skip first (DATA) track */ + while (!strstr(line, "INDEX 01") && !strstr(line, "INDEX 1")) + { + if (fgets(line, 128, fd) == NULL) + { + break; } } - /* CD loaded */ - cdd.loaded = 1; + /* read next lines until end of file */ + while (fgets(line, 128, fd) != NULL) + { + /* skip any SPACE characters */ + lptr = line; + while (*lptr == 0x20) lptr++; + + /* decode FILE commands */ + if (!(memcmp(lptr, "FILE", 4))) + { + /* check supported file types */ + if (!strstr(lptr," BINARY") && !strstr(lptr," WAVE")) + { + /* unsupported file type */ + break; + } + + /* skip "FILE" attribute */ + lptr += 4; + + /* skip DOUBLE QUOTE or SPACE characters */ + while ((*lptr == 0x20) || (*lptr == '\"')) lptr++; + + /* set pointer at the end of filepath */ + ptr = fname + strlen(fname) - 1; + while ((ptr - fname) && (*ptr != '/') && (*ptr != '\\')) ptr--; + if (ptr - fname) ptr++; + + /* append filename characters after filepath */ + while ((*lptr != '\"') && memcmp(lptr, " BINARY", 7) && memcmp(lptr, " WAVE", 5)) + { + *ptr++ = *lptr++; + } + *ptr = 0; + + /* open file & initialize AUDIO track file descriptor */ + cdd.toc.tracks[cdd.toc.last].fd = fopen(fname, "rb"); + if (!cdd.toc.tracks[cdd.toc.last].fd) + { + /* error opening file */ + break; + } + + /* reset current file PREGAP length */ + pregap = 0; + } + + /* decode TRACK commands */ + else if ((sscanf(lptr, "TRACK %02d AUDIO", &bb)) || (sscanf(lptr, "TRACK %d AUDIO", &bb))) + { + /* check track number */ + if (bb != (cdd.toc.last + 1)) + { + /* close any opened file */ + if (cdd.toc.tracks[cdd.toc.last].fd) + { + fclose(cdd.toc.tracks[cdd.toc.last].fd); + } + + /* missing tracks */ + break; + } + + /* check if a single file is used for all tracks */ + if (!cdd.toc.tracks[cdd.toc.last].fd) + { + /* clear previous track end index */ + cdd.toc.tracks[cdd.toc.last - 1].end = 0; + } + } + + /* decode PREGAP commands */ + else if (sscanf(lptr, "PREGAP %02d:%02d:%02d", &mm, &ss, &bb) == 3) + { + /* increment current file PREGAP length */ + pregap += bb + ss*75 + mm*60*75; + } + + /* decode INDEX commands */ + else if ((sscanf(lptr, "INDEX 00 %02d:%02d:%02d", &mm, &ss, &bb) == 3) || + (sscanf(lptr, "INDEX 0 %02d:%02d:%02d", &mm, &ss, &bb) == 3)) + { + /* check if a single file is used for all tracks */ + if (!cdd.toc.tracks[cdd.toc.last].fd) + { + /* set previous track end index */ + cdd.toc.tracks[cdd.toc.last - 1].end = bb + ss*75 + mm*60*75 + pregap; + } + } + else if ((sscanf(lptr, "INDEX 01 %02d:%02d:%02d", &mm, &ss, &bb) == 3) || + (sscanf(lptr, "INDEX 1 %02d:%02d:%02d", &mm, &ss, &bb) == 3)) + { + char id[4]; + + /* set file read offset for current track with current file PREGAP length */ + cdd.toc.tracks[cdd.toc.last].offset = pregap * 2352; + + /* check if a single file is used for all tracks */ + if (!cdd.toc.tracks[cdd.toc.last].fd) + { + /* previous track end index already set (through INDEX00 command) ? */ + if (cdd.toc.tracks[cdd.toc.last - 1].end == 0) + { + /* if not, set previous track end index */ + cdd.toc.tracks[cdd.toc.last - 1].end = bb + ss*75 + mm*60*75; + } + + /* current track start index */ + cdd.toc.tracks[cdd.toc.last].start = bb + ss*75 + mm*60*75 + pregap; + + /* use common file descriptor */ + cdd.toc.tracks[cdd.toc.last].fd = cdd.toc.tracks[0].fd; + } + else + { + /* adjust file read offset with previous track end index */ + cdd.toc.tracks[cdd.toc.last].offset += cdd.toc.end * 2352; + + /* current track start index */ + cdd.toc.tracks[cdd.toc.last].start = (cdd.toc.tracks[cdd.toc.last].offset / 2352) + bb + ss*75 + mm*60*75; + + /* current track end index */ + fseek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_END); + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + ((ftell(cdd.toc.tracks[cdd.toc.last].fd) + 2351) / 2352); + + /* update TOC end */ + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + } + + /* autodetect WAVE header (note: sometime found in BIN files as well) */ + fseek(cdd.toc.tracks[cdd.toc.last].fd, (cdd.toc.tracks[cdd.toc.last].start * 2352) - cdd.toc.tracks[cdd.toc.last].offset, SEEK_SET); + fread(id, 4, 1, cdd.toc.tracks[cdd.toc.last].fd); + if (!memcmp(id,"RIFF",4)) + { + /* adjust file read offset for current track with WAVE header length */ + cdd.toc.tracks[cdd.toc.last].offset -= (ftell(cdd.toc.tracks[cdd.toc.last].fd) + 40); + } + fseek(cdd.toc.tracks[cdd.toc.last].fd, 0, SEEK_SET); + + /* increment track number */ + cdd.toc.last++; + } + } + + /* check if a single file is used for all tracks */ + if (cdd.toc.tracks[cdd.toc.last - 1].fd == cdd.toc.tracks[0].fd) + { + /* adjust TOC end */ + cdd.toc.end += pregap; + + /* last track end index */ + cdd.toc.tracks[cdd.toc.last - 1].end = cdd.toc.end; + } + + /* close .cue file */ + fclose(fd); } + + /* ISO+WAV audio tracks auto-detection */ + else if (cdd.sectorSize == 2048) + { + int i, offset; + + /* set pointer at the end of filename */ + ptr = fname + strlen(fname) - 4; + + /* auto-detect track file extensions */ + for (i=0; i<10; i++) + { + /* auto-detect bad rips with wrong track indexes */ + sprintf(ptr, extensions[i], 1); + fd = fopen(fname, "rb"); + if (fd) + { + offset = 0; + break; + } + + sprintf(ptr, extensions[i], 2); + fd = fopen(fname, "rb"); + if (fd) + { + offset = 1; + break; + } + } + + /* repeat until no more valid track files can be found */ + while (fd) + { + unsigned char head[32]; + + /* make sure this is a valid WAVE file (16-bit stereo @44.1kHz only) */ + fseek(fd, 8, SEEK_SET); + fread(head, 32, 1, fd); + if (!memcmp(head, waveHeader, 32)) + { + /* initialize current track file descriptor */ + cdd.toc.tracks[cdd.toc.last].fd = fd; + + /* initialize current track start index with previous track end index */ + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; + + /* add default 2s PAUSE */ + cdd.toc.tracks[cdd.toc.last].start += 150; + + /* current track end index */ + fseek(fd, 0, SEEK_END); + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + ((ftell(fd) + 2351) / 2352); + + /* initialize file read offset for current track */ + cdd.toc.tracks[cdd.toc.last].offset = cdd.toc.tracks[cdd.toc.last].start * 2352; + + /* auto-detect PAUSE within audio files */ + fseek(fd, 100 * 2352, SEEK_SET); + fread(head, 4, 1, fd); + if (*(int32 *)head == 0) + { + /* assume 2s PAUSE is included at the beginning of the file */ + cdd.toc.tracks[cdd.toc.last].offset -= 150 * 2352; + cdd.toc.tracks[cdd.toc.last].end -= 150; + } + fseek(fd, 0, SEEK_SET); + + /* update TOC end */ + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + + /* adjust file read offset for current track with WAVE header length */ + cdd.toc.tracks[cdd.toc.last].offset -= 44; + + /* increment track number */ + cdd.toc.last++; + } + else + { + /* invalid file type */ + fclose(fd); + break; + } + + /* try to open next audio track file */ + sprintf(ptr, extensions[i], cdd.toc.last + offset); + fd = fopen(fname, "rb"); + } + } + + /* Simulate audio tracks if none found */ + if (cdd.toc.last == 1) + { + /* Some games require specific TOC infos */ + if (strstr(header + 0x180,"T-95035") != NULL) + { + /* Snatcher */ + cdd.toc.last = cdd.toc.end = 0; + do + { + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + toc_snatcher[cdd.toc.last]; + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + cdd.toc.last++; + } + while (cdd.toc.last < 21); + } + else if (strstr(header + 0x180,"T-127015") != NULL) + { + /* Lunar - The Silver Star */ + cdd.toc.last = cdd.toc.end = 0; + do + { + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + toc_lunar[cdd.toc.last]; + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + cdd.toc.last++; + } + while (cdd.toc.last < 52); + } + else + { + /* default TOC (99 tracks & 2s per audio tracks) */ + do + { + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end + 2*75; + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + 2*75; + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + cdd.toc.last++; + } + while ((cdd.toc.last < 99) && (cdd.toc.end < 56*60*75)); + } + } + + /* CD loaded */ + cdd.loaded = 1; + return 1; } void cdd_unload(void) @@ -213,6 +653,13 @@ void cdd_unload(void) if (cdd.toc.tracks[i].fd) { fclose(cdd.toc.tracks[i].fd); + + /* detect single file images */ + if (cdd.toc.tracks[i+1].fd == cdd.toc.tracks[i].fd) + { + /* exit loop */ + i = cdd.toc.last; + } } } @@ -227,10 +674,10 @@ void cdd_unload(void) cdd.sectorSize = 0; } -void cdd_read(uint8 *dst) +void cdd_read_data(uint8 *dst) { - /* start reading from sector 0 */ - if (cdd.lba >= 0) + /* only read DATA track sectors */ + if ((cdd.lba >= 0) && (cdd.lba < cdd.toc.tracks[0].end)) { /* BIN format ? */ if (cdd.sectorSize == 2352) @@ -239,20 +686,112 @@ void cdd_read(uint8 *dst) fseek(cdd.toc.tracks[0].fd, cdd.lba * 2352 + 16, SEEK_SET); } - /* read sector data (MODE 1) */ + /* read sector data (Mode 1 = 2048 bytes) */ fread(dst, 2048, 1, cdd.toc.tracks[0].fd); } } +void cdd_read_audio(unsigned int samples) +{ + /* previous audio outputs */ + int16 l = cdd.audio[0]; + int16 r = cdd.audio[1]; + + /* get number of internal clocks (samples) needed */ + samples = blip_clocks_needed(blip[0], samples); + + /* audio track playing ? */ + if (!scd.regs[0x36>>1].byte.h && cdd.toc.tracks[cdd.index].fd) + { + int i, mul, delta; + + /* current CD-DA fader volume */ + int curVol = cdd.volume; + + /* CD-DA fader volume setup (0-1024) */ + int endVol = scd.regs[0x34>>1].w >> 4; + + /* use CDD buffer as temporary buffer */ +#ifdef LSB_FIRST + int16 *ptr = (int16 *) (cdc.ram); +#else + uint8 *ptr = cdc.ram; +#endif + + /* read samples from current block */ + fread(cdc.ram, 1, samples * 4, cdd.toc.tracks[cdd.index].fd); + + /* process 16-bit (little-endian) stereo samples */ + for (i=0; i endVol) curVol--; + + /* audio is muted */ + if (!endVol) break; + } + + /* save current CD-DA fader volume */ + cdd.volume = curVol; + + /* save last audio output for next frame */ + cdd.audio[0] = l; + cdd.audio[1] = r; + } + else + { + /* no audio output */ + if (l) blip_add_delta_fast(blip[0], 0, -l); + if (r) blip_add_delta_fast(blip[1], 0, -r); + + /* save audio output for next frame */ + cdd.audio[0] = 0; + cdd.audio[1] = 0; + } + + /* end of Blip Buffer timeframe */ + blip_end_frame(blip[0], samples); + blip_end_frame(blip[1], samples); +} + + void cdd_update(void) { #ifdef LOG_CDD error("LBA = %d (track n°%d)\n", cdd.lba, cdd.index); #endif - /* reading track */ + + /* reading disc */ if (cdd.status == CD_PLAY) { - /* read latency */ + /* drive access latency */ if (cdd.latency > 0) { cdd.latency--; @@ -260,27 +799,9 @@ void cdd_update(void) } /* track type */ - if (cdd.index > 0) + if (!cdd.index) { - if (cdd.index < cdd.toc.last) - { - /* audio track sector sent to CD Fader/DAC should also be sent to CDD */ - cdc_decoder_update(0); - - /* next sector is automatically read */ - cdd.lba++; - - /* check end of current track */ - if (cdd.lba >= cdd.toc.tracks[cdd.index].end) - { - /* next track */ - cdd.index++; - } - } - } - else - { - /* sector header (CD-ROM Mode 1) */ + /* DATA sector header (CD-ROM Mode 1) */ uint8 header[4]; uint32 msf = cdd.lba + 150; header[0] = lut_BCD_8[(msf / 75) / 60]; @@ -291,12 +812,54 @@ void cdd_update(void) /* data track sector read is controlled by CDC */ cdd.lba += cdc_decoder_update(*(uint32 *)(header)); } + else if (cdd.index < cdd.toc.last) + { + /* check against audio track start index */ + if (cdd.lba >= cdd.toc.tracks[cdd.index].start) + { + /* audio track playing */ + scd.regs[0x36>>1].byte.h = 0x00; + } + + /* audio blocks are still sent to CDC as well as CD DAC/Fader */ + cdc_decoder_update(0); + + /* next audio block is automatically read */ + cdd.lba++; + } + else + { + return; + } + + /* check end of current track */ + if (cdd.lba >= cdd.toc.tracks[cdd.index].end) + { + /* play next track */ + cdd.index++; + + /* PAUSE between tracks */ + scd.regs[0x36>>1].byte.h = 0x01; + + /* seek to current block */ + if (cdd.toc.tracks[cdd.index].fd) + { + if (cdd.lba < cdd.toc.tracks[cdd.index].start) + { + fseek(cdd.toc.tracks[cdd.index].fd, (cdd.toc.tracks[cdd.index].start * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); + } + else + { + fseek(cdd.toc.tracks[cdd.index].fd, (cdd.lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); + } + } + } } - /* fast scanning disc */ + /* scanning disc */ else if (cdd.status == CD_SCAN) { - /* skip track */ + /* fast-forward or fast-rewind */ cdd.lba += cdd.scanOffset; /* check current track limits */ @@ -304,11 +867,17 @@ void cdd_update(void) { /* next track */ cdd.index++; + + /* skip directly to track start position */ + cdd.lba = cdd.toc.tracks[cdd.index].start; } else if (cdd.lba < cdd.toc.tracks[cdd.index].start) { /* previous track */ cdd.index--; + + /* skip directly to track end position */ + cdd.lba = cdd.toc.tracks[cdd.index].end; } /* check disc limits */ @@ -322,6 +891,23 @@ void cdd_update(void) cdd.index = cdd.toc.last; cdd.lba = cdd.toc.end; } + + /* seek to current block */ + if (!cdd.index) + { + fseek(cdd.toc.tracks[0].fd, cdd.lba * cdd.sectorSize, SEEK_SET); + } + else if (cdd.toc.tracks[cdd.index].fd) + { + if (cdd.lba < cdd.toc.tracks[cdd.index].start) + { + fseek(cdd.toc.tracks[cdd.index].fd, (cdd.toc.tracks[cdd.index].start * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); + } + else + { + fseek(cdd.toc.tracks[cdd.index].fd, (cdd.lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); + } + } } } @@ -339,6 +925,10 @@ void cdd_process(void) case 0x01: /* Stop Drive */ { cdd.status = cdd.loaded ? CD_STOP : NO_DISC; + + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; + scd.regs[0x38>>1].w = 0x0000; scd.regs[0x3a>>1].w = 0x0000; scd.regs[0x3c>>1].w = 0x0000; @@ -349,6 +939,9 @@ void cdd_process(void) case 0x02: /* Read TOC */ { + /* Infos automatically retrieved by CDD processor from Q-Channel */ + /* commands 0x00-0x02: from current block Q-Channel data field */ + /* commands 0x03-0x05: from Lead-In area Q-Channel data field */ switch (scd.regs[0x44>>1].byte.l) { case 0x00: /* Absolute position (MM:SS:FF) */ @@ -423,8 +1016,8 @@ void cdd_process(void) default: { -#ifdef LOG_CDD - error("Unknown Command !!!\n"); +#ifdef LOG_ERROR + error("Unknown CDD Command %02X (%X)\n", scd.regs[0x44>>1].byte.l, s68k.pc); #endif return; } @@ -442,38 +1035,57 @@ void cdd_process(void) (scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l)) * 75 + (scd.regs[0x48>>1].byte.h * 10 + scd.regs[0x48>>1].byte.l) - 150; - /* disc access latency */ + /* CD drive access time */ if (!cdd.latency) { /* Fixes a few games hanging during intro because they expect data to be read with some delay */ - /* Radical Rex needs at least one interrupt delay */ - /* Wolf Team games (Anet Futatabi, Cobra Command, Road Avenger & Time Gal need at least 6 interrupts delay */ - /* Jeopardy needs at least 9 interrupts delay */ + /* Wolf Team games (Anet Futatabi, Cobra Command, Road Avenger & Time Gal) need at least 6 interrupts delay */ + /* Radical Rex need at least one interrupt delay */ + /* Jeopardy need at least 9 interrupts delay (without counting seek time delay below )*/ cdd.latency = 9; } - /* update current LBA */ - cdd.lba = lba; - /* update current track index */ while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++; cdd.index = index; - /* track type */ - if (index) - { - /* AUDIO track */ - scd.regs[0x36>>1].byte.h = 0x00; - } - else - { - /* DATA track */ - scd.regs[0x36>>1].byte.h = 0x01; + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; - /* seek to current block */ - if (lba < 0) lba = 0; - fseek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET); + /* seek to current block */ + if (!index) + { + /* seek DATA track */ + if (lba < 0) + { + fseek(cdd.toc.tracks[0].fd, 0, SEEK_SET); + } + else + { + fseek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET); + } } + else if (cdd.toc.tracks[index].fd) + { + /* CD drive seek time */ + /* Some delay is also needed when playing AUDIO tracks located at the end of the disc (ex: Sonic CD intro) */ + /* max. seek time = 1.5s = 1.5 x 75 = 112.5 CDD interrupts (rounded to 120) for 270000 sectors max on disc */ + /* Note: this is only a rough approximation, on real hardware, drive seek time is much likely not linear */ + cdd.latency += ((abs(lba - cdd.lba) * 120) / 270000); + + /* seek AUDIO track */ + if (lba < cdd.toc.tracks[index].start) + { + fseek(cdd.toc.tracks[index].fd, (cdd.toc.tracks[index].start * 2352) - cdd.toc.tracks[index].offset, SEEK_SET); + } + else + { + fseek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET); + } + } + + /* update current LBA */ + cdd.lba = lba; /* update status */ cdd.status = CD_PLAY; @@ -502,13 +1114,19 @@ void cdd_process(void) while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++; cdd.index = index; - /* DATA track */ + /* seek to current block */ if (!index) { - /* seek to current block */ + /* DATA track */ if (lba < 0) lba = 0; fseek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET); } + else if (cdd.toc.tracks[index].fd) + { + /* AUDIO track */ + if (lba < cdd.toc.tracks[index].start) lba = cdd.toc.tracks[index].start; + fseek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET); + } /* no audio track playing */ scd.regs[0x36>>1].byte.h = 0x01; @@ -540,18 +1158,6 @@ void cdd_process(void) case 0x07: /* Resume */ { - /* track type */ - if (cdd.index) - { - /* AUDIO track */ - scd.regs[0x36>>1].byte.h = 0x00; - } - else - { - /* DATA track */ - scd.regs[0x36>>1].byte.h = 0x01; - } - /* update status */ cdd.status = CD_PLAY; scd.regs[0x38>>1].w = (CD_PLAY << 8) | 0x02; @@ -564,7 +1170,7 @@ void cdd_process(void) case 0x08: /* Forward Scan */ { - cdd.scanOffset = 10; + cdd.scanOffset = CD_SCAN_SPEED; cdd.status = CD_SCAN; scd.regs[0x38>>1].w = (CD_SCAN << 8) | 0x02; scd.regs[0x3a>>1].w = lut_BCD_16[cdd.index+1]; @@ -576,7 +1182,7 @@ void cdd_process(void) case 0x09: /* Rewind Scan */ { - cdd.scanOffset = -10; + cdd.scanOffset = -CD_SCAN_SPEED; cdd.status = CD_SCAN; scd.regs[0x38>>1].w = (CD_SCAN << 8) | 0x02; scd.regs[0x3a>>1].w = lut_BCD_16[cdd.index+1]; @@ -589,6 +1195,9 @@ void cdd_process(void) case 0x0a: /* ??? (usually sent before first & last CD_PLAY command) */ { + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; + /* RS0-RS8 values taken from Gens */ cdd.status = CD_READY; scd.regs[0x38>>1].w = CD_READY << 8; @@ -601,6 +1210,9 @@ void cdd_process(void) case 0x0c: /* Close Tray */ { + /* no audio track playing */ + scd.regs[0x36>>1].byte.h = 0x01; + cdd.status = cdd.loaded ? CD_STOP : NO_DISC; scd.regs[0x38>>1].w = 0x0000; scd.regs[0x3a>>1].w = 0x0000; diff --git a/source/cd_hw/cdd.h b/source/cd_hw/cdd.h index 294dcfa..ed4d883 100644 --- a/source/cd_hw/cdd.h +++ b/source/cd_hw/cdd.h @@ -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); diff --git a/source/cd_hw/gfx.c b/source/cd_hw/gfx.c index cd63169..d1f0341 100644 --- a/source/cd_hw/gfx.c +++ b/source/cd_hw/gfx.c @@ -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; diff --git a/source/cd_hw/gfx.h b/source/cd_hw/gfx.h index 3269a74..6241974 100644 --- a/source/cd_hw/gfx.h +++ b/source/cd_hw/gfx.h @@ -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); diff --git a/source/cd_hw/pcm.c b/source/cd_hw/pcm.c index fe9cd95..caa7f00 100644 --- a/source/cd_hw/pcm.c +++ b/source/cd_hw/pcm.c @@ -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); } diff --git a/source/cd_hw/pcm.h b/source/cd_hw/pcm.h index 8f7ddd2..19641c1 100644 --- a/source/cd_hw/pcm.h +++ b/source/cd_hw/pcm.h @@ -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); diff --git a/source/cd_hw/scd.c b/source/cd_hw/scd.c index 6d3152c..bc75284 100644 --- a/source/cd_hw/scd.c +++ b/source/cd_hw/scd.c @@ -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> 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; } diff --git a/source/cd_hw/scd.h b/source/cd_hw/scd.h index ef3f7a5..1e1bc11 100644 --- a/source/cd_hw/scd.h +++ b/source/cd_hw/scd.h @@ -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); diff --git a/source/genesis.c b/source/genesis.c index 0b88097..e8fd34c 100644 --- a/source/genesis.c +++ b/source/genesis.c @@ -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 */ diff --git a/source/genesis.h b/source/genesis.h index 1c6dc51..45b5467 100644 --- a/source/genesis.h +++ b/source/genesis.h @@ -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); diff --git a/source/gx/config.c b/source/gx/config.c index 898fe47..2f9a4fa 100644 --- a/source/gx/config.c +++ b/source/gx/config.c @@ -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 */ diff --git a/source/gx/config.h b/source/gx/config.h index 41623b3..39e0dac 100644 --- a/source/gx/config.h +++ b/source/gx/config.h @@ -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; diff --git a/source/gx/fileio/file_load.c b/source/gx/fileio/file_load.c index 6cf0442..d836c21 100644 --- a/source/gx/fileio/file_load.c +++ b/source/gx/fileio/file_load.c @@ -51,6 +51,8 @@ #include #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; } diff --git a/source/gx/fileio/file_load.h b/source/gx/fileio/file_load.h index 53eea35..8bdf541 100644 --- a/source/gx/fileio/file_load.h +++ b/source/gx/fileio/file_load.h @@ -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 diff --git a/source/gx/fileio/file_slot.c b/source/gx/fileio/file_slot.c index 9710117..45d117f 100644 --- a/source/gx/fileio/file_slot.c +++ b/source/gx/fileio/file_slot.c @@ -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; } diff --git a/source/gx/fileio/fileio.c b/source/gx/fileio/fileio.c index 9cc4f56..736c6d1 100644 --- a/source/gx/fileio/fileio.c +++ b/source/gx/fileio/fileio.c @@ -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; } diff --git a/source/gx/gui/cheats.c b/source/gx/gui/cheats.c index 638ec4c..829044c 100644 --- a/source/gx/gui/cheats.c +++ b/source/gx/gui/cheats.c @@ -38,6 +38,7 @@ ****************************************************************************************/ #include "shared.h" +#include "file_load.h" #include "cheats.h" #include "font.h" #include "gui.h" diff --git a/source/gx/gui/filesel.c b/source/gx/gui/filesel.c index a2528e4..f95aa5d 100644 --- a/source/gx/gui/filesel.c +++ b/source/gx/gui/filesel.c @@ -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 */ diff --git a/source/gx/gui/menu.c b/source/gx/gui/menu.c index 011b61d..a41db02 100644 --- a/source/gx/gui/menu.c +++ b/source/gx/gui/menu.c @@ -44,7 +44,6 @@ #include "cheats.h" #include "file_load.h" #include "file_slot.h" -#include "md_eeprom.h" #ifdef HW_RVL #include @@ -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 */ diff --git a/source/gx/gx_audio.c b/source/gx/gx_audio.c index b682a8d..dbd340e 100644 --- a/source/gx/gx_audio.c +++ b/source/gx/gx_audio.c @@ -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; } /*** diff --git a/source/gx/gx_audio.h b/source/gx/gx_audio.h index 85e6d5d..1de6a9e 100644 --- a/source/gx/gx_audio.h +++ b/source/gx/gx_audio.h @@ -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 diff --git a/source/gx/gx_input.c b/source/gx/gx_input.c index 69992d0..99d5752 100644 --- a/source/gx/gx_input.c +++ b/source/gx/gx_input.c @@ -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; diff --git a/source/gx/gx_video.c b/source/gx/gx_video.c index d73720c..e7a6ee5 100644 --- a/source/gx/gx_video.c +++ b/source/gx/gx_video.c @@ -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 */ diff --git a/source/gx/gx_video.h b/source/gx/gx_video.h index 9f4a5fc..7261155 100644 --- a/source/gx/gx_video.h +++ b/source/gx/gx_video.h @@ -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 diff --git a/source/gx/main.c b/source/gx/main.c index d9283cb..2e2b9cc 100644 --- a/source/gx/main.c +++ b/source/gx/main.c @@ -49,16 +49,11 @@ #include -/* 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; } } diff --git a/source/gx/osd.h b/source/gx/osd.h index 8f5d6e9..fc4d3d3 100644 --- a/source/gx/osd.h +++ b/source/gx/osd.h @@ -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; diff --git a/source/io_ctrl.c b/source/io_ctrl.c index 2824059..a8642a5 100644 --- a/source/io_ctrl.c +++ b/source/io_ctrl.c @@ -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 */ diff --git a/source/loadrom.c b/source/loadrom.c index 0ec1862..95a2f86 100644 --- a/source/loadrom.c +++ b/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); diff --git a/source/loadrom.h b/source/loadrom.h index 40f00b5..6afe9cc 100644 --- a/source/loadrom.h +++ b/source/loadrom.h @@ -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_ */ diff --git a/source/mem68k.c b/source/mem68k.c index 686ca40..5e0f370 100644 --- a/source/mem68k.c +++ b/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; } diff --git a/source/membnk.c b/source/membnk.c index 136ea42..64f16d8 100644 --- a/source/membnk.c +++ b/source/membnk.c @@ -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); diff --git a/source/memz80.c b/source/memz80.c index c1742af..3a5d9b2 100644 --- a/source/memz80.c +++ b/source/memz80.c @@ -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; } diff --git a/source/sound/Fir_Resampler.c b/source/sound/Fir_Resampler.c deleted file mode 100644 index 7a5f1dc..0000000 --- a/source/sound/Fir_Resampler.c +++ /dev/null @@ -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 -#include -#include -#include - -#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; -} diff --git a/source/sound/Fir_Resampler.h b/source/sound/Fir_Resampler.h deleted file mode 100644 index adcde75..0000000 --- a/source/sound/Fir_Resampler.h +++ /dev/null @@ -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 diff --git a/source/sound/blip.c b/source/sound/blip.c deleted file mode 100644 index 378b33d..0000000 --- a/source/sound/blip.c +++ /dev/null @@ -1,139 +0,0 @@ -/* http://www.slack.net/~ant/ */ - -#include "blip.h" - -#include -#include -#include - -/* 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; -} diff --git a/source/sound/blip.h b/source/sound/blip.h deleted file mode 100644 index 7b09342..0000000 --- a/source/sound/blip.h +++ /dev/null @@ -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 diff --git a/source/sound/blip_buf.c b/source/sound/blip_buf.c new file mode 100644 index 0000000..7068af3 --- /dev/null +++ b/source/sound/blip_buf.c @@ -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 +#endif +#include +#include +#include + +/* 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; +} diff --git a/source/sound/blip_buf.h b/source/sound/blip_buf.h new file mode 100644 index 0000000..21c45d0 --- /dev/null +++ b/source/sound/blip_buf.h @@ -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 diff --git a/source/sound/sn76489.c b/source/sound/sn76489.c index cb57d39..b507ac6 100644 --- a/source/sound/sn76489.c +++ b/source/sound/sn76489.c @@ -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); -} \ No newline at end of file + 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; + } + } +} diff --git a/source/sound/sn76489.h b/source/sound/sn76489.h index 7dfe110..3c59611 100644 --- a/source/sound/sn76489.h +++ b/source/sound/sn76489.h @@ -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_ */ \ No newline at end of file diff --git a/source/sound/sound.c b/source/sound/sound.c index 6743c1f..d36d349 100644 --- a/source/sound/sound.c +++ b/source/sound/sound.c @@ -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); -} diff --git a/source/sound/sound.h b/source/sound/sound.h index ce15313..69e7a3d 100644 --- a/source/sound/sound.h +++ b/source/sound/sound.h @@ -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_ */ diff --git a/source/sound/ym2413.c b/source/sound/ym2413.c index e533d31..b71fbdf 100644 --- a/source/sound/ym2413.c +++ b/source/sound/ym2413.c @@ -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(); -} diff --git a/source/sound/ym2413.h b/source/sound/ym2413.h index 746d190..73c8c84 100644 --- a/source/sound/ym2413.h +++ b/source/sound/ym2413.h @@ -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_*/ diff --git a/source/sound/ym2612.c b/source/sound/ym2612.c index 663f066..e4533fd 100644 --- a/source/sound/ym2612.c +++ b/source/sound/ym2612.c @@ -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<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<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<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<= 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)); diff --git a/source/sound/ym2612.h b/source/sound/ym2612.h index f26c78d..2cad8bf 100644 --- a/source/sound/ym2612.h +++ b/source/sound/ym2612.h @@ -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); diff --git a/source/state.c b/source/state.c index 6a831f5..7ee3fb9 100644 --- a/source/state.c +++ b/source/state.c @@ -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]); } diff --git a/source/state.h b/source/state.h index 7a9384b..63ebd3e 100644 --- a/source/state.h +++ b/source/state.h @@ -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); \ diff --git a/source/system.c b/source/system.c index 87c8967..e291630 100644 --- a/source/system.c +++ b/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; diff --git a/source/system.h b/source/system.h index 726e11f..08c5881 100644 --- a/source/system.h +++ b/source/system.h @@ -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); diff --git a/source/vdp_ctrl.c b/source/vdp_ctrl.c index dbe0665..05a2737 100644 --- a/source/vdp_ctrl.c +++ b/source/vdp_ctrl.c @@ -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; diff --git a/source/vdp_ctrl.h b/source/vdp_ctrl.h index fcfdbb6..e23d10a 100644 --- a/source/vdp_ctrl.h +++ b/source/vdp_ctrl.h @@ -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); diff --git a/source/vdp_render.c b/source/vdp_render.c index 18ed184..bbc637b 100644 --- a/source/vdp_render.c +++ b/source/vdp_render.c @@ -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)) diff --git a/source/win/config.c b/source/win/config.c index 0146a7a..4e09c3d 100644 --- a/source/win/config.c +++ b/source/win/config.c @@ -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 */ diff --git a/source/win/config.h b/source/win/config.h index a993bc3..22ed43c 100644 --- a/source/win/config.h +++ b/source/win/config.h @@ -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]; diff --git a/source/win/main.c b/source/win/main.c index b1c09d2..3c452b4 100644 --- a/source/win/main.c +++ b/source/win/main.c @@ -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");