mirror of
https://github.com/libretro/beetle-pce-libretro.git
synced 2024-11-26 18:00:48 +00:00
initial beetle pce port
This commit is contained in:
parent
433fb7a36f
commit
3ef04cd75c
4
Makefile
4
Makefile
@ -44,14 +44,14 @@ NEED_BLIP = 1
|
||||
NEED_CD = 1
|
||||
NEED_CRC32 = 1
|
||||
WANT_NEW_API = 1
|
||||
CORE_DEFINE := -DWANT_PCE_FAST_EMU -DWANT_STEREO_SOUND
|
||||
CORE_DEFINE := -DWANT_PCE_EMU -DWANT_STEREO_SOUND
|
||||
HAVE_CHD = 1
|
||||
|
||||
prefix := /usr
|
||||
libdir := $(prefix)/lib
|
||||
|
||||
LIBRETRO_INSTALL_DIR := libretro
|
||||
TARGET_NAME := mednafen_pce_fast
|
||||
TARGET_NAME := mednafen_pce
|
||||
|
||||
# GIT HASH
|
||||
GIT_VERSION := " $(shell git rev-parse --short HEAD || echo unknown)"
|
||||
|
103
Makefile.common
103
Makefile.common
@ -4,17 +4,17 @@ SOURCES_C :=
|
||||
DEPS_DIR := $(CORE_DIR)/deps
|
||||
LIBRETRO_COMM_DIR := $(CORE_DIR)/libretro-common
|
||||
MEDNAFEN_DIR := $(CORE_DIR)/mednafen
|
||||
CORE_EMU_DIR := $(MEDNAFEN_DIR)/pce_fast
|
||||
CORE_EMU_DIR := $(MEDNAFEN_DIR)/pce
|
||||
CDROM_DIR := $(MEDNAFEN_DIR)/cdrom
|
||||
|
||||
INCFLAGS := -I$(CORE_DIR) \
|
||||
-I$(MEDNAFEN_DIR) \
|
||||
-I$(MEDNAFEN_DIR)/include \
|
||||
-I$(MEDNAFEN_DIR)/hw_sound \
|
||||
-I$(MEDNAFEN_DIR)/hw_cpu \
|
||||
-I$(MEDNAFEN_DIR)/hw_misc \
|
||||
-I$(LIBRETRO_COMM_DIR)/include \
|
||||
-I$(DEPS_DIR)/zlib
|
||||
-I$(MEDNAFEN_DIR) \
|
||||
-I$(MEDNAFEN_DIR)/include \
|
||||
-I$(MEDNAFEN_DIR)/hw_sound \
|
||||
-I$(MEDNAFEN_DIR)/hw_cpu \
|
||||
-I$(MEDNAFEN_DIR)/hw_misc \
|
||||
-I$(LIBRETRO_COMM_DIR)/include \
|
||||
-I$(DEPS_DIR)/zlib
|
||||
|
||||
ifneq (,$(findstring msvc2003,$(platform)))
|
||||
INCFLAGS += -I$(LIBRETRO_COMM_DIR)/include/compat/msvc
|
||||
@ -22,13 +22,21 @@ endif
|
||||
|
||||
ifneq ($(HAVE_GRIFFIN),1)
|
||||
SOURCES_CXX += \
|
||||
$(CORE_EMU_DIR)/huc6280.cpp \
|
||||
$(CORE_EMU_DIR)/input.cpp \
|
||||
$(CORE_EMU_DIR)/pcecd.cpp \
|
||||
$(CORE_EMU_DIR)/pcecd_drive.cpp \
|
||||
$(CORE_EMU_DIR)/psg.cpp \
|
||||
$(CORE_EMU_DIR)/vdc.cpp \
|
||||
$(MEDNAFEN_DIR)/hw_misc/arcade_card/arcade_card.cpp
|
||||
$(CORE_DIR)/libretro.cpp \
|
||||
$(CORE_EMU_DIR)/huc6280.cpp \
|
||||
$(CORE_EMU_DIR)/huc.cpp \
|
||||
$(CORE_EMU_DIR)/input.cpp \
|
||||
$(CORE_EMU_DIR)/mcgenjin.cpp \
|
||||
$(CORE_EMU_DIR)/pce.cpp \
|
||||
$(CORE_EMU_DIR)/pcecd.cpp \
|
||||
$(CORE_EMU_DIR)/tsushin.cpp \
|
||||
$(CORE_EMU_DIR)/vce.cpp \
|
||||
$(CORE_EMU_DIR)/input/gamepad.cpp \
|
||||
$(CORE_EMU_DIR)/input/mouse.cpp \
|
||||
$(CORE_EMU_DIR)/input/tsushinkb.cpp \
|
||||
$(MEDNAFEN_DIR)/hw_misc/arcade_card/arcade_card.cpp \
|
||||
$(MEDNAFEN_DIR)/hw_sound/pce_psg/pce_psg.cpp \
|
||||
$(MEDNAFEN_DIR)/hw_video/huc6270/vdc.cpp
|
||||
endif
|
||||
|
||||
ifeq ($(NEED_BLIP), 1)
|
||||
@ -76,7 +84,8 @@ FLAGS += -D__LIBRETRO_CACHE_CD__
|
||||
endif
|
||||
|
||||
ifneq ($(HAVE_GRIFFIN),1)
|
||||
SOURCES_CXX += $(CDROM_DIR)/CDAccess.cpp \
|
||||
SOURCES_CXX += \
|
||||
$(CDROM_DIR)/CDAccess.cpp \
|
||||
$(CDROM_DIR)/CDAccess_Image.cpp \
|
||||
$(CDROM_DIR)/CDAccess_CCD.cpp \
|
||||
$(CDROM_DIR)/CDAFReader.cpp \
|
||||
@ -87,7 +96,8 @@ SOURCES_CXX += $(CDROM_DIR)/CDAccess.cpp \
|
||||
$(CDROM_DIR)/galois.cpp \
|
||||
$(CDROM_DIR)/recover-raw.cpp \
|
||||
$(CDROM_DIR)/l-ec.cpp \
|
||||
$(CDROM_DIR)/edc_crc32.cpp
|
||||
$(CDROM_DIR)/edc_crc32.cpp \
|
||||
$(CDROM_DIR)/scsicd.cpp
|
||||
endif
|
||||
FLAGS += -DNEED_CD
|
||||
|
||||
@ -100,25 +110,26 @@ ifeq ($(HAVE_CHD), 1)
|
||||
endif
|
||||
|
||||
ifneq ($(STATIC_LINKING), 1)
|
||||
SOURCES_C += $(DEPS_DIR)/zlib/adler32.c \
|
||||
$(DEPS_DIR)/zlib/compress.c \
|
||||
$(DEPS_DIR)/zlib/crc32.c \
|
||||
$(DEPS_DIR)/zlib/deflate.c \
|
||||
$(DEPS_DIR)/zlib/gzclose.c \
|
||||
$(DEPS_DIR)/zlib/gzlib.c \
|
||||
$(DEPS_DIR)/zlib/gzread.c \
|
||||
$(DEPS_DIR)/zlib/gzwrite.c \
|
||||
$(DEPS_DIR)/zlib/inffast.c \
|
||||
$(DEPS_DIR)/zlib/inflate.c \
|
||||
$(DEPS_DIR)/zlib/inftrees.c \
|
||||
$(DEPS_DIR)/zlib/trees.c \
|
||||
$(DEPS_DIR)/zlib/uncompr.c \
|
||||
$(DEPS_DIR)/zlib/zutil.c
|
||||
SOURCES_C += \
|
||||
$(DEPS_DIR)/zlib/adler32.c \
|
||||
$(DEPS_DIR)/zlib/compress.c \
|
||||
$(DEPS_DIR)/zlib/crc32.c \
|
||||
$(DEPS_DIR)/zlib/deflate.c \
|
||||
$(DEPS_DIR)/zlib/gzclose.c \
|
||||
$(DEPS_DIR)/zlib/gzlib.c \
|
||||
$(DEPS_DIR)/zlib/gzread.c \
|
||||
$(DEPS_DIR)/zlib/gzwrite.c \
|
||||
$(DEPS_DIR)/zlib/inffast.c \
|
||||
$(DEPS_DIR)/zlib/inflate.c \
|
||||
$(DEPS_DIR)/zlib/inftrees.c \
|
||||
$(DEPS_DIR)/zlib/trees.c \
|
||||
$(DEPS_DIR)/zlib/uncompr.c \
|
||||
$(DEPS_DIR)/zlib/zutil.c
|
||||
endif
|
||||
|
||||
ifeq ($(WINDOWS_VERSION), 1)
|
||||
SOURCES_C += \
|
||||
$(DEPS_DIR)/flac-1.3.2/src/libFLAC/windows_unicode_filenames.c
|
||||
$(DEPS_DIR)/flac-1.3.2/src/libFLAC/windows_unicode_filenames.c
|
||||
endif
|
||||
|
||||
SOURCES_C += \
|
||||
@ -186,23 +197,25 @@ SOURCES_CXX += \
|
||||
$(MEDNAFEN_DIR)/state.cpp \
|
||||
$(MEDNAFEN_DIR)/mempatcher.cpp \
|
||||
$(MEDNAFEN_DIR)/okiadpcm.cpp \
|
||||
$(CORE_DIR)/libretro.cpp
|
||||
$(MEDNAFEN_DIR)/sound/OwlResampler.cpp
|
||||
|
||||
SOURCES_C += \
|
||||
$(MEDNAFEN_DIR)/file.c \
|
||||
$(MEDNAFEN_DIR)/mednafen-endian.c
|
||||
$(MEDNAFEN_DIR)/mednafen-endian.c \
|
||||
$(MEDNAFEN_DIR)/cputest/cputest.c
|
||||
endif
|
||||
|
||||
ifneq ($(STATIC_LINKING), 1)
|
||||
SOURCES_C += $(LIBRETRO_COMM_DIR)/streams/file_stream.c \
|
||||
$(LIBRETRO_COMM_DIR)/streams/file_stream_transforms.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/compat_snprintf.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \
|
||||
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
|
||||
$(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.c \
|
||||
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \
|
||||
$(LIBRETRO_COMM_DIR)/string/stdstring.c
|
||||
SOURCES_C += \
|
||||
$(LIBRETRO_COMM_DIR)/streams/file_stream.c \
|
||||
$(LIBRETRO_COMM_DIR)/streams/file_stream_transforms.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/compat_snprintf.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \
|
||||
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
|
||||
$(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.c \
|
||||
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \
|
||||
$(LIBRETRO_COMM_DIR)/string/stdstring.c
|
||||
endif
|
||||
|
@ -7,7 +7,7 @@ image: Visual Studio 2017
|
||||
environment:
|
||||
makefile_location: "."
|
||||
makefile_name: Makefile
|
||||
target_name: mednafen_pce_fast
|
||||
target_name: mednafen_pce
|
||||
|
||||
configuration:
|
||||
- release
|
||||
@ -30,4 +30,4 @@ artifacts:
|
||||
- path: '**\%target_name%*.dll'
|
||||
- path: '**\%target_name%*.lib'
|
||||
- path: '**\%target_name%*.pdb'
|
||||
- path: '**\libretro.h'
|
||||
- path: '**\libretro.h'
|
||||
|
2460
libretro.cpp
2460
libretro.cpp
File diff suppressed because it is too large
Load Diff
@ -197,6 +197,9 @@ static void ChangePhase(const unsigned int new_phase);
|
||||
|
||||
static void FixOPV(void)
|
||||
{
|
||||
if(!cdda.CDDADivAccVolFudge)
|
||||
cdda.CDDADivAccVolFudge = 100;
|
||||
|
||||
for(int port = 0; port < 2; port++)
|
||||
{
|
||||
int32_t tmpvol = cdda.CDDAVolume[port] * 100 / (2 * cdda.CDDADivAccVolFudge);
|
||||
@ -3217,7 +3220,7 @@ int SCSICD_StateAction(StateMem* sm, const unsigned load, const bool data_only,
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, sname);
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, sname, false);
|
||||
|
||||
if(load)
|
||||
{
|
||||
|
86
mednafen/cputest/cputest.c
Normal file
86
mednafen/cputest/cputest.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg 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.
|
||||
*
|
||||
* FFmpeg 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 FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "cputest.h"
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
static int flags, checked = 0;
|
||||
|
||||
|
||||
void cputest_force_flags(int arg)
|
||||
{
|
||||
flags = arg;
|
||||
checked = 1;
|
||||
}
|
||||
|
||||
int cputest_get_flags(void)
|
||||
{
|
||||
if (checked)
|
||||
return flags;
|
||||
|
||||
// if (ARCH_ARM) flags = ff_get_cpu_flags_arm();
|
||||
#if ARCH_POWERPC
|
||||
flags = ff_get_cpu_flags_ppc();
|
||||
#endif
|
||||
|
||||
#if ARCH_X86
|
||||
flags = ff_get_cpu_flags_x86();
|
||||
#endif
|
||||
|
||||
checked = 1;
|
||||
return flags;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//#ifdef TEST
|
||||
|
||||
#undef printf
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int cpu_flags = av_get_cpu_flags();
|
||||
|
||||
printf("cpu_flags = 0x%08X\n", cpu_flags);
|
||||
printf("cpu_flags = %s%s%s%s%s%s%s%s%s%s%s%s%s\n",
|
||||
#if ARCH_ARM
|
||||
cpu_flags & CPUTEST_FLAG_IWMMXT ? "IWMMXT " : "",
|
||||
#elif ARCH_POWERPC
|
||||
cpu_flags & CPUTEST_FLAG_ALTIVEC ? "ALTIVEC " : "",
|
||||
#elif ARCH_X86
|
||||
cpu_flags & CPUTEST_FLAG_MMX ? "MMX " : "",
|
||||
cpu_flags & CPUTEST_FLAG_MMX2 ? "MMX2 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE ? "SSE " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE2 ? "SSE2 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE2SLOW ? "SSE2(slow) " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE3 ? "SSE3 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE3SLOW ? "SSE3(slow) " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSSE3 ? "SSSE3 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_ATOM ? "Atom " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE4 ? "SSE4.1 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_SSE42 ? "SSE4.2 " : "",
|
||||
cpu_flags & CPUTEST_FLAG_AVX ? "AVX " : "",
|
||||
cpu_flags & CPUTEST_FLAG_3DNOW ? "3DNow " : "",
|
||||
cpu_flags & CPUTEST_FLAG_3DNOWEXT ? "3DNowExt " : "");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
73
mednafen/cputest/cputest.h
Normal file
73
mednafen/cputest/cputest.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
|
||||
*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg 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.
|
||||
*
|
||||
* FFmpeg 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 FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef AVUTIL_CPU_H
|
||||
#define AVUTIL_CPU_H
|
||||
|
||||
#define CPUTEST_FLAG_FORCE 0x80000000 /* force usage of selected flags (OR) */
|
||||
|
||||
/* lower 16 bits - CPU features */
|
||||
#define CPUTEST_FLAG_MMX 0x0001 ///< standard MMX
|
||||
#define CPUTEST_FLAG_MMX2 0x0002 ///< SSE integer functions or AMD MMX ext
|
||||
#define CPUTEST_FLAG_3DNOW 0x0004 ///< AMD 3DNOW
|
||||
#define CPUTEST_FLAG_SSE 0x0008 ///< SSE functions
|
||||
#define CPUTEST_FLAG_SSE2 0x0010 ///< PIV SSE2 functions
|
||||
#define CPUTEST_FLAG_SSE2SLOW 0x40000000 ///< SSE2 supported, but usually not faster
|
||||
#define CPUTEST_FLAG_3DNOWEXT 0x0020 ///< AMD 3DNowExt
|
||||
#define CPUTEST_FLAG_SSE3 0x0040 ///< Prescott SSE3 functions
|
||||
#define CPUTEST_FLAG_SSE3SLOW 0x20000000 ///< SSE3 supported, but usually not faster
|
||||
#define CPUTEST_FLAG_SSSE3 0x0080 ///< Conroe SSSE3 functions
|
||||
#define CPUTEST_FLAG_ATOM 0x10000000 ///< Atom processor, some SSSE3 instructions are slower
|
||||
#define CPUTEST_FLAG_SSE4 0x0100 ///< Penryn SSE4.1 functions
|
||||
#define CPUTEST_FLAG_SSE42 0x0200 ///< Nehalem SSE4.2 functions
|
||||
#define CPUTEST_FLAG_AVX 0x4000 ///< AVX functions: requires OS support even if YMM registers aren't used
|
||||
|
||||
#define CPUTEST_FLAG_CMOV 0x8000 // CMOVcc support (Mednafen addition)
|
||||
|
||||
//#define CPUTEST_FLAG_IWMMXT 0x0100 ///< XScale IWMMXT
|
||||
#define CPUTEST_FLAG_ALTIVEC 0x0001 ///< standard
|
||||
|
||||
/**
|
||||
* Return the flags which specify extensions supported by the CPU.
|
||||
*/
|
||||
int cputest_get_flags(void);
|
||||
|
||||
|
||||
/**
|
||||
* Disables cpu detection and forces the specified flags.
|
||||
*/
|
||||
void cputest_force_flags(int flags);
|
||||
|
||||
|
||||
/* The following CPU-specific functions shall not be called directly. */
|
||||
int ff_get_cpu_flags_arm(void);
|
||||
int ff_get_cpu_flags_ppc(void);
|
||||
int ff_get_cpu_flags_x86(void);
|
||||
|
||||
#endif /* AVUTIL_CPU_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} //extern "C"
|
||||
#endif
|
||||
|
859
mednafen/hw_sound/pce_psg/pce_psg.cpp
Normal file
859
mednafen/hw_sound/pce_psg/pce_psg.cpp
Normal file
@ -0,0 +1,859 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* Original skeleton write handler and PSG structure definition:
|
||||
* Copyright (C) 2001 Charles MacDonald
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <mednafen/mednafen.h>
|
||||
#include "pce_psg.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
// Frequency cache cutoff optimization threshold (<= FREQC7M_COT)
|
||||
#define FREQC7M_COT 0x7 //0xA
|
||||
|
||||
void PCE_PSG::SetVolume(double new_volume)
|
||||
{
|
||||
for(int vl = 0; vl < 32; vl++)
|
||||
{
|
||||
double flub = 1.0 * new_volume * 8 / 6;
|
||||
|
||||
if(vl)
|
||||
flub /= pow(2, (double)1 / 4 * vl); // ~1.5dB reduction per increment of vl
|
||||
|
||||
if(vl == 0x1F)
|
||||
flub = 0;
|
||||
|
||||
for(int samp = 0; samp < 32; samp++)
|
||||
{
|
||||
int eff_samp;
|
||||
|
||||
if(revision == REVISION_HUC6280)
|
||||
eff_samp = samp * 2;
|
||||
else
|
||||
eff_samp = samp * 2 - 0x1F;
|
||||
|
||||
dbtable[vl][samp] = (int32)(flub * eff_samp * 128); // * 256);
|
||||
dbtable_volonly[vl] = (int32)(flub * 65536);
|
||||
|
||||
// dbtable[vl][samp] = (int32)(flub * eff_samp * 128);
|
||||
// dbtable_volonly[vl] = (int32)(flub * 65536);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Changing the 0x1F(not that there should be) would require changing the channel pseudo-off volume check logic later on.
|
||||
static const int scale_tab[] =
|
||||
{
|
||||
0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
|
||||
0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F
|
||||
};
|
||||
|
||||
#define CLOCK_LFSR(lfsr) { unsigned int newbit = ((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 11) ^ (lfsr >> 12) ^ (lfsr >> 17)) & 1; lfsr = (lfsr >> 1) | (newbit << 17); }
|
||||
|
||||
static const int16 Phase_Filter[2][7] =
|
||||
{
|
||||
/* 0 */ { 35, 250, 579, 641, 425, 112, 6 }, // 2048
|
||||
/* 1 */ { 6, 112, 425, 641, 579, 250, 35 }, // 2048
|
||||
};
|
||||
|
||||
void INLINE PCE_PSG::UpdateOutputSub(const int32 timestamp, psg_channel *ch, const int32 samp0, const int32 samp1)
|
||||
{
|
||||
int32 delta[2];
|
||||
|
||||
delta[0] = samp0 - ch->blip_prev_samp[0];
|
||||
delta[1] = samp1 - ch->blip_prev_samp[1];
|
||||
|
||||
const int16* c = Phase_Filter[(timestamp >> 1) & 1];
|
||||
const int32 l = (timestamp >> 2) & 0xFFFF;
|
||||
|
||||
HRBufs[0][l + 0] += delta[0] * c[0];
|
||||
HRBufs[0][l + 1] += delta[0] * c[1];
|
||||
HRBufs[0][l + 2] += delta[0] * c[2];
|
||||
HRBufs[0][l + 3] += delta[0] * c[3];
|
||||
HRBufs[0][l + 4] += delta[0] * c[4];
|
||||
HRBufs[0][l + 5] += delta[0] * c[5];
|
||||
HRBufs[0][l + 6] += delta[0] * c[6];
|
||||
|
||||
HRBufs[1][l + 0] += delta[1] * c[0];
|
||||
HRBufs[1][l + 1] += delta[1] * c[1];
|
||||
HRBufs[1][l + 2] += delta[1] * c[2];
|
||||
HRBufs[1][l + 3] += delta[1] * c[3];
|
||||
HRBufs[1][l + 4] += delta[1] * c[4];
|
||||
HRBufs[1][l + 5] += delta[1] * c[5];
|
||||
HRBufs[1][l + 6] += delta[1] * c[6];
|
||||
|
||||
ch->blip_prev_samp[0] = samp0;
|
||||
ch->blip_prev_samp[1] = samp1;
|
||||
}
|
||||
|
||||
void PCE_PSG::UpdateOutput_Norm(const int32 timestamp, psg_channel *ch)
|
||||
{
|
||||
int sv = ch->dda;
|
||||
|
||||
UpdateOutputSub(timestamp, ch, dbtable[ch->vl[0]][sv],
|
||||
dbtable[ch->vl[1]][sv]);
|
||||
}
|
||||
|
||||
void PCE_PSG::UpdateOutput_Noise(const int32 timestamp, psg_channel *ch)
|
||||
{
|
||||
int sv = ((ch->lfsr & 1) << 5) - (ch->lfsr & 1); //(ch->lfsr & 0x1) ? 0x1F : 0;
|
||||
|
||||
UpdateOutputSub(timestamp, ch, dbtable[ch->vl[0]][sv],
|
||||
dbtable[ch->vl[1]][sv]);
|
||||
}
|
||||
|
||||
void PCE_PSG::UpdateOutput_Off(const int32 timestamp, psg_channel *ch)
|
||||
{
|
||||
UpdateOutputSub(timestamp, ch, 0, 0);
|
||||
}
|
||||
|
||||
void PCE_PSG::UpdateOutput_Accum_HuC6280A(const int32 timestamp, psg_channel *ch)
|
||||
{
|
||||
int32 samp[2];
|
||||
|
||||
// 31(5-bit max) * 32 samples = 992
|
||||
// 992 / 2 = 496
|
||||
//
|
||||
// 8 + 5 = 13
|
||||
// 13 - 12 = 1
|
||||
|
||||
samp[0] = ((int32)dbtable_volonly[ch->vl[0]] * ((int32)ch->samp_accum - 496)) >> (8 + 5);
|
||||
samp[1] = ((int32)dbtable_volonly[ch->vl[1]] * ((int32)ch->samp_accum - 496)) >> (8 + 5);
|
||||
|
||||
UpdateOutputSub(timestamp, ch, samp[0], samp[1]);
|
||||
}
|
||||
|
||||
void PCE_PSG::UpdateOutput_Accum_HuC6280(const int32 timestamp, psg_channel *ch)
|
||||
{
|
||||
int32 samp[2];
|
||||
|
||||
samp[0] = ((int32)dbtable_volonly[ch->vl[0]] * (int32)ch->samp_accum) >> (8 + 5);
|
||||
samp[1] = ((int32)dbtable_volonly[ch->vl[1]] * (int32)ch->samp_accum) >> (8 + 5);
|
||||
|
||||
UpdateOutputSub(timestamp, ch, samp[0], samp[1]);
|
||||
}
|
||||
|
||||
|
||||
// This function should always be called after RecalcFreqCache() (it's not called from RecalcFreqCache to avoid redundant code)
|
||||
void PCE_PSG::RecalcUOFunc(int chnum)
|
||||
{
|
||||
psg_channel *ch = &channel[chnum];
|
||||
|
||||
//printf("UO Update: %d, %02x\n", chnum, ch->control);
|
||||
|
||||
if((revision != REVISION_HUC6280 && !(ch->control & 0xC0)) || (revision == REVISION_HUC6280 && !(ch->control & 0x80)))
|
||||
ch->UpdateOutput = &PCE_PSG::UpdateOutput_Off;
|
||||
else if(ch->noisectrl & ch->control & 0x80)
|
||||
ch->UpdateOutput = &PCE_PSG::UpdateOutput_Noise;
|
||||
// If the control for the channel is in waveform play mode, and the (real) playback frequency is too high, and the channel is either not the LFO modulator channel or
|
||||
// if the LFO trigger bit(which halts the LFO modulator channel's waveform incrementing when set) is clear
|
||||
else if((ch->control & 0xC0) == 0x80 && ch->freq_cache <= FREQC7M_COT && (chnum != 1 || !(lfoctrl & 0x80)) )
|
||||
ch->UpdateOutput = UpdateOutput_Accum;
|
||||
else
|
||||
ch->UpdateOutput = &PCE_PSG::UpdateOutput_Norm;
|
||||
}
|
||||
|
||||
|
||||
void PCE_PSG::RecalcFreqCache(int chnum)
|
||||
{
|
||||
psg_channel *ch = &channel[chnum];
|
||||
|
||||
if(chnum == 0 && (lfoctrl & 0x03))
|
||||
{
|
||||
const uint32 shift = (((lfoctrl & 0x3) - 1) << 1);
|
||||
uint8 la = channel[1].dda;
|
||||
uint32 tmp_freq = (ch->frequency + ((uint32)(la - 0x10) << shift)) & 0xFFF;
|
||||
|
||||
ch->freq_cache = (tmp_freq ? tmp_freq : 4096) << 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch->freq_cache = (ch->frequency ? ch->frequency : 4096) << 1;
|
||||
|
||||
if(chnum == 1 && (lfoctrl & 0x03))
|
||||
ch->freq_cache *= lfofreq ? lfofreq : 256;
|
||||
}
|
||||
}
|
||||
|
||||
void PCE_PSG::RecalcNoiseFreqCache(int chnum)
|
||||
{
|
||||
psg_channel *ch = &channel[chnum];
|
||||
int32 freq = 0x1F - (ch->noisectrl & 0x1F);
|
||||
|
||||
if(!freq)
|
||||
freq = 0x20;
|
||||
else
|
||||
freq <<= 6;
|
||||
|
||||
freq <<= 1;
|
||||
|
||||
ch->noise_freq_cache = freq;
|
||||
}
|
||||
|
||||
void PCE_PSG::PeekWave(const unsigned int ch, uint32 Address, uint32 Length, uint8 *Buffer)
|
||||
{
|
||||
assert(ch <= 5);
|
||||
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0x1F;
|
||||
*Buffer = channel[ch].waveform[Address];
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
|
||||
void PCE_PSG::PokeWave(const unsigned int ch, uint32 Address, uint32 Length, const uint8 *Buffer)
|
||||
{
|
||||
assert(ch <= 5);
|
||||
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0x1F;
|
||||
channel[ch].samp_accum -= channel[ch].waveform[Address];
|
||||
channel[ch].waveform[Address] = *Buffer & 0x1F;
|
||||
channel[ch].samp_accum += channel[ch].waveform[Address];
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 PCE_PSG::GetRegister(const unsigned int id, char *special, const uint32 special_len)
|
||||
{
|
||||
uint32 value = 0xDEADBEEF;
|
||||
const int ch = (id >> 8) & 0xF;
|
||||
|
||||
switch(id & 0xF0FF)
|
||||
{
|
||||
default: break;
|
||||
|
||||
case PSG_GSREG_SELECT:
|
||||
value = select;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_GBALANCE:
|
||||
value = globalbalance;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_LFOFREQ:
|
||||
value = lfofreq;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_LFOCTRL:
|
||||
value = lfoctrl;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_FREQ:
|
||||
value = channel[ch].frequency;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_CTRL:
|
||||
value = channel[ch].control;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_BALANCE:
|
||||
value = channel[ch].balance;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_WINDEX:
|
||||
value = channel[ch].waveform_index;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_SCACHE:
|
||||
value = channel[ch].dda;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_NCTRL:
|
||||
value = channel[ch].noisectrl;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_LFSR:
|
||||
value = channel[ch].lfsr & 0x3FFFF;
|
||||
break;
|
||||
}
|
||||
return(value);
|
||||
}
|
||||
|
||||
|
||||
void PCE_PSG::SetRegister(const unsigned int id, const uint32 value)
|
||||
{
|
||||
const int ch = (id >> 8) & 0xF;
|
||||
|
||||
switch(id & 0xF0FF)
|
||||
{
|
||||
default: break;
|
||||
|
||||
case PSG_GSREG_SELECT:
|
||||
select = value & 0x07;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_GBALANCE:
|
||||
globalbalance = value & 0xFF;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_LFOFREQ:
|
||||
lfofreq = value & 0xFF;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_LFOCTRL:
|
||||
lfoctrl = value & 0x83;
|
||||
RecalcFreqCache(0);
|
||||
RecalcUOFunc(0);
|
||||
RecalcFreqCache(1);
|
||||
RecalcUOFunc(1);
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_FREQ:
|
||||
channel[ch].frequency = value & 0xFFF;
|
||||
RecalcFreqCache(ch);
|
||||
RecalcUOFunc(ch);
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_CTRL:
|
||||
channel[ch].control = value & 0xFF;
|
||||
RecalcFreqCache(ch);
|
||||
RecalcUOFunc(ch);
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_BALANCE:
|
||||
channel[ch].balance = value & 0xFF;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_WINDEX:
|
||||
channel[ch].waveform_index = value & 0x1F;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_SCACHE:
|
||||
channel[ch].dda = value & 0x1F;
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_NCTRL:
|
||||
channel[ch].noisectrl = value & 0xFF;
|
||||
RecalcNoiseFreqCache(ch);
|
||||
RecalcUOFunc(ch);
|
||||
break;
|
||||
|
||||
case PSG_GSREG_CH0_LFSR:
|
||||
channel[ch].lfsr = value & 0x3FFFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PCE_PSG::PCE_PSG(int32* hr_l, int32* hr_r, int want_revision)
|
||||
{
|
||||
//printf("Test: %u, %u\n", sizeof(psg_channel), (uint8*)&channel[0].balance - (uint8*)&channel[0].waveform[0]);
|
||||
|
||||
revision = want_revision;
|
||||
switch(revision)
|
||||
{
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
|
||||
case REVISION_HUC6280:
|
||||
UpdateOutput_Accum = &PCE_PSG::UpdateOutput_Accum_HuC6280;
|
||||
break;
|
||||
|
||||
case REVISION_HUC6280A:
|
||||
UpdateOutput_Accum = &PCE_PSG::UpdateOutput_Accum_HuC6280A;
|
||||
break;
|
||||
}
|
||||
HRBufs[0] = hr_l;
|
||||
HRBufs[1] = hr_r;
|
||||
|
||||
lastts = 0;
|
||||
for(int ch = 0; ch < 6; ch++)
|
||||
{
|
||||
channel[ch].blip_prev_samp[0] = 0;
|
||||
channel[ch].blip_prev_samp[1] = 0;
|
||||
channel[ch].lastts = 0;
|
||||
}
|
||||
|
||||
SetVolume(1.0); // Will build dbtable in the process.
|
||||
Power(0);
|
||||
}
|
||||
|
||||
PCE_PSG::~PCE_PSG()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
int32 PCE_PSG::GetVL(const int chnum, const int lr)
|
||||
{
|
||||
psg_channel *ch = &channel[chnum];
|
||||
|
||||
const int gbal = 0x1F - scale_tab[(globalbalance >> (lr ? 0 : 4)) & 0xF];
|
||||
const int bal = 0x1F - scale_tab[(ch->balance >> (lr ? 0 : 4)) & 0xF];
|
||||
const int al = 0x1F - (ch->control & 0x1F);
|
||||
int vol_reduction;
|
||||
|
||||
vol_reduction = gbal + bal + al;
|
||||
|
||||
if(vol_reduction > 0x1F)
|
||||
vol_reduction = 0x1F;
|
||||
|
||||
return(vol_reduction);
|
||||
}
|
||||
|
||||
void PCE_PSG::Write(int32 timestamp, uint8 A, uint8 V)
|
||||
{
|
||||
A &= 0x0F;
|
||||
|
||||
if(A == 0x00)
|
||||
{
|
||||
select = (V & 0x07);
|
||||
return;
|
||||
}
|
||||
|
||||
Update(timestamp);
|
||||
|
||||
psg_channel *ch = &channel[select];
|
||||
|
||||
//if(A == 0x01 || select == 5)
|
||||
// printf("Write Ch: %d %04x %02x, %d\n", select, A, V, timestamp);
|
||||
|
||||
switch(A)
|
||||
{
|
||||
default: break;
|
||||
|
||||
case 0x01: /* Global sound balance */
|
||||
globalbalance = V;
|
||||
vol_pending = true;
|
||||
break;
|
||||
|
||||
case 0x02: /* Channel frequency (LSB) */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
|
||||
ch->frequency = (ch->frequency & 0x0F00) | V;
|
||||
RecalcFreqCache(select);
|
||||
RecalcUOFunc(select);
|
||||
break;
|
||||
|
||||
case 0x03: /* Channel frequency (MSB) */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
|
||||
ch->frequency = (ch->frequency & 0x00FF) | ((V & 0x0F) << 8);
|
||||
RecalcFreqCache(select);
|
||||
RecalcUOFunc(select);
|
||||
break;
|
||||
|
||||
case 0x04: /* Channel enable, DDA, volume */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
|
||||
if((ch->control & 0x40) && !(V & 0x40))
|
||||
{
|
||||
ch->waveform_index = 0;
|
||||
ch->dda = ch->waveform[ch->waveform_index];
|
||||
ch->counter = ch->freq_cache;
|
||||
}
|
||||
|
||||
if(!(ch->control & 0x80) && (V & 0x80))
|
||||
{
|
||||
if(!(V & 0x40))
|
||||
{
|
||||
ch->waveform_index = (ch->waveform_index + 1) & 0x1F;
|
||||
ch->dda = ch->waveform[ch->waveform_index];
|
||||
}
|
||||
}
|
||||
|
||||
ch->control = V;
|
||||
RecalcFreqCache(select);
|
||||
RecalcUOFunc(select);
|
||||
|
||||
vol_pending = true;
|
||||
break;
|
||||
|
||||
case 0x05: /* Channel balance */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
ch->balance = V;
|
||||
|
||||
vol_pending = true;
|
||||
break;
|
||||
|
||||
case 0x06: /* Channel waveform data */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
V &= 0x1F;
|
||||
|
||||
if(!(ch->control & 0x40))
|
||||
{
|
||||
ch->samp_accum -= ch->waveform[ch->waveform_index];
|
||||
ch->waveform[ch->waveform_index] = V;
|
||||
ch->samp_accum += ch->waveform[ch->waveform_index];
|
||||
}
|
||||
|
||||
if((ch->control & 0xC0) == 0x00)
|
||||
ch->waveform_index = ((ch->waveform_index + 1) & 0x1F);
|
||||
|
||||
if(ch->control & 0x80)
|
||||
{
|
||||
// According to my tests(on SuperGrafx), writing to this channel
|
||||
// will update the waveform value cache/latch regardless of DDA mode being enabled.
|
||||
ch->dda = V;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x07: /* Noise enable and frequency */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
if(select >= 4)
|
||||
{
|
||||
ch->noisectrl = V;
|
||||
RecalcNoiseFreqCache(select);
|
||||
RecalcUOFunc(select);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x08: /* LFO frequency */
|
||||
lfofreq = V & 0xFF;
|
||||
//printf("LFO Freq: %02x\n", V);
|
||||
break;
|
||||
|
||||
case 0x09: /* LFO trigger and control */
|
||||
//printf("LFO Ctrl: %02x\n", V);
|
||||
if(V & 0x80)
|
||||
{
|
||||
channel[1].waveform_index = 0;
|
||||
channel[1].dda = channel[1].waveform[channel[1].waveform_index];
|
||||
channel[1].counter = channel[1].freq_cache;
|
||||
}
|
||||
lfoctrl = V;
|
||||
RecalcFreqCache(0);
|
||||
RecalcUOFunc(0);
|
||||
RecalcFreqCache(1);
|
||||
RecalcUOFunc(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't use INLINE, which has always_inline in it, due to gcc's inability to cope with the type of recursion
|
||||
// used in this function.
|
||||
void PCE_PSG::RunChannel(int chc, int32 timestamp, const bool LFO_On)
|
||||
{
|
||||
psg_channel *ch = &channel[chc];
|
||||
int32 running_timestamp = ch->lastts;
|
||||
int32 run_time = timestamp - ch->lastts;
|
||||
|
||||
ch->lastts = timestamp;
|
||||
|
||||
if(!run_time)
|
||||
return;
|
||||
|
||||
(this->*ch->UpdateOutput)(running_timestamp, ch);
|
||||
|
||||
if(chc >= 4)
|
||||
{
|
||||
int32 freq = ch->noise_freq_cache;
|
||||
|
||||
ch->noisecount -= run_time;
|
||||
|
||||
if(&PCE_PSG::UpdateOutput_Noise == ch->UpdateOutput)
|
||||
while(ch->noisecount <= 0)
|
||||
{
|
||||
CLOCK_LFSR(ch->lfsr);
|
||||
UpdateOutput_Noise(timestamp + ch->noisecount, ch);
|
||||
ch->noisecount += freq;
|
||||
}
|
||||
else
|
||||
while(ch->noisecount <= 0)
|
||||
{
|
||||
CLOCK_LFSR(ch->lfsr);
|
||||
ch->noisecount += freq;
|
||||
}
|
||||
}
|
||||
|
||||
// D7 of control is 0, don't clock the counter at all.
|
||||
// D7 of lfocontrol is 1(and chc == 1), don't clock the counter at all(not sure about this)
|
||||
// In DDA mode, don't clock the counter.
|
||||
// (Noise being enabled isn't handled here since AFAIK it doesn't disable clocking of the waveform portion, its sound just overrides the sound from
|
||||
// the waveform portion when the noise enable bit is set, which is handled in our RecalcUOFunc).
|
||||
if(!(ch->control & 0x80) || (chc == 1 && (lfoctrl & 0x80)) || (ch->control & 0x40))
|
||||
return;
|
||||
|
||||
ch->counter -= run_time;
|
||||
|
||||
if(!LFO_On && ch->freq_cache <= FREQC7M_COT)
|
||||
{
|
||||
if(ch->counter <= 0)
|
||||
{
|
||||
const int32 inc_count = ((0 - ch->counter) / ch->freq_cache) + 1;
|
||||
|
||||
ch->counter += inc_count * ch->freq_cache;
|
||||
|
||||
ch->waveform_index = (ch->waveform_index + inc_count) & 0x1F;
|
||||
ch->dda = ch->waveform[ch->waveform_index];
|
||||
}
|
||||
}
|
||||
|
||||
while(ch->counter <= 0)
|
||||
{
|
||||
ch->waveform_index = (ch->waveform_index + 1) & 0x1F;
|
||||
ch->dda = ch->waveform[ch->waveform_index];
|
||||
|
||||
(this->*ch->UpdateOutput)(timestamp + ch->counter, ch);
|
||||
|
||||
if(LFO_On)
|
||||
{
|
||||
RunChannel(1, timestamp + ch->counter, false);
|
||||
RecalcFreqCache(0);
|
||||
RecalcUOFunc(0);
|
||||
|
||||
ch->counter += (ch->freq_cache <= FREQC7M_COT) ? FREQC7M_COT : ch->freq_cache; // Not particularly accurate, but faster.
|
||||
}
|
||||
else
|
||||
ch->counter += ch->freq_cache;
|
||||
}
|
||||
}
|
||||
|
||||
void PCE_PSG::UpdateSubLFO(int32 timestamp)
|
||||
{
|
||||
for(int chc = 0; chc < 6; chc++)
|
||||
RunChannel(chc, timestamp, chc == 0);
|
||||
}
|
||||
|
||||
void PCE_PSG::UpdateSubNonLFO(int32 timestamp)
|
||||
{
|
||||
for(int chc = 0; chc < 6; chc++)
|
||||
RunChannel(chc, timestamp, false);
|
||||
}
|
||||
|
||||
void PCE_PSG::Update(int32 timestamp)
|
||||
{
|
||||
int32 run_time = timestamp - lastts;
|
||||
|
||||
if(vol_pending && !vol_update_counter && !vol_update_which)
|
||||
{
|
||||
vol_update_counter = 1;
|
||||
vol_pending = false;
|
||||
}
|
||||
|
||||
bool lfo_on = (bool)(lfoctrl & 0x03);
|
||||
|
||||
if(lfo_on)
|
||||
{
|
||||
if(!(channel[1].control & 0x80) || (lfoctrl & 0x80))
|
||||
{
|
||||
lfo_on = 0;
|
||||
RecalcFreqCache(0);
|
||||
RecalcUOFunc(0);
|
||||
}
|
||||
}
|
||||
|
||||
int32 clocks = run_time;
|
||||
int32 running_timestamp = lastts;
|
||||
|
||||
while(clocks > 0)
|
||||
{
|
||||
int32 chunk_clocks = clocks;
|
||||
|
||||
if(vol_update_counter > 0 && chunk_clocks > vol_update_counter)
|
||||
chunk_clocks = vol_update_counter;
|
||||
|
||||
running_timestamp += chunk_clocks;
|
||||
clocks -= chunk_clocks;
|
||||
|
||||
if(lfo_on)
|
||||
UpdateSubLFO(running_timestamp);
|
||||
else
|
||||
UpdateSubNonLFO(running_timestamp);
|
||||
|
||||
if(vol_update_counter > 0)
|
||||
{
|
||||
vol_update_counter -= chunk_clocks;
|
||||
if(!vol_update_counter)
|
||||
{
|
||||
const int phase = vol_update_which & 1;
|
||||
const int lr = ((vol_update_which >> 1) & 1) ^ 1;
|
||||
const int chnum = vol_update_which >> 2;
|
||||
|
||||
if(!phase)
|
||||
{
|
||||
//printf("Volume update(Read, %d since last): ch=%d, lr=%d, ts=%d\n", running_timestamp - last_read, chnum, lr, running_timestamp);
|
||||
|
||||
if(chnum < 6)
|
||||
vol_update_vllatch = GetVL(chnum, lr);
|
||||
//last_read = running_timestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// printf("Volume update(Apply): ch=%d, lr=%d, ts=%d\n", chnum, lr, running_timestamp);
|
||||
if(chnum < 6)
|
||||
channel[chnum].vl[lr] = vol_update_vllatch;
|
||||
//last_apply = running_timestamp;
|
||||
}
|
||||
vol_update_which = (vol_update_which + 1) & 0x1F;
|
||||
|
||||
if(vol_update_which)
|
||||
vol_update_counter = phase ? 1 : 255;
|
||||
else if(vol_pending)
|
||||
{
|
||||
vol_update_counter = phase ? 1 : 255;
|
||||
vol_pending = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastts = running_timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
void PCE_PSG::ResetTS(int32 ts_base)
|
||||
{
|
||||
lastts = ts_base;
|
||||
|
||||
for(int chc = 0; chc < 6; chc++)
|
||||
channel[chc].lastts = ts_base;
|
||||
}
|
||||
|
||||
void PCE_PSG::Power(const int32 timestamp)
|
||||
{
|
||||
// Not sure about power-on values, these are mostly just intuitive guesses(with some laziness thrown in).
|
||||
if(timestamp != lastts)
|
||||
Update(timestamp);
|
||||
|
||||
// Don't memset channel to 0, there's stuff like lastts and blip_prev_samp that shouldn't be altered on Power().
|
||||
|
||||
select = 0;
|
||||
globalbalance = 0;
|
||||
lfofreq = 0;
|
||||
lfoctrl = 0;
|
||||
|
||||
for(int ch = 0; ch < 6; ch++)
|
||||
{
|
||||
channel[ch].frequency = 0;
|
||||
channel[ch].control = 0x00;
|
||||
channel[ch].balance = 0;
|
||||
memset(channel[ch].waveform, 0, 32);
|
||||
channel[ch].samp_accum = 0;
|
||||
|
||||
channel[ch].waveform_index = 0;
|
||||
channel[ch].dda = 0x00;
|
||||
channel[ch].noisectrl = 0x00;
|
||||
|
||||
channel[ch].vl[0] = 0x1F;
|
||||
channel[ch].vl[1] = 0x1F;
|
||||
|
||||
channel[ch].samp_accum = 0;
|
||||
|
||||
RecalcFreqCache(ch);
|
||||
RecalcUOFunc(ch);
|
||||
|
||||
channel[ch].counter = channel[ch].freq_cache;
|
||||
|
||||
if(ch >= 4)
|
||||
RecalcNoiseFreqCache(ch);
|
||||
channel[ch].noisecount = 1;
|
||||
channel[ch].lfsr = 1;
|
||||
}
|
||||
|
||||
vol_pending = false;
|
||||
vol_update_counter = 0;
|
||||
vol_update_which = 0;
|
||||
}
|
||||
|
||||
int PCE_PSG::StateAction(StateMem *sm, const unsigned load, const bool data_only)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
for(int ch = 0; ch < 6; ch++)
|
||||
{
|
||||
char tmpstr[5] = "SCHx";
|
||||
psg_channel *pt = &channel[ch];
|
||||
|
||||
SFORMAT CH_StateRegs[] =
|
||||
{
|
||||
SFVARN(pt->counter, "counter"),
|
||||
SFVARN(pt->frequency, "frequency"),
|
||||
SFVARN(pt->control, "control"),
|
||||
SFVARN(pt->balance, "balance"),
|
||||
SFARRAYN(pt->waveform, 32, "waveform"),
|
||||
SFVARN(pt->waveform_index, "waveform_index"),
|
||||
SFVARN(pt->dda, "dda"),
|
||||
SFVARN(pt->noisectrl, "noisectrl"),
|
||||
SFVARN(pt->noisecount, "noisecount"),
|
||||
SFVARN(pt->lfsr, "lfsr"),
|
||||
SFARRAY32N(pt->vl, 2, "vl"), // TODO
|
||||
SFEND
|
||||
};
|
||||
tmpstr[3] = '0' + ch;
|
||||
ret &= MDFNSS_StateAction(sm, load, data_only, CH_StateRegs, tmpstr, false);
|
||||
}
|
||||
|
||||
SFORMAT PSG_StateRegs[] =
|
||||
{
|
||||
SFVAR(select),
|
||||
SFVAR(globalbalance),
|
||||
SFVAR(lfofreq),
|
||||
SFVAR(lfoctrl),
|
||||
|
||||
SFVAR(vol_update_counter),
|
||||
SFVAR(vol_update_which),
|
||||
SFVAR(vol_update_vllatch),
|
||||
SFVAR(vol_pending),
|
||||
SFEND
|
||||
};
|
||||
|
||||
ret &= MDFNSS_StateAction(sm, load, data_only, PSG_StateRegs, "PSG", false);
|
||||
|
||||
if(load)
|
||||
{
|
||||
vol_update_which &= 0x1F;
|
||||
|
||||
if(!channel[4].lfsr)
|
||||
channel[4].lfsr = 1;
|
||||
|
||||
if(!channel[5].lfsr)
|
||||
channel[5].lfsr = 1;
|
||||
|
||||
for(int ch = 0; ch < 6; ch++)
|
||||
{
|
||||
channel[ch].waveform_index &= 0x1F;
|
||||
channel[ch].frequency &= 0xFFF;
|
||||
channel[ch].dda &= 0x1F;
|
||||
|
||||
channel[ch].samp_accum = 0;
|
||||
for(int wi = 0; wi < 32; wi++)
|
||||
{
|
||||
channel[ch].waveform[wi] &= 0x1F;
|
||||
channel[ch].samp_accum += channel[ch].waveform[wi];
|
||||
}
|
||||
|
||||
for(int lr = 0; lr < 2; lr++)
|
||||
channel[ch].vl[lr] &= 0x1F;
|
||||
|
||||
if(channel[ch].noisecount <= 0 && ch >= 4)
|
||||
{
|
||||
printf("ch=%d, noisecount <= 0\n", ch);
|
||||
channel[ch].noisecount = 1;
|
||||
}
|
||||
|
||||
if(channel[ch].counter <= 0)
|
||||
{
|
||||
printf("ch=%d, counter <= 0\n", ch);
|
||||
channel[ch].counter = 1;
|
||||
}
|
||||
|
||||
if(ch >= 4)
|
||||
RecalcNoiseFreqCache(ch);
|
||||
RecalcFreqCache(ch);
|
||||
RecalcUOFunc(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
191
mednafen/hw_sound/pce_psg/pce_psg.h
Normal file
191
mednafen/hw_sound/pce_psg/pce_psg.h
Normal file
@ -0,0 +1,191 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* Original skeleton write handler and PSG structure definition:
|
||||
* Copyright (C) 2001 Charles MacDonald
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _PCE_PSG_H
|
||||
#define _PCE_PSG_H
|
||||
|
||||
class PCE_PSG;
|
||||
|
||||
struct psg_channel
|
||||
{
|
||||
uint8 waveform[32]; /* Waveform data */
|
||||
uint8 waveform_index; /* Waveform data index */
|
||||
uint8 dda;
|
||||
uint8 control; /* Channel enable, DDA, volume */
|
||||
uint8 noisectrl; /* Noise enable/ctrl (channels 4,5 only) */
|
||||
|
||||
int32 vl[2]; //vll, vlr;
|
||||
|
||||
int32 counter;
|
||||
|
||||
void (PCE_PSG::*UpdateOutput)(const int32 timestamp, psg_channel *ch);
|
||||
|
||||
uint32 freq_cache;
|
||||
uint32 noise_freq_cache; // Channel 4,5 only
|
||||
int32 noisecount;
|
||||
uint32 lfsr;
|
||||
|
||||
int32 samp_accum; // The result of adding up all the samples in the waveform buffer(part of an optimization for high-frequency playback).
|
||||
int32 blip_prev_samp[2];
|
||||
int32 lastts;
|
||||
|
||||
uint16 frequency; /* Channel frequency */
|
||||
uint8 balance; /* Channel balance */
|
||||
};
|
||||
|
||||
// Only CH4 and CH5 have NCTRL and LFSR, but it's here for the other channels for "consistency".
|
||||
enum
|
||||
{
|
||||
PSG_GSREG_CH0_FREQ = 0x000,
|
||||
// PSG_GSREG_CH0_COUNTER,
|
||||
PSG_GSREG_CH0_CTRL,
|
||||
PSG_GSREG_CH0_BALANCE,
|
||||
PSG_GSREG_CH0_WINDEX,
|
||||
PSG_GSREG_CH0_SCACHE,
|
||||
PSG_GSREG_CH0_NCTRL,
|
||||
PSG_GSREG_CH0_LFSR,
|
||||
|
||||
PSG_GSREG_CH1_FREQ = 0x100,
|
||||
// PSG_GSREG_CH1_COUNTER,
|
||||
PSG_GSREG_CH1_CTRL,
|
||||
PSG_GSREG_CH1_BALANCE,
|
||||
PSG_GSREG_CH1_WINDEX,
|
||||
PSG_GSREG_CH1_SCACHE,
|
||||
PSG_GSREG_CH1_NCTRL,
|
||||
PSG_GSREG_CH1_LFSR,
|
||||
|
||||
PSG_GSREG_CH2_FREQ = 0x200,
|
||||
// PSG_GSREG_CH2_COUNTER,
|
||||
PSG_GSREG_CH2_CTRL,
|
||||
PSG_GSREG_CH2_BALANCE,
|
||||
PSG_GSREG_CH2_WINDEX,
|
||||
PSG_GSREG_CH2_SCACHE,
|
||||
PSG_GSREG_CH2_NCTRL,
|
||||
PSG_GSREG_CH2_LFSR,
|
||||
|
||||
PSG_GSREG_CH3_FREQ = 0x300,
|
||||
// PSG_GSREG_CH3_COUNTER,
|
||||
PSG_GSREG_CH3_CTRL,
|
||||
PSG_GSREG_CH3_BALANCE,
|
||||
PSG_GSREG_CH3_WINDEX,
|
||||
PSG_GSREG_CH3_SCACHE,
|
||||
PSG_GSREG_CH3_NCTRL,
|
||||
PSG_GSREG_CH3_LFSR,
|
||||
|
||||
PSG_GSREG_CH4_FREQ = 0x400,
|
||||
// PSG_GSREG_CH4_COUNTER,
|
||||
PSG_GSREG_CH4_CTRL,
|
||||
PSG_GSREG_CH4_BALANCE,
|
||||
PSG_GSREG_CH4_WINDEX,
|
||||
PSG_GSREG_CH4_SCACHE,
|
||||
PSG_GSREG_CH4_NCTRL,
|
||||
PSG_GSREG_CH4_LFSR,
|
||||
|
||||
PSG_GSREG_CH5_FREQ = 0x500,
|
||||
// PSG_GSREG_CH5_COUNTER,
|
||||
PSG_GSREG_CH5_CTRL,
|
||||
PSG_GSREG_CH5_BALANCE,
|
||||
PSG_GSREG_CH5_WINDEX,
|
||||
PSG_GSREG_CH5_SCACHE,
|
||||
PSG_GSREG_CH5_NCTRL,
|
||||
PSG_GSREG_CH5_LFSR,
|
||||
|
||||
PSG_GSREG_SELECT = 0x1000,
|
||||
PSG_GSREG_GBALANCE,
|
||||
PSG_GSREG_LFOFREQ,
|
||||
PSG_GSREG_LFOCTRL,
|
||||
_PSG_GSREG_COUNT
|
||||
};
|
||||
|
||||
class PCE_PSG
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
REVISION_HUC6280 = 0,
|
||||
REVISION_HUC6280A,
|
||||
_REVISION_COUNT
|
||||
};
|
||||
|
||||
|
||||
PCE_PSG(int32* hr_l, int32* hr_r, int want_revision) MDFN_COLD;
|
||||
~PCE_PSG() MDFN_COLD;
|
||||
|
||||
int StateAction(StateMem *sm, const unsigned load, const bool data_only);
|
||||
|
||||
void Power(const int32 timestamp) MDFN_COLD;
|
||||
void Write(int32 timestamp, uint8 A, uint8 V);
|
||||
|
||||
void SetVolume(double new_volume);
|
||||
|
||||
void Update(int32 timestamp);
|
||||
void ResetTS(int32 ts_base = 0);
|
||||
|
||||
// TODO: timestamp
|
||||
uint32 GetRegister(const unsigned int id, char *special, const uint32 special_len);
|
||||
void SetRegister(const unsigned int id, const uint32 value);
|
||||
|
||||
void PeekWave(const unsigned int ch, uint32 Address, uint32 Length, uint8 *Buffer);
|
||||
void PokeWave(const unsigned int ch, uint32 Address, uint32 Length, const uint8 *Buffer);
|
||||
|
||||
private:
|
||||
|
||||
void UpdateSubLFO(int32 timestamp);
|
||||
void UpdateSubNonLFO(int32 timestamp);
|
||||
|
||||
void RecalcUOFunc(int chnum);
|
||||
void UpdateOutputSub(const int32 timestamp, psg_channel *ch, const int32 samp0, const int32 samp1);
|
||||
void UpdateOutput_Off(const int32 timestamp, psg_channel *ch);
|
||||
void UpdateOutput_Accum_HuC6280(const int32 timestamp, psg_channel *ch);
|
||||
void UpdateOutput_Accum_HuC6280A(const int32 timestamp, psg_channel *ch);
|
||||
void UpdateOutput_Norm(const int32 timestamp, psg_channel *ch);
|
||||
void UpdateOutput_Noise(const int32 timestamp, psg_channel *ch);
|
||||
void (PCE_PSG::*UpdateOutput_Accum)(const int32 timestamp, psg_channel *ch);
|
||||
|
||||
int32 GetVL(const int chnum, const int lr);
|
||||
|
||||
void RecalcFreqCache(int chnum);
|
||||
void RecalcNoiseFreqCache(int chnum);
|
||||
void RunChannel(int chc, int32 timestamp, bool LFO_On);
|
||||
|
||||
uint8 select; /* Selected channel (0-5) */
|
||||
uint8 globalbalance; /* Global sound balance */
|
||||
uint8 lfofreq; /* LFO frequency */
|
||||
uint8 lfoctrl; /* LFO control */
|
||||
|
||||
int32 vol_update_counter;
|
||||
int32 vol_update_which;
|
||||
int32 vol_update_vllatch;
|
||||
bool vol_pending;
|
||||
|
||||
psg_channel channel[6];
|
||||
|
||||
int32 lastts;
|
||||
int revision;
|
||||
|
||||
int32* HRBufs[2];
|
||||
|
||||
int32 dbtable_volonly[32];
|
||||
|
||||
int32 dbtable[32][32];
|
||||
};
|
||||
|
||||
#endif
|
1830
mednafen/hw_video/huc6270/vdc.cpp
Normal file
1830
mednafen/hw_video/huc6270/vdc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
528
mednafen/hw_video/huc6270/vdc.h
Normal file
528
mednafen/hw_video/huc6270/vdc.h
Normal file
@ -0,0 +1,528 @@
|
||||
#ifndef __PCE_VDC_H
|
||||
#define __PCE_VDC_H
|
||||
|
||||
#include <mednafen/lepacker.h>
|
||||
|
||||
#define VDC_PIXEL_OUT_MASK 0x01FF
|
||||
|
||||
// This bit will be set for a non-sprite pixel if the BG layer is disabled(via ToggleLayer()),
|
||||
#define VDC_BGDISABLE_OUT_MASK 0x0200
|
||||
|
||||
// HSync and VSync out bits are only valid when the EX bits in VDC's CR
|
||||
// are set so that the VDC will output sync signals rather than
|
||||
// input them. If it is not configured in this manner, the bit(s) shall always be 0.
|
||||
#define VDC_HSYNC_OUT_MASK 0x2000
|
||||
#define VDC_VSYNC_OUT_MASK 0x4000
|
||||
|
||||
// The DISP bit can either denote active display area(1 = active, 0 = inactive),
|
||||
// colorburst insertion period(0 = insert colorburst, 1 = not in colorburst period; may not be emulated correctly),
|
||||
// or "internal horizontal synchronous signal"(may not be emulated correctly), depending on the TE
|
||||
// bits in the VDC's CR.
|
||||
#define VDC_DISP_OUT_MASK 0x8000
|
||||
|
||||
#define VDC_REGSETP(_reg, _data, _msb) { _reg &= 0xFF << ((_msb) ? 0 : 8); _reg |= (_data) << ((_msb) ? 8 : 0); }
|
||||
#define VDC_REGGETP(_reg, _msb) ((_reg >> ((_msb) ? 8 : 0)) & 0xFF)
|
||||
|
||||
static const unsigned int vram_inc_tab[4] = { 1, 32, 64, 128 };
|
||||
|
||||
#define VDC_IS_BSY (pending_read || pending_write)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32 x;
|
||||
uint32 flags;
|
||||
uint8 palette_index;
|
||||
uint16 pattern_data[4];
|
||||
} SPRLE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// In the case the VDC access doesn't cause a VRAM read/write, only ReadCount/WriteCount will be set to 0.
|
||||
uint32 ReadStart;
|
||||
uint32 ReadCount;
|
||||
uint32 WriteStart;
|
||||
uint32 WriteCount;
|
||||
|
||||
uint32 RegRWIndex;
|
||||
bool RegWriteDone;
|
||||
bool RegReadDone;
|
||||
} VDC_SimulateResult;
|
||||
|
||||
class VDC
|
||||
{
|
||||
public:
|
||||
|
||||
VDC() MDFN_COLD;
|
||||
~VDC() MDFN_COLD;
|
||||
|
||||
// Default false.
|
||||
void SetUnlimitedSprites(const bool nospritelimit);
|
||||
|
||||
// The VRAM size is specified in 16-bit words. Default 65536.
|
||||
// Reset() should be called after changing this setting, otherwise things may be broken.
|
||||
void SetVRAMSize(const uint32 par_VRAM_size);
|
||||
|
||||
int32 Reset(void) MDFN_WARN_UNUSED_RESULT;
|
||||
|
||||
// ResetSimulate(), SimulateWrite(), and SimulateRead() are intended to handle VRAM read/write breakpoints.
|
||||
// SimulateWrite() and SimulateRead() will return the VRAM address that will EVENTUALLY be written(upper 32-bits) and/or read(lower 32-bits) to
|
||||
// due to the access, or 0xFFFFFFFF in the upper or lower 32-bits if no VRAM access of that type occurs.
|
||||
//
|
||||
// The feature is intended to support block moves to VRAM in a single instruction. It may not function properly if the address passed to SimulateRead()
|
||||
// or SimulateWrite() alternates(even if just once) between the data port high byte and control port between calls to ResetSimulate()
|
||||
// Call to reset simulation state.
|
||||
|
||||
|
||||
INLINE void ResetSimulate(void)
|
||||
{
|
||||
Simulate_MAWR = MAWR;
|
||||
Simulate_MARR = MARR;
|
||||
|
||||
Simulate_select = select;
|
||||
Simulate_CR = CR;
|
||||
|
||||
Simulate_LENR = LENR;
|
||||
}
|
||||
|
||||
INLINE void SimulateRead(uint32 A, VDC_SimulateResult *result)
|
||||
{
|
||||
result->ReadCount = 0;
|
||||
result->WriteCount = 0;
|
||||
result->RegReadDone = false;
|
||||
result->RegWriteDone = false;
|
||||
|
||||
if(A & 0x2)
|
||||
{
|
||||
result->RegReadDone = true;
|
||||
result->RegRWIndex = Simulate_select;
|
||||
}
|
||||
|
||||
if((A & 0x3) == 0x3 && Simulate_select == 0x02)
|
||||
{
|
||||
Simulate_MARR += vram_inc_tab[(Simulate_CR >> 11) & 0x3];
|
||||
|
||||
result->ReadStart = Simulate_MARR;
|
||||
result->ReadCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void SimulateWrite(uint32 A, uint8 V, VDC_SimulateResult *result)
|
||||
{
|
||||
result->ReadCount = 0;
|
||||
result->WriteCount = 0;
|
||||
result->RegReadDone = false;
|
||||
result->RegWriteDone = false;
|
||||
|
||||
const unsigned int msb = A & 1;
|
||||
|
||||
switch(A & 0x3)
|
||||
{
|
||||
case 0x00:
|
||||
Simulate_select = V & 0x1F;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
result->RegWriteDone = true;
|
||||
result->RegRWIndex = Simulate_select;
|
||||
|
||||
switch(Simulate_select)
|
||||
{
|
||||
case 0x00:
|
||||
VDC_REGSETP(Simulate_MAWR, V, msb);
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
VDC_REGSETP(Simulate_MARR, V, msb);
|
||||
Simulate_MARR += vram_inc_tab[(Simulate_CR >> 11) & 0x3];
|
||||
|
||||
result->ReadStart = Simulate_MARR;
|
||||
result->ReadCount = 1;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
if(msb)
|
||||
{
|
||||
result->WriteStart = Simulate_MAWR;
|
||||
result->WriteCount = 1;
|
||||
|
||||
Simulate_MAWR += vram_inc_tab[(Simulate_CR >> 11) & 0x3];
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x12:
|
||||
VDC_REGSETP(Simulate_LENR, V, msb);
|
||||
if(msb)
|
||||
{
|
||||
result->ReadStart = SOUR;
|
||||
result->ReadCount = Simulate_LENR + 1;
|
||||
|
||||
if(DCR & 0x4)
|
||||
result->ReadStart = (result->ReadStart - (result->ReadCount - 1)) & 0xFFFF;
|
||||
|
||||
result->WriteStart = DESR;
|
||||
result->WriteCount = Simulate_LENR + 1;
|
||||
|
||||
if(DCR & 0x8)
|
||||
result->WriteStart = (result->WriteStart - (result->WriteCount - 1)) & 0xFFFF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void SimulateRead16(bool A, VDC_SimulateResult *result)
|
||||
{
|
||||
result->ReadCount = 0;
|
||||
result->WriteCount = 0;
|
||||
result->RegReadDone = false;
|
||||
result->RegWriteDone = false;
|
||||
|
||||
if(A & 0x2)
|
||||
{
|
||||
result->RegReadDone = true;
|
||||
result->RegRWIndex = Simulate_select;
|
||||
}
|
||||
|
||||
if(A && Simulate_select == 0x02)
|
||||
{
|
||||
Simulate_MARR += vram_inc_tab[(Simulate_CR >> 11) & 0x3];
|
||||
|
||||
result->ReadStart = Simulate_MARR;
|
||||
result->ReadCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void SimulateWrite16(bool A, uint16 V, VDC_SimulateResult *result)
|
||||
{
|
||||
result->ReadCount = 0;
|
||||
result->WriteCount = 0;
|
||||
result->RegReadDone = false;
|
||||
result->RegWriteDone = false;
|
||||
|
||||
if(!A)
|
||||
Simulate_select = V & 0x1F;
|
||||
else
|
||||
{
|
||||
result->RegWriteDone = true;
|
||||
result->RegRWIndex = Simulate_select;
|
||||
|
||||
switch(Simulate_select)
|
||||
{
|
||||
case 0x00:
|
||||
Simulate_MAWR = V;
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
Simulate_MARR = V;
|
||||
Simulate_MARR += vram_inc_tab[(Simulate_CR >> 11) & 0x3];
|
||||
|
||||
result->ReadStart = Simulate_MARR;
|
||||
result->ReadCount = 1;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
result->WriteStart = Simulate_MAWR;
|
||||
result->WriteCount = 1;
|
||||
|
||||
Simulate_MAWR += vram_inc_tab[(Simulate_CR >> 11) & 0x3];
|
||||
break;
|
||||
|
||||
case 0x12:
|
||||
Simulate_LENR = V;
|
||||
result->ReadStart = SOUR;
|
||||
result->ReadCount = Simulate_LENR + 1;
|
||||
|
||||
if(DCR & 0x4)
|
||||
result->ReadStart = (result->ReadStart - (result->ReadCount - 1)) & 0xFFFF;
|
||||
|
||||
result->WriteStart = DESR;
|
||||
result->WriteCount = Simulate_LENR + 1;
|
||||
|
||||
if(DCR & 0x8)
|
||||
result->WriteStart = (result->WriteStart - (result->WriteCount - 1)) & 0xFFFF;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32 HSync(bool);
|
||||
int32 VSync(bool);
|
||||
|
||||
|
||||
void Write(uint32 A, uint8 V, int32 &next_event);
|
||||
uint8 Read(uint32 A, int32 &next_event, bool peek = false);
|
||||
|
||||
void Write16(bool A, uint16 V);
|
||||
uint16 Read16(bool A, bool peek = false);
|
||||
|
||||
int32 Run(int32 clocks, /*bool hs, bool vs,*/ uint16 *pixels, bool skip);
|
||||
|
||||
|
||||
void FixTileCache(uint16);
|
||||
void SetLayerEnableMask(uint64 mask);
|
||||
|
||||
void RunDMA(int32, bool force_completion = false);
|
||||
void RunSATDMA(int32, bool force_completion = false);
|
||||
|
||||
void IncRCR(void);
|
||||
void DoVBIRQTest(void);
|
||||
void HDS_Start(void);
|
||||
|
||||
void StateExtra(MDFN::LEPacker &sl_packer, bool load);
|
||||
int StateAction(StateMem *sm, const unsigned load, const bool data_only, const char *sname);
|
||||
|
||||
// Peek(VRAM/SAT) and Poke(VRAM/SAT) work in 16-bit VRAM word units.
|
||||
INLINE uint16 PeekVRAM(uint16 Address)
|
||||
{
|
||||
if(Address < VRAM_Size)
|
||||
return(VRAM[Address]);
|
||||
else
|
||||
return(0);
|
||||
}
|
||||
|
||||
INLINE uint16 PeekSAT(uint8 Address)
|
||||
{
|
||||
return(SAT[Address]);
|
||||
}
|
||||
|
||||
INLINE void PokeVRAM(uint16 Address, const uint16 Data)
|
||||
{
|
||||
if(Address < VRAM_Size)
|
||||
{
|
||||
VRAM[Address] = Data;
|
||||
FixTileCache(Address);
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void PokeSAT(uint8 Address, const uint16 Data)
|
||||
{
|
||||
SAT[Address] = Data;
|
||||
}
|
||||
|
||||
|
||||
// Register enums for GetRegister() and SetRegister()
|
||||
enum
|
||||
{
|
||||
GSREG_MAWR = 0,
|
||||
GSREG_MARR,
|
||||
GSREG_CR,
|
||||
GSREG_RCR,
|
||||
GSREG_BXR,
|
||||
GSREG_BYR,
|
||||
GSREG_MWR,
|
||||
GSREG_HSR,
|
||||
GSREG_HDR,
|
||||
GSREG_VSR,
|
||||
GSREG_VDR,
|
||||
GSREG_VCR,
|
||||
GSREG_DCR,
|
||||
GSREG_SOUR,
|
||||
GSREG_DESR,
|
||||
GSREG_LENR,
|
||||
GSREG_DVSSR,
|
||||
|
||||
GSREG_SELECT,
|
||||
GSREG_STATUS,
|
||||
|
||||
__GSREG_COUNT
|
||||
};
|
||||
|
||||
// Pass NULL if you don't want more information about the special meaning of the value in the specified
|
||||
// register. Otherwise, pass a buffer of at least 256 bytes in size.
|
||||
uint32 GetRegister(const unsigned int id, char *special, const uint32 special_len);
|
||||
void SetRegister(const unsigned int id, const uint32 value);
|
||||
|
||||
INLINE bool PeekIRQ(void)
|
||||
{
|
||||
return((bool)(status & 0x3F));
|
||||
}
|
||||
|
||||
INLINE void SetIRQHook(void (*irqh)(bool))
|
||||
{
|
||||
IRQHook = irqh;
|
||||
}
|
||||
|
||||
INLINE void SetWSHook(bool (*wsh)(int32))
|
||||
{
|
||||
WSHook = wsh;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
int TimeFromHDSStartToBYRLatch(void);
|
||||
int TimeFromBYRLatchToBXRLatch(void);
|
||||
|
||||
enum
|
||||
{
|
||||
HPHASE_HDS = 0,
|
||||
HPHASE_HDS_PART2,
|
||||
HPHASE_HDS_PART3,
|
||||
HPHASE_HDW,
|
||||
HPHASE_HDW_FINAL,
|
||||
HPHASE_HDE,
|
||||
HPHASE_HSW,
|
||||
HPHASE_COUNT
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
VPHASE_VDS = 0,
|
||||
VPHASE_VDW,
|
||||
VPHASE_VCR,
|
||||
VPHASE_VSW,
|
||||
VPHASE_COUNT
|
||||
};
|
||||
|
||||
int VRAM_Size; // = 0x8000;
|
||||
int VRAM_SizeMask; // = VRAM_Size - 1; //0x7FFF;
|
||||
int VRAM_BGTileNoMask; // = VRAM_SizeMask / 16; //0x7FF;
|
||||
|
||||
void (*IRQHook)(bool);
|
||||
bool (*WSHook)(int32);
|
||||
|
||||
void DoWaitStates(void);
|
||||
void CheckAndCommitPending(void);
|
||||
|
||||
INLINE int32 CalcNextEvent(void)
|
||||
{
|
||||
int32 next_event = HPhaseCounter;
|
||||
|
||||
if(sat_dma_counter > 0 && sat_dma_counter < next_event)
|
||||
next_event = sat_dma_counter;
|
||||
|
||||
if(sprite_cg_fetch_counter > 0 && sprite_cg_fetch_counter < next_event)
|
||||
next_event = sprite_cg_fetch_counter;
|
||||
|
||||
if(DMARunning)
|
||||
{
|
||||
assert(VDMA_CycleCounter < 2);
|
||||
|
||||
int32 next_vram_dma_event = ((LENR + 1) * 4) - (DMAReadWrite * 2) - VDMA_CycleCounter;
|
||||
|
||||
assert(next_vram_dma_event > 0);
|
||||
|
||||
if(next_vram_dma_event > 0 && next_vram_dma_event < next_event)
|
||||
next_event = next_vram_dma_event;
|
||||
|
||||
//printf("Next VRAM DMA event: %d(LENR = %d)\n", next_vram_dma_event, LENR);
|
||||
}
|
||||
|
||||
assert(next_event > 0);
|
||||
return(next_event);
|
||||
}
|
||||
|
||||
bool in_exhsync, in_exvsync;
|
||||
|
||||
void CalcWidthStartEnd(uint32 &display_width, uint32 &start, uint32 &end);
|
||||
void DrawBG(uint16 *target, int enabled);
|
||||
void DrawSprites(uint16 *target, int enabled);
|
||||
void FetchSpriteData(void);
|
||||
|
||||
|
||||
uint8 Simulate_select;
|
||||
uint16 Simulate_MAWR;
|
||||
uint16 Simulate_MARR;
|
||||
uint16 Simulate_CR;
|
||||
uint16 Simulate_LENR;
|
||||
|
||||
int32 sat_dma_counter;
|
||||
|
||||
uint8 select;
|
||||
uint16 MAWR; // Memory Address Write Register
|
||||
uint16 MARR; // Memory Address Read Register
|
||||
|
||||
uint16 CR; // Control Register
|
||||
uint16 CR_cache; // Cache for BG/SPR enable
|
||||
uint16 RCR; // Raster Compare Register
|
||||
uint16 BXR; // Background X-Scroll Register
|
||||
uint16 BYR; // Background Y-Scroll Register
|
||||
uint16 MWR; // Memory Width Register
|
||||
|
||||
uint16 HSR; // Horizontal Sync Register
|
||||
uint16 HDR; // Horizontal Display Register
|
||||
uint16 VSR;
|
||||
uint16 VDR;
|
||||
|
||||
uint16 VCR;
|
||||
uint16 DCR;
|
||||
uint16 SOUR;
|
||||
uint16 DESR;
|
||||
uint16 LENR;
|
||||
uint16 DVSSR;
|
||||
|
||||
// Internal SAT DMA transfer variables.
|
||||
//uint16 SAT_SOUR;
|
||||
//uint16 SAT_DESR;
|
||||
//uint16 SAT_LENR;
|
||||
|
||||
int32 VDMA_CycleCounter;
|
||||
|
||||
uint32 RCRCount;
|
||||
|
||||
bool pending_read;
|
||||
uint16 pending_read_addr;
|
||||
uint16 read_buffer;
|
||||
|
||||
uint8 write_latch; // LSB
|
||||
|
||||
bool pending_write;
|
||||
uint16 pending_write_addr;
|
||||
uint16 pending_write_latch;
|
||||
|
||||
uint8 status;
|
||||
|
||||
uint16 SAT[0x100];
|
||||
|
||||
uint16 VRAM[65536]; //VRAM_Size];
|
||||
|
||||
union
|
||||
{
|
||||
uint64 bg_tile_cache64[65536 / 16][8]; // Tile, y, x
|
||||
uint8 bg_tile_cache[65536 / 16][8][8];
|
||||
};
|
||||
|
||||
uint16 DMAReadBuffer;
|
||||
bool DMAReadWrite;
|
||||
bool DMARunning;
|
||||
bool DMAPending;
|
||||
bool SATBPending;
|
||||
bool burst_mode;
|
||||
|
||||
uint32 BG_YOffset; // Reloaded from BYR at start of display area?
|
||||
uint32 BG_XOffset; // Reloaded from BXR at each scanline, methinks.
|
||||
|
||||
uint32 HSW_cache, HDS_cache, HDW_cache, HDE_cache;
|
||||
|
||||
uint32 VDS_cache;
|
||||
uint32 VSW_cache;
|
||||
uint32 VDW_cache;
|
||||
uint32 VCR_cache;
|
||||
uint16 MWR_cache;
|
||||
|
||||
|
||||
uint32 BG_YMoo;
|
||||
bool NeedRCRInc, NeedVBIRQTest, NeedSATDMATest, NeedBGYInc;
|
||||
int HPhase, VPhase;
|
||||
int32 HPhaseCounter, VPhaseCounter;
|
||||
|
||||
int32 sprite_cg_fetch_counter;
|
||||
|
||||
|
||||
int32 mystery_counter;
|
||||
bool mystery_phase;
|
||||
|
||||
uint16 linebuf[1024 + 512];
|
||||
uint32 pixel_desu;
|
||||
int32 pixel_copy_count;
|
||||
uint32 userle; // User layer enable.
|
||||
bool unlimited_sprites;
|
||||
|
||||
int active_sprites;
|
||||
SPRLE SpriteList[64 * 2]; // (see unlimited_sprites option, *2 to accommodate 32-pixel-width sprites ) //16];
|
||||
};
|
||||
|
||||
#endif
|
99
mednafen/lepacker.h
Normal file
99
mednafen/lepacker.h
Normal file
@ -0,0 +1,99 @@
|
||||
#ifndef __MDFN_LEPACKER_H
|
||||
#define __MDFN_LEPACKER_H
|
||||
|
||||
#include "mednafen.h"
|
||||
|
||||
/* Little-endian byte packer(and unpacker). */
|
||||
|
||||
namespace MDFN
|
||||
{
|
||||
|
||||
class LEPacker;
|
||||
class LEPackable
|
||||
{
|
||||
public:
|
||||
virtual void pack(LEPacker &lep) = 0;
|
||||
};
|
||||
|
||||
class LEPacker : public std::vector<uint8>
|
||||
{
|
||||
public:
|
||||
|
||||
LEPacker() : read_mode(0), read_pos(0), randomize_read_mode(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline void set_read_mode(bool new_read_mode, bool new_randomize_read_mode = false)
|
||||
{
|
||||
read_mode = new_read_mode;
|
||||
randomize_read_mode = new_randomize_read_mode;
|
||||
}
|
||||
|
||||
inline void reset_read_pos(void)
|
||||
{
|
||||
read_pos = 0;
|
||||
}
|
||||
|
||||
void operator^(LEPackable &o)
|
||||
{
|
||||
o.pack(*this);
|
||||
}
|
||||
|
||||
template<typename T> INLINE void operator^(T &val)
|
||||
{
|
||||
size_type csize = size();
|
||||
|
||||
if(read_mode)
|
||||
{
|
||||
if((read_pos + sizeof(T)) > csize)
|
||||
throw(std::out_of_range("LEPacker::operator^"));
|
||||
|
||||
uint8 *ptr = &(*this)[read_pos];
|
||||
val = 0;
|
||||
|
||||
if(randomize_read_mode)
|
||||
{
|
||||
for(unsigned int n = 0; n < sizeof(T); n++)
|
||||
val |= ((T)((rand() >> 4) & 0xFF)) << (n << 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned int n = 0; n < sizeof(T); n++)
|
||||
val |= ((T)ptr[n]) << (n << 3);
|
||||
}
|
||||
|
||||
read_pos += sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
resize(csize + sizeof(T));
|
||||
|
||||
uint8 *ptr = &(*this)[csize];
|
||||
|
||||
for(unsigned int n = 0; n < sizeof(T); n++)
|
||||
ptr[n] = val >> (n << 3);
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void operator^(bool &val)
|
||||
{
|
||||
uint8 tmp = val;
|
||||
|
||||
(*this) ^ tmp;
|
||||
|
||||
if(read_mode)
|
||||
val = tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool read_mode;
|
||||
uint64 read_pos;
|
||||
bool randomize_read_mode;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -141,12 +141,27 @@ static INLINE uint32_t MDFN_de24msb(const uint8_t *morp)
|
||||
return((morp[2]<<0)|(morp[1]<<8)|(morp[0]<<16));
|
||||
}
|
||||
|
||||
|
||||
static INLINE uint32_t MDFN_de32msb(const uint8_t *morp)
|
||||
{
|
||||
return(morp[3]|(morp[2]<<8)|(morp[1]<<16)|(morp[0]<<24));
|
||||
}
|
||||
|
||||
static INLINE uint64_t MDFN_de64msb(const uint8_t *morp)
|
||||
{
|
||||
uint64_t ret = 0;
|
||||
|
||||
ret |= (uint64_t)morp[7];
|
||||
ret |= (uint64_t)morp[6] << 8;
|
||||
ret |= (uint64_t)morp[5] << 16;
|
||||
ret |= (uint64_t)morp[4] << 24;
|
||||
ret |= (uint64_t)morp[3] << 32;
|
||||
ret |= (uint64_t)morp[2] << 40;
|
||||
ret |= (uint64_t)morp[1] << 48;
|
||||
ret |= (uint64_t)morp[0] << 56;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -22,6 +22,26 @@
|
||||
#define GET_FSIZE_PTR(fp) (fp->size)
|
||||
#define GET_FEXTS_PTR(fp) (fp->ext)
|
||||
|
||||
template <typename T>
|
||||
static T min_T(T x, T y)
|
||||
{
|
||||
return (x < y) ? x : y;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T max_T(T x, T y)
|
||||
{
|
||||
return (x > y) ? x : y;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void swap_T(T& t1, T& t2)
|
||||
{
|
||||
T tmp(t1);
|
||||
t1 = t2;
|
||||
t2 = tmp;
|
||||
}
|
||||
|
||||
extern MDFNGI *MDFNGameInfo;
|
||||
|
||||
#include "settings.h"
|
||||
|
593
mednafen/pce/huc.cpp
Normal file
593
mednafen/pce/huc.cpp
Normal file
@ -0,0 +1,593 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "pce.h"
|
||||
#include "huc.h"
|
||||
#include "pcecd.h"
|
||||
#include <encodings/crc32.h>
|
||||
#include <mednafen/hw_misc/arcade_card/arcade_card.h>
|
||||
#include <mednafen/file.h>
|
||||
#include <mednafen/cdrom/cdromif.h>
|
||||
#include <mednafen/mempatcher.h>
|
||||
#include <mednafen/FileStream.h>
|
||||
|
||||
#include "mcgenjin.h"
|
||||
|
||||
static const uint8 BRAM_Init_String[8] = { 'H', 'U', 'B', 'M', 0x00, 0x88, 0x10, 0x80 }; //"HUBM\x00\x88\x10\x80";
|
||||
static bool BRAM_Disabled; // Cached at game load, don't remove this caching behavior or save game loss may result(if we ever get a GUI).
|
||||
|
||||
ArcadeCard *arcade_card = NULL;
|
||||
|
||||
static MCGenjin *mcg = NULL;
|
||||
static uint8 *HuCROM = NULL;
|
||||
static uint8 *ROMMap[0x100] = { NULL };
|
||||
|
||||
bool IsPopulous;
|
||||
bool IsTsushin;
|
||||
bool PCE_IsCD;
|
||||
|
||||
uint8 *TsushinRAM = NULL; // 0x8000
|
||||
uint8 *PopRAM = NULL; // 0x8000
|
||||
uint8 SaveRAM[2048];
|
||||
uint8 *CDRAM = NULL; //262144;
|
||||
|
||||
static uint8 *SysCardRAM = NULL;
|
||||
|
||||
static void Cleanup(void)
|
||||
{
|
||||
if(HuCROM)
|
||||
{
|
||||
delete[] HuCROM;
|
||||
HuCROM = NULL;
|
||||
}
|
||||
|
||||
if(PopRAM)
|
||||
{
|
||||
delete[] PopRAM;
|
||||
PopRAM = NULL;
|
||||
}
|
||||
|
||||
if(TsushinRAM)
|
||||
{
|
||||
delete[] TsushinRAM;
|
||||
TsushinRAM = NULL;
|
||||
}
|
||||
|
||||
if(CDRAM)
|
||||
{
|
||||
delete[] CDRAM;
|
||||
CDRAM = NULL;
|
||||
}
|
||||
|
||||
if(SysCardRAM)
|
||||
{
|
||||
delete[] SysCardRAM;
|
||||
SysCardRAM = NULL;
|
||||
}
|
||||
|
||||
if(arcade_card)
|
||||
{
|
||||
delete arcade_card;
|
||||
arcade_card = NULL;
|
||||
}
|
||||
|
||||
if(mcg)
|
||||
{
|
||||
delete mcg;
|
||||
mcg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static DECLFR(AC_PhysRead)
|
||||
{
|
||||
return(arcade_card->PhysRead(A, 0));
|
||||
}
|
||||
|
||||
static DECLFW(AC_PhysWrite)
|
||||
{
|
||||
return(arcade_card->PhysWrite(A, V));
|
||||
}
|
||||
|
||||
static DECLFW(SysCardRAMWrite)
|
||||
{
|
||||
SysCardRAM[A - 0x68 * 8192] = V;
|
||||
}
|
||||
|
||||
static DECLFR(SysCardRAMRead)
|
||||
{
|
||||
return(SysCardRAM[A - 0x68 * 8192]);
|
||||
}
|
||||
|
||||
static DECLFW(CDRAMWrite)
|
||||
{
|
||||
CDRAM[A - 0x80 * 8192] = V;
|
||||
}
|
||||
|
||||
static DECLFR(CDRAMRead)
|
||||
{
|
||||
return(CDRAM[A - 0x80 * 8192]);
|
||||
}
|
||||
|
||||
static DECLFR(SaveRAMRead)
|
||||
{
|
||||
if(BRAM_Disabled)
|
||||
return(0xFF);
|
||||
|
||||
if((!PCE_IsCD || PCECD_IsBRAMEnabled()) && (A & 8191) < 2048)
|
||||
return(SaveRAM[A & 2047]);
|
||||
else
|
||||
return(0xFF);
|
||||
}
|
||||
|
||||
static DECLFW(SaveRAMWrite)
|
||||
{
|
||||
if(BRAM_Disabled)
|
||||
return;
|
||||
|
||||
if((!PCE_IsCD || PCECD_IsBRAMEnabled()) && (A & 8191) < 2048)
|
||||
SaveRAM[A & 2047] = V;
|
||||
}
|
||||
|
||||
static DECLFR(HuCRead)
|
||||
{
|
||||
return(ROMMap[A >> 13][A]);
|
||||
}
|
||||
|
||||
static DECLFW(HuCRAMWrite)
|
||||
{
|
||||
ROMMap[A >> 13][A] = V;
|
||||
}
|
||||
|
||||
static uint8 HuCSF2Latch;
|
||||
static uint8 HuCSF2BankMask;
|
||||
|
||||
static DECLFR(HuCSF2ReadLow)
|
||||
{
|
||||
return(HuCROM[A]);
|
||||
}
|
||||
|
||||
static DECLFR(HuCSF2Read)
|
||||
{
|
||||
return(HuCROM[(A & 0x7FFFF) + 0x80000 + (HuCSF2Latch & HuCSF2BankMask) * 0x80000 ]);
|
||||
}
|
||||
|
||||
static DECLFW(HuCSF2Write)
|
||||
{
|
||||
if((A & 0x1FF0) == 0x1FF0)
|
||||
HuCSF2Latch = A & 0xF;
|
||||
}
|
||||
|
||||
static DECLFR(MCG_ReadHandler)
|
||||
{
|
||||
return mcg->Read(HuCPU.Timestamp(), A);
|
||||
}
|
||||
|
||||
static DECLFW(MCG_WriteHandler)
|
||||
{
|
||||
mcg->Write(HuCPU.Timestamp(), A, V);
|
||||
}
|
||||
|
||||
static void LoadSaveMemory(const std::string& path, uint8* const data, const uint64 len, bool possibly_gz = false)
|
||||
{
|
||||
#if 0
|
||||
try
|
||||
{
|
||||
std::unique_ptr<Stream> fp((Stream*)(new FileStream(path, FileStream::MODE_READ)));
|
||||
const uint64 fp_size_tmp = fp->size();
|
||||
|
||||
if(fp_size_tmp != len)
|
||||
throw MDFN_Error("Save game memory file \"%s\" is an incorrect size(%llu bytes). The correct size is %llu bytes.", path.c_str(), (unsigned long long)fp_size_tmp, (unsigned long long)len);
|
||||
|
||||
fp->read(data, len);
|
||||
}
|
||||
catch(MDFN_Error &e)
|
||||
{
|
||||
if(e.GetErrno() != ENOENT)
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32 HuC_Load(MDFNFILE* fp, bool DisableBRAM, SysCardType syscard)
|
||||
{
|
||||
uint32 crc = 0;
|
||||
const uint32 sf2_threshold = 2048 * 1024;
|
||||
bool sf2_mapper = false;
|
||||
bool mcg_mapper = false;
|
||||
bool UseBRAM = false;
|
||||
|
||||
uint64 len, m_len;
|
||||
|
||||
len = fp->size;
|
||||
if(len & 512) // Skip copier header.
|
||||
{
|
||||
len &= ~512;
|
||||
file_seek(fp, 512, SEEK_SET);
|
||||
}
|
||||
m_len = (len + 8191) &~ 8191;
|
||||
|
||||
if(len >= 8192)
|
||||
{
|
||||
uint8 buf[8192];
|
||||
|
||||
file_read(fp, buf, 8192, 1);
|
||||
|
||||
if(!memcmp(buf + 0x1FD0, "MCGENJIN", 8))
|
||||
mcg_mapper = true;
|
||||
|
||||
file_seek(fp, -8192, SEEK_CUR); // Seek backwards so we don't undo skip copier header.
|
||||
}
|
||||
|
||||
if(!syscard && m_len >= sf2_threshold && !mcg_mapper)
|
||||
{
|
||||
sf2_mapper = true;
|
||||
|
||||
// Only used the "extended" SF2 mapper if it's considerably larger than the normal SF2 mapper size.
|
||||
if(m_len < (512 * 1024 * 6))
|
||||
m_len = 512 * 1024 * 5;
|
||||
else
|
||||
m_len = round_up_pow2(m_len - 512 * 1024) + 512 * 1024;
|
||||
|
||||
if(m_len > 8912896)
|
||||
MDFN_printf("ROM image is too large for extended SF2 mapper!");
|
||||
|
||||
HuCSF2BankMask = ((m_len - 512 * 1024) / (512 * 1024)) - 1;
|
||||
|
||||
//printf("%d %d, %02x\n", len, m_len, HuCSF2BankMask);
|
||||
}
|
||||
|
||||
IsPopulous = 0;
|
||||
PCE_IsCD = 0;
|
||||
|
||||
if(syscard != SYSCARD_NONE)
|
||||
{
|
||||
CDRAM = new uint8[8 * 8192];
|
||||
|
||||
for(int x = 0x80; x < 0x88; x++)
|
||||
{
|
||||
ROMMap[x] = &CDRAM[(x - 0x80) * 8192] - x * 8192;
|
||||
HuCPU.SetFastRead(x, ROMMap[x] + x * 8192);
|
||||
|
||||
HuCPU.SetReadHandler(x, CDRAMRead);
|
||||
HuCPU.SetWriteHandler(x, CDRAMWrite);
|
||||
}
|
||||
MDFNMP_AddRAM(8 * 8192, 0x80 * 8192, CDRAM);
|
||||
|
||||
UseBRAM = true;
|
||||
}
|
||||
|
||||
if(mcg_mapper)
|
||||
{
|
||||
mcg = new MCGenjin(fp);
|
||||
|
||||
for(unsigned i = 0; i < 128; i++)
|
||||
{
|
||||
HuCPU.SetFastRead(i, NULL);
|
||||
HuCPU.SetReadHandler(i, MCG_ReadHandler);
|
||||
HuCPU.SetWriteHandler(i, MCG_WriteHandler);
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < mcg->GetNVPDC(); i++)
|
||||
{
|
||||
uint32 nvs = mcg->GetNVSize(i);
|
||||
|
||||
if(nvs)
|
||||
{
|
||||
char buf[32];
|
||||
std::vector<uint8> tmp_buf;
|
||||
|
||||
tmp_buf.resize(nvs);
|
||||
|
||||
//LoadSaveMemory(MDFN_MakeFName(MDFNMKF_SAV, 0, buf), &tmp_buf[0], tmp_buf.size(), false);
|
||||
mcg->WriteNV(i, &tmp_buf[0], 0, tmp_buf.size());
|
||||
}
|
||||
}
|
||||
|
||||
goto BRAM_Init; // SO EVIL YES EVVIIIIIL(FIXME)
|
||||
}
|
||||
|
||||
HuCROM = new uint8[m_len];
|
||||
memset(HuCROM, 0xFF, m_len);
|
||||
file_read(fp, HuCROM, min_T<uint64>(m_len, len), 1);
|
||||
crc = encoding_crc32(0, HuCROM, min_T<uint64>(m_len, len));
|
||||
|
||||
if(syscard == SYSCARD_NONE)
|
||||
MDFN_printf("ROM: %lluKiB\n", (unsigned long long)((m_len < len) ? m_len : len) / 1024);
|
||||
|
||||
if(m_len == 0x60000)
|
||||
{
|
||||
for(int x = 0; x < 128; x++)
|
||||
{
|
||||
ROMMap[x] = &HuCROM[(x & 0x1F) * 8192] - x * 8192;
|
||||
|
||||
HuCPU.SetFastRead(x, ROMMap[x] + x * 8192);
|
||||
HuCPU.SetReadHandler(x, HuCRead);
|
||||
}
|
||||
|
||||
for(int x = 64; x < 128; x++)
|
||||
{
|
||||
ROMMap[x] = &HuCROM[((x & 0xF) + 32) * 8192] - x * 8192;
|
||||
|
||||
HuCPU.SetFastRead(x, ROMMap[x] + x * 8192);
|
||||
HuCPU.SetReadHandler(x, HuCRead);
|
||||
}
|
||||
}
|
||||
else if(m_len == 0x80000)
|
||||
{
|
||||
for(int x = 0; x < 64; x++)
|
||||
{
|
||||
ROMMap[x] = &HuCROM[(x & 0x3F) * 8192] - x * 8192;
|
||||
|
||||
HuCPU.SetFastRead(x, ROMMap[x] + x * 8192);
|
||||
HuCPU.SetReadHandler(x, HuCRead);
|
||||
}
|
||||
for(int x = 64; x < 128; x++)
|
||||
{
|
||||
ROMMap[x] = &HuCROM[((x & 0x1F) + 32) * 8192] - x * 8192;
|
||||
|
||||
HuCPU.SetFastRead(x, ROMMap[x] + x * 8192);
|
||||
HuCPU.SetReadHandler(x, HuCRead);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int x = 0; x < 128; x++)
|
||||
{
|
||||
uint8 bank = x % (m_len / 8192);
|
||||
|
||||
ROMMap[x] = &HuCROM[bank * 8192] - x * 8192;
|
||||
|
||||
HuCPU.SetFastRead(x, ROMMap[x] + x * 8192);
|
||||
HuCPU.SetReadHandler(x, HuCRead);
|
||||
}
|
||||
}
|
||||
|
||||
if(syscard)
|
||||
{
|
||||
if(syscard == SYSCARD_3 || syscard == SYSCARD_ARCADE)
|
||||
{
|
||||
SysCardRAM = new uint8[24 * 8192];
|
||||
|
||||
for(int x = 0x68; x < 0x80; x++)
|
||||
{
|
||||
ROMMap[x] = &SysCardRAM[(x - 0x68) * 8192] - x * 8192;
|
||||
HuCPU.SetFastRead(x, ROMMap[x] + x * 8192);
|
||||
|
||||
HuCPU.SetReadHandler(x, SysCardRAMRead);
|
||||
HuCPU.SetWriteHandler(x, SysCardRAMWrite);
|
||||
}
|
||||
MDFNMP_AddRAM(24 * 8192, 0x68 * 8192, SysCardRAM);
|
||||
}
|
||||
|
||||
if(syscard == SYSCARD_ARCADE)
|
||||
{
|
||||
arcade_card = new ArcadeCard();
|
||||
|
||||
for(int x = 0x40; x < 0x44; x++)
|
||||
{
|
||||
ROMMap[x] = NULL;
|
||||
HuCPU.SetFastRead(x, NULL);
|
||||
|
||||
HuCPU.SetReadHandler(x, AC_PhysRead);
|
||||
HuCPU.SetWriteHandler(x, AC_PhysWrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!memcmp(HuCROM + 0x1F26, "POPULOUS", strlen("POPULOUS")))
|
||||
{
|
||||
PopRAM = new uint8[32768];
|
||||
memset(PopRAM, 0xFF, 32768);
|
||||
|
||||
//LoadSaveMemory(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav"), PopRAM, 32768);
|
||||
|
||||
IsPopulous = 1;
|
||||
MDFN_printf("Populous\n");
|
||||
for(int x = 0x40; x < 0x44; x++)
|
||||
{
|
||||
ROMMap[x] = &PopRAM[(x & 3) * 8192] - x * 8192;
|
||||
HuCPU.SetFastRead(x, ROMMap[x] + x * 8192);
|
||||
|
||||
HuCPU.SetReadHandler(x, HuCRead);
|
||||
HuCPU.SetWriteHandler(x, HuCRAMWrite);
|
||||
}
|
||||
MDFNMP_AddRAM(32768, 0x40 * 8192, PopRAM);
|
||||
}
|
||||
else if(crc == 0x34dc65c4) // Tsushin Booster
|
||||
{
|
||||
TsushinRAM = new uint8[0x8000];
|
||||
memset(TsushinRAM, 0xFF, 0x8000);
|
||||
|
||||
//LoadSaveMemory(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav"), TsushinRAM, 32768);
|
||||
|
||||
IsTsushin = 1;
|
||||
MDFN_printf("Tsushin Booster\n");
|
||||
for(int x = 0x88; x < 0x8C; x++)
|
||||
{
|
||||
ROMMap[x] = &TsushinRAM[(x & 3) * 8192] - x * 8192;
|
||||
HuCPU.SetFastRead(x, ROMMap[x] + x * 8192);
|
||||
|
||||
HuCPU.SetReadHandler(x, HuCRead);
|
||||
HuCPU.SetWriteHandler(x, HuCRAMWrite);
|
||||
}
|
||||
MDFNMP_AddRAM(32768, 0x88 * 8192, TsushinRAM);
|
||||
}
|
||||
else
|
||||
UseBRAM = true;
|
||||
|
||||
// 0x1A558
|
||||
if(sf2_mapper)
|
||||
{
|
||||
for(int x = 0x20; x < 0x40; x++)
|
||||
HuCPU.SetReadHandler(x, HuCSF2ReadLow);
|
||||
for(int x = 0x40; x < 0x80; x++)
|
||||
{
|
||||
HuCPU.SetFastRead(x, NULL); // Make sure our reads go through our read function, and not a table lookup
|
||||
HuCPU.SetReadHandler(x, HuCSF2Read);
|
||||
}
|
||||
HuCPU.SetWriteHandler(0, HuCSF2Write);
|
||||
|
||||
MDFN_printf("Street Fighter 2 Mapper\n");
|
||||
HuCSF2Latch = 0;
|
||||
}
|
||||
} // end else to if(syscard)
|
||||
|
||||
BRAM_Init:
|
||||
|
||||
BRAM_Disabled = DisableBRAM;
|
||||
if(BRAM_Disabled)
|
||||
UseBRAM = false;
|
||||
|
||||
if(UseBRAM)
|
||||
{
|
||||
// Initialize BRAM here so users don't have to manually intialize the file cabinet
|
||||
// in the CD BIOS screen.
|
||||
memset(SaveRAM, 0x00, 2048);
|
||||
memcpy(SaveRAM, BRAM_Init_String, 8);
|
||||
|
||||
//LoadSaveMemory(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav"), SaveRAM, 2048);
|
||||
|
||||
HuCPU.SetWriteHandler(0xF7, SaveRAMWrite);
|
||||
HuCPU.SetReadHandler(0xF7, SaveRAMRead);
|
||||
MDFNMP_AddRAM(2048, 0xF7 * 8192, SaveRAM);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
bool IsBRAMUsed(void)
|
||||
{
|
||||
if(BRAM_Disabled)
|
||||
return(false);
|
||||
|
||||
if(memcmp(SaveRAM, BRAM_Init_String, 8)) // HUBM string is modified/missing
|
||||
return(1);
|
||||
|
||||
for(int x = 8; x < 2048; x++)
|
||||
if(SaveRAM[x]) return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
int HuC_StateAction(StateMem *sm, const unsigned load, const bool data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFARRAY(PopRAM, IsPopulous ? 32768 : 0),
|
||||
SFARRAY(TsushinRAM, IsTsushin ? 32768 : 0),
|
||||
SFARRAY(SaveRAM, (IsPopulous || IsTsushin || BRAM_Disabled) ? 0 : 2048),
|
||||
SFARRAY(CDRAM, CDRAM ? (8192 * 8) : 0),
|
||||
SFARRAY(SysCardRAM, SysCardRAM ? (8192 * 24) : 0),
|
||||
SFVAR(HuCSF2Latch),
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "HuC", false);
|
||||
|
||||
if(load)
|
||||
HuCSF2Latch &= 0xF;
|
||||
|
||||
if(PCE_IsCD)
|
||||
{
|
||||
if(arcade_card)
|
||||
ret &= arcade_card->StateAction(sm, load, data_only);
|
||||
|
||||
ret &= PCECD_StateAction(sm, load, data_only);
|
||||
}
|
||||
|
||||
if(mcg)
|
||||
ret &= mcg->StateAction(sm, load, data_only);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void HuC_SaveNV(void)
|
||||
{
|
||||
if(mcg)
|
||||
{
|
||||
for(unsigned i = 0; i < mcg->GetNVPDC(); i++)
|
||||
{
|
||||
uint32 nvs = mcg->GetNVSize(i);
|
||||
|
||||
if(nvs)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
//MDFN_DumpToFile(MDFN_MakeFName(MDFNMKF_SAV, 0, buf), mcg->ReadNV(i), nvs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsPopulous)
|
||||
{
|
||||
if(PopRAM)
|
||||
{
|
||||
//MDFN_DumpToFile(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav"), PopRAM, 32768);
|
||||
}
|
||||
}
|
||||
else if(IsTsushin)
|
||||
{
|
||||
if(TsushinRAM)
|
||||
{
|
||||
//MDFN_DumpToFile(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav"), TsushinRAM, 32768);
|
||||
}
|
||||
}
|
||||
else if(!BRAM_Disabled && IsBRAMUsed())
|
||||
{
|
||||
//MDFN_DumpToFile(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav"), SaveRAM, 2048);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// HuC_Kill() may be called before HuC_Load*() is called or even after it errors out, so we have a separate HuC_SaveNV()
|
||||
// to prevent save game file corruption in case of error.
|
||||
void HuC_Kill(void)
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void HuC_Update(int32 timestamp)
|
||||
{
|
||||
if(mcg)
|
||||
mcg->Update(timestamp);
|
||||
}
|
||||
|
||||
void HuC_ResetTS(int32 ts_base)
|
||||
{
|
||||
if(mcg)
|
||||
mcg->ResetTS(ts_base);
|
||||
}
|
||||
|
||||
void HuC_Power(void)
|
||||
{
|
||||
if(CDRAM)
|
||||
memset(CDRAM, 0x00, 8 * 8192);
|
||||
|
||||
if(SysCardRAM)
|
||||
memset(SysCardRAM, 0x00, 24 * 8192);
|
||||
|
||||
if(arcade_card)
|
||||
arcade_card->Power();
|
||||
|
||||
HuCSF2Latch = 0;
|
||||
|
||||
if(mcg)
|
||||
mcg->Power();
|
||||
}
|
36
mednafen/pce/huc.h
Normal file
36
mednafen/pce/huc.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef __MDFN_PCE_HUC_H
|
||||
#define __MDFN_PCE_HUC_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SYSCARD_NONE = 0,
|
||||
SYSCARD_1,
|
||||
SYSCARD_2,
|
||||
SYSCARD_3,
|
||||
SYSCARD_ARCADE // 3.0 + extras
|
||||
} SysCardType;
|
||||
|
||||
uint32 HuC_Load(MDFNFILE* fp, bool DisableBRAM = false, SysCardType syscard = SYSCARD_NONE) MDFN_COLD;
|
||||
void HuC_SaveNV(void);
|
||||
void HuC_Kill(void) MDFN_COLD;
|
||||
|
||||
void HuC_Update(int32 timestamp);
|
||||
void HuC_ResetTS(int32 ts_base);
|
||||
|
||||
int HuC_StateAction(StateMem *sm, const unsigned load, const bool data_only);
|
||||
|
||||
void HuC_Power(void);
|
||||
|
||||
DECLFR(PCE_ACRead);
|
||||
DECLFW(PCE_ACWrite);
|
||||
|
||||
extern bool PCE_IsCD;
|
||||
extern bool IsPopulous;
|
||||
extern bool IsTsushin;
|
||||
|
||||
extern uint8 *TsushinRAM;
|
||||
extern uint8 *PopRAM;
|
||||
extern uint8 SaveRAM[2048];
|
||||
extern uint8 *CDRAM;
|
||||
|
||||
#endif
|
820
mednafen/pce/huc6280.cpp
Normal file
820
mednafen/pce/huc6280.cpp
Normal file
@ -0,0 +1,820 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
Major organizational differences compared to real HuC6280:
|
||||
PSG emulation is in a completely separate file and class.
|
||||
|
||||
Timer and IRQ read/write handlers are called externally from the main(external) PC Engine I/O page memory handler function, for
|
||||
speed reasons.
|
||||
|
||||
Bus cycle extension on VDC and VCE access is simulated/handled in the main(external) PC Engine I/O page memory handler function, for
|
||||
speed reasons.
|
||||
|
||||
Input port emulation is done externally.
|
||||
*/
|
||||
|
||||
#include <mednafen/mednafen.h>
|
||||
#include "huc6280.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void INLINE HuC6280::LastCycle(void)
|
||||
{
|
||||
/*assert(((P & I_FLAG) ? 0 : (uint32)~0) == PIMaskCache);*/
|
||||
IRQSample = (IRQlow & IRQMask) & PIMaskCache;
|
||||
IFlagSample = P & I_FLAG;
|
||||
ADDCYC(1);
|
||||
}
|
||||
|
||||
void HuC6280::StealCycle(void)
|
||||
{
|
||||
ADDCYC(1);
|
||||
}
|
||||
|
||||
void HuC6280::StealCycles(const int count)
|
||||
{
|
||||
ADDCYC(count);
|
||||
}
|
||||
|
||||
void HuC6280::StealMasterCycles(const int count)
|
||||
{
|
||||
ADDCYC_MASTER(count);
|
||||
}
|
||||
|
||||
void HuC6280::FlushMPRCache(void)
|
||||
{
|
||||
for(int x = 0; x < 9; x++)
|
||||
SetMPR(x, MPR[x & 0x7]);
|
||||
}
|
||||
|
||||
void INLINE HuC6280::PUSH(const uint8 V)
|
||||
{
|
||||
WrMem(0x2100 | S, V);
|
||||
S--;
|
||||
}
|
||||
|
||||
uint8 INLINE HuC6280::POP(void)
|
||||
{
|
||||
S++;
|
||||
|
||||
return(RdMem(0x2100 | S));
|
||||
}
|
||||
|
||||
static uint8 ZNTable[256];
|
||||
/* Some of these operations will only make sense if you know what the flag
|
||||
constants are. */
|
||||
|
||||
void INLINE HuC6280::X_ZN(const uint8 zort)
|
||||
{
|
||||
P &= ~(Z_FLAG|N_FLAG);
|
||||
P |= ZNTable[zort];
|
||||
}
|
||||
|
||||
void INLINE HuC6280::X_ZNT(const uint8 zort)
|
||||
{
|
||||
P |= ZNTable[zort];
|
||||
}
|
||||
|
||||
template<bool DebugMode>
|
||||
void INLINE HuC6280::JR(const bool cond, const bool BBRS)
|
||||
{
|
||||
if(cond)
|
||||
{
|
||||
int32 disp;
|
||||
disp=(int8)RdOp(PC);
|
||||
PC++;
|
||||
ADDCYC(3);
|
||||
PC+=disp;
|
||||
|
||||
if(DebugMode && ADDBT)
|
||||
ADDBT(PC - disp - 2 - (BBRS ? 1 : 0), PC, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ADDCYC(1);
|
||||
PC++;
|
||||
}
|
||||
LastCycle();
|
||||
}
|
||||
|
||||
template<bool DebugMode>
|
||||
void INLINE HuC6280::BBRi(const uint8 val, const unsigned int bitto)
|
||||
{
|
||||
JR<DebugMode>(!(val & (1 << bitto)), true);
|
||||
}
|
||||
|
||||
template<bool DebugMode>
|
||||
void INLINE HuC6280::BBSi(const uint8 val, const unsigned int bitto)
|
||||
{
|
||||
JR<DebugMode>(val & (1 << bitto), true);
|
||||
}
|
||||
|
||||
// Total cycles for ST0/ST1/ST2 is effectively 5(4 here, +1 stealcycle in the PC Engine memory handler logic)
|
||||
#define ST0 { ADDCYC(3); LastCycle(); WrMemPhysical(0x80000000 | (0xFF * 8192 + 0), x); }
|
||||
#define ST1 { ADDCYC(3); LastCycle(); WrMemPhysical(0x80000000 | (0xFF * 8192 + 2), x); }
|
||||
#define ST2 { ADDCYC(3); LastCycle(); WrMemPhysical(0x80000000 | (0xFF * 8192 + 3), x); }
|
||||
|
||||
#define LDA A=x;X_ZN(A)
|
||||
#define LDX X=x;X_ZN(X)
|
||||
#define LDY Y=x;X_ZN(Y)
|
||||
|
||||
|
||||
#define IMP(op) op; break;
|
||||
|
||||
#define SAX { uint8 tmp = X; X = A; A = tmp; ADDCYC(2); LastCycle(); }
|
||||
#define SAY { uint8 tmp = Y; Y = A; A = tmp; ADDCYC(2); LastCycle(); }
|
||||
#define SXY { uint8 tmp = X; X = Y; Y = tmp; ADDCYC(2); LastCycle(); }
|
||||
|
||||
#define TAX { X = A; X_ZN(A); ADDCYC(1); LastCycle(); }
|
||||
#define TXA { A = X; X_ZN(A); ADDCYC(1); LastCycle(); }
|
||||
#define TAY { Y = A; X_ZN(A); ADDCYC(1); LastCycle(); }
|
||||
#define TYA { A = Y; X_ZN(A); ADDCYC(1); LastCycle(); }
|
||||
#define TSX { X = S; X_ZN(X); ADDCYC(1); LastCycle(); }
|
||||
#define TXS { S = X; ADDCYC(1); LastCycle(); }
|
||||
|
||||
#define DEX { X--; X_ZN(X); ADDCYC(1); LastCycle(); }
|
||||
#define DEY { Y--; X_ZN(Y); ADDCYC(1); LastCycle(); }
|
||||
#define INX { X++; X_ZN(X); ADDCYC(1); LastCycle(); }
|
||||
#define INY { Y++; X_ZN(Y); ADDCYC(1); LastCycle(); }
|
||||
|
||||
|
||||
|
||||
// Combined cycle total of TPREFIX and TPOSTFIX must be 3.
|
||||
// WARNING: LASTCYCLE is called twice in instructions that make use TPREFIX/TPOSTFIX, so allow for that in the LASTCYCLE
|
||||
// macro!
|
||||
#define TPREFIX { uint8 Abackup = A; if(P & T_FLAG) { ADDCYC(1); A = RdMem(0x2000 + X); }
|
||||
#define TPOSTFIX if(P & T_FLAG) { ADDCYC(1); WrMem(0x2000 + X, A); LastCycle(); A = Abackup; } }
|
||||
|
||||
/* All of the freaky arithmetic operations. */
|
||||
#define AND TPREFIX; A&=x;X_ZN(A); TPOSTFIX;
|
||||
#define BIT P &= ~(Z_FLAG|V_FLAG|N_FLAG); P|=ZNTable[x&A]&Z_FLAG; P|=x&(V_FLAG|N_FLAG);
|
||||
#define EOR TPREFIX; A^=x;X_ZN(A); TPOSTFIX;
|
||||
#define ORA TPREFIX; A|=x;X_ZN(A); TPOSTFIX;
|
||||
|
||||
|
||||
// ADC and SBC in decimal mode take 1 extra CPU cycle...we'll add it by using "LASTCYCLE". So now that makes
|
||||
// LASTCYCLE being called at most 3 times. Not very LASTy, is it!!
|
||||
#define ADC \
|
||||
TPREFIX; { \
|
||||
if(P & D_FLAG) \
|
||||
{ \
|
||||
uint32 tmp; \
|
||||
tmp = (A & 0x0F) + (x & 0x0F) + (P & 1); \
|
||||
if(tmp >= 0x0A) \
|
||||
tmp += 0x06; \
|
||||
tmp += (A & 0xF0) + (x & 0xF0); \
|
||||
if(tmp >= 0xA0) \
|
||||
tmp += 0x60; \
|
||||
P &= ~(Z_FLAG | N_FLAG | C_FLAG); \
|
||||
if(tmp & 0xFF00) \
|
||||
P |= C_FLAG; \
|
||||
A = tmp; \
|
||||
X_ZNT(A); \
|
||||
LastCycle(); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
uint32 l=A+x+(P&1); \
|
||||
P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \
|
||||
P|=((((A^x)&0x80)^0x80) & ((A^l)&0x80))>>1; \
|
||||
P|=(l>>8)&C_FLAG; \
|
||||
A=l; \
|
||||
X_ZNT(A); \
|
||||
} \
|
||||
} TPOSTFIX;
|
||||
|
||||
#define SBC \
|
||||
if(P & D_FLAG) \
|
||||
{ \
|
||||
const uint8 m = (A & 0xF) - (x & 0xF) - ((P & 1) ^ 1); \
|
||||
const uint8 n = (A >> 4) - (x >> 4) - ((m >> 4) & 1); \
|
||||
uint8 res = (n << 4) | (m & 0xF); \
|
||||
P &= ~(Z_FLAG | N_FLAG | C_FLAG); \
|
||||
if(m & 0x10) \
|
||||
res -= 0x06; \
|
||||
if(n & 0x10) \
|
||||
res -= 0x60; \
|
||||
A = res; \
|
||||
P |= ((n >> 4) & 0x1) ^ 1; \
|
||||
X_ZNT(A); \
|
||||
LastCycle(); \
|
||||
} else { \
|
||||
uint32 l=A-x-((P&1)^1); \
|
||||
P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \
|
||||
P|=((A^l)&(A^x)&0x80)>>1; \
|
||||
P|=((l>>8)&C_FLAG)^C_FLAG; \
|
||||
A=l; \
|
||||
X_ZNT(A); \
|
||||
}
|
||||
|
||||
#define CMPL(a1,a2) { \
|
||||
uint32 t=a1-a2; \
|
||||
X_ZN(t&0xFF); \
|
||||
P&=~C_FLAG; \
|
||||
P|=((t>>8)&C_FLAG)^C_FLAG; \
|
||||
}
|
||||
|
||||
#define TAM \
|
||||
for(int i = 0; i < 8; i ++) { \
|
||||
if(x & (1 << i)) \
|
||||
{ \
|
||||
SetMPR(i, A); \
|
||||
} \
|
||||
} SetMPR(8, MPR[0]); \
|
||||
ADDCYC(4); \
|
||||
LastCycle();
|
||||
|
||||
#define TMA \
|
||||
for(int i = 0; i < 8; i ++) { \
|
||||
if(x & (1 << i)) \
|
||||
A = MPR[i]; \
|
||||
} \
|
||||
ADDCYC(3); \
|
||||
LastCycle();
|
||||
|
||||
// Note: CSL/CSH's speed timing changes take effect for the last CPU cycle of CSL/CSH. Be cautious
|
||||
// not to change the order here:
|
||||
#define CSL { /*printf("CSL: %04x\n", PC);*/ ADDCYC(2); speed = 0; REDOSPEEDCACHE(); LastCycle(); }
|
||||
#define CSH { /*printf("CSH: %04x\n", PC);*/ ADDCYC(2); speed = 1; REDOSPEEDCACHE(); LastCycle(); }
|
||||
|
||||
#define RMB(bitto) x &= ~(1 << (bitto & 7))
|
||||
#define SMB(bitto) x |= 1 << (bitto & 7)
|
||||
|
||||
#define TSB { P &= ~(Z_FLAG | V_FLAG | N_FLAG); P |= (x | A) ? 0 : Z_FLAG; P |= x & (N_FLAG | V_FLAG); x |= A; }
|
||||
#define TRB { P &= ~(Z_FLAG | V_FLAG | N_FLAG); P |= (x & ~A) ? 0 : Z_FLAG; P |= x & (N_FLAG | V_FLAG); x &= ~A; }
|
||||
|
||||
#define TST { P &= ~(Z_FLAG | V_FLAG | N_FLAG); P |= (x & zoomhack) ? 0: Z_FLAG; P |= x & (V_FLAG | N_FLAG); }
|
||||
|
||||
#define CMP CMPL(A,x)
|
||||
#define CPX CMPL(X,x)
|
||||
#define CPY CMPL(Y,x)
|
||||
|
||||
/* The following operations modify the byte being worked on. */
|
||||
#define DEC x--;X_ZN(x)
|
||||
#define INC x++;X_ZN(x)
|
||||
|
||||
#define ASL P&=~C_FLAG;P|=x>>7;x<<=1;X_ZN(x)
|
||||
#define LSR P&=~(C_FLAG|N_FLAG|Z_FLAG);P|=x&1;x>>=1;X_ZNT(x)
|
||||
|
||||
#define ROL { \
|
||||
uint8 l=x>>7; \
|
||||
x<<=1; \
|
||||
x|=P&C_FLAG; \
|
||||
P&=~(Z_FLAG|N_FLAG|C_FLAG); \
|
||||
P|=l; \
|
||||
X_ZNT(x); \
|
||||
}
|
||||
|
||||
#define ROR { \
|
||||
uint8 l=x&1; \
|
||||
x>>=1; \
|
||||
x|=(P&C_FLAG)<<7; \
|
||||
P&=~(Z_FLAG|N_FLAG|C_FLAG); \
|
||||
P|=l; \
|
||||
X_ZNT(x); \
|
||||
}
|
||||
|
||||
/* Absolute */
|
||||
#define GetAB(target) { \
|
||||
target=RdOp(PC); \
|
||||
PC++; \
|
||||
target|=RdOp(PC)<<8; \
|
||||
PC++; \
|
||||
}
|
||||
|
||||
/* Absolute Indexed(for reads) */
|
||||
#define GetABI(target, i) { \
|
||||
unsigned int tmp; \
|
||||
GetAB(tmp); \
|
||||
target=tmp; \
|
||||
target+=i; \
|
||||
}
|
||||
|
||||
/* Zero Page */
|
||||
#define GetZP(target) { \
|
||||
target=0x2000 | RdOp(PC); \
|
||||
PC++; \
|
||||
}
|
||||
|
||||
/* Zero Page Indexed */
|
||||
#define GetZPI(target,i) { \
|
||||
target=0x2000 | ((i+RdOp(PC)) & 0xFF); \
|
||||
PC++; \
|
||||
}
|
||||
|
||||
/* Indirect */
|
||||
#define GetIND(target) { \
|
||||
uint8 tmp; \
|
||||
tmp=RdOp(PC); \
|
||||
PC++; \
|
||||
target=RdMem(0x2000 + tmp); \
|
||||
tmp++; \
|
||||
target|=RdMem(0x2000 + tmp)<<8; \
|
||||
}
|
||||
|
||||
|
||||
/* Indexed Indirect */
|
||||
#define GetIX(target) { \
|
||||
uint8 tmp; \
|
||||
tmp=RdOp(PC); \
|
||||
PC++; \
|
||||
tmp+=X; \
|
||||
target=RdMem(0x2000 + tmp); \
|
||||
tmp++; \
|
||||
target|=RdMem(0x2000 + tmp) <<8; \
|
||||
}
|
||||
|
||||
/* Indirect Indexed(for reads) */
|
||||
#define GetIY(target) { \
|
||||
unsigned int rt; \
|
||||
uint8 tmp; \
|
||||
tmp=RdOp(PC); \
|
||||
rt=RdMem(0x2000 + tmp); \
|
||||
tmp++; \
|
||||
rt|=RdMem(0x2000 + tmp)<<8; \
|
||||
target = (rt + Y); \
|
||||
PC++; \
|
||||
}
|
||||
|
||||
/* Now come the macros to wrap up all of the above stuff addressing mode functions
|
||||
and operation macros. Note that operation macros will always operate(redundant
|
||||
redundant) on the variable "x".
|
||||
*/
|
||||
|
||||
#define RMW_A(op) { uint8 x = A; op; A = x; ADDCYC(1); LastCycle(); break; } /* Meh... */
|
||||
#define RMW_AB(op) { unsigned int EA; uint8 x; GetAB(EA); ADDCYC(6); x=RdMem(EA); op; LastCycle(); WrMem(EA,x); break; }
|
||||
#define RMW_ABI(reg,op) { unsigned int EA; uint8 x; GetABI(EA,reg); ADDCYC(6); x=RdMem(EA); op; LastCycle(); WrMem(EA,x); break; }
|
||||
#define RMW_ABX(op) RMW_ABI(X,op)
|
||||
#define RMW_ABY(op) RMW_ABI(Y,op)
|
||||
#define RMW_ZP(op) { unsigned int EA; uint8 x; GetZP(EA); ADDCYC(5); x=RdMem(EA); op; LastCycle(); WrMem(EA,x); break; }
|
||||
#define RMW_ZPX(op) { unsigned int EA; uint8 x; GetZPI(EA, X); ADDCYC(5); x=RdMem(EA); op; LastCycle(); WrMem(EA,x); break;}
|
||||
|
||||
// For RMB/SMB...
|
||||
#define RMW_ZP_B(op) { unsigned int EA; uint8 x; GetZP(EA); ADDCYC(5); x=RdMem(EA); ADDCYC(1); op; LastCycle(); WrMem(EA,x); break; }
|
||||
|
||||
|
||||
// A LD_IM for complex immediate instructions that take care of cycle consumption in their operation(TAM, TMA, ST0, ST1, ST2)
|
||||
#define LD_IM_COMPLEX(op) { uint8 x = RdOp(PC); PC++; op; break; }
|
||||
|
||||
#define LD_IM(op) {uint8 x; x=RdOp(PC); PC++; ADDCYC(1); LastCycle(); op; break;}
|
||||
#define LD_ZP(op) {unsigned int EA; uint8 x; GetZP(EA); ADDCYC(3); LastCycle(); x=RdMem(EA); op; break;}
|
||||
#define LD_ZPX(op) {unsigned int EA; uint8 x; GetZPI(EA, X); ADDCYC(3); LastCycle(); x=RdMem(EA); op; break;}
|
||||
#define LD_ZPY(op) {unsigned int EA; uint8 x; GetZPI(EA, Y); ADDCYC(3); LastCycle(); x=RdMem(EA); op; break;}
|
||||
#define LD_AB(op) {unsigned int EA; uint8 x; GetAB(EA); ADDCYC(4); LastCycle(); x=RdMem(EA); op; break; }
|
||||
#define LD_ABI(reg,op) {unsigned int EA; uint8 x; GetABI(EA,reg); ADDCYC(4); LastCycle(); x=RdMem(EA); op; break;}
|
||||
#define LD_ABX(op) LD_ABI(X, op)
|
||||
#define LD_ABY(op) LD_ABI(Y, op)
|
||||
|
||||
#define LD_IND(op) {unsigned int EA; uint8 x; GetIND(EA); ADDCYC(6); LastCycle(); x=RdMem(EA); op; break;}
|
||||
#define LD_IX(op) {unsigned int EA; uint8 x; GetIX(EA); ADDCYC(6); LastCycle(); x=RdMem(EA); op; break;}
|
||||
#define LD_IY(op) {unsigned int EA; uint8 x; GetIY(EA); ADDCYC(6); LastCycle(); x=RdMem(EA); op; break;}
|
||||
|
||||
// For the funky TST instruction
|
||||
#define LD_IM_TST(op, lt) { uint8 lt = RdOp(PC); PC++; ADDCYC(3); op; }
|
||||
#define LD_IM_ZP(op) LD_IM_TST(LD_ZP(TST), zoomhack);
|
||||
#define LD_IM_ZPX(op) LD_IM_TST(LD_ZPX(TST), zoomhack);
|
||||
#define LD_IM_AB(op) LD_IM_TST(LD_AB(TST), zoomhack);
|
||||
#define LD_IM_ABX(op) LD_IM_TST(LD_ABX(TST), zoomhack);
|
||||
|
||||
|
||||
#define BMT_PREFIX(pork) in_block_move = IBM_##pork;
|
||||
#define BMT_LOOPCHECK(pork) if(!runrunrun) { TimerSync(); return; } continue_the_##pork:
|
||||
|
||||
#define BMT_TDD BMT_PREFIX(TDD); do { ADDCYC(6); WrMem(bmt_dest, RdMem(bmt_src)); bmt_src--; bmt_dest--; BMT_LOOPCHECK(TDD); bmt_length--; } while(bmt_length);
|
||||
#define BMT_TAI BMT_PREFIX(TAI); {bmt_alternate = 0; do { ADDCYC(6); WrMem(bmt_dest, RdMem(bmt_src + bmt_alternate)); bmt_dest++; bmt_alternate ^= 1; BMT_LOOPCHECK(TAI); bmt_length--; } while(bmt_length); }
|
||||
#define BMT_TIA BMT_PREFIX(TIA); {bmt_alternate = 0; do { ADDCYC(6); WrMem(bmt_dest + bmt_alternate, RdMem(bmt_src)); bmt_src++; bmt_alternate ^= 1; BMT_LOOPCHECK(TIA); bmt_length--; } while(bmt_length); }
|
||||
#define BMT_TII BMT_PREFIX(TII); do { ADDCYC(6); WrMem(bmt_dest, RdMem(bmt_src)); bmt_src++; bmt_dest++; BMT_LOOPCHECK(TII); bmt_length--; } while(bmt_length);
|
||||
#define BMT_TIN BMT_PREFIX(TIN); do { ADDCYC(6); WrMem(bmt_dest, RdMem(bmt_src)); bmt_src++; BMT_LOOPCHECK(TIN); bmt_length--; } while(bmt_length);
|
||||
|
||||
// Block memory transfer load
|
||||
#define LD_BMT(op) { PUSH(Y); PUSH(A); PUSH(X); GetAB(bmt_src); GetAB(bmt_dest); GetAB(bmt_length); ADDCYC(14); op; in_block_move = 0; X = POP(); A = POP(); Y = POP(); ADDCYC(2); LastCycle(); break; }
|
||||
|
||||
#define ST_ZP(r) {unsigned int EA; GetZP(EA); ADDCYC(3); LastCycle(); WrMem(EA, r); break;}
|
||||
#define ST_ZPX(r) {unsigned int EA; GetZPI(EA,X); ADDCYC(3); LastCycle(); WrMem(EA, r); break;}
|
||||
#define ST_ZPY(r) {unsigned int EA; GetZPI(EA,Y); ADDCYC(3); LastCycle(); WrMem(EA, r); break;}
|
||||
#define ST_AB(r) {unsigned int EA; GetAB(EA); ADDCYC(4); LastCycle(); WrMem(EA, r); break;}
|
||||
#define ST_ABI(reg,r) {unsigned int EA; GetABI(EA,reg); ADDCYC(4); LastCycle(); WrMem(EA,r); break; }
|
||||
#define ST_ABX(r) ST_ABI(X, r)
|
||||
#define ST_ABY(r) ST_ABI(Y, r)
|
||||
|
||||
#define ST_IND(r) {unsigned int EA; GetIND(EA); ADDCYC(6); LastCycle(); WrMem(EA,r); break; }
|
||||
#define ST_IX(r) {unsigned int EA; GetIX(EA); ADDCYC(6); LastCycle(); WrMem(EA,r); break; }
|
||||
#define ST_IY(r) {unsigned int EA; GetIY(EA); ADDCYC(6); LastCycle(); WrMem(EA,r); break; }
|
||||
|
||||
void HuC6280::Reset(void)
|
||||
{
|
||||
timer_inreload = false;
|
||||
timer_div = 1024;
|
||||
timer_load = 0;
|
||||
timer_value = 0;
|
||||
timer_status = 0;
|
||||
in_block_move = 0;
|
||||
|
||||
IRQSample = IQRESET;
|
||||
IRQlow = IQRESET;
|
||||
}
|
||||
|
||||
HuC6280::HuC6280()
|
||||
{
|
||||
for(int x = 0; x < 256; x++)
|
||||
{
|
||||
if(!x)
|
||||
ZNTable[x] = Z_FLAG;
|
||||
else if (x & 0x80)
|
||||
ZNTable[x] = N_FLAG;
|
||||
else
|
||||
ZNTable[x] = 0;
|
||||
}
|
||||
|
||||
Init(false);
|
||||
}
|
||||
|
||||
void HuC6280::Init(const bool emulate_wai)
|
||||
{
|
||||
#ifdef ARCH_X86
|
||||
//printf("%llu\n", (unsigned long long)((uint8*)&FastPageR[0] - (uint8*)this));
|
||||
//printf("Blorp: %016llx\n", (unsigned long long)this);
|
||||
assert(((uint8*)&FastPageR[0] - (uint8*)this) < 128);
|
||||
#endif
|
||||
|
||||
EmulateWAI = emulate_wai;
|
||||
|
||||
timestamp = 0;
|
||||
next_user_event = 0;
|
||||
next_event = 0;
|
||||
|
||||
timer_lastts = 0;
|
||||
timer_inreload = 0;
|
||||
timer_status = 0;
|
||||
timer_value = 0;
|
||||
timer_load = 0;
|
||||
timer_div = 0;
|
||||
|
||||
in_block_move = 0;
|
||||
|
||||
LastLogicalReadAddr = 0;
|
||||
LastLogicalWriteAddr = 0;
|
||||
}
|
||||
|
||||
HuC6280::~HuC6280()
|
||||
{
|
||||
}
|
||||
|
||||
void HuC6280::Power(void)
|
||||
{
|
||||
IODataBuffer = 0xFF;
|
||||
|
||||
IRQlow = 0;
|
||||
|
||||
PC = 0;
|
||||
A = 0;
|
||||
X = 0;
|
||||
Y = 0;
|
||||
S = 0;
|
||||
P = 0;
|
||||
|
||||
REDOPIMCACHE();
|
||||
|
||||
for(int i = 0; i < 9; i++)
|
||||
{
|
||||
MPR[i] = 0;
|
||||
FastPageR[i] = 0;
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
|
||||
// TimerSync() doesn't call CalcNextEvent(), so we'll need to call it some time after TimerSync() (TimerSync() is
|
||||
// used in HappySync(), TimerRead(), and TimerWrite().
|
||||
void HuC6280::TimerSync(void)
|
||||
{
|
||||
int32 clocks = timestamp - timer_lastts;
|
||||
|
||||
timer_div -= clocks;
|
||||
|
||||
while(timer_div <= 0)
|
||||
{
|
||||
int32 reload_div = 1024 * 3;
|
||||
|
||||
if(timer_inreload)
|
||||
{
|
||||
timer_value = timer_load;
|
||||
reload_div = reload_div - 1; //1023;
|
||||
timer_inreload = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(timer_status)
|
||||
{
|
||||
timer_value --;
|
||||
if(timer_value < 0)
|
||||
{
|
||||
timer_inreload = true;
|
||||
reload_div = 1;
|
||||
IRQBegin(IQTIMER);
|
||||
}
|
||||
}
|
||||
}
|
||||
timer_div += reload_div;
|
||||
}
|
||||
|
||||
timer_lastts = timestamp;
|
||||
}
|
||||
|
||||
void HuC6280::HappySync(void)
|
||||
{
|
||||
TimerSync();
|
||||
|
||||
if(next_user_event <= 0)
|
||||
next_user_event = EventHandler(timestamp);
|
||||
|
||||
CalcNextEvent();
|
||||
}
|
||||
|
||||
template<bool DebugMode>
|
||||
NO_INLINE void HuC6280::RunSub(void)
|
||||
{
|
||||
uint32 old_PC;
|
||||
|
||||
if(in_block_move)
|
||||
{
|
||||
IBM_Dispatch: ;
|
||||
switch(in_block_move)
|
||||
{
|
||||
default:
|
||||
case IBM_TIA: goto continue_the_TIA;
|
||||
case IBM_TAI: goto continue_the_TAI;
|
||||
case IBM_TDD: goto continue_the_TDD;
|
||||
case IBM_TII: goto continue_the_TII;
|
||||
case IBM_TIN: goto continue_the_TIN;
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if(DebugMode)
|
||||
old_PC = PC;
|
||||
|
||||
if(DebugMode && CPUHook)
|
||||
{
|
||||
TimerSync();
|
||||
CalcNextEvent();
|
||||
if(CPUHook(PC))
|
||||
{
|
||||
if(in_block_move)
|
||||
goto IBM_Dispatch;
|
||||
}
|
||||
}
|
||||
|
||||
if(IRQSample | IRQlow)
|
||||
{
|
||||
if(MDFN_UNLIKELY(IRQSample & IQRESET))
|
||||
{
|
||||
speed = 0;
|
||||
REDOSPEEDCACHE();
|
||||
|
||||
IRQMask = 7;
|
||||
SetMPR(7, 0);
|
||||
PC=RdMem(0xFFFE);
|
||||
PC|=RdMem(0xFFFF)<<8;
|
||||
P=I_FLAG;
|
||||
REDOPIMCACHE();
|
||||
IRQSample &= ~IQRESET;
|
||||
IRQlow &= ~IQRESET;
|
||||
|
||||
if(DebugMode && ADDBT)
|
||||
ADDBT(old_PC, PC, 0xFFFE);
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 tmpa = 0;
|
||||
|
||||
if((IRQlow & IQTIMER & IRQMask) && !IFlagSample) //IRQSample & IQTIMER)
|
||||
tmpa = 0xFFFA;
|
||||
else if(IRQSample & IQIRQ1)
|
||||
tmpa = 0xFFF8;
|
||||
else if(IRQSample & IQIRQ2)
|
||||
tmpa = 0xFFF6;
|
||||
//else
|
||||
// puts("DANGER WILL ROBINSON DANGER");
|
||||
|
||||
//printf("IRQ: %04x\n", tmpa);
|
||||
|
||||
if(tmpa)
|
||||
{
|
||||
// Total: 8 cycles(7 ADDCYC(1), 1 LASTCYCLE)
|
||||
|
||||
ADDCYC(1); // Cycle 1
|
||||
RdMem(PC); // Dummy read
|
||||
|
||||
ADDCYC(1); // Cycle 2
|
||||
RdMem(PC + 1); // Dummy read
|
||||
|
||||
ADDCYC(1); // Cycle 3
|
||||
PUSH(PC>>8); // Push PCH
|
||||
|
||||
ADDCYC(1); // Cycle 4
|
||||
PUSH(PC); // Push PCL
|
||||
|
||||
ADDCYC(1); // Cycle 5
|
||||
PUSH((P & ~B_FLAG)); // Push P
|
||||
P |= I_FLAG;
|
||||
REDOPIMCACHE();
|
||||
P &= ~(T_FLAG | D_FLAG);
|
||||
|
||||
ADDCYC(1); // Cycle 6
|
||||
PC=RdMem(tmpa); // Fetch vector PCL
|
||||
|
||||
ADDCYC(1); // Cycle 7
|
||||
PC|=RdMem(tmpa + 1) << 8; // Fect vector PCH
|
||||
|
||||
LastCycle(); // Cycle 8(internal operation?)
|
||||
|
||||
if(DebugMode && ADDBT)
|
||||
ADDBT(old_PC, PC, tmpa);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
PC &= 0xFFFF; // Our cpu core can only handle PC going about 8192 bytes over, so make sure it never gets that far...
|
||||
|
||||
lastop = RdOp(PC);
|
||||
|
||||
PC++;
|
||||
|
||||
switch(lastop)
|
||||
{
|
||||
#include "huc6280_ops.inc"
|
||||
}
|
||||
|
||||
P &= ~T_FLAG;
|
||||
skip_T_flag_clear:; // goto'd by the SET code
|
||||
} while(MDFN_LIKELY(runrunrun > 0));
|
||||
}
|
||||
|
||||
void HuC6280::Run(const bool StepMode)
|
||||
{
|
||||
if(StepMode)
|
||||
runrunrun = -1; // Needed so a BMT isn't interrupted.
|
||||
else
|
||||
runrunrun = 1;
|
||||
|
||||
if(CPUHook || ADDBT)
|
||||
RunSub<true>();
|
||||
else
|
||||
RunSub<false>();
|
||||
}
|
||||
|
||||
uint8 HuC6280::TimerRead(unsigned int address, bool peek)
|
||||
{
|
||||
if(!peek)
|
||||
{
|
||||
TimerSync();
|
||||
CalcNextEvent();
|
||||
}
|
||||
|
||||
return(timer_value | (IODataBuffer & 0x80));
|
||||
}
|
||||
|
||||
void HuC6280::TimerWrite(unsigned int address, uint8 V)
|
||||
{
|
||||
TimerSync();
|
||||
|
||||
switch(address & 1)
|
||||
{
|
||||
case 0: timer_load = (V & 0x7F); break;
|
||||
case 1: if(V & 1) // Enable counter
|
||||
{
|
||||
if(timer_status == 0)
|
||||
{
|
||||
//if(timer_inreload)
|
||||
// puts("Oops");
|
||||
timer_div = 1024 * 3;
|
||||
timer_value = timer_load;
|
||||
}
|
||||
}
|
||||
timer_status = V & 1;
|
||||
break;
|
||||
}
|
||||
|
||||
CalcNextEvent();
|
||||
}
|
||||
|
||||
|
||||
uint8 HuC6280::IRQStatusRead(unsigned int address, bool peek)
|
||||
{
|
||||
if(!(address & 2))
|
||||
return(IODataBuffer);
|
||||
|
||||
switch(address & 1)
|
||||
{
|
||||
case 0:
|
||||
if(!peek)
|
||||
IRQEnd(IQTIMER);
|
||||
return(IRQMask ^ 0x7);
|
||||
|
||||
case 1:
|
||||
{
|
||||
int status = 0;
|
||||
if(IRQlow & IQIRQ1) status |= 2;
|
||||
if(IRQlow & IQIRQ2) status |= 1;
|
||||
if(IRQlow & IQTIMER) status |= 4;
|
||||
return(status | (IODataBuffer & ~(1 | 2 | 4)));
|
||||
}
|
||||
}
|
||||
return(IODataBuffer);
|
||||
}
|
||||
|
||||
void HuC6280::IRQStatusWrite(unsigned int address, uint8 V)
|
||||
{
|
||||
if(!(address & 2))
|
||||
return;
|
||||
|
||||
switch(address & 1)
|
||||
{
|
||||
case 0: IRQMask = (V & 0x7) ^ 0x7;
|
||||
break;
|
||||
|
||||
case 1: IRQEnd(IQTIMER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int HuC6280::StateAction(StateMem *sm, const unsigned load, const bool data_only)
|
||||
{
|
||||
uint16 tmp_PC = PC;
|
||||
|
||||
SFORMAT StateRegs[]=
|
||||
{
|
||||
SFVAR(runrunrun), // For the benefit of save states while in step mode in the debugger.
|
||||
|
||||
SFVARN(tmp_PC, "PC"),
|
||||
SFVARN(A, "A"),
|
||||
SFVARN(P, "P"),
|
||||
SFVARN(IFlagSample, "IFlagSample"),
|
||||
SFVARN(X, "X"),
|
||||
SFVARN(Y, "Y"),
|
||||
SFVARN(S, "S"),
|
||||
|
||||
SFVARN(lastop, "lastop"),
|
||||
|
||||
SFVARN(IRQSample, "IRQSample"),
|
||||
SFVARN(IRQlow, "IRQlow"),
|
||||
SFVARN(IRQMask, "IRQMask"),
|
||||
SFARRAYN(MPR, 8, "MPR"),
|
||||
SFVARN(speed, "speed"),
|
||||
|
||||
SFVARN(timer_inreload, "timer_inreload"),
|
||||
SFVARN(timer_status, "timer_status"),
|
||||
SFVARN(timer_value, "timer_value"),
|
||||
SFVARN(timer_load, "timer_load"),
|
||||
SFVARN(timer_div, "timer_div"),
|
||||
|
||||
SFVARN(in_block_move, "IBM"),
|
||||
SFVARN(bmt_src, "IBM_SRC"),
|
||||
SFVARN(bmt_dest, "IBM_DEST"),
|
||||
SFVARN(bmt_length, "IBM_LENGTH"),
|
||||
SFVARN(bmt_alternate, "IBM_ALTERNATE"),
|
||||
|
||||
SFVARN(next_event, "next_event"),
|
||||
SFVARN(next_user_event, "next_user_event"),
|
||||
|
||||
SFVAR(IODataBuffer),
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "CPU", false);
|
||||
|
||||
if(load)
|
||||
{
|
||||
speed &= 0x01;
|
||||
if(timer_div < 1)
|
||||
timer_div = 1;
|
||||
//
|
||||
PC = tmp_PC;
|
||||
|
||||
// Update MPR cache
|
||||
FlushMPRCache();
|
||||
REDOSPEEDCACHE();
|
||||
REDOPIMCACHE();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
314
mednafen/pce/huc6280.h
Normal file
314
mednafen/pce/huc6280.h
Normal file
@ -0,0 +1,314 @@
|
||||
#ifndef __MDFN_PCE_HUC6280_H
|
||||
#define __MDFN_PCE_HUC6280_H
|
||||
|
||||
class alignas(128) HuC6280
|
||||
{
|
||||
public:
|
||||
|
||||
typedef void (MDFN_FASTCALL *writefunc)(uint32 A, uint8 V);
|
||||
typedef uint8 (MDFN_FASTCALL *readfunc)(uint32 A);
|
||||
typedef int32 (MDFN_FASTCALL *ehfunc)(const int32 timestamp);
|
||||
|
||||
enum { N_FLAG = 0x80 };
|
||||
enum { V_FLAG = 0x40 };
|
||||
enum { T_FLAG = 0x20 };
|
||||
enum { B_FLAG = 0x10 };
|
||||
enum { D_FLAG = 0x08 };
|
||||
enum { I_FLAG = 0x04 };
|
||||
enum { Z_FLAG = 0x02 };
|
||||
enum { C_FLAG = 0x01 };
|
||||
|
||||
// If emulate_wai is true, then the "0xCB" opcode will be handled by waiting for the next high-level event, NOT
|
||||
// for the IRQ line to be asserted as on a 65816.
|
||||
// It's mainly a hack intended for less CPU-intensive HES playback.
|
||||
HuC6280() MDFN_COLD;
|
||||
~HuC6280() MDFN_COLD;
|
||||
|
||||
void Init(const bool emulate_wai) MDFN_COLD;
|
||||
|
||||
void Reset(void) MDFN_COLD;
|
||||
void Power(void) MDFN_COLD;
|
||||
|
||||
enum { IQIRQ1 = 0x002 };
|
||||
enum { IQIRQ2 = 0x001 };
|
||||
enum { IQTIMER = 0x004 };
|
||||
enum { IQRESET = 0x020 };
|
||||
|
||||
void INLINE IRQBegin(int w)
|
||||
{
|
||||
IRQlow |= w;
|
||||
}
|
||||
|
||||
void INLINE IRQEnd(int w)
|
||||
{
|
||||
IRQlow &= ~w;
|
||||
}
|
||||
|
||||
void TimerSync(void);
|
||||
|
||||
uint8 INLINE GetIODataBuffer(void)
|
||||
{
|
||||
return(IODataBuffer);
|
||||
}
|
||||
|
||||
void INLINE SetIODataBuffer(uint8 v)
|
||||
{
|
||||
IODataBuffer = v;
|
||||
}
|
||||
|
||||
uint8 TimerRead(unsigned int address, bool peek = false);
|
||||
void TimerWrite(unsigned int address, uint8 V);
|
||||
|
||||
uint8 IRQStatusRead(unsigned int address, bool peek = false);
|
||||
void IRQStatusWrite(unsigned int address, uint8 V);
|
||||
|
||||
int StateAction(StateMem *sm, const unsigned load, const bool data_only);
|
||||
|
||||
template<bool DebugMode>
|
||||
void RunSub(void) NO_INLINE;
|
||||
|
||||
void Run(const bool StepMode = false);
|
||||
|
||||
void INLINE Exit(void)
|
||||
{
|
||||
runrunrun = 0;
|
||||
}
|
||||
|
||||
void INLINE SyncAndResetTimestamp(uint32 ts_base = 0)
|
||||
{
|
||||
TimerSync();
|
||||
|
||||
timer_lastts = ts_base;
|
||||
timestamp = ts_base;
|
||||
}
|
||||
|
||||
bool INLINE InBlockMove(void)
|
||||
{
|
||||
return(in_block_move);
|
||||
}
|
||||
|
||||
void StealCycle(void);
|
||||
void StealCycles(const int count);
|
||||
void StealMasterCycles(const int count);
|
||||
|
||||
void NO_INLINE SetEvent(const int32 cycles)
|
||||
{
|
||||
next_user_event = cycles;
|
||||
CalcNextEvent();
|
||||
}
|
||||
|
||||
void INLINE SetEventHandler(ehfunc new_EventHandler)
|
||||
{
|
||||
EventHandler = new_EventHandler;
|
||||
}
|
||||
|
||||
uint32 INLINE Timestamp(void)
|
||||
{
|
||||
return(timestamp);
|
||||
}
|
||||
|
||||
void INLINE SetFastRead(unsigned int i, uint8 *ptr)
|
||||
{
|
||||
assert(i < 0x100);
|
||||
FastMap[i] = ptr;
|
||||
}
|
||||
|
||||
readfunc INLINE GetReadHandler(unsigned int i)
|
||||
{
|
||||
assert(i < 0x100);
|
||||
return(ReadMap[i]);
|
||||
}
|
||||
|
||||
|
||||
void INLINE SetReadHandler(unsigned int i, readfunc func)
|
||||
{
|
||||
assert(i < 0x100);
|
||||
ReadMap[i] = func;
|
||||
}
|
||||
|
||||
void INLINE SetWriteHandler(unsigned int i, writefunc func)
|
||||
{
|
||||
assert(i < 0x100);
|
||||
WriteMap[i] = func;
|
||||
}
|
||||
|
||||
// If external debugging code uses this function, then SetFastRead() must not be used with a pointer other than NULL for it to work
|
||||
// properly.
|
||||
uint32 INLINE GetLastLogicalReadAddr(void)
|
||||
{
|
||||
return(LastLogicalReadAddr);
|
||||
}
|
||||
|
||||
uint32 INLINE GetLastLogicalWriteAddr(void)
|
||||
{
|
||||
return(LastLogicalWriteAddr);
|
||||
}
|
||||
|
||||
private:
|
||||
void FlushMPRCache(void);
|
||||
|
||||
void INLINE SetMPR(int i, int v)
|
||||
{
|
||||
MPR[i] = v;
|
||||
FastPageR[i] = FastMap[v] ? ((uintptr_t)FastMap[v] - i * 8192) : 0;
|
||||
}
|
||||
|
||||
|
||||
// Logical
|
||||
uint8 INLINE RdMem(unsigned int address)
|
||||
{
|
||||
if(FastPageR[address >> 13])
|
||||
return *(uint8*)(FastPageR[address >> 13] + address);
|
||||
|
||||
LastLogicalReadAddr = address;
|
||||
|
||||
uint8 wmpr = MPR[address >> 13];
|
||||
return ReadMap[wmpr]((wmpr << 13) | (address & 0x1FFF));
|
||||
}
|
||||
|
||||
// Logical
|
||||
uint8 INLINE RdOp(unsigned int address)
|
||||
{
|
||||
return RdMem(address);
|
||||
}
|
||||
|
||||
// Logical
|
||||
void INLINE WrMem(unsigned int address, uint8 V)
|
||||
{
|
||||
uint8 wmpr = MPR[address >> 13];
|
||||
|
||||
LastLogicalWriteAddr = address;
|
||||
|
||||
WriteMap[wmpr]((wmpr << 13) | (address & 0x1FFF), V);
|
||||
}
|
||||
|
||||
// Used for ST0, ST1, ST2
|
||||
// Must not modify address(upper bit is abused for ST0/ST1/ST2 handling).
|
||||
void INLINE WrMemPhysical(uint32 address, uint8 data)
|
||||
{
|
||||
WriteMap[(address >> 13) & 0xFF](address, data);
|
||||
}
|
||||
|
||||
void INLINE REDOPIMCACHE(void)
|
||||
{
|
||||
PIMaskCache = (P & I_FLAG) ? 0 : ~0;
|
||||
}
|
||||
|
||||
void INLINE REDOSPEEDCACHE(void)
|
||||
{
|
||||
speed_shift_cache = (speed ^ 1) << 1;
|
||||
}
|
||||
|
||||
void LastCycle(void);
|
||||
|
||||
void INLINE ADDCYC(int x)
|
||||
{
|
||||
int master = (x * 3) << speed_shift_cache;
|
||||
|
||||
timestamp += master;
|
||||
next_event -= master;
|
||||
next_user_event -= master;
|
||||
|
||||
if(next_event <= 0)
|
||||
HappySync();
|
||||
}
|
||||
|
||||
void INLINE ADDCYC_MASTER(int master)
|
||||
{
|
||||
timestamp += master;
|
||||
next_event -= master;
|
||||
next_user_event -= master;
|
||||
|
||||
if(next_event <= 0)
|
||||
HappySync();
|
||||
}
|
||||
|
||||
|
||||
void HappySync(void);
|
||||
|
||||
void INLINE CalcNextEvent(void)
|
||||
{
|
||||
next_event = timer_div;
|
||||
|
||||
if(next_event > next_user_event)
|
||||
next_event = next_user_event;
|
||||
}
|
||||
|
||||
void X_ZN(const uint8);
|
||||
void X_ZNT(const uint8);
|
||||
|
||||
void PUSH(const uint8 V);
|
||||
uint8 POP(void);
|
||||
|
||||
template<bool DebugMode>
|
||||
void JR(const bool cond, const bool BBRS = false);
|
||||
|
||||
template<bool DebugMode>
|
||||
void BBRi(const uint8 val, const unsigned int bitto);
|
||||
|
||||
template<bool DebugMode>
|
||||
void BBSi(const uint8 val, const unsigned int bitto);
|
||||
|
||||
private:
|
||||
uint32 timestamp;
|
||||
int32 next_event; // Next event, period. Timer, user, ALIENS ARE INVADING SAVE ME HELP
|
||||
int32 next_user_event;
|
||||
int32 timer_lastts;
|
||||
ehfunc EventHandler;
|
||||
|
||||
uint32 PC; // Program Counter(16-bit, but as a 32-bit variable for performance reasons)
|
||||
uint8 A; // Accumulator
|
||||
uint8 X; // X Index register
|
||||
uint8 Y; // Y Indes register
|
||||
uint8 S; // Stack Pointer
|
||||
uint8 P; // Processor Flags/Status Register
|
||||
uint8 IRQMask;
|
||||
uint32 PIMaskCache; // Will be = 0 if (P & I_FLAG) is set, ~0 if (P & I_FLAG) is clear.
|
||||
uint32 IRQlow; /* Simulated IRQ pin held low(or is it high?).
|
||||
And other junk hooked on for speed reasons.*/
|
||||
int32 IRQSample;
|
||||
int32 IFlagSample;
|
||||
uint8 MPR[9]; // 8, + 1 for PC overflow from $ffff to $10000
|
||||
|
||||
uint8 lastop;
|
||||
uint8 speed;
|
||||
uint8 speed_shift_cache;
|
||||
|
||||
uint8 IODataBuffer;
|
||||
|
||||
bool timer_inreload;
|
||||
uint8 timer_status;
|
||||
int32 timer_value, timer_load;
|
||||
int32 timer_div;
|
||||
|
||||
int32 runrunrun; // Don't change to bool(main possibles values are -1, 0, 1).
|
||||
|
||||
enum
|
||||
{
|
||||
IBM_TIA = 1,
|
||||
IBM_TAI = 2,
|
||||
IBM_TDD = 3,
|
||||
IBM_TII = 4,
|
||||
IBM_TIN = 5
|
||||
};
|
||||
uint32 in_block_move;
|
||||
uint32 bmt_alternate;
|
||||
uint16 bmt_src, bmt_dest, bmt_length;
|
||||
|
||||
uint32 LastLogicalReadAddr; // Some helper variables for debugging code(external)
|
||||
uint32 LastLogicalWriteAddr; // to know where the read/write occurred in the 16-bit logical space.
|
||||
|
||||
uintptr_t FastPageR[9]; // Biased fast page read cache for each 8KiB in the 16-bit logical address space
|
||||
// (Reloaded on corresponding MPR change)
|
||||
|
||||
uint8 *FastMap[0x100]; // Direct pointers to memory for mapped RAM and ROM for faster reads.
|
||||
readfunc ReadMap[0x100]; // Read handler pointers for each 8KiB in the 21-bit physical address space.
|
||||
writefunc WriteMap[0x100]; // Write handler pointers for each 8KiB in the 21-bit physical address space.
|
||||
|
||||
bool (*CPUHook)(uint32);
|
||||
void (*ADDBT)(uint32, uint32, uint32);
|
||||
|
||||
bool EmulateWAI; // For speed hacks
|
||||
};
|
||||
|
||||
#endif
|
558
mednafen/pce/huc6280_ops.inc
Normal file
558
mednafen/pce/huc6280_ops.inc
Normal file
@ -0,0 +1,558 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
case 0x00: /* BRK */
|
||||
PC++;
|
||||
P &= ~T_FLAG;
|
||||
PUSH(PC >> 8);
|
||||
PUSH(PC);
|
||||
PUSH(P | B_FLAG);
|
||||
|
||||
P |= I_FLAG;
|
||||
REDOPIMCACHE();
|
||||
|
||||
P &= ~D_FLAG;
|
||||
|
||||
PC = RdOp(0xFFF6);
|
||||
PC |= RdOp(0xFFF7) << 8;
|
||||
|
||||
if(DebugMode && ADDBT)
|
||||
ADDBT(old_PC, PC, 0);
|
||||
|
||||
ADDCYC(7);
|
||||
LastCycle();
|
||||
break;
|
||||
|
||||
case 0x40: /* RTI */
|
||||
P = POP();
|
||||
REDOPIMCACHE();
|
||||
PC = POP();
|
||||
PC |= POP() << 8;
|
||||
|
||||
if(DebugMode && ADDBT)
|
||||
ADDBT(old_PC, PC, 0);
|
||||
|
||||
ADDCYC(6);
|
||||
LastCycle();
|
||||
|
||||
goto skip_T_flag_clear;
|
||||
|
||||
break;
|
||||
|
||||
case 0x60: /* RTS */
|
||||
PC = POP();
|
||||
PC |= POP() << 8;
|
||||
PC++;
|
||||
|
||||
if(DebugMode && ADDBT)
|
||||
ADDBT(old_PC, PC, 0);
|
||||
|
||||
ADDCYC(6);
|
||||
LastCycle();
|
||||
break;
|
||||
|
||||
case 0x48: /* PHA */
|
||||
ADDCYC(2);
|
||||
LastCycle();
|
||||
PUSH(A);
|
||||
break;
|
||||
|
||||
case 0x08: /* PHP */
|
||||
ADDCYC(2);
|
||||
LastCycle();
|
||||
P &= ~T_FLAG;
|
||||
PUSH(P | B_FLAG);
|
||||
break;
|
||||
|
||||
case 0xDA: // PHX 65C02
|
||||
ADDCYC(2);
|
||||
LastCycle();
|
||||
PUSH(X);
|
||||
break;
|
||||
|
||||
case 0x5A: // PHY 65C02
|
||||
ADDCYC(2);
|
||||
LastCycle();
|
||||
PUSH(Y);
|
||||
break;
|
||||
|
||||
case 0x68: /* PLA */
|
||||
ADDCYC(3);
|
||||
LastCycle();
|
||||
A = POP();
|
||||
X_ZN(A);
|
||||
break;
|
||||
|
||||
case 0xFA: // PLX 65C02
|
||||
ADDCYC(3);
|
||||
LastCycle();
|
||||
X = POP();
|
||||
X_ZN(X);
|
||||
break;
|
||||
|
||||
case 0x7A: // PLY 65C02
|
||||
ADDCYC(3);
|
||||
LastCycle();
|
||||
Y = POP();
|
||||
X_ZN(Y);
|
||||
break;
|
||||
|
||||
case 0x28: /* PLP */
|
||||
ADDCYC(3);
|
||||
LastCycle();
|
||||
P = POP();
|
||||
REDOPIMCACHE();
|
||||
|
||||
goto skip_T_flag_clear;
|
||||
|
||||
break;
|
||||
|
||||
case 0x4C: /* JMP ABSOLUTE */
|
||||
{
|
||||
uint16 ptmp = PC;
|
||||
unsigned int npc;
|
||||
|
||||
npc = RdOp(ptmp);
|
||||
ptmp++;
|
||||
npc |= RdOp(ptmp)<<8;
|
||||
PC = npc;
|
||||
|
||||
if(DebugMode && ADDBT)
|
||||
ADDBT(old_PC, PC, 0);
|
||||
|
||||
ADDCYC(3);
|
||||
LastCycle();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x6C: /* JMP Indirect */
|
||||
{
|
||||
uint32 tmp;
|
||||
GetAB(tmp);
|
||||
PC = RdMem(tmp);
|
||||
PC |= RdMem(tmp + 1) << 8;
|
||||
|
||||
if(DebugMode && ADDBT)
|
||||
ADDBT(old_PC, PC, 0);
|
||||
|
||||
ADDCYC(6);
|
||||
LastCycle();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7C: // JMP Indirect X - 65C02
|
||||
{
|
||||
uint32 tmp;
|
||||
GetAB(tmp);
|
||||
tmp += X;
|
||||
|
||||
PC = RdMem(tmp);
|
||||
PC |= RdMem(tmp + 1) << 8;
|
||||
|
||||
if(DebugMode && ADDBT)
|
||||
ADDBT(old_PC, PC, 0);
|
||||
|
||||
ADDCYC(6);
|
||||
LastCycle();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x20: /* JSR */
|
||||
{
|
||||
uint8 npc;
|
||||
npc=RdOp(PC);
|
||||
PC++;
|
||||
PUSH(PC >> 8);
|
||||
PUSH(PC);
|
||||
PC = RdOp(PC) << 8;
|
||||
PC |= npc;
|
||||
|
||||
if(DebugMode && ADDBT)
|
||||
ADDBT(old_PC, PC, 0);
|
||||
|
||||
ADDCYC(6);
|
||||
LastCycle();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xAA: IMP(TAX);
|
||||
|
||||
case 0x8A: IMP(TXA);
|
||||
|
||||
case 0xA8: IMP(TAY);
|
||||
|
||||
case 0x98: IMP(TYA);
|
||||
|
||||
case 0xBA: IMP(TSX);
|
||||
|
||||
case 0x9A: IMP(TXS);
|
||||
|
||||
case 0xCA: IMP(DEX);
|
||||
|
||||
case 0x88: IMP(DEY);
|
||||
|
||||
case 0xE8: IMP(INX);
|
||||
|
||||
case 0xC8: IMP(INY);
|
||||
|
||||
case 0x54: IMP(CSL);
|
||||
case 0xD4: IMP(CSH);
|
||||
|
||||
#define OP_CLEARR(r) { ADDCYC(1); LastCycle(); r = 0; break; }
|
||||
|
||||
case 0x62: OP_CLEARR(A); // CLA
|
||||
case 0x82: OP_CLEARR(X); // CLX
|
||||
case 0xC2: OP_CLEARR(Y); // CLY
|
||||
|
||||
// The optional argument(s) will run at the end, immediately before the break.
|
||||
#define OP_CLEARF(f, ...) { ADDCYC(1); LastCycle(); P &= ~f; __VA_ARGS__ break; }
|
||||
#define OP_SETF(f, ...) { ADDCYC(1); LastCycle(); P |= f; __VA_ARGS__ break; }
|
||||
|
||||
case 0x18: /* CLC */
|
||||
OP_CLEARF(C_FLAG);
|
||||
|
||||
case 0xD8: /* CLD */
|
||||
OP_CLEARF(D_FLAG);
|
||||
|
||||
case 0x58: /* CLI */
|
||||
OP_CLEARF(I_FLAG, REDOPIMCACHE(););
|
||||
|
||||
case 0xB8: /* CLV */
|
||||
OP_CLEARF(V_FLAG);
|
||||
|
||||
case 0x38: /* SEC */
|
||||
OP_SETF(C_FLAG);
|
||||
|
||||
case 0xF8: /* SED */
|
||||
OP_SETF(D_FLAG);
|
||||
|
||||
case 0x78: /* SEI */
|
||||
OP_SETF(I_FLAG, REDOPIMCACHE(););
|
||||
|
||||
case 0xF4: /* SET */
|
||||
//puts("SET");
|
||||
ADDCYC(1);
|
||||
LastCycle();
|
||||
P |= T_FLAG;
|
||||
|
||||
goto skip_T_flag_clear;
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 0xEA: /* NOP */
|
||||
ADDCYC(1);
|
||||
LastCycle();
|
||||
break;
|
||||
|
||||
case 0x0A: RMW_A(ASL);
|
||||
case 0x06: RMW_ZP(ASL);
|
||||
case 0x16: RMW_ZPX(ASL);
|
||||
case 0x0E: RMW_AB(ASL);
|
||||
case 0x1E: RMW_ABX(ASL);
|
||||
|
||||
case 0x3A: RMW_A(DEC);
|
||||
case 0xC6: RMW_ZP(DEC);
|
||||
case 0xD6: RMW_ZPX(DEC);
|
||||
case 0xCE: RMW_AB(DEC);
|
||||
case 0xDE: RMW_ABX(DEC);
|
||||
|
||||
case 0x1A: RMW_A(INC); // 65C02
|
||||
case 0xE6: RMW_ZP(INC);
|
||||
case 0xF6: RMW_ZPX(INC);
|
||||
case 0xEE: RMW_AB(INC);
|
||||
case 0xFE: RMW_ABX(INC);
|
||||
|
||||
case 0x4A: RMW_A(LSR);
|
||||
case 0x46: RMW_ZP(LSR);
|
||||
case 0x56: RMW_ZPX(LSR);
|
||||
case 0x4E: RMW_AB(LSR);
|
||||
case 0x5E: RMW_ABX(LSR);
|
||||
|
||||
case 0x2A: RMW_A(ROL);
|
||||
case 0x26: RMW_ZP(ROL);
|
||||
case 0x36: RMW_ZPX(ROL);
|
||||
case 0x2E: RMW_AB(ROL);
|
||||
case 0x3E: RMW_ABX(ROL);
|
||||
|
||||
case 0x6A: RMW_A(ROR);
|
||||
case 0x66: RMW_ZP(ROR);
|
||||
case 0x76: RMW_ZPX(ROR);
|
||||
case 0x6E: RMW_AB(ROR);
|
||||
case 0x7E: RMW_ABX(ROR);
|
||||
|
||||
case 0x69: LD_IM(ADC);
|
||||
case 0x65: LD_ZP(ADC);
|
||||
case 0x75: LD_ZPX(ADC);
|
||||
case 0x6D: LD_AB(ADC);
|
||||
case 0x7D: LD_ABX(ADC);
|
||||
case 0x79: LD_ABY(ADC);
|
||||
case 0x72: LD_IND(ADC);
|
||||
case 0x61: LD_IX(ADC);
|
||||
case 0x71: LD_IY(ADC);
|
||||
|
||||
case 0x29: LD_IM(AND);
|
||||
case 0x25: LD_ZP(AND);
|
||||
case 0x35: LD_ZPX(AND);
|
||||
case 0x2D: LD_AB(AND);
|
||||
case 0x3D: LD_ABX(AND);
|
||||
case 0x39: LD_ABY(AND);
|
||||
case 0x32: LD_IND(AND);
|
||||
case 0x21: LD_IX(AND);
|
||||
case 0x31: LD_IY(AND);
|
||||
|
||||
case 0x89: LD_IM(BIT);
|
||||
case 0x24: LD_ZP(BIT);
|
||||
case 0x34: LD_ZPX(BIT);
|
||||
case 0x2C: LD_AB(BIT);
|
||||
case 0x3C: LD_ABX(BIT);
|
||||
|
||||
case 0xC9: LD_IM(CMP);
|
||||
case 0xC5: LD_ZP(CMP);
|
||||
case 0xD5: LD_ZPX(CMP);
|
||||
case 0xCD: LD_AB(CMP);
|
||||
case 0xDD: LD_ABX(CMP);
|
||||
case 0xD9: LD_ABY(CMP);
|
||||
case 0xD2: LD_IND(CMP);
|
||||
case 0xC1: LD_IX(CMP);
|
||||
case 0xD1: LD_IY(CMP);
|
||||
|
||||
case 0xE0: LD_IM(CPX);
|
||||
case 0xE4: LD_ZP(CPX);
|
||||
case 0xEC: LD_AB(CPX);
|
||||
|
||||
case 0xC0: LD_IM(CPY);
|
||||
case 0xC4: LD_ZP(CPY);
|
||||
case 0xCC: LD_AB(CPY);
|
||||
|
||||
case 0x49: LD_IM(EOR);
|
||||
case 0x45: LD_ZP(EOR);
|
||||
case 0x55: LD_ZPX(EOR);
|
||||
case 0x4D: LD_AB(EOR);
|
||||
case 0x5D: LD_ABX(EOR);
|
||||
case 0x59: LD_ABY(EOR);
|
||||
case 0x52: LD_IND(EOR);
|
||||
case 0x41: LD_IX(EOR);
|
||||
case 0x51: LD_IY(EOR);
|
||||
|
||||
case 0xA9: LD_IM(LDA);
|
||||
case 0xA5: LD_ZP(LDA);
|
||||
case 0xB5: LD_ZPX(LDA);
|
||||
case 0xAD: LD_AB(LDA);
|
||||
case 0xBD: LD_ABX(LDA);
|
||||
case 0xB9: LD_ABY(LDA);
|
||||
case 0xB2: LD_IND(LDA);
|
||||
case 0xA1: LD_IX(LDA);
|
||||
case 0xB1: LD_IY(LDA);
|
||||
|
||||
case 0xA2: LD_IM(LDX);
|
||||
case 0xA6: LD_ZP(LDX);
|
||||
case 0xB6: LD_ZPY(LDX);
|
||||
case 0xAE: LD_AB(LDX);
|
||||
case 0xBE: LD_ABY(LDX);
|
||||
|
||||
case 0xA0: LD_IM(LDY);
|
||||
case 0xA4: LD_ZP(LDY);
|
||||
case 0xB4: LD_ZPX(LDY);
|
||||
case 0xAC: LD_AB(LDY);
|
||||
case 0xBC: LD_ABX(LDY);
|
||||
|
||||
case 0x09: LD_IM(ORA);
|
||||
case 0x05: LD_ZP(ORA);
|
||||
case 0x15: LD_ZPX(ORA);
|
||||
case 0x0D: LD_AB(ORA);
|
||||
case 0x1D: LD_ABX(ORA);
|
||||
case 0x19: LD_ABY(ORA);
|
||||
case 0x12: LD_IND(ORA);
|
||||
case 0x01: LD_IX(ORA);
|
||||
case 0x11: LD_IY(ORA);
|
||||
|
||||
case 0xE9: LD_IM(SBC);
|
||||
case 0xE5: LD_ZP(SBC);
|
||||
case 0xF5: LD_ZPX(SBC);
|
||||
case 0xED: LD_AB(SBC);
|
||||
case 0xFD: LD_ABX(SBC);
|
||||
case 0xF9: LD_ABY(SBC);
|
||||
case 0xF2: LD_IND(SBC);
|
||||
case 0xE1: LD_IX(SBC);
|
||||
case 0xF1: LD_IY(SBC);
|
||||
|
||||
case 0x85: ST_ZP(A);
|
||||
case 0x95: ST_ZPX(A);
|
||||
case 0x8D: ST_AB(A);
|
||||
case 0x9D: ST_ABX(A);
|
||||
case 0x99: ST_ABY(A);
|
||||
case 0x92: ST_IND(A);
|
||||
case 0x81: ST_IX(A);
|
||||
case 0x91: ST_IY(A);
|
||||
|
||||
case 0x86: ST_ZP(X);
|
||||
case 0x96: ST_ZPY(X);
|
||||
case 0x8E: ST_AB(X);
|
||||
|
||||
case 0x84: ST_ZP(Y);
|
||||
case 0x94: ST_ZPX(Y);
|
||||
case 0x8C: ST_AB(Y);
|
||||
|
||||
/* BBRi */
|
||||
case 0x0F: LD_ZP(BBRi<DebugMode>(x, 0));
|
||||
case 0x1F: LD_ZP(BBRi<DebugMode>(x, 1));
|
||||
case 0x2F: LD_ZP(BBRi<DebugMode>(x, 2));
|
||||
case 0x3F: LD_ZP(BBRi<DebugMode>(x, 3));
|
||||
case 0x4F: LD_ZP(BBRi<DebugMode>(x, 4));
|
||||
case 0x5F: LD_ZP(BBRi<DebugMode>(x, 5));
|
||||
case 0x6F: LD_ZP(BBRi<DebugMode>(x, 6));
|
||||
case 0x7F: LD_ZP(BBRi<DebugMode>(x, 7));
|
||||
|
||||
/* BBSi */
|
||||
case 0x8F: LD_ZP(BBSi<DebugMode>(x, 0));
|
||||
case 0x9F: LD_ZP(BBSi<DebugMode>(x, 1));
|
||||
case 0xAF: LD_ZP(BBSi<DebugMode>(x, 2));
|
||||
case 0xBF: LD_ZP(BBSi<DebugMode>(x, 3));
|
||||
case 0xCF: LD_ZP(BBSi<DebugMode>(x, 4));
|
||||
case 0xDF: LD_ZP(BBSi<DebugMode>(x, 5));
|
||||
case 0xEF: LD_ZP(BBSi<DebugMode>(x, 6));
|
||||
case 0xFF: LD_ZP(BBSi<DebugMode>(x, 7));
|
||||
|
||||
/* BRA */
|
||||
case 0x80: JR<DebugMode>(1); break;
|
||||
|
||||
/* BSR */
|
||||
case 0x44:
|
||||
{
|
||||
PUSH(PC >> 8);
|
||||
PUSH(PC);
|
||||
ADDCYC(4);
|
||||
JR<DebugMode>(1);
|
||||
}
|
||||
break;
|
||||
|
||||
/* BCC */
|
||||
case 0x90: JR<DebugMode>(!(P&C_FLAG)); break;
|
||||
|
||||
/* BCS */
|
||||
case 0xB0: JR<DebugMode>(P&C_FLAG); break;
|
||||
|
||||
/* BEQ */
|
||||
case 0xF0: JR<DebugMode>(P&Z_FLAG); break;
|
||||
|
||||
/* BNE */
|
||||
case 0xD0: JR<DebugMode>(!(P&Z_FLAG)); break;
|
||||
|
||||
/* BMI */
|
||||
case 0x30: JR<DebugMode>(P&N_FLAG); break;
|
||||
|
||||
/* BPL */
|
||||
case 0x10: JR<DebugMode>(!(P&N_FLAG)); break;
|
||||
|
||||
/* BVC */
|
||||
case 0x50: JR<DebugMode>(!(P&V_FLAG)); break;
|
||||
|
||||
/* BVS */
|
||||
case 0x70: JR<DebugMode>(P&V_FLAG); break;
|
||||
|
||||
|
||||
// RMB 65SC02
|
||||
case 0x07: RMW_ZP_B(RMB(0));
|
||||
case 0x17: RMW_ZP_B(RMB(1));
|
||||
case 0x27: RMW_ZP_B(RMB(2));
|
||||
case 0x37: RMW_ZP_B(RMB(3));
|
||||
case 0x47: RMW_ZP_B(RMB(4));
|
||||
case 0x57: RMW_ZP_B(RMB(5));
|
||||
case 0x67: RMW_ZP_B(RMB(6));
|
||||
case 0x77: RMW_ZP_B(RMB(7));
|
||||
|
||||
// SMB 65SC02
|
||||
case 0x87: RMW_ZP_B(SMB(0));
|
||||
case 0x97: RMW_ZP_B(SMB(1));
|
||||
case 0xa7: RMW_ZP_B(SMB(2));
|
||||
case 0xb7: RMW_ZP_B(SMB(3));
|
||||
case 0xc7: RMW_ZP_B(SMB(4));
|
||||
case 0xd7: RMW_ZP_B(SMB(5));
|
||||
case 0xe7: RMW_ZP_B(SMB(6));
|
||||
case 0xf7: RMW_ZP_B(SMB(7));
|
||||
|
||||
// STZ 65C02
|
||||
case 0x64: ST_ZP(0);
|
||||
case 0x74: ST_ZPX(0);
|
||||
case 0x9C: ST_AB(0);
|
||||
case 0x9E: ST_ABX(0);
|
||||
|
||||
// TRB 65SC02
|
||||
case 0x14: RMW_ZP(TRB);
|
||||
case 0x1C: RMW_AB(TRB);
|
||||
|
||||
// TSB 65SC02
|
||||
case 0x04: RMW_ZP(TSB);
|
||||
case 0x0C: RMW_AB(TSB);
|
||||
|
||||
// TST
|
||||
case 0x83: LD_IM_ZP(TST);
|
||||
case 0xA3: LD_IM_ZPX(TST);
|
||||
case 0x93: LD_IM_AB(TST);
|
||||
case 0xB3: LD_IM_ABX(TST);
|
||||
|
||||
case 0x02: IMP(SXY);
|
||||
case 0x22: IMP(SAX);
|
||||
case 0x42: IMP(SAY);
|
||||
|
||||
|
||||
|
||||
case 0x73: // TII
|
||||
LD_BMT(BMT_TII);
|
||||
|
||||
case 0xC3: // TDD
|
||||
LD_BMT(BMT_TDD);
|
||||
|
||||
case 0xD3: // TIN
|
||||
LD_BMT(BMT_TIN);
|
||||
|
||||
case 0xE3: // TIA
|
||||
LD_BMT(BMT_TIA);
|
||||
|
||||
case 0xF3: // TAI
|
||||
LD_BMT(BMT_TAI);
|
||||
|
||||
case 0x43: // TMAi
|
||||
LD_IM_COMPLEX(TMA);
|
||||
|
||||
case 0x53: // TAMi
|
||||
LD_IM_COMPLEX(TAM);
|
||||
|
||||
case 0x03: // ST0
|
||||
LD_IM_COMPLEX(ST0);
|
||||
|
||||
case 0x13: // ST1
|
||||
LD_IM_COMPLEX(ST1);
|
||||
|
||||
case 0x23: // ST2
|
||||
LD_IM_COMPLEX(ST2);
|
||||
|
||||
|
||||
case 0xCB:
|
||||
if(EmulateWAI)
|
||||
{
|
||||
if(next_event > 1)
|
||||
ADDCYC(next_event - 1);
|
||||
LastCycle();
|
||||
break;
|
||||
}
|
||||
default: //MDFN_printf("Bad %02x at $%04x\n", lastop, PC);
|
||||
ADDCYC(1);
|
||||
LastCycle();
|
||||
break;
|
||||
|
306
mednafen/pce/input.cpp
Normal file
306
mednafen/pce/input.cpp
Normal file
@ -0,0 +1,306 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "pce.h"
|
||||
#include "input.h"
|
||||
#include "huc.h"
|
||||
|
||||
#include "input/gamepad.h"
|
||||
#include "input/mouse.h"
|
||||
#include "input/tsushinkb.h"
|
||||
|
||||
enum
|
||||
{
|
||||
PCEINPUT_NONE = 0,
|
||||
PCEINPUT_GAMEPAD = 1,
|
||||
PCEINPUT_MOUSE = 2,
|
||||
PCEINPUT_TSUSHINKB = 3,
|
||||
};
|
||||
|
||||
//PCE_Input_Device::PCE_Input_Device(int which)
|
||||
//{
|
||||
//
|
||||
//}
|
||||
|
||||
PCE_Input_Device::~PCE_Input_Device()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PCE_Input_Device::Power(int32 timestamp)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PCE_Input_Device::TransformInput(uint8* data, const bool DisableSR)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PCE_Input_Device::AdjustTS(int32 delta)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PCE_Input_Device::Write(int32 timestamp, bool old_SEL, bool new_SEL, bool old_CLR, bool new_CLR)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint8 PCE_Input_Device::Read(int32 timestamp)
|
||||
{
|
||||
return(0xF);
|
||||
}
|
||||
|
||||
void PCE_Input_Device::Update(const void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int PCE_Input_Device::StateAction(StateMem *sm, int load, int data_only, const char *section_name)
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
|
||||
static PCE_Input_Device *devices[5] = { NULL };
|
||||
static bool MultiTapEnabled = true;
|
||||
static int InputTypes[5] = { 0 };
|
||||
static uint8 *data_ptr[5];
|
||||
static bool SEL, CLR;
|
||||
static uint8 read_index = 0;
|
||||
static bool DisableSR = false;
|
||||
|
||||
static void RemakeDevices(int which = -1)
|
||||
{
|
||||
int s = 0;
|
||||
int e = 5;
|
||||
|
||||
if(which != -1)
|
||||
{
|
||||
s = which;
|
||||
e = which + 1;
|
||||
}
|
||||
|
||||
for(int i = s; i < e; i++)
|
||||
{
|
||||
if(devices[i])
|
||||
delete devices[i];
|
||||
devices[i] = NULL;
|
||||
|
||||
switch(InputTypes[i])
|
||||
{
|
||||
default:
|
||||
case PCEINPUT_NONE: break;
|
||||
case PCEINPUT_GAMEPAD: devices[i] = PCEINPUT_MakeGamepad(); break;
|
||||
case PCEINPUT_MOUSE: devices[i] = PCEINPUT_MakeMouse(); break;
|
||||
case PCEINPUT_TSUSHINKB: devices[i] = PCEINPUT_MakeTsushinKB(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void SyncSettings(void);
|
||||
|
||||
void PCEINPUT_SettingChanged(const char *name)
|
||||
{
|
||||
SyncSettings();
|
||||
}
|
||||
|
||||
void PCEINPUT_Init(void)
|
||||
{
|
||||
SyncSettings();
|
||||
RemakeDevices();
|
||||
}
|
||||
|
||||
void PCEINPUT_TransformInput(void)
|
||||
{
|
||||
for(unsigned i = 0; i < 5; i++)
|
||||
{
|
||||
if(devices[i])
|
||||
devices[i]->TransformInput(data_ptr[i], DisableSR);
|
||||
}
|
||||
}
|
||||
|
||||
void PCEINPUT_SetInput(unsigned port, const char *type, uint8 *ptr)
|
||||
{
|
||||
assert(port < 5);
|
||||
|
||||
if(!strcasecmp(type, "gamepad"))
|
||||
InputTypes[port] = PCEINPUT_GAMEPAD;
|
||||
else if(!strcasecmp(type, "mouse"))
|
||||
InputTypes[port] = PCEINPUT_MOUSE;
|
||||
else if(!strcasecmp(type, "tsushinkb"))
|
||||
InputTypes[port] = PCEINPUT_TSUSHINKB;
|
||||
else
|
||||
InputTypes[port] = PCEINPUT_NONE;
|
||||
|
||||
data_ptr[port] = (uint8 *)ptr;
|
||||
|
||||
RemakeDevices(port);
|
||||
}
|
||||
|
||||
void INPUT_Frame(void)
|
||||
{
|
||||
for(int i = 0; i < 5; i++)
|
||||
if(devices[i])
|
||||
devices[i]->Update(data_ptr[i]);
|
||||
}
|
||||
|
||||
void INPUT_AdjustTS(int32 delta_timestamp)
|
||||
{
|
||||
for(int i = 0; i < 5; i++)
|
||||
if(devices[i])
|
||||
devices[i]->AdjustTS(delta_timestamp);
|
||||
}
|
||||
|
||||
static INLINE uint8 RealPortRead(int32 timestamp, int which)
|
||||
{
|
||||
uint8 ret = 0xF;
|
||||
|
||||
//printf("RealInputRead: %d %d\n", which, timestamp);
|
||||
if(devices[which])
|
||||
ret = devices[which]->Read(timestamp);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static INLINE void RealPortWrite(int32 timestamp, int which, bool old_SEL, bool new_SEL, bool old_CLR, bool new_CLR)
|
||||
{
|
||||
if(devices[which])
|
||||
devices[which]->Write(timestamp, old_SEL, new_SEL, old_CLR, new_CLR);
|
||||
}
|
||||
|
||||
static INLINE uint8 MultiTapRead(int32 timestamp)
|
||||
{
|
||||
uint8 ret = 0xF;
|
||||
|
||||
if(read_index > 4)
|
||||
ret = 0;
|
||||
else
|
||||
ret = RealPortRead(timestamp, read_index);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static INLINE void MultiTapWrite(int32 timestamp, bool old_SEL, bool new_SEL, bool old_CLR, bool new_CLR)
|
||||
{
|
||||
for(int i = 0; i < 5; i++)
|
||||
RealPortWrite(timestamp, i, old_SEL, new_SEL, old_CLR, new_CLR);
|
||||
|
||||
// SEL held high, CLR transitions from 0->1, reset counter.
|
||||
// Scratch that, only check if SEL is high on the write that changes CLR from 0 to 1, to fix "Strip Fighter".
|
||||
if(/*old_SEL &&*/ new_SEL && !old_CLR && new_CLR)
|
||||
{
|
||||
//puts("Reset read index");
|
||||
|
||||
read_index = 0;
|
||||
}
|
||||
// CLR held low, SEL transitions from 0->1, increment counter.
|
||||
else if(!old_CLR && !new_CLR && !old_SEL && new_SEL)
|
||||
{
|
||||
//puts("Increment read index");
|
||||
if(read_index < 255)
|
||||
read_index++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 INPUT_Read(int32 timestamp, unsigned int A)
|
||||
{
|
||||
uint8 ret;
|
||||
|
||||
//{
|
||||
// extern VCE *vce;
|
||||
// printf("Input Read: %04x, %d\n", A, vce->GetScanlineNo());
|
||||
//}
|
||||
|
||||
if(MultiTapEnabled && InputTypes[0] != PCEINPUT_TSUSHINKB)
|
||||
ret = MultiTapRead(timestamp);
|
||||
else
|
||||
ret = RealPortRead(timestamp, 0);
|
||||
|
||||
//printf("Input read: %04x, %02x\n",A,ret);
|
||||
|
||||
if(!PCE_IsCD)
|
||||
ret |= 0x80; // Set when CDROM is not attached
|
||||
|
||||
//ret |= 0x40; // PC Engine if set, TG16 if clear. Let's leave it clear, PC Engine games don't seem to mind if it's clear, but TG16 games barf if it's set.
|
||||
|
||||
ret |= 0x30; // Always-set?
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void INPUT_Write(int32 timestamp, unsigned int A, uint8 V)
|
||||
{
|
||||
//printf("Input Write: %04x, %02x\n", A, V);
|
||||
bool new_SEL = V & 0x1;
|
||||
bool new_CLR = V & 0x2;
|
||||
|
||||
if(MultiTapEnabled)
|
||||
MultiTapWrite(timestamp, SEL, new_SEL, CLR, new_CLR);
|
||||
else
|
||||
RealPortWrite(timestamp, 0, SEL, new_SEL, CLR, new_CLR);
|
||||
|
||||
SEL = new_SEL;
|
||||
CLR = new_CLR;
|
||||
}
|
||||
|
||||
void PCEINPUT_Power(int32 timestamp)
|
||||
{
|
||||
read_index = 0;
|
||||
SEL = 0;
|
||||
CLR = 0;
|
||||
|
||||
for(int i = 0; i < 5; i++)
|
||||
if(devices[i])
|
||||
devices[i]->Power(timestamp);
|
||||
}
|
||||
|
||||
int INPUT_StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(SEL),
|
||||
SFVAR(CLR),
|
||||
SFVAR(read_index),
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "JOY", false);
|
||||
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
if(devices[i])
|
||||
{
|
||||
char sn[5] = "JOYx";
|
||||
sn[3] = '0' + i;
|
||||
|
||||
ret &= devices[i]->StateAction(sm, load, data_only, sn);
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static void SyncSettings(void)
|
||||
{
|
||||
MDFNGameInfo->mouse_sensitivity = MDFN_GetSettingF("pce.mouse_sensitivity");
|
||||
MultiTapEnabled = MDFN_GetSettingB("pce.input.multitap");
|
||||
DisableSR = MDFN_GetSettingB("pce.disable_softreset");
|
||||
}
|
30
mednafen/pce/input.h
Normal file
30
mednafen/pce/input.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef __MDFN_PCE_INPUT_H
|
||||
#define __MDFN_PCE_INPUT_H
|
||||
|
||||
class PCE_Input_Device
|
||||
{
|
||||
public:
|
||||
// PCE_Input_Device(int which); // "which" is advisory and only should be used in status messages.
|
||||
virtual ~PCE_Input_Device();
|
||||
virtual void TransformInput(uint8* data, const bool DisableSR);
|
||||
virtual void AdjustTS(int32 delta);
|
||||
virtual void Write(int32 timestamp, bool old_SEL, bool new_SEL, bool old_CLR, bool new_CLR);
|
||||
virtual uint8 Read(int32 timestamp);
|
||||
virtual void Power(int32 timestamp);
|
||||
virtual void Update(const void *data);
|
||||
virtual int StateAction(StateMem *sm, int load, int data_only, const char *section_name);
|
||||
};
|
||||
|
||||
void PCEINPUT_Power(int32 timestamp);
|
||||
void PCEINPUT_Init(void) MDFN_COLD;
|
||||
void PCEINPUT_SettingChanged(const char *name);
|
||||
void PCEINPUT_TransformInput(void);
|
||||
void PCEINPUT_SetInput(unsigned port, const char *type, uint8 *ptr);
|
||||
uint8 INPUT_Read(int32 timestamp, unsigned int A);
|
||||
void INPUT_Write(int32 timestamp, unsigned int A, uint8 V);
|
||||
void INPUT_Frame(void);
|
||||
int INPUT_StateAction(StateMem *sm, int load, int data_only);
|
||||
extern const std::vector<InputPortInfoStruct> PCEPortInfo;
|
||||
void INPUT_AdjustTS(int32 delta_timestamp);
|
||||
|
||||
#endif
|
124
mednafen/pce/input/gamepad.cpp
Normal file
124
mednafen/pce/input/gamepad.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../pce.h"
|
||||
#include "../input.h"
|
||||
#include "gamepad.h"
|
||||
|
||||
class PCE_Input_Gamepad : public PCE_Input_Device
|
||||
{
|
||||
public:
|
||||
PCE_Input_Gamepad();
|
||||
virtual void TransformInput(uint8* data, const bool DisableSR) override;
|
||||
virtual void Power(int32 timestamp) override;
|
||||
virtual void Write(int32 timestamp, bool old_SEL, bool new_SEL, bool old_CLR, bool new_CLR) override;
|
||||
virtual uint8 Read(int32 timestamp) override;
|
||||
virtual void Update(const void *data) override;
|
||||
virtual int StateAction(StateMem *sm, int load, int data_only, const char *section_name) override;
|
||||
|
||||
private:
|
||||
bool SEL, CLR;
|
||||
uint16 buttons;
|
||||
bool AVPad6Which;
|
||||
};
|
||||
|
||||
|
||||
PCE_Input_Gamepad::PCE_Input_Gamepad()
|
||||
{
|
||||
Power(0); // FIXME?
|
||||
}
|
||||
|
||||
void PCE_Input_Gamepad::Power(int32 timestamp)
|
||||
{
|
||||
SEL = 0;
|
||||
CLR = 0;
|
||||
buttons = 0;
|
||||
AVPad6Which = 0;
|
||||
}
|
||||
|
||||
void PCE_Input_Gamepad::TransformInput(uint8* data, const bool DisableSR)
|
||||
{
|
||||
if(DisableSR)
|
||||
{
|
||||
uint16 tmp = MDFN_de16lsb(data);
|
||||
|
||||
if((tmp & 0xC) == 0xC)
|
||||
tmp &= ~0xC;
|
||||
|
||||
MDFN_en16lsb(data, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void PCE_Input_Gamepad::Update(const void *data)
|
||||
{
|
||||
buttons = MDFN_de16lsb((uint8 *)data);
|
||||
}
|
||||
|
||||
uint8 PCE_Input_Gamepad::Read(int32 timestamp)
|
||||
{
|
||||
uint8 ret = 0xF;
|
||||
|
||||
if(AVPad6Which && (buttons & 0x1000))
|
||||
{
|
||||
if(SEL)
|
||||
ret ^= 0xF;
|
||||
else
|
||||
ret ^= (buttons >> 8) & 0x0F;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(SEL)
|
||||
ret ^= (buttons >> 4) & 0x0F;
|
||||
else
|
||||
ret ^= buttons & 0x0F;
|
||||
}
|
||||
|
||||
//if(CLR)
|
||||
// ret = 0;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void PCE_Input_Gamepad::Write(int32 timestamp, bool old_SEL, bool new_SEL, bool old_CLR, bool new_CLR)
|
||||
{
|
||||
SEL = new_SEL;
|
||||
CLR = new_CLR;
|
||||
|
||||
//if(old_SEL && new_SEL && old_CLR && !new_CLR)
|
||||
if(!old_SEL && new_SEL)
|
||||
AVPad6Which = !AVPad6Which;
|
||||
}
|
||||
|
||||
int PCE_Input_Gamepad::StateAction(StateMem *sm, int load, int data_only, const char *section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(SEL),
|
||||
SFVAR(CLR),
|
||||
SFVAR(buttons),
|
||||
SFVAR(AVPad6Which),
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name, false);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
PCE_Input_Device *PCEINPUT_MakeGamepad(void)
|
||||
{
|
||||
return new PCE_Input_Gamepad();
|
||||
}
|
6
mednafen/pce/input/gamepad.h
Normal file
6
mednafen/pce/input/gamepad.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef __PCE_INPUT_GAMEPAD_H
|
||||
#define __PCE_INPUT_GAMEPAD_H
|
||||
|
||||
PCE_Input_Device *PCEINPUT_MakeGamepad(void);
|
||||
|
||||
#endif
|
153
mednafen/pce/input/mouse.cpp
Normal file
153
mednafen/pce/input/mouse.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../pce.h"
|
||||
#include "../input.h"
|
||||
#include "mouse.h"
|
||||
|
||||
class PCE_Input_Mouse : public PCE_Input_Device
|
||||
{
|
||||
public:
|
||||
PCE_Input_Mouse();
|
||||
virtual void Power(int32 timestamp);
|
||||
|
||||
virtual void AdjustTS(int32 delta);
|
||||
virtual void Write(int32 timestamp, bool old_SEL, bool new_SEL, bool old_CLR, bool new_CLR);
|
||||
virtual uint8 Read(int32 timestamp);
|
||||
virtual void Update(const void *data);
|
||||
virtual int StateAction(StateMem *sm, int load, int data_only, const char *section_name);
|
||||
|
||||
private:
|
||||
bool SEL, CLR;
|
||||
int64 mouse_last_meow;
|
||||
|
||||
int32 mouse_x, mouse_y;
|
||||
uint8 pce_mouse_button;
|
||||
uint8 mouse_index;
|
||||
|
||||
uint16 mouse_shifter;
|
||||
};
|
||||
|
||||
void PCE_Input_Mouse::Power(int32 timestamp)
|
||||
{
|
||||
SEL = CLR = 0;
|
||||
mouse_last_meow = 0;
|
||||
mouse_x = mouse_y = 0;
|
||||
pce_mouse_button = 0;
|
||||
mouse_index = 0;
|
||||
mouse_shifter = 0;
|
||||
}
|
||||
|
||||
PCE_Input_Mouse::PCE_Input_Mouse()
|
||||
{
|
||||
Power(0);
|
||||
}
|
||||
|
||||
void PCE_Input_Mouse::Update(const void *data)
|
||||
{
|
||||
//puts("Frame");
|
||||
uint8 *data_ptr = (uint8 *)data;
|
||||
|
||||
mouse_x += (int32)MDFN_de32lsb(data_ptr + 0);
|
||||
mouse_y += (int32)MDFN_de32lsb(data_ptr + 4);
|
||||
pce_mouse_button = *(uint8 *)(data_ptr + 8);
|
||||
}
|
||||
|
||||
void PCE_Input_Mouse::AdjustTS(int32 delta)
|
||||
{
|
||||
//printf("Adjust: %d\n", delta);
|
||||
mouse_last_meow += delta;
|
||||
}
|
||||
|
||||
void PCE_Input_Mouse::Write(int32 timestamp, bool old_SEL, bool new_SEL, bool old_CLR, bool new_CLR)
|
||||
{
|
||||
//printf("Write: %d old_SEL=%d, new_SEL=%d, old_CLR=%d, new_CLR=%d\n", timestamp, old_SEL, new_SEL, old_CLR, new_CLR);
|
||||
if(!old_CLR && new_CLR)
|
||||
{
|
||||
//printf("%lld\n", (int64)timestamp - mouse_last_meow);
|
||||
if(((int64)timestamp - mouse_last_meow) > 10000 * 3)
|
||||
{
|
||||
mouse_last_meow = timestamp;
|
||||
|
||||
int32 rel_x = (int32)((0-mouse_x));
|
||||
int32 rel_y = (int32)((0-mouse_y));
|
||||
|
||||
if(rel_x < -127) rel_x = -127;
|
||||
if(rel_x > 127) rel_x = 127;
|
||||
if(rel_y < -127) rel_y = -127;
|
||||
if(rel_y > 127) rel_y = 127;
|
||||
|
||||
mouse_shifter = ((rel_x & 0xF0) >> 4) | ((rel_x & 0x0F) << 4);
|
||||
mouse_shifter |= (((rel_y & 0xF0) >> 4) | ((rel_y & 0x0F) << 4)) << 8;
|
||||
|
||||
mouse_x += (int32)(rel_x);
|
||||
mouse_y += (int32)(rel_y);
|
||||
|
||||
//printf("Latch: %d %d, %04x\n", rel_x, rel_y, mouse_shifter);
|
||||
}
|
||||
else
|
||||
{
|
||||
//puts("Shift");
|
||||
mouse_shifter >>= 4;
|
||||
}
|
||||
}
|
||||
|
||||
SEL = new_SEL;
|
||||
CLR = new_CLR;
|
||||
}
|
||||
|
||||
uint8 PCE_Input_Mouse::Read(int32 timestamp)
|
||||
{
|
||||
uint8 ret = 0xF;
|
||||
|
||||
//printf("Read: %d\n", timestamp);
|
||||
|
||||
if(SEL)
|
||||
{
|
||||
ret = (mouse_shifter & 0xF);
|
||||
//printf("Read: %02x\n", ret);
|
||||
}
|
||||
else
|
||||
ret ^= pce_mouse_button & 0xF;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
int PCE_Input_Mouse::StateAction(StateMem *sm, int load, int data_only, const char *section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(SEL),
|
||||
SFVAR(CLR),
|
||||
SFVAR(mouse_last_meow),
|
||||
SFVAR(mouse_x),
|
||||
SFVAR(mouse_y),
|
||||
SFVAR(pce_mouse_button),
|
||||
SFVAR(mouse_index),
|
||||
SFVAR(mouse_shifter),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name, false);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
PCE_Input_Device *PCEINPUT_MakeMouse(void)
|
||||
{
|
||||
return(new PCE_Input_Mouse());
|
||||
}
|
6
mednafen/pce/input/mouse.h
Normal file
6
mednafen/pce/input/mouse.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef __PCE_INPUT_MOUSE_H
|
||||
#define __PCE_INPUT_MOUSE_H
|
||||
|
||||
PCE_Input_Device *PCEINPUT_MakeMouse(void);
|
||||
|
||||
#endif
|
131
mednafen/pce/input/tsushinkb.cpp
Normal file
131
mednafen/pce/input/tsushinkb.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../pce.h"
|
||||
#include "../input.h"
|
||||
#include "tsushinkb.h"
|
||||
|
||||
class PCE_Input_TsushinKB : public PCE_Input_Device
|
||||
{
|
||||
public:
|
||||
|
||||
PCE_Input_TsushinKB();
|
||||
|
||||
virtual void Power(int32 timestamp);
|
||||
virtual void Write(int32 timestamp, bool old_SEL, bool new_SEL, bool old_CLR, bool new_CLR);
|
||||
virtual uint8 Read(int32 timestamp);
|
||||
virtual void Update(const void *data);
|
||||
virtual int StateAction(StateMem *sm, int load, int data_only, const char *section_name);
|
||||
|
||||
private:
|
||||
bool SEL, CLR;
|
||||
uint8 TsuKBState[16];
|
||||
uint8 TsuKBLatch[16 + 2 + 1];
|
||||
uint32 TsuKBIndex;
|
||||
bool last_capslock;
|
||||
};
|
||||
|
||||
void PCE_Input_TsushinKB::Power(int32 timestamp)
|
||||
{
|
||||
SEL = CLR = 0;
|
||||
memset(TsuKBState, 0, sizeof(TsuKBState));
|
||||
memset(TsuKBLatch, 0, sizeof(TsuKBLatch));
|
||||
TsuKBIndex = 0;
|
||||
last_capslock = 0;
|
||||
}
|
||||
|
||||
PCE_Input_TsushinKB::PCE_Input_TsushinKB()
|
||||
{
|
||||
Power(0);
|
||||
}
|
||||
|
||||
void PCE_Input_TsushinKB::Update(const void *data)
|
||||
{
|
||||
uint8 *data_ptr = (uint8 *)data;
|
||||
bool capslock = TsuKBState[0xE] & 0x10;
|
||||
bool new_capslock = data_ptr[0xE] & 0x10;
|
||||
|
||||
if(!last_capslock && new_capslock)
|
||||
capslock ^= 1;
|
||||
|
||||
for(int i = 0; i < 16; i++)
|
||||
TsuKBState[i] = data_ptr[i];
|
||||
|
||||
TsuKBState[0xE] = (TsuKBState[0xE] & ~0x10) | (capslock ? 0x10 : 0x00);
|
||||
|
||||
last_capslock = new_capslock;
|
||||
}
|
||||
|
||||
uint8 PCE_Input_TsushinKB::Read(int32 timestamp)
|
||||
{
|
||||
uint8 ret;
|
||||
|
||||
ret = ((TsuKBLatch[TsuKBIndex] >> (SEL * 4)) & 0xF);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void PCE_Input_TsushinKB::Write(int32 timestamp, bool old_SEL, bool new_SEL, bool old_CLR, bool new_CLR)
|
||||
{
|
||||
SEL = new_SEL;
|
||||
CLR = new_CLR;
|
||||
|
||||
//printf("Write: %d %d %d %d\n", old_SEL, new_SEL, old_CLR, new_CLR);
|
||||
|
||||
if(!old_CLR && new_CLR)
|
||||
{
|
||||
TsuKBLatch[0] = 0x02;
|
||||
|
||||
for(int i = 0; i < 16; i++)
|
||||
TsuKBLatch[i + 1] = TsuKBState[i] ^ 0xFF;
|
||||
|
||||
TsuKBLatch[17] = 0x02;
|
||||
TsuKBIndex = 0;
|
||||
//puts("Latched");
|
||||
}
|
||||
else if(!old_SEL && new_SEL)
|
||||
{
|
||||
TsuKBIndex = (TsuKBIndex + 1) % 18;
|
||||
if(!TsuKBIndex)
|
||||
{
|
||||
for(int i = 0; i < 16; i++)
|
||||
TsuKBLatch[i + 1] = TsuKBState[i] ^ 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int PCE_Input_TsushinKB::StateAction(StateMem *sm, int load, int data_only, const char *section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(SEL),
|
||||
SFVAR(CLR),
|
||||
SFARRAY(TsuKBState, sizeof(TsuKBState)),
|
||||
SFARRAY(TsuKBLatch, sizeof(TsuKBLatch)),
|
||||
SFVAR(TsuKBIndex),
|
||||
SFVAR(last_capslock),
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name, false);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
PCE_Input_Device *PCEINPUT_MakeTsushinKB(void)
|
||||
{
|
||||
return(new PCE_Input_TsushinKB());
|
||||
}
|
6
mednafen/pce/input/tsushinkb.h
Normal file
6
mednafen/pce/input/tsushinkb.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef __PCE_INPUT_TSUSHINKB_H
|
||||
#define __PCE_INPUT_TSUSHINKB_H
|
||||
|
||||
PCE_Input_Device *PCEINPUT_MakeTsushinKB(void);
|
||||
|
||||
#endif
|
285
mednafen/pce/mcgenjin.cpp
Normal file
285
mednafen/pce/mcgenjin.cpp
Normal file
@ -0,0 +1,285 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "pce.h"
|
||||
#include "mcgenjin.h"
|
||||
|
||||
MCGenjin_CS_Device::MCGenjin_CS_Device()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
MCGenjin_CS_Device::~MCGenjin_CS_Device()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MCGenjin_CS_Device::Power(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MCGenjin_CS_Device::Update(int32 timestamp)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MCGenjin_CS_Device::ResetTS(int32 ts_base)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int MCGenjin_CS_Device::StateAction(StateMem *sm, int load, int data_only, const char *sname)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
uint8 MCGenjin_CS_Device::Read(int32 timestamp, uint32 A)
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
void MCGenjin_CS_Device::Write(int32 timestamp, uint32 A, uint8 V)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint32 MCGenjin_CS_Device::GetNVSize(void) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint8* MCGenjin_CS_Device::ReadNV(void) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void MCGenjin_CS_Device::WriteNV(const uint8 *buffer, uint32 offset, uint32 count)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class MCGenjin_CS_Device_RAM : public MCGenjin_CS_Device
|
||||
{
|
||||
public:
|
||||
|
||||
MCGenjin_CS_Device_RAM(uint32 size, bool nv)
|
||||
{
|
||||
assert(round_up_pow2(size) == size);
|
||||
|
||||
ram.resize(size);
|
||||
nonvolatile = nv;
|
||||
}
|
||||
|
||||
virtual ~MCGenjin_CS_Device_RAM() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void Power(void) override
|
||||
{
|
||||
if(!nonvolatile)
|
||||
ram.assign(ram.size(), 0xFF);
|
||||
|
||||
bank_select = 0;
|
||||
}
|
||||
|
||||
virtual int StateAction(StateMem *sm, int load, int data_only, const char *sname) override
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFARRAY(&ram[0], ram.size()),
|
||||
SFVAR(bank_select),
|
||||
SFEND
|
||||
};
|
||||
int ret = 1;
|
||||
|
||||
ret &= MDFNSS_StateAction(sm, load, data_only, StateRegs, sname, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
virtual uint8 Read(int32 timestamp, uint32 A) override
|
||||
{
|
||||
return ram[(A | (bank_select << 18)) & (ram.size() - 1)];
|
||||
}
|
||||
|
||||
virtual void Write(int32 timestamp, uint32 A, uint8 V) override
|
||||
{
|
||||
if(!A)
|
||||
bank_select = V;
|
||||
|
||||
ram[(A | (bank_select << 18)) & (ram.size() - 1)] = V;
|
||||
}
|
||||
|
||||
virtual uint32 GetNVSize(void) const override
|
||||
{
|
||||
return nonvolatile ? ram.size() : 0;
|
||||
}
|
||||
|
||||
virtual const uint8* ReadNV(void) const override
|
||||
{
|
||||
return &ram[0];
|
||||
}
|
||||
|
||||
virtual void WriteNV(const uint8 *buffer, uint32 offset, uint32 count) override
|
||||
{
|
||||
while(count)
|
||||
{
|
||||
ram[offset % ram.size()] = *buffer;
|
||||
buffer++;
|
||||
offset++;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<uint8> ram;
|
||||
bool nonvolatile;
|
||||
uint8 bank_select;
|
||||
};
|
||||
|
||||
void MCGenjin::Power(void)
|
||||
{
|
||||
bank_select = 0;
|
||||
dlr = 0;
|
||||
|
||||
stmode_control = 0x00;
|
||||
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
cs[i]->Power();
|
||||
}
|
||||
|
||||
void MCGenjin::Update(int32 timestamp)
|
||||
{
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
cs[i]->Update(timestamp);
|
||||
}
|
||||
|
||||
void MCGenjin::ResetTS(int32 ts_base)
|
||||
{
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
cs[i]->ResetTS(ts_base);
|
||||
}
|
||||
|
||||
uint32 MCGenjin::GetNVSize(const unsigned di) const
|
||||
{
|
||||
return cs[di]->GetNVSize();
|
||||
}
|
||||
|
||||
const uint8* MCGenjin::ReadNV(const unsigned di) const
|
||||
{
|
||||
return cs[di]->ReadNV();
|
||||
}
|
||||
|
||||
void MCGenjin::WriteNV(const unsigned di, const uint8 *buffer, uint32 offset, uint32 count)
|
||||
{
|
||||
cs[di]->WriteNV(buffer, offset, count);
|
||||
}
|
||||
|
||||
MCGenjin::MCGenjin(MDFNFILE* fp)
|
||||
{
|
||||
const uint64 rr_size = fp->size;
|
||||
uint8 revision, num256_pages, region, cs_di[2];
|
||||
|
||||
if(rr_size > 1024 * 1024 * 128)
|
||||
{
|
||||
MDFN_printf("MCGenjin ROM size is too large!");
|
||||
return;
|
||||
}
|
||||
|
||||
if(rr_size < 8192)
|
||||
{
|
||||
MDFN_printf("MCGenjin ROM size is too small!");
|
||||
return;
|
||||
}
|
||||
|
||||
rom.resize(round_up_pow2(rr_size));
|
||||
file_read(fp, &rom[0], rr_size, 1);
|
||||
|
||||
if(memcmp(&rom[0x1FD0], "MCGENJIN", 8))
|
||||
{
|
||||
MDFN_printf("MC Genjin header magic missing!");
|
||||
return;
|
||||
}
|
||||
|
||||
revision = rom[0x1FD8];
|
||||
num256_pages = rom[0x1FD9];
|
||||
region = rom[0x1FDA];
|
||||
cs_di[0] = rom[0x1FDB];
|
||||
cs_di[1] = rom[0x1FDC];
|
||||
|
||||
MDFN_printf("MCGenjin Header:\n");
|
||||
MDFN_indent(1);
|
||||
MDFN_printf("Revision: 0x%02x\n", revision);
|
||||
MDFN_printf("ROM Size: %u\n", num256_pages * 262144);
|
||||
MDFN_printf("Region: 0x%02x\n", region);
|
||||
MDFN_printf("CS0 Type: 0x%02x\n", cs_di[0]);
|
||||
MDFN_printf("CS1 Type: 0x%02x\n", cs_di[1]);
|
||||
MDFN_indent(-1);
|
||||
|
||||
// Don't set addr_write_mask to larger than 0xF unless code in mcgenjin.h is adjusted as well.
|
||||
if(revision >= 0x80)
|
||||
addr_write_mask = 0xF;
|
||||
else
|
||||
addr_write_mask = 0x3;
|
||||
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
if((cs_di[i] >= 0x10 && cs_di[i] <= 0x18) || (cs_di[i] >= 0x20 && cs_di[i] <= 0x28))
|
||||
{
|
||||
MDFN_printf("CS%d: %uKiB %sRAM\n", i, 8 << (cs_di[i] & 0xF), (cs_di[i] & 0x20) ? "Nonvolatile " : "");
|
||||
cs[i].reset(new MCGenjin_CS_Device_RAM(8192 << (cs_di[i] & 0xF), (bool)(cs_di[i] & 0x20)));
|
||||
}
|
||||
else switch(cs_di[i])
|
||||
{
|
||||
default:
|
||||
MDFN_printf("Unsupported MCGENJIN device on CS%d: 0x%02x", i, cs_di[i]);
|
||||
break;
|
||||
|
||||
case 0x00:
|
||||
MDFN_printf("CS%d: Unused\n", i);
|
||||
cs[i].reset(new MCGenjin_CS_Device());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MCGenjin::~MCGenjin()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int MCGenjin::StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(bank_select),
|
||||
SFVAR(dlr),
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "MCGENJIN", false);
|
||||
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
ret &= MDFNSS_StateAction(sm, load, data_only, StateRegs, i ? "MCGENJIN_CS1" : "MCGENJIN_CS0", false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
188
mednafen/pce/mcgenjin.h
Normal file
188
mednafen/pce/mcgenjin.h
Normal file
@ -0,0 +1,188 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __MDFN_PCE_MCGENJIN_H
|
||||
#define __MDFN_PCE_MCGENJIN_H
|
||||
|
||||
class MCGenjin_CS_Device
|
||||
{
|
||||
public:
|
||||
|
||||
MCGenjin_CS_Device();
|
||||
virtual ~MCGenjin_CS_Device();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual void Update(int32 timestamp);
|
||||
virtual void ResetTS(int32 ts_base);
|
||||
|
||||
virtual int StateAction(StateMem *sm, int load, int data_only, const char *sname);
|
||||
|
||||
virtual uint8 Read(int32 timestamp, uint32 A);
|
||||
virtual void Write(int32 timestamp, uint32 A, uint8 V) ;
|
||||
|
||||
virtual uint32 GetNVSize(void) const;
|
||||
virtual const uint8* ReadNV(void) const;
|
||||
virtual void WriteNV(const uint8 *buffer, uint32 offset, uint32 count);
|
||||
};
|
||||
|
||||
class MCGenjin
|
||||
{
|
||||
public:
|
||||
|
||||
MCGenjin(MDFNFILE* fp);
|
||||
~MCGenjin();
|
||||
|
||||
void Power(void);
|
||||
void Update(int32 timestamp);
|
||||
void ResetTS(int32 ts_base);
|
||||
int StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
unsigned INLINE GetNVPDC(void) { return 2; }
|
||||
uint32 GetNVSize(const unsigned di) const;
|
||||
const uint8* ReadNV(const unsigned di) const;
|
||||
void WriteNV(const unsigned di, const uint8 *buffer, uint32 offset, uint32 count);
|
||||
|
||||
uint8 INLINE combobble(uint8 v)
|
||||
{
|
||||
if(dlr)
|
||||
return ((((v * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL) >> 32);
|
||||
else
|
||||
return v;
|
||||
}
|
||||
|
||||
template<unsigned ar>
|
||||
uint8 INLINE ReadTP(int32 timestamp, uint32 A)
|
||||
{
|
||||
uint8 ret = 0xFF;
|
||||
|
||||
switch(ar)
|
||||
{
|
||||
case 0: ret = combobble(rom[A & 0x3FFFF & (rom.size() - 1)]); break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
const uint32 rom_addr_or = bank_select << 18;
|
||||
const uint32 rom_addr_xor = ((stmode_control & 0x7E) << 6) | (stmode_control & STMODEC_MASK_ENABLE);
|
||||
uint32 rom_addr_and = (rom.size() - 1);
|
||||
|
||||
if((stmode_control & 0x80) && !(A & 0x1))
|
||||
rom_addr_and &= ~0x1FFC;
|
||||
|
||||
ret = combobble(rom[(((A & 0x3FFFF) | rom_addr_or) ^ rom_addr_xor) & rom_addr_and]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: ret = cs[0]->Read(timestamp, A & 0x3FFFF); break;
|
||||
case 3: ret = cs[1]->Read(timestamp, A & 0x3FFFF); break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<unsigned ar>
|
||||
void INLINE WriteTP(int32 timestamp, uint32 A, uint8 V)
|
||||
{
|
||||
switch(ar)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
switch((A & addr_write_mask) | (~addr_write_mask & 0xF))
|
||||
{
|
||||
case 0x0: avl_mask[0] = V; break;
|
||||
case 0x1: avl_mask[1] = V; break;
|
||||
case 0x2: avl_mask[2] = V; break;
|
||||
case 0x3: avl_mask[3] = V; break;
|
||||
|
||||
case 0x4: avl[0] &= 0xFF00; avl[0] |= V; break;
|
||||
case 0x5: avl[1] &= 0xFF00; avl[1] |= V; break;
|
||||
case 0x6: avl[2] &= 0xFF00; avl[2] |= V; break;
|
||||
case 0x7: avl[3] &= 0xFF00; avl[3] |= V; break;
|
||||
|
||||
case 0x8: avl[0] &= 0x00FF; avl[0] |= V << 8; break;
|
||||
case 0x9: avl[1] &= 0x00FF; avl[1] |= V << 8; break;
|
||||
case 0xA: avl[2] &= 0x00FF; avl[2] |= V << 8; break;
|
||||
case 0xB: avl[3] &= 0x00FF; avl[3] |= V << 8; break;
|
||||
|
||||
case 0xC:
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
if(V & (1U << i))
|
||||
shadow_avl[i] += avl[i];
|
||||
else
|
||||
shadow_avl[i] = avl[i];
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xD: stmode_control = V; break;
|
||||
case 0xE: dlr = V & 0x1; break;
|
||||
case 0xF: bank_select = V; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: return cs[0]->Write(timestamp, A & 0x3FFFF, V);
|
||||
case 3: return cs[1]->Write(timestamp, A & 0x3FFFF, V);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8 INLINE Read(int32 timestamp, uint32 A)
|
||||
{
|
||||
switch((A >> 18) & 0x3)
|
||||
{
|
||||
default: return 0xFF;
|
||||
case 0: return ReadTP<0>(timestamp, A);
|
||||
case 1: return ReadTP<1>(timestamp, A);
|
||||
case 2: return ReadTP<2>(timestamp, A);
|
||||
case 3: return ReadTP<3>(timestamp, A);
|
||||
}
|
||||
}
|
||||
|
||||
void INLINE Write(int32 timestamp, uint32 A, uint8 V)
|
||||
{
|
||||
switch((A >> 18) & 0x3)
|
||||
{
|
||||
case 0: WriteTP<0>(timestamp, A, V);
|
||||
case 1: WriteTP<1>(timestamp, A, V);
|
||||
case 2: WriteTP<2>(timestamp, A, V);
|
||||
case 3: WriteTP<3>(timestamp, A, V);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::vector<uint8> rom;
|
||||
|
||||
std::unique_ptr<MCGenjin_CS_Device> cs[2];
|
||||
|
||||
uint8 bank_select;
|
||||
uint8 dlr;
|
||||
|
||||
enum { STMODEC_MASK_ENABLE = 0x80 };
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
uint8 addr_write_mask;
|
||||
|
||||
uint8 stmode_control;
|
||||
|
||||
uint16 avl[4];
|
||||
uint8 avl_mask[4];
|
||||
uint16 shadow_avl[4];
|
||||
};
|
||||
|
||||
#endif
|
882
mednafen/pce/pce.cpp
Normal file
882
mednafen/pce/pce.cpp
Normal file
@ -0,0 +1,882 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "pce.h"
|
||||
#include "vce.h"
|
||||
#include <mednafen/hw_sound/pce_psg/pce_psg.h>
|
||||
#include <encodings/crc32.h>
|
||||
#include "input.h"
|
||||
#include "huc.h"
|
||||
#include "pcecd.h"
|
||||
#include <mednafen/cdrom/scsicd.h>
|
||||
#include "tsushin.h"
|
||||
#include <mednafen/hw_misc/arcade_card/arcade_card.h>
|
||||
#include <mednafen/mempatcher.h>
|
||||
#include <mednafen/cdrom/cdromif.h>
|
||||
#include <mednafen/FileStream.h>
|
||||
#include <mednafen/sound/OwlResampler.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#define PCE_DEBUG(x, ...) { /* printf(x, ## __VA_ARGS__); */ }
|
||||
|
||||
extern MDFNGI EmulatedPCE;
|
||||
|
||||
static const MDFNSetting_EnumList PSGRevisionList[] =
|
||||
{
|
||||
{ "huc6280", PCE_PSG::REVISION_HUC6280, "HuC6280", "HuC6280 as found in the original PC Engine." },
|
||||
{ "huc6280a", PCE_PSG::REVISION_HUC6280A, "HuC6280A", "HuC6280A as found in the SuperGrafx and CoreGrafx I. Provides proper channel amplitude centering, but may cause clicking in a few games designed with the original HuC6280's sound characteristics in mind." },
|
||||
{ "match", PCE_PSG::_REVISION_COUNT, "Match emulation mode.", "Selects \"huc6280\" for non-SuperGrafx mode, and \"huc6280a\" for SuperGrafx(full) mode." },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
static std::vector<CDIF*> *cdifs = NULL;
|
||||
|
||||
HuC6280 HuCPU;
|
||||
|
||||
VCE *vce = NULL;
|
||||
|
||||
static PCE_PSG *psg = NULL;
|
||||
|
||||
extern ArcadeCard *arcade_card; // Bah, lousy globals.
|
||||
|
||||
static OwlBuffer* HRBufs[2] = { NULL, NULL };
|
||||
static RavenBuffer* ADPCMBuf = NULL;
|
||||
static RavenBuffer* CDDABufs[2] = { NULL, NULL };
|
||||
static OwlResampler* HRRes = NULL;
|
||||
|
||||
static bool SetSoundRate(double rate);
|
||||
|
||||
static void Cleanup(void);
|
||||
|
||||
bool PCE_ACEnabled;
|
||||
uint64 PCE_TimestampBase; // Only used with the debugger for the time being.
|
||||
|
||||
static bool IsSGX;
|
||||
|
||||
uint8 BaseRAM[32768]; // 8KB for PCE, 32KB for Super Grafx
|
||||
|
||||
HuC6280::readfunc NonCheatPCERead[0x100];
|
||||
|
||||
static DECLFR(PCEBusRead)
|
||||
{
|
||||
PCE_DEBUG("Unmapped Read: %02x %04x\n", A >> 13, A);
|
||||
return(0xFF);
|
||||
}
|
||||
|
||||
static DECLFW(PCENullWrite)
|
||||
{
|
||||
PCE_DEBUG("Unmapped Write: %02x, %08x %02x\n", A >> 13, A, V);
|
||||
}
|
||||
|
||||
static DECLFR(BaseRAMReadSGX)
|
||||
{
|
||||
return(BaseRAM[A & 0x7FFF]);
|
||||
}
|
||||
|
||||
static DECLFW(BaseRAMWriteSGX)
|
||||
{
|
||||
BaseRAM[A & 0x7FFF] = V;
|
||||
}
|
||||
|
||||
static DECLFR(BaseRAMRead)
|
||||
{
|
||||
return(BaseRAM[A & 0x1FFF]);
|
||||
}
|
||||
|
||||
static DECLFW(BaseRAMWrite)
|
||||
{
|
||||
BaseRAM[A & 0x1FFF] = V;
|
||||
}
|
||||
|
||||
static DECLFR(IORead)
|
||||
{
|
||||
A &= 0x1FFF;
|
||||
|
||||
switch(A & 0x1c00)
|
||||
{
|
||||
case 0x0000:
|
||||
HuCPU.StealCycle();
|
||||
return(vce->ReadVDC(A));
|
||||
|
||||
case 0x0400:
|
||||
HuCPU.StealCycle();
|
||||
return(vce->Read(A));
|
||||
|
||||
case 0x0800:
|
||||
if(HuCPU.InBlockMove())
|
||||
return(0);
|
||||
return(HuCPU.GetIODataBuffer());
|
||||
|
||||
case 0x0c00:
|
||||
if(HuCPU.InBlockMove())
|
||||
return(0);
|
||||
{
|
||||
uint8 ret = HuCPU.TimerRead(A, 0);
|
||||
HuCPU.SetIODataBuffer(ret);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
case 0x1000:
|
||||
if(HuCPU.InBlockMove())
|
||||
return(0);
|
||||
{
|
||||
uint8 ret = INPUT_Read(HuCPU.Timestamp(), A);
|
||||
HuCPU.SetIODataBuffer(ret);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
case 0x1400:
|
||||
if(HuCPU.InBlockMove())
|
||||
return(0);
|
||||
{
|
||||
uint8 ret = HuCPU.IRQStatusRead(A, 0);
|
||||
HuCPU.SetIODataBuffer(ret);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
case 0x1800:
|
||||
if(IsTsushin)
|
||||
return(PCE_TsushinRead(A));
|
||||
|
||||
if(!PCE_IsCD)
|
||||
break;
|
||||
if((A & 0x1E00) == 0x1A00)
|
||||
{
|
||||
if(arcade_card)
|
||||
return(arcade_card->Read(A, 0));
|
||||
else
|
||||
return(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 next_cd_event;
|
||||
uint8 ret;
|
||||
|
||||
ret = PCECD_Read(HuCPU.Timestamp(), A, next_cd_event, 0);
|
||||
|
||||
vce->SetCDEvent(next_cd_event);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
case 0x1C00:
|
||||
break; // Expansion
|
||||
}
|
||||
|
||||
PCE_DEBUG("I/O Unmapped Read: %04x\n", A);
|
||||
|
||||
return(0xFF);
|
||||
}
|
||||
|
||||
static DECLFW(IOWrite)
|
||||
{
|
||||
switch(A & 0x1c00)
|
||||
{
|
||||
case 0x0000:
|
||||
HuCPU.StealCycle();
|
||||
vce->WriteVDC(A & 0x80001FFF, V);
|
||||
break;
|
||||
|
||||
case 0x0400:
|
||||
HuCPU.StealCycle();
|
||||
vce->Write(A & 0x1FFF, V);
|
||||
break;
|
||||
|
||||
case 0x0800:
|
||||
HuCPU.SetIODataBuffer(V);
|
||||
psg->Write(HuCPU.Timestamp() / 3, A & 0x1FFF, V);
|
||||
break;
|
||||
|
||||
case 0x0c00:
|
||||
HuCPU.SetIODataBuffer(V);
|
||||
HuCPU.TimerWrite(A & 0x1FFF, V);
|
||||
break;
|
||||
|
||||
case 0x1000:
|
||||
HuCPU.SetIODataBuffer(V);
|
||||
INPUT_Write(HuCPU.Timestamp(), A & 0x1FFF, V);
|
||||
break;
|
||||
|
||||
case 0x1400:
|
||||
HuCPU.SetIODataBuffer(V);
|
||||
HuCPU.IRQStatusWrite(A & 0x1FFF, V);
|
||||
break;
|
||||
|
||||
case 0x1800:
|
||||
if(IsTsushin)
|
||||
PCE_TsushinWrite(A & 0x1FFF, V);
|
||||
|
||||
if(!PCE_IsCD)
|
||||
{
|
||||
PCE_DEBUG("I/O Unmapped Write: %04x %02x\n", A, V);
|
||||
break;
|
||||
}
|
||||
|
||||
if((A & 0x1E00) == 0x1A00)
|
||||
{
|
||||
if(arcade_card)
|
||||
arcade_card->Write(A& 0x1FFF, V);
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 next_cd_event = PCECD_Write(HuCPU.Timestamp(), A & 0x1FFF, V);
|
||||
|
||||
vce->SetCDEvent(next_cd_event);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x1C00:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void PCECDIRQCB(bool asserted)
|
||||
{
|
||||
if(asserted)
|
||||
HuCPU.IRQBegin(HuC6280::IQIRQ2);
|
||||
else
|
||||
HuCPU.IRQEnd(HuC6280::IQIRQ2);
|
||||
}
|
||||
|
||||
static int LoadCommon(void);
|
||||
static void LoadCommonPre(void);
|
||||
|
||||
static void SetCDSettings(bool silent_status = false)
|
||||
{
|
||||
double cdpsgvolume;
|
||||
PCECD_Settings cd_settings;
|
||||
memset(&cd_settings, 0, sizeof(PCECD_Settings));
|
||||
|
||||
cdpsgvolume = (double)MDFN_GetSettingUI("pce.cdpsgvolume") / 100;
|
||||
cd_settings.CDDA_Volume = (double)MDFN_GetSettingUI("pce.cddavolume") / 100;
|
||||
cd_settings.ADPCM_Volume = (double)MDFN_GetSettingUI("pce.adpcmvolume") / 100;
|
||||
cd_settings.ADPCM_ExtraPrecision = MDFN_GetSettingB("pce.adpcmextraprec");
|
||||
|
||||
if(!silent_status)
|
||||
{
|
||||
if(cd_settings.CDDA_Volume != 1.0 || cd_settings.ADPCM_Volume != 1.0 || cdpsgvolume != 1.0)
|
||||
{
|
||||
MDFN_printf("CD-DA Volume: %d%%\n", (int)(100 * cd_settings.CDDA_Volume));
|
||||
MDFN_printf("ADPCM Volume: %d%%\n", (int)(100 * cd_settings.ADPCM_Volume));
|
||||
MDFN_printf("CD PSG Volume: %d%%\n", (int)(100 * cdpsgvolume));
|
||||
}
|
||||
}
|
||||
|
||||
PCECD_SetSettings(&cd_settings);
|
||||
psg->SetVolume(0.678 * cdpsgvolume);
|
||||
}
|
||||
|
||||
static void CDSettingChanged(const char *name)
|
||||
{
|
||||
SetCDSettings(true);
|
||||
}
|
||||
|
||||
|
||||
static const struct
|
||||
{
|
||||
uint32 crc;
|
||||
const char* name;
|
||||
} sgx_table[] =
|
||||
{
|
||||
{ 0xbebfe042, "Darius Plus", },
|
||||
{ 0x4c2126b0, "Aldynes" },
|
||||
{ 0x8c4588e2, "1941 - Counter Attack" },
|
||||
{ 0x1f041166, "Madouou Granzort" },
|
||||
{ 0xb486a8ed, "Daimakaimura" },
|
||||
{ 0x3b13af61, "Battle Ace" },
|
||||
{ 0, "" }
|
||||
};
|
||||
|
||||
MDFN_COLD int PCE_Load(MDFNFILE *fp)
|
||||
{
|
||||
IsSGX = false;
|
||||
|
||||
LoadCommonPre();
|
||||
|
||||
uint32 crc;
|
||||
|
||||
crc = HuC_Load(fp, MDFN_GetSettingB("pce.disable_bram_hucard"));
|
||||
|
||||
if(fp->ext == "sgx")
|
||||
IsSGX = true;
|
||||
else
|
||||
{
|
||||
for(int lcv = 0; sgx_table[lcv].crc; lcv++)
|
||||
{
|
||||
if(sgx_table[lcv].crc == crc)
|
||||
{
|
||||
IsSGX = true;
|
||||
MDFN_printf("SuperGrafx: %s\n", sgx_table[lcv].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LoadCommon();
|
||||
}
|
||||
|
||||
static MDFN_COLD void LoadCommonPre(void)
|
||||
{
|
||||
// Initialize sound buffers
|
||||
for(unsigned ch = 0; ch < 2; ch++)
|
||||
HRBufs[ch] = new OwlBuffer();
|
||||
|
||||
// FIXME: Make these globals less global!
|
||||
PCE_ACEnabled = MDFN_GetSettingB("pce.arcadecard");
|
||||
|
||||
HuCPU.Init(0);
|
||||
|
||||
for(int x = 0; x < 0x100; x++)
|
||||
{
|
||||
HuCPU.SetFastRead(x, NULL);
|
||||
HuCPU.SetReadHandler(x, PCEBusRead);
|
||||
HuCPU.SetWriteHandler(x, PCENullWrite);
|
||||
}
|
||||
|
||||
MDFNMP_Init(1024, (1 << 21) / 1024);
|
||||
}
|
||||
|
||||
static MDFN_COLD int LoadCommon(void)
|
||||
{
|
||||
IsSGX |= MDFN_GetSettingB("pce.forcesgx") ? 1 : 0;
|
||||
|
||||
// Don't modify IsSGX past this point.
|
||||
const uint32 vram_size = MDFN_GetSettingUI("pce.vramsize");
|
||||
|
||||
vce = new VCE(IsSGX, vram_size);
|
||||
vce->SetVDCUnlimitedSprites(MDFN_GetSettingB("pce.nospritelimit"));
|
||||
|
||||
if(IsSGX)
|
||||
MDFN_printf("SuperGrafx Emulation Enabled.\n");
|
||||
|
||||
for(int i = 0xF8; i < 0xFC; i++)
|
||||
{
|
||||
HuCPU.SetReadHandler(i, IsSGX ? BaseRAMReadSGX : BaseRAMRead);
|
||||
HuCPU.SetWriteHandler(i, IsSGX ? BaseRAMWriteSGX : BaseRAMWrite);
|
||||
|
||||
if(IsSGX)
|
||||
HuCPU.SetFastRead(i, BaseRAM + (i & 0x3) * 8192);
|
||||
else
|
||||
HuCPU.SetFastRead(i, BaseRAM);
|
||||
}
|
||||
|
||||
MDFNMP_AddRAM(IsSGX ? 32768 : 8192, 0xf8 * 8192, BaseRAM);
|
||||
|
||||
HuCPU.SetReadHandler(0xFF, IORead);
|
||||
HuCPU.SetWriteHandler(0xFF, IOWrite);
|
||||
|
||||
int psgrevision = MDFN_GetSettingI("pce.psgrevision");
|
||||
|
||||
if(psgrevision == PCE_PSG::_REVISION_COUNT)
|
||||
{
|
||||
psgrevision = IsSGX ? PCE_PSG::REVISION_HUC6280A : PCE_PSG::REVISION_HUC6280;
|
||||
}
|
||||
|
||||
for(const MDFNSetting_EnumList *el = PSGRevisionList; el->string; el++)
|
||||
{
|
||||
if(el->number == psgrevision)
|
||||
{
|
||||
MDFN_printf("PSG Revision: %s\n", el->description);
|
||||
break;
|
||||
}
|
||||
}
|
||||
psg = new PCE_PSG(HRBufs[0]->Buf(), HRBufs[1]->Buf(), psgrevision);
|
||||
|
||||
psg->SetVolume(1.0);
|
||||
|
||||
if(PCE_IsCD)
|
||||
SetCDSettings();
|
||||
|
||||
PCEINPUT_Init();
|
||||
|
||||
PCE_Power();
|
||||
|
||||
MDFNGameInfo->fps = (uint32)((double)7159090.90909090 / 455 / 263 * 65536 * 256);
|
||||
|
||||
for(unsigned int i = 0; i < 0x100; i++)
|
||||
NonCheatPCERead[i] = HuCPU.GetReadHandler(i);
|
||||
|
||||
MDFNGameInfo->nominal_height = MDFN_GetSettingUI("pce.slend") - MDFN_GetSettingUI("pce.slstart") + 1;
|
||||
MDFNGameInfo->nominal_width = MDFN_GetSettingB("pce.h_overscan") ? 320 : 288;
|
||||
|
||||
MDFNGameInfo->lcm_width = MDFN_GetSettingB("pce.h_overscan") ? 1120 : 1024;
|
||||
MDFNGameInfo->lcm_height = MDFNGameInfo->nominal_height;
|
||||
|
||||
vce->SetShowHorizOS(MDFN_GetSettingB("pce.h_overscan"));
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
static bool DetectGECD(CDIF *cdiface) // Very half-assed detection until(if) we get ISO-9660 reading code.
|
||||
{
|
||||
uint8 sector_buffer[2048];
|
||||
TOC toc;
|
||||
|
||||
cdiface->ReadTOC(&toc);
|
||||
|
||||
// Now, test for the Games Express CD games. The GE BIOS seems to always look at sector 0x10, but only if the first track is a
|
||||
// data track.
|
||||
if(toc.first_track == 1 && (toc.tracks[1].control & 0x4))
|
||||
{
|
||||
if(cdiface->ReadSector(sector_buffer, 0x10, 1) == 0x1)
|
||||
{
|
||||
if(!memcmp((char *)sector_buffer + 0x8, "HACKER CD ROM SYSTEM", 0x14))
|
||||
return(true);
|
||||
|
||||
if(!memcmp((char *)sector_buffer + 0x01, "CD001", 0x5))
|
||||
{
|
||||
if(cdiface->ReadSector(sector_buffer, 0x14, 1) == 0x1)
|
||||
{
|
||||
static const uint32 known_crcs[] =
|
||||
{
|
||||
0xd7b47c06, // AV Tanjou
|
||||
0x86aec522, // Bishoujo Jyanshi [...]
|
||||
0xc8d1b5ef, // CD Bishoujo [...]
|
||||
0x0bdbde64, // CD Pachisuro [...]
|
||||
};
|
||||
uint32 zecrc = encoding_crc32(0, sector_buffer, 2048);
|
||||
|
||||
//printf("%04x\n", zecrc);
|
||||
for(unsigned int i = 0; i < sizeof(known_crcs) / sizeof(uint32); i++)
|
||||
if(known_crcs[i] == zecrc)
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
static MDFN_COLD bool DetectSGXCD(std::vector<CDIF*>* CDInterfaces)
|
||||
{
|
||||
CDIF *cdiface = (*CDInterfaces)[0];
|
||||
TOC toc;
|
||||
uint8 sector_buffer[2048];
|
||||
bool ret = false;
|
||||
|
||||
memset(sector_buffer, 0, sizeof(sector_buffer));
|
||||
|
||||
cdiface->ReadTOC(&toc);
|
||||
|
||||
// Check all data tracks for the 16-byte magic(4D 65 64 6E 61 66 65 6E 74 AB 90 19 42 62 7D E6) at offset 0x86A(assuming mode 1 sectors).
|
||||
for(int32 track = toc.first_track; track <= toc.last_track; track++)
|
||||
{
|
||||
if(toc.tracks[track].control & 0x4)
|
||||
{
|
||||
if(cdiface->ReadSector(sector_buffer, toc.tracks[track].lba + 1, 1) != 0x1)
|
||||
continue;
|
||||
|
||||
if(MDFN_de64msb(§or_buffer[0x6A]) == 0x4D65646E6166656EULL && MDFN_de64msb(§or_buffer[0x6A + 8]) == 0x74AB901942627DE6ULL)
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MDFN_COLD int PCE_LoadCD(std::vector<CDIF *> *CDInterfaces)
|
||||
{
|
||||
IsSGX = DetectSGXCD(CDInterfaces);
|
||||
|
||||
LoadCommonPre();
|
||||
|
||||
const char *bios_sname = DetectGECD((*CDInterfaces)[0]) ? "pce.gecdbios" : "pce.cdbios";
|
||||
std::string bios_path = MDFN_GetSettingS("filesys.path_firmware") + "/" + MDFN_GetSettingS(bios_sname).c_str();
|
||||
|
||||
if (log_cb)
|
||||
MDFN_printf("Loading bios %s\n", bios_path.c_str());
|
||||
|
||||
MDFNFILE *fp = file_open(bios_path.c_str());
|
||||
if (!fp)
|
||||
{
|
||||
MDFN_printf("Error: Failed to load bios!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool disable_bram_cd = MDFN_GetSettingB("pce.disable_bram_cd");
|
||||
|
||||
if(disable_bram_cd)
|
||||
MDFN_printf("Warning: BRAM is disabled per pcfx.disable_bram_cd setting. This is simulating a malfunction.\n");
|
||||
|
||||
HuC_Load(fp, disable_bram_cd, PCE_ACEnabled ? SYSCARD_ARCADE : SYSCARD_3);
|
||||
|
||||
ADPCMBuf = new RavenBuffer();
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
CDDABufs[lr] = new RavenBuffer();
|
||||
|
||||
PCE_IsCD = 1;
|
||||
PCECD_Init(NULL, PCECDIRQCB, PCE_MASTER_CLOCK, ADPCMBuf->Buf(), CDDABufs[0]->Buf(), CDDABufs[1]->Buf());
|
||||
|
||||
cdifs = CDInterfaces;
|
||||
|
||||
SCSICD_SetDisc(true, NULL, true);
|
||||
SCSICD_SetDisc(false, (*cdifs)[0], true);
|
||||
|
||||
MDFN_printf("Arcade Card Emulation: %s\n", PCE_ACEnabled ? "Enabled" : "Disabled");
|
||||
|
||||
return LoadCommon();
|
||||
}
|
||||
|
||||
static MDFN_COLD void Cleanup(void)
|
||||
{
|
||||
if(PCE_IsCD)
|
||||
PCECD_Close();
|
||||
|
||||
HuC_Kill();
|
||||
|
||||
if(vce)
|
||||
{
|
||||
delete vce;
|
||||
vce = NULL;
|
||||
}
|
||||
|
||||
if(psg)
|
||||
{
|
||||
delete psg;
|
||||
psg = NULL;
|
||||
}
|
||||
|
||||
for(unsigned ch = 0; ch < 2; ch++)
|
||||
{
|
||||
if(HRBufs[ch])
|
||||
{
|
||||
delete HRBufs[ch];
|
||||
HRBufs[ch] = NULL;
|
||||
}
|
||||
|
||||
if(CDDABufs[ch])
|
||||
{
|
||||
delete CDDABufs[ch];
|
||||
CDDABufs[ch] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(ADPCMBuf)
|
||||
{
|
||||
delete ADPCMBuf;
|
||||
ADPCMBuf = NULL;
|
||||
}
|
||||
|
||||
if(HRRes)
|
||||
{
|
||||
delete HRRes;
|
||||
HRRes = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MDFN_COLD void PCE_CloseGame(void)
|
||||
{
|
||||
HuC_SaveNV();
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
static EmulateSpecStruct *es;
|
||||
void Emulate(EmulateSpecStruct *espec)
|
||||
{
|
||||
es = espec;
|
||||
|
||||
espec->MasterCycles = 0;
|
||||
espec->SoundBufSize = 0;
|
||||
|
||||
MDFNMP_ApplyPeriodicCheats();
|
||||
|
||||
if(espec->VideoFormatChanged)
|
||||
vce->SetPixelFormat(NULL, 0); //espec->CustomPalette, espec->CustomPaletteNumEntries
|
||||
|
||||
if(espec->SoundFormatChanged)
|
||||
SetSoundRate(espec->SoundRate);
|
||||
|
||||
//int t = MDFND_GetTime();
|
||||
|
||||
vce->StartFrame(espec->surface, &espec->DisplayRect, espec->LineWidths, espec->skip);
|
||||
|
||||
// Begin loop here:
|
||||
//for(int i = 0; i < 2; i++)
|
||||
bool rp_rv;
|
||||
do
|
||||
{
|
||||
assert(HuCPU.Timestamp() < 12);
|
||||
//printf("ST: %d\n", HuCPU.Timestamp());
|
||||
|
||||
INPUT_Frame();
|
||||
|
||||
//vce->RunFrame(espec->surface, &espec->DisplayRect, espec->LineWidths, IsHES ? 1 : espec->skip);
|
||||
do
|
||||
{
|
||||
rp_rv = vce->RunPartial();
|
||||
} while(espec->NeedSoundReverse && !rp_rv);
|
||||
|
||||
const uint32 end_timestamp = HuCPU.Timestamp();
|
||||
const uint32 end_timestamp_div12 = end_timestamp / 12;
|
||||
const uint32 end_timestamp_mod12 = end_timestamp % 12;
|
||||
|
||||
INPUT_AdjustTS((int32)end_timestamp_mod12 - (int32)end_timestamp); // Careful with this!
|
||||
|
||||
psg->Update(end_timestamp / 3);
|
||||
psg->ResetTS(end_timestamp_mod12 / 3);
|
||||
|
||||
HuC_Update(end_timestamp);
|
||||
HuC_ResetTS(end_timestamp_mod12);
|
||||
|
||||
const unsigned rsc = min_T<unsigned>(65536, end_timestamp_div12);
|
||||
int32 new_sc;
|
||||
|
||||
if(ADPCMBuf)
|
||||
PCECD_ProcessADPCMBuffer(rsc);
|
||||
|
||||
for(unsigned ch = 0; ch < 2; ch++)
|
||||
{
|
||||
if(HRRes)
|
||||
{
|
||||
//
|
||||
// These filter parameters cause much less of a lowpass and much much less of a highpass filter effect than what I've tested on my Turbo Duo,
|
||||
// but I think it's probably broken(in need of capacitor replacements).
|
||||
//
|
||||
HRBufs[ch]->Integrate(rsc, 2 /* lp shift, lower = less lp effect */, 14 /* hp shift, higher = less hp effect*/, ADPCMBuf, CDDABufs[ch]);
|
||||
}
|
||||
|
||||
if(espec->SoundBuf && HRRes)
|
||||
{
|
||||
//printf("%04x\n", rsc);
|
||||
new_sc = HRRes->Resample(HRBufs[ch], rsc, espec->SoundBuf + (espec->SoundBufSize * 2) + ch, espec->SoundBufMaxSize - espec->SoundBufSize, espec->NeedSoundReverse);
|
||||
}
|
||||
else
|
||||
{
|
||||
HRBufs[ch]->ResampleSkipped(rsc);
|
||||
new_sc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
espec->NeedSoundReverse = false;
|
||||
|
||||
if(ADPCMBuf)
|
||||
ADPCMBuf->Finish(rsc);
|
||||
|
||||
if(CDDABufs[0])
|
||||
{
|
||||
CDDABufs[0]->Finish(rsc);
|
||||
CDDABufs[1]->Finish(rsc);
|
||||
}
|
||||
|
||||
espec->SoundBufSize += new_sc;
|
||||
|
||||
if(PCE_IsCD)
|
||||
PCECD_ResetTS(end_timestamp_mod12);
|
||||
|
||||
vce->ResetTS(end_timestamp_mod12);
|
||||
|
||||
HuCPU.SyncAndResetTimestamp(end_timestamp_mod12);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
PCE_TimestampBase += end_timestamp - end_timestamp_mod12;
|
||||
espec->MasterCycles += end_timestamp - end_timestamp_mod12;
|
||||
|
||||
if(!rp_rv)
|
||||
MDFN_MidSync(espec);
|
||||
} while(!rp_rv);
|
||||
|
||||
//printf("%d\n", MDFND_GetTime() - t);
|
||||
|
||||
// End loop here.
|
||||
//printf("%d\n", vce->GetScanlineNo());
|
||||
|
||||
vce->EndFrame(&espec->DisplayRect);
|
||||
}
|
||||
|
||||
int StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFARRAY(BaseRAM, IsSGX ? 32768 : 8192),
|
||||
SFVAR(PCE_TimestampBase),
|
||||
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "MAIN", false);
|
||||
|
||||
ret &= HuCPU.StateAction(sm, load, data_only);
|
||||
ret &= vce->StateAction(sm, load, data_only);
|
||||
ret &= psg->StateAction(sm, load, data_only);
|
||||
ret &= INPUT_StateAction(sm, load, data_only);
|
||||
ret &= HuC_StateAction(sm, load, data_only);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PCE_Power(void)
|
||||
{
|
||||
memset(BaseRAM, 0x00, sizeof(BaseRAM));
|
||||
|
||||
HuCPU.Power();
|
||||
PCE_TimestampBase = 0; // FIXME, move to init.
|
||||
const int32 timestamp = HuCPU.Timestamp();
|
||||
|
||||
vce->Reset(timestamp);
|
||||
psg->Power(timestamp / 3);
|
||||
|
||||
HuC_Power();
|
||||
|
||||
PCEINPUT_Power(timestamp);
|
||||
|
||||
if(PCE_IsCD)
|
||||
vce->SetCDEvent(PCECD_Power(timestamp));
|
||||
//printf("%d\n", HuCPU.Timestamp());
|
||||
}
|
||||
|
||||
static void DoSimpleCommand(int cmd)
|
||||
{
|
||||
switch(cmd)
|
||||
{
|
||||
case MDFN_MSC_RESET: PCE_Power(); break;
|
||||
case MDFN_MSC_POWER: PCE_Power(); break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MDFNSetting PCESettings[] =
|
||||
{
|
||||
{ "pce.input.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, "Enable multitap(TurboTap) emulation.", NULL, MDFNST_BOOL, "1" },
|
||||
|
||||
{ "pce.slstart", MDFNSF_NOFLAGS, "First rendered scanline.", NULL, MDFNST_UINT, "4", "0", "239" },
|
||||
{ "pce.slend", MDFNSF_NOFLAGS, "Last rendered scanline.", NULL, MDFNST_UINT, "235", "0", "239" },
|
||||
|
||||
{ "pce.h_overscan", MDFNSF_NOFLAGS, "Show horizontal overscan area.", NULL, MDFNST_BOOL, "0" },
|
||||
|
||||
{ "pce.mouse_sensitivity", MDFNSF_NOFLAGS, "Emulated mouse sensitivity.", NULL, MDFNST_FLOAT, "0.50", NULL, NULL, NULL, PCEINPUT_SettingChanged },
|
||||
{ "pce.disable_softreset", MDFNSF_NOFLAGS, "If set, when RUN+SEL are pressed simultaneously, disable both buttons temporarily.", NULL, MDFNST_BOOL, "0", NULL, NULL, NULL, PCEINPUT_SettingChanged },
|
||||
|
||||
{ "pce.disable_bram_cd", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, "Disable BRAM(saved game memory) for CD games.", "It is intended for viewing CD games' error screens that may be different from simple BRAM full and uninitialized BRAM error screens, though it can cause the game to crash outright.", MDFNST_BOOL, "0" },
|
||||
{ "pce.disable_bram_hucard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, "Disable BRAM(saved game memory) for HuCard games.", "It is intended for changing the behavior(passwords vs save games) of some HuCard games.", MDFNST_BOOL, "0" },
|
||||
|
||||
{ "pce.forcesgx", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, "Force SuperGrafx emulation.",
|
||||
"Enabling this option is not necessary to run unrecognized PCE ROM images in SuperGrafx mode, and enabling it is discouraged; ROM images with a file extension of \".sgx\" will automatically enable SuperGrafx emulation.", MDFNST_BOOL, "0" },
|
||||
|
||||
{ "pce.arcadecard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, "Enable Arcade Card emulation.",
|
||||
"Leaving this option enabled is recommended, unless you want to see special warning screens on ACD games, or you prefer the non-enhanced modes of ACD-enhanced SCD games. Additionally, you may want to disable it you you wish to use state rewinding with a SCD ACD-enhanced game on a slow CPU, as the extra 2MiB of RAM the Arcade Card offers is difficult to compress in real-time.", MDFNST_BOOL, "1" },
|
||||
|
||||
{ "pce.nospritelimit", MDFNSF_NOFLAGS, "Remove 16-sprites-per-scanline hardware limit.",
|
||||
"WARNING: Enabling this option may cause undesirable graphics glitching on some games(such as \"Bloody Wolf\").", MDFNST_BOOL, "0" },
|
||||
|
||||
{ "pce.cdbios", MDFNSF_EMU_STATE, "Path to the CD BIOS", NULL, MDFNST_STRING, "syscard3.pce" },
|
||||
{ "pce.gecdbios", MDFNSF_EMU_STATE, "Path to the GE CD BIOS", "Games Express CD Card BIOS (Unlicensed)", MDFNST_STRING, "gecard.pce" },
|
||||
|
||||
{ "pce.psgrevision", MDFNSF_NOFLAGS, "Select PSG revision.", "WARNING: HES playback will always use the \"huc6280a\" revision if this setting is set to \"match\", since HES playback is always done with SuperGrafx emulation enabled.", MDFNST_ENUM, "match", NULL, NULL, NULL, NULL, PSGRevisionList },
|
||||
|
||||
{ "pce.cdpsgvolume", MDFNSF_NOFLAGS, "PSG volume when playing a CD game.", "Setting this volume control too high may cause sample clipping.", MDFNST_UINT, "100", "0", "200", NULL, CDSettingChanged },
|
||||
{ "pce.cddavolume", MDFNSF_NOFLAGS, "CD-DA volume.", "Setting this volume control too high may cause sample clipping.", MDFNST_UINT, "100", "0", "200", NULL, CDSettingChanged },
|
||||
{ "pce.adpcmvolume", MDFNSF_NOFLAGS, "ADPCM volume.", "Setting this volume control too high may cause sample clipping.", MDFNST_UINT, "100", "0", "200", NULL, CDSettingChanged },
|
||||
{ "pce.adpcmextraprec", MDFNSF_NOFLAGS, "Output the full 12-bit ADPCM predictor.", "Enabling this option causes the MSM5205 ADPCM predictor to be outputted with full precision of 12-bits, rather than only outputting 10-bits of precision(as an actual MSM5205 does). Enable this option to reduce whining noise during ADPCM playback.", MDFNST_BOOL, "0" },
|
||||
|
||||
{ "pce.resamp_quality", MDFNSF_NOFLAGS, "Sound quality.", "Higher values correspond to better SNR and better preservation of higher frequencies(\"brightness\"), at the cost of increased computational complexity and a negligible increase in latency.\n\nHigher values will also slightly increase the probability of sample clipping(relevant if Mednafen's volume control settings are set too high), due to increased (time-domain) ringing.", MDFNST_INT, "3", "0", "5" },
|
||||
{ "pce.resamp_rate_error", MDFNSF_NOFLAGS, "Sound output rate tolerance.", "Lower values correspond to better matching of the output rate of the resampler to the actual desired output rate, at the expense of increased RAM usage and poorer CPU cache utilization.", MDFNST_FLOAT, "0.0000009", "0.0000001", "0.0000350" },
|
||||
|
||||
{ "pce.vramsize", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE | MDFNSF_SUPPRESS_DOC, "Size of emulated VRAM per VDC in 16-bit words. DO NOT CHANGE THIS UNLESS YOU KNOW WTF YOU ARE DOING.", NULL, MDFNST_UINT, "32768", "32768", "65536" },
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static DECLFR(CheatReadFunc)
|
||||
{
|
||||
std::vector<SUBCHEAT>::iterator chit;
|
||||
uint8 retval = NonCheatPCERead[(A / 8192) & 0xFF](A);
|
||||
|
||||
for(chit = SubCheats[A & 0x7].begin(); chit != SubCheats[A & 0x7].end(); chit++)
|
||||
{
|
||||
if(A == chit->addr)
|
||||
{
|
||||
if(chit->compare == -1 || chit->compare == retval)
|
||||
{
|
||||
retval = chit->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
||||
uint8 MemRead(uint32 addr)
|
||||
{
|
||||
return(NonCheatPCERead[(addr / 8192) & 0xFF](addr));
|
||||
}
|
||||
|
||||
static void InstallReadPatch(uint32 address, uint8 value, int compare)
|
||||
{
|
||||
HuCPU.SetFastRead(address >> 13, NULL);
|
||||
HuCPU.SetReadHandler(address >> 13, CheatReadFunc);
|
||||
}
|
||||
|
||||
static void RemoveReadPatches(void)
|
||||
{
|
||||
for(int x = 0; x < 0x100; x++)
|
||||
HuCPU.SetReadHandler(x, NonCheatPCERead[x]);
|
||||
}
|
||||
|
||||
static void SetLayerEnableMask(uint64 mask)
|
||||
{
|
||||
vce->SetLayerEnableMask(mask);
|
||||
}
|
||||
|
||||
static const FileExtensionSpecStruct KnownExtensions[] =
|
||||
{
|
||||
{ ".pce", "PC Engine ROM Image" },
|
||||
{ ".sgx", "SuperGrafx ROM Image" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static bool SetSoundRate(double rate)
|
||||
{
|
||||
if(HRRes)
|
||||
{
|
||||
delete HRRes;
|
||||
HRRes = NULL;
|
||||
}
|
||||
|
||||
if(rate > 0)
|
||||
{
|
||||
HRRes = new OwlResampler(PCE_MASTER_CLOCK / 12, rate, MDFN_GetSettingF("pce.resamp_rate_error"), 20, MDFN_GetSettingUI("pce.resamp_quality"));
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
HRRes->ResetBufResampState(HRBufs[i]);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
MDFNGI EmulatedPCE =
|
||||
{
|
||||
PCESettings,
|
||||
MDFN_MASTERCLOCK_FIXED(PCE_MASTER_CLOCK),
|
||||
0,
|
||||
|
||||
true, // Multires possible?
|
||||
|
||||
0, // lcm_width
|
||||
0, // lcm_height
|
||||
NULL, // Dummy
|
||||
|
||||
512, // Nominal width
|
||||
243, // Nominal height
|
||||
|
||||
1365, // Framebuffer width
|
||||
270, // Framebuffer height
|
||||
|
||||
2, // Number of output sound channels
|
||||
};
|
30
mednafen/pce/pce.h
Normal file
30
mednafen/pce/pce.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef __MDFN_PCE_PCE_H
|
||||
#define __MDFN_PCE_PCE_H
|
||||
|
||||
#include <mednafen/mednafen.h>
|
||||
#include <mednafen/state.h>
|
||||
#include <mednafen/general.h>
|
||||
|
||||
#define PCE_MASTER_CLOCK 21477272.727273
|
||||
|
||||
#define DECLFR(x) MDFN_FASTCALL uint8 x (uint32 A)
|
||||
#define DECLFW(x) MDFN_FASTCALL void x (uint32 A, uint8 V)
|
||||
|
||||
#include "huc6280.h"
|
||||
|
||||
extern HuC6280 HuCPU;
|
||||
extern uint8 BaseRAM[32768];
|
||||
extern MDFNGI EmulatedPCE;
|
||||
|
||||
extern bool PCE_ACEnabled; // Arcade Card emulation enabled?
|
||||
extern void PCE_Power(void);
|
||||
|
||||
extern MDFN_COLD int PCE_Load(MDFNFILE *fp);
|
||||
extern MDFN_COLD int PCE_LoadCD(std::vector<CDIF *> *CDInterfaces);
|
||||
extern void Emulate(EmulateSpecStruct *espec);
|
||||
extern int StateAction(StateMem *sm, int load, int data_only);
|
||||
extern MDFN_COLD void PCE_CloseGame(void);
|
||||
|
||||
extern uint8 MemRead(uint32 addr);
|
||||
|
||||
#endif
|
1249
mednafen/pce/pcecd.cpp
Normal file
1249
mednafen/pce/pcecd.cpp
Normal file
File diff suppressed because it is too large
Load Diff
61
mednafen/pce/pcecd.h
Normal file
61
mednafen/pce/pcecd.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef __MDFN_PCE_PCECD_H
|
||||
#define __MDFN_PCE_PCECD_H
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double CDDA_Volume;
|
||||
double ADPCM_Volume;
|
||||
bool ADPCM_ExtraPrecision;
|
||||
unsigned int CD_Speed;
|
||||
} PCECD_Settings;
|
||||
|
||||
enum
|
||||
{
|
||||
CD_GSREG_BSY = 0,
|
||||
CD_GSREG_REQ, // RO
|
||||
CD_GSREG_MSG, // RO
|
||||
CD_GSREG_CD, // RO
|
||||
CD_GSREG_IO, // RO
|
||||
CD_GSREG_SEL,
|
||||
|
||||
CD_GSREG_ADPCM_CONTROL,
|
||||
CD_GSREG_ADPCM_FREQ,
|
||||
CD_GSREG_ADPCM_CUR,
|
||||
CD_GSREG_ADPCM_WRADDR,
|
||||
CD_GSREG_ADPCM_RDADDR,
|
||||
CD_GSREG_ADPCM_LENGTH,
|
||||
CD_GSREG_ADPCM_PLAYNIBBLE,
|
||||
|
||||
CD_GSREG_ADPCM_PLAYING,
|
||||
CD_GSREG_ADPCM_HALFREACHED,
|
||||
CD_GSREG_ADPCM_ENDREACHED,
|
||||
};
|
||||
|
||||
uint32 PCECD_GetRegister(const unsigned int id, char *special, const uint32 special_len);
|
||||
void PCECD_SetRegister(const unsigned int id, const uint32 value);
|
||||
|
||||
|
||||
int32 MDFN_FASTCALL PCECD_Run(uint32 in_timestamp);
|
||||
void PCECD_ResetTS(uint32 ts_base = 0);
|
||||
void PCECD_ProcessADPCMBuffer(const uint32 rsc);
|
||||
|
||||
void PCECD_Init(const PCECD_Settings *settings, void (*irqcb)(bool), double master_clock, int32* adbuf, int32* hrbuf_l, int32* hrbuf_r) MDFN_COLD;
|
||||
bool PCECD_SetSettings(const PCECD_Settings *settings);
|
||||
|
||||
void PCECD_Close() MDFN_COLD;
|
||||
|
||||
// Returns number of cycles until next CD event.
|
||||
int32 PCECD_Power(uint32 timestamp);
|
||||
|
||||
uint8 MDFN_FASTCALL PCECD_Read(uint32 timestamp, uint32, int32 &next_event, const bool PeekMode = false);
|
||||
int32 MDFN_FASTCALL PCECD_Write(uint32 timestamp, uint32, uint8 data) MDFN_WARN_UNUSED_RESULT;
|
||||
|
||||
bool PCECD_IsBRAMEnabled();
|
||||
|
||||
int PCECD_StateAction(StateMem *sm, const unsigned load, const bool data_only);
|
||||
|
||||
void ADPCM_PeekRAM(uint32 Address, uint32 Length, uint8 *Buffer);
|
||||
void ADPCM_PokeRAM(uint32 Address, uint32 Length, const uint8 *Buffer);
|
||||
|
||||
#endif
|
||||
|
47
mednafen/pce/tsushin.cpp
Normal file
47
mednafen/pce/tsushin.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include "pce.h"
|
||||
|
||||
/*
|
||||
Startup:
|
||||
0x80 -> $1830
|
||||
0x40 -> $1823
|
||||
0x50 -> $1823
|
||||
0x02 -> $1821
|
||||
0x40 -> $1823
|
||||
|
||||
Read from $1822 16 times, expecting (return value & 0xC0) == 0x80 each time
|
||||
|
||||
0x50 -> $1823
|
||||
0x01 -> $1821
|
||||
0x40 -> $1823
|
||||
0x04 -> $1822
|
||||
*/
|
||||
|
||||
DECLFR(PCE_TsushinRead)
|
||||
{
|
||||
//printf("Read: %04x, %04x\n", A, HuCPU.PC);
|
||||
|
||||
return(0x80);
|
||||
}
|
||||
|
||||
DECLFW(PCE_TsushinWrite)
|
||||
{
|
||||
//printf("Write: %04x %02x, %04x\n", A, V, HuCPU.PC);
|
||||
}
|
7
mednafen/pce/tsushin.h
Normal file
7
mednafen/pce/tsushin.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef __MDFN_PCE_TSUSHIN_H
|
||||
#define __MDFN_PCE_TSUSHIN_H
|
||||
|
||||
DECLFR(PCE_TsushinRead);
|
||||
DECLFW(PCE_TsushinWrite);
|
||||
|
||||
#endif
|
1018
mednafen/pce/vce.cpp
Normal file
1018
mednafen/pce/vce.cpp
Normal file
File diff suppressed because it is too large
Load Diff
146
mednafen/pce/vce.h
Normal file
146
mednafen/pce/vce.h
Normal file
@ -0,0 +1,146 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __MDFN_PCE_VCE_H
|
||||
#define __MDFN_PCE_VCE_H
|
||||
|
||||
#include "huc6280.h"
|
||||
#include <mednafen/hw_video/huc6270/vdc.h>
|
||||
|
||||
class VCE final
|
||||
{
|
||||
public:
|
||||
|
||||
VCE(const bool want_sgfx, const uint32 vram_size = 32768) MDFN_COLD;
|
||||
~VCE() MDFN_COLD;
|
||||
|
||||
void SetVDCUnlimitedSprites(const bool nospritelimit);
|
||||
void SetShowHorizOS(bool show);
|
||||
void SetLayerEnableMask(uint64 mask);
|
||||
|
||||
int StateAction(StateMem *sm, const unsigned load, const bool data_only);
|
||||
|
||||
void SetPixelFormat(const uint8* CustomColorMap, const uint32 CustomColorMapLen);
|
||||
|
||||
void StartFrame(MDFN_Surface *surface, MDFN_Rect *DisplayRect, int32 *LineWidths, int skip);
|
||||
bool RunPartial(void);
|
||||
void EndFrame(MDFN_Rect *DisplayRect);
|
||||
|
||||
void Update(const int32 timestamp);
|
||||
|
||||
void INLINE ResetTS(int ts_base)
|
||||
{
|
||||
last_ts = ts_base;
|
||||
}
|
||||
|
||||
int INLINE GetScanlineNo(void)
|
||||
{
|
||||
return(scanline);
|
||||
}
|
||||
|
||||
void Reset(const int32 timestamp);
|
||||
|
||||
void Write(uint32 A, uint8 V);
|
||||
uint8 Read(uint32 A);
|
||||
|
||||
uint8 ReadVDC(uint32 A);
|
||||
void WriteVDC(uint32 A, uint8 V);
|
||||
void WriteVDC_ST(uint32 A, uint8 V);
|
||||
|
||||
void IRQChangeCheck(void);
|
||||
|
||||
bool WS_Hook(int32 vdc_cycles);
|
||||
|
||||
void SetCDEvent(const int32 cycles);
|
||||
|
||||
int32 SyncReal(const int32 timestamp);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
private:
|
||||
|
||||
template<bool TA_SuperGrafx, bool TA_AwesomeMode>
|
||||
void SyncSub(int32 clocks);
|
||||
|
||||
void FixPCache(int entry);
|
||||
void SetVCECR(uint8 V);
|
||||
|
||||
int32 CalcNextEvent(void);
|
||||
int32 child_event[2];
|
||||
|
||||
int32 cd_event;
|
||||
|
||||
uint16 *fb; // Pointer to the framebuffer.
|
||||
uint16 pitch32; // Pitch(in 16-bit pixels)
|
||||
bool FrameDone;
|
||||
bool ShowHorizOS;
|
||||
bool sgfx;
|
||||
|
||||
bool skipframe;
|
||||
int32 *LW;
|
||||
unsigned chip_count; // = 1 when sgfx is false, = 2 when sgfx is true
|
||||
|
||||
int32 clock_divider;
|
||||
|
||||
int32 scanline;
|
||||
uint16 *scanline_out_ptr; // Pointer into fb
|
||||
int32 pixel_offset;
|
||||
|
||||
int32 hblank_counter;
|
||||
int32 vblank_counter;
|
||||
|
||||
bool hblank; // true if in HBLANK, false if not.
|
||||
bool vblank; // true if in vblank, false if not
|
||||
|
||||
bool NeedSLReset;
|
||||
|
||||
uint8 CR; // Control Register
|
||||
bool lc263; // CR->263 line count if set, 262 if not
|
||||
bool bw; // CR->Black and White
|
||||
uint8 dot_clock; // CR->Dot Clock(5, 7, or 10 MHz = 0, 1, 2/3)
|
||||
int32 dot_clock_ratio; // CR->Dot Clock ratio cache
|
||||
|
||||
int32 ws_counter;
|
||||
|
||||
int32 last_ts;
|
||||
|
||||
//
|
||||
// SuperGrafx HuC6202 VPC state
|
||||
//
|
||||
int32 window_counter[2];
|
||||
uint16 winwidths[2];
|
||||
uint8 priority[2];
|
||||
uint8 st_mode;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
uint16 ctaddress;
|
||||
uint32 color_table_cache[0x200 * 2]; // * 2 for user layer disabling stuff.
|
||||
uint16 pixel_buffer[2][2048]; // Internal temporary pixel buffers.
|
||||
uint16 color_table[0x200];
|
||||
uint32 surf_clut[2][512];
|
||||
|
||||
VDC vdc[2];
|
||||
};
|
||||
|
||||
#endif
|
@ -1,11 +0,0 @@
|
||||
int HuCLoad(const uint8 *data, uint32 len) MDFN_COLD;
|
||||
int HuCLoadCD(const char *bios_path) MDFN_COLD;
|
||||
void HuC_Close(void) MDFN_COLD;
|
||||
int HuC_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
void HuC_Power(void) MDFN_COLD;
|
||||
|
||||
DECLFR(PCE_ACRead);
|
||||
DECLFW(PCE_ACWrite);
|
||||
|
||||
extern bool PCE_IsCD;
|
@ -1,781 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "pce.h"
|
||||
#include "vdc.h"
|
||||
|
||||
HuC6280 HuCPU;
|
||||
uint8 *HuCPUFastMap[0x100];
|
||||
|
||||
#define HU_PC PC_local //HuCPU.PC
|
||||
#define HU_PC_base HuCPU.PC_base
|
||||
#define HU_A HuCPU.A
|
||||
#define HU_X X_local //HuCPU.X
|
||||
#define HU_Y Y_local //HuCPU.Y
|
||||
#define HU_S HuCPU.S
|
||||
#define HU_P P_local //HuCPU.P
|
||||
#define HU_PI HuCPU.mooPI
|
||||
#define HU_IRQlow HuCPU.IRQlow
|
||||
#define HU_Page1 Page1_local
|
||||
//HuCPU.Page1
|
||||
//Page1_local //HuCPU.Page1
|
||||
|
||||
#ifdef HUC6280_LAZY_FLAGS
|
||||
#define HU_ZNFlags HuCPU.ZNFlags
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HUC6280_CRAZY_VERSION
|
||||
#define LOAD_LOCALS_PC() register uint8 *PC_local = HuCPU.PC;
|
||||
#else
|
||||
#define LOAD_LOCALS_PC() register uint32 PC_local /*asm ("edi")*/ = HuCPU.PC; // asm ("edi") = HuCPU.PC;
|
||||
#endif
|
||||
|
||||
#define LOAD_LOCALS() \
|
||||
LOAD_LOCALS_PC(); \
|
||||
uint8 X_local = HuCPU.X; \
|
||||
uint8 Y_local = HuCPU.Y; \
|
||||
uint8 P_local = HuCPU.P; \
|
||||
uint8 *Page1_local = HuCPU.Page1;
|
||||
|
||||
#define SAVE_LOCALS() HuCPU.PC = PC_local; \
|
||||
HuCPU.X = X_local; \
|
||||
HuCPU.Y = Y_local; \
|
||||
HuCPU.P = P_local; \
|
||||
HuCPU.Page1 = Page1_local;
|
||||
|
||||
#ifdef HUC6280_LAZY_FLAGS
|
||||
#define COMPRESS_FLAGS() HU_P &= ~(N_FLAG | Z_FLAG); HU_P |= ((HU_ZNFlags >> 24) & 0x80) | ((HU_ZNFlags & 0xFF) ? 0 : Z_FLAG);
|
||||
//((((HU_ZNFlags & 0xFF) - 1) >> 8) & Z_FLAG);
|
||||
#define EXPAND_FLAGS() HU_ZNFlags = (HU_P << 24) | ((HU_P & Z_FLAG) ^ Z_FLAG);
|
||||
#else
|
||||
#define COMPRESS_FLAGS()
|
||||
#define EXPAND_FLAGS()
|
||||
#endif
|
||||
|
||||
#ifdef HUC6280_CRAZY_VERSION
|
||||
#define GetRealPC() ((unsigned int)(HU_PC - HU_PC_base))
|
||||
#define GetRealPC_EXTERNAL() ((unsigned int)(HuCPU.PC - HuCPU.PC_base))
|
||||
#else
|
||||
#define GetRealPC() (HU_PC)
|
||||
#define GetRealPC_EXTERNAL() (HuCPU.PC)
|
||||
#endif
|
||||
|
||||
#ifdef HUC6280_CRAZY_VERSION
|
||||
#define SetPC(value) { unsigned int tempmoo = value; HU_PC = &HuCPU.FastPageR[tempmoo >> 13][tempmoo]; \
|
||||
HU_PC_base = HU_PC - tempmoo; }
|
||||
#define SetPC_EXTERNAL(value) { unsigned int tempmoo = value; \
|
||||
HuCPU.PC = &HuCPU.FastPageR[tempmoo >> 13][tempmoo]; HuCPU.PC_base = HuCPU.PC - tempmoo; }
|
||||
#else
|
||||
#define SetPC(value) { HU_PC = (value); }
|
||||
#define SetPC_EXTERNAL(value) { HuCPU.PC = (value); }
|
||||
#endif
|
||||
|
||||
// Page change PC, GET IT?!
|
||||
#ifdef HUC6280_CRAZY_VERSION
|
||||
#define FixPC_PC() SetPC(GetRealPC());
|
||||
#else
|
||||
#define FixPC_PC()
|
||||
#endif
|
||||
|
||||
//#define IncPC() { HU_PC++; if(!(GetRealPC() & 0x1FFF)) printf("Bank crossing: %04x\n", GetRealPC()); }
|
||||
//#define IncPC() HU_PC++;
|
||||
#if 0
|
||||
#define IncPC() { HU_PC++; if(!(GetRealPC() & 0x1FFF) && \
|
||||
HuCPU.MPR[(GetRealPC() - 1) >> 13] != (HuCPU.MPR[(GetRealPC()) >> 13] - 1)) \
|
||||
printf("Bank crossing: %04x, %02x, %02x\n", GetRealPC(), HuCPU.MPR[(GetRealPC() - 1) >> 13], \
|
||||
HuCPU.MPR[GetRealPC() >> 13]); }
|
||||
#else
|
||||
#define IncPC() HU_PC++;
|
||||
#endif
|
||||
|
||||
#ifdef HUC6280_CRAZY_VERSION
|
||||
#define RdAtPC() (*HU_PC)
|
||||
//#define RdAtAndIncPC_16() (HU_PC += 2, *(uint16 *)(HU_PC - 2))
|
||||
#else
|
||||
#define RdAtPC() RdOp(HU_PC)
|
||||
//#define RdAtAndIncPC_16() (RdOp(HU_PC++) | ((RdOp(HU_PC++) << 8)))
|
||||
#endif
|
||||
|
||||
// If we change this definition, we'll need to also fix HuC6280_StealCycle() in huc6280.h
|
||||
#define ADDCYC(x) { HuCPU.timestamp += x; }
|
||||
|
||||
static uint8 dummy_bank[8192 + 8192]; // + 8192 for PC-as-ptr safety padding
|
||||
|
||||
#define SET_MPR(arg_i, arg_v) \
|
||||
{ \
|
||||
const unsigned int wmpr = arg_i, wbank = arg_v; \
|
||||
if(wmpr == 1) \
|
||||
{ \
|
||||
HU_Page1 = HuCPUFastMap[wbank] ? HuCPUFastMap[wbank] + wbank * 8192 : dummy_bank; \
|
||||
} \
|
||||
HuCPU.MPR[wmpr] = wbank; \
|
||||
HuCPU.FastPageR[wmpr] = HuCPUFastMap[wbank] ? (HuCPUFastMap[wbank] + wbank * 8192) - wmpr * 8192 : (dummy_bank - wmpr * 8192); \
|
||||
}
|
||||
|
||||
void HuC6280_SetMPR(int i, int v)
|
||||
{
|
||||
uint8 *Page1_local = HuCPU.Page1;
|
||||
|
||||
SET_MPR(i, v);
|
||||
|
||||
HuCPU.Page1 = Page1_local;
|
||||
}
|
||||
|
||||
|
||||
static void HuC6280_FlushMPRCache(void)
|
||||
{
|
||||
for(int x = 0; x < 9; x++)
|
||||
HuC6280_SetMPR(x, HuCPU.MPR[x & 0x7]);
|
||||
}
|
||||
|
||||
static INLINE uint8 RdMem(unsigned int A)
|
||||
{
|
||||
uint8 wmpr = HuCPU.MPR[A >> 13];
|
||||
return(PCERead[wmpr]((wmpr << 13) | (A & 0x1FFF)));
|
||||
}
|
||||
|
||||
static INLINE uint16 RdMem16(unsigned int A)
|
||||
{
|
||||
return(RdMem(A) | (RdMem(A + 1) << 8));
|
||||
}
|
||||
|
||||
static INLINE void WrMem(unsigned int A, uint8 V)
|
||||
{
|
||||
uint8 wmpr = HuCPU.MPR[A >> 13];
|
||||
PCEWrite[wmpr]((wmpr << 13) | (A & 0x1FFF), V);
|
||||
}
|
||||
|
||||
static INLINE uint8 RdOp(unsigned int A)
|
||||
{
|
||||
return(HuCPU.FastPageR[A >> 13][A]);
|
||||
}
|
||||
|
||||
#define PUSH(V) \
|
||||
{ \
|
||||
HU_Page1[0x100 + HU_S] = V; \
|
||||
HU_S--; \
|
||||
}
|
||||
|
||||
#define PUSH_PC() \
|
||||
{ \
|
||||
unsigned int real_pc = GetRealPC(); \
|
||||
PUSH(real_pc >> 8); \
|
||||
PUSH(real_pc); \
|
||||
}
|
||||
|
||||
#define POP() HU_Page1[0x100 + ++HU_S]
|
||||
|
||||
#define POP_PC() \
|
||||
{ \
|
||||
unsigned int npc; \
|
||||
npc = POP(); \
|
||||
npc |= POP() << 8; \
|
||||
SetPC(npc); \
|
||||
}
|
||||
|
||||
// Hopefully we never RTS to 0x0000. ;)
|
||||
#define POP_PC_AP() \
|
||||
{ \
|
||||
uint32 npc; \
|
||||
npc = POP(); \
|
||||
npc |= POP() << 8; \
|
||||
npc++; \
|
||||
SetPC(npc); \
|
||||
}
|
||||
|
||||
/* Some of these operations will only make sense if you know what the flag
|
||||
constants are. */
|
||||
|
||||
#ifdef HUC6280_LAZY_FLAGS
|
||||
#define X_ZN(zort) { HU_ZNFlags = (int32)(int8)(uint8)(zort); }
|
||||
#define X_ZN_BIT(opres, argie) { HU_ZNFlags = (opres) | ((argie) << 24); }
|
||||
#else
|
||||
static uint8 ZNTable[256];
|
||||
#define X_ZN(zort) HU_P&=~(Z_FLAG|N_FLAG);HU_P|=ZNTable[zort]
|
||||
#define X_ZN_BIT(opres, argie) { HU_P &= ~(Z_FLAG | N_FLAG); HU_P |= ZNTable[opres] & Z_FLAG; HU_P |= argie & N_FLAG; }
|
||||
#endif
|
||||
|
||||
#define JR(cond) \
|
||||
{ \
|
||||
if(cond) \
|
||||
{ \
|
||||
int32 disp; \
|
||||
disp = 1 + (int8)RdAtPC(); \
|
||||
ADDCYC(2); \
|
||||
HU_PC+=disp; \
|
||||
} \
|
||||
else IncPC(); \
|
||||
}
|
||||
|
||||
#define BRA \
|
||||
{ \
|
||||
int32 disp; \
|
||||
disp = 1 + (int8)RdAtPC(); \
|
||||
HU_PC+=disp; \
|
||||
}
|
||||
|
||||
#define BBRi(bitto) JR(!(x & (1 << bitto)))
|
||||
#define BBSi(bitto) JR(x & (1 << bitto))
|
||||
|
||||
#define ST0 VDC_Write_ST(0, x)
|
||||
#define ST1 VDC_Write_ST(2, x)
|
||||
#define ST2 VDC_Write_ST(3, x)
|
||||
|
||||
#define LDA HU_A=x;X_ZN(HU_A)
|
||||
#define LDX HU_X=x;X_ZN(HU_X)
|
||||
#define LDY HU_Y=x;X_ZN(HU_Y)
|
||||
|
||||
/* All of the freaky arithmetic operations. */
|
||||
#define AND HU_A&=x;X_ZN(HU_A);
|
||||
|
||||
// FIXME:
|
||||
#define BIT HU_P&=~V_FLAG; X_ZN_BIT(x & HU_A, x); HU_P |= x & V_FLAG;
|
||||
#define EOR HU_A^=x;X_ZN(HU_A);
|
||||
#define ORA HU_A|=x;X_ZN(HU_A);
|
||||
|
||||
#define ADC { \
|
||||
if(HU_P & D_FLAG) \
|
||||
{ \
|
||||
uint32 tmp; \
|
||||
tmp = (HU_A & 0x0F) + (x & 0x0F) + (HU_P & 1); \
|
||||
if(tmp >= 0x0A) \
|
||||
tmp += 0x06; \
|
||||
tmp += (HU_A & 0xF0) + (x & 0xF0); \
|
||||
if(tmp >= 0xA0) \
|
||||
tmp += 0x60; \
|
||||
HU_P &= ~C_FLAG; \
|
||||
if(tmp & 0xFF00) \
|
||||
HU_P |= C_FLAG; \
|
||||
HU_A = tmp; \
|
||||
X_ZN(HU_A); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
uint32 l=HU_A+x+(HU_P&1); \
|
||||
HU_P&=~(C_FLAG|V_FLAG); \
|
||||
HU_P|=((((HU_A^x)&0x80)^0x80) & ((HU_A^l)&0x80))>>1; \
|
||||
HU_P|=(l>>8)&C_FLAG; \
|
||||
HU_A=l; \
|
||||
X_ZN(HU_A); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SBC if(HU_P & D_FLAG) \
|
||||
{ \
|
||||
const uint8 m = (HU_A & 0xF) - (x & 0xF) - ((HU_P & 1) ^ 1); \
|
||||
const uint8 n = (HU_A >> 4) - (x >> 4) - ((m >> 4) & 1); \
|
||||
uint8 res = (n << 4) | (m & 0xF); \
|
||||
if(m & 0x10) \
|
||||
res -= 0x06; \
|
||||
if(n & 0x10) \
|
||||
res -= 0x60; \
|
||||
HU_A = res; \
|
||||
HU_P &= ~C_FLAG; \
|
||||
HU_P |= ((n >> 4) & 0x1) ^ 1; \
|
||||
X_ZN(HU_A); \
|
||||
} else { \
|
||||
uint32 l=HU_A-x-((HU_P&1)^1); \
|
||||
HU_P&=~(C_FLAG|V_FLAG); \
|
||||
HU_P|=((HU_A^l)&(HU_A^x)&0x80)>>1; \
|
||||
HU_P|=((l>>8)&C_FLAG)^C_FLAG; \
|
||||
HU_A=l; \
|
||||
X_ZN(HU_A); \
|
||||
}
|
||||
|
||||
#define CMPL(a1,a2) { \
|
||||
uint32 t=a1-a2; \
|
||||
X_ZN(t&0xFF); \
|
||||
HU_P&=~C_FLAG; \
|
||||
HU_P|=((t>>8)&C_FLAG)^C_FLAG; \
|
||||
}
|
||||
|
||||
#define TAM for(int i = 0; i < 8; i ++) { \
|
||||
if(x & (1 << i)) \
|
||||
{ \
|
||||
SET_MPR(i, HU_A); \
|
||||
} \
|
||||
} SET_MPR(8, HuCPU.MPR[0]);
|
||||
|
||||
#define TMA for(int i = 0; i < 8; i ++) { \
|
||||
if(x & (1 << i)) \
|
||||
HU_A = HuCPU.MPR[i]; \
|
||||
}
|
||||
|
||||
#define CSL
|
||||
#define CSH
|
||||
|
||||
#define RMB(bitto) x &= ~(1 << (bitto & 7))
|
||||
#define SMB(bitto) x |= 1 << (bitto & 7)
|
||||
|
||||
// FIXME
|
||||
#define TSB { HU_P &= ~V_FLAG; X_ZN_BIT(x | HU_A, x); HU_P |= x & V_FLAG; x |= HU_A; }
|
||||
#define TRB { HU_P &= ~V_FLAG; X_ZN_BIT(x & ~HU_A, x); HU_P |= x & V_FLAG; x &= ~HU_A; }
|
||||
#define TST { HU_P &= ~V_FLAG; X_ZN_BIT(x & zoomhack, x); HU_P |= x & V_FLAG; }
|
||||
|
||||
#define CMP CMPL(HU_A,x)
|
||||
#define CPX CMPL(HU_X,x)
|
||||
#define CPY CMPL(HU_Y,x)
|
||||
|
||||
/* The following operations modify the byte being worked on. */
|
||||
#define DEC x--;X_ZN(x)
|
||||
#define INC x++;X_ZN(x)
|
||||
|
||||
#define ASL HU_P&=~C_FLAG;HU_P|=x>>7;x<<=1;X_ZN(x)
|
||||
#define LSR HU_P&=~C_FLAG;HU_P|=x&1;x>>=1;X_ZN(x)
|
||||
|
||||
#define ROL { \
|
||||
uint8 l=x>>7; \
|
||||
x<<=1; \
|
||||
x|=HU_P&C_FLAG; \
|
||||
HU_P&=~C_FLAG; \
|
||||
HU_P|=l; \
|
||||
X_ZN(x); \
|
||||
}
|
||||
#define ROR { \
|
||||
uint8 l=x&1; \
|
||||
x>>=1; \
|
||||
x|=(HU_P&C_FLAG)<<7; \
|
||||
HU_P&=~C_FLAG; \
|
||||
HU_P|=l; \
|
||||
X_ZN(x); \
|
||||
}
|
||||
|
||||
/* Absolute */
|
||||
#define GetAB(target) \
|
||||
{ \
|
||||
target=RdAtPC(); \
|
||||
IncPC(); \
|
||||
target|=RdAtPC()<<8; \
|
||||
IncPC(); \
|
||||
}
|
||||
|
||||
/* Absolute Indexed(for reads) */
|
||||
#define GetABI(target, i) \
|
||||
{ \
|
||||
unsigned int tmp; \
|
||||
GetAB(tmp); \
|
||||
target=tmp; \
|
||||
target+=i; \
|
||||
}
|
||||
|
||||
/* Zero Page */
|
||||
#define GetZP(target) \
|
||||
{ \
|
||||
target=RdAtPC(); \
|
||||
IncPC(); \
|
||||
}
|
||||
|
||||
/* Zero Page Indexed */
|
||||
#define GetZPI(target,i) \
|
||||
{ \
|
||||
target=i+RdAtPC(); \
|
||||
IncPC(); \
|
||||
}
|
||||
|
||||
/* Indirect */
|
||||
#define GetIND(target) \
|
||||
{ \
|
||||
uint8 tmp; \
|
||||
tmp=RdAtPC(); \
|
||||
IncPC(); \
|
||||
target=HU_Page1[tmp]; \
|
||||
tmp++; \
|
||||
target|=HU_Page1[tmp]<<8; \
|
||||
}
|
||||
|
||||
|
||||
/* Indexed Indirect */
|
||||
#define GetIX(target) \
|
||||
{ \
|
||||
uint8 tmp; \
|
||||
tmp=RdAtPC(); \
|
||||
IncPC(); \
|
||||
tmp+=HU_X; \
|
||||
target=HU_Page1[tmp]; \
|
||||
tmp++; \
|
||||
target|=HU_Page1[tmp] <<8; \
|
||||
}
|
||||
|
||||
/* Indirect Indexed(for reads) */
|
||||
#define GetIY(target) \
|
||||
{ \
|
||||
unsigned int rt; \
|
||||
uint8 tmp; \
|
||||
tmp=RdAtPC(); \
|
||||
rt=HU_Page1[tmp]; \
|
||||
tmp++; \
|
||||
rt|=HU_Page1[tmp]<<8; \
|
||||
target = (rt + HU_Y); \
|
||||
IncPC(); \
|
||||
}
|
||||
|
||||
/* Now come the macros to wrap up all of the above stuff addressing mode functions
|
||||
and operation macros. Note that operation macros will always operate(redundant
|
||||
redundant) on the variable "x".
|
||||
*/
|
||||
|
||||
#define RMW_A(op) {uint8 x=HU_A; op; HU_A=x; break; } /* Meh... */
|
||||
#define RMW_AB(op) {unsigned int EA; uint8 x; GetAB(EA); x=RdMem(EA); op; WrMem(EA,x); break; }
|
||||
#define RMW_ABI(reg,op) {unsigned int EA; uint8 x; GetABI(EA,reg); x=RdMem(EA); op; WrMem(EA,x); break; }
|
||||
#define RMW_ABX(op) RMW_ABI(HU_X,op)
|
||||
#define RMW_ABY(op) RMW_ABI(HU_Y,op)
|
||||
#define RMW_IND(op) { unsigned int EA; uint8 x; GetIND(EA); x = RdMem(EA); op; WrMem(EA, x); break; }
|
||||
#define RMW_IX(op) { unsigned int EA; uint8 x; GetIX(EA); x=RdMem(EA); op; WrMem(EA,x); break; }
|
||||
#define RMW_IY(op) { unsigned int EA; uint8 x; GetIY(EA); x=RdMem(EA); op; WrMem(EA,x); break; }
|
||||
#define RMW_ZP(op) { uint8 EA; uint8 x; GetZP(EA); x=HU_Page1[EA]; op; HU_Page1[EA] = x; break; }
|
||||
#define RMW_ZPX(op) { uint8 EA; uint8 x; GetZPI(EA,HU_X); x=HU_Page1[EA]; op; HU_Page1[EA] = x; break;}
|
||||
|
||||
#define LD_IM(op) { uint8 x; x=RdAtPC(); IncPC(); op; break; }
|
||||
#define LD_ZP(op) { uint8 EA; uint8 x; GetZP(EA); x=HU_Page1[EA]; op; break; }
|
||||
#define LD_ZPX(op) { uint8 EA; uint8 x; GetZPI(EA,HU_X); x=HU_Page1[EA]; op; break; }
|
||||
#define LD_ZPY(op) { uint8 EA; uint8 x; GetZPI(EA,HU_Y); x=HU_Page1[EA]; op; break; }
|
||||
#define LD_AB(op) { unsigned int EA; uint8 x; GetAB(EA); x=RdMem(EA); op; break; }
|
||||
#define LD_ABI(reg,op) { unsigned int EA; uint8 x; GetABI(EA,reg); x=RdMem(EA); op; break; }
|
||||
#define LD_ABX(op) LD_ABI(HU_X,op)
|
||||
#define LD_ABY(op) LD_ABI(HU_Y,op)
|
||||
|
||||
#define LD_IND(op) { unsigned int EA; uint8 x; GetIND(EA); x=RdMem(EA); op; break; }
|
||||
#define LD_IX(op) { unsigned int EA; uint8 x; GetIX(EA); x=RdMem(EA); op; break; }
|
||||
#define LD_IY(op) { unsigned int EA; uint8 x; GetIY(EA); x=RdMem(EA); op; break; }
|
||||
|
||||
#define BMT_PREHONK(pork) HuCPU.in_block_move = IBM_##pork;
|
||||
#define BMT_HONKHONK(pork) if(HuCPU.timestamp >= next_user_event) goto GetOutBMT; continue_the_##pork:
|
||||
|
||||
#define BMT_TDD BMT_PREHONK(TDD); do { ADDCYC(6); WrMem(HuCPU.bmt_dest, RdMem(HuCPU.bmt_src)); HuCPU.bmt_src--; HuCPU.bmt_dest--; BMT_HONKHONK(TDD); HuCPU.bmt_length--; } while(HuCPU.bmt_length);
|
||||
#define BMT_TAI BMT_PREHONK(TAI); {HuCPU.bmt_alternate = 0; do { ADDCYC(6); WrMem(HuCPU.bmt_dest, RdMem(HuCPU.bmt_src + HuCPU.bmt_alternate)); HuCPU.bmt_dest++; HuCPU.bmt_alternate ^= 1; BMT_HONKHONK(TAI); HuCPU.bmt_length--; } while(HuCPU.bmt_length); }
|
||||
#define BMT_TIA BMT_PREHONK(TIA); {HuCPU.bmt_alternate = 0; do { ADDCYC(6); WrMem(HuCPU.bmt_dest + HuCPU.bmt_alternate, RdMem(HuCPU.bmt_src)); HuCPU.bmt_src++; HuCPU.bmt_alternate ^= 1; BMT_HONKHONK(TIA); HuCPU.bmt_length--; } while(HuCPU.bmt_length); }
|
||||
#define BMT_TII BMT_PREHONK(TII); do { ADDCYC(6); WrMem(HuCPU.bmt_dest, RdMem(HuCPU.bmt_src)); HuCPU.bmt_src++; HuCPU.bmt_dest++; BMT_HONKHONK(TII); HuCPU.bmt_length--; } while(HuCPU.bmt_length);
|
||||
#define BMT_TIN BMT_PREHONK(TIN); do { ADDCYC(6); WrMem(HuCPU.bmt_dest, RdMem(HuCPU.bmt_src)); HuCPU.bmt_src++; BMT_HONKHONK(TIN); HuCPU.bmt_length--; } while(HuCPU.bmt_length);
|
||||
|
||||
// Block memory transfer load
|
||||
#define LD_BMT(op) { PUSH(HU_Y); PUSH(HU_A); PUSH(HU_X); GetAB(HuCPU.bmt_src); GetAB(HuCPU.bmt_dest); GetAB(HuCPU.bmt_length); op; HuCPU.in_block_move = 0; HU_X = POP(); HU_A = POP(); HU_Y = POP(); break; }
|
||||
|
||||
#define ST_ZP(r) {uint8 EA; GetZP(EA); HU_Page1[EA] = r; break;}
|
||||
#define ST_ZPX(r) {uint8 EA; GetZPI(EA,HU_X); HU_Page1[EA] = r; break;}
|
||||
#define ST_ZPY(r) {uint8 EA; GetZPI(EA,HU_Y); HU_Page1[EA] = r; break;}
|
||||
#define ST_AB(r) {unsigned int EA; GetAB(EA); WrMem(EA, r); break;}
|
||||
#define ST_ABI(reg,r) {unsigned int EA; GetABI(EA,reg); WrMem(EA,r); break; }
|
||||
#define ST_ABX(r) ST_ABI(HU_X,r)
|
||||
#define ST_ABY(r) ST_ABI(HU_Y,r)
|
||||
|
||||
#define ST_IND(r) {unsigned int EA; GetIND(EA); WrMem(EA,r); break; }
|
||||
#define ST_IX(r) {unsigned int EA; GetIX(EA); WrMem(EA,r); break; }
|
||||
#define ST_IY(r) {unsigned int EA; GetIY(EA); WrMem(EA,r); break; }
|
||||
|
||||
static const uint8 CycTable[256] =
|
||||
{
|
||||
/*0x00*/ 8, 7, 3, 4, 6, 4, 6, 7, 3, 2, 2, 2, 7, 5, 7, 6,
|
||||
/*0x10*/ 2, 7, 7, 4, 6, 4, 6, 7, 2, 5, 2, 2, 7, 5, 7, 6,
|
||||
/*0x20*/ 7, 7, 3, 4, 4, 4, 6, 7, 4, 2, 2, 2, 5, 5, 7, 6,
|
||||
/*0x30*/ 2, 7, 7, 2, 4, 4, 6, 7, 2, 5, 2, 2, 5, 5, 7, 6,
|
||||
/*0x40*/ 7, 7, 3, 4, 8, 4, 6, 7, 3, 2, 2, 2, 4, 5, 7, 6,
|
||||
/*0x50*/ 2, 7, 7, 5, 3, 4, 6, 7, 2, 5, 3, 2, 2, 5, 7, 6,
|
||||
/*0x60*/ 7, 7, 2, 2, 4, 4, 6, 7, 4, 2, 2, 2, 7, 5, 7, 6,
|
||||
/*0x70*/ 2, 7, 7, 17, 4, 4, 6, 7, 2, 5, 4, 2, 7, 5, 7, 6,
|
||||
|
||||
/*0x80*/ 4, 7, 2, 7, 4, 4, 4, 7, 2, 2, 2, 2, 5, 5, 5, 6,
|
||||
/*0x90*/ 2, 7, 7, 8, 4, 4, 4, 7, 2, 5, 2, 2, 5, 5, 5, 6,
|
||||
/*0xA0*/ 2, 7, 2, 7, 4, 4, 4, 7, 2, 2, 2, 2, 5, 5, 5, 6,
|
||||
/*0xB0*/ 2, 7, 7, 8, 4, 4, 4, 7, 2, 5, 2, 2, 5, 5, 5, 6,
|
||||
/*0xC0*/ 2, 7, 2, 17, 4, 4, 6, 7, 2, 2, 2, 2, 5, 5, 7, 6,
|
||||
/*0xD0*/ 2, 7, 7, 17, 3, 4, 6, 7, 2, 5, 3, 2, 2, 5, 7, 6,
|
||||
/*0xE0*/ 2, 7, 2, 17, 4, 4, 6, 7, 2, 2, 2, 2, 5, 5, 7, 6,
|
||||
/*0xF0*/ 2, 7, 7, 17, 2, 4, 6, 7, 2, 5, 4, 2, 2, 5, 7, 6,
|
||||
};
|
||||
#if 0
|
||||
static bool WillIRQOccur(void) NO_INLINE;
|
||||
static bool WillIRQOccur(void)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if(HU_IRQlow)
|
||||
{
|
||||
if(!(HU_PI&I_FLAG))
|
||||
{
|
||||
if(HU_IRQlow & MDFN_IQTIMER & HuCPU.IRQMaskDelay)
|
||||
ret = true;
|
||||
else if((HU_IRQlow & MDFN_IQIRQ1 & HuCPU.IRQMaskDelay) || ((HU_IRQlow >> 8) & MDFN_IQIRQ1 & HuCPU.IRQMaskDelay))
|
||||
ret = true;
|
||||
else if(HU_IRQlow & MDFN_IQIRQ2 & HuCPU.IRQMaskDelay)
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
void HuC6280_IRQBegin(int w)
|
||||
{
|
||||
HU_IRQlow|=w;
|
||||
}
|
||||
|
||||
void HuC6280_IRQEnd(int w)
|
||||
{
|
||||
HU_IRQlow&=~w;
|
||||
}
|
||||
|
||||
void HuC6280_Reset(void)
|
||||
{
|
||||
HuCPU.timer_next_timestamp = HuCPU.timestamp + 1024;
|
||||
|
||||
HuCPU.timer_load = 0;
|
||||
HuCPU.timer_value = 0;
|
||||
HuCPU.timer_status = 0;
|
||||
HuCPU.in_block_move = 0;
|
||||
|
||||
unsigned int npc;
|
||||
|
||||
HuCPU.IRQMask = HuCPU.IRQMaskDelay = 7;
|
||||
|
||||
HuC6280_SetMPR(0, 0xFF);
|
||||
HuC6280_SetMPR(8, 0xFF);
|
||||
HuC6280_SetMPR(1, 0xF8);
|
||||
|
||||
for(int i = 2; i < 8; i++)
|
||||
HuC6280_SetMPR(i, 0);
|
||||
|
||||
npc = RdMem16(0xFFFE);
|
||||
|
||||
#define PC_local HuCPU.PC
|
||||
SetPC(npc);
|
||||
#undef PC_local
|
||||
|
||||
HuCPU.mooPI = I_FLAG;
|
||||
HuCPU.P = I_FLAG;
|
||||
|
||||
HU_IRQlow = 0;
|
||||
}
|
||||
|
||||
void HuC6280_Init(void)
|
||||
{
|
||||
memset((void *)&HuCPU,0,sizeof(HuCPU));
|
||||
memset(dummy_bank, 0, sizeof(dummy_bank));
|
||||
|
||||
#ifdef HUC6280_LAZY_FLAGS
|
||||
|
||||
#else
|
||||
for(int x=0; x < 256; x++)
|
||||
if(!x) ZNTable[x]=Z_FLAG;
|
||||
else if (x&0x80) ZNTable[x]=N_FLAG;
|
||||
else ZNTable[x]=0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void HuC6280_Power(void)
|
||||
{
|
||||
HuCPU.IRQlow = 0;
|
||||
|
||||
HuCPU.A = 0;
|
||||
HuCPU.X = 0;
|
||||
HuCPU.Y = 0;
|
||||
HuCPU.S = 0;
|
||||
HuCPU.P = 0;
|
||||
HuCPU.mooPI = 0;
|
||||
|
||||
#if 0
|
||||
HU_PC = HU_PC_base = NULL;
|
||||
#else
|
||||
HuCPU.PC = 0;
|
||||
#endif
|
||||
|
||||
HuCPU.timestamp = 0;
|
||||
|
||||
for(int i = 0; i < 9; i++)
|
||||
{
|
||||
HuCPU.MPR[i] = 0;
|
||||
HuCPU.FastPageR[i] = NULL;
|
||||
}
|
||||
HuC6280_Reset();
|
||||
}
|
||||
|
||||
void HuC6280_Run(int32 cycles)
|
||||
{
|
||||
const int32 next_user_event = HuCPU.previous_next_user_event + cycles * pce_overclocked;
|
||||
|
||||
HuCPU.previous_next_user_event = next_user_event;
|
||||
|
||||
LOAD_LOCALS();
|
||||
|
||||
if(HuCPU.timestamp >= next_user_event)
|
||||
return;
|
||||
|
||||
int32 next_event;
|
||||
|
||||
if(HuCPU.in_block_move)
|
||||
{
|
||||
next_event = (next_user_event < HuCPU.timer_next_timestamp) ? next_user_event : HuCPU.timer_next_timestamp;
|
||||
|
||||
switch(HuCPU.in_block_move)
|
||||
{
|
||||
case IBM_TIA: goto continue_the_TIA;
|
||||
case IBM_TAI: goto continue_the_TAI;
|
||||
case IBM_TDD: goto continue_the_TDD;
|
||||
case IBM_TII: goto continue_the_TII;
|
||||
case IBM_TIN: goto continue_the_TIN;
|
||||
}
|
||||
}
|
||||
|
||||
while(MDFN_LIKELY(HuCPU.timestamp < next_user_event))
|
||||
{
|
||||
next_event = (next_user_event < HuCPU.timer_next_timestamp) ? next_user_event : HuCPU.timer_next_timestamp;
|
||||
|
||||
while(MDFN_LIKELY(HuCPU.timestamp < next_event))
|
||||
{
|
||||
uint8 b1;
|
||||
|
||||
if(HU_IRQlow)
|
||||
{
|
||||
if(!(HU_PI&I_FLAG))
|
||||
{
|
||||
uint32 tmpa = 0;
|
||||
|
||||
if(HU_IRQlow & MDFN_IQTIMER & HuCPU.IRQMaskDelay)
|
||||
tmpa = 0xFFFA;
|
||||
else if((HU_IRQlow & MDFN_IQIRQ1 & HuCPU.IRQMaskDelay) || ((HU_IRQlow >> 8) & MDFN_IQIRQ1 & HuCPU.IRQMaskDelay))
|
||||
tmpa = 0xFFF8;
|
||||
else if(HU_IRQlow & MDFN_IQIRQ2 & HuCPU.IRQMaskDelay)
|
||||
tmpa = 0xFFF6;
|
||||
|
||||
if(tmpa)
|
||||
{
|
||||
unsigned int npc;
|
||||
|
||||
ADDCYC(8);
|
||||
PUSH_PC();
|
||||
|
||||
COMPRESS_FLAGS();
|
||||
PUSH((HU_P&~B_FLAG));
|
||||
HU_P |= I_FLAG;
|
||||
HU_P &= ~(T_FLAG | D_FLAG);
|
||||
HU_PI = HU_P;
|
||||
|
||||
npc = RdMem16(tmpa);
|
||||
SetPC(npc);
|
||||
|
||||
if(tmpa == 0xFFF8)
|
||||
HU_IRQlow &= ~0x200;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} // end if(HU_IRQlow)
|
||||
|
||||
//printf("%04x\n", GetRealPC());
|
||||
HU_PI = HU_P;
|
||||
HuCPU.IRQMaskDelay = HuCPU.IRQMask;
|
||||
|
||||
b1 = RdAtPC();
|
||||
|
||||
ADDCYC(CycTable[b1]);
|
||||
|
||||
IncPC();
|
||||
|
||||
switch(b1)
|
||||
{
|
||||
#include "huc6280_ops.inc"
|
||||
}
|
||||
|
||||
#ifndef HUC6280_EXTRA_CRAZY
|
||||
FixPC_PC();
|
||||
#endif
|
||||
} // end while(HuCPU.timestamp < next_event)
|
||||
|
||||
while(HuCPU.timestamp >= HuCPU.timer_next_timestamp)
|
||||
{
|
||||
HuCPU.timer_next_timestamp += 1024 * pce_overclocked;
|
||||
|
||||
if(HuCPU.timer_status)
|
||||
{
|
||||
HuCPU.timer_value --;
|
||||
if(HuCPU.timer_value < 0)
|
||||
{
|
||||
HuCPU.timer_value = HuCPU.timer_load;
|
||||
HuC6280_IRQBegin(MDFN_IQTIMER);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end while(HuCPU.timestamp < next_user_event)
|
||||
|
||||
GetOutBMT:
|
||||
|
||||
SAVE_LOCALS();
|
||||
}
|
||||
|
||||
void HuC6280_ResetTS(void)
|
||||
{
|
||||
HuCPU.timer_next_timestamp -= HuCPU.timestamp;
|
||||
HuCPU.previous_next_user_event -= HuCPU.timestamp;
|
||||
HuCPU.timestamp = 0;
|
||||
}
|
||||
|
||||
int HuC6280_StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
uint16 tmp_PC = GetRealPC_EXTERNAL();
|
||||
|
||||
#define P_local HuCPU.P
|
||||
COMPRESS_FLAGS();
|
||||
|
||||
SFORMAT SFCPU[]=
|
||||
{
|
||||
SFVARN(tmp_PC, "PC"),
|
||||
SFVARN(HuCPU.A, "A"),
|
||||
SFVARN(HuCPU.P, "P"),
|
||||
SFVARN(HuCPU.X, "X"),
|
||||
SFVARN(HuCPU.Y, "Y"),
|
||||
SFVARN(HuCPU.S, "S"),
|
||||
SFVARN(HuCPU.mooPI, "PI"),
|
||||
|
||||
SFVARN(HuCPU.IRQMask, "IRQMask"),
|
||||
SFVARN(HuCPU.IRQMaskDelay, "IRQMaskDelay"),
|
||||
SFARRAYN(HuCPU.MPR, 8, "MPR"),
|
||||
SFVARN(HuCPU.timer_status, "timer_status"),
|
||||
SFVARN(HuCPU.timer_value, "timer_value"),
|
||||
SFVARN(HuCPU.timer_load, "timer_load"),
|
||||
|
||||
|
||||
SFVARN(HuCPU.IRQlow, "IRQlow"),
|
||||
SFVARN(HuCPU.in_block_move, "IBM"),
|
||||
SFVARN(HuCPU.bmt_src, "IBM_SRC"),
|
||||
SFVARN(HuCPU.bmt_dest, "IBM_DEST"),
|
||||
SFVARN(HuCPU.bmt_length, "IBM_LENGTH"),
|
||||
SFVARN(HuCPU.bmt_alternate, "IBM_ALTERNATE"),
|
||||
|
||||
SFVARN(HuCPU.timestamp, "timestamp"),
|
||||
SFVARN(HuCPU.timer_next_timestamp, "timer_next_timestamp"),
|
||||
SFVARN(HuCPU.previous_next_user_event, "previous_next_user_event"),
|
||||
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, SFCPU, "CPU", false);
|
||||
|
||||
if(load)
|
||||
{
|
||||
// Update MPR cache
|
||||
HuC6280_FlushMPRCache();
|
||||
|
||||
// This must be after the MPR cache is updated:
|
||||
SetPC_EXTERNAL(tmp_PC);
|
||||
}
|
||||
|
||||
EXPAND_FLAGS();
|
||||
#undef P_local
|
||||
|
||||
return(ret);
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
#ifndef _HuC6280H
|
||||
|
||||
#define HUC6280_CRAZY_VERSION
|
||||
//#define HUC6280_EXTRA_CRAZY
|
||||
|
||||
#define HUC6280_LAZY_FLAGS
|
||||
|
||||
typedef struct __HuC6280
|
||||
{
|
||||
#ifdef HUC6280_CRAZY_VERSION
|
||||
uint8 *PC, *PC_base;
|
||||
#else
|
||||
uint16 PC;
|
||||
#endif
|
||||
|
||||
uint8 A,X,Y,S,P,mooPI;
|
||||
#ifdef HUC6280_LAZY_FLAGS
|
||||
uint32 ZNFlags;
|
||||
#endif
|
||||
uint8 MPR[9]; // 8, + 1 for PC overflow from $ffff to $10000
|
||||
uint8 *FastPageR[9];
|
||||
uint8 *Page1;
|
||||
//uint8 *PAGE1_W;
|
||||
//const uint8 *PAGE1_R;
|
||||
|
||||
uint32 IRQlow; /* Simulated IRQ pin held low(or is it high?).
|
||||
And other junk hooked on for speed reasons.*/
|
||||
int32 timestamp;
|
||||
|
||||
uint8 IRQMask, IRQMaskDelay;
|
||||
uint8 timer_status;
|
||||
int32 timer_value, timer_load;
|
||||
int32 timer_next_timestamp;
|
||||
|
||||
uint32 in_block_move;
|
||||
uint16 bmt_src, bmt_dest, bmt_length;
|
||||
uint32 bmt_alternate;
|
||||
#define IBM_TIA 1
|
||||
#define IBM_TAI 2
|
||||
#define IBM_TDD 3
|
||||
#define IBM_TII 4
|
||||
#define IBM_TIN 5
|
||||
|
||||
int32 previous_next_user_event;
|
||||
} HuC6280;
|
||||
|
||||
void HuC6280_Run(int32 cycles);
|
||||
void HuC6280_ResetTS(void);
|
||||
|
||||
extern HuC6280 HuCPU;
|
||||
extern uint8 *HuCPUFastMap[0x100];
|
||||
|
||||
#define N_FLAG 0x80
|
||||
#define V_FLAG 0x40
|
||||
#define T_FLAG 0x20
|
||||
#define B_FLAG 0x10
|
||||
#define D_FLAG 0x08
|
||||
#define I_FLAG 0x04
|
||||
#define Z_FLAG 0x02
|
||||
#define C_FLAG 0x01
|
||||
|
||||
#define NTSC_CPU 1789772.7272727272727272
|
||||
#define PAL_CPU 1662607.125
|
||||
|
||||
#define MDFN_IQIRQ1 0x002
|
||||
#define MDFN_IQIRQ2 0x001
|
||||
#define MDFN_IQTIMER 0x004
|
||||
#define MDFN_IQRESET 0x020
|
||||
|
||||
void HuC6280_Init(void) MDFN_COLD;
|
||||
void HuC6280_Reset(void) MDFN_COLD;
|
||||
void HuC6280_Power(void) MDFN_COLD;
|
||||
|
||||
void HuC6280_IRQBegin(int w);
|
||||
void HuC6280_IRQEnd(int w);
|
||||
|
||||
int HuC6280_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
static INLINE void HuC6280_StealCycle(void)
|
||||
{
|
||||
HuCPU.timestamp++;
|
||||
}
|
||||
|
||||
static INLINE uint8 HuC6280_TimerRead(unsigned int A)
|
||||
{
|
||||
#if 0
|
||||
return(HuCPU.timer_value | (PCEIODataBuffer & 0x80));
|
||||
#endif
|
||||
|
||||
uint8 tvr = HuCPU.timer_value;
|
||||
|
||||
if(HuCPU.timer_next_timestamp == HuCPU.timestamp)
|
||||
tvr = (tvr - 1) & 0x7F;
|
||||
|
||||
return(tvr | (PCEIODataBuffer & 0x80));
|
||||
}
|
||||
|
||||
static INLINE void HuC6280_TimerWrite(unsigned int A, uint8 V)
|
||||
{
|
||||
switch(A & 1)
|
||||
{
|
||||
case 0: HuCPU.timer_load = (V & 0x7F); break;
|
||||
case 1: if(V & 1) // Enable counter
|
||||
{
|
||||
if(HuCPU.timer_status == 0)
|
||||
{
|
||||
HuCPU.timer_next_timestamp = HuCPU.timestamp + 1024;
|
||||
HuCPU.timer_value = HuCPU.timer_load;
|
||||
}
|
||||
}
|
||||
HuCPU.timer_status = V & 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE uint8 HuC6280_IRQStatusRead(unsigned int A)
|
||||
{
|
||||
if(!(A & 2))
|
||||
return(PCEIODataBuffer);
|
||||
|
||||
switch(A & 1)
|
||||
{
|
||||
case 0:
|
||||
HuC6280_IRQEnd(MDFN_IQTIMER);
|
||||
return(HuCPU.IRQMask ^ 0x7);
|
||||
case 1:
|
||||
{
|
||||
int status = 0;
|
||||
if(HuCPU.IRQlow & MDFN_IQIRQ1) status |= 2;
|
||||
if(HuCPU.IRQlow & MDFN_IQIRQ2) status |= 1;
|
||||
if(HuCPU.IRQlow & MDFN_IQTIMER) status |= 4;
|
||||
return(status | (PCEIODataBuffer & ~(1 | 2 | 4)));
|
||||
}
|
||||
}
|
||||
return(PCEIODataBuffer);
|
||||
}
|
||||
|
||||
static INLINE void HuC6280_IRQStatusWrite(unsigned int A, uint8 V)
|
||||
{
|
||||
if(!(A & 2)) return;
|
||||
switch(A & 1)
|
||||
{
|
||||
case 0: HuCPU.IRQMask = (V & 0x7) ^ 0x7; break;
|
||||
case 1: HuC6280_IRQEnd(MDFN_IQTIMER); break;
|
||||
}
|
||||
}
|
||||
|
||||
#define _HuC6280H
|
||||
#endif
|
@ -1,635 +0,0 @@
|
||||
/* Mednafen - NES/Famicom Emulator
|
||||
*
|
||||
* Copyright notice for this file:
|
||||
* Copyright (C) 2002 Xodnizel
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define TEST_WEIRD_TFLAG(n) { if(HU_P & T_FLAG) puts("RAWR" n); }
|
||||
|
||||
case 0x00: /* BRK */
|
||||
IncPC();
|
||||
HU_P &= ~T_FLAG;
|
||||
PUSH_PC();
|
||||
|
||||
COMPRESS_FLAGS();
|
||||
PUSH(HU_P|B_FLAG);
|
||||
HU_P|=I_FLAG;
|
||||
HU_P &= ~D_FLAG;
|
||||
HU_PI|=I_FLAG;
|
||||
{
|
||||
unsigned int npc;
|
||||
|
||||
npc=RdOp(0xFFF6);
|
||||
npc|=RdOp(0xFFF7)<<8;
|
||||
|
||||
SetPC(npc);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x40: /* RTI */
|
||||
HU_P = POP();
|
||||
EXPAND_FLAGS();
|
||||
/* HU_PI=HU_P; This is probably incorrect, so it's commented out. */
|
||||
HU_PI = HU_P;
|
||||
POP_PC();
|
||||
|
||||
// T-flag handling here:
|
||||
TEST_WEIRD_TFLAG("RTI");
|
||||
break;
|
||||
|
||||
case 0x60: /* RTS */
|
||||
POP_PC_AP();
|
||||
break;
|
||||
|
||||
case 0x48: /* PHA */
|
||||
PUSH(HU_A);
|
||||
break;
|
||||
|
||||
case 0x08: /* PHP */
|
||||
HU_P &= ~T_FLAG;
|
||||
COMPRESS_FLAGS();
|
||||
PUSH(HU_P|B_FLAG);
|
||||
break;
|
||||
|
||||
case 0xDA: // PHX 65C02
|
||||
PUSH(HU_X);
|
||||
break;
|
||||
|
||||
case 0x5A: // PHY 65C02
|
||||
PUSH(HU_Y);
|
||||
break;
|
||||
|
||||
case 0x68: /* PLA */
|
||||
HU_A = POP();
|
||||
X_ZN(HU_A);
|
||||
break;
|
||||
|
||||
case 0xFA: // PLX 65C02
|
||||
HU_X = POP();
|
||||
X_ZN(HU_X);
|
||||
break;
|
||||
|
||||
case 0x7A: // PLY 65C02
|
||||
HU_Y = POP();
|
||||
X_ZN(HU_Y);
|
||||
break;
|
||||
|
||||
case 0x28: /* PLP */
|
||||
HU_P = POP();
|
||||
EXPAND_FLAGS();
|
||||
|
||||
// T-flag handling here:
|
||||
TEST_WEIRD_TFLAG("PLP");
|
||||
break;
|
||||
|
||||
case 0x4C:
|
||||
{
|
||||
unsigned int npc;
|
||||
|
||||
npc = RdAtPC();
|
||||
IncPC();
|
||||
npc|=RdAtPC() << 8;
|
||||
|
||||
SetPC(npc);
|
||||
}
|
||||
break; /* JMP ABSOLUTE */
|
||||
|
||||
case 0x6C: /* JMP Indirect */
|
||||
{
|
||||
uint32 tmp;
|
||||
unsigned int npc;
|
||||
|
||||
GetAB(tmp);
|
||||
|
||||
npc=RdMem(tmp);
|
||||
npc|=RdMem(tmp + 1)<<8;
|
||||
|
||||
SetPC(npc);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7C: // JMP Indirect X - 65C02
|
||||
{
|
||||
uint32 tmp;
|
||||
unsigned int npc;
|
||||
|
||||
GetAB(tmp);
|
||||
tmp += HU_X;
|
||||
|
||||
npc=RdMem(tmp);
|
||||
npc|=RdMem(tmp + 1)<<8;
|
||||
|
||||
SetPC(npc);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x20: /* JSR */
|
||||
{
|
||||
unsigned int npc;
|
||||
|
||||
npc = RdAtPC();
|
||||
|
||||
IncPC();
|
||||
PUSH_PC();
|
||||
|
||||
npc |= RdAtPC() <<8;
|
||||
|
||||
SetPC(npc);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xAA: /* TAX */
|
||||
HU_X=HU_A;
|
||||
X_ZN(HU_A);
|
||||
break;
|
||||
|
||||
case 0x8A: /* TXA */
|
||||
HU_A=HU_X;
|
||||
X_ZN(HU_A);
|
||||
break;
|
||||
|
||||
case 0xA8: /* TAY */
|
||||
HU_Y=HU_A;
|
||||
X_ZN(HU_A);
|
||||
break;
|
||||
case 0x98: /* TYA */
|
||||
HU_A=HU_Y;
|
||||
X_ZN(HU_A);
|
||||
break;
|
||||
|
||||
case 0xBA: /* TSX */
|
||||
HU_X=HU_S;
|
||||
X_ZN(HU_X);
|
||||
break;
|
||||
case 0x9A: /* TXS */
|
||||
HU_S=HU_X;
|
||||
break;
|
||||
|
||||
case 0xCA: /* DEX */
|
||||
HU_X--;
|
||||
X_ZN(HU_X);
|
||||
break;
|
||||
case 0x88: /* DEY */
|
||||
HU_Y--;
|
||||
X_ZN(HU_Y);
|
||||
break;
|
||||
|
||||
case 0xE8: /* INX */
|
||||
HU_X++;
|
||||
X_ZN(HU_X);
|
||||
break;
|
||||
case 0xC8: /* INY */
|
||||
HU_Y++;
|
||||
X_ZN(HU_Y);
|
||||
break;
|
||||
|
||||
case 0x54: CSL; break;
|
||||
case 0xD4: CSH; break;
|
||||
|
||||
case 0x62: HU_A = 0; break; // CLA
|
||||
case 0x82: HU_X = 0; break; // CLX
|
||||
case 0xC2: HU_Y = 0; break; // CLY
|
||||
|
||||
case 0x18: /* CLC */
|
||||
HU_P&=~C_FLAG;
|
||||
break;
|
||||
|
||||
case 0xD8: /* CLD */
|
||||
HU_P&=~D_FLAG;
|
||||
break;
|
||||
|
||||
case 0x58: /* CLI */
|
||||
if((HU_P & I_FLAG) && (HU_IRQlow & MDFN_IQIRQ1))
|
||||
{
|
||||
uint8 moo_op = RdAtPC();
|
||||
if((moo_op == 0xAC || moo_op == 0xAD || moo_op == 0xAE) &&
|
||||
((RdOp(GetRealPC() + 1) & 0x3) == 0) && ((RdOp(GetRealPC() + 2) & 0xFC) == 0))
|
||||
{
|
||||
HU_IRQlow |= 0x200;
|
||||
//puts("CLI/LDA madness!");
|
||||
}
|
||||
}
|
||||
HU_P&=~I_FLAG;
|
||||
break;
|
||||
|
||||
case 0xB8: /* CLV */
|
||||
HU_P&=~V_FLAG;
|
||||
break;
|
||||
|
||||
case 0x38: /* SEC */
|
||||
HU_P|=C_FLAG;
|
||||
break;
|
||||
|
||||
case 0xF8: /* SED */
|
||||
HU_P|=D_FLAG;
|
||||
break;
|
||||
|
||||
case 0x78: /* SEI */
|
||||
HU_P|=I_FLAG;
|
||||
break;
|
||||
|
||||
case 0xEA: /* NOP */
|
||||
break;
|
||||
|
||||
case 0x0A: RMW_A(ASL);
|
||||
case 0x06: RMW_ZP(ASL);
|
||||
case 0x16: RMW_ZPX(ASL);
|
||||
case 0x0E: RMW_AB(ASL);
|
||||
case 0x1E: RMW_ABX(ASL);
|
||||
|
||||
case 0x3A: RMW_A(DEC);
|
||||
case 0xC6: RMW_ZP(DEC);
|
||||
case 0xD6: RMW_ZPX(DEC);
|
||||
case 0xCE: RMW_AB(DEC);
|
||||
case 0xDE: RMW_ABX(DEC);
|
||||
|
||||
case 0x1A: RMW_A(INC); // 65C02
|
||||
case 0xE6: RMW_ZP(INC);
|
||||
case 0xF6: RMW_ZPX(INC);
|
||||
case 0xEE: RMW_AB(INC);
|
||||
case 0xFE: RMW_ABX(INC);
|
||||
|
||||
case 0x4A: RMW_A(LSR);
|
||||
case 0x46: RMW_ZP(LSR);
|
||||
case 0x56: RMW_ZPX(LSR);
|
||||
case 0x4E: RMW_AB(LSR);
|
||||
case 0x5E: RMW_ABX(LSR);
|
||||
|
||||
case 0x2A: RMW_A(ROL);
|
||||
case 0x26: RMW_ZP(ROL);
|
||||
case 0x36: RMW_ZPX(ROL);
|
||||
case 0x2E: RMW_AB(ROL);
|
||||
case 0x3E: RMW_ABX(ROL);
|
||||
|
||||
case 0x6A: RMW_A(ROR);
|
||||
case 0x66: RMW_ZP(ROR);
|
||||
case 0x76: RMW_ZPX(ROR);
|
||||
case 0x6E: RMW_AB(ROR);
|
||||
case 0x7E: RMW_ABX(ROR);
|
||||
|
||||
case 0x69: LD_IM(ADC);
|
||||
case 0x65: LD_ZP(ADC);
|
||||
case 0x75: LD_ZPX(ADC);
|
||||
case 0x6D: LD_AB(ADC);
|
||||
case 0x7D: LD_ABX(ADC);
|
||||
case 0x79: LD_ABY(ADC);
|
||||
case 0x72: LD_IND(ADC);
|
||||
case 0x61: LD_IX(ADC);
|
||||
case 0x71: LD_IY(ADC);
|
||||
|
||||
case 0x29: LD_IM(AND);
|
||||
case 0x25: LD_ZP(AND);
|
||||
case 0x35: LD_ZPX(AND);
|
||||
case 0x2D: LD_AB(AND);
|
||||
case 0x3D: LD_ABX(AND);
|
||||
case 0x39: LD_ABY(AND);
|
||||
case 0x32: LD_IND(AND);
|
||||
case 0x21: LD_IX(AND);
|
||||
case 0x31: LD_IY(AND);
|
||||
|
||||
case 0x89: LD_IM(BIT);
|
||||
case 0x24: LD_ZP(BIT);
|
||||
case 0x34: LD_ZPX(BIT);
|
||||
case 0x2C: LD_AB(BIT);
|
||||
case 0x3C: LD_ABX(BIT);
|
||||
|
||||
case 0xC9: LD_IM(CMP);
|
||||
case 0xC5: LD_ZP(CMP);
|
||||
case 0xD5: LD_ZPX(CMP);
|
||||
case 0xCD: LD_AB(CMP);
|
||||
case 0xDD: LD_ABX(CMP);
|
||||
case 0xD9: LD_ABY(CMP);
|
||||
case 0xD2: LD_IND(CMP);
|
||||
case 0xC1: LD_IX(CMP);
|
||||
case 0xD1: LD_IY(CMP);
|
||||
|
||||
case 0xE0: LD_IM(CPX);
|
||||
case 0xE4: LD_ZP(CPX);
|
||||
case 0xEC: LD_AB(CPX);
|
||||
|
||||
case 0xC0: LD_IM(CPY);
|
||||
case 0xC4: LD_ZP(CPY);
|
||||
case 0xCC: LD_AB(CPY);
|
||||
|
||||
case 0x49: LD_IM(EOR);
|
||||
case 0x45: LD_ZP(EOR);
|
||||
case 0x55: LD_ZPX(EOR);
|
||||
case 0x4D: LD_AB(EOR);
|
||||
case 0x5D: LD_ABX(EOR);
|
||||
case 0x59: LD_ABY(EOR);
|
||||
case 0x52: LD_IND(EOR);
|
||||
case 0x41: LD_IX(EOR);
|
||||
case 0x51: LD_IY(EOR);
|
||||
|
||||
case 0xA9: LD_IM(LDA);
|
||||
case 0xA5: LD_ZP(LDA);
|
||||
case 0xB5: LD_ZPX(LDA);
|
||||
case 0xAD: LD_AB(LDA);
|
||||
case 0xBD: LD_ABX(LDA);
|
||||
case 0xB9: LD_ABY(LDA);
|
||||
case 0xB2: LD_IND(LDA);
|
||||
case 0xA1: LD_IX(LDA);
|
||||
case 0xB1: LD_IY(LDA);
|
||||
|
||||
case 0xA2: LD_IM(LDX);
|
||||
case 0xA6: LD_ZP(LDX);
|
||||
case 0xB6: LD_ZPY(LDX);
|
||||
case 0xAE: LD_AB(LDX);
|
||||
case 0xBE: LD_ABY(LDX);
|
||||
|
||||
case 0xA0: LD_IM(LDY);
|
||||
case 0xA4: LD_ZP(LDY);
|
||||
case 0xB4: LD_ZPX(LDY);
|
||||
case 0xAC: LD_AB(LDY);
|
||||
case 0xBC: LD_ABX(LDY);
|
||||
|
||||
case 0x09: LD_IM(ORA);
|
||||
case 0x05: LD_ZP(ORA);
|
||||
case 0x15: LD_ZPX(ORA);
|
||||
case 0x0D: LD_AB(ORA);
|
||||
case 0x1D: LD_ABX(ORA);
|
||||
case 0x19: LD_ABY(ORA);
|
||||
case 0x12: LD_IND(ORA);
|
||||
case 0x01: LD_IX(ORA);
|
||||
case 0x11: LD_IY(ORA);
|
||||
|
||||
case 0xE9: LD_IM(SBC);
|
||||
case 0xE5: LD_ZP(SBC);
|
||||
case 0xF5: LD_ZPX(SBC);
|
||||
case 0xED: LD_AB(SBC);
|
||||
case 0xFD: LD_ABX(SBC);
|
||||
case 0xF9: LD_ABY(SBC);
|
||||
case 0xF2: LD_IND(SBC);
|
||||
case 0xE1: LD_IX(SBC);
|
||||
case 0xF1: LD_IY(SBC);
|
||||
|
||||
case 0x85: ST_ZP(HU_A);
|
||||
case 0x95: ST_ZPX(HU_A);
|
||||
case 0x8D: ST_AB(HU_A);
|
||||
case 0x9D: ST_ABX(HU_A);
|
||||
case 0x99: ST_ABY(HU_A);
|
||||
case 0x92: ST_IND(HU_A);
|
||||
case 0x81: ST_IX(HU_A);
|
||||
case 0x91: ST_IY(HU_A);
|
||||
|
||||
case 0x86: ST_ZP(HU_X);
|
||||
case 0x96: ST_ZPY(HU_X);
|
||||
case 0x8E: ST_AB(HU_X);
|
||||
|
||||
case 0x84: ST_ZP(HU_Y);
|
||||
case 0x94: ST_ZPX(HU_Y);
|
||||
case 0x8C: ST_AB(HU_Y);
|
||||
|
||||
/* BBRi */
|
||||
case 0x0F: LD_ZP(BBRi(0));
|
||||
case 0x1F: LD_ZP(BBRi(1));
|
||||
case 0x2F: LD_ZP(BBRi(2));
|
||||
case 0x3F: LD_ZP(BBRi(3));
|
||||
case 0x4F: LD_ZP(BBRi(4));
|
||||
case 0x5F: LD_ZP(BBRi(5));
|
||||
case 0x6F: LD_ZP(BBRi(6));
|
||||
case 0x7F: LD_ZP(BBRi(7));
|
||||
|
||||
/* BBSi */
|
||||
case 0x8F: LD_ZP(BBSi(0));
|
||||
case 0x9F: LD_ZP(BBSi(1));
|
||||
case 0xAF: LD_ZP(BBSi(2));
|
||||
case 0xBF: LD_ZP(BBSi(3));
|
||||
case 0xCF: LD_ZP(BBSi(4));
|
||||
case 0xDF: LD_ZP(BBSi(5));
|
||||
case 0xEF: LD_ZP(BBSi(6));
|
||||
case 0xFF: LD_ZP(BBSi(7));
|
||||
|
||||
/* BRA */
|
||||
case 0x80: BRA; break;
|
||||
|
||||
/* BSR */
|
||||
case 0x44:
|
||||
{
|
||||
PUSH_PC();
|
||||
BRA;
|
||||
}
|
||||
break;
|
||||
|
||||
/* BCC */
|
||||
case 0x90: JR(!(HU_P&C_FLAG)); break;
|
||||
|
||||
/* BCS */
|
||||
case 0xB0: JR(HU_P&C_FLAG); break;
|
||||
|
||||
/* BVC */
|
||||
case 0x50: JR(!(HU_P&V_FLAG)); break;
|
||||
|
||||
/* BVS */
|
||||
case 0x70: JR(HU_P&V_FLAG); break;
|
||||
|
||||
#ifdef HUC6280_LAZY_FLAGS
|
||||
|
||||
/* BEQ */
|
||||
case 0xF0: JR(!(HU_ZNFlags & 0xFF)); break;
|
||||
|
||||
/* BNE */
|
||||
case 0xD0: JR((HU_ZNFlags & 0xFF)); break;
|
||||
|
||||
/* BMI */
|
||||
case 0x30: JR((HU_ZNFlags & 0x80000000)); break;
|
||||
|
||||
/* BPL */
|
||||
case 0x10: JR(!(HU_ZNFlags & 0x80000000)); break;
|
||||
|
||||
#else
|
||||
|
||||
/* BEQ */
|
||||
case 0xF0: JR(HU_P&Z_FLAG); break;
|
||||
|
||||
/* BNE */
|
||||
case 0xD0: JR(!(HU_P&Z_FLAG)); break;
|
||||
|
||||
/* BMI */
|
||||
case 0x30: JR(HU_P&N_FLAG); break;
|
||||
|
||||
/* BPL */
|
||||
case 0x10: JR(!(HU_P&N_FLAG)); break;
|
||||
|
||||
#endif
|
||||
|
||||
// RMB 65SC02
|
||||
case 0x07: RMW_ZP(RMB(0));
|
||||
case 0x17: RMW_ZP(RMB(1));
|
||||
case 0x27: RMW_ZP(RMB(2));
|
||||
case 0x37: RMW_ZP(RMB(3));
|
||||
case 0x47: RMW_ZP(RMB(4));
|
||||
case 0x57: RMW_ZP(RMB(5));
|
||||
case 0x67: RMW_ZP(RMB(6));
|
||||
case 0x77: RMW_ZP(RMB(7));
|
||||
|
||||
// SMB 65SC02
|
||||
case 0x87: RMW_ZP(SMB(0));
|
||||
case 0x97: RMW_ZP(SMB(1));
|
||||
case 0xa7: RMW_ZP(SMB(2));
|
||||
case 0xb7: RMW_ZP(SMB(3));
|
||||
case 0xc7: RMW_ZP(SMB(4));
|
||||
case 0xd7: RMW_ZP(SMB(5));
|
||||
case 0xe7: RMW_ZP(SMB(6));
|
||||
case 0xf7: RMW_ZP(SMB(7));
|
||||
|
||||
// STZ 65C02
|
||||
case 0x64: ST_ZP(0);
|
||||
case 0x74: ST_ZPX(0);
|
||||
case 0x9C: ST_AB(0);
|
||||
case 0x9E: ST_ABX(0);
|
||||
|
||||
// TRB 65SC02
|
||||
case 0x14: RMW_ZP(TRB);
|
||||
case 0x1C: RMW_AB(TRB);
|
||||
|
||||
// TSB 65SC02
|
||||
case 0x04: RMW_ZP(TSB);
|
||||
case 0x0C: RMW_AB(TSB);
|
||||
|
||||
// TST
|
||||
case 0x83: { uint8 zoomhack=RdAtPC(); IncPC(); LD_ZP(TST); }
|
||||
case 0xA3: { uint8 zoomhack=RdAtPC(); IncPC(); LD_ZPX(TST); }
|
||||
case 0x93: { uint8 zoomhack=RdAtPC(); IncPC(); LD_AB(TST); }
|
||||
case 0xB3: { uint8 zoomhack=RdAtPC(); IncPC(); LD_ABX(TST); }
|
||||
|
||||
case 0x22: // SAX(amaphone!)
|
||||
{
|
||||
uint8 tmp = HU_X;
|
||||
HU_X = HU_A;
|
||||
HU_A = tmp;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x42: // SAY(what?)
|
||||
{
|
||||
uint8 tmp = HU_Y;
|
||||
HU_Y = HU_A;
|
||||
HU_A = tmp;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02: // SXY
|
||||
{
|
||||
uint8 tmp = HU_X;
|
||||
HU_X = HU_Y;
|
||||
HU_Y = tmp;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x73: // TII
|
||||
LD_BMT(BMT_TII);
|
||||
|
||||
case 0xC3: // TDD
|
||||
LD_BMT(BMT_TDD);
|
||||
|
||||
case 0xD3: // TIN
|
||||
LD_BMT(BMT_TIN);
|
||||
|
||||
case 0xE3: // TIA
|
||||
LD_BMT(BMT_TIA);
|
||||
|
||||
case 0xF3: // TAI
|
||||
LD_BMT(BMT_TAI);
|
||||
|
||||
case 0x43: // TMAi
|
||||
LD_IM(TMA);
|
||||
|
||||
case 0x53: // TAMi
|
||||
LD_IM(TAM);
|
||||
|
||||
case 0x03: // ST0
|
||||
LD_IM(ST0);
|
||||
|
||||
case 0x13: // ST1
|
||||
LD_IM(ST1);
|
||||
|
||||
case 0x23: // ST2
|
||||
LD_IM(ST2);
|
||||
|
||||
|
||||
case 0xF4: /* SET */
|
||||
{
|
||||
// AND, EOR, ORA, ADC
|
||||
uint8 Abackup = HU_A;
|
||||
|
||||
ADDCYC(3);
|
||||
HU_A = HU_Page1[HU_X]; //PAGE1_R[HU_X];
|
||||
|
||||
switch(RdAtPC())
|
||||
{
|
||||
default: //puts("Bad SET");
|
||||
break;
|
||||
|
||||
case 0x69: IncPC(); LD_IM(ADC);
|
||||
case 0x65: IncPC(); LD_ZP(ADC);
|
||||
case 0x75: IncPC(); LD_ZPX(ADC);
|
||||
case 0x6D: IncPC(); LD_AB(ADC);
|
||||
case 0x7D: IncPC(); LD_ABX(ADC);
|
||||
case 0x79: IncPC(); LD_ABY(ADC);
|
||||
case 0x72: IncPC(); LD_IND(ADC);
|
||||
case 0x61: IncPC(); LD_IX(ADC);
|
||||
case 0x71: IncPC(); LD_IY(ADC);
|
||||
|
||||
case 0x29: IncPC(); LD_IM(AND);
|
||||
case 0x25: IncPC(); LD_ZP(AND);
|
||||
case 0x35: IncPC(); LD_ZPX(AND);
|
||||
case 0x2D: IncPC(); LD_AB(AND);
|
||||
case 0x3D: IncPC(); LD_ABX(AND);
|
||||
case 0x39: IncPC(); LD_ABY(AND);
|
||||
case 0x32: IncPC(); LD_IND(AND);
|
||||
case 0x21: IncPC(); LD_IX(AND);
|
||||
case 0x31: IncPC(); LD_IY(AND);
|
||||
|
||||
case 0x49: IncPC(); LD_IM(EOR);
|
||||
case 0x45: IncPC(); LD_ZP(EOR);
|
||||
case 0x55: IncPC(); LD_ZPX(EOR);
|
||||
case 0x4D: IncPC(); LD_AB(EOR);
|
||||
case 0x5D: IncPC(); LD_ABX(EOR);
|
||||
case 0x59: IncPC(); LD_ABY(EOR);
|
||||
case 0x52: IncPC(); LD_IND(EOR);
|
||||
case 0x41: IncPC(); LD_IX(EOR);
|
||||
case 0x51: IncPC(); LD_IY(EOR);
|
||||
|
||||
case 0x09: IncPC(); LD_IM(ORA);
|
||||
case 0x05: IncPC(); LD_ZP(ORA);
|
||||
case 0x15: IncPC(); LD_ZPX(ORA);
|
||||
case 0x0D: IncPC(); LD_AB(ORA);
|
||||
case 0x1D: IncPC(); LD_ABX(ORA);
|
||||
case 0x19: IncPC(); LD_ABY(ORA);
|
||||
case 0x12: IncPC(); LD_IND(ORA);
|
||||
case 0x01: IncPC(); LD_IX(ORA);
|
||||
case 0x11: IncPC(); LD_IY(ORA);
|
||||
}
|
||||
HU_Page1[HU_X] /*PAGE1_W[HU_X]*/ = HU_A;
|
||||
HU_A = Abackup;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFC:
|
||||
{
|
||||
int32 ec_tmp;
|
||||
ec_tmp = next_event - HuCPU.timestamp;
|
||||
if(ec_tmp > 0)
|
||||
{
|
||||
ADDCYC(ec_tmp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: //MDFN_printf("Bad %02x at $%04x\n", b1, GetRealPC());
|
||||
break;
|
@ -1,334 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "pce.h"
|
||||
#include "input.h"
|
||||
#include "huc.h"
|
||||
#include "../mednafen-endian.h"
|
||||
|
||||
static int InputTypes[5];
|
||||
static uint8 *data_ptr[5];
|
||||
|
||||
static bool AVPad6Which[5]; // Lower(8 buttons) or higher(4 buttons).
|
||||
bool AVPad6Enabled[5];
|
||||
|
||||
uint16 pce_jp_data[5];
|
||||
|
||||
static int64 mouse_last_meow[5];
|
||||
|
||||
static int32 mouse_x[5], mouse_y[5];
|
||||
static uint16 mouse_rel[5];
|
||||
|
||||
uint8 pce_mouse_button[5];
|
||||
uint8 mouse_index[5];
|
||||
|
||||
static uint8 sel;
|
||||
static uint8 read_index = 0;
|
||||
|
||||
static void SyncSettings(void);
|
||||
|
||||
void PCEINPUT_SettingChanged(const char *name)
|
||||
{
|
||||
SyncSettings();
|
||||
}
|
||||
|
||||
void PCEINPUT_Init(void)
|
||||
{
|
||||
SyncSettings();
|
||||
}
|
||||
|
||||
void PCEINPUT_SetInput(int port, const char *type, void *ptr)
|
||||
{
|
||||
assert(port < 5);
|
||||
|
||||
if(!strcasecmp(type, "gamepad"))
|
||||
InputTypes[port] = 1;
|
||||
else if(!strcasecmp(type, "mouse"))
|
||||
InputTypes[port] = 2;
|
||||
else
|
||||
InputTypes[port] = 0;
|
||||
data_ptr[port] = (uint8 *)ptr;
|
||||
}
|
||||
|
||||
void INPUT_Frame(void)
|
||||
{
|
||||
unsigned x;
|
||||
|
||||
for(x = 0; x < 5; x++)
|
||||
{
|
||||
if(InputTypes[x] == 1)
|
||||
{
|
||||
uint16 new_data = data_ptr[x][0] | (data_ptr[x][1] << 8);
|
||||
|
||||
if((new_data & 0x1000) && !(pce_jp_data[x] & 0x1000))
|
||||
{
|
||||
AVPad6Enabled[x] = !AVPad6Enabled[x];
|
||||
MDFN_DispMessage("%d-button mode selected for pad %d", AVPad6Enabled[x] ? 6 : 2, x + 1);
|
||||
}
|
||||
|
||||
pce_jp_data[x] = new_data;
|
||||
}
|
||||
else if(InputTypes[x] == 2)
|
||||
{
|
||||
mouse_x[x] += (int32)MDFN_de32lsb(data_ptr[x] + 0);
|
||||
mouse_y[x] += (int32)MDFN_de32lsb(data_ptr[x] + 4);
|
||||
pce_mouse_button[x] = *(uint8 *)(data_ptr[x] + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void INPUT_FixTS(void)
|
||||
{
|
||||
unsigned x;
|
||||
|
||||
for(x = 0; x < 5; x++)
|
||||
{
|
||||
if(InputTypes[x] == 2)
|
||||
mouse_last_meow[x] -= HuCPU.timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE bool CheckLM(int n)
|
||||
{
|
||||
if((int64)HuCPU.timestamp - mouse_last_meow[n] > 10000)
|
||||
{
|
||||
mouse_last_meow[n] = HuCPU.timestamp;
|
||||
|
||||
int32 rel_x = (int32)((0-mouse_x[n]));
|
||||
int32 rel_y = (int32)((0-mouse_y[n]));
|
||||
|
||||
if(rel_x < -127) rel_x = -127;
|
||||
if(rel_x > 127) rel_x = 127;
|
||||
if(rel_y < -127) rel_y = -127;
|
||||
if(rel_y > 127) rel_y = 127;
|
||||
|
||||
mouse_rel[n] = ((rel_x & 0xF0) >> 4) | ((rel_x & 0x0F) << 4);
|
||||
mouse_rel[n] |= (((rel_y & 0xF0) >> 4) | ((rel_y & 0x0F) << 4)) << 8;
|
||||
|
||||
mouse_x[n] += (int32)(rel_x);
|
||||
mouse_y[n] += (int32)(rel_y);
|
||||
|
||||
return(1);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
uint8 INPUT_Read(unsigned int A)
|
||||
{
|
||||
uint8 ret = 0xF;
|
||||
int tmp_ri = read_index;
|
||||
|
||||
if(tmp_ri > 4)
|
||||
ret ^= 0xF;
|
||||
else
|
||||
{
|
||||
if(!InputTypes[tmp_ri])
|
||||
ret ^= 0xF;
|
||||
else if(InputTypes[tmp_ri] == 2) // Mouse
|
||||
{
|
||||
if(sel & 1)
|
||||
{
|
||||
CheckLM(tmp_ri);
|
||||
ret ^= 0xF;
|
||||
ret ^= mouse_rel[tmp_ri] & 0xF;
|
||||
|
||||
mouse_rel[tmp_ri] >>= 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(pce_mouse_button[tmp_ri] & 1)
|
||||
ret ^= 0x3; //pce_mouse_button[tmp_ri];
|
||||
|
||||
if(pce_mouse_button[tmp_ri] & 0x2)
|
||||
ret ^= 0x8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(InputTypes[tmp_ri] == 1) // Gamepad
|
||||
{
|
||||
if(AVPad6Which[tmp_ri] && AVPad6Enabled[tmp_ri])
|
||||
{
|
||||
if(sel & 1)
|
||||
ret ^= 0x0F;
|
||||
else
|
||||
ret ^= (pce_jp_data[tmp_ri] >> 8) & 0x0F;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(sel & 1)
|
||||
ret ^= (pce_jp_data[tmp_ri] >> 4) & 0x0F;
|
||||
else
|
||||
ret ^= pce_jp_data[tmp_ri] & 0x0F;
|
||||
}
|
||||
if(!(sel & 1))
|
||||
AVPad6Which[tmp_ri] = !AVPad6Which[tmp_ri];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!PCE_IsCD)
|
||||
ret |= 0x80; // Set when CDROM is not attached
|
||||
|
||||
//ret |= 0x40; // PC Engine if set, TG16 if clear. Let's leave it clear, PC Engine games don't seem to mind if it's clear, but TG16 games barf if it's set.
|
||||
|
||||
ret |= 0x30; // Always-set?
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void INPUT_Write(unsigned int A, uint8 V)
|
||||
{
|
||||
if((V & 1) && !(sel & 2) && (V & 2))
|
||||
read_index = 0;
|
||||
else if((V & 1) && !(sel & 1))
|
||||
{
|
||||
if(read_index < 255)
|
||||
read_index++;
|
||||
}
|
||||
sel = V & 3;
|
||||
}
|
||||
|
||||
int INPUT_StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
// 0.8.A fix:
|
||||
SFARRAYB(AVPad6Enabled, 5),
|
||||
SFARRAYB(AVPad6Which, 5),
|
||||
|
||||
SFVARN(mouse_last_meow[0], "mlm_0"),
|
||||
SFVARN(mouse_last_meow[1], "mlm_1"),
|
||||
SFVARN(mouse_last_meow[2], "mlm_2"),
|
||||
SFVARN(mouse_last_meow[3], "mlm_3"),
|
||||
SFVARN(mouse_last_meow[4], "mlm_4"),
|
||||
|
||||
SFARRAY32(mouse_x, 5),
|
||||
SFARRAY32(mouse_y, 5),
|
||||
SFARRAY16(mouse_rel, 5),
|
||||
SFARRAY(pce_mouse_button, 5),
|
||||
SFARRAY(mouse_index, 5),
|
||||
// end 0.8.A fix
|
||||
|
||||
SFARRAY16(pce_jp_data, 5),
|
||||
SFVAR(sel),
|
||||
SFVAR(read_index),
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "JOY", false);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
// GamepadIDII and GamepadIDII_DSR must be EXACTLY the same except for the RUN+SELECT exclusion in the latter.
|
||||
static const InputDeviceInputInfoStruct GamepadIDII[] =
|
||||
{
|
||||
{ "i", "I", 12, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "ii", "II", 11, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "select", "SELECT", 4, IDIT_BUTTON, NULL },
|
||||
{ "run", "RUN", 5, IDIT_BUTTON, NULL },
|
||||
{ "up", "UP ↑", 0, IDIT_BUTTON, "down" },
|
||||
{ "right", "RIGHT →", 3, IDIT_BUTTON, "left" },
|
||||
{ "down", "DOWN ↓", 1, IDIT_BUTTON, "up" },
|
||||
{ "left", "LEFT ←", 2, IDIT_BUTTON, "right" },
|
||||
{ "iii", "III", 10, IDIT_BUTTON, NULL },
|
||||
{ "iv", "IV", 7, IDIT_BUTTON, NULL },
|
||||
{ "v", "V", 8, IDIT_BUTTON, NULL },
|
||||
{ "vi", "VI", 9, IDIT_BUTTON, NULL },
|
||||
{ "mode_select", "2/6 Mode Select", 6, IDIT_BUTTON, NULL },
|
||||
};
|
||||
static const InputDeviceInputInfoStruct GamepadIDII_DSR[] =
|
||||
{
|
||||
{ "i", "I", 12, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "ii", "II", 11, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "select", "SELECT", 4, IDIT_BUTTON, "run" },
|
||||
{ "run", "RUN", 5, IDIT_BUTTON, "select" },
|
||||
{ "up", "UP ↑", 0, IDIT_BUTTON, "down" },
|
||||
{ "right", "RIGHT →", 3, IDIT_BUTTON, "left" },
|
||||
{ "down", "DOWN ↓", 1, IDIT_BUTTON, "up" },
|
||||
{ "left", "LEFT ←", 2, IDIT_BUTTON, "right" },
|
||||
{ "iii", "III", 10, IDIT_BUTTON, NULL },
|
||||
{ "iv", "IV", 7, IDIT_BUTTON, NULL },
|
||||
{ "v", "V", 8, IDIT_BUTTON, NULL },
|
||||
{ "vi", "VI", 9, IDIT_BUTTON, NULL },
|
||||
{ "mode_select", "2/6 Mode Select", 6, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
static const InputDeviceInputInfoStruct MouseIDII[] =
|
||||
{
|
||||
{ "x_axis", "X Axis", -1, IDIT_X_AXIS_REL },
|
||||
{ "y_axis", "Y Axis", -1, IDIT_Y_AXIS_REL },
|
||||
{ "left", "Left Button", 0, IDIT_BUTTON, NULL },
|
||||
{ "right", "Right Button", 1, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
// If we add more devices to this array, REMEMBER TO UPDATE the hackish array indexing in the SyncSettings() function
|
||||
// below.
|
||||
static InputDeviceInfoStruct InputDeviceInfo[] =
|
||||
{
|
||||
// None
|
||||
{
|
||||
"none",
|
||||
"none",
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
|
||||
// Gamepad
|
||||
{
|
||||
"gamepad",
|
||||
"Gamepad",
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof(GamepadIDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
GamepadIDII,
|
||||
},
|
||||
|
||||
// Mouse
|
||||
{
|
||||
"mouse",
|
||||
"Mouse",
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof(MouseIDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
MouseIDII,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static const InputPortInfoStruct PortInfo[] =
|
||||
{
|
||||
{ "port1", "Port 1", sizeof(InputDeviceInfo) / sizeof(InputDeviceInfoStruct), InputDeviceInfo, "gamepad" },
|
||||
{ "port2", "Port 2", sizeof(InputDeviceInfo) / sizeof(InputDeviceInfoStruct), InputDeviceInfo, "gamepad" },
|
||||
{ "port3", "Port 3", sizeof(InputDeviceInfo) / sizeof(InputDeviceInfoStruct), InputDeviceInfo, "gamepad" },
|
||||
{ "port4", "Port 4", sizeof(InputDeviceInfo) / sizeof(InputDeviceInfoStruct), InputDeviceInfo, "gamepad" },
|
||||
{ "port5", "Port 5", sizeof(InputDeviceInfo) / sizeof(InputDeviceInfoStruct), InputDeviceInfo, "gamepad" },
|
||||
};
|
||||
|
||||
InputInfoStruct PCEInputInfo =
|
||||
{
|
||||
sizeof(PortInfo) / sizeof(InputPortInfoStruct),
|
||||
PortInfo
|
||||
};
|
||||
|
||||
static void SyncSettings(void)
|
||||
{
|
||||
MDFNGameInfo->mouse_sensitivity = MDFN_GetSettingF("pce_fast.mouse_sensitivity");
|
||||
InputDeviceInfo[1].IDII = MDFN_GetSettingB("pce_fast.disable_softreset") ? GamepadIDII_DSR : GamepadIDII;
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#ifndef __PCE_INPUT_H
|
||||
#define __PCE_INPUT_H
|
||||
|
||||
void PCEINPUT_Init(void);
|
||||
void PCEINPUT_SettingChanged(const char *name);
|
||||
void PCEINPUT_SetInput(int port, const char *type, void *ptr);
|
||||
uint8 INPUT_Read(unsigned int A);
|
||||
void INPUT_Write(unsigned int A, uint8 V);
|
||||
void INPUT_Frame(void);
|
||||
int INPUT_StateAction(StateMem *sm, int load, int data_only);
|
||||
extern InputInfoStruct PCEInputInfo;
|
||||
void INPUT_FixTS(void);
|
||||
extern bool AVPad6Enabled[5];
|
||||
|
||||
#endif
|
@ -1,129 +0,0 @@
|
||||
A &= 0x1FFF;
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER)
|
||||
const void * const IOReadHandlers[0x20] =
|
||||
{
|
||||
&&VDC_00, &&VDC_01, &&VDC_02, &&VDC_03,
|
||||
&&VCE_00, &&VCE_01, &&VCE_02, &&VCE_03,
|
||||
&&PSG_00, &&PSG_01, &&PSG_02, &&PSG_03,
|
||||
&&TIMER_00, &&TIMER_01, &&TIMER_02, &&TIMER_03,
|
||||
&&INPUT_00, &&INPUT_01, &&INPUT_02, &&INPUT_03,
|
||||
&&IRQ_00, &&IRQ_01, &&IRQ_02, &&IRQ_03,
|
||||
&&CDROM_00, &&CDROM_01, &&CDROM_02, &&CDROM_03,
|
||||
&&EXP_00, &&EXP_01, &&EXP_02, &&EXP_03
|
||||
};
|
||||
|
||||
goto *IOReadHandlers[((A & 0x1C00) >> 8) | (A & 0x3)];
|
||||
{
|
||||
#define PCEF_CASEL(label, caseval) label
|
||||
#else
|
||||
#define PCEF_CASEL(label, caseval) case (caseval)
|
||||
|
||||
switch(((A & 0x1C00) >> 8) | (A & 0x3))
|
||||
{
|
||||
#endif
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
PCEF_CASEL(VDC_00, 0x00):
|
||||
HuC6280_StealCycle();
|
||||
return(VDC_Read(0, FALSE));
|
||||
|
||||
PCEF_CASEL(VDC_01, 0x01):
|
||||
HuC6280_StealCycle();
|
||||
return(VDC_Read(1, FALSE));
|
||||
|
||||
PCEF_CASEL(VDC_02, 0x02):
|
||||
HuC6280_StealCycle();
|
||||
return(VDC_Read(2, FALSE));
|
||||
|
||||
PCEF_CASEL(VDC_03, 0x03):
|
||||
HuC6280_StealCycle();
|
||||
return(VDC_Read(3, FALSE));
|
||||
|
||||
PCEF_CASEL(VCE_00, 0x04):
|
||||
PCEF_CASEL(VCE_01, 0x05):
|
||||
PCEF_CASEL(VCE_02, 0x06):
|
||||
PCEF_CASEL(VCE_03, 0x07):
|
||||
HuC6280_StealCycle();
|
||||
return(VCE_Read(A));
|
||||
|
||||
PCEF_CASEL(PSG_00, 0x08):
|
||||
PCEF_CASEL(PSG_01, 0x09):
|
||||
PCEF_CASEL(PSG_02, 0x0A):
|
||||
PCEF_CASEL(PSG_03, 0x0B):
|
||||
if(HuCPU.in_block_move)
|
||||
return(0);
|
||||
return(PCEIODataBuffer);
|
||||
|
||||
|
||||
PCEF_CASEL(TIMER_00, 0x0C):
|
||||
PCEF_CASEL(TIMER_01, 0x0D):
|
||||
PCEF_CASEL(TIMER_02, 0x0E):
|
||||
PCEF_CASEL(TIMER_03, 0x0F):
|
||||
if(HuCPU.in_block_move)
|
||||
return(0);
|
||||
{
|
||||
uint8 ret = HuC6280_TimerRead(A);
|
||||
PCEIODataBuffer = ret;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
PCEF_CASEL(INPUT_00, 0x10):
|
||||
PCEF_CASEL(INPUT_01, 0x11):
|
||||
PCEF_CASEL(INPUT_02, 0x12):
|
||||
PCEF_CASEL(INPUT_03, 0x13):
|
||||
if(HuCPU.in_block_move)
|
||||
return(0);
|
||||
{
|
||||
uint8 ret = INPUT_Read(A);
|
||||
PCEIODataBuffer = ret;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
PCEF_CASEL(IRQ_00, 0x14):
|
||||
PCEF_CASEL(IRQ_01, 0x15):
|
||||
PCEF_CASEL(IRQ_02, 0x16):
|
||||
PCEF_CASEL(IRQ_03, 0x17):
|
||||
if(HuCPU.in_block_move)
|
||||
return(0);
|
||||
{
|
||||
uint8 ret = HuC6280_IRQStatusRead(A);
|
||||
PCEIODataBuffer = ret;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
PCEF_CASEL(CDROM_00, 0x18):
|
||||
PCEF_CASEL(CDROM_01, 0x19):
|
||||
PCEF_CASEL(CDROM_02, 0x1A):
|
||||
PCEF_CASEL(CDROM_03, 0x1B):
|
||||
if(!PCE_IsCD)
|
||||
return(0xFF);
|
||||
|
||||
if((A & 0x1E00) == 0x1A00)
|
||||
{
|
||||
if(arcade_card)
|
||||
return(arcade_card->Read(A & 0x1FFF));
|
||||
else
|
||||
return(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(PCECD_Read(HuCPU.timestamp * 3, A));
|
||||
}
|
||||
|
||||
PCEF_CASEL(EXP_00, 0x1C):
|
||||
PCEF_CASEL(EXP_01, 0x1D):
|
||||
PCEF_CASEL(EXP_02, 0x1E):
|
||||
PCEF_CASEL(EXP_03, 0x1F):
|
||||
#ifdef HAVE_HES
|
||||
if(IsHES)
|
||||
return(ReadIBP(A));
|
||||
#endif
|
||||
return(0xFF);
|
||||
|
||||
}
|
||||
#undef PCEF_CASEL
|
||||
//printf("Meow: %08x, %02x:%04x\n", A, A >> 13, A & 0x1FFF);
|
@ -1,34 +0,0 @@
|
||||
#ifndef _PCE_H
|
||||
#define _PCE_H
|
||||
|
||||
#include "../mednafen-types.h"
|
||||
#include "../mednafen.h"
|
||||
#include "../state.h"
|
||||
#include "../general.h"
|
||||
|
||||
#define PCE_MASTER_CLOCK 21477272.727273
|
||||
|
||||
#define DECLFR(x) uint8 MDFN_FASTCALL x (uint32 A)
|
||||
#define DECLFW(x) void MDFN_FASTCALL x (uint32 A, uint8 V)
|
||||
|
||||
extern uint8 ROMSpace[0x88 * 8192 + 8192];
|
||||
|
||||
typedef void (MDFN_FASTCALL *writefunc)(uint32 A, uint8 V);
|
||||
typedef uint8 (MDFN_FASTCALL *readfunc)(uint32 A);
|
||||
|
||||
extern uint8 PCEIODataBuffer;
|
||||
|
||||
bool PCE_InitCD(void) MDFN_COLD;
|
||||
|
||||
extern bool PCE_ACEnabled; // Arcade Card emulation enabled?
|
||||
void PCE_Power(void) MDFN_COLD;
|
||||
|
||||
extern readfunc PCERead[0x100];
|
||||
extern writefunc PCEWrite[0x100];
|
||||
extern int pce_overclocked;
|
||||
|
||||
extern uint8 BaseRAM[32768 + 8192];
|
||||
|
||||
#include "huc6280.h"
|
||||
|
||||
#endif
|
@ -1,950 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* Copyright notice for this file:
|
||||
* Copyright (C) 2004 Ki
|
||||
* Copyright (C) 2007-2011 Mednafen Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
PCE_FAST(less accurate, faster, etc.) fork from PCE module "pcecd.cpp".
|
||||
*/
|
||||
|
||||
#include "../mednafen.h"
|
||||
#include "../cdrom/cdromif.h"
|
||||
#include "pcecd_drive.h"
|
||||
#include "../okiadpcm.h"
|
||||
|
||||
#include "pcecd.h"
|
||||
#include "../cdrom/SimpleFIFO.h"
|
||||
|
||||
//#define PCECD_DEBUG
|
||||
|
||||
static unsigned int OC_Multiplier;
|
||||
|
||||
static void (*IRQCB)(bool asserted);
|
||||
|
||||
static float CDDAVolumeSetting; // User setting!
|
||||
|
||||
static bool bBRAMEnabled;
|
||||
static uint8 _Port[15];
|
||||
static uint8 ACKStatus;
|
||||
|
||||
static SimpleFIFO<uint8> SubChannelFIFO(16);
|
||||
|
||||
static Blip_Buffer *sbuf[2];
|
||||
static int16 RawPCMVolumeCache[2];
|
||||
|
||||
static int32 ClearACKDelay;
|
||||
|
||||
static int32 lastts;
|
||||
static int32 pcecd_drive_ne = 0;
|
||||
|
||||
// ADPCM variables and whatnot
|
||||
static inline void ADPCM_DEBUG(const char *format, ...)
|
||||
{
|
||||
/*printf("[Half=%d, End=%d, Playing=%d] "x, ADPCM.HalfReached, ADPCM.EndReached, ADPCM.Playing, ## __VA_ARGS__);*/
|
||||
}
|
||||
|
||||
typedef Blip_Synth ADSynth;
|
||||
static ADSynth ADPCMSynth;
|
||||
static OKIADPCM_Decoder<OKIADPCM_MSM5205> MSM5205;
|
||||
|
||||
static bool ADPCMLP;
|
||||
typedef struct
|
||||
{
|
||||
uint8 *RAM; // = NULL; //0x10000;
|
||||
uint16 Addr;
|
||||
uint16 ReadAddr;
|
||||
uint16 WriteAddr;
|
||||
uint16 LengthCount;
|
||||
|
||||
bool HalfReached;
|
||||
bool EndReached;
|
||||
bool Playing;
|
||||
|
||||
uint8 LastCmd;
|
||||
uint32 SampleFreq;
|
||||
uint32 LPF_SampleFreq;
|
||||
|
||||
uint8 PlayBuffer;
|
||||
uint8 ReadBuffer;
|
||||
int32 ReadPending;
|
||||
int32 WritePending;
|
||||
uint8 WritePendingValue;
|
||||
|
||||
uint32 PlayNibble;
|
||||
|
||||
|
||||
int64 bigdivacc;
|
||||
int64 bigdiv;
|
||||
int32 last_pcm;
|
||||
} ADPCM_t;
|
||||
|
||||
static ADPCM_t ADPCM;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8 Command;
|
||||
int32 Volume;
|
||||
|
||||
int32 CycleCounter;
|
||||
uint32 CountValue; // What to reload CycleCounter with when it expires.
|
||||
bool Clocked;
|
||||
} FADE_t;
|
||||
|
||||
static FADE_t Fader;
|
||||
static int32 ADPCMFadeVolume, CDDAFadeVolume;
|
||||
|
||||
static INLINE void Fader_SyncWhich(void)
|
||||
{
|
||||
if(Fader.Command & 0x2) // ADPCM fade
|
||||
{
|
||||
ADPCMFadeVolume = Fader.Volume;
|
||||
CDDAFadeVolume = 65536;
|
||||
}
|
||||
else // CD-DA Fade
|
||||
{
|
||||
CDDAFadeVolume = Fader.Volume;
|
||||
ADPCMFadeVolume = 65536;
|
||||
}
|
||||
|
||||
ADPCMFadeVolume >>= 6;
|
||||
PCECD_Drive_SetCDDAVolume(0.50f * CDDAFadeVolume * CDDAVolumeSetting);
|
||||
}
|
||||
|
||||
static INLINE int32 ADPCM_ClocksToNextEvent(void)
|
||||
{
|
||||
int32 ret = (ADPCM.bigdiv + 65535) >> 16;
|
||||
|
||||
if(ADPCM.WritePending && ret > ADPCM.WritePending)
|
||||
ret = ADPCM.WritePending;
|
||||
|
||||
if(ADPCM.ReadPending && ret > ADPCM.ReadPending)
|
||||
ret = ADPCM.ReadPending;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static int32 CalcNextEvent(int32 base)
|
||||
{
|
||||
int32 next_event = base;
|
||||
int32 ADPCM_ctne = ADPCM_ClocksToNextEvent();
|
||||
|
||||
if(next_event > ADPCM_ctne)
|
||||
next_event = ADPCM_ctne;
|
||||
|
||||
if(ClearACKDelay > 0 && next_event > ClearACKDelay)
|
||||
next_event = ClearACKDelay;
|
||||
|
||||
if(next_event > pcecd_drive_ne)
|
||||
next_event = pcecd_drive_ne;
|
||||
|
||||
if(Fader.Clocked && next_event > Fader.CycleCounter)
|
||||
next_event = Fader.CycleCounter;
|
||||
|
||||
return(next_event);
|
||||
}
|
||||
|
||||
static void update_irq_state()
|
||||
{
|
||||
uint8 irq = _Port[2] & _Port[0x3] & (0x4|0x8|0x10|0x20|0x40);
|
||||
|
||||
IRQCB((bool)irq);
|
||||
}
|
||||
|
||||
static void StuffSubchannel(uint8 meow, int subindex)
|
||||
{
|
||||
uint8 tmp_data = meow & 0x7F;
|
||||
|
||||
if(subindex == -2)
|
||||
tmp_data = 0x00;
|
||||
else if(subindex == -1)
|
||||
tmp_data = 0x80;
|
||||
|
||||
if(SubChannelFIFO.CanWrite())
|
||||
SubChannelFIFO.Write(&tmp_data, 1);
|
||||
|
||||
_Port[0x3] |= 0x10;
|
||||
update_irq_state();
|
||||
}
|
||||
|
||||
static void CDIRQ(int type)
|
||||
{
|
||||
#ifdef PCECD_DEBUG
|
||||
if(type != 0x8000 || _Port[0x3] & 0x60)
|
||||
printf("CDIRQ: %d\n", type);
|
||||
#endif
|
||||
if(type & 0x8000)
|
||||
{
|
||||
type &= 0x7FFF;
|
||||
if(type == PCECD_Drive_IRQ_DATA_TRANSFER_DONE)
|
||||
_Port[0x3] &= ~0x20;
|
||||
else if(type == PCECD_Drive_IRQ_DATA_TRANSFER_READY)
|
||||
_Port[0x3] &= ~0x40;
|
||||
}
|
||||
else if(type == PCECD_Drive_IRQ_DATA_TRANSFER_DONE)
|
||||
{
|
||||
_Port[0x3] |= 0x20;
|
||||
}
|
||||
else if(type == PCECD_Drive_IRQ_DATA_TRANSFER_READY)
|
||||
{
|
||||
_Port[0x3] |= 0x40;
|
||||
}
|
||||
update_irq_state();
|
||||
}
|
||||
|
||||
static void UpdateADPCMIRQState(void)
|
||||
{
|
||||
_Port[0x3] &= ~0xC;
|
||||
|
||||
_Port[0x3] |= ADPCM.HalfReached ? 0x4 : 0x0;
|
||||
_Port[0x3] |= ADPCM.EndReached ? 0x8 : 0x0;
|
||||
|
||||
update_irq_state();
|
||||
}
|
||||
|
||||
static INLINE uint8 read_1808(int32 timestamp)
|
||||
{
|
||||
uint8 ret = PCECD_Drive_GetDB();
|
||||
|
||||
if(PCECD_Drive_GetREQ() && !PCECD_Drive_GetACK() && !PCECD_Drive_GetCD())
|
||||
{
|
||||
if(PCECD_Drive_GetIO())
|
||||
{
|
||||
PCECD_Drive_SetACK(TRUE);
|
||||
ACKStatus = TRUE;
|
||||
pcecd_drive_ne = PCECD_Drive_Run(timestamp);
|
||||
ClearACKDelay = 15 * 3;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
bool PCECD_SetSettings(const PCECD_Settings *settings)
|
||||
{
|
||||
if(settings)
|
||||
{
|
||||
assert(settings->CDDA_Volume <= 2.0);
|
||||
assert(settings->ADPCM_Volume <= 2.0);
|
||||
}
|
||||
|
||||
CDDAVolumeSetting = settings ? settings->CDDA_Volume : 1.0;
|
||||
Fader_SyncWhich();
|
||||
|
||||
Blip_Synth_set_volume(&ADPCMSynth, 0.42735f * (settings ? settings->ADPCM_Volume : 1.0), 0x4000);
|
||||
ADPCMLP = settings ? settings->ADPCM_LPF : 0;
|
||||
|
||||
PCECD_Drive_SetTransferRate(126000 * (settings ? settings->CD_Speed : 1));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PCECD_Init(const PCECD_Settings *settings, void (*irqcb)(bool), double master_clock, unsigned int ocm, Blip_Buffer *soundbuf_l, Blip_Buffer *soundbuf_r)
|
||||
{
|
||||
lastts = 0;
|
||||
|
||||
OC_Multiplier = ocm;
|
||||
|
||||
IRQCB = irqcb;
|
||||
|
||||
sbuf[0] = soundbuf_l;
|
||||
sbuf[1] = soundbuf_r;
|
||||
|
||||
// Warning: magic number 126000 in PCECD_SetSettings() too
|
||||
PCECD_Drive_Init(3 * OC_Multiplier, sbuf[0], sbuf[1], 126000 * (settings ? settings->CD_Speed : 1), master_clock * OC_Multiplier, CDIRQ, StuffSubchannel);
|
||||
|
||||
if(!(ADPCM.RAM = (uint8 *)malloc(0x10000)))
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
PCECD_SetSettings(settings);
|
||||
|
||||
ADPCM.bigdivacc = (int64)((double)master_clock * OC_Multiplier * 65536 / 32087.5);
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
void PCECD_Close(void)
|
||||
{
|
||||
if(ADPCM.RAM)
|
||||
free(ADPCM.RAM);
|
||||
ADPCM.RAM = NULL;
|
||||
PCECD_Drive_Close();
|
||||
}
|
||||
|
||||
|
||||
void PCECD_Power(uint32 timestamp)
|
||||
{
|
||||
if((int32)timestamp != lastts)
|
||||
(void)PCECD_Run(timestamp);
|
||||
|
||||
IRQCB(0);
|
||||
|
||||
PCECD_Drive_Power(timestamp);
|
||||
pcecd_drive_ne = 0x7fffffff;
|
||||
|
||||
bBRAMEnabled = FALSE;
|
||||
memset(_Port, 0, sizeof(_Port));
|
||||
ACKStatus = 0;
|
||||
ClearACKDelay = 0;
|
||||
|
||||
memset(ADPCM.RAM, 0x00, 65536);
|
||||
|
||||
ADPCM.ReadPending = ADPCM.WritePending = 0;
|
||||
ADPCM.ReadBuffer = 0;
|
||||
ADPCM.PlayBuffer = 0;
|
||||
|
||||
ADPCM.LastCmd = 0;
|
||||
MSM5205.SetSample(0x800);
|
||||
MSM5205.SetSSI(0);
|
||||
|
||||
ADPCM.SampleFreq = 0;
|
||||
ADPCM.LPF_SampleFreq = 0;
|
||||
ADPCM.bigdiv = ADPCM.bigdivacc * (16 - ADPCM.SampleFreq);
|
||||
|
||||
ADPCM.Addr = 0;
|
||||
ADPCM.ReadAddr = 0;
|
||||
ADPCM.WriteAddr = 0;
|
||||
ADPCM.LengthCount = 0;
|
||||
ADPCM.LastCmd = 0;
|
||||
|
||||
ADPCM.HalfReached = false;
|
||||
ADPCM.EndReached = false;
|
||||
ADPCM.Playing = false;
|
||||
ADPCM.PlayNibble = 0;
|
||||
|
||||
UpdateADPCMIRQState();
|
||||
|
||||
Fader.Command = 0x00;
|
||||
Fader.Volume = 0;
|
||||
Fader.CycleCounter = 0;
|
||||
Fader.CountValue = 0;
|
||||
Fader.Clocked = FALSE;
|
||||
}
|
||||
|
||||
bool PCECD_IsBRAMEnabled(void)
|
||||
{
|
||||
return bBRAMEnabled;
|
||||
}
|
||||
|
||||
uint8 PCECD_Read(uint32 timestamp, uint32 A)
|
||||
{
|
||||
uint8 ret = 0;
|
||||
|
||||
if((A & 0x18c0) == 0x18c0)
|
||||
{
|
||||
switch (A & 0x18cf)
|
||||
{
|
||||
case 0x18c1: ret = 0xaa; break;
|
||||
case 0x18c2: ret = 0x55; break;
|
||||
case 0x18c3: ret = 0x00; break;
|
||||
case 0x18c5: ret = 0xaa; break;
|
||||
case 0x18c6: ret = 0x55; break;
|
||||
case 0x18c7: ret = 0x03; break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PCECD_Run(timestamp);
|
||||
|
||||
switch(A & 0xf)
|
||||
{
|
||||
case 0x0:
|
||||
ret = 0;
|
||||
ret |= PCECD_Drive_GetBSY() ? 0x80 : 0x00;
|
||||
ret |= PCECD_Drive_GetREQ() ? 0x40 : 0x00;
|
||||
ret |= PCECD_Drive_GetMSG() ? 0x20 : 0x00;
|
||||
ret |= PCECD_Drive_GetCD() ? 0x10 : 0x00;
|
||||
ret |= PCECD_Drive_GetIO() ? 0x08 : 0x00;
|
||||
break;
|
||||
|
||||
case 0x1: ret = PCECD_Drive_GetDB();
|
||||
break;
|
||||
|
||||
case 0x2: ret = _Port[2];
|
||||
break;
|
||||
|
||||
case 0x3: bBRAMEnabled = FALSE;
|
||||
|
||||
/* switch left/right of digitized cd playback */
|
||||
ret = _Port[0x3];
|
||||
_Port[0x3] ^= 2;
|
||||
break;
|
||||
|
||||
case 0x4: ret = _Port[4];
|
||||
break;
|
||||
|
||||
case 0x5: if(_Port[0x3] & 0x2)
|
||||
ret = RawPCMVolumeCache[1] & 0xff; // Right
|
||||
else
|
||||
ret = RawPCMVolumeCache[0] & 0xff; // Left
|
||||
break;
|
||||
|
||||
case 0x6: if(_Port[0x3] & 0x2)
|
||||
ret = ((uint16)RawPCMVolumeCache[1]) >> 8; // Right
|
||||
else
|
||||
ret = ((uint16)RawPCMVolumeCache[0]) >> 8; // Left
|
||||
break;
|
||||
|
||||
case 0x7:
|
||||
if(SubChannelFIFO.in_count > 0)
|
||||
ret = SubChannelFIFO.ReadByte();
|
||||
else
|
||||
ret = 0x00; // Not sure if it's 0, 0xFF, the last byte read, or something else.
|
||||
|
||||
if(SubChannelFIFO.in_count == 0)
|
||||
{
|
||||
_Port[0x3] &= ~0x10;
|
||||
update_irq_state();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
ret = read_1808(timestamp);
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
ADPCM_DEBUG("ReadBuffer\n");
|
||||
ADPCM.ReadPending = 19 * 3; //24 * 3;
|
||||
ret = ADPCM.ReadBuffer;
|
||||
break;
|
||||
|
||||
case 0xb:
|
||||
ret = _Port[0xb];
|
||||
break;
|
||||
|
||||
case 0xc:
|
||||
//printf("ADPCM Status Read: %d\n", timestamp);
|
||||
ret = 0x00;
|
||||
|
||||
ret |= (ADPCM.EndReached) ? 0x01 : 0x00;
|
||||
ret |= (ADPCM.Playing) ? 0x08 : 0x00;
|
||||
ret |= (ADPCM.WritePending > 0) ? 0x04 : 0x00;
|
||||
ret |= (ADPCM.ReadPending > 0) ? 0x80 : 0x00;
|
||||
break;
|
||||
|
||||
case 0xd:
|
||||
ret = ADPCM.LastCmd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PCECD_DEBUG
|
||||
printf("Read: %04x %02x, %d\n", A, ret, timestamp);
|
||||
#endif
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static INLINE void Fader_Run(const int32 clocks)
|
||||
{
|
||||
if(Fader.Clocked)
|
||||
{
|
||||
Fader.CycleCounter -= clocks;
|
||||
while(Fader.CycleCounter <= 0)
|
||||
{
|
||||
if(Fader.Volume)
|
||||
Fader.Volume--;
|
||||
|
||||
Fader_SyncWhich();
|
||||
|
||||
Fader.CycleCounter += Fader.CountValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PCECD_Write(uint32 timestamp, uint32 physAddr, uint8 data)
|
||||
{
|
||||
const uint8 V = data;
|
||||
|
||||
#ifdef PCECD_DEBUG
|
||||
printf("Write: (PC=%04x, t=%6d) %04x %02x; MSG: %d, REQ: %d, ACK: %d, CD: %d, IO: %d, BSY: %d, SEL: %d\n", HuCPU.PC, timestamp, physAddr, data, PCECD_Drive_GetMSG(), PCECD_Drive_GetREQ(), PCECD_Drive_GetACK(), PCECD_Drive_GetCD(), PCECD_Drive_GetIO(), PCECD_Drive_GetBSY(), PCECD_Drive_GetSEL());
|
||||
#endif
|
||||
|
||||
PCECD_Run(timestamp);
|
||||
|
||||
switch (physAddr & 0xf)
|
||||
{
|
||||
case 0x0:
|
||||
PCECD_Drive_SetSEL(1);
|
||||
PCECD_Drive_Run(timestamp);
|
||||
PCECD_Drive_SetSEL(0);
|
||||
pcecd_drive_ne = PCECD_Drive_Run(timestamp);
|
||||
|
||||
/* reset irq status */
|
||||
_Port[0x3] &= ~(0x20 | 0x40); // TODO: Confirm writing this register really reset these bits.
|
||||
update_irq_state();
|
||||
break;
|
||||
|
||||
case 0x1: // $1801
|
||||
_Port[1] = data;
|
||||
PCECD_Drive_SetDB(data);
|
||||
pcecd_drive_ne = PCECD_Drive_Run(timestamp);
|
||||
break;
|
||||
|
||||
case 0x2: // $1802
|
||||
#ifdef PCECD_DEBUG
|
||||
if(!(_Port[0x3] & _Port[2] & 0x40) && (_Port[0x3] & data & 0x40))
|
||||
puts("IRQ on waah 0x40");
|
||||
if(!(_Port[0x3] & _Port[2] & 0x20) && (_Port[0x3] & data & 0x20))
|
||||
puts("IRQ on waah 0x20");
|
||||
#endif
|
||||
|
||||
PCECD_Drive_SetACK(data & 0x80);
|
||||
pcecd_drive_ne = PCECD_Drive_Run(timestamp);
|
||||
_Port[2] = data;
|
||||
ACKStatus = (bool)(data & 0x80);
|
||||
update_irq_state();
|
||||
break;
|
||||
|
||||
case 0x3: // read only
|
||||
break;
|
||||
|
||||
case 0x4:
|
||||
PCECD_Drive_SetRST(data & 0x2);
|
||||
pcecd_drive_ne = PCECD_Drive_Run(timestamp);
|
||||
if(data & 0x2)
|
||||
{
|
||||
_Port[0x3] &= ~0x70;
|
||||
update_irq_state();
|
||||
}
|
||||
_Port[4] = data;
|
||||
break;
|
||||
|
||||
case 0x5:
|
||||
case 0x6:
|
||||
{
|
||||
int16 left, right;
|
||||
PCECD_Drive_GetCDDAValues(left, right);
|
||||
RawPCMVolumeCache[0] = ((int64)abs(left) * CDDAFadeVolume) >> 16;
|
||||
RawPCMVolumeCache[1] = ((int64)abs(right) * CDDAFadeVolume) >> 16;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7: // $1807: D7=1 enables backup ram
|
||||
if (data & 0x80)
|
||||
{
|
||||
bBRAMEnabled = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8: // Set ADPCM address low
|
||||
if(ADPCM.LastCmd & 0x80)
|
||||
break;
|
||||
|
||||
ADPCM.Addr &= 0xFF00;
|
||||
ADPCM.Addr |= V;
|
||||
|
||||
ADPCM_DEBUG("SAL: %02x, %d\n", V, timestamp);
|
||||
|
||||
// Length appears to be constantly latched when D4 is set(tested on a real system)
|
||||
if(ADPCM.LastCmd & 0x10)
|
||||
{
|
||||
ADPCM_DEBUG("Set length(crazy way L): %04x\n", ADPCM.Addr);
|
||||
ADPCM.LengthCount = ADPCM.Addr;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9: // Set ADPCM address high
|
||||
if(ADPCM.LastCmd & 0x80)
|
||||
break;
|
||||
|
||||
ADPCM.Addr &= 0x00FF;
|
||||
ADPCM.Addr |= V << 8;
|
||||
|
||||
ADPCM_DEBUG("SAH: %02x, %d\n", V, timestamp);
|
||||
|
||||
// Length appears to be constantly latched when D4 is set(tested on a real system)
|
||||
if(ADPCM.LastCmd & 0x10)
|
||||
{
|
||||
ADPCM_DEBUG("Set length(crazy way H): %04x\n", ADPCM.Addr);
|
||||
ADPCM.LengthCount = ADPCM.Addr;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
//ADPCM_DEBUG("Write: %02x, %d\n", V, timestamp);
|
||||
ADPCM.WritePending = 3 * 11;
|
||||
ADPCM.WritePendingValue = data;
|
||||
break;
|
||||
|
||||
case 0xb: // adpcm dma
|
||||
ADPCM_DEBUG("DMA: %02x\n", V);
|
||||
_Port[0xb] = data;
|
||||
break;
|
||||
|
||||
case 0xc: // read-only
|
||||
break;
|
||||
|
||||
case 0xd:
|
||||
ADPCM_DEBUG("Write180D: %02x\n", V);
|
||||
if(data & 0x80)
|
||||
{
|
||||
ADPCM.Addr = 0;
|
||||
ADPCM.ReadAddr = 0;
|
||||
ADPCM.WriteAddr = 0;
|
||||
ADPCM.LengthCount = 0;
|
||||
ADPCM.LastCmd = 0;
|
||||
|
||||
ADPCM.Playing = false;
|
||||
ADPCM.HalfReached = false;
|
||||
ADPCM.EndReached = false;
|
||||
|
||||
ADPCM.PlayNibble = 0;
|
||||
|
||||
UpdateADPCMIRQState();
|
||||
|
||||
MSM5205.SetSample(0x800);
|
||||
MSM5205.SetSSI(0);
|
||||
break;
|
||||
}
|
||||
|
||||
if(ADPCM.Playing && !(data & 0x20))
|
||||
ADPCM.Playing = false;
|
||||
|
||||
if(!ADPCM.Playing && (data & 0x20))
|
||||
{
|
||||
ADPCM.bigdiv = ADPCM.bigdivacc * (16 - ADPCM.SampleFreq);
|
||||
ADPCM.Playing = true;
|
||||
ADPCM.HalfReached = false; // Not sure about this.
|
||||
ADPCM.PlayNibble = 0;
|
||||
MSM5205.SetSample(0x800);
|
||||
MSM5205.SetSSI(0);
|
||||
}
|
||||
|
||||
// Length appears to be constantly latched when D4 is set(tested on a real system)
|
||||
if(data & 0x10)
|
||||
{
|
||||
ADPCM_DEBUG("Set length: %04x\n", ADPCM.Addr);
|
||||
ADPCM.LengthCount = ADPCM.Addr;
|
||||
ADPCM.EndReached = false;
|
||||
}
|
||||
|
||||
// D2 and D3 control read address
|
||||
if(!(ADPCM.LastCmd & 0x8) && (data & 0x08))
|
||||
{
|
||||
if(data & 0x4)
|
||||
ADPCM.ReadAddr = ADPCM.Addr;
|
||||
else
|
||||
ADPCM.ReadAddr = (ADPCM.Addr - 1) & 0xFFFF;
|
||||
|
||||
ADPCM_DEBUG("Set ReadAddr: %04x, %06x\n", ADPCM.Addr, ADPCM.ReadAddr);
|
||||
}
|
||||
|
||||
// D0 and D1 control write address
|
||||
if(!(ADPCM.LastCmd & 0x2) && (data & 0x2))
|
||||
{
|
||||
ADPCM.WriteAddr = ADPCM.Addr;
|
||||
if(!(data & 0x1))
|
||||
ADPCM.WriteAddr = (ADPCM.WriteAddr - 1) & 0xFFFF;
|
||||
ADPCM_DEBUG("Set WriteAddr: %04x, %06x\n", ADPCM.Addr, ADPCM.WriteAddr);
|
||||
}
|
||||
ADPCM.LastCmd = data;
|
||||
UpdateADPCMIRQState();
|
||||
break;
|
||||
|
||||
case 0xe: // Set ADPCM playback rate
|
||||
{
|
||||
uint8 freq = V & 0x0F;
|
||||
|
||||
ADPCM.SampleFreq = freq;
|
||||
|
||||
ADPCM_DEBUG("Freq: %02x\n", freq);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xf:
|
||||
Fader.Command = V;
|
||||
|
||||
#ifdef PCECD_DEBUG
|
||||
printf("Fade: %02x\n", data);
|
||||
#endif
|
||||
|
||||
// Cancel fade
|
||||
if(!(V & 0x8))
|
||||
{
|
||||
Fader.Volume = 65536;
|
||||
Fader.CycleCounter = 0;
|
||||
Fader.CountValue = 0;
|
||||
Fader.Clocked = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Fader.CountValue = OC_Multiplier * 3 * ((V & 0x4) ? 273 : 655); // 2.500s : 6.000s;
|
||||
|
||||
if(!Fader.Clocked)
|
||||
Fader.CycleCounter = Fader.CountValue;
|
||||
|
||||
Fader.Clocked = TRUE;
|
||||
}
|
||||
Fader_SyncWhich();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE void ADPCM_PB_Run(int32 basetime, int32 run_time)
|
||||
{
|
||||
ADPCM.bigdiv -= run_time * 65536;
|
||||
|
||||
while(ADPCM.bigdiv <= 0)
|
||||
{
|
||||
ADPCM.bigdiv += ADPCM.bigdivacc * (16 - ADPCM.SampleFreq);
|
||||
|
||||
if(ADPCM.Playing && !ADPCM.PlayNibble) // Do playback sample buffer fetch.
|
||||
{
|
||||
ADPCM.HalfReached = (ADPCM.LengthCount < 32768);
|
||||
if(!ADPCM.LengthCount && !(ADPCM.LastCmd & 0x10))
|
||||
{
|
||||
if(ADPCM.EndReached)
|
||||
ADPCM.HalfReached = false;
|
||||
|
||||
ADPCM.EndReached = true;
|
||||
|
||||
if(ADPCM.LastCmd & 0x40)
|
||||
ADPCM.Playing = false;
|
||||
}
|
||||
|
||||
ADPCM.PlayBuffer = ADPCM.RAM[ADPCM.ReadAddr];
|
||||
ADPCM.ReadAddr = (ADPCM.ReadAddr + 1) & 0xFFFF;
|
||||
|
||||
if(ADPCM.LengthCount && !(ADPCM.LastCmd & 0x10))
|
||||
ADPCM.LengthCount--;
|
||||
}
|
||||
|
||||
if(ADPCM.Playing)
|
||||
{
|
||||
int32 pcm;
|
||||
uint8 nibble;
|
||||
|
||||
nibble = (ADPCM.PlayBuffer >> (ADPCM.PlayNibble ^ 4)) & 0x0F;
|
||||
pcm = MSM5205.Decode(nibble) - 2048;
|
||||
|
||||
ADPCM.PlayNibble ^= 4;
|
||||
|
||||
pcm = (pcm * ADPCMFadeVolume) >> 8;
|
||||
uint32 synthtime = ((basetime + (ADPCM.bigdiv >> 16))) / (3 * OC_Multiplier);
|
||||
|
||||
if(sbuf[0] && sbuf[1])
|
||||
{
|
||||
Blip_Synth_offset(&ADPCMSynth, synthtime, pcm - ADPCM.last_pcm, sbuf[0]);
|
||||
Blip_Synth_offset(&ADPCMSynth, synthtime, pcm - ADPCM.last_pcm, sbuf[1]);
|
||||
}
|
||||
ADPCM.last_pcm = pcm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE void ADPCM_Run(const int32 clocks, const int32 timestamp)
|
||||
{
|
||||
//printf("ADPCM Run: %d\n", clocks);
|
||||
ADPCM_PB_Run(timestamp, clocks);
|
||||
|
||||
if(ADPCM.WritePending)
|
||||
{
|
||||
ADPCM.WritePending -= clocks;
|
||||
if(ADPCM.WritePending <= 0)
|
||||
{
|
||||
ADPCM.HalfReached = (ADPCM.LengthCount < 32768);
|
||||
if(!(ADPCM.LastCmd & 0x10) && ADPCM.LengthCount < 0xFFFF)
|
||||
ADPCM.LengthCount++;
|
||||
|
||||
ADPCM.RAM[ADPCM.WriteAddr++] = ADPCM.WritePendingValue;
|
||||
ADPCM.WritePending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ADPCM.WritePending)
|
||||
{
|
||||
if(_Port[0xb] & 0x3)
|
||||
{
|
||||
// Run PCECD_Drive before we examine the signals.
|
||||
pcecd_drive_ne = PCECD_Drive_Run(timestamp);
|
||||
|
||||
if(!PCECD_Drive_GetCD() && PCECD_Drive_GetIO() && PCECD_Drive_GetREQ() && !PCECD_Drive_GetACK())
|
||||
{
|
||||
ADPCM.WritePendingValue = read_1808(timestamp);
|
||||
ADPCM.WritePending = 10 * 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ADPCM.ReadPending)
|
||||
{
|
||||
ADPCM.ReadPending -= clocks;
|
||||
if(ADPCM.ReadPending <= 0)
|
||||
{
|
||||
ADPCM.ReadBuffer = ADPCM.RAM[ADPCM.ReadAddr];
|
||||
ADPCM.ReadAddr = (ADPCM.ReadAddr + 1) & 0xFFFF;
|
||||
ADPCM.ReadPending = 0;
|
||||
|
||||
ADPCM.HalfReached = (ADPCM.LengthCount < 32768);
|
||||
if(!(ADPCM.LastCmd & 0x10))
|
||||
{
|
||||
if(ADPCM.LengthCount)
|
||||
ADPCM.LengthCount--;
|
||||
else
|
||||
{
|
||||
ADPCM.EndReached = true;
|
||||
ADPCM.HalfReached = false;
|
||||
|
||||
if(ADPCM.LastCmd & 0x40)
|
||||
ADPCM.Playing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateADPCMIRQState();
|
||||
}
|
||||
|
||||
void PCECD_Run(uint32 in_timestamp)
|
||||
{
|
||||
int32 clocks = in_timestamp - lastts;
|
||||
int32 running_ts = lastts;
|
||||
|
||||
//printf("Run Begin: Clocks=%d(%d - %d), cl=%d\n", clocks, in_timestamp, lastts, CalcNextEvent);
|
||||
//fflush(stdout);
|
||||
|
||||
while(clocks > 0)
|
||||
{
|
||||
int32 chunk_clocks = CalcNextEvent(clocks);
|
||||
|
||||
running_ts += chunk_clocks;
|
||||
|
||||
if(ClearACKDelay > 0)
|
||||
{
|
||||
ClearACKDelay -= chunk_clocks;
|
||||
if(ClearACKDelay <= 0)
|
||||
{
|
||||
ACKStatus = FALSE;
|
||||
PCECD_Drive_SetACK(FALSE);
|
||||
PCECD_Drive_Run(running_ts);
|
||||
if(PCECD_Drive_GetCD())
|
||||
{
|
||||
_Port[0xb] &= ~1;
|
||||
#ifdef PCECD_DEBUG
|
||||
puts("DMA End");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Fader_Run(chunk_clocks);
|
||||
|
||||
ADPCM_Run(chunk_clocks, running_ts);
|
||||
pcecd_drive_ne = PCECD_Drive_Run(running_ts);
|
||||
|
||||
clocks -= chunk_clocks;
|
||||
}
|
||||
|
||||
lastts = in_timestamp;
|
||||
}
|
||||
|
||||
void PCECD_ResetTS(void)
|
||||
{
|
||||
PCECD_Drive_ResetTS();
|
||||
lastts = 0;
|
||||
}
|
||||
|
||||
static int ADPCM_StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
uint32 ad_sample = MSM5205.GetSample();
|
||||
int32 ad_ref_index = MSM5205.GetSSI();
|
||||
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFARRAY(ADPCM.RAM, 0x10000),
|
||||
|
||||
SFVAR(ADPCM.bigdiv),
|
||||
SFVAR(ADPCM.Addr),
|
||||
SFVAR(ADPCM.ReadAddr),
|
||||
SFVAR(ADPCM.WriteAddr),
|
||||
SFVAR(ADPCM.LengthCount),
|
||||
SFVAR(ADPCM.LastCmd),
|
||||
SFVAR(ADPCM.SampleFreq),
|
||||
|
||||
SFVAR(ADPCM.ReadPending),
|
||||
SFVAR(ADPCM.ReadBuffer),
|
||||
SFVAR(ADPCM.PlayBuffer),
|
||||
|
||||
SFVAR(ADPCM.WritePending),
|
||||
SFVAR(ADPCM.WritePendingValue),
|
||||
|
||||
SFVAR(ADPCM.HalfReached),
|
||||
SFVAR(ADPCM.EndReached),
|
||||
SFVAR(ADPCM.Playing),
|
||||
|
||||
SFVAR(ADPCM.PlayNibble),
|
||||
|
||||
SFVAR(ad_sample),
|
||||
SFVAR(ad_ref_index),
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "APCM", false);
|
||||
if(load)
|
||||
{
|
||||
MSM5205.SetSample(ad_sample);
|
||||
MSM5205.SetSSI(ad_ref_index);
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
int PCECD_StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(bBRAMEnabled),
|
||||
SFVAR(ACKStatus),
|
||||
SFVAR(ClearACKDelay),
|
||||
SFARRAY16(RawPCMVolumeCache, 2),
|
||||
SFARRAY(_Port, sizeof(_Port)),
|
||||
|
||||
SFVAR(Fader.Command),
|
||||
SFVAR(Fader.Volume),
|
||||
SFVAR(Fader.CycleCounter),
|
||||
SFVAR(Fader.CountValue),
|
||||
SFVAR(Fader.Clocked),
|
||||
|
||||
SFARRAY(&SubChannelFIFO.data[0], SubChannelFIFO.size),
|
||||
SFVAR(SubChannelFIFO.read_pos),
|
||||
SFVAR(SubChannelFIFO.write_pos),
|
||||
SFVAR(SubChannelFIFO.in_count),
|
||||
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "PECD", false);
|
||||
ret &= PCECD_Drive_StateAction(sm, load, data_only, "CDRM");
|
||||
ret &= ADPCM_StateAction(sm, load, data_only);
|
||||
|
||||
if(load)
|
||||
{
|
||||
Fader_SyncWhich();
|
||||
//PCECD_Drive_SetDB(_Port[1]);
|
||||
PCECD_Drive_SetACK(ACKStatus);
|
||||
PCECD_Drive_SetRST(_Port[4] & 0x2);
|
||||
|
||||
SubChannelFIFO.read_pos %= SubChannelFIFO.size;
|
||||
SubChannelFIFO.write_pos %= SubChannelFIFO.size;
|
||||
}
|
||||
return(ret);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#ifndef __PCE_CDROM_H
|
||||
#define __PCE_CDROM_H
|
||||
|
||||
#include "../include/blip/Blip_Buffer.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float CDDA_Volume; // Max 2.000...
|
||||
float ADPCM_Volume; // Max 2.000...
|
||||
|
||||
unsigned int CD_Speed;
|
||||
|
||||
bool ADPCM_LPF;
|
||||
} PCECD_Settings;
|
||||
|
||||
void PCECD_Run(uint32 in_timestamp);
|
||||
void PCECD_ResetTS(void);
|
||||
|
||||
bool PCECD_Init(const PCECD_Settings *settings, void (*irqcb)(bool), double master_clock, unsigned int ocm, Blip_Buffer *soundbuf_l, Blip_Buffer *soundbuf_r) MDFN_COLD;
|
||||
bool PCECD_SetSettings(const PCECD_Settings *settings) MDFN_COLD;
|
||||
void PCECD_Close(void) MDFN_COLD;
|
||||
void PCECD_Power(uint32 timestamp) MDFN_COLD;
|
||||
|
||||
|
||||
uint8 PCECD_Read(uint32 timestamp, uint32);
|
||||
void PCECD_Write(uint32 timestamp, uint32, uint8 data);
|
||||
|
||||
bool PCECD_IsBRAMEnabled(void);
|
||||
|
||||
int PCECD_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,84 +0,0 @@
|
||||
#ifndef __PCEFAST_PCECD_Drive_H
|
||||
#define __PCEFAST_PCECD_Drive_H
|
||||
|
||||
#include "../include/blip/Blip_Buffer.h"
|
||||
|
||||
typedef int32 pcecd_drive_timestamp_t;
|
||||
|
||||
struct pcecd_drive_bus_t
|
||||
{
|
||||
// Data bus(FIXME: we should have a variable for the target and the initiator, and OR them together to be truly accurate).
|
||||
uint8 DB;
|
||||
|
||||
uint32 signals;
|
||||
};
|
||||
|
||||
extern pcecd_drive_bus_t cd_bus; // Don't access this structure directly by name outside of pcecd_drive.c, but use the macros below.
|
||||
|
||||
// Signals under our(the "target") control.
|
||||
#define PCECD_Drive_IO_mask 0x001
|
||||
#define PCECD_Drive_CD_mask 0x002
|
||||
#define PCECD_Drive_MSG_mask 0x004
|
||||
#define PCECD_Drive_REQ_mask 0x008
|
||||
#define PCECD_Drive_BSY_mask 0x010
|
||||
|
||||
// Signals under the control of the initiator(not us!)
|
||||
#define PCECD_Drive_kingRST_mask 0x020
|
||||
#define PCECD_Drive_kingACK_mask 0x040
|
||||
#define PCECD_Drive_kingSEL_mask 0x100
|
||||
|
||||
#define BSY_signal ((const bool)(cd_bus.signals & PCECD_Drive_BSY_mask))
|
||||
#define ACK_signal ((const bool)(cd_bus.signals & PCECD_Drive_kingACK_mask))
|
||||
#define RST_signal ((const bool)(cd_bus.signals & PCECD_Drive_kingRST_mask))
|
||||
#define MSG_signal ((const bool)(cd_bus.signals & PCECD_Drive_MSG_mask))
|
||||
#define SEL_signal ((const bool)(cd_bus.signals & PCECD_Drive_kingSEL_mask))
|
||||
#define REQ_signal ((const bool)(cd_bus.signals & PCECD_Drive_REQ_mask))
|
||||
#define IO_signal ((const bool)(cd_bus.signals & PCECD_Drive_IO_mask))
|
||||
#define CD_signal ((const bool)(cd_bus.signals & PCECD_Drive_CD_mask))
|
||||
|
||||
#define DB_signal ((const uint8)cd_bus.DB)
|
||||
|
||||
#define PCECD_Drive_GetDB() DB_signal
|
||||
#define PCECD_Drive_GetBSY() BSY_signal
|
||||
#define PCECD_Drive_GetIO() IO_signal
|
||||
#define PCECD_Drive_GetCD() CD_signal
|
||||
#define PCECD_Drive_GetMSG() MSG_signal
|
||||
#define PCECD_Drive_GetREQ() REQ_signal
|
||||
|
||||
// Should we phase out getting these initiator-driven signals like this(the initiator really should keep track of them itself)?
|
||||
#define PCECD_Drive_GetACK() ACK_signal
|
||||
#define PCECD_Drive_GetRST() RST_signal
|
||||
#define PCECD_Drive_GetSEL() SEL_signal
|
||||
|
||||
void PCECD_Drive_Power(pcecd_drive_timestamp_t system_timestamp);
|
||||
void PCECD_Drive_SetDB(uint8 data);
|
||||
|
||||
// These PCECD_Drive_Set* functions are kind of misnomers, at least in comparison to the PCECD_Drive_Get* functions...
|
||||
// They will set/clear the bits corresponding to the KING's side of the bus.
|
||||
void PCECD_Drive_SetACK(bool set);
|
||||
void PCECD_Drive_SetSEL(bool set);
|
||||
void PCECD_Drive_SetRST(bool set);
|
||||
|
||||
uint32 PCECD_Drive_Run(pcecd_drive_timestamp_t);
|
||||
void PCECD_Drive_ResetTS(void);
|
||||
|
||||
enum
|
||||
{
|
||||
PCECD_Drive_IRQ_DATA_TRANSFER_DONE = 1,
|
||||
PCECD_Drive_IRQ_DATA_TRANSFER_READY,
|
||||
PCECD_Drive_IRQ_MAGICAL_REQ,
|
||||
};
|
||||
|
||||
void PCECD_Drive_GetCDDAValues(int16 &left, int16 &right);
|
||||
|
||||
void PCECD_Drive_SetLog(void (*logfunc)(const char *, const char *, ...)) MDFN_COLD;
|
||||
void PCECD_Drive_Init(int CDDATimeDiv, Blip_Buffer *leftbuf, Blip_Buffer *rightbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int)) MDFN_COLD;
|
||||
void PCECD_Drive_Close(void) MDFN_COLD;
|
||||
|
||||
void PCECD_Drive_SetTransferRate(uint32 TransferRate);
|
||||
void PCECD_Drive_SetCDDAVolume(unsigned vol); // vol of 65536 = 1.0 = maximum.
|
||||
int PCECD_Drive_StateAction(StateMem *sm, int load, int data_only, const char *sname);
|
||||
|
||||
void PCECD_Drive_SetDisc(bool tray_open, CDIF *cdif, bool no_emu_side_effects = false) MDFN_COLD;
|
||||
|
||||
#endif
|
@ -1,670 +0,0 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../mednafen.h"
|
||||
#include "psg.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
void PCEFast_PSG::SetVolume(double new_volume)
|
||||
{
|
||||
OutputVolume = new_volume;
|
||||
|
||||
Blip_Synth_set_volume(&Synth, OutputVolume / 6, 8192);
|
||||
}
|
||||
|
||||
void PCEFast_PSG::UpdateOutput_Norm(const int32 timestamp, psg_channel *ch)
|
||||
{
|
||||
int32 samp[2];
|
||||
int sv = ch->dda;
|
||||
|
||||
samp[0] = dbtable[ch->vl[0]][sv];
|
||||
samp[1] = dbtable[ch->vl[1]][sv];
|
||||
|
||||
Blip_Synth_offset(&Synth, timestamp, samp[0] - ch->blip_prev_samp[0], sbuf[0]);
|
||||
Blip_Synth_offset(&Synth, timestamp, samp[1] - ch->blip_prev_samp[1], sbuf[1]);
|
||||
|
||||
ch->blip_prev_samp[0] = samp[0];
|
||||
ch->blip_prev_samp[1] = samp[1];
|
||||
}
|
||||
|
||||
void PCEFast_PSG::UpdateOutput_Noise(const int32 timestamp, psg_channel *ch)
|
||||
{
|
||||
int32 samp[2];
|
||||
int sv = ((ch->lfsr & 1) << 5) - (ch->lfsr & 1); //(ch->lfsr & 0x1) ? 0x1F : 0;
|
||||
|
||||
samp[0] = dbtable[ch->vl[0]][sv];
|
||||
samp[1] = dbtable[ch->vl[1]][sv];
|
||||
|
||||
Blip_Synth_offset(&Synth, timestamp, samp[0] - ch->blip_prev_samp[0], sbuf[0]);
|
||||
Blip_Synth_offset(&Synth, timestamp, samp[1] - ch->blip_prev_samp[1], sbuf[1]);
|
||||
|
||||
ch->blip_prev_samp[0] = samp[0];
|
||||
ch->blip_prev_samp[1] = samp[1];
|
||||
}
|
||||
|
||||
void PCEFast_PSG::UpdateOutput_Off(const int32 timestamp, psg_channel *ch)
|
||||
{
|
||||
int32 samp[2];
|
||||
|
||||
samp[0] = samp[1] = 0;
|
||||
|
||||
Blip_Synth_offset(&Synth, timestamp, samp[0] - ch->blip_prev_samp[0], sbuf[0]);
|
||||
Blip_Synth_offset(&Synth, timestamp, samp[1] - ch->blip_prev_samp[1], sbuf[1]);
|
||||
|
||||
ch->blip_prev_samp[0] = samp[0];
|
||||
ch->blip_prev_samp[1] = samp[1];
|
||||
}
|
||||
|
||||
|
||||
void PCEFast_PSG::UpdateOutput_Accum(const int32 timestamp, psg_channel *ch)
|
||||
{
|
||||
int32 samp[2];
|
||||
|
||||
samp[0] = ((int32)dbtable_volonly[ch->vl[0]] * ((int32)ch->samp_accum - 496)) >> (8 + 5);
|
||||
samp[1] = ((int32)dbtable_volonly[ch->vl[1]] * ((int32)ch->samp_accum - 496)) >> (8 + 5);
|
||||
|
||||
Blip_Synth_offset(&Synth, timestamp, samp[0] - ch->blip_prev_samp[0], sbuf[0]);
|
||||
Blip_Synth_offset(&Synth, timestamp, samp[1] - ch->blip_prev_samp[1], sbuf[1]);
|
||||
|
||||
ch->blip_prev_samp[0] = samp[0];
|
||||
ch->blip_prev_samp[1] = samp[1];
|
||||
}
|
||||
|
||||
// This function should always be called after RecalcFreqCache() (it's not called from RecalcFreqCache to avoid redundant code)
|
||||
void PCEFast_PSG::RecalcUOFunc(int chnum)
|
||||
{
|
||||
psg_channel *ch = &channel[chnum];
|
||||
|
||||
//printf("UO Update: %d, %02x\n", chnum, ch->control);
|
||||
|
||||
if(!(ch->control & 0xC0))
|
||||
ch->UpdateOutput = &PCEFast_PSG::UpdateOutput_Off;
|
||||
else if(ch->noisectrl & ch->control & 0x80)
|
||||
ch->UpdateOutput = &PCEFast_PSG::UpdateOutput_Noise;
|
||||
// If the control for the channel is in waveform play mode, and the (real) playback frequency is too high, and the channel is either not the LFO modulator channel or
|
||||
// if the LFO trigger bit(which halts the LFO modulator channel's waveform incrementing when set) is clear
|
||||
else if((ch->control & 0xC0) == 0x80 && ch->freq_cache <= 0xA && (chnum != 1 || !(lfoctrl & 0x80)) )
|
||||
ch->UpdateOutput = &PCEFast_PSG::UpdateOutput_Accum;
|
||||
else
|
||||
ch->UpdateOutput = &PCEFast_PSG::UpdateOutput_Norm;
|
||||
}
|
||||
|
||||
|
||||
void PCEFast_PSG::RecalcFreqCache(int chnum)
|
||||
{
|
||||
psg_channel *ch = &channel[chnum];
|
||||
|
||||
if(chnum == 0 && (lfoctrl & 0x03))
|
||||
{
|
||||
const uint32 shift = (((lfoctrl & 0x3) - 1) << 1);
|
||||
uint8 la = channel[1].dda;
|
||||
int32 tmp_freq = ((int32)ch->frequency + ((la - 0x10) << shift)) & 0xFFF;
|
||||
|
||||
ch->freq_cache = (tmp_freq ? tmp_freq : 4096) << 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch->freq_cache = (ch->frequency ? ch->frequency : 4096) << 1;
|
||||
|
||||
if(chnum == 1 && (lfoctrl & 0x03))
|
||||
ch->freq_cache *= lfofreq ? lfofreq : 256;
|
||||
}
|
||||
}
|
||||
|
||||
void PCEFast_PSG::RecalcNoiseFreqCache(int chnum)
|
||||
{
|
||||
psg_channel *ch = &channel[chnum];
|
||||
int32 freq = 0x1F - (ch->noisectrl & 0x1F);
|
||||
|
||||
if(!freq)
|
||||
freq = 0x20;
|
||||
else
|
||||
freq <<= 6;
|
||||
|
||||
freq <<= 1;
|
||||
|
||||
ch->noise_freq_cache = freq;
|
||||
}
|
||||
|
||||
PCEFast_PSG::PCEFast_PSG(Blip_Buffer *bb_l, Blip_Buffer *bb_r)
|
||||
{
|
||||
//printf("Test: %u, %u\n", sizeof(psg_channel), (uint8*)&channel[0].balance - (uint8*)&channel[0].waveform[0]);
|
||||
|
||||
sbuf[0] = bb_l;
|
||||
sbuf[1] = bb_r;
|
||||
|
||||
lastts = 0;
|
||||
for(int ch = 0; ch < 6; ch++)
|
||||
{
|
||||
channel[ch].blip_prev_samp[0] = 0;
|
||||
channel[ch].blip_prev_samp[1] = 0;
|
||||
channel[ch].lastts = 0;
|
||||
}
|
||||
|
||||
SetVolume(1.0);
|
||||
|
||||
for(int vl = 0; vl < 32; vl++)
|
||||
{
|
||||
double flub = 1;
|
||||
|
||||
if(vl)
|
||||
flub /= powf(2, (double)1 / 4 * vl); // ~1.5dB reduction per increment of vl
|
||||
|
||||
if(vl == 0x1F)
|
||||
flub = 0;
|
||||
|
||||
for(int samp = 0; samp < 32; samp++)
|
||||
{
|
||||
int eff_samp = samp * 2 - 0x1F;
|
||||
|
||||
dbtable[vl][samp] = (int32)(flub * eff_samp * 128);
|
||||
dbtable_volonly[vl] = (int32)(flub * 65536);
|
||||
}
|
||||
}
|
||||
|
||||
Power(0);
|
||||
}
|
||||
|
||||
PCEFast_PSG::~PCEFast_PSG()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
int32 PCEFast_PSG::GetVL(const int chnum, const int lr)
|
||||
{
|
||||
// Note: Changing the 0x1F(not that there should be) would require changing the channel pseudo-off volume check logic later on.
|
||||
static const uint8 scale_tab[] =
|
||||
{
|
||||
0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
|
||||
0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F
|
||||
};
|
||||
|
||||
psg_channel *ch = &channel[chnum];
|
||||
|
||||
const int gbal = 0x1F - scale_tab[(globalbalance >> (lr ? 0 : 4)) & 0xF];
|
||||
const int bal = 0x1F - scale_tab[(ch->balance >> (lr ? 0 : 4)) & 0xF];
|
||||
const int al = 0x1F - (ch->control & 0x1F);
|
||||
int vol_reduction;
|
||||
|
||||
vol_reduction = gbal + bal + al;
|
||||
|
||||
if(vol_reduction > 0x1F)
|
||||
vol_reduction = 0x1F;
|
||||
|
||||
return(vol_reduction);
|
||||
}
|
||||
|
||||
void PCEFast_PSG::Write(int32 timestamp, uint8 A, uint8 V)
|
||||
{
|
||||
A &= 0x0F;
|
||||
|
||||
if(A == 0x00)
|
||||
{
|
||||
select = (V & 0x07);
|
||||
return;
|
||||
}
|
||||
|
||||
Update(timestamp);
|
||||
|
||||
psg_channel *ch = &channel[select];
|
||||
|
||||
//if(A == 0x01 || select == 5)
|
||||
// printf("Write Ch: %d %04x %02x, %d\n", select, A, V, timestamp);
|
||||
|
||||
switch(A)
|
||||
{
|
||||
default: break;
|
||||
|
||||
case 0x01: /* Global sound balance */
|
||||
globalbalance = V;
|
||||
vol_pending = true;
|
||||
break;
|
||||
|
||||
case 0x02: /* Channel frequency (LSB) */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
|
||||
ch->frequency = (ch->frequency & 0x0F00) | V;
|
||||
RecalcFreqCache(select);
|
||||
RecalcUOFunc(select);
|
||||
break;
|
||||
|
||||
case 0x03: /* Channel frequency (MSB) */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
|
||||
ch->frequency = (ch->frequency & 0x00FF) | ((V & 0x0F) << 8);
|
||||
RecalcFreqCache(select);
|
||||
RecalcUOFunc(select);
|
||||
break;
|
||||
|
||||
case 0x04: /* Channel enable, DDA, volume */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
|
||||
if((ch->control & 0x40) && !(V & 0x40))
|
||||
{
|
||||
ch->waveform_index = 0;
|
||||
ch->dda = ch->waveform[ch->waveform_index];
|
||||
ch->counter = ch->freq_cache;
|
||||
}
|
||||
|
||||
if(!(ch->control & 0x80) && (V & 0x80))
|
||||
{
|
||||
if(!(V & 0x40))
|
||||
{
|
||||
ch->waveform_index = (ch->waveform_index + 1) & 0x1F;
|
||||
ch->dda = ch->waveform[ch->waveform_index];
|
||||
}
|
||||
}
|
||||
|
||||
ch->control = V;
|
||||
RecalcFreqCache(select);
|
||||
RecalcUOFunc(select);
|
||||
|
||||
vol_pending = true;
|
||||
break;
|
||||
|
||||
case 0x05: /* Channel balance */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
ch->balance = V;
|
||||
|
||||
vol_pending = true;
|
||||
break;
|
||||
|
||||
case 0x06: /* Channel waveform data */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
V &= 0x1F;
|
||||
|
||||
if(!(ch->control & 0x40))
|
||||
{
|
||||
ch->samp_accum -= ch->waveform[ch->waveform_index];
|
||||
ch->waveform[ch->waveform_index] = V;
|
||||
ch->samp_accum += ch->waveform[ch->waveform_index];
|
||||
}
|
||||
|
||||
if((ch->control & 0xC0) == 0x00)
|
||||
ch->waveform_index = ((ch->waveform_index + 1) & 0x1F);
|
||||
|
||||
if(ch->control & 0x80)
|
||||
{
|
||||
// According to my tests(on SuperGrafx), writing to this channel
|
||||
// will update the waveform value cache/latch regardless of DDA mode being enabled.
|
||||
ch->dda = V;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x07: /* Noise enable and frequency */
|
||||
if(select > 5) return; // no more than 6 channels, silly game.
|
||||
if(select >= 4)
|
||||
{
|
||||
ch->noisectrl = V;
|
||||
RecalcNoiseFreqCache(select);
|
||||
RecalcUOFunc(select);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x08: /* LFO frequency */
|
||||
lfofreq = V & 0xFF;
|
||||
//printf("LFO Freq: %02x\n", V);
|
||||
break;
|
||||
|
||||
case 0x09: /* LFO trigger and control */
|
||||
//printf("LFO Ctrl: %02x\n", V);
|
||||
if(V & 0x80)
|
||||
{
|
||||
channel[1].waveform_index = 0;
|
||||
channel[1].dda = channel[1].waveform[channel[1].waveform_index];
|
||||
channel[1].counter = channel[1].freq_cache;
|
||||
}
|
||||
lfoctrl = V;
|
||||
RecalcFreqCache(0);
|
||||
RecalcUOFunc(0);
|
||||
RecalcFreqCache(1);
|
||||
RecalcUOFunc(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't use INLINE, which has always_inline in it, due to gcc's inability to cope with the type of recursion
|
||||
// used in this function.
|
||||
template<bool LFO_On>
|
||||
void PCEFast_PSG::RunChannel(int chc, int32 timestamp)
|
||||
{
|
||||
psg_channel *ch = &channel[chc];
|
||||
int32 running_timestamp = ch->lastts;
|
||||
int32 run_time = timestamp - ch->lastts;
|
||||
|
||||
ch->lastts = timestamp;
|
||||
|
||||
if(!run_time)
|
||||
return;
|
||||
|
||||
(this->*ch->UpdateOutput)(running_timestamp, ch);
|
||||
|
||||
if(chc >= 4)
|
||||
{
|
||||
int32 freq = ch->noise_freq_cache;
|
||||
|
||||
ch->noisecount -= run_time;
|
||||
|
||||
#define CLOCK_LFSR(lfsr) { unsigned int newbit = ((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 11) ^ (lfsr >> 12) ^ (lfsr >> 17)) & 1; lfsr = (lfsr >> 1) | (newbit << 17); }
|
||||
if(&PCEFast_PSG::UpdateOutput_Noise == ch->UpdateOutput)
|
||||
{
|
||||
while(ch->noisecount <= 0)
|
||||
{
|
||||
CLOCK_LFSR(ch->lfsr);
|
||||
UpdateOutput_Noise(timestamp + ch->noisecount, ch);
|
||||
ch->noisecount += freq;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(ch->noisecount <= 0)
|
||||
{
|
||||
CLOCK_LFSR(ch->lfsr);
|
||||
ch->noisecount += freq;
|
||||
}
|
||||
}
|
||||
#undef CLOCK_LFSR
|
||||
}
|
||||
|
||||
// D7 of control is 0, don't clock the counter at all.
|
||||
// D7 of lfocontrol is 1(and chc == 1), don't clock the counter at all(not sure about this)
|
||||
// In DDA mode, don't clock the counter.
|
||||
// (Noise being enabled isn't handled here since AFAIK it doesn't disable clocking of the waveform portion, its sound just overrides the sound from
|
||||
// the waveform portion when the noise enable bit is set, which is handled in our RecalcUOFunc).
|
||||
if(!(ch->control & 0x80) || (chc == 1 && (lfoctrl & 0x80)) || (ch->control & 0x40))
|
||||
return;
|
||||
|
||||
ch->counter -= run_time;
|
||||
|
||||
if(!LFO_On && ch->freq_cache <= 0xA)
|
||||
{
|
||||
if(ch->counter <= 0)
|
||||
{
|
||||
const int32 inc_count = ((0 - ch->counter) / ch->freq_cache) + 1;
|
||||
|
||||
ch->counter += inc_count * ch->freq_cache;
|
||||
|
||||
ch->waveform_index = (ch->waveform_index + inc_count) & 0x1F;
|
||||
ch->dda = ch->waveform[ch->waveform_index];
|
||||
}
|
||||
}
|
||||
|
||||
while(ch->counter <= 0)
|
||||
{
|
||||
ch->waveform_index = (ch->waveform_index + 1) & 0x1F;
|
||||
ch->dda = ch->waveform[ch->waveform_index];
|
||||
|
||||
(this->*ch->UpdateOutput)(timestamp + ch->counter, ch);
|
||||
|
||||
if(LFO_On)
|
||||
{
|
||||
RunChannel<false>(1, timestamp + ch->counter);
|
||||
RecalcFreqCache(0);
|
||||
RecalcUOFunc(0);
|
||||
|
||||
ch->counter += (ch->freq_cache <= 0xA) ? 0xA : ch->freq_cache; // Not particularly accurate, but faster.
|
||||
}
|
||||
else
|
||||
ch->counter += ch->freq_cache;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void PCEFast_PSG::UpdateSubLFO(int32 timestamp)
|
||||
{
|
||||
RunChannel<true>(0, timestamp);
|
||||
|
||||
for(int chc = 1; chc < 6; chc++)
|
||||
RunChannel<false>(chc, timestamp);
|
||||
}
|
||||
|
||||
INLINE void PCEFast_PSG::UpdateSubNonLFO(int32 timestamp)
|
||||
{
|
||||
for(int chc = 0; chc < 6; chc++)
|
||||
RunChannel<false>(chc, timestamp);
|
||||
}
|
||||
|
||||
//static int32 last_read;
|
||||
//static int32 last_apply;
|
||||
|
||||
void PCEFast_PSG::Update(int32 timestamp)
|
||||
{
|
||||
int32 run_time = timestamp - lastts;
|
||||
|
||||
if(vol_pending && !vol_update_counter && !vol_update_which)
|
||||
{
|
||||
vol_update_counter = 1;
|
||||
vol_pending = false;
|
||||
}
|
||||
|
||||
bool lfo_on = (bool)(lfoctrl & 0x03);
|
||||
|
||||
if(lfo_on)
|
||||
{
|
||||
if(!(channel[1].control & 0x80) || (lfoctrl & 0x80))
|
||||
{
|
||||
lfo_on = 0;
|
||||
RecalcFreqCache(0);
|
||||
RecalcUOFunc(0);
|
||||
}
|
||||
}
|
||||
|
||||
int32 clocks = run_time;
|
||||
int32 running_timestamp = lastts;
|
||||
|
||||
while(clocks > 0)
|
||||
{
|
||||
int32 chunk_clocks = clocks;
|
||||
|
||||
if(vol_update_counter > 0 && chunk_clocks > vol_update_counter)
|
||||
chunk_clocks = vol_update_counter;
|
||||
|
||||
running_timestamp += chunk_clocks;
|
||||
clocks -= chunk_clocks;
|
||||
|
||||
if(lfo_on)
|
||||
UpdateSubLFO(running_timestamp);
|
||||
else
|
||||
UpdateSubNonLFO(running_timestamp);
|
||||
|
||||
if(vol_update_counter > 0)
|
||||
{
|
||||
vol_update_counter -= chunk_clocks;
|
||||
if(!vol_update_counter)
|
||||
{
|
||||
const int phase = vol_update_which & 1;
|
||||
const int lr = ((vol_update_which >> 1) & 1) ^ 1;
|
||||
const int chnum = vol_update_which >> 2;
|
||||
|
||||
if(!phase)
|
||||
{
|
||||
//printf("Volume update(Read, %d since last): ch=%d, lr=%d, ts=%d\n", running_timestamp - last_read, chnum, lr, running_timestamp);
|
||||
|
||||
if(chnum < 6)
|
||||
{
|
||||
vol_update_vllatch = GetVL(chnum, lr);
|
||||
}
|
||||
//last_read = running_timestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// printf("Volume update(Apply): ch=%d, lr=%d, ts=%d\n", chnum, lr, running_timestamp);
|
||||
if(chnum < 6)
|
||||
{
|
||||
channel[chnum].vl[lr] = vol_update_vllatch;
|
||||
}
|
||||
//last_apply = running_timestamp;
|
||||
}
|
||||
vol_update_which = (vol_update_which + 1) & 0x1F;
|
||||
|
||||
if(vol_update_which)
|
||||
vol_update_counter = phase ? 1 : 255;
|
||||
else if(vol_pending)
|
||||
{
|
||||
vol_update_counter = phase ? 1 : 255;
|
||||
vol_pending = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastts = running_timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
void PCEFast_PSG::EndFrame(int32 timestamp)
|
||||
{
|
||||
Update(timestamp);
|
||||
lastts = 0;
|
||||
for(int chc = 0; chc < 6; chc++)
|
||||
channel[chc].lastts = 0;
|
||||
}
|
||||
|
||||
void PCEFast_PSG::Power(const int32 timestamp)
|
||||
{
|
||||
// Not sure about power-on values, these are mostly just intuitive guesses(with some laziness thrown in).
|
||||
if(timestamp != lastts)
|
||||
Update(timestamp);
|
||||
|
||||
memset(&channel, 0, sizeof(channel));
|
||||
|
||||
select = 0;
|
||||
globalbalance = 0;
|
||||
lfofreq = 0;
|
||||
lfoctrl = 0;
|
||||
|
||||
for(int ch = 0; ch < 6; ch++)
|
||||
{
|
||||
channel[ch].frequency = 0;
|
||||
channel[ch].control = 0x00;
|
||||
channel[ch].balance = 0;
|
||||
memset(channel[ch].waveform, 0, 32);
|
||||
channel[ch].samp_accum = 0;
|
||||
|
||||
channel[ch].waveform_index = 0;
|
||||
channel[ch].dda = 0x00;
|
||||
channel[ch].noisectrl = 0x00;
|
||||
|
||||
channel[ch].vl[0] = 0x1F;
|
||||
channel[ch].vl[1] = 0x1F;
|
||||
|
||||
channel[ch].samp_accum = 0;
|
||||
|
||||
RecalcFreqCache(ch);
|
||||
RecalcUOFunc(ch);
|
||||
|
||||
channel[ch].counter = channel[ch].freq_cache;
|
||||
|
||||
if(ch >= 4)
|
||||
{
|
||||
RecalcNoiseFreqCache(ch);
|
||||
channel[ch].noisecount = 1;
|
||||
channel[ch].lfsr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
vol_pending = false;
|
||||
vol_update_counter = 0;
|
||||
vol_update_which = 0;
|
||||
}
|
||||
|
||||
int PCEFast_PSG::StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
for(int ch = 0; ch < 6; ch++)
|
||||
{
|
||||
char tmpstr[5] = "SCHx";
|
||||
psg_channel *pt = &channel[ch];
|
||||
|
||||
SFORMAT CH_StateRegs[] =
|
||||
{
|
||||
SFVARN(pt->counter, "counter"),
|
||||
SFVARN(pt->frequency, "frequency"),
|
||||
SFVARN(pt->control, "control"),
|
||||
SFVARN(pt->balance, "balance"),
|
||||
SFARRAYN(pt->waveform, 32, "waveform"),
|
||||
SFVARN(pt->waveform_index, "waveform_index"),
|
||||
SFVARN(pt->dda, "dda"),
|
||||
SFVARN(pt->noisectrl, "noisectrl"),
|
||||
SFVARN(pt->noisecount, "noisecount"),
|
||||
SFVARN(pt->lfsr, "lfsr"),
|
||||
SFARRAY32N(pt->vl, 2, "vl"), // TODO
|
||||
SFEND
|
||||
};
|
||||
tmpstr[3] = '0' + ch;
|
||||
ret &= MDFNSS_StateAction(sm, load, data_only, CH_StateRegs, tmpstr, false);
|
||||
}
|
||||
|
||||
SFORMAT PSG_StateRegs[] =
|
||||
{
|
||||
SFVAR(select),
|
||||
SFVAR(globalbalance),
|
||||
SFVAR(lfofreq),
|
||||
SFVAR(lfoctrl),
|
||||
|
||||
SFVAR(vol_update_counter),
|
||||
SFVAR(vol_update_which),
|
||||
SFVAR(vol_pending),
|
||||
SFEND
|
||||
};
|
||||
|
||||
ret &= MDFNSS_StateAction(sm, load, data_only, PSG_StateRegs, "PSG", false);
|
||||
|
||||
if(load)
|
||||
{
|
||||
vol_update_which &= 0x1F;
|
||||
|
||||
if(!channel[4].lfsr)
|
||||
channel[4].lfsr = 1;
|
||||
|
||||
if(!channel[5].lfsr)
|
||||
channel[5].lfsr = 1;
|
||||
|
||||
for(int ch = 0; ch < 6; ch++)
|
||||
{
|
||||
channel[ch].samp_accum = 0;
|
||||
for(int wi = 0; wi < 32; wi++)
|
||||
{
|
||||
channel[ch].waveform[wi] &= 0x1F;
|
||||
channel[ch].samp_accum += channel[ch].waveform[wi];
|
||||
}
|
||||
|
||||
for(int lr = 0; lr < 2; lr++)
|
||||
channel[ch].vl[lr] &= 0x1F;
|
||||
|
||||
if(!channel[ch].noisecount && ch >= 4)
|
||||
{
|
||||
printf("ch=%d, noisecount == 0\n", ch);
|
||||
channel[ch].noisecount = 1;
|
||||
}
|
||||
|
||||
if(channel[ch].counter <= 0)
|
||||
{
|
||||
printf("ch=%d, counter <= 0\n", ch);
|
||||
channel[ch].counter = 1;
|
||||
}
|
||||
|
||||
if(ch >= 4)
|
||||
RecalcNoiseFreqCache(ch);
|
||||
RecalcFreqCache(ch);
|
||||
RecalcUOFunc(ch);
|
||||
}
|
||||
}
|
||||
return(ret);
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
#ifndef _PCEFast_PSG_H
|
||||
#define _PCEFast_PSG_H
|
||||
|
||||
#include "../include/blip/Blip_Buffer.h"
|
||||
|
||||
class PCEFast_PSG;
|
||||
|
||||
struct psg_channel
|
||||
{
|
||||
uint8 waveform[32]; /* Waveform data */
|
||||
uint8 waveform_index; /* Waveform data index */
|
||||
uint8 dda;
|
||||
uint8 control; /* Channel enable, DDA, volume */
|
||||
uint8 noisectrl; /* Noise enable/ctrl (channels 4,5 only) */
|
||||
|
||||
int32 vl[2]; //vll, vlr;
|
||||
|
||||
int32 counter;
|
||||
|
||||
void (PCEFast_PSG::*UpdateOutput)(const int32 timestamp, psg_channel *ch);
|
||||
|
||||
uint32 freq_cache;
|
||||
uint32 noise_freq_cache; // Channel 4,5 only
|
||||
int32 noisecount;
|
||||
uint32 lfsr;
|
||||
|
||||
int32 samp_accum; // The result of adding up all the samples in the waveform buffer(part of an optimization for high-frequency playback).
|
||||
int32 blip_prev_samp[2];
|
||||
int32 lastts;
|
||||
|
||||
uint16 frequency; /* Channel frequency */
|
||||
uint8 balance; /* Channel balance */
|
||||
};
|
||||
|
||||
class PCEFast_PSG
|
||||
{
|
||||
public:
|
||||
|
||||
PCEFast_PSG(Blip_Buffer *bb_l, Blip_Buffer *bb_r) MDFN_COLD;
|
||||
~PCEFast_PSG() MDFN_COLD;
|
||||
|
||||
int StateAction(StateMem *sm, int load, int data_only) MDFN_COLD;
|
||||
|
||||
void Power(const int32 timestamp) MDFN_COLD;
|
||||
void Write(int32 timestamp, uint8 A, uint8 V);
|
||||
|
||||
void SetVolume(double new_volume) MDFN_COLD;
|
||||
|
||||
void EndFrame(int32 timestamp);
|
||||
|
||||
private:
|
||||
|
||||
void Update(int32 timestamp);
|
||||
|
||||
void UpdateSubLFO(int32 timestamp);
|
||||
void UpdateSubNonLFO(int32 timestamp);
|
||||
|
||||
void RecalcUOFunc(int chnum);
|
||||
void UpdateOutput_Off(const int32 timestamp, psg_channel *ch);
|
||||
void UpdateOutput_Accum(const int32 timestamp, psg_channel *ch);
|
||||
void UpdateOutput_Norm(const int32 timestamp, psg_channel *ch);
|
||||
void UpdateOutput_Noise(const int32 timestamp, psg_channel *ch);
|
||||
|
||||
int32 GetVL(const int chnum, const int lr);
|
||||
|
||||
void RecalcFreqCache(int chnum);
|
||||
void RecalcNoiseFreqCache(int chnum);
|
||||
template<bool LFO_On>
|
||||
void RunChannel(int chc, int32 timestamp);
|
||||
double OutputVolume;
|
||||
|
||||
uint8 select; /* Selected channel (0-5) */
|
||||
uint8 globalbalance; /* Global sound balance */
|
||||
uint8 lfofreq; /* LFO frequency */
|
||||
uint8 lfoctrl; /* LFO control */
|
||||
|
||||
int32 vol_update_counter;
|
||||
int32 vol_update_which;
|
||||
int32 vol_update_vllatch;
|
||||
bool vol_pending;
|
||||
|
||||
psg_channel channel[6];
|
||||
|
||||
int32 lastts;
|
||||
|
||||
Blip_Buffer *sbuf[2];
|
||||
Blip_Synth Synth;
|
||||
|
||||
int32 dbtable_volonly[32];
|
||||
|
||||
int32 dbtable[32][32];
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,177 +0,0 @@
|
||||
#ifndef _PCE_VDC_H
|
||||
#define _PCE_VDC_H
|
||||
|
||||
#define REGSETP(_reg, _data, _msb) { _reg &= 0xFF << ((_msb) ? 0 : 8); _reg |= (_data) << ((_msb) ? 8 : 0); }
|
||||
#define REGGETP(_reg, _msb) ((_reg >> ((_msb) ? 8 : 0)) & 0xFF)
|
||||
|
||||
#define VDC_DEBUG(x)
|
||||
//printf("%s: %d\n", x, vdc->display_counter);
|
||||
#define VDC_UNDEFINED(x) { }
|
||||
//{ printf("%s: %d\n", x, vdc->display_counter); }
|
||||
|
||||
static const uint8 vram_inc_tab[4] = { 1, 32, 64, 128 };
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8 priority[2];
|
||||
uint16 winwidths[2];
|
||||
uint8 st_mode;
|
||||
} vpc_t;
|
||||
|
||||
extern vpc_t vpc;
|
||||
|
||||
static const int VRAM_Size = 0x8000;
|
||||
static const int VRAM_SizeMask = VRAM_Size - 1; //0x7FFF;
|
||||
static const int VRAM_BGTileNoMask = VRAM_SizeMask / 16; //0x7FF;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8 CR;
|
||||
|
||||
bool lc263; // 263 line count if set, 262 if not
|
||||
bool bw; // Black and White
|
||||
uint8 dot_clock; // Dot Clock(5, 7, or 10 MHz = 0, 1, 2)
|
||||
uint16 color_table[0x200];
|
||||
uint16 color_table_cache[0x200];
|
||||
uint16 ctaddress;
|
||||
} vce_t;
|
||||
|
||||
extern vce_t vce;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int16 y;
|
||||
uint16 height;
|
||||
uint16 x;
|
||||
uint16 no;
|
||||
uint16 flags;
|
||||
bool cgmode;
|
||||
} SAT_Cache_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32 display_counter;
|
||||
|
||||
int32 sat_dma_slcounter;
|
||||
|
||||
uint8 select;
|
||||
uint16 MAWR; // Memory Address Write Register
|
||||
uint16 MARR; // Memory Address Read Register
|
||||
|
||||
uint16 CR; // Control Register
|
||||
uint16 RCR; // Raster Compare Register
|
||||
uint16 BXR; // Background X-Scroll Register
|
||||
uint16 BYR; // Background Y-Scroll Register
|
||||
uint16 MWR; // Memory Width Register
|
||||
|
||||
uint16 HSR; // Horizontal Sync Register
|
||||
uint16 HDR; // Horizontal Display Register
|
||||
uint16 VSR;
|
||||
uint16 VDR;
|
||||
|
||||
uint16 VCR;
|
||||
uint16 DCR;
|
||||
uint16 SOUR;
|
||||
uint16 DESR;
|
||||
uint16 LENR;
|
||||
uint16 SATB;
|
||||
|
||||
uint32 RCRCount;
|
||||
|
||||
uint16 read_buffer;
|
||||
uint8 write_latch;
|
||||
uint8 status;
|
||||
|
||||
uint16 DMAReadBuffer;
|
||||
bool DMAReadWrite;
|
||||
bool DMARunning;
|
||||
bool SATBPending;
|
||||
bool burst_mode;
|
||||
|
||||
uint32 BG_YOffset;
|
||||
uint32 BG_XOffset;
|
||||
|
||||
|
||||
|
||||
int SAT_Cache_Valid; // 64 through 128, depending on the number of 32-pixel-wide sprites.
|
||||
SAT_Cache_t SAT_Cache[128]; //64];
|
||||
|
||||
uint16 SAT[0x100];
|
||||
|
||||
uint16 VRAM[65536]; //VRAM_Size];
|
||||
uint64 bg_tile_cache[65536][8]; // Tile, y, x
|
||||
uint8 spr_tile_cache[1024][16][16]; // Tile, y, x
|
||||
uint8 spr_tile_clean[1024]; //VRAM_Size / 64];
|
||||
} vdc_t;
|
||||
|
||||
extern vdc_t *vdc;
|
||||
|
||||
|
||||
void VDC_SetPixelFormat(void);
|
||||
void VDC_RunFrame(EmulateSpecStruct *espec, bool IsHES);
|
||||
void VDC_SetLayerEnableMask(uint64 mask);
|
||||
|
||||
DECLFW(VDC_Write);
|
||||
DECLFW(VDC_Write_ST);
|
||||
|
||||
DECLFR(VCE_Read);
|
||||
|
||||
static INLINE uint8 VDC_Read(unsigned int A, bool SGX)
|
||||
{
|
||||
uint8 ret = 0;
|
||||
int msb = A & 1;
|
||||
int chip = 0;
|
||||
|
||||
{
|
||||
A &= 0x3;
|
||||
}
|
||||
|
||||
switch(A)
|
||||
{
|
||||
case 0x0: ret = vdc->status;
|
||||
|
||||
vdc->status &= ~0x3F;
|
||||
|
||||
HuC6280_IRQEnd(MDFN_IQIRQ1); // Clear VDC IRQ line
|
||||
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
ret = REGGETP(vdc->read_buffer, msb);
|
||||
if(vdc->select == 0x2) // VRR - VRAM Read Register
|
||||
{
|
||||
if(msb)
|
||||
{
|
||||
vdc->MARR += vram_inc_tab[(vdc->CR >> 11) & 0x3];
|
||||
|
||||
if(vdc->MARR >= VRAM_Size)
|
||||
VDC_UNDEFINED("Unmapped VRAM VRR read");
|
||||
vdc->read_buffer = vdc->VRAM[vdc->MARR & VRAM_SizeMask];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//if(HuCPU.isopread && (A == 1 || A == 3)) //(A == 2 || A == 3)) // && A == 1)
|
||||
if(A == 1)
|
||||
{
|
||||
//if(vdc->display_counter >= (VDS + VSW) && vdc->display_counter < (VDS + VSW + VDW + 1) && vce.dot_clock > 0)
|
||||
if(vce.dot_clock > 0)
|
||||
ret = 0x40;
|
||||
//printf("%d %d %02x\n", vdc->display_counter, vce.dot_clock, ret);
|
||||
//ret = 0x40;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
DECLFW(VCE_Write);
|
||||
|
||||
void VDC_Init(int sgx) MDFN_COLD;
|
||||
void VDC_Close(void) MDFN_COLD;
|
||||
void VDC_Reset(void) MDFN_COLD;
|
||||
void VDC_Power(void) MDFN_COLD;
|
||||
|
||||
int VDC_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
#endif
|
@ -20,37 +20,42 @@
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include "settings.h"
|
||||
#include <mednafen/hw_sound/pce_psg/pce_psg.h>
|
||||
|
||||
int setting_initial_scanline = 0;
|
||||
int setting_last_scanline = 242;
|
||||
int setting_pce_initial_scanline = 0;
|
||||
int setting_pce_last_scanline = 242;
|
||||
int setting_pce_hoverscan = 352;
|
||||
int setting_pce_fast_nospritelimit = 0;
|
||||
int setting_pce_nospritelimit = 0;
|
||||
int setting_pce_overclocked = 1;
|
||||
int setting_pce_fast_cddavolume = 100;
|
||||
int setting_pce_fast_adpcmvolume = 100;
|
||||
int setting_pce_fast_cdpsgvolume = 100;
|
||||
uint32_t setting_pce_fast_cdspeed = 1;
|
||||
std::string setting_pce_fast_cdbios = "syscard3.pce";
|
||||
int setting_pce_cddavolume = 100;
|
||||
int setting_pce_adpcmvolume = 100;
|
||||
int setting_pce_cdpsgvolume = 100;
|
||||
uint32_t setting_pce_cdspeed = 1;
|
||||
std::string setting_pce_cdbios = "syscard3.pce";
|
||||
bool OrderOfGriffonFix = false;
|
||||
|
||||
uint64 MDFN_GetSettingUI(const char *name)
|
||||
{
|
||||
if (!strcmp("pce_fast.cddavolume", name))
|
||||
return setting_pce_fast_cddavolume;
|
||||
if (!strcmp("pce_fast.adpcmvolume", name))
|
||||
return setting_pce_fast_adpcmvolume;
|
||||
if (!strcmp("pce_fast.cdpsgvolume", name))
|
||||
return setting_pce_fast_cdpsgvolume;
|
||||
if (!strcmp("pce_fast.cdspeed", name))
|
||||
return setting_pce_fast_cdspeed;
|
||||
if (!strcmp("pce_fast.ocmultiplier", name))
|
||||
if (!strcmp("pce.cddavolume", name))
|
||||
return setting_pce_cddavolume;
|
||||
if (!strcmp("pce.adpcmvolume", name))
|
||||
return setting_pce_adpcmvolume;
|
||||
if (!strcmp("pce.cdpsgvolume", name))
|
||||
return setting_pce_cdpsgvolume;
|
||||
if (!strcmp("pce.cdspeed", name))
|
||||
return setting_pce_cdspeed;
|
||||
if (!strcmp("pce.ocmultiplier", name))
|
||||
return setting_pce_overclocked;
|
||||
if (!strcmp("pce_fast.slstart", name))
|
||||
return setting_initial_scanline;
|
||||
if (!strcmp("pce_fast.slend", name))
|
||||
return setting_last_scanline;
|
||||
if (!strcmp("pce_fast.hoverscan", name))
|
||||
if (!strcmp("pce.slstart", name))
|
||||
return setting_pce_initial_scanline;
|
||||
if (!strcmp("pce.slend", name))
|
||||
return setting_pce_last_scanline;
|
||||
if (!strcmp("pce.hoverscan", name))
|
||||
return setting_pce_hoverscan;
|
||||
if (!strcmp("pce.resamp_quality", name))
|
||||
return 3;
|
||||
if (!strcmp("pce.vramsize", name))
|
||||
return 32768;
|
||||
|
||||
fprintf(stderr, "unhandled setting UI: %s\n", name);
|
||||
return 0;
|
||||
@ -58,12 +63,20 @@ uint64 MDFN_GetSettingUI(const char *name)
|
||||
|
||||
int64 MDFN_GetSettingI(const char *name)
|
||||
{
|
||||
if (!strcmp("pce.psgrevision", name))
|
||||
return PCE_PSG::_REVISION_COUNT;
|
||||
|
||||
fprintf(stderr, "unhandled setting I: %s\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
double MDFN_GetSettingF(const char *name)
|
||||
{
|
||||
if (!strcmp("pce.resamp_rate_error", name))
|
||||
return 0.0000009;
|
||||
if (!strcmp("pce.mouse_sensitivity", name))
|
||||
return 1.0;
|
||||
|
||||
fprintf(stderr, "unhandled setting F: %s\n", name);
|
||||
return 0;
|
||||
}
|
||||
@ -75,17 +88,27 @@ bool MDFN_GetSettingB(const char *name)
|
||||
/* LIBRETRO */
|
||||
if (!strcmp("libretro.cd_load_into_ram", name))
|
||||
return 0;
|
||||
if (!strcmp("pce_fast.input.multitap", name))
|
||||
if (!strcmp("pce.input.multitap", name))
|
||||
return 1;
|
||||
if (!strcmp("pce_fast.arcadecard", name))
|
||||
if (!strcmp("pce.arcadecard", name))
|
||||
return 1;
|
||||
if (!strcmp("pce_fast.nospritelimit", name))
|
||||
return setting_pce_fast_nospritelimit;
|
||||
if (!strcmp("pce_fast.forcemono", name))
|
||||
if (!strcmp("pce.nospritelimit", name))
|
||||
return setting_pce_nospritelimit;
|
||||
if (!strcmp("pce.forcemono", name))
|
||||
return 0;
|
||||
if (!strcmp("pce_fast.disable_softreset", name))
|
||||
if (!strcmp("pce.disable_softreset", name))
|
||||
return 0;
|
||||
if (!strcmp("pce_fast.adpcmlp", name))
|
||||
if (!strcmp("pce.adpcmlp", name))
|
||||
return 0;
|
||||
if (!strcmp("pce.forcesgx", name))
|
||||
return 0;
|
||||
if (!strcmp("pce.h_overscan", name))
|
||||
return 0;
|
||||
if (!strcmp("pce.disable_bram_hucard", name))
|
||||
return 0;
|
||||
if (!strcmp("pce.disable_bram_cd", name))
|
||||
return 0;
|
||||
if (!strcmp("pce.adpcmextraprec", name))
|
||||
return 0;
|
||||
/* CDROM */
|
||||
if (!strcmp("cdrom.lec_eval", name))
|
||||
@ -103,8 +126,8 @@ extern std::string retro_base_directory;
|
||||
|
||||
std::string MDFN_GetSettingS(const char *name)
|
||||
{
|
||||
if (!strcmp("pce_fast.cdbios", name))
|
||||
return setting_pce_fast_cdbios;
|
||||
if (!strcmp("pce.cdbios", name))
|
||||
return setting_pce_cdbios;
|
||||
/* FILESYS */
|
||||
if (!strcmp("filesys.path_firmware", name))
|
||||
return retro_base_directory;
|
||||
|
@ -3,16 +3,16 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
extern int setting_initial_scanline;
|
||||
extern int setting_last_scanline;
|
||||
extern int setting_pce_fast_nospritelimit;
|
||||
extern int setting_pce_initial_scanline;
|
||||
extern int setting_pce_last_scanline;
|
||||
extern int setting_pce_nospritelimit;
|
||||
extern int setting_pce_overclocked;
|
||||
extern int setting_pce_hoverscan;
|
||||
extern int setting_pce_fast_cddavolume;
|
||||
extern int setting_pce_fast_adpcmvolume;
|
||||
extern int setting_pce_fast_cdpsgvolume;
|
||||
extern uint32_t setting_pce_fast_cdspeed;
|
||||
extern std::string setting_pce_fast_cdbios;
|
||||
extern int setting_pce_cddavolume;
|
||||
extern int setting_pce_adpcmvolume;
|
||||
extern int setting_pce_cdpsgvolume;
|
||||
extern uint32_t setting_pce_cdspeed;
|
||||
extern std::string setting_pce_cdbios;
|
||||
extern bool OrderOfGriffonFix;
|
||||
|
||||
// This should assert() or something if the setting isn't found, since it would
|
||||
|
864
mednafen/sound/OwlResampler.cpp
Normal file
864
mednafen/sound/OwlResampler.cpp
Normal file
@ -0,0 +1,864 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// Don't pass more than about 40ms worth of audio data to Resample()
|
||||
// at a time.
|
||||
|
||||
#include <mednafen/mednafen.h>
|
||||
#include <mednafen/state.h>
|
||||
#include "OwlResampler.h"
|
||||
#include "../cputest/cputest.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_POWERPC_ALTIVEC) && defined(HAVE_ALTIVEC_H)
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
|
||||
#ifdef __ARM_NEON__
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FAST_MATH__
|
||||
#error "OwlResampler.cpp not compatible with unsafe math optimizations!"
|
||||
#endif
|
||||
|
||||
#define MIN(a,x,y) \
|
||||
((a)(x) < (a)(y) ? (a)(x) : (a)(y))
|
||||
|
||||
OwlBuffer::OwlBuffer()
|
||||
{
|
||||
assert(sizeof(I32_F_Pudding) == 4);
|
||||
assert(sizeof(float) == 4);
|
||||
|
||||
memset(HRBuf, 0, sizeof(HRBuf));
|
||||
|
||||
accum = 0;
|
||||
filter_state[0] = 0;
|
||||
filter_state[1] = 0;
|
||||
|
||||
leftover = 0;
|
||||
|
||||
InputIndex = 0;
|
||||
InputPhase = 0;
|
||||
|
||||
debias = 0;
|
||||
}
|
||||
|
||||
|
||||
OwlBuffer::~OwlBuffer()
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void OwlBuffer::StateAction(StateMem* sm, const unsigned load, const bool data_only, const char* sname_prefix, const unsigned scount)
|
||||
{
|
||||
unsigned InBuf = scount;
|
||||
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(accum),
|
||||
SFVAR(leftover),
|
||||
|
||||
SFARRAY64(filter_state, 2),
|
||||
|
||||
SFVAR(InputIndex),
|
||||
|
||||
SFVAR(InputPhase),
|
||||
|
||||
SFVAR(debias),
|
||||
|
||||
SFVAR(InBuf),
|
||||
|
||||
SFEND
|
||||
};
|
||||
|
||||
MDFNSS_StateAction(sm, load, data_only, StateRegs, sname_prefix, false);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if(leftover < 0)
|
||||
leftover = 0;
|
||||
|
||||
if(leftover > HRBUF_LEFTOVER_PADDING)
|
||||
leftover = HRBUF_LEFTOVER_PADDING;
|
||||
|
||||
if(InBuf > 65536)
|
||||
InBuf = 65536;
|
||||
}
|
||||
|
||||
char lod_sname[256];
|
||||
snprintf(lod_sname, sizeof(lod_sname), "%s_LOD", sname_prefix);
|
||||
|
||||
SFORMAT StateRegs_LOD[] =
|
||||
{
|
||||
SFARRAY32(Buf() - leftover, leftover + InBuf + HRBUF_OVERFLOW_PADDING),
|
||||
SFEND
|
||||
};
|
||||
|
||||
MDFNSS_StateAction(sm, load, data_only, StateRegs_LOD, lod_sname, false);
|
||||
}
|
||||
|
||||
template<unsigned DoExMix, bool Integrate, unsigned IntegrateShift, bool Lowpass, bool Highpass, bool FloatOutput>
|
||||
static int32 ProcessLoop(unsigned count, int32 a, int32* b, int32* exmix0 = NULL, int32* exmix1 = NULL, unsigned lp_shift = 0, unsigned hp_shift = 0, int64* f_in = NULL)
|
||||
{
|
||||
int64 lp_f;
|
||||
int64 hp_f;
|
||||
|
||||
if(Lowpass)
|
||||
{
|
||||
lp_f = f_in[0];
|
||||
}
|
||||
|
||||
if(Highpass)
|
||||
{
|
||||
hp_f = f_in[1];
|
||||
}
|
||||
|
||||
while(count--)
|
||||
{
|
||||
int32 tmp;
|
||||
|
||||
if(Integrate)
|
||||
{
|
||||
a += *b;
|
||||
tmp = a >> IntegrateShift;
|
||||
}
|
||||
else
|
||||
tmp = *b;
|
||||
|
||||
if(Lowpass)
|
||||
{
|
||||
lp_f += ((int64)((uint64)(int64)tmp << 16) - lp_f) >> lp_shift;
|
||||
tmp = lp_f >> 16;
|
||||
}
|
||||
|
||||
if(Highpass)
|
||||
{
|
||||
hp_f += ((int64)((uint64)(int64)tmp << 16) - hp_f) >> hp_shift;
|
||||
tmp = tmp - (hp_f >> 16);
|
||||
}
|
||||
|
||||
if(DoExMix >= 1)
|
||||
{
|
||||
tmp += *exmix0;
|
||||
exmix0++;
|
||||
}
|
||||
|
||||
if(DoExMix >= 2)
|
||||
{
|
||||
tmp += *exmix1;
|
||||
exmix1++;
|
||||
}
|
||||
|
||||
if(FloatOutput)
|
||||
*(float*)b = tmp;
|
||||
else
|
||||
*b = tmp;
|
||||
|
||||
b++;
|
||||
}
|
||||
|
||||
if(Lowpass)
|
||||
f_in[0] = lp_f;
|
||||
|
||||
if(Highpass)
|
||||
f_in[1] = hp_f;
|
||||
|
||||
return(a);
|
||||
}
|
||||
|
||||
void OwlBuffer::ResampleSkipped(unsigned count)
|
||||
{
|
||||
memmove(HRBuf, &HRBuf[count], HRBUF_OVERFLOW_PADDING * sizeof(HRBuf[0]));
|
||||
memset(&HRBuf[HRBUF_OVERFLOW_PADDING], 0, count * sizeof(HRBuf[0]));
|
||||
}
|
||||
|
||||
void OwlBuffer::Integrate(unsigned count, unsigned lp_shift, unsigned hp_shift, RavenBuffer* mixin0, RavenBuffer* mixin1)
|
||||
{
|
||||
//lp_shift = hp_shift = 0;
|
||||
if(lp_shift != 0 || hp_shift != 0)
|
||||
{
|
||||
if(mixin0 && mixin1)
|
||||
accum = ProcessLoop<2, true, 3, true, true, true>(count, accum, Buf(), mixin0->Buf(), mixin1->Buf(), lp_shift, hp_shift, filter_state);
|
||||
else if(mixin0)
|
||||
accum = ProcessLoop<1, true, 3, true, true, true>(count, accum, Buf(), mixin0->Buf(), NULL, lp_shift, hp_shift, filter_state);
|
||||
else
|
||||
accum = ProcessLoop<0, true, 3, true, true, true>(count, accum, Buf(), NULL, NULL, lp_shift, hp_shift, filter_state);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mixin0 && mixin1)
|
||||
accum = ProcessLoop<2, true, 3, false, false, true>(count, accum, Buf(), mixin0->Buf(), mixin1->Buf());
|
||||
else if(mixin0)
|
||||
accum = ProcessLoop<1, true, 3, false, false, true>(count, accum, Buf(), mixin0->Buf());
|
||||
else
|
||||
accum = ProcessLoop<0, true, 3, false, false, true>(count, accum, Buf());
|
||||
}
|
||||
|
||||
if(accum >= 32767 * 256 * 8 || accum <= -32767 * 256 * 8)
|
||||
{
|
||||
//printf("Possible delta sample loss; accum=%d\n", accum);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
RavenBuffer::RavenBuffer()
|
||||
{
|
||||
memset(BB, 0, sizeof(BB));
|
||||
|
||||
accum = 0;
|
||||
|
||||
filter_state[0] = 0;
|
||||
filter_state[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
RavenBuffer::~RavenBuffer()
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RavenBuffer::Process(unsigned count, bool integrate, unsigned lp_shift)
|
||||
{
|
||||
if(integrate)
|
||||
{
|
||||
if(lp_shift != 0)
|
||||
accum = ProcessLoop<0, true, 3, true, false, false>(count, accum, Buf(), NULL, NULL, lp_shift, 0, filter_state);
|
||||
else
|
||||
accum = ProcessLoop<0, true, 3, false, false, false>(count, accum, Buf(), NULL, NULL, lp_shift, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(lp_shift != 0)
|
||||
accum = ProcessLoop<0, false, 0, true, false, false>(count, accum, Buf(), NULL, NULL, lp_shift, 0, filter_state);
|
||||
else
|
||||
accum = ProcessLoop<0, false, 0, false, false, false>(count, accum, Buf(), NULL, NULL, lp_shift, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void RavenBuffer::Finish(unsigned count)
|
||||
{
|
||||
memmove(BB, &BB[count], OwlBuffer::HRBUF_OVERFLOW_PADDING * sizeof(BB[0]));
|
||||
memset(&BB[OwlBuffer::HRBUF_OVERFLOW_PADDING], 0, count * sizeof(BB[0]));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void kaiser_window( double* io, int count, double beta )
|
||||
{
|
||||
int const accuracy = 16; //12;
|
||||
|
||||
double* end = io + count;
|
||||
|
||||
double beta2 = beta * beta * (double) -0.25;
|
||||
double to_fract = beta2 / ((double) count * count);
|
||||
double i = 0;
|
||||
double rescale = 0; // Doesn't need an initializer, to shut up gcc
|
||||
|
||||
for ( ; io < end; ++io, i += 1 )
|
||||
{
|
||||
double x = i * i * to_fract - beta2;
|
||||
double u = x;
|
||||
double k = x + 1;
|
||||
|
||||
double n = 2;
|
||||
do
|
||||
{
|
||||
u *= x / (n * n);
|
||||
n += 1;
|
||||
k += u;
|
||||
}
|
||||
while ( k <= u * (1 << accuracy) );
|
||||
|
||||
if ( !i )
|
||||
rescale = 1 / k; // otherwise values get large
|
||||
|
||||
*io *= k * rescale;
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_sinc( double* out, int size, double cutoff, double kaiser )
|
||||
{
|
||||
assert( size % 2 == 0 ); // size must be enev
|
||||
|
||||
int const half_size = size / 2;
|
||||
double* const mid = &out [half_size];
|
||||
|
||||
// Generate right half of sinc
|
||||
for ( int i = 0; i < half_size; i++ )
|
||||
{
|
||||
double angle = (i * 2 + 1) * (M_PI / 2);
|
||||
mid [i] = sin( angle * cutoff ) / angle;
|
||||
}
|
||||
|
||||
kaiser_window( mid, half_size, kaiser );
|
||||
|
||||
// Mirror for left half
|
||||
for ( int i = 0; i < half_size; i++ )
|
||||
out [i] = mid [half_size - 1 - i];
|
||||
}
|
||||
|
||||
static void normalize( double* io, int size, double gain = 1.0 )
|
||||
{
|
||||
double sum = 0;
|
||||
for ( int i = 0; i < size; i++ )
|
||||
sum += io [i];
|
||||
|
||||
double scale = gain / sum;
|
||||
for ( int i = 0; i < size; i++ )
|
||||
io [i] *= scale;
|
||||
}
|
||||
|
||||
|
||||
static INLINE void DoMAC(float *wave, float *coeffs, int32 count, int32 *accum_output)
|
||||
{
|
||||
float acc[4] = { 0, 0, 0, 0 };
|
||||
|
||||
for(int c = 0; MDFN_LIKELY(c < count); c += 4)
|
||||
{
|
||||
acc[0] += wave[c + 0] * coeffs[c + 0];
|
||||
acc[1] += wave[c + 1] * coeffs[c + 1];
|
||||
acc[2] += wave[c + 2] * coeffs[c + 2];
|
||||
acc[3] += wave[c + 3] * coeffs[c + 3];
|
||||
}
|
||||
|
||||
*accum_output = (acc[0] + acc[2]) + (acc[1] + acc[3]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef ARCH_X86
|
||||
#include "OwlResampler_x86.inc"
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_POWERPC_ALTIVEC
|
||||
#include "OwlResampler_altivec.inc"
|
||||
#endif
|
||||
|
||||
#ifdef __ARM_NEON__
|
||||
#include "OwlResampler_neon.inc"
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T, unsigned sa>
|
||||
static T SDP2(T v)
|
||||
{
|
||||
T tmp;
|
||||
|
||||
tmp = (v >> ((sizeof(T) * 8) - 1)) & (((T)1 << sa) - 1);
|
||||
|
||||
return ((v + tmp) >> sa);
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
SIMD_NONE = 0,
|
||||
|
||||
#ifdef ARCH_X86
|
||||
SIMD_SSE_16X,
|
||||
|
||||
#ifdef HAVE_INLINEASM_AVX
|
||||
SIMD_AVX_32X,
|
||||
SIMD_AVX_32X_P16,
|
||||
#else
|
||||
#warning "Compiling without AVX inline assembly."
|
||||
#endif
|
||||
#elif defined(ARCH_POWERPC_ALTIVEC)
|
||||
SIMD_ALTIVEC,
|
||||
#elif defined __ARM_NEON__
|
||||
SIMD_NEON
|
||||
#endif
|
||||
};
|
||||
|
||||
template<unsigned TA_SIMD_Type>
|
||||
NO_INLINE int32 OwlResampler::T_Resample(OwlBuffer* in, const uint32 in_count, int16* out, const uint32 max_out_count, const bool reverse)
|
||||
{
|
||||
if(reverse)
|
||||
{
|
||||
int32* a = in->Buf();
|
||||
int32* b = in->Buf() + in_count - 1;
|
||||
|
||||
while(MDFN_LIKELY(a < b))
|
||||
{
|
||||
swap_T<int32>(*a, *b);
|
||||
a++;
|
||||
b--;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
uint32 count = 0;
|
||||
int32* I32Out = &IntermediateBuffer[0];
|
||||
const uint32 in_count_WLO = in->leftover + in_count;
|
||||
const uint32 max = max_T<int64>(0, (int64)in_count_WLO - NumCoeffs);
|
||||
uint32 InputPhase = in->InputPhase;
|
||||
uint32 InputIndex = in->InputIndex;
|
||||
OwlBuffer::I32_F_Pudding* InSamps = in->BufPudding() - in->leftover;
|
||||
int32 leftover;
|
||||
|
||||
if(MDFN_UNLIKELY(InputPhase >= NumPhases))
|
||||
{
|
||||
fprintf(stderr, "[BUG] InputPhase >= NumPhases\n"); // Save states can also trigger this.
|
||||
InputPhase = 0;
|
||||
}
|
||||
|
||||
while(InputIndex < max)
|
||||
{
|
||||
float* wave = &InSamps[InputIndex].f;
|
||||
float* coeffs = &PInfos[InputPhase].Coeffs[0];
|
||||
int32 coeff_count = NumCoeffs;
|
||||
|
||||
switch(TA_SIMD_Type)
|
||||
{
|
||||
default:
|
||||
case SIMD_NONE:
|
||||
DoMAC(wave, coeffs, coeff_count, I32Out);
|
||||
break;
|
||||
|
||||
#ifdef ARCH_X86
|
||||
case SIMD_SSE_16X:
|
||||
DoMAC_SSE_16X(wave, coeffs, coeff_count, I32Out);
|
||||
break;
|
||||
#ifdef HAVE_INLINEASM_AVX
|
||||
case SIMD_AVX_32X:
|
||||
DoMAC_AVX_32X(wave, coeffs, coeff_count, I32Out);
|
||||
break;
|
||||
|
||||
case SIMD_AVX_32X_P16:
|
||||
DoMAC_AVX_32X_P16(wave, coeffs, coeff_count, I32Out);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#elif defined(ARCH_POWERPC_ALTIVEC)
|
||||
case SIMD_ALTIVEC:
|
||||
DoMAC_AltiVec(wave, coeffs, coeff_count, I32Out);
|
||||
break;
|
||||
#elif defined __ARM_NEON__
|
||||
case SIMD_NEON:
|
||||
DoMAC_NEON(wave, coeffs, coeff_count, I32Out);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
I32Out++;
|
||||
count++;
|
||||
|
||||
InputPhase = PInfos[InputPhase].Next;
|
||||
InputIndex += PInfos[InputPhase].Step;
|
||||
}
|
||||
|
||||
#if defined(ARCH_X86) && defined(HAVE_INLINEASM_AVX)
|
||||
if(TA_SIMD_Type == SIMD_AVX_32X || TA_SIMD_Type == SIMD_AVX_32X_P16)
|
||||
{
|
||||
asm volatile("vzeroupper\n\t" : : :
|
||||
#if defined(__AVX__)
|
||||
"ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7"
|
||||
#if defined(__x86_64__)
|
||||
, "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15"
|
||||
#endif
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(InputIndex > in_count_WLO)
|
||||
{
|
||||
leftover = 0;
|
||||
InputIndex -= in_count_WLO;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftover = (int32)in_count_WLO - (int32)InputIndex;
|
||||
InputIndex = 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
for(uint32 x = 0; x < count; x++)
|
||||
{
|
||||
int s = IntermediateBuffer[x] >> 8;
|
||||
|
||||
if(s < -32768 || s > 32767)
|
||||
{
|
||||
//printf("Flow: %6d\n", s);
|
||||
if(s < -32768)
|
||||
s = -32768;
|
||||
else if(s > 32767)
|
||||
s = 32767;
|
||||
}
|
||||
out[x * 2] = s;
|
||||
}
|
||||
#else
|
||||
{
|
||||
int64 debias = in->debias;
|
||||
|
||||
for(uint32 x = 0; x < count; x++)
|
||||
{
|
||||
int32 sample = IntermediateBuffer[x];
|
||||
int32 s;
|
||||
|
||||
debias += (((int64)((uint64)(int64)sample << 16) - debias) * debias_multiplier) >> 16;
|
||||
s = SDP2<int32, 8>(sample - (debias >> 16));
|
||||
if(s < -32768 || s > 32767)
|
||||
{
|
||||
//printf("Flow: %6d\n", s);
|
||||
if(s < -32768)
|
||||
s = -32768;
|
||||
else if(s > 32767)
|
||||
s = 32767;
|
||||
}
|
||||
out[x * 2] = s;
|
||||
}
|
||||
|
||||
in->debias = debias;
|
||||
}
|
||||
#endif
|
||||
memmove(in->Buf() - leftover,
|
||||
in->Buf() + in_count - leftover,
|
||||
sizeof(int32) * (leftover + OwlBuffer::HRBUF_OVERFLOW_PADDING));
|
||||
|
||||
memset(in->Buf() + OwlBuffer::HRBUF_OVERFLOW_PADDING, 0, sizeof(int32) * in_count);
|
||||
|
||||
in->leftover = leftover;
|
||||
in->InputPhase = InputPhase;
|
||||
in->InputIndex = InputIndex;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void OwlResampler::ResetBufResampState(OwlBuffer* buf)
|
||||
{
|
||||
memset(buf->HRBuf, 0, sizeof(buf->HRBuf[0]) * OwlBuffer::HRBUF_LEFTOVER_PADDING);
|
||||
buf->InputPhase = 0;
|
||||
}
|
||||
|
||||
|
||||
OwlResampler::~OwlResampler()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Flush denormals, and coefficients that could lead to denormals, to zero.
|
||||
//
|
||||
static float FilterDenormal(float v)
|
||||
{
|
||||
union
|
||||
{
|
||||
float f;
|
||||
uint32 i;
|
||||
} cat_pun;
|
||||
|
||||
cat_pun.f = v;
|
||||
|
||||
if(((cat_pun.i >> 23) & 0xFF) <= 24) // Maybe < 24 is more correct?
|
||||
{
|
||||
MDFN_printf("Small FP coefficient detected: 0x%08x --- raw_sign=%d, raw_exp=0x%02x, raw_mantissa=0x%06x\n", cat_pun.i, cat_pun.i >> 31, (cat_pun.i >> 23) & 0xFF, cat_pun.i & ((1U << 23) - 1));
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(v);
|
||||
}
|
||||
|
||||
OwlResampler::OwlResampler(double input_rate, double output_rate, double rate_error, double debias_corner, int quality, double nyq_fudge)
|
||||
{
|
||||
std::unique_ptr<double[]> FilterBuf;
|
||||
double ratio = (double)output_rate / input_rate;
|
||||
double cutoff;
|
||||
double required_bandwidth;
|
||||
double k_beta;
|
||||
double k_d;
|
||||
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
int a = SDP2<int32, 3>(i);
|
||||
int b = SDP2<int32, 3>(-i);
|
||||
int c = i / (1 << 3);
|
||||
|
||||
assert(a == -b && a == c);
|
||||
}
|
||||
|
||||
assert(sizeof(OwlBuffer::I32_F_Pudding) == 4);
|
||||
|
||||
InputRate = input_rate;
|
||||
OutputRate = output_rate;
|
||||
RateError = rate_error;
|
||||
DebiasCorner = debias_corner;
|
||||
Quality = quality;
|
||||
|
||||
IntermediateBuffer.resize(OutputRate * 4 / 50); // *4 for safety padding, / min(50,60), an approximate calculation
|
||||
|
||||
const uint32 cpuext = cputest_get_flags();
|
||||
|
||||
MDFN_printf("OwlResampler.cpp debug info:\n");
|
||||
MDFN_indent(1);
|
||||
|
||||
// Get the number of phases required, and adjust ratio.
|
||||
{
|
||||
double s_ratio = (double)input_rate / output_rate;
|
||||
double findo = 0;
|
||||
uint32 count = 0;
|
||||
uint32 findo_i;
|
||||
|
||||
do
|
||||
{
|
||||
count++;
|
||||
findo += s_ratio;
|
||||
} while( fabs(1.0 - ((floor(0.5 + findo) / count) / s_ratio)) > rate_error);
|
||||
|
||||
s_ratio = floor(0.5 + findo) / count;
|
||||
findo_i = (uint32) floor(0.5 + findo);
|
||||
ratio = 1 / s_ratio;
|
||||
NumPhases = count;
|
||||
|
||||
PInfos.resize(NumPhases);
|
||||
|
||||
uint32 last_indoo = 0;
|
||||
for(unsigned int i = 0; i < NumPhases; i++)
|
||||
{
|
||||
uint32 index_pos = i * findo_i / NumPhases;
|
||||
|
||||
PInfos[i].Next = (i + 1) % (NumPhases);
|
||||
PInfos[i].Step = index_pos - last_indoo;
|
||||
last_indoo = index_pos;
|
||||
}
|
||||
PInfos[0].Step = findo_i - last_indoo;
|
||||
|
||||
Ratio_Dividend = findo_i;
|
||||
Ratio_Divisor = NumPhases;
|
||||
|
||||
MDFN_printf("Phases: %d, Output rate: %f, %d %d\n", NumPhases, input_rate * ratio, Ratio_Dividend, Ratio_Divisor);
|
||||
|
||||
MDFN_printf("Desired maximum rate error: %.10f, Actual rate error: %.10f\n", rate_error, fabs((double)input_rate / output_rate * ratio - 1));
|
||||
}
|
||||
|
||||
static const struct
|
||||
{
|
||||
double beta;
|
||||
double d;
|
||||
double obw;
|
||||
} QualityTable[7] =
|
||||
{
|
||||
{ 5.658, 3.62, 0.65 },
|
||||
{ 6.764, 4.32, 0.70 },
|
||||
{ 7.865, 5.00, 0.75 },
|
||||
{ 8.960, 5.70, 0.80 },
|
||||
{ 10.056, 6.40, 0.85 },
|
||||
{ 10.056, 6.40, 0.90 },
|
||||
|
||||
{ 10.056, 6.40, 0.9333 }, // 1.0 - (6.40 / 96)
|
||||
};
|
||||
|
||||
assert(quality >= 0 && quality <= 6);
|
||||
|
||||
k_beta = QualityTable[quality].beta;
|
||||
k_d = QualityTable[quality].d;
|
||||
|
||||
|
||||
//
|
||||
// As far as filter frequency response design goes, we clamp the output rate parameter
|
||||
// to keep PCE CD and PC-FX CD-DA sample amplitudes from going wild since we're not resampling CD-DA totally properly.
|
||||
//
|
||||
#define OWLRESAMP_FCALC_RATE_CLAMP 128000.0 //192000.0 //96000.0 //48000.0
|
||||
|
||||
// A little SOMETHING to widen the transition band a bit to reduce computational complexity with higher output rates.
|
||||
const double something = min_T<double>(OWLRESAMP_FCALC_RATE_CLAMP, (48000.0 + min_T<double>(OWLRESAMP_FCALC_RATE_CLAMP, output_rate)) / 2 / QualityTable[quality].obw);
|
||||
|
||||
//
|
||||
// Note: Cutoff calculation is performed again(though slightly differently) down below after the SIMD check.
|
||||
//
|
||||
cutoff = QualityTable[quality].obw * (min_T<double>(something, min_T<double>(input_rate, output_rate)) / input_rate);
|
||||
|
||||
required_bandwidth = (min_T<double>(OWLRESAMP_FCALC_RATE_CLAMP, min_T<double>(input_rate, output_rate)) / input_rate) - cutoff;
|
||||
|
||||
NumCoeffs = ceil(k_d / required_bandwidth);
|
||||
|
||||
MDFN_printf("Initial number of coefficients per phase: %u\n", NumCoeffs);
|
||||
MDFN_printf("Initial nominal cutoff frequency: %f\n", InputRate * cutoff / 2);
|
||||
|
||||
//
|
||||
// Put this lower limit BEFORE the SIMD stuff, otherwise the NumCoeffs calculation will be off.
|
||||
//
|
||||
if(NumCoeffs < 16)
|
||||
NumCoeffs = 16;
|
||||
|
||||
if(0)
|
||||
{
|
||||
abort(); // The sky is falling AAAAAAAAAAAAA
|
||||
}
|
||||
#ifdef ARCH_X86
|
||||
#ifdef HAVE_INLINEASM_AVX
|
||||
else if((cpuext & CPUTEST_FLAG_AVX) && (NumCoeffs + 0xF) >= 32)
|
||||
{
|
||||
MDFN_printf("SIMD: AVX\n");
|
||||
|
||||
// AVX loop can't handle less than 32 MACs properly.
|
||||
NumCoeffs = max_T<uint32>(32, NumCoeffs);
|
||||
|
||||
// Past 32 MACs, AVX loop granularity is 16 MACs(with some ugly maaaagic~)
|
||||
NumCoeffs = (NumCoeffs + 0xF) &~ 0xF;
|
||||
|
||||
if(NumCoeffs & 0x10)
|
||||
Resample_ = &OwlResampler::T_Resample<SIMD_AVX_32X_P16>;
|
||||
else
|
||||
Resample_ = &OwlResampler::T_Resample<SIMD_AVX_32X>;
|
||||
}
|
||||
#endif
|
||||
else if(cpuext & CPUTEST_FLAG_SSE)
|
||||
{
|
||||
MDFN_printf("SIMD: SSE\n");
|
||||
|
||||
// SSE loop does 16 MACs per iteration.
|
||||
NumCoeffs = (NumCoeffs + 0xF) &~ 0xF;
|
||||
Resample_ = &OwlResampler::T_Resample<SIMD_SSE_16X>;
|
||||
}
|
||||
#endif
|
||||
#ifdef ARCH_POWERPC_ALTIVEC
|
||||
else if(1)
|
||||
{
|
||||
MDFN_printf("SIMD: AltiVec\n");
|
||||
|
||||
// AltiVec loop does 16 MACs per iteration.
|
||||
NumCoeffs = (NumCoeffs + 0xF) &~ 0xF;
|
||||
Resample_ = &OwlResampler::T_Resample<SIMD_ALTIVEC>;
|
||||
}
|
||||
#endif
|
||||
#ifdef __ARM_NEON__
|
||||
else if(1)
|
||||
{
|
||||
MDFN_printf("SIMD: NEON\n");
|
||||
|
||||
// NEON loop does 16 MACs per iteration.
|
||||
NumCoeffs = (NumCoeffs + 0xF) &~ 0xF;
|
||||
Resample_ = &OwlResampler::T_Resample<SIMD_NEON>;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// Default loop does 4 MACs per iteration.
|
||||
NumCoeffs = (NumCoeffs + 3) &~ 3;
|
||||
Resample_ = &OwlResampler::T_Resample<SIMD_NONE>;
|
||||
}
|
||||
//
|
||||
// Don't alter NumCoeffs anymore from here on.
|
||||
//
|
||||
|
||||
#if !defined(ARCH_X86) && !defined(ARCH_POWERPC_ALTIVEC) && !defined(__ARM_NEON__)
|
||||
#ifdef _MSC_VER
|
||||
#pragma message("OwlResampler is being compiled without SIMD support.")
|
||||
#else
|
||||
#warning "OwlResampler is being compiled without SIMD support."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//
|
||||
// Adjust cutoff now that NumCoeffs may have been increased.
|
||||
//
|
||||
cutoff = min_T<double>(QualityTable[quality].obw * something / input_rate, (min_T<double>(input_rate, output_rate) / input_rate - ((double)k_d / NumCoeffs)));
|
||||
|
||||
cutoff *= nyq_fudge;
|
||||
if(ceil(cutoff) > 1.0)
|
||||
cutoff = 1.0;
|
||||
|
||||
|
||||
MDFN_printf("Adjusted number of coefficients per phase: %u\n", NumCoeffs);
|
||||
MDFN_printf("Adjusted nominal cutoff frequency: %f\n", InputRate * cutoff / 2);
|
||||
|
||||
assert(NumCoeffs <= OwlBuffer::HRBUF_LEFTOVER_PADDING);
|
||||
|
||||
CoeffsBuffer.resize((256 / sizeof(float)) + NumCoeffs * NumPhases);
|
||||
|
||||
for(unsigned int i = 0; i < NumPhases; i++)
|
||||
PInfos[i].Coeffs = (float *)(((uintptr_t)&CoeffsBuffer[0] + 0xFF) &~ 0xFF) + (i * NumCoeffs);
|
||||
|
||||
MDFN_printf("Impulse response table memory usage: %zu bytes\n", CoeffsBuffer.size() * sizeof(float));
|
||||
|
||||
FilterBuf.reset(new double[NumCoeffs * NumPhases]);
|
||||
gen_sinc(&FilterBuf[0], NumCoeffs * NumPhases, cutoff / NumPhases, k_beta);
|
||||
normalize(&FilterBuf[0], NumCoeffs * NumPhases);
|
||||
|
||||
#if 0
|
||||
for(int i = 0; i < NumCoeffs * NumPhases; i++)
|
||||
fprintf(stderr, "%.20f\n", FilterBuf[i]);
|
||||
#endif
|
||||
|
||||
for(unsigned int phase = 0; phase < NumPhases; phase++)
|
||||
{
|
||||
//double sum_d = 0;
|
||||
//float sum_f4[4] = { 0, 0, 0, 0 };
|
||||
|
||||
const unsigned sp = (NumPhases - 1 - (((uint64)phase * Ratio_Dividend) % NumPhases));
|
||||
const unsigned tp = phase;
|
||||
|
||||
for(unsigned int i = 0; i < NumCoeffs; i++)
|
||||
{
|
||||
double tmpcod = FilterBuf[i * NumPhases + sp] * NumPhases; // Tasty cod.
|
||||
|
||||
PInfos[tp].Coeffs[i] = FilterDenormal(tmpcod);
|
||||
//sum_d += PInfos[tp].Coeffs[i];
|
||||
//sum_f4[i % 4] += PInfos[tp].Coeffs[i];
|
||||
}
|
||||
|
||||
#if 0
|
||||
{
|
||||
double sf4t = (sum_f4[0] + sum_f4[2]) + (sum_f4[1] + sum_f4[3]);
|
||||
double sd_div_sf4t = sum_d / sf4t;
|
||||
|
||||
MDFN_printf("Phase %4u: sum_d=%.10f, sum_f4t=%.10f, sum_d div sum_f4t=%.10f(*65536=%f, dB=%.8f)\n", sp, sum_d, (double)sf4t, sd_div_sf4t, 65536.0 * sd_div_sf4t, fabs(20 * log10(sum_d / sf4t)));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
assert(debias_corner < (output_rate / 16));
|
||||
debias_multiplier = (uint32)(((uint64)1 << 16) * debias_corner / output_rate);
|
||||
|
||||
MDFN_indent(-1);
|
||||
|
||||
//abort();
|
||||
|
||||
#if 0
|
||||
{
|
||||
static float dummy_wave[1024];
|
||||
static float dummy_coeffs[1024];
|
||||
int32 dummy_out;
|
||||
uint32 begin_time = MDFND_GetTime();
|
||||
|
||||
for(int i = 0; i < 1024 * 1024; i++)
|
||||
{
|
||||
DoMAC_AVX_32X(dummy_wave, dummy_coeffs, 1024, &dummy_out);
|
||||
//DoMAC_SSE_16X(dummy_wave, dummy_coeffs, 1024, &dummy_out);
|
||||
}
|
||||
|
||||
printf("%u\n", MDFND_GetTime() - begin_time);
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
159
mednafen/sound/OwlResampler.h
Normal file
159
mednafen/sound/OwlResampler.h
Normal file
@ -0,0 +1,159 @@
|
||||
#ifndef __MDFN_SOUND_OWLRESAMPLER_H
|
||||
#define __MDFN_SOUND_OWLRESAMPLER_H
|
||||
|
||||
class OwlResampler;
|
||||
class RavenBuffer;
|
||||
|
||||
#include <mednafen/state.h>
|
||||
|
||||
class OwlBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
enum { HRBUF_LEFTOVER_PADDING = 8192 };
|
||||
enum { HRBUF_OVERFLOW_PADDING = 32 }; // For deltas and impulse responses and whatnot that are dangling off the end(>= final timestamp) sorta.
|
||||
|
||||
union I32_F_Pudding
|
||||
{
|
||||
int32 i32;
|
||||
float f;
|
||||
};
|
||||
|
||||
OwlBuffer();
|
||||
~OwlBuffer();
|
||||
|
||||
INLINE int32* Buf(void)
|
||||
{
|
||||
return &HRBuf[HRBUF_LEFTOVER_PADDING].i32;
|
||||
}
|
||||
|
||||
INLINE I32_F_Pudding* BufPudding(void)
|
||||
{
|
||||
return &HRBuf[HRBUF_LEFTOVER_PADDING];
|
||||
}
|
||||
|
||||
void Integrate(unsigned count, unsigned lp_shift = 0, unsigned hp_shift = 0, RavenBuffer* mixin0 = NULL, RavenBuffer* mixin1 = NULL); // Convenience function.
|
||||
void ResampleSkipped(unsigned count);
|
||||
|
||||
void ZeroLeftover(void);
|
||||
|
||||
void StateAction(StateMem* sm, const unsigned load, const bool data_only, const char* sname_prefix, const unsigned scount);
|
||||
|
||||
private:
|
||||
|
||||
I32_F_Pudding HRBuf[HRBUF_LEFTOVER_PADDING + 65536 + HRBUF_OVERFLOW_PADDING];
|
||||
int32 accum;
|
||||
int64 filter_state[2];
|
||||
|
||||
//
|
||||
// Resampler state:
|
||||
//
|
||||
int32 leftover;
|
||||
|
||||
// Index into the input buffer
|
||||
uint32 InputIndex;
|
||||
|
||||
// Current input phase
|
||||
uint32 InputPhase;
|
||||
|
||||
// DC bias removal filter thingy
|
||||
int64 debias;
|
||||
|
||||
friend class OwlResampler;
|
||||
};
|
||||
|
||||
class RavenBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
RavenBuffer();
|
||||
~RavenBuffer();
|
||||
|
||||
INLINE int32* Buf(void)
|
||||
{
|
||||
return &BB[0];
|
||||
}
|
||||
|
||||
void Process(unsigned count, bool integrate = true, uint32 lp_shift = 0);
|
||||
void Finish(unsigned count);
|
||||
|
||||
friend class OwlBuffer;
|
||||
|
||||
private:
|
||||
int32 BB[65536 + OwlBuffer::HRBUF_OVERFLOW_PADDING];
|
||||
int32 accum;
|
||||
int64 filter_state[2];
|
||||
};
|
||||
|
||||
class OwlResampler
|
||||
{
|
||||
public:
|
||||
|
||||
// Resamples from input_rate to output_rate, allowing for rate_error(output_rate +/- output_rate*rate_error)
|
||||
// error in the resample ratio.
|
||||
//
|
||||
// debias_corner is the cheap high-pass DC bias removal filter coefficient. Higher values will result in more bias removal(and
|
||||
// case a high-pass filter effect), while lower values will lower this effect. It should be <= output_rate / 64, to be on the safe side(prevent
|
||||
// multiplication overflow). A value of 0 will disable its effect.
|
||||
//
|
||||
// quality is an arbitrary control of quality(0 for lowest quality, 5 for highest quality)
|
||||
//
|
||||
// nyq_fudge may be a tasty sleep drug.
|
||||
//
|
||||
OwlResampler(double input_rate, double output_rate, double rate_error, double debias_corner, int quality, double nyq_fudge = 1.0) MDFN_COLD;
|
||||
OwlResampler(const OwlResampler &resamp) MDFN_COLD;
|
||||
~OwlResampler() MDFN_COLD;
|
||||
|
||||
INLINE int32 Resample(OwlBuffer* in, const uint32 in_count, int16* out, const uint32 max_out_count, const bool reverse = false)
|
||||
{
|
||||
return (this->*OwlResampler::Resample_)(in, in_count, out, max_out_count, reverse);
|
||||
}
|
||||
void ResetBufResampState(OwlBuffer* buf);
|
||||
|
||||
// Get the InputRate / OutputRate ratio, expressed as a / b
|
||||
void GetRatio(int32 *a, int32 *b)
|
||||
{
|
||||
*a = Ratio_Dividend;
|
||||
*b = Ratio_Divisor;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Copy of the parameters passed to the constructor
|
||||
double InputRate, OutputRate, RateError, DebiasCorner;
|
||||
int Quality;
|
||||
|
||||
// Number of phases.
|
||||
uint32 NumPhases;
|
||||
uint32 NumPhases_Padded;
|
||||
|
||||
// Coefficients(in each phase, not total)
|
||||
uint32 NumCoeffs;
|
||||
|
||||
struct PhaseInfo
|
||||
{
|
||||
// One pointer for each phase
|
||||
float* Coeffs;
|
||||
|
||||
// In the FIR loop: InputPhase = PInfos[InputPhase].Next
|
||||
uint32 Next;
|
||||
|
||||
// Incrementor for InputIndex. In the FIR loop, after updating InputPhase: InputIndex += PInfos[InputPhase].Step
|
||||
uint32 Step;
|
||||
};
|
||||
|
||||
std::vector<PhaseInfo> PInfos;
|
||||
std::vector<float> CoeffsBuffer;
|
||||
std::vector<int32> IntermediateBuffer; //int32 boobuf[8192];
|
||||
|
||||
template<unsigned TA_SIMD_Type>
|
||||
int32 T_Resample(OwlBuffer* in, const uint32 in_count, int16* out, const uint32 max_out_count, const bool reverse);
|
||||
int32 (OwlResampler::*Resample_)(OwlBuffer* in, const uint32 in_count, int16* out, const uint32 max_out_count, const bool reverse);
|
||||
|
||||
uint16 debias_multiplier;
|
||||
|
||||
// for GetRatio()
|
||||
int32 Ratio_Dividend;
|
||||
int32 Ratio_Divisor;
|
||||
};
|
||||
#endif
|
99
mednafen/sound/OwlResampler_altivec.inc
Normal file
99
mednafen/sound/OwlResampler_altivec.inc
Normal file
@ -0,0 +1,99 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
static INLINE void DoMAC_AltiVec(float* wave, float* coeffs, int32 count, int32* accum_output)
|
||||
{
|
||||
register vector float acc0, acc1, acc2, acc3;
|
||||
|
||||
acc0 = (vector float)vec_splat_u8(0);
|
||||
acc1 = acc0;
|
||||
acc2 = acc0;
|
||||
acc3 = acc0;
|
||||
|
||||
|
||||
count >>= 4;
|
||||
|
||||
if(!((uint64)wave & 0xF))
|
||||
{
|
||||
register vector float w, c;
|
||||
do
|
||||
{
|
||||
w = vec_ld(0, wave);
|
||||
c = vec_ld(0, coeffs);
|
||||
acc0 = vec_madd(w, c, acc0);
|
||||
|
||||
w = vec_ld(16, wave);
|
||||
c = vec_ld(16, coeffs);
|
||||
acc1 = vec_madd(w, c, acc1);
|
||||
|
||||
w = vec_ld(32, wave);
|
||||
c = vec_ld(32, coeffs);
|
||||
acc2 = vec_madd(w, c, acc2);
|
||||
|
||||
w = vec_ld(48, wave);
|
||||
c = vec_ld(48, coeffs);
|
||||
acc3 = vec_madd(w, c, acc3);
|
||||
|
||||
coeffs += 16;
|
||||
wave += 16;
|
||||
} while(--count);
|
||||
}
|
||||
else
|
||||
{
|
||||
register vector unsigned char lperm;
|
||||
register vector float loado;
|
||||
|
||||
lperm = vec_lvsl(0, wave);
|
||||
loado = vec_ld(0, wave);
|
||||
|
||||
do
|
||||
{
|
||||
register vector float tl;
|
||||
register vector float w;
|
||||
register vector float c;
|
||||
|
||||
tl = vec_ld(15 + 0, wave);
|
||||
w = vec_perm(loado, tl, lperm);
|
||||
c = vec_ld(0, coeffs);
|
||||
loado = tl;
|
||||
acc0 = vec_madd(w, c, acc0);
|
||||
|
||||
tl = vec_ld(15 + 16, wave);
|
||||
w = vec_perm(loado, tl, lperm);
|
||||
c = vec_ld(16, coeffs);
|
||||
loado = tl;
|
||||
acc1 = vec_madd(w, c, acc1);
|
||||
|
||||
tl = vec_ld(15 + 32, wave);
|
||||
w = vec_perm(loado, tl, lperm);
|
||||
c = vec_ld(32, coeffs);
|
||||
loado = tl;
|
||||
acc2 = vec_madd(w, c, acc2);
|
||||
|
||||
tl = vec_ld(15 + 48, wave);
|
||||
w = vec_perm(loado, tl, lperm);
|
||||
c = vec_ld(48, coeffs);
|
||||
loado = tl;
|
||||
acc3 = vec_madd(w, c, acc3);
|
||||
|
||||
coeffs += 16;
|
||||
wave += 16;
|
||||
} while(--count);
|
||||
}
|
||||
|
||||
{
|
||||
vector float sum;
|
||||
vector float sums0;
|
||||
vector signed int sum_i;
|
||||
|
||||
sum = vec_add(vec_add(acc0, acc1), vec_add(acc2, acc3));
|
||||
sums0 = vec_sld(sum, sum, 8);
|
||||
sum = vec_add(sum, sums0);
|
||||
sums0 = vec_sld(sum, sum, 4);
|
||||
sum = vec_add(sum, sums0);
|
||||
|
||||
sum_i = vec_cts(sum, 0);
|
||||
vec_ste(sum_i, 0, accum_output);
|
||||
}
|
||||
}
|
||||
|
34
mednafen/sound/OwlResampler_neon.inc
Normal file
34
mednafen/sound/OwlResampler_neon.inc
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
static INLINE void DoMAC_NEON(float* wave, float* coeffs, int32 count, int32* accum_output)
|
||||
{
|
||||
register float32x4_t acc0, acc1, acc2, acc3;
|
||||
|
||||
acc0 = acc1 = acc2 = acc3 = vdupq_n_f32(0);
|
||||
|
||||
count >>= 4;
|
||||
|
||||
do
|
||||
{
|
||||
acc0 = vmlaq_f32(acc0, vld1q_f32(MDFN_ASSUME_ALIGNED(coeffs + 0, sizeof(float32x4_t))), vld1q_f32(wave + 0));
|
||||
acc1 = vmlaq_f32(acc1, vld1q_f32(MDFN_ASSUME_ALIGNED(coeffs + 4, sizeof(float32x4_t))), vld1q_f32(wave + 4));
|
||||
acc2 = vmlaq_f32(acc2, vld1q_f32(MDFN_ASSUME_ALIGNED(coeffs + 8, sizeof(float32x4_t))), vld1q_f32(wave + 8));
|
||||
acc3 = vmlaq_f32(acc3, vld1q_f32(MDFN_ASSUME_ALIGNED(coeffs + 12, sizeof(float32x4_t))), vld1q_f32(wave + 12));
|
||||
|
||||
coeffs += 16;
|
||||
wave += 16;
|
||||
} while(MDFN_LIKELY(--count));
|
||||
//
|
||||
//
|
||||
//
|
||||
register float32x4_t sum4;
|
||||
register float32x2_t sum2;
|
||||
|
||||
sum4 = vaddq_f32(vaddq_f32(acc0, acc1), vaddq_f32(acc2, acc3));
|
||||
sum2 = vadd_f32(vget_high_f32(sum4), vget_low_f32(sum4));
|
||||
sum2 = vpadd_f32(sum2, sum2);
|
||||
|
||||
vst1_lane_s32(accum_output, vcvt_s32_f32(sum2), 0);
|
||||
}
|
||||
|
277
mednafen/sound/OwlResampler_x86.inc
Normal file
277
mednafen/sound/OwlResampler_x86.inc
Normal file
@ -0,0 +1,277 @@
|
||||
//
|
||||
// Don't use vzeroall in AVX code unless we mark ymm8-ymm15/xmm8-xmm15 as clobbers on x86_64
|
||||
// (Also, remember to mark xmm* instead of ymm* as clobbers if __AVX__ isn't defined!)
|
||||
|
||||
#if defined(__x86_64__) && !defined(__ILP32__)
|
||||
#define X86_REGC "r"
|
||||
#define X86_REGAT ""
|
||||
#else
|
||||
#define X86_REGC "e"
|
||||
#define X86_REGAT "l"
|
||||
#endif
|
||||
|
||||
static INLINE void DoMAC_AVX_32X(float* wave, float* coeffs, int32 count, int32* accum_output)
|
||||
{
|
||||
// Multiplies 32 coefficients at a time.
|
||||
int dummy;
|
||||
int32 tmp;
|
||||
|
||||
//printf("%f\n", adj);
|
||||
/*
|
||||
?di = wave pointer
|
||||
?si = coeffs pointer
|
||||
ecx = count / 16
|
||||
edx = 32-bit int output pointer
|
||||
|
||||
|
||||
*/
|
||||
// Will read 32 bytes of input waveform past end.
|
||||
asm volatile(
|
||||
//".arch bdver1\n\t"
|
||||
"vxorps %%ymm3, %%ymm3, %%ymm3\n\t" // For a loop optimization
|
||||
|
||||
"vxorps %%ymm4, %%ymm4, %%ymm4\n\t"
|
||||
"vxorps %%ymm5, %%ymm5, %%ymm5\n\t"
|
||||
"vxorps %%ymm6, %%ymm6, %%ymm6\n\t"
|
||||
"vxorps %%ymm7, %%ymm7, %%ymm7\n\t"
|
||||
|
||||
"vmovups 0(%%" X86_REGC "di), %%ymm0\n\t"
|
||||
"1:\n\t"
|
||||
|
||||
"vmovups 32(%%" X86_REGC "di), %%ymm1\n\t"
|
||||
"vmulps 0(%%" X86_REGC "si), %%ymm0, %%ymm0\n\t"
|
||||
"vaddps %%ymm3, %%ymm7, %%ymm7\n\t"
|
||||
|
||||
"vmovups 64(%%" X86_REGC "di), %%ymm2\n\t"
|
||||
"vmulps 32(%%" X86_REGC "si), %%ymm1, %%ymm1\n\t"
|
||||
"vaddps %%ymm0, %%ymm4, %%ymm4\n\t"
|
||||
|
||||
"vmovups 96(%%" X86_REGC "di), %%ymm3\n\t"
|
||||
"vmulps 64(%%" X86_REGC "si), %%ymm2, %%ymm2\n\t"
|
||||
"vaddps %%ymm1, %%ymm5, %%ymm5\n\t"
|
||||
|
||||
"vmovups 128(%%" X86_REGC "di), %%ymm0\n\t"
|
||||
"vmulps 96(%%" X86_REGC "si), %%ymm3, %%ymm3\n\t"
|
||||
"vaddps %%ymm2, %%ymm6, %%ymm6\n\t"
|
||||
|
||||
"add" X86_REGAT " $128, %%" X86_REGC "si\n\t"
|
||||
"add" X86_REGAT " $128, %%" X86_REGC "di\n\t"
|
||||
"subl $1, %%ecx\n\t"
|
||||
"jnz 1b\n\t"
|
||||
|
||||
"vaddps %%ymm3, %%ymm7, %%ymm7\n\t" // For a loop optimization
|
||||
|
||||
//
|
||||
// Add the four summation ymm regs together into one ymm register, ymm7
|
||||
//
|
||||
"vaddps %%ymm4, %%ymm5, %%ymm5\n\t"
|
||||
"vaddps %%ymm6, %%ymm7, %%ymm7\n\t"
|
||||
"vaddps %%ymm5, %%ymm7, %%ymm7\n\t"
|
||||
|
||||
//
|
||||
// Horizontal addition.
|
||||
//
|
||||
// A,B,C,D, E,F,G,H
|
||||
"vhaddps %%ymm7, %%ymm7, %%ymm7\n\t"
|
||||
// A+B, C+D, A+B, C+D, E+F, G+H, E+F, G+H,
|
||||
"vhaddps %%ymm7, %%ymm7, %%ymm7\n\t"
|
||||
"vextractf128 $1, %%ymm7, %%xmm6\n\t"
|
||||
"vaddss %%xmm7, %%xmm6, %%xmm6\n\t"
|
||||
// A+B+C+D, A+B+C+D, A+B+C+D, A+B+C+D, E+F+G+H, E+F+G+H, E+F+G+H, E+F+G+H,
|
||||
//"vhaddps %%ymm5, %%ymm5, %%ymm5\n\t"
|
||||
// A+B+C+D+E+F+G+H
|
||||
|
||||
"vcvtss2si %%xmm6, %%ecx\n\t"
|
||||
: "=D" (dummy), "=S" (dummy), "=c" (tmp)
|
||||
: "D" (wave), "S" (coeffs), "c" (count >> 5)
|
||||
#ifdef __AVX__
|
||||
: "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", "cc", "memory"
|
||||
#elif defined(__SSE__)
|
||||
: "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "cc", "memory"
|
||||
#else
|
||||
: "cc", "memory"
|
||||
#endif
|
||||
);
|
||||
|
||||
*accum_output = tmp;
|
||||
}
|
||||
|
||||
static INLINE void DoMAC_AVX_32X_P16(float* wave, float* coeffs, int32 count, int32* accum_output)
|
||||
{
|
||||
// Multiplies 32 coefficients at a time.
|
||||
int dummy;
|
||||
int32 tmp;
|
||||
|
||||
//printf("%f\n", adj);
|
||||
/*
|
||||
?di = wave pointer
|
||||
?si = coeffs pointer
|
||||
ecx = count / 16
|
||||
edx = 32-bit int output pointer
|
||||
|
||||
|
||||
*/
|
||||
|
||||
asm volatile(
|
||||
"vxorps %%ymm3, %%ymm3, %%ymm3\n\t" // For a loop optimization
|
||||
|
||||
"vxorps %%ymm4, %%ymm4, %%ymm4\n\t"
|
||||
"vxorps %%ymm5, %%ymm5, %%ymm5\n\t"
|
||||
"vxorps %%ymm6, %%ymm6, %%ymm6\n\t"
|
||||
"vxorps %%ymm7, %%ymm7, %%ymm7\n\t"
|
||||
|
||||
"vmovups 0(%%" X86_REGC "di), %%ymm0\n\t"
|
||||
"1:\n\t"
|
||||
|
||||
"vmovups 32(%%" X86_REGC "di), %%ymm1\n\t"
|
||||
"vmulps 0(%%" X86_REGC "si), %%ymm0, %%ymm0\n\t"
|
||||
"vaddps %%ymm3, %%ymm7, %%ymm7\n\t"
|
||||
|
||||
"vmovups 64(%%" X86_REGC "di), %%ymm2\n\t"
|
||||
"vmulps 32(%%" X86_REGC "si), %%ymm1, %%ymm1\n\t"
|
||||
"vaddps %%ymm0, %%ymm4, %%ymm4\n\t"
|
||||
|
||||
"vmovups 96(%%" X86_REGC "di), %%ymm3\n\t"
|
||||
"vmulps 64(%%" X86_REGC "si), %%ymm2, %%ymm2\n\t"
|
||||
"vaddps %%ymm1, %%ymm5, %%ymm5\n\t"
|
||||
|
||||
"vmovups 128(%%" X86_REGC "di), %%ymm0\n\t"
|
||||
"vmulps 96(%%" X86_REGC "si), %%ymm3, %%ymm3\n\t"
|
||||
"vaddps %%ymm2, %%ymm6, %%ymm6\n\t"
|
||||
|
||||
"add" X86_REGAT " $128, %%" X86_REGC "si\n\t"
|
||||
"add" X86_REGAT " $128, %%" X86_REGC "di\n\t"
|
||||
"subl $1, %%ecx\n\t"
|
||||
"jnz 1b\n\t"
|
||||
|
||||
/////
|
||||
"vmovups 32(%%" X86_REGC "di), %%ymm1\n\t"
|
||||
"vmulps 0(%%" X86_REGC "si), %%ymm0, %%ymm0\n\t"
|
||||
"vaddps %%ymm3, %%ymm7, %%ymm7\n\t"
|
||||
"vmulps 32(%%" X86_REGC "si), %%ymm1, %%ymm1\n\t"
|
||||
"vaddps %%ymm0, %%ymm4, %%ymm4\n\t"
|
||||
/////
|
||||
"vaddps %%ymm1, %%ymm5, %%ymm5\n\t" // For a loop optimization
|
||||
|
||||
//
|
||||
// Add the four summation ymm regs together into one ymm register, ymm7
|
||||
//
|
||||
"vaddps %%ymm4, %%ymm5, %%ymm5\n\t"
|
||||
"vaddps %%ymm6, %%ymm7, %%ymm7\n\t"
|
||||
"vaddps %%ymm5, %%ymm7, %%ymm7\n\t"
|
||||
|
||||
//
|
||||
// Horizontal addition.
|
||||
//
|
||||
// A,B,C,D, E,F,G,H
|
||||
"vhaddps %%ymm7, %%ymm7, %%ymm7\n\t"
|
||||
// A+B, C+D, A+B, C+D, E+F, G+H, E+F, G+H,
|
||||
"vhaddps %%ymm7, %%ymm7, %%ymm7\n\t"
|
||||
"vextractf128 $1, %%ymm7, %%xmm6\n\t"
|
||||
"vaddss %%xmm7, %%xmm6, %%xmm6\n\t"
|
||||
// A+B+C+D, A+B+C+D, A+B+C+D, A+B+C+D, E+F+G+H, E+F+G+H, E+F+G+H, E+F+G+H,
|
||||
//"vhaddps %%ymm5, %%ymm5, %%ymm5\n\t"
|
||||
// A+B+C+D+E+F+G+H
|
||||
|
||||
"vcvtss2si %%xmm6, %%ecx\n\t"
|
||||
: "=D" (dummy), "=S" (dummy), "=c" (tmp)
|
||||
: "D" (wave), "S" (coeffs), "c" (count >> 5)
|
||||
#ifdef __AVX__
|
||||
: "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", "cc", "memory"
|
||||
#elif defined(__SSE__)
|
||||
: "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "cc", "memory"
|
||||
#else
|
||||
: "cc", "memory"
|
||||
#endif
|
||||
);
|
||||
|
||||
*accum_output = tmp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static INLINE void DoMAC_SSE_16X(float *wave, float *coeffs, int32 count, int32 *accum_output)
|
||||
{
|
||||
// Multiplies 16 coefficients at a time.
|
||||
int dummy;
|
||||
|
||||
//printf("%f\n", adj);
|
||||
/*
|
||||
?di = wave pointer
|
||||
?si = coeffs pointer
|
||||
ecx = count / 16
|
||||
edx = 32-bit int output pointer
|
||||
|
||||
|
||||
*/
|
||||
// Will read 16 bytes of input waveform past end.
|
||||
asm volatile(
|
||||
"xorps %%xmm3, %%xmm3\n\t" // For a loop optimization
|
||||
|
||||
"xorps %%xmm4, %%xmm4\n\t"
|
||||
"xorps %%xmm5, %%xmm5\n\t"
|
||||
"xorps %%xmm6, %%xmm6\n\t"
|
||||
"xorps %%xmm7, %%xmm7\n\t"
|
||||
|
||||
"movups 0(%%" X86_REGC "di), %%xmm0\n\t"
|
||||
"1:\n\t"
|
||||
|
||||
"movups 16(%%" X86_REGC "di), %%xmm1\n\t"
|
||||
"mulps 0(%%" X86_REGC "si), %%xmm0\n\t"
|
||||
"addps %%xmm3, %%xmm7\n\t"
|
||||
|
||||
"movups 32(%%" X86_REGC "di), %%xmm2\n\t"
|
||||
"mulps 16(%%" X86_REGC "si), %%xmm1\n\t"
|
||||
"addps %%xmm0, %%xmm4\n\t"
|
||||
|
||||
"movups 48(%%" X86_REGC "di), %%xmm3\n\t"
|
||||
"mulps 32(%%" X86_REGC "si), %%xmm2\n\t"
|
||||
"addps %%xmm1, %%xmm5\n\t"
|
||||
|
||||
"movups 64(%%" X86_REGC "di), %%xmm0\n\t"
|
||||
"mulps 48(%%" X86_REGC "si), %%xmm3\n\t"
|
||||
"addps %%xmm2, %%xmm6\n\t"
|
||||
|
||||
"add" X86_REGAT " $64, %%" X86_REGC "si\n\t"
|
||||
"add" X86_REGAT " $64, %%" X86_REGC "di\n\t"
|
||||
"subl $1, %%ecx\n\t"
|
||||
"jnz 1b\n\t"
|
||||
|
||||
"addps %%xmm3, %%xmm7\n\t" // For a loop optimization
|
||||
|
||||
//
|
||||
// Add the four summation xmm regs together into one xmm register, xmm7
|
||||
//
|
||||
"addps %%xmm4, %%xmm5\n\t"
|
||||
"addps %%xmm6, %%xmm7\n\t"
|
||||
"addps %%xmm5, %%xmm7\n\t"
|
||||
|
||||
//
|
||||
// Now for the "fun" horizontal addition...
|
||||
//
|
||||
//
|
||||
"movaps %%xmm7, %%xmm4\n\t"
|
||||
// (3 * 2^0) + (2 * 2^2) + (1 * 2^4) + (0 * 2^6) = 27
|
||||
"shufps $27, %%xmm7, %%xmm4\n\t"
|
||||
"addps %%xmm4, %%xmm7\n\t"
|
||||
|
||||
// At this point, xmm7:
|
||||
// (3 + 0), (2 + 1), (1 + 2), (0 + 3)
|
||||
//
|
||||
// (1 * 2^0) + (0 * 2^2) = 1
|
||||
"movaps %%xmm7, %%xmm4\n\t"
|
||||
"shufps $1, %%xmm7, %%xmm4\n\t"
|
||||
"addss %%xmm4, %%xmm7\n\t" // No sense in doing packed addition here.
|
||||
|
||||
"cvtss2si %%xmm7, %%ecx\n\t"
|
||||
"movl %%ecx, (%%" X86_REGC "dx)\n\t"
|
||||
: "=D" (dummy), "=S" (dummy), "=c" (dummy)
|
||||
: "D" (wave), "S" (coeffs), "c" (count >> 4), "d" (accum_output)
|
||||
#ifdef __SSE__
|
||||
: "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "cc", "memory"
|
||||
#else
|
||||
: "cc", "memory"
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
98
mednafen/sound/okiadpcm-deltatable.h
Normal file
98
mednafen/sound/okiadpcm-deltatable.h
Normal file
@ -0,0 +1,98 @@
|
||||
{
|
||||
2, 6, 10, 14, 18, 22, 26, 30, -2, -6, -10, -14, -18, -22, -26, -30 },
|
||||
{
|
||||
2, 6, 10, 14, 19, 23, 27, 31, -2, -6, -10, -14, -19, -23, -27, -31 },
|
||||
{
|
||||
2, 6, 11, 15, 21, 25, 30, 34, -2, -6, -11, -15, -21, -25, -30, -34 },
|
||||
{
|
||||
2, 7, 12, 17, 23, 28, 33, 38, -2, -7, -12, -17, -23, -28, -33, -38 },
|
||||
{
|
||||
2, 7, 13, 18, 25, 30, 36, 41, -2, -7, -13, -18, -25, -30, -36, -41 },
|
||||
{
|
||||
3, 9, 15, 21, 28, 34, 40, 46, -3, -9, -15, -21, -28, -34, -40, -46 },
|
||||
{
|
||||
3, 10, 17, 24, 31, 38, 45, 52, -3, -10, -17, -24, -31, -38, -45, -52 },
|
||||
{
|
||||
3, 10, 18, 25, 34, 41, 49, 56, -3, -10, -18, -25, -34, -41, -49, -56 },
|
||||
{
|
||||
4, 12, 21, 29, 38, 46, 55, 63, -4, -12, -21, -29, -38, -46, -55, -63 },
|
||||
{
|
||||
4, 13, 22, 31, 41, 50, 59, 68, -4, -13, -22, -31, -41, -50, -59, -68 },
|
||||
{
|
||||
5, 15, 25, 35, 46, 56, 66, 76, -5, -15, -25, -35, -46, -56, -66, -76 },
|
||||
{
|
||||
5, 16, 27, 38, 50, 61, 72, 83, -5, -16, -27, -38, -50, -61, -72, -83 },
|
||||
{
|
||||
6, 18, 31, 43, 56, 68, 81, 93, -6, -18, -31, -43, -56, -68, -81, -93 },
|
||||
{
|
||||
6, 19, 33, 46, 61, 74, 88, 101, -6, -19, -33, -46, -61, -74, -88, -101 },
|
||||
{
|
||||
7, 22, 37, 52, 67, 82, 97, 112, -7, -22, -37, -52, -67, -82, -97, -112 },
|
||||
{
|
||||
8, 24, 41, 57, 74, 90, 107, 123, -8, -24, -41, -57, -74, -90, -107, -123 },
|
||||
{
|
||||
9, 27, 45, 63, 82, 100, 118, 136, -9, -27, -45, -63, -82, -100, -118, -136 },
|
||||
{
|
||||
10, 30, 50, 70, 90, 110, 130, 150, -10, -30, -50, -70, -90, -110, -130, -150 },
|
||||
{
|
||||
11, 33, 55, 77, 99, 121, 143, 165, -11, -33, -55, -77, -99, -121, -143, -165 },
|
||||
{
|
||||
12, 36, 60, 84, 109, 133, 157, 181, -12, -36, -60, -84, -109, -133, -157, -181 },
|
||||
{
|
||||
13, 39, 66, 92, 120, 146, 173, 199, -13, -39, -66, -92, -120, -146, -173, -199 },
|
||||
{
|
||||
14, 43, 73, 102, 132, 161, 191, 220, -14, -43, -73, -102, -132, -161, -191, -220 },
|
||||
{
|
||||
16, 48, 81, 113, 146, 178, 211, 243, -16, -48, -81, -113, -146, -178, -211, -243 },
|
||||
{
|
||||
17, 52, 88, 123, 160, 195, 231, 266, -17, -52, -88, -123, -160, -195, -231, -266 },
|
||||
{
|
||||
19, 58, 97, 136, 176, 215, 254, 293, -19, -58, -97, -136, -176, -215, -254, -293 },
|
||||
{
|
||||
21, 64, 107, 150, 194, 237, 280, 323, -21, -64, -107, -150, -194, -237, -280, -323 },
|
||||
{
|
||||
23, 70, 118, 165, 213, 260, 308, 355, -23, -70, -118, -165, -213, -260, -308, -355 },
|
||||
{
|
||||
26, 78, 130, 182, 235, 287, 339, 391, -26, -78, -130, -182, -235, -287, -339, -391 },
|
||||
{
|
||||
28, 85, 143, 200, 258, 315, 373, 430, -28, -85, -143, -200, -258, -315, -373, -430 },
|
||||
{
|
||||
31, 94, 157, 220, 284, 347, 410, 473, -31, -94, -157, -220, -284, -347, -410, -473 },
|
||||
{
|
||||
34, 103, 173, 242, 313, 382, 452, 521, -34, -103, -173, -242, -313, -382, -452, -521 },
|
||||
{
|
||||
38, 114, 191, 267, 345, 421, 498, 574, -38, -114, -191, -267, -345, -421, -498, -574 },
|
||||
{
|
||||
42, 126, 210, 294, 379, 463, 547, 631, -42, -126, -210, -294, -379, -463, -547, -631 },
|
||||
{
|
||||
46, 138, 231, 323, 417, 509, 602, 694, -46, -138, -231, -323, -417, -509, -602, -694 },
|
||||
{
|
||||
51, 153, 255, 357, 459, 561, 663, 765, -51, -153, -255, -357, -459, -561, -663, -765 },
|
||||
{
|
||||
56, 168, 280, 392, 505, 617, 729, 841, -56, -168, -280, -392, -505, -617, -729, -841 },
|
||||
{
|
||||
61, 184, 308, 431, 555, 678, 802, 925, -61, -184, -308, -431, -555, -678, -802, -925 },
|
||||
{
|
||||
68, 204, 340, 476, 612, 748, 884, 1020, -68, -204, -340, -476, -612, -748, -884, -1020 },
|
||||
{
|
||||
74, 223, 373, 522, 672, 821, 971, 1120, -74, -223, -373, -522, -672, -821, -971, -1120 },
|
||||
{
|
||||
82, 246, 411, 575, 740, 904, 1069, 1233, -82, -246, -411, -575, -740, -904, -1069, -1233 },
|
||||
{
|
||||
90, 271, 452, 633, 814, 995, 1176, 1357, -90, -271, -452, -633, -814, -995, -1176, -1357 },
|
||||
{
|
||||
99, 298, 497, 696, 895, 1094, 1293, 1492, -99, -298, -497, -696, -895, -1094, -1293, -1492 },
|
||||
{
|
||||
109, 328, 547, 766, 985, 1204, 1423, 1642, -109, -328, -547, -766, -985, -1204, -1423, -1642 },
|
||||
{
|
||||
120, 360, 601, 841, 1083, 1323, 1564, 1804, -120, -360, -601, -841, -1083, -1323, -1564, -1804 },
|
||||
{
|
||||
132, 397, 662, 927, 1192, 1457, 1722, 1987, -132, -397, -662, -927, -1192, -1457, -1722, -1987 },
|
||||
{
|
||||
145, 436, 728, 1019, 1311, 1602, 1894, 2185, -145, -436, -728, -1019, -1311, -1602, -1894, -2185 },
|
||||
{
|
||||
160, 480, 801, 1121, 1442, 1762, 2083, 2403, -160, -480, -801, -1121, -1442, -1762, -2083, -2403 },
|
||||
{
|
||||
176, 528, 881, 1233, 1587, 1939, 2292, 2644, -176, -528, -881, -1233, -1587, -1939, -2292, -2644 },
|
||||
{
|
||||
194, 582, 970, 1358, 1746, 2134, 2522, 2910, -194, -582, -970, -1358, -1746, -2134, -2522, -2910 },
|
49
mednafen/sound/okiadpcm.cpp
Normal file
49
mednafen/sound/okiadpcm.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "mednafen.h"
|
||||
#include "okiadpcm.h"
|
||||
#include <math.h>
|
||||
|
||||
const int OKIADPCM_StepSizes[49] =
|
||||
{
|
||||
// These can also be generated like:
|
||||
// double start = 16;
|
||||
// for(int x = 0; x < 49; x++)
|
||||
// {
|
||||
// printf("%d, ", (int)start);
|
||||
// start *= 1.1;
|
||||
// }
|
||||
16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50,
|
||||
55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157,
|
||||
173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
|
||||
494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552,
|
||||
};
|
||||
|
||||
const int OKIADPCM_StepIndexDeltas[16] =
|
||||
{
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
const int32 OKIADPCM_DeltaTable[49][16] =
|
||||
{
|
||||
#ifndef OKIADPCM_GENERATE_DELTATABLE
|
||||
#include "okiadpcm-deltatable.h"
|
||||
#endif
|
||||
};
|
||||
|
165
mednafen/sound/okiadpcm.h
Normal file
165
mednafen/sound/okiadpcm.h
Normal file
@ -0,0 +1,165 @@
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __MDFN_OKIADPCM_H
|
||||
#define __MDFN_OKIADPCM_H
|
||||
|
||||
// PC-FX ADPCM decoder not finished!
|
||||
|
||||
typedef enum
|
||||
{
|
||||
OKIADPCM_MSM5205 = 0,
|
||||
OKIADPCM_MSM5218 = 1,
|
||||
OKIADPCM_COUNT
|
||||
} OKIADPCM_Chip;
|
||||
|
||||
extern const int OKIADPCM_StepSizes[49];
|
||||
extern const int OKIADPCM_StepIndexDeltas[16];
|
||||
extern const int32 OKIADPCM_DeltaTable[49][16];
|
||||
|
||||
template <OKIADPCM_Chip CHIP_TYPE>
|
||||
class OKIADPCM_Decoder
|
||||
{
|
||||
public:
|
||||
|
||||
OKIADPCM_Decoder()
|
||||
{
|
||||
assert(CHIP_TYPE < OKIADPCM_COUNT);
|
||||
|
||||
CurSample = 0x0800;
|
||||
|
||||
StepSizeIndex = 0;
|
||||
}
|
||||
~OKIADPCM_Decoder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
INLINE uint16 GetSample(void)
|
||||
{
|
||||
return(CurSample);
|
||||
}
|
||||
|
||||
INLINE void SetSample(uint16 new_sample)
|
||||
{
|
||||
assert(new_sample <= 0xFFF);
|
||||
CurSample = new_sample;
|
||||
}
|
||||
|
||||
INLINE uint8 GetSSI(void)
|
||||
{
|
||||
return(StepSizeIndex);
|
||||
}
|
||||
|
||||
INLINE void SetSSI(uint8 new_ssi)
|
||||
{
|
||||
assert(new_ssi <= 48);
|
||||
StepSizeIndex = new_ssi;
|
||||
}
|
||||
|
||||
// DecodeDelta returns the coded delta for the given nibble and (previous) predictor.
|
||||
// It will not wrap nor saturate the returned value, and CurSample is not updated.
|
||||
INLINE int32 DecodeDelta(const uint8 nibble)
|
||||
{
|
||||
int32 ret = OKIADPCM_DeltaTable[StepSizeIndex][nibble];
|
||||
|
||||
StepSizeIndex += OKIADPCM_StepIndexDeltas[nibble];
|
||||
|
||||
if(StepSizeIndex < 0)
|
||||
StepSizeIndex = 0;
|
||||
|
||||
if(StepSizeIndex > 48)
|
||||
StepSizeIndex = 48;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
// This function will return the full 12-bits, it's up to the caller to
|
||||
// truncate as necessary(MSM5205 only has a 10-bit D/A, MSM5218 has a 12-bit D/A)
|
||||
INLINE uint16 Decode(const uint8 nibble)
|
||||
{
|
||||
CurSample += DecodeDelta(nibble);
|
||||
|
||||
if(CHIP_TYPE == OKIADPCM_MSM5205)
|
||||
{
|
||||
CurSample &= 0xFFF;
|
||||
}
|
||||
else if(CHIP_TYPE == OKIADPCM_MSM5218)
|
||||
{
|
||||
if(CurSample > 0xFFF)
|
||||
CurSample = 0xFFF;
|
||||
if(CurSample < 0)
|
||||
CurSample = 0;
|
||||
}
|
||||
return(CurSample);
|
||||
}
|
||||
|
||||
private:
|
||||
int32 CurSample;
|
||||
int32 StepSizeIndex;
|
||||
};
|
||||
|
||||
template <OKIADPCM_Chip CHIP_TYPE>
|
||||
class OKIADPCM_Encoder
|
||||
{
|
||||
public:
|
||||
|
||||
OKIADPCM_Encoder()
|
||||
{
|
||||
Accum = 0x800;
|
||||
StepSizeIndex = 0;
|
||||
}
|
||||
|
||||
~OKIADPCM_Encoder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint8 EncodeSample(uint16 in_sample)
|
||||
{
|
||||
uint8 nibble = 0;
|
||||
int32 sample_delta = in_sample - Accum;
|
||||
int piece;
|
||||
|
||||
piece = (abs(sample_delta) * 4 / OKIADPCM_StepSizes[StepSizeIndex]);
|
||||
if(piece > 0x7)
|
||||
piece = 0x7;
|
||||
|
||||
nibble = ((uint32)(sample_delta >> 31) & 0x8) | piece;
|
||||
|
||||
// Update Accum and StepSizeIndex!
|
||||
Accum += OKIADPCM_DeltaTable[StepSizeIndex][nibble];
|
||||
StepSizeIndex += OKIADPCM_StepIndexDeltas[nibble];
|
||||
|
||||
if(Accum > 0xFFF) Accum = 0xFFF;
|
||||
if(Accum < 0) Accum = 0;
|
||||
|
||||
if(StepSizeIndex < 0)
|
||||
StepSizeIndex = 0;
|
||||
|
||||
if(StepSizeIndex > 48)
|
||||
StepSizeIndex = 48;
|
||||
|
||||
return(nibble);
|
||||
}
|
||||
|
||||
private:
|
||||
int32 Accum;
|
||||
int32 StepSizeIndex;
|
||||
OKIADPCM_Chip ChipType;
|
||||
};
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user