diff --git a/Makefile b/Makefile index 35899a0..ea86225 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ FRONTEND_SUPPORTS_RGB565 = 1 MEDNAFEN_DIR := mednafen MEDNAFEN_LIBRETRO_DIR := mednafen-libretro NEED_TREMOR = 0 +LIBRETRO_SOURCES := ifeq ($(platform),) platform = unix @@ -71,7 +72,8 @@ else ifeq ($(core), pce-fast) ifneq ($(platform), osx) PTHREAD_FLAGS = -pthread endif - NEED_BPP = 32 + HAVE_HES = 0 + NEED_BPP = 16 NEED_TREMOR = 1 NEED_BLIP = 1 NEED_CD = 1 @@ -79,15 +81,23 @@ endif NEED_SCSI_CD = 1 NEED_THREADING = 1 NEED_CRC32 = 1 + WANT_NEW_API = 1 CORE_DEFINE := -DWANT_PCE_FAST_EMU - CORE_DIR := $(MEDNAFEN_DIR)/pce_fast + CORE_DIR := $(MEDNAFEN_DIR)/pce_fast-09333 -CORE_SOURCES := $(CORE_DIR)/huc.cpp \ +CORE_SOURCES := \ + $(CORE_DIR)/huc.cpp \ $(CORE_DIR)/huc6280.cpp \ $(CORE_DIR)/input.cpp \ $(CORE_DIR)/pce.cpp \ - $(CORE_DIR)/tsushin.cpp \ + $(CORE_DIR)/pcecd.cpp \ + $(CORE_DIR)/pcecd_drive.cpp \ + $(CORE_DIR)/psg.cpp \ $(CORE_DIR)/vdc.cpp + +ifeq ($(HAVE_HES),1) +CORE_SOURCES += $(CORE_DIR)/hes.cpp +endif TARGET_NAME := mednafen_pce_fast_libretro arch = intel @@ -484,6 +494,7 @@ endif ifeq ($(NEED_CRC32), 1) FLAGS += -DWANT_CRC32 + LIBRETRO_SOURCES += scrc32.cpp endif ifeq ($(NEED_DEINTERLACER), 1) @@ -538,7 +549,7 @@ MEDNAFEN_SOURCES := $(MEDNAFEN_DIR)/mednafen.cpp \ $(MEDNAFEN_DIR)/md5.cpp -LIBRETRO_SOURCES := libretro.cpp stubs.cpp $(THREAD_STUBS) +LIBRETRO_SOURCES += libretro.cpp stubs.cpp $(THREAD_STUBS) TRIO_SOURCES += $(MEDNAFEN_DIR)/trio/trio.c \ $(MEDNAFEN_DIR)/trio/triostr.c @@ -589,6 +600,10 @@ ifeq ($(CACHE_CD), 1) FLAGS += -D__LIBRETRO_CACHE_CD__ endif +ifeq ($(NEED_BPP), 8) +FLAGS += -DWANT_8BPP +endif + ifeq ($(NEED_BPP), 16) FLAGS += -DWANT_16BPP endif @@ -601,6 +616,9 @@ ifeq ($(NEED_BPP), 32) FLAGS += -DWANT_32BPP endif +ifeq ($(WANT_NEW_API), 1) +FLAGS += -DWANT_NEW_API +endif CXXFLAGS += $(FLAGS) CFLAGS += $(FLAGS) diff --git a/libretro.cpp b/libretro.cpp index 4e58760..601d211 100644 --- a/libretro.cpp +++ b/libretro.cpp @@ -92,7 +92,7 @@ static bool is_pal = false; #include "mednafen/cdrom/pcecd.h" #define MEDNAFEN_CORE_NAME_MODULE "pce_fast" #define MEDNAFEN_CORE_NAME "Mednafen PCE Fast" -#define MEDNAFEN_CORE_VERSION "v0.9.28" +#define MEDNAFEN_CORE_VERSION "v0.9.33.3" #define MEDNAFEN_CORE_EXTENSIONS "pce|sgx|cue" #define MEDNAFEN_CORE_TIMING_FPS 59.82 #define MEDNAFEN_CORE_GEOMETRY_BASE_W (game->nominal_width) diff --git a/mednafen/cputest/cputest.h b/mednafen/cputest/cputest.h new file mode 100644 index 0000000..835b3ec --- /dev/null +++ b/mednafen/cputest/cputest.h @@ -0,0 +1,4 @@ +#ifndef _MEDNAFEN_CPUTEST_H +#define _MEDNAFEN_CPUTEST_H + +#endif diff --git a/mednafen/git.h b/mednafen/git.h index 5dd943d..be5581c 100644 --- a/mednafen/git.h +++ b/mednafen/git.h @@ -104,6 +104,27 @@ typedef struct const InputPortInfoStruct *Types; } InputInfoStruct; +struct MemoryPatch; + +struct CheatFormatStruct +{ + const char *FullName; //"Game Genie", "GameShark", "Pro Action Catplay", etc. + const char *Description; // Whatever? + + bool (*DecodeCheat)(const std::string& cheat_string, MemoryPatch* patch); // *patch should be left as initialized by MemoryPatch::MemoryPatch(), unless this is the + // second(or third or whatever) part of a multipart cheat. + // + // Will throw an std::exception(or derivative) on format error. + // + // Will return true if this is part of a multipart cheat. +}; + +struct CheatFormatInfoStruct +{ + unsigned NumFormats; + + CheatFormatStruct *Formats; +}; // Miscellaneous system/simple commands(power, reset, dip switch toggles, coin insert, etc.) // (for DoSimpleCommand() ) @@ -302,6 +323,10 @@ typedef struct void (*RemoveReadPatches)(void); uint8 (*MemRead)(uint32 addr); +#ifdef WANT_NEW_API + CheatFormatInfoStruct *CheatFormatInfo; +#endif + bool SaveStateAltersState; // true for bsnes and some libco-style emulators, false otherwise. // Main save state routine, called by the save state code in state.cpp. // When saving, load is set to 0. When loading, load is set to the version field of the save state being loaded. diff --git a/mednafen/mednafen.h b/mednafen/mednafen.h index c96e3fb..5e40834 100644 --- a/mednafen/mednafen.h +++ b/mednafen/mednafen.h @@ -28,6 +28,9 @@ void MDFN_DebugPrintReal(const char *file, const int line, const char *format, . void MDFN_LoadGameCheats(void *override); void MDFN_FlushGameCheats(int nosave); +void MDFN_MidSync(EmulateSpecStruct *espec); +void MDFN_MidLineUpdate(EmulateSpecStruct *espec, int y); + #include "mednafen-driver.h" #include "mednafen-endian.h" diff --git a/mednafen/movie.h b/mednafen/movie.h new file mode 100644 index 0000000..aa13eac --- /dev/null +++ b/mednafen/movie.h @@ -0,0 +1,4 @@ +#ifndef _MEDNAFEN_MOVIE_H +#define _MEDNAFEN_MOVIE_H + +#endif diff --git a/mednafen/pce_fast/Makefile.am b/mednafen/pce_fast-0928/Makefile.am similarity index 100% rename from mednafen/pce_fast/Makefile.am rename to mednafen/pce_fast-0928/Makefile.am diff --git a/mednafen/pce_fast/Makefile.in b/mednafen/pce_fast-0928/Makefile.in similarity index 100% rename from mednafen/pce_fast/Makefile.in rename to mednafen/pce_fast-0928/Makefile.in diff --git a/mednafen/pce_fast/hes.cpp b/mednafen/pce_fast-0928/hes.cpp similarity index 100% rename from mednafen/pce_fast/hes.cpp rename to mednafen/pce_fast-0928/hes.cpp diff --git a/mednafen/pce_fast/hes.h b/mednafen/pce_fast-0928/hes.h similarity index 100% rename from mednafen/pce_fast/hes.h rename to mednafen/pce_fast-0928/hes.h diff --git a/mednafen/pce_fast/huc.cpp b/mednafen/pce_fast-0928/huc.cpp similarity index 100% rename from mednafen/pce_fast/huc.cpp rename to mednafen/pce_fast-0928/huc.cpp diff --git a/mednafen/pce_fast/huc.h b/mednafen/pce_fast-0928/huc.h similarity index 100% rename from mednafen/pce_fast/huc.h rename to mednafen/pce_fast-0928/huc.h diff --git a/mednafen/pce_fast/huc6280.cpp b/mednafen/pce_fast-0928/huc6280.cpp similarity index 100% rename from mednafen/pce_fast/huc6280.cpp rename to mednafen/pce_fast-0928/huc6280.cpp diff --git a/mednafen/pce_fast/huc6280.h b/mednafen/pce_fast-0928/huc6280.h similarity index 100% rename from mednafen/pce_fast/huc6280.h rename to mednafen/pce_fast-0928/huc6280.h diff --git a/mednafen/pce_fast/huc6280_ops.inc b/mednafen/pce_fast-0928/huc6280_ops.inc similarity index 100% rename from mednafen/pce_fast/huc6280_ops.inc rename to mednafen/pce_fast-0928/huc6280_ops.inc diff --git a/mednafen/pce_fast/input.cpp b/mednafen/pce_fast-0928/input.cpp similarity index 100% rename from mednafen/pce_fast/input.cpp rename to mednafen/pce_fast-0928/input.cpp diff --git a/mednafen/pce_fast/input.h b/mednafen/pce_fast-0928/input.h similarity index 100% rename from mednafen/pce_fast/input.h rename to mednafen/pce_fast-0928/input.h diff --git a/mednafen/pce_fast/ioread.inc b/mednafen/pce_fast-0928/ioread.inc similarity index 100% rename from mednafen/pce_fast/ioread.inc rename to mednafen/pce_fast-0928/ioread.inc diff --git a/mednafen/pce_fast/pce.cpp b/mednafen/pce_fast-0928/pce.cpp similarity index 100% rename from mednafen/pce_fast/pce.cpp rename to mednafen/pce_fast-0928/pce.cpp diff --git a/mednafen/pce_fast/pce.h b/mednafen/pce_fast-0928/pce.h similarity index 100% rename from mednafen/pce_fast/pce.h rename to mednafen/pce_fast-0928/pce.h diff --git a/mednafen/pce_fast/tsushin.cpp b/mednafen/pce_fast-0928/tsushin.cpp similarity index 100% rename from mednafen/pce_fast/tsushin.cpp rename to mednafen/pce_fast-0928/tsushin.cpp diff --git a/mednafen/pce_fast/tsushin.h b/mednafen/pce_fast-0928/tsushin.h similarity index 100% rename from mednafen/pce_fast/tsushin.h rename to mednafen/pce_fast-0928/tsushin.h diff --git a/mednafen/pce_fast/vdc.cpp b/mednafen/pce_fast-0928/vdc.cpp similarity index 100% rename from mednafen/pce_fast/vdc.cpp rename to mednafen/pce_fast-0928/vdc.cpp diff --git a/mednafen/pce_fast/vdc.h b/mednafen/pce_fast-0928/vdc.h similarity index 100% rename from mednafen/pce_fast/vdc.h rename to mednafen/pce_fast-0928/vdc.h diff --git a/mednafen/pce_fast/vpc_mix_inner.inc b/mednafen/pce_fast-0928/vpc_mix_inner.inc similarity index 100% rename from mednafen/pce_fast/vpc_mix_inner.inc rename to mednafen/pce_fast-0928/vpc_mix_inner.inc diff --git a/mednafen/pce_fast-09333/Makefile.am b/mednafen/pce_fast-09333/Makefile.am new file mode 100644 index 0000000..fd5d454 --- /dev/null +++ b/mednafen/pce_fast-09333/Makefile.am @@ -0,0 +1,8 @@ +AUTOMAKE_OPTIONS = subdir-objects +DEFS = -DLOCALEDIR=\"$(datadir)/locale\" @DEFS@ +DEFAULT_INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/intl -I$(top_srcdir)/src/hw_misc -I$(top_srcdir)/src/hw_sound + +noinst_LIBRARIES = libpce_fast.a +mednafen_SOURCES = huc6280.cpp pce.cpp vdc.cpp input.cpp huc.cpp hes.cpp pcecd.cpp pcecd_drive.cpp psg.cpp + +libpce_fast_a_SOURCES = $(mednafen_SOURCES) diff --git a/mednafen/pce_fast-09333/Makefile.in b/mednafen/pce_fast-09333/Makefile.in new file mode 100644 index 0000000..5c5b227 --- /dev/null +++ b/mednafen/pce_fast-09333/Makefile.in @@ -0,0 +1,634 @@ +# Makefile.in generated by automake 1.11.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = src/pce_fast +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cflags_gcc_option.m4 \ + $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/fcntl-o.m4 \ + $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/glibc2.m4 \ + $(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intdiv0.m4 $(top_srcdir)/m4/intl.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intmax.m4 \ + $(top_srcdir)/m4/inttypes-pri.m4 \ + $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/lcmessage.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/lock.m4 $(top_srcdir)/m4/longlong.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/printf-posix.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/stdint_h.m4 \ + $(top_srcdir)/m4/threadlib.m4 $(top_srcdir)/m4/uintmax_t.m4 \ + $(top_srcdir)/m4/visibility.m4 $(top_srcdir)/m4/wchar_t.m4 \ + $(top_srcdir)/m4/wint_t.m4 $(top_srcdir)/m4/xsize.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/include/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +libpce_fast_a_AR = $(AR) $(ARFLAGS) +libpce_fast_a_LIBADD = +am__objects_1 = huc6280.$(OBJEXT) pce.$(OBJEXT) vdc.$(OBJEXT) \ + input.$(OBJEXT) huc.$(OBJEXT) hes.$(OBJEXT) pcecd.$(OBJEXT) \ + pcecd_drive.$(OBJEXT) psg.$(OBJEXT) +am_libpce_fast_a_OBJECTS = $(am__objects_1) +libpce_fast_a_OBJECTS = $(am_libpce_fast_a_OBJECTS) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(libpce_fast_a_SOURCES) +DIST_SOURCES = $(libpce_fast_a_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +ALSA_CFLAGS = @ALSA_CFLAGS@ +ALSA_LIBS = @ALSA_LIBS@ +AMTAR = @AMTAR@ +AM_CFLAGS = @AM_CFLAGS@ +AM_CXXFLAGS = @AM_CXXFLAGS@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = -DLOCALEDIR=\"$(datadir)/locale\" @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GBA_EXTRA_FLAGS = @GBA_EXTRA_FLAGS@ +GENCAT = @GENCAT@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GLIBC2 = @GLIBC2@ +GLIBC21 = @GLIBC21@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +HAVE_ASPRINTF = @HAVE_ASPRINTF@ +HAVE_NEWLOCALE = @HAVE_NEWLOCALE@ +HAVE_POSIX_PRINTF = @HAVE_POSIX_PRINTF@ +HAVE_SNPRINTF = @HAVE_SNPRINTF@ +HAVE_VISIBILITY = @HAVE_VISIBILITY@ +HAVE_WPRINTF = @HAVE_WPRINTF@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLBISON = @INTLBISON@ +INTLLIBS = @INTLLIBS@ +INTLOBJS = @INTLOBJS@ +INTL_DEFAULT_VERBOSITY = @INTL_DEFAULT_VERBOSITY@ +INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +JACK_CFLAGS = @JACK_CFLAGS@ +JACK_LIBS = @JACK_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCDIO_CFLAGS = @LIBCDIO_CFLAGS@ +LIBCDIO_LIBS = @LIBCDIO_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMULTITHREAD = @LIBMULTITHREAD@ +LIBOBJS = @LIBOBJS@ +LIBPTH = @LIBPTH@ +LIBPTH_PREFIX = @LIBPTH_PREFIX@ +LIBS = @LIBS@ +LIBTHREAD = @LIBTHREAD@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBC = @LTLIBC@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBMULTITHREAD = @LTLIBMULTITHREAD@ +LTLIBOBJS = @LTLIBOBJS@ +LTLIBPTH = @LTLIBPTH@ +LTLIBTHREAD = @LTLIBTHREAD@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MMX_CFLAGS = @MMX_CFLAGS@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +PRI_MACROS_BROKEN = @PRI_MACROS_BROKEN@ +RANLIB = @RANLIB@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_CONFIG = @SDL_CONFIG@ +SDL_LIBS = @SDL_LIBS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SNDFILE_CFLAGS = @SNDFILE_CFLAGS@ +SNDFILE_LIBS = @SNDFILE_LIBS@ +SNES_EXTRA_CXXFLAGS = @SNES_EXTRA_CXXFLAGS@ +SNES_EXTRA_FLAGS = @SNES_EXTRA_FLAGS@ +SNES_PERF_EXTRA_CXXFLAGS = @SNES_PERF_EXTRA_CXXFLAGS@ +SNES_PERF_EXTRA_FLAGS = @SNES_PERF_EXTRA_FLAGS@ +SSE2_CFLAGS = @SSE2_CFLAGS@ +SSE3_CFLAGS = @SSE3_CFLAGS@ +SSE_CFLAGS = @SSE_CFLAGS@ +STRIP = @STRIP@ +TRIO_CFLAGS = @TRIO_CFLAGS@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WARNING_FLAGS = @WARNING_FLAGS@ +WINDRES = @WINDRES@ +WOE32 = @WOE32@ +WOE32DLL = @WOE32DLL@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = subdir-objects +DEFAULT_INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/intl -I$(top_srcdir)/src/hw_misc -I$(top_srcdir)/src/hw_sound +noinst_LIBRARIES = libpce_fast.a +mednafen_SOURCES = huc6280.cpp pce.cpp vdc.cpp input.cpp huc.cpp hes.cpp pcecd.cpp pcecd_drive.cpp psg.cpp +libpce_fast_a_SOURCES = $(mednafen_SOURCES) +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/pce_fast/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/pce_fast/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libpce_fast.a: $(libpce_fast_a_OBJECTS) $(libpce_fast_a_DEPENDENCIES) $(EXTRA_libpce_fast_a_DEPENDENCIES) + $(AM_V_at)-rm -f libpce_fast.a + $(AM_V_AR)$(libpce_fast_a_AR) libpce_fast.a $(libpce_fast_a_OBJECTS) $(libpce_fast_a_LIBADD) + $(AM_V_at)$(RANLIB) libpce_fast.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hes.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/huc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/huc6280.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pce.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcecd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcecd_drive.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vdc.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/mednafen/pce_fast-09333/hes.cpp b/mednafen/pce_fast-09333/hes.cpp new file mode 100644 index 0000000..44c2f2f --- /dev/null +++ b/mednafen/pce_fast-09333/hes.cpp @@ -0,0 +1,249 @@ +/* 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 "hes.h" +#include "huc.h" +#include "pcecd.h" +#include "../player.h" +#include "../endian.h" + +namespace PCE_Fast +{ + + +static uint8 mpr_start[8]; +static uint8 IBP_Bank[0x2000]; +static uint8 *rom = NULL, *rom_backup = NULL; + +static uint8 CurrentSong; +static bool bootstrap; +static bool ROMWriteWarningGiven; + +uint8 ReadIBP(unsigned int A) +{ + if(!(A & 0x100)) + return(IBP_Bank[0x1C00 + (A & 0xF)]); + + if(bootstrap) + { + memcpy(rom + 0x1FF0, rom_backup + 0x1FF0, 16); + bootstrap = false; + return(CurrentSong); + } + return(0xFF); +} + +static DECLFW(HESROMWrite) +{ + rom[A] = V; + //printf("%08x: %02x\n", A, V); + if(!ROMWriteWarningGiven) + { + MDFN_printf(_("Warning: HES is writing to physical address %08x. Future warnings of this nature are temporarily disabled for this HES file.\n"), A); + ROMWriteWarningGiven = TRUE; + } +} + +static DECLFR(HESROMRead) +{ + return(rom[A]); +} + +int PCE_HESLoad(const uint8 *buf, uint32 size) +{ + uint32 LoadAddr, LoadSize; + uint32 CurPos; + uint16 InitAddr; + uint8 StartingSong; + int TotalSongs; + + InitAddr = MDFN_de16lsb(&buf[0x6]); + + CurPos = 0x10; + + if(!(rom = (uint8 *)MDFN_malloc(0x88 * 8192, _("HES ROM")))) + { + return(0); + } + + if(!(rom_backup = (uint8 *)MDFN_malloc(0x88 * 8192, _("HES ROM")))) + { + return(0); + } + + memset(rom, 0, 0x88 * 8192); + memset(rom_backup, 0, 0x88 * 8192); + + while(CurPos < (size - 0x10)) + { + LoadSize = MDFN_de32lsb(&buf[CurPos + 0x4]); + LoadAddr = MDFN_de32lsb(&buf[CurPos + 0x8]); + + //printf("Size: %08x(%d), Addr: %08x, La: %02x\n", LoadSize, LoadSize, LoadAddr, LoadAddr / 8192); + + CurPos += 0x10; + + if(((uint64)LoadSize + CurPos) > size) + { + uint32 NewLoadSize = size - CurPos; + + MDFN_printf(_("Warning: HES is trying to load more data than is present in the file(%u attempted, %u left)!\n"), LoadSize, NewLoadSize); + + LoadSize = NewLoadSize; + } + + // 0x88 * 8192 = 0x110000 + if(((uint64)LoadAddr + LoadSize) > 0x110000) + { + MDFN_printf(_("Warning: HES is trying to load data past boundary.\n")); + + if(LoadAddr >= 0x110000) + break; + + LoadSize = 0x110000 - LoadAddr; + } + + memcpy(rom + LoadAddr, &buf[CurPos], LoadSize); + CurPos += LoadSize; + } + + for(int x = 0; x < 8; x++) + mpr_start[x] = buf[0x8 + x]; + + memcpy(rom_backup, rom, 0x88 * 8192); + + CurrentSong = StartingSong = buf[5]; + TotalSongs = 256; + + memset(IBP_Bank, 0, 0x2000); + + uint8 *IBP_WR = IBP_Bank + 0x1C00; + + for(int i = 0; i < 8; i++) + { + *IBP_WR++ = 0xA9; // LDA (immediate) + *IBP_WR++ = mpr_start[i]; + *IBP_WR++ = 0x53; // TAM + *IBP_WR++ = 1 << i; + } + + *IBP_WR++ = 0xAD; // LDA(absolute) + *IBP_WR++ = 0x00; // + *IBP_WR++ = 0x1D; // + *IBP_WR++ = 0x20; // JSR + *IBP_WR++ = InitAddr; // JSR target LSB + *IBP_WR++ = InitAddr >> 8; // JSR target MSB + *IBP_WR++ = 0x58; // CLI + *IBP_WR++ = 0xFC; // (Mednafen Special) + *IBP_WR++ = 0x80; // BRA + *IBP_WR++ = 0xFD; // -3 + + Player_Init(TotalSongs, "", "", ""); //NULL, NULL, NULL, NULL); //UTF8 **snames); + + for(int x = 0; x < 0x80; x++) + { + HuCPUFastMap[x] = rom; + PCERead[x] = HESROMRead; + PCEWrite[x] = HESROMWrite; + } + + HuCPUFastMap[0xFF] = IBP_Bank - (0xFF * 8192); + + // FIXME: If a HES rip tries to execute a SCSI command, the CD emulation code will probably crash. Obviously, a HES rip shouldn't do this, + // but Mednafen shouldn't crash either. ;) + PCE_IsCD = 1; + PCE_InitCD(); + + ROMWriteWarningGiven = FALSE; + + return(1); +} + + +static const uint8 BootROM[16] = { 0xA9, 0xFF, 0x53, 0x01, 0xEA, 0xEA, 0xEA, 0xEA, + 0xEA, 0xEA, 0xEA, 0x4C, 0x00, 0x1C, 0xF0, 0xFF }; +void HES_Reset(void) +{ + memcpy(rom, rom_backup, 0x88 * 8192); + memcpy(rom + 0x1FF0, BootROM, 16); + bootstrap = true; +} + + +void HES_Draw(MDFN_Surface *surface, MDFN_Rect *DisplayRect, int16 *SoundBuf, int32 SoundBufSize) +{ + extern uint16 pce_jp_data[5]; + static uint8 last = 0; + bool needreload = 0; + uint8 newset = (pce_jp_data[0] ^ last) & pce_jp_data[0]; + + if(newset & 0x20) + { + CurrentSong++; + needreload = 1; + } + + if(newset & 0x80) + { + CurrentSong--; + needreload = 1; + } + + if(newset & 0x08) + needreload = 1; + + if(newset & 0x10) + { + CurrentSong += 10; + needreload = 1; + } + + if(newset & 0x40) + { + CurrentSong -= 10; + needreload = 1; + } + + last = pce_jp_data[0]; + + if(needreload) + PCE_Power(); + + Player_Draw(surface, DisplayRect, CurrentSong, SoundBuf, SoundBufSize); +} + +void HES_Close(void) +{ + PCECD_Close(); + + if(rom) + { + MDFN_free(rom); + rom = NULL; + } + + if(rom_backup) + { + MDFN_free(rom_backup); + rom_backup = NULL; + } +} + + + +}; diff --git a/mednafen/pce_fast-09333/hes.h b/mednafen/pce_fast-09333/hes.h new file mode 100644 index 0000000..421d35c --- /dev/null +++ b/mednafen/pce_fast-09333/hes.h @@ -0,0 +1,11 @@ +namespace PCE_Fast +{ + +uint8 ReadIBP(unsigned int A); +void HES_Draw(MDFN_Surface *surface, MDFN_Rect *DisplayRect, int16 *samples, int32 sampcount); + +int PCE_HESLoad(const uint8 *buf, uint32 size) MDFN_COLD; +void HES_Reset(void) MDFN_COLD; +void HES_Close(void) MDFN_COLD; + +}; diff --git a/mednafen/pce_fast-09333/huc.cpp b/mednafen/pce_fast-09333/huc.cpp new file mode 100644 index 0000000..ddaf336 --- /dev/null +++ b/mednafen/pce_fast-09333/huc.cpp @@ -0,0 +1,409 @@ +/* Mednafen - Multi-system Emulator + * + * Portions of this file Copyright (C) 2004 Ki + * + * 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 +#include "pcecd.h" +#include "arcade_card/arcade_card.h" +#include "../md5.h" +#include "../file.h" +#include "../cdrom/cdromif.h" +#include "../mempatcher.h" + +namespace PCE_Fast +{ + +static const uint8 BRAM_Init_String[8] = { 'H', 'U', 'B', 'M', 0x00, 0x88, 0x10, 0x80 }; //"HUBM\x00\x88\x10\x80"; + +ArcadeCard *arcade_card = NULL; + +static uint8 *HuCROM = NULL; + +static bool IsPopulous; +bool PCE_IsCD; + +static uint8 SaveRAM[2048]; + +static DECLFW(ACPhysWrite) +{ + arcade_card->PhysWrite(A, V); +} + +static DECLFR(ACPhysRead) +{ + return(arcade_card->PhysRead(A)); +} + +static DECLFR(SaveRAMRead) +{ + if((!PCE_IsCD || PCECD_IsBRAMEnabled()) && (A & 8191) < 2048) + return(SaveRAM[A & 2047]); + else + return(0xFF); +} + +static DECLFW(SaveRAMWrite) +{ + if((!PCE_IsCD || PCECD_IsBRAMEnabled()) && (A & 8191) < 2048) + SaveRAM[A & 2047] = V; +} + +static DECLFR(HuCRead) +{ + return ROMSpace[A]; +} + +static DECLFW(HuCRAMWrite) +{ + ROMSpace[A] = V; +} + +static DECLFW(HuCRAMWriteCDSpecial) // Hyper Dyne Special hack +{ + BaseRAM[0x2000 | (A & 0x1FFF)] = V; + ROMSpace[A] = V; +} + +static uint8 HuCSF2Latch = 0; + +static DECLFR(HuCSF2Read) +{ + return(HuCROM[(A & 0x7FFFF) + 0x80000 + HuCSF2Latch * 0x80000 ]); // | (HuCSF2Latch << 19) ]); +} + +static DECLFW(HuCSF2Write) +{ + if((A & 0x1FFC) == 0x1FF0) + { + HuCSF2Latch = (A & 0x3); + } +} + +int HuCLoad(const uint8 *data, uint32 len, uint32 crc32) +{ + uint32 sf2_threshold = 2048 * 1024; + uint32 sf2_required_size = 2048 * 1024 + 512 * 1024; + uint32 m_len = (len + 8191)&~8191; + bool sf2_mapper = FALSE; + + if(m_len >= sf2_threshold) + { + sf2_mapper = TRUE; + + if(m_len != sf2_required_size) + m_len = sf2_required_size; + } + + IsPopulous = 0; + PCE_IsCD = 0; + + md5_context md5; + md5.starts(); + md5.update(data, len); + md5.finish(MDFNGameInfo->MD5); + + MDFN_printf(_("ROM: %dKiB\n"), (len + 1023) / 1024); + MDFN_printf(_("ROM CRC32: 0x%04x\n"), crc32); + MDFN_printf(_("ROM MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str()); + + if(!(HuCROM = (uint8 *)MDFN_malloc(m_len, _("HuCard ROM")))) + { + return(0); + } + + memset(HuCROM, 0xFF, m_len); + memcpy(HuCROM, data, (m_len < len) ? m_len : len); + + memset(ROMSpace, 0xFF, 0x88 * 8192 + 8192); + + if(m_len == 0x60000) + { + memcpy(ROMSpace + 0x00 * 8192, HuCROM, 0x20 * 8192); + memcpy(ROMSpace + 0x20 * 8192, HuCROM, 0x20 * 8192); + memcpy(ROMSpace + 0x40 * 8192, HuCROM + 0x20 * 8192, 0x10 * 8192); + memcpy(ROMSpace + 0x50 * 8192, HuCROM + 0x20 * 8192, 0x10 * 8192); + memcpy(ROMSpace + 0x60 * 8192, HuCROM + 0x20 * 8192, 0x10 * 8192); + memcpy(ROMSpace + 0x70 * 8192, HuCROM + 0x20 * 8192, 0x10 * 8192); + } + else if(m_len == 0x80000) + { + memcpy(ROMSpace + 0x00 * 8192, HuCROM, 0x40 * 8192); + memcpy(ROMSpace + 0x40 * 8192, HuCROM + 0x20 * 8192, 0x20 * 8192); + memcpy(ROMSpace + 0x60 * 8192, HuCROM + 0x20 * 8192, 0x20 * 8192); + } + else + { + memcpy(ROMSpace + 0x00 * 8192, HuCROM, (m_len < 1024 * 1024) ? m_len : 1024 * 1024); + } + + for(int x = 0x00; x < 0x80; x++) + { + HuCPUFastMap[x] = ROMSpace; + PCERead[x] = HuCRead; + } + + if(!memcmp(HuCROM + 0x1F26, "POPULOUS", strlen("POPULOUS"))) + { + uint8 *PopRAM = ROMSpace + 0x40 * 8192; + FILE *fp; + + memset(PopRAM, 0xFF, 32768); + if((fp = (FILE*)fopen(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav").c_str(), "rb"))) + { + gzread(fp, PopRAM, 32768); + fclose(fp); + } + + IsPopulous = 1; + + MDFN_printf("Populous\n"); + + for(int x = 0x40; x < 0x44; x++) + { + HuCPUFastMap[x] = &PopRAM[(x & 3) * 8192] - x * 8192; + PCERead[x] = HuCRead; + PCEWrite[x] = HuCRAMWrite; + } + MDFNMP_AddRAM(32768, 0x40 * 8192, PopRAM); + } + else + { + FILE *fp; + + memset(SaveRAM, 0x00, 2048); + memcpy(SaveRAM, BRAM_Init_String, 8); // So users don't have to manually intialize the file cabinet + // in the CD BIOS screen. + if((fp = (FILE*)fopen(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav").c_str(), "rb"))) + { + gzread(fp, SaveRAM, 2048); + fclose(fp); + } + PCEWrite[0xF7] = SaveRAMWrite; + PCERead[0xF7] = SaveRAMRead; + MDFNMP_AddRAM(2048, 0xF7 * 8192, SaveRAM); + } + + // 0x1A558 + //if(len >= 0x20000 && !memcmp(HuCROM + 0x1A558, "STREET FIGHTER#", strlen("STREET FIGHTER#"))) + if(sf2_mapper) + { + for(int x = 0x40; x < 0x80; x++) + { + // FIXME: PCE_FAST + HuCPUFastMap[x] = NULL; // Make sure our reads go through our read function, and not a table lookup + PCERead[x] = HuCSF2Read; + } + PCEWrite[0] = HuCSF2Write; + MDFN_printf("Street Fighter 2 Mapper\n"); + HuCSF2Latch = 0; + } + + return(1); +} + +bool IsBRAMUsed(void) +{ + 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 HuCLoadCD(const char *bios_path) +{ + static const FileExtensionSpecStruct KnownBIOSExtensions[] = + { + { ".pce", gettext_noop("PC Engine ROM Image") }, + { ".bin", gettext_noop("PC Engine ROM Image") }, + { ".bios", gettext_noop("BIOS Image") }, + { NULL, NULL } + }; + + MDFNFILE fp; + + if(!fp.Open(bios_path, KnownBIOSExtensions, _("CD BIOS"))) + { + return(0); + } + + memset(ROMSpace, 0xFF, 262144); + + memcpy(ROMSpace, GET_FDATA(fp) + (GET_FSIZE(fp) & 512), ((GET_FSIZE(fp) & ~512) > 262144) ? 262144 : (GET_FSIZE(fp) &~ 512) ); + + fp.Close(); + + PCE_IsCD = 1; + PCE_InitCD(); + + md5_context md5; + md5.starts(); +// md5_update(&md5, HuCROM, 262144); + +#if 0 + int32 track = CDIF_GetFirstTrack(); + int32 last_track = CDIF_GetLastTrack(); + bool DTFound = 0; + for(; track <= last_track; track++) + { + CDIF_Track_Format format; + + if(CDIF_GetTrackFormat(track, format) && format == CDIF_FORMAT_MODE1) + { + DTFound = 1; + break; + } + } + + if(DTFound) // Only add the MD5 hash if we were able to find a data track. + { + uint32 start_sector = CDIF_GetTrackStartPositionLBA(track); + uint8 sector_buffer[2048]; + + for(int x = 0; x < 128; x++) + { + memset(sector_buffer, 0, 2048); + CDIF_ReadSector(sector_buffer, NULL, start_sector + x, 1); + md5.update(sector_buffer, 2048); + } + } + md5.finish(MDFNGameInfo->MD5); + MDFN_printf(_("CD MD5(first 256KiB): 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str()); + #endif + + MDFN_printf(_("Arcade Card Emulation: %s\n"), PCE_ACEnabled ? _("Enabled") : _("Disabled")); + for(int x = 0; x < 0x40; x++) + { + HuCPUFastMap[x] = ROMSpace; + PCERead[x] = HuCRead; + } + + for(int x = 0x68; x < 0x88; x++) + { + HuCPUFastMap[x] = ROMSpace; + PCERead[x] = HuCRead; + PCEWrite[x] = HuCRAMWrite; + } + PCEWrite[0x80] = HuCRAMWriteCDSpecial; // Hyper Dyne Special hack + MDFNMP_AddRAM(262144, 0x68 * 8192, ROMSpace + 0x68 * 8192); + + if(PCE_ACEnabled) + { + try + { + arcade_card = new ArcadeCard(); + } + catch(std::exception &e) + { + MDFN_PrintError(_("Error creating %s object: %s"), "ArcadeCard", e.what()); + //Cleanup(); // TODO + return(0); + } + + for(int x = 0x40; x < 0x44; x++) + { + HuCPUFastMap[x] = NULL; + PCERead[x] = ACPhysRead; + PCEWrite[x] = ACPhysWrite; + } + } + + FILE *srp; + + memset(SaveRAM, 0x00, 2048); + memcpy(SaveRAM, BRAM_Init_String, 8); // So users don't have to manually intialize the file cabinet + // in the CD BIOS screen. + + if((srp = (FILE*)fopen(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav").c_str(), "rb"))) + { + gzread(srp, SaveRAM, 2048); + fclose(srp); + } + PCEWrite[0xF7] = SaveRAMWrite; + PCERead[0xF7] = SaveRAMRead; + MDFNMP_AddRAM(2048, 0xF7 * 8192, SaveRAM); + return(1); +} + +int HuC_StateAction(StateMem *sm, int load, int data_only) +{ + SFORMAT StateRegs[] = + { + SFARRAY(ROMSpace + 0x40 * 8192, IsPopulous ? 32768 : 0), + SFARRAY(SaveRAM, IsPopulous ? 0 : 2048), + SFARRAY(ROMSpace + 0x68 * 8192, PCE_IsCD ? 262144 : 0), + SFVAR(HuCSF2Latch), + SFEND + }; + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "HuC"); + + if(load) + HuCSF2Latch &= 0x3; + + if(PCE_IsCD) + { + ret &= PCECD_StateAction(sm, load, data_only); + + if(arcade_card) + ret &= arcade_card->StateAction(sm, load, data_only); + } + return(ret); +} + +void HuCClose(void) +{ + if(IsPopulous) + { + MDFN_DumpToFile(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav").c_str(), 6, ROMSpace + 0x40 * 8192, 32768); + } + else if(IsBRAMUsed()) + { + MDFN_DumpToFile(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav").c_str(), 0, SaveRAM, 2048); + } + + if(arcade_card) + { + delete arcade_card; + arcade_card = NULL; + } + + if(PCE_IsCD) + { + PCECD_Close(); + } + + if(HuCROM) + { + MDFN_free(HuCROM); + HuCROM = NULL; + } +} + +void HuC_Power(void) +{ + if(PCE_IsCD) + memset(ROMSpace + 0x68 * 8192, 0x00, 262144); + + if(arcade_card) + arcade_card->Power(); +} + +}; diff --git a/mednafen/pce_fast-09333/huc.h b/mednafen/pce_fast-09333/huc.h new file mode 100644 index 0000000..6d643e4 --- /dev/null +++ b/mednafen/pce_fast-09333/huc.h @@ -0,0 +1,16 @@ +namespace PCE_Fast +{ + +int HuCLoad(const uint8 *data, uint32 len, uint32 crc32) MDFN_COLD; +int HuCLoadCD(const char *bios_path) MDFN_COLD; +void HuCClose(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; + +}; diff --git a/mednafen/pce_fast-09333/huc6280.cpp b/mednafen/pce_fast-09333/huc6280.cpp new file mode 100644 index 0000000..397ba48 --- /dev/null +++ b/mednafen/pce_fast-09333/huc6280.cpp @@ -0,0 +1,785 @@ +/* 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" + +namespace PCE_Fast +{ + +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) \ + { \ + if(wbank != 0xF8 || !HuCPUFastMap[wbank]) \ + printf("Crazy page 1: %02x\n", wbank); \ + 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 low = (HU_A & 0x0F) + (x & 0x0F) + (HU_P & 1); \ + uint32 high = (HU_A & 0xF0) + (x & 0xF0); \ + HU_P &= ~C_FLAG; \ + if(low > 0x09) { high += 0x10; low += 0x06; } \ + if(high > 0x90) { high += 0x60; } \ + HU_P |= (high >> 8) & C_FLAG; \ + HU_A = (low & 0x0F) | (high & 0xF0); \ + 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) \ + { \ + uint32 c = (HU_P & 1) ^ 1; \ + uint32 l = HU_A - x - c; \ + uint32 low = (HU_A & 0x0f) - (x & 0x0f) - c; \ + uint32 high = (HU_A & 0xf0) - (x & 0xf0); \ + HU_P &= ~(C_FLAG); \ + if(low & 0xf0) low -= 0x06; \ + if(low & 0x80) high -= 0x10; \ + if(high & 0x0f00) high -= 0x60; \ + HU_P |= ((l >> 8) & C_FLAG) ^ C_FLAG; \ + HU_A = (low & 0x0F) | (high & 0xf0); \ + 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"); + + 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); +} + + +}; diff --git a/mednafen/pce_fast-09333/huc6280.h b/mednafen/pce_fast-09333/huc6280.h new file mode 100644 index 0000000..0137d29 --- /dev/null +++ b/mednafen/pce_fast-09333/huc6280.h @@ -0,0 +1,156 @@ +#ifndef _HuC6280H + +#define HUC6280_CRAZY_VERSION +//#define HUC6280_EXTRA_CRAZY + +#define HUC6280_LAZY_FLAGS + +namespace PCE_Fast +{ + + +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 diff --git a/mednafen/pce_fast-09333/huc6280_ops.inc b/mednafen/pce_fast-09333/huc6280_ops.inc new file mode 100644 index 0000000..2875dda --- /dev/null +++ b/mednafen/pce_fast-09333/huc6280_ops.inc @@ -0,0 +1,635 @@ +/* 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; diff --git a/mednafen/pce_fast-09333/input.cpp b/mednafen/pce_fast-09333/input.cpp new file mode 100644 index 0000000..6346f97 --- /dev/null +++ b/mednafen/pce_fast-09333/input.cpp @@ -0,0 +1,338 @@ +/* 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 "../movie.h" +#include "../mednafen-endian.h" + +namespace PCE_Fast +{ + +static int InputTypes[5]; +static uint8 *data_ptr[5]; + +static bool AVPad6Which[5]; // Lower(8 buttons) or higher(4 buttons). +static 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) +{ + for(int 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) +{ + for(int 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"); + + 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; +} + +}; diff --git a/mednafen/pce_fast-09333/input.h b/mednafen/pce_fast-09333/input.h new file mode 100644 index 0000000..92010ad --- /dev/null +++ b/mednafen/pce_fast-09333/input.h @@ -0,0 +1,19 @@ +#ifndef __PCE_INPUT_H +#define __PCE_INPUT_H + +namespace PCE_Fast +{ + +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); + +}; + +#endif diff --git a/mednafen/pce_fast-09333/ioread.inc b/mednafen/pce_fast-09333/ioread.inc new file mode 100644 index 0000000..b2eb001 --- /dev/null +++ b/mednafen/pce_fast-09333/ioread.inc @@ -0,0 +1,138 @@ + 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 +// +// +// + +#if IOREAD_SGX==1 + PCEF_CASEL(VDC_00, 0x00): + PCEF_CASEL(VDC_01, 0x01): + PCEF_CASEL(VDC_02, 0x02): + PCEF_CASEL(VDC_03, 0x03): + HuC6280_StealCycle(); + return(VDC_Read(A & 0x1F, TRUE)); +#else + + 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)); +#endif + + 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); diff --git a/mednafen/pce_fast-09333/pce.cpp b/mednafen/pce_fast-09333/pce.cpp new file mode 100644 index 0000000..bbc9967 --- /dev/null +++ b/mednafen/pce_fast-09333/pce.cpp @@ -0,0 +1,714 @@ +/* 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 +#include "vdc.h" +#include "psg.h" +#include "input.h" +#include "huc.h" +#include "pcecd.h" +#include "pcecd_drive.h" +#include "hes.h" +#include "arcade_card/arcade_card.h" +#include "../mempatcher.h" +#include "../cdrom/cdromif.h" + +namespace PCE_Fast +{ + +static PCEFast_PSG *psg = NULL; +extern ArcadeCard *arcade_card; // Bah, lousy globals. + +static Blip_Buffer sbuf[2]; + +bool PCE_ACEnabled; + +static bool IsSGX; +static bool IsHES; +int pce_overclocked; + +// Statically allocated for speed...or something. +uint8 ROMSpace[0x88 * 8192 + 8192]; // + 8192 for PC-as-pointer safety padding + +uint8 BaseRAM[32768 + 8192]; // 8KB for PCE, 32KB for Super Grafx // + 8192 for PC-as-pointer safety padding + +uint8 PCEIODataBuffer; +readfunc PCERead[0x100]; +writefunc PCEWrite[0x100]; + +static DECLFR(PCEBusRead) +{ + //printf("BUS Read: %02x %04x\n", A >> 13, A); + return(0xFF); +} + +static DECLFW(PCENullWrite) +{ + //printf("Null Write: %02x, %08x %02x\n", A >> 13, A, V); +} + +static DECLFR(BaseRAMReadSGX) +{ + return((BaseRAM - (0xF8 * 8192))[A]); +} + +static DECLFW(BaseRAMWriteSGX) +{ + (BaseRAM - (0xF8 * 8192))[A] = V; +} + +static DECLFR(BaseRAMRead) +{ + return((BaseRAM - (0xF8 * 8192))[A]); +} + +static DECLFR(BaseRAMRead_Mirrored) +{ + return(BaseRAM[A & 0x1FFF]); +} + +static DECLFW(BaseRAMWrite) +{ + (BaseRAM - (0xF8 * 8192))[A] = V; +} + +static DECLFW(BaseRAMWrite_Mirrored) +{ + BaseRAM[A & 0x1FFF] = V; +} + +static DECLFR(IORead) +{ + #define IOREAD_SGX 0 + #include "ioread.inc" + #undef IOREAD_SGX +} + +static DECLFR(IOReadSGX) +{ + #define IOREAD_SGX 1 + #include "ioread.inc" + #undef IOREAD_SGX +} + +static DECLFW(IOWrite) +{ + A &= 0x1FFF; + + switch(A >> 10) + { + case 0: HuC6280_StealCycle(); + VDC_Write(A, V); + break; + + case 1: HuC6280_StealCycle(); + VCE_Write(A, V); + break; + + case 2: PCEIODataBuffer = V; + psg->Write(HuCPU.timestamp / pce_overclocked, A, V); + break; + + case 3: PCEIODataBuffer = V; + HuC6280_TimerWrite(A, V); + break; + + case 4: PCEIODataBuffer = V; INPUT_Write(A, V); break; + case 5: PCEIODataBuffer = V; HuC6280_IRQStatusWrite(A, V); break; + case 6: + if(!PCE_IsCD) + break; + + if((A & 0x1E00) == 0x1A00) + { + if(arcade_card) + arcade_card->Write(A & 0x1FFF, V); + } + else + { + PCECD_Write(HuCPU.timestamp * 3, A, V); + } + break; + + case 7: break; // Expansion. + } +} + +static void PCECDIRQCB(bool asserted) +{ + if(asserted) + HuC6280_IRQBegin(MDFN_IQIRQ2); + else + HuC6280_IRQEnd(MDFN_IQIRQ2); +} + +bool PCE_InitCD(void) +{ + PCECD_Settings cd_settings; + memset(&cd_settings, 0, sizeof(PCECD_Settings)); + + cd_settings.CDDA_Volume = (double)MDFN_GetSettingUI("pce_fast.cddavolume") / 100; + cd_settings.CD_Speed = MDFN_GetSettingUI("pce_fast.cdspeed"); + + cd_settings.ADPCM_Volume = (double)MDFN_GetSettingUI("pce_fast.adpcmvolume") / 100; + cd_settings.ADPCM_LPF = MDFN_GetSettingB("pce_fast.adpcmlp"); + + if(cd_settings.CDDA_Volume != 1.0) + MDFN_printf(_("CD-DA Volume: %d%%\n"), (int)(100 * cd_settings.CDDA_Volume)); + + if(cd_settings.ADPCM_Volume != 1.0) + MDFN_printf(_("ADPCM Volume: %d%%\n"), (int)(100 * cd_settings.ADPCM_Volume)); + + return(PCECD_Init(&cd_settings, PCECDIRQCB, PCE_MASTER_CLOCK, pce_overclocked, &sbuf[0], &sbuf[1])); +} + + +static int LoadCommon(void); +static void LoadCommonPre(void); + +static bool TestMagic(const char *name, MDFNFILE *fp) +{ + if(memcmp(GET_FDATA_PTR(fp), "HESM", 4) && strcasecmp(GET_FEXTS_PTR(fp), "pce") && strcasecmp(GET_FEXTS_PTR(fp), "sgx")) + return(FALSE); + + return(TRUE); +} + +static int Load(const char *name, MDFNFILE *fp) +{ + uint32 headerlen = 0; + uint32 r_size; + + IsHES = 0; + IsSGX = 0; + + if(!memcmp(GET_FDATA_PTR(fp), "HESM", 4)) + IsHES = 1; + + LoadCommonPre(); + + if(!IsHES) + { + if(GET_FSIZE_PTR(fp) & 0x200) // 512 byte header! + headerlen = 512; + } + + r_size = GET_FSIZE_PTR(fp) - headerlen; + if(r_size > 4096 * 1024) r_size = 4096 * 1024; + + for(int x = 0; x < 0x100; x++) + { + PCERead[x] = PCEBusRead; + PCEWrite[x] = PCENullWrite; + } + + uint32 crc = crc32(0, GET_FDATA_PTR(fp) + headerlen, GET_FSIZE_PTR(fp) - headerlen); + + +#ifdef HAVE_HES + if(IsHES) + { + if(!PCE_HESLoad(GET_FDATA_PTR(fp), GET_FSIZE_PTR(fp))) + return(0); + } + else +#endif + HuCLoad(GET_FDATA_PTR(fp) + headerlen, GET_FSIZE_PTR(fp) - headerlen, crc); + + if(!strcasecmp(GET_FEXTS_PTR(fp), "sgx")) + IsSGX = TRUE; + + if(GET_FSIZE_PTR(fp) >= 8192 && !memcmp(GET_FDATA_PTR(fp) + headerlen, "DARIUS Version 1.11b", strlen("DARIUS VERSION 1.11b"))) + { + MDFN_printf("SuperGfx: Darius Plus\n"); + IsSGX = 1; + } + + if(crc == 0x4c2126b0) + { + MDFN_printf("SuperGfx: Aldynes\n"); + IsSGX = 1; + } + + if(crc == 0x8c4588e2) + { + MDFN_printf("SuperGfx: 1941 - Counter Attack\n"); + IsSGX = 1; + } + if(crc == 0x1f041166) + { + MDFN_printf("SuperGfx: Madouou Granzort\n"); + IsSGX = 1; + } + if(crc == 0xb486a8ed) + { + MDFN_printf("SuperGfx: Daimakaimura\n"); + IsSGX = 1; + } + if(crc == 0x3b13af61) + { + MDFN_printf("SuperGfx: Battle Ace\n"); + IsSGX = 1; + } + + return(LoadCommon()); +} + +static void LoadCommonPre(void) +{ + // FIXME: Make these globals less global! + pce_overclocked = MDFN_GetSettingUI("pce_fast.ocmultiplier"); + PCE_ACEnabled = MDFN_GetSettingB("pce_fast.arcadecard"); + + if(pce_overclocked > 1) + MDFN_printf(_("CPU overclock: %dx\n"), pce_overclocked); + + if(MDFN_GetSettingUI("pce_fast.cdspeed") > 1) + MDFN_printf(_("CD-ROM speed: %ux\n"), (unsigned int)MDFN_GetSettingUI("pce_fast.cdspeed")); + + memset(HuCPUFastMap, 0, sizeof(HuCPUFastMap)); + for(int x = 0; x < 0x100; x++) + { + PCERead[x] = PCEBusRead; + PCEWrite[x] = PCENullWrite; + } + + MDFNMP_Init(1024, (1 << 21) / 1024); +} + +static int LoadCommon(void) +{ + IsSGX |= MDFN_GetSettingB("pce_fast.forcesgx") ? 1 : 0; + + if(IsHES) + IsSGX = 1; + // Don't modify IsSGX past this point. + + VDC_Init(IsSGX); + + if(IsSGX) + { + MDFN_printf("SuperGrafx Emulation Enabled.\n"); + PCERead[0xF8] = PCERead[0xF9] = PCERead[0xFA] = PCERead[0xFB] = BaseRAMReadSGX; + PCEWrite[0xF8] = PCEWrite[0xF9] = PCEWrite[0xFA] = PCEWrite[0xFB] = BaseRAMWriteSGX; + + for(int x = 0xf8; x < 0xfb; x++) + HuCPUFastMap[x] = BaseRAM - 0xf8 * 8192; + + PCERead[0xFF] = IOReadSGX; + } + else + { + PCERead[0xF8] = BaseRAMRead; + PCERead[0xF9] = PCERead[0xFA] = PCERead[0xFB] = BaseRAMRead_Mirrored; + + PCEWrite[0xF8] = BaseRAMWrite; + PCEWrite[0xF9] = PCEWrite[0xFA] = PCEWrite[0xFB] = BaseRAMWrite_Mirrored; + + for(int x = 0xf8; x < 0xfb; x++) + HuCPUFastMap[x] = BaseRAM - x * 8192; + + PCERead[0xFF] = IORead; + } + + MDFNMP_AddRAM(IsSGX ? 32768 : 8192, 0xf8 * 8192, BaseRAM); + + PCEWrite[0xFF] = IOWrite; + + HuC6280_Init(); + + psg = new PCEFast_PSG(&sbuf[0], &sbuf[1]); + + psg->SetVolume(1.0); + + if(PCE_IsCD) + { + unsigned int cdpsgvolume = MDFN_GetSettingUI("pce_fast.cdpsgvolume"); + + if(cdpsgvolume != 100) + { + MDFN_printf(_("CD PSG Volume: %d%%\n"), cdpsgvolume); + } + + psg->SetVolume(0.678 * cdpsgvolume / 100); + + } + + PCEINPUT_Init(); + + PCE_Power(); + + MDFNGameInfo->LayerNames = IsSGX ? "BG0\0SPR0\0BG1\0SPR1\0" : "Background\0Sprites\0"; + MDFNGameInfo->fps = (uint32)((double)7159090.90909090 / 455 / 263 * 65536 * 256); + + // Clean this up: + if(!MDFN_GetSettingB("pce_fast.correct_aspect")) + MDFNGameInfo->fb_width = 682; + + if(!IsHES) + { + MDFNGameInfo->nominal_width = MDFN_GetSettingB("pce_fast.correct_aspect") ? 288 : 341; + MDFNGameInfo->nominal_height = MDFN_GetSettingUI("pce_fast.slend") - MDFN_GetSettingUI("pce_fast.slstart") + 1; + + MDFNGameInfo->lcm_width = MDFN_GetSettingB("pce_fast.correct_aspect") ? 1024 : 341; + MDFNGameInfo->lcm_height = MDFNGameInfo->nominal_height; + } + + return(1); +} + +static bool TestMagicCD(std::vector *CDInterfaces) +{ + static const uint8 magic_test[0x20] = { 0x82, 0xB1, 0x82, 0xCC, 0x83, 0x76, 0x83, 0x8D, 0x83, 0x4F, 0x83, 0x89, 0x83, 0x80, 0x82, 0xCC, + 0x92, 0x98, 0x8D, 0xEC, 0x8C, 0xA0, 0x82, 0xCD, 0x8A, 0x94, 0x8E, 0xAE, 0x89, 0xEF, 0x8E, 0xD0 + }; + uint8 sector_buffer[2048]; + CDIF *cdiface = (*CDInterfaces)[0]; + CDUtility::TOC toc; + bool ret = FALSE; + + memset(sector_buffer, 0, sizeof(sector_buffer)); + + cdiface->ReadTOC(&toc); + + for(int32 track = toc.first_track; track <= toc.last_track; track++) + { + if(toc.tracks[track].control & 0x4) + { + cdiface->ReadSector(sector_buffer, toc.tracks[track].lba, 1); + + if(!memcmp((char*)sector_buffer, (char *)magic_test, 0x20)) + ret = TRUE; + + // PCE CD BIOS apparently only looks at the first data track. + break; + } + } + + + // If it's a PC-FX CD(Battle Heat), return false. + // This is very kludgy. + for(int32 track = toc.first_track; track <= toc.last_track; track++) + { + if(toc.tracks[track].control & 0x4) + { + cdiface->ReadSector(sector_buffer, toc.tracks[track].lba, 1); + if(!strncmp("PC-FX:Hu_CD-ROM", (char*)sector_buffer, strlen("PC-FX:Hu_CD-ROM"))) + { + return(false); + } + } + } + + + // 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)) + { + if(!memcmp((char *)sector_buffer + 0x8, "HACKER CD ROM SYSTEM", 0x14)) + { + ret = TRUE; + } + } + } + + return(ret); +} + +static int LoadCD(std::vector *CDInterfaces) +{ + std::string bios_path = MDFN_MakeFName(MDFNMKF_FIRMWARE, 0, MDFN_GetSettingS("pce_fast.cdbios").c_str() ); + + IsHES = 0; + IsSGX = 0; + + LoadCommonPre(); + + if(!HuCLoadCD(bios_path.c_str())) + return(0); + + PCECD_Drive_SetDisc(true, NULL, true); + PCECD_Drive_SetDisc(false, (*CDInterfaces)[0], true); + + return(LoadCommon()); +} + + +static void CloseGame(void) +{ +#ifdef HAVE_HES + if(IsHES) + HES_Close(); + else +#endif + { + HuCClose(); + } + VDC_Close(); + if(psg) + { + delete psg; + psg = NULL; + } +} + +static void Emulate(EmulateSpecStruct *espec) +{ + INPUT_Frame(); + + MDFNMP_ApplyPeriodicCheats(); + + #if 0 + { + static bool firstcat = true; + MDFN_PixelFormat nf; + + nf.bpp = 16; + nf.colorspace = MDFN_COLORSPACE_RGB; + nf.Rshift = 11; + nf.Gshift = 5; + nf.Bshift = 0; + nf.Ashift = 16; + + nf.Rprec = 5; + nf.Gprec = 6; + nf.Bprec = 5; + nf.Aprec = 8; + + espec->surface->SetFormat(nf, false); + espec->VideoFormatChanged = firstcat; + firstcat = false; + } + #endif + +#if 0 + static bool firstcat = true; + + MDFN_PixelFormat tmp_pf; + + tmp_pf.Rshift = 0; + tmp_pf.Gshift = 0; + tmp_pf.Bshift = 0; + tmp_pf.Ashift = 8; + + tmp_pf.Rprec = 0; + tmp_pf.Gprec = 0; + tmp_pf.Bprec = 0; + tmp_pf.Aprec = 0; + + tmp_pf.bpp = 8; + tmp_pf.colorspace = MDFN_COLORSPACE_RGB; + + espec->surface->SetFormat(tmp_pf, false); + espec->VideoFormatChanged = firstcat; + firstcat = false; +#endif + + if(espec->VideoFormatChanged) + VDC_SetPixelFormat(espec->surface->format); //.Rshift, espec->surface->format.Gshift, espec->surface->format.Bshift); + + if(espec->SoundFormatChanged) + { + for(int y = 0; y < 2; y++) + { + sbuf[y].set_sample_rate(espec->SoundRate ? espec->SoundRate : 44100, 50); + sbuf[y].clock_rate((long)(PCE_MASTER_CLOCK / 3)); + sbuf[y].bass_freq(10); + } + } + VDC_RunFrame(espec, IsHES); + + if(PCE_IsCD) + { + PCECD_Run(HuCPU.timestamp * 3); + } + + psg->EndFrame(HuCPU.timestamp / pce_overclocked); + + if(espec->SoundBuf) + { + for(int y = 0; y < 2; y++) + { + sbuf[y].end_frame(HuCPU.timestamp / pce_overclocked); + espec->SoundBufSize = sbuf[y].read_samples(espec->SoundBuf + y, espec->SoundBufMaxSize, 1); + } + } + + espec->MasterCycles = HuCPU.timestamp * 3; + + INPUT_FixTS(); + + HuC6280_ResetTS(); + + if(PCE_IsCD) + PCECD_ResetTS(); + +#ifdef HAVE_HES + if(IsHES && !espec->skip) + HES_Draw(espec->surface, &espec->DisplayRect, espec->SoundBuf, espec->SoundBufSize); +#endif +} + +static int StateAction(StateMem *sm, int load, int data_only) +{ + SFORMAT StateRegs[] = + { + SFARRAY(BaseRAM, IsSGX? 32768 : 8192), + SFVAR(PCEIODataBuffer), + SFEND + }; + + //for(int i = 8192; i < 32768; i++) + // if(BaseRAM[i] != 0xFF) + // printf("%d %02x\n", i, BaseRAM[i]); + + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "MAIN"); + + ret &= HuC6280_StateAction(sm, load, data_only); + ret &= VDC_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); + + if(load) + { + + } + + return(ret); +} + +void PCE_Power(void) +{ + memset(BaseRAM, 0x00, sizeof(BaseRAM)); + + if(!IsSGX) + for(int i = 8192; i < 32768; i++) + BaseRAM[i] = 0xFF; + + PCEIODataBuffer = 0xFF; + +#ifdef HAVE_HES + if(IsHES) + HES_Reset(); +#endif + + HuC6280_Power(); + VDC_Power(); + psg->Power(HuCPU.timestamp / pce_overclocked); + HuC_Power(); + + if(PCE_IsCD) + { + PCECD_Power(HuCPU.timestamp * 3); + } +} + +static void DoSimpleCommand(int cmd) +{ + switch(cmd) + { + case MDFN_MSC_RESET: PCE_Power(); break; + case MDFN_MSC_POWER: PCE_Power(); break; + } +} + +static MDFNSetting PCESettings[] = +{ + { "pce_fast.correct_aspect", MDFNSF_CAT_VIDEO, gettext_noop("Correct the aspect ratio."), NULL, MDFNST_BOOL, "1" }, + { "pce_fast.slstart", MDFNSF_NOFLAGS, gettext_noop("First rendered scanline."), NULL, MDFNST_UINT, "4", "0", "239" }, + { "pce_fast.slend", MDFNSF_NOFLAGS, gettext_noop("Last rendered scanline."), NULL, MDFNST_UINT, "235", "0", "239" }, + { "pce_fast.mouse_sensitivity", MDFNSF_NOFLAGS, gettext_noop("Mouse sensitivity."), NULL, MDFNST_FLOAT, "0.50", NULL, NULL, NULL, PCEINPUT_SettingChanged }, + { "pce_fast.disable_softreset", MDFNSF_NOFLAGS, gettext_noop("If set, when RUN+SEL are pressed simultaneously, disable both buttons temporarily."), NULL, MDFNST_BOOL, "0", NULL, NULL, NULL, PCEINPUT_SettingChanged }, + { "pce_fast.forcesgx", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Force SuperGrafx emulation."), NULL, MDFNST_BOOL, "0" }, + { "pce_fast.arcadecard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable Arcade Card emulation."), NULL, MDFNST_BOOL, "1" }, + { "pce_fast.ocmultiplier", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("CPU overclock multiplier."), NULL, MDFNST_UINT, "1", "1", "100"}, + { "pce_fast.cdspeed", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("CD-ROM data transfer speed multiplier."), NULL, MDFNST_UINT, "1", "1", "100" }, + { "pce_fast.nospritelimit", MDFNSF_NOFLAGS, gettext_noop("Remove 16-sprites-per-scanline hardware limit."), NULL, MDFNST_BOOL, "0" }, + + { "pce_fast.cdbios", MDFNSF_EMU_STATE, gettext_noop("Path to the CD BIOS"), NULL, MDFNST_STRING, "syscard3.pce" }, + + { "pce_fast.adpcmlp", MDFNSF_NOFLAGS, gettext_noop("Enable dynamic ADPCM lowpass filter."), NULL, MDFNST_BOOL, "0" }, + { "pce_fast.cdpsgvolume", MDFNSF_NOFLAGS, gettext_noop("PSG volume when playing a CD game."), NULL, MDFNST_UINT, "100", "0", "200" }, + { "pce_fast.cddavolume", MDFNSF_NOFLAGS, gettext_noop("CD-DA volume."), NULL, MDFNST_UINT, "100", "0", "200" }, + { "pce_fast.adpcmvolume", MDFNSF_NOFLAGS, gettext_noop("ADPCM volume."), NULL, MDFNST_UINT, "100", "0", "200" }, + { NULL } +}; + +static uint8 MemRead(uint32 addr) +{ + return(PCERead[(addr / 8192) & 0xFF](addr)); +} + +static const FileExtensionSpecStruct KnownExtensions[] = +{ + { ".pce", gettext_noop("PC Engine ROM Image") }, + { ".hes", gettext_noop("PC Engine Music Rip") }, + { ".sgx", gettext_noop("SuperGrafx ROM Image") }, + { NULL, NULL } +}; + +}; + +MDFNGI EmulatedPCE_Fast = +{ + "pce_fast", + "PC Engine (CD)/TurboGrafx 16 (CD)/SuperGrafx", + KnownExtensions, + MODPRIO_INTERNAL_LOW, + NULL, + &PCEInputInfo, + Load, + TestMagic, + LoadCD, + TestMagicCD, + CloseGame, + VDC_SetLayerEnableMask, + NULL, + NULL, + NULL, + NULL, + NULL, + MemRead, + NULL, + false, + StateAction, + Emulate, + PCEINPUT_SetInput, + DoSimpleCommand, + PCESettings, + MDFN_MASTERCLOCK_FIXED(PCE_MASTER_CLOCK), + 0, + + true, // Multires possible? + + 0, // lcm_width + 0, // lcm_height + NULL, // Dummy + + 288, // Nominal width + 232, // Nominal height + + 512, // Framebuffer width + 242, // Framebuffer height + + 2, // Number of output sound channels +}; + diff --git a/mednafen/pce_fast-09333/pce.h b/mednafen/pce_fast-09333/pce.h new file mode 100644 index 0000000..614a08a --- /dev/null +++ b/mednafen/pce_fast-09333/pce.h @@ -0,0 +1,62 @@ +#ifndef _PCE_H +#define _PCE_H + +#include "../mednafen-types.h" +#include "../mednafen.h" +#include "../state.h" +#include "../general.h" +#include "../mednafen-memory.h" + +#ifdef __LIBRETRO__ +#define GET_FDATA(fp) (fp.f_data) +#define GET_FSIZE(fp) (fp.f_size) +#define GET_FEXTS(fp) (fp.f_ext) +#define GET_FDATA_PTR(fp) (fp->f_data) +#define GET_FSIZE_PTR(fp) (fp->f_size) +#define GET_FEXTS_PTR(fp) (fp->f_ext) +#define gzread(a, b, c) fread(b, c, 1, a) +#else +#define GET_FDATA(fp) (fp.Data()) +#define GET_FSIZE(fp) (fp.Size()) +#define GET_FDATA_PTR(fp) (fp->data) +#define GET_FSIZE_PTR(fp) (fp->size) +#define GET_FEXTS_PTR(fp) (fp->ext) +#define gzread(a, b, c) gzread(a, b, c) +#endif + +#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) + +namespace PCE_Fast +{ +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; + +}; + +#include "huc6280.h" + +namespace PCE_Fast +{ +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]; + +}; + +using namespace PCE_Fast; + +#endif diff --git a/mednafen/pce_fast-09333/pcecd.cpp b/mednafen/pce_fast-09333/pcecd.cpp new file mode 100644 index 0000000..55045ea --- /dev/null +++ b/mednafen/pce_fast-09333/pcecd.cpp @@ -0,0 +1,984 @@ +/* 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" + +namespace PCE_Fast +{ + +//#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 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 +#define ADPCM_DEBUG(x, ...) { /*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 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 void RedoLPF(int f) +{ + if(sbuf[0] && sbuf[1]) + { + if(ADPCMLP) + { + if(f >= 14) + { + int rolloff = (int)((((double)32087.5 / (16 - f)) / 2) * 0.70); + ADPCMSynth.treble_eq( blip_eq_t(-1000, rolloff, sbuf[0]->sample_rate())); + } + else + { + int rolloff = (int)((((double)32087.5 / (16 - f)) / 2) * 0.80); + ADPCMSynth.treble_eq( blip_eq_t(-1000, rolloff, sbuf[0]->sample_rate())); + } + } + else + ADPCMSynth.treble_eq(-8.0); + } +} + +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(); + + ADPCMSynth.volume(0.42735f * (settings ? settings->ADPCM_Volume : 1.0)); + 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 *)MDFN_malloc(0x10000, _("PCE ADPCM RAM")))) + { + 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) + { + MDFN_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); + RedoLPF(ADPCM.LPF_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.CanRead() > 0) + ret = SubChannelFIFO.ReadByte(); + else + ret = 0x00; // Not sure if it's 0, 0xFF, the last byte read, or something else. + + if(SubChannelFIFO.CanRead() == 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]) + { + ADPCMSynth.offset(synthtime, pcm - ADPCM.last_pcm, sbuf[0]); + ADPCMSynth.offset(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) +{ + if(ADPCM.SampleFreq != ADPCM.LPF_SampleFreq) + { + ADPCM.LPF_SampleFreq = ADPCM.SampleFreq; + RedoLPF(ADPCM.LPF_SampleFreq); + } + 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"); + if(load) + { + MSM5205.SetSample(ad_sample); + MSM5205.SetSSI(ad_ref_index); + RedoLPF(ADPCM.SampleFreq); + } + 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.data.size()), + SFVAR(SubChannelFIFO.read_pos), + SFVAR(SubChannelFIFO.write_pos), + SFVAR(SubChannelFIFO.in_count), + + SFEND + }; + + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "PECD"); + 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); +} + +} diff --git a/mednafen/pce_fast-09333/pcecd.h b/mednafen/pce_fast-09333/pcecd.h new file mode 100644 index 0000000..4a5cfff --- /dev/null +++ b/mednafen/pce_fast-09333/pcecd.h @@ -0,0 +1,37 @@ +#ifndef __PCE_CDROM_H +#define __PCE_CDROM_H + +#include + +namespace PCE_Fast +{ + +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 + diff --git a/mednafen/pce_fast-09333/pcecd_drive.cpp b/mednafen/pce_fast-09333/pcecd_drive.cpp new file mode 100644 index 0000000..bcc0839 --- /dev/null +++ b/mednafen/pce_fast-09333/pcecd_drive.cpp @@ -0,0 +1,1367 @@ +/* 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 "../clamp.h" +#include +#include +#include "pcecd_drive.h" +#include "../cdrom/cdromif.h" +#include "../cdrom/SimpleFIFO.h" + +namespace PCE_Fast +{ +#define SCSIDBG(format, ...) { printf("SCSI: " format "\n", ## __VA_ARGS__); } +//#define SCSIDBG(format, ...) { } + +using namespace CDUtility; + +static uint32 CD_DATA_TRANSFER_RATE; +static uint32 System_Clock; +static void (*CDIRQCallback)(int); +static void (*CDStuffSubchannels)(uint8, int); +static Blip_Buffer *sbuf[2]; +static CDIF *Cur_CDIF; + +// Internal operation to the SCSI CD unit. Only pass 1 or 0 to these macros! +#define SetIOP(mask, set) { cd_bus.signals &= ~mask; if(set) cd_bus.signals |= mask; } + +#define SetBSY(set) SetIOP(PCECD_Drive_BSY_mask, set) +#define SetIO(set) SetIOP(PCECD_Drive_IO_mask, set) +#define SetCD(set) SetIOP(PCECD_Drive_CD_mask, set) +#define SetMSG(set) SetIOP(PCECD_Drive_MSG_mask, set) + +static INLINE void SetREQ(bool set) +{ + if(set && !REQ_signal) + CDIRQCallback(PCECD_Drive_IRQ_MAGICAL_REQ); + + SetIOP(PCECD_Drive_REQ_mask, set); +} + +#define SetkingACK(set) SetIOP(PCECD_Drive_kingACK_mask, set) +#define SetkingRST(set) SetIOP(PCECD_Drive_kingRST_mask, set) +#define SetkingSEL(set) SetIOP(PCECD_Drive_kingSEL_mask, set) + + +enum +{ + QMode_Zero = 0, + QMode_Time = 1, + QMode_MCN = 2, // Media Catalog Number + QMode_ISRC = 3 // International Standard Recording Code +}; + +typedef struct +{ + bool last_RST_signal; + + // The pending message to send(in the message phase) + uint8 message_pending; + + bool status_sent, message_sent; + + // Pending error codes + uint8 key_pending, asc_pending, ascq_pending, fru_pending; + + uint8 command_buffer[256]; + uint8 command_buffer_pos; + uint8 command_size_left; + + // FALSE if not all pending data is in the FIFO, TRUE if it is. + // Used for multiple sector CD reads. + bool data_transfer_done; + + bool TrayOpen; + bool DiscChanged; + + uint8 SubQBuf[4][0xC]; // One for each of the 4 most recent q-Modes. + uint8 SubQBuf_Last[0xC]; // The most recent q subchannel data, regardless of q-mode. + + uint8 SubPWBuf[96]; + +} pcecd_drive_t; + +typedef Blip_Synth < blip_good_quality, 1> CDSynth; + +enum +{ + CDDASTATUS_PAUSED = -1, + CDDASTATUS_STOPPED = 0, + CDDASTATUS_PLAYING = 1, +}; + +enum +{ + PLAYMODE_SILENT = 0x00, + PLAYMODE_NORMAL, + PLAYMODE_INTERRUPT, + PLAYMODE_LOOP, +}; + +typedef struct +{ + int32 CDDADivAcc; + uint32 scan_sec_end; + + uint8 PlayMode; + CDSynth CDDASynth; + int32 CDDAVolume; + int16 last_sample[2]; + int16 CDDASectorBuffer[1176]; + uint32 CDDAReadPos; + + int8 CDDAStatus; + uint8 ScanMode; + int32 CDDADiv; + int CDDATimeDiv; +} cdda_t; + +static INLINE void MakeSense(uint8 target[18], uint8 key, uint8 asc, uint8 ascq, uint8 fru) +{ + memset(target, 0, 18); + + target[0] = 0x70; // Current errors and sense data is not SCSI compliant + target[2] = key; + target[7] = 0x0A; + target[12] = asc; // Additional Sense Code + target[13] = ascq; // Additional Sense Code Qualifier + target[14] = fru; // Field Replaceable Unit code +} + +static void (*SCSILog)(const char *, const char *format, ...); + +static pcecd_drive_timestamp_t lastts; +static int64 monotonic_timestamp; +static int64 pce_lastsapsp_timestamp; + +static pcecd_drive_t cd; +pcecd_drive_bus_t cd_bus; +static cdda_t cdda; + +static SimpleFIFO din(2048); + +static CDUtility::TOC toc; + +static uint32 read_sec_start; +static uint32 read_sec; +static uint32 read_sec_end; + +static int32 CDReadTimer; +static uint32 SectorAddr; +static uint32 SectorCount; + + +enum +{ + PHASE_BUS_FREE = 0, + PHASE_COMMAND, + PHASE_DATA_IN, + PHASE_STATUS, + PHASE_MESSAGE_IN, +}; + +static unsigned int CurrentPhase; +static void ChangePhase(const unsigned int new_phase); + +static void VirtualReset(void) +{ + din.Flush(); + + cdda.CDDADivAcc = (int64)System_Clock * 65536 / 44100; + CDReadTimer = 0; + + pce_lastsapsp_timestamp = monotonic_timestamp; + + SectorAddr = SectorCount = 0; + read_sec_start = read_sec = 0; + read_sec_end = ~0; + + cdda.PlayMode = PLAYMODE_SILENT; + cdda.CDDAReadPos = 0; + cdda.CDDAStatus = CDDASTATUS_STOPPED; + cdda.CDDADiv = 0; + + cdda.ScanMode = 0; + cdda.scan_sec_end = 0; + + ChangePhase(PHASE_BUS_FREE); +} + +void PCECD_Drive_Power(pcecd_drive_timestamp_t system_timestamp) +{ + memset(&cd, 0, sizeof(pcecd_drive_t)); + memset(&cd_bus, 0, sizeof(pcecd_drive_bus_t)); + + monotonic_timestamp = system_timestamp; + + cd.DiscChanged = false; + + if(Cur_CDIF && !cd.TrayOpen) + Cur_CDIF->ReadTOC(&toc); + + CurrentPhase = PHASE_BUS_FREE; + + VirtualReset(); +} + + +void PCECD_Drive_SetDB(uint8 data) +{ + cd_bus.DB = data; + //printf("Set DB: %02x\n", data); +} + +void PCECD_Drive_SetACK(bool set) +{ + SetkingACK(set); + //printf("Set ACK: %d\n", set); +} + +void PCECD_Drive_SetSEL(bool set) +{ + SetkingSEL(set); + //printf("Set SEL: %d\n", set); +} + +void PCECD_Drive_SetRST(bool set) +{ + SetkingRST(set); + //printf("Set RST: %d\n", set); +} + +static void GenSubQFromSubPW(void) +{ + uint8 SubQBuf[0xC]; + + subq_deinterleave(cd.SubPWBuf, SubQBuf); + + //printf("Real %d/ SubQ %d - ", read_sec, BCD_to_U8(SubQBuf[7]) * 75 * 60 + BCD_to_U8(SubQBuf[8]) * 75 + BCD_to_U8(SubQBuf[9]) - 150); + // Debug code, remove me. + //for(int i = 0; i < 0xC; i++) + // printf("%02x ", SubQBuf[i]); + //printf("\n"); + + if(!subq_check_checksum(SubQBuf)) + { + SCSIDBG("SubQ checksum error!"); + } + else + { + memcpy(cd.SubQBuf_Last, SubQBuf, 0xC); + + uint8 adr = SubQBuf[0] & 0xF; + + if(adr <= 0x3) + memcpy(cd.SubQBuf[adr], SubQBuf, 0xC); + + //if(adr == 0x02) + //for(int i = 0; i < 12; i++) + // printf("%02x\n", cd.SubQBuf[0x2][i]); + } +} + + +#define STATUS_GOOD 0 +#define STATUS_CHECK_CONDITION 1 + +#define SENSEKEY_NO_SENSE 0x0 +#define SENSEKEY_NOT_READY 0x2 +#define SENSEKEY_MEDIUM_ERROR 0x3 +#define SENSEKEY_HARDWARE_ERROR 0x4 +#define SENSEKEY_ILLEGAL_REQUEST 0x5 +#define SENSEKEY_UNIT_ATTENTION 0x6 +#define SENSEKEY_ABORTED_COMMAND 0xB + +#define ASC_MEDIUM_NOT_PRESENT 0x3A + + +// NEC sub-errors(ASC), no ASCQ. +#define NSE_NO_DISC 0x0B // Used with SENSEKEY_NOT_READY - This condition occurs when tray is closed with no disc present. +#define NSE_TRAY_OPEN 0x0D // Used with SENSEKEY_NOT_READY +#define NSE_SEEK_ERROR 0x15 +#define NSE_HEADER_READ_ERROR 0x16 // Used with SENSEKEY_MEDIUM_ERROR +#define NSE_NOT_AUDIO_TRACK 0x1C // Used with SENSEKEY_MEDIUM_ERROR +#define NSE_NOT_DATA_TRACK 0x1D // Used with SENSEKEY_MEDIUM_ERROR +#define NSE_INVALID_COMMAND 0x20 +#define NSE_INVALID_ADDRESS 0x21 +#define NSE_INVALID_PARAMETER 0x22 +#define NSE_END_OF_VOLUME 0x25 +#define NSE_INVALID_REQUEST_IN_CDB 0x27 +#define NSE_DISC_CHANGED 0x28 // Used with SENSEKEY_UNIT_ATTENTION +#define NSE_AUDIO_NOT_PLAYING 0x2C + +// ASC, ASCQ pair +#define AP_UNRECOVERED_READ_ERROR 0x11, 0x00 +#define AP_LEC_UNCORRECTABLE_ERROR 0x11, 0x05 +#define AP_CIRC_UNRECOVERED_ERROR 0x11, 0x06 + +#define AP_UNKNOWN_MEDIUM_FORMAT 0x30, 0x01 +#define AP_INCOMPAT_MEDIUM_FORMAT 0x30, 0x02 + +static void ChangePhase(const unsigned int new_phase) +{ + //printf("New phase: %d %lld\n", new_phase, monotonic_timestamp); + switch(new_phase) + { + case PHASE_BUS_FREE: + SetBSY(false); + SetMSG(false); + SetCD(false); + SetIO(false); + SetREQ(false); + + CDIRQCallback(0x8000 | PCECD_Drive_IRQ_DATA_TRANSFER_DONE); + break; + + case PHASE_DATA_IN: // Us to them + SetBSY(true); + SetMSG(false); + SetCD(false); + SetIO(true); + //SetREQ(true); + SetREQ(false); + break; + + case PHASE_STATUS: // Us to them + SetBSY(true); + SetMSG(false); + SetCD(true); + SetIO(true); + SetREQ(true); + break; + + case PHASE_MESSAGE_IN: // Us to them + SetBSY(true); + SetMSG(true); + SetCD(true); + SetIO(true); + SetREQ(true); + break; + + case PHASE_COMMAND: // Them to us + SetBSY(true); + SetMSG(false); + SetCD(true); + SetIO(false); + SetREQ(true); + break; + } + CurrentPhase = new_phase; +} + +static void SendStatusAndMessage(uint8 status, uint8 message) +{ + // This should never ever happen, but that doesn't mean it won't. ;) + if(din.CanRead()) + { + printf("BUG: %d bytes still in SCSI CD FIFO\n", din.CanRead()); + din.Flush(); + } + + cd.message_pending = message; + + cd.status_sent = FALSE; + cd.message_sent = FALSE; + + + if(status == STATUS_GOOD) + cd_bus.DB = 0x00; + else + cd_bus.DB = 0x01; + + + ChangePhase(PHASE_STATUS); +} + +static void DoSimpleDataIn(const uint8 *data_in, uint32 len) +{ + din.Write(data_in, len); + + cd.data_transfer_done = true; + + ChangePhase(PHASE_DATA_IN); +} + +void PCECD_Drive_SetDisc(bool tray_open, CDIF *cdif, bool no_emu_side_effects) +{ + Cur_CDIF = cdif; + + // Closing the tray. + if(cd.TrayOpen && !tray_open) + { + cd.TrayOpen = false; + + if(cdif) + { + cdif->ReadTOC(&toc); + + if(!no_emu_side_effects) + { + memset(cd.SubQBuf, 0, sizeof(cd.SubQBuf)); + memset(cd.SubQBuf_Last, 0, sizeof(cd.SubQBuf_Last)); + cd.DiscChanged = true; + } + } + } + else if(!cd.TrayOpen && tray_open) // Opening the tray + { + cd.TrayOpen = true; + } +} + +static void CommandCCError(int key, int asc = 0, int ascq = 0) +{ + printf("CC Error: %02x %02x %02x\n", key, asc, ascq); + + cd.key_pending = key; + cd.asc_pending = asc; + cd.ascq_pending = ascq; + cd.fru_pending = 0x00; + + SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00); +} + +static bool ValidateRawDataSector(uint8 *data, const uint32 lba) +{ + if(!edc_lec_check_and_correct(data, false)) + { + MDFN_DispMessage(_("Uncorrectable data at sector %u"), lba); + MDFN_PrintError(_("Uncorrectable data at sector %u"), lba); + + din.Flush(); + cd.data_transfer_done = false; + + CommandCCError(SENSEKEY_MEDIUM_ERROR, AP_LEC_UNCORRECTABLE_ERROR); + return(false); + } + + return(true); +} + +static void DoREADBase(uint32 sa, uint32 sc) +{ + int track; + + if(sa > toc.tracks[100].lba) // Another one of those off-by-one PC-FX CD bugs. + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + return; + } + + if((track = toc.FindTrackByLBA(sa)) == 0) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + return; + } + + if(!(toc.tracks[track].control) & 0x4) + { + CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_NOT_DATA_TRACK); + return; + } + + // Case for READ(10) and READ(12) where sc == 0, and sa == toc.tracks[100].lba + if(!sc && sa == toc.tracks[100].lba) + { + CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_HEADER_READ_ERROR); + return; + } + + if(SCSILog) + { + int Track = toc.FindTrackByLBA(sa); + uint32 Offset = sa - toc.tracks[Track].lba; //Cur_CDIF->GetTrackStartPositionLBA(Track); + SCSILog("SCSI", "Read: start=0x%08x(track=%d, offs=0x%08x), cnt=0x%08x", sa, Track, Offset, sc); + } + + SectorAddr = sa; + SectorCount = sc; + if(SectorCount) + { + Cur_CDIF->HintReadSector(sa); //, sa + sc); + + CDReadTimer = (uint64)1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE; + } + else + { + CDReadTimer = 0; + SendStatusAndMessage(STATUS_GOOD, 0x00); + } + cdda.CDDAStatus = CDDASTATUS_STOPPED; +} + +// +// +// +static void DoTESTUNITREADY(const uint8 *cdb) +{ + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + +static void DoREQUESTSENSE(const uint8 *cdb) +{ + uint8 data_in[18]; + + MakeSense(data_in, cd.key_pending, cd.asc_pending, cd.ascq_pending, cd.fru_pending); + + DoSimpleDataIn(data_in, 18); + + cd.key_pending = 0; + cd.asc_pending = 0; + cd.ascq_pending = 0; + cd.fru_pending = 0; +} + +/******************************************************** +* * +* SCSI Command 0x08 - READ(6) * +* * +********************************************************/ +static void DoREAD6(const uint8 *cdb) +{ + uint32 sa = ((cdb[1] & 0x1F) << 16) | (cdb[2] << 8) | (cdb[3] << 0); + uint32 sc = cdb[4]; + + // TODO: confirm real PCE does this(PC-FX does at least). + if(!sc) + { + SCSIDBG("READ(6) with count == 0.\n"); + sc = 256; + } + + DoREADBase(sa, sc); +} + +/******************************************************** +* * +* PC Engine CD Command 0xD8 - SAPSP * +* * +********************************************************/ +static void DoNEC_PCE_SAPSP(const uint8 *cdb) +{ + uint32 new_read_sec_start; + + //printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]); + switch (cdb[9] & 0xc0) + { + default: SCSIDBG("Unknown SAPSP 9: %02x\n", cdb[9]); + case 0x00: + new_read_sec_start = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; + break; + + case 0x40: + new_read_sec_start = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4])); + break; + + case 0x80: + { + int track = BCD_to_U8(cdb[2]); + + if(!track) + track = 1; + else if(track >= toc.last_track + 1) + track = 100; + new_read_sec_start = toc.tracks[track].lba; + } + break; + } + + //printf("%lld\n", (long long)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock); + if(cdda.CDDAStatus == CDDASTATUS_PLAYING && new_read_sec_start == read_sec_start && ((int64)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock) < 190) + { + pce_lastsapsp_timestamp = monotonic_timestamp; + + SendStatusAndMessage(STATUS_GOOD, 0x00); + CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE); + return; + } + + pce_lastsapsp_timestamp = monotonic_timestamp; + + read_sec = read_sec_start = new_read_sec_start; + read_sec_end = toc.tracks[100].lba; + + + cdda.CDDAReadPos = 588; + + cdda.CDDAStatus = CDDASTATUS_PAUSED; + cdda.PlayMode = PLAYMODE_SILENT; + + if(cdb[1]) + { + cdda.PlayMode = PLAYMODE_NORMAL; + cdda.CDDAStatus = CDDASTATUS_PLAYING; + } + + if(read_sec < toc.tracks[100].lba) + Cur_CDIF->HintReadSector(read_sec); + + SendStatusAndMessage(STATUS_GOOD, 0x00); + CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE); +} + + + +/******************************************************** +* * +* PC Engine CD Command 0xD9 - SAPEP * +* * +********************************************************/ +static void DoNEC_PCE_SAPEP(const uint8 *cdb) +{ + uint32 new_read_sec_end; + + //printf("Set audio end: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]); + + switch (cdb[9] & 0xc0) + { + default: SCSIDBG("Unknown SAPEP 9: %02x\n", cdb[9]); + + case 0x00: + new_read_sec_end = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; + break; + + case 0x40: + new_read_sec_end = BCD_to_U8(cdb[4]) + 75 * (BCD_to_U8(cdb[3]) + 60 * BCD_to_U8(cdb[2])); + new_read_sec_end -= 150; + break; + + case 0x80: + { + int track = BCD_to_U8(cdb[2]); + + if(!track) + track = 1; + else if(track >= toc.last_track + 1) + track = 100; + new_read_sec_end = toc.tracks[track].lba; + } + break; + } + + read_sec_end = new_read_sec_end; + + switch(cdb[1]) // PCE CD(TODO: Confirm these, and check the mode mask): + { + default: + case 0x03: cdda.PlayMode = PLAYMODE_NORMAL; + cdda.CDDAStatus = CDDASTATUS_PLAYING; + break; + + case 0x02: cdda.PlayMode = PLAYMODE_INTERRUPT; + cdda.CDDAStatus = CDDASTATUS_PLAYING; + break; + + case 0x01: cdda.PlayMode = PLAYMODE_LOOP; + cdda.CDDAStatus = CDDASTATUS_PLAYING; + break; + + case 0x00: cdda.PlayMode = PLAYMODE_SILENT; + cdda.CDDAStatus = CDDASTATUS_STOPPED; + break; + } + + SendStatusAndMessage(STATUS_GOOD, 0x00); +} + + + +/******************************************************** +* * +* PC Engine CD Command 0xDA - Pause * +* * +********************************************************/ +static void DoNEC_PCE_PAUSE(const uint8 *cdb) +{ + if(cdda.CDDAStatus != CDDASTATUS_STOPPED) // Hmm, should we give an error if it tries to pause and it's already paused? + { + cdda.CDDAStatus = CDDASTATUS_PAUSED; + SendStatusAndMessage(STATUS_GOOD, 0x00); + } + else // Definitely give an error if it tries to pause when no track is playing! + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING); + } +} + + + +/******************************************************** +* * +* PC Engine CD Command 0xDD - Read Subchannel Q * +* * +********************************************************/ +static void DoNEC_PCE_READSUBQ(const uint8 *cdb) +{ + uint8 *SubQBuf = cd.SubQBuf[QMode_Time]; + uint8 data_in[8192]; + + memset(data_in, 0x00, 10); + + data_in[2] = SubQBuf[1]; // Track + data_in[3] = SubQBuf[2]; // Index + data_in[4] = SubQBuf[3]; // M(rel) + data_in[5] = SubQBuf[4]; // S(rel) + data_in[6] = SubQBuf[5]; // F(rel) + data_in[7] = SubQBuf[7]; // M(abs) + data_in[8] = SubQBuf[8]; // S(abs) + data_in[9] = SubQBuf[9]; // F(abs) + + if(cdda.CDDAStatus == CDDASTATUS_PAUSED) + data_in[0] = 2; // Pause + else if(cdda.CDDAStatus == CDDASTATUS_PLAYING) + data_in[0] = 0; // Playing + else + data_in[0] = 3; // Stopped + + DoSimpleDataIn(data_in, 10); +} + + + +/******************************************************** +* * +* PC Engine CD Command 0xDE - Get Directory Info * +* * +********************************************************/ +static void DoNEC_PCE_GETDIRINFO(const uint8 *cdb) +{ + // Problems: + // Returned data lengths on real PCE are not confirmed. + // Mode 0x03 behavior not tested on real PCE + + uint8 data_in[2048]; + uint32 data_in_size = 0; + + memset(data_in, 0, sizeof(data_in)); + + switch(cdb[1]) + { + default: MDFN_DispMessage("Unknown GETDIRINFO Mode: %02x", cdb[1]); + printf("Unknown GETDIRINFO Mode: %02x", cdb[1]); + case 0x0: + data_in[0] = U8_to_BCD(toc.first_track); + data_in[1] = U8_to_BCD(toc.last_track); + + data_in_size = 2; + break; + + case 0x1: + { + uint8 m, s, f; + + LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f); + + data_in[0] = U8_to_BCD(m); + data_in[1] = U8_to_BCD(s); + data_in[2] = U8_to_BCD(f); + + data_in_size = 3; + } + break; + + case 0x2: + { + uint8 m, s, f; + int track = BCD_to_U8(cdb[2]); + + if(!track) + track = 1; + else if(cdb[2] == 0xAA) + { + track = 100; + } + else if(track > 99) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER); + return; + } + + LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f); + + data_in[0] = U8_to_BCD(m); + data_in[1] = U8_to_BCD(s); + data_in[2] = U8_to_BCD(f); + data_in[3] = toc.tracks[track].control; + data_in_size = 4; + } + break; + } + + DoSimpleDataIn(data_in, data_in_size); +} + +#define SCF_REQUIRES_MEDIUM 0x01 + +typedef struct +{ + uint8 cmd; + uint8 flags; + void (*func)(const uint8 *cdb); + const char *pretty_name; + const char *format_string; +} SCSICH; + +static const uint8 RequiredCDBLen[16] = +{ + 6, // 0x0n + 6, // 0x1n + 10, // 0x2n + 10, // 0x3n + 10, // 0x4n + 10, // 0x5n + 10, // 0x6n + 10, // 0x7n + 10, // 0x8n + 10, // 0x9n + 12, // 0xAn + 12, // 0xBn + 10, // 0xCn + 10, // 0xDn + 10, // 0xEn + 10, // 0xFn +}; + +static SCSICH PCECommandDefs[] = +{ + { 0x00, SCF_REQUIRES_MEDIUM, DoTESTUNITREADY, "Test Unit Ready" }, + { 0x03, 0, DoREQUESTSENSE, "Request Sense" }, + { 0x08, SCF_REQUIRES_MEDIUM, DoREAD6, "Read(6)" }, + + { 0xD8, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPSP, "Set Audio Playback Start Position" }, + { 0xD9, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPEP, "Set Audio Playback End Position" }, + { 0xDA, SCF_REQUIRES_MEDIUM, DoNEC_PCE_PAUSE, "Pause" }, + { 0xDD, SCF_REQUIRES_MEDIUM, DoNEC_PCE_READSUBQ, "Read Subchannel Q" }, + { 0xDE, SCF_REQUIRES_MEDIUM, DoNEC_PCE_GETDIRINFO, "Get Dir Info" }, + + { 0xFF, 0, 0, NULL, NULL }, +}; + +void PCECD_Drive_ResetTS(void) +{ + lastts = 0; +} + +void PCECD_Drive_GetCDDAValues(int16 &left, int16 &right) +{ + if(cdda.CDDAStatus) + { + left = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2]; + right = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + 1]; + } + else + left = right = 0; +} + +static INLINE void RunCDDA(uint32 system_timestamp, int32 run_time) +{ + if(cdda.CDDAStatus == CDDASTATUS_PLAYING) + { + int32 sample[2]; + + cdda.CDDADiv -= run_time << 16; + + while(cdda.CDDADiv <= 0) + { + const uint32 synthtime = ((system_timestamp + (cdda.CDDADiv >> 16))) / cdda.CDDATimeDiv; + + cdda.CDDADiv += cdda.CDDADivAcc; + + //MDFN_DispMessage("%d %d %d\n", read_sec_start, read_sec, read_sec_end); + + if(cdda.CDDAReadPos == 588) + { + if(read_sec >= read_sec_end) + { + switch(cdda.PlayMode) + { + case PLAYMODE_SILENT: + case PLAYMODE_NORMAL: + cdda.CDDAStatus = CDDASTATUS_STOPPED; + break; + + case PLAYMODE_INTERRUPT: + cdda.CDDAStatus = CDDASTATUS_STOPPED; + CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE); + break; + + case PLAYMODE_LOOP: + read_sec = read_sec_start; + break; + } + + // If CDDA playback is stopped, break out of our while(CDDADiv ...) loop and don't play any more sound! + if(cdda.CDDAStatus == CDDASTATUS_STOPPED) + break; + } + + // Don't play past the user area of the disc. + if(read_sec >= toc.tracks[100].lba) + { + cdda.CDDAStatus = CDDASTATUS_STOPPED; + break; + } + + if(cd.TrayOpen) + { + cdda.CDDAStatus = CDDASTATUS_STOPPED; + + #if 0 + cd.data_transfer_done = FALSE; + cd.key_pending = SENSEKEY_NOT_READY; + cd.asc_pending = ASC_MEDIUM_NOT_PRESENT; + cd.ascq_pending = 0x00; + cd.fru_pending = 0x00; + SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00); + #endif + + break; + } + + + cdda.CDDAReadPos = 0; + + { + uint8 tmpbuf[2352 + 96]; + + Cur_CDIF->ReadRawSector(tmpbuf, read_sec); //, read_sec_end, read_sec_start); + + for(int i = 0; i < 588 * 2; i++) + cdda.CDDASectorBuffer[i] = MDFN_de16lsb(&tmpbuf[i * 2]); + + memcpy(cd.SubPWBuf, tmpbuf + 2352, 96); + } + GenSubQFromSubPW(); + read_sec++; + } // End if(CDDAReadPos == 588) + + // If the last valid sub-Q data decoded indicate that the corresponding sector is a data sector, don't output the + // current sector as audio. + sample[0] = sample[1] = 0; + + if(!(cd.SubQBuf_Last[0] & 0x40) && cdda.PlayMode != PLAYMODE_SILENT) + { + sample[0] = (cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + 0] * cdda.CDDAVolume) >> 16; + sample[1] = (cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + 1] * cdda.CDDAVolume) >> 16; + } + + if(!(cdda.CDDAReadPos % 6)) + { + int subindex = cdda.CDDAReadPos / 6 - 2; + + if(subindex >= 0) + CDStuffSubchannels(cd.SubPWBuf[subindex], subindex); + else // The system-specific emulation code should handle what value the sync bytes are. + CDStuffSubchannels(0x00, subindex); + } + + if(sbuf[0] && sbuf[1]) + { + cdda.CDDASynth.offset_inline(synthtime, sample[0] - cdda.last_sample[0], sbuf[0]); + cdda.CDDASynth.offset_inline(synthtime, sample[1] - cdda.last_sample[1], sbuf[1]); + } + + cdda.last_sample[0] = sample[0]; + cdda.last_sample[1] = sample[1]; + + cdda.CDDAReadPos++; + } + } +} + +static INLINE void RunCDRead(uint32 system_timestamp, int32 run_time) +{ + if(CDReadTimer > 0) + { + CDReadTimer -= run_time; + + if(CDReadTimer <= 0) + { + if(din.CanWrite() < 2048) + { + //printf("Carp: %d %d %d\n", din.CanWrite(), SectorCount, CDReadTimer); + //CDReadTimer = (cd.data_in_size - cd.data_in_pos) * 10; + + CDReadTimer += (uint64) 1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE; + + //CDReadTimer += (uint64) 1 * 128 * System_Clock / CD_DATA_TRANSFER_RATE; + } + else + { + uint8 tmp_read_buf[2352 + 96]; + + if(cd.TrayOpen) + { + din.Flush(); + cd.data_transfer_done = FALSE; + + CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN); + } + else if(SectorAddr >= toc.tracks[100].lba) + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME); + } + else if(!Cur_CDIF->ReadRawSector(tmp_read_buf, SectorAddr)) //, SectorAddr + SectorCount)) + { + cd.data_transfer_done = FALSE; + + CommandCCError(SENSEKEY_ILLEGAL_REQUEST); + } + else if(ValidateRawDataSector(tmp_read_buf, SectorAddr)) + { + memcpy(cd.SubPWBuf, tmp_read_buf + 2352, 96); + + if(tmp_read_buf[12 + 3] == 0x2) + din.Write(tmp_read_buf + 24, 2048); + else + din.Write(tmp_read_buf + 16, 2048); + + GenSubQFromSubPW(); + + CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_READY); + + SectorAddr++; + SectorCount--; + + if(CurrentPhase != PHASE_DATA_IN) + ChangePhase(PHASE_DATA_IN); + + if(SectorCount) + { + cd.data_transfer_done = FALSE; + CDReadTimer += (uint64) 1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE; + } + else + { + cd.data_transfer_done = TRUE; + } + } + } // end else to if(!Cur_CDIF->ReadSector + + } + } +} + + +uint32 PCECD_Drive_Run(pcecd_drive_timestamp_t system_timestamp) +{ + int32 run_time = system_timestamp - lastts; + + if(system_timestamp < lastts) + { + fprintf(stderr, "Meow: %d %d\n", system_timestamp, lastts); + assert(system_timestamp >= lastts); + } + + monotonic_timestamp += run_time; + + lastts = system_timestamp; + + RunCDRead(system_timestamp, run_time); + RunCDDA(system_timestamp, run_time); + + bool ResetNeeded = false; + + if(RST_signal && !cd.last_RST_signal) + ResetNeeded = true; + + cd.last_RST_signal = RST_signal; + + if(ResetNeeded) + { + //puts("RST"); + VirtualReset(); + } + else switch(CurrentPhase) + { + case PHASE_BUS_FREE: + if(SEL_signal) + { + ChangePhase(PHASE_COMMAND); + } + break; + + case PHASE_COMMAND: + if(REQ_signal && ACK_signal) // Data bus is valid nowww + { + //printf("Command Phase Byte I->T: %02x, %d\n", cd_bus.DB, cd.command_buffer_pos); + cd.command_buffer[cd.command_buffer_pos++] = cd_bus.DB; + SetREQ(FALSE); + } + + if(!REQ_signal && !ACK_signal && cd.command_buffer_pos) // Received at least one byte, what should we do? + { + if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4]) + { + const SCSICH* cmd_info_ptr = PCECommandDefs; + + while(cmd_info_ptr->pretty_name && cmd_info_ptr->cmd != cd.command_buffer[0]) + cmd_info_ptr++; + + if(SCSILog) + { + char log_buffer[1024]; + int lb_pos; + + log_buffer[0] = 0; + + lb_pos = trio_snprintf(log_buffer, 1024, "Command: %02x, %s ", cd.command_buffer[0], cmd_info_ptr->pretty_name ? cmd_info_ptr->pretty_name : "!!BAD COMMAND!!"); + + for(int i = 0; i < RequiredCDBLen[cd.command_buffer[0] >> 4]; i++) + lb_pos += trio_snprintf(log_buffer + lb_pos, 1024 - lb_pos, "%02x ", cd.command_buffer[i]); + + SCSILog("SCSI", "%s", log_buffer); + //puts(log_buffer); + } + + + if(cmd_info_ptr->pretty_name == NULL) // Command not found! + { + CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_COMMAND); + + SCSIDBG("Bad Command: %02x\n", cd.command_buffer[0]); + + if(SCSILog) + SCSILog("SCSI", "Bad Command: %02x", cd.command_buffer[0]); + + cd.command_buffer_pos = 0; + } + else + { + if(cd.TrayOpen && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM)) + { + CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN); + } + else if(!Cur_CDIF && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM)) + { + CommandCCError(SENSEKEY_NOT_READY, NSE_NO_DISC); + } + else if(cd.DiscChanged && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM)) + { + CommandCCError(SENSEKEY_UNIT_ATTENTION, NSE_DISC_CHANGED); + cd.DiscChanged = false; + } + else + { + cmd_info_ptr->func(cd.command_buffer); + } + + cd.command_buffer_pos = 0; + } + } // end if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4]) + else // Otherwise, get more data for the command! + SetREQ(TRUE); + } + break; + + case PHASE_STATUS: + if(REQ_signal && ACK_signal) + { + SetREQ(FALSE); + cd.status_sent = TRUE; + } + + if(!REQ_signal && !ACK_signal && cd.status_sent) + { + // Status sent, so get ready to send the message! + cd.status_sent = FALSE; + cd_bus.DB = cd.message_pending; + + ChangePhase(PHASE_MESSAGE_IN); + } + break; + + case PHASE_DATA_IN: + if(!REQ_signal && !ACK_signal) + { + //puts("REQ and ACK false"); + if(din.CanRead() == 0) // aaand we're done! + { + CDIRQCallback(0x8000 | PCECD_Drive_IRQ_DATA_TRANSFER_READY); + + if(cd.data_transfer_done) + { + SendStatusAndMessage(STATUS_GOOD, 0x00); + cd.data_transfer_done = FALSE; + CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE); + } + } + else + { + cd_bus.DB = din.ReadByte(); + SetREQ(TRUE); + } + } + if(REQ_signal && ACK_signal) + { + //puts("REQ and ACK true"); + SetREQ(FALSE); + } + break; + + case PHASE_MESSAGE_IN: + if(REQ_signal && ACK_signal) + { + SetREQ(FALSE); + cd.message_sent = TRUE; + } + + if(!REQ_signal && !ACK_signal && cd.message_sent) + { + cd.message_sent = FALSE; + ChangePhase(PHASE_BUS_FREE); + } + break; + } + + int32 next_time = 0x7fffffff; + + if(CDReadTimer > 0 && CDReadTimer < next_time) + next_time = CDReadTimer; + + if(cdda.CDDAStatus == CDDASTATUS_PLAYING) + { + int32 cdda_div_sexytime = (cdda.CDDADiv + 0xFFFF) >> 16; + if(cdda_div_sexytime > 0 && cdda_div_sexytime < next_time) + next_time = cdda_div_sexytime; + } + + assert(next_time >= 0); + + return(next_time); +} + +void PCECD_Drive_SetLog(void (*logfunc)(const char *, const char *, ...)) +{ + SCSILog = logfunc; +} + +void PCECD_Drive_SetTransferRate(uint32 TransferRate) +{ + CD_DATA_TRANSFER_RATE = TransferRate; +} + +void PCECD_Drive_Close(void) +{ + +} + +void PCECD_Drive_Init(int cdda_time_div, Blip_Buffer *leftbuf, Blip_Buffer *rightbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int)) +{ + Cur_CDIF = NULL; + cd.TrayOpen = false; + + monotonic_timestamp = 0; + lastts = 0; + + SCSILog = NULL; + + //din = new SimpleFIFO(2048); + + cdda.CDDATimeDiv = cdda_time_div; + + cdda.CDDAVolume = 65536; + cdda.CDDASynth.volume(1.0f / 65536); + cdda.CDDASynth.treble_eq(0); + sbuf[0] = leftbuf; + sbuf[1] = rightbuf; + + CD_DATA_TRANSFER_RATE = TransferRate; + System_Clock = SystemClock; + CDIRQCallback = IRQFunc; + CDStuffSubchannels = SSCFunc; +} + +void PCECD_Drive_SetCDDAVolume(unsigned vol) +{ + cdda.CDDAVolume = vol; +} + +int PCECD_Drive_StateAction(StateMem * sm, int load, int data_only, const char *sname) +{ + SFORMAT StateRegs[] = + { + SFVARN(cd_bus.DB, "DB"), + SFVARN(cd_bus.signals, "Signals"), + SFVAR(CurrentPhase), + + SFVARN(cd.last_RST_signal, "last_RST"), + SFVARN(cd.message_pending, "message_pending"), + SFVARN(cd.status_sent, "status_sent"), + SFVARN(cd.message_sent, "message_sent"), + SFVARN(cd.key_pending, "key_pending"), + SFVARN(cd.asc_pending, "asc_pending"), + SFVARN(cd.ascq_pending, "ascq_pending"), + SFVARN(cd.fru_pending, "fru_pending"), + + SFARRAYN(cd.command_buffer, 256, "command_buffer"), + SFVARN(cd.command_buffer_pos, "command_buffer_pos"), + SFVARN(cd.command_size_left, "command_size_left"), + + // Don't save the FIFO's write position, it will be reconstructed from read_pos and in_count + SFARRAYN(&din.data[0], din.data.size(), "din_fifo"), + SFVARN(din.read_pos, "din_read_pos"), + SFVARN(din.in_count, "din_in_count"), + SFVARN(cd.data_transfer_done, "data_transfer_done"), + + SFVARN(cd.TrayOpen, "TrayOpen"), + SFVARN(cd.DiscChanged, "DiscChanged"), + + SFVAR(cdda.PlayMode), + SFARRAY16(cdda.CDDASectorBuffer, 1176), + SFVAR(cdda.CDDAReadPos), + SFVAR(cdda.CDDAStatus), + SFVAR(cdda.CDDADiv), + SFVAR(read_sec_start), + SFVAR(read_sec), + SFVAR(read_sec_end), + + SFVAR(CDReadTimer), + SFVAR(SectorAddr), + SFVAR(SectorCount), + + SFVAR(cdda.ScanMode), + SFVAR(cdda.scan_sec_end), + + SFARRAYN(&cd.SubQBuf[0][0], sizeof(cd.SubQBuf), "SubQBufs"), + SFARRAYN(cd.SubQBuf_Last, sizeof(cd.SubQBuf_Last), "SubQBufLast"), + SFARRAYN(cd.SubPWBuf, sizeof(cd.SubPWBuf), "SubPWBuf"), + + SFVAR(monotonic_timestamp), + SFVAR(pce_lastsapsp_timestamp), + + SFEND + }; + + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, sname); + + if(load) + { + din.in_count &= din.size - 1; + din.read_pos &= din.size - 1; + din.write_pos = (din.read_pos + din.in_count) & (din.size - 1); + + if(cdda.CDDADiv <= 0) + cdda.CDDADiv = 1; + //printf("%d %d %d\n", din.in_count, din.read_pos, din.write_pos); + } + + return (ret); +} + +} diff --git a/mednafen/pce_fast-09333/pcecd_drive.h b/mednafen/pce_fast-09333/pcecd_drive.h new file mode 100644 index 0000000..aa44a87 --- /dev/null +++ b/mednafen/pce_fast-09333/pcecd_drive.h @@ -0,0 +1,89 @@ +#ifndef __PCEFAST_PCECD_Drive_H +#define __PCEFAST_PCECD_Drive_H + +#include + +namespace PCE_Fast +{ + +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 diff --git a/mednafen/pce_fast-09333/psg.cpp b/mednafen/pce_fast-09333/psg.cpp new file mode 100644 index 0000000..0baf7ad --- /dev/null +++ b/mednafen/pce_fast-09333/psg.cpp @@ -0,0 +1,677 @@ +/* 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 +#include +#include + +namespace PCE_Fast +{ + +void PCEFast_PSG::SetVolume(double new_volume) +{ + OutputVolume = new_volume; + + Synth.volume(OutputVolume / 6); +} + +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]; + + Synth.offset_inline(timestamp, samp[0] - ch->blip_prev_samp[0], sbuf[0]); + Synth.offset_inline(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]; + + Synth.offset_inline(timestamp, samp[0] - ch->blip_prev_samp[0], sbuf[0]); + Synth.offset_inline(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; + + Synth.offset_inline(timestamp, samp[0] - ch->blip_prev_samp[0], sbuf[0]); + Synth.offset_inline(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); + + Synth.offset_inline(timestamp, samp[0] - ch->blip_prev_samp[0], sbuf[0]); + Synth.offset_inline(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]); + Synth.treble_eq(-2.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 /= 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 = 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 +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(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(0, timestamp); + + for(int chc = 1; chc < 6; chc++) + RunChannel(chc, timestamp); +} + +INLINE void PCEFast_PSG::UpdateSubNonLFO(int32 timestamp) +{ + for(int chc = 0; chc < 6; chc++) + RunChannel(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); + } + + 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"); + + 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); +} + +} diff --git a/mednafen/pce_fast-09333/psg.h b/mednafen/pce_fast-09333/psg.h new file mode 100644 index 0000000..a87265f --- /dev/null +++ b/mednafen/pce_fast-09333/psg.h @@ -0,0 +1,99 @@ +#ifndef _PCEFast_PSG_H +#define _PCEFast_PSG_H + +#include + +namespace PCE_Fast +{ + +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 + 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 diff --git a/mednafen/pce_fast-09333/vdc.cpp b/mednafen/pce_fast-09333/vdc.cpp new file mode 100644 index 0000000..ef948c5 --- /dev/null +++ b/mednafen/pce_fast-09333/vdc.cpp @@ -0,0 +1,1976 @@ +/* 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 + */ + +/* VDC and VCE emulation */ + +/* +"Tonight I hooked up my Turbo Duo(with no games or CDs in it)'s video output to my PC sound card, recorded it, +and did a FFT and looked at the spectrum(around the line rate, 15-16KHz), and I also counted the number +of samples between the ~60Hz peaks(just to verify that the math shown below is remotely accurate). + +The spectrum peaked at 15734 Hz. 21477272.727272... / 3 / 15734 = 455.00(CPU cycles per scanline)" +*/ + +#include "pce.h" +#include "../video.h" +#include "vdc.h" +#include "huc.h" +#include "pcecd.h" +#include "../cputest/cputest.h" +#include "../FileStream.h" +#include +#include + +namespace PCE_Fast +{ + +static uint8 *CustomColorMap = NULL; // 1024 * 3 +static uint32 CustomColorMapLen; // 512 or 1024 +static uint32 systemColorMap32[512], bw_systemColorMap32[512]; +static uint32 amask; // Alpha channel maskaroo +static uint32 amask_shift; +static uint32 userle; // User layer enable. +static uint32 disabled_layer_color; + +static bool unlimited_sprites; +static bool correct_aspect; + +#define ULE_BG0 1 +#define ULE_SPR0 2 +#define ULE_BG1 4 +#define ULE_SPR1 8 + +static const uint8 bat_width_shift_tab[4] = { 5, 6, 7, 7 }; +static const uint8 bat_height_mask_tab[2] = { 32 - 1, 64 - 1 }; + +static unsigned int VDS; +static unsigned int VSW; +static unsigned int VDW; +static unsigned int VCR; +static unsigned int VBlankFL; + +vce_t vce; + +int VDC_TotalChips = 0; + +vdc_t *vdc_chips[2] = { NULL, NULL }; + +static INLINE void FixPCache(int entry) +{ + uint32 *cm32 = vce.bw ? bw_systemColorMap32 : systemColorMap32; + + if(!(entry & 0xFF)) + { + for(int x = 0; x < 16; x++) + vce.color_table_cache[(entry & 0x100) + (x << 4)] = cm32[vce.color_table[entry & 0x100]] | amask; + } + + if(entry & 0xF) + { + uint32 color = cm32[vce.color_table[entry]]; + + // For SuperGrafx VPCsprite handling to work + if(entry & 0x100) + color |= amask << 2; + + vce.color_table_cache[entry] = color; + } +} + +static INLINE void FixTileCache(vdc_t *which_vdc, uint16 A) +{ + uint32 charname = (A >> 4); + uint32 y = (A & 0x7); + uint64 *tc = &which_vdc->bg_tile_cache[charname][y]; + + uint32 bitplane01 = which_vdc->VRAM[y + charname * 16]; + uint32 bitplane23 = which_vdc->VRAM[y+ 8 + charname * 16]; + + *tc = 0; + + for(int x = 0; x < 8; x++) + { + uint32 raw_pixel = ((bitplane01 >> x) & 1); + raw_pixel |= ((bitplane01 >> (x + 8)) & 1) << 1; + raw_pixel |= ((bitplane23 >> x) & 1) << 2; + raw_pixel |= ((bitplane23 >> (x + 8)) & 1) << 3; + + #ifdef MSB_FIRST + *tc |= (uint64)raw_pixel << ((x) * 8); + #else + *tc |= (uint64)raw_pixel << ((7 - x) * 8); + #endif + } +} + +static INLINE void CheckFixSpriteTileCache(vdc_t *which_vdc, uint16 no, uint32 special) +{ + if(special != 0x4 && special != 0x5) + special = 0; + + if((special | 0x80) == which_vdc->spr_tile_clean[no]) + return; + + //printf("Oops: %d, %d, %d\n", no, special | 0x100, which_vdc->spr_tile_clean[no]); + if((no * 64) >= VRAM_Size) + { + //printf("Unmapped: %d\n", no); + //VDC_UNDEFINED("Unmapped VRAM sprite tile read"); + // Unnecessary, since we reset the sprite tile cache to 0 on reset/init anyway. + //memset(which_vdc->spr_tile_cache[no], 0x00, 16 * 16 * sizeof(uint16)); + } + else if(special) + { + for(int y = 0; y < 16; y++) + { + uint8 *tc = which_vdc->spr_tile_cache[no][y]; + + uint32 bitplane0 = which_vdc->VRAM[y + 0x00 + no * 0x40 + ((special & 1) << 5)]; + uint32 bitplane1 = which_vdc->VRAM[y + 0x10 + no * 0x40 + ((special & 1) << 5)]; + + for(int x = 0; x < 16; x++) + { + uint32 raw_pixel; + raw_pixel = ((bitplane0 >> x) & 1) << 0; + raw_pixel |= ((bitplane1 >> x) & 1) << 1; + tc[x] = raw_pixel; + } + } + } + else + { + for(int y = 0; y < 16; y++) + { + uint8 *tc = which_vdc->spr_tile_cache[no][y]; + + uint32 bitplane0 = which_vdc->VRAM[y + 0x00 + no * 0x40]; + uint32 bitplane1 = which_vdc->VRAM[y + 0x10 + no * 0x40]; + uint32 bitplane2 = which_vdc->VRAM[y + 0x20 + no * 0x40]; + uint32 bitplane3 = which_vdc->VRAM[y + 0x30 + no * 0x40]; + + for(int x = 0; x < 16; x++) + { + uint32 raw_pixel; + raw_pixel = ((bitplane0 >> x) & 1) << 0; + raw_pixel |= ((bitplane1 >> x) & 1) << 1; + raw_pixel |= ((bitplane2 >> x) & 1) << 2; + raw_pixel |= ((bitplane3 >> x) & 1) << 3; + tc[x] = raw_pixel; + } + } + } + + which_vdc->spr_tile_clean[no] = special | 0x80; +} + + +static INLINE void SetVCECR(uint8 V) +{ + if(((V & 0x80) >> 7) != vce.bw) + { + vce.bw = V & 0x80; + for(int x = 0; x < 512; x++) + FixPCache(x); + } + + vce.lc263 = (V & 0x04); + vce.dot_clock = V & 1; + if(V & 2) + vce.dot_clock = 2; + vce.CR = V; +} + +static unsigned int frame_counter; +static int32 need_vbi[2] = { 0, 0 }; +static int32 line_leadin1 = 0; +static int32 magical, cyc_tot; + +vpc_t vpc; + +// Some virtual vdc macros to make code simpler to read +#define M_vdc_HSW (vdc->HSR & 0x1F) // Horizontal Synchro Width +#define M_vdc_HDS ((vdc->HSR >> 8) & 0x7F) // Horizontal Display Start +#define M_vdc_HDW (vdc->HDR & 0x7F) // Horizontal Display Width +#define M_vdc_HDE ((vdc->HDR >> 8) & 0x7F) // Horizontal Display End + +#define M_vdc_VSW (vdc->VSR & 0x1F) // Vertical synchro width +#define M_vdc_VDS ((vdc->VSR >> 8) & 0xFF) // Vertical Display Start +#define M_vdc_VDW (vdc->VDR & 0x1FF) // Vertical Display Width(Height? :b) +#define M_vdc_VCR (vdc->VCR & 0xFF) + +#define VDCS_CR 0x01 // Sprite #0 collision interrupt occurred +#define VDCS_OR 0x02 // sprite overflow "" "" +#define VDCS_RR 0x04 // RCR "" "" +#define VDCS_DS 0x08 // VRAM to SAT DMA completion interrupt occurred +#define VDCS_DV 0x10 // VRAM to VRAM DMA completion interrupt occurred +#define VDCS_VD 0x20 // Vertical blank interrupt occurred +#define VDCS_BSY 0x40 // VDC is waiting for a CPU access slot during the active display area?? + +#if defined(WANT_8BPP) +static MDFN_PaletteEntry PalTest[256]; + +static uint8 FindClose(uint8 r, uint8 g, uint8 b) MDFN_COLD; +static uint8 FindClose(uint8 r, uint8 g, uint8 b) +{ + double rl, gl, bl; + int closest = -1; + double closest_cs = 1000; + + rl = pow((double)r / 255, 2.2 / 1.0); + gl = pow((double)g / 255, 2.2 / 1.0); + bl = pow((double)b / 255, 2.2 / 1.0); + + for(unsigned x = 0; x < 256; x++) + { + double rcl, gcl, bcl; + double cs; + + rcl = pow((double)PalTest[x].r / 255, 2.2 / 1.0); + gcl = pow((double)PalTest[x].g / 255, 2.2 / 1.0); + bcl = pow((double)PalTest[x].b / 255, 2.2 / 1.0); + + cs = fabs(rcl - rl) * 0.2126 + fabs(gcl - gl) * 0.7152 + fabs(bcl - bl) * 0.0722; + if(cs < closest_cs) + { + closest_cs = cs; + closest = x; + } + } + + return(closest); +} + +bool FindMatch(uint8 r, uint8 g, uint8 b) +{ + for(unsigned x = 0; x < 256; x++) + { + if(PalTest[x].r == r && PalTest[x].g == g && PalTest[x].b == b) + return(true); + } + return(false); +} +#endif + +void VDC_SetPixelFormat(const MDFN_PixelFormat &format) +{ + amask = 1 << format.Ashift; + amask_shift = format.Ashift; + + +#if defined(WANT_8BPP) + if(format.bpp == 8) + { + unsigned pti = 0; + + memset(PalTest, 0, sizeof(PalTest)); + + for(int i = 0; i < 8; i++) + { + PalTest[pti].r = i * 36; + PalTest[pti].g = i * 36; + PalTest[pti].b = i * 36; + pti++; + + if(i) + { + PalTest[pti].r = i * 36; + PalTest[pti].g = 0; + PalTest[pti].b = 0; + pti++; + + PalTest[pti].r = 0; + PalTest[pti].g = i * 36; + PalTest[pti].b = 0; + pti++; + + PalTest[pti].r = 0; + PalTest[pti].g = 0; + PalTest[pti].b = i * 36; + pti++; + + PalTest[pti].r = i * 36; + PalTest[pti].g = i * 36; + PalTest[pti].b = 0; + pti++; + + PalTest[pti].r = i * 36; + PalTest[pti].g = 0; + PalTest[pti].b = i * 36; + pti++; + + PalTest[pti].r = 0; + PalTest[pti].g = i * 36; + PalTest[pti].b = i * 36; + pti++; + } + } + + for(int r = 0; r < 8; r++) + { + for(int g = 0; g < 8; g++) + { + for(int b = 0; b < 8; b++) + { + if(FindMatch(r * 36, g * 36, b * 36)) + continue; + + if(g == 6 && b == 6 && r == 5) + goto SkipStuff; + + if(g == 6 && b == 3 && r == 5) + goto SkipStuff; + + if(g == 4 && b == 5 && r == 3) + goto SkipStuff; + + if(!(b & 1)) + continue; + + if(g == (r + 1)) + continue; + + //if(g == 7 && r >= 4 && b < 4) + // continue; + + if(g == (r + 3) && b >= 5) + continue; + + SkipStuff:; + + PalTest[pti].r = r * 36; + PalTest[pti].g = g * 36; + PalTest[pti].b = b * 36; + pti++; + + if(pti == 256) goto EndThingy; + } + } + } + EndThingy:; + //printf("ZOOMBA: %u\n", pti); + //exit(1); + } +#endif + + for(int x = 0; x < 512; x++) + { + int r, g, b; + int sc_r, sc_g, sc_b; + + if(CustomColorMap) + { + r = CustomColorMap[x * 3 + 0]; + g = CustomColorMap[x * 3 + 1]; + b = CustomColorMap[x * 3 + 2]; + } + else + { + b = 36 * (x & 0x007); + r = 36 * ((x & 0x038) >> 3); + g = 36 * ((x & 0x1c0) >> 6); + } + + if(CustomColorMap && CustomColorMapLen == 1024) + { + sc_r = CustomColorMap[(512 + x) * 3 + 0]; + sc_g = CustomColorMap[(512 + x) * 3 + 1]; + sc_b = CustomColorMap[(512 + x) * 3 + 2]; + } + else + { + double y; + + y = floor(0.5 + (0.300 * r + 0.589 * g + 0.111 * b)); + + if(y < 0) + y = 0; + + if(y > 255) + y = 255; + + sc_r = sc_g = sc_b = y; + } + +#if defined(WANT_8BPP) + if(format.bpp == 8) + { + systemColorMap32[x] = FindClose(r, g, b); + bw_systemColorMap32[x] = FindClose(sc_r, sc_g, sc_b); + } + else +#endif + { + systemColorMap32[x] = format.MakeColor(r, g, b); + bw_systemColorMap32[x] = format.MakeColor(sc_r, sc_g, sc_b); + } + } +#if 0 + { + bool usedo[256] = { 0 }; + + for(int x = 0; x < 512; x++) + { + usedo[systemColorMap32[x]] = true; + } + + for(int x = 0; x < 256; x++) + { + if(!usedo[x]) + { + printf("Monkey: %d %d %d\n", PalTest[x].r / 36, PalTest[x].g / 36, PalTest[x].b / 36); + } + } + } +#endif + // I know the temptation is there, but don't combine these two loops just + // because they loop 512 times ;) + for(int x = 0; x < 512; x++) + FixPCache(x); + + disabled_layer_color = format.MakeColor(0x00, 0xFE, 0x00); +} + +DECLFR(VCE_Read) +{ + switch(A & 0x7) + { + case 4: return(vce.color_table[vce.ctaddress & 0x1FF]); + case 5: { + uint8 ret = vce.color_table[vce.ctaddress & 0x1FF] >> 8; + ret &= 1; + ret |= 0xFE; + vce.ctaddress++; + return(ret); + } + } + return(0xFF); +} + +DECLFW(VCE_Write) +{ + //printf("%04x %02x, %04x\n", A, V, HuCPU.PC); + switch(A&0x7) + { + case 0: SetVCECR(V); break; + case 2: vce.ctaddress &= 0x100; vce.ctaddress |= V; break; + case 3: vce.ctaddress &= 0x0FF; vce.ctaddress |= (V & 1) << 8; break; + case 4: vce.color_table[vce.ctaddress & 0x1FF] &= 0x100; + vce.color_table[vce.ctaddress & 0x1FF] |= V; + FixPCache(vce.ctaddress & 0x1FF); + break; + case 5: vce.color_table[vce.ctaddress & 0x1FF] &= 0xFF; + vce.color_table[vce.ctaddress & 0x1FF] |= (V & 1) << 8; + FixPCache(vce.ctaddress & 0x1FF); + vce.ctaddress++; + break; + } +} + + +void VDC_SetLayerEnableMask(uint64 mask) +{ + userle = mask; +} + +DECLFW(VDC_Write_ST) +{ + if(VDC_TotalChips == 2) + A |= vpc.st_mode ? 0x10 : 0; + + //printf("WriteST: %04x %02x\n", A, V); + VDC_Write(A, V); +} + +static void DoDMA(vdc_t *vdc) +{ + // Assuming one cycle for reads, one cycle for write, with DMA? + for(int i = 0; i < 455; i++) + { + if(!vdc->DMAReadWrite) + { + if(vdc->SOUR >= VRAM_Size) + VDC_UNDEFINED("Unmapped VRAM DMA read"); + + vdc->DMAReadBuffer = vdc->VRAM[vdc->SOUR]; + } + else + { + if(vdc->DESR < VRAM_Size) + { + vdc->VRAM[vdc->DESR] = vdc->DMAReadBuffer; + FixTileCache(vdc, vdc->DESR); + vdc->spr_tile_clean[vdc->DESR >> 6] = 0; + } + + //if(vdc->DCR & 0xC) + //printf("Pllal: %02x\n", vdc->DCR); + + vdc->SOUR += (((vdc->DCR & 0x4) >> 1) ^ 2) - 1; + vdc->DESR += (((vdc->DCR & 0x8) >> 2) ^ 2) - 1; + vdc->LENR--; + if(vdc->LENR == 0xFFFF) // DMA is done. + { + vdc->DMARunning = 0; + if(vdc->DCR & 0x02) + { + vdc->status |= VDCS_DV; + HuC6280_IRQBegin(MDFN_IQIRQ1); + VDC_DEBUG("DMA IRQ"); + } + break; + } + } + vdc->DMAReadWrite ^= 1; + } // for() +} + +DECLFW(VDC_Write) +{ + int msb = A & 1; + int chip = 0; + vdc_t *vdc; + + //printf("VDC Write: %04x %02x\n", A, V); + if(VDC_TotalChips == 2) + { + A &= 0x1F; + switch(A) + { + case 0x8: vpc.priority[0] = V; break; + case 0x9: vpc.priority[1] = V; break; + case 0xA: vpc.winwidths[0] &= 0x300; vpc.winwidths[0] |= V; break; + case 0xB: vpc.winwidths[0] &= 0x0FF; vpc.winwidths[0] |= (V & 3) << 8; break; + case 0xC: vpc.winwidths[1] &= 0x300; vpc.winwidths[1] |= V; break; + case 0xD: vpc.winwidths[1] &= 0x0FF; vpc.winwidths[1] |= (V & 3) << 8; break; + case 0xE: vpc.st_mode = V & 1; break; + } + if(A & 0x8) return; + + chip = (A & 0x10) >> 4; + vdc = vdc_chips[chip]; + A &= 0x3; + } + else + { + vdc = vdc_chips[0]; + A &= 0x3; + } + //if((A == 0x2 || A == 0x3) && ((vdc->select & 0x1f) >= 0x09) && ((vdc->select & 0x1f) <= 0x13)) + + //printf("%04x, %02x: %02x, %d\n", A, vdc->select, V, vdc->display_counter); + + switch(A) + { + case 0x0: vdc->select = V & 0x1F; break; + case 0x2: + case 0x3: + switch(vdc->select & 0x1F) + { + case 0x00: REGSETP(vdc->MAWR, V, msb); break; + case 0x01: REGSETP(vdc->MARR, V, msb); + if(msb) + { + if(vdc->MARR >= VRAM_Size) + VDC_UNDEFINED("Unmapped VRAM VRR(MARR set) read"); + + vdc->read_buffer = vdc->VRAM[vdc->MARR]; + } + break; + case 0x02: if(!msb) vdc->write_latch = V; + else + { + if(vdc->MAWR < VRAM_Size) + { + // A hack to fix Crest of Wolf, and maybe others. + while(vdc->DMARunning) + DoDMA(vdc); + + vdc->VRAM[vdc->MAWR] = (V << 8) | vdc->write_latch; + FixTileCache(vdc, vdc->MAWR); + vdc->spr_tile_clean[vdc->MAWR >> 6] = 0; + } + else + { + //VDC_UNDEFINED("Unmapped VRAM write"); + //printf("VROOM: %04x, %02x\n", vdc->MAWR, (vdc->CR >> 11) & 0x3); + } + vdc->MAWR += vram_inc_tab[(vdc->CR >> 11) & 0x3]; + } + break; + case 0x05: REGSETP(vdc->CR, V, msb); break; + case 0x06: REGSETP(vdc->RCR, V, msb); vdc->RCR &= 0x3FF; break; + case 0x07: REGSETP(vdc->BXR, V, msb); vdc->BXR &= 0x3FF; break; + case 0x08: REGSETP(vdc->BYR, V, msb); vdc->BYR &= 0x1FF; + vdc->BG_YOffset = vdc->BYR; // Set it on LSB and MSB writes(only changing on MSB breaks Youkai Douchuuki) + //printf("%04x\n", HuCPU.PC); + break; + case 0x09: REGSETP(vdc->MWR, V, msb); break; + case 0x0a: REGSETP(vdc->HSR, V, msb); break; + case 0x0b: REGSETP(vdc->HDR, V, msb); break; + case 0x0c: REGSETP(vdc->VSR, V, msb); break; + case 0x0d: REGSETP(vdc->VDR, V, msb); break; + case 0x0e: REGSETP(vdc->VCR, V, msb); break; + case 0x0f: REGSETP(vdc->DCR, V, msb); break; + case 0x10: REGSETP(vdc->SOUR, V, msb); break; + case 0x11: REGSETP(vdc->DESR, V, msb); break; + case 0x12: REGSETP(vdc->LENR, V, msb); + if(msb) + { + vdc->DMARunning = 1; + vdc->DMAReadWrite = 0; + if(vdc->burst_mode && !(vdc->DCR & 0x02)) + DoDMA(vdc); // Do one line's worth of DMA transfers + // because Cosmic Fantasy 4 is evil + // and uses timed writes to the DMA + // start register, rather than waiting until + // the machine says we're done, + // which would require cycle-accurate VDC emulation...like that's + // going to happen when I don't even have accurate values + // for HuC6280 instruction timings. :b + } + break; + case 0x13: REGSETP(vdc->SATB, V, msb); vdc->SATBPending = 1; break; +// default: printf("Oops 2: %04x %02x\n", vdc->select, V);break; + } + break; + } +} + + +// 682 + 8 + 128 = 818. +static INLINE void CalcStartEnd(const vdc_t *vdc, uint32 &start, uint32 &end) +{ + //static const unsigned int ClockModeWidths[3] = { 288, 384, 576 }; + static const unsigned int ClockPixelWidths[3] = { 341, 455, 682 }; + + start = (M_vdc_HDS + 1) * 8; + // Semi-hack for Asuka 120% + if(vce.dot_clock == 1 && M_vdc_HDS == 5 && M_vdc_HDE == 6 && M_vdc_HDW == 43 && M_vdc_HSW == 2) + start -= 8; + else if(vce.dot_clock == 0 && M_vdc_HDS == 2 && M_vdc_HDE == 3 && M_vdc_HDW == 33 && M_vdc_HSW == 2) + start -= 4; + // and for Addams Family + else if(vce.dot_clock == 1 && M_vdc_HDS == 4 && M_vdc_HDE == 4 && M_vdc_HDW == 43 && M_vdc_HSW == 9) + start -= 4; + end = start + (M_vdc_HDW + 1) * 8; + + if(start > (ClockPixelWidths[vce.dot_clock])) + start = ClockPixelWidths[vce.dot_clock]; + + if(end > (ClockPixelWidths[vce.dot_clock])) + end = ClockPixelWidths[vce.dot_clock]; + + if(start == end) // In case HDS is way off-screen, REMOVE when we confirm the rest of the code won't flip out + start = end - 8; // when start == end; + + // For: start - (vdc->BG_XOffset & 7) + end += 8; + start += 8; + + // For: alignment space when correct_aspect == 0 + end += 128; + start += 128; + +#if 0 + uint32 display_width; + display_width = (M_vdc_HDW + 1) * 8; + + if(display_width > ClockModeWidths[vce.dot_clock]) + display_width = ClockModeWidths[vce.dot_clock]; + + start = (ClockModeWidths[vce.dot_clock] - display_width) / 2; + + // For: start - (vdc->BG_XOffset & 7) + start += 8; + + // For: alignment space when correct_aspect == 0 + start += 128; + + // Semi-hack for Asuka 120% + if(vce.dot_clock == 1 && M_vdc_HDS == 5 && M_vdc_HDE == 6 && M_vdc_HDW == 43 && M_vdc_HSW == 2) + start += 8; + else if(vce.dot_clock == 0 && M_vdc_HDS == 2 && M_vdc_HDE == 3 && M_vdc_HDW == 33 && M_vdc_HSW == 2) + start += 4; + // and for Addams Family + else if(vce.dot_clock == 1 && M_vdc_HDS == 4 && M_vdc_HDE == 4 && M_vdc_HDW == 43 && M_vdc_HSW == 9) + start += 4; + + //MDFN_DispMessage((UTF8*)"dc: %d, %d %d %d %d; %d %d\n", vce.dot_clock, M_vdc_HDS, M_vdc_HDE, M_vdc_HDW, M_vdc_HSW, start, (M_vdc_HDS + 1) * 8); + + end = start + display_width; + if(end > (ClockModeWidths[vce.dot_clock] + 8 + 128)) + end = ClockModeWidths[vce.dot_clock] + 8 + 128; +#endif +} + +#define CB_EXL(n) (((n) << 4) | ((n) << 12) | ((n) << 20) | ((n) << 28) | ((n) << 36) | ((n) << 44) | ((n) << 52) | ((n) << 60)) +static const uint64 cblock_exlut[16] = { + CB_EXL(0ULL), CB_EXL(1ULL), CB_EXL(2ULL), CB_EXL(3ULL), CB_EXL(4ULL), CB_EXL(5ULL), CB_EXL(6ULL), CB_EXL(7ULL), + CB_EXL(8ULL), CB_EXL(9ULL), CB_EXL(10ULL), CB_EXL(11ULL), CB_EXL(12ULL), CB_EXL(13ULL), CB_EXL(14ULL), CB_EXL(15ULL) + }; + +static void DrawBG(const vdc_t *vdc, const uint32 count, uint8 *target) NO_INLINE; +static void DrawBG(const vdc_t *vdc, const uint32 count, uint8 *target) +{ + int bat_width_shift = bat_width_shift_tab[(vdc->MWR >> 4) & 3]; + int bat_width_mask = (1U << bat_width_shift) - 1; + int bat_height_mask = bat_height_mask_tab[(vdc->MWR >> 6) & 1]; + uint64 *target64 = (uint64 *)target; + + { + int bat_y = ((vdc->BG_YOffset >> 3) & bat_height_mask) << bat_width_shift; + + int bat_boom = (vdc->BG_XOffset >> 3) & bat_width_mask; + int line_sub = vdc->BG_YOffset & 7; + + const uint16 *BAT_Base = &vdc->VRAM[bat_y]; + const uint64 *CG_Base = &vdc->bg_tile_cache[0][line_sub]; + + if((vdc->MWR & 0x3) == 0x3) + { + const uint64 cg_mask = (vdc->MWR & 0x80) ? 0xCCCCCCCCCCCCCCCCULL : 0x3333333333333333ULL; + + for(int x = count - 1; x >= 0; x -= 8) + { + const uint16 bat = BAT_Base[bat_boom]; + const uint64 color_or = cblock_exlut[bat >> 12]; + + *target64 = (CG_Base[(bat & 0xFFF) * 8] & cg_mask) | color_or; + + bat_boom = (bat_boom + 1) & bat_width_mask; + target64++; + } + } // End 2-bit CG rendering + else + { + for(int x = count - 1; x >= 0; x -= 8) + { + const uint16 bat = BAT_Base[bat_boom]; + const uint64 color_or = cblock_exlut[bat >> 12]; + + *target64 = CG_Base[(bat & 0xFFF) * 8] | color_or; + + bat_boom = (bat_boom + 1) & bat_width_mask; + target64++; + } + } // End normal CG rendering + } +} + +#define SPRF_PRIORITY 0x00080 +#define SPRF_HFLIP 0x00800 +#define SPRF_VFLIP 0x08000 +#define SPRF_SPRITE0 0x10000 + +static const unsigned int sprite_height_tab[4] = { 16, 32, 64, 64 }; +static const unsigned int sprite_height_no_mask[4] = { ~0U, ~2U, ~6U, ~6U }; + +static INLINE void RebuildSATCache(vdc_t *vdc) +{ + SAT_Cache_t *sat_ptr = vdc->SAT_Cache; + + vdc->SAT_Cache_Valid = 0; + + for(int i = 0; i < 64; i++) + { + const uint16 SATR0 = vdc->SAT[i * 4 + 0x0]; + const uint16 SATR1 = vdc->SAT[i * 4 + 0x1]; + const uint16 SATR2 = vdc->SAT[i * 4 + 0x2]; + const uint16 SATR3 = vdc->SAT[i * 4 + 0x3]; + + int16 y; + uint16 height; + uint16 x; + uint16 no; + uint16 flags; + bool cgmode; + uint32 width; + + y = (int16)(SATR0 & 0x3FF) - 0x40; + x = SATR1 & 0x3FF; + no = (SATR2 >> 1) & 0x3FF; + flags = (SATR3); + cgmode = SATR2 & 0x1; + + width = ((flags >> 8) & 1); + flags &= ~0x100; + + height = sprite_height_tab[(flags >> 12) & 3]; + no &= sprite_height_no_mask[(flags >> 12) & 3]; + + no = ((no & ~width) | 0) ^ ((flags & SPRF_HFLIP) ? width : 0); + + sat_ptr->y = y; + sat_ptr->height = height; + sat_ptr->x = x; + sat_ptr->no = no; + sat_ptr->flags = flags; + sat_ptr->cgmode = cgmode; + + sat_ptr++; + vdc->SAT_Cache_Valid++; + + if(width) + { + no = ((no & ~width) | 1) ^ ((flags & SPRF_HFLIP) ? width : 0); + x += 16; + + *sat_ptr = *(sat_ptr - 1); + + sat_ptr->no = no; + sat_ptr->x = x; + + sat_ptr++; + vdc->SAT_Cache_Valid++; + } + } +} + +static INLINE void DoSATDMA(vdc_t *vdc) +{ + if(vdc->SATB > (VRAM_Size - 0x100)) + VDC_UNDEFINED("Unmapped VRAM SATB DMA read"); + + for(int i = 0; i < 256; i++) + vdc->SAT[i] = vdc->VRAM[(vdc->SATB + i) & 0xFFFF]; + + RebuildSATCache(vdc); +} + + +typedef struct +{ + uint32 x; + uint32 flags; + uint8 palette_index; + uint16 no; + uint16 sub_y; +} SPRLE; + +static const unsigned int spr_hpmask = 0x8000; // High priority bit mask + +// DrawSprites will write up to 0x20 units before the start of the pointer it's passed. +static void DrawSprites(vdc_t *vdc, const int32 end, uint16 *spr_linebuf) NO_INLINE; +static void DrawSprites(vdc_t *vdc, const int32 end, uint16 *spr_linebuf) +{ + int active_sprites = 0; + SPRLE SpriteList[64 * 2]; // (see unlimited_sprites option, *2 to accomodate 32-pixel-width sprites ) //16]; + + // First, grab the up to 16(or 128 for unlimited_sprites) sprite units(16xWHATEVER; each 32xWHATEVER sprite counts as 2 sprite units when + // rendering a scanline) for this scanline. + for(int i = 0; i < vdc->SAT_Cache_Valid; i++) + { + const SAT_Cache_t *SATR = &vdc->SAT_Cache[i]; + + int16 y = SATR->y; + uint16 x = SATR->x; + uint16 no = SATR->no; + const uint16 flags = SATR->flags; + const uint8 cgmode = SATR->cgmode; + const uint16 height = SATR->height; + const uint32 palette_index = (flags & 0xF) << 4; + + uint32 y_offset = vdc->RCRCount - y; + if(y_offset < height) + { + if(active_sprites == 16) + { + if(vdc->CR & 0x2) + { + vdc->status |= VDCS_OR; + HuC6280_IRQBegin(MDFN_IQIRQ1); + VDC_DEBUG("Overflow IRQ"); + } + if(!unlimited_sprites) + break; + } + + if(flags & SPRF_VFLIP) + y_offset = height - 1 - y_offset; + + no |= (y_offset & 0x30) >> 3; + + SpriteList[active_sprites].flags = flags; + + //printf("Found: %d %d\n", vdc->RCRCount, x); + SpriteList[active_sprites].x = x; + SpriteList[active_sprites].palette_index = palette_index; + + SpriteList[active_sprites].no = no; + SpriteList[active_sprites].sub_y = (y_offset & 15); + + + CheckFixSpriteTileCache(vdc, no, (vdc->MWR & 0xC) | cgmode); + + SpriteList[active_sprites].flags |= i ? 0 : SPRF_SPRITE0; + + active_sprites++; + } + } + + //if(!active_sprites) + // return; + + //memset(spr_linebuf, 0, sizeof(uint16) * end); + MDFN_FastU32MemsetM8((uint32 *)spr_linebuf, 0, ((end + 3) >> 1) & ~1); + + if(!active_sprites) + return; + + for(int i = (active_sprites - 1) ; i >= 0; i--) + { + int32 pos = SpriteList[i].x - 0x20; + uint32 prio_or; + uint16 *dest_pix; + + if(pos > end) + continue; + + dest_pix = &spr_linebuf[pos]; + + prio_or = 0x100 | SpriteList[i].palette_index; + + if(SpriteList[i].flags & SPRF_PRIORITY) + prio_or |= spr_hpmask; + + if((SpriteList[i].flags & SPRF_SPRITE0) && (vdc->CR & 0x01)) + { + const uint8 *pix_source = vdc->spr_tile_cache[SpriteList[i].no][SpriteList[i].sub_y]; + + if(SpriteList[i].flags & SPRF_HFLIP) + { + for(int32 x = 0; x < 16; x++) + { + const uint32 raw_pixel = pix_source[x]; + if(raw_pixel) + { + if(((uint32)pos + x) >= (uint32)end) // Covers negative and overflowing the right side(to prevent spurious sprite hits) + continue; + + if(dest_pix[x] & 0x100) + { + vdc->status |= VDCS_CR; + VDC_DEBUG("Sprite hit IRQ"); + HuC6280_IRQBegin(MDFN_IQIRQ1); + } + dest_pix[x] = raw_pixel | prio_or; + } + } + } + else + { + for(int32 x = 0; x < 16; x++) + { + const uint32 raw_pixel = pix_source[15 - x]; + if(raw_pixel) + { + if(((uint32)pos + x) >= (uint32)end) // Covers negative and overflowing the right side(to prevent spurious sprite hits) + continue; + + if(dest_pix[x] & 0x100) + { + vdc->status |= VDCS_CR; + VDC_DEBUG("Sprite hit IRQ"); + HuC6280_IRQBegin(MDFN_IQIRQ1); + } + dest_pix[x] = raw_pixel | prio_or; + } + } + } + } // End sprite0 handling + else // No sprite0 hit: + { + const uint8 *pix_source = vdc->spr_tile_cache[SpriteList[i].no][SpriteList[i].sub_y]; + + // x must be signed, for "pos + x" to not be promoted to unsigned, which will cause a stack overflow. + if(SpriteList[i].flags & SPRF_HFLIP) + { + for(int32 x = 0; x < 16; x++) + { + const uint32 raw_pixel = pix_source[x]; + if(raw_pixel) + dest_pix[x] = raw_pixel | prio_or; + } + } + else + { + for(int32 x = 0; x < 16; x++) + { + const uint32 raw_pixel = pix_source[15 - x]; + if(raw_pixel) + dest_pix[x] = raw_pixel | prio_or; + } + } + + } // End no sprite0 hit + } +} + +void (*MixBGSPR)(const uint32 count, const uint8 *bg_linebuf, const uint16 *spr_linebuf, uint32 *target) = NULL; + +template +void MixBGSPR_Generic(const uint32 count_in, const uint8 *bg_linebuf_in, const uint16 *spr_linebuf_in, T *target_in) +{ + for(unsigned int x = 0; x < count_in; x++) + { + const uint32 bg_pixel = bg_linebuf_in[x]; + const uint32 spr_pixel = spr_linebuf_in[x]; + uint32 pixel = bg_pixel; + + if(((int16)(spr_pixel | ((bg_pixel & 0x0F) - 1))) < 0) + pixel = spr_pixel; + + target_in[x] = vce.color_table_cache[pixel & 0x1FF]; + } +} + +#ifdef ARCH_X86 + +#ifdef __x86_64__ + +void MixBGSPR_x86(const uint32 count, const uint8 *bg_linebuf, const uint16 *spr_linebuf, uint32 *target) +{ + // rdx: bg_linebuf + // rsi: spr_linebuf + // rbp: vce.color_table_cache + // rdi: target + // rcx: count + + // eax: bg pixel + // ebx: spr pixel + int dummy; + + asm volatile( + "push %%rbx\n\t" + + "movq %%rax, %%rbp\n\t" + "negq %%rcx\n\t" + "xorq %%rax, %%rax\n\t" + "xorq %%rbx, %%rbx\n\t" + + "BoomBuggy:\n\t" + "movzbl (%%rdx, %%rcx, 1), %%eax\n\t" + "movswl (%%rsi, %%rcx, 2), %%ebx\n\t" + + "testl $15, %%eax\n\t" + "bt $15, %%ebx\n\t" + + "cmovbe %%ebx, %%eax\n\t" + "andl $511, %%eax\n\t" + + "movl (%%rbp, %%rax, 4), %%ebx\n\t" + "movl %%ebx, (%%rdi, %%rcx, 4)\n\t" + + "addq $1, %%rcx\n\t" + "jnz BoomBuggy\n\t" + + "pop %%rbx\n\t" + : "=c" (dummy), "=a" (dummy) + : "d" (bg_linebuf + count), "S" (spr_linebuf + count), "D" (target + count), "c" (count), "a" (vce.color_table_cache) + : "memory", "cc", "rbp" + ); +} + +#else // else to __x86_64__ +void MixBGSPR_x86(const uint32 count, const uint8 *bg_linebuf, const uint16 *spr_linebuf, uint32 *target) +{ + // edx: bg_linebuf + // esi: spr_linebuf + // ebp: vce.color_table_cache + // edi: target + // ecx: count + + // eax: bg pixel + // ebx: spr pixel + int dummy; + + asm volatile( + "push %%ebx\n\t" + + "movl %%eax, %%ebp\n\t" + "negl %%ecx\n\t" + "xorl %%eax, %%eax\n\t" + "xorl %%ebx, %%ebx\n\t" + + "BoomBuggy:\n\t" + "movzbl (%%edx, %%ecx, 1), %%eax\n\t" + "movswl (%%esi, %%ecx, 2), %%ebx\n\t" + + "testl $15, %%eax\n\t" + "bt $15, %%ebx\n\t" + + "jnbe SkipMove\n\t" + "movl %%ebx, %%eax\n\t" + "andl $511, %%eax\n\t" + "SkipMove:\n\t" + + "movl (%%ebp, %%eax, 4), %%ebx\n\t" + "movl %%ebx, (%%edi, %%ecx, 4)\n\t" + + "addl $1, %%ecx\n\t" + "jnz BoomBuggy\n\t" + + "pop %%ebx\n\t" + : "=c" (dummy), "=a" (dummy) + : "d" (bg_linebuf + count), "S" (spr_linebuf + count), "D" (target + count), "c" (count), "a" (vce.color_table_cache) + : "memory", "cc", "ebp" + ); +} + +void MixBGSPR_x86_CMOV(const uint32 count, const uint8 *bg_linebuf, const uint16 *spr_linebuf, uint32 *target) +{ + // edx: bg_linebuf + // esi: spr_linebuf + // ebp: vce.color_table_cache + // edi: target + // ecx: count + + // eax: bg pixel + // ebx: spr pixel + int dummy; + + asm volatile( + "push %%ebx\n\t" + + "movl %%eax, %%ebp\n\t" + "negl %%ecx\n\t" + "xorl %%eax, %%eax\n\t" + "xorl %%ebx, %%ebx\n\t" + + "BoomBuggyCMOV:\n\t" + "movzbl (%%edx, %%ecx, 1), %%eax\n\t" + "movswl (%%esi, %%ecx, 2), %%ebx\n\t" + + "testl $15, %%eax\n\t" + "bt $15, %%ebx\n\t" + + "cmovbe %%ebx, %%eax\n\t" + "andl $511, %%eax\n\t" + + "movl (%%ebp, %%eax, 4), %%ebx\n\t" + "movl %%ebx, (%%edi, %%ecx, 4)\n\t" + + "addl $1, %%ecx\n\t" + "jnz BoomBuggyCMOV\n\t" + + "pop %%ebx\n\t" + : "=c" (dummy), "=a" (dummy) + : "d" (bg_linebuf + count), "S" (spr_linebuf + count), "D" (target + count), "c" (count), "a" (vce.color_table_cache) + : "memory", "cc", "ebp" + ); +} + +#endif + +#endif // ARCH_X86 + +template +void MixBGOnly(const uint32 count, const uint8 *bg_linebuf, T *target) +{ + for(unsigned int x = 0; x < count; x++) + target[x] = vce.color_table_cache[bg_linebuf[x]]; +} + +template +void MixSPROnly(const uint32 count, const uint16 *spr_linebuf, T *target) +{ + for(unsigned int x = 0; x < count; x++) + target[x] = vce.color_table_cache[(spr_linebuf[x] | 0x100) & 0x1FF]; +} + +template +void MixNone(const uint32 count, T *target) +{ + uint32 bg_color = vce.color_table_cache[0x000]; + + for(unsigned int x = 0; x < count; x++) + target[x] = bg_color; +} + +//static void MixVPC(const uint32 count, const uint32 *lb0, const uint32 *lb1, uint32 *target) NO_INLINE; + +template +static void MixVPC(const uint32 count, const uint32 *lb0, const uint32 *lb1, T *target) +{ + static const int prio_select[4] = { 1, 1, 0, 0 }; + static const int prio_shift[4] = { 4, 0, 4, 0 }; + + // Windowing disabled. + if(vpc.winwidths[0] <= 0x40 && vpc.winwidths[1] <= 0x40) + { + const uint8 pb = (vpc.priority[prio_select[0]] >> prio_shift[0]) & 0xF; + + switch(pb) + { + default: + //printf("%02x\n", pb); + for(int x = 0; x < (int)count; x++) + { + #include "vpc_mix_inner.inc" + } + break; + + case 0x3: + for(int x = 0; x < (int)count; x++) + { + #include "vpc_mix_inner.inc" + } + break; + + case 0x7: + for(int x = 0; x < (int)count; x++) + { + #include "vpc_mix_inner.inc" + } + break; + + case 0xB: + for(int x = 0; x < (int)count; x++) + { + #include "vpc_mix_inner.inc" + } + break; + + case 0xF: + for(int x = 0; x < (int)count; x++) + { + #include "vpc_mix_inner.inc" + } + break; + } + + //switch(pb & 0xF) + //{ + // case 0x0: for(int x = 0; x < (int)count; x++) + // { + // #include "vpc_mix_inner.inc" + // } + // break; + //} + } + else for(int x = 0; x < (int)count; x++) + { + int in_window = 0; + + if(x < (vpc.winwidths[0] - 0x40)) + in_window |= 1; + + if(x < (vpc.winwidths[1] - 0x40)) + in_window |= 2; + + uint8 pb = (vpc.priority[prio_select[in_window]] >> prio_shift[in_window]) & 0xF; + + #include "vpc_mix_inner.inc" + } +} + +template +void DrawOverscan(const vdc_t *vdc, T *target, const MDFN_Rect *lw, const bool full = true, const int32 vpl = 0, const int32 vpr = 0) +{ + uint32 os_color = vce.color_table_cache[0x100]; + + //printf("%d %d\n", lw->x, lw->w); + + if(full) + { + // Fill in entire viewport(horizontally!) with overscan color + for(int x = lw->x; x < lw->x + lw->w; x++) + target[x] = os_color; + } + else + { + // Fill in viewport to the left of HDW with overscan color. + for(int x = lw->x; x < vpl; x++) + target[x] = os_color; + + // Fill in viewport to the right of HDW with overscan color. + for(int x = vpr; x < lw->x + lw->w; x++) + target[x] = os_color; + } +} + +void VDC_RunFrame(EmulateSpecStruct *espec, bool IsHES) +{ + vdc_t *vdc = vdc_chips[0]; + int max_dc = 0; + MDFN_Surface *surface = espec->surface; + MDFN_Rect *DisplayRect = &espec->DisplayRect; + MDFN_Rect *LineWidths = espec->LineWidths; + bool skip = espec->skip || IsHES; + + // x and w should be overwritten in the big loop + + if(!skip) + { +#if defined(WANT_8BPP) + if(surface->palette) + memcpy(surface->palette, PalTest, sizeof(PalTest)); +#endif + + DisplayRect->x = 0; + DisplayRect->w = 256; + + DisplayRect->y = MDFN_GetSettingUI("pce_fast.slstart"); + DisplayRect->h = MDFN_GetSettingUI("pce_fast.slend") - DisplayRect->y + 1; + + // Hack for the input latency-reduction hack, part 1. + for(int y = DisplayRect->y; y < DisplayRect->y + DisplayRect->h; y++) + LineWidths[y].w = 0; + } + + do + { + const bool SHOULD_DRAW = (!skip && (int)frame_counter >= (DisplayRect->y + 14) && (int)frame_counter < (DisplayRect->y + DisplayRect->h + 14)); + + vdc = vdc_chips[0]; + + if(frame_counter == 0) + { + VDS = M_vdc_VDS; + VSW = M_vdc_VSW; + VDW = M_vdc_VDW; + VCR = M_vdc_VCR; + VBlankFL = VDS + VSW + VDW + 1; + + if(VBlankFL > 261) + VBlankFL = 261; + } + + uint32 line_buffer[2][1024]; // For super grafx emulation + + need_vbi[0] = need_vbi[1] = 0; + + #if 1 + line_leadin1 = 0; + + magical = M_vdc_HDS + (M_vdc_HDW + 1) + M_vdc_HDE; + magical = (magical + 2) & ~1; + magical -= M_vdc_HDW + 1; + cyc_tot = magical * 8; //ClockPixelWidths[vce.dot_clock] - magical * 8; + cyc_tot-=2; + switch(vce.dot_clock) + { + case 0: cyc_tot = 4 * cyc_tot / 3; break; + case 1: break; + case 2: cyc_tot = 2 * cyc_tot / 3; break; + } + + if(cyc_tot < 0) cyc_tot = 0; + line_leadin1 = cyc_tot; + #endif + + #if 0 + { + int vdc_to_master = 4; + + line_leadin1 = 1365 - ((M_vdc_HDW + 1) * 8 - 4 + 6) * vdc_to_master; + + if(line_leadin1 < 0) + { + line_leadin1 = 0; + puts("Eep"); + } + + if(M_vdc_HDS > 2) + line_leadin1 += 2; + + line_leadin1 = line_leadin1 / 3; + } + + if(line_leadin1 < 0) + line_leadin1 = 0; + else if(line_leadin1 > 400) + line_leadin1 = 400; + #endif + + //printf("%d\n", line_leadin1); + if(max_dc < vce.dot_clock) + max_dc = vce.dot_clock; + + if(!skip) + { + static const int ws[2][3] = { + { 341, 341, 682 }, + { 256, 341, 512 } + }; + + DisplayRect->x = 0; //128 + 8 + xs[correct_aspect][vce.dot_clock]; + DisplayRect->w = ws[correct_aspect][vce.dot_clock]; + } + + for(int chip = 0; chip < VDC_TotalChips; chip++) + { + vdc = vdc_chips[chip]; + if(frame_counter == 0) + { + vdc->display_counter = 0; + vdc->burst_mode = !(vdc->CR & 0xC0); + } + + if(vdc->display_counter == (VDS + VSW)) + { + vdc->burst_mode = !(vdc->CR & 0xC0); + vdc->RCRCount = 0; + } + int have_free_time = 1; + + if(!vdc->burst_mode && vdc->display_counter >= (VDS + VSW) && vdc->display_counter < (VDS + VSW + VDW + 1)) + have_free_time = 0; + + if(have_free_time) // We're outside of the active display area. Weehee + { + if(vdc->DMARunning) + DoDMA(vdc); + } + + if(vdc->display_counter == VBlankFL) + { + need_vbi[chip] = 1; + if(vdc->SATBPending || (vdc->DCR & 0x10)) + { + vdc->SATBPending = 0; + vdc->sat_dma_slcounter = 2; + + DoSATDMA(vdc); + } + } + if((int)vdc->RCRCount == ((int)vdc->RCR - 0x40) && (vdc->CR & 0x04)) + { + VDC_DEBUG("RCR IRQ"); + vdc->status |= VDCS_RR; + HuC6280_IRQBegin(MDFN_IQIRQ1); + } + } + + HuC6280_Run(line_leadin1); + + const bool fc_vrm = (frame_counter >= 14 && frame_counter < (14 + 242)); + + for(int chip = 0; chip < VDC_TotalChips; chip++) + { + MDFN_ALIGN(8) uint8 bg_linebuf[8 + 1024]; + MDFN_ALIGN(8) uint16 spr_linebuf[16 + 1024]; + + uint32 *target_ptr = NULL; + uint16 *target_ptr16 = NULL; + uint8 *target_ptr8 = NULL; + + vdc = vdc_chips[chip]; + + if(VDC_TotalChips == 2) + target_ptr = line_buffer[chip]; + else + { +#if defined(WANT_8BPP) + //if(surface->format.bpp == 8) + target_ptr8 = surface->pixels8 + (frame_counter - 14) * surface->pitchinpix; +#elif defined(WANT_16BPP) + //if(surface->format.bpp == 16) + target_ptr16 = surface->pixels16 + (frame_counter - 14) * surface->pitchinpix; +#elif defined(WANT_32BPP) + //else + target_ptr = surface->pixels + (frame_counter - 14) * surface->pitchinpix; +#endif + } + + if(fc_vrm && !skip) + LineWidths[frame_counter - 14] = *DisplayRect; + + if(vdc->burst_mode) + { + if(fc_vrm && SHOULD_DRAW) + { +#if defined(WANT_8BPP) + //if(target_ptr8) + DrawOverscan(vdc, target_ptr8, DisplayRect); +#elif defined(WANT_16BPP) + //else if(target_ptr16) + DrawOverscan(vdc, target_ptr16, DisplayRect); +#elif defined(WANT_32BPP) + //else + DrawOverscan(vdc, target_ptr, DisplayRect); +#endif + } + } + else if(vdc->display_counter >= (VDS + VSW) && vdc->display_counter < (VDS + VSW + VDW + 1)) + { + if(vdc->display_counter == (VDS + VSW)) + vdc->BG_YOffset = vdc->BYR; + else + vdc->BG_YOffset++; + vdc->BG_XOffset = vdc->BXR; + + if(fc_vrm) + { + uint32 start, end; + + CalcStartEnd(vdc, start, end); + + if((vdc->CR & 0x80) && SHOULD_DRAW) + { + if(userle & (chip ? ULE_BG1 : ULE_BG0)) + DrawBG(vdc, end - start + (vdc->BG_XOffset & 7), bg_linebuf); + else + memset(bg_linebuf, 0, end - start + (vdc->BG_XOffset & 7)); + } + + if((vdc->CR & 0x40) && (SHOULD_DRAW || (vdc->CR & 0x03))) // Don't skip sprite drawing if we can generate sprite #0 or sprite overflow IRQs. + { + if((userle & (chip ? ULE_SPR1 : ULE_SPR0)) || (vdc->CR & 0x03)) + DrawSprites(vdc, end - start, spr_linebuf + 0x20); + + if(!(userle & (chip ? ULE_SPR1 : ULE_SPR0))) + memset(spr_linebuf + 0x20, 0, sizeof(uint16) * (end - start)); + } + + if(SHOULD_DRAW) + { + static const int xs[2][3] = { + { 24 - 43, 38, 96 - 43 * 2 }, + { 24, 38, 96 } + }; + + int32 width = end - start; + int32 source_offset = 0; + int32 target_offset = start - (128 + 8 + xs[correct_aspect][vce.dot_clock]); + + if(target_offset < 0) + { + width += target_offset; + source_offset += 0 - target_offset; + target_offset = 0; + } + + if((target_offset + width) > DisplayRect->w) + width = (int32)DisplayRect->w - target_offset; + + //if(vdc->display_counter == 50) + // MDFN_DispMessage("soffset=%d, toffset=%d, width=%d", source_offset, target_offset, width); + + if(width > 0) + { +#if defined(WANT_8BPP) + //if(target_ptr8) + { + switch(vdc->CR & 0xC0) + { + case 0xC0: MixBGSPR_Generic(width, bg_linebuf + (vdc->BG_XOffset & 7) + source_offset, spr_linebuf + 0x20 + source_offset, target_ptr8 + target_offset); + break; + + case 0x80: MixBGOnly(width, bg_linebuf + (vdc->BG_XOffset & 7) + source_offset, target_ptr8 + target_offset); + break; + + case 0x40: MixSPROnly(width, spr_linebuf + 0x20 + source_offset, target_ptr8 + target_offset); + break; + + case 0x00: MixNone(width, target_ptr8 + target_offset); + break; + } + } +#elif defined(WANT_16BPP) + //else if(target_ptr16) + { + switch(vdc->CR & 0xC0) + { + case 0xC0: MixBGSPR_Generic(width, bg_linebuf + (vdc->BG_XOffset & 7) + source_offset, spr_linebuf + 0x20 + source_offset, target_ptr16 + target_offset); + break; + + case 0x80: MixBGOnly(width, bg_linebuf + (vdc->BG_XOffset & 7) + source_offset, target_ptr16 + target_offset); + break; + + case 0x40: MixSPROnly(width, spr_linebuf + 0x20 + source_offset, target_ptr16 + target_offset); + break; + + case 0x00: MixNone(width, target_ptr16 + target_offset); + break; + } + } +#elif defined(WANT_32BPP) + //else + switch(vdc->CR & 0xC0) + { + case 0xC0: MixBGSPR(width, bg_linebuf + (vdc->BG_XOffset & 7) + source_offset, spr_linebuf + 0x20 + source_offset, target_ptr + target_offset); + break; + + case 0x80: MixBGOnly(width, bg_linebuf + (vdc->BG_XOffset & 7) + source_offset, target_ptr + target_offset); + break; + + case 0x40: MixSPROnly(width, spr_linebuf + 0x20 + source_offset, target_ptr + target_offset); + break; + + case 0x00: MixNone(width, target_ptr + target_offset); + break; + } +#endif + } + +#if defined(WANT_8BPP) + //if(target_ptr8) + DrawOverscan(vdc, target_ptr8, DisplayRect, false, target_offset, target_offset + width); +#elif defined(WANT_16BPP) + //else if(target_ptr16) + DrawOverscan(vdc, target_ptr16, DisplayRect, false, target_offset, target_offset + width); +#elif defined(WANT_32BPP) + //else + DrawOverscan(vdc, target_ptr, DisplayRect, false, target_offset, target_offset + width); +#endif + } // end if(SHOULD_DRAW) + } + } + else if(SHOULD_DRAW && fc_vrm) // Hmm, overscan... + { +#if defined(WANT_8BPP) + //if(target_ptr8) + DrawOverscan(vdc, target_ptr8, DisplayRect); +#elif defined(WANT_16BPP) + //else if(target_ptr16) + DrawOverscan(vdc, target_ptr16, DisplayRect); +#elif defined(WANT_32BPP) + //else + DrawOverscan(vdc, target_ptr, DisplayRect); +#endif + } + } + + if(VDC_TotalChips == 2 && SHOULD_DRAW && fc_vrm) + { +#if defined(WANT_8BPP) + //if(surface->format.bpp == 8) + MixVPC(DisplayRect->w, line_buffer[0] + DisplayRect->x, line_buffer[1] + DisplayRect->x, surface->pixels8 + (frame_counter - 14) * surface->pitchinpix + DisplayRect->x); +#elif defined(WANT_16BPP) + //else if(surface->format.bpp == 16) + MixVPC(DisplayRect->w, line_buffer[0] + DisplayRect->x, line_buffer[1] + DisplayRect->x, surface->pixels16 + (frame_counter - 14) * surface->pitchinpix + DisplayRect->x); +#elif defined(WANT_32BPP) + //else + MixVPC(DisplayRect->w, line_buffer[0] + DisplayRect->x, line_buffer[1] + DisplayRect->x, surface->pixels + (frame_counter - 14) * surface->pitchinpix + DisplayRect->x); +#endif + } + + if(SHOULD_DRAW && fc_vrm) + { + MDFN_MidLineUpdate(espec, frame_counter - 14); + } + + for(int chip = 0; chip < VDC_TotalChips; chip++) + if((vdc_chips[chip]->CR & 0x08) && need_vbi[chip]) + vdc_chips[chip]->status |= VDCS_VD; + + HuC6280_Run(2); + + for(int chip = 0; chip < VDC_TotalChips; chip++) + if(vdc_chips[chip]->status & VDCS_VD) + { + VDC_DEBUG("VBlank IRQ"); + HuC6280_IRQBegin(MDFN_IQIRQ1); + } + + HuC6280_Run(455 - line_leadin1 - 2); + + if(PCE_IsCD) + { + PCECD_Run(HuCPU.timestamp * 3); + } + + for(int chip = 0; chip < VDC_TotalChips; chip++) + { + vdc = vdc_chips[chip]; + vdc->RCRCount++; + + //vdc->BG_YOffset = (vdc->BG_YOffset + 1); + vdc->display_counter++; + + if(vdc->sat_dma_slcounter) + { + vdc->sat_dma_slcounter--; + if(!vdc->sat_dma_slcounter) + { + if(vdc->DCR & 0x01) + { + VDC_DEBUG("Sprite DMA IRQ"); + vdc->status |= VDCS_DS; + HuC6280_IRQBegin(MDFN_IQIRQ1); + } + } + } + + if(vdc->display_counter == (VDS + VSW + VDW + VCR + 3)) + { + vdc->display_counter = 0; + } + } + + frame_counter = (frame_counter + 1) % (vce.lc263 ? 263 : 262); + //printf("%d\n", vce.lc263); + } while(frame_counter != VBlankFL); // big frame loop! + + // Hack for the input latency-reduction hack, part 2. + if(!skip) + { + for(int y = DisplayRect->y; y < DisplayRect->y + DisplayRect->h; y++) + { + if(!LineWidths[y].w) + { + uint32 *target_ptr = NULL; + uint16 *target_ptr16 = NULL; + uint8 *target_ptr8 = NULL; + +#if defined(WANT_8BPP) + //if(surface->format.bpp == 8) + target_ptr8 = surface->pixels8 + y * surface->pitchinpix; +#elif defined(WANT_16BPP) + //if(surface->format.bpp == 16) + target_ptr16 = surface->pixels16 + y * surface->pitchinpix; +#elif defined(WANT_32BPP) + //else + target_ptr = surface->pixels + y * surface->pitchinpix; +#endif + + LineWidths[y] = *DisplayRect; + + if(target_ptr8) + DrawOverscan(vdc_chips[0], target_ptr8, DisplayRect); + else if(target_ptr16) + DrawOverscan(vdc_chips[0], target_ptr16, DisplayRect); + else + DrawOverscan(vdc_chips[0], target_ptr, DisplayRect); + + MDFN_MidLineUpdate(espec, y); + } + } + } +} + +void VDC_Reset(void) +{ + vdc_chips[0]->read_buffer = 0xFFFF; + + vpc.priority[0] = vpc.priority[1] = 0x11; + + vdc_chips[0]->HSR = vdc_chips[0]->HDR = vdc_chips[0]->VSR = vdc_chips[0]->VDR = vdc_chips[0]->VCR = 0xFF; // Needed for Body Conquest 2 + + if(vdc_chips[1]) + { + vdc_chips[1]->read_buffer = 0xFFFF; + vdc_chips[1]->HSR = vdc_chips[1]->HDR = vdc_chips[1]->VSR = vdc_chips[1]->VDR = vdc_chips[1]->VCR = 0xFF; // and for HES playback to not go bonkers + } + frame_counter = 0; +} + +void VDC_Power(void) +{ + for(int chip = 0; chip < VDC_TotalChips; chip++) + memset(vdc_chips[chip], 0, sizeof(vdc_t)); + VDC_Reset(); +} + +// Warning: As of 0.8.x, this custom colormap function will only work if it's called from VDC_Init(), in the Load() +// game load path. +static bool LoadCustomPalette(const char *path) MDFN_COLD; +static bool LoadCustomPalette(const char *path) +{ + MDFN_printf(_("Loading custom palette from \"%s\"...\n"), path); + MDFN_indent(1); + + CustomColorMap = NULL; + + try + { + FileStream fp(path, FileStream::MODE_READ); + + CustomColorMap = new uint8[1024 * 3]; + + fp.read(CustomColorMap, 512 * 3, true); + if(fp.read(CustomColorMap, 512 * 3, false) == 512 * 3) + CustomColorMapLen = 1024; + else + { + MDFN_printf(_("Palette is missing the full set of 512 greyscale entries. Strip-colorburst entries will be calculated.\n")); + CustomColorMapLen = 512; + } + } + catch(std::exception &e) + { + if(CustomColorMap) + { + MDFN_free(CustomColorMap); + CustomColorMap = NULL; + } + + MDFN_printf("%s\n", e.what()); + MDFN_indent(-1); + return(false); + } + + MDFN_indent(-1); + + return(true); +} + + +void VDC_Init(int sgx) +{ + unlimited_sprites = MDFN_GetSettingB("pce_fast.nospritelimit"); + correct_aspect = MDFN_GetSettingB("pce_fast.correct_aspect"); + userle = ~0; + + VDC_TotalChips = sgx ? 2 : 1; + + for(int chip = 0; chip < VDC_TotalChips; chip++) + { + vdc_chips[chip] = (vdc_t *)MDFN_malloc(sizeof(vdc_t), "VDC"); + } + + LoadCustomPalette(MDFN_MakeFName(MDFNMKF_PALETTE, 0, NULL).c_str()); + + MixBGSPR = MixBGSPR_Generic; + +#ifdef ARCH_X86 + #ifndef __x86_64__ + if(cputest_get_flags() & CPUTEST_FLAG_CMOV) + { + puts("CMOV"); + MixBGSPR = MixBGSPR_x86_CMOV; + } + else + #endif + MixBGSPR = MixBGSPR_x86; +#endif + +} + +void VDC_Close(void) +{ + for(int chip = 0; chip < VDC_TotalChips; chip++) + { + if(vdc_chips[chip]) + MDFN_free(vdc_chips[chip]); + vdc_chips[chip] = NULL; + } + VDC_TotalChips = 0; +} + +int VDC_StateAction(StateMem *sm, int load, int data_only) +{ + SFORMAT VCE_StateRegs[] = + { + SFVARN(vce.CR, "VCECR"), + SFVARN(vce.lc263, "lc263"), + SFVARN(vce.bw, "bw"), + SFVARN(vce.dot_clock, "dot clock"), + SFVARN(vce.ctaddress, "ctaddress"), + SFARRAY16N(vce.color_table, 0x200, "color_table"), + SFEND + }; + + + int ret = MDFNSS_StateAction(sm, load, data_only, VCE_StateRegs, "VCE"); + + int max_chips = VDC_TotalChips; + + if(VDC_TotalChips == 2) + { + SFORMAT VPC_StateRegs[] = + { + SFVARN(vpc.st_mode, "st_mode"), + SFARRAYN(vpc.priority, 2, "priority"), + SFARRAY16N(vpc.winwidths, 2, "winwidths"), + SFEND + }; + ret &= MDFNSS_StateAction(sm, load, data_only, VPC_StateRegs, "VPC"); + } + + for(int chip = 0; chip < max_chips; chip++) + { + vdc_t *vdc = vdc_chips[chip]; + SFORMAT VDC_StateRegs[] = + { + SFVARN(vdc->display_counter, "display_counter"), + SFVARN(vdc->sat_dma_slcounter, "sat_dma_slcounter"), + + SFVARN(vdc->select, "select"), + SFVARN(vdc->MAWR, "MAWR"), + SFVARN(vdc->MARR, "MARR"), + SFVARN(vdc->CR, "CR"), + SFVARN(vdc->RCR, "RCR"), + SFVARN(vdc->BXR, "BXR"), + SFVARN(vdc->BYR, "BYR"), + SFVARN(vdc->MWR, "MWR"), + + SFVARN(vdc->HSR, "HSR"), + SFVARN(vdc->HDR, "HDR"), + SFVARN(vdc->VSR, "VSR"), + SFVARN(vdc->VDR, "VDR"), + + SFVARN(vdc->VCR, "VCR"), + SFVARN(vdc->DCR, "DCR"), + SFVARN(vdc->SOUR, "SOUR"), + SFVARN(vdc->DESR, "DESR"), + SFVARN(vdc->LENR, "LENR"), + SFVARN(vdc->SATB, "SATB"), + + SFVARN(vdc->RCRCount, "RCRCount"), + + + SFVARN(vdc->read_buffer, "read_buffer"), + SFVARN(vdc->write_latch, "write_latch"), + SFVARN(vdc->status, "status"), + + SFARRAY16N(vdc->SAT, 0x100, "SAT"), + + SFARRAY16N(vdc->VRAM, VRAM_Size, "VRAM"), + SFVARN(vdc->DMAReadBuffer, "DMAReadBuffer"), + SFVARN(vdc->DMAReadWrite, "DMAReadWrite"), + SFVARN(vdc->DMARunning, "DMARunning"), + SFVARN(vdc->SATBPending, "SATBPending"), + SFVARN(vdc->burst_mode, "burst_mode"), + + SFVARN(vdc->BG_YOffset, "BG_YOffset"), + SFVARN(vdc->BG_XOffset, "BG_XOffset"), + SFVAR(frame_counter), + SFVARN(VDS, "VDS_cache"), + SFVARN(VSW, "VSW_cache"), + SFVARN(VDW, "VDW_cache"), + SFVARN(VCR, "VCR_cache"), + SFVARN(VBlankFL, "VBlankFL_cache"), + + SFARRAY32(need_vbi, 2), + SFVAR(line_leadin1), + SFVAR(magical), + SFVAR(cyc_tot), + SFEND + }; + + ret &= MDFNSS_StateAction(sm, load, data_only, VDC_StateRegs, chip ? "VDC1" : "VDC0"); + + if(load) + { + for(int x = 0; x < VRAM_Size; x++) + { + FixTileCache(vdc, x); + vdc->spr_tile_clean[x >> 6] = 0; + } + for(int x = 0; x < 512; x++) + FixPCache(x); + RebuildSATCache(vdc); + } + + } + + return(ret); +} + +}; diff --git a/mednafen/pce_fast-09333/vdc.h b/mednafen/pce_fast-09333/vdc.h new file mode 100644 index 0000000..065b103 --- /dev/null +++ b/mednafen/pce_fast-09333/vdc.h @@ -0,0 +1,211 @@ +#ifndef _PCE_VDC_H +#define _PCE_VDC_H + +namespace PCE_Fast +{ + +#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]; + uint32 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_chips[2]; +extern int VDC_TotalChips; + + +void VDC_SetPixelFormat(const MDFN_PixelFormat &format) MDFN_COLD; +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; + vdc_t *vdc; + + if(SGX) + { + A &= 0x1F; + switch(A) + { + case 0x8: return(vpc.priority[0]); + case 0x9: return(vpc.priority[1]); + case 0xA: return(vpc.winwidths[0]); + case 0xB: return(vpc.winwidths[0] >> 8); + case 0xC: return(vpc.winwidths[1]); + case 0xD: return(vpc.winwidths[1] >> 8); + case 0xE: return(0); + } + if(A & 0x8) return(0); + chip = (A & 0x10) >> 4; + vdc = vdc_chips[chip]; + A &= 0x3; + } + else + { + vdc = vdc_chips[0]; + A &= 0x3; + } + + switch(A) + { + case 0x0: ret = vdc->status; + + vdc->status &= ~0x3F; + + if(SGX) + { + if(!(vdc_chips[0]->status & 0x3F) && !(vdc_chips[1]->status & 0x3F)) + HuC6280_IRQEnd(MDFN_IQIRQ1); + } + else + 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 diff --git a/mednafen/pce_fast-09333/vpc_mix_inner.inc b/mednafen/pce_fast-09333/vpc_mix_inner.inc new file mode 100644 index 0000000..9f4137a --- /dev/null +++ b/mednafen/pce_fast-09333/vpc_mix_inner.inc @@ -0,0 +1,35 @@ + + uint32 vdc2_pixel, vdc1_pixel; + + vdc2_pixel = vdc1_pixel = vce.color_table_cache[0]; + + if(pb & 1) + vdc1_pixel = lb0[x]; + + if(pb & 2) + vdc2_pixel = lb1[x]; + + /* Dai MakaiMura uses setting 1, and expects VDC #2 sprites in front of VDC #1 background, but + behind VDC #1's sprites. + */ + switch(pb >> 2) + { + case 1: + //if((vdc2_pixel & (amask << 2)) && !(vdc1_pixel & (amask << 2))) + // vdc1_pixel |= amask; + vdc1_pixel |= (((vdc2_pixel ^ vdc1_pixel) & vdc2_pixel) >> 2) & amask; + break; + + case 2: + //if((vdc1_pixel & (amask << 2)) && !(vdc2_pixel & (amask << 2)) && !(vdc2_pixel & amask)) + // vdc1_pixel |= amask; + puts("MOO"); + // TODO: Verify that this is correct logic. + { + const uint32 intermediate = ((vdc1_pixel ^ vdc2_pixel) & vdc1_pixel) >> 2; + vdc1_pixel |= (intermediate ^ vdc2_pixel) & intermediate & amask; + } + break; + } + target[x] = (vdc1_pixel & amask) ? vdc2_pixel : vdc1_pixel; + diff --git a/mednafen/video/surface.cpp b/mednafen/video/surface.cpp index 32e0680..8d3dc25 100644 --- a/mednafen/video/surface.cpp +++ b/mednafen/video/surface.cpp @@ -69,14 +69,26 @@ void MDFN_Surface::Init(void *const p_pixels, const uint32 p_width, const uint32 pixels16 = NULL; pixels = NULL; +#if defined(WANT_8BPP) + palette = NULL; +#endif if(!(rpix = calloc(1, p_pitchinpix * p_height * (nf.bpp / 8)))) throw(1); - if(nf.bpp == 16) +#if defined(WANT_8BPP) + //if(nf.bpp == 8) + { + pixels8 = (uint8 *)rpix; + palette = (MDFN_PaletteEntry*)calloc(sizeof(MDFN_PaletteEntry), 256); + } +#elif defined(WANT_16BPP) + //if(nf.bpp == 16) pixels16 = (uint16 *)rpix; - else +#elif defined(WANT_32BPP) + //else pixels = (uint32 *)rpix; +#endif w = p_width; h = p_height; @@ -94,9 +106,17 @@ void MDFN_Surface::SetFormat(const MDFN_PixelFormat &nf, bool convert) MDFN_Surface::~MDFN_Surface() { - if(pixels) - free(pixels); +#if defined(WANT_16BPP) if(pixels16) free(pixels16); +#elif defined(WANT_32BPP) + if(pixels) + free(pixels); +#elif defined(WANT_8BPP) + pixels8 = NULL; + if(palette) + free(palette); + palette = NULL; +#endif } diff --git a/mednafen/video/surface.h b/mednafen/video/surface.h index 587df2b..3a83a53 100644 --- a/mednafen/video/surface.h +++ b/mednafen/video/surface.h @@ -33,6 +33,11 @@ #define MAKECOLOR(r, g, b, a) (((r >> RED_EXPAND) << RED_SHIFT) | ((g >> GREEN_EXPAND) << GREEN_SHIFT) | ((b >> BLUE_EXPAND) << BLUE_SHIFT)) #endif +struct MDFN_PaletteEntry +{ + uint8 r, g, b; +}; + typedef struct { int32 x, y, w, h; @@ -126,6 +131,8 @@ class MDFN_Surface //typedef struct int32 pitchinpix; // New name, new code should use this. }; + MDFN_PaletteEntry *palette; + MDFN_PixelFormat format; void SetFormat(const MDFN_PixelFormat &new_format, bool convert); diff --git a/scrc32.cpp b/scrc32.cpp index e69de29..3154087 100644 --- a/scrc32.cpp +++ b/scrc32.cpp @@ -0,0 +1,86 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WANT_CRC32 + +static const unsigned long crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +#define DO1_CRC32(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define DO2_CRC32(buf) DO1_CRC32(buf); DO1_CRC32(buf); +#define DO4_CRC32(buf) DO2_CRC32(buf); DO2_CRC32(buf); +#define DO8_CRC32(buf) DO4_CRC32(buf); DO4_CRC32(buf); + +unsigned long crc32(unsigned long crc, const unsigned char *buf, unsigned int len) +{ + if (buf == 0) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) + { + DO8_CRC32(buf); + len -= 8; + } + if (len) do { + DO1_CRC32(buf); + } while (--len); + return crc ^ 0xffffffffL; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/scrc32.h b/scrc32.h index 2c64382..bfa7614 100644 --- a/scrc32.h +++ b/scrc32.h @@ -5,84 +5,7 @@ extern "C" { #endif -#ifdef WANT_CRC32 - -static const unsigned long crc_table[256] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL -}; - -#define DO1_CRC32(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); -#define DO2_CRC32(buf) DO1_CRC32(buf); DO1_CRC32(buf); -#define DO4_CRC32(buf) DO2_CRC32(buf); DO2_CRC32(buf); -#define DO8_CRC32(buf) DO4_CRC32(buf); DO4_CRC32(buf); - -static unsigned long crc32(unsigned long crc, const unsigned char *buf, unsigned int len) -{ - if (buf == 0) return 0L; - crc = crc ^ 0xffffffffL; - while (len >= 8) - { - DO8_CRC32(buf); - len -= 8; - } - if (len) do { - DO1_CRC32(buf); - } while (--len); - return crc ^ 0xffffffffL; -} - -#endif +unsigned long crc32(unsigned long crc, const unsigned char *buf, unsigned int len); #ifdef __cplusplus } diff --git a/stubs.cpp b/stubs.cpp index 4a83bbd..9240028 100644 --- a/stubs.cpp +++ b/stubs.cpp @@ -92,6 +92,11 @@ void MDFND_Message(const char *str) void MDFND_MidSync(const EmulateSpecStruct *) {} +void MDFN_MidLineUpdate(EmulateSpecStruct *espec, int y) +{ + //MDFND_MidLineUpdate(espec, y); +} + void MDFND_PrintError(const char* err) { if (log_cb)