diff --git a/Makefile b/Makefile index b3e34f8..853316f 100644 --- a/Makefile +++ b/Makefile @@ -176,6 +176,63 @@ HW_SOUND_SOURCES += $(MEDNAFEN_DIR)/hw_sound/gb_apu/Gb_Apu.cpp \ $(MEDNAFEN_DIR)/hw_sound/gb_apu/Gb_Oscs.cpp EXTRA_CORE_INCDIR = -I$(MEDNAFEN_DIR)/hw_sound/ -I$(MEDNAFEN_DIR)/include/blip TARGET_NAME := mednafen_gba_libretro +else ifeq ($(core), snes) + core = snes + NEED_BPP = 32 + NEED_BLIP = 1 + NEED_STEREO_SOUND = 1 + CORE_DEFINE := -DWANT_SNES_EMU + CORE_DIR := $(MEDNAFEN_DIR)/snes + +CORE_SOURCES := $(CORE_DIR)/interface.cpp \ + $(CORE_DIR)/src/cartridge/cartridge.cpp \ + $(CORE_DIR)/src/cartridge/header.cpp \ + $(CORE_DIR)/src/cartridge/gameboyheader.cpp \ + $(CORE_DIR)/src/cartridge/serialization.cpp \ + $(CORE_DIR)/src/cheat/cheat.cpp \ + $(CORE_DIR)/src/chip/21fx/21fx.cpp \ + $(CORE_DIR)/src/chip/bsx/bsx.cpp \ + $(CORE_DIR)/src/chip/bsx/bsx_base.cpp \ + $(CORE_DIR)/src/chip/bsx/bsx_cart.cpp \ + $(CORE_DIR)/src/chip/bsx/bsx_flash.cpp \ + $(CORE_DIR)/src/chip/cx4/cx4.cpp \ + $(CORE_DIR)/src/chip/cx4/data.cpp \ + $(CORE_DIR)/src/chip/cx4/functions.cpp \ + $(CORE_DIR)/src/chip/cx4/oam.cpp \ + $(CORE_DIR)/src/chip/cx4/opcodes.cpp \ + $(CORE_DIR)/src/chip/cx4/serialization.cpp \ + $(CORE_DIR)/src/chip/dsp1/dsp1.cpp \ + $(CORE_DIR)/src/chip/dsp2/dsp2.cpp \ + $(CORE_DIR)/src/chip/dsp3/dsp3.cpp \ + $(CORE_DIR)/src/chip/dsp4/dsp4.cpp \ + $(CORE_DIR)/src/chip/obc1/obc1.cpp \ + $(CORE_DIR)/src/chip/sa1/sa1.cpp \ + $(CORE_DIR)/src/chip/sdd1/sdd1.cpp \ + $(CORE_DIR)/src/chip/spc7110/spc7110.cpp \ + $(CORE_DIR)/src/chip/srtc/srtc.cpp \ + $(CORE_DIR)/src/chip/st010/st010.cpp \ + $(CORE_DIR)/src/chip/st011/st011.cpp \ + $(CORE_DIR)/src/chip/st018/st018.cpp \ + $(CORE_DIR)/src/chip/superfx/superfx.cpp \ + $(CORE_DIR)/src/chip/supergameboy/supergameboy.cpp \ + $(CORE_DIR)/src/cpu/cpu.cpp \ + $(CORE_DIR)/src/cpu/core/core.cpp \ + $(CORE_DIR)/src/cpu/scpu/scpu.cpp \ + $(CORE_DIR)/src/dsp/sdsp/sdsp.cpp \ + $(CORE_DIR)/src/lib/libco/libco.c \ + $(CORE_DIR)/src/memory/memory.cpp \ + $(CORE_DIR)/src/memory/smemory/smemory.cpp \ + $(CORE_DIR)/src/ppu/ppu.cpp \ + $(CORE_DIR)/src/ppu/bppu/bppu.cpp \ + $(CORE_DIR)/src/smp/smp.cpp \ + $(CORE_DIR)/src/smp/core/core.cpp \ + $(CORE_DIR)/src/smp/ssmp/ssmp.cpp \ + $(CORE_DIR)/src/system/system.cpp + +HW_SOUND_SOURCES += $(MEDNAFEN_DIR)/sound/Fir_Resampler.cpp +EXTRA_CORE_INCDIR = -I$(MEDNAFEN_DIR)/hw_sound/ -I$(MEDNAFEN_DIR)/include/blip -I$(MEDNAFEN_DIR)/snes/src/lib +TARGET_NAME := mednafen_snes_libretro +LDFLAGS += -ldl endif ifeq ($(NEED_BLIP), 1) diff --git a/libretro.cpp b/libretro.cpp index 789af88..39011bc 100644 --- a/libretro.cpp +++ b/libretro.cpp @@ -100,6 +100,20 @@ static Deinterlacer deint; #define FB_WIDTH 240 #define FB_HEIGHT 160 +#elif defined(WANT_SNES_EMU) +#define MEDNAFEN_CORE_NAME_MODULE "snes" +#define MEDNAFEN_CORE_NAME "Mednafen bSNES" +#define MEDNAFEN_CORE_VERSION "v0.9.26" +#define MEDNAFEN_CORE_EXTENSIONS "sfc|SFC|zip|ZIP" +#define MEDNAFEN_CORE_TIMING_FPS 60.10 +#define MEDNAFEN_CORE_GEOMETRY_BASE_W (game->nominal_width) +#define MEDNAFEN_CORE_GEOMETRY_BASE_H (game->nominal_height) +#define MEDNAFEN_CORE_GEOMETRY_MAX_W 512 +#define MEDNAFEN_CORE_GEOMETRY_MAX_H 512 +#define MEDNAFEN_CORE_GEOMETRY_ASPECT_RATIO (4.0 / 3.0) +#define FB_WIDTH 512 +#define FB_HEIGHT 512 + #endif #ifdef WANT_16BPP @@ -396,6 +410,41 @@ static void update_input(void) // Possible endian bug ... game->SetInput(0, "gamepad", &input_buf); +#elif defined(WANT_SNES_EMU) + static uint8_t input_buf[5][2]; + + static unsigned map[] = { + RETRO_DEVICE_ID_JOYPAD_B, + RETRO_DEVICE_ID_JOYPAD_Y, + RETRO_DEVICE_ID_JOYPAD_SELECT, + RETRO_DEVICE_ID_JOYPAD_START, + RETRO_DEVICE_ID_JOYPAD_UP, + RETRO_DEVICE_ID_JOYPAD_DOWN, + RETRO_DEVICE_ID_JOYPAD_LEFT, + RETRO_DEVICE_ID_JOYPAD_RIGHT, + RETRO_DEVICE_ID_JOYPAD_A, + RETRO_DEVICE_ID_JOYPAD_X, + RETRO_DEVICE_ID_JOYPAD_L, + RETRO_DEVICE_ID_JOYPAD_R, + }; + + if (input_state_cb) + { + for (unsigned j = 0; j < 5; j++) + { + uint16_t input_state = 0; + for (unsigned i = 0; i < 13; i++) + input_state |= input_state_cb(j, RETRO_DEVICE_JOYPAD, 0, map[i]) ? (1 << i) : 0; + + // Input data must be little endian. + input_buf[j][0] = (input_state >> 0) & 0xff; + input_buf[j][1] = (input_state >> 8) & 0xff; + } + } + + // Possible endian bug ... + for (unsigned i = 0; i < 5; i++) + game->SetInput(i, "gamepad", &input_buf[i][0]); #else static uint16_t input_buf[1]; input_buf[0] = 0; diff --git a/mednafen/mednafen.cpp b/mednafen/mednafen.cpp index 084e5d7..c4e4320 100644 --- a/mednafen/mednafen.cpp +++ b/mednafen/mednafen.cpp @@ -527,6 +527,8 @@ extern MDFNGI EmulatedWSwan; extern MDFNGI EmulatedNGP; #elif defined(WANT_GBA_EMU) extern MDFNGI EmulatedGBA; +#elif defined(WANT_SNES_EMU) +extern MDFNGI EmulatedSNES; #endif bool MDFNI_InitializeModules(const std::vector &ExternalSystems) diff --git a/mednafen/settings.cpp b/mednafen/settings.cpp index 943a9ad..9927392 100644 --- a/mednafen/settings.cpp +++ b/mednafen/settings.cpp @@ -109,6 +109,13 @@ bool MDFN_GetSettingB(const char *name) /* LIBRETRO */ if(!strcmp("libretro.cd_load_into_ram", name)) return 0; + /* SNES */ + if(!strcmp("snes.correct_aspect", name)) + return 0; + if(!strcmp("snes.input.port1.multitap", name)) + return 0; + if(!strcmp("snes.input.port2.multitap", name)) + return 0; /* PCE_FAST */ if(!strcmp("pce_fast.input.multitap", name)) return 1; diff --git a/mednafen/snes/Makefile.am b/mednafen/snes/Makefile.am new file mode 100644 index 0000000..404d479 --- /dev/null +++ b/mednafen/snes/Makefile.am @@ -0,0 +1,80 @@ +AM_CFLAGS = @AM_CFLAGS@ @SNES_EXTRA_FLAGS@ +AM_CXXFLAGS = @AM_CXXFLAGS@ @SNES_EXTRA_FLAGS@ @SNES_EXTRA_CXXFLAGS@ +AUTOMAKE_OPTIONS = subdir-objects +DEFS = -DLOCALEDIR=\"$(datadir)/locale\" @DEFS@ @MATH_OPTIMIZER_FLAGS@ -DNOMINMAX +DEFAULT_INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/intl -I$(top_srcdir)/src/snes/src/lib + +noinst_LIBRARIES = libsnes.a + +meow = interface.cpp + + +meow += src/lib/libco/libco.c + + +#meow += src/lib/nall/string.cpp + +################# +### utilities ### +################# + +meow += src/cartridge/cartridge.cpp src/cheat/cheat.cpp + +############## +### memory ### +############## + +meow += src/memory/memory.cpp src/memory/smemory/smemory.cpp + +########### +### cpu ### +########### + +meow += src/cpu/cpu.cpp src/cpu/core/core.cpp src/cpu/scpu/scpu.cpp + +########### +### smp ### +########### + +meow += src/smp/smp.cpp src/smp/core/core.cpp src/smp/ssmp/ssmp.cpp + +########### +### dsp ### +########### + +# src/dsp/adsp/adsp.cpp +meow += src/dsp/sdsp/sdsp.cpp + +########### +### ppu ### +########### + +meow += src/ppu/ppu.cpp src/ppu/bppu/bppu.cpp + +############## +### system ### +############## + +meow += src/system/system.cpp + +##################### +### special chips ### +##################### + +meow += src/chip/sa1/sa1.cpp src/chip/bsx/bsx.cpp src/chip/srtc/srtc.cpp src/chip/sdd1/sdd1.cpp +meow += src/chip/spc7110/spc7110.cpp src/chip/cx4/cx4.cpp + +meow += src/chip/dsp1/dsp1.cpp src/chip/dsp2/dsp2.cpp src/chip/dsp3/dsp3.cpp src/chip/dsp4/dsp4.cpp + +meow += src/chip/obc1/obc1.cpp + +meow += src/chip/st010/st010.cpp src/chip/st011/st011.cpp src/chip/st018/st018.cpp + +meow += src/chip/superfx/superfx.cpp + +meow += src/chip/supergameboy/supergameboy.cpp + +meow += src/chip/21fx/21fx.cpp + +libsnes_a_SOURCES = $(meow) + diff --git a/mednafen/snes/Makefile.in b/mednafen/snes/Makefile.in new file mode 100644 index 0000000..b3a8b4a --- /dev/null +++ b/mednafen/snes/Makefile.in @@ -0,0 +1,1096 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@ +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/snes +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_$(V)) +am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY)) +am__v_AR_0 = @echo " AR " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +libsnes_a_AR = $(AR) $(ARFLAGS) +libsnes_a_LIBADD = +am__dirstamp = $(am__leading_dot)dirstamp +am__objects_1 = interface.$(OBJEXT) src/lib/libco/libco.$(OBJEXT) \ + src/cartridge/cartridge.$(OBJEXT) src/cheat/cheat.$(OBJEXT) \ + src/memory/memory.$(OBJEXT) \ + src/memory/smemory/smemory.$(OBJEXT) src/cpu/cpu.$(OBJEXT) \ + src/cpu/core/core.$(OBJEXT) src/cpu/scpu/scpu.$(OBJEXT) \ + src/smp/smp.$(OBJEXT) src/smp/core/core.$(OBJEXT) \ + src/smp/ssmp/ssmp.$(OBJEXT) src/dsp/sdsp/sdsp.$(OBJEXT) \ + src/ppu/ppu.$(OBJEXT) src/ppu/bppu/bppu.$(OBJEXT) \ + src/system/system.$(OBJEXT) src/chip/sa1/sa1.$(OBJEXT) \ + src/chip/bsx/bsx.$(OBJEXT) src/chip/srtc/srtc.$(OBJEXT) \ + src/chip/sdd1/sdd1.$(OBJEXT) \ + src/chip/spc7110/spc7110.$(OBJEXT) src/chip/cx4/cx4.$(OBJEXT) \ + src/chip/dsp1/dsp1.$(OBJEXT) src/chip/dsp2/dsp2.$(OBJEXT) \ + src/chip/dsp3/dsp3.$(OBJEXT) src/chip/dsp4/dsp4.$(OBJEXT) \ + src/chip/obc1/obc1.$(OBJEXT) src/chip/st010/st010.$(OBJEXT) \ + src/chip/st011/st011.$(OBJEXT) src/chip/st018/st018.$(OBJEXT) \ + src/chip/superfx/superfx.$(OBJEXT) \ + src/chip/supergameboy/supergameboy.$(OBJEXT) \ + src/chip/21fx/21fx.$(OBJEXT) +am_libsnes_a_OBJECTS = $(am__objects_1) +libsnes_a_OBJECTS = $(am_libsnes_a_OBJECTS) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +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_$(V)) +am__v_CXX_ = $(am__v_CXX_$(AM_DEFAULT_VERBOSITY)) +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_$(V)) +am__v_CXXLD_ = $(am__v_CXXLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CXXLD_0 = @echo " CXXLD " $@; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(libsnes_a_SOURCES) +DIST_SOURCES = $(libsnes_a_SOURCES) +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@ @SNES_EXTRA_FLAGS@ +AM_CXXFLAGS = @AM_CXXFLAGS@ @SNES_EXTRA_FLAGS@ @SNES_EXTRA_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@ @MATH_OPTIMIZER_FLAGS@ -DNOMINMAX +DEPDIR = @DEPDIR@ +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@ +MATH_OPTIMIZER_FLAGS = @MATH_OPTIMIZER_FLAGS@ +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@ +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@ +XMKMF = @XMKMF@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +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@ +lt_ECHO = @lt_ECHO@ +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/snes/src/lib +noinst_LIBRARIES = libsnes.a + +#meow += src/lib/nall/string.cpp + +################# +### utilities ### +################# + +############## +### memory ### +############## + +########### +### cpu ### +########### + +########### +### smp ### +########### + +########### +### dsp ### +########### + +# src/dsp/adsp/adsp.cpp + +########### +### ppu ### +########### + +############## +### system ### +############## + +##################### +### special chips ### +##################### +meow = interface.cpp src/lib/libco/libco.c src/cartridge/cartridge.cpp \ + src/cheat/cheat.cpp src/memory/memory.cpp \ + src/memory/smemory/smemory.cpp src/cpu/cpu.cpp \ + src/cpu/core/core.cpp src/cpu/scpu/scpu.cpp src/smp/smp.cpp \ + src/smp/core/core.cpp src/smp/ssmp/ssmp.cpp \ + src/dsp/sdsp/sdsp.cpp src/ppu/ppu.cpp src/ppu/bppu/bppu.cpp \ + src/system/system.cpp src/chip/sa1/sa1.cpp \ + src/chip/bsx/bsx.cpp src/chip/srtc/srtc.cpp \ + src/chip/sdd1/sdd1.cpp src/chip/spc7110/spc7110.cpp \ + src/chip/cx4/cx4.cpp src/chip/dsp1/dsp1.cpp \ + src/chip/dsp2/dsp2.cpp src/chip/dsp3/dsp3.cpp \ + src/chip/dsp4/dsp4.cpp src/chip/obc1/obc1.cpp \ + src/chip/st010/st010.cpp src/chip/st011/st011.cpp \ + src/chip/st018/st018.cpp src/chip/superfx/superfx.cpp \ + src/chip/supergameboy/supergameboy.cpp src/chip/21fx/21fx.cpp +libsnes_a_SOURCES = $(meow) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .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/snes/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/snes/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) +src/lib/libco/$(am__dirstamp): + @$(MKDIR_P) src/lib/libco + @: > src/lib/libco/$(am__dirstamp) +src/lib/libco/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/lib/libco/$(DEPDIR) + @: > src/lib/libco/$(DEPDIR)/$(am__dirstamp) +src/lib/libco/libco.$(OBJEXT): src/lib/libco/$(am__dirstamp) \ + src/lib/libco/$(DEPDIR)/$(am__dirstamp) +src/cartridge/$(am__dirstamp): + @$(MKDIR_P) src/cartridge + @: > src/cartridge/$(am__dirstamp) +src/cartridge/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/cartridge/$(DEPDIR) + @: > src/cartridge/$(DEPDIR)/$(am__dirstamp) +src/cartridge/cartridge.$(OBJEXT): src/cartridge/$(am__dirstamp) \ + src/cartridge/$(DEPDIR)/$(am__dirstamp) +src/cheat/$(am__dirstamp): + @$(MKDIR_P) src/cheat + @: > src/cheat/$(am__dirstamp) +src/cheat/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/cheat/$(DEPDIR) + @: > src/cheat/$(DEPDIR)/$(am__dirstamp) +src/cheat/cheat.$(OBJEXT): src/cheat/$(am__dirstamp) \ + src/cheat/$(DEPDIR)/$(am__dirstamp) +src/memory/$(am__dirstamp): + @$(MKDIR_P) src/memory + @: > src/memory/$(am__dirstamp) +src/memory/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/memory/$(DEPDIR) + @: > src/memory/$(DEPDIR)/$(am__dirstamp) +src/memory/memory.$(OBJEXT): src/memory/$(am__dirstamp) \ + src/memory/$(DEPDIR)/$(am__dirstamp) +src/memory/smemory/$(am__dirstamp): + @$(MKDIR_P) src/memory/smemory + @: > src/memory/smemory/$(am__dirstamp) +src/memory/smemory/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/memory/smemory/$(DEPDIR) + @: > src/memory/smemory/$(DEPDIR)/$(am__dirstamp) +src/memory/smemory/smemory.$(OBJEXT): \ + src/memory/smemory/$(am__dirstamp) \ + src/memory/smemory/$(DEPDIR)/$(am__dirstamp) +src/cpu/$(am__dirstamp): + @$(MKDIR_P) src/cpu + @: > src/cpu/$(am__dirstamp) +src/cpu/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/cpu/$(DEPDIR) + @: > src/cpu/$(DEPDIR)/$(am__dirstamp) +src/cpu/cpu.$(OBJEXT): src/cpu/$(am__dirstamp) \ + src/cpu/$(DEPDIR)/$(am__dirstamp) +src/cpu/core/$(am__dirstamp): + @$(MKDIR_P) src/cpu/core + @: > src/cpu/core/$(am__dirstamp) +src/cpu/core/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/cpu/core/$(DEPDIR) + @: > src/cpu/core/$(DEPDIR)/$(am__dirstamp) +src/cpu/core/core.$(OBJEXT): src/cpu/core/$(am__dirstamp) \ + src/cpu/core/$(DEPDIR)/$(am__dirstamp) +src/cpu/scpu/$(am__dirstamp): + @$(MKDIR_P) src/cpu/scpu + @: > src/cpu/scpu/$(am__dirstamp) +src/cpu/scpu/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/cpu/scpu/$(DEPDIR) + @: > src/cpu/scpu/$(DEPDIR)/$(am__dirstamp) +src/cpu/scpu/scpu.$(OBJEXT): src/cpu/scpu/$(am__dirstamp) \ + src/cpu/scpu/$(DEPDIR)/$(am__dirstamp) +src/smp/$(am__dirstamp): + @$(MKDIR_P) src/smp + @: > src/smp/$(am__dirstamp) +src/smp/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/smp/$(DEPDIR) + @: > src/smp/$(DEPDIR)/$(am__dirstamp) +src/smp/smp.$(OBJEXT): src/smp/$(am__dirstamp) \ + src/smp/$(DEPDIR)/$(am__dirstamp) +src/smp/core/$(am__dirstamp): + @$(MKDIR_P) src/smp/core + @: > src/smp/core/$(am__dirstamp) +src/smp/core/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/smp/core/$(DEPDIR) + @: > src/smp/core/$(DEPDIR)/$(am__dirstamp) +src/smp/core/core.$(OBJEXT): src/smp/core/$(am__dirstamp) \ + src/smp/core/$(DEPDIR)/$(am__dirstamp) +src/smp/ssmp/$(am__dirstamp): + @$(MKDIR_P) src/smp/ssmp + @: > src/smp/ssmp/$(am__dirstamp) +src/smp/ssmp/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/smp/ssmp/$(DEPDIR) + @: > src/smp/ssmp/$(DEPDIR)/$(am__dirstamp) +src/smp/ssmp/ssmp.$(OBJEXT): src/smp/ssmp/$(am__dirstamp) \ + src/smp/ssmp/$(DEPDIR)/$(am__dirstamp) +src/dsp/sdsp/$(am__dirstamp): + @$(MKDIR_P) src/dsp/sdsp + @: > src/dsp/sdsp/$(am__dirstamp) +src/dsp/sdsp/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/dsp/sdsp/$(DEPDIR) + @: > src/dsp/sdsp/$(DEPDIR)/$(am__dirstamp) +src/dsp/sdsp/sdsp.$(OBJEXT): src/dsp/sdsp/$(am__dirstamp) \ + src/dsp/sdsp/$(DEPDIR)/$(am__dirstamp) +src/ppu/$(am__dirstamp): + @$(MKDIR_P) src/ppu + @: > src/ppu/$(am__dirstamp) +src/ppu/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/ppu/$(DEPDIR) + @: > src/ppu/$(DEPDIR)/$(am__dirstamp) +src/ppu/ppu.$(OBJEXT): src/ppu/$(am__dirstamp) \ + src/ppu/$(DEPDIR)/$(am__dirstamp) +src/ppu/bppu/$(am__dirstamp): + @$(MKDIR_P) src/ppu/bppu + @: > src/ppu/bppu/$(am__dirstamp) +src/ppu/bppu/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/ppu/bppu/$(DEPDIR) + @: > src/ppu/bppu/$(DEPDIR)/$(am__dirstamp) +src/ppu/bppu/bppu.$(OBJEXT): src/ppu/bppu/$(am__dirstamp) \ + src/ppu/bppu/$(DEPDIR)/$(am__dirstamp) +src/system/$(am__dirstamp): + @$(MKDIR_P) src/system + @: > src/system/$(am__dirstamp) +src/system/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/system/$(DEPDIR) + @: > src/system/$(DEPDIR)/$(am__dirstamp) +src/system/system.$(OBJEXT): src/system/$(am__dirstamp) \ + src/system/$(DEPDIR)/$(am__dirstamp) +src/chip/sa1/$(am__dirstamp): + @$(MKDIR_P) src/chip/sa1 + @: > src/chip/sa1/$(am__dirstamp) +src/chip/sa1/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/sa1/$(DEPDIR) + @: > src/chip/sa1/$(DEPDIR)/$(am__dirstamp) +src/chip/sa1/sa1.$(OBJEXT): src/chip/sa1/$(am__dirstamp) \ + src/chip/sa1/$(DEPDIR)/$(am__dirstamp) +src/chip/bsx/$(am__dirstamp): + @$(MKDIR_P) src/chip/bsx + @: > src/chip/bsx/$(am__dirstamp) +src/chip/bsx/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/bsx/$(DEPDIR) + @: > src/chip/bsx/$(DEPDIR)/$(am__dirstamp) +src/chip/bsx/bsx.$(OBJEXT): src/chip/bsx/$(am__dirstamp) \ + src/chip/bsx/$(DEPDIR)/$(am__dirstamp) +src/chip/srtc/$(am__dirstamp): + @$(MKDIR_P) src/chip/srtc + @: > src/chip/srtc/$(am__dirstamp) +src/chip/srtc/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/srtc/$(DEPDIR) + @: > src/chip/srtc/$(DEPDIR)/$(am__dirstamp) +src/chip/srtc/srtc.$(OBJEXT): src/chip/srtc/$(am__dirstamp) \ + src/chip/srtc/$(DEPDIR)/$(am__dirstamp) +src/chip/sdd1/$(am__dirstamp): + @$(MKDIR_P) src/chip/sdd1 + @: > src/chip/sdd1/$(am__dirstamp) +src/chip/sdd1/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/sdd1/$(DEPDIR) + @: > src/chip/sdd1/$(DEPDIR)/$(am__dirstamp) +src/chip/sdd1/sdd1.$(OBJEXT): src/chip/sdd1/$(am__dirstamp) \ + src/chip/sdd1/$(DEPDIR)/$(am__dirstamp) +src/chip/spc7110/$(am__dirstamp): + @$(MKDIR_P) src/chip/spc7110 + @: > src/chip/spc7110/$(am__dirstamp) +src/chip/spc7110/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/spc7110/$(DEPDIR) + @: > src/chip/spc7110/$(DEPDIR)/$(am__dirstamp) +src/chip/spc7110/spc7110.$(OBJEXT): src/chip/spc7110/$(am__dirstamp) \ + src/chip/spc7110/$(DEPDIR)/$(am__dirstamp) +src/chip/cx4/$(am__dirstamp): + @$(MKDIR_P) src/chip/cx4 + @: > src/chip/cx4/$(am__dirstamp) +src/chip/cx4/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/cx4/$(DEPDIR) + @: > src/chip/cx4/$(DEPDIR)/$(am__dirstamp) +src/chip/cx4/cx4.$(OBJEXT): src/chip/cx4/$(am__dirstamp) \ + src/chip/cx4/$(DEPDIR)/$(am__dirstamp) +src/chip/dsp1/$(am__dirstamp): + @$(MKDIR_P) src/chip/dsp1 + @: > src/chip/dsp1/$(am__dirstamp) +src/chip/dsp1/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/dsp1/$(DEPDIR) + @: > src/chip/dsp1/$(DEPDIR)/$(am__dirstamp) +src/chip/dsp1/dsp1.$(OBJEXT): src/chip/dsp1/$(am__dirstamp) \ + src/chip/dsp1/$(DEPDIR)/$(am__dirstamp) +src/chip/dsp2/$(am__dirstamp): + @$(MKDIR_P) src/chip/dsp2 + @: > src/chip/dsp2/$(am__dirstamp) +src/chip/dsp2/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/dsp2/$(DEPDIR) + @: > src/chip/dsp2/$(DEPDIR)/$(am__dirstamp) +src/chip/dsp2/dsp2.$(OBJEXT): src/chip/dsp2/$(am__dirstamp) \ + src/chip/dsp2/$(DEPDIR)/$(am__dirstamp) +src/chip/dsp3/$(am__dirstamp): + @$(MKDIR_P) src/chip/dsp3 + @: > src/chip/dsp3/$(am__dirstamp) +src/chip/dsp3/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/dsp3/$(DEPDIR) + @: > src/chip/dsp3/$(DEPDIR)/$(am__dirstamp) +src/chip/dsp3/dsp3.$(OBJEXT): src/chip/dsp3/$(am__dirstamp) \ + src/chip/dsp3/$(DEPDIR)/$(am__dirstamp) +src/chip/dsp4/$(am__dirstamp): + @$(MKDIR_P) src/chip/dsp4 + @: > src/chip/dsp4/$(am__dirstamp) +src/chip/dsp4/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/dsp4/$(DEPDIR) + @: > src/chip/dsp4/$(DEPDIR)/$(am__dirstamp) +src/chip/dsp4/dsp4.$(OBJEXT): src/chip/dsp4/$(am__dirstamp) \ + src/chip/dsp4/$(DEPDIR)/$(am__dirstamp) +src/chip/obc1/$(am__dirstamp): + @$(MKDIR_P) src/chip/obc1 + @: > src/chip/obc1/$(am__dirstamp) +src/chip/obc1/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/obc1/$(DEPDIR) + @: > src/chip/obc1/$(DEPDIR)/$(am__dirstamp) +src/chip/obc1/obc1.$(OBJEXT): src/chip/obc1/$(am__dirstamp) \ + src/chip/obc1/$(DEPDIR)/$(am__dirstamp) +src/chip/st010/$(am__dirstamp): + @$(MKDIR_P) src/chip/st010 + @: > src/chip/st010/$(am__dirstamp) +src/chip/st010/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/st010/$(DEPDIR) + @: > src/chip/st010/$(DEPDIR)/$(am__dirstamp) +src/chip/st010/st010.$(OBJEXT): src/chip/st010/$(am__dirstamp) \ + src/chip/st010/$(DEPDIR)/$(am__dirstamp) +src/chip/st011/$(am__dirstamp): + @$(MKDIR_P) src/chip/st011 + @: > src/chip/st011/$(am__dirstamp) +src/chip/st011/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/st011/$(DEPDIR) + @: > src/chip/st011/$(DEPDIR)/$(am__dirstamp) +src/chip/st011/st011.$(OBJEXT): src/chip/st011/$(am__dirstamp) \ + src/chip/st011/$(DEPDIR)/$(am__dirstamp) +src/chip/st018/$(am__dirstamp): + @$(MKDIR_P) src/chip/st018 + @: > src/chip/st018/$(am__dirstamp) +src/chip/st018/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/st018/$(DEPDIR) + @: > src/chip/st018/$(DEPDIR)/$(am__dirstamp) +src/chip/st018/st018.$(OBJEXT): src/chip/st018/$(am__dirstamp) \ + src/chip/st018/$(DEPDIR)/$(am__dirstamp) +src/chip/superfx/$(am__dirstamp): + @$(MKDIR_P) src/chip/superfx + @: > src/chip/superfx/$(am__dirstamp) +src/chip/superfx/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/superfx/$(DEPDIR) + @: > src/chip/superfx/$(DEPDIR)/$(am__dirstamp) +src/chip/superfx/superfx.$(OBJEXT): src/chip/superfx/$(am__dirstamp) \ + src/chip/superfx/$(DEPDIR)/$(am__dirstamp) +src/chip/supergameboy/$(am__dirstamp): + @$(MKDIR_P) src/chip/supergameboy + @: > src/chip/supergameboy/$(am__dirstamp) +src/chip/supergameboy/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/supergameboy/$(DEPDIR) + @: > src/chip/supergameboy/$(DEPDIR)/$(am__dirstamp) +src/chip/supergameboy/supergameboy.$(OBJEXT): \ + src/chip/supergameboy/$(am__dirstamp) \ + src/chip/supergameboy/$(DEPDIR)/$(am__dirstamp) +src/chip/21fx/$(am__dirstamp): + @$(MKDIR_P) src/chip/21fx + @: > src/chip/21fx/$(am__dirstamp) +src/chip/21fx/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/chip/21fx/$(DEPDIR) + @: > src/chip/21fx/$(DEPDIR)/$(am__dirstamp) +src/chip/21fx/21fx.$(OBJEXT): src/chip/21fx/$(am__dirstamp) \ + src/chip/21fx/$(DEPDIR)/$(am__dirstamp) +libsnes.a: $(libsnes_a_OBJECTS) $(libsnes_a_DEPENDENCIES) + $(AM_V_at)-rm -f libsnes.a + $(AM_V_AR)$(libsnes_a_AR) libsnes.a $(libsnes_a_OBJECTS) $(libsnes_a_LIBADD) + $(AM_V_at)$(RANLIB) libsnes.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f src/cartridge/cartridge.$(OBJEXT) + -rm -f src/cheat/cheat.$(OBJEXT) + -rm -f src/chip/21fx/21fx.$(OBJEXT) + -rm -f src/chip/bsx/bsx.$(OBJEXT) + -rm -f src/chip/cx4/cx4.$(OBJEXT) + -rm -f src/chip/dsp1/dsp1.$(OBJEXT) + -rm -f src/chip/dsp2/dsp2.$(OBJEXT) + -rm -f src/chip/dsp3/dsp3.$(OBJEXT) + -rm -f src/chip/dsp4/dsp4.$(OBJEXT) + -rm -f src/chip/obc1/obc1.$(OBJEXT) + -rm -f src/chip/sa1/sa1.$(OBJEXT) + -rm -f src/chip/sdd1/sdd1.$(OBJEXT) + -rm -f src/chip/spc7110/spc7110.$(OBJEXT) + -rm -f src/chip/srtc/srtc.$(OBJEXT) + -rm -f src/chip/st010/st010.$(OBJEXT) + -rm -f src/chip/st011/st011.$(OBJEXT) + -rm -f src/chip/st018/st018.$(OBJEXT) + -rm -f src/chip/superfx/superfx.$(OBJEXT) + -rm -f src/chip/supergameboy/supergameboy.$(OBJEXT) + -rm -f src/cpu/core/core.$(OBJEXT) + -rm -f src/cpu/cpu.$(OBJEXT) + -rm -f src/cpu/scpu/scpu.$(OBJEXT) + -rm -f src/dsp/sdsp/sdsp.$(OBJEXT) + -rm -f src/lib/libco/libco.$(OBJEXT) + -rm -f src/memory/memory.$(OBJEXT) + -rm -f src/memory/smemory/smemory.$(OBJEXT) + -rm -f src/ppu/bppu/bppu.$(OBJEXT) + -rm -f src/ppu/ppu.$(OBJEXT) + -rm -f src/smp/core/core.$(OBJEXT) + -rm -f src/smp/smp.$(OBJEXT) + -rm -f src/smp/ssmp/ssmp.$(OBJEXT) + -rm -f src/system/system.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interface.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/cartridge/$(DEPDIR)/cartridge.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/cheat/$(DEPDIR)/cheat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/21fx/$(DEPDIR)/21fx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/bsx/$(DEPDIR)/bsx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/cx4/$(DEPDIR)/cx4.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/dsp1/$(DEPDIR)/dsp1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/dsp2/$(DEPDIR)/dsp2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/dsp3/$(DEPDIR)/dsp3.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/dsp4/$(DEPDIR)/dsp4.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/obc1/$(DEPDIR)/obc1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/sa1/$(DEPDIR)/sa1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/sdd1/$(DEPDIR)/sdd1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/spc7110/$(DEPDIR)/spc7110.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/srtc/$(DEPDIR)/srtc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/st010/$(DEPDIR)/st010.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/st011/$(DEPDIR)/st011.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/st018/$(DEPDIR)/st018.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/superfx/$(DEPDIR)/superfx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/chip/supergameboy/$(DEPDIR)/supergameboy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/cpu/$(DEPDIR)/cpu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/cpu/core/$(DEPDIR)/core.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/cpu/scpu/$(DEPDIR)/scpu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/dsp/sdsp/$(DEPDIR)/sdsp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/lib/libco/$(DEPDIR)/libco.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/memory/$(DEPDIR)/memory.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/memory/smemory/$(DEPDIR)/smemory.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/ppu/$(DEPDIR)/ppu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/ppu/bppu/$(DEPDIR)/bppu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/smp/$(DEPDIR)/smp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/smp/core/$(DEPDIR)/core.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/smp/ssmp/$(DEPDIR)/ssmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/system/$(DEPDIR)/system.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +.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 +@am__fastdepCXX_FALSE@ $(AM_V_CXX) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(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 +@am__fastdepCXX_FALSE@ $(AM_V_CXX) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(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 +@am__fastdepCXX_FALSE@ $(AM_V_CXX) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +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) + -rm -f src/cartridge/$(DEPDIR)/$(am__dirstamp) + -rm -f src/cartridge/$(am__dirstamp) + -rm -f src/cheat/$(DEPDIR)/$(am__dirstamp) + -rm -f src/cheat/$(am__dirstamp) + -rm -f src/chip/21fx/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/21fx/$(am__dirstamp) + -rm -f src/chip/bsx/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/bsx/$(am__dirstamp) + -rm -f src/chip/cx4/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/cx4/$(am__dirstamp) + -rm -f src/chip/dsp1/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/dsp1/$(am__dirstamp) + -rm -f src/chip/dsp2/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/dsp2/$(am__dirstamp) + -rm -f src/chip/dsp3/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/dsp3/$(am__dirstamp) + -rm -f src/chip/dsp4/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/dsp4/$(am__dirstamp) + -rm -f src/chip/obc1/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/obc1/$(am__dirstamp) + -rm -f src/chip/sa1/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/sa1/$(am__dirstamp) + -rm -f src/chip/sdd1/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/sdd1/$(am__dirstamp) + -rm -f src/chip/spc7110/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/spc7110/$(am__dirstamp) + -rm -f src/chip/srtc/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/srtc/$(am__dirstamp) + -rm -f src/chip/st010/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/st010/$(am__dirstamp) + -rm -f src/chip/st011/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/st011/$(am__dirstamp) + -rm -f src/chip/st018/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/st018/$(am__dirstamp) + -rm -f src/chip/superfx/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/superfx/$(am__dirstamp) + -rm -f src/chip/supergameboy/$(DEPDIR)/$(am__dirstamp) + -rm -f src/chip/supergameboy/$(am__dirstamp) + -rm -f src/cpu/$(DEPDIR)/$(am__dirstamp) + -rm -f src/cpu/$(am__dirstamp) + -rm -f src/cpu/core/$(DEPDIR)/$(am__dirstamp) + -rm -f src/cpu/core/$(am__dirstamp) + -rm -f src/cpu/scpu/$(DEPDIR)/$(am__dirstamp) + -rm -f src/cpu/scpu/$(am__dirstamp) + -rm -f src/dsp/sdsp/$(DEPDIR)/$(am__dirstamp) + -rm -f src/dsp/sdsp/$(am__dirstamp) + -rm -f src/lib/libco/$(DEPDIR)/$(am__dirstamp) + -rm -f src/lib/libco/$(am__dirstamp) + -rm -f src/memory/$(DEPDIR)/$(am__dirstamp) + -rm -f src/memory/$(am__dirstamp) + -rm -f src/memory/smemory/$(DEPDIR)/$(am__dirstamp) + -rm -f src/memory/smemory/$(am__dirstamp) + -rm -f src/ppu/$(DEPDIR)/$(am__dirstamp) + -rm -f src/ppu/$(am__dirstamp) + -rm -f src/ppu/bppu/$(DEPDIR)/$(am__dirstamp) + -rm -f src/ppu/bppu/$(am__dirstamp) + -rm -f src/smp/$(DEPDIR)/$(am__dirstamp) + -rm -f src/smp/$(am__dirstamp) + -rm -f src/smp/core/$(DEPDIR)/$(am__dirstamp) + -rm -f src/smp/core/$(am__dirstamp) + -rm -f src/smp/ssmp/$(DEPDIR)/$(am__dirstamp) + -rm -f src/smp/ssmp/$(am__dirstamp) + -rm -f src/system/$(DEPDIR)/$(am__dirstamp) + -rm -f src/system/$(am__dirstamp) + +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) src/cartridge/$(DEPDIR) src/cheat/$(DEPDIR) src/chip/21fx/$(DEPDIR) src/chip/bsx/$(DEPDIR) src/chip/cx4/$(DEPDIR) src/chip/dsp1/$(DEPDIR) src/chip/dsp2/$(DEPDIR) src/chip/dsp3/$(DEPDIR) src/chip/dsp4/$(DEPDIR) src/chip/obc1/$(DEPDIR) src/chip/sa1/$(DEPDIR) src/chip/sdd1/$(DEPDIR) src/chip/spc7110/$(DEPDIR) src/chip/srtc/$(DEPDIR) src/chip/st010/$(DEPDIR) src/chip/st011/$(DEPDIR) src/chip/st018/$(DEPDIR) src/chip/superfx/$(DEPDIR) src/chip/supergameboy/$(DEPDIR) src/cpu/$(DEPDIR) src/cpu/core/$(DEPDIR) src/cpu/scpu/$(DEPDIR) src/dsp/sdsp/$(DEPDIR) src/lib/libco/$(DEPDIR) src/memory/$(DEPDIR) src/memory/smemory/$(DEPDIR) src/ppu/$(DEPDIR) src/ppu/bppu/$(DEPDIR) src/smp/$(DEPDIR) src/smp/core/$(DEPDIR) src/smp/ssmp/$(DEPDIR) src/system/$(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) src/cartridge/$(DEPDIR) src/cheat/$(DEPDIR) src/chip/21fx/$(DEPDIR) src/chip/bsx/$(DEPDIR) src/chip/cx4/$(DEPDIR) src/chip/dsp1/$(DEPDIR) src/chip/dsp2/$(DEPDIR) src/chip/dsp3/$(DEPDIR) src/chip/dsp4/$(DEPDIR) src/chip/obc1/$(DEPDIR) src/chip/sa1/$(DEPDIR) src/chip/sdd1/$(DEPDIR) src/chip/spc7110/$(DEPDIR) src/chip/srtc/$(DEPDIR) src/chip/st010/$(DEPDIR) src/chip/st011/$(DEPDIR) src/chip/st018/$(DEPDIR) src/chip/superfx/$(DEPDIR) src/chip/supergameboy/$(DEPDIR) src/cpu/$(DEPDIR) src/cpu/core/$(DEPDIR) src/cpu/scpu/$(DEPDIR) src/dsp/sdsp/$(DEPDIR) src/lib/libco/$(DEPDIR) src/memory/$(DEPDIR) src/memory/smemory/$(DEPDIR) src/ppu/$(DEPDIR) src/ppu/bppu/$(DEPDIR) src/smp/$(DEPDIR) src/smp/core/$(DEPDIR) src/smp/ssmp/$(DEPDIR) src/system/$(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/snes/VERSION.BSNES b/mednafen/snes/VERSION.BSNES new file mode 100644 index 0000000..f4d0b3a --- /dev/null +++ b/mednafen/snes/VERSION.BSNES @@ -0,0 +1 @@ +bsnes v0.59 + Cx4 Op10 fix patch + blargg_libco_ppc64-5 + a few minor timing/scheduler related changes for Mednafen diff --git a/mednafen/snes/interface.cpp b/mednafen/snes/interface.cpp new file mode 100644 index 0000000..862b271 --- /dev/null +++ b/mednafen/snes/interface.cpp @@ -0,0 +1,940 @@ +/* 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 "../md5.h" +#include "../general.h" +#include "src/base.hpp" +#include "../mempatcher.h" +#include "Fir_Resampler.h" +#include + +static void Cleanup(void); + +static Fir_Resampler<24> resampler; +class MeowFace : public SNES::Interface +{ + virtual void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height); + virtual void audio_sample(uint16_t l_sample, uint16_t r_sample); + virtual void input_poll(); + virtual int16_t input_poll(bool port, unsigned device, unsigned index, unsigned id); +// virtual int16_t input_poll(unsigned deviceid, unsigned id); +}; + +static bool InProperEmu; +static bool SoundOn; +static double SoundLastRate = 0; +static MeowFace meowface; + +static int32 CycleCounter; +static MDFN_Surface *tsurf = NULL; +static MDFN_Rect *tlw = NULL; +static MDFN_Rect *tdr = NULL; + +static int InputType[2]; +static uint8 *InputPtr[8] = { NULL }; +static uint16 PadLatch[8]; +static bool MultitapEnabled[2]; +static bool HasPolledThisFrame; + +static int16 MouseXLatch[2]; +static int16 MouseYLatch[2]; +static uint8 MouseBLatch[2]; + +static uint8 *CustomColorMap = NULL; +//static uint32 ColorMap[32768]; +static std::vector ColorMap; + +static bool LoadCPalette(const char *syspalname, uint8 **ptr, uint32 num_entries) +{ + std::string colormap_fn = MDFN_MakeFName(MDFNMKF_PALETTE, 0, syspalname).c_str(); + FILE *fp; + + MDFN_printf(_("Loading custom palette from \"%s\"...\n"), colormap_fn.c_str()); + MDFN_indent(1); + + if(!(fp = fopen(colormap_fn.c_str(), "rb"))) + { + ErrnoHolder ene(errno); + + MDFN_printf(_("Error opening file: %s\n"), ene.StrError()); + + MDFN_indent(-1); + + return(ene.Errno() == ENOENT); // Return fatal error if it's an error other than the file not being found. + } + + if(!(*ptr = (uint8 *)MDFN_malloc(num_entries * 3, _("custom color map")))) + { + MDFN_indent(-1); + + fclose(fp); + return(false); + } + + if(fread(*ptr, 1, num_entries * 3, fp) != (num_entries * 3)) + { + ErrnoHolder ene(errno); + + MDFN_printf(_("Error reading file: %s\n"), feof(fp) ? "EOF" : ene.StrError()); + MDFN_indent(-1); + + MDFN_free(*ptr); + *ptr = NULL; + fclose(fp); + + return(false); + } + + fclose(fp); + + MDFN_indent(-1); + + return(true); +} + + +static void BuildColorMap(MDFN_PixelFormat &format) +{ + for(int x = 0; x < 32768; x++) + { + int r, g, b; + + r = (x & (0x1F << 0)) << 3; + g = (x & (0x1F << 5)) >> (5 - 3); + b = (x & (0x1F << 10)) >> (5 * 2 - 3); + + //r = ((((x >> 0) & 0x1F) * 255 + 15) / 31); + //g = ((((x >> 5) & 0x1F) * 255 + 15) / 31); + //b = ((((x >> 10) & 0x1F) * 255 + 15) / 31); + + if(CustomColorMap) + { + r = CustomColorMap[x * 3 + 0]; + g = CustomColorMap[x * 3 + 1]; + b = CustomColorMap[x * 3 + 2]; + } + + ColorMap[x] = format.MakeColor(r, g, b); + } +} + +void MeowFace::video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) +{ + if(!tsurf || !tlw || !tdr) + return; + + const uint16 *source_line = data; + uint32 *dest_line = tsurf->pixels; + + assert(!(pitch & 1)); + + //if(height != 224) + // printf("%d\n", height); + + //if(tsurf->format.bpp == 32) + //{ + // + //} + //else + { + for(int y = 0; y < height; y++, source_line += pitch >> 1, dest_line += tsurf->pitch32) + for(int x = 0; x < width; tlw[y].x = 0, tlw[y].w = (width == 512) ? line[y] : 256, x++) + dest_line[x] = ColorMap[source_line[x] & 0x7FFF]; + } + + tdr->w = width; + tdr->h = height; +} + +void MeowFace::audio_sample(uint16_t l_sample, uint16_t r_sample) +{ + CycleCounter++; + + if(!SoundOn) + return; + + if(resampler.max_write() >= 2) + { + resampler.buffer()[0] = l_sample; + resampler.buffer()[1] = r_sample; + resampler.write(2); + } + else + { + MDFN_DispMessage("Buffer overflow?"); + } +} + +#if 0 +class Input { +public: + enum Device { + DeviceNone, + DeviceJoypad, + DeviceMultitap, + DeviceMouse, + DeviceSuperScope, + DeviceJustifier, + DeviceJustifiers, + }; + + enum JoypadID { + JoypadB = 0, JoypadY = 1, + JoypadSelect = 2, JoypadStart = 3, + JoypadUp = 4, JoypadDown = 5, + JoypadLeft = 6, JoypadRight = 7, + JoypadA = 8, JoypadX = 9, + JoypadL = 10, JoypadR = 11, + }; +#endif + +void MeowFace::input_poll() +{ + if(!InProperEmu) + return; + + HasPolledThisFrame = true; + + for(int port = 0; port < 2; port++) + { + switch(InputType[port]) + { + case SNES::Input::DeviceJoypad: + PadLatch[port] = MDFN_de16lsb(InputPtr[port]); + break; + + case SNES::Input::DeviceMultitap: + for(int index = 0; index < 4; index++) + { + if(!index) + PadLatch[port] = MDFN_de16lsb(InputPtr[port]); + else + { + int pi = 2 + 3 * (port ^ 1) + (index - 1); + PadLatch[pi] = MDFN_de16lsb(InputPtr[pi]); + } + } + break; + + case SNES::Input::DeviceMouse: + MouseXLatch[port] = (int32)MDFN_de32lsb(InputPtr[port] + 0); + MouseYLatch[port] = (int32)MDFN_de32lsb(InputPtr[port] + 4); + MouseBLatch[port] = *(uint8 *)(InputPtr[port] + 8); + break; + } + } +} + +static INLINE int16 sats32tos16(int32 val) +{ + if(val > 32767) + val = 32767; + if(val < -32768) + val = -32768; + + return(val); +} + +int16_t MeowFace::input_poll(bool port, unsigned device, unsigned index, unsigned id) +{ + if(!HasPolledThisFrame) + printf("input_poll(...) before input_poll() for frame, %d %d %d %d\n", port, device, index, id); + + switch(device) + { + case SNES::Input::DeviceJoypad: + { + return((PadLatch[port] >> id) & 1); + } + break; + + case SNES::Input::DeviceMultitap: + { + if(!index) + return((PadLatch[port] >> id) & 1); + else + return((PadLatch[2 + 3 * (port ^ 1) + (index - 1)] >> id) & 1); + } + break; + + case SNES::Input::DeviceMouse: + { + assert(port < 2); + switch(id) + { + case SNES::Input::MouseX: + return(sats32tos16(MouseXLatch[port])); + break; + + case SNES::Input::MouseY: + return(sats32tos16(MouseYLatch[port])); + break; + + case SNES::Input::MouseLeft: + return((int)(bool)(MouseBLatch[port] & 1)); + break; + + case SNES::Input::MouseRight: + return((int)(bool)(MouseBLatch[port] & 2)); + break; + } + } + break; + } + + return(0); +} + +#if 0 +void MeowFace::init() +{ + + +} + +void MeowFace::term() +{ + + +} +#endif + +#if 0 + +namespace memory { + extern MappedRAM cartrom, cartram, cartrtc; + extern MappedRAM bsxflash, bsxram, bsxpram; + extern MappedRAM stArom, stAram; + extern MappedRAM stBrom, stBram; + extern MappedRAM gbrom, gbram; +}; + +#endif + +// For loading: Return false on fatal error during loading, or true on success(or file not found) +static bool SaveMemorySub(bool load, const char *extension, SNES::MappedRAM *memoryA, SNES::MappedRAM *memoryB = NULL) +{ + const std::string path = MDFN_MakeFName(MDFNMKF_SAV, 0, extension); + std::vector MemToSave; + + if(load) + { + FILE *gp; + + errno = 0; + gp = fopen(path.c_str(), "rb"); + if(!gp) + { + ErrnoHolder ene(errno); + if(ene.Errno() == ENOENT) + return(true); + + MDFN_PrintError(_("Error opening save file \"%s\": %s"), path.c_str(), ene.StrError()); + return(false); + } + + if(memoryA && memoryA->size() != 0 && memoryA->size() != -1U) + { + errno = 0; + if(fread(memoryA->data(), memoryA->size(), 1, gp) != memoryA->size()) + { + ErrnoHolder ene(errno); + + MDFN_PrintError(_("Error reading save file \"%s\": %s"), path.c_str(), ene.StrError()); + return(false); + } + } + + if(memoryB && memoryB->size() != 0 && memoryB->size() != -1U) + { + errno = 0; + if(fread(memoryB->data(), memoryB->size(), 1, gp) != memoryB->size()) + { + ErrnoHolder ene(errno); + + MDFN_PrintError(_("Error reading save file \"%s\": %s"), path.c_str(), ene.StrError()); + return(false); + } + } + + fclose(gp); + + return(true); + } + else + { + if(memoryA && memoryA->size() != 0 && memoryA->size() != -1U) + MemToSave.push_back(PtrLengthPair(memoryA->data(), memoryA->size())); + + if(memoryB && memoryB->size() != 0 && memoryB->size() != -1U) + MemToSave.push_back(PtrLengthPair(memoryB->data(), memoryB->size())); + + return(MDFN_DumpToFile(path.c_str(), 6, MemToSave)); + } +} + +static bool SaveLoadMemory(bool load) +{ + if(SNES::cartridge.loaded() == false) + return(FALSE); + + bool ret = true; + + switch(SNES::cartridge.mode()) + { + case SNES::Cartridge::ModeNormal: + case SNES::Cartridge::ModeBsxSlotted: + { + ret &= SaveMemorySub(load, "srm", &SNES::memory::cartram); + ret &= SaveMemorySub(load, "rtc", &SNES::memory::cartrtc); + } + break; + + case SNES::Cartridge::ModeBsx: + { + ret &= SaveMemorySub(load, "srm", &SNES::memory::bsxram ); + ret &= SaveMemorySub(load, "psr", &SNES::memory::bsxpram); + } + break; + + case SNES::Cartridge::ModeSufamiTurbo: + { + ret &= SaveMemorySub(load, "srm", &SNES::memory::stAram, &SNES::memory::stBram); + } + break; + + case SNES::Cartridge::ModeSuperGameBoy: + { + ret &= SaveMemorySub(load, "sav", &SNES::memory::gbram); + ret &= SaveMemorySub(load, "rtc", &SNES::memory::gbrtc); + } + break; + } + + return(ret); +} + + +static bool TestMagic(const char *name, MDFNFILE *fp) +{ + if(strcasecmp(fp->ext, "smc") && strcasecmp(fp->ext, "swc") && strcasecmp(fp->ext, "sfc") && strcasecmp(fp->ext, "fig") && + strcasecmp(fp->ext, "bs") && strcasecmp(fp->ext, "st")) + { + return(false); + } + + return(true); +} + +static void SetupMisc(bool PAL) +{ + MDFNGameInfo->fps = PAL ? 838977920 : 1008307711; + MDFNGameInfo->MasterClock = MDFN_MASTERCLOCK_FIXED(32040.40); //MDFN_MASTERCLOCK_FIXED(21477272); //PAL ? PAL_CPU : NTSC_CPU); + + { + MDFNGameInfo->nominal_width = MDFN_GetSettingB("snes.correct_aspect") ? 292 : 256; + MDFNGameInfo->nominal_height = PAL ? 239 : 224; + MDFNGameInfo->lcm_height = MDFNGameInfo->nominal_height * 2; + } + + resampler.buffer_size(32040 / 10); + //resampler.time_ratio((double)32040.40 / 48000, 0.9965); + SoundLastRate = 0; +} + +static bool LoadSNSF(MDFNFILE *fp) +{ + return(false); +} + +static void Cleanup(void) +{ + SNES::memory::cartrom.map(NULL, 0); // So it delete[]s the pointer it took ownership of. + + if(CustomColorMap) + { + MDFN_free(CustomColorMap); + CustomColorMap = NULL; + } + + ColorMap.resize(0); +} + +static int Load(const char *name, MDFNFILE *fp) +{ + bool PAL = FALSE; + + CycleCounter = 0; + + try + { + // Allocate 8MiB of space regardless of actual ROM image size, to prevent malformed or corrupted ROM images + // from crashing the bsnes cart loading code. + + const uint32 header_adjust = (((fp->size & 0x7FFF) == 512) ? 512 : 0); + uint8 *export_ptr; + + if((fp->size - header_adjust) > (8192 * 1024)) + { + throw MDFN_Error(0, _("SNES ROM image is too large.")); + } + + md5_context md5; + + md5.starts(); + md5.update(fp->data, fp->size); + md5.finish(MDFNGameInfo->MD5); + + SNES::system.init(&meowface); + + //const SNES::Cartridge::Type rom_type = SNES::cartridge.detect_image_type((uint8 *)fp->data, fp->size); + + export_ptr = new uint8[8192 * 1024]; + memset(export_ptr, 0x00, 8192 * 1024); + memcpy(export_ptr, fp->data + header_adjust, fp->size - header_adjust); + + SNES::memory::cartrom.map(export_ptr, fp->size - header_adjust); + + SNES::cartridge.load(SNES::Cartridge::ModeNormal); + + SNES::system.power(); + + PAL = (SNES::system.region() == SNES::System::PAL); + + SetupMisc(PAL); + + MultitapEnabled[0] = MDFN_GetSettingB("snes.input.port1.multitap"); + MultitapEnabled[1] = MDFN_GetSettingB("snes.input.port2.multitap"); + + if(!SaveLoadMemory(true)) + { + Cleanup(); + return(0); + } + + //printf(" %d %d\n", FSettings.SndRate, resampler.max_write()); + + MDFNMP_Init(1024, (1 << 24) / 1024); + + MDFNMP_AddRAM(131072, 0x7E << 16, SNES::memory::wram.data()); + + ColorMap.resize(32768); + + if(!LoadCPalette(NULL, &CustomColorMap, 32768)) + { + Cleanup(); + return(0); + } + } + catch(std::exception &e) + { + MDFND_PrintError(e.what()); + Cleanup(); + return 0; + } + + return(1); +} + +static void CloseGame(void) +{ + { + SaveLoadMemory(false); + } + Cleanup(); +} + +static void Emulate(EmulateSpecStruct *espec) +{ + tsurf = espec->surface; + tlw = espec->LineWidths; + tdr = &espec->DisplayRect; + + { + if(espec->VideoFormatChanged) + BuildColorMap(espec->surface->format); + } + + if(SoundLastRate != espec->SoundRate) + { + double ratio = (double)32040.40 / (espec->SoundRate ? espec->SoundRate : 48000); + resampler.time_ratio(ratio, 0.9965); + printf("%f, %f\n", ratio, resampler.ratio()); + SoundLastRate = espec->SoundRate; + } + + MDFNMP_ApplyPeriodicCheats(); + + // Make sure to trash any leftover samples, generated from system.runtosave() in save state saving, if sound is now disabled. + if(SoundOn && !espec->SoundBuf) + { + resampler.clear(); + } + + SoundOn = espec->SoundBuf ? true : false; + + HasPolledThisFrame = false; + InProperEmu = TRUE; + SNES::system.run_mednafen_custom(); + tsurf = NULL; + tlw = NULL; + tdr = NULL; + InProperEmu = FALSE; + + espec->MasterCycles = CycleCounter; + CycleCounter = 0; + + //printf("%d\n", espec->MasterCycles); + + if(espec->SoundBuf) + espec->SoundBufSize = resampler.read(espec->SoundBuf, resampler.avail()) >> 1; + + MDFNGameInfo->mouse_sensitivity = MDFN_GetSettingF("snes.mouse_sensitivity"); +} + +static int StateAction(StateMem *sm, int load, int data_only) +{ + //if(!SNES::Cartridge::saveStatesSupported()) + //return(0); + + if(load) + { + uint32 length; + uint8 *ptr; + + SFORMAT StateLengthCat[] = + { + SFVARN(length, "length"), + SFEND + }; + + if(!MDFNSS_StateAction(sm, 1, data_only, StateLengthCat, "LEN")) + return(0); + + ptr = (uint8 *)MDFN_calloc(1, length, _("SNES save state buffer")); + + SFORMAT StateRegs[] = + { + SFARRAYN(ptr, length, "OmniCat"), + SFARRAY16(PadLatch, 8), + SFARRAY16(MouseXLatch, 2), + SFARRAY16(MouseYLatch, 2), + SFARRAY(MouseBLatch, 2), + SFEND + }; + + if(!MDFNSS_StateAction(sm, 1, data_only, StateRegs, "DATA")) + { + free(ptr); + return(0); + } + + serializer state(ptr, length); + int result; + + result = SNES::system.unserialize(state); + + free(ptr); + return(result); + } + else // save: + { + uint32 length; + + if(SNES::scheduler.sync != SNES::Scheduler::SyncAll) + SNES::system.runtosave(); + + serializer state = SNES::system.serialize(); + + length = state.size(); + + SFORMAT StateLengthCat[] = + { + SFVARN(length, "length"), + SFEND + }; + + if(!MDFNSS_StateAction(sm, 0, data_only, StateLengthCat, "LEN")) + return(0); + + uint8 *ptr = const_cast(state.data()); + + SFORMAT StateRegs[] = + { + SFARRAYN(ptr, length, "OmniCat"), + SFARRAY16(PadLatch, 8), + SFARRAY16(MouseXLatch, 2), + SFARRAY16(MouseYLatch, 2), + SFARRAY(MouseBLatch, 2), + SFEND + }; + + if(!MDFNSS_StateAction(sm, 0, data_only, StateRegs, "DATA")) + return(0); + + return(1); + } + +} + +struct StrToBSIT_t +{ + const char *str; + const int id; +}; + +static const StrToBSIT_t StrToBSIT[] = +{ + { "none", SNES::Input::DeviceNone }, + { "gamepad", SNES::Input::DeviceJoypad }, + { "multitap", SNES::Input::DeviceMultitap }, + { "mouse", SNES::Input::DeviceMouse }, + { "superscope", SNES::Input::DeviceSuperScope }, + { "justifier", SNES::Input::DeviceJustifier }, + { "justifiers", SNES::Input::DeviceJustifiers }, + { NULL, -1 }, +}; + + +static void SetInput(int port, const char *type, void *ptr) +{ + assert(port >= 0 && port < 8); + + if(port < 2) + { + const StrToBSIT_t *sb = StrToBSIT; + int id = -1; + + if(MultitapEnabled[port] && !strcmp(type, "gamepad")) + type = "multitap"; + + while(sb->str && id == -1) + { + if(!strcmp(type, sb->str)) + id = sb->id; + sb++; + } + assert(id != -1); + + InputType[port] = id; + + SNES::input.port_set_device(port, id); + +#if 0 + switch(config().input.port1) { default: + case ControllerPort1::None: mapper().port1 = 0; break; + case ControllerPort1::Gamepad: mapper().port1 = &Controllers::gamepad1; break; + case ControllerPort1::Asciipad: mapper().port1 = &Controllers::asciipad1; break; + case ControllerPort1::Multitap: mapper().port1 = &Controllers::multitap1; break; + case ControllerPort1::Mouse: mapper().port1 = &Controllers::mouse1; break; + } + + switch(config().input.port2) { default: + case ControllerPort2::None: mapper().port2 = 0; break; + case ControllerPort2::Gamepad: mapper().port2 = &Controllers::gamepad2; break; + case ControllerPort2::Asciipad: mapper().port2 = &Controllers::asciipad2; break; + case ControllerPort2::Multitap: mapper().port2 = &Controllers::multitap2; break; + case ControllerPort2::Mouse: mapper().port2 = &Controllers::mouse2; break; + case ControllerPort2::SuperScope: mapper().port2 = &Controllers::superscope; break; + case ControllerPort2::Justifier: mapper().port2 = &Controllers::justifier1; break; + case ControllerPort2::Justifiers: mapper().port2 = &Controllers::justifiers; break; + } +#endif + + } + + + InputPtr[port] = (uint8 *)ptr; +} + +static void SetLayerEnableMask(uint64 mask) +{ + +} + + +static void DoSimpleCommand(int cmd) +{ + switch(cmd) + { + case MDFN_MSC_RESET: SNES::system.reset(); break; + case MDFN_MSC_POWER: SNES::system.power(); break; + } +} + +static const InputDeviceInputInfoStruct GamepadIDII[] = +{ + { "b", "B (center, lower)", 7, IDIT_BUTTON_CAN_RAPID, NULL }, + { "y", "Y (left)", 6, IDIT_BUTTON_CAN_RAPID, NULL }, + { "select", "SELECT", 4, IDIT_BUTTON, NULL }, + { "start", "START", 5, IDIT_BUTTON, NULL }, + { "up", "UP ↑", 0, IDIT_BUTTON, "down" }, + { "down", "DOWN ↓", 1, IDIT_BUTTON, "up" }, + { "left", "LEFT ←", 2, IDIT_BUTTON, "right" }, + { "right", "RIGHT →", 3, IDIT_BUTTON, "left" }, + { "a", "A (right)", 9, IDIT_BUTTON_CAN_RAPID, NULL }, + { "x", "X (center, upper)", 8, IDIT_BUTTON_CAN_RAPID, NULL }, + { "l", "Left Shoulder", 10, IDIT_BUTTON, NULL }, + { "r", "Right Shoulder", 11, IDIT_BUTTON, NULL }, +}; + +static const InputDeviceInputInfoStruct MouseIDII[0x4] = +{ + { "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 }, +}; + + +static InputDeviceInfoStruct InputDeviceInfoSNESPort[] = +{ + // 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 InputDeviceInfoStruct InputDeviceInfoTapPort[] = +{ + // Gamepad + { + "gamepad", + "Gamepad", + NULL, + NULL, + sizeof(GamepadIDII) / sizeof(InputDeviceInputInfoStruct), + GamepadIDII, + }, +}; + + +static const InputPortInfoStruct PortInfo[] = +{ + { "port1", "Port 1/1A", sizeof(InputDeviceInfoSNESPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoSNESPort, "gamepad" }, + { "port2", "Port 2/2A", sizeof(InputDeviceInfoSNESPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoSNESPort, "gamepad" }, + { "port3", "Port 2B", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" }, + { "port4", "Port 2C", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" }, + { "port5", "Port 2D", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" }, + { "port6", "Port 1B", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" }, + { "port7", "Port 1C", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" }, + { "port8", "Port 1D", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" }, +}; + +static InputInfoStruct SNESInputInfo = +{ + sizeof(PortInfo) / sizeof(InputPortInfoStruct), + PortInfo +}; + +static const MDFNSetting SNESSettings[] = +{ + { "snes.input.port1.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap on SNES port 1."), NULL, MDFNST_BOOL, "0", NULL, NULL }, + { "snes.input.port2.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap on SNES port 2."), NULL, MDFNST_BOOL, "0", NULL, NULL }, + + { "snes.mouse_sensitivity", MDFNSF_NOFLAGS, gettext_noop("Emulated mouse sensitivity."), NULL, MDFNST_FLOAT, "0.50", NULL, NULL, NULL }, + + { "snes.correct_aspect", MDFNSF_CAT_VIDEO, gettext_noop("Correct the aspect ratio."), gettext_noop("Note that regardless of this setting's value, \"512\" and \"256\" width modes will be scaled to the same dimensions for display."), MDFNST_BOOL, "0" }, + + { NULL } +}; + +static const FileExtensionSpecStruct KnownExtensions[] = +{ + { ".smc", "Super Magicom ROM Image" }, + { ".swc", "Super Wildcard ROM Image" }, + { ".sfc", "Cartridge ROM Image" }, + { ".fig", "Cartridge ROM Image" }, + + { ".bs", "BS-X EEPROM Image" }, + { ".st", "Sufami Turbo Cartridge ROM Image" }, + + { NULL, NULL } +}; + +MDFNGI EmulatedSNES = +{ + "snes", + "Super Nintendo Entertainment System/Super Famicom", + KnownExtensions, + MODPRIO_INTERNAL_HIGH, + NULL, // Debugger + &SNESInputInfo, + Load, + TestMagic, + NULL, + NULL, + CloseGame, + SetLayerEnableMask, + NULL, // Layer names, null-delimited + NULL, + NULL, + NULL, //InstallReadPatch, + NULL, //RemoveReadPatches, + NULL, //MemRead, + true, + StateAction, + Emulate, + SetInput, + DoSimpleCommand, + SNESSettings, + 0, + 0, + FALSE, // Multires + + 512, // lcm_width + 480, // lcm_height (replaced in game load) + NULL, // Dummy + + 256, // Nominal width (replaced in game load) + 240, // Nominal height (replaced in game load) + + 512, // Framebuffer width + 512, // Framebuffer height + + 2, // Number of output sound channels +}; + + diff --git a/mednafen/snes/src/Makefile b/mednafen/snes/src/Makefile new file mode 100755 index 0000000..feb1171 --- /dev/null +++ b/mednafen/snes/src/Makefile @@ -0,0 +1,227 @@ +include lib/nall/Makefile +include lib/nall/Makefile-qt +ui = ui_qt + +################ +### compiler ### +################ + +c := $(compiler) +cpp := $(subst cc,++,$(compiler)) +flags := -O3 -fomit-frame-pointer -Ilib +link := + +# profile-guided instrumentation: +# flags += -fprofile-generate +# link += -lgcov + +# profile-guided optimization: +# flags += -fprofile-use + +################ +### platform ### +################ + +ifeq ($(platform),x) + link += -s + + ruby := video.glx video.xv video.qtraster video.sdl + ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao + ruby += input.sdl input.x + + link += $(if $(findstring audio.openal,$(ruby)),-lopenal) +else ifeq ($(platform),osx) + ruby := video.qtopengl video.qtraster + ruby += audio.openal + ruby += input.carbon + + link += $(if $(findstring audio.openal,$(ruby)),-framework OpenAL) +else ifeq ($(platform),win) + link += -mwindows -mthreads +# link += -mconsole -mthreads + link += -s -luuid -lkernel32 -luser32 -lgdi32 -lshell32 + # statically link Qt for Windows build + link += -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc + + ruby := video.direct3d video.wgl video.directdraw video.gdi video.qtraster + ruby += audio.directsound + ruby += input.rawinput input.directinput + + link += $(if $(findstring audio.openal,$(ruby)),-lopenal32) +else + unknown_platform: help; +endif + +############ +### ruby ### +############ + +rubyflags := $(call ifhas,.sdl,$(ruby),`sdl-config --cflags`) +rubyflags += $(call ifhas,.qt,$(ruby),$(qtinc)) + +link += $(call ifhas,.sdl,$(ruby),`sdl-config --libs`) +link += $(call ifhas,video.direct3d,$(ruby),-ld3d9) +link += $(call ifhas,video.directdraw,$(ruby),-lddraw) +link += $(call ifhas,video.glx,$(ruby),-lGL) +link += $(call ifhas,video.wgl,$(ruby),-lopengl32) +link += $(call ifhas,video.xv,$(ruby),-lXv) +link += $(call ifhas,audio.alsa,$(ruby),-lasound) +link += $(call ifhas,audio.ao,$(ruby),-lao) +link += $(call ifhas,audio.directsound,$(ruby),-ldsound) +link += $(call ifhas,audio.pulseaudio,$(ruby),-lpulse-simple) +link += $(call ifhas,input.directinput,$(ruby),-ldinput8 -ldxguid) +link += $(call ifhas,input.rawinput,$(ruby),-ldinput8 -ldxguid) + +############### +### objects ### +############### + +objects := libco ruby +objects += system cartridge cheat +objects += memory smemory cpu cpucore scpu smp smpcore ssmp sdsp ppu bppu +objects += supergameboy superfx sa1 +objects += bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 st011 st018 +objects += 21fx + +###################### +### implicit rules ### +###################### + +compile = \ + $(strip \ + $(if $(filter %.c,$<), \ + $(c) $(flags) $1 -c $< -o $@, \ + $(if $(filter %.cpp,$<), \ + $(cpp) $(flags) $1 -c $< -o $@ \ + ) \ + ) \ + ) + +%.o: $<; $(call compile) + +all: build; + +include $(ui)/Makefile +objects := $(patsubst %,obj/%.o,$(objects)) +rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),-D$c) + +################# +### libraries ### +################# + +obj/ruby.o: lib/ruby/ruby.cpp $(call rwildcard,lib/ruby/*) + $(call compile,$(rubydef) $(rubyflags)) +obj/libco.o: lib/libco/libco.c lib/libco/* + +################# +### utilities ### +################# + +obj/cartridge.o: cartridge/cartridge.cpp cartridge/* +obj/cheat.o : cheat/cheat.cpp cheat/* + +############## +### memory ### +############## + +obj/memory.o : memory/memory.cpp memory/* +obj/smemory.o: memory/smemory/smemory.cpp $(call rwildcard,memory/smemory/) + +########### +### cpu ### +########### + +obj/cpu.o : cpu/cpu.cpp cpu/* +obj/cpucore.o: cpu/core/core.cpp $(call rwildcard,cpu/core/) +obj/scpu.o : cpu/scpu/scpu.cpp $(call rwildcard,cpu/scpu/) + +########### +### smp ### +########### + +obj/smp.o : smp/smp.cpp smp/* +obj/smpcore.o: smp/core/core.cpp $(call rwildcard,smp/core/) +obj/ssmp.o : smp/ssmp/ssmp.cpp $(call rwildcard,smp/ssmp/) + +########### +### dsp ### +########### + +obj/adsp.o: dsp/adsp/adsp.cpp dsp/adsp/* +obj/sdsp.o: dsp/sdsp/sdsp.cpp dsp/sdsp/* + +########### +### ppu ### +########### + +obj/ppu.o : ppu/ppu.cpp ppu/* +obj/bppu.o: ppu/bppu/bppu.cpp $(call rwildcard,ppu/bppu/) + +############## +### system ### +############## + +obj/system.o: system/system.cpp $(call rwildcard,system/) + +##################### +### special chips ### +##################### + +obj/supergameboy.o: chip/supergameboy/supergameboy.cpp $(call rwildcard,chip/supergameboy/) +obj/superfx.o : chip/superfx/superfx.cpp $(call rwildcard,chip/superfx/) +obj/sa1.o : chip/sa1/sa1.cpp $(call rwildcard,chip/sa1/) +obj/bsx.o : chip/bsx/bsx.cpp chip/bsx/* +obj/srtc.o : chip/srtc/srtc.cpp chip/srtc/* +obj/sdd1.o : chip/sdd1/sdd1.cpp chip/sdd1/* +obj/spc7110.o : chip/spc7110/spc7110.cpp chip/spc7110/* +obj/cx4.o : chip/cx4/cx4.cpp chip/cx4/* +obj/dsp1.o : chip/dsp1/dsp1.cpp chip/dsp1/* +obj/dsp2.o : chip/dsp2/dsp2.cpp chip/dsp2/* +obj/dsp3.o : chip/dsp3/dsp3.cpp chip/dsp3/* +obj/dsp4.o : chip/dsp4/dsp4.cpp chip/dsp4/* +obj/obc1.o : chip/obc1/obc1.cpp chip/obc1/* +obj/st010.o : chip/st010/st010.cpp chip/st010/* +obj/st011.o : chip/st011/st011.cpp chip/st011/* +obj/st018.o : chip/st018/st018.cpp chip/st018/* +obj/21fx.o : chip/21fx/21fx.cpp chip/21fx/* + +############### +### targets ### +############### + +build: ui_build $(objects) +ifeq ($(platform),osx) + test -d ../bsnes.app || mkdir -p ../bsnes.app/Contents/MacOS + $(strip $(cpp) -o ../bsnes.app/Contents/MacOS/bsnes $(objects) $(link)) +else + $(strip $(cpp) -o ../bsnes $(objects) $(link)) +endif + +install: + install -D -m 755 ../bsnes $(DESTDIR)$(prefix)/bin/bsnes + install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png + install -D -m 644 data/bsnes.desktop $(DESTDIR)$(prefix)/share/applications/bsnes.desktop + +clean: ui_clean + -@$(call delete,obj/*.o) + -@$(call delete,*.res) + -@$(call delete,*.pgd) + -@$(call delete,*.pgc) + -@$(call delete,*.ilk) + -@$(call delete,*.pdb) + -@$(call delete,*.manifest) + +help: + @echo "Usage: $(MAKE) platform=(os) compiler=(cc) [options]" + @echo "" + @echo "Supported platforms:" + @echo " x - Linux / BSD (x86, x86-64)" + @echo " win - Windows (x86, x86-64)" + @echo "" + @echo "Supported compilers:" + @echo " gcc - GCC compiler" + @echo " mingw32-gcc - MinGW compiler" + @echo " i586-mingw32-gcc - MinGW cross compiler" + @echo "" + @echo "Example: $(MAKE) platform=x compiler=gcc" + @echo "" diff --git a/mednafen/snes/src/base.hpp b/mednafen/snes/src/base.hpp new file mode 100755 index 0000000..17bc404 --- /dev/null +++ b/mednafen/snes/src/base.hpp @@ -0,0 +1,46 @@ +static const char bsnesVersion[] = "0.059"; +static const char bsnesTitle[] = "bsnes"; +static const unsigned bsnesSerializerVersion = 4; + +//S-DSP can be encapsulated into a state machine using #define magic +//this avoids ~2.048m co_switch() calls per second (~5% speedup) +#define DSP_STATE_MACHINE + +//game genie + pro action replay code support (~2% speed hit) +#define CHEAT_SYSTEM + +//enable debugging extensions (~15% speed hit) +//#define DEBUGGER + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; + +#include "interface.hpp" diff --git a/mednafen/snes/src/cartridge/cartridge.cpp b/mednafen/snes/src/cartridge/cartridge.cpp new file mode 100755 index 0000000..af477d2 --- /dev/null +++ b/mednafen/snes/src/cartridge/cartridge.cpp @@ -0,0 +1,133 @@ +#include <../base.hpp> +#include +#include + +#define CARTRIDGE_CPP +namespace SNES { + +#include "header.cpp" +#include "gameboyheader.cpp" +#include "serialization.cpp" + +namespace memory { + MappedRAM cartrom, cartram, cartrtc; + MappedRAM bsxflash, bsxram, bsxpram; + MappedRAM stArom, stAram; + MappedRAM stBrom, stBram; + MappedRAM gbrom, gbram, gbrtc; +}; + +Cartridge cartridge; + +void Cartridge::load(Mode cartridge_mode) { + mode = cartridge_mode; + read_header(memory::cartrom.data(), memory::cartrom.size()); + + if(ram_size > 0) { + memory::cartram.map(allocate(ram_size, 0xff), ram_size); + } + + if(has_srtc || has_spc7110rtc) { + memory::cartrtc.map(allocate(20, 0xff), 20); + } + + if(mode == ModeBsx) { + memory::bsxram.map (allocate( 32 * 1024, 0xff), 32 * 1024); + memory::bsxpram.map(allocate(512 * 1024, 0xff), 512 * 1024); + } + + if(mode == ModeSufamiTurbo) { + if(memory::stArom.data()) memory::stAram.map(allocate(128 * 1024, 0xff), 128 * 1024); + if(memory::stBrom.data()) memory::stBram.map(allocate(128 * 1024, 0xff), 128 * 1024); + } + + if(mode == ModeSuperGameBoy) { + if(memory::gbrom.data()) { + unsigned ram_size = gameboy_ram_size(); + unsigned rtc_size = gameboy_rtc_size(); + + if(ram_size) memory::gbram.map(allocate(ram_size, 0xff), ram_size); + if(rtc_size) memory::gbrtc.map(allocate(rtc_size, 0x00), rtc_size); + } + } + + memory::cartrom.write_protect(true); + memory::cartram.write_protect(false); + memory::cartrtc.write_protect(false); + memory::bsxflash.write_protect(true); + memory::bsxram.write_protect(false); + memory::bsxpram.write_protect(false); + memory::stArom.write_protect(true); + memory::stAram.write_protect(false); + memory::stBrom.write_protect(true); + memory::stBram.write_protect(false); + memory::gbrom.write_protect(true); + memory::gbram.write_protect(false); + memory::gbrtc.write_protect(false); + + unsigned checksum = ~0; + for(unsigned n = 0; n < memory::cartrom.size(); n++) checksum = crc32_adjust(checksum, memory::cartrom[n]); + if(memory::bsxflash.size() != 0 && memory::bsxflash.size() != ~0) + for(unsigned n = 0; n < memory::bsxflash.size(); n++) checksum = crc32_adjust(checksum, memory::bsxflash[n]); + if(memory::stArom.size() != 0 && memory::stArom.size() != ~0) + for(unsigned n = 0; n < memory::stArom.size(); n++) checksum = crc32_adjust(checksum, memory::stArom[n]); + if(memory::stBrom.size() != 0 && memory::stBrom.size() != ~0) + for(unsigned n = 0; n < memory::stBrom.size(); n++) checksum = crc32_adjust(checksum, memory::stBrom[n]); + if(memory::gbrom.size() != 0 && memory::gbrom.size() != ~0) + for(unsigned n = 0; n < memory::gbrom.size(); n++) checksum = crc32_adjust(checksum, memory::gbrom[n]); + crc32 = ~checksum; + +#if 0 + fprintf(stdout, "crc32 = %.8x\n", (unsigned)crc32); + + sha256_ctx sha; + uint8_t shahash[32]; + sha256_init(&sha); + sha256_chunk(&sha, memory::cartrom.data(), memory::cartrom.size()); + sha256_final(&sha); + sha256_hash(&sha, shahash); + + fprintf(stdout, "sha256 = "); + for(unsigned i = 0; i < 32; i++) fprintf(stdout, "%.2x", shahash[i]); + fprintf(stdout, "\n"); +#endif + + bus.load_cart(); + system.serialize_init(); + loaded = true; +} + +void Cartridge::unload() { + memory::cartrom.reset(); + memory::cartram.reset(); + memory::cartrtc.reset(); + memory::bsxflash.reset(); + memory::bsxram.reset(); + memory::bsxpram.reset(); + memory::stArom.reset(); + memory::stAram.reset(); + memory::stBrom.reset(); + memory::stBram.reset(); + memory::gbrom.reset(); + memory::gbram.reset(); + memory::gbrtc.reset(); + + if(loaded == false) return; + bus.unload_cart(); + loaded = false; +} + +bool Cartridge::has_21fx() const { + return s21fx.exists(); +} + +Cartridge::Cartridge() { + loaded = false; + unload(); +} + +Cartridge::~Cartridge() { + unload(); +} + +} diff --git a/mednafen/snes/src/cartridge/cartridge.hpp b/mednafen/snes/src/cartridge/cartridge.hpp new file mode 100755 index 0000000..3773e92 --- /dev/null +++ b/mednafen/snes/src/cartridge/cartridge.hpp @@ -0,0 +1,116 @@ +class Cartridge : property { +public: + enum Mode { + ModeNormal, + ModeBsxSlotted, + ModeBsx, + ModeSufamiTurbo, + ModeSuperGameBoy, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeSuperGameBoy1Bios, + TypeSuperGameBoy2Bios, + TypeGameBoy, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + LoROM, + HiROM, + ExLoROM, + ExHiROM, + SuperFXROM, + SA1ROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + readonly loaded; //is a base cartridge inserted? + readonly crc32; //crc32 of all cartridges (base+slot(s)) + + readonly mode; + readonly type; + readonly region; + readonly mapper; + readonly dsp1_mapper; + + readonly has_bsx_slot; + readonly has_superfx; + readonly has_sa1; + readonly has_srtc; + readonly has_sdd1; + readonly has_spc7110; + readonly has_spc7110rtc; + readonly has_cx4; + readonly has_dsp1; + readonly has_dsp2; + readonly has_dsp3; + readonly has_dsp4; + readonly has_obc1; + readonly has_st010; + readonly has_st011; + readonly has_st018; + bool has_21fx() const; + + void load(Mode); + void unload(); + + void serialize(serializer&); + Cartridge(); + ~Cartridge(); + +private: + enum HeaderField { + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, + }; + + unsigned ram_size; + void read_header(const uint8_t *data, unsigned size); + unsigned find_header(const uint8_t *data, unsigned size) const; + unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const; + + unsigned gameboy_ram_size() const; + unsigned gameboy_rtc_size() const; +}; + +namespace memory { + extern MappedRAM cartrom, cartram, cartrtc; + extern MappedRAM bsxflash, bsxram, bsxpram; + extern MappedRAM stArom, stAram; + extern MappedRAM stBrom, stBram; + extern MappedRAM gbrom, gbram, gbrtc; +}; + +extern Cartridge cartridge; diff --git a/mednafen/snes/src/cartridge/gameboyheader.cpp b/mednafen/snes/src/cartridge/gameboyheader.cpp new file mode 100755 index 0000000..2bc9f8f --- /dev/null +++ b/mednafen/snes/src/cartridge/gameboyheader.cpp @@ -0,0 +1,22 @@ +#ifdef CARTRIDGE_CPP + +unsigned Cartridge::gameboy_ram_size() const { + if(memory::gbrom.size() < 512) return 0; + switch(memory::gbrom[0x0149]) { + case 0x00: return 0 * 1024; + case 0x01: return 8 * 1024; + case 0x02: return 8 * 1024; + case 0x03: return 32 * 1024; + case 0x04: return 128 * 1024; + case 0x05: return 128 * 1024; + default: return 128 * 1024; + } +} + +unsigned Cartridge::gameboy_rtc_size() const { + if(memory::gbrom.size() < 512) return 0; + if(memory::gbrom[0x0147] == 0x0f || memory::gbrom[0x0147] == 0x10) return 4; + return 0; +} + +#endif diff --git a/mednafen/snes/src/cartridge/header.cpp b/mednafen/snes/src/cartridge/header.cpp new file mode 100755 index 0000000..805029a --- /dev/null +++ b/mednafen/snes/src/cartridge/header.cpp @@ -0,0 +1,321 @@ +#ifdef CARTRIDGE_CPP + +void Cartridge::read_header(const uint8_t *data, unsigned size) { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + region = NTSC; + ram_size = 0; + + has_bsx_slot = false; + has_superfx = false; + has_sa1 = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_cx4 = false; + has_dsp1 = false; + has_dsp2 = false; + has_dsp3 = false; + has_dsp4 = false; + has_obc1 = false; + has_st010 = false; + has_st011 = false; + has_st018 = false; + + //===================== + //detect Game Boy carts + //===================== + + if(size >= 0x0140) { + if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 + && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { + type = TypeGameBoy; + return; + } + } + + const unsigned index = find_header(data, size); + const uint8 mapperid = data[index + Mapper]; + const uint8 rom_type = data[index + RomType]; + const uint8 rom_size = data[index + RomSize]; + const uint8 company = data[index + Company]; + const uint8 regionid = data[index + CartRegion] & 0x7f; + + ram_size = 1024 << (data[index + RamSize] & 7); + if(ram_size == 1024) ram_size = 0; //no RAM present + + //0, 1, 13 = NTSC; 2 - 12 = PAL + region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL; + + //======================= + //detect BS-X flash carts + //======================= + + if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { + if(data[index + 0x14] == 0x00) { + const uint8_t n15 = data[index + 0x15]; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { + if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { + type = TypeBsx; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; + } + } + } + } + + //========================= + //detect Sufami Turbo carts + //========================= + + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { + if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { + type = TypeSufamiTurboBios; + } else { + type = TypeSufamiTurbo; + } + mapper = STROM; + region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled outside this routine + } + + //========================== + //detect Super Game Boy BIOS + //========================== + + if(!memcmp(data + index, "Super GAMEBOY2", 14)) { + type = TypeSuperGameBoy2Bios; + return; + } + + if(!memcmp(data + index, "Super GAMEBOY", 13)) { + type = TypeSuperGameBoy1Bios; + return; + } + + //===================== + //detect standard carts + //===================== + + //detect presence of BS-X flash cartridge connector (reads extended header information) + if(data[index - 14] == 'Z') { + if(data[index - 11] == 'J') { + uint8 n13 = data[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { + has_bsx_slot = true; + } + } + } + } + + if(has_bsx_slot) { + if(!memcmp(data + index, "Satellaview BS-X ", 21)) { + //BS-X base cart + type = TypeBsxBios; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + } else { + type = TypeBsxSlotted; + mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + region = NTSC; //BS-X slotted cartridges only released in Japan + } + } else { + //standard cart + type = TypeNormal; + + if(index == 0x7fc0 && size >= 0x401000) { + mapper = ExLoROM; + } else if(index == 0x7fc0 && mapperid == 0x32) { + mapper = ExLoROM; + } else if(index == 0x7fc0) { + mapper = LoROM; + } else if(index == 0xffc0) { + mapper = HiROM; + } else { //index == 0x40ffc0 + mapper = ExHiROM; + } + } + + if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + has_superfx = true; + mapper = SuperFXROM; + ram_size = 1024 << (data[index - 3] & 7); + if(ram_size == 1024) ram_size = 0; + } + + if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) { + has_sa1 = true; + mapper = SA1ROM; + } + + if(mapperid == 0x35 && rom_type == 0x55) { + has_srtc = true; + } + + if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + has_sdd1 = true; + } + + if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + has_spc7110 = true; + has_spc7110rtc = (rom_type == 0xf9); + mapper = SPC7110ROM; + } + + if(mapperid == 0x20 && rom_type == 0xf3) { + has_cx4 = true; + } + + if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { + has_dsp1 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { + has_dsp1 = true; + } + + if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + has_dsp1 = true; + } + + if(has_dsp1 == true) { + if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { + dsp1_mapper = DSP1LoROM1MB; + } else if((mapperid & 0x2f) == 0x20) { + dsp1_mapper = DSP1LoROM2MB; + } else if((mapperid & 0x2f) == 0x21) { + dsp1_mapper = DSP1HiROM; + } + } + + if(mapperid == 0x20 && rom_type == 0x05) { + has_dsp2 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { + has_dsp3 = true; + } + + if(mapperid == 0x30 && rom_type == 0x03) { + has_dsp4 = true; + } + + if(mapperid == 0x30 && rom_type == 0x25) { + has_obc1 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + has_st010 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { + has_st011 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf5) { + has_st018 = true; + } +} + +unsigned Cartridge::find_header(const uint8_t *data, unsigned size) const { + unsigned score_lo = score_header(data, size, 0x007fc0); + unsigned score_hi = score_header(data, size, 0x00ffc0); + unsigned score_ex = score_header(data, size, 0x40ffc0); + if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits + + if(score_lo >= score_hi && score_lo >= score_ex) { + return 0x007fc0; + } else if(score_hi >= score_ex) { + return 0x00ffc0; + } else { + return 0x40ffc0; + } +} + +unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) const { + if(size < addr + 64) return 0; //image too small to contain header at this location? + int score = 0; + + uint16 resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16 checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16 complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); + + uint8 resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8 mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit + + //$00:[000-7fff] contains uninitialized RAM and MMIO. + //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. + if(resetvector < 0x8000) return 0; + + //some images duplicate the header in multiple locations, and others have completely + //invalid header information that cannot be relied upon. + //below code will analyze the first opcode executed at the specified reset vector to + //determine the probability that this is the correct header. + + //most likely opcodes + if(resetop == 0x78 //sei + || resetop == 0x18 //clc (clc; xce) + || resetop == 0x38 //sec (sec; xce) + || resetop == 0x9c //stz $nnnn (stz $4200) + || resetop == 0x4c //jmp $nnnn + || resetop == 0x5c //jml $nnnnnn + ) score += 8; + + //plausible opcodes + if(resetop == 0xc2 //rep #$nn + || resetop == 0xe2 //sep #$nn + || resetop == 0xad //lda $nnnn + || resetop == 0xae //ldx $nnnn + || resetop == 0xac //ldy $nnnn + || resetop == 0xaf //lda $nnnnnn + || resetop == 0xa9 //lda #$nn + || resetop == 0xa2 //ldx #$nn + || resetop == 0xa0 //ldy #$nn + || resetop == 0x20 //jsr $nnnn + || resetop == 0x22 //jsl $nnnnnn + ) score += 4; + + //implausible opcodes + if(resetop == 0x40 //rti + || resetop == 0x60 //rts + || resetop == 0x6b //rtl + || resetop == 0xcd //cmp $nnnn + || resetop == 0xec //cpx $nnnn + || resetop == 0xcc //cpy $nnnn + ) score -= 4; + + //least likely opcodes + if(resetop == 0x00 //brk #$nn + || resetop == 0x02 //cop #$nn + || resetop == 0xdb //stp + || resetop == 0x42 //wdm + || resetop == 0xff //sbc $nnnnnn,x + ) score -= 8; + + //at times, both the header and reset vector's first opcode will match ... + //fallback and rely on info validity in these cases to determine more likely header. + + //a valid checksum is the biggest indicator of a valid header. + if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; + + if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM + if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM + if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM + if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM + + if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header + if(data[addr + RomType] < 0x08) score++; + if(data[addr + RomSize] < 0x10) score++; + if(data[addr + RamSize] < 0x08) score++; + if(data[addr + CartRegion] < 14) score++; + + if(score < 0) score = 0; + return score; +} + +#endif diff --git a/mednafen/snes/src/cartridge/serialization.cpp b/mednafen/snes/src/cartridge/serialization.cpp new file mode 100755 index 0000000..847b235 --- /dev/null +++ b/mednafen/snes/src/cartridge/serialization.cpp @@ -0,0 +1,37 @@ +#ifdef CARTRIDGE_CPP + +void Cartridge::serialize(serializer &s) { + if(memory::cartram.size() != 0 && memory::cartram.size() != ~0) { + s.array(memory::cartram.data(), memory::cartram.size()); + } + + if(memory::cartrtc.size() != 0 && memory::cartrtc.size() != ~0) { + s.array(memory::cartrtc.data(), memory::cartrtc.size()); + } + + if(memory::bsxram.size() != 0 && memory::bsxram.size() != ~0) { + s.array(memory::bsxram.data(), memory::bsxram.size()); + } + + if(memory::bsxpram.size() != 0 && memory::bsxpram.size() != ~0) { + s.array(memory::bsxpram.data(), memory::bsxpram.size()); + } + + if(memory::stAram.size() != 0 && memory::stAram.size() != ~0) { + s.array(memory::stAram.data(), memory::stAram.size()); + } + + if(memory::stBram.size() != 0 && memory::stBram.size() != ~0) { + s.array(memory::stBram.data(), memory::stBram.size()); + } + + if(memory::gbram.size() != 0 && memory::gbram.size() != ~0) { + s.array(memory::gbram.data(), memory::gbram.size()); + } + + if(memory::gbrtc.size() != 0 && memory::gbrtc.size() != ~0) { + s.array(memory::gbrtc.data(), memory::gbrtc.size()); + } +} + +#endif diff --git a/mednafen/snes/src/cc.bat b/mednafen/snes/src/cc.bat new file mode 100755 index 0000000..be183a4 --- /dev/null +++ b/mednafen/snes/src/cc.bat @@ -0,0 +1,3 @@ +@mingw32-make + +@pause diff --git a/mednafen/snes/src/cheat/cheat-inline.hpp b/mednafen/snes/src/cheat/cheat-inline.hpp new file mode 100755 index 0000000..a80f47f --- /dev/null +++ b/mednafen/snes/src/cheat/cheat-inline.hpp @@ -0,0 +1,2 @@ +bool Cheat::active() const { return cheat_enabled; } +bool Cheat::exists(unsigned addr) const { return bitmask[addr >> 3] & 1 << (addr & 7); } diff --git a/mednafen/snes/src/cheat/cheat.cpp b/mednafen/snes/src/cheat/cheat.cpp new file mode 100755 index 0000000..381f6e2 --- /dev/null +++ b/mednafen/snes/src/cheat/cheat.cpp @@ -0,0 +1,196 @@ +#include <../base.hpp> + +#define CHEAT_CPP +namespace SNES { + +Cheat cheat; + +bool Cheat::enabled() const { + return system_enabled; +} + +void Cheat::enable(bool state) { + system_enabled = state; + cheat_enabled = system_enabled && code_enabled; +} + +void Cheat::synchronize() { + memset(bitmask, 0x00, sizeof bitmask); + code_enabled = false; + + for(unsigned i = 0; i < size(); i++) { + const CheatCode &code = operator[](i); + if(code.enabled == false) continue; + + for(unsigned n = 0; n < code.addr.size(); n++) { + code_enabled = true; + + unsigned addr = mirror(code.addr[n]); + bitmask[addr >> 3] |= 1 << (addr & 7); + if((addr & 0xffe000) == 0x7e0000) { + //mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff + unsigned mirroraddr; + for(unsigned x = 0; x <= 0x3f; x++) { + mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff); + bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7); + + mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff); + bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7); + } + } + } + } + + cheat_enabled = system_enabled && code_enabled; +} + +bool Cheat::read(unsigned addr, uint8 &data) const { + addr = mirror(addr); + + for(unsigned i = 0; i < size(); i++) { + const CheatCode &code = operator[](i); + if(code.enabled == false) continue; + + for(unsigned n = 0; n < code.addr.size(); n++) { + if(addr == mirror(code.addr[n])) { + data = code.data[n]; + return true; + } + } + } + + return false; +} + +Cheat::Cheat() { + system_enabled = true; + synchronize(); +} + +//=============== +//encode / decode +//=============== + +bool Cheat::decode(const char *s, unsigned &addr, uint8 &data, Type &type) { + string t = s; + strlower(t); + + #define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f')) + + if(strlen(t) == 8 || (strlen(t) == 9 && t[6] == ':')) { + //strip ':' + if(strlen(t) == 9 && t[6] == ':') t = string() << substr(t, 0, 6) << substr(t, 7); + //validate input + for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false; + + type = ProActionReplay; + unsigned r = strhex((const char*)t); + addr = r >> 8; + data = r & 0xff; + return true; + } else if(strlen(t) == 9 && t[4] == '-') { + //strip '-' + t = string() << substr(t, 0, 4) << substr(t, 5); + //validate input + for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false; + + type = GameGenie; + strtr(t, "df4709156bc8a23e", "0123456789abcdef"); + unsigned r = strhex((const char*)t); + //8421 8421 8421 8421 8421 8421 + //abcd efgh ijkl mnop qrst uvwx + //ijkl qrst opab cduv wxef ghmn + addr = (!!(r & 0x002000) << 23) | (!!(r & 0x001000) << 22) + | (!!(r & 0x000800) << 21) | (!!(r & 0x000400) << 20) + | (!!(r & 0x000020) << 19) | (!!(r & 0x000010) << 18) + | (!!(r & 0x000008) << 17) | (!!(r & 0x000004) << 16) + | (!!(r & 0x800000) << 15) | (!!(r & 0x400000) << 14) + | (!!(r & 0x200000) << 13) | (!!(r & 0x100000) << 12) + | (!!(r & 0x000002) << 11) | (!!(r & 0x000001) << 10) + | (!!(r & 0x008000) << 9) | (!!(r & 0x004000) << 8) + | (!!(r & 0x080000) << 7) | (!!(r & 0x040000) << 6) + | (!!(r & 0x020000) << 5) | (!!(r & 0x010000) << 4) + | (!!(r & 0x000200) << 3) | (!!(r & 0x000100) << 2) + | (!!(r & 0x000080) << 1) | (!!(r & 0x000040) << 0); + data = r >> 24; + return true; + } else { + return false; + } + + #undef ischr +} + +bool Cheat::encode(string &s, unsigned addr, uint8 data, Type type) { + char t[16]; + + if(type == ProActionReplay) { + sprintf(t, "%.6x%.2x", addr, data); + s = t; + return true; + } else if(type == GameGenie) { + unsigned r = addr; + addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22) + | (!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20) + | (!!(r & 0x000080) << 19) | (!!(r & 0x000040) << 18) + | (!!(r & 0x000020) << 17) | (!!(r & 0x000010) << 16) + | (!!(r & 0x000200) << 15) | (!!(r & 0x000100) << 14) + | (!!(r & 0x800000) << 13) | (!!(r & 0x400000) << 12) + | (!!(r & 0x200000) << 11) | (!!(r & 0x100000) << 10) + | (!!(r & 0x000008) << 9) | (!!(r & 0x000004) << 8) + | (!!(r & 0x000002) << 7) | (!!(r & 0x000001) << 6) + | (!!(r & 0x080000) << 5) | (!!(r & 0x040000) << 4) + | (!!(r & 0x020000) << 3) | (!!(r & 0x010000) << 2) + | (!!(r & 0x000800) << 1) | (!!(r & 0x000400) << 0); + sprintf(t, "%.2x%.2x-%.4x", data, addr >> 16, addr & 0xffff); + strtr(t, "0123456789abcdef", "df4709156bc8a23e"); + s = t; + return true; + } else { + return false; + } +} + +//======== +//internal +//======== + +unsigned Cheat::mirror(unsigned addr) const { + //$00-3f|80-bf:0000-1fff -> $7e:0000-1fff + if((addr & 0x40e000) == 0x000000) return (0x7e0000 + (addr & 0x1fff)); + return addr; +} + +//========= +//CheatCode +//========= + +bool CheatCode::operator=(string s) { + addr.reset(); + data.reset(); + + lstring list; + list.split("+", s.replace(" ", "")); + + for(unsigned i = 0; i < list.size(); i++) { + unsigned addr_; + uint8 data_; + Cheat::Type type_; + if(Cheat::decode(list[i], addr_, data_, type_) == false) { + addr.reset(); + data.reset(); + return false; + } + + addr.add(addr_); + data.add(data_); + } + + return true; +} + +CheatCode::CheatCode() { + enabled = false; +} + +} diff --git a/mednafen/snes/src/cheat/cheat.hpp b/mednafen/snes/src/cheat/cheat.hpp new file mode 100755 index 0000000..388085c --- /dev/null +++ b/mednafen/snes/src/cheat/cheat.hpp @@ -0,0 +1,35 @@ +struct CheatCode { + bool enabled; + array addr; + array data; + + bool operator=(string); + CheatCode(); +}; + +class Cheat : public vector { +public: + enum Type { ProActionReplay, GameGenie }; + + bool enabled() const; + void enable(bool); + void synchronize(); + bool read(unsigned, uint8&) const; + + inline bool active() const; + inline bool exists(unsigned addr) const; + + Cheat(); + + static bool decode(const char*, unsigned&, uint8&, Type&); + static bool encode(string&, unsigned, uint8, Type); + +private: + uint8 bitmask[0x200000]; + bool system_enabled; + bool code_enabled; + bool cheat_enabled; + unsigned mirror(unsigned) const; +}; + +extern Cheat cheat; diff --git a/mednafen/snes/src/chip/21fx/21fx.cpp b/mednafen/snes/src/chip/21fx/21fx.cpp new file mode 100755 index 0000000..82590b7 --- /dev/null +++ b/mednafen/snes/src/chip/21fx/21fx.cpp @@ -0,0 +1,195 @@ +#include <../base.hpp> + +//B-bus interface + +//$21f0 command port (r/w) +//------------------------- +//$00 set data port address (sr[3-0] = address) +//$01 set audio track number (sr[1-0] = track number) +//$02 set volume (sr[1] = left, sr[0] = right) +//$03 set audio state (sr[0].d1 = pause, sr[0].d0 = repeat) +// +//d7 = data port busy +//d6 = audio port busy +//d5 = audio playing +//d4 = reserved (0) +//d3-d0 = version (0) +// +// +//$21f1 parameter port (w) +//------------------------- +//(shift register) +// +// +//$21f2 data port (r) +//-------------------- +//(auto-increment read port) + +//A-bus interface + +//$2200 command port (r/w) +//------------------------- +//$00 set data port address (sr[3-0] = address) +//$01 set audio track number (sr[1-0] = track number) +//$02 set volume (sr[1] = left, sr[0] = right) +//$03 set audio state (sr[0].d1 = pause, sr[0].d0 = repeat) +// +//d7 = data port busy +//d6 = audio port busy +//d5 = audio playing +//d4 = reserved (0) +//d3-d0 = version (0) +// +//$2201 data port (r/w) +//---------------------- +//(shift register) +// +//(auto-increment read port) + +#define S21FX_CPP +namespace SNES { + +S21fx s21fx; + +#include "serialization.cpp" + +void S21fx::enter() { + scheduler.clock.cop_freq = 44100; + + while(true) { + while(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + + int16 left = 0, right = 0; + + if((mmio.status & AudioPlaying) && !mmio.audio_pause) { + if(audiofile.open()) { + if(audiofile.end()) { + if(!mmio.audio_repeat) mmio.status &= ~AudioPlaying; + audiofile.seek(mmio.audio_offset = 58); + } else { + mmio.audio_offset += 4; + left = audiofile.readl(2); + right = audiofile.readl(2); + } + } else { + mmio.status &= ~AudioPlaying; + } + } + + left = sclamp<16>((double)left * (double)mmio.audio_volume_left / 255.0); + right = sclamp<16>((double)right * (double)mmio.audio_volume_right / 255.0); + + audio.coprocessor_sample(left, right); + scheduler.addclocks_cop(1); + scheduler.sync_copcpu(); + } +} + +void S21fx::init() { +} + +void S21fx::enable() { + audio.coprocessor_enable(true); + audio.coprocessor_frequency(44100.0); + + for(unsigned i = 0x21f0; i <= 0x21f7; i++) { + memory::mmio.map(i, *this); + } + + memory::mmio.map(0x2200, *this); + memory::mmio.map(0x2201, *this); + + if(datafile.open()) datafile.close(); + datafile.open(string() << basepath << "21fx.bin", file::mode_read); +} + +void S21fx::power() { + reset(); +} + +void S21fx::reset() { + mmio.status = DataPortBusy | AudioBusy; + mmio.shift_register = 0; + + mmio.data_offset = 0; + mmio.audio_offset = 0; + mmio.audio_track = 0; + mmio.audio_volume_left = 255; + mmio.audio_volume_right = 255; + mmio.audio_repeat = false; + mmio.audio_pause = false; +} + +uint8 S21fx::mmio_read(unsigned addr) { + addr &= 0xffff; + + if((addr == 0x21f0) || (addr == 0x2200)) { + return mmio.status | 0x00; + } + + if((addr == 0x21f2) || (addr == 0x2201)) { + if(mmio.status & DataPortBusy) return 0x00; + mmio.data_offset++; + if(datafile.open()) return datafile.read(); + return 0x00; + } + + return 0x00; +} + +void S21fx::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + if((addr == 0x21f0) || (addr == 0x2200)) { + if(data == 0x00) { + mmio.data_offset = mmio.shift_register & 0xffffffff; + if(datafile.open()) { + datafile.seek(mmio.data_offset); + } + mmio.status &= ~DataPortBusy; + } + + if(data == 0x01) { + mmio.audio_track = mmio.shift_register & 0xffff; + if(audiofile.open()) audiofile.close(); + char track[16]; + sprintf(track, "%.5u", mmio.audio_track); + if(audiofile.open(string() << basepath << "audio" << track << ".wav", file::mode_read)) { + audiofile.seek(mmio.audio_offset = 58); //skip WAV header + } + mmio.status &= ~(AudioBusy | AudioPlaying); + } + + if(data == 0x02) { + mmio.audio_volume_left = mmio.shift_register >> 8; + mmio.audio_volume_right = mmio.shift_register >> 0; + } + + if(data == 0x03) { + mmio.status |= AudioPlaying; + mmio.audio_repeat = mmio.shift_register & 1; + mmio.audio_pause = mmio.shift_register & 2; + } + + mmio.shift_register = 0; + } + + if((addr == 0x21f1) || (addr == 0x2201)) { + mmio.shift_register = (mmio.shift_register << 8) | data; + } +} + +void S21fx::base(const string& path) { + basepath = path; +} + +bool S21fx::exists() { + return file::exists(string() << basepath << "21fx.bin"); +} + +S21fx::S21fx() { +} + +} diff --git a/mednafen/snes/src/chip/21fx/21fx.hpp b/mednafen/snes/src/chip/21fx/21fx.hpp new file mode 100755 index 0000000..69a595e --- /dev/null +++ b/mednafen/snes/src/chip/21fx/21fx.hpp @@ -0,0 +1,44 @@ +class S21fx : public MMIO { +public: + void enter(); + + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + void base(const string &path); + bool exists(); + + void serialize(serializer&); + S21fx(); + +private: + string basepath; + file datafile; + file audiofile; + + enum Flag { + DataPortBusy = 0x80, + AudioBusy = 0x40, + AudioPlaying = 0x20, + }; + + struct MMIO { + uint8 status; + uint64 shift_register; + + uint32 data_offset; + uint32 audio_offset; + uint16 audio_track; + uint8 audio_volume_left; + uint8 audio_volume_right; + bool audio_repeat; + bool audio_pause; + } mmio; +}; + +extern S21fx s21fx; diff --git a/mednafen/snes/src/chip/21fx/serialization.cpp b/mednafen/snes/src/chip/21fx/serialization.cpp new file mode 100755 index 0000000..cfd86b5 --- /dev/null +++ b/mednafen/snes/src/chip/21fx/serialization.cpp @@ -0,0 +1,31 @@ +#ifdef S21FX_CPP + +void S21fx::serialize(serializer &s) { + s.integer(mmio.status); + s.integer(mmio.shift_register); + + s.integer(mmio.data_offset); + s.integer(mmio.audio_offset); + s.integer(mmio.audio_track); + s.integer(mmio.audio_volume_left); + s.integer(mmio.audio_volume_right); + s.integer(mmio.audio_repeat); + s.integer(mmio.audio_pause); + + //flush file handles and indices, as a different track may be playing, + //or the file offsets may be at the wrong location ... + + if(datafile.open()) datafile.close(); + if(datafile.open(string() << basepath << "21fx.bin", file::mode_read)) { + datafile.seek(mmio.data_offset); + } + + if(audiofile.open()) audiofile.close(); + char track[16]; + sprintf(track, "%.5u", mmio.audio_track); + if(audiofile.open(string() << basepath << "audio" << track << ".wav", file::mode_read)) { + audiofile.seek(mmio.audio_offset); + } +} + +#endif diff --git a/mednafen/snes/src/chip/bsx/bsx.cpp b/mednafen/snes/src/chip/bsx/bsx.cpp new file mode 100755 index 0000000..723911c --- /dev/null +++ b/mednafen/snes/src/chip/bsx/bsx.cpp @@ -0,0 +1,10 @@ +#include <../base.hpp> + +#define BSX_CPP +namespace SNES { + +#include "bsx_base.cpp" +#include "bsx_cart.cpp" +#include "bsx_flash.cpp" +}; + diff --git a/mednafen/snes/src/chip/bsx/bsx.hpp b/mednafen/snes/src/chip/bsx/bsx.hpp new file mode 100755 index 0000000..4e06b64 --- /dev/null +++ b/mednafen/snes/src/chip/bsx/bsx.hpp @@ -0,0 +1,71 @@ +class BSXBase : public MMIO { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + +private: + struct { + uint8 r2188, r2189, r218a, r218b; + uint8 r218c, r218d, r218e, r218f; + uint8 r2190, r2191, r2192, r2193; + uint8 r2194, r2195, r2196, r2197; + uint8 r2198, r2199, r219a, r219b; + uint8 r219c, r219d, r219e, r219f; + + uint8 r2192_counter; + uint8 r2192_hour, r2192_minute, r2192_second; + } regs; +}; + +class BSXCart : public MMIO { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + BSXCart(); + ~BSXCart(); + +private: + struct { + uint8 r[16]; + } regs; + + void update_memory_map(); +}; + +class BSXFlash : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + unsigned size() const; + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + +private: + struct { + unsigned command; + uint8 write_old; + uint8 write_new; + + bool flash_enable; + bool read_enable; + bool write_enable; + } regs; +}; + +extern BSXBase bsxbase; +extern BSXCart bsxcart; +extern BSXFlash bsxflash; diff --git a/mednafen/snes/src/chip/bsx/bsx_base.cpp b/mednafen/snes/src/chip/bsx/bsx_base.cpp new file mode 100755 index 0000000..2272c17 --- /dev/null +++ b/mednafen/snes/src/chip/bsx/bsx_base.cpp @@ -0,0 +1,140 @@ +#ifdef BSX_CPP + +BSXBase bsxbase; + +void BSXBase::init() { +} + +void BSXBase::enable() { + for(uint16 i = 0x2188; i <= 0x219f; i++) memory::mmio.map(i, *this); +} + +void BSXBase::power() { + reset(); +} + +void BSXBase::reset() { + memset(®s, 0x00, sizeof regs); +} + +uint8 BSXBase::mmio_read(unsigned addr) { + addr &= 0xffff; + + switch(addr) { + case 0x2188: return regs.r2188; + case 0x2189: return regs.r2189; + case 0x218a: return regs.r218a; + case 0x218c: return regs.r218c; + case 0x218e: return regs.r218e; + case 0x218f: return regs.r218f; + case 0x2190: return regs.r2190; + + case 0x2192: { + unsigned counter = regs.r2192_counter++; + if(regs.r2192_counter >= 18) regs.r2192_counter = 0; + + if(counter == 0) { + time_t rawtime; + time(&rawtime); + tm *t = localtime(&rawtime); + + regs.r2192_hour = t->tm_hour; + regs.r2192_minute = t->tm_min; + regs.r2192_second = t->tm_sec; + } + + switch(counter) { + case 0: return 0x00; //??? + case 1: return 0x00; //??? + case 2: return 0x00; //??? + case 3: return 0x00; //??? + case 4: return 0x00; //??? + case 5: return 0x01; + case 6: return 0x01; + case 7: return 0x00; + case 8: return 0x00; + case 9: return 0x00; + case 10: return regs.r2192_second; + case 11: return regs.r2192_minute; + case 12: return regs.r2192_hour; + case 13: return 0x00; //??? + case 14: return 0x00; //??? + case 15: return 0x00; //??? + case 16: return 0x00; //??? + case 17: return 0x00; //??? + } + } break; + + case 0x2193: return regs.r2193 & ~0x0c; + case 0x2194: return regs.r2194; + case 0x2196: return regs.r2196; + case 0x2197: return regs.r2197; + case 0x2199: return regs.r2199; + } + + return cpu.regs.mdr; +} + +void BSXBase::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + switch(addr) { + case 0x2188: { + regs.r2188 = data; + } break; + + case 0x2189: { + regs.r2189 = data; + } break; + + case 0x218a: { + regs.r218a = data; + } break; + + case 0x218b: { + regs.r218b = data; + } break; + + case 0x218c: { + regs.r218c = data; + } break; + + case 0x218e: { + regs.r218e = data; + } break; + + case 0x218f: { + regs.r218e >>= 1; + regs.r218e = regs.r218f - regs.r218e; + regs.r218f >>= 1; + } break; + + case 0x2191: { + regs.r2191 = data; + regs.r2192_counter = 0; + } break; + + case 0x2192: { + regs.r2190 = 0x80; + } break; + + case 0x2193: { + regs.r2193 = data; + } break; + + case 0x2194: { + regs.r2194 = data; + } break; + + case 0x2197: { + regs.r2197 = data; + } break; + + case 0x2199: { + regs.r2199 = data; + } break; + } +} + +#endif + diff --git a/mednafen/snes/src/chip/bsx/bsx_cart.cpp b/mednafen/snes/src/chip/bsx/bsx_cart.cpp new file mode 100755 index 0000000..92d6ead --- /dev/null +++ b/mednafen/snes/src/chip/bsx/bsx_cart.cpp @@ -0,0 +1,97 @@ +#ifdef BSX_CPP + +BSXCart bsxcart; + +void BSXCart::init() { +} + +void BSXCart::enable() { + for(uint16 i = 0x5000; i <= 0x5fff; i++) memory::mmio.map(i, *this); +} + +void BSXCart::power() { + reset(); +} + +void BSXCart::reset() { + for(unsigned i = 0; i < 16; i++) regs.r[i] = 0x00; + regs.r[0x07] = 0x80; + regs.r[0x08] = 0x80; + + update_memory_map(); +} + +void BSXCart::update_memory_map() { + Memory &cart = (regs.r[0x01] & 0x80) == 0x00 ? (Memory&)bsxflash : (Memory&)memory::bsxpram; + + if((regs.r[0x02] & 0x80) == 0x00) { + //LoROM mapping + bus.map(Bus::MapLinear, 0x00, 0x7d, 0x8000, 0xffff, cart); + bus.map(Bus::MapLinear, 0x80, 0xff, 0x8000, 0xffff, cart); + } else { + //HiROM mapping + bus.map(Bus::MapShadow, 0x00, 0x3f, 0x8000, 0xffff, cart); + bus.map(Bus::MapLinear, 0x40, 0x7d, 0x0000, 0xffff, cart); + bus.map(Bus::MapShadow, 0x80, 0xbf, 0x8000, 0xffff, cart); + bus.map(Bus::MapLinear, 0xc0, 0xff, 0x0000, 0xffff, cart); + } + + if(regs.r[0x03] & 0x80) { + bus.map(Bus::MapLinear, 0x60, 0x6f, 0x0000, 0xffff, memory::bsxpram); + //bus.map(Bus::MapLinear, 0x70, 0x77, 0x0000, 0xffff, memory::bsxpram); + } + + if((regs.r[0x05] & 0x80) == 0x00) { + bus.map(Bus::MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::bsxpram); + } + + if((regs.r[0x06] & 0x80) == 0x00) { + bus.map(Bus::MapLinear, 0x50, 0x5f, 0x0000, 0xffff, memory::bsxpram); + } + + if(regs.r[0x07] & 0x80) { + bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom); + } + + if(regs.r[0x08] & 0x80) { + bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom); + } + + bus.map(Bus::MapShadow, 0x20, 0x3f, 0x6000, 0x7fff, memory::bsxpram); + bus.map(Bus::MapLinear, 0x70, 0x77, 0x0000, 0xffff, memory::bsxpram); +} + +uint8 BSXCart::mmio_read(unsigned addr) { + if((addr & 0xf0ffff) == 0x005000) { //$[00-0f]:5000 MMIO + uint8 n = (addr >> 16) & 15; + return regs.r[n]; + } + + if((addr & 0xf8f000) == 0x105000) { //$[10-17]:[5000-5fff] SRAM + return memory::bsxram.read(((addr >> 16) & 7) * 0x1000 + (addr & 0xfff)); + } + + return 0x00; +} + +void BSXCart::mmio_write(unsigned addr, uint8 data) { + if((addr & 0xf0ffff) == 0x005000) { //$[00-0f]:5000 MMIO + uint8 n = (addr >> 16) & 15; + regs.r[n] = data; + if(n == 0x0e && data & 0x80) update_memory_map(); + return; + } + + if((addr & 0xf8f000) == 0x105000) { //$[10-17]:[5000-5fff] SRAM + return memory::bsxram.write(((addr >> 16) & 7) * 0x1000 + (addr & 0xfff), data); + } +} + +BSXCart::BSXCart() { +} + +BSXCart::~BSXCart() { +} + +#endif + diff --git a/mednafen/snes/src/chip/bsx/bsx_flash.cpp b/mednafen/snes/src/chip/bsx/bsx_flash.cpp new file mode 100755 index 0000000..dfd7ead --- /dev/null +++ b/mednafen/snes/src/chip/bsx/bsx_flash.cpp @@ -0,0 +1,119 @@ +#ifdef BSX_CPP + +BSXFlash bsxflash; + +void BSXFlash::init() {} +void BSXFlash::enable() {} + +void BSXFlash::power() { + reset(); +} + +void BSXFlash::reset() { + regs.command = 0; + regs.write_old = 0x00; + regs.write_new = 0x00; + + regs.flash_enable = false; + regs.read_enable = false; + regs.write_enable = false; + memory::bsxflash.write_protect(!regs.write_enable); +} + +unsigned BSXFlash::size() const { + return memory::bsxflash.size(); +} + +uint8 BSXFlash::read(unsigned addr) { + if(addr == 0x0002) { + if(regs.flash_enable) return 0x80; + } + + if(addr == 0x5555) { + if(regs.flash_enable) return 0x80; + } + + if(regs.read_enable && addr >= 0xff00 && addr <= 0xff13) { + //read flash cartridge vendor information + switch(addr - 0xff00) { + case 0x00: return 0x4d; + case 0x01: return 0x00; + case 0x02: return 0x50; + case 0x03: return 0x00; + case 0x04: return 0x00; + case 0x05: return 0x00; + case 0x06: return 0x2a; //0x2a = 8mbit, 0x2b = 16mbit (not known to exist, though BIOS recognizes ID) + case 0x07: return 0x00; + default: return 0x00; + } + } + + return memory::bsxflash.read(addr); +} + +void BSXFlash::write(unsigned addr, uint8 data) { + //there exist both read-only and read-write BS-X flash cartridges ... + //unfortunately, the vendor info is not stored inside memory dumps + //of BS-X flashcarts, so it is impossible to determine whether a + //given flashcart is writeable. + //however, it has been observed that LoROM-mapped BS-X carts always + //use read-write flashcarts, and HiROM-mapped BS-X carts always use + //read-only flashcarts. + //below is an unfortunately necessary workaround to this problem. + if(cartridge.mapper() == Cartridge::BSCHiROM) return; + + if((addr & 0xff0000) == 0) { + regs.write_old = regs.write_new; + regs.write_new = data; + + if(regs.write_enable && regs.write_old == regs.write_new) { + return memory::bsxflash.write(addr, data); + } + } else { + if(regs.write_enable) { + return memory::bsxflash.write(addr, data); + } + } + + if(addr == 0x0000) { + regs.command <<= 8; + regs.command |= data; + + if((regs.command & 0xffff) == 0x38d0) { + regs.flash_enable = true; + regs.read_enable = true; + } + } + + if(addr == 0x2aaa) { + regs.command <<= 8; + regs.command |= data; + } + + if(addr == 0x5555) { + regs.command <<= 8; + regs.command |= data; + + if((regs.command & 0xffffff) == 0xaa5570) { + regs.write_enable = false; + } + + if((regs.command & 0xffffff) == 0xaa55a0) { + regs.write_old = 0x00; + regs.write_new = 0x00; + regs.flash_enable = true; + regs.write_enable = true; + } + + if((regs.command & 0xffffff) == 0xaa55f0) { + regs.flash_enable = false; + regs.read_enable = false; + regs.write_enable = false; + } + + memory::bsxflash.write_protect(!regs.write_enable); + } +} + +#endif + diff --git a/mednafen/snes/src/chip/chip.hpp b/mednafen/snes/src/chip/chip.hpp new file mode 100755 index 0000000..2d8f573 --- /dev/null +++ b/mednafen/snes/src/chip/chip.hpp @@ -0,0 +1,17 @@ +#include "supergameboy/supergameboy.hpp" +#include "superfx/superfx.hpp" +#include "sa1/sa1.hpp" +#include "bsx/bsx.hpp" +#include "srtc/srtc.hpp" +#include "sdd1/sdd1.hpp" +#include "spc7110/spc7110.hpp" +#include "cx4/cx4.hpp" +#include "dsp1/dsp1.hpp" +#include "dsp2/dsp2.hpp" +#include "dsp3/dsp3.hpp" +#include "dsp4/dsp4.hpp" +#include "obc1/obc1.hpp" +#include "st010/st010.hpp" +#include "st011/st011.hpp" +#include "st018/st018.hpp" +#include "21fx/21fx.hpp" diff --git a/mednafen/snes/src/chip/cx4/cx4.cpp b/mednafen/snes/src/chip/cx4/cx4.cpp new file mode 100755 index 0000000..e26b188 --- /dev/null +++ b/mednafen/snes/src/chip/cx4/cx4.cpp @@ -0,0 +1,210 @@ +//============= +//Cx4 emulation +//============= +//Used in Rockman X2/X3 (Megaman X2/X3) +//Portions (c) anomie, Overload, zsKnight, Nach, byuu + +#include <../base.hpp> + +#define CX4_CPP +namespace SNES { + +Cx4 cx4; + +#include "serialization.cpp" +#include "data.cpp" +#include "functions.cpp" +#include "oam.cpp" +#include "opcodes.cpp" + +void Cx4::init() { +} + +void Cx4::enable() { + bus.map(Bus::MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, *this); + bus.map(Bus::MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, *this); +} + +uint32 Cx4::ldr(uint8 r) { + uint16 addr = 0x0080 + (r * 3); + return (reg[addr + 0] << 0) + | (reg[addr + 1] << 8) + | (reg[addr + 2] << 16); +} + +void Cx4::str(uint8 r, uint32 data) { + uint16 addr = 0x0080 + (r * 3); + reg[addr + 0] = (data >> 0); + reg[addr + 1] = (data >> 8); + reg[addr + 2] = (data >> 16); +} + +void Cx4::mul(uint32 x, uint32 y, uint32 &rl, uint32 &rh) { + int64 rx = x & 0xffffff; + int64 ry = y & 0xffffff; + if(rx & 0x800000)rx |= ~0x7fffff; + if(ry & 0x800000)ry |= ~0x7fffff; + + rx *= ry; + + rl = (rx) & 0xffffff; + rh = (rx >> 24) & 0xffffff; +} + +uint32 Cx4::sin(uint32 rx) { + r0 = rx & 0x1ff; + if(r0 & 0x100)r0 ^= 0x1ff; + if(r0 & 0x080)r0 ^= 0x0ff; + if(rx & 0x100) { + return sin_table[r0 + 0x80]; + } else { + return sin_table[r0]; + } +} + +uint32 Cx4::cos(uint32 rx) { + return sin(rx + 0x080); +} + +void Cx4::immediate_reg(uint32 start) { + r0 = ldr(0); + for(uint32 i = start; i < 48; i++) { + if((r0 & 0x0fff) < 0x0c00) { + ram[r0 & 0x0fff] = immediate_data[i]; + } + r0++; + } + str(0, r0); +} + +void Cx4::transfer_data() { + uint32 src; + uint16 dest, count; + + src = (reg[0x40]) | (reg[0x41] << 8) | (reg[0x42] << 16); + count = (reg[0x43]) | (reg[0x44] << 8); + dest = (reg[0x45]) | (reg[0x46] << 8); + + for(uint32 i=0;i> 2; + return; + } + + switch(data) { + case 0x00: op00(); break; + case 0x01: op01(); break; + case 0x05: op05(); break; + case 0x0d: op0d(); break; + case 0x10: op10(); break; + case 0x13: op13(); break; + case 0x15: op15(); break; + case 0x1f: op1f(); break; + case 0x22: op22(); break; + case 0x25: op25(); break; + case 0x2d: op2d(); break; + case 0x40: op40(); break; + case 0x54: op54(); break; + case 0x5c: op5c(); break; + case 0x5e: op5e(); break; + case 0x60: op60(); break; + case 0x62: op62(); break; + case 0x64: op64(); break; + case 0x66: op66(); break; + case 0x68: op68(); break; + case 0x6a: op6a(); break; + case 0x6c: op6c(); break; + case 0x6e: op6e(); break; + case 0x70: op70(); break; + case 0x72: op72(); break; + case 0x74: op74(); break; + case 0x76: op76(); break; + case 0x78: op78(); break; + case 0x7a: op7a(); break; + case 0x7c: op7c(); break; + case 0x89: op89(); break; + } + } +} + +void Cx4::writeb(uint16 addr, uint8 data) { + write(addr, data); +} + +void Cx4::writew(uint16 addr, uint16 data) { + write(addr + 0, data >> 0); + write(addr + 1, data >> 8); +} + +void Cx4::writel(uint16 addr, uint32 data) { + write(addr + 0, data >> 0); + write(addr + 1, data >> 8); + write(addr + 2, data >> 16); +} + +uint8 Cx4::read(unsigned addr) { + addr &= 0x1fff; + + if(addr < 0x0c00) { + return ram[addr]; + } + + if(addr >= 0x1f00) { + return reg[addr & 0xff]; + } + + return cpu.regs.mdr; +} + +uint8 Cx4::readb(uint16 addr) { + return read(addr); +} + +uint16 Cx4::readw(uint16 addr) { + return read(addr) | (read(addr + 1) << 8); +} + +uint32 Cx4::readl(uint16 addr) { + return read(addr) | (read(addr + 1) << 8) + (read(addr + 2) << 16); +} + +void Cx4::power() { + reset(); +} + +void Cx4::reset() { + memset(ram, 0, 0x0c00); + memset(reg, 0, 0x0100); +} + +}; diff --git a/mednafen/snes/src/chip/cx4/cx4.hpp b/mednafen/snes/src/chip/cx4/cx4.hpp new file mode 100755 index 0000000..f971f44 --- /dev/null +++ b/mednafen/snes/src/chip/cx4/cx4.hpp @@ -0,0 +1,95 @@ +class Cx4 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + +private: + uint8 ram[0x0c00]; + uint8 reg[0x0100]; + uint32 r0, r1, r2, r3, r4, r5, r6, r7, + r8, r9, r10, r11, r12, r13, r14, r15; + + static const uint8 immediate_data[48]; + static const uint16 wave_data[40]; + static const uint32 sin_table[256]; + + static const int16 SinTable[512]; + static const int16 CosTable[512]; + + int16 C4WFXVal, C4WFYVal, C4WFZVal, C4WFX2Val, C4WFY2Val, C4WFDist, C4WFScale; + int16 C41FXVal, C41FYVal, C41FAngleRes, C41FDist, C41FDistVal; + + void C4TransfWireFrame(); + void C4TransfWireFrame2(); + void C4CalcWireFrame(); + void C4DrawLine(int32 X1, int32 Y1, int16 Z1, int32 X2, int32 Y2, int16 Z2, uint8 Color); + void C4DrawWireFrame(); + void C4DoScaleRotate(int row_padding); + +public: + uint32 ldr(uint8 r); + void str(uint8 r, uint32 data); + void mul(uint32 x, uint32 y, uint32 &rl, uint32 &rh); + uint32 sin(uint32 rx); + uint32 cos(uint32 rx); + + void transfer_data(); + void immediate_reg(uint32 num); + + void op00_00(); + void op00_03(); + void op00_05(); + void op00_07(); + void op00_08(); + void op00_0b(); + void op00_0c(); + + void op00(); + void op01(); + void op05(); + void op0d(); + void op10(); + void op13(); + void op15(); + void op1f(); + void op22(); + void op25(); + void op2d(); + void op40(); + void op54(); + void op5c(); + void op5e(); + void op60(); + void op62(); + void op64(); + void op66(); + void op68(); + void op6a(); + void op6c(); + void op6e(); + void op70(); + void op72(); + void op74(); + void op76(); + void op78(); + void op7a(); + void op7c(); + void op89(); + + uint8 readb(uint16 addr); + uint16 readw(uint16 addr); + uint32 readl(uint16 addr); + + void writeb(uint16 addr, uint8 data); + void writew(uint16 addr, uint16 data); + void writel(uint16 addr, uint32 data); +}; + +extern Cx4 cx4; diff --git a/mednafen/snes/src/chip/cx4/data.cpp b/mednafen/snes/src/chip/cx4/data.cpp new file mode 100755 index 0000000..8538f60 --- /dev/null +++ b/mednafen/snes/src/chip/cx4/data.cpp @@ -0,0 +1,187 @@ +#ifdef CX4_CPP + +const uint8 Cx4::immediate_data[48] = { + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0xff, 0x7f, + 0x00, 0x80, 0x00, 0xff, 0x7f, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0xff, + 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x00, 0x01, 0x00, 0xff, 0xfe, 0x00 +}; + +const uint16 Cx4::wave_data[40] = { + 0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e, + 0x0200, 0x0202, 0x0204, 0x0206, 0x0208, 0x020a, 0x020c, 0x020e, + 0x0400, 0x0402, 0x0404, 0x0406, 0x0408, 0x040a, 0x040c, 0x040e, + 0x0600, 0x0602, 0x0604, 0x0606, 0x0608, 0x060a, 0x060c, 0x060e, + 0x0800, 0x0802, 0x0804, 0x0806, 0x0808, 0x080a, 0x080c, 0x080e +}; + +const uint32 Cx4::sin_table[256] = { + 0x000000, 0x000324, 0x000648, 0x00096c, 0x000c8f, 0x000fb2, 0x0012d5, 0x0015f6, + 0x001917, 0x001c37, 0x001f56, 0x002273, 0x002590, 0x0028aa, 0x002bc4, 0x002edb, + 0x0031f1, 0x003505, 0x003817, 0x003b26, 0x003e33, 0x00413e, 0x004447, 0x00474d, + 0x004a50, 0x004d50, 0x00504d, 0x005347, 0x00563e, 0x005931, 0x005c22, 0x005f0e, + 0x0061f7, 0x0064dc, 0x0067bd, 0x006a9b, 0x006d74, 0x007049, 0x007319, 0x0075e5, + 0x0078ad, 0x007b70, 0x007e2e, 0x0080e7, 0x00839c, 0x00864b, 0x0088f5, 0x008b9a, + 0x008e39, 0x0090d3, 0x009368, 0x0095f6, 0x00987f, 0x009b02, 0x009d7f, 0x009ff6, + 0x00a267, 0x00a4d2, 0x00a736, 0x00a994, 0x00abeb, 0x00ae3b, 0x00b085, 0x00b2c8, + 0x00b504, 0x00b73a, 0x00b968, 0x00bb8f, 0x00bdae, 0x00bfc7, 0x00c1d8, 0x00c3e2, + 0x00c5e4, 0x00c7de, 0x00c9d1, 0x00cbbb, 0x00cd9f, 0x00cf7a, 0x00d14d, 0x00d318, + 0x00d4db, 0x00d695, 0x00d848, 0x00d9f2, 0x00db94, 0x00dd2d, 0x00debe, 0x00e046, + 0x00e1c5, 0x00e33c, 0x00e4aa, 0x00e60f, 0x00e76b, 0x00e8bf, 0x00ea09, 0x00eb4b, + 0x00ec83, 0x00edb2, 0x00eed8, 0x00eff5, 0x00f109, 0x00f213, 0x00f314, 0x00f40b, + 0x00f4fa, 0x00f5de, 0x00f6ba, 0x00f78b, 0x00f853, 0x00f912, 0x00f9c7, 0x00fa73, + 0x00fb14, 0x00fbac, 0x00fc3b, 0x00fcbf, 0x00fd3a, 0x00fdab, 0x00fe13, 0x00fe70, + 0x00fec4, 0x00ff0e, 0x00ff4e, 0x00ff84, 0x00ffb1, 0x00ffd3, 0x00ffec, 0x00fffb, + 0x000000, 0xfffcdb, 0xfff9b7, 0xfff693, 0xfff370, 0xfff04d, 0xffed2a, 0xffea09, + 0xffe6e8, 0xffe3c8, 0xffe0a9, 0xffdd8c, 0xffda6f, 0xffd755, 0xffd43b, 0xffd124, + 0xffce0e, 0xffcafa, 0xffc7e8, 0xffc4d9, 0xffc1cc, 0xffbec1, 0xffbbb8, 0xffb8b2, + 0xffb5af, 0xffb2af, 0xffafb2, 0xffacb8, 0xffa9c1, 0xffa6ce, 0xffa3dd, 0xffa0f1, + 0xff9e08, 0xff9b23, 0xff9842, 0xff9564, 0xff928b, 0xff8fb6, 0xff8ce6, 0xff8a1a, + 0xff8752, 0xff848f, 0xff81d1, 0xff7f18, 0xff7c63, 0xff79b4, 0xff770a, 0xff7465, + 0xff71c6, 0xff6f2c, 0xff6c97, 0xff6a09, 0xff6780, 0xff64fd, 0xff6280, 0xff6009, + 0xff5d98, 0xff5b2d, 0xff58c9, 0xff566b, 0xff5414, 0xff51c4, 0xff4f7a, 0xff4d37, + 0xff4afb, 0xff48c5, 0xff4697, 0xff4470, 0xff4251, 0xff4038, 0xff3e27, 0xff3c1e, + 0xff3a1b, 0xff3821, 0xff362e, 0xff3444, 0xff3260, 0xff3085, 0xff2eb2, 0xff2ce7, + 0xff2b24, 0xff296a, 0xff27b7, 0xff260d, 0xff246b, 0xff22d2, 0xff2141, 0xff1fb9, + 0xff1e3a, 0xff1cc3, 0xff1b55, 0xff19f0, 0xff1894, 0xff1740, 0xff15f6, 0xff14b4, + 0xff137c, 0xff124d, 0xff1127, 0xff100a, 0xff0ef6, 0xff0dec, 0xff0ceb, 0xff0bf4, + 0xff0b05, 0xff0a21, 0xff0945, 0xff0874, 0xff07ac, 0xff06ed, 0xff0638, 0xff058d, + 0xff04eb, 0xff0453, 0xff03c4, 0xff0340, 0xff02c5, 0xff0254, 0xff01ec, 0xff018f, + 0xff013b, 0xff00f1, 0xff00b1, 0xff007b, 0xff004e, 0xff002c, 0xff0013, 0xff0004 +}; + +const int16 Cx4::SinTable[512] = { + 0, 402, 804, 1206, 1607, 2009, 2410, 2811, + 3211, 3611, 4011, 4409, 4808, 5205, 5602, 5997, + 6392, 6786, 7179, 7571, 7961, 8351, 8739, 9126, + 9512, 9896, 10278, 10659, 11039, 11416, 11793, 12167, + 12539, 12910, 13278, 13645, 14010, 14372, 14732, 15090, + 15446, 15800, 16151, 16499, 16846, 17189, 17530, 17869, + 18204, 18537, 18868, 19195, 19519, 19841, 20159, 20475, + 20787, 21097, 21403, 21706, 22005, 22301, 22594, 22884, + 23170, 23453, 23732, 24007, 24279, 24547, 24812, 25073, + 25330, 25583, 25832, 26077, 26319, 26557, 26790, 27020, + 27245, 27466, 27684, 27897, 28106, 28310, 28511, 28707, + 28898, 29086, 29269, 29447, 29621, 29791, 29956, 30117, + 30273, 30425, 30572, 30714, 30852, 30985, 31114, 31237, + 31357, 31471, 31581, 31685, 31785, 31881, 31971, 32057, + 32138, 32214, 32285, 32351, 32413, 32469, 32521, 32568, + 32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765, + 32767, 32765, 32758, 32745, 32728, 32706, 32679, 32647, + 32610, 32568, 32521, 32469, 32413, 32351, 32285, 32214, + 32138, 32057, 31971, 31881, 31785, 31685, 31581, 31471, + 31357, 31237, 31114, 30985, 30852, 30714, 30572, 30425, + 30273, 30117, 29956, 29791, 29621, 29447, 29269, 29086, + 28898, 28707, 28511, 28310, 28106, 27897, 27684, 27466, + 27245, 27020, 26790, 26557, 26319, 26077, 25832, 25583, + 25330, 25073, 24812, 24547, 24279, 24007, 23732, 23453, + 23170, 22884, 22594, 22301, 22005, 21706, 21403, 21097, + 20787, 20475, 20159, 19841, 19519, 19195, 18868, 18537, + 18204, 17869, 17530, 17189, 16846, 16499, 16151, 15800, + 15446, 15090, 14732, 14372, 14010, 13645, 13278, 12910, + 12539, 12167, 11793, 11416, 11039, 10659, 10278, 9896, + 9512, 9126, 8739, 8351, 7961, 7571, 7179, 6786, + 6392, 5997, 5602, 5205, 4808, 4409, 4011, 3611, + 3211, 2811, 2410, 2009, 1607, 1206, 804, 402, + 0, -402, -804, -1206, -1607, -2009, -2410, -2811, + -3211, -3611, -4011, -4409, -4808, -5205, -5602, -5997, + -6392, -6786, -7179, -7571, -7961, -8351, -8739, -9126, + -9512, -9896, -10278, -10659, -11039, -11416, -11793, -12167, + -12539, -12910, -13278, -13645, -14010, -14372, -14732, -15090, + -15446, -15800, -16151, -16499, -16846, -17189, -17530, -17869, + -18204, -18537, -18868, -19195, -19519, -19841, -20159, -20475, + -20787, -21097, -21403, -21706, -22005, -22301, -22594, -22884, + -23170, -23453, -23732, -24007, -24279, -24547, -24812, -25073, + -25330, -25583, -25832, -26077, -26319, -26557, -26790, -27020, + -27245, -27466, -27684, -27897, -28106, -28310, -28511, -28707, + -28898, -29086, -29269, -29447, -29621, -29791, -29956, -30117, + -30273, -30425, -30572, -30714, -30852, -30985, -31114, -31237, + -31357, -31471, -31581, -31685, -31785, -31881, -31971, -32057, + -32138, -32214, -32285, -32351, -32413, -32469, -32521, -32568, + -32610, -32647, -32679, -32706, -32728, -32745, -32758, -32765, + -32767, -32765, -32758, -32745, -32728, -32706, -32679, -32647, + -32610, -32568, -32521, -32469, -32413, -32351, -32285, -32214, + -32138, -32057, -31971, -31881, -31785, -31685, -31581, -31471, + -31357, -31237, -31114, -30985, -30852, -30714, -30572, -30425, + -30273, -30117, -29956, -29791, -29621, -29447, -29269, -29086, + -28898, -28707, -28511, -28310, -28106, -27897, -27684, -27466, + -27245, -27020, -26790, -26557, -26319, -26077, -25832, -25583, + -25330, -25073, -24812, -24547, -24279, -24007, -23732, -23453, + -23170, -22884, -22594, -22301, -22005, -21706, -21403, -21097, + -20787, -20475, -20159, -19841, -19519, -19195, -18868, -18537, + -18204, -17869, -17530, -17189, -16846, -16499, -16151, -15800, + -15446, -15090, -14732, -14372, -14010, -13645, -13278, -12910, + -12539, -12167, -11793, -11416, -11039, -10659, -10278, -9896, + -9512, -9126, -8739, -8351, -7961, -7571, -7179, -6786, + -6392, -5997, -5602, -5205, -4808, -4409, -4011, -3611, + -3211, -2811, -2410, -2009, -1607, -1206, -804, -402 +}; + +const int16 Cx4::CosTable[512] = { + 32767, 32765, 32758, 32745, 32728, 32706, 32679, 32647, + 32610, 32568, 32521, 32469, 32413, 32351, 32285, 32214, + 32138, 32057, 31971, 31881, 31785, 31685, 31581, 31471, + 31357, 31237, 31114, 30985, 30852, 30714, 30572, 30425, + 30273, 30117, 29956, 29791, 29621, 29447, 29269, 29086, + 28898, 28707, 28511, 28310, 28106, 27897, 27684, 27466, + 27245, 27020, 26790, 26557, 26319, 26077, 25832, 25583, + 25330, 25073, 24812, 24547, 24279, 24007, 23732, 23453, + 23170, 22884, 22594, 22301, 22005, 21706, 21403, 21097, + 20787, 20475, 20159, 19841, 19519, 19195, 18868, 18537, + 18204, 17869, 17530, 17189, 16846, 16499, 16151, 15800, + 15446, 15090, 14732, 14372, 14010, 13645, 13278, 12910, + 12539, 12167, 11793, 11416, 11039, 10659, 10278, 9896, + 9512, 9126, 8739, 8351, 7961, 7571, 7179, 6786, + 6392, 5997, 5602, 5205, 4808, 4409, 4011, 3611, + 3211, 2811, 2410, 2009, 1607, 1206, 804, 402, + 0, -402, -804, -1206, -1607, -2009, -2410, -2811, + -3211, -3611, -4011, -4409, -4808, -5205, -5602, -5997, + -6392, -6786, -7179, -7571, -7961, -8351, -8739, -9126, + -9512, -9896, -10278, -10659, -11039, -11416, -11793, -12167, + -12539, -12910, -13278, -13645, -14010, -14372, -14732, -15090, + -15446, -15800, -16151, -16499, -16846, -17189, -17530, -17869, + -18204, -18537, -18868, -19195, -19519, -19841, -20159, -20475, + -20787, -21097, -21403, -21706, -22005, -22301, -22594, -22884, + -23170, -23453, -23732, -24007, -24279, -24547, -24812, -25073, + -25330, -25583, -25832, -26077, -26319, -26557, -26790, -27020, + -27245, -27466, -27684, -27897, -28106, -28310, -28511, -28707, + -28898, -29086, -29269, -29447, -29621, -29791, -29956, -30117, + -30273, -30425, -30572, -30714, -30852, -30985, -31114, -31237, + -31357, -31471, -31581, -31685, -31785, -31881, -31971, -32057, + -32138, -32214, -32285, -32351, -32413, -32469, -32521, -32568, + -32610, -32647, -32679, -32706, -32728, -32745, -32758, -32765, + -32767, -32765, -32758, -32745, -32728, -32706, -32679, -32647, + -32610, -32568, -32521, -32469, -32413, -32351, -32285, -32214, + -32138, -32057, -31971, -31881, -31785, -31685, -31581, -31471, + -31357, -31237, -31114, -30985, -30852, -30714, -30572, -30425, + -30273, -30117, -29956, -29791, -29621, -29447, -29269, -29086, + -28898, -28707, -28511, -28310, -28106, -27897, -27684, -27466, + -27245, -27020, -26790, -26557, -26319, -26077, -25832, -25583, + -25330, -25073, -24812, -24547, -24279, -24007, -23732, -23453, + -23170, -22884, -22594, -22301, -22005, -21706, -21403, -21097, + -20787, -20475, -20159, -19841, -19519, -19195, -18868, -18537, + -18204, -17869, -17530, -17189, -16846, -16499, -16151, -15800, + -15446, -15090, -14732, -14372, -14010, -13645, -13278, -12910, + -12539, -12167, -11793, -11416, -11039, -10659, -10278, -9896, + -9512, -9126, -8739, -8351, -7961, -7571, -7179, -6786, + -6392, -5997, -5602, -5205, -4808, -4409, -4011, -3611, + -3211, -2811, -2410, -2009, -1607, -1206, -804, -402, + 0, 402, 804, 1206, 1607, 2009, 2410, 2811, + 3211, 3611, 4011, 4409, 4808, 5205, 5602, 5997, + 6392, 6786, 7179, 7571, 7961, 8351, 8739, 9126, + 9512, 9896, 10278, 10659, 11039, 11416, 11793, 12167, + 12539, 12910, 13278, 13645, 14010, 14372, 14732, 15090, + 15446, 15800, 16151, 16499, 16846, 17189, 17530, 17869, + 18204, 18537, 18868, 19195, 19519, 19841, 20159, 20475, + 20787, 21097, 21403, 21706, 22005, 22301, 22594, 22884, + 23170, 23453, 23732, 24007, 24279, 24547, 24812, 25073, + 25330, 25583, 25832, 26077, 26319, 26557, 26790, 27020, + 27245, 27466, 27684, 27897, 28106, 28310, 28511, 28707, + 28898, 29086, 29269, 29447, 29621, 29791, 29956, 30117, + 30273, 30425, 30572, 30714, 30852, 30985, 31114, 31237, + 31357, 31471, 31581, 31685, 31785, 31881, 31971, 32057, + 32138, 32214, 32285, 32351, 32413, 32469, 32521, 32568, + 32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765 +}; + +#endif diff --git a/mednafen/snes/src/chip/cx4/functions.cpp b/mednafen/snes/src/chip/cx4/functions.cpp new file mode 100755 index 0000000..7466ffb --- /dev/null +++ b/mednafen/snes/src/chip/cx4/functions.cpp @@ -0,0 +1,251 @@ +#ifdef CX4_CPP + +#include +#define Tan(a) (CosTable[a] ? ((((int32)SinTable[a]) << 16) / CosTable[a]) : 0x80000000) +#define sar(b, n) ((b) >> (n)) +#ifdef PI +#undef PI +#endif +#define PI 3.1415926535897932384626433832795 + +//Wireframe Helpers +void Cx4::C4TransfWireFrame() { + double c4x = (double)C4WFXVal; + double c4y = (double)C4WFYVal; + double c4z = (double)C4WFZVal - 0x95; + double tanval, c4x2, c4y2, c4z2; + + //Rotate X + tanval = -(double)C4WFX2Val * PI * 2 / 128; + c4y2 = c4y * ::cos(tanval) - c4z * ::sin(tanval); + c4z2 = c4y * ::sin(tanval) + c4z * ::cos(tanval); + + //Rotate Y + tanval = -(double)C4WFY2Val * PI * 2 / 128; + c4x2 = c4x * ::cos(tanval) + c4z2 * ::sin(tanval); + c4z = c4x * -::sin(tanval) + c4z2 * ::cos(tanval); + + //Rotate Z + tanval = -(double)C4WFDist * PI * 2 / 128; + c4x = c4x2 * ::cos(tanval) - c4y2 * ::sin(tanval); + c4y = c4x2 * ::sin(tanval) + c4y2 * ::cos(tanval); + + //Scale + C4WFXVal = (int16)(c4x * C4WFScale / (0x90 * (c4z + 0x95)) * 0x95); + C4WFYVal = (int16)(c4y * C4WFScale / (0x90 * (c4z + 0x95)) * 0x95); +} + +void Cx4::C4CalcWireFrame() { + C4WFXVal = C4WFX2Val - C4WFXVal; + C4WFYVal = C4WFY2Val - C4WFYVal; + + if(abs(C4WFXVal) > abs(C4WFYVal)) { + C4WFDist = abs(C4WFXVal) + 1; + C4WFYVal = (256 * (long)C4WFYVal) / abs(C4WFXVal); + C4WFXVal = (C4WFXVal < 0) ? -256 : 256; + } else if(C4WFYVal != 0) { + C4WFDist = abs(C4WFYVal) + 1; + C4WFXVal = (256 * (long)C4WFXVal) / abs(C4WFYVal); + C4WFYVal = (C4WFYVal < 0) ? -256 : 256; + } else { + C4WFDist = 0; + } +} + +void Cx4::C4TransfWireFrame2() { + double c4x = (double)C4WFXVal; + double c4y = (double)C4WFYVal; + double c4z = (double)C4WFZVal; + double tanval, c4x2, c4y2, c4z2; + + //Rotate X + tanval = -(double)C4WFX2Val * PI * 2 / 128; + c4y2 = c4y * ::cos(tanval) - c4z * ::sin(tanval); + c4z2 = c4y * ::sin(tanval) + c4z * ::cos(tanval); + + //Rotate Y + tanval = -(double)C4WFY2Val * PI * 2 / 128; + c4x2 = c4x * ::cos(tanval) + c4z2 * ::sin(tanval); + c4z = c4x * -::sin(tanval) + c4z2 * ::cos(tanval); + + //Rotate Z + tanval = -(double)C4WFDist * PI * 2 / 128; + c4x = c4x2 * ::cos(tanval) - c4y2 * ::sin(tanval); + c4y = c4x2 * ::sin(tanval) + c4y2 * ::cos(tanval); + + //Scale + C4WFXVal = (int16)(c4x * C4WFScale / 0x100); + C4WFYVal = (int16)(c4y * C4WFScale / 0x100); +} + +void Cx4::C4DrawWireFrame() { + uint32 line = readl(0x1f80); + uint32 point1, point2; + int16 X1, Y1, Z1; + int16 X2, Y2, Z2; + uint8 Color; + + for(int32 i = ram[0x0295]; i > 0; i--, line += 5) { + if(bus.read(line) == 0xff && bus.read(line + 1) == 0xff) { + int32 tmp = line - 5; + while(bus.read(tmp + 2) == 0xff && bus.read(tmp + 3) == 0xff && (tmp + 2) >= 0) { tmp -= 5; } + point1 = (read(0x1f82) << 16) | (bus.read(tmp + 2) << 8) | bus.read(tmp + 3); + } else { + point1 = (read(0x1f82) << 16) | (bus.read(line) << 8) | bus.read(line + 1); + } + point2 = (read(0x1f82) << 16) | (bus.read(line + 2) << 8) | bus.read(line + 3); + + X1=(bus.read(point1 + 0) << 8) | bus.read(point1 + 1); + Y1=(bus.read(point1 + 2) << 8) | bus.read(point1 + 3); + Z1=(bus.read(point1 + 4) << 8) | bus.read(point1 + 5); + X2=(bus.read(point2 + 0) << 8) | bus.read(point2 + 1); + Y2=(bus.read(point2 + 2) << 8) | bus.read(point2 + 3); + Z2=(bus.read(point2 + 4) << 8) | bus.read(point2 + 5); + Color = bus.read(line + 4); + C4DrawLine(X1, Y1, Z1, X2, Y2, Z2, Color); + } +} + +void Cx4::C4DrawLine(int32 X1, int32 Y1, int16 Z1, int32 X2, int32 Y2, int16 Z2, uint8 Color) { + //Transform coordinates + C4WFXVal = (int16)X1; + C4WFYVal = (int16)Y1; + C4WFZVal = Z1; + C4WFScale = read(0x1f90); + C4WFX2Val = read(0x1f86); + C4WFY2Val = read(0x1f87); + C4WFDist = read(0x1f88); + C4TransfWireFrame2(); + X1 = (C4WFXVal + 48) << 8; + Y1 = (C4WFYVal + 48) << 8; + + C4WFXVal = (int16)X2; + C4WFYVal = (int16)Y2; + C4WFZVal = Z2; + C4TransfWireFrame2(); + X2 = (C4WFXVal + 48) << 8; + Y2 = (C4WFYVal + 48) << 8; + + //Get line info + C4WFXVal = (int16)(X1 >> 8); + C4WFYVal = (int16)(Y1 >> 8); + C4WFX2Val = (int16)(X2 >> 8); + C4WFY2Val = (int16)(Y2 >> 8); + C4CalcWireFrame(); + X2 = (int16)C4WFXVal; + Y2 = (int16)C4WFYVal; + + //Render line + for(int32 i = C4WFDist ? C4WFDist : 1; i > 0; i--) { + if(X1 > 0xff && Y1 > 0xff && X1 < 0x6000 && Y1 < 0x6000) { + uint16 addr = (((Y1 >> 8) >> 3) << 8) - (((Y1 >> 8) >> 3) << 6) + (((X1 >> 8) >> 3) << 4) + ((Y1 >> 8) & 7) * 2; + uint8 bit = 0x80 >> ((X1 >> 8) & 7); + ram[addr + 0x300] &= ~bit; + ram[addr + 0x301] &= ~bit; + if(Color & 1) ram[addr + 0x300] |= bit; + if(Color & 2) ram[addr + 0x301] |= bit; + } + X1 += X2; + Y1 += Y2; + } +} + +void Cx4::C4DoScaleRotate(int row_padding) { + int16 A, B, C, D; + + //Calculate matrix + int32 XScale = readw(0x1f8f); + int32 YScale = readw(0x1f92); + + if(XScale & 0x8000)XScale = 0x7fff; + if(YScale & 0x8000)YScale = 0x7fff; + + if(readw(0x1f80) == 0) { //no rotation + A = (int16)XScale; + B = 0; + C = 0; + D = (int16)YScale; + } else if(readw(0x1f80) == 128) { //90 degree rotation + A = 0; + B = (int16)(-YScale); + C = (int16)XScale; + D = 0; + } else if(readw(0x1f80) == 256) { //180 degree rotation + A = (int16)(-XScale); + B = 0; + C = 0; + D = (int16)(-YScale); + } else if(readw(0x1f80) == 384) { //270 degree rotation + A = 0; + B = (int16)YScale; + C = (int16)(-XScale); + D = 0; + } else { + A = (int16) sar(CosTable[readw(0x1f80) & 0x1ff] * XScale, 15); + B = (int16)(-sar(SinTable[readw(0x1f80) & 0x1ff] * YScale, 15)); + C = (int16) sar(SinTable[readw(0x1f80) & 0x1ff] * XScale, 15); + D = (int16) sar(CosTable[readw(0x1f80) & 0x1ff] * YScale, 15); + } + + //Calculate Pixel Resolution + uint8 w = read(0x1f89) & ~7; + uint8 h = read(0x1f8c) & ~7; + + //Clear the output RAM + memset(ram, 0, (w + row_padding / 4) * h / 2); + + int32 Cx = (int16)readw(0x1f83); + int32 Cy = (int16)readw(0x1f86); + + //Calculate start position (i.e. (Ox, Oy) = (0, 0)) + //The low 12 bits are fractional, so (Cx<<12) gives us the Cx we want in + //the function. We do Cx*A etc normally because the matrix parameters + //already have the fractional parts. + int32 LineX = (Cx << 12) - Cx * A - Cx * B; + int32 LineY = (Cy << 12) - Cy * C - Cy * D; + + //Start loop + uint32 X, Y; + uint8 byte; + int32 outidx = 0; + uint8 bit = 0x80; + + for(int32 y = 0; y < h; y++) { + X = LineX; + Y = LineY; + for(int32 x = 0; x < w; x++) { + if((X >> 12) >= w || (Y >> 12) >= h) { + byte = 0; + } else { + uint32 addr = (Y >> 12) * w + (X >> 12); + byte = read(0x600 + (addr >> 1)); + if(addr & 1) { byte >>= 4; } + } + + //De-bitplanify + if(byte & 1) ram[outidx ] |= bit; + if(byte & 2) ram[outidx + 1] |= bit; + if(byte & 4) ram[outidx + 16] |= bit; + if(byte & 8) ram[outidx + 17] |= bit; + + bit >>= 1; + if(!bit) { + bit = 0x80; + outidx += 32; + } + + X += A; //Add 1 to output x => add an A and a C + Y += C; + } + outidx += 2 + row_padding; + if(outidx & 0x10) { + outidx &= ~0x10; + } else { + outidx -= w * 4 + row_padding; + } + LineX += B; //Add 1 to output y => add a B and a D + LineY += D; + } +} + +#endif diff --git a/mednafen/snes/src/chip/cx4/oam.cpp b/mednafen/snes/src/chip/cx4/oam.cpp new file mode 100755 index 0000000..dcda69e --- /dev/null +++ b/mednafen/snes/src/chip/cx4/oam.cpp @@ -0,0 +1,228 @@ +#ifdef CX4_CPP + +//Build OAM +void Cx4::op00_00() { + uint32 oamptr = ram[0x626] << 2; + for(int32 i = 0x1fd; i > oamptr && i >= 0; i -= 4) { + //clear oam-to-be + if(i >= 0) ram[i] = 0xe0; + } + + uint16 globalx, globaly; + uint32 oamptr2; + int16 sprx, spry; + uint8 sprname, sprattr; + uint8 sprcount; + + globalx = readw(0x621); + globaly = readw(0x623); + oamptr2 = 0x200 + (ram[0x626] >> 2); + + if(!ram[0x620]) return; + + sprcount = 128 - ram[0x626]; + uint8 offset = (ram[0x626] & 3) * 2; + uint32 srcptr = 0x220; + + for(int i = ram[0x620]; i > 0 && sprcount > 0; i--, srcptr += 16) { + sprx = readw(srcptr) - globalx; + spry = readw(srcptr + 2) - globaly; + sprname = ram[srcptr + 5]; + sprattr = ram[srcptr + 4] | ram[srcptr + 6]; + + uint32 spraddr = readl(srcptr + 7); + if(bus.read(spraddr)) { + int16 x, y; + for(int sprcnt = bus.read(spraddr++); sprcnt > 0 && sprcount > 0; sprcnt--, spraddr += 4) { + x = (int8)bus.read(spraddr + 1); + if(sprattr & 0x40) { + x = -x - ((bus.read(spraddr) & 0x20) ? 16 : 8); + } + x += sprx; + if(x >= -16 && x <= 272) { + y = (int8)bus.read(spraddr + 2); + if(sprattr & 0x80) { + y = -y - ((bus.read(spraddr) & 0x20) ? 16 : 8); + } + y += spry; + if(y >= -16 && y <= 224) { + ram[oamptr ] = (uint8)x; + ram[oamptr + 1] = (uint8)y; + ram[oamptr + 2] = sprname + bus.read(spraddr + 3); + ram[oamptr + 3] = sprattr ^ (bus.read(spraddr) & 0xc0); + ram[oamptr2] &= ~(3 << offset); + if(x & 0x100) ram[oamptr2] |= 1 << offset; + if(bus.read(spraddr) & 0x20) ram[oamptr2] |= 2 << offset; + oamptr += 4; + sprcount--; + offset = (offset + 2) & 6; + if(!offset)oamptr2++; + } + } + } + } else if(sprcount > 0) { + ram[oamptr ] = (uint8)sprx; + ram[oamptr + 1] = (uint8)spry; + ram[oamptr + 2] = sprname; + ram[oamptr + 3] = sprattr; + ram[oamptr2] &= ~(3 << offset); + if(sprx & 0x100) ram[oamptr2] |= 3 << offset; + else ram[oamptr2] |= 2 << offset; + oamptr += 4; + sprcount--; + offset = (offset + 2) & 6; + if(!offset) oamptr2++; + } + } +} + +//Scale and Rotate +void Cx4::op00_03() { + C4DoScaleRotate(0); +} + +//Transform Lines +void Cx4::op00_05() { + C4WFX2Val = read(0x1f83); + C4WFY2Val = read(0x1f86); + C4WFDist = read(0x1f89); + C4WFScale = read(0x1f8c); + +//Transform Vertices +uint32 ptr = 0; + for(int32 i = readw(0x1f80); i > 0; i--, ptr += 0x10) { + C4WFXVal = readw(ptr + 1); + C4WFYVal = readw(ptr + 5); + C4WFZVal = readw(ptr + 9); + C4TransfWireFrame(); + + //Displace + writew(ptr + 1, C4WFXVal + 0x80); + writew(ptr + 5, C4WFYVal + 0x50); + } + + writew(0x600, 23); + writew(0x602, 0x60); + writew(0x605, 0x40); + writew(0x600 + 8, 23); + writew(0x602 + 8, 0x60); + writew(0x605 + 8, 0x40); + + ptr = 0xb02; + uint32 ptr2 = 0; + + for(int32 i = readw(0xb00); i > 0; i--, ptr += 2, ptr2 += 8) { + C4WFXVal = readw((read(ptr + 0) << 4) + 1); + C4WFYVal = readw((read(ptr + 0) << 4) + 5); + C4WFX2Val = readw((read(ptr + 1) << 4) + 1); + C4WFY2Val = readw((read(ptr + 1) << 4) + 5); + C4CalcWireFrame(); + writew(ptr2 + 0x600, C4WFDist ? C4WFDist : 1); + writew(ptr2 + 0x602, C4WFXVal); + writew(ptr2 + 0x605, C4WFYVal); + } +} + +//Scale and Rotate +void Cx4::op00_07() { + C4DoScaleRotate(64); +} + +//Draw Wireframe +void Cx4::op00_08() { + C4DrawWireFrame(); +} + +//Disintegrate +void Cx4::op00_0b() { + uint8 width, height; + uint32 startx, starty; + uint32 srcptr; + uint32 x, y; + int32 scalex, scaley; + int32 cx, cy; + int32 i, j; + + width = read(0x1f89); + height = read(0x1f8c); + cx = readw(0x1f80); + cy = readw(0x1f83); + + scalex = (int16)readw(0x1f86); + scaley = (int16)readw(0x1f8f); + startx = -cx * scalex + (cx << 8); + starty = -cy * scaley + (cy << 8); + srcptr = 0x600; + + for(i = 0; i < (width * height) >> 1; i++) { + write(i, 0); + } + + for(y = starty, i = 0;i < height; i++, y += scaley) { + for(x = startx, j = 0;j < width; j++, x += scalex) { + if((x >> 8) < width && (y >> 8) < height && (y >> 8) * width + (x >> 8) < 0x2000) { + uint8 pixel = (j & 1) ? (ram[srcptr] >> 4) : (ram[srcptr]); + int32 index = (y >> 11) * width * 4 + (x >> 11) * 32 + ((y >> 8) & 7) * 2; + uint8 mask = 0x80 >> ((x >> 8) & 7); + + if(pixel & 1) ram[index ] |= mask; + if(pixel & 2) ram[index + 1] |= mask; + if(pixel & 4) ram[index + 16] |= mask; + if(pixel & 8) ram[index + 17] |= mask; + } + if(j & 1) srcptr++; + } + } +} + +//Bitplane Wave +void Cx4::op00_0c() { + uint32 destptr = 0; + uint32 waveptr = read(0x1f83); + uint16 mask1 = 0xc0c0; + uint16 mask2 = 0x3f3f; + + for(int j = 0; j < 0x10; j++) { + do { + int16 height = -((int8)read(waveptr + 0xb00)) - 16; + for(int i = 0; i < 40; i++) { + uint16 temp = readw(destptr + wave_data[i]) & mask2; + if(height >= 0) { + if(height < 8) { + temp |= mask1 & readw(0xa00 + height * 2); + } else { + temp |= mask1 & 0xff00; + } + } + writew(destptr + wave_data[i], temp); + height++; + } + waveptr = (waveptr + 1) & 0x7f; + mask1 = (mask1 >> 2) | (mask1 << 6); + mask2 = (mask2 >> 2) | (mask2 << 6); + } while(mask1 != 0xc0c0); + destptr += 16; + + do { + int16 height = -((int8)read(waveptr + 0xb00)) - 16; + for(int i = 0; i < 40; i++) { + uint16 temp = readw(destptr + wave_data[i]) & mask2; + if(height >= 0) { + if(height < 8) { + temp |= mask1 & readw(0xa10 + height * 2); + } else { + temp |= mask1 & 0xff00; + } + } + writew(destptr + wave_data[i], temp); + height++; + } + waveptr = (waveptr + 1) & 0x7f; + mask1 = (mask1 >> 2) | (mask1 << 6); + mask2 = (mask2 >> 2) | (mask2 << 6); + } while(mask1 != 0xc0c0); + destptr += 16; + } +} + +#endif diff --git a/mednafen/snes/src/chip/cx4/opcodes.cpp b/mednafen/snes/src/chip/cx4/opcodes.cpp new file mode 100755 index 0000000..745f208 --- /dev/null +++ b/mednafen/snes/src/chip/cx4/opcodes.cpp @@ -0,0 +1,229 @@ +#ifdef CX4_CPP + +//Sprite Functions +void Cx4::op00() { + switch(reg[0x4d]) { + case 0x00: op00_00(); break; + case 0x03: op00_03(); break; + case 0x05: op00_05(); break; + case 0x07: op00_07(); break; + case 0x08: op00_08(); break; + case 0x0b: op00_0b(); break; + case 0x0c: op00_0c(); break; + } +} + +//Draw Wireframe +void Cx4::op01() { + memset(ram + 0x300, 0, 2304); + C4DrawWireFrame(); +} + +//Propulsion +void Cx4::op05() { + int32 temp = 0x10000; + if(readw(0x1f83)) { + temp = sar((temp / readw(0x1f83)) * readw(0x1f81), 8); + } + writew(0x1f80, temp); +} + +//Set Vector length +void Cx4::op0d() { + C41FXVal = readw(0x1f80); + C41FYVal = readw(0x1f83); + C41FDistVal = readw(0x1f86); + double tanval = sqrt(((double)C41FYVal) * ((double)C41FYVal) + ((double)C41FXVal) * ((double)C41FXVal)); + tanval = (double)C41FDistVal / tanval; + C41FYVal = (int16)(((double)C41FYVal * tanval) * 0.99); + C41FXVal = (int16)(((double)C41FXVal * tanval) * 0.98); + writew(0x1f89, C41FXVal); + writew(0x1f8c, C41FYVal); +} + +//Triangle +void Cx4::op10() { + r0 = ldr(0); + r1 = ldr(1); + + r4 = r0 & 0x1ff; + + if(r1 & 0x8000)r1 |= ~0x7fff; + else r1 &= 0x7fff; + + mul(cos(r4), r1, r5, r2); + r5 = (r5 >> 16) & 0xff; + r2 = (r2 << 8) + r5; + + mul(sin(r4), r1, r5, r3); + r5 = (r5 >> 16) & 0xff; + r3 = (r3 << 8) + r5; + + str(0, r0); + str(1, r1); + str(2, r2); + str(3, r3); + str(4, r4); + str(5, r5); +} + +//Triangle +void Cx4::op13() { + r0 = ldr(0); + r1 = ldr(1); + + r4 = r0 & 0x1ff; + + mul(cos(r4), r1, r5, r2); + r5 = (r5 >> 8) & 0xffff; + r2 = (r2 << 16) + r5; + + mul(sin(r4), r1, r5, r3); + r5 = (r5 >> 8) & 0xffff; + r3 = (r3 << 16) + r5; + + str(0, r0); + str(1, r1); + str(2, r2); + str(3, r3); + str(4, r4); + str(5, r5); +} + +//Pythagorean +void Cx4::op15() { + C41FXVal = readw(0x1f80); + C41FYVal = readw(0x1f83); + C41FDist = (int16)sqrt((double)C41FXVal * (double)C41FXVal + (double)C41FYVal * (double)C41FYVal); + writew(0x1f80, C41FDist); +} + +//Calculate distance +void Cx4::op1f() { + C41FXVal = readw(0x1f80); + C41FYVal = readw(0x1f83); + if(!C41FXVal) { + C41FAngleRes = (C41FYVal > 0) ? 0x080 : 0x180; + } else { + double tanval = ((double)C41FYVal) / ((double)C41FXVal); + C41FAngleRes = (short)(atan(tanval) / (PI * 2) * 512); + C41FAngleRes = C41FAngleRes; + if(C41FXVal < 0) { + C41FAngleRes += 0x100; + } + C41FAngleRes &= 0x1ff; + } + writew(0x1f86, C41FAngleRes); +} + +//Trapezoid +void Cx4::op22() { + int16 angle1 = readw(0x1f8c) & 0x1ff; + int16 angle2 = readw(0x1f8f) & 0x1ff; + int32 tan1 = Tan(angle1); + int32 tan2 = Tan(angle2); + int16 y = readw(0x1f83) - readw(0x1f89); + int16 left, right; + + for(int32 j = 0; j < 225; j++, y++) { + if(y >= 0) { + left = sar((int32)tan1 * y, 16) - readw(0x1f80) + readw(0x1f86); + right = sar((int32)tan2 * y, 16) - readw(0x1f80) + readw(0x1f86) + readw(0x1f93); + + if(left < 0 && right < 0) { + left = 1; + right = 0; + } else if(left < 0) { + left = 0; + } else if(right < 0) { + right = 0; + } + + if(left > 255 && right > 255) { + left = 255; + right = 254; + } else if(left > 255) { + left = 255; + } else if(right > 255) { + right = 255; + } + } else { + left = 1; + right = 0; + } + ram[j + 0x800] = (uint8)left; + ram[j + 0x900] = (uint8)right; + } +} + +//Multiply +void Cx4::op25() { + r0 = ldr(0); + r1 = ldr(1); + mul(r0, r1, r0, r1); + str(0, r0); + str(1, r1); +} + +//Transform Coords +void Cx4::op2d() { + C4WFXVal = readw(0x1f81); + C4WFYVal = readw(0x1f84); + C4WFZVal = readw(0x1f87); + C4WFX2Val = read (0x1f89); + C4WFY2Val = read (0x1f8a); + C4WFDist = read (0x1f8b); + C4WFScale = readw(0x1f90); + C4TransfWireFrame2(); + writew(0x1f80, C4WFXVal); + writew(0x1f83, C4WFYVal); +} + +//Sum +void Cx4::op40() { + r0 = 0; + for(uint32 i=0;i<0x800;i++) { + r0 += ram[i]; + } + str(0, r0); +} + +//Square +void Cx4::op54() { + r0 = ldr(0); + mul(r0, r0, r1, r2); + str(1, r1); + str(2, r2); +} + +//Immediate Register +void Cx4::op5c() { + str(0, 0x000000); + immediate_reg(0); +} + +//Immediate Register (Multiple) +void Cx4::op5e() { immediate_reg( 0); } +void Cx4::op60() { immediate_reg( 3); } +void Cx4::op62() { immediate_reg( 6); } +void Cx4::op64() { immediate_reg( 9); } +void Cx4::op66() { immediate_reg(12); } +void Cx4::op68() { immediate_reg(15); } +void Cx4::op6a() { immediate_reg(18); } +void Cx4::op6c() { immediate_reg(21); } +void Cx4::op6e() { immediate_reg(24); } +void Cx4::op70() { immediate_reg(27); } +void Cx4::op72() { immediate_reg(30); } +void Cx4::op74() { immediate_reg(33); } +void Cx4::op76() { immediate_reg(36); } +void Cx4::op78() { immediate_reg(39); } +void Cx4::op7a() { immediate_reg(42); } +void Cx4::op7c() { immediate_reg(45); } + +//Immediate ROM +void Cx4::op89() { + str(0, 0x054336); + str(1, 0xffffff); +} + +#endif diff --git a/mednafen/snes/src/chip/cx4/serialization.cpp b/mednafen/snes/src/chip/cx4/serialization.cpp new file mode 100755 index 0000000..1b36a4e --- /dev/null +++ b/mednafen/snes/src/chip/cx4/serialization.cpp @@ -0,0 +1,39 @@ +#ifdef CX4_CPP + +void Cx4::serialize(serializer &s) { + s.array(ram); + s.array(reg); + + s.integer(r0); + s.integer(r1); + s.integer(r2); + s.integer(r3); + s.integer(r4); + s.integer(r5); + s.integer(r6); + s.integer(r7); + s.integer(r8); + s.integer(r9); + s.integer(r10); + s.integer(r11); + s.integer(r12); + s.integer(r13); + s.integer(r14); + s.integer(r15); + + s.integer(C4WFXVal); + s.integer(C4WFYVal); + s.integer(C4WFZVal); + s.integer(C4WFX2Val); + s.integer(C4WFY2Val); + s.integer(C4WFDist); + s.integer(C4WFScale); + + s.integer(C41FXVal); + s.integer(C41FYVal); + s.integer(C41FAngleRes); + s.integer(C41FDist); + s.integer(C41FDistVal); +} + +#endif diff --git a/mednafen/snes/src/chip/dsp1/dsp1.cpp b/mednafen/snes/src/chip/dsp1/dsp1.cpp new file mode 100755 index 0000000..8fe22b3 --- /dev/null +++ b/mednafen/snes/src/chip/dsp1/dsp1.cpp @@ -0,0 +1,83 @@ +#include <../base.hpp> + +#define DSP1_CPP +namespace SNES { + +DSP1 dsp1; + +#include "serialization.cpp" +#include "dsp1emu.cpp" + +void DSP1::init() { +} + +void DSP1::enable() { + switch(cartridge.dsp1_mapper()) { + case Cartridge::DSP1LoROM1MB: { + bus.map(Bus::MapDirect, 0x20, 0x3f, 0x8000, 0xffff, *this); + bus.map(Bus::MapDirect, 0xa0, 0xbf, 0x8000, 0xffff, *this); + } break; + + case Cartridge::DSP1LoROM2MB: { + bus.map(Bus::MapDirect, 0x60, 0x6f, 0x0000, 0x7fff, *this); + bus.map(Bus::MapDirect, 0xe0, 0xef, 0x0000, 0x7fff, *this); + } break; + + case Cartridge::DSP1HiROM: { + bus.map(Bus::MapDirect, 0x00, 0x1f, 0x6000, 0x7fff, *this); + bus.map(Bus::MapDirect, 0x80, 0x9f, 0x6000, 0x7fff, *this); + } break; + } +} + +void DSP1::power() { + reset(); +} + +void DSP1::reset() { + dsp1.reset(); +} + +/***** + * addr_decode() + * determine whether address is accessing + * data register (DR) or status register (SR) + * -- 0 (false) = DR + * -- 1 (true ) = SR + * + * note: there is no need to bounds check addresses, + * as memory mapper will not allow DSP1 accesses outside + * of expected ranges + *****/ +bool DSP1::addr_decode(uint16 addr) { + switch(cartridge.dsp1_mapper()) { + case Cartridge::DSP1LoROM1MB: { + //$[20-3f]:[8000-bfff] = DR, $[20-3f]:[c000-ffff] = SR + return (addr >= 0xc000); + } + + case Cartridge::DSP1LoROM2MB: { + //$[60-6f]:[0000-3fff] = DR, $[60-6f]:[4000-7fff] = SR + return (addr >= 0x4000); + } + + case Cartridge::DSP1HiROM: { + //$[00-1f]:[6000-6fff] = DR, $[00-1f]:[7000-7fff] = SR + return (addr >= 0x7000); + } + } + + return 0; +} + +uint8 DSP1::read(unsigned addr) { + return (addr_decode(addr) == 0) ? dsp1.getDr() : dsp1.getSr(); +} + +void DSP1::write(unsigned addr, uint8 data) { + if(addr_decode(addr) == 0) { + dsp1.setDr(data); + } +} + +}; diff --git a/mednafen/snes/src/chip/dsp1/dsp1.hpp b/mednafen/snes/src/chip/dsp1/dsp1.hpp new file mode 100755 index 0000000..474c098 --- /dev/null +++ b/mednafen/snes/src/chip/dsp1/dsp1.hpp @@ -0,0 +1,20 @@ +#include "dsp1emu.hpp" + +class DSP1 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + +private: + Dsp1 dsp1; + bool addr_decode(uint16 addr); +}; + +extern DSP1 dsp1; diff --git a/mednafen/snes/src/chip/dsp1/dsp1emu.cpp b/mednafen/snes/src/chip/dsp1/dsp1emu.cpp new file mode 100755 index 0000000..d933b11 --- /dev/null +++ b/mednafen/snes/src/chip/dsp1/dsp1emu.cpp @@ -0,0 +1,1625 @@ +#ifdef DSP1_CPP + +// DSP-1's emulation code +// +// Based on research by Overload, The Dumper, Neviksti and Andreas Naive +// Date: June 2006 + +////////////////////////////////////////////////////////////////// + +Dsp1::Dsp1() +{ + reset(); +} + +////////////////////////////////////////////////////////////////// + +uint8 Dsp1::getSr() +{ + mSrLowByteAccess = ~mSrLowByteAccess; + if (mSrLowByteAccess) + return 0; + else + return mSr; +} + +////////////////////////////////////////////////////////////////// + +uint8 Dsp1::getDr() +{ + uint8 oDr; + + fsmStep(true, oDr); + return oDr; +} + +////////////////////////////////////////////////////////////////// + +void Dsp1::setDr(uint8 iDr) +{ + fsmStep(false, iDr); +} + +////////////////////////////////////////////////////////////////// + +void Dsp1::reset() +{ + mSr = DRC|RQM; + mSrLowByteAccess = false; + mDr = 0x0080; // Only a supposition. Is this correct? + mFreeze = false; + mFsmMajorState = WAIT_COMMAND; + memset(&shared, 0, sizeof(SharedData)); // another supposition +} + +////////////////////////////////////////////////////////////////// + +// Though the DSP-1 is unaware of the type of operation (read or write) +// we need to know what is being done by the program, as the class +// is responsible for maintaining the binding between the +// "external" and "internal" representations of the DR (data register). + +void Dsp1::fsmStep(bool read, uint8 &data) +{ + if (0 == (mSr&RQM)) return; + // Now RQM would be cleared; however, as this code is not to be used in + // a multithread environment, we will simply fake RQM operation. + // (The only exception would be Op1A's freeze.) + + // binding + if (read) + { + if (mSr&DRS) + data = static_cast(mDr>>8); + else + data = static_cast(mDr); + } + else + { + if (mSr&DRS) + { + mDr &= 0x00ff; + mDr |= data<<8; + } + else + { + mDr &= 0xff00; + mDr |= data; + } + } + + + switch (mFsmMajorState) + { + case WAIT_COMMAND: + mCommand = static_cast(mDr); + if (!(mCommand & 0xc0)) // valid command? + { + switch(mCommand) + { + // freeze cases + case 0x1a: + case 0x2a: + case 0x3a: + mFreeze = true; + break; + // normal cases + default: + mDataCounter=0; + mFsmMajorState = READ_DATA; + mSr &= ~DRC; + break; + } + } + break; + case READ_DATA: + mSr ^= DRS; + if (!(mSr&DRS)) + { + mReadBuffer[mDataCounter++] = static_cast(mDr); + if (mDataCounter >= mCommandTable[mCommand].reads) + { + (this->*mCommandTable[mCommand].callback)(mReadBuffer, mWriteBuffer); + if (0 != mCommandTable[mCommand].writes) // any output? + { + mDataCounter = 0; + mDr = static_cast(mWriteBuffer[mDataCounter]); + mFsmMajorState = WRITE_DATA; + } + else + { + mDr = 0x0080; // valid command completion + mFsmMajorState = WAIT_COMMAND; + mSr |= DRC; + } + } + } + break; + case WRITE_DATA: + mSr ^= DRS; + if (!(mSr&DRS)) + { + ++mDataCounter; + if (mDataCounter >= mCommandTable[mCommand].writes) + { + if ((mCommand == 0x0a)&&(mDr != 0x8000)) + { + // works in continuous mode + mReadBuffer[0]++; // next raster line + (this->*mCommandTable[mCommand].callback)(mReadBuffer, mWriteBuffer); + mDataCounter = 0; + mDr = static_cast(mWriteBuffer[mDataCounter]); + } + else + { + mDr = 0x0080; // valid command completion + mFsmMajorState = WAIT_COMMAND; + mSr |= DRC; + } + } + else + { + mDr = static_cast(mWriteBuffer[mDataCounter]); + } + } + break; + } + + + + // Now RQM would be set (except when executing Op1A -command equals 0x1a, 0x2a or 0x3a-). + if (mFreeze) + mSr &= ~RQM; +} + +////////////////////////////////////////////////////////////////// + +// The info on this table follows Overload's docs. + +const Dsp1::Command Dsp1::mCommandTable[0x40] = { + {&Dsp1::multiply, 2, 1}, //0x00 + {&Dsp1::attitudeA, 4, 0}, //0x01 + {&Dsp1::parameter, 7, 4}, //0x02 + {&Dsp1::subjectiveA, 3, 3}, //0x03 + {&Dsp1::triangle, 2, 2}, //0x04 + {&Dsp1::attitudeA, 4, 0}, //0x01 + {&Dsp1::project, 3, 3}, //0x06 + {&Dsp1::memoryTest, 1, 1}, //0x0f + {&Dsp1::radius, 3, 2}, //0x08 + {&Dsp1::objectiveA, 3, 3}, //0x0d + {&Dsp1::raster, 1, 4}, // 0x0a. This will normally work in continuous mode + {&Dsp1::scalarA, 3, 1}, //0x0b + {&Dsp1::rotate, 3, 2}, //0x0c + {&Dsp1::objectiveA, 3, 3}, //0x0d + {&Dsp1::target, 2, 2}, //0x0e + {&Dsp1::memoryTest, 1, 1}, //0x0f + + {&Dsp1::inverse, 2, 2}, //0x10 + {&Dsp1::attitudeB, 4, 0}, //0x11 + {&Dsp1::parameter, 7, 4}, //0x02 + {&Dsp1::subjectiveB, 3, 3}, //0x13 + {&Dsp1::gyrate, 6, 3}, //0x14 + {&Dsp1::attitudeB, 4, 0}, //0x11 + {&Dsp1::project, 3, 3}, //0x06 + {&Dsp1::memoryDump, 1, 1024}, //0x1f + {&Dsp1::range, 4, 1}, //0x18 + {&Dsp1::objectiveB, 3, 3}, //0x1d + {0, 0, 0}, // 0x1a; the chip freezes + {&Dsp1::scalarB, 3, 1}, //0x1b + {&Dsp1::polar, 6, 3}, //0x1c + {&Dsp1::objectiveB, 3, 3}, //0x1d + {&Dsp1::target, 2, 2}, //0x0e + {&Dsp1::memoryDump, 1, 1024}, //0x1f + + {&Dsp1::multiply2, 2, 1}, //0x20 + {&Dsp1::attitudeC, 4, 0}, //0x21 + {&Dsp1::parameter, 7, 4}, //0x02 + {&Dsp1::subjectiveC, 3, 3}, //0x23 + {&Dsp1::triangle, 2, 2}, //0x04 + {&Dsp1::attitudeC, 4, 0}, //0x21 + {&Dsp1::project, 3, 3}, //0x06 + {&Dsp1::memorySize, 1, 1}, //0x2f + {&Dsp1::distance, 3, 1}, //0x28 + {&Dsp1::objectiveC, 3, 3}, //0x2d + {0, 0, 0}, // 0x1a; the chip freezes + {&Dsp1::scalarC, 3, 1}, //0x2b + {&Dsp1::rotate, 3, 2}, //0x0c + {&Dsp1::objectiveC, 3, 3}, //0x2d + {&Dsp1::target, 2, 2}, //0x0e + {&Dsp1::memorySize, 1, 1}, //0x2f + + {&Dsp1::inverse, 2, 2}, //0x10 + {&Dsp1::attitudeA, 4, 0}, //0x01 + {&Dsp1::parameter, 7, 4}, //0x02 + {&Dsp1::subjectiveA, 3, 3}, //0x03 + {&Dsp1::gyrate, 6, 3}, //0x14 + {&Dsp1::attitudeA, 4, 0}, //0x01 + {&Dsp1::project, 3, 3}, //0x06 + {&Dsp1::memoryDump, 1, 1024}, //0x1f + {&Dsp1::range2, 4, 1}, //0x38 + {&Dsp1::objectiveA, 3, 3}, //0x0d + {0, 0, 0}, // 0x1a; the chip freezes + {&Dsp1::scalarA, 3, 1}, //0x0b + {&Dsp1::polar, 6, 3}, //0x1c + {&Dsp1::objectiveA, 3, 3}, //0x0d + {&Dsp1::target, 2, 2}, //0x0e + {&Dsp1::memoryDump, 1, 1024}, //0x1f +}; + +////////////////////////////////////////////////////////////////// + +void Dsp1::memoryTest(int16 *input, int16 *output) +{ + int16& Size = input[0]; + int16& Result = output[0]; + + Result = 0x0000; +} + +////////////////////////////////////////////////////////////////// + +void Dsp1::memoryDump(int16 *input, int16 *output) +{ + memcpy(output, DataRom, 1024); +} + +////////////////////////////////////////////////////////////////// + +void Dsp1::memorySize(int16 *input, int16 *output) +{ + int16& Size = output[0]; + + Size = 0x0100; +} + +////////////////////////////////////////////////////////////////// + +// 16-bit multiplication + +void Dsp1::multiply(int16 *input, int16 *output) +{ + int16& Multiplicand = input[0]; + int16& Multiplier = input[1]; + int16& Product = output[0]; + + Product = Multiplicand * Multiplier >> 15; +} + +////////////////////////////////////////////////////////////////// + +// 16-bit multiplication. 'Alternative' method. Can anyone check this carefully? + +void Dsp1::multiply2(int16 *input, int16 *output) +{ + int16& Multiplicand = input[0]; + int16& Multiplier = input[1]; + int16& Product = output[0]; + + Product = (Multiplicand * Multiplier >> 15)+1; +} + +////////////////////////////////////////////////////////////////// + +// This command determines the inverse of a floating point decimal number. + +void Dsp1::inverse(int16 *input, int16 *output) +{ + int16& Coefficient = input[0]; + int16& Exponent = input[1]; + int16& iCoefficient = output[0]; + int16& iExponent = output[1]; + + inverse(Coefficient, Exponent, iCoefficient, iExponent); +} + +////////////////////////////////////////////////////////////////// + +// Vector component calculation. Determines the X and Y components for a +// two-dimensional vector whose size and direction is known. +// Y = Radius * sin(Angle) +// X = Radius * cos(Angle) + +void Dsp1::triangle(int16 *input, int16 *output) +{ + int16& Angle = input[0]; + int16& Radius = input[1]; + int16& Y = output[0]; + int16& X = output[1]; + + Y = sin(Angle) * Radius >> 15; + X = cos(Angle) * Radius >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Determines the squared norm of a vector (X,Y,Z) +// The output is Radius = X^2+Y^2+Z^2 (double integer) + +void Dsp1::radius(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& RadiusLow = output[0]; + int16& RadiusHigh = output[1]; + + int32 Radius; + + Radius = (X * X + Y * Y + Z * Z) << 1; + RadiusLow = static_cast(Radius); + RadiusHigh = static_cast(Radius>>16); +} + +////////////////////////////////////////////////////////////////// + +// Vector size comparison. This command compares the size of the vector (X,Y,Z) and the distance (R) +// from a particular point, and so may be used to determine if a point is within the sphere or radius R. +// The output is D = X^2+Y^2+Z^2-R^2 + +void Dsp1::range(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& Radius = input[3]; + int16& Range = output[0]; + + Range = (X * X + Y * Y + Z * Z - Radius * Radius) >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Vector size comparison. 'Alternative' method. + +void Dsp1::range2(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& Radius = input[3]; + int16& Range = output[0]; + + Range = ((X * X + Y * Y + Z * Z - Radius * Radius) >> 15) + 1; +} + +////////////////////////////////////////////////////////////////// + +// This command calculates the norm of a (X,Y,Z) vector, or the distance from +// the point (X,Y,Z) to (0,0,0), as you prefer to see it. +// Distance = sqrt(X^2+Y^2+Z^2) +// The square root of a number 'a' is calculated by doing this: you +// write 'a' as b*2^2n, with 'b' between 1/4 and 1; then, you calculate +// c=sqrt(b) by using lineal interpolation between points of a +// look-up table and, finally, you output the result as c*2^n. + +void Dsp1::distance(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& Distance = output[0]; + + int32 Radius = X * X + Y * Y + Z * Z; + + if (Radius == 0) Distance = 0; + else + { + int16 C, E; + normalizeDouble(Radius, C, E); + if (E & 1) C = C * 0x4000 >> 15; + + int16 Pos = C * 0x0040 >> 15; + + int16 Node1 = DataRom[0x00d5 + Pos]; + int16 Node2 = DataRom[0x00d6 + Pos]; + + Distance = ((Node2 - Node1) * (C & 0x1ff) >> 9) + Node1; + +#if DSP1_VERSION < 0x0102 + if (Pos & 1) Distance -= (Node2 - Node1); +#endif + Distance >>= (E >> 1); + } +} + +////////////////////////////////////////////////////////////////// + +// Determines the (X2, Y2) coordinates obtained by rotating (X1, Y1) +// clockwise for an angle 'Angle'. The official documentation says +// 'counterclockwise', but it's obviously wrong (surprise! :P) +// +// In matrix notation: +// |X2| |cos(Angle) sin(Angle)| |X1| +// | | = | | | | +// |Y2| |-sin(Angle cos(Angle)| |Y1| + +void Dsp1::rotate(int16 *input, int16 *output) +{ + int16& Angle = input[0]; + int16& X1 = input[1]; + int16& Y1 = input[2]; + int16& X2 = output[0]; + int16& Y2 = output[1]; + + X2 = (Y1 * sin(Angle) >> 15) + (X1 * cos(Angle) >> 15); + Y2 = (Y1 * cos(Angle) >> 15) - (X1 * sin(Angle) >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Calculate the coordinates (X2, Y2, Z2) obtained when rotating (X1, Y1, Z1) +// three-dimensionally. Rotation is done in the order of Az around the Z axis, +// Ay around the Y axis and Ax around the X axis. As occur with the "attitude" commands +// (see comments in the "gyrate" command), this doesn't match what explained in +// the official documentation, but it's coherent with what it is done in the "attitude" +// command (but not with the "gyrate" command). +// +// In matrix notation: +// |X2| |1 0 0 | |cosRy 0 -sinRy| | cosRz sinRz 0| |X1| +// |Y2| = |0 cosRx sinRx| | 0 1 0 | |-sinRz cosRz 0| |Y1| +// |Z2| |0 -sinRx cosRx| |sinRy 0 cosRy| | 0 0 1| |Z1| + +void Dsp1::polar(int16 *input, int16 *output) +{ + int16& Az = input[0]; + int16& Ay = input[1]; + int16& Ax = input[2]; + int16& X1 = input[3]; + int16& Y1 = input[4]; + int16& Z1 = input[5]; + int16& X2 = output[0]; + int16& Y2 = output[1]; + int16& Z2 = output[2]; + + int16 X, Y, Z; + + // Rotate Around Z + X = (Y1 * sin(Az) >> 15) + (X1 * cos(Az) >> 15); + Y = (Y1 * cos(Az) >> 15) - (X1 * sin(Az) >> 15); + X1 = X; Y1 = Y; + + // Rotate Around Y + Z = (X1 * sin(Ay) >> 15) + (Z1 * cos(Ay) >> 15); + X = (X1 * cos(Ay) >> 15) - (Z1 * sin(Ay) >> 15); + X2 = X; Z1 = Z; + + // Rotate Around X + Y = (Z1 * sin(Ax) >> 15) + (Y1 * cos(Ax) >> 15); + Z = (Z1 * cos(Ax) >> 15) - (Y1 * sin(Ax) >> 15); + Y2 = Y; Z2 = Z; +} + +////////////////////////////////////////////////////////////////// + +// Set up the elements of an "attitude matrix" (there are other ones): +// S | cosRz sinRz 0| |cosRy 0 -sinRy| |1 0 0 | +// MatrixA = - |-sinRz cosRz 0| | 0 1 0 | |0 cosRx sinRx| +// 2 | 0 0 1| |sinRy 0 cosRy| |0 -sinRx cosRx| +// This matrix is thought to be used within the following framework: +// let's suppose we define positive rotations around a system of orthogonal axes in this manner: +// a rotation of +90 degrees around axis3 converts axis2 into axis1 +// a rotation of +90 degrees around axis2 converts axis1 into axis3 +// a rotation of +90 degrees around axis1 converts axis3 into axis2 +// and let's suppose that we have defined a new orthonormal axes system (FLU) +// by doing the following operations about the standard one (XYZ): +// first rotating the XYZ system around Z by an angle Rz (obtaining X'Y'Z'), +// then rotating the resulting system around Y by an angle Ry (obtaining X''Y''Z'') +// and, finally, rotating the resulting system around X by an angle Rx (obtaining FLU) +// This FLU (forward/left/up) system represents an "attitude" and, then, the matrix here defined +// is the change of coordinates matrix that transform coordinates in the FLU +// system (the "object coordinates") into the standard XYZ system (the "global coordinates"), +// multiplied by a scale factor S/2, that is: +// |x| S |f| +// |y| * - = MatrixA * |l| +// |z| 2 |u| +// In a similar way, if we use the transpose of the matrix, we can transform global coordinates +// into object coordinates: +// |f| S |x| +// |l| * - = MatrixA_transposed * |y| +// |u| 2 |z| +// +// input[0]: S +// input[1]: Rz +// input[2]: Ry +// input[3]: Rx + +void Dsp1::attitudeA(int16 *input, int16 *output) +{ + int16& S = input[0]; + int16& Rz = input[1]; + int16& Ry = input[2]; + int16& Rx = input[3]; + + int16 SinRz = sin(Rz); + int16 CosRz = cos(Rz); + int16 SinRy = sin(Ry); + int16 CosRy = cos(Ry); + int16 SinRx = sin(Rx); + int16 CosRx = cos(Rx); + + S >>= 1; + + shared.MatrixA[0][0] = (S * CosRz >> 15) * CosRy >> 15; + shared.MatrixA[0][1] = ((S * SinRz >> 15) * CosRx >> 15) + (((S * CosRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixA[0][2] = ((S * SinRz >> 15) * SinRx >> 15) - (((S * CosRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixA[1][0] = -((S * SinRz >> 15) * CosRy >> 15); + shared.MatrixA[1][1] = ((S * CosRz >> 15) * CosRx >> 15) - (((S * SinRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixA[1][2] = ((S * CosRz >> 15) * SinRx >> 15) + (((S * SinRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixA[2][0] = S * SinRy >> 15; + shared.MatrixA[2][1] = -((S * SinRx >> 15) * CosRy >> 15); + shared.MatrixA[2][2] = (S * CosRx >> 15) * CosRy >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Same than 'attitudeA', but with a difference attitude matrix (matrixB) + +void Dsp1::attitudeB(int16 *input, int16 *output) +{ + int16& S = input[0]; + int16& Rz = input[1]; + int16& Ry = input[2]; + int16& Rx = input[3]; + + int16 SinRz = sin(Rz); + int16 CosRz = cos(Rz); + int16 SinRy = sin(Ry); + int16 CosRy = cos(Ry); + int16 SinRx = sin(Rx); + int16 CosRx = cos(Rx); + + S >>= 1; + + shared.MatrixB[0][0] = (S * CosRz >> 15) * CosRy >> 15; + shared.MatrixB[0][1] = ((S * SinRz >> 15) * CosRx >> 15) + (((S * CosRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixB[0][2] = ((S * SinRz >> 15) * SinRx >> 15) - (((S * CosRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixB[1][0] = -((S * SinRz >> 15) * CosRy >> 15); + shared.MatrixB[1][1] = ((S * CosRz >> 15) * CosRx >> 15) - (((S * SinRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixB[1][2] = ((S * CosRz >> 15) * SinRx >> 15) + (((S * SinRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixB[2][0] = S * SinRy >> 15; + shared.MatrixB[2][1] = -((S * SinRx >> 15) * CosRy >> 15); + shared.MatrixB[2][2] = (S * CosRx >> 15) * CosRy >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Same than 'attitudeA', but with a difference attitude matrix (matrixC) + +void Dsp1::attitudeC(int16 *input, int16 *output) +{ + int16& S = input[0]; + int16& Rz = input[1]; + int16& Ry = input[2]; + int16& Rx = input[3]; + + int16 SinRz = sin(Rz); + int16 CosRz = cos(Rz); + int16 SinRy = sin(Ry); + int16 CosRy = cos(Ry); + int16 SinRx = sin(Rx); + int16 CosRx = cos(Rx); + + S >>= 1; + + shared.MatrixC[0][0] = (S * CosRz >> 15) * CosRy >> 15; + shared.MatrixC[0][1] = ((S * SinRz >> 15) * CosRx >> 15) + (((S * CosRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixC[0][2] = ((S * SinRz >> 15) * SinRx >> 15) - (((S * CosRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixC[1][0] = -((S * SinRz >> 15) * CosRy >> 15); + shared.MatrixC[1][1] = ((S * CosRz >> 15) * CosRx >> 15) - (((S * SinRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixC[1][2] = ((S * CosRz >> 15) * SinRx >> 15) + (((S * SinRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixC[2][0] = S * SinRy >> 15; + shared.MatrixC[2][1] = -((S * SinRx >> 15) * CosRy >> 15); + shared.MatrixC[2][2] = (S * CosRx >> 15) * CosRy >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Convert global coordinates (X,Y,Z) to object coordinates (F,L,U) +// See the comment in "attitudeA" for a explanation about the calculation. +// +// input[0]: X ; input[1]: Y ; input[2]: Z +// output[0]: F ; output[1]: L ; output[2]: U + +void Dsp1::objectiveA(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& F = output[0]; + int16& L = output[1]; + int16& U = output[2]; + + F = (shared.MatrixA[0][0] * X >> 15) + (shared.MatrixA[1][0] * Y >> 15) + (shared.MatrixA[2][0] * Z >> 15); + L = (shared.MatrixA[0][1] * X >> 15) + (shared.MatrixA[1][1] * Y >> 15) + (shared.MatrixA[2][1] * Z >> 15); + U = (shared.MatrixA[0][2] * X >> 15) + (shared.MatrixA[1][2] * Y >> 15) + (shared.MatrixA[2][2] * Z >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Same than 'objectiveA', but for the 'B' attitude + +void Dsp1::objectiveB(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& F = output[0]; + int16& L = output[1]; + int16& U = output[2]; + + F = (shared.MatrixB[0][0] * X >> 15) + (shared.MatrixB[1][0] * Y >> 15) + (shared.MatrixB[2][0] * Z >> 15); + L = (shared.MatrixB[0][1] * X >> 15) + (shared.MatrixB[1][1] * Y >> 15) + (shared.MatrixB[2][1] * Z >> 15); + U = (shared.MatrixB[0][2] * X >> 15) + (shared.MatrixB[1][2] * Y >> 15) + (shared.MatrixB[2][2] * Z >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Same than 'objectiveA', but for the 'C' attitude + +void Dsp1::objectiveC(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& F = output[0]; + int16& L = output[1]; + int16& U = output[2]; + + F = (shared.MatrixC[0][0] * X >> 15) + (shared.MatrixC[1][0] * Y >> 15) + (shared.MatrixC[2][0] * Z >> 15); + L = (shared.MatrixC[0][1] * X >> 15) + (shared.MatrixC[1][1] * Y >> 15) + (shared.MatrixC[2][1] * Z >> 15); + U = (shared.MatrixC[0][2] * X >> 15) + (shared.MatrixC[1][2] * Y >> 15) + (shared.MatrixC[2][2] * Z >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Convert object coordinates (F,L,U) to object coordinates (X,Y,Z) +// See the comment in "attitudeA" for a explanation about the calculation. +// +// input[0]: F ; input[1]: L ; input[2]: U +// output[0]: X ; output[1]: Y ; output[2]: Z + +void Dsp1::subjectiveA(int16 *input, int16 *output) +{ + int16& F = input[0]; + int16& L = input[1]; + int16& U = input[2]; + int16& X = output[0]; + int16& Y = output[1]; + int16& Z = output[2]; + + X = (shared.MatrixA[0][0] * F >> 15) + (shared.MatrixA[0][1] * L >> 15) + (shared.MatrixA[0][2] * U >> 15); + Y = (shared.MatrixA[1][0] * F >> 15) + (shared.MatrixA[1][1] * L >> 15) + (shared.MatrixA[1][2] * U >> 15); + Z = (shared.MatrixA[2][0] * F >> 15) + (shared.MatrixA[2][1] * L >> 15) + (shared.MatrixA[2][2] * U >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Same than 'subjectiveA', but for the 'B' attitude + +void Dsp1::subjectiveB(int16 *input, int16 *output) +{ + int16& F = input[0]; + int16& L = input[1]; + int16& U = input[2]; + int16& X = output[0]; + int16& Y = output[1]; + int16& Z = output[2]; + + X = (shared.MatrixB[0][0] * F >> 15) + (shared.MatrixB[0][1] * L >> 15) + (shared.MatrixB[0][2] * U >> 15); + Y = (shared.MatrixB[1][0] * F >> 15) + (shared.MatrixB[1][1] * L >> 15) + (shared.MatrixB[1][2] * U >> 15); + Z = (shared.MatrixB[2][0] * F >> 15) + (shared.MatrixB[2][1] * L >> 15) + (shared.MatrixB[2][2] * U >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Same than 'subjectiveA', but for the 'C' attitude + +void Dsp1::subjectiveC(int16 *input, int16 *output) +{ + int16& F = input[0]; + int16& L = input[1]; + int16& U = input[2]; + int16& X = output[0]; + int16& Y = output[1]; + int16& Z = output[2]; + + X = (shared.MatrixC[0][0] * F >> 15) + (shared.MatrixC[0][1] * L >> 15) + (shared.MatrixC[0][2] * U >> 15); + Y = (shared.MatrixC[1][0] * F >> 15) + (shared.MatrixC[1][1] * L >> 15) + (shared.MatrixC[1][2] * U >> 15); + Z = (shared.MatrixC[2][0] * F >> 15) + (shared.MatrixC[2][1] * L >> 15) + (shared.MatrixC[2][2] * U >> 15); +} + +////////////////////////////////////////////////////////////////// + +// This command calculates the inner product (S) of a vector (X,Y,Z) and +// the first column of MatrixA. It should be noted that that first column +// represent the global coordinates of an unity vector in the forward +// direction in the object coordinate system (coordinates (1,0,0) in the FLU +// axes system). +// +// input[0]: X ; input[1]: Y ; input[2]: Z +// output[0]: S + +void Dsp1::scalarA(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& S = output[0]; + + S = (X * shared.MatrixA[0][0] + Y * shared.MatrixA[1][0] + Z * shared.MatrixA[2][0]) >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Same than 'scalarA', but for the 'B' attitude + +void Dsp1::scalarB(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& S = output[0]; + + S = (X * shared.MatrixB[0][0] + Y * shared.MatrixB[1][0] + Z * shared.MatrixB[2][0]) >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Same than 'scalarA', but for the 'C' attitude + +void Dsp1::scalarC(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& S = output[0]; + + S = (X * shared.MatrixC[0][0] + Y * shared.MatrixC[1][0] + Z * shared.MatrixC[2][0]) >> 15; +} + +////////////////////////////////////////////////////////////////// + +// This command determines the final attitude angles after the body with attitude angles (Ax, Ay, Az) with +// respect to the global coordinates is rotated by the minor angular displacements (DeltaF, DeltaL, DeltaU). +// It means that the XYZ axes are rotated by (Ax, Ay, Az) to obtain the FLU axes and, then, these +// are rotated by (DeltaF, DeltaL, DeltaU). The command calculates and return the new FLU angles respect to the +// XYZ system (Rx, Ry, Rz) +// The formulae are: +// Rx = Ax + (DeltaU*sin(Ay)+DeltaF*cos(Ay)) +// Ry = Ay + DeltaL - tan(Ax)*(DeltaU*cos(Ay)+DeltaF*sin(Ay)) +// Rz = Az + sec(Ax)*(DeltaU*cos(Ay)-DeltaF*sin(Ay)) +// +// Now the discussion: according to the official documentation, as described in various commands, you pass from +// XYZ to FLU by doing the rotations in the order Y, X, Z. In this command, the formulae are coherent with the +// fact that Y is the first axis to do a rotation around it. However, in the "attitude" command, while the official +// document describe it that way, we have discovered, when reverse engineering the command, that the calculated +// matrix do the rotation around Y in the second place. This incoherent behaviour of various commands is, in my +// opinion, a pretty severe implementation error. However, if you only use small "minor displacements", the error term +// introduced by that incoherence should be almost negligible. + +void Dsp1::gyrate(int16 *input, int16 *output) +{ + int16& Az = input[0]; + int16& Ax = input[1]; + int16& Ay = input[2]; + int16& U = input[3]; + int16& F = input[4]; + int16& L = input[5]; + int16& Rz = output[0]; + int16& Rx = output[1]; + int16& Ry = output[2]; + + int16 CSec, ESec, CSin, C, E; + int16 SinAy = sin(Ay); + int16 CosAy = cos(Ay); + + inverse(cos(Ax), 0, CSec, ESec); + + // Rotation Around Z + normalizeDouble(U * CosAy - F * SinAy, C, E); + + E = ESec - E; + + normalize(C * CSec >> 15, C, E); + + Rz = Az + denormalizeAndClip(C, E); + + // Rotation Around X + Rx = Ax + (U * SinAy >> 15) + (F * CosAy >> 15); + + // Rotation Around Y + normalizeDouble(U * CosAy + F * SinAy, C, E); + + E = ESec - E; + + normalize(sin(Ax), CSin, E); + + normalize(-(C * (CSec * CSin >> 15) >> 15), C, E); + + Ry = Ay + denormalizeAndClip(C, E) + L; +} + +////////////////////////////////////////////////////////////////// + +const int16 Dsp1::MaxAZS_Exp[16] = { + 0x38b4, 0x38b7, 0x38ba, 0x38be, 0x38c0, 0x38c4, 0x38c7, 0x38ca, + 0x38ce, 0x38d0, 0x38d4, 0x38d7, 0x38da, 0x38dd, 0x38e0, 0x38e4 +}; + +////////////////////////////////////////////////////////////////// + + +// Set-up the projection framework. Besides returning some values, it store in RAM some values that +// will be used by the other three projection commands (raster, target an project) +// Input: +// (Fx, Fy, Fz)-> coordinates of base point (global coordinates) +// Lfe-> distance between the base point and the viewpoint (center of projection) +// Les-> distance between the base point and the screen +// Aas-> azimuth angle (0 degrees is east; 90 degrees is north) +// Azs-> zenith angle (0 degrees is zenith) +// Output: +// Vof-> raster line of imaginary center (whatever it means ;) ) +// Vva-> raster line representing the horizon line +// (Cx, Cy)-> coordinates of the projection of the center of the screen over the ground (ground coordinates) + +void Dsp1::parameter(int16 *input, int16 *output) +{ + int16& Fx = input[0]; + int16& Fy = input[1]; + int16& Fz = input[2]; + int16& Lfe = input[3]; + int16& Les = input[4]; + int16& Aas = input[5]; + int16& Azs = input[6]; + int16& Vof = output[0]; + int16& Vva = output[1]; + int16& Cx = output[2]; + int16& Cy = output[3]; + + int16 CSec, C, E; + int16 LfeNx, LfeNy, LfeNz; + int16 LesNx, LesNy, LesNz; + + // Copy Zenith angle for clipping + int16 AZS = Azs; + + // Store Les and his coefficient and exponent when normalized + shared.Les = Les; + shared.E_Les=0; + normalize(Les, shared.C_Les, shared.E_Les); + + // Store Sine and Cosine of Azimuth and Zenith angle + shared.SinAas = sin(Aas); + shared.CosAas = cos(Aas); + shared.SinAzs = sin(Azs); + shared.CosAzs = cos(Azs); + + // normal vector to the screen (norm 1, points toward the center of projection) + shared.Nx = shared.SinAzs * -shared.SinAas >> 15; + shared.Ny = shared.SinAzs * shared.CosAas >> 15; + shared.Nz = shared.CosAzs * 0x7fff >> 15; + + // horizontal vector of the screen (Hz=0, norm 1, points toward the right of the screen) + shared.Hx = shared.CosAas*0x7fff>>15; + shared.Hy = shared.SinAas*0x7fff>>15; + + // vertical vector of the screen (norm 1, points toward the top of the screen) + shared.Vx = shared.CosAzs*-shared.SinAas>>15; + shared.Vy = shared.CosAzs*shared.CosAas>>15; + shared.Vz = -shared.SinAzs*0x7fff>>15; + + LfeNx = Lfe*shared.Nx>>15; + LfeNy = Lfe*shared.Ny>>15; + LfeNz = Lfe*shared.Nz>>15; + + // Center of Projection + shared.CentreX = Fx+LfeNx; + shared.CentreY = Fy+LfeNy; + shared.CentreZ = Fz+LfeNz; + + LesNx = Les*shared.Nx>>15; + LesNy = Les*shared.Ny>>15; + LesNz = Les*shared.Nz>>15; + + // center of the screen (global coordinates) + shared.Gx=shared.CentreX-LesNx; + shared.Gy=shared.CentreY-LesNy; + shared.Gz=shared.CentreZ-LesNz; + + + E = 0; + normalize(shared.CentreZ, C, E); + + shared.CentreZ_C = C; + shared.CentreZ_E = E; + + // Determine clip boundary and clip Zenith angle if necessary + // (Why to clip? Maybe to avoid the screen can only show sky with no ground? Only a guess...) + int16 MaxAZS = MaxAZS_Exp[-E]; + + if (AZS < 0) { + MaxAZS = -MaxAZS; + if (AZS < MaxAZS + 1) AZS = MaxAZS + 1; + } else { + if (AZS > MaxAZS) AZS = MaxAZS; + } + + // Store Sine and Cosine of clipped Zenith angle + shared.SinAZS = sin(AZS); + shared.CosAZS = cos(AZS); + + // calculate the separation of (cx, cy) from the projection of + // the 'centre of projection' over the ground... (CentreZ*tg(AZS)) + inverse(shared.CosAZS, 0, shared.SecAZS_C1, shared.SecAZS_E1); + normalize(C * shared.SecAZS_C1 >> 15, C, E); + E += shared.SecAZS_E1; + C = denormalizeAndClip(C, E) * shared.SinAZS >> 15; + + // ... and then take into account the position of the centre of + // projection and the azimuth angle + shared.CentreX += C * shared.SinAas >> 15; + shared.CentreY -= C * shared.CosAas >> 15; + + Cx = shared.CentreX; + Cy = shared.CentreY; + + // Raster number of imaginary center and horizontal line + Vof = 0; + + if ((Azs != AZS) || (Azs == MaxAZS)) + { + // correct vof and vva when Azs is outside the 'non-clipping interval' + // we have only some few Taylor coefficients, so we cannot guess which ones + // are the approximated functions and, what is worse, we don't know why + // the own clipping stuff (and, particularly, this correction) is done + if (Azs == -32768) Azs = -32767; + + C = Azs - MaxAZS; + if (C >= 0) C--; + int16 Aux = ~(C << 2); + + // Vof += x+(1/3)*x^3, where x ranges from 0 to PI/4 when Azs-MaxAZS goes from 0 to 0x2000 + C = Aux * DataRom[0x0328] >> 15; + C = (C * Aux >> 15) + DataRom[0x0327]; + Vof -= (C * Aux >> 15) * Les >> 15; + + // CosAZS *= 1+(1/2)*x^2+(5/24)*x^24, where x ranges from 0 to PI/4 when Azs-MaxAZS goes from 0 to 0x2000 + C = Aux * Aux >> 15; + Aux = (C * DataRom[0x0324] >> 15) + DataRom[0x0325]; + shared.CosAZS += (C * Aux >> 15) * shared.CosAZS >> 15; + } + + // vertical offset of the screen with regard to the horizontal plane + // containing the centre of projection + shared.VOffset = Les * shared.CosAZS >> 15; + + // The horizon line (the line in the screen that is crossed by the horizon plane + // -the horizontal plane containing the 'centre of projection'-), + // will be at distance Les*cotg(AZS) from the centre of the screen. This is difficult + // to explain but easily seen in a graph. To better see it, consider it in this way: + // Les*tg(AZS-90), draw some lines and apply basic trigonometry. ;) + inverse(shared.SinAZS, 0, CSec, E); + normalize(shared.VOffset, C, E); + normalize(C * CSec >> 15, C, E); + + if (C == -32768) { C >>= 1; E++; } + + Vva = denormalizeAndClip(-C, E); + + // Store Secant of clipped Zenith angle + inverse(shared.CosAZS, 0, shared.SecAZS_C2, shared.SecAZS_E2); +} + +////////////////////////////////////////////////////////////////// + +// Calculates the matrix which transform an object situated on a raster line (Vs) into +// his projection over the ground. The modified SecAZS is used here, so +// i don't understand the fine details, but, basically, it's done +// this way: The vertical offset between the point of projection and the +// raster line is calculated (Vs*SinAzs>>15)+VOffset, then the height of +// the center of projection is measured in that units (*CentreZ_C). If, now +// you consider the "reference case" (center of projection at an unit of height), +// the projection of a thin strip containing the raster line will have the same +// width (as the raster line would be on the ground in this case, but will suffer a +// change of scale in height (as the ground and the vertical axis would form an angle of 180-Azs degrees). +// This scale factor, when the angle 'center of screen-center of projection-raster line' is small, +// can be aproximated by the one of the center of the screen, 1/cos(Azs).(**) (Here is when it's used +// SecAZS). By last, you have to consider the effect of the azimuth angle Aas, and you are done. +// +// Using matrix notation: +// |A B| Centre_ZS | cos(Aas) -sin(Aas)| |1 0| +// ProjectionMatrix = | | = ----------- * | | * | | +// |C D| Vs*sin(Azs) |sin(Aas) cos(Aas)| |0 sec(Azs)| +// +// (**) +// If Les=1, the vertical offset between the center +// of projection and the center of the screen is Cos(Azs); then, if the vertical +// offset is 1, the ratio of the projection over the ground respect to the +// line on the screen is 1/cos(Azs). + +void Dsp1::raster(int16 *input, int16 *output) +{ + int16& Vs = input[0]; + int16& An = output[0]; + int16& Bn = output[1]; + int16& Cn = output[2]; + int16& Dn = output[3]; + + int16 C, E, C1, E1; + + inverse((Vs * shared.SinAzs >> 15) + shared.VOffset, 7, C, E); + + E += shared.CentreZ_E; + C1 = C * shared.CentreZ_C >> 15; + + E1 = E + shared.SecAZS_E2; + + normalize(C1, C, E); + C = denormalizeAndClip(C, E); + + An = C * shared.CosAas >> 15; + Cn = C * shared.SinAas >> 15; + + normalize(C1 * shared.SecAZS_C2 >> 15, C, E1); + C = denormalizeAndClip(C, E1); + + Bn = C * -shared.SinAas >> 15; + Dn = C * shared.CosAas >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Calculate the projection over the ground of a selected point of screen +// It simply apply the projection matrix described in the "Raster" command +// to the vector (H,V) transposed, and add the result to the position of +// the centre of projection. +// The only special point to take into account is the directions on the screen: +// H is positive rightward, but V is positive downward; this is why +// the signs take that configuration + +void Dsp1::target(int16 *input, int16 *output) +{ + int16& H = input[0]; + int16& V = input[1]; + int16& X = output[0]; + int16& Y = output[1]; + + int16 C, E, C1, E1; + + inverse((V * shared.SinAzs >> 15) + shared.VOffset, 8, C, E); + + E += shared.CentreZ_E; + C1 = C * shared.CentreZ_C >> 15; + + E1 = E + shared.SecAZS_E1; + + H <<= 8; + normalize(C1, C, E); + C = denormalizeAndClip(C, E) * H >> 15; + + X = shared.CentreX + (C * shared.CosAas >> 15); + Y = shared.CentreY - (C * shared.SinAas >> 15); + + V <<= 8; + normalize(C1 * shared.SecAZS_C1 >> 15, C, E1); + C = denormalizeAndClip(C, E1) * V >> 15; + + X += C * -shared.SinAas >> 15; + Y += C * shared.CosAas >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Calculation of the projection over the screen (H,V) of an object (X,Y,Z) and his +// 'enlargement ratio' (M). The positive directions on the screen are as described +// in the targe command. M is scaled down by 2^-7, that is, M==0x0100 means ratio 1:1 + + void Dsp1::project(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& H = output[0]; + int16& V = output[1]; + int16& M = output[2]; + + int32 aux, aux4; + int16 E, E2, E3, E4, E5, refE, E6, E7; + int16 C2, C4, C6, C8, C9, C10, C11, C12, C16, C17, C18, C19, C20, C21, C22, C23, C24, C25, C26; + int16 Px, Py, Pz; + + E4=E3=E2=E=E5=0; + + normalizeDouble(int32(X)-shared.Gx, Px, E4); + normalizeDouble(int32(Y)-shared.Gy, Py, E); + normalizeDouble(int32(Z)-shared.Gz, Pz, E3); + Px>>=1; E4--; // to avoid overflows when calculating the scalar products + Py>>=1; E--; + Pz>>=1; E3--; + + refE = (E>15); + C8=- (Py*shared.Ny>>15); + C9=- (Pz*shared.Nz>>15); + C12=C11+C8+C9; // this cannot overflow! + + aux4=C12; // de-normalization with 32-bits arithmetic + refE = 16-refE; // refE can be up to 3 + if (refE>=0) + aux4 <<=(refE); + else + aux4 >>=-(refE); + if (aux4==-1) aux4 = 0; // why? + aux4>>=1; + + aux = static_cast(shared.Les) + aux4; // Les - the scalar product of P with the normal vector of the screen + normalizeDouble(aux, C10, E2); + E2 = 15-E2; + + inverse(C10, 0, C4, E4); + C2=C4*shared.C_Les>>15; // scale factor + + + // H + E7=0; + C16= (Px*shared.Hx>>15); + C20= (Py*shared.Hy>>15); + C17=C16+C20; // scalar product of P with the normalized horizontal vector of the screen... + + C18=C17*C2>>15; // ... multiplied by the scale factor + normalize(C18, C19, E7); + H=denormalizeAndClip(C19, shared.E_Les-E2+refE+E7); + + // V + E6=0; + C21 = Px*shared.Vx>>15; + C22 = Py*shared.Vy>>15; + C23 = Pz*shared.Vz>>15; + C24=C21+C22+C23; // scalar product of P with the normalized vertical vector of the screen... + + C26=C24*C2>>15; // ... multiplied by the scale factor + normalize(C26, C25, E6); + V=denormalizeAndClip(C25, shared.E_Les-E2+refE+E6); + + // M + normalize(C2, C6, E4); + M=denormalizeAndClip(C6, E4+shared.E_Les-E2-7); // M is the scale factor divided by 2^7 +} + +////////////////////////////////////////////////////////////////// + +// Calculate the sine of the input parameter +// this is done by linear interpolation between +// the points of a look-up table + +int16 Dsp1::sin(int16 Angle) +{ + if (Angle < 0) { + if (Angle == -32768) return 0; + return -sin(-Angle); + } + int32 S = SinTable[Angle >> 8] + (MulTable[Angle & 0xff] * SinTable[0x40 + (Angle >> 8)] >> 15); + if (S > 32767) S = 32767; + return (int16) S; +} + +////////////////////////////////////////////////////////////////// + +// Calculate the cosine of the input parameter. +// It's used the same method than in sin(int16) + +int16 Dsp1::cos(int16 Angle) +{ + if (Angle < 0) { + if (Angle == -32768) return -32768; + Angle = -Angle; + } + int32 S = SinTable[0x40 + (Angle >> 8)] - (MulTable[Angle & 0xff] * SinTable[Angle >> 8] >> 15); + if (S < -32768) S = -32767; + return (int16) S; +} + +////////////////////////////////////////////////////////////////// + +// Determines the inverse of a floating point decimal number +// iCoefficient*2^iExponent = 1/(Coefficient*2^Exponent), with the output +// normalized (iCoefficient represents a number whose absolute value is between 1/2 and 1) +// To invert 'Coefficient' a first initial guess is taken from a look-up table +// and, then, two iterations of the Newton method (applied to the function +// f(x)=1/(2*x)-Coefficient) are done. This results in a close approximation (iCoefficient) to a number 'y' +// that verify Coefficient*y=1/2. This is why you have to correct the exponent by one +// unit at the end. + +void Dsp1::inverse(int16 Coefficient, int16 Exponent, int16 &iCoefficient, int16 &iExponent) +{ + // Step One: Division by Zero + if (Coefficient == 0x0000) + { + iCoefficient = 0x7fff; + iExponent = 0x002f; + } + else + { + int16 Sign = 1; + + // Step Two: Remove Sign + if (Coefficient < 0) + { + if (Coefficient < -32767) Coefficient = -32767; + Coefficient = -Coefficient; + Sign = -1; + } + + // Step Three: Normalize + while (Coefficient < 0x4000) + { + Coefficient <<= 1; + Exponent--; + } + + // Step Four: Special Case + if (Coefficient == 0x4000) + if (Sign == 1) iCoefficient = 0x7fff; + else { + iCoefficient = -0x4000; + Exponent--; + } + else { + // Step Five: Initial Guess + int16 i = DataRom[((Coefficient - 0x4000) >> 7) + 0x0065]; + + // Step Six: Iterate Newton's Method + i = (i + (-i * (Coefficient * i >> 15) >> 15)) << 1; + i = (i + (-i * (Coefficient * i >> 15) >> 15)) << 1; + + iCoefficient = i * Sign; + } + + iExponent = 1 - Exponent; + } +} + +////////////////////////////////////////////////////////////////// + +int16 Dsp1::denormalizeAndClip(int16 C, int16 E) +{ + if (E > 0) { + if (C > 0) return 32767; else if (C < 0) return -32767; + } else { + if (E < 0) return C * DataRom[0x0031 + E] >> 15; + } + return C; +} + +////////////////////////////////////////////////////////////////// + +// Normalize the input number (m), understood as ranging from -1 to 1, +// to the form: Coefficient*2^Exponent, +// where the absolute value of Coefficient is >= 1/2 +// (Coefficient>=0x4000 or Coefficient <= (int16)0xc001) + +void Dsp1::normalize(int16 m, int16 &Coefficient, int16 &Exponent) +{ + int16 i = 0x4000; + int16 e = 0; + + if (m < 0) + while ((m & i) && i) + { + i >>= 1; + e++; + } + else + while (!(m & i) && i) + { + i >>= 1; + e++; + } + + if (e > 0) + Coefficient = m * DataRom[0x21 + e] << 1; + else + Coefficient = m; + + Exponent -= e; +} + +////////////////////////////////////////////////////////////////// + +// Same than 'normalize' but with an int32 input + +void Dsp1::normalizeDouble(int32 Product, int16 &Coefficient, int16 &Exponent) +{ + int16 n = Product & 0x7fff; + int16 m = Product >> 15; + int16 i = 0x4000; + int16 e = 0; + + if (m < 0) + while ((m & i) && i) + { + i >>= 1; + e++; + } + else + while (!(m & i) && i) + { + i >>= 1; + e++; + } + + if (e > 0) + { + Coefficient = m * DataRom[0x0021 + e] << 1; + + if (e < 15) + Coefficient += n * DataRom[0x0040 - e] >> 15; + else + { + i = 0x4000; + + if (m < 0) + while ((n & i) && i) + { + i >>= 1; + e++; + } + else + while (!(n & i) && i) + { + i >>= 1; + e++; + } + + if (e > 15) + Coefficient = n * DataRom[0x0012 + e] << 1; + else + Coefficient += n; + } + } + else + Coefficient = m; + + Exponent = e; +} + +////////////////////////////////////////////////////////////////// + +// Shift to the right + +int16 Dsp1::shiftR(int16 C, int16 E) +{ + return (C * DataRom[0x0031 + E] >> 15); +} + +////////////////////////////////////////////////////////////////// + +// this is, indeed, only part of the Data ROM +const int16 Dsp1::SinTable[256] = { + 0x0000, 0x0324, 0x0647, 0x096a, 0x0c8b, 0x0fab, 0x12c8, 0x15e2, + 0x18f8, 0x1c0b, 0x1f19, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11, + 0x30fb, 0x33de, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a, + 0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842, + 0x5a82, 0x5cb4, 0x5ed7, 0x60ec, 0x62f2, 0x64e8, 0x66cf, 0x68a6, + 0x6a6d, 0x6c24, 0x6dca, 0x6f5f, 0x70e2, 0x7255, 0x73b5, 0x7504, + 0x7641, 0x776c, 0x7884, 0x798a, 0x7a7d, 0x7b5d, 0x7c29, 0x7ce3, + 0x7d8a, 0x7e1d, 0x7e9d, 0x7f09, 0x7f62, 0x7fa7, 0x7fd8, 0x7ff6, + 0x7fff, 0x7ff6, 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d, + 0x7d8a, 0x7ce3, 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c, + 0x7641, 0x7504, 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24, + 0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4, + 0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4, + 0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de, + 0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b, + 0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324, + -0x0000, -0x0324, -0x0647, -0x096a, -0x0c8b, -0x0fab, -0x12c8, -0x15e2, + -0x18f8, -0x1c0b, -0x1f19, -0x2223, -0x2528, -0x2826, -0x2b1f, -0x2e11, + -0x30fb, -0x33de, -0x36ba, -0x398c, -0x3c56, -0x3f17, -0x41ce, -0x447a, + -0x471c, -0x49b4, -0x4c3f, -0x4ebf, -0x5133, -0x539b, -0x55f5, -0x5842, + -0x5a82, -0x5cb4, -0x5ed7, -0x60ec, -0x62f2, -0x64e8, -0x66cf, -0x68a6, + -0x6a6d, -0x6c24, -0x6dca, -0x6f5f, -0x70e2, -0x7255, -0x73b5, -0x7504, + -0x7641, -0x776c, -0x7884, -0x798a, -0x7a7d, -0x7b5d, -0x7c29, -0x7ce3, + -0x7d8a, -0x7e1d, -0x7e9d, -0x7f09, -0x7f62, -0x7fa7, -0x7fd8, -0x7ff6, + -0x7fff, -0x7ff6, -0x7fd8, -0x7fa7, -0x7f62, -0x7f09, -0x7e9d, -0x7e1d, + -0x7d8a, -0x7ce3, -0x7c29, -0x7b5d, -0x7a7d, -0x798a, -0x7884, -0x776c, + -0x7641, -0x7504, -0x73b5, -0x7255, -0x70e2, -0x6f5f, -0x6dca, -0x6c24, + -0x6a6d, -0x68a6, -0x66cf, -0x64e8, -0x62f2, -0x60ec, -0x5ed7, -0x5cb4, + -0x5a82, -0x5842, -0x55f5, -0x539b, -0x5133, -0x4ebf, -0x4c3f, -0x49b4, + -0x471c, -0x447a, -0x41ce, -0x3f17, -0x3c56, -0x398c, -0x36ba, -0x33de, + -0x30fb, -0x2e11, -0x2b1f, -0x2826, -0x2528, -0x2223, -0x1f19, -0x1c0b, + -0x18f8, -0x15e2, -0x12c8, -0x0fab, -0x0c8b, -0x096a, -0x0647, -0x0324}; + + ////////////////////////////////////////////////////////////////// + +// Optimised for Performance + const int16 Dsp1::MulTable[256] = { + 0x0000, 0x0003, 0x0006, 0x0009, 0x000c, 0x000f, 0x0012, 0x0015, + 0x0019, 0x001c, 0x001f, 0x0022, 0x0025, 0x0028, 0x002b, 0x002f, + 0x0032, 0x0035, 0x0038, 0x003b, 0x003e, 0x0041, 0x0045, 0x0048, + 0x004b, 0x004e, 0x0051, 0x0054, 0x0057, 0x005b, 0x005e, 0x0061, + 0x0064, 0x0067, 0x006a, 0x006d, 0x0071, 0x0074, 0x0077, 0x007a, + 0x007d, 0x0080, 0x0083, 0x0087, 0x008a, 0x008d, 0x0090, 0x0093, + 0x0096, 0x0099, 0x009d, 0x00a0, 0x00a3, 0x00a6, 0x00a9, 0x00ac, + 0x00af, 0x00b3, 0x00b6, 0x00b9, 0x00bc, 0x00bf, 0x00c2, 0x00c5, + 0x00c9, 0x00cc, 0x00cf, 0x00d2, 0x00d5, 0x00d8, 0x00db, 0x00df, + 0x00e2, 0x00e5, 0x00e8, 0x00eb, 0x00ee, 0x00f1, 0x00f5, 0x00f8, + 0x00fb, 0x00fe, 0x0101, 0x0104, 0x0107, 0x010b, 0x010e, 0x0111, + 0x0114, 0x0117, 0x011a, 0x011d, 0x0121, 0x0124, 0x0127, 0x012a, + 0x012d, 0x0130, 0x0133, 0x0137, 0x013a, 0x013d, 0x0140, 0x0143, + 0x0146, 0x0149, 0x014d, 0x0150, 0x0153, 0x0156, 0x0159, 0x015c, + 0x015f, 0x0163, 0x0166, 0x0169, 0x016c, 0x016f, 0x0172, 0x0175, + 0x0178, 0x017c, 0x017f, 0x0182, 0x0185, 0x0188, 0x018b, 0x018e, + 0x0192, 0x0195, 0x0198, 0x019b, 0x019e, 0x01a1, 0x01a4, 0x01a8, + 0x01ab, 0x01ae, 0x01b1, 0x01b4, 0x01b7, 0x01ba, 0x01be, 0x01c1, + 0x01c4, 0x01c7, 0x01ca, 0x01cd, 0x01d0, 0x01d4, 0x01d7, 0x01da, + 0x01dd, 0x01e0, 0x01e3, 0x01e6, 0x01ea, 0x01ed, 0x01f0, 0x01f3, + 0x01f6, 0x01f9, 0x01fc, 0x0200, 0x0203, 0x0206, 0x0209, 0x020c, + 0x020f, 0x0212, 0x0216, 0x0219, 0x021c, 0x021f, 0x0222, 0x0225, + 0x0228, 0x022c, 0x022f, 0x0232, 0x0235, 0x0238, 0x023b, 0x023e, + 0x0242, 0x0245, 0x0248, 0x024b, 0x024e, 0x0251, 0x0254, 0x0258, + 0x025b, 0x025e, 0x0261, 0x0264, 0x0267, 0x026a, 0x026e, 0x0271, + 0x0274, 0x0277, 0x027a, 0x027d, 0x0280, 0x0284, 0x0287, 0x028a, + 0x028d, 0x0290, 0x0293, 0x0296, 0x029a, 0x029d, 0x02a0, 0x02a3, + 0x02a6, 0x02a9, 0x02ac, 0x02b0, 0x02b3, 0x02b6, 0x02b9, 0x02bc, + 0x02bf, 0x02c2, 0x02c6, 0x02c9, 0x02cc, 0x02cf, 0x02d2, 0x02d5, + 0x02d8, 0x02db, 0x02df, 0x02e2, 0x02e5, 0x02e8, 0x02eb, 0x02ee, + 0x02f1, 0x02f5, 0x02f8, 0x02fb, 0x02fe, 0x0301, 0x0304, 0x0307, + 0x030b, 0x030e, 0x0311, 0x0314, 0x0317, 0x031a, 0x031d, 0x0321}; + +////////////////////////////////////////////////////////////////// + +// Data ROM, as logged from a DSP-1B with the 0x1f command; +// it contains the tables and constants used by the commands. +// The tables used are: two shift tables (0x022-0x031 and 0x031-0x040 -this last one +// with an error in 0x03c which has survived to all the DSP-1 revisions-); a inverse +// table (used as initial guess) at 0x065-0x0e4; a square root table (used also +// as initial guess) at 0x0e5-0x115; two sin and cos tables (used as nodes to construct +// a interpolation curve) at, respectively, 0x116-0x197 and 0x196-0x215. +// As a curiosity, in the positions 0x21c-0x31c it's contained a +// 257-points arccos table that, apparently, have been not used anywhere +// (maybe for the MaxAZS_Exp table?). + const uint16 Dsp1::DataRom[1024] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, + 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, + 0x4000, 0x7fff, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, + 0x0100, 0x0080, 0x0040, 0x0020, 0x0001, 0x0008, 0x0004, 0x0002, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x8000, 0xffe5, 0x0100, 0x7fff, 0x7f02, 0x7e08, + 0x7d12, 0x7c1f, 0x7b30, 0x7a45, 0x795d, 0x7878, 0x7797, 0x76ba, + 0x75df, 0x7507, 0x7433, 0x7361, 0x7293, 0x71c7, 0x70fe, 0x7038, + 0x6f75, 0x6eb4, 0x6df6, 0x6d3a, 0x6c81, 0x6bca, 0x6b16, 0x6a64, + 0x69b4, 0x6907, 0x685b, 0x67b2, 0x670b, 0x6666, 0x65c4, 0x6523, + 0x6484, 0x63e7, 0x634c, 0x62b3, 0x621c, 0x6186, 0x60f2, 0x6060, + 0x5fd0, 0x5f41, 0x5eb5, 0x5e29, 0x5d9f, 0x5d17, 0x5c91, 0x5c0c, + 0x5b88, 0x5b06, 0x5a85, 0x5a06, 0x5988, 0x590b, 0x5890, 0x5816, + 0x579d, 0x5726, 0x56b0, 0x563b, 0x55c8, 0x5555, 0x54e4, 0x5474, + 0x5405, 0x5398, 0x532b, 0x52bf, 0x5255, 0x51ec, 0x5183, 0x511c, + 0x50b6, 0x5050, 0x4fec, 0x4f89, 0x4f26, 0x4ec5, 0x4e64, 0x4e05, + 0x4da6, 0x4d48, 0x4cec, 0x4c90, 0x4c34, 0x4bda, 0x4b81, 0x4b28, + 0x4ad0, 0x4a79, 0x4a23, 0x49cd, 0x4979, 0x4925, 0x48d1, 0x487f, + 0x482d, 0x47dc, 0x478c, 0x473c, 0x46ed, 0x469f, 0x4651, 0x4604, + 0x45b8, 0x456c, 0x4521, 0x44d7, 0x448d, 0x4444, 0x43fc, 0x43b4, + 0x436d, 0x4326, 0x42e0, 0x429a, 0x4255, 0x4211, 0x41cd, 0x4189, + 0x4146, 0x4104, 0x40c2, 0x4081, 0x4040, 0x3fff, 0x41f7, 0x43e1, + 0x45bd, 0x478d, 0x4951, 0x4b0b, 0x4cbb, 0x4e61, 0x4fff, 0x5194, + 0x5322, 0x54a9, 0x5628, 0x57a2, 0x5914, 0x5a81, 0x5be9, 0x5d4a, + 0x5ea7, 0x5fff, 0x6152, 0x62a0, 0x63ea, 0x6530, 0x6672, 0x67b0, + 0x68ea, 0x6a20, 0x6b53, 0x6c83, 0x6daf, 0x6ed9, 0x6fff, 0x7122, + 0x7242, 0x735f, 0x747a, 0x7592, 0x76a7, 0x77ba, 0x78cb, 0x79d9, + 0x7ae5, 0x7bee, 0x7cf5, 0x7dfa, 0x7efe, 0x7fff, 0x0000, 0x0324, + 0x0647, 0x096a, 0x0c8b, 0x0fab, 0x12c8, 0x15e2, 0x18f8, 0x1c0b, + 0x1f19, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11, 0x30fb, 0x33de, + 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a, 0x471c, 0x49b4, + 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842, 0x5a82, 0x5cb4, + 0x5ed7, 0x60ec, 0x62f2, 0x64e8, 0x66cf, 0x68a6, 0x6a6d, 0x6c24, + 0x6dca, 0x6f5f, 0x70e2, 0x7255, 0x73b5, 0x7504, 0x7641, 0x776c, + 0x7884, 0x798a, 0x7a7d, 0x7b5d, 0x7c29, 0x7ce3, 0x7d8a, 0x7e1d, + 0x7e9d, 0x7f09, 0x7f62, 0x7fa7, 0x7fd8, 0x7ff6, 0x7fff, 0x7ff6, + 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d, 0x7d8a, 0x7ce3, + 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c, 0x7641, 0x7504, + 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24, 0x6a6d, 0x68a6, + 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4, 0x5a82, 0x5842, + 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4, 0x471c, 0x447a, + 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de, 0x30fb, 0x2e11, + 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b, 0x18f8, 0x15e2, + 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324, 0x7fff, 0x7ff6, + 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d, 0x7d8a, 0x7ce3, + 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c, 0x7641, 0x7504, + 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24, 0x6a6d, 0x68a6, + 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4, 0x5a82, 0x5842, + 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4, 0x471c, 0x447a, + 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de, 0x30fb, 0x2e11, + 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b, 0x18f8, 0x15e2, + 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324, 0x0000, 0xfcdc, + 0xf9b9, 0xf696, 0xf375, 0xf055, 0xed38, 0xea1e, 0xe708, 0xe3f5, + 0xe0e7, 0xdddd, 0xdad8, 0xd7da, 0xd4e1, 0xd1ef, 0xcf05, 0xcc22, + 0xc946, 0xc674, 0xc3aa, 0xc0e9, 0xbe32, 0xbb86, 0xb8e4, 0xb64c, + 0xb3c1, 0xb141, 0xaecd, 0xac65, 0xaa0b, 0xa7be, 0xa57e, 0xa34c, + 0xa129, 0x9f14, 0x9d0e, 0x9b18, 0x9931, 0x975a, 0x9593, 0x93dc, + 0x9236, 0x90a1, 0x8f1e, 0x8dab, 0x8c4b, 0x8afc, 0x89bf, 0x8894, + 0x877c, 0x8676, 0x8583, 0x84a3, 0x83d7, 0x831d, 0x8276, 0x81e3, + 0x8163, 0x80f7, 0x809e, 0x8059, 0x8028, 0x800a, 0x6488, 0x0080, + 0x03ff, 0x0116, 0x0002, 0x0080, 0x4000, 0x3fd7, 0x3faf, 0x3f86, + 0x3f5d, 0x3f34, 0x3f0c, 0x3ee3, 0x3eba, 0x3e91, 0x3e68, 0x3e40, + 0x3e17, 0x3dee, 0x3dc5, 0x3d9c, 0x3d74, 0x3d4b, 0x3d22, 0x3cf9, + 0x3cd0, 0x3ca7, 0x3c7f, 0x3c56, 0x3c2d, 0x3c04, 0x3bdb, 0x3bb2, + 0x3b89, 0x3b60, 0x3b37, 0x3b0e, 0x3ae5, 0x3abc, 0x3a93, 0x3a69, + 0x3a40, 0x3a17, 0x39ee, 0x39c5, 0x399c, 0x3972, 0x3949, 0x3920, + 0x38f6, 0x38cd, 0x38a4, 0x387a, 0x3851, 0x3827, 0x37fe, 0x37d4, + 0x37aa, 0x3781, 0x3757, 0x372d, 0x3704, 0x36da, 0x36b0, 0x3686, + 0x365c, 0x3632, 0x3609, 0x35df, 0x35b4, 0x358a, 0x3560, 0x3536, + 0x350c, 0x34e1, 0x34b7, 0x348d, 0x3462, 0x3438, 0x340d, 0x33e3, + 0x33b8, 0x338d, 0x3363, 0x3338, 0x330d, 0x32e2, 0x32b7, 0x328c, + 0x3261, 0x3236, 0x320b, 0x31df, 0x31b4, 0x3188, 0x315d, 0x3131, + 0x3106, 0x30da, 0x30ae, 0x3083, 0x3057, 0x302b, 0x2fff, 0x2fd2, + 0x2fa6, 0x2f7a, 0x2f4d, 0x2f21, 0x2ef4, 0x2ec8, 0x2e9b, 0x2e6e, + 0x2e41, 0x2e14, 0x2de7, 0x2dba, 0x2d8d, 0x2d60, 0x2d32, 0x2d05, + 0x2cd7, 0x2ca9, 0x2c7b, 0x2c4d, 0x2c1f, 0x2bf1, 0x2bc3, 0x2b94, + 0x2b66, 0x2b37, 0x2b09, 0x2ada, 0x2aab, 0x2a7c, 0x2a4c, 0x2a1d, + 0x29ed, 0x29be, 0x298e, 0x295e, 0x292e, 0x28fe, 0x28ce, 0x289d, + 0x286d, 0x283c, 0x280b, 0x27da, 0x27a9, 0x2777, 0x2746, 0x2714, + 0x26e2, 0x26b0, 0x267e, 0x264c, 0x2619, 0x25e7, 0x25b4, 0x2581, + 0x254d, 0x251a, 0x24e6, 0x24b2, 0x247e, 0x244a, 0x2415, 0x23e1, + 0x23ac, 0x2376, 0x2341, 0x230b, 0x22d6, 0x229f, 0x2269, 0x2232, + 0x21fc, 0x21c4, 0x218d, 0x2155, 0x211d, 0x20e5, 0x20ad, 0x2074, + 0x203b, 0x2001, 0x1fc7, 0x1f8d, 0x1f53, 0x1f18, 0x1edd, 0x1ea1, + 0x1e66, 0x1e29, 0x1ded, 0x1db0, 0x1d72, 0x1d35, 0x1cf6, 0x1cb8, + 0x1c79, 0x1c39, 0x1bf9, 0x1bb8, 0x1b77, 0x1b36, 0x1af4, 0x1ab1, + 0x1a6e, 0x1a2a, 0x19e6, 0x19a1, 0x195c, 0x1915, 0x18ce, 0x1887, + 0x183f, 0x17f5, 0x17ac, 0x1761, 0x1715, 0x16c9, 0x167c, 0x162e, + 0x15df, 0x158e, 0x153d, 0x14eb, 0x1497, 0x1442, 0x13ec, 0x1395, + 0x133c, 0x12e2, 0x1286, 0x1228, 0x11c9, 0x1167, 0x1104, 0x109e, + 0x1036, 0x0fcc, 0x0f5f, 0x0eef, 0x0e7b, 0x0e04, 0x0d89, 0x0d0a, + 0x0c86, 0x0bfd, 0x0b6d, 0x0ad6, 0x0a36, 0x098d, 0x08d7, 0x0811, + 0x0736, 0x063e, 0x0519, 0x039a, 0x0000, 0x7fff, 0x0100, 0x0080, + 0x021d, 0x00c8, 0x00ce, 0x0048, 0x0a26, 0x277a, 0x00ce, 0x6488, + 0x14ac, 0x0001, 0x00f9, 0x00fc, 0x00ff, 0x00fc, 0x00f9, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}; + +////////////////////////////////////////////////////////////////// + +#endif diff --git a/mednafen/snes/src/chip/dsp1/dsp1emu.hpp b/mednafen/snes/src/chip/dsp1/dsp1emu.hpp new file mode 100755 index 0000000..9ae313a --- /dev/null +++ b/mednafen/snes/src/chip/dsp1/dsp1emu.hpp @@ -0,0 +1,129 @@ +// DSP-1's emulation code +// +// Based on research by Overload, The Dumper, Neviksti and Andreas Naive +// Date: June 2006 + +#ifndef __DSP1EMUL_H +#define __DSP1EMUL_H + +#define DSP1_VERSION 0x0102 + +class Dsp1 +{ + public: + // The DSP-1 status register has 16 bits, but only + // the upper 8 bits can be accessed from an external device, so all these + // positions are referred to the upper byte (bits D8 to D15) + enum SrFlags {DRC=0x04, DRS=0x10, RQM=0x80}; + + // According to Overload's docs, these are the meanings of the flags: + // DRC: The Data Register Control (DRC) bit specifies the data transfer length to and from the host CPU. + // 0: Data transfer to and from the DSP-1 is 16 bits. + // 1: Data transfer to and from the DSP-1 is 8 bits. + // DRS: The Data Register Status (DRS) bit indicates the data transfer status in the case of transfering 16-bit data. + // 0: Data transfer has terminated. + // 1: Data transfer in progress. + // RQM: The Request for Master (RQM) indicates that the DSP1 is requesting host CPU for data read/write. + // 0: Internal Data Register Transfer. + // 1: External Data Register Transfer. + + Dsp1(); + uint8 getSr(); // return the status register's high byte + uint8 getDr(); + void setDr(uint8 iDr); + void reset(); + + void serialize(serializer&); + + private: + enum FsmMajorState {WAIT_COMMAND, READ_DATA, WRITE_DATA}; + enum MaxDataAccesses {MAX_READS=7, MAX_WRITES=1024}; + + struct Command { + void (Dsp1::*callback)(int16 *, int16 *); + unsigned int reads; + unsigned int writes; + }; + + static const Command mCommandTable[]; + static const int16 MaxAZS_Exp[16]; + static const int16 SinTable[]; + static const int16 MulTable[]; + static const uint16 DataRom[]; + + struct SharedData { // some RAM variables shared between commands + int16 MatrixA[3][3]; // attitude matrix A + int16 MatrixB[3][3]; + int16 MatrixC[3][3]; + int16 CentreX, CentreY, CentreZ; // center of projection + int16 CentreZ_C, CentreZ_E; + int16 VOffset; // vertical offset of the screen with regard to the centre of projection + int16 Les, C_Les, E_Les; + int16 SinAas, CosAas; + int16 SinAzs, CosAzs; + int16 SinAZS, CosAZS; + int16 SecAZS_C1, SecAZS_E1; + int16 SecAZS_C2, SecAZS_E2; + int16 Nx, Ny, Nz; // normal vector to the screen (norm 1, points toward the center of projection) + int16 Gx, Gy, Gz; // center of the screen (global coordinates) + int16 Hx, Hy; // horizontal vector of the screen (Hz=0, norm 1, points toward the right of the screen) + int16 Vx, Vy, Vz; // vertical vector of the screen (norm 1, points toward the top of the screen) + + } shared; + + uint8 mSr; // status register + int mSrLowByteAccess; + uint16 mDr; // "internal" representation of the data register + unsigned mFsmMajorState; // current major state of the FSM + uint8 mCommand; // current command processed by the FSM + uint8 mDataCounter; // #uint16 read/writes counter used by the FSM + int16 mReadBuffer[MAX_READS]; + int16 mWriteBuffer[MAX_WRITES]; + bool mFreeze; // need explanation? ;) + + void fsmStep(bool read, uint8 &data); // FSM logic + + // commands + void memoryTest(int16 *input, int16 *output); + void memoryDump(int16 *input, int16 *output); + void memorySize(int16 *input, int16 *output); + void multiply(int16* input, int16* output); + void multiply2(int16* input, int16* output); + void inverse(int16 *input, int16 *output); + void triangle(int16 *input, int16 *output); + void radius(int16 *input, int16 *output); + void range(int16 *input, int16 *output); + void range2(int16 *input, int16 *output); + void distance(int16 *input, int16 *output); + void rotate(int16 *input, int16 *output); + void polar(int16 *input, int16 *output); + void attitudeA(int16 *input, int16 *output); + void attitudeB(int16 *input, int16 *output); + void attitudeC(int16 *input, int16 *output); + void objectiveA(int16 *input, int16 *output); + void objectiveB(int16 *input, int16 *output); + void objectiveC(int16 *input, int16 *output); + void subjectiveA(int16 *input, int16 *output); + void subjectiveB(int16 *input, int16 *output); + void subjectiveC(int16 *input, int16 *output); + void scalarA(int16 *input, int16 *output); + void scalarB(int16 *input, int16 *output); + void scalarC(int16 *input, int16 *output); + void gyrate(int16 *input, int16 *output); + void parameter(int16 *input, int16 *output); + void raster(int16 *input, int16 *output); + void target(int16 *input, int16 *output); + void project(int16 *input, int16 *output); + + // auxiliar functions + int16 sin(int16 Angle); + int16 cos(int16 Angle); + void inverse(int16 Coefficient, int16 Exponent, int16 &iCoefficient, int16 &iExponent); + int16 denormalizeAndClip(int16 C, int16 E); + void normalize(int16 m, int16 &Coefficient, int16 &Exponent); + void normalizeDouble(int32 Product, int16 &Coefficient, int16 &Exponent); + int16 shiftR(int16 C, int16 E); +}; + +#endif + diff --git a/mednafen/snes/src/chip/dsp1/serialization.cpp b/mednafen/snes/src/chip/dsp1/serialization.cpp new file mode 100755 index 0000000..f7edb38 --- /dev/null +++ b/mednafen/snes/src/chip/dsp1/serialization.cpp @@ -0,0 +1,56 @@ +#ifdef DSP1_CPP + +void DSP1::serialize(serializer &s) { + dsp1.serialize(s); +} + +void Dsp1::serialize(serializer &s) { + for(unsigned i = 0; i < 3; i++) { + s.array(shared.MatrixA[i]); + s.array(shared.MatrixB[i]); + s.array(shared.MatrixC[i]); + } + + s.integer(shared.CentreX); + s.integer(shared.CentreY); + s.integer(shared.CentreZ); + s.integer(shared.CentreZ_C); + s.integer(shared.CentreZ_E); + s.integer(shared.VOffset); + s.integer(shared.Les); + s.integer(shared.C_Les); + s.integer(shared.E_Les); + s.integer(shared.SinAas); + s.integer(shared.CosAas); + s.integer(shared.SinAzs); + s.integer(shared.CosAzs); + s.integer(shared.SinAZS); + s.integer(shared.CosAZS); + s.integer(shared.SecAZS_C1); + s.integer(shared.SecAZS_E1); + s.integer(shared.SecAZS_C2); + s.integer(shared.SecAZS_E2); + s.integer(shared.Nx); + s.integer(shared.Ny); + s.integer(shared.Nz); + s.integer(shared.Gx); + s.integer(shared.Gy); + s.integer(shared.Gz); + s.integer(shared.Hx); + s.integer(shared.Hy); + s.integer(shared.Vx); + s.integer(shared.Vy); + s.integer(shared.Vz); + + s.integer(mSr); + s.integer(mSrLowByteAccess); + s.integer(mDr); + s.integer(mFsmMajorState); + s.integer(mCommand); + s.integer(mDataCounter); + s.array(mReadBuffer); + s.array(mWriteBuffer); + s.integer(mFreeze); +} + +#endif diff --git a/mednafen/snes/src/chip/dsp2/dsp2.cpp b/mednafen/snes/src/chip/dsp2/dsp2.cpp new file mode 100755 index 0000000..c44d03e --- /dev/null +++ b/mednafen/snes/src/chip/dsp2/dsp2.cpp @@ -0,0 +1,149 @@ +#include <../base.hpp> + +#define DSP2_CPP +namespace SNES { + +DSP2 dsp2; + +#include "serialization.cpp" +#include "opcodes.cpp" + +void DSP2::init() { +} + +void DSP2::enable() { + bus.map(Bus::MapDirect, 0x20, 0x3f, 0x6000, 0x6fff, *this); + bus.map(Bus::MapDirect, 0x20, 0x3f, 0x8000, 0xbfff, *this); + bus.map(Bus::MapDirect, 0xa0, 0xbf, 0x6000, 0x6fff, *this); + bus.map(Bus::MapDirect, 0xa0, 0xbf, 0x8000, 0xbfff, *this); +} + +void DSP2::power() { + reset(); +} + +void DSP2::reset() { + status.waiting_for_command = true; + status.in_count = 0; + status.in_index = 0; + status.out_count = 0; + status.out_index = 0; + + status.op05transparent = 0; + status.op05haslen = false; + status.op05len = 0; + status.op06haslen = false; + status.op06len = 0; + status.op09word1 = 0; + status.op09word2 = 0; + status.op0dhaslen = false; + status.op0doutlen = 0; + status.op0dinlen = 0; +} + +uint8 DSP2::read(unsigned addr) { + uint8 r = 0xff; + if(status.out_count) { + r = status.output[status.out_index++]; + status.out_index &= 511; + if(status.out_count == status.out_index) { + status.out_count = 0; + } + } + return r; +} + +void DSP2::write(unsigned addr, uint8 data) { + if(status.waiting_for_command) { + status.command = data; + status.in_index = 0; + status.waiting_for_command = false; + + switch(data) { + case 0x01: status.in_count = 32; break; + case 0x03: status.in_count = 1; break; + case 0x05: status.in_count = 1; break; + case 0x06: status.in_count = 1; break; + case 0x07: break; + case 0x08: break; + case 0x09: status.in_count = 4; break; + case 0x0d: status.in_count = 2; break; + case 0x0f: status.in_count = 0; break; + } + } else { + status.parameters[status.in_index++] = data; + status.in_index &= 511; + } + + if(status.in_count == status.in_index) { + status.waiting_for_command = true; + status.out_index = 0; + switch(status.command) { + case 0x01: { + status.out_count = 32; + op01(); + } break; + + case 0x03: { + op03(); + } break; + + case 0x05: { + if(status.op05haslen) { + status.op05haslen = false; + status.out_count = status.op05len; + op05(); + } else { + status.op05len = status.parameters[0]; + status.in_index = 0; + status.in_count = status.op05len * 2; + status.op05haslen = true; + if(data)status.waiting_for_command = false; + } + } break; + + case 0x06: { + if(status.op06haslen) { + status.op06haslen = false; + status.out_count = status.op06len; + op06(); + } else { + status.op06len = status.parameters[0]; + status.in_index = 0; + status.in_count = status.op06len; + status.op06haslen = true; + if(data)status.waiting_for_command = false; + } + } break; + + case 0x07: break; + case 0x08: break; + + case 0x09: { + op09(); + } break; + + case 0x0d: { + if(status.op0dhaslen) { + status.op0dhaslen = false; + status.out_count = status.op0doutlen; + op0d(); + } else { + status.op0dinlen = status.parameters[0]; + status.op0doutlen = status.parameters[1]; + status.in_index = 0; + status.in_count = (status.op0dinlen + 1) >> 1; + status.op0dhaslen = true; + if(data)status.waiting_for_command = false; + } + } break; + + case 0x0f: break; + } + } +} + +DSP2::DSP2() {} +DSP2::~DSP2() {} +}; + diff --git a/mednafen/snes/src/chip/dsp2/dsp2.hpp b/mednafen/snes/src/chip/dsp2/dsp2.hpp new file mode 100755 index 0000000..10a6a23 --- /dev/null +++ b/mednafen/snes/src/chip/dsp2/dsp2.hpp @@ -0,0 +1,45 @@ +class DSP2 : public Memory { +public: + struct { + bool waiting_for_command; + unsigned command; + unsigned in_count, in_index; + unsigned out_count, out_index; + + uint8 parameters[512]; + uint8 output[512]; + + uint8 op05transparent; + bool op05haslen; + int op05len; + bool op06haslen; + int op06len; + uint16 op09word1; + uint16 op09word2; + bool op0dhaslen; + int op0doutlen; + int op0dinlen; + } status; + + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + DSP2(); + ~DSP2(); + +protected: + void op01(); + void op03(); + void op05(); + void op06(); + void op09(); + void op0d(); +}; + +extern DSP2 dsp2; diff --git a/mednafen/snes/src/chip/dsp2/opcodes.cpp b/mednafen/snes/src/chip/dsp2/opcodes.cpp new file mode 100755 index 0000000..f015ac3 --- /dev/null +++ b/mednafen/snes/src/chip/dsp2/opcodes.cpp @@ -0,0 +1,177 @@ +#ifdef DSP2_CPP + +//convert bitmap to bitplane tile +void DSP2::op01() { +//op01 size is always 32 bytes input and output +//the hardware does strange things if you vary the size + +unsigned char c0, c1, c2, c3; +unsigned char *p1 = status.parameters; +unsigned char *p2a = status.output; +unsigned char *p2b = status.output + 16; //halfway + +//process 8 blocks of 4 bytes each + for(int j = 0; j < 8; j++) { + c0 = *p1++; + c1 = *p1++; + c2 = *p1++; + c3 = *p1++; + + *p2a++ = (c0 & 0x10) << 3 | + (c0 & 0x01) << 6 | + (c1 & 0x10) << 1 | + (c1 & 0x01) << 4 | + (c2 & 0x10) >> 1 | + (c2 & 0x01) << 2 | + (c3 & 0x10) >> 3 | + (c3 & 0x01); + + *p2a++ = (c0 & 0x20) << 2 | + (c0 & 0x02) << 5 | + (c1 & 0x20) | + (c1 & 0x02) << 3 | + (c2 & 0x20) >> 2 | + (c2 & 0x02) << 1 | + (c3 & 0x20) >> 4 | + (c3 & 0x02) >> 1; + + *p2b++ = (c0 & 0x40) << 1 | + (c0 & 0x04) << 4 | + (c1 & 0x40) >> 1 | + (c1 & 0x04) << 2 | + (c2 & 0x40) >> 3 | + (c2 & 0x04) | + (c3 & 0x40) >> 5 | + (c3 & 0x04) >> 2; + + *p2b++ = (c0 & 0x80) | + (c0 & 0x08) << 3 | + (c1 & 0x80) >> 2 | + (c1 & 0x08) << 1 | + (c2 & 0x80) >> 4 | + (c2 & 0x08) >> 1 | + (c3 & 0x80) >> 6 | + (c3 & 0x08) >> 3; + } +} + +//set transparent color +void DSP2::op03() { + status.op05transparent = status.parameters[0]; +} + +//replace bitmap using transparent color +void DSP2::op05() { +uint8 color; +// Overlay bitmap with transparency. +// Input: +// +// Bitmap 1: i[0] <=> i[size-1] +// Bitmap 2: i[size] <=> i[2*size-1] +// +// Output: +// +// Bitmap 3: o[0] <=> o[size-1] +// +// Processing: +// +// Process all 4-bit pixels (nibbles) in the bitmap +// +// if ( BM2_pixel == transparent_color ) +// pixelout = BM1_pixel +// else +// pixelout = BM2_pixel + +// The max size bitmap is limited to 255 because the size parameter is a byte +// I think size=0 is an error. The behavior of the chip on size=0 is to +// return the last value written to DR if you read DR on Op05 with +// size = 0. I don't think it's worth implementing this quirk unless it's +// proven necessary. + +unsigned char c1, c2; +unsigned char *p1 = status.parameters; +unsigned char *p2 = status.parameters + status.op05len; +unsigned char *p3 = status.output; + + color = status.op05transparent & 0x0f; + + for(int n = 0; n < status.op05len; n++) { + c1 = *p1++; + c2 = *p2++; + *p3++ = ( ((c2 >> 4) == color ) ? c1 & 0xf0 : c2 & 0xf0 ) | + ( ((c2 & 0x0f) == color ) ? c1 & 0x0f : c2 & 0x0f ); + } +} + +//reverse bitmap +void DSP2::op06() { +// Input: +// size +// bitmap + +int i, j; + for(i = 0, j = status.op06len - 1; i < status.op06len; i++, j--) { + status.output[j] = (status.parameters[i] << 4) | (status.parameters[i] >> 4); + } +} + +//multiply +void DSP2::op09() { + status.out_count = 4; + + status.op09word1 = status.parameters[0] | (status.parameters[1] << 8); + status.op09word2 = status.parameters[2] | (status.parameters[3] << 8); + +uint32 r; + r = status.op09word1 * status.op09word2; + status.output[0] = r; + status.output[1] = r >> 8; + status.output[2] = r >> 16; + status.output[3] = r >> 24; +} + +//scale bitmap +void DSP2::op0d() { +// Bit accurate hardware algorithm - uses fixed point math +// This should match the DSP2 Op0D output exactly +// I wouldn't recommend using this unless you're doing hardware debug. +// In some situations it has small visual artifacts that +// are not readily apparent on a TV screen but show up clearly +// on a monitor. Use Overload's scaling instead. +// This is for hardware verification testing. +// +// One note: the HW can do odd byte scaling but since we divide +// by two to get the count of bytes this won't work well for +// odd byte scaling (in any of the current algorithm implementations). +// So far I haven't seen Dungeon Master use it. +// If it does we can adjust the parameters and code to work with it + +uint32 multiplier; // Any size int >= 32-bits +uint32 pixloc; // match size of multiplier +int i, j; +uint8 pixelarray[512]; + if(status.op0dinlen <= status.op0doutlen) { + multiplier = 0x10000; // In our self defined fixed point 0x10000 == 1 + } else { + multiplier = (status.op0dinlen << 17) / ((status.op0doutlen << 1) + 1); + } + + pixloc = 0; + for(i = 0; i < status.op0doutlen * 2; i++) { + j = pixloc >> 16; + + if(j & 1) { + pixelarray[i] = (status.parameters[j >> 1] & 0x0f); + } else { + pixelarray[i] = (status.parameters[j >> 1] & 0xf0) >> 4; + } + + pixloc += multiplier; + } + + for(i = 0; i < status.op0doutlen; i++) { + status.output[i] = (pixelarray[i << 1] << 4) | pixelarray[(i << 1) + 1]; + } +} + +#endif diff --git a/mednafen/snes/src/chip/dsp2/serialization.cpp b/mednafen/snes/src/chip/dsp2/serialization.cpp new file mode 100755 index 0000000..d089b0f --- /dev/null +++ b/mednafen/snes/src/chip/dsp2/serialization.cpp @@ -0,0 +1,26 @@ +#ifdef DSP2_CPP + +void DSP2::serialize(serializer &s) { + s.integer(status.waiting_for_command); + s.integer(status.command); + s.integer(status.in_count); + s.integer(status.in_index); + s.integer(status.out_count); + s.integer(status.out_index); + + s.array(status.parameters); + s.array(status.output); + + s.integer(status.op05transparent); + s.integer(status.op05haslen); + s.integer(status.op05len); + s.integer(status.op06haslen); + s.integer(status.op06len); + s.integer(status.op09word1); + s.integer(status.op09word2); + s.integer(status.op0dhaslen); + s.integer(status.op0doutlen); + s.integer(status.op0dinlen); +} + +#endif diff --git a/mednafen/snes/src/chip/dsp3/dsp3.cpp b/mednafen/snes/src/chip/dsp3/dsp3.cpp new file mode 100755 index 0000000..b2e6b3d --- /dev/null +++ b/mednafen/snes/src/chip/dsp3/dsp3.cpp @@ -0,0 +1,42 @@ +#include <../base.hpp> + +#define DSP3_CPP +namespace SNES { + +DSP3 dsp3; + +namespace DSP3i { + #define bool8 uint8 + #include "dsp3emu.c" + #undef bool8 +}; + +void DSP3::init() { +} + +void DSP3::enable() { + bus.map(Bus::MapDirect, 0x20, 0x3f, 0x8000, 0xffff, *this); + bus.map(Bus::MapDirect, 0xa0, 0xbf, 0x8000, 0xffff, *this); +} + +void DSP3::power() { + reset(); +} + +void DSP3::reset() { + DSP3i::DSP3_Reset(); +} + +uint8 DSP3::read(unsigned addr) { + DSP3i::dsp3_address = addr & 0xffff; + DSP3i::DSP3GetByte(); + return DSP3i::dsp3_byte; +} + +void DSP3::write(unsigned addr, uint8 data) { + DSP3i::dsp3_address = addr & 0xffff; + DSP3i::dsp3_byte = data; + DSP3i::DSP3SetByte(); +} + +}; diff --git a/mednafen/snes/src/chip/dsp3/dsp3.hpp b/mednafen/snes/src/chip/dsp3/dsp3.hpp new file mode 100755 index 0000000..b3353e3 --- /dev/null +++ b/mednafen/snes/src/chip/dsp3/dsp3.hpp @@ -0,0 +1,12 @@ +class DSP3 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); +}; + +extern DSP3 dsp3; diff --git a/mednafen/snes/src/chip/dsp3/dsp3emu.c b/mednafen/snes/src/chip/dsp3/dsp3emu.c new file mode 100755 index 0000000..9e65677 --- /dev/null +++ b/mednafen/snes/src/chip/dsp3/dsp3emu.c @@ -0,0 +1,1146 @@ +#ifdef DSP3_CPP + +//DSP-3 emulator code +//Copyright (c) 2003-2006 John Weidman, Kris Bleakley, Lancer, z80 gaiden + +uint16 DSP3_DataROM[1024] = { + 0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100, + 0x0080, 0x0040, 0x0020, 0x0010, 0x0008, 0x0004, 0x0002, 0x0001, + 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, + 0x0000, 0x000f, 0x0400, 0x0200, 0x0140, 0x0400, 0x0200, 0x0040, + 0x007d, 0x007e, 0x007e, 0x007b, 0x007c, 0x007d, 0x007b, 0x007c, + 0x0002, 0x0020, 0x0030, 0x0000, 0x000d, 0x0019, 0x0026, 0x0032, + 0x003e, 0x004a, 0x0056, 0x0062, 0x006d, 0x0079, 0x0084, 0x008e, + 0x0098, 0x00a2, 0x00ac, 0x00b5, 0x00be, 0x00c6, 0x00ce, 0x00d5, + 0x00dc, 0x00e2, 0x00e7, 0x00ec, 0x00f1, 0x00f5, 0x00f8, 0x00fb, + 0x00fd, 0x00ff, 0x0100, 0x0100, 0x0100, 0x00ff, 0x00fd, 0x00fb, + 0x00f8, 0x00f5, 0x00f1, 0x00ed, 0x00e7, 0x00e2, 0x00dc, 0x00d5, + 0x00ce, 0x00c6, 0x00be, 0x00b5, 0x00ac, 0x00a2, 0x0099, 0x008e, + 0x0084, 0x0079, 0x006e, 0x0062, 0x0056, 0x004a, 0x003e, 0x0032, + 0x0026, 0x0019, 0x000d, 0x0000, 0xfff3, 0xffe7, 0xffdb, 0xffce, + 0xffc2, 0xffb6, 0xffaa, 0xff9e, 0xff93, 0xff87, 0xff7d, 0xff72, + 0xff68, 0xff5e, 0xff54, 0xff4b, 0xff42, 0xff3a, 0xff32, 0xff2b, + 0xff25, 0xff1e, 0xff19, 0xff14, 0xff0f, 0xff0b, 0xff08, 0xff05, + 0xff03, 0xff01, 0xff00, 0xff00, 0xff00, 0xff01, 0xff03, 0xff05, + 0xff08, 0xff0b, 0xff0f, 0xff13, 0xff18, 0xff1e, 0xff24, 0xff2b, + 0xff32, 0xff3a, 0xff42, 0xff4b, 0xff54, 0xff5d, 0xff67, 0xff72, + 0xff7c, 0xff87, 0xff92, 0xff9e, 0xffa9, 0xffb5, 0xffc2, 0xffce, + 0xffda, 0xffe7, 0xfff3, 0x002b, 0x007f, 0x0020, 0x00ff, 0xff00, + 0xffbe, 0x0000, 0x0044, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffc1, 0x0001, 0x0002, 0x0045, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffc5, 0x0003, 0x0004, 0x0005, 0x0047, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffca, 0x0006, 0x0007, 0x0008, + 0x0009, 0x004a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffd0, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x004e, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffd7, 0x000f, 0x0010, 0x0011, + 0x0012, 0x0013, 0x0014, 0x0053, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffdf, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, + 0x0059, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffe8, 0x001c, 0x001d, 0x001e, + 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0060, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xfff2, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, + 0x002b, 0x002c, 0x0068, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xfffd, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0071, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffc7, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, + 0x003e, 0x003f, 0x0040, 0x0041, 0x007b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffd4, 0x0000, 0x0001, 0x0002, + 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, + 0x000b, 0x0044, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffe2, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, + 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0050, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xfff1, 0x0019, 0x001a, 0x001b, + 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0x005d, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffcb, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, + 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, + 0x006b, 0x0000, 0x0000, 0x0000, 0xffdc, 0x0000, 0x0001, 0x0002, + 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, + 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0044, 0x0000, 0x0000, + 0xffee, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, + 0x001f, 0x0020, 0x0054, 0x0000, 0xffee, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, + 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0065, + 0xffbe, 0x0000, 0xfeac, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffc1, 0x0001, 0x0002, 0xfead, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffc5, 0x0003, 0x0004, 0x0005, 0xfeaf, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffca, 0x0006, 0x0007, 0x0008, + 0x0009, 0xfeb2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffd0, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0xfeb6, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffd7, 0x000f, 0x0010, 0x0011, + 0x0012, 0x0013, 0x0014, 0xfebb, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffdf, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, + 0xfec1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffe8, 0x001c, 0x001d, 0x001e, + 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0xfec8, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xfff2, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, + 0x002b, 0x002c, 0xfed0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xfffd, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0xfed9, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffc7, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, + 0x003e, 0x003f, 0x0040, 0x0041, 0xfee3, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffd4, 0x0000, 0x0001, 0x0002, + 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, + 0x000b, 0xfeac, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffe2, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, + 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0xfeb8, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xfff1, 0x0019, 0x001a, 0x001b, + 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0xfec5, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffcb, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, + 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, + 0xfed3, 0x0000, 0x0000, 0x0000, 0xffdc, 0x0000, 0x0001, 0x0002, + 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, + 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0xfeac, 0x0000, 0x0000, + 0xffee, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, + 0x001f, 0x0020, 0xfebc, 0x0000, 0xffee, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, + 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0xfecd, + 0x0154, 0x0218, 0x0110, 0x00b0, 0x00cc, 0x00b0, 0x0088, 0x00b0, + 0x0044, 0x00b0, 0x0000, 0x00b0, 0x00fe, 0xff07, 0x0002, 0x00ff, + 0x00f8, 0x0007, 0x00fe, 0x00ee, 0x07ff, 0x0200, 0x00ef, 0xf800, + 0x0700, 0x00ee, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, + 0xffff, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, + 0xffff, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xffff, + 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0044, 0x0088, 0x00cc, + 0x0110, 0x0154, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff +}; + +void (*SetDSP3)(); +void DSP3_Command(); + +uint16 DSP3_DR; +uint16 DSP3_SR; +uint16 DSP3_MemoryIndex; + +void DSP3_Reset() +{ + DSP3_DR = 0x0080; + DSP3_SR = 0x0084; + SetDSP3 = &DSP3_Command; +} + +void DSP3_MemorySize() +{ + DSP3_DR = 0x0300; + SetDSP3 = &DSP3_Reset; +} + +void DSP3_TestMemory() +{ + DSP3_DR = 0x0000; + SetDSP3 = &DSP3_Reset; +} + +void DSP3_DumpDataROM() +{ + DSP3_DR = DSP3_DataROM[DSP3_MemoryIndex++]; + if (DSP3_MemoryIndex == 1024) + SetDSP3 = &DSP3_Reset; +} + +void DSP3_MemoryDump() +{ + DSP3_MemoryIndex = 0; + SetDSP3 = &DSP3_DumpDataROM; + DSP3_DumpDataROM(); +} + +int16 DSP3_WinLo; +int16 DSP3_WinHi; + +void DSP3_OP06() +{ + DSP3_WinLo = (uint8)(DSP3_DR); + DSP3_WinHi = (uint8)(DSP3_DR >> 8); + DSP3_Reset(); +} + +void DSP3_OP03() +{ + int16 Lo = (uint8)(DSP3_DR); + int16 Hi = (uint8)(DSP3_DR >> 8); + int16 Ofs = (DSP3_WinLo * Hi << 1) + (Lo << 1); + DSP3_DR = Ofs >> 1; + SetDSP3 = &DSP3_Reset; +} + +int16 DSP3_AddLo; +int16 DSP3_AddHi; + +void DSP3_OP07_B() +{ + int16 Ofs = (DSP3_WinLo * DSP3_AddHi << 1) + (DSP3_AddLo << 1); + DSP3_DR = Ofs >> 1; + SetDSP3 = &DSP3_Reset; +} + +void DSP3_OP07_A() +{ + int16 Lo = (uint8)(DSP3_DR); + int16 Hi = (uint8)(DSP3_DR >> 8); + + if (Lo & 1) Hi += (DSP3_AddLo & 1); + + DSP3_AddLo += Lo; + DSP3_AddHi += Hi; + + if (DSP3_AddLo < 0) + DSP3_AddLo += DSP3_WinLo; + else + if (DSP3_AddLo >= DSP3_WinLo) + DSP3_AddLo -= DSP3_WinLo; + + if (DSP3_AddHi < 0) + DSP3_AddHi += DSP3_WinHi; + else + if (DSP3_AddHi >= DSP3_WinHi) + DSP3_AddHi -= DSP3_WinHi; + + DSP3_DR = DSP3_AddLo | (DSP3_AddHi << 8) | ((DSP3_AddHi >> 8) & 0xff); + SetDSP3 = &DSP3_OP07_B; +} + +void DSP3_OP07() +{ + uint32 dataOfs = ((DSP3_DR << 1) + 0x03b2) & 0x03ff; + + DSP3_AddHi = DSP3_DataROM[dataOfs]; + DSP3_AddLo = DSP3_DataROM[dataOfs + 1]; + + SetDSP3 = &DSP3_OP07_A; + DSP3_SR = 0x0080; +} + +uint16 DSP3_Codewords; +uint16 DSP3_Outwords; +uint16 DSP3_Symbol; +uint16 DSP3_BitCount; +uint16 DSP3_Index; +uint16 DSP3_Codes[512]; +uint16 DSP3_BitsLeft; +uint16 DSP3_ReqBits; +uint16 DSP3_ReqData; +uint16 DSP3_BitCommand; +uint8 DSP3_BaseLength; +uint16 DSP3_BaseCodes; +uint16 DSP3_BaseCode; +uint8 DSP3_CodeLengths[8]; +uint16 DSP3_CodeOffsets[8]; +uint16 DSP3_LZCode; +uint8 DSP3_LZLength; + +uint16 DSP3_X; +uint16 DSP3_Y; + +void DSP3_Coordinate() +{ + DSP3_Index++; + + switch (DSP3_Index) + { + case 3: + { + if (DSP3_DR == 0xffff) + DSP3_Reset(); + break; + } + case 4: + { + DSP3_X = DSP3_DR; + break; + } + case 5: + { + DSP3_Y = DSP3_DR; + DSP3_DR = 1; + break; + } + case 6: + { + DSP3_DR = DSP3_X; + break; + } + case 7: + { + DSP3_DR = DSP3_Y; + DSP3_Index = 0; + break; + } + } +} + +uint8 DSP3_Bitmap[8]; +uint8 DSP3_Bitplane[8]; +uint16 DSP3_BMIndex; +uint16 DSP3_BPIndex; +uint16 DSP3_Count; + +void DSP3_Convert_A() +{ + if (DSP3_BMIndex < 8) + { + DSP3_Bitmap[DSP3_BMIndex++] = (uint8) (DSP3_DR); + DSP3_Bitmap[DSP3_BMIndex++] = (uint8) (DSP3_DR >> 8); + + if (DSP3_BMIndex == 8) + { + short i, j; + for (i=0; i < 8; i++) + for (j=0; j < 8; j++) + { + DSP3_Bitplane[j] <<= 1; + DSP3_Bitplane[j] |= (DSP3_Bitmap[i] >> j) & 1; + } + + DSP3_BPIndex = 0; + DSP3_Count--; + } + } + + if (DSP3_BMIndex == 8) + { + if (DSP3_BPIndex == 8) + { + if (!DSP3_Count) DSP3_Reset(); + DSP3_BMIndex = 0; + } + else + { + DSP3_DR = DSP3_Bitplane[DSP3_BPIndex++]; + DSP3_DR |= DSP3_Bitplane[DSP3_BPIndex++] << 8; + } + } +} + +void DSP3_Convert() +{ + DSP3_Count = DSP3_DR; + DSP3_BMIndex = 0; + SetDSP3 = &DSP3_Convert_A; +} + +bool DSP3_GetBits(uint8 Count) +{ + if (!DSP3_BitsLeft) + { + DSP3_BitsLeft = Count; + DSP3_ReqBits = 0; + } + + do { + if (!DSP3_BitCount) + { + DSP3_SR = 0xC0; + return false; + } + + DSP3_ReqBits <<= 1; + if (DSP3_ReqData & 0x8000) DSP3_ReqBits++; + DSP3_ReqData <<= 1; + + DSP3_BitCount--; + DSP3_BitsLeft--; + + } while (DSP3_BitsLeft); + + return true; +} + +void DSP3_Decode_Data() +{ + if (!DSP3_BitCount) + { + if (DSP3_SR & 0x40) + { + DSP3_ReqData = DSP3_DR; + DSP3_BitCount += 16; + } + else + { + DSP3_SR = 0xC0; + return; + } + } + + if (DSP3_LZCode == 1) + { + if (!DSP3_GetBits(1)) + return; + + if (DSP3_ReqBits) + DSP3_LZLength = 12; + else + DSP3_LZLength = 8; + + DSP3_LZCode++; + } + + if (DSP3_LZCode == 2) + { + if (!DSP3_GetBits(DSP3_LZLength)) + return; + + DSP3_LZCode = 0; + DSP3_Outwords--; + if (!DSP3_Outwords) SetDSP3 = &DSP3_Reset; + + DSP3_SR = 0x80; + DSP3_DR = DSP3_ReqBits; + return; + } + + if (DSP3_BaseCode == 0xffff) + { + if (!DSP3_GetBits(DSP3_BaseLength)) + return; + + DSP3_BaseCode = DSP3_ReqBits; + } + + if (!DSP3_GetBits(DSP3_CodeLengths[DSP3_BaseCode])) + return; + + DSP3_Symbol = DSP3_Codes[DSP3_CodeOffsets[DSP3_BaseCode] + DSP3_ReqBits]; + DSP3_BaseCode = 0xffff; + + if (DSP3_Symbol & 0xff00) + { + DSP3_Symbol += 0x7f02; + DSP3_LZCode++; + } + else + { + DSP3_Outwords--; + if (!DSP3_Outwords) + SetDSP3 = &DSP3_Reset; + } + + DSP3_SR = 0x80; + DSP3_DR = DSP3_Symbol; +} + +void DSP3_Decode_Tree() +{ + if (!DSP3_BitCount) + { + DSP3_ReqData = DSP3_DR; + DSP3_BitCount += 16; + } + + if (!DSP3_BaseCodes) + { + DSP3_GetBits(1); + if (DSP3_ReqBits) + { + DSP3_BaseLength = 3; + DSP3_BaseCodes = 8; + } + else + { + DSP3_BaseLength = 2; + DSP3_BaseCodes = 4; + } + } + + while (DSP3_BaseCodes) + { + if (!DSP3_GetBits(3)) + return; + + DSP3_ReqBits++; + + DSP3_CodeLengths[DSP3_Index] = (uint8) DSP3_ReqBits; + DSP3_CodeOffsets[DSP3_Index] = DSP3_Symbol; + DSP3_Index++; + + DSP3_Symbol += 1 << DSP3_ReqBits; + DSP3_BaseCodes--; + } + + DSP3_BaseCode = 0xffff; + DSP3_LZCode = 0; + + SetDSP3 = &DSP3_Decode_Data; + if (DSP3_BitCount) DSP3_Decode_Data(); +} + +void DSP3_Decode_Symbols() +{ + DSP3_ReqData = DSP3_DR; + DSP3_BitCount += 16; + + do { + + if (DSP3_BitCommand == 0xffff) + { + if (!DSP3_GetBits(2)) return; + DSP3_BitCommand = DSP3_ReqBits; + } + + switch (DSP3_BitCommand) + { + case 0: + { + if (!DSP3_GetBits(9)) return; + DSP3_Symbol = DSP3_ReqBits; + break; + } + case 1: + { + DSP3_Symbol++; + break; + } + case 2: + { + if (!DSP3_GetBits(1)) return; + DSP3_Symbol += 2 + DSP3_ReqBits; + break; + } + case 3: + { + if (!DSP3_GetBits(4)) return; + DSP3_Symbol += 4 + DSP3_ReqBits; + break; + } + } + + DSP3_BitCommand = 0xffff; + + DSP3_Codes[DSP3_Index++] = DSP3_Symbol; + DSP3_Codewords--; + + } while (DSP3_Codewords); + + DSP3_Index = 0; + DSP3_Symbol = 0; + DSP3_BaseCodes = 0; + + SetDSP3 = &DSP3_Decode_Tree; + if (DSP3_BitCount) DSP3_Decode_Tree(); +} + +void DSP3_Decode_A() +{ + DSP3_Outwords = DSP3_DR; + SetDSP3 = &DSP3_Decode_Symbols; + DSP3_BitCount = 0; + DSP3_BitsLeft = 0; + DSP3_Symbol = 0; + DSP3_Index = 0; + DSP3_BitCommand = 0xffff; + DSP3_SR = 0xC0; +} + +void DSP3_Decode() +{ + DSP3_Codewords = DSP3_DR; + SetDSP3 = &DSP3_Decode_A; +} + + +// Opcodes 1E/3E bit-perfect to 'dsp3-intro' log +// src: adapted from SD Gundam X/G-Next + +int16 op3e_x; +int16 op3e_y; + +int16 op1e_terrain[0x2000]; +int16 op1e_cost[0x2000]; +int16 op1e_weight[0x2000]; + +int16 op1e_cell; +int16 op1e_turn; +int16 op1e_search; + +int16 op1e_x; +int16 op1e_y; + +int16 op1e_min_radius; +int16 op1e_max_radius; + +int16 op1e_max_search_radius; +int16 op1e_max_path_radius; + +int16 op1e_lcv_radius; +int16 op1e_lcv_steps; +int16 op1e_lcv_turns; + +void DSP3_OP3E() +{ + op3e_x = (uint8)(DSP3_DR & 0x00ff); + op3e_y = (uint8)((DSP3_DR & 0xff00)>>8); + + DSP3_OP03(); + + op1e_terrain[ DSP3_DR ] = 0x00; + op1e_cost[ DSP3_DR ] = 0xff; + op1e_weight[ DSP3_DR ] = 0; + + op1e_max_search_radius = 0; + op1e_max_path_radius = 0; +} + +void DSP3_OP1E_A(); +void DSP3_OP1E_A1(); +void DSP3_OP1E_A2(); +void DSP3_OP1E_A3(); + +void DSP3_OP1E_B(); +void DSP3_OP1E_B1(); +void DSP3_OP1E_B2(); + +void DSP3_OP1E_C(); +void DSP3_OP1E_C1(); +void DSP3_OP1E_C2(); + +void DSP3_OP1E_D( int16, int16 *, int16 * ); +void DSP3_OP1E_D1( int16 move, int16 *lo, int16 *hi ); + +void DSP3_OP1E() +{ + int lcv; + + op1e_min_radius = (uint8)(DSP3_DR & 0x00ff); + op1e_max_radius = (uint8)((DSP3_DR & 0xff00)>>8); + + if( op1e_min_radius == 0 ) + op1e_min_radius++; + + if( op1e_max_search_radius >= op1e_min_radius ) + op1e_min_radius = op1e_max_search_radius+1; + + if( op1e_max_radius > op1e_max_search_radius ) + op1e_max_search_radius = op1e_max_radius; + + op1e_lcv_radius = op1e_min_radius; + op1e_lcv_steps = op1e_min_radius; + + op1e_lcv_turns = 6; + op1e_turn = 0; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_min_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + + DSP3_OP1E_A(); +} + +void DSP3_OP1E_A() +{ + int lcv; + + if( op1e_lcv_steps == 0 ) { + op1e_lcv_radius++; + + op1e_lcv_steps = op1e_lcv_radius; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_lcv_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + } + + if( op1e_lcv_radius > op1e_max_radius ) { + op1e_turn++; + op1e_lcv_turns--; + + op1e_lcv_radius = op1e_min_radius; + op1e_lcv_steps = op1e_min_radius; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_min_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + } + + if( op1e_lcv_turns == 0 ) { + DSP3_DR = 0xffff; + DSP3_SR = 0x0080; + SetDSP3 = &DSP3_OP1E_B; + return; + } + + DSP3_DR = (uint8)(op1e_x) | ((uint8)(op1e_y)<<8); + DSP3_OP03(); + + op1e_cell = DSP3_DR; + + DSP3_SR = 0x0080; + SetDSP3 = &DSP3_OP1E_A1; +} + +void DSP3_OP1E_A1() +{ + DSP3_SR = 0x0084; + SetDSP3 = &DSP3_OP1E_A2; +} + +void DSP3_OP1E_A2() +{ + op1e_terrain[ op1e_cell ] = (uint8)(DSP3_DR & 0x00ff); + + DSP3_SR = 0x0084; + SetDSP3 = &DSP3_OP1E_A3; +} + +void DSP3_OP1E_A3() +{ + op1e_cost[ op1e_cell ] = (uint8)(DSP3_DR & 0x00ff); + + if( op1e_lcv_radius == 1 ) { + if( op1e_terrain[ op1e_cell ] & 1 ) { + op1e_weight[ op1e_cell ] = 0xff; + } else { + op1e_weight[ op1e_cell ] = op1e_cost[ op1e_cell ]; + } + } + else { + op1e_weight[ op1e_cell ] = 0xff; + } + + DSP3_OP1E_D( (int16)(op1e_turn+2), &op1e_x, &op1e_y ); + op1e_lcv_steps--; + + DSP3_SR = 0x0080; + DSP3_OP1E_A(); +} + + +void DSP3_OP1E_B() +{ + op1e_x = op3e_x; + op1e_y = op3e_y; + op1e_lcv_radius = 1; + + op1e_search = 0; + + DSP3_OP1E_B1(); + + SetDSP3 = &DSP3_OP1E_C; +} + + +void DSP3_OP1E_B1() +{ + while( op1e_lcv_radius < op1e_max_radius ) { + op1e_y--; + + op1e_lcv_turns = 6; + op1e_turn = 5; + + while( op1e_lcv_turns ) { + op1e_lcv_steps = op1e_lcv_radius; + + while( op1e_lcv_steps ) { + DSP3_OP1E_D1( op1e_turn, &op1e_x, &op1e_y ); + + if( 0 <= op1e_y && op1e_y < DSP3_WinHi && + 0 <= op1e_x && op1e_x < DSP3_WinLo ) { + DSP3_DR = (uint8)(op1e_x) | ((uint8)(op1e_y)<<8); + DSP3_OP03(); + + op1e_cell = DSP3_DR; + if( op1e_cost[ op1e_cell ] < 0x80 && + op1e_terrain[ op1e_cell ] < 0x40 ) { + DSP3_OP1E_B2(); + } // end cell perimeter + } + + op1e_lcv_steps--; + } // end search line + + op1e_turn--; + if( op1e_turn == 0 ) op1e_turn = 6; + + op1e_lcv_turns--; + } // end circle search + + op1e_lcv_radius++; + } // end radius search +} + + +void DSP3_OP1E_B2() +{ + int16 cell; + int16 path; + int16 x,y; + int16 lcv_turns; + + path = 0xff; + lcv_turns = 6; + + while( lcv_turns ) { + x = op1e_x; + y = op1e_y; + + DSP3_OP1E_D1( lcv_turns, &x, &y ); + + DSP3_DR = (uint8)(x) | ((uint8)(y)<<8); + DSP3_OP03(); + + cell = DSP3_DR; + + if( 0 <= y && y < DSP3_WinHi && + 0 <= x && x < DSP3_WinLo ) { + + if( op1e_terrain[ cell ] < 0x80 || op1e_weight[ cell ] == 0 ) { + if( op1e_weight[ cell ] < path ) { + path = op1e_weight[ cell ]; + } + } + } // end step travel + + lcv_turns--; + } // end while turns + + if( path != 0xff ) { + op1e_weight[ op1e_cell ] = path + op1e_cost[ op1e_cell ]; + } +} + + +void DSP3_OP1E_C() +{ + int lcv; + + op1e_min_radius = (uint8)(DSP3_DR & 0x00ff); + op1e_max_radius = (uint8)((DSP3_DR & 0xff00)>>8); + + if( op1e_min_radius == 0 ) + op1e_min_radius++; + + if( op1e_max_path_radius >= op1e_min_radius ) + op1e_min_radius = op1e_max_path_radius+1; + + if( op1e_max_radius > op1e_max_path_radius ) + op1e_max_path_radius = op1e_max_radius; + + op1e_lcv_radius = op1e_min_radius; + op1e_lcv_steps = op1e_min_radius; + + op1e_lcv_turns = 6; + op1e_turn = 0; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_min_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + + DSP3_OP1E_C1(); +} + + +void DSP3_OP1E_C1() +{ + int lcv; + + if( op1e_lcv_steps == 0 ) { + op1e_lcv_radius++; + + op1e_lcv_steps = op1e_lcv_radius; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_lcv_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + } + + if( op1e_lcv_radius > op1e_max_radius ) { + op1e_turn++; + op1e_lcv_turns--; + + op1e_lcv_radius = op1e_min_radius; + op1e_lcv_steps = op1e_min_radius; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_min_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + } + + if( op1e_lcv_turns == 0 ) { + DSP3_DR = 0xffff; + DSP3_SR = 0x0080; + SetDSP3 = &DSP3_Reset; + return; + } + + DSP3_DR = (uint8)(op1e_x) | ((uint8)(op1e_y)<<8); + DSP3_OP03(); + + op1e_cell = DSP3_DR; + + DSP3_SR = 0x0080; + SetDSP3 = &DSP3_OP1E_C2; +} + + +void DSP3_OP1E_C2() +{ + DSP3_DR = op1e_weight[ op1e_cell ]; + + DSP3_OP1E_D( (int16)(op1e_turn+2), &op1e_x, &op1e_y ); + op1e_lcv_steps--; + + DSP3_SR = 0x0084; + SetDSP3 = &DSP3_OP1E_C1; +} + + +void DSP3_OP1E_D( int16 move, int16 *lo, int16 *hi ) +{ + uint32 dataOfs = ((move << 1) + 0x03b2) & 0x03ff; + int16 Lo; + int16 Hi; + + DSP3_AddHi = DSP3_DataROM[dataOfs]; + DSP3_AddLo = DSP3_DataROM[dataOfs + 1]; + + Lo = (uint8)(*lo); + Hi = (uint8)(*hi); + + if (Lo & 1) Hi += (DSP3_AddLo & 1); + + DSP3_AddLo += Lo; + DSP3_AddHi += Hi; + + if (DSP3_AddLo < 0) + DSP3_AddLo += DSP3_WinLo; + else + if (DSP3_AddLo >= DSP3_WinLo) + DSP3_AddLo -= DSP3_WinLo; + + if (DSP3_AddHi < 0) + DSP3_AddHi += DSP3_WinHi; + else + if (DSP3_AddHi >= DSP3_WinHi) + DSP3_AddHi -= DSP3_WinHi; + + *lo = DSP3_AddLo; + *hi = DSP3_AddHi; +} + + +void DSP3_OP1E_D1( int16 move, int16 *lo, int16 *hi ) +{ + //uint32 dataOfs = ((move << 1) + 0x03b2) & 0x03ff; + int16 Lo; + int16 Hi; + + const unsigned short HiAdd[] = { + 0x00, 0xFF, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00 + }; + const unsigned short LoAdd[] = { + 0x00, 0x00, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x00 + }; + + if( (*lo) & 1 ) + DSP3_AddHi = HiAdd[ move + 8 ]; + else + DSP3_AddHi = HiAdd[ move + 0 ]; + DSP3_AddLo = LoAdd[ move ]; + + Lo = (uint8)(*lo); + Hi = (uint8)(*hi); + + if (Lo & 1) Hi += (DSP3_AddLo & 1); + + DSP3_AddLo += Lo; + DSP3_AddHi += Hi; + + *lo = DSP3_AddLo; + *hi = DSP3_AddHi; +} + + +void DSP3_OP10() +{ + if( DSP3_DR == 0xffff ) { + DSP3_Reset(); + } else { + // absorb 2 bytes + DSP3_DR = DSP3_DR; + } +} + + +void DSP3_OP0C_A() +{ + // absorb 2 bytes + + DSP3_DR = 0; + SetDSP3 = &DSP3_Reset; +} + + +void DSP3_OP0C() +{ + // absorb 2 bytes + + DSP3_DR = 0; + //SetDSP3 = &DSP3_OP0C_A; + SetDSP3 = &DSP3_Reset; +} + + +void DSP3_OP1C_C() +{ + // return 2 bytes + DSP3_DR = 0; + SetDSP3 = &DSP3_Reset; +} + + +void DSP3_OP1C_B() +{ + // absorb 2 bytes + + // return 2 bytes + DSP3_DR = 0; + SetDSP3 = &DSP3_OP1C_C; +} + + +void DSP3_OP1C_A() +{ + // absorb 2 bytes + + SetDSP3 = &DSP3_OP1C_B; +} + + +void DSP3_OP1C() +{ + // absorb 2 bytes + + SetDSP3 = &DSP3_OP1C_A; +} + + +void DSP3_Command() +{ + if (DSP3_DR < 0x40) + { + switch (DSP3_DR) + { + case 0x02: SetDSP3 = &DSP3_Coordinate; break; + case 0x03: SetDSP3 = &DSP3_OP03; break; + case 0x06: SetDSP3 = &DSP3_OP06; break; + case 0x07: SetDSP3 = &DSP3_OP07; return; + case 0x0c: SetDSP3 = &DSP3_OP0C; break; + case 0x0f: SetDSP3 = &DSP3_TestMemory; break; + case 0x10: SetDSP3 = &DSP3_OP10; break; + case 0x18: SetDSP3 = &DSP3_Convert; break; + case 0x1c: SetDSP3 = &DSP3_OP1C; break; + case 0x1e: SetDSP3 = &DSP3_OP1E; break; + case 0x1f: SetDSP3 = &DSP3_MemoryDump; break; + case 0x38: SetDSP3 = &DSP3_Decode; break; + case 0x3e: SetDSP3 = &DSP3_OP3E; break; + default: + return; + } + DSP3_SR = 0x0080; + DSP3_Index = 0; + } +} + +uint8 dsp3_byte; +uint16 dsp3_address; + +void DSP3SetByte() +{ + if (dsp3_address < 0xC000) + { + if (DSP3_SR & 0x04) + { + DSP3_DR = (DSP3_DR & 0xff00) + dsp3_byte; + (*SetDSP3)(); + } + else + { + DSP3_SR ^= 0x10; + + if (DSP3_SR & 0x10) + DSP3_DR = (DSP3_DR & 0xff00) + dsp3_byte; + else + { + DSP3_DR = (DSP3_DR & 0x00ff) + (dsp3_byte << 8); + (*SetDSP3)(); + } + } + } +} + +void DSP3GetByte() +{ + if (dsp3_address < 0xC000) + { + if (DSP3_SR & 0x04) + { + dsp3_byte = (uint8) DSP3_DR; + (*SetDSP3)(); + } + else + { + DSP3_SR ^= 0x10; + + if (DSP3_SR & 0x10) + dsp3_byte = (uint8) (DSP3_DR); + else + { + dsp3_byte = (uint8) (DSP3_DR >> 8); + (*SetDSP3)(); + } + } + + } + else + { + dsp3_byte = (uint8) DSP3_SR; + } +} + +void InitDSP3() +{ + DSP3_Reset(); +} + +#endif diff --git a/mednafen/snes/src/chip/dsp4/dsp4.cpp b/mednafen/snes/src/chip/dsp4/dsp4.cpp new file mode 100755 index 0000000..c885dd7 --- /dev/null +++ b/mednafen/snes/src/chip/dsp4/dsp4.cpp @@ -0,0 +1,62 @@ +#include <../base.hpp> + +#define DSP4_CPP +namespace SNES { + +DSP4 dsp4; + +void DSP4::init() { +} + +void DSP4::enable() { + bus.map(Bus::MapDirect, 0x30, 0x3f, 0x8000, 0xffff, *this); + bus.map(Bus::MapDirect, 0xb0, 0xbf, 0x8000, 0xffff, *this); +} + +namespace DSP4i { + inline uint16 READ_WORD(uint8 *addr) { + return (addr[0]) + (addr[1] << 8); + } + + inline uint32 READ_DWORD(uint8 *addr) { + return (addr[0]) + (addr[1] << 8) + (addr[2] << 16) + (addr[3] << 24); + } + + inline void WRITE_WORD(uint8 *addr, uint16 data) { + addr[0] = data; + addr[1] = data >> 8; + } + + #define bool8 uint8 + #include "dsp4emu.c" + #undef bool8 +}; + +void DSP4::power() { + reset(); +} + +void DSP4::reset() { + DSP4i::InitDSP4(); +} + +uint8 DSP4::read(unsigned addr) { + addr &= 0xffff; + if(addr < 0xc000) { + DSP4i::dsp4_address = addr; + DSP4i::DSP4GetByte(); + return DSP4i::dsp4_byte; + } + return 0x80; +} + +void DSP4::write(unsigned addr, uint8 data) { + addr &= 0xffff; + if(addr < 0xc000) { + DSP4i::dsp4_address = addr; + DSP4i::dsp4_byte = data; + DSP4i::DSP4SetByte(); + } +} + +}; diff --git a/mednafen/snes/src/chip/dsp4/dsp4.hpp b/mednafen/snes/src/chip/dsp4/dsp4.hpp new file mode 100755 index 0000000..7a26119 --- /dev/null +++ b/mednafen/snes/src/chip/dsp4/dsp4.hpp @@ -0,0 +1,12 @@ +class DSP4 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); +}; + +extern DSP4 dsp4; diff --git a/mednafen/snes/src/chip/dsp4/dsp4emu.c b/mednafen/snes/src/chip/dsp4/dsp4emu.c new file mode 100755 index 0000000..73c1ec3 --- /dev/null +++ b/mednafen/snes/src/chip/dsp4/dsp4emu.c @@ -0,0 +1,2150 @@ +#ifdef DSP4_CPP + +//DSP-4 emulator code +//Copyright (c) 2004-2006 Dreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden + +/* +Due recognition and credit are given on Overload's DSP website. +Thank those contributors for their hard work on this chip. + + +Fixed-point math reminder: + +[sign, integer, fraction] +1.15.00 * 1.15.00 = 2.30.00 -> 1.30.00 (DSP) -> 1.31.00 (LSB is '0') +1.15.00 * 1.00.15 = 2.15.15 -> 1.15.15 (DSP) -> 1.15.16 (LSB is '0') +*/ + +#include "dsp4emu.h" + +struct DSP4_t DSP4; +struct DSP4_vars_t DSP4_vars; + +////////////////////////////////////////////////////////////// + +// input protocol + +static int16 DSP4_READ_WORD() +{ + int16 out; + + out = READ_WORD(DSP4.parameters + DSP4.in_index); + DSP4.in_index += 2; + + return out; +} + +static int32 DSP4_READ_DWORD() +{ + int32 out; + + out = READ_DWORD(DSP4.parameters + DSP4.in_index); + DSP4.in_index += 4; + + return out; +} + + +////////////////////////////////////////////////////////////// + +// output protocol + +#define DSP4_CLEAR_OUT() \ +{ DSP4.out_count = 0; DSP4.out_index = 0; } + +#define DSP4_WRITE_BYTE( d ) \ +{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count++; } + +#define DSP4_WRITE_WORD( d ) \ +{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count += 2; } + +#ifndef MSB_FIRST +#define DSP4_WRITE_16_WORD( d ) \ +{ memcpy(DSP4.output + DSP4.out_count, ( d ), 32); DSP4.out_count += 32; } +#else +#define DSP4_WRITE_16_WORD( d ) \ +{ int16 *p = ( d ), *end = ( d )+16; \ + for (; p != end; p++) \ + { \ + WRITE_WORD( DSP4.output + DSP4.out_count, *p ); \ + } \ + DSP4.out_count += 32; \ +} +#endif + +#ifdef PRINT_OP +#define DSP4_WRITE_DEBUG( x, d ) \ + WRITE_WORD( nop + x, d ); +#endif + +#ifdef DEBUG_DSP +#define DSP4_WRITE_DEBUG( x, d ) \ + WRITE_WORD( nop + x, d ); +#endif + +////////////////////////////////////////////////////////////// + +// used to wait for dsp i/o + +#define DSP4_WAIT( x ) \ + DSP4.in_index = 0; DSP4_vars.DSP4_Logic = x; return; + +////////////////////////////////////////////////////////////// + +// 1.7.8 -> 1.15.16 +#define SEX78( a ) ( ( (int32) ( (int16) (a) ) ) << 8 ) + +// 1.15.0 -> 1.15.16 +#define SEX16( a ) ( ( (int32) ( (int16) (a) ) ) << 16 ) + +#ifdef PRINT_OP +#define U16( a ) ( (uint16) ( a ) ) +#endif + +#ifdef DEBUG_DSP +#define U16( a ) ( (uint16) ( a ) ) +#endif + +////////////////////////////////////////////////////////////// + +// Attention: This lookup table is not verified +static const uint16 div_lut[64] = { 0x0000, 0x8000, 0x4000, 0x2aaa, 0x2000, 0x1999, 0x1555, 0x1249, 0x1000, 0x0e38, + 0x0ccc, 0x0ba2, 0x0aaa, 0x09d8, 0x0924, 0x0888, 0x0800, 0x0787, 0x071c, 0x06bc, + 0x0666, 0x0618, 0x05d1, 0x0590, 0x0555, 0x051e, 0x04ec, 0x04bd, 0x0492, 0x0469, + 0x0444, 0x0421, 0x0400, 0x03e0, 0x03c3, 0x03a8, 0x038e, 0x0375, 0x035e, 0x0348, + 0x0333, 0x031f, 0x030c, 0x02fa, 0x02e8, 0x02d8, 0x02c8, 0x02b9, 0x02aa, 0x029c, + 0x028f, 0x0282, 0x0276, 0x026a, 0x025e, 0x0253, 0x0249, 0x023e, 0x0234, 0x022b, + 0x0222, 0x0219, 0x0210, 0x0208, }; +int16 DSP4_Inverse(int16 value) +{ + // saturate bounds + if (value < 0) + { + value = 0; + } + if (value > 63) + { + value = 63; + } + + return div_lut[value]; +} + +////////////////////////////////////////////////////////////// + +// Prototype +void DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop); + +////////////////////////////////////////////////////////////// + +// OP00 +void DSP4_Multiply(int16 Multiplicand, int16 Multiplier, int32 *Product) +{ + *Product = (Multiplicand * Multiplier << 1) >> 1; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP01() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + case 3: + goto resume3; break; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // sort inputs + DSP4_vars.world_y = DSP4_READ_DWORD(); + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + DSP4_vars.world_x = DSP4_READ_DWORD(); + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.world_yofs = DSP4_READ_WORD(); + DSP4_vars.world_dy = DSP4_READ_DWORD(); + DSP4_vars.world_dx = DSP4_READ_DWORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.world_xenv = DSP4_READ_DWORD(); + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting DSP4_vars.raster line + DSP4_vars.view_x1 = (int16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16); + DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); + DSP4_vars.view_xofs1 = (int16)(DSP4_vars.world_x >> 16); + DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; + DSP4_vars.view_turnoff_x = 0; + DSP4_vars.view_turnoff_dx = 0; + + // first DSP4_vars.raster line + DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // perspective projection of world (x,y,scroll) points + // based on the current projection lines + DSP4_vars.view_x2 = (int16)(( ( ( DSP4_vars.world_x + DSP4_vars.world_xenv ) >> 16 ) * DSP4_vars.distance >> 15 ) + ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 )); + DSP4_vars.view_y2 = (int16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15); + DSP4_vars.view_xofs2 = DSP4_vars.view_x2; + DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; + + + // 1. World x-location before transformation + // 2. Viewer x-position at the next + // 3. World y-location before perspective projection + // 4. Viewer y-position below the horizon + // 5. Number of DSP4_vars.raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((uint16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_x2); + DSP4_WRITE_WORD((uint16)(DSP4_vars.world_y >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of DSP4_vars.raster lines used + DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2; + + // prevent overdraw + if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) + DSP4_vars.segments = 0; + else + DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; + + // don't draw outside the window + if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(DSP4_vars.segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + + // starting step values + x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); + y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); + + // SR = 0x80 + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + // 1. HDMA memory pointer (bg1) + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); + DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); + DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); + + + // update memory address + DSP4_vars.poly_ptr[0][0] -= 4; + + // update screen values + x_scroll += px_dx; + y_scroll += py_dy; + } + } + + //////////////////////////////////////////////////// + // Post-update + + // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn + DSP4_vars.view_x1 = DSP4_vars.view_x2; + DSP4_vars.view_y1 = DSP4_vars.view_y2; + DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; + DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; + + // add deltas for projection lines + DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx); + DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy); + + // update projection lines + DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv); + DSP4_vars.world_y += DSP4_vars.world_dy; + + // update road turnoff position + DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // check for termination + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + break; + + // road turnoff + if( (uint16) DSP4_vars.distance == 0x8001 ) + { + DSP4.in_count = 6; + DSP4_WAIT(2) resume2: + + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_vars.view_turnoff_x = DSP4_READ_WORD(); + DSP4_vars.view_turnoff_dx = DSP4_READ_WORD(); + + // factor in new changes + DSP4_vars.view_x1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ); + DSP4_vars.view_xofs1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ); + + // update stepping values + DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx; + + DSP4.in_count = 2; + DSP4_WAIT(1) + } + + // already have 2 bytes read + DSP4.in_count = 6; + DSP4_WAIT(3) resume3 : + + // inspect inputs + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // no envelope here + DSP4_vars.world_xenv = 0; + } + while (1); + + // terminate op + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP03() +{ + DSP4_vars.OAM_RowMax = 33; + memset(DSP4_vars.OAM_Row, 0, 64); +} + + +////////////////////////////////////////////////////////////// + + +void DSP4_OP05() +{ + DSP4_vars.OAM_index = 0; + DSP4_vars.OAM_bits = 0; + memset(DSP4_vars.OAM_attr, 0, 32); + DSP4_vars.sprite_count = 0; +} + + +////////////////////////////////////////////////////////////// + +void DSP4_OP06() +{ + DSP4_CLEAR_OUT(); + DSP4_WRITE_16_WORD(DSP4_vars.OAM_attr); +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP07() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + } + + //////////////////////////////////////////////////// + // sort inputs + + DSP4_vars.world_y = DSP4_READ_DWORD(); + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + DSP4_vars.world_x = DSP4_READ_DWORD(); + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.world_yofs = DSP4_READ_WORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_vars.view_y2 = DSP4_READ_WORD(); + DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_x2 = DSP4_READ_WORD(); + DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting DSP4_vars.raster line + DSP4_vars.view_x1 = (int16)(DSP4_vars.world_x >> 16); + DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); + DSP4_vars.view_xofs1 = DSP4_vars.view_x1; + DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; + + // first DSP4_vars.raster line + DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; + + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // add shaping + DSP4_vars.view_x2 += DSP4_vars.view_dx; + DSP4_vars.view_y2 += DSP4_vars.view_dy; + + // vertical scroll calculation + DSP4_vars.view_xofs2 = DSP4_vars.view_x2; + DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; + + // 1. Viewer x-position at the next + // 2. Viewer y-position below the horizon + // 3. Number of DSP4_vars.raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(DSP4_vars.view_x2); + DSP4_WRITE_WORD(DSP4_vars.view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of DSP4_vars.raster lines used + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2; + + // prevent overdraw + if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) + DSP4_vars.segments = 0; + else + DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; + + // don't draw outside the window + if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(DSP4_vars.segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + + // starting step values + x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); + y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); + + // SR = 0x80 + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + // 1. HDMA memory pointer (bg2) + // 2. vertical scroll offset ($2110) + // 3. horizontal scroll offset ($210F) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); + DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); + DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); + + // update memory address + DSP4_vars.poly_ptr[0][0] -= 4; + + // update screen values + x_scroll += px_dx; + y_scroll += py_dy; + } + } + + ///////////////////////////////////////////////////// + // Post-update + + // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn + DSP4_vars.view_x1 = DSP4_vars.view_x2; + DSP4_vars.view_y1 = DSP4_vars.view_y2; + DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; + DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // check for opcode termination + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 10; + DSP4_WAIT(2) resume2 : + + // inspect inputs + DSP4_vars.view_y2 = DSP4_READ_WORD(); + DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_x2 = DSP4_READ_WORD(); + DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + } + while (1); + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP08() +{ + int16 win_left, win_right; + int16 view_x[2], view_y[2]; + int16 envelope[2][2]; + + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + } + + //////////////////////////////////////////////////// + // process initial inputs for two polygons + + // clip values + DSP4_vars.poly_clipRt[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_clipRt[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_clipRt[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_clipRt[1][1] = DSP4_READ_WORD(); + + DSP4_vars.poly_clipLf[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_clipLf[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_clipLf[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_clipLf[1][1] = DSP4_READ_WORD(); + + // unknown (constant) (ex. 1P/2P = $00A6, $00A6, $00A6, $00A6) + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + // unknown (constant) (ex. 1P/2P = $00A5, $00A5, $00A7, $00A7) + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + // polygon centering (left,right) + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][1] = DSP4_READ_WORD(); + + // HDMA pointer locations + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[1][1] = DSP4_READ_WORD(); + + // starting DSP4_vars.raster line below the horizon + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_bottom[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_bottom[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_bottom[1][1] = DSP4_READ_WORD(); + + // top boundary line to clip + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_top[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[1][1] = DSP4_READ_WORD(); + + // unknown + // (ex. 1P = $2FC8, $0034, $FF5C, $0035) + // + // (ex. 2P = $3178, $0034, $FFCC, $0035) + // (ex. 2P = $2FC8, $0034, $FFCC, $0035) + + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + // look at guidelines for both polygon shapes + DSP4_vars.distance = DSP4_READ_WORD(); + view_x[0] = DSP4_READ_WORD(); + view_y[0] = DSP4_READ_WORD(); + view_x[1] = DSP4_READ_WORD(); + view_y[1] = DSP4_READ_WORD(); + + // envelope shaping guidelines (one frame only) + envelope[0][0] = DSP4_READ_WORD(); + envelope[0][1] = DSP4_READ_WORD(); + envelope[1][0] = DSP4_READ_WORD(); + envelope[1][1] = DSP4_READ_WORD(); + + // starting base values to project from + DSP4_vars.poly_start[0] = view_x[0]; + DSP4_vars.poly_start[1] = view_x[1]; + + // starting DSP4_vars.raster lines to begin drawing + DSP4_vars.poly_raster[0][0] = view_y[0]; + DSP4_vars.poly_raster[0][1] = view_y[0]; + DSP4_vars.poly_raster[1][0] = view_y[1]; + DSP4_vars.poly_raster[1][1] = view_y[1]; + + // starting distances + DSP4_vars.poly_plane[0] = DSP4_vars.distance; + DSP4_vars.poly_plane[1] = DSP4_vars.distance; + + // SR = 0x00 + + // re-center coordinates + win_left = DSP4_vars.poly_cx[0][0] - view_x[0] + envelope[0][0]; + win_right = DSP4_vars.poly_cx[0][1] - view_x[0] + envelope[0][1]; + + // saturate offscreen data for polygon #1 + if (win_left < DSP4_vars.poly_clipLf[0][0]) + { + win_left = DSP4_vars.poly_clipLf[0][0]; + } + if (win_left > DSP4_vars.poly_clipRt[0][0]) + { + win_left = DSP4_vars.poly_clipRt[0][0]; + } + if (win_right < DSP4_vars.poly_clipLf[0][1]) + { + win_right = DSP4_vars.poly_clipLf[0][1]; + } + if (win_right > DSP4_vars.poly_clipRt[0][1]) + { + win_right = DSP4_vars.poly_clipRt[0][1]; + } + + // SR = 0x80 + + // initial output for polygon #1 + DSP4_CLEAR_OUT(); + DSP4_WRITE_BYTE(win_left & 0xff); + DSP4_WRITE_BYTE(win_right & 0xff); + + + do + { + int16 polygon; + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // terminate op + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 16; + + DSP4_WAIT(2) resume2 : + + // look at guidelines for both polygon shapes + view_x[0] = DSP4_READ_WORD(); + view_y[0] = DSP4_READ_WORD(); + view_x[1] = DSP4_READ_WORD(); + view_y[1] = DSP4_READ_WORD(); + + // envelope shaping guidelines (one frame only) + envelope[0][0] = DSP4_READ_WORD(); + envelope[0][1] = DSP4_READ_WORD(); + envelope[1][0] = DSP4_READ_WORD(); + envelope[1][1] = DSP4_READ_WORD(); + + //////////////////////////////////////////////////// + // projection begins + + // init + DSP4_CLEAR_OUT(); + + + ////////////////////////////////////////////// + // solid polygon renderer - 2 shapes + + for (polygon = 0; polygon < 2; polygon++) + { + int32 left_inc, right_inc; + int16 x1_final, x2_final; + int16 env[2][2]; + int16 poly; + + // SR = 0x00 + + // # DSP4_vars.raster lines to draw + DSP4_vars.segments = DSP4_vars.poly_raster[polygon][0] - view_y[polygon]; + + // prevent overdraw + if (DSP4_vars.segments > 0) + { + // bump drawing cursor + DSP4_vars.poly_raster[polygon][0] = view_y[polygon]; + DSP4_vars.poly_raster[polygon][1] = view_y[polygon]; + } + else + DSP4_vars.segments = 0; + + // don't draw outside the window + if (view_y[polygon] < DSP4_vars.poly_top[polygon][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (view_y[polygon] >= DSP4_vars.poly_top[polygon][0]) + DSP4_vars.segments = view_y[polygon] - DSP4_vars.poly_top[polygon][0]; + } + + // SR = 0x80 + + // tell user how many DSP4_vars.raster structures to read in + DSP4_WRITE_WORD(DSP4_vars.segments); + + // normal parameters + poly = polygon; + + ///////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 win_left, win_right; + + // road turnoff selection + if( (uint16) envelope[ polygon ][ 0 ] == (uint16) 0xc001 ) + poly = 1; + else if( envelope[ polygon ][ 1 ] == 0x3fff ) + poly = 1; + + /////////////////////////////////////////////// + // left side of polygon + + // perspective correction on additional shaping parameters + env[0][0] = envelope[polygon][0] * DSP4_vars.poly_plane[poly] >> 15; + env[0][1] = envelope[polygon][0] * DSP4_vars.distance >> 15; + + // project new shapes (left side) + x1_final = view_x[poly] + env[0][0]; + x2_final = DSP4_vars.poly_start[poly] + env[0][1]; + + // interpolate between projected points with shaping + left_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4_vars.segments) << 1; + if (DSP4_vars.segments == 1) + left_inc = -left_inc; + + /////////////////////////////////////////////// + // right side of polygon + + // perspective correction on additional shaping parameters + env[1][0] = envelope[polygon][1] * DSP4_vars.poly_plane[poly] >> 15;; + env[1][1] = envelope[polygon][1] * DSP4_vars.distance >> 15; + + // project new shapes (right side) + x1_final = view_x[poly] + env[1][0]; + x2_final = DSP4_vars.poly_start[poly] + env[1][1]; + + + // interpolate between projected points with shaping + right_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4_vars.segments) << 1; + if (DSP4_vars.segments == 1) + right_inc = -right_inc; + + /////////////////////////////////////////////// + // update each point on the line + + win_left = SEX16(DSP4_vars.poly_cx[polygon][0] - DSP4_vars.poly_start[poly] + env[0][0]); + win_right = SEX16(DSP4_vars.poly_cx[polygon][1] - DSP4_vars.poly_start[poly] + env[1][0]); + + // update DSP4_vars.distance drawn into world + DSP4_vars.poly_plane[polygon] = DSP4_vars.distance; + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + int16 x_left, x_right; + + // project new coordinates + win_left += left_inc; + win_right += right_inc; + + // grab integer portion, drop fraction (no rounding) + x_left = (int16)(win_left >> 16); + x_right = (int16)(win_right >> 16); + + // saturate offscreen data + if (x_left < DSP4_vars.poly_clipLf[polygon][0]) + x_left = DSP4_vars.poly_clipLf[polygon][0]; + if (x_left > DSP4_vars.poly_clipRt[polygon][0]) + x_left = DSP4_vars.poly_clipRt[polygon][0]; + if (x_right < DSP4_vars.poly_clipLf[polygon][1]) + x_right = DSP4_vars.poly_clipLf[polygon][1]; + if (x_right > DSP4_vars.poly_clipRt[polygon][1]) + x_right = DSP4_vars.poly_clipRt[polygon][1]; + + // 1. HDMA memory pointer + // 2. Left window position ($2126/$2128) + // 3. Right window position ($2127/$2129) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[polygon][0]); + DSP4_WRITE_BYTE(x_left & 0xff); + DSP4_WRITE_BYTE(x_right & 0xff); + + + // update memory pointers + DSP4_vars.poly_ptr[polygon][0] -= 4; + DSP4_vars.poly_ptr[polygon][1] -= 4; + } // end rasterize line + } + + //////////////////////////////////////////////// + // Post-update + + // new projection spot to continue rasterizing from + DSP4_vars.poly_start[polygon] = view_x[poly]; + } // end polygon rasterizer + } + while (1); + + // unknown output + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(0); + + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP09() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + case 3: + goto resume3; break; + case 4: + goto resume4; break; + case 5: + goto resume5; break; + case 6: + goto resume6; break; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // grab screen information + DSP4_vars.viewport_cx = DSP4_READ_WORD(); + DSP4_vars.viewport_cy = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.viewport_left = DSP4_READ_WORD(); + DSP4_vars.viewport_right = DSP4_READ_WORD(); + DSP4_vars.viewport_top = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + + // starting DSP4_vars.raster line below the horizon + DSP4_vars.poly_bottom[0][0] = DSP4_vars.viewport_bottom - DSP4_vars.viewport_cy; + DSP4_vars.poly_raster[0][0] = 0x100; + + do + { + //////////////////////////////////////////////////// + // check for new sprites + + DSP4.in_count = 4; + DSP4_WAIT(1) resume1 : + + //////////////////////////////////////////////// + // DSP4_vars.raster overdraw check + + DSP4_vars.raster = DSP4_READ_WORD(); + + // continue updating the DSP4_vars.raster line where overdraw begins + if (DSP4_vars.raster < DSP4_vars.poly_raster[0][0]) + { + DSP4_vars.sprite_clipy = DSP4_vars.viewport_bottom - (DSP4_vars.poly_bottom[0][0] - DSP4_vars.raster); + DSP4_vars.poly_raster[0][0] = DSP4_vars.raster; + } + + ///////////////////////////////////////////////// + // identify sprite + + // op termination + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + goto terminate; + + + // no sprite + if (DSP4_vars.distance == 0x0000) + { + continue; + } + + //////////////////////////////////////////////////// + // process projection information + + // vehicle sprite + if ((uint16) DSP4_vars.distance == 0x9000) + { + int16 car_left, car_right, car_back; + int16 impact_left, impact_back; + int16 world_spx, world_spy; + int16 view_spx, view_spy; + uint16 energy; + + // we already have 4 bytes we want + DSP4.in_count = 14; + DSP4_WAIT(2) resume2 : + + // filter inputs + energy = DSP4_READ_WORD(); + impact_back = DSP4_READ_WORD(); + car_back = DSP4_READ_WORD(); + impact_left = DSP4_READ_WORD(); + car_left = DSP4_READ_WORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + car_right = DSP4_READ_WORD(); + + // calculate car's world (x,y) values + world_spx = car_right - car_left; + world_spy = car_back; + + // add in collision vector [needs bit-twiddling] + world_spx -= energy * (impact_left - car_left) >> 16; + world_spy -= energy * (car_back - impact_back) >> 16; + + // perspective correction for world (x,y) + view_spx = world_spx * DSP4_vars.distance >> 15; + view_spy = world_spy * DSP4_vars.distance >> 15; + + // convert to screen values + DSP4_vars.sprite_x = DSP4_vars.viewport_cx + view_spx; + DSP4_vars.sprite_y = DSP4_vars.viewport_bottom - (DSP4_vars.poly_bottom[0][0] - view_spy); + + // make the car's (x)-coordinate available + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(world_spx); + + // grab a few remaining vehicle values + DSP4.in_count = 4; + DSP4_WAIT(3) resume3 : + + // add vertical lift factor + DSP4_vars.sprite_y += DSP4_READ_WORD(); + } + // terrain sprite + else + { + int16 world_spx, world_spy; + int16 view_spx, view_spy; + + // we already have 4 bytes we want + DSP4.in_count = 10; + DSP4_WAIT(4) resume4 : + + // sort loop inputs + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_raster[0][1] = DSP4_READ_WORD(); + world_spx = DSP4_READ_WORD(); + world_spy = DSP4_READ_WORD(); + + // compute base DSP4_vars.raster line from the bottom + DSP4_vars.segments = DSP4_vars.poly_bottom[0][0] - DSP4_vars.raster; + + // perspective correction for world (x,y) + view_spx = world_spx * DSP4_vars.distance >> 15; + view_spy = world_spy * DSP4_vars.distance >> 15; + + // convert to screen values + DSP4_vars.sprite_x = DSP4_vars.viewport_cx + view_spx - DSP4_vars.poly_cx[0][0]; + DSP4_vars.sprite_y = DSP4_vars.viewport_bottom - DSP4_vars.segments + view_spy; + } + + // default sprite size: 16x16 + DSP4_vars.sprite_size = 1; + DSP4_vars.sprite_attr = DSP4_READ_WORD(); + + //////////////////////////////////////////////////// + // convert tile data to SNES OAM format + + do + { + uint16 header; + + int16 sp_x, sp_y, sp_attr, sp_dattr; + int16 sp_dx, sp_dy; + int16 pixels; + + bool8 draw; + + DSP4.in_count = 2; + DSP4_WAIT(5) resume5 : + + draw = TRUE; + + // opcode termination + DSP4_vars.raster = DSP4_READ_WORD(); + if (DSP4_vars.raster == -0x8000) + goto terminate; + + // stop code + if (DSP4_vars.raster == 0x0000 && !DSP4_vars.sprite_size) + break; + + // toggle sprite size + if (DSP4_vars.raster == 0x0000) + { + DSP4_vars.sprite_size = !DSP4_vars.sprite_size; + continue; + } + + // check for valid sprite header + header = DSP4_vars.raster; + header >>= 8; + if (header != 0x20 && + header != 0x2e && //This is for attractor sprite + header != 0x40 && + header != 0x60 && + header != 0xa0 && + header != 0xc0 && + header != 0xe0) + break; + + // read in rest of sprite data + DSP4.in_count = 4; + DSP4_WAIT(6) resume6 : + + draw = TRUE; + + ///////////////////////////////////// + // process tile data + + // sprite deltas + sp_dattr = DSP4_vars.raster; + sp_dy = DSP4_READ_WORD(); + sp_dx = DSP4_READ_WORD(); + + // update coordinates to screen space + sp_x = DSP4_vars.sprite_x + sp_dx; + sp_y = DSP4_vars.sprite_y + sp_dy; + + // update sprite nametable/attribute information + sp_attr = DSP4_vars.sprite_attr + sp_dattr; + + // allow partially visibile tiles + pixels = DSP4_vars.sprite_size ? 15 : 7; + + DSP4_CLEAR_OUT(); + + // transparent tile to clip off parts of a sprite (overdraw) + if (DSP4_vars.sprite_clipy - pixels <= sp_y && + sp_y <= DSP4_vars.sprite_clipy && + sp_x >= DSP4_vars.viewport_left - pixels && + sp_x <= DSP4_vars.viewport_right && + DSP4_vars.sprite_clipy >= DSP4_vars.viewport_top - pixels && + DSP4_vars.sprite_clipy <= DSP4_vars.viewport_bottom) + { + DSP4_OP0B(&draw, sp_x, DSP4_vars.sprite_clipy, 0x00EE, DSP4_vars.sprite_size, 0); + } + + + // normal sprite tile + if (sp_x >= DSP4_vars.viewport_left - pixels && + sp_x <= DSP4_vars.viewport_right && + sp_y >= DSP4_vars.viewport_top - pixels && + sp_y <= DSP4_vars.viewport_bottom && + sp_y <= DSP4_vars.sprite_clipy) + { + DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, DSP4_vars.sprite_size, 0); + } + + + // no following OAM data + DSP4_OP0B(&draw, 0, 0x0100, 0, 0, 1); + } + while (1); + } + while (1); + + terminate : DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +const uint16 OP0A_Values[16] = { 0x0000, 0x0030, 0x0060, 0x0090, 0x00c0, 0x00f0, 0x0120, 0x0150, 0xfe80, + 0xfeb0, 0xfee0, 0xff10, 0xff40, 0xff70, 0xffa0, 0xffd0 }; + +void DSP4_OP0A(int16 n2, int16 *o1, int16 *o2, int16 *o3, int16 *o4) +{ + *o4 = OP0A_Values[(n2 & 0x000f)]; + *o3 = OP0A_Values[(n2 & 0x00f0) >> 4]; + *o2 = OP0A_Values[(n2 & 0x0f00) >> 8]; + *o1 = OP0A_Values[(n2 & 0xf000) >> 12]; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop) +{ + int16 Row1, Row2; + + // SR = 0x00 + + // align to nearest 8-pixel row + Row1 = (sp_y >> 3) & 0x1f; + Row2 = (Row1 + 1) & 0x1f; + + // check boundaries + if (!((sp_y < 0) || ((sp_y & 0x01ff) < 0x00eb))) + { + *draw = 0; + } + if (size) + { + if (DSP4_vars.OAM_Row[Row1] + 1 >= DSP4_vars.OAM_RowMax) + *draw = 0; + if (DSP4_vars.OAM_Row[Row2] + 1 >= DSP4_vars.OAM_RowMax) + *draw = 0; + } + else + { + if (DSP4_vars.OAM_Row[Row1] >= DSP4_vars.OAM_RowMax) + { + *draw = 0; + } + } + + // emulator fail-safe (unknown if this really exists) + if (DSP4_vars.sprite_count >= 128) + { + *draw = 0; + } + + // SR = 0x80 + + if (*draw) + { + // Row tiles + if (size) + { + DSP4_vars.OAM_Row[Row1] += 2; + DSP4_vars.OAM_Row[Row2] += 2; + } + else + { + DSP4_vars.OAM_Row[Row1]++; + } + + // yield OAM output + DSP4_WRITE_WORD(1); + + // pack OAM data: x,y,name,attr + DSP4_WRITE_BYTE(sp_x & 0xff); + DSP4_WRITE_BYTE(sp_y & 0xff); + DSP4_WRITE_WORD(sp_attr); + + DSP4_vars.sprite_count++; + + // OAM: size,msb data + // save post-oam table data for future retrieval + DSP4_vars.OAM_attr[DSP4_vars.OAM_index] |= ((sp_x <0 || sp_x> 255) << DSP4_vars.OAM_bits); + DSP4_vars.OAM_bits++; + + DSP4_vars.OAM_attr[DSP4_vars.OAM_index] |= (size << DSP4_vars.OAM_bits); + DSP4_vars.OAM_bits++; + + // move to next byte in buffer + if (DSP4_vars.OAM_bits == 16) + { + DSP4_vars.OAM_bits = 0; + DSP4_vars.OAM_index++; + } + } + else if (stop) + { + // yield no OAM output + DSP4_WRITE_WORD(0); + } +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP0D() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // sort inputs + DSP4_vars.world_y = DSP4_READ_DWORD(); + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + DSP4_vars.world_x = DSP4_READ_DWORD(); + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.world_yofs = DSP4_READ_WORD(); + DSP4_vars.world_dy = DSP4_READ_DWORD(); + DSP4_vars.world_dx = DSP4_READ_DWORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.world_xenv = SEX78(DSP4_READ_WORD()); + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting DSP4_vars.raster line + DSP4_vars.view_x1 = (int16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16); + DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); + DSP4_vars.view_xofs1 = (int16)(DSP4_vars.world_x >> 16); + DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; + + // first DSP4_vars.raster line + DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; + + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // perspective projection of world (x,y,scroll) points + // based on the current projection lines + DSP4_vars.view_x2 = (int16)(( ( ( DSP4_vars.world_x + DSP4_vars.world_xenv ) >> 16 ) * DSP4_vars.distance >> 15 ) + ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 )); + DSP4_vars.view_y2 = (int16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15); + DSP4_vars.view_xofs2 = DSP4_vars.view_x2; + DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; + + // 1. World x-location before transformation + // 2. Viewer x-position at the current + // 3. World y-location before perspective projection + // 4. Viewer y-position below the horizon + // 5. Number of DSP4_vars.raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((uint16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_x2); + DSP4_WRITE_WORD((uint16)(DSP4_vars.world_y >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_y2); + + ////////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of DSP4_vars.raster lines used + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2; + + // prevent overdraw + if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) + DSP4_vars.segments = 0; + else + DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; + + // don't draw outside the window + if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(DSP4_vars.segments); + + ////////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + + // starting step values + x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); + y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); + + // SR = 0x80 + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + // 1. HDMA memory pointer (bg1) + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); + DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); + DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); + + + // update memory address + DSP4_vars.poly_ptr[0][0] -= 4; + + // update screen values + x_scroll += px_dx; + y_scroll += py_dy; + } + } + + ///////////////////////////////////////////////////// + // Post-update + + // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn + DSP4_vars.view_x1 = DSP4_vars.view_x2; + DSP4_vars.view_y1 = DSP4_vars.view_y2; + DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; + DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; + + // add deltas for projection lines + DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx); + DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy); + + // update projection lines + DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv); + DSP4_vars.world_y += DSP4_vars.world_dy; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // inspect input + DSP4_vars.distance = DSP4_READ_WORD(); + + // terminate op + if (DSP4_vars.distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 6; + DSP4_WAIT(2) resume2: + + // inspect inputs + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // no envelope here + DSP4_vars.world_xenv = 0; + } + while (1); + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP0E() +{ + DSP4_vars.OAM_RowMax = 16; + memset(DSP4_vars.OAM_Row, 0, 64); +} + + +////////////////////////////////////////////////////////////// + +void DSP4_OP0F() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + case 3: + goto resume3; break; + case 4: + goto resume4; break; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // sort inputs + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.world_y = DSP4_READ_DWORD(); + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + DSP4_vars.world_x = DSP4_READ_DWORD(); + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.world_yofs = DSP4_READ_WORD(); + DSP4_vars.world_dy = DSP4_READ_DWORD(); + DSP4_vars.world_dx = DSP4_READ_DWORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.world_xenv = DSP4_READ_DWORD(); + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting DSP4_vars.raster line + DSP4_vars.view_x1 = (int16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16); + DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); + DSP4_vars.view_xofs1 = (int16)(DSP4_vars.world_x >> 16); + DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; + DSP4_vars.view_turnoff_x = 0; + DSP4_vars.view_turnoff_dx = 0; + + // first DSP4_vars.raster line + DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; + + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // perspective projection of world (x,y,scroll) points + // based on the current projection lines + DSP4_vars.view_x2 = (int16)(((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16) * DSP4_vars.distance >> 15); + DSP4_vars.view_y2 = (int16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15); + DSP4_vars.view_xofs2 = DSP4_vars.view_x2; + DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; + + // 1. World x-location before transformation + // 2. Viewer x-position at the next + // 3. World y-location before perspective projection + // 4. Viewer y-position below the horizon + // 5. Number of DSP4_vars.raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((uint16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_x2); + DSP4_WRITE_WORD((uint16)(DSP4_vars.world_y >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of DSP4_vars.raster lines used + DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2; + + // prevent overdraw + if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) + DSP4_vars.segments = 0; + else + DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; + + // don't draw outside the window + if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(DSP4_vars.segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < 4; DSP4_vars.lcv++) + { + // grab inputs + DSP4.in_count = 4; + DSP4_WAIT(1); + resume1 : + for (;;) + { + int16 distance; + int16 color, red, green, blue; + + distance = DSP4_READ_WORD(); + color = DSP4_READ_WORD(); + + // U1+B5+G5+R5 + red = color & 0x1f; + green = (color >> 5) & 0x1f; + blue = (color >> 10) & 0x1f; + + // dynamic lighting + red = (red * distance >> 15) & 0x1f; + green = (green * distance >> 15) & 0x1f; + blue = (blue * distance >> 15) & 0x1f; + color = red | (green << 5) | (blue << 10); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(color); + break; + } + } + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + + + // starting step values + x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); + y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); + + // SR = 0x80 + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + // 1. HDMA memory pointer + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); + DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); + DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); + + // update memory address + DSP4_vars.poly_ptr[0][0] -= 4; + + // update screen values + x_scroll += px_dx; + y_scroll += py_dy; + } + } + + //////////////////////////////////////////////////// + // Post-update + + // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn + DSP4_vars.view_x1 = DSP4_vars.view_x2; + DSP4_vars.view_y1 = DSP4_vars.view_y2; + DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; + DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; + + // add deltas for projection lines + DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx); + DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy); + + // update projection lines + DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv); + DSP4_vars.world_y += DSP4_vars.world_dy; + + // update road turnoff position + DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(2) resume2: + + // check for termination + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + break; + + // road splice + if( (uint16) DSP4_vars.distance == 0x8001 ) + { + DSP4.in_count = 6; + DSP4_WAIT(3) resume3: + + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_vars.view_turnoff_x = DSP4_READ_WORD(); + DSP4_vars.view_turnoff_dx = DSP4_READ_WORD(); + + // factor in new changes + DSP4_vars.view_x1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ); + DSP4_vars.view_xofs1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ); + + // update stepping values + DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx; + + DSP4.in_count = 2; + DSP4_WAIT(2) + } + + // already have 2 bytes in queue + DSP4.in_count = 6; + DSP4_WAIT(4) resume4 : + + // inspect inputs + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // no envelope here + DSP4_vars.world_xenv = 0; + } + while (1); + + // terminate op + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP10() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + case 3: + goto resume3; break; + } + + //////////////////////////////////////////////////// + // sort inputs + + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.world_y = DSP4_READ_DWORD(); + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + DSP4_vars.world_x = DSP4_READ_DWORD(); + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.world_yofs = DSP4_READ_WORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_vars.view_y2 = DSP4_READ_WORD(); + DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_x2 = DSP4_READ_WORD(); + DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting DSP4_vars.raster line + DSP4_vars.view_x1 = (int16)(DSP4_vars.world_x >> 16); + DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); + DSP4_vars.view_xofs1 = DSP4_vars.view_x1; + DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; + + // first DSP4_vars.raster line + DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // add shaping + DSP4_vars.view_x2 += DSP4_vars.view_dx; + DSP4_vars.view_y2 += DSP4_vars.view_dy; + + // vertical scroll calculation + DSP4_vars.view_xofs2 = DSP4_vars.view_x2; + DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; + + // 1. Viewer x-position at the next + // 2. Viewer y-position below the horizon + // 3. Number of DSP4_vars.raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(DSP4_vars.view_x2); + DSP4_WRITE_WORD(DSP4_vars.view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of DSP4_vars.raster lines used + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2; + + // prevent overdraw + if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) + DSP4_vars.segments = 0; + else + DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; + + // don't draw outside the window + if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(DSP4_vars.segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < 4; DSP4_vars.lcv++) + { + // grab inputs + DSP4.in_count = 4; + DSP4_WAIT(1); + resume1 : + for (;;) + { + int16 distance; + int16 color, red, green, blue; + + distance = DSP4_READ_WORD(); + color = DSP4_READ_WORD(); + + // U1+B5+G5+R5 + red = color & 0x1f; + green = (color >> 5) & 0x1f; + blue = (color >> 10) & 0x1f; + + // dynamic lighting + red = (red * distance >> 15) & 0x1f; + green = (green * distance >> 15) & 0x1f; + blue = (blue * distance >> 15) & 0x1f; + color = red | (green << 5) | (blue << 10); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(color); + break; + } + } + } + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + + // starting step values + x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); + y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); + + // SR = 0x80 + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + // 1. HDMA memory pointer (bg2) + // 2. vertical scroll offset ($2110) + // 3. horizontal scroll offset ($210F) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); + DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); + DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); + + // update memory address + DSP4_vars.poly_ptr[0][0] -= 4; + + // update screen values + x_scroll += px_dx; + y_scroll += py_dy; + } + } + + ///////////////////////////////////////////////////// + // Post-update + + // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn + DSP4_vars.view_x1 = DSP4_vars.view_x2; + DSP4_vars.view_y1 = DSP4_vars.view_y2; + DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; + DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(2) resume2 : + + // check for opcode termination + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 10; + DSP4_WAIT(3) resume3 : + + + // inspect inputs + DSP4_vars.view_y2 = DSP4_READ_WORD(); + DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_x2 = DSP4_READ_WORD(); + DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + } + while (1); + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP11(int16 A, int16 B, int16 C, int16 D, int16 *M) +{ + // 0x155 = 341 = Horizontal Width of the Screen + *M = ((A * 0x0155 >> 2) & 0xf000) | + ((B * 0x0155 >> 6) & 0x0f00) | + ((C * 0x0155 >> 10) & 0x00f0) | + ((D * 0x0155 >> 14) & 0x000f); +} + + + + + +///////////////////////////////////////////////////////////// +//Processing Code +///////////////////////////////////////////////////////////// +uint8 dsp4_byte; +uint16 dsp4_address; + +void InitDSP4() +{ + memset(&DSP4, 0, sizeof(DSP4)); + DSP4.waiting4command = TRUE; +} + +void DSP4SetByte() +{ + // clear pending read + if (DSP4.out_index < DSP4.out_count) + { + DSP4.out_index++; + return; + } + + if (DSP4.waiting4command) + { + if (DSP4.half_command) + { + DSP4.command |= (dsp4_byte << 8); + DSP4.in_index = 0; + DSP4.waiting4command = FALSE; + DSP4.half_command = FALSE; + DSP4.out_count = 0; + DSP4.out_index = 0; + + DSP4_vars.DSP4_Logic = 0; + + + switch (DSP4.command) + { + case 0x0000: + DSP4.in_count = 4; break; + case 0x0001: + DSP4.in_count = 44; break; + case 0x0003: + DSP4.in_count = 0; break; + case 0x0005: + DSP4.in_count = 0; break; + case 0x0006: + DSP4.in_count = 0; break; + case 0x0007: + DSP4.in_count = 34; break; + case 0x0008: + DSP4.in_count = 90; break; + case 0x0009: + DSP4.in_count = 14; break; + case 0x000a: + DSP4.in_count = 6; break; + case 0x000b: + DSP4.in_count = 6; break; + case 0x000d: + DSP4.in_count = 42; break; + case 0x000e: + DSP4.in_count = 0; break; + case 0x000f: + DSP4.in_count = 46; break; + case 0x0010: + DSP4.in_count = 36; break; + case 0x0011: + DSP4.in_count = 8; break; + default: + DSP4.waiting4command = TRUE; + break; + } + } + else + { + DSP4.command = dsp4_byte; + DSP4.half_command = TRUE; + } + } + else + { + DSP4.parameters[DSP4.in_index] = dsp4_byte; + DSP4.in_index++; + } + + if (!DSP4.waiting4command && DSP4.in_count == DSP4.in_index) + { + // Actually execute the command + DSP4.waiting4command = TRUE; + DSP4.out_index = 0; + DSP4.in_index = 0; + + switch (DSP4.command) + { + // 16-bit multiplication + case 0x0000: + { + int16 multiplier, multiplicand; + int32 product; + + multiplier = DSP4_READ_WORD(); + multiplicand = DSP4_READ_WORD(); + + DSP4_Multiply(multiplicand, multiplier, &product); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((uint16)(product)); + DSP4_WRITE_WORD((uint16)(product >> 16)); + } + break; + + // single-player track projection + case 0x0001: + DSP4_OP01(); break; + + // single-player selection + case 0x0003: + DSP4_OP03(); break; + + // clear OAM + case 0x0005: + DSP4_OP05(); break; + + // transfer OAM + case 0x0006: + DSP4_OP06(); break; + + // single-player track turnoff projection + case 0x0007: + DSP4_OP07(); break; + + // solid polygon projection + case 0x0008: + DSP4_OP08(); break; + + // sprite projection + case 0x0009: + DSP4_OP09(); break; + + // unknown + case 0x000A: + { + int16 in1a = DSP4_READ_WORD(); + int16 in2a = DSP4_READ_WORD(); + int16 in3a = DSP4_READ_WORD(); + int16 out1a, out2a, out3a, out4a; + + DSP4_OP0A(in2a, &out2a, &out1a, &out4a, &out3a); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(out1a); + DSP4_WRITE_WORD(out2a); + DSP4_WRITE_WORD(out3a); + DSP4_WRITE_WORD(out4a); + } + break; + + // set OAM + case 0x000B: + { + int16 sp_x = DSP4_READ_WORD(); + int16 sp_y = DSP4_READ_WORD(); + int16 sp_attr = DSP4_READ_WORD(); + bool8 draw = 1; + + DSP4_CLEAR_OUT(); + + DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, 0, 1); + } + break; + + // multi-player track projection + case 0x000D: + DSP4_OP0D(); break; + + // multi-player selection + case 0x000E: + DSP4_OP0E(); break; + + // single-player track projection with lighting + case 0x000F: + DSP4_OP0F(); break; + + // single-player track turnoff projection with lighting + case 0x0010: + DSP4_OP10(); break; + + // unknown: horizontal mapping command + case 0x0011: + { + int16 a, b, c, d, m; + + + d = DSP4_READ_WORD(); + c = DSP4_READ_WORD(); + b = DSP4_READ_WORD(); + a = DSP4_READ_WORD(); + + DSP4_OP11(a, b, c, d, &m); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(m); + + break; + } + + default: + break; + } + } +} + +void DSP4GetByte() +{ + if (DSP4.out_count) + { + dsp4_byte = (uint8) DSP4.output[DSP4.out_index&0x1FF]; + DSP4.out_index++; + if (DSP4.out_count == DSP4.out_index) + DSP4.out_count = 0; + } + else + { + dsp4_byte = 0xff; + } +} + +#endif diff --git a/mednafen/snes/src/chip/dsp4/dsp4emu.h b/mednafen/snes/src/chip/dsp4/dsp4emu.h new file mode 100755 index 0000000..834b33d --- /dev/null +++ b/mednafen/snes/src/chip/dsp4/dsp4emu.h @@ -0,0 +1,108 @@ +//DSP-4 emulator code +//Copyright (c) 2004-2006 Dreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden + +#ifndef DSP4EMU_H +#define DSP4EMU_H + +#undef TRUE +#undef FALSE +#define TRUE true +#define FALSE false + +struct DSP4_t +{ + bool8 waiting4command; + bool8 half_command; + uint16 command; + uint32 in_count; + uint32 in_index; + uint32 out_count; + uint32 out_index; + uint8 parameters[512]; + uint8 output[512]; +}; + +extern struct DSP4_t DSP4; + +struct DSP4_vars_t +{ + // op control + int8 DSP4_Logic; // controls op flow + + + // projection format + int16 lcv; // loop-control variable + int16 distance; // z-position into virtual world + int16 raster; // current raster line + int16 segments; // number of raster lines drawn + + // 1.15.16 or 1.15.0 [sign, integer, fraction] + int32 world_x; // line of x-projection in world + int32 world_y; // line of y-projection in world + int32 world_dx; // projection line x-delta + int32 world_dy; // projection line y-delta + int16 world_ddx; // x-delta increment + int16 world_ddy; // y-delta increment + int32 world_xenv; // world x-shaping factor + int16 world_yofs; // world y-vertical scroll + + int16 view_x1; // current viewer-x + int16 view_y1; // current viewer-y + int16 view_x2; // future viewer-x + int16 view_y2; // future viewer-y + int16 view_dx; // view x-delta factor + int16 view_dy; // view y-delta factor + int16 view_xofs1; // current viewer x-vertical scroll + int16 view_yofs1; // current viewer y-vertical scroll + int16 view_xofs2; // future viewer x-vertical scroll + int16 view_yofs2; // future viewer y-vertical scroll + int16 view_yofsenv; // y-scroll shaping factor + int16 view_turnoff_x; // road turnoff data + int16 view_turnoff_dx; // road turnoff delta factor + + + // drawing area + + int16 viewport_cx; // x-center of viewport window + int16 viewport_cy; // y-center of render window + int16 viewport_left; // x-left of viewport + int16 viewport_right; // x-right of viewport + int16 viewport_top; // y-top of viewport + int16 viewport_bottom; // y-bottom of viewport + + + // sprite structure + + int16 sprite_x; // projected x-pos of sprite + int16 sprite_y; // projected y-pos of sprite + int16 sprite_attr; // obj attributes + bool8 sprite_size; // sprite size: 8x8 or 16x16 + int16 sprite_clipy; // visible line to clip pixels off + int16 sprite_count; + + // generic projection variables designed for + // two solid polygons + two polygon sides + + int16 poly_clipLf[2][2]; // left clip boundary + int16 poly_clipRt[2][2]; // right clip boundary + int16 poly_ptr[2][2]; // HDMA structure pointers + int16 poly_raster[2][2]; // current raster line below horizon + int16 poly_top[2][2]; // top clip boundary + int16 poly_bottom[2][2]; // bottom clip boundary + int16 poly_cx[2][2]; // center for left/right points + int16 poly_start[2]; // current projection points + int16 poly_plane[2]; // previous z-plane distance + + + // OAM + int16 OAM_attr[16]; // OAM (size,MSB) data + int16 OAM_index; // index into OAM table + int16 OAM_bits; // offset into OAM table + + int16 OAM_RowMax; // maximum number of tiles per 8 aligned pixels (row) + int16 OAM_Row[32]; // current number of tiles per row +}; + +extern struct DSP4_vars_t DSP4_vars; + +#endif diff --git a/mednafen/snes/src/chip/obc1/obc1.cpp b/mednafen/snes/src/chip/obc1/obc1.cpp new file mode 100755 index 0000000..39b22cf --- /dev/null +++ b/mednafen/snes/src/chip/obc1/obc1.cpp @@ -0,0 +1,84 @@ +#include <../base.hpp> + +#define OBC1_CPP +namespace SNES { + +OBC1 obc1; + +#include "serialization.cpp" + +void OBC1::init() { +} + +void OBC1::enable() { + bus.map(Bus::MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, *this); + bus.map(Bus::MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, *this); +} + +void OBC1::power() { + reset(); +} + +void OBC1::reset() { + for(unsigned i = 0x0000; i <= 0x1fff; i++) ram_write(i, 0xff); + + status.baseptr = (ram_read(0x1ff5) & 1) ? 0x1800 : 0x1c00; + status.address = (ram_read(0x1ff6) & 0x7f); + status.shift = (ram_read(0x1ff6) & 3) << 1; +} + +uint8 OBC1::read(unsigned addr) { + addr &= 0x1fff; + if((addr & 0x1ff8) != 0x1ff0) return ram_read(addr); + + switch(addr) { default: //never used, avoids compiler warning + case 0x1ff0: return ram_read(status.baseptr + (status.address << 2) + 0); + case 0x1ff1: return ram_read(status.baseptr + (status.address << 2) + 1); + case 0x1ff2: return ram_read(status.baseptr + (status.address << 2) + 2); + case 0x1ff3: return ram_read(status.baseptr + (status.address << 2) + 3); + case 0x1ff4: return ram_read(status.baseptr + (status.address >> 2) + 0x200); + case 0x1ff5: case 0x1ff6: case 0x1ff7: return ram_read(addr); + } +} + +void OBC1::write(unsigned addr, uint8 data) { + addr &= 0x1fff; + if((addr & 0x1ff8) != 0x1ff0) return ram_write(addr, data); + + switch(addr) { + case 0x1ff0: ram_write(status.baseptr + (status.address << 2) + 0, data); break; + case 0x1ff1: ram_write(status.baseptr + (status.address << 2) + 1, data); break; + case 0x1ff2: ram_write(status.baseptr + (status.address << 2) + 2, data); break; + case 0x1ff3: ram_write(status.baseptr + (status.address << 2) + 3, data); break; + case 0x1ff4: { + uint8 temp = ram_read(status.baseptr + (status.address >> 2) + 0x200); + temp = (temp & ~(3 << status.shift)) | ((data & 3) << status.shift); + ram_write(status.baseptr + (status.address >> 2) + 0x200, temp); + } break; + case 0x1ff5: { + status.baseptr = (data & 1) ? 0x1800 : 0x1c00; + ram_write(addr, data); + } break; + case 0x1ff6: { + status.address = (data & 0x7f); + status.shift = (data & 3) << 1; + ram_write(addr, data); + } break; + case 0x1ff7: { + ram_write(addr, data); + } break; + } +} + +uint8 OBC1::ram_read(unsigned addr) { + return memory::cartram.read(addr & 0x1fff); +} + +void OBC1::ram_write(unsigned addr, uint8 data) { + memory::cartram.write(addr & 0x1fff, data); +} + +OBC1::OBC1() {} +OBC1::~OBC1() {} + +}; diff --git a/mednafen/snes/src/chip/obc1/obc1.hpp b/mednafen/snes/src/chip/obc1/obc1.hpp new file mode 100755 index 0000000..4b5d440 --- /dev/null +++ b/mednafen/snes/src/chip/obc1/obc1.hpp @@ -0,0 +1,26 @@ +class OBC1 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + OBC1(); + ~OBC1(); + +private: + uint8 ram_read(unsigned addr); + void ram_write(unsigned addr, uint8 data); + + struct { + uint16 address; + uint16 baseptr; + uint16 shift; + } status; +}; + +extern OBC1 obc1; diff --git a/mednafen/snes/src/chip/obc1/serialization.cpp b/mednafen/snes/src/chip/obc1/serialization.cpp new file mode 100755 index 0000000..3d61aaf --- /dev/null +++ b/mednafen/snes/src/chip/obc1/serialization.cpp @@ -0,0 +1,9 @@ +#ifdef OBC1_CPP + +void OBC1::serialize(serializer &s) { + s.integer(status.address); + s.integer(status.baseptr); + s.integer(status.shift); +} + +#endif diff --git a/mednafen/snes/src/chip/sa1/bus/bus.cpp b/mednafen/snes/src/chip/sa1/bus/bus.cpp new file mode 100755 index 0000000..062328d --- /dev/null +++ b/mednafen/snes/src/chip/sa1/bus/bus.cpp @@ -0,0 +1,243 @@ +#ifdef SA1_CPP + +VBRBus vbrbus; +SA1Bus sa1bus; + +namespace memory { + StaticRAM iram(2048); + //accessed by: + VectorSelectionPage vectorsp; //S-CPU + SA-1 + CPUIRAM cpuiram; //S-CPU + SA1IRAM sa1iram; //SA-1 + SA1BWRAM sa1bwram; //SA-1 + CC1BWRAM cc1bwram; //S-CPU + BitmapRAM bitmapram; //SA-1 +} + +//$230c (VDPL), $230d (VDPH) use this bus to read variable-length data. +//this is used both to avoid VBR-reads from accessing MMIO registers, and +//to avoid syncing the S-CPU and SA-1*; as both chips are able to access +//these ports. +//(* eg, memory::cartram is used directly, as memory::sa1bwram syncs to the S-CPU) +void VBRBus::init() { + map(MapDirect, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped); + + map(MapLinear, 0x00, 0x3f, 0x0000, 0x07ff, memory::iram); + map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::iram); + map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cartram); + map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::cartram); + map(MapLinear, 0x80, 0xbf, 0x0000, 0x07ff, memory::iram); + map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::iram); + map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cartram); + map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom); +} + +void SA1Bus::init() { + map(MapDirect, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped); + for(unsigned i = 0x2200; i <= 0x23ff; i++) memory::mmio.map(i, sa1); + + map(MapLinear, 0x00, 0x3f, 0x0000, 0x07ff, memory::sa1iram); + map(MapDirect, 0x00, 0x3f, 0x2200, 0x23ff, memory::mmio); + map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::sa1iram); + map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::sa1bwram); + map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::sa1bwram); + map(MapLinear, 0x60, 0x6f, 0x0000, 0xffff, memory::bitmapram); + map(MapLinear, 0x80, 0xbf, 0x0000, 0x07ff, memory::sa1iram); + map(MapDirect, 0x80, 0xbf, 0x2200, 0x23ff, memory::mmio); + map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::sa1iram); + map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1bwram); + map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom); + + bus.map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::cpuiram); + bus.map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cc1bwram); + bus.map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); + bus.map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::cc1bwram); + bus.map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::cpuiram); + bus.map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cc1bwram); + bus.map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); + bus.map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom); + + memory::vectorsp.sync(); +} + +//=================== +//VectorSelectionPage +//=================== + +//this class maps $00:[ff00-ffff] for the purpose of supporting: +//$2209.d6 IVSW (S-CPU IRQ vector selection) (0 = cart, 1 = SA-1) +//$2209.d4 NVSW (S-CPU NMI vector selection) (0 = cart, 1 = SA-1) +//when set, vector addresses are over-ridden with SA-1 register settings: +//SIV = S-CPU IRQ vector address override +//SNV = S-CPU NMI vector address override +// +//$00:[ffea-ffeb|ffee-ffef] are special cased on read; +//all other addresses return original mapped data. + +uint8 VectorSelectionPage::read(unsigned addr) { + switch(0xff00 | (addr & 0xff)) { + case 0xffea: case 0xffeb: { + if(sa1.mmio.cpu_nvsw == true) return (sa1.mmio.snv >> ((addr & 1) << 3)); + } break; + + case 0xffee: case 0xffef: { + if(sa1.mmio.cpu_ivsw == true) return (sa1.mmio.siv >> ((addr & 1) << 3)); + } break; + } + + return access->read(addr); +} + +void VectorSelectionPage::write(unsigned addr, uint8 data) { + return access->write(addr, data); +} + +//call this whenever bus is remapped. +//note: S-CPU and SA-1 bus always share $00:[ff00-ffff] as cartridge ROM data; +//the SA-1 MMC does not allow mapping these independently between processors. +//this allows this class to be shared for both, caching only ones' access class. +void VectorSelectionPage::sync() { + if(bus.page[0x00ff00 >> 8].access != this) { + //bus was re-mapped, hook access routine + access = bus.page[0x00ff00 >> 8].access; + bus.page[0x00ff00 >> 8].access = this; + sa1bus.page[0x00ff00 >> 8].access = this; + } +} + +//======= +//SA1IRAM +//======= + +unsigned SA1IRAM::size() const { + return memory::iram.size(); +} + +uint8 SA1IRAM::read(unsigned addr) { + scheduler.sync_copcpu(); + return memory::iram.read(addr); +} + +void SA1IRAM::write(unsigned addr, uint8 data) { + scheduler.sync_copcpu(); + memory::iram.write(addr, data); +} + +//======= +//CPUIRAM +//======= + +unsigned CPUIRAM::size() const { + return memory::iram.size(); +} + +uint8 CPUIRAM::read(unsigned addr) { + scheduler.sync_cpucop(); + return memory::iram.read(addr); +} + +void CPUIRAM::write(unsigned addr, uint8 data) { + scheduler.sync_cpucop(); + memory::iram.write(addr, data); +} + +//======== +//SA1BWRAM +//======== + +unsigned SA1BWRAM::size() const { + return memory::cartram.size(); +} + +uint8 SA1BWRAM::read(unsigned addr) { + scheduler.sync_copcpu(); + return memory::cartram.read(addr); +} + +void SA1BWRAM::write(unsigned addr, uint8 data) { + scheduler.sync_copcpu(); + memory::cartram.write(addr, data); +} + +//======== +//CC1BWRAM +//======== + +unsigned CC1BWRAM::size() const { + return memory::cartram.size(); +} + +uint8 CC1BWRAM::read(unsigned addr) { + scheduler.sync_cpucop(); + if(dma) return sa1.dma_cc1_read(addr); + return memory::cartram.read(addr); +} + +void CC1BWRAM::write(unsigned addr, uint8 data) { + scheduler.sync_cpucop(); + memory::cartram.write(addr, data); +} + +//========= +//BitmapRAM +//========= + +unsigned BitmapRAM::size() const { + return 0x100000; +} + +uint8 BitmapRAM::read(unsigned addr) { + scheduler.sync_copcpu(); + + if(sa1.mmio.bbf == 0) { + //4bpp + unsigned shift = addr & 1; + addr = (addr >> 1) & (memory::cartram.size() - 1); + switch(shift) { default: + case 0: return (memory::cartram.read(addr) >> 0) & 15; + case 1: return (memory::cartram.read(addr) >> 4) & 15; + } + } else { + //2bpp + unsigned shift = addr & 3; + addr = (addr >> 2) & (memory::cartram.size() - 1); + switch(shift) { default: + case 0: return (memory::cartram.read(addr) >> 0) & 3; + case 1: return (memory::cartram.read(addr) >> 2) & 3; + case 2: return (memory::cartram.read(addr) >> 4) & 3; + case 3: return (memory::cartram.read(addr) >> 6) & 3; + } + } +} + +void BitmapRAM::write(unsigned addr, uint8 data) { + scheduler.sync_copcpu(); + + if(sa1.mmio.bbf == 0) { + //4bpp + unsigned shift = addr & 1; + addr = (addr >> 1) & (memory::cartram.size() - 1); + switch(shift) { default: + case 0: data = (memory::cartram.read(addr) & 0xf0) | ((data & 15) << 0); break; + case 1: data = (memory::cartram.read(addr) & 0x0f) | ((data & 15) << 4); break; + } + } else { + //2bpp + unsigned shift = addr & 3; + addr = (addr >> 2) & (memory::cartram.size() - 1); + switch(shift) { default: + case 0: data = (memory::cartram.read(addr) & 0xfc) | ((data & 3) << 0); break; + case 1: data = (memory::cartram.read(addr) & 0xf3) | ((data & 3) << 2); break; + case 2: data = (memory::cartram.read(addr) & 0xcf) | ((data & 3) << 4); break; + case 3: data = (memory::cartram.read(addr) & 0x3f) | ((data & 3) << 6); break; + } + } + + memory::cartram.write(addr, data); +} + +#endif diff --git a/mednafen/snes/src/chip/sa1/bus/bus.hpp b/mednafen/snes/src/chip/sa1/bus/bus.hpp new file mode 100755 index 0000000..bec2f82 --- /dev/null +++ b/mednafen/snes/src/chip/sa1/bus/bus.hpp @@ -0,0 +1,56 @@ +struct VBRBus : Bus { + void init(); +}; + +struct SA1Bus : Bus { + void init(); +}; + +struct VectorSelectionPage : Memory { + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); + void sync(); + Memory *access; +}; + +struct CPUIRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); +}; + +struct SA1IRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); +}; + +struct SA1BWRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); +}; + +struct CC1BWRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); + bool dma; +}; + +struct BitmapRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); +}; + +namespace memory { + extern StaticRAM iram; + + extern VectorSelectionPage vectorsp; + extern CPUIRAM cpuiram; + extern SA1IRAM sa1iram; + extern SA1BWRAM sa1bwram; + extern CC1BWRAM cc1bwram; + extern BitmapRAM bitmapram; +}; diff --git a/mednafen/snes/src/chip/sa1/dma/dma.cpp b/mednafen/snes/src/chip/sa1/dma/dma.cpp new file mode 100755 index 0000000..aecb735 --- /dev/null +++ b/mednafen/snes/src/chip/sa1/dma/dma.cpp @@ -0,0 +1,139 @@ +#ifdef SA1_CPP + +//==================== +//direct data transfer +//==================== + +void SA1::dma_normal() { + while(mmio.dtc--) { + uint8 data = regs.mdr; + uint32 dsa = mmio.dsa++; + uint32 dda = mmio.dda++; + + //source and destination cannot be the same + if(mmio.sd == DMA::SourceBWRAM && mmio.dd == DMA::DestBWRAM) continue; + if(mmio.sd == DMA::SourceIRAM && mmio.dd == DMA::DestIRAM ) continue; + + switch(mmio.sd) { + case DMA::SourceROM: { + if((dsa & 0x408000) == 0x008000 || (dsa & 0xc00000) == 0xc00000) { + data = sa1bus.read(dsa); + } + } break; + + case DMA::SourceBWRAM: { + if((dsa & 0x40e000) == 0x006000 || (dsa & 0xf00000) == 0x400000) { + data = sa1bus.read(dsa); + } + } break; + + case DMA::SourceIRAM: { + data = memory::iram.read(dsa & 0x07ff); + } break; + } + + switch(mmio.dd) { + case DMA::DestBWRAM: { + if((dda & 0x40e000) == 0x006000 || (dda & 0xf00000) == 0x400000) { + sa1bus.write(dda, data); + } + } break; + + case DMA::DestIRAM: { + memory::iram.write(dda & 0x07ff, data); + } break; + } + } + + mmio.dma_irqfl = true; + if(mmio.dma_irqen) mmio.dma_irqcl = 0; +} + +//((byte & 6) << 3) + (byte & 1) explanation: +//transforms a byte index (0-7) into a planar index: +//result[] = { 0, 1, 16, 17, 32, 33, 48, 49 }; +//works for 2bpp, 4bpp and 8bpp modes + +//=========================== +//type-1 character conversion +//=========================== + +void SA1::dma_cc1() { + memory::cc1bwram.dma = true; + mmio.chdma_irqfl = true; + if(mmio.chdma_irqen) { + mmio.chdma_irqcl = 0; + cpu.regs.irq = 1; + } +} + +uint8 SA1::dma_cc1_read(unsigned addr) { + //16 bytes/char (2bpp); 32 bytes/char (4bpp); 64 bytes/char (8bpp) + unsigned charmask = (1 << (6 - mmio.dmacb)) - 1; + + if((addr & charmask) == 0) { + //buffer next character to I-RAM + unsigned bpp = 2 << (2 - mmio.dmacb); + unsigned bpl = (8 << mmio.dmasize) >> mmio.dmacb; + unsigned bwmask = memory::cartram.size() - 1; + unsigned tile = ((addr - mmio.dsa) & bwmask) >> (6 - mmio.dmacb); + unsigned ty = (tile >> mmio.dmasize); + unsigned tx = tile & ((1 << mmio.dmasize) - 1); + unsigned bwaddr = mmio.dsa + ty * 8 * bpl + tx * bpp; + + for(unsigned y = 0; y < 8; y++) { + uint64 data = 0; + for(unsigned byte = 0; byte < bpp; byte++) { + data |= (uint64)memory::cartram.read((bwaddr + byte) & bwmask) << (byte << 3); + } + bwaddr += bpl; + + uint8 out[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + for(unsigned x = 0; x < 8; x++) { + out[0] |= (data & 1) << (7 - x); data >>= 1; + out[1] |= (data & 1) << (7 - x); data >>= 1; + if(mmio.dmacb == 2) continue; + out[2] |= (data & 1) << (7 - x); data >>= 1; + out[3] |= (data & 1) << (7 - x); data >>= 1; + if(mmio.dmacb == 1) continue; + out[4] |= (data & 1) << (7 - x); data >>= 1; + out[5] |= (data & 1) << (7 - x); data >>= 1; + out[6] |= (data & 1) << (7 - x); data >>= 1; + out[7] |= (data & 1) << (7 - x); data >>= 1; + } + + for(unsigned byte = 0; byte < bpp; byte++) { + unsigned p = mmio.dda + (y << 1) + ((byte & 6) << 3) + (byte & 1); + memory::iram.write(p & 0x07ff, out[byte]); + } + } + } + + return memory::iram.read((mmio.dda + (addr & charmask)) & 0x07ff); +} + +//=========================== +//type-2 character conversion +//=========================== + +void SA1::dma_cc2() { + //select register file index (0-7 or 8-15) + const uint8 *brf = &mmio.brf[(dma.line & 1) << 3]; + unsigned bpp = 2 << (2 - mmio.dmacb); + unsigned addr = mmio.dda & 0x07ff; + addr &= ~((1 << (7 - mmio.dmacb)) - 1); + addr += (dma.line & 8) * bpp; + addr += (dma.line & 7) * 2; + + for(unsigned byte = 0; byte < bpp; byte++) { + uint8 output = 0; + for(unsigned bit = 0; bit < 8; bit++) { + output |= ((brf[bit] >> byte) & 1) << (7 - bit); + } + memory::iram.write(addr + ((byte & 6) << 3) + (byte & 1), output); + } + + dma.line = (dma.line + 1) & 15; +} + +#endif diff --git a/mednafen/snes/src/chip/sa1/dma/dma.hpp b/mednafen/snes/src/chip/sa1/dma/dma.hpp new file mode 100755 index 0000000..4d3cff7 --- /dev/null +++ b/mednafen/snes/src/chip/sa1/dma/dma.hpp @@ -0,0 +1,11 @@ +struct DMA { + enum CDEN { DmaNormal = 0, DmaCharConversion = 1 }; + enum SD { SourceROM = 0, SourceBWRAM = 1, SourceIRAM = 2 }; + enum DD { DestIRAM = 0, DestBWRAM = 1 }; + unsigned line; +} dma; + +void dma_normal(); +void dma_cc1(); +uint8 dma_cc1_read(unsigned addr); +void dma_cc2(); diff --git a/mednafen/snes/src/chip/sa1/memory/memory.cpp b/mednafen/snes/src/chip/sa1/memory/memory.cpp new file mode 100755 index 0000000..b2779a3 --- /dev/null +++ b/mednafen/snes/src/chip/sa1/memory/memory.cpp @@ -0,0 +1,24 @@ +#ifdef SA1_CPP + +//ROM, I-RAM and MMIO registers are accessed at ~10.74MHz (2 clock ticks) +//BW-RAM is accessed at ~5.37MHz (4 clock ticks) +//tick() == 2 clock ticks +//note: bus conflict delays are not emulated at this time + +void SA1::op_io() { + tick(); +} + +uint8 SA1::op_read(unsigned addr) { + tick(); + if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick(); + return sa1bus.read(addr); +} + +void SA1::op_write(unsigned addr, uint8 data) { + tick(); + if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick(); + sa1bus.write(addr, data); +} + +#endif diff --git a/mednafen/snes/src/chip/sa1/memory/memory.hpp b/mednafen/snes/src/chip/sa1/memory/memory.hpp new file mode 100755 index 0000000..d466c41 --- /dev/null +++ b/mednafen/snes/src/chip/sa1/memory/memory.hpp @@ -0,0 +1,5 @@ +alwaysinline void op_io(); +alwaysinline uint8 op_read(unsigned addr); +alwaysinline void op_write(unsigned addr, uint8 data); + +uint8_t vbr_read(unsigned addr); diff --git a/mednafen/snes/src/chip/sa1/mmio/mmio.cpp b/mednafen/snes/src/chip/sa1/mmio/mmio.cpp new file mode 100755 index 0000000..eb6832e --- /dev/null +++ b/mednafen/snes/src/chip/sa1/mmio/mmio.cpp @@ -0,0 +1,633 @@ +#ifdef SA1_CPP + +//BS-X flash carts, when present, are mapped to 0x400000+ +Memory& SA1::mmio_access(unsigned &addr) { + if(!memory::bsxflash.data()) return memory::cartrom; + if(addr < 0x400000) return memory::cartrom; + addr &= 0x3fffff; + return bsxflash; +} + +//(CCNT) SA-1 control +void SA1::mmio_w2200(uint8 data) { + if(mmio.sa1_resb && !(data & 0x80)) { + //reset SA-1 CPU + regs.pc.w = mmio.crv; + regs.pc.b = 0x00; + } + + mmio.sa1_irq = (data & 0x80); + mmio.sa1_rdyb = (data & 0x40); + mmio.sa1_resb = (data & 0x20); + mmio.sa1_nmi = (data & 0x10); + mmio.smeg = (data & 0x0f); + + if(mmio.sa1_irq) { + mmio.sa1_irqfl = true; + if(mmio.sa1_irqen) mmio.sa1_irqcl = 0; + } + + if(mmio.sa1_nmi) { + mmio.sa1_nmifl = true; + if(mmio.sa1_nmien) mmio.sa1_nmicl = 0; + } +} + +//(SIE) S-CPU interrupt enable +void SA1::mmio_w2201(uint8 data) { + if(!mmio.cpu_irqen && (data & 0x80)) { + if(mmio.cpu_irqfl) { + mmio.cpu_irqcl = 0; + cpu.regs.irq = 1; + } + } + + if(!mmio.chdma_irqen && (data & 0x20)) { + if(mmio.chdma_irqfl) { + mmio.chdma_irqcl = 0; + cpu.regs.irq = 1; + } + } + + mmio.cpu_irqen = (data & 0x80); + mmio.chdma_irqen = (data & 0x20); +} + +//(SIC) S-CPU interrupt clear +void SA1::mmio_w2202(uint8 data) { + mmio.cpu_irqcl = (data & 0x80); + mmio.chdma_irqcl = (data & 0x20); + + if(mmio.cpu_irqcl ) mmio.cpu_irqfl = false; + if(mmio.chdma_irqcl) mmio.chdma_irqfl = false; + + if(!mmio.cpu_irqfl && !mmio.chdma_irqfl) cpu.regs.irq = 0; +} + +//(CRV) SA-1 reset vector +void SA1::mmio_w2203(uint8 data) { mmio.crv = (mmio.crv & 0xff00) | data; } +void SA1::mmio_w2204(uint8 data) { mmio.crv = (data << 8) | (mmio.crv & 0xff); } + +//(CNV) SA-1 NMI vector +void SA1::mmio_w2205(uint8 data) { mmio.cnv = (mmio.cnv & 0xff00) | data; } +void SA1::mmio_w2206(uint8 data) { mmio.cnv = (data << 8) | (mmio.cnv & 0xff); } + +//(CIV) SA-1 IRQ vector +void SA1::mmio_w2207(uint8 data) { mmio.civ = (mmio.civ & 0xff00) | data; } +void SA1::mmio_w2208(uint8 data) { mmio.civ = (data << 8) | (mmio.civ & 0xff); } + +//(SCNT) S-CPU control +void SA1::mmio_w2209(uint8 data) { + mmio.cpu_irq = (data & 0x80); + mmio.cpu_ivsw = (data & 0x40); + mmio.cpu_nvsw = (data & 0x10); + mmio.cmeg = (data & 0x0f); + + if(mmio.cpu_irq) { + mmio.cpu_irqfl = true; + if(mmio.cpu_irqen) { + mmio.cpu_irqcl = 0; + cpu.regs.irq = 1; + } + } +} + +//(CIE) SA-1 interrupt enable +void SA1::mmio_w220a(uint8 data) { + if(!mmio.sa1_irqen && (data & 0x80) && mmio.sa1_irqfl ) mmio.sa1_irqcl = 0; + if(!mmio.timer_irqen && (data & 0x40) && mmio.timer_irqfl) mmio.timer_irqcl = 0; + if(!mmio.dma_irqen && (data & 0x20) && mmio.dma_irqfl ) mmio.dma_irqcl = 0; + if(!mmio.sa1_nmien && (data & 0x10) && mmio.sa1_nmifl ) mmio.sa1_nmicl = 0; + + mmio.sa1_irqen = (data & 0x80); + mmio.timer_irqen = (data & 0x40); + mmio.dma_irqen = (data & 0x20); + mmio.sa1_nmien = (data & 0x10); +} + +//(CIC) SA-1 interrupt clear +void SA1::mmio_w220b(uint8 data) { + mmio.sa1_irqcl = (data & 0x80); + mmio.timer_irqcl = (data & 0x40); + mmio.dma_irqcl = (data & 0x20); + mmio.sa1_nmicl = (data & 0x10); + + if(mmio.sa1_irqcl) mmio.sa1_irqfl = false; + if(mmio.timer_irqcl) mmio.timer_irqfl = false; + if(mmio.dma_irqcl) mmio.dma_irqfl = false; + if(mmio.sa1_nmicl) mmio.sa1_nmifl = false; +} + +//(SNV) S-CPU NMI vector +void SA1::mmio_w220c(uint8 data) { mmio.snv = (mmio.snv & 0xff00) | data; } +void SA1::mmio_w220d(uint8 data) { mmio.snv = (data << 8) | (mmio.snv & 0xff); } + +//(SIV) S-CPU IRQ vector +void SA1::mmio_w220e(uint8 data) { mmio.siv = (mmio.siv & 0xff00) | data; } +void SA1::mmio_w220f(uint8 data) { mmio.siv = (data << 8) | (mmio.siv & 0xff); } + +//(TMC) H/V timer control +void SA1::mmio_w2210(uint8 data) { + mmio.hvselb = (data & 0x80); + mmio.ven = (data & 0x02); + mmio.hen = (data & 0x01); +} + +//(CTR) SA-1 timer restart +void SA1::mmio_w2211(uint8 data) { + status.vcounter = 0; + status.hcounter = 0; +} + +//(HCNT) H-count +void SA1::mmio_w2212(uint8 data) { mmio.hcnt = (mmio.hcnt & 0xff00) | (data << 0); } +void SA1::mmio_w2213(uint8 data) { mmio.hcnt = (mmio.hcnt & 0x00ff) | (data << 8); } + +//(VCNT) V-count +void SA1::mmio_w2214(uint8 data) { mmio.vcnt = (mmio.vcnt & 0xff00) | (data << 0); } +void SA1::mmio_w2215(uint8 data) { mmio.vcnt = (mmio.vcnt & 0x00ff) | (data << 8); } + +//(CXB) Super MMC bank C +void SA1::mmio_w2220(uint8 data) { + mmio.cbmode = (data & 0x80); + mmio.cb = (data & 0x07); + + unsigned addr = mmio.cb << 20; + Memory &access = mmio_access(addr); + + if(mmio.cbmode == 0) { + bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, 0x000000); + sa1bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, 0x000000); + } else { + bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, access, addr); + sa1bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, access, addr); + } + + bus.map(Bus::MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, access, addr); + sa1bus.map(Bus::MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, access, addr); + + memory::vectorsp.sync(); +} + +//(DXB) Super MMC bank D +void SA1::mmio_w2221(uint8 data) { + mmio.dbmode = (data & 0x80); + mmio.db = (data & 0x07); + + unsigned addr = mmio.db << 20; + Memory &access = mmio_access(addr); + + if(mmio.dbmode == 0) { + bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, 0x100000); + sa1bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, 0x100000); + } else { + bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, access, addr); + sa1bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, access, addr); + } + + bus.map(Bus::MapLinear, 0xd0, 0xdf, 0x0000, 0xffff, access, addr); + sa1bus.map(Bus::MapLinear, 0xd0, 0xdf, 0x0000, 0xffff, access, addr); +} + +//(EXB) Super MMC bank E +void SA1::mmio_w2222(uint8 data) { + mmio.ebmode = (data & 0x80); + mmio.eb = (data & 0x07); + + unsigned addr = mmio.eb << 20; + Memory &access = mmio_access(addr); + + if(mmio.ebmode == 0) { + bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, 0x200000); + sa1bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, 0x200000); + } else { + bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, access, addr); + sa1bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, access, addr); + } + + bus.map(Bus::MapLinear, 0xe0, 0xef, 0x0000, 0xffff, access, addr); + sa1bus.map(Bus::MapLinear, 0xe0, 0xef, 0x0000, 0xffff, access, addr); +} + +//(FXB) Super MMC bank F +void SA1::mmio_w2223(uint8 data) { + mmio.fbmode = (data & 0x80); + mmio.fb = (data & 0x07); + + unsigned addr = mmio.fb << 20; + Memory &access = mmio_access(addr); + + if(mmio.fbmode == 0) { + bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, 0x300000); + sa1bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, 0x300000); + } else { + bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, access, addr); + sa1bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, access, addr); + } + + bus.map(Bus::MapLinear, 0xf0, 0xff, 0x0000, 0xffff, access, addr); + sa1bus.map(Bus::MapLinear, 0xf0, 0xff, 0x0000, 0xffff, access, addr); +} + +//(BMAPS) S-CPU BW-RAM address mapping +void SA1::mmio_w2224(uint8 data) { + mmio.sbm = (data & 0x1f); + + bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cc1bwram, mmio.sbm * 0x2000, 0x2000); + bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cc1bwram, mmio.sbm * 0x2000, 0x2000); +} + +//(BMAP) SA-1 BW-RAM address mapping +void SA1::mmio_w2225(uint8 data) { + mmio.sw46 = (data & 0x80); + mmio.cbm = (data & 0x7f); + + if(mmio.sw46 == 0) { + //$[40-43]:[0000-ffff] x 32 projection + sa1bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::sa1bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000); + sa1bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000); + } else { + //$[60-6f]:[0000-ffff] x 128 projection + sa1bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::bitmapram, mmio.cbm * 0x2000, 0x2000); + sa1bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::bitmapram, mmio.cbm * 0x2000, 0x2000); + } +} + +//(SWBE) S-CPU BW-RAM write enable +void SA1::mmio_w2226(uint8 data) { + mmio.swen = (data & 0x80); +} + +//(CWBE) SA-1 BW-RAM write enable +void SA1::mmio_w2227(uint8 data) { + mmio.cwen = (data & 0x80); +} + +//(BWPA) BW-RAM write-protected area +void SA1::mmio_w2228(uint8 data) { + mmio.bwp = (data & 0x0f); +} + +//(SIWP) S-CPU I-RAM write protection +void SA1::mmio_w2229(uint8 data) { + mmio.siwp = data; +} + +//(CIWP) SA-1 I-RAM write protection +void SA1::mmio_w222a(uint8 data) { + mmio.ciwp = data; +} + +//(DCNT) DMA control +void SA1::mmio_w2230(uint8 data) { + mmio.dmaen = (data & 0x80); + mmio.dprio = (data & 0x40); + mmio.cden = (data & 0x20); + mmio.cdsel = (data & 0x10); + mmio.dd = (data & 0x04); + mmio.sd = (data & 0x03); + + if(mmio.dmaen == 0) dma.line = 0; +} + +//(CDMA) character conversion DMA parameters +void SA1::mmio_w2231(uint8 data) { + mmio.chdend = (data & 0x80); + mmio.dmasize = (data >> 2) & 7; + mmio.dmacb = (data & 0x03); + + if(mmio.chdend) memory::cc1bwram.dma = false; + if(mmio.dmasize > 5) mmio.dmasize = 5; + if(mmio.dmacb > 2) mmio.dmacb = 2; +} + +//(SDA) DMA source device start address +void SA1::mmio_w2232(uint8 data) { mmio.dsa = (mmio.dsa & 0xffff00) | (data << 0); } +void SA1::mmio_w2233(uint8 data) { mmio.dsa = (mmio.dsa & 0xff00ff) | (data << 8); } +void SA1::mmio_w2234(uint8 data) { mmio.dsa = (mmio.dsa & 0x00ffff) | (data << 16); } + +//(DDA) DMA destination start address +void SA1::mmio_w2235(uint8 data) { + mmio.dda = (mmio.dda & 0xffff00) | (data << 0); +} + +void SA1::mmio_w2236(uint8 data) { + mmio.dda = (mmio.dda & 0xff00ff) | (data << 8); + + if(mmio.dmaen == true) { + if(mmio.cden == 0 && mmio.dd == DMA::DestIRAM) { + dma_normal(); + } else if(mmio.cden == 1 && mmio.cdsel == 1) { + dma_cc1(); + } + } +} + +void SA1::mmio_w2237(uint8 data) { + mmio.dda = (mmio.dda & 0x00ffff) | (data << 16); + + if(mmio.dmaen == true) { + if(mmio.cden == 0 && mmio.dd == DMA::DestBWRAM) { + dma_normal(); + } + } +} + +//(DTC) DMA terminal counter +void SA1::mmio_w2238(uint8 data) { mmio.dtc = (mmio.dtc & 0xff00) | (data << 0); } +void SA1::mmio_w2239(uint8 data) { mmio.dtc = (mmio.dtc & 0x00ff) | (data << 8); } + +//(BBF) BW-RAM bitmap format +void SA1::mmio_w223f(uint8 data) { + mmio.bbf = (data & 0x80); +} + +//(BRF) bitmap register files +void SA1::mmio_w2240(uint8 data) { mmio.brf[ 0] = data; } +void SA1::mmio_w2241(uint8 data) { mmio.brf[ 1] = data; } +void SA1::mmio_w2242(uint8 data) { mmio.brf[ 2] = data; } +void SA1::mmio_w2243(uint8 data) { mmio.brf[ 3] = data; } +void SA1::mmio_w2244(uint8 data) { mmio.brf[ 4] = data; } +void SA1::mmio_w2245(uint8 data) { mmio.brf[ 5] = data; } +void SA1::mmio_w2246(uint8 data) { mmio.brf[ 6] = data; } +void SA1::mmio_w2247(uint8 data) { mmio.brf[ 7] = data; + if(mmio.dmaen == true) { + if(mmio.cden == 1 && mmio.cdsel == 0) { + dma_cc2(); + } + } +} + +void SA1::mmio_w2248(uint8 data) { mmio.brf[ 8] = data; } +void SA1::mmio_w2249(uint8 data) { mmio.brf[ 9] = data; } +void SA1::mmio_w224a(uint8 data) { mmio.brf[10] = data; } +void SA1::mmio_w224b(uint8 data) { mmio.brf[11] = data; } +void SA1::mmio_w224c(uint8 data) { mmio.brf[12] = data; } +void SA1::mmio_w224d(uint8 data) { mmio.brf[13] = data; } +void SA1::mmio_w224e(uint8 data) { mmio.brf[14] = data; } +void SA1::mmio_w224f(uint8 data) { mmio.brf[15] = data; + if(mmio.dmaen == true) { + if(mmio.cden == 1 && mmio.cdsel == 0) { + dma_cc2(); + } + } +} + +//(MCNT) arithmetic control +void SA1::mmio_w2250(uint8 data) { + mmio.acm = (data & 0x02); + mmio.md = (data & 0x01); + + if(mmio.acm) mmio.mr = 0; +} + +//(MAL) multiplicand / dividend low +void SA1::mmio_w2251(uint8 data) { + mmio.ma = (mmio.ma & 0xff00) | data; +} + +//(MAH) multiplicand / dividend high +void SA1::mmio_w2252(uint8 data) { + mmio.ma = (data << 8) | (mmio.ma & 0x00ff); +} + +//(MBL) multiplier / divisor low +void SA1::mmio_w2253(uint8 data) { + mmio.mb = (mmio.mb & 0xff00) | data; +} + +//(MBH) multiplier / divisor high +//multiplication / cumulative sum only resets MB +//division resets both MA and MB +void SA1::mmio_w2254(uint8 data) { + mmio.mb = (data << 8) | (mmio.mb & 0x00ff); + + if(mmio.acm == 0) { + if(mmio.md == 0) { + //signed multiplication + mmio.mr = (int16)mmio.ma * (int16)mmio.mb; + mmio.mb = 0; + } else { + //unsigned division + if(mmio.mb == 0) { + mmio.mr = 0; + } else { + int16 quotient = (int16)mmio.ma / (uint16)mmio.mb; + uint16 remainder = (int16)mmio.ma % (uint16)mmio.mb; + mmio.mr = (remainder << 16) | quotient; + } + mmio.ma = 0; + mmio.mb = 0; + } + } else { + //sigma (accumulative multiplication) + mmio.mr += (int16)mmio.ma * (int16)mmio.mb; + mmio.overflow = (mmio.mr >= (1ULL << 40)); + mmio.mr &= (1ULL << 40) - 1; + mmio.mb = 0; + } +} + +//(VBD) variable-length bit processing +void SA1::mmio_w2258(uint8 data) { + mmio.hl = (data & 0x80); + mmio.vb = (data & 0x0f); + if(mmio.vb == 0) mmio.vb = 16; + + if(mmio.hl == 0) { + //fixed mode + mmio.vbit += mmio.vb; + mmio.va += (mmio.vbit >> 3); + mmio.vbit &= 7; + } +} + +//(VDA) variable-length bit game pak ROM start address +void SA1::mmio_w2259(uint8 data) { mmio.va = (mmio.va & 0xffff00) | (data << 0); } +void SA1::mmio_w225a(uint8 data) { mmio.va = (mmio.va & 0xff00ff) | (data << 8); } +void SA1::mmio_w225b(uint8 data) { mmio.va = (mmio.va & 0x00ffff) | (data << 16); mmio.vbit = 0; } + +//(SFR) S-CPU flag read +uint8 SA1::mmio_r2300() { + uint8 data; + data = mmio.cpu_irqfl << 7; + data |= mmio.cpu_ivsw << 6; + data |= mmio.chdma_irqfl << 5; + data |= mmio.cpu_nvsw << 4; + data |= mmio.cmeg; + return data; +} + +//(CFR) SA-1 flag read +uint8 SA1::mmio_r2301() { + uint8 data; + data = mmio.sa1_irqfl << 7; + data |= mmio.timer_irqfl << 6; + data |= mmio.dma_irqfl << 5; + data |= mmio.sa1_nmifl << 4; + data |= mmio.smeg; + return data; +} + +//(HCR) hcounter read +uint8 SA1::mmio_r2302() { + //latch counters + mmio.hcr = status.hcounter >> 2; + mmio.vcr = status.vcounter; + return mmio.hcr >> 0; } +uint8 SA1::mmio_r2303() { return mmio.hcr >> 8; } + +//(VCR) vcounter read +uint8 SA1::mmio_r2304() { return mmio.vcr >> 0; } +uint8 SA1::mmio_r2305() { return mmio.vcr >> 8; } + +//(MR) arithmetic result +uint8 SA1::mmio_r2306() { return mmio.mr >> 0; } +uint8 SA1::mmio_r2307() { return mmio.mr >> 8; } +uint8 SA1::mmio_r2308() { return mmio.mr >> 16; } +uint8 SA1::mmio_r2309() { return mmio.mr >> 24; } +uint8 SA1::mmio_r230a() { return mmio.mr >> 32; } + +//(OF) arithmetic overflow flag +uint8 SA1::mmio_r230b() { return mmio.overflow << 7; } + +//(VDPL) variable-length data read port low +uint8 SA1::mmio_r230c() { + uint32 data = (vbrbus.read(mmio.va + 0) << 0) + | (vbrbus.read(mmio.va + 1) << 8) + | (vbrbus.read(mmio.va + 2) << 16); + data >>= mmio.vbit; + return data >> 0; +} + +//(VDPH) variable-length data read port high +uint8 SA1::mmio_r230d() { + uint32 data = (vbrbus.read(mmio.va + 0) << 0) + | (vbrbus.read(mmio.va + 1) << 8) + | (vbrbus.read(mmio.va + 2) << 16); + data >>= mmio.vbit; + + if(mmio.hl == 1) { + //auto-increment mode + mmio.vbit += mmio.vb; + mmio.va += (mmio.vbit >> 3); + mmio.vbit &= 7; + } + + return data >> 8; +} + +//(VC) version code register +uint8 SA1::mmio_r230e() { + return 0x01; //true value unknown +} + +uint8 SA1::mmio_read(unsigned addr) { + (co_active() == scheduler.thread_cpu ? scheduler.sync_cpucop() : scheduler.sync_copcpu()); + addr &= 0xffff; + + switch(addr) { + case 0x2300: return mmio_r2300(); + case 0x2301: return mmio_r2301(); + case 0x2302: return mmio_r2302(); + case 0x2303: return mmio_r2303(); + case 0x2304: return mmio_r2304(); + case 0x2305: return mmio_r2305(); + case 0x2306: return mmio_r2306(); + case 0x2307: return mmio_r2307(); + case 0x2308: return mmio_r2308(); + case 0x2309: return mmio_r2309(); + case 0x230a: return mmio_r230a(); + case 0x230b: return mmio_r230b(); + case 0x230c: return mmio_r230c(); + case 0x230d: return mmio_r230d(); + case 0x230e: return mmio_r230e(); + } + + return 0x00; +} + +void SA1::mmio_write(unsigned addr, uint8 data) { + (co_active() == scheduler.thread_cpu ? scheduler.sync_cpucop() : scheduler.sync_copcpu()); + addr &= 0xffff; + + switch(addr) { + case 0x2200: return mmio_w2200(data); + case 0x2201: return mmio_w2201(data); + case 0x2202: return mmio_w2202(data); + case 0x2203: return mmio_w2203(data); + case 0x2204: return mmio_w2204(data); + case 0x2205: return mmio_w2205(data); + case 0x2206: return mmio_w2206(data); + case 0x2207: return mmio_w2207(data); + case 0x2208: return mmio_w2208(data); + case 0x2209: return mmio_w2209(data); + case 0x220a: return mmio_w220a(data); + case 0x220b: return mmio_w220b(data); + case 0x220c: return mmio_w220c(data); + case 0x220d: return mmio_w220d(data); + case 0x220e: return mmio_w220e(data); + case 0x220f: return mmio_w220f(data); + + case 0x2210: return mmio_w2210(data); + case 0x2211: return mmio_w2211(data); + case 0x2212: return mmio_w2212(data); + case 0x2213: return mmio_w2213(data); + case 0x2214: return mmio_w2214(data); + case 0x2215: return mmio_w2215(data); + + case 0x2220: return mmio_w2220(data); + case 0x2221: return mmio_w2221(data); + case 0x2222: return mmio_w2222(data); + case 0x2223: return mmio_w2223(data); + case 0x2224: return mmio_w2224(data); + case 0x2225: return mmio_w2225(data); + case 0x2226: return mmio_w2226(data); + case 0x2227: return mmio_w2227(data); + case 0x2228: return mmio_w2228(data); + case 0x2229: return mmio_w2229(data); + case 0x222a: return mmio_w222a(data); + + case 0x2230: return mmio_w2230(data); + case 0x2231: return mmio_w2231(data); + case 0x2232: return mmio_w2232(data); + case 0x2233: return mmio_w2233(data); + case 0x2234: return mmio_w2234(data); + case 0x2235: return mmio_w2235(data); + case 0x2236: return mmio_w2236(data); + case 0x2237: return mmio_w2237(data); + case 0x2238: return mmio_w2238(data); + case 0x2239: return mmio_w2239(data); + + case 0x223f: return mmio_w223f(data); + case 0x2240: return mmio_w2240(data); + case 0x2241: return mmio_w2241(data); + case 0x2242: return mmio_w2242(data); + case 0x2243: return mmio_w2243(data); + case 0x2244: return mmio_w2244(data); + case 0x2245: return mmio_w2245(data); + case 0x2246: return mmio_w2246(data); + case 0x2247: return mmio_w2247(data); + case 0x2248: return mmio_w2248(data); + case 0x2249: return mmio_w2249(data); + case 0x224a: return mmio_w224a(data); + case 0x224b: return mmio_w224b(data); + case 0x224c: return mmio_w224c(data); + case 0x224d: return mmio_w224d(data); + case 0x224e: return mmio_w224e(data); + case 0x224f: return mmio_w224f(data); + + case 0x2250: return mmio_w2250(data); + case 0x2251: return mmio_w2251(data); + case 0x2252: return mmio_w2252(data); + case 0x2253: return mmio_w2253(data); + case 0x2254: return mmio_w2254(data); + + case 0x2258: return mmio_w2258(data); + case 0x2259: return mmio_w2259(data); + case 0x225a: return mmio_w225a(data); + case 0x225b: return mmio_w225b(data); + } +} + +#endif diff --git a/mednafen/snes/src/chip/sa1/mmio/mmio.hpp b/mednafen/snes/src/chip/sa1/mmio/mmio.hpp new file mode 100755 index 0000000..e63ad1f --- /dev/null +++ b/mednafen/snes/src/chip/sa1/mmio/mmio.hpp @@ -0,0 +1,256 @@ +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); +Memory& mmio_access(unsigned &addr); + +struct MMIO { + //$2200 CCNT + bool sa1_irq; + bool sa1_rdyb; + bool sa1_resb; + bool sa1_nmi; + uint8 smeg; + + //$2201 SIE + bool cpu_irqen; + bool chdma_irqen; + + //$2202 SIC + bool cpu_irqcl; + bool chdma_irqcl; + + //$2203,$2204 CRV + uint16 crv; + + //$2205,$2206 CNV + uint16 cnv; + + //$2207,$2208 CIV + uint16 civ; + + //$2209 SCNT + bool cpu_irq; + bool cpu_ivsw; + bool cpu_nvsw; + uint8 cmeg; + + //$220a CIE + bool sa1_irqen; + bool timer_irqen; + bool dma_irqen; + bool sa1_nmien; + + //$220b CIC + bool sa1_irqcl; + bool timer_irqcl; + bool dma_irqcl; + bool sa1_nmicl; + + //$220c,$220d SNV + uint16 snv; + + //$220e,$220f SIV + uint16 siv; + + //$2210 TMC + bool hvselb; + bool ven; + bool hen; + + //$2212,$2213 + uint16 hcnt; + + //$2214,$2215 + uint16 vcnt; + + //$2220 CXB + bool cbmode; + uint8 cb; + + //$2221 DXB + bool dbmode; + uint8 db; + + //$2222 EXB + bool ebmode; + uint8 eb; + + //$2223 FXB + bool fbmode; + uint8 fb; + + //$2224 BMAPS + uint8 sbm; + + //$2225 BMAP + bool sw46; + uint8 cbm; + + //$2226 SBWE + bool swen; + + //$2227 CBWE + bool cwen; + + //$2228 BWPA + uint8 bwp; + + //$2229 SIWP + uint8 siwp; + + //$222a CIWP + uint8 ciwp; + + //$2230 DCNT + bool dmaen; + bool dprio; + bool cden; + bool cdsel; + bool dd; + uint8 sd; + + //$2231 CDMA + bool chdend; + uint8 dmasize; + uint8 dmacb; + + //$2232-$2234 SDA + uint32 dsa; + + //$2235-$2237 DDA + uint32 dda; + + //$2238,$2239 DTC + uint16 dtc; + + //$223f BBF + bool bbf; + + //$2240-224f BRF + uint8 brf[16]; + + //$2250 MCNT + bool acm; + bool md; + + //$2251,$2252 MA + uint16 ma; + + //$2253,$2254 MB + uint16 mb; + + //$2258 VBD + bool hl; + uint8 vb; + + //$2259-$225b VDA + uint32 va; + uint8 vbit; + + //$2300 SFR + bool cpu_irqfl; + bool chdma_irqfl; + + //$2301 CFR + bool sa1_irqfl; + bool timer_irqfl; + bool dma_irqfl; + bool sa1_nmifl; + + //$2302,$2303 HCR + uint16 hcr; + + //$2304,$2305 VCR + uint16 vcr; + + //$2306-230a MR + uint64 mr; + + //$230b OF + bool overflow; +} mmio; + +void mmio_w2200(uint8); //CCNT +void mmio_w2201(uint8); //SIE +void mmio_w2202(uint8); //SIC +void mmio_w2203(uint8); //CRVL +void mmio_w2204(uint8); //CRVH +void mmio_w2205(uint8); //CNVL +void mmio_w2206(uint8); //CNVH +void mmio_w2207(uint8); //CIVL +void mmio_w2208(uint8); //CIVH +void mmio_w2209(uint8); //SCNT +void mmio_w220a(uint8); //CIE +void mmio_w220b(uint8); //CIC +void mmio_w220c(uint8); //SNVL +void mmio_w220d(uint8); //SNVH +void mmio_w220e(uint8); //SIVL +void mmio_w220f(uint8); //SIVH +void mmio_w2210(uint8); //TMC +void mmio_w2211(uint8); //CTR +void mmio_w2212(uint8); //HCNTL +void mmio_w2213(uint8); //HCNTH +void mmio_w2214(uint8); //VCNTL +void mmio_w2215(uint8); //VCNTH +void mmio_w2220(uint8); //CXB +void mmio_w2221(uint8); //DXB +void mmio_w2222(uint8); //EXB +void mmio_w2223(uint8); //FXB +void mmio_w2224(uint8); //BMAPS +void mmio_w2225(uint8); //BMAP +void mmio_w2226(uint8); //SBWE +void mmio_w2227(uint8); //CBWE +void mmio_w2228(uint8); //BWPA +void mmio_w2229(uint8); //SIWP +void mmio_w222a(uint8); //CIWP +void mmio_w2230(uint8); //DCNT +void mmio_w2231(uint8); //CDMA +void mmio_w2232(uint8); //SDAL +void mmio_w2233(uint8); //SDAH +void mmio_w2234(uint8); //SDAB +void mmio_w2235(uint8); //DDAL +void mmio_w2236(uint8); //DDAH +void mmio_w2237(uint8); //DDAB +void mmio_w2238(uint8); //DTCL +void mmio_w2239(uint8); //DTCH +void mmio_w223f(uint8); //BBF +void mmio_w2240(uint8); //BRF0 +void mmio_w2241(uint8); //BRF1 +void mmio_w2242(uint8); //BRF2 +void mmio_w2243(uint8); //BRF3 +void mmio_w2244(uint8); //BRF4 +void mmio_w2245(uint8); //BRF5 +void mmio_w2246(uint8); //BRF6 +void mmio_w2247(uint8); //BRF7 +void mmio_w2248(uint8); //BRF8 +void mmio_w2249(uint8); //BRF9 +void mmio_w224a(uint8); //BRFA +void mmio_w224b(uint8); //BRFB +void mmio_w224c(uint8); //BRFC +void mmio_w224d(uint8); //BRFD +void mmio_w224e(uint8); //BRFE +void mmio_w224f(uint8); //BRFF +void mmio_w2250(uint8); //MCNT +void mmio_w2251(uint8); //MAL +void mmio_w2252(uint8); //MAH +void mmio_w2253(uint8); //MBL +void mmio_w2254(uint8); //MBH +void mmio_w2258(uint8); //VBD +void mmio_w2259(uint8); //VDAL +void mmio_w225a(uint8); //VDAH +void mmio_w225b(uint8); //VDAB + +uint8 mmio_r2300(); //SFR +uint8 mmio_r2301(); //CFR +uint8 mmio_r2302(); //HCRL +uint8 mmio_r2303(); //HCRH +uint8 mmio_r2304(); //VCRL +uint8 mmio_r2305(); //VCRH +uint8 mmio_r2306(); //MR [00-07] +uint8 mmio_r2307(); //MR [08-15] +uint8 mmio_r2308(); //MR [16-23] +uint8 mmio_r2309(); //MR [24-31] +uint8 mmio_r230a(); //MR [32-40] +uint8 mmio_r230b(); //OF +uint8 mmio_r230c(); //VDPL +uint8 mmio_r230d(); //VDPH +uint8 mmio_r230e(); //VC diff --git a/mednafen/snes/src/chip/sa1/sa1.cpp b/mednafen/snes/src/chip/sa1/sa1.cpp new file mode 100755 index 0000000..b3f86bc --- /dev/null +++ b/mednafen/snes/src/chip/sa1/sa1.cpp @@ -0,0 +1,327 @@ +#include <../base.hpp> + +#define SA1_CPP +namespace SNES { + +SA1 sa1; + +#include "serialization.cpp" +#include "bus/bus.cpp" +#include "dma/dma.cpp" +#include "memory/memory.cpp" +#include "mmio/mmio.cpp" + +void SA1::enter() { + while(true) { + while(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + + if(mmio.sa1_rdyb || mmio.sa1_resb) { + //SA-1 co-processor is asleep + tick(); + scheduler.sync_copcpu(); + continue; + } + + if(status.interrupt_pending) { + status.interrupt_pending = false; + interrupt(status.interrupt_vector); + } + + (this->*opcode_table[op_readpc()])(); + } +} + +void SA1::last_cycle() { + if(mmio.sa1_nmi && !mmio.sa1_nmicl) { + status.interrupt_pending = true; + status.interrupt_vector = mmio.cnv; + mmio.sa1_nmifl = true; + mmio.sa1_nmicl = 1; + regs.wai = false; + } else if(!regs.p.i) { + if(mmio.timer_irqen && !mmio.timer_irqcl) { + status.interrupt_pending = true; + status.interrupt_vector = mmio.civ; + mmio.timer_irqfl = true; + regs.wai = false; + } else if(mmio.dma_irqen && !mmio.dma_irqcl) { + status.interrupt_pending = true; + status.interrupt_vector = mmio.civ; + mmio.dma_irqfl = true; + regs.wai = false; + } else if(mmio.sa1_irq && !mmio.sa1_irqcl) { + status.interrupt_pending = true; + status.interrupt_vector = mmio.civ; + mmio.sa1_irqfl = true; + regs.wai = false; + } + } +} + +void SA1::interrupt(uint16 vector) { + op_read(regs.pc.d); + op_io(); + if(!regs.e) op_writestack(regs.pc.b); + op_writestack(regs.pc.h); + op_writestack(regs.pc.l); + op_writestack(regs.e ? (regs.p & ~0x10) : regs.p); + regs.pc.w = vector; + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; +} + +bool SA1::interrupt_pending() { + return status.interrupt_pending; +} + +void SA1::tick() { + scheduler.addclocks_cop(2); + if(++status.tick_counter == 0) scheduler.sync_copcpu(); + + //adjust counters: + //note that internally, status counters are in clocks; + //whereas MMIO register counters are in dots (4 clocks = 1 dot) + if(mmio.hvselb == 0) { + //HV timer + status.hcounter += 2; + if(status.hcounter >= 1364) { + status.hcounter = 0; + if(++status.vcounter >= status.scanlines) status.vcounter = 0; + } + } else { + //linear timer + status.hcounter += 2; + status.vcounter += (status.hcounter >> 11); + status.hcounter &= 0x07ff; + status.vcounter &= 0x01ff; + } + + //test counters for timer IRQ + switch((mmio.ven << 1) + (mmio.hen << 0)) { + case 0: break; + case 1: if(status.hcounter == (mmio.hcnt << 2)) trigger_irq(); break; + case 2: if(status.vcounter == mmio.vcnt && status.hcounter == 0) trigger_irq(); break; + case 3: if(status.vcounter == mmio.hcnt && status.hcounter == (mmio.hcnt << 2)) trigger_irq(); break; + } +} + +void SA1::trigger_irq() { + mmio.timer_irqfl = true; + if(mmio.timer_irqen) mmio.timer_irqcl = 0; +} + +void SA1::init() { +} + +void SA1::enable() { +} + +void SA1::power() { + regs.a = regs.x = regs.y = 0x0000; + regs.s = 0x01ff; + + reset(); +} + +void SA1::reset() { + memory::vectorsp.access = 0; + memory::cc1bwram.dma = false; + for(unsigned addr = 0; addr < memory::iram.size(); addr++) { + memory::iram.write(addr, 0x00); + } + vbrbus.init(); + sa1bus.init(); + + regs.pc.d = 0x000000; + regs.x.h = 0x00; + regs.y.h = 0x00; + regs.s.h = 0x01; + regs.d = 0x0000; + regs.db = 0x00; + regs.p = 0x34; + regs.e = 1; + regs.mdr = 0x00; + regs.wai = false; + CPUcore::update_table(); + + status.tick_counter = 0; + + status.interrupt_pending = false; + status.interrupt_vector = 0x0000; + + status.scanlines = (system.region() == System::NTSC ? 262 : 312); + status.vcounter = 0; + status.hcounter = 0; + + dma.line = 0; + + //$2200 CCNT + mmio.sa1_irq = false; + mmio.sa1_rdyb = false; + mmio.sa1_resb = true; + mmio.sa1_nmi = false; + mmio.smeg = 0; + + //$2201 SIE + mmio.cpu_irqen = false; + mmio.chdma_irqen = false; + + //$2202 SIC + mmio.cpu_irqcl = false; + mmio.chdma_irqcl = false; + + //$2203,$2204 CRV + mmio.crv = 0x0000; + + //$2205,$2206 CNV + mmio.cnv = 0x0000; + + //$2207,$2208 CIV + mmio.civ = 0x0000; + + //$2209 SCNT + mmio.cpu_irq = false; + mmio.cpu_ivsw = false; + mmio.cpu_nvsw = false; + mmio.cmeg = 0; + + //$220a CIE + mmio.sa1_irqen = false; + mmio.timer_irqen = false; + mmio.dma_irqen = false; + mmio.sa1_nmien = false; + + //$220b CIC + mmio.sa1_irqcl = false; + mmio.timer_irqcl = false; + mmio.dma_irqcl = false; + mmio.sa1_nmicl = false; + + //$220c,$220d SNV + mmio.snv = 0x0000; + + //$220e,$220f SIV + mmio.siv = 0x0000; + + //$2210 + mmio.hvselb = false; + mmio.ven = false; + mmio.hen = false; + + //$2212,$2213 HCNT + mmio.hcnt = 0x0000; + + //$2214,$2215 VCNT + mmio.vcnt = 0x0000; + + //$2220-2223 CXB, DXB, EXB, FXB + mmio.cbmode = 0; + mmio.dbmode = 0; + mmio.ebmode = 0; + mmio.fbmode = 0; + + mmio.cb = 0x00; + mmio.db = 0x01; + mmio.eb = 0x02; + mmio.fb = 0x03; + + //$2224 BMAPS + mmio.sbm = 0x00; + + //$2225 BMAP + mmio.sw46 = false; + mmio.cbm = 0x00; + + //$2226 SWBE + mmio.swen = false; + + //$2227 CWBE + mmio.cwen = false; + + //$2228 BWPA + mmio.bwp = 0x0f; + + //$2229 SIWP + mmio.siwp = 0x00; + + //$222a CIWP + mmio.ciwp = 0x00; + + //$2230 DCNT + mmio.dmaen = false; + mmio.dprio = false; + mmio.cden = false; + mmio.cdsel = false; + mmio.dd = 0; + mmio.sd = 0; + + //$2231 CDMA + mmio.chdend = false; + mmio.dmasize = 0; + mmio.dmacb = 0; + + //$2232-$2234 SDA + mmio.dsa = 0x000000; + + //$2235-$2237 DDA + mmio.dda = 0x000000; + + //$2238,$2239 DTC + mmio.dtc = 0x0000; + + //$223f BBF + mmio.bbf = 0; + + //$2240-$224f BRF + for(unsigned i = 0; i < 16; i++) { + mmio.brf[i] = 0x00; + } + + //$2250 MCNT + mmio.acm = 0; + mmio.md = 0; + + //$2251,$2252 MA + mmio.ma = 0x0000; + + //$2253,$2254 MB + mmio.mb = 0x0000; + + //$2258 VBD + mmio.hl = false; + mmio.vb = 16; + + //$2259-$225b + mmio.va = 0x000000; + mmio.vbit = 0; + + //$2300 SFR + mmio.cpu_irqfl = false; + mmio.chdma_irqfl = false; + + //$2301 CFR + mmio.sa1_irqfl = false; + mmio.timer_irqfl = false; + mmio.dma_irqfl = false; + mmio.sa1_nmifl = false; + + //$2302,$2303 HCR + mmio.hcr = 0x0000; + + //$2304,$2305 VCR + mmio.vcr = 0x0000; + + //$2306-$230a MR + mmio.mr = 0; + + //$230b + mmio.overflow = false; +} + +SA1::SA1() { +} + +}; diff --git a/mednafen/snes/src/chip/sa1/sa1.hpp b/mednafen/snes/src/chip/sa1/sa1.hpp new file mode 100755 index 0000000..83abc93 --- /dev/null +++ b/mednafen/snes/src/chip/sa1/sa1.hpp @@ -0,0 +1,38 @@ +#include "bus/bus.hpp" + +class SA1 : public CPUcore, public MMIO { +public: + #include "dma/dma.hpp" + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + + struct Status { + uint8 tick_counter; + + bool interrupt_pending; + uint16 interrupt_vector; + + uint16 scanlines; + uint16 vcounter; + uint16 hcounter; + } status; + + void enter(); + void interrupt(uint16 vector); + void tick(); + + alwaysinline void trigger_irq(); + alwaysinline void last_cycle(); + alwaysinline bool interrupt_pending(); + + void init(); + void enable(); + void power(); + void reset(); + + void serialize(serializer&); + SA1(); +}; + +extern SA1 sa1; +extern SA1Bus sa1bus; diff --git a/mednafen/snes/src/chip/sa1/serialization.cpp b/mednafen/snes/src/chip/sa1/serialization.cpp new file mode 100755 index 0000000..9d599c1 --- /dev/null +++ b/mednafen/snes/src/chip/sa1/serialization.cpp @@ -0,0 +1,150 @@ +#ifdef SA1_CPP + +void SA1::serialize(serializer &s) { + CPUcore::core_serialize(s); + + //sa1.hpp + s.integer(status.tick_counter); + + s.integer(status.interrupt_pending); + s.integer(status.interrupt_vector); + + s.integer(status.scanlines); + s.integer(status.vcounter); + s.integer(status.hcounter); + + //bus/bus.hpp + s.array(memory::iram.data(), memory::iram.size()); + + memory::vectorsp.sync(); + + s.integer(memory::cc1bwram.dma); + + //dma/dma.hpp + s.integer(dma.line); + + //mmio/mmio.hpp + s.integer(mmio.sa1_irq); + s.integer(mmio.sa1_rdyb); + s.integer(mmio.sa1_resb); + s.integer(mmio.sa1_nmi); + s.integer(mmio.smeg); + + s.integer(mmio.cpu_irqen); + s.integer(mmio.chdma_irqen); + + s.integer(mmio.cpu_irqcl); + s.integer(mmio.chdma_irqcl); + + s.integer(mmio.crv); + + s.integer(mmio.cnv); + + s.integer(mmio.civ); + + s.integer(mmio.cpu_irq); + s.integer(mmio.cpu_ivsw); + s.integer(mmio.cpu_nvsw); + s.integer(mmio.cmeg); + + s.integer(mmio.sa1_irqen); + s.integer(mmio.timer_irqen); + s.integer(mmio.dma_irqen); + s.integer(mmio.sa1_nmien); + + s.integer(mmio.sa1_irqcl); + s.integer(mmio.timer_irqcl); + s.integer(mmio.dma_irqcl); + s.integer(mmio.sa1_nmicl); + + s.integer(mmio.snv); + + s.integer(mmio.siv); + + s.integer(mmio.hvselb); + s.integer(mmio.ven); + s.integer(mmio.hen); + + s.integer(mmio.hcnt); + + s.integer(mmio.vcnt); + + s.integer(mmio.cbmode); + s.integer(mmio.cb); + + s.integer(mmio.dbmode); + s.integer(mmio.db); + + s.integer(mmio.ebmode); + s.integer(mmio.eb); + + s.integer(mmio.fbmode); + s.integer(mmio.fb); + + s.integer(mmio.sbm); + + s.integer(mmio.sw46); + s.integer(mmio.cbm); + + s.integer(mmio.swen); + + s.integer(mmio.cwen); + + s.integer(mmio.bwp); + + s.integer(mmio.siwp); + + s.integer(mmio.ciwp); + + s.integer(mmio.dmaen); + s.integer(mmio.dprio); + s.integer(mmio.cden); + s.integer(mmio.cdsel); + s.integer(mmio.dd); + s.integer(mmio.sd); + + s.integer(mmio.chdend); + s.integer(mmio.dmasize); + s.integer(mmio.dmacb); + + s.integer(mmio.dsa); + + s.integer(mmio.dda); + + s.integer(mmio.dtc); + + s.integer(mmio.bbf); + + s.array(mmio.brf); + + s.integer(mmio.acm); + s.integer(mmio.md); + + s.integer(mmio.ma); + + s.integer(mmio.mb); + + s.integer(mmio.hl); + s.integer(mmio.vb); + + s.integer(mmio.va); + s.integer(mmio.vbit); + + s.integer(mmio.cpu_irqfl); + s.integer(mmio.chdma_irqfl); + + s.integer(mmio.sa1_irqfl); + s.integer(mmio.timer_irqfl); + s.integer(mmio.dma_irqfl); + s.integer(mmio.sa1_nmifl); + + s.integer(mmio.hcr); + + s.integer(mmio.vcr); + + s.integer(mmio.mr); + + s.integer(mmio.overflow); +} + +#endif diff --git a/mednafen/snes/src/chip/sdd1/sdd1.cpp b/mednafen/snes/src/chip/sdd1/sdd1.cpp new file mode 100755 index 0000000..171a8ec --- /dev/null +++ b/mednafen/snes/src/chip/sdd1/sdd1.cpp @@ -0,0 +1,161 @@ +#include <../base.hpp> + +#define SDD1_CPP +namespace SNES { + +SDD1 sdd1; + +#include "serialization.cpp" +#include "sdd1emu.cpp" + +void SDD1::init() {} + +void SDD1::enable() { + //hook S-CPU DMA MMIO registers to gather information for struct dma[]; + //buffer address and transfer size information for use in SDD1::read() + for(unsigned i = 0x4300; i <= 0x437f; i++) { + cpu_mmio[i & 0x7f] = memory::mmio.mmio[i - 0x2000]; + memory::mmio.map(i, *this); + } + + //hook S-DD1 MMIO registers + for(unsigned i = 0x4800; i <= 0x4807; i++) { + memory::mmio.map(i, *this); + } +} + +void SDD1::power() { + reset(); +} + +void SDD1::reset() { + sdd1_enable = 0x00; + xfer_enable = 0x00; + + mmc[0] = 0 << 20; + mmc[1] = 1 << 20; + mmc[2] = 2 << 20; + mmc[3] = 3 << 20; + + for(unsigned i = 0; i < 8; i++) { + dma[i].addr = 0; + dma[i].size = 0; + } + + buffer.ready = false; + + bus.map(Bus::MapDirect, 0xc0, 0xff, 0x0000, 0xffff, *this); +} + +uint8 SDD1::mmio_read(unsigned addr) { + addr &= 0xffff; + + if((addr & 0x4380) == 0x4300) { + return cpu_mmio[addr & 0x7f]->mmio_read(addr); + } + + switch(addr) { + case 0x4804: return mmc[0] >> 20; + case 0x4805: return mmc[1] >> 20; + case 0x4806: return mmc[2] >> 20; + case 0x4807: return mmc[3] >> 20; + } + + return cpu.regs.mdr; +} + +void SDD1::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + if((addr & 0x4380) == 0x4300) { + unsigned channel = (addr >> 4) & 7; + switch(addr & 15) { + case 2: dma[channel].addr = (dma[channel].addr & 0xffff00) + (data << 0); break; + case 3: dma[channel].addr = (dma[channel].addr & 0xff00ff) + (data << 8); break; + case 4: dma[channel].addr = (dma[channel].addr & 0x00ffff) + (data << 16); break; + + case 5: dma[channel].size = (dma[channel].size & 0xff00) + (data << 0); break; + case 6: dma[channel].size = (dma[channel].size & 0x00ff) + (data << 8); break; + } + return cpu_mmio[addr & 0x7f]->mmio_write(addr, data); + } + + switch(addr) { + case 0x4800: sdd1_enable = data; break; + case 0x4801: xfer_enable = data; break; + + case 0x4804: mmc[0] = data << 20; break; + case 0x4805: mmc[1] = data << 20; break; + case 0x4806: mmc[2] = data << 20; break; + case 0x4807: mmc[3] = data << 20; break; + } +} + +//SDD1::read() is mapped to $[c0-ff]:[0000-ffff] +//the design is meant to be as close to the hardware design as possible, thus this code +//avoids adding S-DD1 hooks inside S-CPU::DMA emulation. +// +//the real S-DD1 cannot see $420b (DMA enable) writes, as they are not placed on the bus. +//however, $43x0-$43xf writes (DMAx channel settings) most likely do appear on the bus. +//the S-DD1 also requires fixed addresses for transfers, which wouldn't be necessary if +//it could see $420b writes (eg it would know when the transfer should begin.) +// +//the hardware needs a way to distinguish program code after $4801 writes from DMA +//decompression that follows soon after. +// +//the only plausible design for hardware would be for the S-DD1 to spy on DMAx settings, +//and begin spooling decompression on writes to $4801 that activate a channel. after that, +//it feeds decompressed data only when the ROM read address matches the DMA channel address. +// +//the actual S-DD1 transfer can occur on any channel, but it is most likely limited to +//one transfer per $420b write (for spooling purposes). however, this is not known for certain. +uint8 SDD1::read(unsigned addr) { + if(sdd1_enable & xfer_enable) { + //at least one channel has S-DD1 decompression enabled ... + for(unsigned i = 0; i < 8; i++) { + if(sdd1_enable & xfer_enable & (1 << i)) { + //S-DD1 always uses fixed transfer mode, so address will not change during transfer + if(addr == dma[i].addr) { + if(!buffer.ready) { + //first byte read for channel performs full decompression. + //this really should stream byte-by-byte, but it's not necessary since the size is known + buffer.offset = 0; + buffer.size = dma[i].size ? dma[i].size : 65536; + + //sdd1emu calls this function; it needs to access uncompressed data; + //so temporarily disable decompression mode for decompress() call. + uint8 temp = sdd1_enable; + sdd1_enable = false; + sdd1emu.decompress(addr, buffer.size, buffer.data); + sdd1_enable = temp; + + buffer.ready = true; + } + + //fetch a decompressed byte; once buffer is depleted, disable channel and invalidate buffer + uint8 data = buffer.data[(uint16)buffer.offset++]; + if(buffer.offset >= buffer.size) { + buffer.ready = false; + xfer_enable &= ~(1 << i); + } + + return data; + } //address matched + } //channel enabled + } //channel loop + } //S-DD1 decompressor enabled + + //S-DD1 decompression mode inactive; return ROM data + return memory::cartrom.read(mmc[(addr >> 20) & 3] + (addr & 0x0fffff)); +} + +void SDD1::write(unsigned addr, uint8 data) { +} + +SDD1::SDD1() { +} + +SDD1::~SDD1() { +} + +}; diff --git a/mednafen/snes/src/chip/sdd1/sdd1.hpp b/mednafen/snes/src/chip/sdd1/sdd1.hpp new file mode 100755 index 0000000..fd95f6b --- /dev/null +++ b/mednafen/snes/src/chip/sdd1/sdd1.hpp @@ -0,0 +1,41 @@ +#include "sdd1emu.hpp" + +class SDD1 : public MMIO, public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + SDD1(); + ~SDD1(); + +private: + MMIO *cpu_mmio[0x80]; //bus spying hooks to glean information for struct dma[] + + uint8 sdd1_enable; //channel bit-mask + uint8 xfer_enable; //channel bit-mask + unsigned mmc[4]; //memory map controller ROM indices + + struct { + unsigned addr; //$43x2-$43x4 -- DMA transfer address + uint16 size; //$43x5-$43x6 -- DMA transfer size + } dma[8]; + + SDD1emu sdd1emu; + struct { + uint8 data[65536]; //pointer to decompressed S-DD1 data + uint16 offset; //read index into S-DD1 decompression buffer + unsigned size; //length of data buffer; reads decrement counter, set ready to false at 0 + bool ready; //true when data[] is valid; false to invoke sdd1emu.decompress() + } buffer; +}; + +extern SDD1 sdd1; diff --git a/mednafen/snes/src/chip/sdd1/sdd1emu.cpp b/mednafen/snes/src/chip/sdd1/sdd1emu.cpp new file mode 100755 index 0000000..a7eb364 --- /dev/null +++ b/mednafen/snes/src/chip/sdd1/sdd1emu.cpp @@ -0,0 +1,452 @@ +#ifdef SDD1_CPP + +/************************************************************************ + +S-DD1'algorithm emulation code +------------------------------ + +Author: Andreas Naive +Date: August 2003 +Last update: October 2004 + +This code is Public Domain. There is no copyright holded by the author. +Said this, the author wish to explicitly emphasize his inalienable moral rights +over this piece of intelectual work and the previous research that made it +possible, as recognized by most of the copyright laws around the world. + +This code is provided 'as-is', with no warranty, expressed or implied. +No responsability is assumed by the author in connection with it. + +The author is greatly indebted with The Dumper, without whose help and +patience providing him with real S-DD1 data the research would have never been +possible. He also wish to note that in the very beggining of his research, +Neviksti had done some steps in the right direction. By last, the author is +indirectly indebted to all the people that worked and contributed in the +S-DD1 issue in the past. + +An algorithm's documentation is available as a separate document. +The implementation is obvious when the algorithm is +understood. + +************************************************************************/ + +typedef uint8 bool8; +#define SDD1_read(__addr) (sdd1.read(__addr)) + +//////////////////////////////////////////////////// + + +void SDD1_IM::prepareDecomp(uint32 in_buf) { + + byte_ptr=in_buf; + bit_count=4; + +} + +//////////////////////////////////////////////////// + + +uint8 SDD1_IM::getCodeword(uint8 code_len) { + + uint8 codeword; + uint8 comp_count; + + codeword = (SDD1_read(byte_ptr))<>(9-bit_count); + bit_count+=code_len; + } + + if (bit_count & 0x08) { + byte_ptr++; + bit_count&=0x07; + } + + return codeword; + +} + +////////////////////////////////////////////////////// + + +SDD1_GCD::SDD1_GCD(SDD1_IM *associatedIM) : + IM(associatedIM) +{ + +} + +////////////////////////////////////////////////////// + + +void SDD1_GCD::getRunCount(uint8 code_num, uint8 *MPScount, bool8 *LPSind) { + + const uint8 run_count[] = { + 0x00, 0x00, 0x01, 0x00, 0x03, 0x01, 0x02, 0x00, + 0x07, 0x03, 0x05, 0x01, 0x06, 0x02, 0x04, 0x00, + 0x0f, 0x07, 0x0b, 0x03, 0x0d, 0x05, 0x09, 0x01, + 0x0e, 0x06, 0x0a, 0x02, 0x0c, 0x04, 0x08, 0x00, + 0x1f, 0x0f, 0x17, 0x07, 0x1b, 0x0b, 0x13, 0x03, + 0x1d, 0x0d, 0x15, 0x05, 0x19, 0x09, 0x11, 0x01, + 0x1e, 0x0e, 0x16, 0x06, 0x1a, 0x0a, 0x12, 0x02, + 0x1c, 0x0c, 0x14, 0x04, 0x18, 0x08, 0x10, 0x00, + 0x3f, 0x1f, 0x2f, 0x0f, 0x37, 0x17, 0x27, 0x07, + 0x3b, 0x1b, 0x2b, 0x0b, 0x33, 0x13, 0x23, 0x03, + 0x3d, 0x1d, 0x2d, 0x0d, 0x35, 0x15, 0x25, 0x05, + 0x39, 0x19, 0x29, 0x09, 0x31, 0x11, 0x21, 0x01, + 0x3e, 0x1e, 0x2e, 0x0e, 0x36, 0x16, 0x26, 0x06, + 0x3a, 0x1a, 0x2a, 0x0a, 0x32, 0x12, 0x22, 0x02, + 0x3c, 0x1c, 0x2c, 0x0c, 0x34, 0x14, 0x24, 0x04, + 0x38, 0x18, 0x28, 0x08, 0x30, 0x10, 0x20, 0x00, + 0x7f, 0x3f, 0x5f, 0x1f, 0x6f, 0x2f, 0x4f, 0x0f, + 0x77, 0x37, 0x57, 0x17, 0x67, 0x27, 0x47, 0x07, + 0x7b, 0x3b, 0x5b, 0x1b, 0x6b, 0x2b, 0x4b, 0x0b, + 0x73, 0x33, 0x53, 0x13, 0x63, 0x23, 0x43, 0x03, + 0x7d, 0x3d, 0x5d, 0x1d, 0x6d, 0x2d, 0x4d, 0x0d, + 0x75, 0x35, 0x55, 0x15, 0x65, 0x25, 0x45, 0x05, + 0x79, 0x39, 0x59, 0x19, 0x69, 0x29, 0x49, 0x09, + 0x71, 0x31, 0x51, 0x11, 0x61, 0x21, 0x41, 0x01, + 0x7e, 0x3e, 0x5e, 0x1e, 0x6e, 0x2e, 0x4e, 0x0e, + 0x76, 0x36, 0x56, 0x16, 0x66, 0x26, 0x46, 0x06, + 0x7a, 0x3a, 0x5a, 0x1a, 0x6a, 0x2a, 0x4a, 0x0a, + 0x72, 0x32, 0x52, 0x12, 0x62, 0x22, 0x42, 0x02, + 0x7c, 0x3c, 0x5c, 0x1c, 0x6c, 0x2c, 0x4c, 0x0c, + 0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, + 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, + 0x70, 0x30, 0x50, 0x10, 0x60, 0x20, 0x40, 0x00, + }; + + uint8 codeword=IM->getCodeword(code_num); + + if (codeword & 0x80) { + *LPSind=1; + *MPScount=run_count[codeword>>(code_num^0x07)]; + } + else { + *MPScount=(1<getRunCount(code_num, &MPScount, &LPSind); + + if (MPScount) { + bit=0; + MPScount--; + } + else { + bit=1; + LPSind=0; + } + + if (MPScount || LPSind) (*endOfRun)=0; + else (*endOfRun)=1; + + return bit; + +} + +///////////////////////////////////////////////// + + +SDD1_PEM::SDD1_PEM(SDD1_BG *associatedBG0, SDD1_BG *associatedBG1, + SDD1_BG *associatedBG2, SDD1_BG *associatedBG3, + SDD1_BG *associatedBG4, SDD1_BG *associatedBG5, + SDD1_BG *associatedBG6, SDD1_BG *associatedBG7) { + + BG[0]=associatedBG0; + BG[1]=associatedBG1; + BG[2]=associatedBG2; + BG[3]=associatedBG3; + BG[4]=associatedBG4; + BG[5]=associatedBG5; + BG[6]=associatedBG6; + BG[7]=associatedBG7; + +} + +///////////////////////////////////////////////////////// + + +const SDD1_PEM::state SDD1_PEM::evolution_table[]={ + { 0,25,25}, + { 0, 2, 1}, + { 0, 3, 1}, + { 0, 4, 2}, + { 0, 5, 3}, + { 1, 6, 4}, + { 1, 7, 5}, + { 1, 8, 6}, + { 1, 9, 7}, + { 2,10, 8}, + { 2,11, 9}, + { 2,12,10}, + { 2,13,11}, + { 3,14,12}, + { 3,15,13}, + { 3,16,14}, + { 3,17,15}, + { 4,18,16}, + { 4,19,17}, + { 5,20,18}, + { 5,21,19}, + { 6,22,20}, + { 6,23,21}, + { 7,24,22}, + { 7,24,23}, + { 0,26, 1}, + { 1,27, 2}, + { 2,28, 4}, + { 3,29, 8}, + { 4,30,12}, + { 5,31,16}, + { 6,32,18}, + { 7,24,22} + }; + +////////////////////////////////////////////////////// + + +void SDD1_PEM::prepareDecomp(void) { + + for (uint8 i=0; i<32; i++) { + contextInfo[i].status=0; + contextInfo[i].MPS=0; + } + +} + +///////////////////////////////////////////////////////// + + +uint8 SDD1_PEM::getBit(uint8 context) { + + bool8 endOfRun; + uint8 bit; + + SDD1_ContextInfo *pContInfo=&contextInfo[context]; + uint8 currStatus = pContInfo->status; + const state *pState=&SDD1_PEM::evolution_table[currStatus]; + uint8 currentMPS=pContInfo->MPS; + + bit=(BG[pState->code_num])->getBit(&endOfRun); + + if (endOfRun) + if (bit) { + if (!(currStatus & 0xfe)) (pContInfo->MPS)^=0x01; + (pContInfo->status)=pState->nextIfLPS; + } + else + (pContInfo->status)=pState->nextIfMPS; + + return bit^currentMPS; + +} + +////////////////////////////////////////////////////////////// + + +SDD1_CM::SDD1_CM(SDD1_PEM *associatedPEM) : + PEM(associatedPEM) +{ + +} + +////////////////////////////////////////////////////////////// + + +void SDD1_CM::prepareDecomp(uint32 first_byte) { + + bitplanesInfo = SDD1_read(first_byte) & 0xc0; + contextBitsInfo = SDD1_read(first_byte) & 0x30; + bit_number=0; + for (int i=0; i<8; i++) prevBitplaneBits[i]=0; + switch (bitplanesInfo) { + case 0x00: + currBitplane = 1; + break; + case 0x40: + currBitplane = 7; + break; + case 0x80: + currBitplane = 3; + } + +} + +///////////////////////////////////////////////////////////// + + +uint8 SDD1_CM::getBit(void) { + + uint8 currContext; + uint16 *context_bits; + + switch (bitplanesInfo) { + case 0x00: + currBitplane ^= 0x01; + break; + case 0x40: + currBitplane ^= 0x01; + if (!(bit_number & 0x7f)) currBitplane = ((currBitplane+2) & 0x07); + break; + case 0x80: + currBitplane ^= 0x01; + if (!(bit_number & 0x7f)) currBitplane ^= 0x02; + break; + case 0xc0: + currBitplane = bit_number & 0x07; + } + + context_bits = &prevBitplaneBits[currBitplane]; + + currContext=(currBitplane & 0x01)<<4; + switch (contextBitsInfo) { + case 0x00: + currContext|=((*context_bits & 0x01c0)>>5)|(*context_bits & 0x0001); + break; + case 0x10: + currContext|=((*context_bits & 0x0180)>>5)|(*context_bits & 0x0001); + break; + case 0x20: + currContext|=((*context_bits & 0x00c0)>>5)|(*context_bits & 0x0001); + break; + case 0x30: + currContext|=((*context_bits & 0x0180)>>5)|(*context_bits & 0x0003); + } + + uint8 bit=PEM->getBit(currContext); + + *context_bits <<= 1; + *context_bits |= bit; + + bit_number++; + + return bit; + +} + +////////////////////////////////////////////////// + + +SDD1_OL::SDD1_OL(SDD1_CM *associatedCM) : + CM(associatedCM) +{ + +} + +/////////////////////////////////////////////////// + + +void SDD1_OL::prepareDecomp(uint32 first_byte, uint16 out_len, uint8 *out_buf) { + + bitplanesInfo = SDD1_read(first_byte) & 0xc0; + length=out_len; + buffer=out_buf; + +} + +/////////////////////////////////////////////////// + + +void SDD1_OL::launch(void) { + + uint8 i; + uint8 register1, register2; + + switch (bitplanesInfo) { + case 0x00: + case 0x40: + case 0x80: + i=1; + do { //if length==0, we output 2^16 bytes + if (!i) { + *(buffer++)=register2; + i=~i; + } + else { + for (register1=register2=0, i=0x80; i; i>>=1) { + if (CM->getBit()) register1 |= i; + if (CM->getBit()) register2 |= i; + } + *(buffer++)=register1; + } + } while (--length); + break; + case 0xc0: + do { + for (register1=0, i=0x01; i; i<<=1) { + if (CM->getBit()) register1 |= i; + } + *(buffer++)=register1; + } while (--length); + } + +} + +/////////////////////////////////////////////////////// + + +void SDD1emu::decompress(uint32 in_buf, uint16 out_len, uint8 *out_buf) { + + IM.prepareDecomp(in_buf); + BG0.prepareDecomp(); + BG1.prepareDecomp(); + BG2.prepareDecomp(); + BG3.prepareDecomp(); + BG4.prepareDecomp(); + BG5.prepareDecomp(); + BG6.prepareDecomp(); + BG7.prepareDecomp(); + PEM.prepareDecomp(); + CM.prepareDecomp(in_buf); + OL.prepareDecomp(in_buf, out_len, out_buf); + + OL.launch(); + +} + +//////////////////////////////////////////////////////////// + + +SDD1emu::SDD1emu() : + GCD(&IM), + BG0(&GCD, 0), BG1(&GCD, 1), BG2(&GCD, 2), BG3(&GCD, 3), + BG4(&GCD, 4), BG5(&GCD, 5), BG6(&GCD, 6), BG7(&GCD, 7), + PEM(&BG0, &BG1, &BG2, &BG3, &BG4, &BG5, &BG6, &BG7), + CM(&PEM), + OL(&CM) +{ + +} + +/////////////////////////////////////////////////////////// + +#endif diff --git a/mednafen/snes/src/chip/sdd1/sdd1emu.hpp b/mednafen/snes/src/chip/sdd1/sdd1emu.hpp new file mode 100755 index 0000000..00187b3 --- /dev/null +++ b/mednafen/snes/src/chip/sdd1/sdd1emu.hpp @@ -0,0 +1,164 @@ +/************************************************************************ + +S-DD1'algorithm emulation code +------------------------------ + +Author: Andreas Naive +Date: August 2003 +Last update: October 2004 + +This code is Public Domain. There is no copyright holded by the author. +Said this, the author wish to explicitly emphasize his inalienable moral rights +over this piece of intelectual work and the previous research that made it +possible, as recognized by most of the copyright laws around the world. + +This code is provided 'as-is', with no warranty, expressed or implied. +No responsability is assumed by the author in connection with it. + +The author is greatly indebted with The Dumper, without whose help and +patience providing him with real S-DD1 data the research would have never been +possible. He also wish to note that in the very beggining of his research, +Neviksti had done some steps in the right direction. By last, the author is +indirectly indebted to all the people that worked and contributed in the +S-DD1 issue in the past. + +An algorithm's documentation is available as a separate document. +The implementation is obvious when the algorithm is +understood. + +************************************************************************/ + +#define bool8 uint8 + +class SDD1_IM { //Input Manager + + public: + SDD1_IM(void) {} + void prepareDecomp(uint32 in_buf); + uint8 getCodeword(const uint8 code_len); + + private: + uint32 byte_ptr; + uint8 bit_count; + +}; + +//////////////////////////////////////////////////// + + +class SDD1_GCD { //Golomb-Code Decoder + + public: + SDD1_GCD(SDD1_IM *associatedIM); + void getRunCount(uint8 code_num, uint8 *MPScount, bool8 *LPSind); + + private: + SDD1_IM *const IM; + +}; + +////////////////////////////////////////////////////// + + +class SDD1_BG { // Bits Generator + + public: + SDD1_BG(SDD1_GCD *associatedGCD, uint8 code); + void prepareDecomp(void); + uint8 getBit(bool8 *endOfRun); + + private: + SDD1_GCD *const GCD; + const uint8 code_num; + uint8 MPScount; + bool8 LPSind; + +}; + +//////////////////////////////////////////////// + + +class SDD1_PEM { //Probability Estimation Module + + public: + SDD1_PEM(SDD1_BG *associatedBG0, SDD1_BG *associatedBG1, + SDD1_BG *associatedBG2, SDD1_BG *associatedBG3, + SDD1_BG *associatedBG4, SDD1_BG *associatedBG5, + SDD1_BG *associatedBG6, SDD1_BG *associatedBG7); + void prepareDecomp(void); + uint8 getBit(uint8 context); + + private: + struct state { + uint8 code_num; + uint8 nextIfMPS; + uint8 nextIfLPS; + }; + static const state evolution_table[]; + struct SDD1_ContextInfo { + uint8 status; + uint8 MPS; + } contextInfo[32]; + SDD1_BG * BG[8]; + +}; + +/////////////////////////////////////////////////// + + +class SDD1_CM { //Context Model + + public: + SDD1_CM(SDD1_PEM *associatedPEM); + void prepareDecomp(uint32 first_byte); + uint8 getBit(void); + + private: + uint8 bitplanesInfo; + uint8 contextBitsInfo; + uint8 bit_number; + uint8 currBitplane; + uint16 prevBitplaneBits[8]; + SDD1_PEM *const PEM; + +}; + +/////////////////////////////////////////////////// + + +class SDD1_OL { //Output Logic + + public: + SDD1_OL(SDD1_CM *associatedCM); + void prepareDecomp(uint32 first_byte, uint16 out_len, uint8 *out_buf); + void launch(void); + + private: + uint8 bitplanesInfo; + uint16 length; + uint8 *buffer; + SDD1_CM *const CM; + +}; + +///////////////////////////////////////////////////////// + + +class SDD1emu { + + public: + SDD1emu(void); + void decompress(uint32 in_buf, uint16 out_len, uint8 *out_buf); + + private: + SDD1_IM IM; + SDD1_GCD GCD; + SDD1_BG BG0; SDD1_BG BG1; SDD1_BG BG2; SDD1_BG BG3; + SDD1_BG BG4; SDD1_BG BG5; SDD1_BG BG6; SDD1_BG BG7; + SDD1_PEM PEM; + SDD1_CM CM; + SDD1_OL OL; + +}; + +#undef bool8 diff --git a/mednafen/snes/src/chip/sdd1/serialization.cpp b/mednafen/snes/src/chip/sdd1/serialization.cpp new file mode 100755 index 0000000..87da13f --- /dev/null +++ b/mednafen/snes/src/chip/sdd1/serialization.cpp @@ -0,0 +1,19 @@ +#ifdef SDD1_CPP + +void SDD1::serialize(serializer &s) { + s.integer(sdd1_enable); + s.integer(xfer_enable); + s.array(mmc); + + for(unsigned n = 0; n < 8; n++) { + s.integer(dma[n].addr); + s.integer(dma[n].size); + } + + s.array(buffer.data); + s.integer(buffer.offset); + s.integer(buffer.size); + s.integer(buffer.ready); +} + +#endif diff --git a/mednafen/snes/src/chip/spc7110/decomp.cpp b/mednafen/snes/src/chip/spc7110/decomp.cpp new file mode 100755 index 0000000..99d4e1b --- /dev/null +++ b/mednafen/snes/src/chip/spc7110/decomp.cpp @@ -0,0 +1,511 @@ +#ifdef SPC7110_CPP + +uint8 SPC7110Decomp::read() { + if(decomp_buffer_length == 0) { + //decompress at least (decomp_buffer_size / 2) bytes to the buffer + switch(decomp_mode) { + case 0: mode0(false); break; + case 1: mode1(false); break; + case 2: mode2(false); break; + default: return 0x00; + } + } + + uint8 data = decomp_buffer[decomp_buffer_rdoffset++]; + decomp_buffer_rdoffset &= decomp_buffer_size - 1; + decomp_buffer_length--; + return data; +} + +void SPC7110Decomp::write(uint8 data) { + decomp_buffer[decomp_buffer_wroffset++] = data; + decomp_buffer_wroffset &= decomp_buffer_size - 1; + decomp_buffer_length++; +} + +uint8 SPC7110Decomp::dataread() { + unsigned size = memory::cartrom.size() - 0x100000; + while(decomp_offset >= size) decomp_offset -= size; + return memory::cartrom.read(0x100000 + decomp_offset++); +} + +void SPC7110Decomp::init(unsigned mode, unsigned offset, unsigned index) { + decomp_mode = mode; + decomp_offset = offset; + + decomp_buffer_rdoffset = 0; + decomp_buffer_wroffset = 0; + decomp_buffer_length = 0; + + //reset context states + for(unsigned i = 0; i < 32; i++) { + context[i].index = 0; + context[i].invert = 0; + } + + switch(decomp_mode) { + case 0: mode0(true); break; + case 1: mode1(true); break; + case 2: mode2(true); break; + } + + //decompress up to requested output data index + while(index--) read(); +} + +// + +void SPC7110Decomp::mode0(bool init) { + static uint8 val, in, span; + static int out, inverts, lps, in_count; + + if(init == true) { + out = inverts = lps = 0; + span = 0xff; + val = dataread(); + in = dataread(); + in_count = 8; + return; + } + + while(decomp_buffer_length < (decomp_buffer_size >> 1)) { + for(unsigned bit = 0; bit < 8; bit++) { + //get context + uint8 mask = (1 << (bit & 3)) - 1; + uint8 con = mask + ((inverts & mask) ^ (lps & mask)); + if(bit > 3) con += 15; + + //get prob and mps + unsigned prob = probability(con); + unsigned mps = (((out >> 15) & 1) ^ context[con].invert); + + //get bit + unsigned flag_lps; + if(val <= span - prob) { //mps + span = span - prob; + out = (out << 1) + mps; + flag_lps = 0; + } else { //lps + val = val - (span - (prob - 1)); + span = prob - 1; + out = (out << 1) + 1 - mps; + flag_lps = 1; + } + + //renormalize + unsigned shift = 0; + while(span < 0x7f) { + shift++; + + span = (span << 1) + 1; + val = (val << 1) + (in >> 7); + + in <<= 1; + if(--in_count == 0) { + in = dataread(); + in_count = 8; + } + } + + //update processing info + lps = (lps << 1) + flag_lps; + inverts = (inverts << 1) + context[con].invert; + + //update context state + if(flag_lps & toggle_invert(con)) context[con].invert ^= 1; + if(flag_lps) context[con].index = next_lps(con); + else if(shift) context[con].index = next_mps(con); + } + + //save byte + write(out); + } +} + +void SPC7110Decomp::mode1(bool init) { + static int pixelorder[4], realorder[4]; + static uint8 in, val, span; + static int out, inverts, lps, in_count; + + if(init == true) { + for(unsigned i = 0; i < 4; i++) pixelorder[i] = i; + out = inverts = lps = 0; + span = 0xff; + val = dataread(); + in = dataread(); + in_count = 8; + return; + } + + while(decomp_buffer_length < (decomp_buffer_size >> 1)) { + for(unsigned pixel = 0; pixel < 8; pixel++) { + //get first symbol context + unsigned a = ((out >> (1 * 2)) & 3); + unsigned b = ((out >> (7 * 2)) & 3); + unsigned c = ((out >> (8 * 2)) & 3); + unsigned con = (a == b) ? (b != c) : (b == c) ? 2 : 4 - (a == c); + + //update pixel order + unsigned m, n; + for(m = 0; m < 4; m++) if(pixelorder[m] == a) break; + for(n = m; n > 0; n--) pixelorder[n] = pixelorder[n - 1]; + pixelorder[0] = a; + + //calculate the real pixel order + for(m = 0; m < 4; m++) realorder[m] = pixelorder[m]; + + //rotate reference pixel c value to top + for(m = 0; m < 4; m++) if(realorder[m] == c) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = c; + + //rotate reference pixel b value to top + for(m = 0; m < 4; m++) if(realorder[m] == b) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = b; + + //rotate reference pixel a value to top + for(m = 0; m < 4; m++) if(realorder[m] == a) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = a; + + //get 2 symbols + for(unsigned bit = 0; bit < 2; bit++) { + //get prob + unsigned prob = probability(con); + + //get symbol + unsigned flag_lps; + if(val <= span - prob) { //mps + span = span - prob; + flag_lps = 0; + } else { //lps + val = val - (span - (prob - 1)); + span = prob - 1; + flag_lps = 1; + } + + //renormalize + unsigned shift = 0; + while(span < 0x7f) { + shift++; + + span = (span << 1) + 1; + val = (val << 1) + (in >> 7); + + in <<= 1; + if(--in_count == 0) { + in = dataread(); + in_count = 8; + } + } + + //update processing info + lps = (lps << 1) + flag_lps; + inverts = (inverts << 1) + context[con].invert; + + //update context state + if(flag_lps & toggle_invert(con)) context[con].invert ^= 1; + if(flag_lps) context[con].index = next_lps(con); + else if(shift) context[con].index = next_mps(con); + + //get next context + con = 5 + (con << 1) + ((lps ^ inverts) & 1); + } + + //get pixel + b = realorder[(lps ^ inverts) & 3]; + out = (out << 2) + b; + } + + //turn pixel data into bitplanes + unsigned data = morton_2x8(out); + write(data >> 8); + write(data >> 0); + } +} + +void SPC7110Decomp::mode2(bool init) { + static int pixelorder[16], realorder[16]; + static uint8 bitplanebuffer[16], buffer_index; + static uint8 in, val, span; + static int out0, out1, inverts, lps, in_count; + + if(init == true) { + for(unsigned i = 0; i < 16; i++) pixelorder[i] = i; + buffer_index = 0; + out0 = out1 = inverts = lps = 0; + span = 0xff; + val = dataread(); + in = dataread(); + in_count = 8; + return; + } + + while(decomp_buffer_length < (decomp_buffer_size >> 1)) { + for(unsigned pixel = 0; pixel < 8; pixel++) { + //get first symbol context + unsigned a = ((out0 >> (0 * 4)) & 15); + unsigned b = ((out0 >> (7 * 4)) & 15); + unsigned c = ((out1 >> (0 * 4)) & 15); + unsigned con = 0; + unsigned refcon = (a == b) ? (b != c) : (b == c) ? 2 : 4 - (a == c); + + //update pixel order + unsigned m, n; + for(m = 0; m < 16; m++) if(pixelorder[m] == a) break; + for(n = m; n > 0; n--) pixelorder[n] = pixelorder[n - 1]; + pixelorder[0] = a; + + //calculate the real pixel order + for(m = 0; m < 16; m++) realorder[m] = pixelorder[m]; + + //rotate reference pixel c value to top + for(m = 0; m < 16; m++) if(realorder[m] == c) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = c; + + //rotate reference pixel b value to top + for(m = 0; m < 16; m++) if(realorder[m] == b) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = b; + + //rotate reference pixel a value to top + for(m = 0; m < 16; m++) if(realorder[m] == a) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = a; + + //get 4 symbols + for(unsigned bit = 0; bit < 4; bit++) { + //get prob + unsigned prob = probability(con); + + //get symbol + unsigned flag_lps; + if(val <= span - prob) { //mps + span = span - prob; + flag_lps = 0; + } else { //lps + val = val - (span - (prob - 1)); + span = prob - 1; + flag_lps = 1; + } + + //renormalize + unsigned shift = 0; + while(span < 0x7f) { + shift++; + + span = (span << 1) + 1; + val = (val << 1) + (in >> 7); + + in <<= 1; + if(--in_count == 0) { + in = dataread(); + in_count = 8; + } + } + + //update processing info + lps = (lps << 1) + flag_lps; + unsigned invertbit = context[con].invert; + inverts = (inverts << 1) + invertbit; + + //update context state + if(flag_lps & toggle_invert(con)) context[con].invert ^= 1; + if(flag_lps) context[con].index = next_lps(con); + else if(shift) context[con].index = next_mps(con); + + //get next context + con = mode2_context_table[con][flag_lps ^ invertbit] + (con == 1 ? refcon : 0); + } + + //get pixel + b = realorder[(lps ^ inverts) & 0x0f]; + out1 = (out1 << 4) + ((out0 >> 28) & 0x0f); + out0 = (out0 << 4) + b; + } + + //convert pixel data into bitplanes + unsigned data = morton_4x8(out0); + write(data >> 24); + write(data >> 16); + bitplanebuffer[buffer_index++] = data >> 8; + bitplanebuffer[buffer_index++] = data >> 0; + + if(buffer_index == 16) { + for(unsigned i = 0; i < 16; i++) write(bitplanebuffer[i]); + buffer_index = 0; + } + } +} + +// + +const uint8 SPC7110Decomp::evolution_table[53][4] = { +//{ prob, nextlps, nextmps, toggle invert }, + + { 0x5a, 1, 1, 1 }, + { 0x25, 6, 2, 0 }, + { 0x11, 8, 3, 0 }, + { 0x08, 10, 4, 0 }, + { 0x03, 12, 5, 0 }, + { 0x01, 15, 5, 0 }, + + { 0x5a, 7, 7, 1 }, + { 0x3f, 19, 8, 0 }, + { 0x2c, 21, 9, 0 }, + { 0x20, 22, 10, 0 }, + { 0x17, 23, 11, 0 }, + { 0x11, 25, 12, 0 }, + { 0x0c, 26, 13, 0 }, + { 0x09, 28, 14, 0 }, + { 0x07, 29, 15, 0 }, + { 0x05, 31, 16, 0 }, + { 0x04, 32, 17, 0 }, + { 0x03, 34, 18, 0 }, + { 0x02, 35, 5, 0 }, + + { 0x5a, 20, 20, 1 }, + { 0x48, 39, 21, 0 }, + { 0x3a, 40, 22, 0 }, + { 0x2e, 42, 23, 0 }, + { 0x26, 44, 24, 0 }, + { 0x1f, 45, 25, 0 }, + { 0x19, 46, 26, 0 }, + { 0x15, 25, 27, 0 }, + { 0x11, 26, 28, 0 }, + { 0x0e, 26, 29, 0 }, + { 0x0b, 27, 30, 0 }, + { 0x09, 28, 31, 0 }, + { 0x08, 29, 32, 0 }, + { 0x07, 30, 33, 0 }, + { 0x05, 31, 34, 0 }, + { 0x04, 33, 35, 0 }, + { 0x04, 33, 36, 0 }, + { 0x03, 34, 37, 0 }, + { 0x02, 35, 38, 0 }, + { 0x02, 36, 5, 0 }, + + { 0x58, 39, 40, 1 }, + { 0x4d, 47, 41, 0 }, + { 0x43, 48, 42, 0 }, + { 0x3b, 49, 43, 0 }, + { 0x34, 50, 44, 0 }, + { 0x2e, 51, 45, 0 }, + { 0x29, 44, 46, 0 }, + { 0x25, 45, 24, 0 }, + + { 0x56, 47, 48, 1 }, + { 0x4f, 47, 49, 0 }, + { 0x47, 48, 50, 0 }, + { 0x41, 49, 51, 0 }, + { 0x3c, 50, 52, 0 }, + { 0x37, 51, 43, 0 }, +}; + +const uint8 SPC7110Decomp::mode2_context_table[32][2] = { +//{ next 0, next 1 }, + + { 1, 2 }, + + { 3, 8 }, + { 13, 14 }, + + { 15, 16 }, + { 17, 18 }, + { 19, 20 }, + { 21, 22 }, + { 23, 24 }, + { 25, 26 }, + { 25, 26 }, + { 25, 26 }, + { 25, 26 }, + { 25, 26 }, + { 27, 28 }, + { 29, 30 }, + + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + + { 31, 31 }, +}; + +uint8 SPC7110Decomp::probability (unsigned n) { return evolution_table[context[n].index][0]; } +uint8 SPC7110Decomp::next_lps (unsigned n) { return evolution_table[context[n].index][1]; } +uint8 SPC7110Decomp::next_mps (unsigned n) { return evolution_table[context[n].index][2]; } +bool SPC7110Decomp::toggle_invert(unsigned n) { return evolution_table[context[n].index][3]; } + +unsigned SPC7110Decomp::morton_2x8(unsigned data) { + //reverse morton lookup: de-interleave two 8-bit values + //15, 13, 11, 9, 7, 5, 3, 1 -> 15- 8 + //14, 12, 10, 8, 6, 4, 2, 0 -> 7- 0 + return morton16[0][(data >> 0) & 255] + morton16[1][(data >> 8) & 255]; +} + +unsigned SPC7110Decomp::morton_4x8(unsigned data) { + //reverse morton lookup: de-interleave four 8-bit values + //31, 27, 23, 19, 15, 11, 7, 3 -> 31-24 + //30, 26, 22, 18, 14, 10, 6, 2 -> 23-16 + //29, 25, 21, 17, 13, 9, 5, 1 -> 15- 8 + //28, 24, 20, 16, 12, 8, 4, 0 -> 7- 0 + return morton32[0][(data >> 0) & 255] + morton32[1][(data >> 8) & 255] + + morton32[2][(data >> 16) & 255] + morton32[3][(data >> 24) & 255]; +} + +// + +void SPC7110Decomp::reset() { + //mode 3 is invalid; this is treated as a special case to always return 0x00 + //set to mode 3 so that reading decomp port before starting first decomp will return 0x00 + decomp_mode = 3; + + decomp_buffer_rdoffset = 0; + decomp_buffer_wroffset = 0; + decomp_buffer_length = 0; +} + +SPC7110Decomp::SPC7110Decomp() { + decomp_buffer = new uint8_t[decomp_buffer_size]; + reset(); + + //initialize reverse morton lookup tables + for(unsigned i = 0; i < 256; i++) { + #define map(x, y) (((i >> x) & 1) << y) + //2x8-bit + morton16[1][i] = map(7, 15) + map(6, 7) + map(5, 14) + map(4, 6) + + map(3, 13) + map(2, 5) + map(1, 12) + map(0, 4); + morton16[0][i] = map(7, 11) + map(6, 3) + map(5, 10) + map(4, 2) + + map(3, 9) + map(2, 1) + map(1, 8) + map(0, 0); + //4x8-bit + morton32[3][i] = map(7, 31) + map(6, 23) + map(5, 15) + map(4, 7) + + map(3, 30) + map(2, 22) + map(1, 14) + map(0, 6); + morton32[2][i] = map(7, 29) + map(6, 21) + map(5, 13) + map(4, 5) + + map(3, 28) + map(2, 20) + map(1, 12) + map(0, 4); + morton32[1][i] = map(7, 27) + map(6, 19) + map(5, 11) + map(4, 3) + + map(3, 26) + map(2, 18) + map(1, 10) + map(0, 2); + morton32[0][i] = map(7, 25) + map(6, 17) + map(5, 9) + map(4, 1) + + map(3, 24) + map(2, 16) + map(1, 8) + map(0, 0); + #undef map + } +} + +SPC7110Decomp::~SPC7110Decomp() { + delete[] decomp_buffer; +} + +#endif diff --git a/mednafen/snes/src/chip/spc7110/decomp.hpp b/mednafen/snes/src/chip/spc7110/decomp.hpp new file mode 100755 index 0000000..2b67605 --- /dev/null +++ b/mednafen/snes/src/chip/spc7110/decomp.hpp @@ -0,0 +1,46 @@ +class SPC7110Decomp { +public: + uint8 read(); + void init(unsigned mode, unsigned offset, unsigned index); + void reset(); + + void serialize(serializer&); + SPC7110Decomp(); + ~SPC7110Decomp(); + +private: + unsigned decomp_mode; + unsigned decomp_offset; + + //read() will spool chunks half the size of decomp_buffer_size + enum { decomp_buffer_size = 64 }; //must be >= 64, and must be a power of two + uint8 *decomp_buffer; + unsigned decomp_buffer_rdoffset; + unsigned decomp_buffer_wroffset; + unsigned decomp_buffer_length; + + void write(uint8 data); + uint8 dataread(); + + void mode0(bool init); + void mode1(bool init); + void mode2(bool init); + + static const uint8 evolution_table[53][4]; + static const uint8 mode2_context_table[32][2]; + + struct ContextState { + uint8 index; + uint8 invert; + } context[32]; + + uint8 probability(unsigned n); + uint8 next_lps(unsigned n); + uint8 next_mps(unsigned n); + bool toggle_invert(unsigned n); + + unsigned morton16[2][256]; + unsigned morton32[4][256]; + unsigned morton_2x8(unsigned data); + unsigned morton_4x8(unsigned data); +}; diff --git a/mednafen/snes/src/chip/spc7110/serialization.cpp b/mednafen/snes/src/chip/spc7110/serialization.cpp new file mode 100755 index 0000000..2e77d52 --- /dev/null +++ b/mednafen/snes/src/chip/spc7110/serialization.cpp @@ -0,0 +1,81 @@ +#ifdef SPC7110_CPP + +void SPC7110Decomp::serialize(serializer &s) { + s.integer(decomp_mode); + s.integer(decomp_offset); + + s.array(decomp_buffer, decomp_buffer_size); + s.integer(decomp_buffer_rdoffset); + s.integer(decomp_buffer_wroffset); + s.integer(decomp_buffer_length); + + for(unsigned n = 0; n < 32; n++) { + s.integer(context[n].index); + s.integer(context[n].invert); + } +} + +void SPC7110::serialize(serializer &s) { + s.integer(r4801); + s.integer(r4802); + s.integer(r4803); + s.integer(r4804); + s.integer(r4805); + s.integer(r4806); + s.integer(r4807); + s.integer(r4808); + s.integer(r4809); + s.integer(r480a); + s.integer(r480b); + s.integer(r480c); + decomp.serialize(s); + + s.integer(r4811); + s.integer(r4812); + s.integer(r4813); + s.integer(r4814); + s.integer(r4815); + s.integer(r4816); + s.integer(r4817); + s.integer(r4818); + s.integer(r481x); + s.integer(r4814_latch); + s.integer(r4815_latch); + + s.integer(r4820); + s.integer(r4821); + s.integer(r4822); + s.integer(r4823); + s.integer(r4824); + s.integer(r4825); + s.integer(r4826); + s.integer(r4827); + s.integer(r4828); + s.integer(r4829); + s.integer(r482a); + s.integer(r482b); + s.integer(r482c); + s.integer(r482d); + s.integer(r482e); + s.integer(r482f); + + s.integer(r4830); + s.integer(r4831); + s.integer(r4832); + s.integer(r4833); + s.integer(r4834); + + s.integer(dx_offset); + s.integer(ex_offset); + s.integer(fx_offset); + + s.integer(r4840); + s.integer(r4841); + s.integer(r4842); + + s.integer(rtc_state); + s.integer(rtc_mode); + s.integer(rtc_index); +} + +#endif diff --git a/mednafen/snes/src/chip/spc7110/spc7110.cpp b/mednafen/snes/src/chip/spc7110/spc7110.cpp new file mode 100755 index 0000000..15f5040 --- /dev/null +++ b/mednafen/snes/src/chip/spc7110/spc7110.cpp @@ -0,0 +1,677 @@ +#include <../base.hpp> + +#define SPC7110_CPP +namespace SNES { + +SPC7110 spc7110; + +#include "serialization.cpp" +#include "decomp.cpp" + +const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +void SPC7110::init() {} + +void SPC7110::enable() { + uint16_t limit = (cartridge.has_spc7110rtc() ? 0x4842 : 0x483f); + for(uint16_t i = 0x4800; i <= limit; i++) memory::mmio.map(i, *this); +} + +void SPC7110::power() { + reset(); +} + +void SPC7110::reset() { + r4801 = 0x00; + r4802 = 0x00; + r4803 = 0x00; + r4804 = 0x00; + r4805 = 0x00; + r4806 = 0x00; + r4807 = 0x00; + r4808 = 0x00; + r4809 = 0x00; + r480a = 0x00; + r480b = 0x00; + r480c = 0x00; + + decomp.reset(); + + r4811 = 0x00; + r4812 = 0x00; + r4813 = 0x00; + r4814 = 0x00; + r4815 = 0x00; + r4816 = 0x00; + r4817 = 0x00; + r4818 = 0x00; + + r481x = 0x00; + r4814_latch = false; + r4815_latch = false; + + r4820 = 0x00; + r4821 = 0x00; + r4822 = 0x00; + r4823 = 0x00; + r4824 = 0x00; + r4825 = 0x00; + r4826 = 0x00; + r4827 = 0x00; + r4828 = 0x00; + r4829 = 0x00; + r482a = 0x00; + r482b = 0x00; + r482c = 0x00; + r482d = 0x00; + r482e = 0x00; + r482f = 0x00; + + r4830 = 0x00; + mmio_write(0x4831, 0); + mmio_write(0x4832, 1); + mmio_write(0x4833, 2); + r4834 = 0x00; + + r4840 = 0x00; + r4841 = 0x00; + r4842 = 0x00; + + if(cartridge.has_spc7110rtc()) { + rtc_state = RTCS_Inactive; + rtc_mode = RTCM_Linear; + rtc_index = 0; + } +} + +unsigned SPC7110::datarom_addr(unsigned addr) { + unsigned size = memory::cartrom.size() - 0x100000; + while(addr >= size) addr -= size; + return addr + 0x100000; +} + +unsigned SPC7110::data_pointer() { return r4811 + (r4812 << 8) + (r4813 << 16); } +unsigned SPC7110::data_adjust() { return r4814 + (r4815 << 8); } +unsigned SPC7110::data_increment() { return r4816 + (r4817 << 8); } +void SPC7110::set_data_pointer(unsigned addr) { r4811 = addr; r4812 = addr >> 8; r4813 = addr >> 16; } +void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8; } + +void SPC7110::update_time(int offset) { + time_t rtc_time + = (memory::cartrtc.read(16) << 0) + | (memory::cartrtc.read(17) << 8) + | (memory::cartrtc.read(18) << 16) + | (memory::cartrtc.read(19) << 24); + time_t current_time = time(0) - offset; + + //sizeof(time_t) is platform-dependent; though memory::cartrtc needs to be platform-agnostic. + //yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by + //accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow + //memory::cartrtc timestamp to remain valid for up to ~34 years from the last update, even if + //time_t overflows. calculation should be valid regardless of number representation, time_t size, + //or whether time_t is signed or unsigned. + time_t diff + = (current_time >= rtc_time) + ? (current_time - rtc_time) + : (std::numeric_limits::max() - rtc_time + current_time + 1); //compensate for overflow + if(diff > std::numeric_limits::max() / 2) diff = 0; //compensate for underflow + + bool update = true; + if(memory::cartrtc.read(13) & 1) update = false; //do not update if CR0 timer disable flag is set + if(memory::cartrtc.read(15) & 3) update = false; //do not update if CR2 timer disable flags are set + + if(diff > 0 && update == true) { + unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; + unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10; + unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10; + unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10; + unsigned month = memory::cartrtc.read( 8) + memory::cartrtc.read( 9) * 10; + unsigned year = memory::cartrtc.read(10) + memory::cartrtc.read(11) * 10; + unsigned weekday = memory::cartrtc.read(12); + + day--; + month--; + year += (year >= 90) ? 1900 : 2000; //range = 1990-2089 + + second += diff; + while(second >= 60) { + second -= 60; + + minute++; + if(minute < 60) continue; + minute = 0; + + hour++; + if(hour < 24) continue; + hour = 0; + + day++; + weekday = (weekday + 1) % 7; + unsigned days = months[month % 12]; + if(days == 28) { + bool leapyear = false; + if((year % 4) == 0) { + leapyear = true; + if((year % 100) == 0 && (year % 400) != 0) leapyear = false; + } + if(leapyear) days++; + } + if(day < days) continue; + day = 0; + + month++; + if(month < 12) continue; + month = 0; + + year++; + } + + day++; + month++; + year %= 100; + + memory::cartrtc.write( 0, second % 10); + memory::cartrtc.write( 1, second / 10); + memory::cartrtc.write( 2, minute % 10); + memory::cartrtc.write( 3, minute / 10); + memory::cartrtc.write( 4, hour % 10); + memory::cartrtc.write( 5, hour / 10); + memory::cartrtc.write( 6, day % 10); + memory::cartrtc.write( 7, day / 10); + memory::cartrtc.write( 8, month % 10); + memory::cartrtc.write( 9, month / 10); + memory::cartrtc.write(10, year % 10); + memory::cartrtc.write(11, (year / 10) % 10); + memory::cartrtc.write(12, weekday % 7); + } + + memory::cartrtc.write(16, current_time >> 0); + memory::cartrtc.write(17, current_time >> 8); + memory::cartrtc.write(18, current_time >> 16); + memory::cartrtc.write(19, current_time >> 24); +} + +uint8 SPC7110::mmio_read(unsigned addr) { + addr &= 0xffff; + + switch(addr) { + //================== + //decompression unit + //================== + + case 0x4800: { + uint16 counter = (r4809 + (r480a << 8)); + counter--; + r4809 = counter; + r480a = counter >> 8; + return decomp.read(); + } + case 0x4801: return r4801; + case 0x4802: return r4802; + case 0x4803: return r4803; + case 0x4804: return r4804; + case 0x4805: return r4805; + case 0x4806: return r4806; + case 0x4807: return r4807; + case 0x4808: return r4808; + case 0x4809: return r4809; + case 0x480a: return r480a; + case 0x480b: return r480b; + case 0x480c: { + uint8 status = r480c; + r480c &= 0x7f; + return status; + } + + //============== + //data port unit + //============== + + case 0x4810: { + if(r481x != 0x07) return 0x00; + + unsigned addr = data_pointer(); + unsigned adjust = data_adjust(); + if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend + + unsigned adjustaddr = addr; + if(r4818 & 2) { + adjustaddr += adjust; + set_data_adjust(adjust + 1); + } + + uint8 data = memory::cartrom.read(datarom_addr(adjustaddr)); + if(!(r4818 & 2)) { + unsigned increment = (r4818 & 1) ? data_increment() : 1; + if(r4818 & 4) increment = (int16)increment; //16-bit sign extend + + if((r4818 & 16) == 0) { + set_data_pointer(addr + increment); + } else { + set_data_adjust(adjust + increment); + } + } + + return data; + } + case 0x4811: return r4811; + case 0x4812: return r4812; + case 0x4813: return r4813; + case 0x4814: return r4814; + case 0x4815: return r4815; + case 0x4816: return r4816; + case 0x4817: return r4817; + case 0x4818: return r4818; + case 0x481a: { + if(r481x != 0x07) return 0x00; + + unsigned addr = data_pointer(); + unsigned adjust = data_adjust(); + if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend + + uint8 data = memory::cartrom.read(datarom_addr(addr + adjust)); + if((r4818 & 0x60) == 0x60) { + if((r4818 & 16) == 0) { + set_data_pointer(addr + adjust); + } else { + set_data_adjust(adjust + adjust); + } + } + + return data; + } + + //========= + //math unit + //========= + + case 0x4820: return r4820; + case 0x4821: return r4821; + case 0x4822: return r4822; + case 0x4823: return r4823; + case 0x4824: return r4824; + case 0x4825: return r4825; + case 0x4826: return r4826; + case 0x4827: return r4827; + case 0x4828: return r4828; + case 0x4829: return r4829; + case 0x482a: return r482a; + case 0x482b: return r482b; + case 0x482c: return r482c; + case 0x482d: return r482d; + case 0x482e: return r482e; + case 0x482f: { + uint8 status = r482f; + r482f &= 0x7f; + return status; + } + + //=================== + //memory mapping unit + //=================== + + case 0x4830: return r4830; + case 0x4831: return r4831; + case 0x4832: return r4832; + case 0x4833: return r4833; + case 0x4834: return r4834; + + //==================== + //real-time clock unit + //==================== + + case 0x4840: return r4840; + case 0x4841: { + if(rtc_state == RTCS_Inactive || rtc_state == RTCS_ModeSelect) return 0x00; + + r4842 = 0x80; + uint8 data = memory::cartrtc.read(rtc_index); + rtc_index = (rtc_index + 1) & 15; + return data; + } + case 0x4842: { + uint8 status = r4842; + r4842 &= 0x7f; + return status; + } + } + + return cpu.regs.mdr; +} + +void SPC7110::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + switch(addr) { + //================== + //decompression unit + //================== + + case 0x4801: r4801 = data; break; + case 0x4802: r4802 = data; break; + case 0x4803: r4803 = data; break; + case 0x4804: r4804 = data; break; + case 0x4805: r4805 = data; break; + case 0x4806: { + r4806 = data; + + unsigned table = (r4801 + (r4802 << 8) + (r4803 << 16)); + unsigned index = (r4804 << 2); + unsigned length = (r4809 + (r480a << 8)); + unsigned addr = datarom_addr(table + index); + unsigned mode = (memory::cartrom.read(addr + 0)); + unsigned offset = (memory::cartrom.read(addr + 1) << 16) + + (memory::cartrom.read(addr + 2) << 8) + + (memory::cartrom.read(addr + 3) << 0); + + decomp.init(mode, offset, (r4805 + (r4806 << 8)) << mode); + r480c = 0x80; + } break; + + case 0x4807: r4807 = data; break; + case 0x4808: r4808 = data; break; + case 0x4809: r4809 = data; break; + case 0x480a: r480a = data; break; + case 0x480b: r480b = data; break; + + //============== + //data port unit + //============== + + case 0x4811: r4811 = data; r481x |= 0x01; break; + case 0x4812: r4812 = data; r481x |= 0x02; break; + case 0x4813: r4813 = data; r481x |= 0x04; break; + case 0x4814: { + r4814 = data; + r4814_latch = true; + if(!r4815_latch) break; + if(!(r4818 & 2)) break; + if(r4818 & 0x10) break; + + if((r4818 & 0x60) == 0x20) { + unsigned increment = data_adjust() & 0xff; + if(r4818 & 8) increment = (int8)increment; //8-bit sign extend + set_data_pointer(data_pointer() + increment); + } else if((r4818 & 0x60) == 0x40) { + unsigned increment = data_adjust(); + if(r4818 & 8) increment = (int16)increment; //16-bit sign extend + set_data_pointer(data_pointer() + increment); + } + } break; + case 0x4815: { + r4815 = data; + r4815_latch = true; + if(!r4814_latch) break; + if(!(r4818 & 2)) break; + if(r4818 & 0x10) break; + + if((r4818 & 0x60) == 0x20) { + unsigned increment = data_adjust() & 0xff; + if(r4818 & 8) increment = (int8)increment; //8-bit sign extend + set_data_pointer(data_pointer() + increment); + } else if((r4818 & 0x60) == 0x40) { + unsigned increment = data_adjust(); + if(r4818 & 8) increment = (int16)increment; //16-bit sign extend + set_data_pointer(data_pointer() + increment); + } + } break; + case 0x4816: r4816 = data; break; + case 0x4817: r4817 = data; break; + case 0x4818: { + if(r481x != 0x07) break; + + r4818 = data; + r4814_latch = r4815_latch = false; + } break; + + //========= + //math unit + //========= + + case 0x4820: r4820 = data; break; + case 0x4821: r4821 = data; break; + case 0x4822: r4822 = data; break; + case 0x4823: r4823 = data; break; + case 0x4824: r4824 = data; break; + case 0x4825: { + r4825 = data; + + if(r482e & 1) { + //signed 16-bit x 16-bit multiplication + int16 r0 = (int16)(r4824 + (r4825 << 8)); + int16 r1 = (int16)(r4820 + (r4821 << 8)); + + signed result = r0 * r1; + r4828 = result; + r4829 = result >> 8; + r482a = result >> 16; + r482b = result >> 24; + } else { + //unsigned 16-bit x 16-bit multiplication + uint16 r0 = (uint16)(r4824 + (r4825 << 8)); + uint16 r1 = (uint16)(r4820 + (r4821 << 8)); + + unsigned result = r0 * r1; + r4828 = result; + r4829 = result >> 8; + r482a = result >> 16; + r482b = result >> 24; + } + + r482f = 0x80; + } break; + case 0x4826: r4826 = data; break; + case 0x4827: { + r4827 = data; + + if(r482e & 1) { + //signed 32-bit x 16-bit division + int32 dividend = (int32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24)); + int16 divisor = (int16)(r4826 + (r4827 << 8)); + + int32 quotient; + int16 remainder; + + if(divisor) { + quotient = (int32)(dividend / divisor); + remainder = (int32)(dividend % divisor); + } else { + //illegal division by zero + quotient = 0; + remainder = dividend & 0xffff; + } + + r4828 = quotient; + r4829 = quotient >> 8; + r482a = quotient >> 16; + r482b = quotient >> 24; + + r482c = remainder; + r482d = remainder >> 8; + } else { + //unsigned 32-bit x 16-bit division + uint32 dividend = (uint32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24)); + uint16 divisor = (uint16)(r4826 + (r4827 << 8)); + + uint32 quotient; + uint16 remainder; + + if(divisor) { + quotient = (uint32)(dividend / divisor); + remainder = (uint16)(dividend % divisor); + } else { + //illegal division by zero + quotient = 0; + remainder = dividend & 0xffff; + } + + r4828 = quotient; + r4829 = quotient >> 8; + r482a = quotient >> 16; + r482b = quotient >> 24; + + r482c = remainder; + r482d = remainder >> 8; + } + + r482f = 0x80; + } break; + + case 0x482e: { + //reset math unit + r4820 = r4821 = r4822 = r4823 = 0; + r4824 = r4825 = r4826 = r4827 = 0; + r4828 = r4829 = r482a = r482b = 0; + r482c = r482d = 0; + + r482e = data; + } break; + + //=================== + //memory mapping unit + //=================== + + case 0x4830: r4830 = data; break; + + case 0x4831: { + r4831 = data; + dx_offset = datarom_addr(data * 0x100000); + } break; + + case 0x4832: { + r4832 = data; + ex_offset = datarom_addr(data * 0x100000); + } break; + + case 0x4833: { + r4833 = data; + fx_offset = datarom_addr(data * 0x100000); + } break; + + case 0x4834: r4834 = data; break; + + //==================== + //real-time clock unit + //==================== + + case 0x4840: { + r4840 = data; + if(!(r4840 & 1)) { + //disable RTC + rtc_state = RTCS_Inactive; + update_time(); + } else { + //enable RTC + r4842 = 0x80; + rtc_state = RTCS_ModeSelect; + } + } break; + + case 0x4841: { + r4841 = data; + + switch(rtc_state) { + case RTCS_ModeSelect: { + if(data == RTCM_Linear || data == RTCM_Indexed) { + r4842 = 0x80; + rtc_state = RTCS_IndexSelect; + rtc_mode = (RTC_Mode)data; + rtc_index = 0; + } + } break; + + case RTCS_IndexSelect: { + r4842 = 0x80; + rtc_index = data & 15; + if(rtc_mode == RTCM_Linear) rtc_state = RTCS_Write; + } break; + + case RTCS_Write: { + r4842 = 0x80; + + //control register 0 + if(rtc_index == 13) { + //increment second counter + if(data & 2) update_time(+1); + + //round minute counter + if(data & 8) { + update_time(); + + unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; + //clear seconds + memory::cartrtc.write(0, 0); + memory::cartrtc.write(1, 0); + + if(second >= 30) update_time(+60); + } + } + + //control register 2 + if(rtc_index == 15) { + //disable timer and clear second counter + if((data & 1) && !(memory::cartrtc.read(15) & 1)) { + update_time(); + + //clear seconds + memory::cartrtc.write(0, 0); + memory::cartrtc.write(1, 0); + } + + //disable timer + if((data & 2) && !(memory::cartrtc.read(15) & 2)) { + update_time(); + } + } + + memory::cartrtc.write(rtc_index, data & 15); + rtc_index = (rtc_index + 1) & 15; + } break; + } //switch(rtc_state) + } break; + } +} + +uint8 SPC7110::read(unsigned addr) { + //$[00-0f|80-8f]:[8000-ffff], $[c0-cf]:[0000-ffff] mapped directly to memory::cartrom + + if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) { + //$[00|30]:[6000-7fff] + return memory::cartram.read(addr & 0x1fff); + } + + if((addr & 0xff0000) == 0x500000) { + //$[50]:[0000-ffff] + return mmio_read(0x4800); + } + + if((addr & 0xf00000) == 0xd00000) { + //$[d0-df]:[0000-ffff] + return memory::cartrom.read(dx_offset + (addr & 0x0fffff)); + } + + if((addr & 0xf00000) == 0xe00000) { + //$[e0-ef]:[0000-ffff] + return memory::cartrom.read(ex_offset + (addr & 0x0fffff)); + } + + if((addr & 0xf00000) == 0xf00000) { + //$[f0-ff]:[0000-ffff] + return memory::cartrom.read(fx_offset + (addr & 0x0fffff)); + } + + return cpu.regs.mdr; +} + +void SPC7110::write(unsigned addr, uint8 data) { + if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) { + //$[00|30]:[6000-7fff] + if(r4830 & 0x80) memory::cartram.write(addr & 0x1fff, data); + return; + } +} + +SPC7110::SPC7110() { +} + +}; diff --git a/mednafen/snes/src/chip/spc7110/spc7110.hpp b/mednafen/snes/src/chip/spc7110/spc7110.hpp new file mode 100755 index 0000000..fa6770f --- /dev/null +++ b/mednafen/snes/src/chip/spc7110/spc7110.hpp @@ -0,0 +1,136 @@ +/***** + * SPC7110 emulator - version 0.03 (2008-08-10) + * Copyright (c) 2008, byuu and neviksti + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * The software is provided "as is" and the author disclaims all warranties + * with regard to this software including all implied warranties of + * merchantibility and fitness, in no event shall the author be liable for + * any special, direct, indirect, or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether in an + * action of contract, negligence or other tortious action, arising out of + * or in connection with the use or performance of this software. + *****/ + +#include "decomp.hpp" + +class SPC7110 : public MMIO, public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + unsigned datarom_addr(unsigned addr); + + unsigned data_pointer(); + unsigned data_adjust(); + unsigned data_increment(); + void set_data_pointer(unsigned addr); + void set_data_adjust(unsigned addr); + + void update_time(int offset = 0); + time_t create_time(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + //spc7110decomp + void decomp_init(); + uint8 decomp_read(); + + void serialize(serializer&); + SPC7110(); + +private: + //================== + //decompression unit + //================== + uint8 r4801; //compression table low + uint8 r4802; //compression table high + uint8 r4803; //compression table bank + uint8 r4804; //compression table index + uint8 r4805; //decompression buffer index low + uint8 r4806; //decompression buffer index high + uint8 r4807; //??? + uint8 r4808; //??? + uint8 r4809; //compression length low + uint8 r480a; //compression length high + uint8 r480b; //decompression control register + uint8 r480c; //decompression status + + SPC7110Decomp decomp; + + //============== + //data port unit + //============== + uint8 r4811; //data pointer low + uint8 r4812; //data pointer high + uint8 r4813; //data pointer bank + uint8 r4814; //data adjust low + uint8 r4815; //data adjust high + uint8 r4816; //data increment low + uint8 r4817; //data increment high + uint8 r4818; //data port control register + + uint8 r481x; + + bool r4814_latch; + bool r4815_latch; + + //========= + //math unit + //========= + uint8 r4820; //16-bit multiplicand B0, 32-bit dividend B0 + uint8 r4821; //16-bit multiplicand B1, 32-bit dividend B1 + uint8 r4822; //32-bit dividend B2 + uint8 r4823; //32-bit dividend B3 + uint8 r4824; //16-bit multiplier B0 + uint8 r4825; //16-bit multiplier B1 + uint8 r4826; //16-bit divisor B0 + uint8 r4827; //16-bit divisor B1 + uint8 r4828; //32-bit product B0, 32-bit quotient B0 + uint8 r4829; //32-bit product B1, 32-bit quotient B1 + uint8 r482a; //32-bit product B2, 32-bit quotient B2 + uint8 r482b; //32-bit product B3, 32-bit quotient B3 + uint8 r482c; //16-bit remainder B0 + uint8 r482d; //16-bit remainder B1 + uint8 r482e; //math control register + uint8 r482f; //math status + + //=================== + //memory mapping unit + //=================== + uint8 r4830; //SRAM write enable + uint8 r4831; //$[d0-df]:[0000-ffff] mapping + uint8 r4832; //$[e0-ef]:[0000-ffff] mapping + uint8 r4833; //$[f0-ff]:[0000-ffff] mapping + uint8 r4834; //??? + + unsigned dx_offset; + unsigned ex_offset; + unsigned fx_offset; + + //==================== + //real-time clock unit + //==================== + uint8 r4840; //RTC latch + uint8 r4841; //RTC index/data port + uint8 r4842; //RTC status + + enum RTC_State { RTCS_Inactive, RTCS_ModeSelect, RTCS_IndexSelect, RTCS_Write }; + enum RTC_Mode { RTCM_Linear = 0x03, RTCM_Indexed = 0x0c }; + unsigned rtc_state; + unsigned rtc_mode; + unsigned rtc_index; + + static const unsigned months[12]; +}; + +extern SPC7110 spc7110; diff --git a/mednafen/snes/src/chip/srtc/serialization.cpp b/mednafen/snes/src/chip/srtc/serialization.cpp new file mode 100755 index 0000000..538a1bd --- /dev/null +++ b/mednafen/snes/src/chip/srtc/serialization.cpp @@ -0,0 +1,8 @@ +#ifdef SRTC_CPP + +void SRTC::serialize(serializer &s) { + s.integer(rtc_mode); + s.integer(rtc_index); +} + +#endif diff --git a/mednafen/snes/src/chip/srtc/srtc.cpp b/mednafen/snes/src/chip/srtc/srtc.cpp new file mode 100755 index 0000000..3ba3fd6 --- /dev/null +++ b/mednafen/snes/src/chip/srtc/srtc.cpp @@ -0,0 +1,233 @@ +#include <../base.hpp> + +#define SRTC_CPP +namespace SNES { + +SRTC srtc; + +#include "serialization.cpp" + +const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +void SRTC::init() { +} + +void SRTC::enable() { + memory::mmio.map(0x2800, *this); + memory::mmio.map(0x2801, *this); +} + +void SRTC::power() { + reset(); +} + +void SRTC::reset() { + rtc_mode = RtcRead; + rtc_index = -1; + update_time(); +} + +void SRTC::update_time() { + time_t rtc_time + = (memory::cartrtc.read(16) << 0) + | (memory::cartrtc.read(17) << 8) + | (memory::cartrtc.read(18) << 16) + | (memory::cartrtc.read(19) << 24); + time_t current_time = time(0); + + //sizeof(time_t) is platform-dependent; though memory::cartrtc needs to be platform-agnostic. + //yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by + //accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow + //memory::cartrtc timestamp to remain valid for up to ~34 years from the last update, even if + //time_t overflows. calculation should be valid regardless of number representation, time_t size, + //or whether time_t is signed or unsigned. + time_t diff + = (current_time >= rtc_time) + ? (current_time - rtc_time) + : (std::numeric_limits::max() - rtc_time + current_time + 1); //compensate for overflow + if(diff > std::numeric_limits::max() / 2) diff = 0; //compensate for underflow + + if(diff > 0) { + unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; + unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10; + unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10; + unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10; + unsigned month = memory::cartrtc.read( 8); + unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100; + unsigned weekday = memory::cartrtc.read(12); + + day--; + month--; + year += 1000; + + second += diff; + while(second >= 60) { + second -= 60; + + minute++; + if(minute < 60) continue; + minute = 0; + + hour++; + if(hour < 24) continue; + hour = 0; + + day++; + weekday = (weekday + 1) % 7; + unsigned days = months[month % 12]; + if(days == 28) { + bool leapyear = false; + if((year % 4) == 0) { + leapyear = true; + if((year % 100) == 0 && (year % 400) != 0) leapyear = false; + } + if(leapyear) days++; + } + if(day < days) continue; + day = 0; + + month++; + if(month < 12) continue; + month = 0; + + year++; + } + + day++; + month++; + year -= 1000; + + memory::cartrtc.write( 0, second % 10); + memory::cartrtc.write( 1, second / 10); + memory::cartrtc.write( 2, minute % 10); + memory::cartrtc.write( 3, minute / 10); + memory::cartrtc.write( 4, hour % 10); + memory::cartrtc.write( 5, hour / 10); + memory::cartrtc.write( 6, day % 10); + memory::cartrtc.write( 7, day / 10); + memory::cartrtc.write( 8, month); + memory::cartrtc.write( 9, year % 10); + memory::cartrtc.write(10, (year / 10) % 10); + memory::cartrtc.write(11, year / 100); + memory::cartrtc.write(12, weekday % 7); + } + + memory::cartrtc.write(16, current_time >> 0); + memory::cartrtc.write(17, current_time >> 8); + memory::cartrtc.write(18, current_time >> 16); + memory::cartrtc.write(19, current_time >> 24); +} + +//returns day of week for specified date +//eg 0 = Sunday, 1 = Monday, ... 6 = Saturday +//usage: weekday(2008, 1, 1) returns weekday of January 1st, 2008 +unsigned SRTC::weekday(unsigned year, unsigned month, unsigned day) { + unsigned y = 1900, m = 1; //epoch is 1900-01-01 + unsigned sum = 0; //number of days passed since epoch + + year = max(1900, year); + month = max(1, min(12, month)); + day = max(1, min(31, day)); + + while(y < year) { + bool leapyear = false; + if((y % 4) == 0) { + leapyear = true; + if((y % 100) == 0 && (y % 400) != 0) leapyear = false; + } + sum += leapyear ? 366 : 365; + y++; + } + + while(m < month) { + unsigned days = months[m - 1]; + if(days == 28) { + bool leapyear = false; + if((y % 4) == 0) { + leapyear = true; + if((y % 100) == 0 && (y % 400) != 0) leapyear = false; + } + if(leapyear) days++; + } + sum += days; + m++; + } + + sum += day - 1; + return (sum + 1) % 7; //1900-01-01 was a Monday +} + +uint8 SRTC::mmio_read(unsigned addr) { + addr &= 0xffff; + + if(addr == 0x2800) { + if(rtc_mode != RtcRead) return 0x00; + + if(rtc_index < 0) { + update_time(); + rtc_index++; + return 0x0f; + } else if(rtc_index > 12) { + rtc_index = -1; + return 0x0f; + } else { + return memory::cartrtc.read(rtc_index++); + } + } + + return cpu.regs.mdr; +} + +void SRTC::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + if(addr == 0x2801) { + data &= 0x0f; //only the low four bits are used + + if(data == 0x0d) { + rtc_mode = RtcRead; + rtc_index = -1; + return; + } + + if(data == 0x0e) { + rtc_mode = RtcCommand; + return; + } + + if(data == 0x0f) return; //unknown behavior + + if(rtc_mode == RtcWrite) { + if(rtc_index >= 0 && rtc_index < 12) { + memory::cartrtc.write(rtc_index++, data); + + if(rtc_index == 12) { + //day of week is automatically calculated and written + unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10; + unsigned month = memory::cartrtc.read( 8); + unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100; + year += 1000; + + memory::cartrtc.write(rtc_index++, weekday(year, month, day)); + } + } + } else if(rtc_mode == RtcCommand) { + if(data == 0) { + rtc_mode = RtcWrite; + rtc_index = 0; + } else if(data == 4) { + rtc_mode = RtcReady; + rtc_index = -1; + for(unsigned i = 0; i < 13; i++) memory::cartrtc.write(i, 0); + } else { + //unknown behavior + rtc_mode = RtcReady; + } + } + } +} + +SRTC::SRTC() { +} + +}; diff --git a/mednafen/snes/src/chip/srtc/srtc.hpp b/mednafen/snes/src/chip/srtc/srtc.hpp new file mode 100755 index 0000000..7bbe7f4 --- /dev/null +++ b/mednafen/snes/src/chip/srtc/srtc.hpp @@ -0,0 +1,24 @@ +class SRTC : public MMIO { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + void serialize(serializer&); + SRTC(); + +private: + static const unsigned months[12]; + enum RtcMode { RtcReady, RtcCommand, RtcRead, RtcWrite }; + unsigned rtc_mode; + signed rtc_index; + + void update_time(); + unsigned weekday(unsigned year, unsigned month, unsigned day); +}; + +extern SRTC srtc; diff --git a/mednafen/snes/src/chip/st010/serialization.cpp b/mednafen/snes/src/chip/st010/serialization.cpp new file mode 100755 index 0000000..8c0a67c --- /dev/null +++ b/mednafen/snes/src/chip/st010/serialization.cpp @@ -0,0 +1,7 @@ +#ifdef ST010_CPP + +void ST010::serialize(serializer &s) { + s.array(ram); +} + +#endif diff --git a/mednafen/snes/src/chip/st010/st010.cpp b/mednafen/snes/src/chip/st010/st010.cpp new file mode 100755 index 0000000..20726ab --- /dev/null +++ b/mednafen/snes/src/chip/st010/st010.cpp @@ -0,0 +1,95 @@ +#include <../base.hpp> + +#define ST010_CPP +namespace SNES { + +ST010 st010; + +#include "st010_data.hpp" +#include "serialization.cpp" +#include "st010_op.cpp" + +void ST010::init() { +} + +void ST010::enable() { + bus.map(Bus::MapDirect, 0x68, 0x6f, 0x0000, 0x0fff, *this); + bus.map(Bus::MapDirect, 0xe8, 0xef, 0x0000, 0x0fff, *this); +} + +int16 ST010::sin(int16 theta) { + return sin_table[(theta >> 8) & 0xff]; +} + +int16 ST010::cos(int16 theta) { + return sin_table[((theta + 0x4000) >> 8) & 0xff]; +} + +uint8 ST010::readb(uint16 addr) { + return ram[addr & 0xfff]; +} + +uint16 ST010::readw(uint16 addr) { + return (readb(addr + 0) << 0) | + (readb(addr + 1) << 8); +} + +uint32 ST010::readd(uint16 addr) { + return (readb(addr + 0) << 0) | + (readb(addr + 1) << 8) | + (readb(addr + 2) << 16) | + (readb(addr + 3) << 24); +} + +void ST010::writeb(uint16 addr, uint8 data) { + ram[addr & 0xfff] = data; +} + +void ST010::writew(uint16 addr, uint16 data) { + writeb(addr + 0, data >> 0); + writeb(addr + 1, data >> 8); +} + +void ST010::writed(uint16 addr, uint32 data) { + writeb(addr + 0, data >> 0); + writeb(addr + 1, data >> 8); + writeb(addr + 2, data >> 16); + writeb(addr + 3, data >> 24); +} + +// + +void ST010::power() { + reset(); +} + +void ST010::reset() { + memset(ram, 0x00, sizeof ram); +} + +// + +uint8 ST010::read(unsigned addr) { + return readb(addr); +} + +void ST010::write(unsigned addr, uint8 data) { + writeb(addr, data); + + if((addr & 0xfff) == 0x0021 && (data & 0x80)) { + switch(ram[0x0020]) { + case 0x01: op_01(); break; + case 0x02: op_02(); break; + case 0x03: op_03(); break; + case 0x04: op_04(); break; + case 0x05: op_05(); break; + case 0x06: op_06(); break; + case 0x07: op_07(); break; + case 0x08: op_08(); break; + } + + ram[0x0021] &= ~0x80; + } +} + +}; diff --git a/mednafen/snes/src/chip/st010/st010.hpp b/mednafen/snes/src/chip/st010/st010.hpp new file mode 100755 index 0000000..ab837ca --- /dev/null +++ b/mednafen/snes/src/chip/st010/st010.hpp @@ -0,0 +1,44 @@ +class ST010 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + +private: + uint8 ram[0x1000]; + static const int16 sin_table[256]; + static const int16 mode7_scale[176]; + static const uint8 arctan[32][32]; + + //interfaces to sin table + int16 sin(int16 theta); + int16 cos(int16 theta); + + //interfaces to ram buffer + uint8 readb (uint16 addr); + uint16 readw (uint16 addr); + uint32 readd (uint16 addr); + void writeb(uint16 addr, uint8 data); + void writew(uint16 addr, uint16 data); + void writed(uint16 addr, uint32 data); + + //opcodes + void op_01(); + void op_02(); + void op_03(); + void op_04(); + void op_05(); + void op_06(); + void op_07(); + void op_08(); + + void op_01(int16 x0, int16 y0, int16 &x1, int16 &y1, int16 &quadrant, int16 &theta); +}; + +extern ST010 st010; diff --git a/mednafen/snes/src/chip/st010/st010_data.hpp b/mednafen/snes/src/chip/st010/st010_data.hpp new file mode 100755 index 0000000..53f816f --- /dev/null +++ b/mednafen/snes/src/chip/st010/st010_data.hpp @@ -0,0 +1,126 @@ +const int16 ST010::sin_table[256] = { + 0x0000, 0x0324, 0x0648, 0x096a, 0x0c8c, 0x0fab, 0x12c8, 0x15e2, + 0x18f9, 0x1c0b, 0x1f1a, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11, + 0x30fb, 0x33df, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a, + 0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842, + 0x5a82, 0x5cb3, 0x5ed7, 0x60eb, 0x62f1, 0x64e8, 0x66cf, 0x68a6, + 0x6a6d, 0x6c23, 0x6dc9, 0x6f5e, 0x70e2, 0x7254, 0x73b5, 0x7504, + 0x7641, 0x776b, 0x7884, 0x7989, 0x7a7c, 0x7b5c, 0x7c29, 0x7ce3, + 0x7d89, 0x7e1d, 0x7e9c, 0x7f09, 0x7f61, 0x7fa6, 0x7fd8, 0x7ff5, + 0x7fff, 0x7ff5, 0x7fd8, 0x7fa6, 0x7f61, 0x7f09, 0x7e9c, 0x7e1d, + 0x7d89, 0x7ce3, 0x7c29, 0x7b5c, 0x7a7c, 0x7989, 0x7884, 0x776b, + 0x7641, 0x7504, 0x73b5, 0x7254, 0x70e2, 0x6f5e, 0x6dc9, 0x6c23, + 0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f1, 0x60eb, 0x5ed7, 0x5cb3, + 0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4, + 0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33df, + 0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f1a, 0x1c0b, + 0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8c, 0x096a, 0x0648, 0x0324, + 0x0000, -0x0324, -0x0648, -0x096b, -0x0c8c, -0x0fab, -0x12c8, -0x15e2, + -0x18f9, -0x1c0b, -0x1f1a, -0x2223, -0x2528, -0x2826, -0x2b1f, -0x2e11, + -0x30fb, -0x33df, -0x36ba, -0x398d, -0x3c56, -0x3f17, -0x41ce, -0x447a, + -0x471c, -0x49b4, -0x4c3f, -0x4ebf, -0x5133, -0x539b, -0x55f5, -0x5842, + -0x5a82, -0x5cb3, -0x5ed7, -0x60ec, -0x62f1, -0x64e8, -0x66cf, -0x68a6, + -0x6a6d, -0x6c23, -0x6dc9, -0x6f5e, -0x70e2, -0x7254, -0x73b5, -0x7504, + -0x7641, -0x776b, -0x7884, -0x7989, -0x7a7c, -0x7b5c, -0x7c29, -0x7ce3, + -0x7d89, -0x7e1d, -0x7e9c, -0x7f09, -0x7f61, -0x7fa6, -0x7fd8, -0x7ff5, + -0x7fff, -0x7ff5, -0x7fd8, -0x7fa6, -0x7f61, -0x7f09, -0x7e9c, -0x7e1d, + -0x7d89, -0x7ce3, -0x7c29, -0x7b5c, -0x7a7c, -0x7989, -0x7883, -0x776b, + -0x7641, -0x7504, -0x73b5, -0x7254, -0x70e2, -0x6f5e, -0x6dc9, -0x6c23, + -0x6a6d, -0x68a6, -0x66cf, -0x64e8, -0x62f1, -0x60eb, -0x5ed7, -0x5cb3, + -0x5a82, -0x5842, -0x55f5, -0x539a, -0x5133, -0x4ebf, -0x4c3f, -0x49b3, + -0x471c, -0x447a, -0x41cd, -0x3f17, -0x3c56, -0x398c, -0x36b9, -0x33de, + -0x30fb, -0x2e10, -0x2b1f, -0x2826, -0x2527, -0x2223, -0x1f19, -0x1c0b, + -0x18f8, -0x15e2, -0x12c8, -0x0fab, -0x0c8b, -0x096a, -0x0647, -0x0324 +}; + +const int16 ST010::mode7_scale[176] = { + 0x0380, 0x0325, 0x02da, 0x029c, 0x0268, 0x023b, 0x0215, 0x01f3, + 0x01d5, 0x01bb, 0x01a3, 0x018e, 0x017b, 0x016a, 0x015a, 0x014b, + 0x013e, 0x0132, 0x0126, 0x011c, 0x0112, 0x0109, 0x0100, 0x00f8, + 0x00f0, 0x00e9, 0x00e3, 0x00dc, 0x00d6, 0x00d1, 0x00cb, 0x00c6, + 0x00c1, 0x00bd, 0x00b8, 0x00b4, 0x00b0, 0x00ac, 0x00a8, 0x00a5, + 0x00a2, 0x009e, 0x009b, 0x0098, 0x0095, 0x0093, 0x0090, 0x008d, + 0x008b, 0x0088, 0x0086, 0x0084, 0x0082, 0x0080, 0x007e, 0x007c, + 0x007a, 0x0078, 0x0076, 0x0074, 0x0073, 0x0071, 0x006f, 0x006e, + 0x006c, 0x006b, 0x0069, 0x0068, 0x0067, 0x0065, 0x0064, 0x0063, + 0x0062, 0x0060, 0x005f, 0x005e, 0x005d, 0x005c, 0x005b, 0x005a, + 0x0059, 0x0058, 0x0057, 0x0056, 0x0055, 0x0054, 0x0053, 0x0052, + 0x0051, 0x0051, 0x0050, 0x004f, 0x004e, 0x004d, 0x004d, 0x004c, + 0x004b, 0x004b, 0x004a, 0x0049, 0x0048, 0x0048, 0x0047, 0x0047, + 0x0046, 0x0045, 0x0045, 0x0044, 0x0044, 0x0043, 0x0042, 0x0042, + 0x0041, 0x0041, 0x0040, 0x0040, 0x003f, 0x003f, 0x003e, 0x003e, + 0x003d, 0x003d, 0x003c, 0x003c, 0x003b, 0x003b, 0x003a, 0x003a, + 0x003a, 0x0039, 0x0039, 0x0038, 0x0038, 0x0038, 0x0037, 0x0037, + 0x0036, 0x0036, 0x0036, 0x0035, 0x0035, 0x0035, 0x0034, 0x0034, + 0x0034, 0x0033, 0x0033, 0x0033, 0x0032, 0x0032, 0x0032, 0x0031, + 0x0031, 0x0031, 0x0030, 0x0030, 0x0030, 0x0030, 0x002f, 0x002f, + 0x002f, 0x002e, 0x002e, 0x002e, 0x002e, 0x002d, 0x002d, 0x002d, + 0x002d, 0x002c, 0x002c, 0x002c, 0x002c, 0x002b, 0x002b, 0x002b +}; + +const uint8 ST010::arctan[32][32] = { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x80, 0xa0, 0xad, 0xb3, 0xb6, 0xb8, 0xb9, 0xba, 0xbb, 0xbb, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf }, + { 0x80, 0x93, 0xa0, 0xa8, 0xad, 0xb0, 0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd }, + { 0x80, 0x8d, 0x98, 0xa0, 0xa6, 0xaa, 0xad, 0xb0, 0xb1, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbc }, + { 0x80, 0x8a, 0x93, 0x9a, 0xa0, 0xa5, 0xa8, 0xab, 0xad, 0xaf, 0xb0, 0xb2, 0xb3, 0xb4, 0xb5, 0xb5, + 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb }, + { 0x80, 0x88, 0x90, 0x96, 0x9b, 0xa0, 0xa4, 0xa7, 0xa9, 0xab, 0xad, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb4, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9 }, + { 0x80, 0x87, 0x8d, 0x93, 0x98, 0x9c, 0xa0, 0xa3, 0xa6, 0xa8, 0xaa, 0xac, 0xad, 0xae, 0xb0, 0xb0, + 0xb1, 0xb2, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8 }, + { 0x80, 0x86, 0x8b, 0x90, 0x95, 0x99, 0x9d, 0xa0, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac, 0xad, 0xae, + 0xaf, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7 }, + { 0x80, 0x85, 0x8a, 0x8f, 0x93, 0x97, 0x9a, 0x9d, 0xa0, 0xa2, 0xa5, 0xa6, 0xa8, 0xaa, 0xab, 0xac, + 0xad, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5 }, + { 0x80, 0x85, 0x89, 0x8d, 0x91, 0x95, 0x98, 0x9b, 0x9e, 0xa0, 0xa0, 0xa4, 0xa6, 0xa7, 0xa9, 0xaa, + 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4 }, + { 0x80, 0x84, 0x88, 0x8c, 0x90, 0x93, 0x96, 0x99, 0x9b, 0x9e, 0xa0, 0xa2, 0xa4, 0xa5, 0xa7, 0xa8, + 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3 }, + { 0x80, 0x84, 0x87, 0x8b, 0x8e, 0x91, 0x94, 0x97, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa3, 0xa5, 0xa6, + 0xa7, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2 }, + { 0x80, 0x83, 0x87, 0x8a, 0x8d, 0x90, 0x93, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa3, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1 }, + { 0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x94, 0x96, 0x99, 0x9b, 0x9d, 0x9e, 0xa0, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xb0 }, + { 0x80, 0x83, 0x86, 0x89, 0x8b, 0x8e, 0x90, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9d, 0x9e, 0xa0, 0xa1, + 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf }, + { 0x80, 0x83, 0x85, 0x88, 0x8b, 0x8d, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9b, 0x9d, 0x9f, 0xa0, + 0xa1, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae }, + { 0x80, 0x83, 0x85, 0x88, 0x8a, 0x8c, 0x8f, 0x91, 0x93, 0x95, 0x97, 0x99, 0x9a, 0x9c, 0x9d, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xab, 0xac, 0xad }, + { 0x80, 0x82, 0x85, 0x87, 0x89, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x97, 0x99, 0x9b, 0x9c, 0x9d, + 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac }, + { 0x80, 0x82, 0x85, 0x87, 0x89, 0x8b, 0x8d, 0x8f, 0x91, 0x93, 0x95, 0x96, 0x98, 0x99, 0x9b, 0x9c, + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab }, + { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x95, 0x97, 0x98, 0x9a, 0x9b, + 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa }, + { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x99, 0x9a, + 0x9b, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9 }, + { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8f, 0x90, 0x92, 0x94, 0x95, 0x97, 0x98, 0x99, + 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8 }, + { 0x80, 0x82, 0x84, 0x86, 0x87, 0x89, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x98, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7 }, + { 0x80, 0x82, 0x84, 0x85, 0x87, 0x89, 0x8a, 0x8c, 0x8e, 0x8f, 0x91, 0x92, 0x94, 0x95, 0x96, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5, 0xa6 }, + { 0x80, 0x82, 0x83, 0x85, 0x87, 0x88, 0x8a, 0x8c, 0x8d, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5 }, + { 0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4 }, + { 0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x89, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x95, + 0x96, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4 }, + { 0x80, 0x82, 0x83, 0x85, 0x86, 0x87, 0x89, 0x8a, 0x8c, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x93, 0x95, + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2, 0xa3 }, + { 0x80, 0x81, 0x83, 0x84, 0x86, 0x87, 0x89, 0x8a, 0x8b, 0x8d, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2 }, + { 0x80, 0x81, 0x83, 0x84, 0x86, 0x87, 0x88, 0x8a, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa1 }, + { 0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8b, 0x8c, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1 }, + { 0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0 } +}; diff --git a/mednafen/snes/src/chip/st010/st010_op.cpp b/mednafen/snes/src/chip/st010/st010_op.cpp new file mode 100755 index 0000000..b22a861 --- /dev/null +++ b/mednafen/snes/src/chip/st010/st010_op.cpp @@ -0,0 +1,261 @@ +#ifdef ST010_CPP + +//ST-010 emulation code - Copyright (C) 2003 The Dumper, Matthew Kendora, Overload, Feather +//bsnes port - Copyright (C) 2007 byuu + +void ST010::op_01(int16 x0, int16 y0, int16 &x1, int16 &y1, int16 &quadrant, int16 &theta) { + if((x0 < 0) && (y0 < 0)) { + x1 = -x0; + y1 = -y0; + quadrant = -0x8000; + } else if(x0 < 0) { + x1 = y0; + y1 = -x0; + quadrant = -0x4000; + } else if(y0 < 0) { + x1 = -y0; + y1 = x0; + quadrant = 0x4000; + } else { + x1 = x0; + y1 = y0; + quadrant = 0x0000; + } + + while((x1 > 0x1f) || (y1 > 0x1f)) { + if(x1 > 1) { x1 >>= 1; } + if(y1 > 1) { y1 >>= 1; } + } + + if(y1 == 0) { quadrant += 0x4000; } + + theta = (arctan[y1][x1] << 8) ^ quadrant; +} + +// + +void ST010::op_01() { + int16 x0 = readw(0x0000); + int16 y0 = readw(0x0002); + int16 x1, y1, quadrant, theta; + + op_01(x0, y0, x1, y1, quadrant, theta); + + writew(0x0000, x1); + writew(0x0002, y1); + writew(0x0004, quadrant); +//writew(0x0006, y0); //Overload's docs note this write occurs, SNES9x disagrees + writew(0x0010, theta); +} + +void ST010::op_02() { + int16 positions = readw(0x0024); + uint16 *places = (uint16*)(ram + 0x0040); + uint16 *drivers = (uint16*)(ram + 0x0080); + + bool sorted; + uint16 temp; + if(positions > 1) { + do { + sorted = true; + for(int i = 0; i < positions - 1; i++) { + if(places[i] < places[i + 1]) { + temp = places[i + 1]; + places[i + 1] = places[i]; + places[i] = temp; + + temp = drivers[i + 1]; + drivers[i + 1] = drivers[i]; + drivers[i] = temp; + + sorted = false; + } + } + positions--; + } while(!sorted); + } +} + +void ST010::op_03() { + int16 x0 = readw(0x0000); + int16 y0 = readw(0x0002); + int16 multiplier = readw(0x0004); + int32 x1, y1; + + x1 = x0 * multiplier << 1; + y1 = y0 * multiplier << 1; + + writed(0x0010, x1); + writed(0x0014, y1); +} + +void ST010::op_04() { + int16 x = readw(0x0000); + int16 y = readw(0x0002); + int16 square; + //calculate the vector length of (x,y) + square = (int16)sqrt((double)(y * y + x * x)); + + writew(0x0010, square); +} + +void ST010::op_05() { + int32 dx, dy; + int16 a1, b1, c1; + uint16 o1; + bool wrap = false; + + //target (x,y) coordinates + int16 ypos_max = readw(0x00c0); + int16 xpos_max = readw(0x00c2); + + //current coordinates and direction + int32 ypos = readd(0x00c4); + int32 xpos = readd(0x00c8); + uint16 rot = readw(0x00cc); + + //physics + uint16 speed = readw(0x00d4); + uint16 accel = readw(0x00d6); + uint16 speed_max = readw(0x00d8); + + //special condition acknowledgement + int16 system = readw(0x00da); + int16 flags = readw(0x00dc); + + //new target coordinates + int16 ypos_new = readw(0x00de); + int16 xpos_new = readw(0x00e0); + + //mask upper bit + xpos_new &= 0x7fff; + + //get the current distance + dx = xpos_max - (xpos >> 16); + dy = ypos_max - (ypos >> 16); + + //quirk: clear and move in9 + writew(0x00d2, 0xffff); + writew(0x00da, 0x0000); + + //grab the target angle + op_01(dy, dx, a1, b1, c1, (int16&)o1); + + //check for wrapping + if(abs(o1 - rot) > 0x8000) { + o1 += 0x8000; + rot += 0x8000; + wrap = true; + } + + uint16 old_speed = speed; + + //special case + if(abs(o1 - rot) == 0x8000) { + speed = 0x100; + } + + //slow down for sharp curves + else if(abs(o1 - rot) >= 0x1000) { + uint32 slow = abs(o1 - rot); + slow >>= 4; //scaling + speed -= slow; + } + + //otherwise accelerate + else { + speed += accel; + if(speed > speed_max) { + speed = speed_max; //clip speed + } + } + + //prevent negative/positive overflow + if(abs(old_speed - speed) > 0x8000) { + if(old_speed < speed) { speed = 0; } + else speed = 0xff00; + } + + //adjust direction by so many degrees + //be careful of negative adjustments + if((o1 > rot && (o1 - rot) > 0x80) || (o1 < rot && (rot - o1) >= 0x80)) { + if(o1 < rot) { rot -= 0x280; } + else if(o1 > rot) { rot += 0x280; } + } + + //turn off wrapping + if(wrap) { rot -= 0x8000; } + + //now check the distances (store for later) + dx = (xpos_max << 16) - xpos; + dy = (ypos_max << 16) - ypos; + dx >>= 16; + dy >>= 16; + + //if we're in so many units of the target, signal it + if((system && (dy <= 6 && dy >= -8) && (dx <= 126 && dx >= -128)) || (!system && (dx <= 6 && dx >= -8) && (dy <= 126 && dy >= -128))) { + //announce our new destination and flag it + xpos_max = xpos_new & 0x7fff; + ypos_max = ypos_new; + flags |= 0x08; + } + + //update position + xpos -= (cos(rot) * 0x400 >> 15) * (speed >> 8) << 1; + ypos -= (sin(rot) * 0x400 >> 15) * (speed >> 8) << 1; + + //quirk: mask upper byte + xpos &= 0x1fffffff; + ypos &= 0x1fffffff; + + writew(0x00c0, ypos_max); + writew(0x00c2, xpos_max); + writed(0x00c4, ypos); + writed(0x00c8, xpos); + writew(0x00cc, rot); + writew(0x00d4, speed); + writew(0x00dc, flags); +} + +void ST010::op_06() { + int16 multiplicand = readw(0x0000); + int16 multiplier = readw(0x0002); + int32 product; + + product = multiplicand * multiplier << 1; + + writed(0x0010, product); +} + +void ST010::op_07() { + int16 theta = readw(0x0000); + + int16 data; + for(int i = 0, offset = 0; i < 176; i++) { + data = mode7_scale[i] * cos(theta) >> 15; + writew(0x00f0 + offset, data); + writew(0x0510 + offset, data); + + data = mode7_scale[i] * sin(theta) >> 15; + writew(0x0250 + offset, data); + if(data) { data = ~data; } + writew(0x03b0 + offset, data); + + offset += 2; + } +} + +void ST010::op_08() { + int16 x0 = readw(0x0000); + int16 y0 = readw(0x0002); + int16 theta = readw(0x0004); + int16 x1, y1; + + x1 = (y0 * sin(theta) >> 15) + (x0 * cos(theta) >> 15); + y1 = (y0 * cos(theta) >> 15) - (x0 * sin(theta) >> 15); + + writew(0x0010, x1); + writew(0x0012, y1); +} + +#endif diff --git a/mednafen/snes/src/chip/st011/st011.cpp b/mednafen/snes/src/chip/st011/st011.cpp new file mode 100755 index 0000000..2ec90f4 --- /dev/null +++ b/mednafen/snes/src/chip/st011/st011.cpp @@ -0,0 +1,20 @@ +#include <../base.hpp> + +#define ST011_CPP +namespace SNES { + +ST011 st011; + +void ST011::init() { +} + +void ST011::enable() { +} + +void ST011::power() { +} + +void ST011::reset() { +} + +}; diff --git a/mednafen/snes/src/chip/st011/st011.hpp b/mednafen/snes/src/chip/st011/st011.hpp new file mode 100755 index 0000000..ddc510d --- /dev/null +++ b/mednafen/snes/src/chip/st011/st011.hpp @@ -0,0 +1,9 @@ +class ST011 { +public: + void init(); + void enable(); + void power(); + void reset(); +}; + +extern ST011 st011; diff --git a/mednafen/snes/src/chip/st018/st018.cpp b/mednafen/snes/src/chip/st018/st018.cpp new file mode 100755 index 0000000..c3c34b8 --- /dev/null +++ b/mednafen/snes/src/chip/st018/st018.cpp @@ -0,0 +1,124 @@ +#include <../base.hpp> + +#define ST018_CPP +namespace SNES { + +ST018 st018; + +uint8 ST018::mmio_read(unsigned addr) { + addr &= 0xffff; + if(addr == 0x3800) return regs.r3800; + if(addr == 0x3804) return regs.r3804; + return cpu.regs.mdr; +} + +void ST018::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + if(addr == 0x3802) { + switch(regs.mode) { + case Waiting: { + switch(data) { + case 0x01: regs.r3800 = regs.r3800_01; break; + case 0xaa: op_board_upload(); break; + case 0xb2: op_b2(); break; + case 0xb3: op_b3(); break; + case 0xb4: op_b4(); break; + case 0xb5: op_b5(); break; + case 0xf1: op_query_chip(); break; + case 0xf2: op_query_chip(); break; + default: fprintf(stdout, "* ST018 w3802::%.2x\n", data); break; + } + } return; + + case BoardUpload: { + op_board_upload(data); + } return; + } + } + + if(addr == 0x3804) { + regs.w3804 <<= 8; + regs.w3804 |= data; + regs.w3804 &= 0xffffff; + return; + } +} + +void ST018::init() { +} + +void ST018::enable() { + for(unsigned i = 0x3800; i <= 0x38ff; i++) memory::mmio.map(i, *this); +} + +void ST018::power() { + reset(); +} + +void ST018::reset() { + regs.mode = Waiting; + regs.r3800 = 0x00; + regs.r3804 = 0x85; + regs.w3804 = 0; + for(unsigned i = 0; i < 97; i++) board[i] = 0; +} + +//============= +//ST018 opcodes +//============= + +void ST018::op_board_upload() { + regs.mode = BoardUpload; + regs.counter = 0; + regs.r3800 = 0xe0; +} + +void ST018::op_board_upload(uint8 data) { + board[regs.counter] = data; + regs.r3800 = 96 - regs.counter; + regs.counter++; + if(regs.counter >= 97) { + regs.mode = Waiting; + #if 0 + for(unsigned y = 0; y < 9; y++) { + for(unsigned x = 0; x < 9; x++) { + fprintf(stdout, "%.2x ", board[y * 9 + x]); + } + fprintf(stdout, "\n"); + } + for(unsigned n = 0; n < 16; n++) fprintf(stdout, "%.2x ", board[81 + n]); + fprintf(stdout, "\n\n"); + #endif + } +} + +void ST018::op_b2() { + fprintf(stdout, "* ST018 w3802::b2\n"); + regs.r3800 = 0xe0; + regs.r3800_01 = 0; //unknown +} + +void ST018::op_b3() { + fprintf(stdout, "* ST018 w3802::b3\n"); + regs.r3800 = 0xe0; + regs.r3800_01 = 1; //0 = player lost? +} + +void ST018::op_b4() { + fprintf(stdout, "* ST018 w3802::b4\n"); + regs.r3800 = 0xe0; + regs.r3800_01 = 1; //0 = player won? +} + +void ST018::op_b5() { + fprintf(stdout, "* ST018 w3802::b5\n"); + regs.r3800 = 0xe0; + regs.r3800_01 = 0; //1 = move will result in checkmate? +} + +void ST018::op_query_chip() { + regs.r3800 = 0x00; +} + +}; diff --git a/mednafen/snes/src/chip/st018/st018.hpp b/mednafen/snes/src/chip/st018/st018.hpp new file mode 100755 index 0000000..d64be4e --- /dev/null +++ b/mednafen/snes/src/chip/st018/st018.hpp @@ -0,0 +1,51 @@ +class ST018 : public MMIO { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + enum mode_t { Waiting, BoardUpload }; + struct regs_t { + mode_t mode; + + uint8 r3800; + uint8 r3800_01; + uint8 r3804; + + unsigned w3804; + unsigned counter; + } regs; + + enum PieceID { + Pawn = 0x00, //foot soldier + Lance = 0x04, //incense chariot + Knight = 0x08, //cassia horse + Silver = 0x0c, //silver general + Gold = 0x10, //gold general + Rook = 0x14, //flying chariot + Bishop = 0x18, //angle mover + King = 0x1c, //king + }; + + enum PieceFlag { + PlayerA = 0x20, + PlayerB = 0x40, + }; + + uint8 board[9 * 9 + 16]; + +private: + void op_board_upload(); + void op_board_upload(uint8 data); + void op_b2(); + void op_b3(); + void op_b4(); + void op_b5(); + void op_query_chip(); +}; + +extern ST018 st018; diff --git a/mednafen/snes/src/chip/superfx/bus/bus.cpp b/mednafen/snes/src/chip/superfx/bus/bus.cpp new file mode 100755 index 0000000..ac932ac --- /dev/null +++ b/mednafen/snes/src/chip/superfx/bus/bus.cpp @@ -0,0 +1,106 @@ +#ifdef SUPERFX_CPP + +SuperFXBus superfxbus; + +namespace memory { + SuperFXGSUROM gsurom; + SuperFXGSURAM gsuram; + SuperFXCPUROM fxrom; + SuperFXCPURAM fxram; +} + +void SuperFXBus::init() { + map(MapDirect, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped); + + map(MapLinear, 0x00, 0x3f, 0x0000, 0x7fff, memory::gsurom); + map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::gsurom); + map(MapLinear, 0x40, 0x5f, 0x0000, 0xffff, memory::gsurom); + map(MapLinear, 0x60, 0x7f, 0x0000, 0xffff, memory::gsuram); + + bus.map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::fxram, 0x0000, 0x2000); + bus.map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::fxrom); + bus.map(MapLinear, 0x40, 0x5f, 0x0000, 0xffff, memory::fxrom); + bus.map(MapLinear, 0x60, 0x7d, 0x0000, 0xffff, memory::fxram); + bus.map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::fxram, 0x0000, 0x2000); + bus.map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::fxrom); + bus.map(MapLinear, 0xc0, 0xdf, 0x0000, 0xffff, memory::fxrom); + bus.map(MapLinear, 0xe0, 0xff, 0x0000, 0xffff, memory::fxram); +} + +//ROM / RAM access from the SuperFX CPU + +unsigned SuperFXGSUROM::size() const { + return memory::cartrom.size(); +} + +uint8 SuperFXGSUROM::read(unsigned addr) { + while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SyncAll) { + superfx.add_clocks(6); + scheduler.sync_copcpu(); + } + return memory::cartrom.read(addr); +} + +void SuperFXGSUROM::write(unsigned addr, uint8 data) { + while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SyncAll) { + superfx.add_clocks(6); + scheduler.sync_copcpu(); + } + memory::cartrom.write(addr, data); +} + +unsigned SuperFXGSURAM::size() const { + return memory::cartram.size(); +} + +uint8 SuperFXGSURAM::read(unsigned addr) { + while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SyncAll) { + superfx.add_clocks(6); + scheduler.sync_copcpu(); + } + return memory::cartram.read(addr); +} + +void SuperFXGSURAM::write(unsigned addr, uint8 data) { + while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SyncAll) { + superfx.add_clocks(6); + scheduler.sync_copcpu(); + } + memory::cartram.write(addr, data); +} + +//ROM / RAM access from the S-CPU + +unsigned SuperFXCPUROM::size() const { + return memory::cartrom.size(); +} + +uint8 SuperFXCPUROM::read(unsigned addr) { + if(superfx.regs.sfr.g && superfx.regs.scmr.ron) { + static const uint8_t data[16] = { + 0x00, 0x01, 0x00, 0x01, 0x04, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x08, 0x01, 0x00, 0x01, 0x0c, 0x01, + }; + return data[addr & 15]; + } + return memory::cartrom.read(addr); +} + +void SuperFXCPUROM::write(unsigned addr, uint8 data) { + memory::cartrom.write(addr, data); +} + +unsigned SuperFXCPURAM::size() const { + return memory::cartram.size(); +} + +uint8 SuperFXCPURAM::read(unsigned addr) { + if(superfx.regs.sfr.g && superfx.regs.scmr.ran) return cpu.regs.mdr; + return memory::cartram.read(addr); +} + +void SuperFXCPURAM::write(unsigned addr, uint8 data) { + memory::cartram.write(addr, data); +} + +#endif diff --git a/mednafen/snes/src/chip/superfx/bus/bus.hpp b/mednafen/snes/src/chip/superfx/bus/bus.hpp new file mode 100755 index 0000000..dd615c6 --- /dev/null +++ b/mednafen/snes/src/chip/superfx/bus/bus.hpp @@ -0,0 +1,34 @@ +struct SuperFXBus : Bus { + void init(); +}; + +struct SuperFXGSUROM : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +struct SuperFXGSURAM : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +struct SuperFXCPUROM : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +struct SuperFXCPURAM : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +namespace memory { + extern SuperFXGSUROM gsurom; + extern SuperFXGSURAM gsuram; + extern SuperFXCPUROM fxrom; + extern SuperFXCPURAM fxram; +} diff --git a/mednafen/snes/src/chip/superfx/core/core.cpp b/mednafen/snes/src/chip/superfx/core/core.cpp new file mode 100755 index 0000000..229ad24 --- /dev/null +++ b/mednafen/snes/src/chip/superfx/core/core.cpp @@ -0,0 +1,107 @@ +#ifdef SUPERFX_CPP + +#include "opcodes.cpp" +#include "opcode_table.cpp" + +uint8 SuperFX::color(uint8 source) { + if(regs.por.highnibble) return (regs.colr & 0xf0) | (source >> 4); + if(regs.por.freezehigh) return (regs.colr & 0xf0) | (source & 0x0f); + return source; +} + +void SuperFX::plot(uint8 x, uint8 y) { + uint8 color = regs.colr; + + if(regs.por.dither && regs.scmr.md != 3) { + if((x ^ y) & 1) color >>= 4; + color &= 0x0f; + } + + if(!regs.por.transparent) { + if(regs.scmr.md == 3) { + if(regs.por.freezehigh) { + if((color & 0x0f) == 0) return; + } else { + if(color == 0) return; + } + } else { + if((color & 0x0f) == 0) return; + } + } + + uint16 offset = (y << 5) + (x >> 3); + if(offset != pixelcache[0].offset) { + pixelcache_flush(pixelcache[1]); + pixelcache[1] = pixelcache[0]; + pixelcache[0].bitpend = 0x00; + pixelcache[0].offset = offset; + } + + x = (x & 7) ^ 7; + pixelcache[0].data[x] = color; + pixelcache[0].bitpend |= 1 << x; + if(pixelcache[0].bitpend == 0xff) { + pixelcache_flush(pixelcache[1]); + pixelcache[1] = pixelcache[0]; + pixelcache[0].bitpend = 0x00; + } +} + +uint8 SuperFX::rpix(uint8 x, uint8 y) { + pixelcache_flush(pixelcache[1]); + pixelcache_flush(pixelcache[0]); + + unsigned cn; //character number + switch(regs.por.obj ? 3 : regs.scmr.ht) { + case 0: cn = ((x & 0xf8) << 1) + ((y & 0xf8) >> 3); break; + case 1: cn = ((x & 0xf8) << 1) + ((x & 0xf8) >> 1) + ((y & 0xf8) >> 3); break; + case 2: cn = ((x & 0xf8) << 1) + ((x & 0xf8) << 0) + ((y & 0xf8) >> 3); break; + case 3: cn = ((y & 0x80) << 2) + ((x & 0x80) << 1) + ((y & 0x78) << 1) + ((x & 0x78) >> 3); break; + } + unsigned bpp = 2 << (regs.scmr.md - (regs.scmr.md >> 1)); // = [regs.scmr.md]{ 2, 4, 4, 8 }; + unsigned addr = 0x700000 + (cn * (bpp << 3)) + (regs.scbr << 10) + ((y & 0x07) * 2); + uint8 data = 0x00; + x = (x & 7) ^ 7; + + for(unsigned n = 0; n < bpp; n++) { + unsigned byte = ((n >> 1) << 4) + (n & 1); // = [n]{ 0, 1, 16, 17, 32, 33, 48, 49 }; + add_clocks(memory_access_speed); + data |= ((superfxbus.read(addr + byte) >> x) & 1) << n; + } + + return data; +} + +void SuperFX::pixelcache_flush(pixelcache_t &cache) { + if(cache.bitpend == 0x00) return; + + uint8 x = cache.offset << 3; + uint8 y = cache.offset >> 5; + + unsigned cn; //character number + switch(regs.por.obj ? 3 : regs.scmr.ht) { + case 0: cn = ((x & 0xf8) << 1) + ((y & 0xf8) >> 3); break; + case 1: cn = ((x & 0xf8) << 1) + ((x & 0xf8) >> 1) + ((y & 0xf8) >> 3); break; + case 2: cn = ((x & 0xf8) << 1) + ((x & 0xf8) << 0) + ((y & 0xf8) >> 3); break; + case 3: cn = ((y & 0x80) << 2) + ((x & 0x80) << 1) + ((y & 0x78) << 1) + ((x & 0x78) >> 3); break; + } + unsigned bpp = 2 << (regs.scmr.md - (regs.scmr.md >> 1)); // = [regs.scmr.md]{ 2, 4, 4, 8 }; + unsigned addr = 0x700000 + (cn * (bpp << 3)) + (regs.scbr << 10) + ((y & 0x07) * 2); + + for(unsigned n = 0; n < bpp; n++) { + unsigned byte = ((n >> 1) << 4) + (n & 1); // = [n]{ 0, 1, 16, 17, 32, 33, 48, 49 }; + uint8 data = 0x00; + for(unsigned x = 0; x < 8; x++) data |= ((cache.data[x] >> n) & 1) << x; + if(cache.bitpend != 0xff) { + add_clocks(memory_access_speed); + data &= cache.bitpend; + data |= superfxbus.read(addr + byte) & ~cache.bitpend; + } + add_clocks(memory_access_speed); + superfxbus.write(addr + byte, data); + } + + cache.bitpend = 0x00; +} + +#endif diff --git a/mednafen/snes/src/chip/superfx/core/core.hpp b/mednafen/snes/src/chip/superfx/core/core.hpp new file mode 100755 index 0000000..1c13f1c --- /dev/null +++ b/mednafen/snes/src/chip/superfx/core/core.hpp @@ -0,0 +1,92 @@ +#include "registers.hpp" + +uint8 color(uint8 source); +void plot(uint8 x, uint8 y); +uint8 rpix(uint8 x, uint8 y); +void pixelcache_flush(pixelcache_t &cache); + +void (SuperFX::*opcode_table[1024])(); +void initialize_opcode_table(); + +//opcodes.cpp +template void op_adc_i(); +template void op_adc_r(); +template void op_add_i(); +template void op_add_r(); +void op_alt1(); +void op_alt2(); +void op_alt3(); +template void op_and_i(); +template void op_and_r(); +void op_asr(); +void op_bge(); +void op_bcc(); +void op_bcs(); +void op_beq(); +template void op_bic_i(); +template void op_bic_r(); +void op_blt(); +void op_bmi(); +void op_bne(); +void op_bpl(); +void op_bra(); +void op_bvc(); +void op_bvs(); +void op_cache(); +void op_cmode(); +template void op_cmp_r(); +void op_color(); +template void op_dec_r(); +void op_div2(); +void op_fmult(); +template void op_from_r(); +void op_getb(); +void op_getbl(); +void op_getbh(); +void op_getbs(); +void op_getc(); +void op_hib(); +template void op_ibt_r(); +template void op_inc_r(); +template void op_iwt_r(); +template void op_jmp_r(); +template void op_ldb_ir(); +template void op_ldw_ir(); +template void op_link(); +template void op_ljmp_r(); +template void op_lm_r(); +template void op_lms_r(); +void op_lmult(); +void op_lob(); +void op_loop(); +void op_lsr(); +void op_merge(); +template void op_mult_i(); +template void op_mult_r(); +void op_nop(); +void op_not(); +template void op_or_i(); +template void op_or_r(); +void op_plot(); +void op_ramb(); +void op_rol(); +void op_romb(); +void op_ror(); +void op_rpix(); +template void op_sbc_r(); +void op_sbk(); +void op_sex(); +template void op_sm_r(); +template void op_sms_r(); +template void op_stb_ir(); +void op_stop(); +template void op_stw_ir(); +template void op_sub_i(); +template void op_sub_r(); +void op_swap(); +template void op_to_r(); +template void op_umult_i(); +template void op_umult_r(); +template void op_with_r(); +template void op_xor_i(); +template void op_xor_r(); diff --git a/mednafen/snes/src/chip/superfx/core/opcode_table.cpp b/mednafen/snes/src/chip/superfx/core/opcode_table.cpp new file mode 100755 index 0000000..7908253 --- /dev/null +++ b/mednafen/snes/src/chip/superfx/core/opcode_table.cpp @@ -0,0 +1,270 @@ +#ifdef SUPERFX_CPP + +void SuperFX::initialize_opcode_table() { + #define op4(id, name) \ + op(id+ 0, name< 1>) op(id+ 1, name< 2>) op(id+ 2, name< 3>) op(id+ 3, name< 4>) + + #define op6(id, name) \ + op(id+ 0, name< 8>) op(id+ 1, name< 9>) op(id+ 2, name<10>) op(id+ 3, name<11>) \ + op(id+ 4, name<12>) op(id+ 5, name<13>) + + #define op12(id, name) \ + op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \ + op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \ + op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) + + #define op15l(id, name) \ + op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \ + op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \ + op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) \ + op(id+12, name<12>) op(id+13, name<13>) op(id+14, name<14>) + + #define op15h(id, name) \ + op(id+ 0, name< 1>) op(id+ 1, name< 2>) op(id+ 2, name< 3>) op(id+ 3, name< 4>) \ + op(id+ 4, name< 5>) op(id+ 5, name< 6>) op(id+ 6, name< 7>) op(id+ 7, name< 8>) \ + op(id+ 8, name< 9>) op(id+ 9, name<10>) op(id+10, name<11>) op(id+11, name<12>) \ + op(id+12, name<13>) op(id+13, name<14>) op(id+14, name<15>) + + #define op16(id, name) \ + op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \ + op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \ + op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) \ + op(id+12, name<12>) op(id+13, name<13>) op(id+14, name<14>) op(id+15, name<15>) + + //====== + // ALT0 + //====== + + #define op(id, name) opcode_table[ 0 + id] = &SuperFX::op_##name; + op (0x00, stop) + op (0x01, nop) + op (0x02, cache) + op (0x03, lsr) + op (0x04, rol) + op (0x05, bra) + op (0x06, blt) + op (0x07, bge) + op (0x08, bne) + op (0x09, beq) + op (0x0a, bpl) + op (0x0b, bmi) + op (0x0c, bcc) + op (0x0d, bcs) + op (0x0e, bvc) + op (0x0f, bvs) + op16 (0x10, to_r) + op16 (0x20, with_r) + op12 (0x30, stw_ir) + op (0x3c, loop) + op (0x3d, alt1) + op (0x3e, alt2) + op (0x3f, alt3) + op12 (0x40, ldw_ir) + op (0x4c, plot) + op (0x4d, swap) + op (0x4e, color) + op (0x4f, not) + op16 (0x50, add_r) + op16 (0x60, sub_r) + op (0x70, merge) + op15h(0x71, and_r) + op16 (0x80, mult_r) + op (0x90, sbk) + op4 (0x91, link) + op (0x95, sex) + op (0x96, asr) + op (0x97, ror) + op6 (0x98, jmp_r) + op (0x9e, lob) + op (0x9f, fmult) + op16 (0xa0, ibt_r) + op16 (0xb0, from_r) + op (0xc0, hib) + op15h(0xc1, or_r) + op15l(0xd0, inc_r) + op (0xdf, getc) + op15l(0xe0, dec_r) + op (0xef, getb) + op16 (0xf0, iwt_r) + #undef op + + //====== + // ALT1 + //====== + + #define op(id, name) opcode_table[256 + id] = &SuperFX::op_##name; + op (0x00, stop) + op (0x01, nop) + op (0x02, cache) + op (0x03, lsr) + op (0x04, rol) + op (0x05, bra) + op (0x06, blt) + op (0x07, bge) + op (0x08, bne) + op (0x09, beq) + op (0x0a, bpl) + op (0x0b, bmi) + op (0x0c, bcc) + op (0x0d, bcs) + op (0x0e, bvc) + op (0x0f, bvs) + op16 (0x10, to_r) + op16 (0x20, with_r) + op12 (0x30, stb_ir) + op (0x3c, loop) + op (0x3d, alt1) + op (0x3e, alt2) + op (0x3f, alt3) + op12 (0x40, ldb_ir) + op (0x4c, rpix) + op (0x4d, swap) + op (0x4e, cmode) + op (0x4f, not) + op16 (0x50, adc_r) + op16 (0x60, sbc_r) + op (0x70, merge) + op15h(0x71, bic_r) + op16 (0x80, umult_r) + op (0x90, sbk) + op4 (0x91, link) + op (0x95, sex) + op (0x96, div2) + op (0x97, ror) + op6 (0x98, ljmp_r) + op (0x9e, lob) + op (0x9f, lmult) + op16 (0xa0, lms_r) + op16 (0xb0, from_r) + op (0xc0, hib) + op15h(0xc1, xor_r) + op15l(0xd0, inc_r) + op (0xdf, getc) + op15l(0xe0, dec_r) + op (0xef, getbh) + op16 (0xf0, lm_r) + #undef op + + //====== + // ALT2 + //====== + + #define op(id, name) opcode_table[512 + id] = &SuperFX::op_##name; + op (0x00, stop) + op (0x01, nop) + op (0x02, cache) + op (0x03, lsr) + op (0x04, rol) + op (0x05, bra) + op (0x06, blt) + op (0x07, bge) + op (0x08, bne) + op (0x09, beq) + op (0x0a, bpl) + op (0x0b, bmi) + op (0x0c, bcc) + op (0x0d, bcs) + op (0x0e, bvc) + op (0x0f, bvs) + op16 (0x10, to_r) + op16 (0x20, with_r) + op12 (0x30, stw_ir) + op (0x3c, loop) + op (0x3d, alt1) + op (0x3e, alt2) + op (0x3f, alt3) + op12 (0x40, ldw_ir) + op (0x4c, plot) + op (0x4d, swap) + op (0x4e, color) + op (0x4f, not) + op16 (0x50, add_i) + op16 (0x60, sub_i) + op (0x70, merge) + op15h(0x71, and_i) + op16 (0x80, mult_i) + op (0x90, sbk) + op4 (0x91, link) + op (0x95, sex) + op (0x96, asr) + op (0x97, ror) + op6 (0x98, jmp_r) + op (0x9e, lob) + op (0x9f, fmult) + op16 (0xa0, sms_r) + op16 (0xb0, from_r) + op (0xc0, hib) + op15h(0xc1, or_i) + op15l(0xd0, inc_r) + op (0xdf, ramb) + op15l(0xe0, dec_r) + op (0xef, getbl) + op16 (0xf0, sm_r) + #undef op + + //====== + // ALT3 + //====== + + #define op(id, name) opcode_table[768 + id] = &SuperFX::op_##name; + op (0x00, stop) + op (0x01, nop) + op (0x02, cache) + op (0x03, lsr) + op (0x04, rol) + op (0x05, bra) + op (0x06, blt) + op (0x07, bge) + op (0x08, bne) + op (0x09, beq) + op (0x0a, bpl) + op (0x0b, bmi) + op (0x0c, bcc) + op (0x0d, bcs) + op (0x0e, bvc) + op (0x0f, bvs) + op16 (0x10, to_r) + op16 (0x20, with_r) + op12 (0x30, stb_ir) + op (0x3c, loop) + op (0x3d, alt1) + op (0x3e, alt2) + op (0x3f, alt3) + op12 (0x40, ldb_ir) + op (0x4c, rpix) + op (0x4d, swap) + op (0x4e, cmode) + op (0x4f, not) + op16 (0x50, adc_i) + op16 (0x60, cmp_r) + op (0x70, merge) + op15h(0x71, bic_i) + op16 (0x80, umult_i) + op (0x90, sbk) + op4 (0x91, link) + op (0x95, sex) + op (0x96, div2) + op (0x97, ror) + op6 (0x98, ljmp_r) + op (0x9e, lob) + op (0x9f, lmult) + op16 (0xa0, lms_r) + op16 (0xb0, from_r) + op (0xc0, hib) + op15h(0xc1, xor_i) + op15l(0xd0, inc_r) + op (0xdf, romb) + op15l(0xe0, dec_r) + op (0xef, getbs) + op16 (0xf0, lm_r) + #undef op + + #undef op4 + #undef op6 + #undef op12 + #undef op15l + #undef op15h + #undef op16 +} + +#endif diff --git a/mednafen/snes/src/chip/superfx/core/opcodes.cpp b/mednafen/snes/src/chip/superfx/core/opcodes.cpp new file mode 100755 index 0000000..7d2f13a --- /dev/null +++ b/mednafen/snes/src/chip/superfx/core/opcodes.cpp @@ -0,0 +1,661 @@ +#ifdef SUPERFX_CPP + +//$00 stop +void SuperFX::op_stop() { + if(regs.cfgr.irq == 0) { + regs.sfr.irq = 1; + cpu.regs.irq = 1; + } + + regs.sfr.g = 0; + regs.pipeline = 0x01; + regs.reset(); +} + +//$01 nop +void SuperFX::op_nop() { + regs.reset(); +} + +//$02 cache +void SuperFX::op_cache() { + if(regs.cbr != (regs.r[15] & 0xfff0)) { + regs.cbr = regs.r[15] & 0xfff0; + cache_flush(); + } + regs.reset(); +} + +//$03 lsr +void SuperFX::op_lsr() { + regs.sfr.cy = (regs.sr() & 1); + regs.dr() = regs.sr() >> 1; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$04 rol +void SuperFX::op_rol() { + bool carry = (regs.sr() & 0x8000); + regs.dr() = (regs.sr() << 1) | regs.sfr.cy; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.cy = carry; + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$05 bra e +void SuperFX::op_bra() { + regs.r[15] += (int8)pipe(); +} + +//$06 blt e +void SuperFX::op_blt() { + int e = (int8)pipe(); + if((regs.sfr.s ^ regs.sfr.ov) == 0) regs.r[15] += e; +} + +//$07 bge e +void SuperFX::op_bge() { + int e = (int8)pipe(); + if((regs.sfr.s ^ regs.sfr.ov) == 1) regs.r[15] += e; +} + +//$08 bne e +void SuperFX::op_bne() { + int e = (int8)pipe(); + if(regs.sfr.z == 0) regs.r[15] += e; +} + +//$09 beq e +void SuperFX::op_beq() { + int e = (int8)pipe(); + if(regs.sfr.z == 1) regs.r[15] += e; +} + +//$0a bpl e +void SuperFX::op_bpl() { + int e = (int8)pipe(); + if(regs.sfr.s == 0) regs.r[15] += e; +} + +//$0b bmi e +void SuperFX::op_bmi() { + int e = (int8)pipe(); + if(regs.sfr.s == 1) regs.r[15] += e; +} + +//$0c bcc e +void SuperFX::op_bcc() { + int e = (int8)pipe(); + if(regs.sfr.cy == 0) regs.r[15] += e; +} + +//$0d bcs e +void SuperFX::op_bcs() { + int e = (int8)pipe(); + if(regs.sfr.cy == 1) regs.r[15] += e; +} + +//$0e bvc e +void SuperFX::op_bvc() { + int e = (int8)pipe(); + if(regs.sfr.ov == 0) regs.r[15] += e; +} + +//$0f bvs e +void SuperFX::op_bvs() { + int e = (int8)pipe(); + if(regs.sfr.ov == 1) regs.r[15] += e; +} + +//$10-1f(b0): to rN +//$10-1f(b1): move rN +template void SuperFX::op_to_r() { + if(regs.sfr.b == 0) { + regs.dreg = n; + } else { + regs.r[n] = regs.sr(); + regs.reset(); + } +} + +//$20-2f: with rN +template void SuperFX::op_with_r() { + regs.sreg = n; + regs.dreg = n; + regs.sfr.b = 1; +} + +//$30-3b(alt0): stw (rN) +template void SuperFX::op_stw_ir() { + regs.ramaddr = regs.r[n]; + rambuffer_write(regs.ramaddr ^ 0, regs.sr() >> 0); + rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8); + regs.reset(); +} + +//$30-3b(alt1): stb (rN) +template void SuperFX::op_stb_ir() { + regs.ramaddr = regs.r[n]; + rambuffer_write(regs.ramaddr, regs.sr()); + regs.reset(); +} + +//$3c loop +void SuperFX::op_loop() { + regs.r[12]--; + regs.sfr.s = (regs.r[12] & 0x8000); + regs.sfr.z = (regs.r[12] == 0); + if(!regs.sfr.z) regs.r[15] = regs.r[13]; + regs.reset(); +} + +//$3d alt1 +void SuperFX::op_alt1() { + regs.sfr.b = 0; + regs.sfr.alt1 = 1; +} + +//$3e alt2 +void SuperFX::op_alt2() { + regs.sfr.b = 0; + regs.sfr.alt2 = 1; +} + +//$3f alt3 +void SuperFX::op_alt3() { + regs.sfr.b = 0; + regs.sfr.alt1 = 1; + regs.sfr.alt2 = 1; +} + +//$40-4b(alt0): ldw (rN) +template void SuperFX::op_ldw_ir() { + regs.ramaddr = regs.r[n]; + uint16_t data; + data = rambuffer_read(regs.ramaddr ^ 0) << 0; + data |= rambuffer_read(regs.ramaddr ^ 1) << 8; + regs.dr() = data; + regs.reset(); +} + +//$40-4b(alt1): ldb (rN) +template void SuperFX::op_ldb_ir() { + regs.ramaddr = regs.r[n]; + regs.dr() = rambuffer_read(regs.ramaddr); + regs.reset(); +} + +//$4c(alt0): plot +void SuperFX::op_plot() { + plot(regs.r[1], regs.r[2]); + regs.r[1]++; + regs.reset(); +} + +//$4c(alt1): rpix +void SuperFX::op_rpix() { + regs.dr() = rpix(regs.r[1], regs.r[2]); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$4d: swap +void SuperFX::op_swap() { + regs.dr() = (regs.sr() >> 8) | (regs.sr() << 8); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$4e(alt0): color +void SuperFX::op_color() { + regs.colr = color(regs.sr()); + regs.reset(); +} + +//$4e(alt1): cmode +void SuperFX::op_cmode() { + regs.por = regs.sr(); + regs.reset(); +} + +//$4f: not +void SuperFX::op_not() { + regs.dr() = ~regs.sr(); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$50-5f(alt0): add rN +template void SuperFX::op_add_r() { + int r = regs.sr() + regs.r[n]; + regs.sfr.ov = ~(regs.sr() ^ regs.r[n]) & (regs.r[n] ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0x10000); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$50-5f(alt1): adc rN +template void SuperFX::op_adc_r() { + int r = regs.sr() + regs.r[n] + regs.sfr.cy; + regs.sfr.ov = ~(regs.sr() ^ regs.r[n]) & (regs.r[n] ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0x10000); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$50-5f(alt2): add #N +template void SuperFX::op_add_i() { + int r = regs.sr() + n; + regs.sfr.ov = ~(regs.sr() ^ n) & (n ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0x10000); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$50-5f(alt3): adc #N +template void SuperFX::op_adc_i() { + int r = regs.sr() + n + regs.sfr.cy; + regs.sfr.ov = ~(regs.sr() ^ n) & (n ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0x10000); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$60-6f(alt0): sub rN +template void SuperFX::op_sub_r() { + int r = regs.sr() - regs.r[n]; + regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$60-6f(alt1): sbc rN +template void SuperFX::op_sbc_r() { + int r = regs.sr() - regs.r[n] - !regs.sfr.cy; + regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$60-6f(alt2): sub #N +template void SuperFX::op_sub_i() { + int r = regs.sr() - n; + regs.sfr.ov = (regs.sr() ^ n) & (regs.sr() ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$60-6f(alt3): cmp rN +template void SuperFX::op_cmp_r() { + int r = regs.sr() - regs.r[n]; + regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0); + regs.sfr.z = ((uint16_t)r == 0); + regs.reset(); +} + +//$70: merge +void SuperFX::op_merge() { + regs.dr() = (regs.r[7] & 0xff00) | (regs.r[8] >> 8); + regs.sfr.ov = (regs.dr() & 0xc0c0); + regs.sfr.s = (regs.dr() & 0x8080); + regs.sfr.cy = (regs.dr() & 0xe0e0); + regs.sfr.z = (regs.dr() & 0xf0f0); + regs.reset(); +} + +//$71-7f(alt0): and rN +template void SuperFX::op_and_r() { + regs.dr() = regs.sr() & regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$71-7f(alt1): bic rN +template void SuperFX::op_bic_r() { + regs.dr() = regs.sr() & ~regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$71-7f(alt2): and #N +template void SuperFX::op_and_i() { + regs.dr() = regs.sr() & n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$71-7f(alt3): bic #N +template void SuperFX::op_bic_i() { + regs.dr() = regs.sr() & ~n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$80-8f(alt0): mult rN +template void SuperFX::op_mult_r() { + regs.dr() = (int8)regs.sr() * (int8)regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + if(!regs.cfgr.ms0) add_clocks(2); +} + +//$80-8f(alt1): umult rN +template void SuperFX::op_umult_r() { + regs.dr() = (uint8)regs.sr() * (uint8)regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + if(!regs.cfgr.ms0) add_clocks(2); +} + +//$80-8f(alt2): mult #N +template void SuperFX::op_mult_i() { + regs.dr() = (int8)regs.sr() * (int8)n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + if(!regs.cfgr.ms0) add_clocks(2); +} + +//$80-8f(alt3): umult #N +template void SuperFX::op_umult_i() { + regs.dr() = (uint8)regs.sr() * (uint8)n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + if(!regs.cfgr.ms0) add_clocks(2); +} + +//$90: sbk +void SuperFX::op_sbk() { + rambuffer_write(regs.ramaddr ^ 0, regs.sr() >> 0); + rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8); + regs.reset(); +} + +//$91-94: link #N +template void SuperFX::op_link() { + regs.r[11] = regs.r[15] + n; + regs.reset(); +} + +//$95: sex +void SuperFX::op_sex() { + regs.dr() = (int8)regs.sr(); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$96(alt0): asr +void SuperFX::op_asr() { + regs.sfr.cy = (regs.sr() & 1); + regs.dr() = (int16_t)regs.sr() >> 1; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$96(alt1): div2 +void SuperFX::op_div2() { + regs.sfr.cy = (regs.sr() & 1); + regs.dr() = ((int16_t)regs.sr() >> 1) + ((regs.sr() + 1) >> 16); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$97: ror +void SuperFX::op_ror() { + bool carry = (regs.sr() & 1); + regs.dr() = (regs.sfr.cy << 15) | (regs.sr() >> 1); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.cy = carry; + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$98-9d(alt0): jmp rN +template void SuperFX::op_jmp_r() { + regs.r[15] = regs.r[n]; + regs.reset(); +} + +//$98-9d(alt1): ljmp rN +template void SuperFX::op_ljmp_r() { + regs.pbr = regs.r[n] & 0x7f; + regs.r[15] = regs.sr(); + regs.cbr = regs.r[15] & 0xfff0; + cache_flush(); + regs.reset(); +} + +//$9e: lob +void SuperFX::op_lob() { + regs.dr() = regs.sr() & 0xff; + regs.sfr.s = (regs.dr() & 0x80); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$9f(alt0): fmult +void SuperFX::op_fmult() { + uint32_t result = (int16_t)regs.sr() * (int16_t)regs.r[6]; + regs.dr() = result >> 16; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.cy = (result & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + add_clocks(4 + (regs.cfgr.ms0 << 2)); +} + +//$9f(alt1): lmult +void SuperFX::op_lmult() { + uint32_t result = (int16_t)regs.sr() * (int16_t)regs.r[6]; + regs.r[4] = result; + regs.dr() = result >> 16; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.cy = (result & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + add_clocks(4 + (regs.cfgr.ms0 << 2)); +} + +//$a0-af(alt0): ibt rN,#pp +template void SuperFX::op_ibt_r() { + regs.r[n] = (int8)pipe(); + regs.reset(); +} + +//$a0-af(alt1): lms rN,(yy) +template void SuperFX::op_lms_r() { + regs.ramaddr = pipe() << 1; + uint16_t data; + data = rambuffer_read(regs.ramaddr ^ 0) << 0; + data |= rambuffer_read(regs.ramaddr ^ 1) << 8; + regs.r[n] = data; + regs.reset(); +} + +//$a0-af(alt2): sms (yy),rN +template void SuperFX::op_sms_r() { + regs.ramaddr = pipe() << 1; + rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0); + rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8); + regs.reset(); +} + +//$b0-bf(b0): from rN +//$b0-bf(b1): moves rN +template void SuperFX::op_from_r() { + if(regs.sfr.b == 0) { + regs.sreg = n; + } else { + regs.dr() = regs.r[n]; + regs.sfr.ov = (regs.dr() & 0x80); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + } +} + +//$c0: hib +void SuperFX::op_hib() { + regs.dr() = regs.sr() >> 8; + regs.sfr.s = (regs.dr() & 0x80); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$c1-cf(alt0): or rN +template void SuperFX::op_or_r() { + regs.dr() = regs.sr() | regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$c1-cf(alt1): xor rN +template void SuperFX::op_xor_r() { + regs.dr() = regs.sr() ^ regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$c1-cf(alt2): or #N +template void SuperFX::op_or_i() { + regs.dr() = regs.sr() | n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$c1-cf(alt3): xor #N +template void SuperFX::op_xor_i() { + regs.dr() = regs.sr() ^ n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$d0-de: inc rN +template void SuperFX::op_inc_r() { + regs.r[n]++; + regs.sfr.s = (regs.r[n] & 0x8000); + regs.sfr.z = (regs.r[n] == 0); + regs.reset(); +} + +//$df(alt0): getc +void SuperFX::op_getc() { + regs.colr = color(rombuffer_read()); + regs.reset(); +} + +//$df(alt2): ramb +void SuperFX::op_ramb() { + rambuffer_sync(); + regs.rambr = regs.sr(); + regs.reset(); +} + +//$df(alt3): romb +void SuperFX::op_romb() { + rombuffer_sync(); + regs.rombr = regs.sr() & 0x7f; + regs.reset(); +} + +//$e0-ee: dec rN +template void SuperFX::op_dec_r() { + regs.r[n]--; + regs.sfr.s = (regs.r[n] & 0x8000); + regs.sfr.z = (regs.r[n] == 0); + regs.reset(); +} + +//$ef(alt0): getb +void SuperFX::op_getb() { + regs.dr() = rombuffer_read(); + regs.reset(); +} + +//$ef(alt1): getbh +void SuperFX::op_getbh() { + regs.dr() = (rombuffer_read() << 8) | (regs.sr() & 0x00ff); + regs.reset(); +} + +//$ef(alt2): getbl +void SuperFX::op_getbl() { + regs.dr() = (regs.sr() & 0xff00) | (rombuffer_read() << 0); + regs.reset(); +} + +//$ef(alt3): getbs +void SuperFX::op_getbs() { + regs.dr() = (int8)rombuffer_read(); + regs.reset(); +} + +//$f0-ff(alt0): iwt rN,#xx +template void SuperFX::op_iwt_r() { + uint16_t data; + data = pipe() << 0; + data |= pipe() << 8; + regs.r[n] = data; + regs.reset(); +} + +//$f0-ff(alt1): lm rN,(xx) +template void SuperFX::op_lm_r() { + regs.ramaddr = pipe() << 0; + regs.ramaddr |= pipe() << 8; + uint16_t data; + data = rambuffer_read(regs.ramaddr ^ 0) << 0; + data |= rambuffer_read(regs.ramaddr ^ 1) << 8; + regs.r[n] = data; + regs.reset(); +} + +//$f0-ff(alt2): sm (xx),rN +template void SuperFX::op_sm_r() { + regs.ramaddr = pipe() << 0; + regs.ramaddr |= pipe() << 8; + rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0); + rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8); + regs.reset(); +} + +#endif diff --git a/mednafen/snes/src/chip/superfx/core/registers.hpp b/mednafen/snes/src/chip/superfx/core/registers.hpp new file mode 100755 index 0000000..7346f63 --- /dev/null +++ b/mednafen/snes/src/chip/superfx/core/registers.hpp @@ -0,0 +1,175 @@ +//accepts a callback binding so r14 writes can trigger ROM buffering transparently +struct reg16_t : noncopyable { + uint16 data; + function on_modify; + + inline operator unsigned() const { return data; } + inline uint16 assign(uint16 i) { + if(on_modify) on_modify(i); + else data = i; + return data; + } + + inline unsigned operator++() { return assign(data + 1); } + inline unsigned operator--() { return assign(data - 1); } + inline unsigned operator++(int) { unsigned r = data; assign(data + 1); return r; } + inline unsigned operator--(int) { unsigned r = data; assign(data - 1); return r; } + inline unsigned operator = (unsigned i) { return assign(i); } + inline unsigned operator |= (unsigned i) { return assign(data | i); } + inline unsigned operator ^= (unsigned i) { return assign(data ^ i); } + inline unsigned operator &= (unsigned i) { return assign(data & i); } + inline unsigned operator <<= (unsigned i) { return assign(data << i); } + inline unsigned operator >>= (unsigned i) { return assign(data >> i); } + inline unsigned operator += (unsigned i) { return assign(data + i); } + inline unsigned operator -= (unsigned i) { return assign(data - i); } + inline unsigned operator *= (unsigned i) { return assign(data * i); } + inline unsigned operator /= (unsigned i) { return assign(data / i); } + inline unsigned operator %= (unsigned i) { return assign(data % i); } + + inline unsigned operator = (const reg16_t& i) { return assign(i); } + + reg16_t() : data(0) {} +}; + +struct sfr_t { + bool irq; //interrupt flag + bool b; //WITH flag + bool ih; //immediate higher 8-bit flag + bool il; //immediate lower 8-bit flag + bool alt2; //ALT2 mode + bool alt1; //ALT2 instruction mode + bool r; //ROM r14 read flag + bool g; //GO flag + bool ov; //overflow flag + bool s; //sign flag + bool cy; //carry flag + bool z; //zero flag + + operator unsigned() const { + return (irq << 15) | (b << 12) | (ih << 11) | (il << 10) | (alt2 << 9) | (alt1 << 8) + | (r << 6) | (g << 5) | (ov << 4) | (s << 3) | (cy << 2) | (z << 1); + } + + sfr_t& operator=(uint16_t data) { + irq = data & 0x8000; + b = data & 0x1000; + ih = data & 0x0800; + il = data & 0x0400; + alt2 = data & 0x0200; + alt1 = data & 0x0100; + r = data & 0x0040; + g = data & 0x0020; + ov = data & 0x0010; + s = data & 0x0008; + cy = data & 0x0004; + z = data & 0x0002; + return *this; + } +}; + +struct scmr_t { + unsigned ht; + bool ron; + bool ran; + unsigned md; + + operator unsigned() const { + return ((ht >> 1) << 5) | (ron << 4) | (ran << 3) | ((ht & 1) << 2) | (md); + } + + scmr_t& operator=(uint8 data) { + ht = (bool)(data & 0x20) << 1; + ht |= (bool)(data & 0x04) << 0; + ron = data & 0x10; + ran = data & 0x08; + md = data & 0x03; + return *this; + } +}; + +struct por_t { + bool obj; + bool freezehigh; + bool highnibble; + bool dither; + bool transparent; + + operator unsigned() const { + return (obj << 4) | (freezehigh << 3) | (highnibble << 2) | (dither << 1) | (transparent); + } + + por_t& operator=(uint8 data) { + obj = data & 0x10; + freezehigh = data & 0x08; + highnibble = data & 0x04; + dither = data & 0x02; + transparent = data & 0x01; + return *this; + } +}; + +struct cfgr_t { + bool irq; + bool ms0; + + operator unsigned() const { + return (irq << 7) | (ms0 << 5); + } + + cfgr_t& operator=(uint8 data) { + irq = data & 0x80; + ms0 = data & 0x20; + return *this; + } +}; + +struct regs_t { + uint8 pipeline; + uint16 ramaddr; + + reg16_t r[16]; //general purpose registers + sfr_t sfr; //status flag register + uint8 pbr; //program bank register + uint8 rombr; //game pack ROM bank register + bool rambr; //game pack RAM bank register + uint16 cbr; //cache base register + uint8 scbr; //screen base register + scmr_t scmr; //screen mode register + uint8 colr; //color register + por_t por; //plot option register + bool bramr; //back-up RAM register + uint8 vcr; //version code register + cfgr_t cfgr; //config register + bool clsr; //clock select register + + unsigned romcl; //clock ticks until romdr is valid + uint8 romdr; //ROM buffer data register + + unsigned ramcl; //clock ticks until ramdr is valid + uint16 ramar; //RAM buffer address register + uint8 ramdr; //RAM buffer data register + + unsigned sreg, dreg; + reg16_t& sr() { return r[sreg]; } //source register (from) + reg16_t& dr() { return r[dreg]; } //destination register (to) + + void reset() { + sfr.b = 0; + sfr.alt1 = 0; + sfr.alt2 = 0; + + sreg = 0; + dreg = 0; + } +} regs; + +struct cache_t { + uint8 buffer[512]; + bool valid[32]; +} cache; + +struct pixelcache_t { + uint16 offset; + uint8 bitpend; + uint8 data[8]; +} pixelcache[2]; diff --git a/mednafen/snes/src/chip/superfx/disasm/disasm.cpp b/mednafen/snes/src/chip/superfx/disasm/disasm.cpp new file mode 100755 index 0000000..c2d3e38 --- /dev/null +++ b/mednafen/snes/src/chip/superfx/disasm/disasm.cpp @@ -0,0 +1,279 @@ +#ifdef SUPERFX_CPP + +void SuperFX::disassemble_opcode(char *output) { + *output = 0; + + if(!regs.sfr.alt2) { + if(!regs.sfr.alt1) { + disassemble_alt0(output); + } else { + disassemble_alt1(output); + } + } else { + if(!regs.sfr.alt1) { + disassemble_alt2(output); + } else { + disassemble_alt3(output); + } + } + + unsigned length = strlen(output); + while(length++ < 20) strcat(output, " "); +} + +#define case4(id) \ + case id+ 0: case id+ 1: case id+ 2: case id+ 3 +#define case6(id) \ + case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5 +#define case12(id) \ + case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \ + case id+ 8: case id+ 9: case id+10: case id+11 +#define case15(id) \ + case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \ + case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14 +#define case16(id) \ + case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \ + case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14: case id+15 + +#define op0 regs.pipeline +#define op1 superfxbus.read((regs.pbr << 16) + regs.r[15] + 0) +#define op2 superfxbus.read((regs.pbr << 16) + regs.r[15] + 1) + +void SuperFX::disassemble_alt0(char *output) { + char t[256] = ""; + switch(op0) { + case (0x00): sprintf(t, "stop"); break; + case (0x01): sprintf(t, "nop"); break; + case (0x02): sprintf(t, "cache"); break; + case (0x03): sprintf(t, "lsr"); break; + case (0x04): sprintf(t, "rol"); break; + case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break; + case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break; + case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break; + case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break; + case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break; + case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break; + case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break; + case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break; + case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break; + case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break; + case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break; + case16(0x10): sprintf(t, "to r%u", op0 & 15); break; + case16(0x20): sprintf(t, "with r%u", op0 & 15); break; + case12(0x30): sprintf(t, "stw (r%u)", op0 & 15); break; + case (0x3c): sprintf(t, "loop"); break; + case (0x3d): sprintf(t, "alt1"); break; + case (0x3e): sprintf(t, "alt2"); break; + case (0x3f): sprintf(t, "alt3"); break; + case12(0x40): sprintf(t, "ldw (r%u)", op0 & 15); break; + case (0x4c): sprintf(t, "plot"); break; + case (0x4d): sprintf(t, "swap"); break; + case (0x4e): sprintf(t, "color"); break; + case (0x4f): sprintf(t, "not"); break; + case16(0x50): sprintf(t, "add r%u", op0 & 15); break; + case16(0x60): sprintf(t, "sub r%u", op0 & 15); break; + case (0x70): sprintf(t, "merge"); break; + case15(0x71): sprintf(t, "and r%u", op0 & 15); break; + case16(0x80): sprintf(t, "mult r%u", op0 & 15); break; + case (0x90): sprintf(t, "sbk"); break; + case4 (0x91): sprintf(t, "link #%u", op0 & 15); break; + case (0x95): sprintf(t, "sex"); break; + case (0x96): sprintf(t, "asr"); break; + case (0x97): sprintf(t, "ror"); break; + case6 (0x98): sprintf(t, "jmp r%u", op0 & 15); break; + case (0x9e): sprintf(t, "lob"); break; + case (0x9f): sprintf(t, "fmult"); break; + case16(0xa0): sprintf(t, "ibt r%u,#$%.2x", op0 & 15, op1); break; + case16(0xb0): sprintf(t, "from r%u", op0 & 15); break; + case (0xc0): sprintf(t, "hib"); + case15(0xc1): sprintf(t, "or r%u", op0 & 15); break; + case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break; + case (0xdf): sprintf(t, "getc"); break; + case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break; + case (0xef): sprintf(t, "getb"); break; + case16(0xf0): sprintf(t, "iwt r%u,#$%.2x%.2x", op0 & 15, op2, op1); break; + } + strcat(output, t); +} + +void SuperFX::disassemble_alt1(char *output) { + char t[256] = ""; + switch(op0) { + case (0x00): sprintf(t, "stop"); break; + case (0x01): sprintf(t, "nop"); break; + case (0x02): sprintf(t, "cache"); break; + case (0x03): sprintf(t, "lsr"); break; + case (0x04): sprintf(t, "rol"); break; + case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break; + case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break; + case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break; + case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break; + case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break; + case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break; + case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break; + case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break; + case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break; + case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break; + case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break; + case16(0x10): sprintf(t, "to r%u", op0 & 15); break; + case16(0x20): sprintf(t, "with r%u", op0 & 15); break; + case12(0x30): sprintf(t, "stb (r%u)", op0 & 15); break; + case (0x3c): sprintf(t, "loop"); break; + case (0x3d): sprintf(t, "alt1"); break; + case (0x3e): sprintf(t, "alt2"); break; + case (0x3f): sprintf(t, "alt3"); break; + case12(0x40): sprintf(t, "ldb (r%u)", op0 & 15); break; + case (0x4c): sprintf(t, "rpix"); break; + case (0x4d): sprintf(t, "swap"); break; + case (0x4e): sprintf(t, "cmode"); break; + case (0x4f): sprintf(t, "not"); break; + case16(0x50): sprintf(t, "adc r%u", op0 & 15); break; + case16(0x60): sprintf(t, "sbc r%u", op0 & 15); break; + case (0x70): sprintf(t, "merge"); break; + case15(0x71): sprintf(t, "bic r%u", op0 & 15); break; + case16(0x80): sprintf(t, "umult r%u", op0 & 15); break; + case (0x90): sprintf(t, "sbk"); break; + case4 (0x91): sprintf(t, "link #%u", op0 & 15); break; + case (0x95): sprintf(t, "sex"); break; + case (0x96): sprintf(t, "div2"); break; + case (0x97): sprintf(t, "ror"); break; + case6 (0x98): sprintf(t, "ljmp r%u", op0 & 15); break; + case (0x9e): sprintf(t, "lob"); break; + case (0x9f): sprintf(t, "lmult"); break; + case16(0xa0): sprintf(t, "lms r%u,(#$%.4x)", op0 & 15, op1 << 1); break; + case16(0xb0): sprintf(t, "from r%u", op0 & 15); break; + case (0xc0): sprintf(t, "hib"); break; + case15(0xc1): sprintf(t, "xor r%u", op0 & 15); break; + case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break; + case (0xdf): sprintf(t, "getc"); break; + case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break; + case (0xef): sprintf(t, "getbh"); break; + case16(0xf0): sprintf(t, "lm r%u", op0 & 15); break; + } + strcat(output, t); +} + +void SuperFX::disassemble_alt2(char *output) { + char t[256] = ""; + switch(op0) { + case (0x00): sprintf(t, "stop"); break; + case (0x01): sprintf(t, "nop"); break; + case (0x02): sprintf(t, "cache"); break; + case (0x03): sprintf(t, "lsr"); break; + case (0x04): sprintf(t, "rol"); break; + case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break; + case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break; + case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break; + case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break; + case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break; + case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break; + case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break; + case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break; + case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break; + case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break; + case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break; + case16(0x10): sprintf(t, "to r%u", op0 & 15); break; + case16(0x20): sprintf(t, "with r%u", op0 & 15); break; + case12(0x30): sprintf(t, "stw (r%u)", op0 & 15); break; + case (0x3c): sprintf(t, "loop"); break; + case (0x3d): sprintf(t, "alt1"); break; + case (0x3e): sprintf(t, "alt2"); break; + case (0x3f): sprintf(t, "alt3"); break; + case12(0x40): sprintf(t, "ldw (r%u)", op0 & 15); break; + case (0x4c): sprintf(t, "plot"); break; + case (0x4d): sprintf(t, "swap"); break; + case (0x4e): sprintf(t, "color"); break; + case (0x4f): sprintf(t, "not"); break; + case16(0x50): sprintf(t, "add #%u", op0 & 15); break; + case16(0x60): sprintf(t, "sub #%u", op0 & 15); break; + case (0x70): sprintf(t, "merge"); break; + case15(0x71): sprintf(t, "and #%u", op0 & 15); break; + case16(0x80): sprintf(t, "mult #%u", op0 & 15); break; + case (0x90): sprintf(t, "sbk"); break; + case4 (0x91): sprintf(t, "link #%u", op0 & 15); break; + case (0x95): sprintf(t, "sex"); break; + case (0x96): sprintf(t, "asr"); break; + case (0x97): sprintf(t, "ror"); break; + case6 (0x98): sprintf(t, "jmp r%u", op0 & 15); break; + case (0x9e): sprintf(t, "lob"); break; + case (0x9f): sprintf(t, "fmult"); break; + case16(0xa0): sprintf(t, "sms r%u,(#$%.4x)", op0 & 15, op1 << 1); break; + case16(0xb0): sprintf(t, "from r%u", op0 & 15); break; + case (0xc0): sprintf(t, "hib"); break; + case15(0xc1): sprintf(t, "or #%u", op0 & 15); break; + case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break; + case (0xdf): sprintf(t, "ramb"); break; + case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break; + case (0xef): sprintf(t, "getbl"); break; + case16(0xf0): sprintf(t, "sm r%u", op0 & 15); break; + } + strcat(output, t); +} + +void SuperFX::disassemble_alt3(char *output) { + char t[256] = ""; + switch(op0) { + case (0x00): sprintf(t, "stop"); break; + case (0x01): sprintf(t, "nop"); break; + case (0x02): sprintf(t, "cache"); break; + case (0x03): sprintf(t, "lsr"); break; + case (0x04): sprintf(t, "rol"); break; + case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break; + case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break; + case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break; + case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break; + case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break; + case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break; + case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break; + case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break; + case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break; + case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break; + case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break; + case16(0x10): sprintf(t, "to r%u", op0 & 15); break; + case16(0x20): sprintf(t, "with r%u", op0 & 15); break; + case12(0x30): sprintf(t, "stb (r%u)", op0 & 15); break; + case (0x3c): sprintf(t, "loop"); break; + case (0x3d): sprintf(t, "alt1"); break; + case (0x3e): sprintf(t, "alt2"); break; + case (0x3f): sprintf(t, "alt3"); break; + case12(0x40): sprintf(t, "ldb (r%u)", op0 & 15); break; + case (0x4c): sprintf(t, "rpix"); break; + case (0x4d): sprintf(t, "swap"); break; + case (0x4e): sprintf(t, "cmode"); break; + case (0x4f): sprintf(t, "not"); break; + case16(0x50): sprintf(t, "adc #%u", op0 & 15); break; + case16(0x60): sprintf(t, "cmp r%u", op0 & 15); break; + case (0x70): sprintf(t, "merge"); break; + case15(0x71): sprintf(t, "bic #%u", op0 & 15); break; + case16(0x80): sprintf(t, "umult #%u", op0 & 15); break; + case (0x90): sprintf(t, "sbk"); break; + case4 (0x91): sprintf(t, "link #%u", op0 & 15); break; + case (0x95): sprintf(t, "sex"); break; + case (0x96): sprintf(t, "div2"); break; + case (0x97): sprintf(t, "ror"); break; + case6 (0x98): sprintf(t, "ljmp r%u", op0 & 15); break; + case (0x9e): sprintf(t, "lob"); break; + case (0x9f): sprintf(t, "lmult"); break; + case16(0xa0): sprintf(t, "lms r%u", op0 & 15); break; + case16(0xb0): sprintf(t, "from r%u", op0 & 15); break; + case (0xc0): sprintf(t, "hib"); break; + case15(0xc1): sprintf(t, "xor #%u", op0 & 15); break; + case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break; + case (0xdf): sprintf(t, "romb"); break; + case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break; + case (0xef): sprintf(t, "getbs"); break; + case16(0xf0): sprintf(t, "lm r%u", op0 & 15); break; + } + strcat(output, t); +} + +#undef case4 +#undef case6 +#undef case12 +#undef case15 +#undef case16 +#undef op0 +#undef op1 +#undef op2 + +#endif diff --git a/mednafen/snes/src/chip/superfx/disasm/disasm.hpp b/mednafen/snes/src/chip/superfx/disasm/disasm.hpp new file mode 100755 index 0000000..903615e --- /dev/null +++ b/mednafen/snes/src/chip/superfx/disasm/disasm.hpp @@ -0,0 +1,5 @@ +void disassemble_opcode(char *output); +void disassemble_alt0(char *output); +void disassemble_alt1(char *output); +void disassemble_alt2(char *output); +void disassemble_alt3(char *output); diff --git a/mednafen/snes/src/chip/superfx/memory/memory.cpp b/mednafen/snes/src/chip/superfx/memory/memory.cpp new file mode 100755 index 0000000..9ae6091 --- /dev/null +++ b/mednafen/snes/src/chip/superfx/memory/memory.cpp @@ -0,0 +1,71 @@ +#ifdef SUPERFX_CPP + +uint8 SuperFX::op_read(uint16 addr) { + uint16 offset = addr - regs.cbr; + if(offset < 512) { + if(cache.valid[offset >> 4] == false) { + unsigned dp = offset & 0xfff0; + unsigned sp = (regs.pbr << 16) + ((regs.cbr + dp) & 0xfff0); + for(unsigned n = 0; n < 16; n++) { + add_clocks(memory_access_speed); + cache.buffer[dp++] = superfxbus.read(sp++); + } + cache.valid[offset >> 4] = true; + } else { + add_clocks(cache_access_speed); + } + return cache.buffer[offset]; + } + + if(regs.pbr <= 0x5f) { + //$[00-5f]:[0000-ffff] ROM + rombuffer_sync(); + add_clocks(memory_access_speed); + return superfxbus.read((regs.pbr << 16) + addr); + } else { + //$[60-7f]:[0000-ffff] RAM + rambuffer_sync(); + add_clocks(memory_access_speed); + return superfxbus.read((regs.pbr << 16) + addr); + } +} + +uint8 SuperFX::peekpipe() { + uint8 result = regs.pipeline; + regs.pipeline = op_read(regs.r[15]); + r15_modified = false; + return result; +} + +uint8 SuperFX::pipe() { + uint8 result = regs.pipeline; + regs.pipeline = op_read(++regs.r[15]); + r15_modified = false; + return result; +} + +void SuperFX::cache_flush() { + for(unsigned n = 0; n < 32; n++) cache.valid[n] = false; +} + +uint8 SuperFX::cache_mmio_read(uint16 addr) { + addr = (addr + regs.cbr) & 511; + return cache.buffer[addr]; +} + +void SuperFX::cache_mmio_write(uint16 addr, uint8 data) { + addr = (addr + regs.cbr) & 511; + cache.buffer[addr] = data; + if((addr & 15) == 15) cache.valid[addr >> 4] = true; +} + +void SuperFX::memory_reset() { + for(unsigned n = 0; n < 512; n++) cache.buffer[n] = 0x00; + for(unsigned n = 0; n < 32; n++) cache.valid[n] = false; + for(unsigned n = 0; n < 2; n++) { + pixelcache[n].offset = ~0; + pixelcache[n].bitpend = 0x00; + } +} + +#endif diff --git a/mednafen/snes/src/chip/superfx/memory/memory.hpp b/mednafen/snes/src/chip/superfx/memory/memory.hpp new file mode 100755 index 0000000..3b11a3f --- /dev/null +++ b/mednafen/snes/src/chip/superfx/memory/memory.hpp @@ -0,0 +1,9 @@ +uint8 op_read(uint16 addr); +alwaysinline uint8 peekpipe(); +alwaysinline uint8 pipe(); + +void cache_flush(); +uint8 cache_mmio_read(uint16 addr); +void cache_mmio_write(uint16 addr, uint8 data); + +void memory_reset(); diff --git a/mednafen/snes/src/chip/superfx/mmio/mmio.cpp b/mednafen/snes/src/chip/superfx/mmio/mmio.cpp new file mode 100755 index 0000000..3276a40 --- /dev/null +++ b/mednafen/snes/src/chip/superfx/mmio/mmio.cpp @@ -0,0 +1,118 @@ +#ifdef SUPERFX_CPP + +uint8 SuperFX::mmio_read(unsigned addr) { + scheduler.sync_cpucop(); + addr &= 0xffff; + + if(addr >= 0x3100 && addr <= 0x32ff) { + return cache_mmio_read(addr - 0x3100); + } + + if(addr >= 0x3000 && addr <= 0x301f) { + return regs.r[(addr >> 1) & 15] >> ((addr & 1) << 3); + } + + switch(addr) { + case 0x3030: { + return regs.sfr >> 0; + } + + case 0x3031: { + uint8 r = regs.sfr >> 8; + regs.sfr.irq = 0; + cpu.regs.irq = 0; + return r; + } + + case 0x3034: { + return regs.pbr; + } + + case 0x3036: { + return regs.rombr; + } + + case 0x303b: { + return regs.vcr; + } + + case 0x303c: { + return regs.rambr; + } + + case 0x303e: { + return regs.cbr >> 0; + } + + case 0x303f: { + return regs.cbr >> 8; + } + } + + return 0x00; +} + +void SuperFX::mmio_write(unsigned addr, uint8 data) { + scheduler.sync_cpucop(); + addr &= 0xffff; + + if(addr >= 0x3100 && addr <= 0x32ff) { + return cache_mmio_write(addr - 0x3100, data); + } + + if(addr >= 0x3000 && addr <= 0x301f) { + unsigned n = (addr >> 1) & 15; + if((addr & 1) == 0) { + regs.r[n] = (regs.r[n] & 0xff00) | data; + } else { + regs.r[n] = (data << 8) | (regs.r[n] & 0xff); + } + + if(addr == 0x301f) regs.sfr.g = 1; + return; + } + + switch(addr) { + case 0x3030: { + bool g = regs.sfr.g; + regs.sfr = (regs.sfr & 0xff00) | (data << 0); + if(g == 1 && regs.sfr.g == 0) { + regs.cbr = 0x0000; + cache_flush(); + } + } break; + + case 0x3031: { + regs.sfr = (data << 8) | (regs.sfr & 0x00ff); + } break; + + case 0x3033: { + regs.bramr = data; + } break; + + case 0x3034: { + regs.pbr = data & 0x7f; + cache_flush(); + } break; + + case 0x3037: { + regs.cfgr = data; + update_speed(); + } break; + + case 0x3038: { + regs.scbr = data; + } break; + + case 0x3039: { + regs.clsr = data; + update_speed(); + } break; + + case 0x303a: { + regs.scmr = data; + } break; + } +} + +#endif diff --git a/mednafen/snes/src/chip/superfx/mmio/mmio.hpp b/mednafen/snes/src/chip/superfx/mmio/mmio.hpp new file mode 100755 index 0000000..08cc85a --- /dev/null +++ b/mednafen/snes/src/chip/superfx/mmio/mmio.hpp @@ -0,0 +1,2 @@ +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); diff --git a/mednafen/snes/src/chip/superfx/serialization.cpp b/mednafen/snes/src/chip/superfx/serialization.cpp new file mode 100755 index 0000000..e035567 --- /dev/null +++ b/mednafen/snes/src/chip/superfx/serialization.cpp @@ -0,0 +1,94 @@ +#ifdef SUPERFX_CPP + +void SuperFX::serialize(serializer &s) { + //superfx.hpp + s.integer(clockmode); + s.integer(instruction_counter); + + //core/registers.hpp + s.integer(regs.pipeline); + s.integer(regs.ramaddr); + + s.integer(regs.r[ 0].data); + s.integer(regs.r[ 1].data); + s.integer(regs.r[ 2].data); + s.integer(regs.r[ 3].data); + s.integer(regs.r[ 4].data); + s.integer(regs.r[ 5].data); + s.integer(regs.r[ 6].data); + s.integer(regs.r[ 7].data); + s.integer(regs.r[ 8].data); + s.integer(regs.r[ 9].data); + s.integer(regs.r[10].data); + s.integer(regs.r[11].data); + s.integer(regs.r[12].data); + s.integer(regs.r[13].data); + s.integer(regs.r[14].data); + s.integer(regs.r[15].data); + + s.integer(regs.sfr.irq); + s.integer(regs.sfr.b); + s.integer(regs.sfr.ih); + s.integer(regs.sfr.il); + s.integer(regs.sfr.alt2); + s.integer(regs.sfr.alt1); + s.integer(regs.sfr.r); + s.integer(regs.sfr.g); + s.integer(regs.sfr.ov); + s.integer(regs.sfr.s); + s.integer(regs.sfr.cy); + s.integer(regs.sfr.z); + + s.integer(regs.pbr); + s.integer(regs.rombr); + s.integer(regs.rambr); + s.integer(regs.cbr); + s.integer(regs.scbr); + + s.integer(regs.scmr.ht); + s.integer(regs.scmr.ron); + s.integer(regs.scmr.ran); + s.integer(regs.scmr.md); + + s.integer(regs.colr); + + s.integer(regs.por.obj); + s.integer(regs.por.freezehigh); + s.integer(regs.por.highnibble); + s.integer(regs.por.dither); + s.integer(regs.por.transparent); + + s.integer(regs.bramr); + s.integer(regs.vcr); + + s.integer(regs.cfgr.irq); + s.integer(regs.cfgr.ms0); + + s.integer(regs.clsr); + + s.integer(regs.romcl); + s.integer(regs.romdr); + + s.integer(regs.ramcl); + s.integer(regs.ramar); + s.integer(regs.ramdr); + + s.integer(regs.sreg); + s.integer(regs.dreg); + + s.array(cache.buffer); + s.array(cache.valid); + + for(unsigned i = 0; i < 2; i++) { + s.integer(pixelcache[i].offset); + s.integer(pixelcache[i].bitpend); + s.array(pixelcache[i].data); + } + + //timing/timing.hpp + s.integer(cache_access_speed); + s.integer(memory_access_speed); + s.integer(r15_modified); +} + +#endif diff --git a/mednafen/snes/src/chip/superfx/superfx.cpp b/mednafen/snes/src/chip/superfx/superfx.cpp new file mode 100755 index 0000000..39e538d --- /dev/null +++ b/mednafen/snes/src/chip/superfx/superfx.cpp @@ -0,0 +1,79 @@ +#include <../base.hpp> + +#define SUPERFX_CPP +namespace SNES { + +#include "serialization.cpp" +#include "bus/bus.cpp" +#include "core/core.cpp" +#include "memory/memory.cpp" +#include "mmio/mmio.cpp" +#include "timing/timing.cpp" +#include "disasm/disasm.cpp" + +SuperFX superfx; + +void SuperFX::enter() { + while(true) { + while(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + + if(regs.sfr.g == 0) { + add_clocks(6); + scheduler.sync_copcpu(); + continue; + } + + (this->*opcode_table[(regs.sfr & 0x0300) + peekpipe()])(); + if(r15_modified == false) regs.r[15]++; + + if(++instruction_counter >= 128) { + instruction_counter = 0; + scheduler.sync_copcpu(); + } + } +} + +void SuperFX::init() { + initialize_opcode_table(); + regs.r[14].on_modify = bind(&SuperFX::r14_modify, this); + regs.r[15].on_modify = bind(&SuperFX::r15_modify, this); +} + +void SuperFX::enable() { + for(unsigned i = 0x3000; i <= 0x32ff; i++) memory::mmio.map(i, *this); +} + +void SuperFX::power() { + clockmode = config.superfx.speed; + reset(); +} + +void SuperFX::reset() { + superfxbus.init(); + instruction_counter = 0; + + for(unsigned n = 0; n < 16; n++) regs.r[n] = 0x0000; + regs.sfr = 0x0000; + regs.pbr = 0x00; + regs.rombr = 0x00; + regs.rambr = 0; + regs.cbr = 0x0000; + regs.scbr = 0x00; + regs.scmr = 0x00; + regs.colr = 0x00; + regs.por = 0x00; + regs.bramr = 0; + regs.vcr = 0x04; + regs.cfgr = 0x00; + regs.clsr = 0; + regs.pipeline = 0x01; //nop + regs.ramaddr = 0x0000; + regs.reset(); + + memory_reset(); + timing_reset(); +} + +} diff --git a/mednafen/snes/src/chip/superfx/superfx.hpp b/mednafen/snes/src/chip/superfx/superfx.hpp new file mode 100755 index 0000000..e1561ea --- /dev/null +++ b/mednafen/snes/src/chip/superfx/superfx.hpp @@ -0,0 +1,26 @@ +#include "bus/bus.hpp" + +class SuperFX : public MMIO { +public: + #include "core/core.hpp" + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + #include "timing/timing.hpp" + #include "disasm/disasm.hpp" + + void enter(); + + void init(); + void enable(); + void power(); + void reset(); + + void serialize(serializer&); + +private: + unsigned clockmode; + unsigned instruction_counter; +}; + +extern SuperFX superfx; +extern SuperFXBus superfxbus; diff --git a/mednafen/snes/src/chip/superfx/timing/timing.cpp b/mednafen/snes/src/chip/superfx/timing/timing.cpp new file mode 100755 index 0000000..b177166 --- /dev/null +++ b/mednafen/snes/src/chip/superfx/timing/timing.cpp @@ -0,0 +1,97 @@ +#ifdef SUPERFX_CPP + +void SuperFX::add_clocks(unsigned clocks) { + if(regs.romcl) { + regs.romcl -= min(clocks, regs.romcl); + if(regs.romcl == 0) { + regs.sfr.r = 0; + regs.romdr = superfxbus.read((regs.rombr << 16) + regs.r[14]); + } + } + + if(regs.ramcl) { + regs.ramcl -= min(clocks, regs.ramcl); + if(regs.ramcl == 0) { + superfxbus.write(0x700000 + (regs.rambr << 16) + regs.ramar, regs.ramdr); + } + } + + scheduler.addclocks_cop(clocks); + scheduler.sync_copcpu(); +} + +void SuperFX::rombuffer_sync() { + if(regs.romcl) add_clocks(regs.romcl); +} + +void SuperFX::rombuffer_update() { + regs.sfr.r = 1; + regs.romcl = memory_access_speed; +} + +uint8 SuperFX::rombuffer_read() { + rombuffer_sync(); + return regs.romdr; +} + +void SuperFX::rambuffer_sync() { + if(regs.ramcl) add_clocks(regs.ramcl); +} + +uint8 SuperFX::rambuffer_read(uint16 addr) { + rambuffer_sync(); + return superfxbus.read(0x700000 + (regs.rambr << 16) + addr); +} + +void SuperFX::rambuffer_write(uint16 addr, uint8 data) { + rambuffer_sync(); + regs.ramcl = memory_access_speed; + regs.ramar = addr; + regs.ramdr = data; +} + +void SuperFX::r14_modify(uint16 data) { + regs.r[14].data = data; + rombuffer_update(); +} + +void SuperFX::r15_modify(uint16 data) { + regs.r[15].data = data; + r15_modified = true; +} + +void SuperFX::update_speed() { + //force SuperFX1 mode? + if(clockmode == 1) { + cache_access_speed = 2; + memory_access_speed = 6; + return; + } + + //force SuperFX2 mode? + if(clockmode == 2) { + cache_access_speed = 1; + memory_access_speed = 5; + regs.cfgr.ms0 = 0; //cannot use high-speed multiplication in 21MHz mode + return; + } + + //default: allow S-CPU to select mode + cache_access_speed = (regs.clsr ? 1 : 2); + memory_access_speed = (regs.clsr ? 5 : 6); + if(regs.clsr) regs.cfgr.ms0 = 0; //cannot use high-speed multiplication in 21MHz mode +} + +void SuperFX::timing_reset() { + update_speed(); + r15_modified = false; + + regs.romcl = 0; + regs.romdr = 0; + + regs.ramcl = 0; + regs.ramar = 0; + regs.ramdr = 0; +} + +#endif diff --git a/mednafen/snes/src/chip/superfx/timing/timing.hpp b/mednafen/snes/src/chip/superfx/timing/timing.hpp new file mode 100755 index 0000000..9ae7e8d --- /dev/null +++ b/mednafen/snes/src/chip/superfx/timing/timing.hpp @@ -0,0 +1,19 @@ +unsigned cache_access_speed; +unsigned memory_access_speed; +bool r15_modified; + +void add_clocks(unsigned clocks); + +void rombuffer_sync(); +void rombuffer_update(); +uint8 rombuffer_read(); + +void rambuffer_sync(); +uint8 rambuffer_read(uint16 addr); +void rambuffer_write(uint16 addr, uint8 data); + +void r14_modify(uint16); +void r15_modify(uint16); + +void update_speed(); +void timing_reset(); diff --git a/mednafen/snes/src/chip/supergameboy/supergameboy.cpp b/mednafen/snes/src/chip/supergameboy/supergameboy.cpp new file mode 100755 index 0000000..912c09f --- /dev/null +++ b/mednafen/snes/src/chip/supergameboy/supergameboy.cpp @@ -0,0 +1,140 @@ +#include <../base.hpp> + +#define SUPERGAMEBOY_CPP +namespace SNES { + +SuperGameBoy supergameboy; + +void SuperGameBoy::enter() { + scheduler.clock.cop_freq = (version == SuperGameBoy1 ? 2147727 : 2097152); + + if(!sgb_run) while(true) { + while(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + + audio.coprocessor_sample(0, 0); + scheduler.addclocks_cop(1); + scheduler.sync_copcpu(); + } + + while(true) { + while(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + + unsigned samples = sgb_run(samplebuffer, 16); + for(unsigned i = 0; i < samples; i++) { + int16 left = samplebuffer[i] >> 0; + int16 right = samplebuffer[i] >> 16; + + //SNES audio is notoriously quiet; lower Game Boy samples to match SGB sound effects + audio.coprocessor_sample(left / 3, right / 3); + } + + scheduler.addclocks_cop(samples); + scheduler.sync_copcpu(); + } +} + +uint8 SuperGameBoy::mmio_read(unsigned addr) { + addr &= 0xffff; + + if(addr == 0x2181) return mmio[0]->mmio_read(addr); + if(addr == 0x2182) return mmio[1]->mmio_read(addr); + if(addr == 0x420b) return mmio[2]->mmio_read(addr); + + return 0x00; +} + +void SuperGameBoy::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + if(addr == 0x2181) { + row = (row & 0xff00) | (data << 0); + mmio[0]->mmio_write(addr, data); + } + + if(addr == 0x2182) { + row = (row & 0x00ff) | (data << 8); + mmio[1]->mmio_write(addr, data); + } + + if(addr == 0x420b) { + if(data == 0x10 && sgb_row) { + if(row >= 0x5000 && row <= 0x6540) sgb_row((row - 0x5000) / 320); + if(row >= 0x6800 && row <= 0x7d40) sgb_row((row - 0x6800) / 320); + } + mmio[2]->mmio_write(addr, data); + } +} + +uint8 SuperGameBoy::read(unsigned addr) { + if(sgb_read) return sgb_read(addr); + return 0x00; +} + +void SuperGameBoy::write(unsigned addr, uint8 data) { + if(sgb_write) sgb_write(addr, data); +} + +void SuperGameBoy::init() { + if(open("supergameboy")) { + sgb_rom = sym("sgb_rom"); + sgb_ram = sym("sgb_ram"); + sgb_rtc = sym("sgb_rtc"); + sgb_init = sym("sgb_init"); + sgb_term = sym("sgb_term"); + sgb_power = sym("sgb_power"); + sgb_reset = sym("sgb_reset"); + sgb_row = sym("sgb_row"); + sgb_read = sym("sgb_read"); + sgb_write = sym("sgb_write"); + sgb_run = sym("sgb_run"); + sgb_save = sym("sgb_save"); + sgb_serialize = sym("sgb_serialize"); + } +} + +void SuperGameBoy::enable() { + mmio[0] = memory::mmio.mmio[0x2181 - 0x2000]; + mmio[1] = memory::mmio.mmio[0x2182 - 0x2000]; + mmio[2] = memory::mmio.mmio[0x420b - 0x2000]; + + memory::mmio.map(0x2181, *this); + memory::mmio.map(0x2182, *this); + memory::mmio.map(0x420b, *this); +} + +void SuperGameBoy::power() { + version = (cartridge.type() == Cartridge::TypeSuperGameBoy1Bios ? SuperGameBoy1 : SuperGameBoy2); + + audio.coprocessor_enable(true); + audio.coprocessor_frequency(version == SuperGameBoy1 ? 2147727.0 : 2097152.0); + + bus.map(Bus::MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, *this); + bus.map(Bus::MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, *this); + + sgb_rom(memory::gbrom.data(), memory::gbrom.size() == -1U ? 0 : memory::gbrom.size()); + sgb_ram(memory::gbram.data(), memory::gbram.size() == -1U ? 0 : memory::gbram.size()); + sgb_rtc(memory::gbrtc.data(), memory::gbrtc.size() == -1U ? 0 : memory::gbrtc.size()); + + if(sgb_init) sgb_init(version); + if(sgb_power) sgb_power(); +} + +void SuperGameBoy::reset() { + if(sgb_reset) sgb_reset(); +} + +void SuperGameBoy::unload() { + if(sgb_term) sgb_term(); +} + +void SuperGameBoy::serialize(serializer &s) { + s.integer(row); + s.integer(version); + if(sgb_serialize) sgb_serialize(s); +} + +} diff --git a/mednafen/snes/src/chip/supergameboy/supergameboy.hpp b/mednafen/snes/src/chip/supergameboy/supergameboy.hpp new file mode 100755 index 0000000..f132b95 --- /dev/null +++ b/mednafen/snes/src/chip/supergameboy/supergameboy.hpp @@ -0,0 +1,41 @@ +class SuperGameBoy : public MMIO, public Memory, public library { +public: + void enter(); + + MMIO *mmio[3]; + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void init(); + void enable(); + void power(); + void reset(); + void unload(); + + void serialize(serializer&); + +private: + uint32_t samplebuffer[4096]; + unsigned row; + bool version; + + enum { SuperGameBoy1 = 0, SuperGameBoy2 = 1 }; + function sgb_rom; + function sgb_ram; + function sgb_rtc; + function sgb_init; + function sgb_term; + function sgb_power; + function sgb_reset; + function sgb_row; + function sgb_read; + function sgb_write; + function sgb_run; + function sgb_save; + function sgb_serialize; +}; + +extern SuperGameBoy supergameboy; diff --git a/mednafen/snes/src/clean.bat b/mednafen/snes/src/clean.bat new file mode 100755 index 0000000..d8bb7e0 --- /dev/null +++ b/mednafen/snes/src/clean.bat @@ -0,0 +1 @@ +@mingw32-make clean diff --git a/mednafen/snes/src/cpu/core/algorithms.cpp b/mednafen/snes/src/cpu/core/algorithms.cpp new file mode 100755 index 0000000..9eba837 --- /dev/null +++ b/mednafen/snes/src/cpu/core/algorithms.cpp @@ -0,0 +1,369 @@ +#ifdef CPUCORE_CPP + +inline void CPUcore::op_adc_b() { + int r; + if(regs.p.d) { + uint8 n0 = (regs.a.l ) & 15; + uint8 n1 = (regs.a.l >> 4) & 15; + n0 += (rd.l & 15) + regs.p.c; + if(n0 > 9) { + n0 = (n0 - 10) & 15; + n1++; + } + n1 += ((rd.l >> 4) & 15); + if(n1 > 9) { + n1 = (n1 - 10) & 15; + regs.p.c = 1; + } else { + regs.p.c = 0; + } + r = (n1 << 4) | n0; + } else { + r = regs.a.l + rd.l + regs.p.c; + regs.p.c = r > 0xff; + } + regs.p.n = r & 0x80; + regs.p.v = ~(regs.a.l ^ rd.l) & (regs.a.l ^ r) & 0x80; + regs.p.z = (uint8)r == 0; + regs.a.l = r; +} + +inline void CPUcore::op_adc_w() { + int r; + if(regs.p.d) { + uint8 n0 = (regs.a.w ) & 15; + uint8 n1 = (regs.a.w >> 4) & 15; + uint8 n2 = (regs.a.w >> 8) & 15; + uint8 n3 = (regs.a.w >> 12) & 15; + n0 += (rd.w & 15) + regs.p.c; + if(n0 > 9) { + n0 = (n0 - 10) & 15; + n1++; + } + n1 += ((rd.w >> 4) & 15); + if(n1 > 9) { + n1 = (n1 - 10) & 15; + n2++; + } + n2 += ((rd.w >> 8) & 15); + if(n2 > 9) { + n2 = (n2 - 10) & 15; + n3++; + } + n3 += ((rd.w >> 12) & 15); + if(n3 > 9) { + n3 = (n3 - 10) & 15; + regs.p.c = 1; + } else { + regs.p.c = 0; + } + r = (n3 << 12) | (n2 << 8) | (n1 << 4) | n0; + } else { + r = regs.a.w + rd.w + regs.p.c; + regs.p.c = r > 0xffff; + } + regs.p.n = r & 0x8000; + regs.p.v = ~(regs.a.w ^ rd.w) & (regs.a.w ^ r) & 0x8000; + regs.p.z = (uint16)r == 0; + regs.a.w = r; +} + +inline void CPUcore::op_and_b() { + regs.a.l &= rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void CPUcore::op_and_w() { + regs.a.w &= rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void CPUcore::op_bit_b() { + regs.p.n = rd.l & 0x80; + regs.p.v = rd.l & 0x40; + regs.p.z = (rd.l & regs.a.l) == 0; +} + +inline void CPUcore::op_bit_w() { + regs.p.n = rd.w & 0x8000; + regs.p.v = rd.w & 0x4000; + regs.p.z = (rd.w & regs.a.w) == 0; +} + +inline void CPUcore::op_cmp_b() { + int r = regs.a.l - rd.l; + regs.p.n = r & 0x80; + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_cmp_w() { + int r = regs.a.w - rd.w; + regs.p.n = r & 0x8000; + regs.p.z = (uint16)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_cpx_b() { + int r = regs.x.l - rd.l; + regs.p.n = r & 0x80; + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_cpx_w() { + int r = regs.x.w - rd.w; + regs.p.n = r & 0x8000; + regs.p.z = (uint16)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_cpy_b() { + int r = regs.y.l - rd.l; + regs.p.n = r & 0x80; + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_cpy_w() { + int r = regs.y.w - rd.w; + regs.p.n = r & 0x8000; + regs.p.z = (uint16)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_eor_b() { + regs.a.l ^= rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void CPUcore::op_eor_w() { + regs.a.w ^= rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void CPUcore::op_lda_b() { + regs.a.l = rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void CPUcore::op_lda_w() { + regs.a.w = rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void CPUcore::op_ldx_b() { + regs.x.l = rd.l; + regs.p.n = regs.x.l & 0x80; + regs.p.z = regs.x.l == 0; +} + +inline void CPUcore::op_ldx_w() { + regs.x.w = rd.w; + regs.p.n = regs.x.w & 0x8000; + regs.p.z = regs.x.w == 0; +} + +inline void CPUcore::op_ldy_b() { + regs.y.l = rd.l; + regs.p.n = regs.y.l & 0x80; + regs.p.z = regs.y.l == 0; +} + +inline void CPUcore::op_ldy_w() { + regs.y.w = rd.w; + regs.p.n = regs.y.w & 0x8000; + regs.p.z = regs.y.w == 0; +} + +inline void CPUcore::op_ora_b() { + regs.a.l |= rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void CPUcore::op_ora_w() { + regs.a.w |= rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void CPUcore::op_sbc_b() { + int r; + if(regs.p.d) { + uint8 n0 = (regs.a.l ) & 15; + uint8 n1 = (regs.a.l >> 4) & 15; + n0 -= ((rd.l ) & 15) + !regs.p.c; + n1 -= ((rd.l >> 4) & 15); + if(n0 > 9) { + n0 += 10; + n1--; + } + if(n1 > 9) { + n1 += 10; + regs.p.c = 0; + } else { + regs.p.c = 1; + } + r = (n1 << 4) | (n0); + } else { + r = regs.a.l - rd.l - !regs.p.c; + regs.p.c = r >= 0; + } + regs.p.n = r & 0x80; + regs.p.v = (regs.a.l ^ rd.l) & (regs.a.l ^ r) & 0x80; + regs.p.z = (uint8)r == 0; + regs.a.l = r; +} + +inline void CPUcore::op_sbc_w() { + int r; + if(regs.p.d) { + uint8 n0 = (regs.a.w ) & 15; + uint8 n1 = (regs.a.w >> 4) & 15; + uint8 n2 = (regs.a.w >> 8) & 15; + uint8 n3 = (regs.a.w >> 12) & 15; + n0 -= ((rd.w ) & 15) + !regs.p.c; + n1 -= ((rd.w >> 4) & 15); + n2 -= ((rd.w >> 8) & 15); + n3 -= ((rd.w >> 12) & 15); + if(n0 > 9) { + n0 += 10; + n1--; + } + if(n1 > 9) { + n1 += 10; + n2--; + } + if(n2 > 9) { + n2 += 10; + n3--; + } + if(n3 > 9) { + n3 += 10; + regs.p.c = 0; + } else { + regs.p.c = 1; + } + r = (n3 << 12) | (n2 << 8) | (n1 << 4) | (n0); + } else { + r = regs.a.w - rd.w - !regs.p.c; + regs.p.c = r >= 0; + } + regs.p.n = r & 0x8000; + regs.p.v = (regs.a.w ^ rd.w) & (regs.a.w ^ r) & 0x8000; + regs.p.z = (uint16)r == 0; + regs.a.w = r; +} + +inline void CPUcore::op_inc_b() { + rd.l++; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_inc_w() { + rd.w++; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_dec_b() { + rd.l--; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_dec_w() { + rd.w--; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_asl_b() { + regs.p.c = rd.l & 0x80; + rd.l <<= 1; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_asl_w() { + regs.p.c = rd.w & 0x8000; + rd.w <<= 1; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_lsr_b() { + regs.p.c = rd.l & 1; + rd.l >>= 1; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_lsr_w() { + regs.p.c = rd.w & 1; + rd.w >>= 1; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_rol_b() { + unsigned carry = (unsigned)regs.p.c; + regs.p.c = rd.l & 0x80; + rd.l = (rd.l << 1) | carry; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_rol_w() { + unsigned carry = (unsigned)regs.p.c; + regs.p.c = rd.w & 0x8000; + rd.w = (rd.w << 1) | carry; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_ror_b() { + unsigned carry = (unsigned)regs.p.c << 7; + regs.p.c = rd.l & 1; + rd.l = carry | (rd.l >> 1); + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_ror_w() { + unsigned carry = (unsigned)regs.p.c << 15; + regs.p.c = rd.w & 1; + rd.w = carry | (rd.w >> 1); + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_trb_b() { + regs.p.z = (rd.l & regs.a.l) == 0; + rd.l &= ~regs.a.l; +} + +inline void CPUcore::op_trb_w() { + regs.p.z = (rd.w & regs.a.w) == 0; + rd.w &= ~regs.a.w; +} + +inline void CPUcore::op_tsb_b() { + regs.p.z = (rd.l & regs.a.l) == 0; + rd.l |= regs.a.l; +} + +inline void CPUcore::op_tsb_w() { + regs.p.z = (rd.w & regs.a.w) == 0; + rd.w |= regs.a.w; +} + +#endif diff --git a/mednafen/snes/src/cpu/core/core.cpp b/mednafen/snes/src/cpu/core/core.cpp new file mode 100755 index 0000000..d38cbae --- /dev/null +++ b/mednafen/snes/src/cpu/core/core.cpp @@ -0,0 +1,74 @@ +#include <../base.hpp> + +#define CPUCORE_CPP +namespace SNES { + +#include "serialization.cpp" +#include "algorithms.cpp" +#include "disassembler/disassembler.cpp" + +#define L last_cycle(); +#define A 0 +#define X 1 +#define Y 2 +#define Z 3 +#define S 4 +#define D 5 +#define call(op) (this->*op)() + +#include "opcode_read.cpp" +#include "opcode_write.cpp" +#include "opcode_rmw.cpp" +#include "opcode_pc.cpp" +#include "opcode_misc.cpp" +#include "table.cpp" + +#undef L +#undef A +#undef X +#undef Y +#undef Z +#undef S +#undef D +#undef call + +//immediate, 2-cycle opcodes with I/O cycle will become bus read +//when an IRQ is to be triggered immediately after opcode completion. +//this affects the following opcodes: +// clc, cld, cli, clv, sec, sed, sei, +// tax, tay, txa, txy, tya, tyx, +// tcd, tcs, tdc, tsc, tsx, txs, +// inc, inx, iny, dec, dex, dey, +// asl, lsr, rol, ror, nop, xce. +alwaysinline void CPUcore::op_io_irq() { + if(interrupt_pending()) { + //modify I/O cycle to bus read cycle, do not increment PC + op_read(regs.pc.d); + } else { + op_io(); + } +} + +alwaysinline void CPUcore::op_io_cond2() { + if(regs.d.l != 0x00) { + op_io(); + } +} + +alwaysinline void CPUcore::op_io_cond4(uint16 x, uint16 y) { + if(!regs.p.x || (x & 0xff00) != (y & 0xff00)) { + op_io(); + } +} + +alwaysinline void CPUcore::op_io_cond6(uint16 addr) { + if(regs.e && (regs.pc.w & 0xff00) != (addr & 0xff00)) { + op_io(); + } +} + +CPUcore::CPUcore() { + initialize_opcode_table(); +} + +}; diff --git a/mednafen/snes/src/cpu/core/core.hpp b/mednafen/snes/src/cpu/core/core.hpp new file mode 100755 index 0000000..71304eb --- /dev/null +++ b/mednafen/snes/src/cpu/core/core.hpp @@ -0,0 +1,219 @@ +class CPUcore { +public: + #include "registers.hpp" + #include "memory.hpp" + #include "disassembler/disassembler.hpp" + + regs_t regs; + reg24_t aa, rd; + uint8_t sp, dp; + + virtual void op_io() = 0; + virtual uint8_t op_read(uint32_t addr) = 0; + virtual void op_write(uint32_t addr, uint8_t data) = 0; + virtual void last_cycle() = 0; + virtual bool interrupt_pending() = 0; + + void op_io_irq(); + void op_io_cond2(); + void op_io_cond4(uint16 x, uint16 y); + void op_io_cond6(uint16 addr); + + void op_adc_b(); + void op_adc_w(); + void op_and_b(); + void op_and_w(); + void op_bit_b(); + void op_bit_w(); + void op_cmp_b(); + void op_cmp_w(); + void op_cpx_b(); + void op_cpx_w(); + void op_cpy_b(); + void op_cpy_w(); + void op_eor_b(); + void op_eor_w(); + void op_lda_b(); + void op_lda_w(); + void op_ldx_b(); + void op_ldx_w(); + void op_ldy_b(); + void op_ldy_w(); + void op_ora_b(); + void op_ora_w(); + void op_sbc_b(); + void op_sbc_w(); + + void op_inc_b(); + void op_inc_w(); + void op_dec_b(); + void op_dec_w(); + void op_asl_b(); + void op_asl_w(); + void op_lsr_b(); + void op_lsr_w(); + void op_rol_b(); + void op_rol_w(); + void op_ror_b(); + void op_ror_w(); + void op_trb_b(); + void op_trb_w(); + void op_tsb_b(); + void op_tsb_w(); + + template void op_read_const_b(); + template void op_read_const_w(); + void op_read_bit_const_b(); + void op_read_bit_const_w(); + template void op_read_addr_b(); + template void op_read_addr_w(); + template void op_read_addrx_b(); + template void op_read_addrx_w(); + template void op_read_addry_b(); + template void op_read_addry_w(); + template void op_read_long_b(); + template void op_read_long_w(); + template void op_read_longx_b(); + template void op_read_longx_w(); + template void op_read_dp_b(); + template void op_read_dp_w(); + template void op_read_dpr_b(); + template void op_read_dpr_w(); + template void op_read_idp_b(); + template void op_read_idp_w(); + template void op_read_idpx_b(); + template void op_read_idpx_w(); + template void op_read_idpy_b(); + template void op_read_idpy_w(); + template void op_read_ildp_b(); + template void op_read_ildp_w(); + template void op_read_ildpy_b(); + template void op_read_ildpy_w(); + template void op_read_sr_b(); + template void op_read_sr_w(); + template void op_read_isry_b(); + template void op_read_isry_w(); + + template void op_write_addr_b(); + template void op_write_addr_w(); + template void op_write_addrr_b(); + template void op_write_addrr_w(); + template void op_write_longr_b(); + template void op_write_longr_w(); + template void op_write_dp_b(); + template void op_write_dp_w(); + template void op_write_dpr_b(); + template void op_write_dpr_w(); + void op_sta_idp_b(); + void op_sta_idp_w(); + void op_sta_ildp_b(); + void op_sta_ildp_w(); + void op_sta_idpx_b(); + void op_sta_idpx_w(); + void op_sta_idpy_b(); + void op_sta_idpy_w(); + void op_sta_ildpy_b(); + void op_sta_ildpy_w(); + void op_sta_sr_b(); + void op_sta_sr_w(); + void op_sta_isry_b(); + void op_sta_isry_w(); + + template void op_adjust_imm_b(); + template void op_adjust_imm_w(); + void op_asl_imm_b(); + void op_asl_imm_w(); + void op_lsr_imm_b(); + void op_lsr_imm_w(); + void op_rol_imm_b(); + void op_rol_imm_w(); + void op_ror_imm_b(); + void op_ror_imm_w(); + template void op_adjust_addr_b(); + template void op_adjust_addr_w(); + template void op_adjust_addrx_b(); + template void op_adjust_addrx_w(); + template void op_adjust_dp_b(); + template void op_adjust_dp_w(); + template void op_adjust_dpx_b(); + template void op_adjust_dpx_w(); + + template void op_branch(); + void op_bra(); + void op_brl(); + void op_jmp_addr(); + void op_jmp_long(); + void op_jmp_iaddr(); + void op_jmp_iaddrx(); + void op_jmp_iladdr(); + void op_jsr_addr(); + void op_jsr_long_e(); + void op_jsr_long_n(); + void op_jsr_iaddrx_e(); + void op_jsr_iaddrx_n(); + void op_rti_e(); + void op_rti_n(); + void op_rts(); + void op_rtl_e(); + void op_rtl_n(); + + void op_nop(); + void op_wdm(); + void op_xba(); + template void op_move_b(); + template void op_move_w(); + template void op_interrupt_e(); + template void op_interrupt_n(); + void op_stp(); + void op_wai(); + void op_xce(); + template void op_flag(); + template void op_pflag_e(); + template void op_pflag_n(); + template void op_transfer_b(); + template void op_transfer_w(); + void op_tcs_e(); + void op_tcs_n(); + void op_tsc_e(); + void op_tsc_n(); + void op_tsx_b(); + void op_tsx_w(); + void op_txs_e(); + void op_txs_n(); + template void op_push_b(); + template void op_push_w(); + void op_phd_e(); + void op_phd_n(); + void op_phb(); + void op_phk(); + void op_php(); + template void op_pull_b(); + template void op_pull_w(); + void op_pld_e(); + void op_pld_n(); + void op_plb(); + void op_plp_e(); + void op_plp_n(); + void op_pea_e(); + void op_pea_n(); + void op_pei_e(); + void op_pei_n(); + void op_per_e(); + void op_per_n(); + + void (CPUcore::**opcode_table)(); + void (CPUcore::*op_table[256 * 5])(); + void initialize_opcode_table(); + void update_table(); + + enum { + table_EM = 0, // 8-bit accumulator, 8-bit index (emulation mode) + table_MX = 256, // 8-bit accumulator, 8-bit index + table_Mx = 512, // 8-bit accumulator, 16-bit index + table_mX = 768, //16-bit accumulator, 8-bit index + table_mx = 1024, //16-bit accumulator, 16-bit index + }; + + void core_serialize(serializer&); + CPUcore(); +}; diff --git a/mednafen/snes/src/cpu/core/disassembler/disassembler.cpp b/mednafen/snes/src/cpu/core/disassembler/disassembler.cpp new file mode 100755 index 0000000..030b3ab --- /dev/null +++ b/mednafen/snes/src/cpu/core/disassembler/disassembler.cpp @@ -0,0 +1,483 @@ +#ifdef CPUCORE_CPP + +uint8 CPUcore::dreadb(uint32 addr) { + if((addr & 0x40ffff) >= 0x2000 && (addr & 0x40ffff) <= 0x5fff) { + //$[00-3f|80-bf]:[2000-5fff] + //do not read MMIO registers within debugger + return 0x00; + } + return bus.read(addr); +} + +uint16 CPUcore::dreadw(uint32 addr) { + uint16 r; + r = dreadb((addr + 0) & 0xffffff) << 0; + r |= dreadb((addr + 1) & 0xffffff) << 8; + return r; +} + +uint32 CPUcore::dreadl(uint32 addr) { + uint32 r; + r = dreadb((addr + 0) & 0xffffff) << 0; + r |= dreadb((addr + 1) & 0xffffff) << 8; + r |= dreadb((addr + 2) & 0xffffff) << 16; + return r; +} + +uint32 CPUcore::decode(uint8 offset_type, uint32 addr) { + uint32 r = 0; + + switch(offset_type) { + case OPTYPE_DP: + r = (regs.d + (addr & 0xffff)) & 0xffff; + break; + case OPTYPE_DPX: + r = (regs.d + regs.x + (addr & 0xffff)) & 0xffff; + break; + case OPTYPE_DPY: + r = (regs.d + regs.y + (addr & 0xffff)) & 0xffff; + break; + case OPTYPE_IDP: + addr = (regs.d + (addr & 0xffff)) & 0xffff; + r = (regs.db << 16) + dreadw(addr); + break; + case OPTYPE_IDPX: + addr = (regs.d + regs.x + (addr & 0xffff)) & 0xffff; + r = (regs.db << 16) + dreadw(addr); + break; + case OPTYPE_IDPY: + addr = (regs.d + (addr & 0xffff)) & 0xffff; + r = (regs.db << 16) + dreadw(addr) + regs.y; + break; + case OPTYPE_ILDP: + addr = (regs.d + (addr & 0xffff)) & 0xffff; + r = dreadl(addr); + break; + case OPTYPE_ILDPY: + addr = (regs.d + (addr & 0xffff)) & 0xffff; + r = dreadl(addr) + regs.y; + break; + case OPTYPE_ADDR: + r = (regs.db << 16) + (addr & 0xffff); + break; + case OPTYPE_ADDR_PC: + r = (regs.pc.b << 16) + (addr & 0xffff); + break; + case OPTYPE_ADDRX: + r = (regs.db << 16) + (addr & 0xffff) + regs.x; + break; + case OPTYPE_ADDRY: + r = (regs.db << 16) + (addr & 0xffff) + regs.y; + break; + case OPTYPE_IADDR_PC: + r = (regs.pc.b << 16) + (addr & 0xffff); + break; + case OPTYPE_IADDRX: + r = (regs.pc.b << 16) + ((addr + regs.x) & 0xffff); + break; + case OPTYPE_ILADDR: + r = addr; + break; + case OPTYPE_LONG: + r = addr; + break; + case OPTYPE_LONGX: + r = (addr + regs.x); + break; + case OPTYPE_SR: + r = (regs.s + (addr & 0xff)) & 0xffff; + break; + case OPTYPE_ISRY: + addr = (regs.s + (addr & 0xff)) & 0xffff; + r = (regs.db << 16) + dreadw(addr) + regs.y; + break; + case OPTYPE_RELB: + r = (regs.pc.b << 16) + ((regs.pc.w + 2) & 0xffff); + r += int8(addr); + break; + case OPTYPE_RELW: + r = (regs.pc.b << 16) + ((regs.pc.w + 3) & 0xffff); + r += int16(addr); + break; + } + + return(r & 0xffffff); +} + +void CPUcore::disassemble_opcode(char *output, uint32 addr) { + static reg24_t pc; + char t[256]; + char *s = output; + + if(false /* in_opcode() == true */) { + strcpy(s, "?????? "); + return; + } + + pc.d = addr; + sprintf(s, "%.6x ", (uint32)pc.d); + + uint8 op = dreadb(pc.d); pc.w++; + uint8 op0 = dreadb(pc.d); pc.w++; + uint8 op1 = dreadb(pc.d); pc.w++; + uint8 op2 = dreadb(pc.d); + + #define op8 ((op0)) + #define op16 ((op0) | (op1 << 8)) + #define op24 ((op0) | (op1 << 8) | (op2 << 16)) + #define a8 (regs.e || regs.p.m) + #define x8 (regs.e || regs.p.x) + + switch(op) { + case 0x00: sprintf(t, "brk #$%.2x ", op8); break; + case 0x01: sprintf(t, "ora ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0x02: sprintf(t, "cop #$%.2x ", op8); break; + case 0x03: sprintf(t, "ora $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0x04: sprintf(t, "tsb $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x05: sprintf(t, "ora $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x06: sprintf(t, "asl $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x07: sprintf(t, "ora [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0x08: sprintf(t, "php "); break; + case 0x09: if(a8)sprintf(t, "ora #$%.2x ", op8); + else sprintf(t, "ora #$%.4x ", op16); break; + case 0x0a: sprintf(t, "asl a "); break; + case 0x0b: sprintf(t, "phd "); break; + case 0x0c: sprintf(t, "tsb $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x0d: sprintf(t, "ora $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x0e: sprintf(t, "asl $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x0f: sprintf(t, "ora $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x10: sprintf(t, "bpl $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x11: sprintf(t, "ora ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0x12: sprintf(t, "ora ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0x13: sprintf(t, "ora ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0x14: sprintf(t, "trb $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x15: sprintf(t, "ora $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x16: sprintf(t, "asl $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x17: sprintf(t, "ora [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0x18: sprintf(t, "clc "); break; + case 0x19: sprintf(t, "ora $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0x1a: sprintf(t, "inc "); break; + case 0x1b: sprintf(t, "tcs "); break; + case 0x1c: sprintf(t, "trb $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x1d: sprintf(t, "ora $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x1e: sprintf(t, "asl $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x1f: sprintf(t, "ora $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0x20: sprintf(t, "jsr $%.4x [%.6x]", op16, decode(OPTYPE_ADDR_PC, op16)); break; + case 0x21: sprintf(t, "and ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0x22: sprintf(t, "jsl $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x23: sprintf(t, "and $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0x24: sprintf(t, "bit $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x25: sprintf(t, "and $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x26: sprintf(t, "rol $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x27: sprintf(t, "and [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0x28: sprintf(t, "plp "); break; + case 0x29: if(a8)sprintf(t, "and #$%.2x ", op8); + else sprintf(t, "and #$%.4x ", op16); break; + case 0x2a: sprintf(t, "rol a "); break; + case 0x2b: sprintf(t, "pld "); break; + case 0x2c: sprintf(t, "bit $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x2d: sprintf(t, "and $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x2e: sprintf(t, "rol $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x2f: sprintf(t, "and $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x30: sprintf(t, "bmi $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x31: sprintf(t, "and ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0x32: sprintf(t, "and ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0x33: sprintf(t, "and ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0x34: sprintf(t, "bit $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x35: sprintf(t, "and $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x36: sprintf(t, "rol $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x37: sprintf(t, "and [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0x38: sprintf(t, "sec "); break; + case 0x39: sprintf(t, "and $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0x3a: sprintf(t, "dec "); break; + case 0x3b: sprintf(t, "tsc "); break; + case 0x3c: sprintf(t, "bit $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x3d: sprintf(t, "and $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x3e: sprintf(t, "rol $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x3f: sprintf(t, "and $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0x40: sprintf(t, "rti "); break; + case 0x41: sprintf(t, "eor ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0x42: sprintf(t, "wdm "); break; + case 0x43: sprintf(t, "eor $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0x44: sprintf(t, "mvp $%.2x,$%.2x ", op1, op8); break; + case 0x45: sprintf(t, "eor $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x46: sprintf(t, "lsr $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x47: sprintf(t, "eor [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0x48: sprintf(t, "pha "); break; + case 0x49: if(a8)sprintf(t, "eor #$%.2x ", op8); + else sprintf(t, "eor #$%.4x ", op16); break; + case 0x4a: sprintf(t, "lsr a "); break; + case 0x4b: sprintf(t, "phk "); break; + case 0x4c: sprintf(t, "jmp $%.4x [%.6x]", op16, decode(OPTYPE_ADDR_PC, op16)); break; + case 0x4d: sprintf(t, "eor $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x4e: sprintf(t, "lsr $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x4f: sprintf(t, "eor $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x50: sprintf(t, "bvc $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x51: sprintf(t, "eor ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0x52: sprintf(t, "eor ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0x53: sprintf(t, "eor ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0x54: sprintf(t, "mvn $%.2x,$%.2x ", op1, op8); break; + case 0x55: sprintf(t, "eor $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x56: sprintf(t, "lsr $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x57: sprintf(t, "eor [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0x58: sprintf(t, "cli "); break; + case 0x59: sprintf(t, "eor $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0x5a: sprintf(t, "phy "); break; + case 0x5b: sprintf(t, "tcd "); break; + case 0x5c: sprintf(t, "jml $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x5d: sprintf(t, "eor $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x5e: sprintf(t, "lsr $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x5f: sprintf(t, "eor $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0x60: sprintf(t, "rts "); break; + case 0x61: sprintf(t, "adc ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0x62: sprintf(t, "per $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x63: sprintf(t, "adc $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0x64: sprintf(t, "stz $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x65: sprintf(t, "adc $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x66: sprintf(t, "ror $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x67: sprintf(t, "adc [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0x68: sprintf(t, "pla "); break; + case 0x69: if(a8)sprintf(t, "adc #$%.2x ", op8); + else sprintf(t, "adc #$%.4x ", op16); break; + case 0x6a: sprintf(t, "ror a "); break; + case 0x6b: sprintf(t, "rtl "); break; + case 0x6c: sprintf(t, "jmp ($%.4x) [%.6x]", op16, decode(OPTYPE_IADDR_PC, op16)); break; + case 0x6d: sprintf(t, "adc $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x6e: sprintf(t, "ror $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x6f: sprintf(t, "adc $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x70: sprintf(t, "bvs $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x71: sprintf(t, "adc ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0x72: sprintf(t, "adc ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0x73: sprintf(t, "adc ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0x74: sprintf(t, "stz $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x75: sprintf(t, "adc $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x76: sprintf(t, "ror $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x77: sprintf(t, "adc [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0x78: sprintf(t, "sei "); break; + case 0x79: sprintf(t, "adc $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0x7a: sprintf(t, "ply "); break; + case 0x7b: sprintf(t, "tdc "); break; + case 0x7c: sprintf(t, "jmp ($%.4x,x) [%.6x]", op16, decode(OPTYPE_IADDRX, op16)); break; + case 0x7d: sprintf(t, "adc $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x7e: sprintf(t, "ror $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x7f: sprintf(t, "adc $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0x80: sprintf(t, "bra $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x81: sprintf(t, "sta ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0x82: sprintf(t, "brl $%.4x [%.6x]", uint16(decode(OPTYPE_RELW, op16)), decode(OPTYPE_RELW, op16)); break; + case 0x83: sprintf(t, "sta $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0x84: sprintf(t, "sty $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x85: sprintf(t, "sta $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x86: sprintf(t, "stx $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x87: sprintf(t, "sta [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0x88: sprintf(t, "dey "); break; + case 0x89: if(a8)sprintf(t, "bit #$%.2x ", op8); + else sprintf(t, "bit #$%.4x ", op16); break; + case 0x8a: sprintf(t, "txa "); break; + case 0x8b: sprintf(t, "phb "); break; + case 0x8c: sprintf(t, "sty $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x8d: sprintf(t, "sta $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x8e: sprintf(t, "stx $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x8f: sprintf(t, "sta $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x90: sprintf(t, "bcc $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x91: sprintf(t, "sta ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0x92: sprintf(t, "sta ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0x93: sprintf(t, "sta ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0x94: sprintf(t, "sty $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x95: sprintf(t, "sta $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x96: sprintf(t, "stx $%.2x,y [%.6x]", op8, decode(OPTYPE_DPY, op8)); break; + case 0x97: sprintf(t, "sta [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0x98: sprintf(t, "tya "); break; + case 0x99: sprintf(t, "sta $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0x9a: sprintf(t, "txs "); break; + case 0x9b: sprintf(t, "txy "); break; + case 0x9c: sprintf(t, "stz $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x9d: sprintf(t, "sta $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x9e: sprintf(t, "stz $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x9f: sprintf(t, "sta $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0xa0: if(x8)sprintf(t, "ldy #$%.2x ", op8); + else sprintf(t, "ldy #$%.4x ", op16); break; + case 0xa1: sprintf(t, "lda ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0xa2: if(x8)sprintf(t, "ldx #$%.2x ", op8); + else sprintf(t, "ldx #$%.4x ", op16); break; + case 0xa3: sprintf(t, "lda $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0xa4: sprintf(t, "ldy $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xa5: sprintf(t, "lda $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xa6: sprintf(t, "ldx $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xa7: sprintf(t, "lda [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0xa8: sprintf(t, "tay "); break; + case 0xa9: if(a8)sprintf(t, "lda #$%.2x ", op8); + else sprintf(t, "lda #$%.4x ", op16); break; + case 0xaa: sprintf(t, "tax "); break; + case 0xab: sprintf(t, "plb "); break; + case 0xac: sprintf(t, "ldy $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xad: sprintf(t, "lda $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xae: sprintf(t, "ldx $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xaf: sprintf(t, "lda $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0xb0: sprintf(t, "bcs $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0xb1: sprintf(t, "lda ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0xb2: sprintf(t, "lda ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0xb3: sprintf(t, "lda ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0xb4: sprintf(t, "ldy $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xb5: sprintf(t, "lda $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xb6: sprintf(t, "ldx $%.2x,y [%.6x]", op8, decode(OPTYPE_DPY, op8)); break; + case 0xb7: sprintf(t, "lda [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0xb8: sprintf(t, "clv "); break; + case 0xb9: sprintf(t, "lda $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0xba: sprintf(t, "tsx "); break; + case 0xbb: sprintf(t, "tyx "); break; + case 0xbc: sprintf(t, "ldy $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xbd: sprintf(t, "lda $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xbe: sprintf(t, "ldx $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0xbf: sprintf(t, "lda $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0xc0: if(x8)sprintf(t, "cpy #$%.2x ", op8); + else sprintf(t, "cpy #$%.4x ", op16); break; + case 0xc1: sprintf(t, "cmp ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0xc2: sprintf(t, "rep #$%.2x ", op8); break; + case 0xc3: sprintf(t, "cmp $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0xc4: sprintf(t, "cpy $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xc5: sprintf(t, "cmp $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xc6: sprintf(t, "dec $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xc7: sprintf(t, "cmp [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0xc8: sprintf(t, "iny "); break; + case 0xc9: if(a8)sprintf(t, "cmp #$%.2x ", op8); + else sprintf(t, "cmp #$%.4x ", op16); break; + case 0xca: sprintf(t, "dex "); break; + case 0xcb: sprintf(t, "wai "); break; + case 0xcc: sprintf(t, "cpy $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xcd: sprintf(t, "cmp $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xce: sprintf(t, "dec $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xcf: sprintf(t, "cmp $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0xd0: sprintf(t, "bne $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0xd1: sprintf(t, "cmp ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0xd2: sprintf(t, "cmp ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0xd3: sprintf(t, "cmp ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0xd4: sprintf(t, "pei ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0xd5: sprintf(t, "cmp $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xd6: sprintf(t, "dec $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xd7: sprintf(t, "cmp [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0xd8: sprintf(t, "cld "); break; + case 0xd9: sprintf(t, "cmp $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0xda: sprintf(t, "phx "); break; + case 0xdb: sprintf(t, "stp "); break; + case 0xdc: sprintf(t, "jmp [$%.4x] [%.6x]", op16, decode(OPTYPE_ILADDR, op16)); break; + case 0xdd: sprintf(t, "cmp $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xde: sprintf(t, "dec $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xdf: sprintf(t, "cmp $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0xe0: if(x8)sprintf(t, "cpx #$%.2x ", op8); + else sprintf(t, "cpx #$%.4x ", op16); break; + case 0xe1: sprintf(t, "sbc ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0xe2: sprintf(t, "sep #$%.2x ", op8); break; + case 0xe3: sprintf(t, "sbc $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0xe4: sprintf(t, "cpx $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xe5: sprintf(t, "sbc $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xe6: sprintf(t, "inc $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xe7: sprintf(t, "sbc [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0xe8: sprintf(t, "inx "); break; + case 0xe9: if(a8)sprintf(t, "sbc #$%.2x ", op8); + else sprintf(t, "sbc #$%.4x ", op16); break; + case 0xea: sprintf(t, "nop "); break; + case 0xeb: sprintf(t, "xba "); break; + case 0xec: sprintf(t, "cpx $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xed: sprintf(t, "sbc $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xee: sprintf(t, "inc $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xef: sprintf(t, "sbc $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0xf0: sprintf(t, "beq $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0xf1: sprintf(t, "sbc ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0xf2: sprintf(t, "sbc ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0xf3: sprintf(t, "sbc ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0xf4: sprintf(t, "pea $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xf5: sprintf(t, "sbc $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xf6: sprintf(t, "inc $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xf7: sprintf(t, "sbc [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0xf8: sprintf(t, "sed "); break; + case 0xf9: sprintf(t, "sbc $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0xfa: sprintf(t, "plx "); break; + case 0xfb: sprintf(t, "xce "); break; + case 0xfc: sprintf(t, "jsr ($%.4x,x) [%.6x]", op16, decode(OPTYPE_IADDRX, op16)); break; + case 0xfd: sprintf(t, "sbc $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xfe: sprintf(t, "inc $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xff: sprintf(t, "sbc $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + } + + #undef op8 + #undef op16 + #undef op24 + #undef a8 + #undef x8 + + strcat(s, t); + strcat(s, " "); + + sprintf(t, "A:%.4x X:%.4x Y:%.4x S:%.4x D:%.4x DB:%.2x ", + regs.a.w, regs.x.w, regs.y.w, regs.s.w, regs.d.w, regs.db); + strcat(s, t); + + if(regs.e) { + sprintf(t, "%c%c%c%c%c%c%c%c", + regs.p.n ? 'N' : 'n', regs.p.v ? 'V' : 'v', + regs.p.m ? '1' : '0', regs.p.x ? 'B' : 'b', + regs.p.d ? 'D' : 'd', regs.p.i ? 'I' : 'i', + regs.p.z ? 'Z' : 'z', regs.p.c ? 'C' : 'c'); + } else { + sprintf(t, "%c%c%c%c%c%c%c%c", + regs.p.n ? 'N' : 'n', regs.p.v ? 'V' : 'v', + regs.p.m ? 'M' : 'm', regs.p.x ? 'X' : 'x', + regs.p.d ? 'D' : 'd', regs.p.i ? 'I' : 'i', + regs.p.z ? 'Z' : 'z', regs.p.c ? 'C' : 'c'); + } + + strcat(s, t); + strcat(s, " "); + + sprintf(t, "V:%3d H:%4d", cpu.vcounter(), cpu.hcounter()); + strcat(s, t); +} + +//opcode_length() retrieves the length of the next opcode +//to be executed. It is used by the debugger to step over, +//disable and proceed cpu opcodes. +// +//5 and 6 are special cases, 5 is used for #consts based on +//the A register size, 6 for the X/Y register size. the +//rest are literal sizes. There's no need to test for +//emulation mode, as regs.p.m/regs.p.x should *always* be +//set in emulation mode. + +uint8 CPUcore::opcode_length() { + uint8 op, len; + static uint8 op_len_tbl[256] = { + //0,1,2,3, 4,5,6,7, 8,9,a,b, c,d,e,f + + 2,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x0n + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x1n + 3,2,4,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x2n + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x3n + + 1,2,2,2, 3,2,2,2, 1,5,1,1, 3,3,3,4, //0x4n + 2,2,2,2, 3,2,2,2, 1,3,1,1, 4,3,3,4, //0x5n + 1,2,3,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x6n + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x7n + + 2,2,3,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x8n + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x9n + 6,2,6,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xan + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0xbn + + 6,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xcn + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0xdn + 6,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xen + 2,2,2,2, 3,2,2,2, 1,3,1,1, 3,3,3,4 //0xfn + }; + + if(false /* in_opcode() == true */) { + return 0; + } + + op = dreadb(regs.pc.d); + len = op_len_tbl[op]; + if(len == 5) return (regs.e || regs.p.m) ? 2 : 3; + if(len == 6) return (regs.e || regs.p.x) ? 2 : 3; + return len; +} + +#endif diff --git a/mednafen/snes/src/cpu/core/disassembler/disassembler.hpp b/mednafen/snes/src/cpu/core/disassembler/disassembler.hpp new file mode 100755 index 0000000..b0ee6f0 --- /dev/null +++ b/mednafen/snes/src/cpu/core/disassembler/disassembler.hpp @@ -0,0 +1,30 @@ +enum { + OPTYPE_DP = 0, //dp + OPTYPE_DPX, //dp,x + OPTYPE_DPY, //dp,y + OPTYPE_IDP, //(dp) + OPTYPE_IDPX, //(dp,x) + OPTYPE_IDPY, //(dp),y + OPTYPE_ILDP, //[dp] + OPTYPE_ILDPY, //[dp],y + OPTYPE_ADDR, //addr + OPTYPE_ADDRX, //addr,x + OPTYPE_ADDRY, //addr,y + OPTYPE_IADDRX, //(addr,x) + OPTYPE_ILADDR, //[addr] + OPTYPE_LONG, //long + OPTYPE_LONGX, //long, x + OPTYPE_SR, //sr,s + OPTYPE_ISRY, //(sr,s),y + OPTYPE_ADDR_PC, //pbr:addr + OPTYPE_IADDR_PC, //pbr:(addr) + OPTYPE_RELB, //relb + OPTYPE_RELW, //relw +}; + +void disassemble_opcode(char *output, uint32 addr); +uint8 dreadb(uint32 addr); +uint16 dreadw(uint32 addr); +uint32 dreadl(uint32 addr); +uint32 decode(uint8 offset_type, uint32 addr); +uint8 opcode_length(); diff --git a/mednafen/snes/src/cpu/core/memory.hpp b/mednafen/snes/src/cpu/core/memory.hpp new file mode 100755 index 0000000..4992657 --- /dev/null +++ b/mednafen/snes/src/cpu/core/memory.hpp @@ -0,0 +1,77 @@ +alwaysinline uint8_t op_readpc() { + return op_read((regs.pc.b << 16) + regs.pc.w++); +} + +alwaysinline uint8_t op_readstack() { + regs.e ? regs.s.l++ : regs.s.w++; + return op_read(regs.s.w); +} + +alwaysinline uint8_t op_readstackn() { + return op_read(++regs.s.w); +} + +alwaysinline uint8_t op_readaddr(uint32_t addr) { + return op_read(addr & 0xffff); +} + +alwaysinline uint8_t op_readlong(uint32_t addr) { + return op_read(addr & 0xffffff); +} + +alwaysinline uint8_t op_readdbr(uint32_t addr) { + return op_read(((regs.db << 16) + addr) & 0xffffff); +} + +alwaysinline uint8_t op_readpbr(uint32_t addr) { + return op_read((regs.pc.b << 16) + (addr & 0xffff)); +} + +alwaysinline uint8_t op_readdp(uint32_t addr) { + if(regs.e && regs.d.l == 0x00) { + return op_read((regs.d & 0xff00) + ((regs.d + (addr & 0xffff)) & 0xff)); + } else { + return op_read((regs.d + (addr & 0xffff)) & 0xffff); + } +} + +alwaysinline uint8_t op_readsp(uint32_t addr) { + return op_read((regs.s + (addr & 0xffff)) & 0xffff); +} + +alwaysinline void op_writestack(uint8_t data) { + op_write(regs.s.w, data); + regs.e ? regs.s.l-- : regs.s.w--; +} + +alwaysinline void op_writestackn(uint8_t data) { + op_write(regs.s.w--, data); +} + +alwaysinline void op_writeaddr(uint32_t addr, uint8_t data) { + op_write(addr & 0xffff, data); +} + +alwaysinline void op_writelong(uint32_t addr, uint8_t data) { + op_write(addr & 0xffffff, data); +} + +alwaysinline void op_writedbr(uint32_t addr, uint8_t data) { + op_write(((regs.db << 16) + addr) & 0xffffff, data); +} + +alwaysinline void op_writepbr(uint32_t addr, uint8_t data) { + op_write((regs.pc.b << 16) + (addr & 0xffff), data); +} + +alwaysinline void op_writedp(uint32_t addr, uint8_t data) { + if(regs.e && regs.d.l == 0x00) { + op_write((regs.d & 0xff00) + ((regs.d + (addr & 0xffff)) & 0xff), data); + } else { + op_write((regs.d + (addr & 0xffff)) & 0xffff, data); + } +} + +alwaysinline void op_writesp(uint32_t addr, uint8_t data) { + op_write((regs.s + (addr & 0xffff)) & 0xffff, data); +} diff --git a/mednafen/snes/src/cpu/core/opcode_misc.cpp b/mednafen/snes/src/cpu/core/opcode_misc.cpp new file mode 100755 index 0000000..354082a --- /dev/null +++ b/mednafen/snes/src/cpu/core/opcode_misc.cpp @@ -0,0 +1,352 @@ +#ifdef CPUCORE_CPP + +void CPUcore::op_nop() { +L op_io_irq(); +} + +void CPUcore::op_wdm() { +L op_readpc(); +} + +void CPUcore::op_xba() { + op_io(); +L op_io(); + regs.a.l ^= regs.a.h; + regs.a.h ^= regs.a.l; + regs.a.l ^= regs.a.h; + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +template void CPUcore::op_move_b() { + dp = op_readpc(); + sp = op_readpc(); + regs.db = dp; + rd.l = op_readlong((sp << 16) | regs.x.w); + op_writelong((dp << 16) | regs.y.w, rd.l); + op_io(); + regs.x.l += adjust; + regs.y.l += adjust; +L op_io(); + if(regs.a.w--) regs.pc.w -= 3; +} + +template void CPUcore::op_move_w() { + dp = op_readpc(); + sp = op_readpc(); + regs.db = dp; + rd.l = op_readlong((sp << 16) | regs.x.w); + op_writelong((dp << 16) | regs.y.w, rd.l); + op_io(); + regs.x.w += adjust; + regs.y.w += adjust; +L op_io(); + if(regs.a.w--) regs.pc.w -= 3; +} + +template void CPUcore::op_interrupt_e() { + op_readpc(); + op_writestack(regs.pc.h); + op_writestack(regs.pc.l); + op_writestack(regs.p); + rd.l = op_readlong(vectorE + 0); + regs.pc.b = 0; + regs.p.i = 1; + regs.p.d = 0; +L rd.h = op_readlong(vectorE + 1); + regs.pc.w = rd.w; +} + +template void CPUcore::op_interrupt_n() { + op_readpc(); + op_writestack(regs.pc.b); + op_writestack(regs.pc.h); + op_writestack(regs.pc.l); + op_writestack(regs.p); + rd.l = op_readlong(vectorN + 0); + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; +L rd.h = op_readlong(vectorN + 1); + regs.pc.w = rd.w; +} + +void CPUcore::op_stp() { + while(regs.wai = true) { +L op_io(); + } +} + +void CPUcore::op_wai() { + regs.wai = true; + while(regs.wai) { +L op_io(); + } + op_io(); +} + +void CPUcore::op_xce() { +L op_io_irq(); + bool carry = regs.p.c; + regs.p.c = regs.e; + regs.e = carry; + if(regs.e) { + regs.p |= 0x30; + regs.s.h = 0x01; + } + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + update_table(); +} + +template void CPUcore::op_flag() { +L op_io_irq(); + regs.p = (regs.p & ~mask) | value; +} + +template void CPUcore::op_pflag_e() { + rd.l = op_readpc(); +L op_io(); + regs.p = (mode ? regs.p | rd.l : regs.p & ~rd.l); + regs.p |= 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + update_table(); +} + +template void CPUcore::op_pflag_n() { + rd.l = op_readpc(); +L op_io(); + regs.p = (mode ? regs.p | rd.l : regs.p & ~rd.l); + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + update_table(); +} + +template void CPUcore::op_transfer_b() { +L op_io_irq(); + regs.r[to].l = regs.r[from].l; + regs.p.n = (regs.r[to].l & 0x80); + regs.p.z = (regs.r[to].l == 0); +} + +template void CPUcore::op_transfer_w() { +L op_io_irq(); + regs.r[to].w = regs.r[from].w; + regs.p.n = (regs.r[to].w & 0x8000); + regs.p.z = (regs.r[to].w == 0); +} + +void CPUcore::op_tcs_e() { +L op_io_irq(); + regs.s.l = regs.a.l; +} + +void CPUcore::op_tcs_n() { +L op_io_irq(); + regs.s.w = regs.a.w; +} + +void CPUcore::op_tsc_e() { +L op_io_irq(); + regs.a.w = regs.s.w; + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +void CPUcore::op_tsc_n() { +L op_io_irq(); + regs.a.w = regs.s.w; + regs.p.n = (regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} + +void CPUcore::op_tsx_b() { +L op_io_irq(); + regs.x.l = regs.s.l; + regs.p.n = (regs.x.l & 0x80); + regs.p.z = (regs.x.l == 0); +} + +void CPUcore::op_tsx_w() { +L op_io_irq(); + regs.x.w = regs.s.w; + regs.p.n = (regs.x.w & 0x8000); + regs.p.z = (regs.x.w == 0); +} + +void CPUcore::op_txs_e() { +L op_io_irq(); + regs.s.l = regs.x.l; +} + +void CPUcore::op_txs_n() { +L op_io_irq(); + regs.s.w = regs.x.w; +} + +template void CPUcore::op_push_b() { + op_io(); +L op_writestack(regs.r[n].l); +} + +template void CPUcore::op_push_w() { + op_io(); + op_writestack(regs.r[n].h); +L op_writestack(regs.r[n].l); +} + +void CPUcore::op_phd_e() { + op_io(); + op_writestackn(regs.d.h); +L op_writestackn(regs.d.l); + regs.s.h = 0x01; +} + +void CPUcore::op_phd_n() { + op_io(); + op_writestackn(regs.d.h); +L op_writestackn(regs.d.l); +} + +void CPUcore::op_phb() { + op_io(); +L op_writestack(regs.db); +} + +void CPUcore::op_phk() { + op_io(); +L op_writestack(regs.pc.b); +} + +void CPUcore::op_php() { + op_io(); +L op_writestack(regs.p); +} + +template void CPUcore::op_pull_b() { + op_io(); + op_io(); +L regs.r[n].l = op_readstack(); + regs.p.n = (regs.r[n].l & 0x80); + regs.p.z = (regs.r[n].l == 0); +} + +template void CPUcore::op_pull_w() { + op_io(); + op_io(); + regs.r[n].l = op_readstack(); +L regs.r[n].h = op_readstack(); + regs.p.n = (regs.r[n].w & 0x8000); + regs.p.z = (regs.r[n].w == 0); +} + +void CPUcore::op_pld_e() { + op_io(); + op_io(); + regs.d.l = op_readstackn(); +L regs.d.h = op_readstackn(); + regs.p.n = (regs.d.w & 0x8000); + regs.p.z = (regs.d.w == 0); + regs.s.h = 0x01; +} + +void CPUcore::op_pld_n() { + op_io(); + op_io(); + regs.d.l = op_readstackn(); +L regs.d.h = op_readstackn(); + regs.p.n = (regs.d.w & 0x8000); + regs.p.z = (regs.d.w == 0); +} + +void CPUcore::op_plb() { + op_io(); + op_io(); +L regs.db = op_readstack(); + regs.p.n = (regs.db & 0x80); + regs.p.z = (regs.db == 0); +} + +void CPUcore::op_plp_e() { + op_io(); + op_io(); +L regs.p = op_readstack() | 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + update_table(); +} + +void CPUcore::op_plp_n() { + op_io(); + op_io(); +L regs.p = op_readstack(); + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + update_table(); +} + +void CPUcore::op_pea_e() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writestackn(aa.h); +L op_writestackn(aa.l); + regs.s.h = 0x01; +} + +void CPUcore::op_pea_n() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writestackn(aa.h); +L op_writestackn(aa.l); +} + +void CPUcore::op_pei_e() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_writestackn(aa.h); +L op_writestackn(aa.l); + regs.s.h = 0x01; +} + +void CPUcore::op_pei_n() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_writestackn(aa.h); +L op_writestackn(aa.l); +} + +void CPUcore::op_per_e() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.w = regs.pc.d + (int16)aa.w; + op_writestackn(rd.h); +L op_writestackn(rd.l); + regs.s.h = 0x01; +} + +void CPUcore::op_per_n() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.w = regs.pc.d + (int16)aa.w; + op_writestackn(rd.h); +L op_writestackn(rd.l); +} + +#endif diff --git a/mednafen/snes/src/cpu/core/opcode_pc.cpp b/mednafen/snes/src/cpu/core/opcode_pc.cpp new file mode 100755 index 0000000..3b4543f --- /dev/null +++ b/mednafen/snes/src/cpu/core/opcode_pc.cpp @@ -0,0 +1,181 @@ +#ifdef CPUCORE_CPP + +template void CPUcore::op_branch() { + if((bool)(regs.p & bit) != val) { +L rd.l = op_readpc(); + } else { + rd.l = op_readpc(); + aa.w = regs.pc.d + (int8)rd.l; + op_io_cond6(aa.w); +L op_io(); + regs.pc.w = aa.w; + } +} + +void CPUcore::op_bra() { + rd.l = op_readpc(); + aa.w = regs.pc.d + (int8)rd.l; + op_io_cond6(aa.w); +L op_io(); + regs.pc.w = aa.w; +} + +void CPUcore::op_brl() { + rd.l = op_readpc(); + rd.h = op_readpc(); +L op_io(); + regs.pc.w = regs.pc.d + (int16)rd.w; +} + +void CPUcore::op_jmp_addr() { + rd.l = op_readpc(); +L rd.h = op_readpc(); + regs.pc.w = rd.w; +} + +void CPUcore::op_jmp_long() { + rd.l = op_readpc(); + rd.h = op_readpc(); +L rd.b = op_readpc(); + regs.pc.d = rd.d & 0xffffff; +} + +void CPUcore::op_jmp_iaddr() { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readaddr(aa.w + 0); +L rd.h = op_readaddr(aa.w + 1); + regs.pc.w = rd.w; +} + +void CPUcore::op_jmp_iaddrx() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readpbr(aa.w + regs.x.w + 0); +L rd.h = op_readpbr(aa.w + regs.x.w + 1); + regs.pc.w = rd.w; +} + +void CPUcore::op_jmp_iladdr() { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readaddr(aa.w + 0); + rd.h = op_readaddr(aa.w + 1); +L rd.b = op_readaddr(aa.w + 2); + regs.pc.d = rd.d & 0xffffff; +} + +void CPUcore::op_jsr_addr() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + regs.pc.w--; + op_writestack(regs.pc.h); +L op_writestack(regs.pc.l); + regs.pc.w = aa.w; +} + +void CPUcore::op_jsr_long_e() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writestackn(regs.pc.b); + op_io(); + aa.b = op_readpc(); + regs.pc.w--; + op_writestackn(regs.pc.h); +L op_writestackn(regs.pc.l); + regs.pc.d = aa.d & 0xffffff; + regs.s.h = 0x01; +} + +void CPUcore::op_jsr_long_n() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writestackn(regs.pc.b); + op_io(); + aa.b = op_readpc(); + regs.pc.w--; + op_writestackn(regs.pc.h); +L op_writestackn(regs.pc.l); + regs.pc.d = aa.d & 0xffffff; +} + +void CPUcore::op_jsr_iaddrx_e() { + aa.l = op_readpc(); + op_writestackn(regs.pc.h); + op_writestackn(regs.pc.l); + aa.h = op_readpc(); + op_io(); + rd.l = op_readpbr(aa.w + regs.x.w + 0); +L rd.h = op_readpbr(aa.w + regs.x.w + 1); + regs.pc.w = rd.w; + regs.s.h = 0x01; +} + +void CPUcore::op_jsr_iaddrx_n() { + aa.l = op_readpc(); + op_writestackn(regs.pc.h); + op_writestackn(regs.pc.l); + aa.h = op_readpc(); + op_io(); + rd.l = op_readpbr(aa.w + regs.x.w + 0); +L rd.h = op_readpbr(aa.w + regs.x.w + 1); + regs.pc.w = rd.w; +} + +void CPUcore::op_rti_e() { + op_io(); + op_io(); + regs.p = op_readstack() | 0x30; + rd.l = op_readstack(); +L rd.h = op_readstack(); + regs.pc.w = rd.w; +} + +void CPUcore::op_rti_n() { + op_io(); + op_io(); + regs.p = op_readstack(); + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + rd.l = op_readstack(); + rd.h = op_readstack(); +L rd.b = op_readstack(); + regs.pc.d = rd.d & 0xffffff; + update_table(); +} + +void CPUcore::op_rts() { + op_io(); + op_io(); + rd.l = op_readstack(); + rd.h = op_readstack(); +L op_io(); + regs.pc.w = ++rd.w; +} + +void CPUcore::op_rtl_e() { + op_io(); + op_io(); + rd.l = op_readstackn(); + rd.h = op_readstackn(); +L rd.b = op_readstackn(); + regs.pc.b = rd.b; + regs.pc.w = ++rd.w; + regs.s.h = 0x01; +} + +void CPUcore::op_rtl_n() { + op_io(); + op_io(); + rd.l = op_readstackn(); + rd.h = op_readstackn(); +L rd.b = op_readstackn(); + regs.pc.b = rd.b; + regs.pc.w = ++rd.w; +} + +#endif diff --git a/mednafen/snes/src/cpu/core/opcode_read.cpp b/mednafen/snes/src/cpu/core/opcode_read.cpp new file mode 100755 index 0000000..d539dbc --- /dev/null +++ b/mednafen/snes/src/cpu/core/opcode_read.cpp @@ -0,0 +1,279 @@ +#ifdef CPUCORE_CPP + +template void CPUcore::op_read_const_b() { +L rd.l = op_readpc(); + call(op); +} + +template void CPUcore::op_read_const_w() { + rd.l = op_readpc(); +L rd.h = op_readpc(); + call(op); +} + +void CPUcore::op_read_bit_const_b() { +L rd.l = op_readpc(); + regs.p.z = ((rd.l & regs.a.l) == 0); +} + +void CPUcore::op_read_bit_const_w() { + rd.l = op_readpc(); +L rd.h = op_readpc(); + regs.p.z = ((rd.w & regs.a.w) == 0); +} + +template void CPUcore::op_read_addr_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); +L rd.l = op_readdbr(aa.w); + call(op); +} + +template void CPUcore::op_read_addr_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w + 0); +L rd.h = op_readdbr(aa.w + 1); + call(op); +} + +template void CPUcore::op_read_addrx_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); +L rd.l = op_readdbr(aa.w + regs.x.w); + call(op); +} + +template void CPUcore::op_read_addrx_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + rd.l = op_readdbr(aa.w + regs.x.w + 0); +L rd.h = op_readdbr(aa.w + regs.x.w + 1); + call(op); +} + +template void CPUcore::op_read_addry_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); +L rd.l = op_readdbr(aa.w + regs.y.w); + call(op); +} + +template void CPUcore::op_read_addry_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); + rd.l = op_readdbr(aa.w + regs.y.w + 0); +L rd.h = op_readdbr(aa.w + regs.y.w + 1); + call(op); +} + +template void CPUcore::op_read_long_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); +L rd.l = op_readlong(aa.d); + call(op); +} + +template void CPUcore::op_read_long_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + rd.l = op_readlong(aa.d + 0); +L rd.h = op_readlong(aa.d + 1); + call(op); +} + +template void CPUcore::op_read_longx_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); +L rd.l = op_readlong(aa.d + regs.x.w); + call(op); +} + +template void CPUcore::op_read_longx_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + rd.l = op_readlong(aa.d + regs.x.w + 0); +L rd.h = op_readlong(aa.d + regs.x.w + 1); + call(op); +} + +template void CPUcore::op_read_dp_b() { + dp = op_readpc(); + op_io_cond2(); +L rd.l = op_readdp(dp); + call(op); +} + +template void CPUcore::op_read_dp_w() { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp + 0); +L rd.h = op_readdp(dp + 1); + call(op); +} + +template void CPUcore::op_read_dpr_b() { + dp = op_readpc(); + op_io_cond2(); + op_io(); +L rd.l = op_readdp(dp + regs.r[n].w); + call(op); +} + +template void CPUcore::op_read_dpr_w() { + dp = op_readpc(); + op_io_cond2(); + op_io(); +L rd.l = op_readdp(dp + regs.r[n].w + 0); + rd.h = op_readdp(dp + regs.r[n].w + 1); + call(op); +} + +template void CPUcore::op_read_idp_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); +L rd.l = op_readdbr(aa.w); + call(op); +} + +template void CPUcore::op_read_idp_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + rd.l = op_readdbr(aa.w + 0); +L rd.h = op_readdbr(aa.w + 1); + call(op); +} + +template void CPUcore::op_read_idpx_b() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w + 0); + aa.h = op_readdp(dp + regs.x.w + 1); +L rd.l = op_readdbr(aa.w); + call(op); +} + +template void CPUcore::op_read_idpx_w() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w + 0); + aa.h = op_readdp(dp + regs.x.w + 1); + rd.l = op_readdbr(aa.w + 0); +L rd.h = op_readdbr(aa.w + 1); + call(op); +} + +template void CPUcore::op_read_idpy_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); +L rd.l = op_readdbr(aa.w + regs.y.w); + call(op); +} + +template void CPUcore::op_read_idpy_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); + rd.l = op_readdbr(aa.w + regs.y.w + 0); +L rd.h = op_readdbr(aa.w + regs.y.w + 1); + call(op); +} + +template void CPUcore::op_read_ildp_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); +L rd.l = op_readlong(aa.d); + call(op); +} + +template void CPUcore::op_read_ildp_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + rd.l = op_readlong(aa.d + 0); +L rd.h = op_readlong(aa.d + 1); + call(op); +} + +template void CPUcore::op_read_ildpy_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); +L rd.l = op_readlong(aa.d + regs.y.w); + call(op); +} + +template void CPUcore::op_read_ildpy_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + rd.l = op_readlong(aa.d + regs.y.w + 0); +L rd.h = op_readlong(aa.d + regs.y.w + 1); + call(op); +} + +template void CPUcore::op_read_sr_b() { + sp = op_readpc(); + op_io(); +L rd.l = op_readsp(sp); + call(op); +} + +template void CPUcore::op_read_sr_w() { + sp = op_readpc(); + op_io(); + rd.l = op_readsp(sp + 0); +L rd.h = op_readsp(sp + 1); + call(op); +} + +template void CPUcore::op_read_isry_b() { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp + 0); + aa.h = op_readsp(sp + 1); + op_io(); +L rd.l = op_readdbr(aa.w + regs.y.w); + call(op); +} + +template void CPUcore::op_read_isry_w() { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp + 0); + aa.h = op_readsp(sp + 1); + op_io(); + rd.l = op_readdbr(aa.w + regs.y.w + 0); +L rd.h = op_readdbr(aa.w + regs.y.w + 1); + call(op); +} + +#endif diff --git a/mednafen/snes/src/cpu/core/opcode_rmw.cpp b/mednafen/snes/src/cpu/core/opcode_rmw.cpp new file mode 100755 index 0000000..fed939e --- /dev/null +++ b/mednafen/snes/src/cpu/core/opcode_rmw.cpp @@ -0,0 +1,169 @@ +#ifdef CPUCORE_CPP + +template void CPUcore::op_adjust_imm_b() { +L op_io_irq(); + regs.r[n].l += adjust; + regs.p.n = (regs.r[n].l & 0x80); + regs.p.z = (regs.r[n].l == 0); +} + +template void CPUcore::op_adjust_imm_w() { +L op_io_irq(); + regs.r[n].w += adjust; + regs.p.n = (regs.r[n].w & 0x8000); + regs.p.z = (regs.r[n].w == 0); +} + +void CPUcore::op_asl_imm_b() { +L op_io_irq(); + regs.p.c = (regs.a.l & 0x80); + regs.a.l <<= 1; + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +void CPUcore::op_asl_imm_w() { +L op_io_irq(); + regs.p.c = (regs.a.w & 0x8000); + regs.a.w <<= 1; + regs.p.n = (regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} + +void CPUcore::op_lsr_imm_b() { +L op_io_irq(); + regs.p.c = (regs.a.l & 0x01); + regs.a.l >>= 1; + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +void CPUcore::op_lsr_imm_w() { +L op_io_irq(); + regs.p.c = (regs.a.w & 0x0001); + regs.a.w >>= 1; + regs.p.n = (regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} + +void CPUcore::op_rol_imm_b() { +L op_io_irq(); + bool carry = regs.p.c; + regs.p.c = (regs.a.l & 0x80); + regs.a.l = (regs.a.l << 1) | carry; + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +void CPUcore::op_rol_imm_w() { +L op_io_irq(); + bool carry = regs.p.c; + regs.p.c = (regs.a.w & 0x8000); + regs.a.w = (regs.a.w << 1) | carry; + regs.p.n = (regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} + +void CPUcore::op_ror_imm_b() { +L op_io_irq(); + bool carry = regs.p.c; + regs.p.c = (regs.a.l & 0x01); + regs.a.l = (carry << 7) | (regs.a.l >> 1); + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +void CPUcore::op_ror_imm_w() { +L op_io_irq(); + bool carry = regs.p.c; + regs.p.c = (regs.a.w & 0x0001); + regs.a.w = (carry << 15) | (regs.a.w >> 1); + regs.p.n = (regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} + +template void CPUcore::op_adjust_addr_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w); + op_io(); + call(op); +L op_writedbr(aa.w, rd.l); +} + +template void CPUcore::op_adjust_addr_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w + 0); + rd.h = op_readdbr(aa.w + 1); + op_io(); + call(op); + op_writedbr(aa.w + 1, rd.h); +L op_writedbr(aa.w + 0, rd.l); +} + +template void CPUcore::op_adjust_addrx_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readdbr(aa.w + regs.x.w); + op_io(); + call(op); +L op_writedbr(aa.w + regs.x.w, rd.l); +} + +template void CPUcore::op_adjust_addrx_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readdbr(aa.w + regs.x.w + 0); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_io(); + call(op); + op_writedbr(aa.w + regs.x.w + 1, rd.h); +L op_writedbr(aa.w + regs.x.w + 0, rd.l); +} + +template void CPUcore::op_adjust_dp_b() { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp); + op_io(); + call(op); +L op_writedp(dp, rd.l); +} + +template void CPUcore::op_adjust_dp_w() { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp + 0); + rd.h = op_readdp(dp + 1); + op_io(); + call(op); + op_writedp(dp + 1, rd.h); +L op_writedp(dp + 0, rd.l); +} + +template void CPUcore::op_adjust_dpx_b() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.x.w); + op_io(); + call(op); +L op_writedp(dp + regs.x.w, rd.l); +} + +template void CPUcore::op_adjust_dpx_w() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.x.w + 0); + rd.h = op_readdp(dp + regs.x.w + 1); + op_io(); + call(op); + op_writedp(dp + regs.x.w + 1, rd.h); +L op_writedp(dp + regs.x.w + 0, rd.l); +} + +#endif diff --git a/mednafen/snes/src/cpu/core/opcode_write.cpp b/mednafen/snes/src/cpu/core/opcode_write.cpp new file mode 100755 index 0000000..de85e67 --- /dev/null +++ b/mednafen/snes/src/cpu/core/opcode_write.cpp @@ -0,0 +1,199 @@ +#ifdef CPUCORE_CPP + +template void CPUcore::op_write_addr_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); +L op_writedbr(aa.w, regs.r[n]); +} + +template void CPUcore::op_write_addr_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writedbr(aa.w + 0, regs.r[n] >> 0); +L op_writedbr(aa.w + 1, regs.r[n] >> 8); +} + +template void CPUcore::op_write_addrr_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); +L op_writedbr(aa.w + regs.r[i], regs.r[n]); +} + +template void CPUcore::op_write_addrr_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + op_writedbr(aa.w + regs.r[i] + 0, regs.r[n] >> 0); +L op_writedbr(aa.w + regs.r[i] + 1, regs.r[n] >> 8); +} + +template void CPUcore::op_write_longr_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); +L op_writelong(aa.d + regs.r[i], regs.a.l); +} + +template void CPUcore::op_write_longr_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + op_writelong(aa.d + regs.r[i] + 0, regs.a.l); +L op_writelong(aa.d + regs.r[i] + 1, regs.a.h); +} + +template void CPUcore::op_write_dp_b() { + dp = op_readpc(); + op_io_cond2(); +L op_writedp(dp, regs.r[n]); +} + +template void CPUcore::op_write_dp_w() { + dp = op_readpc(); + op_io_cond2(); + op_writedp(dp + 0, regs.r[n] >> 0); +L op_writedp(dp + 1, regs.r[n] >> 8); +} + +template void CPUcore::op_write_dpr_b() { + dp = op_readpc(); + op_io_cond2(); + op_io(); +L op_writedp(dp + regs.r[i], regs.r[n]); +} + +template void CPUcore::op_write_dpr_w() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + op_writedp(dp + regs.r[i] + 0, regs.r[n] >> 0); +L op_writedp(dp + regs.r[i] + 1, regs.r[n] >> 8); +} + +void CPUcore::op_sta_idp_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); +L op_writedbr(aa.w, regs.a.l); +} + +void CPUcore::op_sta_idp_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_writedbr(aa.w + 0, regs.a.l); +L op_writedbr(aa.w + 1, regs.a.h); +} + +void CPUcore::op_sta_ildp_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); +L op_writelong(aa.d, regs.a.l); +} + +void CPUcore::op_sta_ildp_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + op_writelong(aa.d + 0, regs.a.l); +L op_writelong(aa.d + 1, regs.a.h); +} + +void CPUcore::op_sta_idpx_b() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w + 0); + aa.h = op_readdp(dp + regs.x.w + 1); +L op_writedbr(aa.w, regs.a.l); +} + +void CPUcore::op_sta_idpx_w() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w + 0); + aa.h = op_readdp(dp + regs.x.w + 1); + op_writedbr(aa.w + 0, regs.a.l); +L op_writedbr(aa.w + 1, regs.a.h); +} + +void CPUcore::op_sta_idpy_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_io(); +L op_writedbr(aa.w + regs.y.w, regs.a.l); +} + +void CPUcore::op_sta_idpy_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_io(); + op_writedbr(aa.w + regs.y.w + 0, regs.a.l); +L op_writedbr(aa.w + regs.y.w + 1, regs.a.h); +} + +void CPUcore::op_sta_ildpy_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); +L op_writelong(aa.d + regs.y.w, regs.a.l); +} + +void CPUcore::op_sta_ildpy_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + op_writelong(aa.d + regs.y.w + 0, regs.a.l); +L op_writelong(aa.d + regs.y.w + 1, regs.a.h); +} + +void CPUcore::op_sta_sr_b() { + sp = op_readpc(); + op_io(); +L op_writesp(sp, regs.a.l); +} + +void CPUcore::op_sta_sr_w() { + sp = op_readpc(); + op_io(); + op_writesp(sp + 0, regs.a.l); +L op_writesp(sp + 1, regs.a.h); +} + +void CPUcore::op_sta_isry_b() { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp + 0); + aa.h = op_readsp(sp + 1); + op_io(); +L op_writedbr(aa.w + regs.y.w, regs.a.l); +} + +void CPUcore::op_sta_isry_w() { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp + 0); + aa.h = op_readsp(sp + 1); + op_io(); + op_writedbr(aa.w + regs.y.w + 0, regs.a.l); +L op_writedbr(aa.w + regs.y.w + 1, regs.a.h); +} + +#endif diff --git a/mednafen/snes/src/cpu/core/registers.hpp b/mednafen/snes/src/cpu/core/registers.hpp new file mode 100755 index 0000000..1305d33 --- /dev/null +++ b/mednafen/snes/src/cpu/core/registers.hpp @@ -0,0 +1,81 @@ +struct flag_t { + bool n, v, m, x, d, i, z, c; + + inline operator unsigned() const { + return (n << 7) + (v << 6) + (m << 5) + (x << 4) + + (d << 3) + (i << 2) + (z << 1) + (c << 0); + } + + inline unsigned operator=(uint8 data) { + n = data & 0x80; v = data & 0x40; m = data & 0x20; x = data & 0x10; + d = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01; + return data; + } + + inline unsigned operator|=(unsigned data) { return operator=(operator unsigned() | data); } + inline unsigned operator^=(unsigned data) { return operator=(operator unsigned() ^ data); } + inline unsigned operator&=(unsigned data) { return operator=(operator unsigned() & data); } + + flag_t() : n(0), v(0), m(0), x(0), d(0), i(0), z(0), c(0) {} +}; + +struct reg16_t { + union { + uint16 w; + struct { uint8 order_lsb2(l, h); }; + }; + + inline operator unsigned() const { return w; } + inline unsigned operator = (unsigned i) { return w = i; } + inline unsigned operator |= (unsigned i) { return w |= i; } + inline unsigned operator ^= (unsigned i) { return w ^= i; } + inline unsigned operator &= (unsigned i) { return w &= i; } + inline unsigned operator <<= (unsigned i) { return w <<= i; } + inline unsigned operator >>= (unsigned i) { return w >>= i; } + inline unsigned operator += (unsigned i) { return w += i; } + inline unsigned operator -= (unsigned i) { return w -= i; } + inline unsigned operator *= (unsigned i) { return w *= i; } + inline unsigned operator /= (unsigned i) { return w /= i; } + inline unsigned operator %= (unsigned i) { return w %= i; } + + reg16_t() : w(0) {} +}; + +struct reg24_t { + union { + uint32 d; + struct { uint16 order_lsb2(w, wh); }; + struct { uint8 order_lsb4(l, h, b, bh); }; + }; + + inline operator unsigned() const { return d; } + inline unsigned operator = (unsigned i) { return d = uclip<24>(i); } + inline unsigned operator |= (unsigned i) { return d = uclip<24>(d | i); } + inline unsigned operator ^= (unsigned i) { return d = uclip<24>(d ^ i); } + inline unsigned operator &= (unsigned i) { return d = uclip<24>(d & i); } + inline unsigned operator <<= (unsigned i) { return d = uclip<24>(d << i); } + inline unsigned operator >>= (unsigned i) { return d = uclip<24>(d >> i); } + inline unsigned operator += (unsigned i) { return d = uclip<24>(d + i); } + inline unsigned operator -= (unsigned i) { return d = uclip<24>(d - i); } + inline unsigned operator *= (unsigned i) { return d = uclip<24>(d * i); } + inline unsigned operator /= (unsigned i) { return d = uclip<24>(d / i); } + inline unsigned operator %= (unsigned i) { return d = uclip<24>(d % i); } + + reg24_t() : d(0) {} +}; + +struct regs_t { + reg24_t pc; + reg16_t r[6], &a, &x, &y, &z, &s, &d; + flag_t p; + uint8 db; + bool e; + + bool irq; //IRQ pin (0 = low, 1 = trigger) + bool wai; //raised during wai, cleared after interrupt triggered + uint8 mdr; //memory data register + + regs_t() : a(r[0]), x(r[1]), y(r[2]), z(r[3]), s(r[4]), d(r[5]), db(0), e(false), irq(false), wai(false), mdr(0) { + z = 0; + } +}; diff --git a/mednafen/snes/src/cpu/core/serialization.cpp b/mednafen/snes/src/cpu/core/serialization.cpp new file mode 100755 index 0000000..467765b --- /dev/null +++ b/mednafen/snes/src/cpu/core/serialization.cpp @@ -0,0 +1,36 @@ +#ifdef CPUCORE_CPP + +void CPUcore::core_serialize(serializer &s) { + s.integer(regs.pc.d); + + s.integer(regs.a.w); + s.integer(regs.x.w); + s.integer(regs.y.w); + s.integer(regs.z.w); + s.integer(regs.s.w); + s.integer(regs.d.w); + + s.integer(regs.p.n); + s.integer(regs.p.v); + s.integer(regs.p.m); + s.integer(regs.p.x); + s.integer(regs.p.d); + s.integer(regs.p.i); + s.integer(regs.p.z); + s.integer(regs.p.c); + + s.integer(regs.db); + s.integer(regs.e); + s.integer(regs.irq); + s.integer(regs.wai); + s.integer(regs.mdr); + + s.integer(aa.d); + s.integer(rd.d); + s.integer(sp); + s.integer(dp); + + update_table(); +} + +#endif diff --git a/mednafen/snes/src/cpu/core/table.cpp b/mednafen/snes/src/cpu/core/table.cpp new file mode 100755 index 0000000..2ed22be --- /dev/null +++ b/mednafen/snes/src/cpu/core/table.cpp @@ -0,0 +1,312 @@ +#ifdef CPUCORE_CPP + +void CPUcore::initialize_opcode_table() { + #define opA( id, name ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name; + #define opAII(id, name, x, y ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name; + #define opE( id, name ) op_table[table_EM + id] = &CPUcore::op_##name##_e; op_table[table_MX + id] = op_table[table_Mx + id] = op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_n; + #define opEI( id, name, x ) op_table[table_EM + id] = &CPUcore::op_##name##_e; op_table[table_MX + id] = op_table[table_Mx + id] = op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_n; + #define opEII(id, name, x, y ) op_table[table_EM + id] = &CPUcore::op_##name##_e; op_table[table_MX + id] = op_table[table_Mx + id] = op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_n; + #define opM( id, name ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = &CPUcore::op_##name##_b; op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opMI( id, name, x ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = &CPUcore::op_##name##_b; op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opMII(id, name, x, y ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = &CPUcore::op_##name##_b; op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opMF( id, name, fn ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = &CPUcore::op_##name##_b<&CPUcore::op_##fn##_b>; op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w<&CPUcore::op_##fn##_w>; + #define opMFI(id, name, fn, x) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = &CPUcore::op_##name##_b<&CPUcore::op_##fn##_b, x>; op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w<&CPUcore::op_##fn##_w, x>; + #define opX( id, name ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_mX + id] = &CPUcore::op_##name##_b; op_table[table_Mx + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opXI( id, name, x ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_mX + id] = &CPUcore::op_##name##_b; op_table[table_Mx + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opXII(id, name, x, y ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_mX + id] = &CPUcore::op_##name##_b; op_table[table_Mx + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opXF( id, name, fn ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_mX + id] = &CPUcore::op_##name##_b<&CPUcore::op_##fn##_b>; op_table[table_Mx + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w<&CPUcore::op_##fn##_w>; + #define opXFI(id, name, fn, x) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_mX + id] = &CPUcore::op_##name##_b<&CPUcore::op_##fn##_b, x>; op_table[table_Mx + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w<&CPUcore::op_##fn##_w, x>; + + opEII(0x00, interrupt, 0xfffe, 0xffe6) + opMF (0x01, read_idpx, ora) + opEII(0x02, interrupt, 0xfff4, 0xffe4) + opMF (0x03, read_sr, ora) + opMF (0x04, adjust_dp, tsb) + opMF (0x05, read_dp, ora) + opMF (0x06, adjust_dp, asl) + opMF (0x07, read_ildp, ora) + opA (0x08, php) + opMF (0x09, read_const, ora) + opM (0x0a, asl_imm) + opE (0x0b, phd) + opMF (0x0c, adjust_addr, tsb) + opMF (0x0d, read_addr, ora) + opMF (0x0e, adjust_addr, asl) + opMF (0x0f, read_long, ora) + opAII(0x10, branch, 0x80, false) + opMF (0x11, read_idpy, ora) + opMF (0x12, read_idp, ora) + opMF (0x13, read_isry, ora) + opMF (0x14, adjust_dp, trb) + opMFI(0x15, read_dpr, ora, X) + opMF (0x16, adjust_dpx, asl) + opMF (0x17, read_ildpy, ora) + opAII(0x18, flag, 0x01, 0x00) + opMF (0x19, read_addry, ora) + opMII(0x1a, adjust_imm, A, +1) + opE (0x1b, tcs) + opMF (0x1c, adjust_addr, trb) + opMF (0x1d, read_addrx, ora) + opMF (0x1e, adjust_addrx, asl) + opMF (0x1f, read_longx, ora) + opA (0x20, jsr_addr) + opMF (0x21, read_idpx, and) + opE (0x22, jsr_long) + opMF (0x23, read_sr, and) + opMF (0x24, read_dp, bit) + opMF (0x25, read_dp, and) + opMF (0x26, adjust_dp, rol) + opMF (0x27, read_ildp, and) + opE (0x28, plp) + opMF (0x29, read_const, and) + opM (0x2a, rol_imm) + opE (0x2b, pld) + opMF (0x2c, read_addr, bit) + opMF (0x2d, read_addr, and) + opMF (0x2e, adjust_addr, rol) + opMF (0x2f, read_long, and) + opAII(0x30, branch, 0x80, true) + opMF (0x31, read_idpy, and) + opMF (0x32, read_idp, and) + opMF (0x33, read_isry, and) + opMFI(0x34, read_dpr, bit, X) + opMFI(0x35, read_dpr, and, X) + opMF (0x36, adjust_dpx, rol) + opMF (0x37, read_ildpy, and) + opAII(0x38, flag, 0x01, 0x01) + opMF (0x39, read_addry, and) + opMII(0x3a, adjust_imm, A, -1) + opE (0x3b, tsc) + opMF (0x3c, read_addrx, bit) + opMF (0x3d, read_addrx, and) + opMF (0x3e, adjust_addrx, rol) + opMF (0x3f, read_longx, and) + opE (0x40, rti) + opMF (0x41, read_idpx, eor) + opA (0x42, wdm) + opMF (0x43, read_sr, eor) + opXI (0x44, move, -1) + opMF (0x45, read_dp, eor) + opMF (0x46, adjust_dp, lsr) + opMF (0x47, read_ildp, eor) + opMI (0x48, push, A) + opMF (0x49, read_const, eor) + opM (0x4a, lsr_imm) + opA (0x4b, phk) + opA (0x4c, jmp_addr) + opMF (0x4d, read_addr, eor) + opMF (0x4e, adjust_addr, lsr) + opMF (0x4f, read_long, eor) + opAII(0x50, branch, 0x40, false) + opMF (0x51, read_idpy, eor) + opMF (0x52, read_idp, eor) + opMF (0x53, read_isry, eor) + opXI (0x54, move, +1) + opMFI(0x55, read_dpr, eor, X) + opMF (0x56, adjust_dpx, lsr) + opMF (0x57, read_ildpy, eor) + opAII(0x58, flag, 0x04, 0x00) + opMF (0x59, read_addry, eor) + opXI (0x5a, push, Y) + opAII(0x5b, transfer_w, A, D) + opA (0x5c, jmp_long) + opMF (0x5d, read_addrx, eor) + opMF (0x5e, adjust_addrx, lsr) + opMF (0x5f, read_longx, eor) + opA (0x60, rts) + opMF (0x61, read_idpx, adc) + opE (0x62, per) + opMF (0x63, read_sr, adc) + opMI (0x64, write_dp, Z) + opMF (0x65, read_dp, adc) + opMF (0x66, adjust_dp, ror) + opMF (0x67, read_ildp, adc) + opMI (0x68, pull, A) + opMF (0x69, read_const, adc) + opM (0x6a, ror_imm) + opE (0x6b, rtl) + opA (0x6c, jmp_iaddr) + opMF (0x6d, read_addr, adc) + opMF (0x6e, adjust_addr, ror) + opMF (0x6f, read_long, adc) + opAII(0x70, branch, 0x40, true) + opMF (0x71, read_idpy, adc) + opMF (0x72, read_idp, adc) + opMF (0x73, read_isry, adc) + opMII(0x74, write_dpr, Z, X) + opMFI(0x75, read_dpr, adc, X) + opMF (0x76, adjust_dpx, ror) + opMF (0x77, read_ildpy, adc) + opAII(0x78, flag, 0x04, 0x04) + opMF (0x79, read_addry, adc) + opXI (0x7a, pull, Y) + opAII(0x7b, transfer_w, D, A) + opA (0x7c, jmp_iaddrx) + opMF (0x7d, read_addrx, adc) + opMF (0x7e, adjust_addrx, ror) + opMF (0x7f, read_longx, adc) + opA (0x80, bra) + opM (0x81, sta_idpx) + opA (0x82, brl) + opM (0x83, sta_sr) + opXI (0x84, write_dp, Y) + opMI (0x85, write_dp, A) + opXI (0x86, write_dp, X) + opM (0x87, sta_ildp) + opXII(0x88, adjust_imm, Y, -1) + opM (0x89, read_bit_const) + opMII(0x8a, transfer, X, A) + opA (0x8b, phb) + opXI (0x8c, write_addr, Y) + opMI (0x8d, write_addr, A) + opXI (0x8e, write_addr, X) + opMI (0x8f, write_longr, Z) + opAII(0x90, branch, 0x01, false) + opM (0x91, sta_idpy) + opM (0x92, sta_idp) + opM (0x93, sta_isry) + opXII(0x94, write_dpr, Y, X) + opMII(0x95, write_dpr, A, X) + opXII(0x96, write_dpr, X, Y) + opM (0x97, sta_ildpy) + opMII(0x98, transfer, Y, A) + opMII(0x99, write_addrr, A, Y) + opE (0x9a, txs) + opXII(0x9b, transfer, X, Y) + opMI (0x9c, write_addr, Z) + opMII(0x9d, write_addrr, A, X) + opMII(0x9e, write_addrr, Z, X) + opMI (0x9f, write_longr, X) + opXF (0xa0, read_const, ldy) + opMF (0xa1, read_idpx, lda) + opXF (0xa2, read_const, ldx) + opMF (0xa3, read_sr, lda) + opXF (0xa4, read_dp, ldy) + opMF (0xa5, read_dp, lda) + opXF (0xa6, read_dp, ldx) + opMF (0xa7, read_ildp, lda) + opXII(0xa8, transfer, A, Y) + opMF (0xa9, read_const, lda) + opXII(0xaa, transfer, A, X) + opA (0xab, plb) + opXF (0xac, read_addr, ldy) + opMF (0xad, read_addr, lda) + opXF (0xae, read_addr, ldx) + opMF (0xaf, read_long, lda) + opAII(0xb0, branch, 0x01, true) + opMF (0xb1, read_idpy, lda) + opMF (0xb2, read_idp, lda) + opMF (0xb3, read_isry, lda) + opXFI(0xb4, read_dpr, ldy, X) + opMFI(0xb5, read_dpr, lda, X) + opXFI(0xb6, read_dpr, ldx, Y) + opMF (0xb7, read_ildpy, lda) + opAII(0xb8, flag, 0x40, 0x00) + opMF (0xb9, read_addry, lda) + opX (0xba, tsx) + opXII(0xbb, transfer, Y, X) + opXF (0xbc, read_addrx, ldy) + opMF (0xbd, read_addrx, lda) + opXF (0xbe, read_addry, ldx) + opMF (0xbf, read_longx, lda) + opXF (0xc0, read_const, cpy) + opMF (0xc1, read_idpx, cmp) + opEI (0xc2, pflag, 0) + opMF (0xc3, read_sr, cmp) + opXF (0xc4, read_dp, cpy) + opMF (0xc5, read_dp, cmp) + opMF (0xc6, adjust_dp, dec) + opMF (0xc7, read_ildp, cmp) + opXII(0xc8, adjust_imm, Y, +1) + opMF (0xc9, read_const, cmp) + opXII(0xca, adjust_imm, X, -1) + opA (0xcb, wai) + opXF (0xcc, read_addr, cpy) + opMF (0xcd, read_addr, cmp) + opMF (0xce, adjust_addr, dec) + opMF (0xcf, read_long, cmp) + opAII(0xd0, branch, 0x02, false) + opMF (0xd1, read_idpy, cmp) + opMF (0xd2, read_idp, cmp) + opMF (0xd3, read_isry, cmp) + opE (0xd4, pei) + opMFI(0xd5, read_dpr, cmp, X) + opMF (0xd6, adjust_dpx, dec) + opMF (0xd7, read_ildpy, cmp) + opAII(0xd8, flag, 0x08, 0x00) + opMF (0xd9, read_addry, cmp) + opXI (0xda, push, X) + opA (0xdb, stp) + opA (0xdc, jmp_iladdr) + opMF (0xdd, read_addrx, cmp) + opMF (0xde, adjust_addrx, dec) + opMF (0xdf, read_longx, cmp) + opXF (0xe0, read_const, cpx) + opMF (0xe1, read_idpx, sbc) + opEI (0xe2, pflag, 1) + opMF (0xe3, read_sr, sbc) + opXF (0xe4, read_dp, cpx) + opMF (0xe5, read_dp, sbc) + opMF (0xe6, adjust_dp, inc) + opMF (0xe7, read_ildp, sbc) + opXII(0xe8, adjust_imm, X, +1) + opMF (0xe9, read_const, sbc) + opA (0xea, nop) + opA (0xeb, xba) + opXF (0xec, read_addr, cpx) + opMF (0xed, read_addr, sbc) + opMF (0xee, adjust_addr, inc) + opMF (0xef, read_long, sbc) + opAII(0xf0, branch, 0x02, true) + opMF (0xf1, read_idpy, sbc) + opMF (0xf2, read_idp, sbc) + opMF (0xf3, read_isry, sbc) + opE (0xf4, pea) + opMFI(0xf5, read_dpr, sbc, X) + opMF (0xf6, adjust_dpx, inc) + opMF (0xf7, read_ildpy, sbc) + opAII(0xf8, flag, 0x08, 0x08) + opMF (0xf9, read_addry, sbc) + opXI (0xfa, pull, X) + opA (0xfb, xce) + opE (0xfc, jsr_iaddrx) + opMF (0xfd, read_addrx, sbc) + opMF (0xfe, adjust_addrx, inc) + opMF (0xff, read_longx, sbc) + + #undef opA + #undef opAII + #undef opE + #undef opEI + #undef opEII + #undef opM + #undef opMI + #undef opMII + #undef opMF + #undef opMFI + #undef opX + #undef opXI + #undef opXII + #undef opXF + #undef opXFI +} + +void CPUcore::update_table() { + if(regs.e) { + opcode_table = &op_table[table_EM]; + } else if(regs.p.m) { + if(regs.p.x) { + opcode_table = &op_table[table_MX]; + } else { + opcode_table = &op_table[table_Mx]; + } + } else { + if(regs.p.x) { + opcode_table = &op_table[table_mX]; + } else { + opcode_table = &op_table[table_mx]; + } + } +} + +#endif diff --git a/mednafen/snes/src/cpu/cpu-debugger.cpp b/mednafen/snes/src/cpu/cpu-debugger.cpp new file mode 100755 index 0000000..6dde93c --- /dev/null +++ b/mednafen/snes/src/cpu/cpu-debugger.cpp @@ -0,0 +1,59 @@ +#ifdef CPU_CPP + +bool CPUDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + //internal + if(id == n++) { name = "S-CPU MDR"; value = string::printf("0x%.2x", mdr()); return true; } + + //$2181-2183 + if(id == n++) { name = "$2181-$2183"; value = ""; return true; } + if(id == n++) { name = "WRAM Address"; value = string::printf("0x%.6x", wram_address()); return true; } + + //$4016 + if(id == n++) { name = "$4016"; value = ""; return true; } + if(id == n++) { name = "Joypad Strobe Latch"; value = joypad_strobe_latch(); return true; } + + //$4200 + if(id == n++) { name = "$4200"; value = ""; return true; } + if(id == n++) { name = "NMI Enable"; value = nmi_enable(); return true; } + if(id == n++) { name = "H-IRQ Enable"; value = hirq_enable(); return true; } + if(id == n++) { name = "V-IRQ Enable"; value = virq_enable(); return true; } + if(id == n++) { name = "Auto Joypad Poll"; value = auto_joypad_poll(); return true; } + + //$4201 + if(id == n++) { name = "$4201"; value = ""; return true; } + if(id == n++) { name = "PIO"; value = string::printf("0x%.2x", pio_bits()); return true; } + + //$4202 + if(id == n++) { name = "$4202"; value = ""; return true; } + if(id == n++) { name = "Multiplicand"; value = string::printf("0x%.2x", multiplicand()); return true; } + + //$4203 + if(id == n++) { name = "$4203"; value = ""; return true; } + if(id == n++) { name = "Multiplier"; value = string::printf("0x%.2x", multiplier()); return true; } + + //$4204-$4205 + if(id == n++) { name = "$4204-$4205"; value = ""; return true; } + if(id == n++) { name = "Dividend"; value = string::printf("0x%.4x", dividend()); return true; } + + //$4206 + if(id == n++) { name = "$4206"; value = ""; return true; } + if(id == n++) { name = "Divisor"; value = string::printf("0x%.2x", divisor()); return true; } + + //$4207-$4208 + if(id == n++) { name = "$4207-$4208"; value = ""; return true; } + if(id == n++) { name = "H-Time"; value = string::printf("0x%.4x", htime()); return true; } + + //$4209-$420a + if(id == n++) { name = "$4209-$420a"; value = ""; return true; } + if(id == n++) { name = "V-Time"; value = string::printf("0x%.4x", vtime()); return true; } + + //$420d + if(id == n++) { name = "$420d"; value = ""; return true; } + if(id == n++) { name = "FastROM Enable"; value = fastrom_enable(); return true; } + + return false; +} + +#endif diff --git a/mednafen/snes/src/cpu/cpu-debugger.hpp b/mednafen/snes/src/cpu/cpu-debugger.hpp new file mode 100755 index 0000000..9ed04d0 --- /dev/null +++ b/mednafen/snes/src/cpu/cpu-debugger.hpp @@ -0,0 +1,42 @@ +struct CPUDebugger : ChipDebugger { + bool property(unsigned id, string &name, string &value); + + //internal + virtual unsigned mdr() { return 0; } + + //$2181-2183 + virtual unsigned wram_address() { return 0; } + + //$4016 + virtual bool joypad_strobe_latch() { return 0; } + + //$4200 + virtual bool nmi_enable() { return 0; } + virtual bool hirq_enable() { return 0; } + virtual bool virq_enable() { return 0; } + virtual bool auto_joypad_poll() { return 0; } + + //$4201 + virtual unsigned pio_bits() { return 0; } + + //$4202 + virtual unsigned multiplicand() { return 0; } + + //$4203 + virtual unsigned multiplier() { return 0; } + + //$4204-$4205 + virtual unsigned dividend() { return 0; } + + //$4206 + virtual unsigned divisor() { return 0; } + + //$4207-$4208 + virtual unsigned htime() { return 0; } + + //$4209-$420a + virtual unsigned vtime() { return 0; } + + //$420d + virtual bool fastrom_enable() { return 0; } +}; diff --git a/mednafen/snes/src/cpu/cpu.cpp b/mednafen/snes/src/cpu/cpu.cpp new file mode 100755 index 0000000..df02d31 --- /dev/null +++ b/mednafen/snes/src/cpu/cpu.cpp @@ -0,0 +1,29 @@ +#include <../base.hpp> + +#define CPU_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "cpu-debugger.cpp" +#endif + +void CPU::power() { + cpu_version = config.cpu.version; +} + +void CPU::reset() { + PPUcounter::reset(); +} + +void CPU::serialize(serializer &s) { + PPUcounter::serialize(s); + s.integer(cpu_version); +} + +CPU::CPU() { +} + +CPU::~CPU() { +} + +}; diff --git a/mednafen/snes/src/cpu/cpu.hpp b/mednafen/snes/src/cpu/cpu.hpp new file mode 100755 index 0000000..654c8c6 --- /dev/null +++ b/mednafen/snes/src/cpu/cpu.hpp @@ -0,0 +1,27 @@ +#if defined(DEBUGGER) + #include "cpu-debugger.hpp" +#endif + +class CPU : public PPUcounter, public MMIO { +public: + virtual void enter() = 0; + + //CPU version number + //* 1 and 2 are known + //* reported by $4210 + //* affects timing (DRAM refresh, HDMA init, etc) + uint8 cpu_version; + + virtual uint8 pio() = 0; + virtual bool joylatch() = 0; + virtual uint8 port_read(uint8 port) = 0; + virtual void port_write(uint8 port, uint8 value) = 0; + + virtual void scanline() = 0; + virtual void power(); + virtual void reset(); + + virtual void serialize(serializer&); + CPU(); + virtual ~CPU(); +}; diff --git a/mednafen/snes/src/cpu/scpu/debugger/debugger.cpp b/mednafen/snes/src/cpu/scpu/debugger/debugger.cpp new file mode 100755 index 0000000..5edb616 --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/debugger/debugger.cpp @@ -0,0 +1,88 @@ +#ifdef SCPU_CPP + +void sCPUDebugger::op_step() { + bool break_event = false; + + usage[regs.pc] &= ~(UsageFlagM | UsageFlagX); + usage[regs.pc] |= UsageExec | (regs.p.m << 1) | (regs.p.x << 0); + opcode_pc = regs.pc; + + if(debugger.step_cpu) { + debugger.break_event = Debugger::CPUStep; + scheduler.exit(Scheduler::DebuggerEvent); + } else { + debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Exec, regs.pc, 0x00); + } + + if(step_event) step_event(); + sCPU::op_step(); + scheduler.sync_cpusmp(); +} + +uint8 sCPUDebugger::op_read(uint32 addr) { + uint8 data = sCPU::op_read(addr); + usage[addr] |= UsageRead; + debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Read, addr, data); + return data; +} + +void sCPUDebugger::op_write(uint32 addr, uint8 data) { + sCPU::op_write(addr, data); + usage[addr] |= UsageWrite; + usage[addr] &= ~UsageExec; + debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Write, addr, data); +} + +sCPUDebugger::sCPUDebugger() { + usage = new uint8[1 << 24](); + opcode_pc = 0x8000; +} + +sCPUDebugger::~sCPUDebugger() { + delete[] usage; +} + +//=========== +//CPUDebugger +//=========== + +//internal +unsigned sCPUDebugger::mdr() { return regs.mdr; } + +//$2181-$2183 +unsigned sCPUDebugger::wram_address() { return status.wram_addr; } + +//$4016 +bool sCPUDebugger::joypad_strobe_latch() { return status.joypad_strobe_latch; } + +//$4200 +bool sCPUDebugger::nmi_enable() { return status.nmi_enabled; } +bool sCPUDebugger::hirq_enable() { return status.hirq_enabled; } +bool sCPUDebugger::virq_enable() { return status.virq_enabled; } +bool sCPUDebugger::auto_joypad_poll() { return status.auto_joypad_poll; } + +//$4201 +unsigned sCPUDebugger::pio_bits() { return status.pio; } + +//$4202 +unsigned sCPUDebugger::multiplicand() { return status.mul_a; } + +//$4203 +unsigned sCPUDebugger::multiplier() { return status.mul_b; } + +//$4204-$4205 +unsigned sCPUDebugger::dividend() { return status.div_a; } + +//$4206 +unsigned sCPUDebugger::divisor() { return status.div_b; } + +//$4207-$4208 +unsigned sCPUDebugger::htime() { return status.hirq_pos; } + +//$4209-$420a +unsigned sCPUDebugger::vtime() { return status.virq_pos; } + +//$420d +bool sCPUDebugger::fastrom_enable() { return status.rom_speed; } + +#endif diff --git a/mednafen/snes/src/cpu/scpu/debugger/debugger.hpp b/mednafen/snes/src/cpu/scpu/debugger/debugger.hpp new file mode 100755 index 0000000..c3e1d1e --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/debugger/debugger.hpp @@ -0,0 +1,64 @@ +class sCPUDebugger : public sCPU, public CPUDebugger { +public: + function step_event; + + enum Usage { + UsageRead = 0x80, + UsageWrite = 0x40, + UsageExec = 0x20, + UsageFlagM = 0x02, + UsageFlagX = 0x01, + }; + uint8 *usage; + uint32 opcode_pc; //points to the current opcode, used to backtrace on read/write breakpoints + + void op_step(); + uint8 op_read(uint32 addr); + void op_write(uint32 addr, uint8 data); + + sCPUDebugger(); + ~sCPUDebugger(); + + //=========== + //CPUDebugger + //=========== + + //internal + unsigned mdr(); + + //$2181-$2183 + unsigned wram_address(); + + //$4016 + bool joypad_strobe_latch(); + + //$4200 + bool nmi_enable(); + bool hirq_enable(); + bool virq_enable(); + bool auto_joypad_poll(); + + //$4201 + unsigned pio_bits(); + + //$4202 + unsigned multiplicand(); + + //$4203 + unsigned multiplier(); + + //$4204-$4205 + unsigned dividend(); + + //$4206 + unsigned divisor(); + + //$4207-$4208 + unsigned htime(); + + //$4209-$420a + unsigned vtime(); + + //$420d + bool fastrom_enable(); +}; diff --git a/mednafen/snes/src/cpu/scpu/dma/dma.cpp b/mednafen/snes/src/cpu/scpu/dma/dma.cpp new file mode 100755 index 0000000..02e238f --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/dma/dma.cpp @@ -0,0 +1,272 @@ +#ifdef SCPU_CPP + +void sCPU::dma_add_clocks(unsigned clocks) { + status.dma_clocks += clocks; + add_clocks(clocks); + scheduler.sync_cpucop(); + scheduler.sync_cpuppu(); +} + +bool sCPU::dma_addr_valid(uint32 abus) { + //reads from B-bus or S-CPU registers are invalid + if((abus & 0x40ff00) == 0x2100) return false; //$[00-3f|80-bf]:[2100-21ff] + if((abus & 0x40fe00) == 0x4000) return false; //$[00-3f|80-bf]:[4000-41ff] + if((abus & 0x40ffe0) == 0x4200) return false; //$[00-3f|80-bf]:[4200-421f] + if((abus & 0x40ff80) == 0x4300) return false; //$[00-3f|80-bf]:[4300-437f] + return true; +} + +uint8 sCPU::dma_read(uint32 abus) { + if(dma_addr_valid(abus) == false) return 0x00; //does not return S-CPU MDR + return bus.read(abus); +} + +void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) { + if(direction == 0) { + //a->b transfer (to $21xx) + if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) { + //illegal WRAM->WRAM transfer (bus conflict) + //read most likely occurs; no write occurs + //read is irrelevent, as it cannot be observed by software + dma_add_clocks(8); + } else { + dma_add_clocks(4); + uint8 data = dma_read(abus); + dma_add_clocks(4); + bus.write(0x2100 | bbus, data); + } + } else { + //b->a transfer (from $21xx) + if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) { + //illegal WRAM->WRAM transfer (bus conflict) + //no read occurs; write does occur + dma_add_clocks(8); + bus.write(abus, 0x00); //does not write S-CPU MDR + } else { + dma_add_clocks(4); + uint8 data = bus.read(0x2100 | bbus); + dma_add_clocks(4); + if(dma_addr_valid(abus) == true) { + bus.write(abus, data); + } + } + } +} + +/***** + * address calculation functions + *****/ + +uint8 sCPU::dma_bbus(uint8 i, uint8 index) { + switch(channel[i].xfermode) { default: + case 0: return (channel[i].destaddr); //0 + case 1: return (channel[i].destaddr + (index & 1)); //0,1 + case 2: return (channel[i].destaddr); //0,0 + case 3: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 + case 4: return (channel[i].destaddr + (index & 3)); //0,1,2,3 + case 5: return (channel[i].destaddr + (index & 1)); //0,1,0,1 + case 6: return (channel[i].destaddr); //0,0 [2] + case 7: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 [3] + } +} + +inline uint32 sCPU::dma_addr(uint8 i) { + uint32 r = (channel[i].srcbank << 16) | (channel[i].srcaddr); + + if(channel[i].fixedxfer == false) { + if(channel[i].reversexfer == false) { + channel[i].srcaddr++; + } else { + channel[i].srcaddr--; + } + } + + return r; +} + +inline uint32 sCPU::hdma_addr(uint8 i) { + return (channel[i].srcbank << 16) | (channel[i].hdma_addr++); +} + +inline uint32 sCPU::hdma_iaddr(uint8 i) { + return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++); +} + +/***** + * DMA functions + *****/ + +uint8 sCPU::dma_enabled_channels() { + uint8 r = 0; + for(unsigned i = 0; i < 8; i++) { + if(channel[i].dma_enabled) r++; + } + return r; +} + +void sCPU::dma_run() { + dma_add_clocks(8); + cycle_edge(); + + for(unsigned i = 0; i < 8; i++) { + if(channel[i].dma_enabled == false) continue; + dma_add_clocks(8); + cycle_edge(); + + unsigned index = 0; + do { + dma_transfer(channel[i].direction, dma_bbus(i, index++), dma_addr(i)); + cycle_edge(); + } while(channel[i].dma_enabled && --channel[i].xfersize); + + channel[i].dma_enabled = false; + } + + status.irq_lock = true; + event.enqueue(2, EventIrqLockRelease); +} + +/***** + * HDMA functions + *****/ + +inline bool sCPU::hdma_active(uint8 i) { + return (channel[i].hdma_enabled && !channel[i].hdma_completed); +} + +inline bool sCPU::hdma_active_after(uint8 i) { + for(unsigned n = i + 1; n < 8; n++) { + if(hdma_active(n) == true) return true; + } + return false; +} + +inline uint8 sCPU::hdma_enabled_channels() { + uint8 r = 0; + for(unsigned i = 0; i < 8; i++) { + if(channel[i].hdma_enabled) r++; + } + return r; +} + +inline uint8 sCPU::hdma_active_channels() { + uint8 r = 0; + for(unsigned i = 0; i < 8; i++) { + if(hdma_active(i) == true) r++; + } + return r; +} + +void sCPU::hdma_update(uint8 i) { + channel[i].hdma_line_counter = dma_read(hdma_addr(i)); + dma_add_clocks(8); + + channel[i].hdma_completed = (channel[i].hdma_line_counter == 0); + channel[i].hdma_do_transfer = !channel[i].hdma_completed; + + if(channel[i].hdma_indirect) { + channel[i].hdma_iaddr = dma_read(hdma_addr(i)) << 8; + dma_add_clocks(8); + + if(!channel[i].hdma_completed || hdma_active_after(i)) { + channel[i].hdma_iaddr >>= 8; + channel[i].hdma_iaddr |= dma_read(hdma_addr(i)) << 8; + dma_add_clocks(8); + } + } +} + +void sCPU::hdma_run() { + dma_add_clocks(8); + + for(unsigned i = 0; i < 8; i++) { + if(hdma_active(i) == false) continue; + channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer + + if(channel[i].hdma_do_transfer) { + static const unsigned transfer_length[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; + unsigned length = transfer_length[channel[i].xfermode]; + for(unsigned index = 0; index < length; index++) { + unsigned addr = !channel[i].hdma_indirect ? hdma_addr(i) : hdma_iaddr(i); + dma_transfer(channel[i].direction, dma_bbus(i, index), addr); + } + } + } + + for(unsigned i = 0; i < 8; i++) { + if(hdma_active(i) == false) continue; + + channel[i].hdma_line_counter--; + channel[i].hdma_do_transfer = bool(channel[i].hdma_line_counter & 0x80); + if((channel[i].hdma_line_counter & 0x7f) == 0) { + hdma_update(i); + } else { + dma_add_clocks(8); + } + } + + status.irq_lock = true; + event.enqueue(2, EventIrqLockRelease); +} + +void sCPU::hdma_init_reset() { + for(unsigned i = 0; i < 8; i++) { + channel[i].hdma_completed = false; + channel[i].hdma_do_transfer = false; + } +} + +void sCPU::hdma_init() { + dma_add_clocks(8); + + for(unsigned i = 0; i < 8; i++) { + if(!channel[i].hdma_enabled) continue; + channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer + + channel[i].hdma_addr = channel[i].srcaddr; + hdma_update(i); + } + + status.irq_lock = true; + event.enqueue(2, EventIrqLockRelease); +} + +/***** + * power / reset functions + *****/ + +void sCPU::dma_power() { + for(unsigned i = 0; i < 8; i++) { + channel[i].dmap = 0xff; + channel[i].direction = 1; + channel[i].hdma_indirect = true; + channel[i].reversexfer = true; + channel[i].fixedxfer = true; + channel[i].xfermode = 7; + + channel[i].destaddr = 0xff; + + channel[i].srcaddr = 0xffff; + channel[i].srcbank = 0xff; + + channel[i].xfersize = 0xffff; + //channel[i].hdma_iaddr = 0xffff; //union with xfersize + channel[i].hdma_ibank = 0xff; + + channel[i].hdma_addr = 0xffff; + channel[i].hdma_line_counter = 0xff; + channel[i].unknown = 0xff; + } +} + +void sCPU::dma_reset() { + for(unsigned i = 0; i < 8; i++) { + channel[i].dma_enabled = false; + channel[i].hdma_enabled = false; + + channel[i].hdma_completed = false; + channel[i].hdma_do_transfer = false; + } +} + +#endif diff --git a/mednafen/snes/src/cpu/scpu/dma/dma.hpp b/mednafen/snes/src/cpu/scpu/dma/dma.hpp new file mode 100755 index 0000000..5ebfdb0 --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/dma/dma.hpp @@ -0,0 +1,71 @@ + struct { + //$420b + bool dma_enabled; + + //$420c + bool hdma_enabled; + + //$43x0 + uint8 dmap; + bool direction; + bool hdma_indirect; + bool reversexfer; + bool fixedxfer; + uint8 xfermode; + + //$43x1 + uint8 destaddr; + + //$43x2-$43x3 + uint16 srcaddr; + + //$43x4 + uint8 srcbank; + + //$43x5-$43x6 + union { + uint16 xfersize; + uint16 hdma_iaddr; + }; + + //$43x7 + uint8 hdma_ibank; + + //$43x8-$43x9 + uint16 hdma_addr; + + //$43xa + uint8 hdma_line_counter; + + //$43xb/$43xf + uint8 unknown; + + //internal variables + bool hdma_completed; + bool hdma_do_transfer; + } channel[8]; + + void dma_add_clocks(unsigned clocks); + bool dma_addr_valid(uint32 abus); + uint8 dma_read(uint32 abus); + void dma_transfer(bool direction, uint8 bbus, uint32 abus); + + uint8 dma_bbus(uint8 i, uint8 index); + uint32 dma_addr(uint8 i); + uint32 hdma_addr(uint8 i); + uint32 hdma_iaddr(uint8 i); + + uint8 dma_enabled_channels(); + void dma_run(); + + bool hdma_active(uint8 i); + bool hdma_active_after(uint8 i); + uint8 hdma_enabled_channels(); + uint8 hdma_active_channels(); + void hdma_update(uint8 i); + void hdma_run(); + void hdma_init_reset(); + void hdma_init(); + + void dma_power(); + void dma_reset(); diff --git a/mednafen/snes/src/cpu/scpu/memory/memory.cpp b/mednafen/snes/src/cpu/scpu/memory/memory.cpp new file mode 100755 index 0000000..c3608f9 --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/memory/memory.cpp @@ -0,0 +1,35 @@ +#ifdef SCPU_CPP + +void sCPU::op_io() { + status.clock_count = 6; + cycle_edge(); + add_clocks(6); +} + +uint8 sCPU::op_read(uint32 addr) { + status.clock_count = speed(addr); + cycle_edge(); + add_clocks(status.clock_count - 4); + regs.mdr = bus.read(addr); + add_clocks(4); + return regs.mdr; +} + +void sCPU::op_write(uint32 addr, uint8 data) { + status.clock_count = speed(addr); + cycle_edge(); + add_clocks(status.clock_count); + bus.write(addr, regs.mdr = data); +} + +unsigned sCPU::speed(unsigned addr) const { + if(addr & 0x408000) { + if(addr & 0x800000) return status.rom_speed; + return 8; + } + if((addr + 0x6000) & 0x4000) return 8; + if((addr - 0x4000) & 0x7e00) return 6; + return 12; +} + +#endif diff --git a/mednafen/snes/src/cpu/scpu/memory/memory.hpp b/mednafen/snes/src/cpu/scpu/memory/memory.hpp new file mode 100755 index 0000000..43d296d --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/memory/memory.hpp @@ -0,0 +1,16 @@ +//============================ +//CPU<>APU communication ports +//============================ + +uint8 apu_port[4]; +uint8 port_read(uint8 port) { return apu_port[port & 3]; } +void port_write(uint8 port, uint8 data) { apu_port[port & 3] = data; } + +//====================== +//core CPU bus functions +//====================== + +void op_io(); +debugvirtual uint8 op_read(uint32 addr); +debugvirtual void op_write(uint32 addr, uint8 data); +alwaysinline unsigned speed(unsigned addr) const; diff --git a/mednafen/snes/src/cpu/scpu/mmio/mmio.cpp b/mednafen/snes/src/cpu/scpu/mmio/mmio.cpp new file mode 100755 index 0000000..046bebb --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/mmio/mmio.cpp @@ -0,0 +1,537 @@ +#ifdef SCPU_CPP + +uint8 sCPU::pio() { return status.pio; } +bool sCPU::joylatch() { return status.joypad_strobe_latch; } + +//WMDATA +uint8 sCPU::mmio_r2180() { + uint8 r = bus.read(0x7e0000 | status.wram_addr); + status.wram_addr = (status.wram_addr + 1) & 0x01ffff; + return r; +} + +//WMDATA +void sCPU::mmio_w2180(uint8 data) { + bus.write(0x7e0000 | status.wram_addr, data); + status.wram_addr = (status.wram_addr + 1) & 0x01ffff; +} + +//WMADDL +void sCPU::mmio_w2181(uint8 data) { + status.wram_addr = (status.wram_addr & 0xffff00) | (data); + status.wram_addr &= 0x01ffff; +} + +//WMADDM +void sCPU::mmio_w2182(uint8 data) { + status.wram_addr = (status.wram_addr & 0xff00ff) | (data << 8); + status.wram_addr &= 0x01ffff; +} + +//WMADDH +void sCPU::mmio_w2183(uint8 data) { + status.wram_addr = (status.wram_addr & 0x00ffff) | (data << 16); + status.wram_addr &= 0x01ffff; +} + +//JOYSER0 +//bit 0 is shared between JOYSER0 and JOYSER1, therefore +//strobing $4016.d0 affects both controller port latches. +//$4017 bit 0 writes are ignored. +void sCPU::mmio_w4016(uint8 data) { + status.joypad_strobe_latch = !!(data & 1); + + if(status.joypad_strobe_latch == 1) { + input.poll(); + } +} + +//JOYSER0 +//7-2 = MDR +//1-0 = Joypad serial data +// +//TODO: test whether strobe latch of zero returns +//realtime or buffered status of joypadN.b +uint8 sCPU::mmio_r4016() { + uint8 r = regs.mdr & 0xfc; + r |= input.port_read(0) & 3; + return r; +} + +//JOYSER1 +//7-5 = MDR +//4-2 = Always 1 (pins are connected to GND) +//1-0 = Joypad serial data +uint8 sCPU::mmio_r4017() { + uint8 r = (regs.mdr & 0xe0) | 0x1c; + r |= input.port_read(1) & 3; + return r; +} + +//NMITIMEN +void sCPU::mmio_w4200(uint8 data) { + status.auto_joypad_poll = !!(data & 0x01); + nmitimen_update(data); +} + +//WRIO +void sCPU::mmio_w4201(uint8 data) { + if((status.pio & 0x80) && !(data & 0x80)) { + ppu.latch_counters(); + } + status.pio = data; +} + +//WRMPYA +void sCPU::mmio_w4202(uint8 data) { + status.mul_a = data; +} + +//WRMPYB +void sCPU::mmio_w4203(uint8 data) { + status.mul_b = data; + status.r4216 = status.mul_a * status.mul_b; + + status.alu_lock = true; + event.enqueue(config.cpu.alu_mul_delay, EventAluLockRelease); +} + +//WRDIVL +void sCPU::mmio_w4204(uint8 data) { + status.div_a = (status.div_a & 0xff00) | (data); +} + +//WRDIVH +void sCPU::mmio_w4205(uint8 data) { + status.div_a = (status.div_a & 0x00ff) | (data << 8); +} + +//WRDIVB +void sCPU::mmio_w4206(uint8 data) { + status.div_b = data; + status.r4214 = (status.div_b) ? status.div_a / status.div_b : 0xffff; + status.r4216 = (status.div_b) ? status.div_a % status.div_b : status.div_a; + + status.alu_lock = true; + event.enqueue(config.cpu.alu_div_delay, EventAluLockRelease); +} + +//HTIMEL +void sCPU::mmio_w4207(uint8 data) { + status.hirq_pos = (status.hirq_pos & ~0xff) | (data); + status.hirq_pos &= 0x01ff; +} + +//HTIMEH +void sCPU::mmio_w4208(uint8 data) { + status.hirq_pos = (status.hirq_pos & 0xff) | (data << 8); + status.hirq_pos &= 0x01ff; +} + +//VTIMEL +void sCPU::mmio_w4209(uint8 data) { + status.virq_pos = (status.virq_pos & ~0xff) | (data); + status.virq_pos &= 0x01ff; +} + +//VTIMEH +void sCPU::mmio_w420a(uint8 data) { + status.virq_pos = (status.virq_pos & 0xff) | (data << 8); + status.virq_pos &= 0x01ff; +} + +//DMAEN +void sCPU::mmio_w420b(uint8 data) { + for(unsigned i = 0; i < 8; i++) { + channel[i].dma_enabled = data & (1 << i); + } + if(data) status.dma_pending = true; +} + +//HDMAEN +void sCPU::mmio_w420c(uint8 data) { + for(unsigned i = 0; i < 8; i++) { + channel[i].hdma_enabled = data & (1 << i); + } +} + +//MEMSEL +void sCPU::mmio_w420d(uint8 data) { + status.rom_speed = (data & 1 ? 6 : 8); +} + +//RDNMI +//7 = NMI acknowledge +//6-4 = MDR +//3-0 = CPU (5a22) version +uint8 sCPU::mmio_r4210() { + uint8 r = (regs.mdr & 0x70); + r |= (uint8)(rdnmi()) << 7; + r |= (cpu_version & 0x0f); + return r; +} + +//TIMEUP +//7 = IRQ acknowledge +//6-0 = MDR +uint8 sCPU::mmio_r4211() { + uint8 r = (regs.mdr & 0x7f); + r |= (uint8)(timeup()) << 7; + return r; +} + +//HVBJOY +//7 = VBLANK acknowledge +//6 = HBLANK acknowledge +//5-1 = MDR +//0 = JOYPAD acknowledge +uint8 sCPU::mmio_r4212() { + uint8 r = (regs.mdr & 0x3e); + uint16 vs = ppu.overscan() == false ? 225 : 240; + + //auto joypad polling + if(vcounter() >= vs && vcounter() <= (vs + 2))r |= 0x01; + + //hblank + if(hcounter() <= 2 || hcounter() >= 1096)r |= 0x40; + + //vblank + if(vcounter() >= vs)r |= 0x80; + + return r; +} + +//RDIO +uint8 sCPU::mmio_r4213() { + return status.pio; +} + +//RDDIVL +uint8 sCPU::mmio_r4214() { + if(status.alu_lock) return 0; + return status.r4214; +} + +//RDDIVH +uint8 sCPU::mmio_r4215() { + if(status.alu_lock) return 0; + return status.r4214 >> 8; +} + +//RDMPYL +uint8 sCPU::mmio_r4216() { + if(status.alu_lock) return 0; + return status.r4216; +} + +//RDMPYH +uint8 sCPU::mmio_r4217() { + if(status.alu_lock) return 0; + return status.r4216 >> 8; +} + +//TODO: handle reads during joypad polling (v=225-227) +uint8 sCPU::mmio_r4218() { return status.joy1l; } //JOY1L +uint8 sCPU::mmio_r4219() { return status.joy1h; } //JOY1H +uint8 sCPU::mmio_r421a() { return status.joy2l; } //JOY2L +uint8 sCPU::mmio_r421b() { return status.joy2h; } //JOY2H +uint8 sCPU::mmio_r421c() { return status.joy3l; } //JOY3L +uint8 sCPU::mmio_r421d() { return status.joy3h; } //JOY3H +uint8 sCPU::mmio_r421e() { return status.joy4l; } //JOY4L +uint8 sCPU::mmio_r421f() { return status.joy4h; } //JOY4H + +//DMAPx +uint8 sCPU::mmio_r43x0(uint8 i) { + return channel[i].dmap; +} + +//BBADx +uint8 sCPU::mmio_r43x1(uint8 i) { + return channel[i].destaddr; +} + +//A1TxL +uint8 sCPU::mmio_r43x2(uint8 i) { + return channel[i].srcaddr; +} + +//A1TxH +uint8 sCPU::mmio_r43x3(uint8 i) { + return channel[i].srcaddr >> 8; +} + +//A1Bx +uint8 sCPU::mmio_r43x4(uint8 i) { + return channel[i].srcbank; +} + +//DASxL +//union { uint16 xfersize; uint16 hdma_iaddr; }; +uint8 sCPU::mmio_r43x5(uint8 i) { + return channel[i].xfersize; +} + +//DASxH +//union { uint16 xfersize; uint16 hdma_iaddr; }; +uint8 sCPU::mmio_r43x6(uint8 i) { + return channel[i].xfersize >> 8; +} + +//DASBx +uint8 sCPU::mmio_r43x7(uint8 i) { + return channel[i].hdma_ibank; +} + +//A2AxL +uint8 sCPU::mmio_r43x8(uint8 i) { + return channel[i].hdma_addr; +} + +//A2AxH +uint8 sCPU::mmio_r43x9(uint8 i) { + return channel[i].hdma_addr >> 8; +} + +//NTRLx +uint8 sCPU::mmio_r43xa(uint8 i) { + return channel[i].hdma_line_counter; +} + +//??? +uint8 sCPU::mmio_r43xb(uint8 i) { + return channel[i].unknown; +} + +//DMAPx +void sCPU::mmio_w43x0(uint8 i, uint8 data) { + channel[i].dmap = data; + channel[i].direction = !!(data & 0x80); + channel[i].hdma_indirect = !!(data & 0x40); + channel[i].reversexfer = !!(data & 0x10); + channel[i].fixedxfer = !!(data & 0x08); + channel[i].xfermode = data & 7; +} + +//DDBADx +void sCPU::mmio_w43x1(uint8 i, uint8 data) { + channel[i].destaddr = data; +} + +//A1TxL +void sCPU::mmio_w43x2(uint8 i, uint8 data) { + channel[i].srcaddr = (channel[i].srcaddr & 0xff00) | (data); +} + +//A1TxH +void sCPU::mmio_w43x3(uint8 i, uint8 data) { + channel[i].srcaddr = (channel[i].srcaddr & 0x00ff) | (data << 8); +} + +//A1Bx +void sCPU::mmio_w43x4(uint8 i, uint8 data) { + channel[i].srcbank = data; +} + +//DASxL +//union { uint16 xfersize; uint16 hdma_iaddr; }; +void sCPU::mmio_w43x5(uint8 i, uint8 data) { + channel[i].xfersize = (channel[i].xfersize & 0xff00) | (data); +} + +//DASxH +//union { uint16 xfersize; uint16 hdma_iaddr; }; +void sCPU::mmio_w43x6(uint8 i, uint8 data) { + channel[i].xfersize = (channel[i].xfersize & 0x00ff) | (data << 8); +} + +//DASBx +void sCPU::mmio_w43x7(uint8 i, uint8 data) { + channel[i].hdma_ibank = data; +} + +//A2AxL +void sCPU::mmio_w43x8(uint8 i, uint8 data) { + channel[i].hdma_addr = (channel[i].hdma_addr & 0xff00) | (data); +} + +//A2AxH +void sCPU::mmio_w43x9(uint8 i, uint8 data) { + channel[i].hdma_addr = (channel[i].hdma_addr & 0x00ff) | (data << 8); +} + +//NTRLx +void sCPU::mmio_w43xa(uint8 i, uint8 data) { + channel[i].hdma_line_counter = data; +} + +//??? +void sCPU::mmio_w43xb(uint8 i, uint8 data) { + channel[i].unknown = data; +} + +void sCPU::mmio_power() { +} + +void sCPU::mmio_reset() { + //$2181-$2183 + status.wram_addr = 0x000000; + + //$4016-$4017 + status.joypad_strobe_latch = 0; + status.joypad1_bits = ~0; + status.joypad2_bits = ~0; + + //$4200 + status.nmi_enabled = false; + status.hirq_enabled = false; + status.virq_enabled = false; + status.auto_joypad_poll = false; + + //$4201 + status.pio = 0xff; + + //$4202-$4203 + status.mul_a = 0xff; + status.mul_b = 0xff; + + //$4204-$4206 + status.div_a = 0xffff; + status.div_b = 0xff; + + //$4207-$420a + status.hirq_pos = 0x01ff; + status.virq_pos = 0x01ff; + + //$420d + status.rom_speed = 8; + + //$4214-$4217 + status.r4214 = 0x0000; + status.r4216 = 0x0000; + + //$4218-$421f + status.joy1l = 0x00; + status.joy1h = 0x00; + status.joy2l = 0x00; + status.joy2h = 0x00; + status.joy3l = 0x00; + status.joy3h = 0x00; + status.joy4l = 0x00; + status.joy4h = 0x00; +} + +uint8 sCPU::mmio_read(unsigned addr) { + addr &= 0xffff; + + //APU + if((addr & 0xffc0) == 0x2140) { //$2140-$217f + scheduler.sync_cpusmp(); + return smp.port_read(addr & 3); + } + + //DMA + if((addr & 0xff80) == 0x4300) { //$4300-$437f + unsigned i = (addr >> 4) & 7; + switch(addr & 0xf) { + case 0x0: return mmio_r43x0(i); + case 0x1: return mmio_r43x1(i); + case 0x2: return mmio_r43x2(i); + case 0x3: return mmio_r43x3(i); + case 0x4: return mmio_r43x4(i); + case 0x5: return mmio_r43x5(i); + case 0x6: return mmio_r43x6(i); + case 0x7: return mmio_r43x7(i); + case 0x8: return mmio_r43x8(i); + case 0x9: return mmio_r43x9(i); + case 0xa: return mmio_r43xa(i); + case 0xb: return mmio_r43xb(i); + case 0xc: return regs.mdr; //unmapped + case 0xd: return regs.mdr; //unmapped + case 0xe: return regs.mdr; //unmapped + case 0xf: return mmio_r43xb(i); //mirror of $43xb + } + } + + switch(addr) { + case 0x2180: return mmio_r2180(); + case 0x4016: return mmio_r4016(); + case 0x4017: return mmio_r4017(); + case 0x4210: return mmio_r4210(); + case 0x4211: return mmio_r4211(); + case 0x4212: return mmio_r4212(); + case 0x4213: return mmio_r4213(); + case 0x4214: return mmio_r4214(); + case 0x4215: return mmio_r4215(); + case 0x4216: return mmio_r4216(); + case 0x4217: return mmio_r4217(); + case 0x4218: return mmio_r4218(); + case 0x4219: return mmio_r4219(); + case 0x421a: return mmio_r421a(); + case 0x421b: return mmio_r421b(); + case 0x421c: return mmio_r421c(); + case 0x421d: return mmio_r421d(); + case 0x421e: return mmio_r421e(); + case 0x421f: return mmio_r421f(); + } + + return regs.mdr; +} + +void sCPU::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + //APU + if((addr & 0xffc0) == 0x2140) { //$2140-$217f + scheduler.sync_cpusmp(); + port_write(addr & 3, data); + return; + } + + //DMA + if((addr & 0xff80) == 0x4300) { //$4300-$437f + unsigned i = (addr >> 4) & 7; + switch(addr & 0xf) { + case 0x0: mmio_w43x0(i, data); return; + case 0x1: mmio_w43x1(i, data); return; + case 0x2: mmio_w43x2(i, data); return; + case 0x3: mmio_w43x3(i, data); return; + case 0x4: mmio_w43x4(i, data); return; + case 0x5: mmio_w43x5(i, data); return; + case 0x6: mmio_w43x6(i, data); return; + case 0x7: mmio_w43x7(i, data); return; + case 0x8: mmio_w43x8(i, data); return; + case 0x9: mmio_w43x9(i, data); return; + case 0xa: mmio_w43xa(i, data); return; + case 0xb: mmio_w43xb(i, data); return; + case 0xc: return; //unmapped + case 0xd: return; //unmapped + case 0xe: return; //unmapped + case 0xf: mmio_w43xb(i, data); return; //mirror of $43xb + } + } + + switch(addr) { + case 0x2180: mmio_w2180(data); return; + case 0x2181: mmio_w2181(data); return; + case 0x2182: mmio_w2182(data); return; + case 0x2183: mmio_w2183(data); return; + case 0x4016: mmio_w4016(data); return; + case 0x4017: return; //unmapped + case 0x4200: mmio_w4200(data); return; + case 0x4201: mmio_w4201(data); return; + case 0x4202: mmio_w4202(data); return; + case 0x4203: mmio_w4203(data); return; + case 0x4204: mmio_w4204(data); return; + case 0x4205: mmio_w4205(data); return; + case 0x4206: mmio_w4206(data); return; + case 0x4207: mmio_w4207(data); return; + case 0x4208: mmio_w4208(data); return; + case 0x4209: mmio_w4209(data); return; + case 0x420a: mmio_w420a(data); return; + case 0x420b: mmio_w420b(data); return; + case 0x420c: mmio_w420c(data); return; + case 0x420d: mmio_w420d(data); return; + } +} + +#endif diff --git a/mednafen/snes/src/cpu/scpu/mmio/mmio.hpp b/mednafen/snes/src/cpu/scpu/mmio/mmio.hpp new file mode 100755 index 0000000..4955c4f --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/mmio/mmio.hpp @@ -0,0 +1,71 @@ + void mmio_power(); + void mmio_reset(); + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + uint8 pio(); + bool joylatch(); + + uint8 mmio_r2180(); + uint8 mmio_r4016(); + uint8 mmio_r4017(); + uint8 mmio_r4210(); + uint8 mmio_r4211(); + uint8 mmio_r4212(); + uint8 mmio_r4213(); + uint8 mmio_r4214(); + uint8 mmio_r4215(); + uint8 mmio_r4216(); + uint8 mmio_r4217(); + uint8 mmio_r4218(); + uint8 mmio_r4219(); + uint8 mmio_r421a(); + uint8 mmio_r421b(); + uint8 mmio_r421c(); + uint8 mmio_r421d(); + uint8 mmio_r421e(); + uint8 mmio_r421f(); + uint8 mmio_r43x0(uint8 i); + uint8 mmio_r43x1(uint8 i); + uint8 mmio_r43x2(uint8 i); + uint8 mmio_r43x3(uint8 i); + uint8 mmio_r43x4(uint8 i); + uint8 mmio_r43x5(uint8 i); + uint8 mmio_r43x6(uint8 i); + uint8 mmio_r43x7(uint8 i); + uint8 mmio_r43x8(uint8 i); + uint8 mmio_r43x9(uint8 i); + uint8 mmio_r43xa(uint8 i); + uint8 mmio_r43xb(uint8 i); + + void mmio_w2180(uint8 data); + void mmio_w2181(uint8 data); + void mmio_w2182(uint8 data); + void mmio_w2183(uint8 data); + void mmio_w4016(uint8 data); + void mmio_w4200(uint8 data); + void mmio_w4201(uint8 data); + void mmio_w4202(uint8 data); + void mmio_w4203(uint8 data); + void mmio_w4204(uint8 data); + void mmio_w4205(uint8 data); + void mmio_w4206(uint8 data); + void mmio_w4207(uint8 data); + void mmio_w4208(uint8 data); + void mmio_w4209(uint8 data); + void mmio_w420a(uint8 data); + void mmio_w420b(uint8 data); + void mmio_w420c(uint8 data); + void mmio_w420d(uint8 data); + void mmio_w43x0(uint8 i, uint8 data); + void mmio_w43x1(uint8 i, uint8 data); + void mmio_w43x2(uint8 i, uint8 data); + void mmio_w43x3(uint8 i, uint8 data); + void mmio_w43x4(uint8 i, uint8 data); + void mmio_w43x5(uint8 i, uint8 data); + void mmio_w43x6(uint8 i, uint8 data); + void mmio_w43x7(uint8 i, uint8 data); + void mmio_w43x8(uint8 i, uint8 data); + void mmio_w43x9(uint8 i, uint8 data); + void mmio_w43xa(uint8 i, uint8 data); + void mmio_w43xb(uint8 i, uint8 data); diff --git a/mednafen/snes/src/cpu/scpu/scpu.cpp b/mednafen/snes/src/cpu/scpu/scpu.cpp new file mode 100755 index 0000000..a50d83b --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/scpu.cpp @@ -0,0 +1,113 @@ +#include <../base.hpp> + +#define SCPU_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + sCPUDebugger cpu; +#else + sCPU cpu; +#endif + +#include "serialization.cpp" +#include "dma/dma.cpp" +#include "memory/memory.cpp" +#include "mmio/mmio.cpp" +#include "timing/timing.cpp" + +void sCPU::enter() { + while(true) { + while(scheduler.sync == Scheduler::SyncCpu) { + scheduler.sync = Scheduler::SyncAll; + scheduler.exit(Scheduler::SynchronizeEvent); + } + + if(status.interrupt_pending) { + status.interrupt_pending = false; + if(status.nmi_pending) { + status.nmi_pending = false; + status.interrupt_vector = (regs.e == false ? 0xffea : 0xfffa); + op_irq(); + } else if(status.irq_pending) { + status.irq_pending = false; + status.interrupt_vector = (regs.e == false ? 0xffee : 0xfffe); + op_irq(); + } else if(status.reset_pending) { + status.reset_pending = false; + add_clocks(186); + regs.pc.l = bus.read(0xfffc); + regs.pc.h = bus.read(0xfffd); + } + } + + op_step(); + } +} + +void sCPU::op_step() { + (this->*opcode_table[op_readpc()])(); +} + +void sCPU::op_irq() { + op_read(regs.pc.d); + op_io(); + if(!regs.e) op_writestack(regs.pc.b); + op_writestack(regs.pc.h); + op_writestack(regs.pc.l); + op_writestack(regs.e ? (regs.p & ~0x10) : regs.p); + rd.l = op_read(status.interrupt_vector + 0); + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; + rd.h = op_read(status.interrupt_vector + 1); + regs.pc.w = rd.w; +} + +void sCPU::power() { + CPU::power(); + + regs.a = regs.x = regs.y = 0x0000; + regs.s = 0x01ff; + + mmio_power(); + dma_power(); + timing_power(); + + reset(); +} + +void sCPU::reset() { + CPU::reset(); + + //note: some registers are not fully reset by SNES + regs.pc = 0x000000; + regs.x.h = 0x00; + regs.y.h = 0x00; + regs.s.h = 0x01; + regs.d = 0x0000; + regs.db = 0x00; + regs.p = 0x34; + regs.e = 1; + regs.mdr = 0x00; + regs.wai = false; + update_table(); + + mmio_reset(); + dma_reset(); + timing_reset(); + + apu_port[0] = 0x00; + apu_port[1] = 0x00; + apu_port[2] = 0x00; + apu_port[3] = 0x00; +} + +sCPU::sCPU() : event(512, bind(&sCPU::queue_event, this)) { + PPUcounter::scanline = bind(&sCPU::scanline, this); +} + +sCPU::~sCPU() { +} + +}; diff --git a/mednafen/snes/src/cpu/scpu/scpu.hpp b/mednafen/snes/src/cpu/scpu/scpu.hpp new file mode 100755 index 0000000..687862e --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/scpu.hpp @@ -0,0 +1,106 @@ +class sCPU : public CPU, public CPUcore { +public: + void enter(); + debugvirtual void op_step(); + void op_irq(); + bool interrupt_pending() { return status.interrupt_pending; } + + #include "dma/dma.hpp" + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + #include "timing/timing.hpp" + + priority_queue event; + + struct { + bool interrupt_pending; + uint16 interrupt_vector; + + unsigned clock_count; + unsigned line_clocks; + + //timing + bool irq_lock; + bool alu_lock; + unsigned dram_refresh_position; + + bool nmi_valid; + bool nmi_line; + bool nmi_transition; + bool nmi_pending; + bool nmi_hold; + + bool irq_valid; + bool irq_line; + bool irq_transition; + bool irq_pending; + bool irq_hold; + + bool reset_pending; + + //DMA + bool dma_active; + unsigned dma_counter; + unsigned dma_clocks; + bool dma_pending; + bool hdma_pending; + bool hdma_mode; //0 = init, 1 = run + + //MMIO + + //$2181-$2183 + uint32 wram_addr; + + //$4016-$4017 + bool joypad_strobe_latch; + uint32 joypad1_bits; + uint32 joypad2_bits; + + //$4200 + bool nmi_enabled; + bool hirq_enabled, virq_enabled; + bool auto_joypad_poll; + + //$4201 + uint8 pio; + + //$4202-$4203 + uint8 mul_a, mul_b; + + //$4204-$4206 + uint16 div_a; + uint8 div_b; + + //$4207-$420a + uint16 hirq_pos, virq_pos; + + //$420d + unsigned rom_speed; + + //$4214-$4217 + uint16 r4214; + uint16 r4216; + + //$4218-$421f + uint8 joy1l, joy1h; + uint8 joy2l, joy2h; + uint8 joy3l, joy3h; + uint8 joy4l, joy4h; + } status; + + void power(); + void reset(); + + void serialize(serializer&); + sCPU(); + ~sCPU(); + + friend class sCPUDebug; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern sCPUDebugger cpu; +#else + extern sCPU cpu; +#endif diff --git a/mednafen/snes/src/cpu/scpu/serialization.cpp b/mednafen/snes/src/cpu/scpu/serialization.cpp new file mode 100755 index 0000000..c9e2582 --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/serialization.cpp @@ -0,0 +1,105 @@ +#ifdef SCPU_CPP + +void sCPU::serialize(serializer &s) { + CPU::serialize(s); + CPUcore::core_serialize(s); + + event.serialize(s); + + s.integer(status.interrupt_pending); + s.integer(status.interrupt_vector); + + s.integer(status.clock_count); + s.integer(status.line_clocks); + + s.integer(status.irq_lock); + s.integer(status.alu_lock); + s.integer(status.dram_refresh_position); + + s.integer(status.nmi_valid); + s.integer(status.nmi_line); + s.integer(status.nmi_transition); + s.integer(status.nmi_pending); + s.integer(status.nmi_hold); + + s.integer(status.irq_valid); + s.integer(status.irq_line); + s.integer(status.irq_transition); + s.integer(status.irq_pending); + s.integer(status.irq_hold); + + s.integer(status.reset_pending); + + s.integer(status.dma_active); + s.integer(status.dma_counter); + s.integer(status.dma_clocks); + s.integer(status.dma_pending); + s.integer(status.hdma_pending); + s.integer(status.hdma_mode); + + s.integer(status.wram_addr); + + s.integer(status.joypad_strobe_latch); + s.integer(status.joypad1_bits); + s.integer(status.joypad2_bits); + + s.integer(status.nmi_enabled); + s.integer(status.hirq_enabled); + s.integer(status.virq_enabled); + s.integer(status.auto_joypad_poll); + + s.integer(status.pio); + + s.integer(status.mul_a); + s.integer(status.mul_b); + + s.integer(status.div_a); + s.integer(status.div_b); + + s.integer(status.hirq_pos); + s.integer(status.virq_pos); + + s.integer(status.rom_speed); + + s.integer(status.r4214); + s.integer(status.r4216); + + s.integer(status.joy1l); + s.integer(status.joy1h); + s.integer(status.joy2l); + s.integer(status.joy2h); + s.integer(status.joy3l); + s.integer(status.joy3h); + s.integer(status.joy4l); + s.integer(status.joy4h); + + for(unsigned i = 0; i < 8; i++) { + s.integer(channel[i].dma_enabled); + s.integer(channel[i].hdma_enabled); + s.integer(channel[i].dmap); + s.integer(channel[i].direction); + s.integer(channel[i].hdma_indirect); + s.integer(channel[i].reversexfer); + s.integer(channel[i].fixedxfer); + s.integer(channel[i].xfermode); + s.integer(channel[i].destaddr); + s.integer(channel[i].srcaddr); + s.integer(channel[i].srcbank); + s.integer(channel[i].xfersize); + s.integer(channel[i].hdma_ibank); + s.integer(channel[i].hdma_addr); + s.integer(channel[i].hdma_line_counter); + s.integer(channel[i].unknown); + s.integer(channel[i].hdma_completed); + s.integer(channel[i].hdma_do_transfer); + } + + s.integer(apu_port[0]); + s.integer(apu_port[1]); + s.integer(apu_port[2]); + s.integer(apu_port[3]); + + s.integer(cycle_edge_state); +} + +#endif diff --git a/mednafen/snes/src/cpu/scpu/timing/event.cpp b/mednafen/snes/src/cpu/scpu/timing/event.cpp new file mode 100755 index 0000000..82de924 --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/timing/event.cpp @@ -0,0 +1,35 @@ +#ifdef SCPU_CPP + +void sCPU::queue_event(unsigned id) { + switch(id) { + //interrupts triggered during (H)DMA do not trigger immediately after + case EventIrqLockRelease: { + status.irq_lock = false; + } break; + + //ALU multiplication / division results are not immediately calculated; + //the exact formula for the calculations are unknown, but this lock at least + //allows emulation to avoid returning to fully computed results too soon. + case EventAluLockRelease: { + status.alu_lock = false; + } break; + + //S-CPU WRAM consists of two 64kbyte DRAM chips, which must be refreshed + //once per scanline to avoid memory decay. + case EventDramRefresh: { + add_clocks(40); + } break; + + //HDMA init routine; occurs once per frame + case EventHdmaInit: { + cycle_edge_state |= EventFlagHdmaInit; + } break; + + //HDMA run routine; occurs once per scanline + case EventHdmaRun: { + cycle_edge_state |= EventFlagHdmaRun; + } break; + } +} + +#endif diff --git a/mednafen/snes/src/cpu/scpu/timing/irq.cpp b/mednafen/snes/src/cpu/scpu/timing/irq.cpp new file mode 100755 index 0000000..78664f0 --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/timing/irq.cpp @@ -0,0 +1,107 @@ +#ifdef SCPU_CPP + +//called once every four clock cycles; +//as NMI steps by scanlines (divisible by 4) and IRQ by PPU 4-cycle dots. +// +//ppu.(vh)counter(n) returns the value of said counters n-clocks before current time; +//it is used to emulate hardware communication delay between opcode and interrupt units. +void sCPU::poll_interrupts() { + //NMI hold + if(status.nmi_hold) { + status.nmi_hold = false; + if(status.nmi_enabled) status.nmi_transition = true; + } + + //NMI test + bool nmi_valid = (vcounter(2) >= (!ppu.overscan() ? 225 : 240)); + if(!status.nmi_valid && nmi_valid) { + //0->1 edge sensitive transition + status.nmi_line = true; + status.nmi_hold = true; //hold /NMI for four cycles + } else if(status.nmi_valid && !nmi_valid) { + //1->0 edge sensitive transition + status.nmi_line = false; + } + status.nmi_valid = nmi_valid; + + //IRQ hold + status.irq_hold = false; + if(status.irq_line) { + if(status.virq_enabled || status.hirq_enabled) status.irq_transition = true; + } + + //IRQ test + bool irq_valid = (status.virq_enabled || status.hirq_enabled); + if(irq_valid) { + if((status.virq_enabled && vcounter(10) != (status.virq_pos)) + || (status.hirq_enabled && hcounter(10) != (status.hirq_pos + 1) * 4) + || (status.virq_pos && vcounter(6) == 0) //IRQs cannot trigger on last dot of field + ) irq_valid = false; + } + if(!status.irq_valid && irq_valid) { + //0->1 edge sensitive transition + status.irq_line = true; + status.irq_hold = true; //hold /IRQ for four cycles + } + status.irq_valid = irq_valid; +} + +void sCPU::nmitimen_update(uint8 data) { + bool nmi_enabled = status.nmi_enabled; + bool virq_enabled = status.virq_enabled; + bool hirq_enabled = status.hirq_enabled; + status.nmi_enabled = data & 0x80; + status.virq_enabled = data & 0x20; + status.hirq_enabled = data & 0x10; + + //0->1 edge sensitive transition + if(!nmi_enabled && status.nmi_enabled && status.nmi_line) { + status.nmi_transition = true; + } + + //?->1 level sensitive transition + if(status.virq_enabled && !status.hirq_enabled && status.irq_line) { + status.irq_transition = true; + } + + if(!status.virq_enabled && !status.hirq_enabled) { + status.irq_line = false; + status.irq_transition = false; + } + + status.irq_lock = true; + event.enqueue(2, EventIrqLockRelease); +} + +bool sCPU::rdnmi() { + bool result = status.nmi_line; + if(!status.nmi_hold) { + status.nmi_line = false; + } + return result; +} + +bool sCPU::timeup() { + bool result = status.irq_line; + if(!status.irq_hold) { + status.irq_line = false; + status.irq_transition = false; + } + return result; +} + +bool sCPU::nmi_test() { + if(!status.nmi_transition) return false; + status.nmi_transition = false; + regs.wai = false; + return true; +} + +bool sCPU::irq_test() { + if(!status.irq_transition && !regs.irq) return false; + status.irq_transition = false; + regs.wai = false; + return !regs.p.i; +} + +#endif diff --git a/mednafen/snes/src/cpu/scpu/timing/joypad.cpp b/mednafen/snes/src/cpu/scpu/timing/joypad.cpp new file mode 100755 index 0000000..84149f5 --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/timing/joypad.cpp @@ -0,0 +1,28 @@ +#ifdef SCPU_CPP + +void sCPU::run_auto_joypad_poll() { + uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0; + for(unsigned i = 0; i < 16; i++) { + uint8 port0 = input.port_read(0); + uint8 port1 = input.port_read(1); + + joy1 |= (port0 & 1) ? (0x8000 >> i) : 0; + joy2 |= (port1 & 1) ? (0x8000 >> i) : 0; + joy3 |= (port0 & 2) ? (0x8000 >> i) : 0; + joy4 |= (port1 & 2) ? (0x8000 >> i) : 0; + } + + status.joy1l = joy1; + status.joy1h = joy1 >> 8; + + status.joy2l = joy2; + status.joy2h = joy2 >> 8; + + status.joy3l = joy3; + status.joy3h = joy3 >> 8; + + status.joy4l = joy4; + status.joy4h = joy4 >> 8; +} + +#endif diff --git a/mednafen/snes/src/cpu/scpu/timing/timing.cpp b/mednafen/snes/src/cpu/scpu/timing/timing.cpp new file mode 100755 index 0000000..cc6ba54 --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/timing/timing.cpp @@ -0,0 +1,172 @@ +#ifdef SCPU_CPP + +#include "event.cpp" +#include "irq.cpp" +#include "joypad.cpp" + +unsigned sCPU::dma_counter() { + return (status.dma_counter + hcounter()) & 7; +} + +void sCPU::add_clocks(unsigned clocks) { + event.tick(clocks); + unsigned ticks = clocks >> 1; + while(ticks--) { + tick(); + if(hcounter() & 2) { + input.tick(); + poll_interrupts(); + } + } + scheduler.addclocks_cpu(clocks); +} + +//called by ppu.tick() when Hcounter=0 +void sCPU::scanline() { + status.dma_counter = (status.dma_counter + status.line_clocks) & 7; + status.line_clocks = lineclocks(); + + //forcefully sync S-CPU to other processors, in case chips are not communicating + scheduler.sync_cpuppu(); + scheduler.sync_cpucop(); + scheduler.sync_cpusmp(); + system.scanline(); + + if(vcounter() == 0) { + //hdma init triggers once every frame + event.enqueue(cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter(), EventHdmaInit); + } + + //dram refresh occurs once every scanline + if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter(); + event.enqueue(status.dram_refresh_position, EventDramRefresh); + + //hdma triggers once every visible scanline + if(vcounter() <= (ppu.overscan() == false ? 224 : 239)) { + event.enqueue(1104, EventHdmaRun); + } + + if(status.auto_joypad_poll == true && vcounter() == (ppu.overscan() == false ? 227 : 242)) { + input.poll(); + run_auto_joypad_poll(); + } +} + +//used to test for H/DMA, which can trigger on the edge of every opcode cycle. +void sCPU::cycle_edge() { + while(cycle_edge_state) { + switch(bit::lowest(cycle_edge_state)) { + case EventFlagHdmaInit: { + hdma_init_reset(); + if(hdma_enabled_channels()) { + status.hdma_pending = true; + status.hdma_mode = 0; + } + } break; + + case EventFlagHdmaRun: { + if(hdma_active_channels()) { + status.hdma_pending = true; + status.hdma_mode = 1; + } + } break; + } + + cycle_edge_state = bit::clear_lowest(cycle_edge_state); + } + + //H/DMA pending && DMA inactive? + //.. Run one full CPU cycle + //.. HDMA pending && HDMA enabled ? DMA sync + HDMA run + //.. DMA pending && DMA enabled ? DMA sync + DMA run + //.... HDMA during DMA && HDMA enabled ? DMA sync + HDMA run + //.. Run one bus CPU cycle + //.. CPU sync + + if(status.dma_active == true) { + if(status.hdma_pending) { + status.hdma_pending = false; + if(hdma_enabled_channels()) { + dma_add_clocks(8 - dma_counter()); + status.hdma_mode == 0 ? hdma_init() : hdma_run(); + if(!dma_enabled_channels()) { + add_clocks(status.clock_count - (status.dma_clocks % status.clock_count)); + status.dma_active = false; + } + } + } + + if(status.dma_pending) { + status.dma_pending = false; + if(dma_enabled_channels()) { + dma_add_clocks(8 - dma_counter()); + dma_run(); + add_clocks(status.clock_count - (status.dma_clocks % status.clock_count)); + status.dma_active = false; + } + } + } + + if(status.dma_active == false) { + if(status.dma_pending || status.hdma_pending) { + status.dma_clocks = 0; + status.dma_active = true; + } + } +} + +//used to test for NMI/IRQ, which can trigger on the edge of every opcode. +//test one cycle early to simulate two-stage pipeline of x816 CPU. +// +//status.irq_lock is used to simulate hardware delay before interrupts can +//trigger during certain events (immediately after DMA, writes to $4200, etc) +void sCPU::last_cycle() { + if(!status.irq_lock) { + status.nmi_pending |= nmi_test(); + status.irq_pending |= irq_test(); + + status.interrupt_pending = (status.nmi_pending || status.irq_pending); + } +} + +void sCPU::timing_power() { +} + +void sCPU::timing_reset() { + event.reset(); + + status.clock_count = 0; + status.line_clocks = lineclocks(); + + status.irq_lock = false; + status.alu_lock = false; + status.dram_refresh_position = (cpu_version == 1 ? 530 : 538); + event.enqueue(status.dram_refresh_position, EventDramRefresh); + + status.nmi_valid = false; + status.nmi_line = false; + status.nmi_transition = false; + status.nmi_pending = false; + status.nmi_hold = false; + + status.irq_valid = false; + status.irq_line = false; + status.irq_transition = false; + status.irq_pending = false; + status.irq_hold = false; + + status.reset_pending = true; + status.interrupt_pending = true; + status.interrupt_vector = 0xfffc; //reset vector address + + status.dma_active = false; + status.dma_counter = 0; + status.dma_clocks = 0; + status.dma_pending = false; + status.hdma_pending = false; + status.hdma_mode = 0; + + cycle_edge_state = 0; +} + +#endif diff --git a/mednafen/snes/src/cpu/scpu/timing/timing.hpp b/mednafen/snes/src/cpu/scpu/timing/timing.hpp new file mode 100755 index 0000000..476d88e --- /dev/null +++ b/mednafen/snes/src/cpu/scpu/timing/timing.hpp @@ -0,0 +1,40 @@ + enum { + EventNone, + EventIrqLockRelease, + EventAluLockRelease, + EventDramRefresh, + EventHdmaInit, + EventHdmaRun, + + //cycle edge + EventFlagHdmaInit = 1 << 0, + EventFlagHdmaRun = 1 << 1, + }; + unsigned cycle_edge_state; + + //timing.cpp + unsigned dma_counter(); + + void add_clocks(unsigned clocks); + void scanline(); + + alwaysinline void cycle_edge(); + alwaysinline void last_cycle(); + + void timing_power(); + void timing_reset(); + + //irq.cpp + alwaysinline void poll_interrupts(); + void nmitimen_update(uint8 data); + bool rdnmi(); + bool timeup(); + + alwaysinline bool nmi_test(); + alwaysinline bool irq_test(); + + //joypad.cpp + void run_auto_joypad_poll(); + + //event.cpp + void queue_event(unsigned); //priorityqueue callback function diff --git a/mednafen/snes/src/data/bsnes.Manifest b/mednafen/snes/src/data/bsnes.Manifest new file mode 100755 index 0000000..4602d4f --- /dev/null +++ b/mednafen/snes/src/data/bsnes.Manifest @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/mednafen/snes/src/data/bsnes.desktop b/mednafen/snes/src/data/bsnes.desktop new file mode 100755 index 0000000..d527e97 --- /dev/null +++ b/mednafen/snes/src/data/bsnes.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=bsnes +Comment=SNES emulator +Exec=bsnes +Icon=bsnes +Terminal=false +Type=Application +Categories=Game;Emulator; diff --git a/mednafen/snes/src/data/bsnes.ico b/mednafen/snes/src/data/bsnes.ico new file mode 100755 index 0000000..54acded Binary files /dev/null and b/mednafen/snes/src/data/bsnes.ico differ diff --git a/mednafen/snes/src/data/bsnes.png b/mednafen/snes/src/data/bsnes.png new file mode 100755 index 0000000..02986ba Binary files /dev/null and b/mednafen/snes/src/data/bsnes.png differ diff --git a/mednafen/snes/src/data/documentation.html b/mednafen/snes/src/data/documentation.html new file mode 100755 index 0000000..6f77261 --- /dev/null +++ b/mednafen/snes/src/data/documentation.html @@ -0,0 +1,81 @@ + + + + + +

bsnes Usage Documentation


+ +bsnes is a Super Nintendo / Super Famicom emulator that strives to provide the +most faithful hardware emulation possible. It focuses on accuracy and clean +code, rather than speed and special features. It is meant as a reference +emulator to document how the underlying hardware works. It is thus very useful +for development and research. And while it can be used for general purpose +gaming, it will require significantly more powerful hardware than a typical +emulator. +
+ +

Modes of Operation


+ +bsnes is capable of running both in its default multi-user mode, as well as in +single-user mode. +

+ +In multi-user mode, configuration data is stored inside the user's home +directory. On Windows, this is located at "%APPDATA%/.bsnes". On other operating +systems, this is located at "~/.bsnes". +

+ +To enable single-user mode, create a blank "bsnes.cfg" file inside the same +folder as the bsnes executable. bsnes will then use this file to store +configuration data. +
+ +

Supported Filetypes


+ +SFC: SNES cartridge — ROM image.
+BS: Satellaview BS-X flash cartridge — EEPROM image.
+ST: Sufami Turbo cartridge — ROM image.
+SRM, PSR: non-volatile memory, often used to save game data — (P)SRAM image.
+RTC: real-time clock non-volatile memory.
+UPS: patch data, used to dynamically modify cartridge of same base filename upon load.
+CHT: plain-text list of "Game Genie" / "Pro Action Replay" codes. +
+ +

Known Limitations


+ +Satellaview BS-X emulation: this hardware is only partially supported. +This is mostly because the satellite network it used (St. GIGA) has been shut +down. Access to this network would be required to properly reverse engineer much +of the hardware. Working around this would require game-specific hacks, which +are contrary to the design goals of this emulator. As a result, most BS-X +software will not function correctly. +

+ +Netplay: internet multiplay is not currently supported nor planned. +
+ +

Contributors


+• Andreas Naive
+• anomie
+• _Demo_
+• Derrick Sobodash
+• DMV27
+• FirebrandX
+• FitzRoy
+• GIGO
+• Jonas Quinn
+• kode54
+• krom
+• Matthew Callis
+• Nach
+• neviksti
+• Overload
+• RedDwarf
+• Richard Bannister
+• Shay Green
+• tetsuo55
+• TRAC
+• zones
+ + + diff --git a/mednafen/snes/src/data/icons-16x16/accessories-text-editor.png b/mednafen/snes/src/data/icons-16x16/accessories-text-editor.png new file mode 100755 index 0000000..188e1c1 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/accessories-text-editor.png differ diff --git a/mednafen/snes/src/data/icons-16x16/applications-multimedia.png b/mednafen/snes/src/data/icons-16x16/applications-multimedia.png new file mode 100755 index 0000000..3e4ced5 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/applications-multimedia.png differ diff --git a/mednafen/snes/src/data/icons-16x16/appointment-new.png b/mednafen/snes/src/data/icons-16x16/appointment-new.png new file mode 100755 index 0000000..18b7c67 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/appointment-new.png differ diff --git a/mednafen/snes/src/data/icons-16x16/audio-volume-high.png b/mednafen/snes/src/data/icons-16x16/audio-volume-high.png new file mode 100755 index 0000000..ec8f00b Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/audio-volume-high.png differ diff --git a/mednafen/snes/src/data/icons-16x16/document-open.png b/mednafen/snes/src/data/icons-16x16/document-open.png new file mode 100755 index 0000000..ab94046 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/document-open.png differ diff --git a/mednafen/snes/src/data/icons-16x16/folder-new.png b/mednafen/snes/src/data/icons-16x16/folder-new.png new file mode 100755 index 0000000..628f4d5 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/folder-new.png differ diff --git a/mednafen/snes/src/data/icons-16x16/folder.png b/mednafen/snes/src/data/icons-16x16/folder.png new file mode 100755 index 0000000..65bd0bb Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/folder.png differ diff --git a/mednafen/snes/src/data/icons-16x16/help-browser.png b/mednafen/snes/src/data/icons-16x16/help-browser.png new file mode 100755 index 0000000..f25fc3f Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/help-browser.png differ diff --git a/mednafen/snes/src/data/icons-16x16/image-x-generic.png b/mednafen/snes/src/data/icons-16x16/image-x-generic.png new file mode 100755 index 0000000..68da502 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/image-x-generic.png differ diff --git a/mednafen/snes/src/data/icons-16x16/input-gaming.png b/mednafen/snes/src/data/icons-16x16/input-gaming.png new file mode 100755 index 0000000..9d040ee Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/input-gaming.png differ diff --git a/mednafen/snes/src/data/icons-16x16/item-check-off.png b/mednafen/snes/src/data/icons-16x16/item-check-off.png new file mode 100755 index 0000000..9552941 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/item-check-off.png differ diff --git a/mednafen/snes/src/data/icons-16x16/item-check-on.png b/mednafen/snes/src/data/icons-16x16/item-check-on.png new file mode 100755 index 0000000..af83bfa Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/item-check-on.png differ diff --git a/mednafen/snes/src/data/icons-16x16/item-radio-off.png b/mednafen/snes/src/data/icons-16x16/item-radio-off.png new file mode 100755 index 0000000..e17b659 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/item-radio-off.png differ diff --git a/mednafen/snes/src/data/icons-16x16/item-radio-on.png b/mednafen/snes/src/data/icons-16x16/item-radio-on.png new file mode 100755 index 0000000..8a503f6 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/item-radio-on.png differ diff --git a/mednafen/snes/src/data/icons-16x16/media-playback-start.png b/mednafen/snes/src/data/icons-16x16/media-playback-start.png new file mode 100755 index 0000000..a7de0fe Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/media-playback-start.png differ diff --git a/mednafen/snes/src/data/icons-16x16/media-playback-stop.png b/mednafen/snes/src/data/icons-16x16/media-playback-stop.png new file mode 100755 index 0000000..ede2815 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/media-playback-stop.png differ diff --git a/mednafen/snes/src/data/icons-16x16/media-record.png b/mednafen/snes/src/data/icons-16x16/media-record.png new file mode 100755 index 0000000..2f66cde Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/media-record.png differ diff --git a/mednafen/snes/src/data/icons-16x16/preferences-desktop.png b/mednafen/snes/src/data/icons-16x16/preferences-desktop.png new file mode 100755 index 0000000..68f916c Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/preferences-desktop.png differ diff --git a/mednafen/snes/src/data/icons-16x16/preferences-system.png b/mednafen/snes/src/data/icons-16x16/preferences-system.png new file mode 100755 index 0000000..9460dfc Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/preferences-system.png differ diff --git a/mednafen/snes/src/data/icons-16x16/process-stop.png b/mednafen/snes/src/data/icons-16x16/process-stop.png new file mode 100755 index 0000000..ab6808f Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/process-stop.png differ diff --git a/mednafen/snes/src/data/icons-16x16/system-file-manager.png b/mednafen/snes/src/data/icons-16x16/system-file-manager.png new file mode 100755 index 0000000..60cade4 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/system-file-manager.png differ diff --git a/mednafen/snes/src/data/icons-16x16/system-search.png b/mednafen/snes/src/data/icons-16x16/system-search.png new file mode 100755 index 0000000..fd7f0b0 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/system-search.png differ diff --git a/mednafen/snes/src/data/icons-16x16/text-x-generic.png b/mednafen/snes/src/data/icons-16x16/text-x-generic.png new file mode 100755 index 0000000..2d7f2d6 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/text-x-generic.png differ diff --git a/mednafen/snes/src/data/icons-16x16/utilities-terminal.png b/mednafen/snes/src/data/icons-16x16/utilities-terminal.png new file mode 100755 index 0000000..c5b797a Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/utilities-terminal.png differ diff --git a/mednafen/snes/src/data/icons-16x16/video-display.png b/mednafen/snes/src/data/icons-16x16/video-display.png new file mode 100755 index 0000000..226881f Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/video-display.png differ diff --git a/mednafen/snes/src/data/icons-16x16/view-refresh.png b/mednafen/snes/src/data/icons-16x16/view-refresh.png new file mode 100755 index 0000000..3fd71d6 Binary files /dev/null and b/mednafen/snes/src/data/icons-16x16/view-refresh.png differ diff --git a/mednafen/snes/src/data/license.html b/mednafen/snes/src/data/license.html new file mode 100755 index 0000000..3f3508c --- /dev/null +++ b/mednafen/snes/src/data/license.html @@ -0,0 +1,77 @@ + + + + + +

GNU GENERAL PUBLIC LICENSE
+Version 2, June 1991

+
+ +

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION


+ +0. +This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".

+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.

+ +1. +You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.

+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.

+ +2. +You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:

+
    +
  • a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
  • +
  • b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
  • +
  • c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
  • +
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.

+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

+ +3. +You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: +
    +
  • a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
  • +
  • b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
  • +
  • c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
  • +
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.

+ +4. +You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

+ +5. +You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.

+ +6. +Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.

+ +7. +If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.

+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.

+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

+ +8. +If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.

+ +9. +The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.

+ +10. +If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

+ +NO WARRANTY

+ +11. +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

+ +12. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ +

END OF TERMS AND CONDITIONS

+ + + diff --git a/mednafen/snes/src/data/logo.png b/mednafen/snes/src/data/logo.png new file mode 100755 index 0000000..e78ef27 Binary files /dev/null and b/mednafen/snes/src/data/logo.png differ diff --git a/mednafen/snes/src/dsp/adsp/adsp.cpp b/mednafen/snes/src/dsp/adsp/adsp.cpp new file mode 100755 index 0000000..dd89359 --- /dev/null +++ b/mednafen/snes/src/dsp/adsp/adsp.cpp @@ -0,0 +1,595 @@ +#include <../base.hpp> + +#define ADSP_CPP +namespace SNES { + +aDSP dsp; + +#include "adsp_tables.cpp" + +void aDSP::enter() { loop: + run(); + goto loop; +} + +uint8 aDSP::readb(uint16 addr) { + return spcram[addr]; +} + +void aDSP::writeb(uint16 addr, uint8 data) { + spcram[addr] = data; +} + +uint16 aDSP::readw(uint16 addr) { + return (readb(addr + 0)) | (readb(addr + 1) << 8); +} + +void aDSP::writew(uint16 addr, uint16 data) { + writeb(addr + 0, data); + writeb(addr + 1, data >> 8); +} + +uint8 aDSP::read(uint8 addr) { + addr &= 127; +int v = addr >> 4; +int n = addr & 15; + + switch(addr) { + case 0x00: case 0x10: case 0x20: case 0x30: + case 0x40: case 0x50: case 0x60: case 0x70: + return voice[v].VOLL; + case 0x01: case 0x11: case 0x21: case 0x31: + case 0x41: case 0x51: case 0x61: case 0x71: + return voice[v].VOLR; + case 0x02: case 0x12: case 0x22: case 0x32: + case 0x42: case 0x52: case 0x62: case 0x72: + return voice[v].PITCH; + case 0x03: case 0x13: case 0x23: case 0x33: + case 0x43: case 0x53: case 0x63: case 0x73: + return voice[v].PITCH >> 8; + case 0x04: case 0x14: case 0x24: case 0x34: + case 0x44: case 0x54: case 0x64: case 0x74: + return voice[v].SRCN; + case 0x05: case 0x15: case 0x25: case 0x35: + case 0x45: case 0x55: case 0x65: case 0x75: + return voice[v].ADSR1; + case 0x06: case 0x16: case 0x26: case 0x36: + case 0x46: case 0x56: case 0x66: case 0x76: + return voice[v].ADSR2; + case 0x07: case 0x17: case 0x27: case 0x37: + case 0x47: case 0x57: case 0x67: case 0x77: + return voice[v].GAIN; + case 0x08: case 0x18: case 0x28: case 0x38: + case 0x48: case 0x58: case 0x68: case 0x78: + return voice[v].ENVX; + case 0x09: case 0x19: case 0x29: case 0x39: + case 0x49: case 0x59: case 0x69: case 0x79: + return voice[v].OUTX; + + case 0x0f: case 0x1f: case 0x2f: case 0x3f: + case 0x4f: case 0x5f: case 0x6f: case 0x7f: + return status.FIR[v]; + + case 0x0c: return status.MVOLL; + case 0x1c: return status.MVOLR; + case 0x2c: return status.EVOLL; + case 0x3c: return status.EVOLR; + case 0x4c: return status.KON; + case 0x5c: return status.KOFF; + case 0x6c: return status.FLG; + case 0x7c: return status.ENDX; + + case 0x0d: return status.EFB; + case 0x2d: return status.PMON; + case 0x3d: return status.NON; + case 0x4d: return status.EON; + case 0x5d: return status.DIR; + case 0x6d: return status.ESA; + case 0x7d: return status.EDL; + } + + return dspram[addr]; +} + +void aDSP::write(uint8 addr, uint8 data) { +//0x80-0xff is a read-only mirror of 0x00-0x7f + if(addr & 0x80)return; + +int v = addr >> 4; +int n = addr & 15; + + switch(addr) { + case 0x00: case 0x10: case 0x20: case 0x30: + case 0x40: case 0x50: case 0x60: case 0x70: + voice[v].VOLL = data; + break; + case 0x01: case 0x11: case 0x21: case 0x31: + case 0x41: case 0x51: case 0x61: case 0x71: + voice[v].VOLR = data; + break; + case 0x02: case 0x12: case 0x22: case 0x32: + case 0x42: case 0x52: case 0x62: case 0x72: + voice[v].PITCH &= 0xff00; + voice[v].PITCH |= data; + break; + case 0x03: case 0x13: case 0x23: case 0x33: + case 0x43: case 0x53: case 0x63: case 0x73: + voice[v].PITCH &= 0x00ff; + voice[v].PITCH |= data << 8; + break; + case 0x04: case 0x14: case 0x24: case 0x34: + case 0x44: case 0x54: case 0x64: case 0x74: + voice[v].SRCN = data; + break; + case 0x05: case 0x15: case 0x25: case 0x35: + case 0x45: case 0x55: case 0x65: case 0x75: + voice[v].ADSR1 = data; + voice[v].AdjustEnvelope(); + break; + case 0x06: case 0x16: case 0x26: case 0x36: + case 0x46: case 0x56: case 0x66: case 0x76: + voice[v].ADSR2 = data; + //sustain_level = 0-7, 7 is a special case handled by ATTACK envx mode + voice[v].env_sustain = (voice[v].ADSR_sus_level() + 1) << 8; + voice[v].AdjustEnvelope(); + break; + case 0x07: case 0x17: case 0x27: case 0x37: + case 0x47: case 0x57: case 0x67: case 0x77: + voice[v].GAIN = data; + voice[v].AdjustEnvelope(); + break; + case 0x08: case 0x18: case 0x28: case 0x38: + case 0x48: case 0x58: case 0x68: case 0x78: + voice[v].ENVX = data; + break; + case 0x09: case 0x19: case 0x29: case 0x39: + case 0x49: case 0x59: case 0x69: case 0x79: + voice[v].OUTX = data; + break; + + case 0x0f: case 0x1f: case 0x2f: case 0x3f: + case 0x4f: case 0x5f: case 0x6f: case 0x7f: + status.FIR[v] = data; + break; + + case 0x0c: status.MVOLL = data; break; + case 0x1c: status.MVOLR = data; break; + case 0x2c: status.EVOLL = data; break; + case 0x3c: status.EVOLR = data; break; + + case 0x4c: + status.KON = data; + status.kon = data; + break; + case 0x5c: + status.KOFF = data; + break; + case 0x6c: + status.FLG = data; + status.noise_rate = rate_table[data & 0x1f]; + break; + + case 0x7c: + //read-only register, writes clear all bits of ENDX + status.ENDX = 0; + break; + + case 0x0d: status.EFB = data; break; + case 0x2d: status.PMON = data; break; + case 0x3d: status.NON = data; break; + case 0x4d: status.EON = data; break; + case 0x5d: status.DIR = data; break; + case 0x6d: status.ESA = data; break; + case 0x7d: status.EDL = data; break; + } + + dspram[addr] = data; +} + +void aDSP::power() { + spcram = r_smp->get_spcram_handle(); + memset(dspram, 0x00, 128); + + for(int v = 0; v < 8; v++) { + voice[v].VOLL = 0; + voice[v].VOLR = 0; + voice[v].PITCH = 0; + voice[v].SRCN = 0; + voice[v].ADSR1 = 0; + voice[v].ADSR2 = 0; + voice[v].GAIN = 0; + + status.FIR[v] = 0; + } + + status.FLG = 0xe0; + status.MVOLL = status.MVOLR = 0; + status.EVOLL = status.EVOLR = 0; + status.ENDX = 0; + status.EFB = 0; + status.PMON = 0; + status.NON = 0; + status.EON = 0; + status.DIR = 0; + status.ESA = 0; + status.EDL = 0; + + status.echo_length = 0; + + reset(); +} + +void aDSP::reset() { + status.KON = 0x00; + status.KOFF = 0x00; + status.FLG |= 0xe0; + + status.kon = 0x00; + status.esa = 0x00; + + status.noise_ctr = 0; + status.noise_rate = 0; + status.noise_sample = 0x4000; + + status.echo_index = 0; + status.fir_buffer_index = 0; + + for(int v = 0; v < 8; v++) { + voice[v].ENVX = 0; + voice[v].OUTX = 0; + + voice[v].pitch_ctr = 0; + + voice[v].brr_index = 0; + voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2)); + voice[v].brr_looped = false; + voice[v].brr_data[0] = 0; + voice[v].brr_data[1] = 0; + voice[v].brr_data[2] = 0; + voice[v].brr_data[3] = 0; + voice[v].brr_data_index = 0; + + voice[v].envx = 0; + voice[v].env_ctr = 0; + voice[v].env_rate = 0; + voice[v].env_state = SILENCE; + voice[v].env_mode = DIRECT; + + status.fir_buffer[0][v] = 0; + status.fir_buffer[1][v] = 0; + } + + dsp_counter = 0; +} + +void aDSP::run() { +uint8 pmon; +int32 sample; +int32 msamplel, msampler; +int32 esamplel, esampler; +int32 fir_samplel, fir_sampler; + pmon = status.PMON & ~status.NON & ~1; + + if((dsp_counter++ & 1) == 0) { + for(uint v = 0; v < 8; v++) { + if(status.soft_reset()) { + if(voice[v].env_state != SILENCE) { + voice[v].env_state = SILENCE; + voice[v].AdjustEnvelope(); + } + } + if(status.KOFF & (1 << v)) { + if(voice[v].env_state != SILENCE && voice[v].env_state != RELEASE) { + voice[v].env_state = RELEASE; + voice[v].AdjustEnvelope(); + } + } + if(status.kon & (1 << v)) { + voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2)); + voice[v].brr_index = -9; + voice[v].brr_looped = false; + voice[v].brr_data[0] = 0; + voice[v].brr_data[1] = 0; + voice[v].brr_data[2] = 0; + voice[v].brr_data[3] = 0; + voice[v].envx = 0; + voice[v].env_state = ATTACK; + voice[v].AdjustEnvelope(); + } + } + status.ENDX &= ~status.kon; + status.kon = 0; + } + +/***** + * update noise + *****/ + status.noise_ctr += status.noise_rate; + if(status.noise_ctr >= 0x7800) { + status.noise_ctr -= 0x7800; + status.noise_sample = (status.noise_sample >> 1) | (((status.noise_sample << 14) ^ (status.noise_sample << 13)) & 0x4000); + } + + msamplel = msampler = 0; + esamplel = esampler = 0; + +/***** + * process voice channels + *****/ + for(int v = 0; v < 8; v++) { + if(voice[v].brr_index < -1) { + voice[v].brr_index++; + voice[v].OUTX = voice[v].outx = 0; + voice[v].ENVX = 0; + continue; + } + + if(voice[v].brr_index >= 0) { + if(pmon & (1 << v)) { + voice[v].pitch_ctr += (voice[v].pitch_rate() * (voice[v - 1].outx + 0x8000)) >> 15; + } else { + voice[v].pitch_ctr += voice[v].pitch_rate(); + } + } else { + voice[v].pitch_ctr = 0x3000; + voice[v].brr_index = 0; + } + + /***** + * decode BRR samples + *****/ + while(voice[v].pitch_ctr >= 0) { + voice[v].pitch_ctr -= 0x1000; + + voice[v].brr_data_index++; + voice[v].brr_data_index &= 3; + + if(voice[v].brr_index == 0) { + voice[v].brr_header = readb(voice[v].brr_ptr); + + if(voice[v].brr_header_flags() == BRR_END) { + status.ENDX |= (1 << v); + voice[v].env_state = SILENCE; + voice[v].AdjustEnvelope(); + } + } + +#define S(x) voice[v].brr_data[(voice[v].brr_data_index + (x)) & 3] + if(voice[v].env_state != SILENCE) { + sample = readb(voice[v].brr_ptr + 1 + (voice[v].brr_index >> 1)); + if(voice[v].brr_index & 1) { + sample = sclip<4>(sample); + } else { + sample = sclip<4>(sample >> 4); + } + + if(voice[v].brr_header_shift() <= 12) { + sample = (sample << voice[v].brr_header_shift() >> 1); + } else { + sample &= ~0x7ff; + } + + switch(voice[v].brr_header_filter()) { + case 0: //direct + break; + case 1: //15/16 + sample += S(-1) + ((-S(-1)) >> 4); + break; + case 2: //61/32 - 15/16 + sample += (S(-1) << 1) + ((-((S(-1) << 1) + S(-1))) >> 5) + - S(-2) + (S(-2) >> 4); + break; + case 3: //115/64 - 13/16 + sample += (S(-1) << 1) + ((-(S(-1) + (S(-1) << 2) + (S(-1) << 3))) >> 6) + - S(-2) + (((S(-2) << 1) + S(-2)) >> 4); + break; + } + + S(0) = sample = sclip<15>(sclamp<16>(sample)); + } else { + S(0) = sample = 0; + } + + if(++voice[v].brr_index > 15) { + voice[v].brr_index = 0; + if(voice[v].brr_header_flags() & BRR_END) { + if(voice[v].brr_header_flags() & BRR_LOOP) { + status.ENDX |= (1 << v); + } + voice[v].brr_ptr = readw((status.DIR << 8) + (voice[v].SRCN << 2) + 2); + voice[v].brr_looped = true; + } else { + voice[v].brr_ptr += 9; + } + } + } + + /***** + * volume envelope adjust + *****/ + voice[v].env_ctr += voice[v].env_rate; + + if(voice[v].env_ctr >= 0x7800) { + voice[v].env_ctr -= 0x7800; + switch(voice[v].env_mode) { + case DIRECT: + voice[v].env_rate = 0; + break; + case LINEAR_DEC: + voice[v].envx -= 32; + if(voice[v].envx <= 0) { + voice[v].envx = 0; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + } + break; + case LINEAR_INC: + voice[v].envx += 32; + if(voice[v].envx >= 0x7ff) { + voice[v].envx = 0x7ff; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + if(voice[v].ADSR_enabled() && voice[v].env_state == ATTACK) { + voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY); + voice[v].AdjustEnvelope(); + } + } + break; + case EXP_DEC: + //multiply by 255/256ths + voice[v].envx -= ((voice[v].envx - 1) >> 8) + 1; + if(voice[v].ADSR_enabled() && voice[v].env_state == DECAY && voice[v].envx <= voice[v].env_sustain) { + voice[v].env_state = SUSTAIN; + voice[v].AdjustEnvelope(); + } else if(voice[v].envx <= 0) { + voice[v].envx = 0; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + } + break; + case BENT_INC: + if(voice[v].envx < 0x600) { + voice[v].envx += 32; + } else { + voice[v].envx += 8; + + if(voice[v].envx >= 0x7ff) { + voice[v].envx = 0x7ff; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + } + } + break; + case FAST_ATTACK: + voice[v].envx += 0x400; + if(voice[v].envx >= 0x7ff) { + voice[v].envx = 0x7ff; + + //attack raises to max envx. if sustain is also set to max envx, skip decay phase + voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY); + voice[v].AdjustEnvelope(); + } + break; + case RELEASE_DEC: + voice[v].envx -= 8; + if(voice[v].envx <= 0) { + voice[v].env_state = SILENCE; + voice[v].AdjustEnvelope(); + } + break; + } + } + + voice[v].ENVX = voice[v].envx >> 4; + + /***** + * gaussian interpolation / noise + *****/ + if(status.NON & (1 << v)) { + sample = sclip<15>(status.noise_sample); + } else { + int32 d = voice[v].pitch_ctr >> 4; //-256 <= sample <= -1 + sample = ((gaussian_table[ -1 - d] * S(-3)) >> 11); + sample += ((gaussian_table[255 - d] * S(-2)) >> 11); + sample += ((gaussian_table[512 + d] * S(-1)) >> 11); + sample = sclip <15>(sample); + sample += ((gaussian_table[256 + d] * S( 0)) >> 11); + sample = sclamp<15>(sample); + } +#undef S + + /***** + * envelope / volume adjust + *****/ + sample = (sample * voice[v].envx) >> 11; + voice[v].outx = sample << 1; + voice[v].OUTX = sample >> 7; + + if(!status.mute()) { + msamplel += ((sample * voice[v].VOLL) >> 7) << 1; + msampler += ((sample * voice[v].VOLR) >> 7) << 1; + } + + if((status.EON & (1 << v)) && status.echo_write()) { + esamplel += ((sample * voice[v].VOLL) >> 7) << 1; + esampler += ((sample * voice[v].VOLR) >> 7) << 1; + } + } + +/***** + * echo (FIR) adjust + *****/ +#define F(c,x) status.fir_buffer[c][(status.fir_buffer_index + (x)) & 7] + status.fir_buffer_index++; + F(0,0) = readw((status.esa << 8) + status.echo_index + 0); + F(1,0) = readw((status.esa << 8) + status.echo_index + 2); + + fir_samplel = (F(0,-0) * status.FIR[7] + + F(0,-1) * status.FIR[6] + + F(0,-2) * status.FIR[5] + + F(0,-3) * status.FIR[4] + + F(0,-4) * status.FIR[3] + + F(0,-5) * status.FIR[2] + + F(0,-6) * status.FIR[1] + + F(0,-7) * status.FIR[0]); + + fir_sampler = (F(1,-0) * status.FIR[7] + + F(1,-1) * status.FIR[6] + + F(1,-2) * status.FIR[5] + + F(1,-3) * status.FIR[4] + + F(1,-4) * status.FIR[3] + + F(1,-5) * status.FIR[2] + + F(1,-6) * status.FIR[1] + + F(1,-7) * status.FIR[0]); +#undef F + +/***** + * update echo buffer + *****/ + if(status.echo_write()) { + esamplel += (fir_samplel * status.EFB) >> 14; + esampler += (fir_sampler * status.EFB) >> 14; + + esamplel = sclamp<16>(esamplel); + esampler = sclamp<16>(esampler); + + writew((status.esa << 8) + status.echo_index + 0, esamplel); + writew((status.esa << 8) + status.echo_index + 2, esampler); + } + + status.echo_index += 4; + if(status.echo_index >= status.echo_length) { + status.echo_index = 0; + status.echo_length = (status.EDL & 0x0f) << 11; + } + +//ESA read occurs at roughly 22/32th sample +//ESA fetch occurs at roughly 29/32th sample +//as this is not a subsample-level S-DSP emulator, +//simulate ~25/32th delay by caching ESA for one +//complete sample ... + status.esa = status.ESA; + +/***** + * main output adjust + *****/ + if(!status.mute()) { + msamplel = (msamplel * status.MVOLL) >> 7; + msampler = (msampler * status.MVOLR) >> 7; + + msamplel += (fir_samplel * status.EVOLL) >> 14; + msampler += (fir_sampler * status.EVOLR) >> 14; + + msamplel = sclamp<16>(msamplel); + msampler = sclamp<16>(msampler); + } + + audio.sample(msamplel, msampler); + scheduler.addclocks_dsp(32 * 3 * 8); + scheduler.sync_dspsmp(); +} + +aDSP::aDSP() {} +aDSP::~aDSP() {} + +}; diff --git a/mednafen/snes/src/dsp/adsp/adsp.hpp b/mednafen/snes/src/dsp/adsp/adsp.hpp new file mode 100755 index 0000000..18d1f91 --- /dev/null +++ b/mednafen/snes/src/dsp/adsp/adsp.hpp @@ -0,0 +1,174 @@ +class aDSP : public DSP { +private: +uint8 dspram[128]; +uint8 *spcram; + +uint32 dsp_counter; + +enum { BRR_END = 1, BRR_LOOP = 2 }; + +uint8 readb (uint16 addr); +uint16 readw (uint16 addr); +void writeb(uint16 addr, uint8 data); +void writew(uint16 addr, uint16 data); + +public: +static const uint16 rate_table[32]; +static const int16 gaussian_table[512]; + +enum EnvelopeStates { + ATTACK, + DECAY, + SUSTAIN, + RELEASE, + SILENCE +}; + +enum EnvelopeModes { + DIRECT, + LINEAR_DEC, + EXP_DEC, + LINEAR_INC, + BENT_INC, + + FAST_ATTACK, + RELEASE_DEC +}; + +private: +struct Status { +//$0c,$1c + int8 MVOLL, MVOLR; +//$2c,$3c + int8 EVOLL, EVOLR; +//$4c,$5c + uint8 KON, KOFF; +//$6c + uint8 FLG; +//$7c + uint8 ENDX; +//$0d + int8 EFB; +//$2d,$3d,$4d + uint8 PMON, NON, EON; +//$5d + uint8 DIR; +//$6d,$7d + uint8 ESA, EDL; + +//$xf + int8 FIR[8]; + +//internal variables + uint8 kon, esa; + + int16 noise_ctr, noise_rate; + uint16 noise_sample; + + uint16 echo_index, echo_length; + int16 fir_buffer[2][8]; + uint8 fir_buffer_index; + +//functions + bool soft_reset() { return !!(FLG & 0x80); } + bool mute() { return !!(FLG & 0x40); } + bool echo_write() { return !(FLG & 0x20); } +} status; + +struct Voice { +//$x0-$x1 + int8 VOLL, VOLR; +//$x2-$x3 + int16 PITCH; +//$x4 + uint8 SRCN; +//$x5-$x7 + uint8 ADSR1, ADSR2, GAIN; +//$x8-$x9 + uint8 ENVX, OUTX; + +//internal variables + int16 pitch_ctr; + + int8 brr_index; + uint16 brr_ptr; + uint8 brr_header; + bool brr_looped; + + int16 brr_data[4]; + uint8 brr_data_index; + + int16 envx; + uint16 env_ctr, env_rate, env_sustain; + enum EnvelopeStates env_state; + enum EnvelopeModes env_mode; + + int16 outx; + +//functions + int16 pitch_rate() { return PITCH & 0x3fff; } + + uint8 brr_header_shift() { return brr_header >> 4; } + uint8 brr_header_filter() { return (brr_header >> 2) & 3; } + uint8 brr_header_flags() { return brr_header & 3; } + + bool ADSR_enabled() { return !!(ADSR1 & 0x80); } + uint8 ADSR_decay() { return (ADSR1 >> 4) & 7; } + uint8 ADSR_attack() { return ADSR1 & 15; } + uint8 ADSR_sus_level() { return ADSR2 >> 5; } + uint8 ADSR_sus_rate() { return ADSR2 & 31; } + + void AdjustEnvelope() { + if(env_state == SILENCE) { + env_mode = DIRECT; + env_rate = 0; + envx = 0; + } else if(env_state == RELEASE) { + env_mode = RELEASE_DEC; + env_rate = 0x7800; + } else if(ADSR_enabled()) { + switch(env_state) { + case ATTACK: + env_rate = rate_table[(ADSR_attack() << 1) + 1]; + env_mode = (env_rate == 0x7800) ? FAST_ATTACK : LINEAR_INC; + break; + case DECAY: + env_rate = rate_table[(ADSR_decay() << 1) + 0x10]; + env_mode = EXP_DEC; + break; + case SUSTAIN: + env_rate = rate_table[ADSR_sus_rate()]; + env_mode = (env_rate == 0) ? DIRECT : EXP_DEC; + break; + } + } else if(GAIN & 0x80) { + switch(GAIN & 0x60) { + case 0x00: env_mode = LINEAR_DEC; break; + case 0x20: env_mode = EXP_DEC; break; + case 0x40: env_mode = LINEAR_INC; break; + case 0x60: env_mode = BENT_INC; break; + } + env_rate = rate_table[GAIN & 0x1f]; + } else { + env_mode = DIRECT; + env_rate = 0; + envx = (GAIN & 0x7f) << 4; + } + } +} voice[8]; + +public: + void enter(); + void run(); + + uint8 read (uint8 addr); + void write(uint8 addr, uint8 data); + + void power(); + void reset(); + + aDSP(); + ~aDSP(); +}; + +extern aDSP dsp; diff --git a/mednafen/snes/src/dsp/adsp/adsp_tables.cpp b/mednafen/snes/src/dsp/adsp/adsp_tables.cpp new file mode 100755 index 0000000..ea54902 --- /dev/null +++ b/mednafen/snes/src/dsp/adsp/adsp_tables.cpp @@ -0,0 +1,77 @@ +#ifdef ADSP_CPP + +const uint16 aDSP::rate_table[32] = { + 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, + 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, + 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00, + 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800 +}; + +const int16 aDSP::gaussian_table[512] = { + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, + 0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008, + 0x008, 0x008, 0x009, 0x009, 0x009, 0x00A, 0x00A, 0x00A, + 0x00B, 0x00B, 0x00B, 0x00C, 0x00C, 0x00D, 0x00D, 0x00E, + 0x00E, 0x00F, 0x00F, 0x00F, 0x010, 0x010, 0x011, 0x011, + 0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016, + 0x017, 0x017, 0x018, 0x018, 0x019, 0x01A, 0x01B, 0x01B, + 0x01C, 0x01D, 0x01D, 0x01E, 0x01F, 0x020, 0x020, 0x021, + 0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028, + 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030, + 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, + 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x040, 0x041, 0x042, + 0x043, 0x045, 0x046, 0x047, 0x049, 0x04A, 0x04C, 0x04D, + 0x04E, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059, + 0x05A, 0x05C, 0x05E, 0x05F, 0x061, 0x063, 0x064, 0x066, + 0x068, 0x06A, 0x06B, 0x06D, 0x06F, 0x071, 0x073, 0x075, + 0x076, 0x078, 0x07A, 0x07C, 0x07E, 0x080, 0x082, 0x084, + 0x086, 0x089, 0x08B, 0x08D, 0x08F, 0x091, 0x093, 0x096, + 0x098, 0x09A, 0x09C, 0x09F, 0x0A1, 0x0A3, 0x0A6, 0x0A8, + 0x0AB, 0x0AD, 0x0AF, 0x0B2, 0x0B4, 0x0B7, 0x0BA, 0x0BC, + 0x0BF, 0x0C1, 0x0C4, 0x0C7, 0x0C9, 0x0CC, 0x0CF, 0x0D2, + 0x0D4, 0x0D7, 0x0DA, 0x0DD, 0x0E0, 0x0E3, 0x0E6, 0x0E9, + 0x0EC, 0x0EF, 0x0F2, 0x0F5, 0x0F8, 0x0FB, 0x0FE, 0x101, + 0x104, 0x107, 0x10B, 0x10E, 0x111, 0x114, 0x118, 0x11B, + 0x11E, 0x122, 0x125, 0x129, 0x12C, 0x130, 0x133, 0x137, + 0x13A, 0x13E, 0x141, 0x145, 0x148, 0x14C, 0x150, 0x153, + 0x157, 0x15B, 0x15F, 0x162, 0x166, 0x16A, 0x16E, 0x172, + 0x176, 0x17A, 0x17D, 0x181, 0x185, 0x189, 0x18D, 0x191, + 0x195, 0x19A, 0x19E, 0x1A2, 0x1A6, 0x1AA, 0x1AE, 0x1B2, + 0x1B7, 0x1BB, 0x1BF, 0x1C3, 0x1C8, 0x1CC, 0x1D0, 0x1D5, + 0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1EB, 0x1EF, 0x1F3, 0x1F8, + 0x1FC, 0x201, 0x205, 0x20A, 0x20F, 0x213, 0x218, 0x21C, + 0x221, 0x226, 0x22A, 0x22F, 0x233, 0x238, 0x23D, 0x241, + 0x246, 0x24B, 0x250, 0x254, 0x259, 0x25E, 0x263, 0x267, + 0x26C, 0x271, 0x276, 0x27B, 0x280, 0x284, 0x289, 0x28E, + 0x293, 0x298, 0x29D, 0x2A2, 0x2A6, 0x2AB, 0x2B0, 0x2B5, + 0x2BA, 0x2BF, 0x2C4, 0x2C9, 0x2CE, 0x2D3, 0x2D8, 0x2DC, + 0x2E1, 0x2E6, 0x2EB, 0x2F0, 0x2F5, 0x2FA, 0x2FF, 0x304, + 0x309, 0x30E, 0x313, 0x318, 0x31D, 0x322, 0x326, 0x32B, + 0x330, 0x335, 0x33A, 0x33F, 0x344, 0x349, 0x34E, 0x353, + 0x357, 0x35C, 0x361, 0x366, 0x36B, 0x370, 0x374, 0x379, + 0x37E, 0x383, 0x388, 0x38C, 0x391, 0x396, 0x39B, 0x39F, + 0x3A4, 0x3A9, 0x3AD, 0x3B2, 0x3B7, 0x3BB, 0x3C0, 0x3C5, + 0x3C9, 0x3CE, 0x3D2, 0x3D7, 0x3DC, 0x3E0, 0x3E5, 0x3E9, + 0x3ED, 0x3F2, 0x3F6, 0x3FB, 0x3FF, 0x403, 0x408, 0x40C, + 0x410, 0x415, 0x419, 0x41D, 0x421, 0x425, 0x42A, 0x42E, + 0x432, 0x436, 0x43A, 0x43E, 0x442, 0x446, 0x44A, 0x44E, + 0x452, 0x455, 0x459, 0x45D, 0x461, 0x465, 0x468, 0x46C, + 0x470, 0x473, 0x477, 0x47A, 0x47E, 0x481, 0x485, 0x488, + 0x48C, 0x48F, 0x492, 0x496, 0x499, 0x49C, 0x49F, 0x4A2, + 0x4A6, 0x4A9, 0x4AC, 0x4AF, 0x4B2, 0x4B5, 0x4B7, 0x4BA, + 0x4BD, 0x4C0, 0x4C3, 0x4C5, 0x4C8, 0x4CB, 0x4CD, 0x4D0, + 0x4D2, 0x4D5, 0x4D7, 0x4D9, 0x4DC, 0x4DE, 0x4E0, 0x4E3, + 0x4E5, 0x4E7, 0x4E9, 0x4EB, 0x4ED, 0x4EF, 0x4F1, 0x4F3, + 0x4F5, 0x4F6, 0x4F8, 0x4FA, 0x4FB, 0x4FD, 0x4FF, 0x500, + 0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50A, 0x50B, + 0x50C, 0x50D, 0x50E, 0x50F, 0x510, 0x511, 0x511, 0x512, + 0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517, + 0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519 +}; + +#endif diff --git a/mednafen/snes/src/dsp/dsp.hpp b/mednafen/snes/src/dsp/dsp.hpp new file mode 100755 index 0000000..ae95095 --- /dev/null +++ b/mednafen/snes/src/dsp/dsp.hpp @@ -0,0 +1,12 @@ +class DSP { +public: + virtual void enter() = 0; + + virtual uint8 read(uint8 addr) = 0; + virtual void write(uint8 addr, uint8 data) = 0; + + virtual void power() = 0; + virtual void reset() = 0; + + virtual void serialize(serializer&) {} +}; diff --git a/mednafen/snes/src/dsp/sdsp/brr.cpp b/mednafen/snes/src/dsp/sdsp/brr.cpp new file mode 100755 index 0000000..ba5f0a7 --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/brr.cpp @@ -0,0 +1,62 @@ +#ifdef SDSP_CPP + +void sDSP::brr_decode(voice_t &v) { + //state.t_brr_byte = ram[v.brr_addr + v.brr_offset] cached from previous clock cycle + int nybbles = (state.t_brr_byte << 8) + memory::apuram[(uint16)(v.brr_addr + v.brr_offset + 1)]; + + const int filter = (state.t_brr_header >> 2) & 3; + const int scale = (state.t_brr_header >> 4); + + //decode four samples + for(unsigned i = 0; i < 4; i++) { + //bits 12-15 = current nybble; sign extend, then shift right to 4-bit precision + //result: s = 4-bit sign-extended sample value + int s = (int16)nybbles >> 12; + nybbles <<= 4; //slide nybble so that on next loop iteration, bits 12-15 = current nybble + + if(scale <= 12) { + s <<= scale; + s >>= 1; + } else { + s &= ~0x7ff; + } + + //apply IIR filter (2 is the most commonly used) + const int p1 = v.buffer[v.buf_pos - 1]; + const int p2 = v.buffer[v.buf_pos - 2] >> 1; + + switch(filter) { + case 0: break; //no filter + + case 1: { + //s += p1 * 0.46875 + s += p1 >> 1; + s += (-p1) >> 5; + } break; + + case 2: { + //s += p1 * 0.953125 - p2 * 0.46875 + s += p1; + s -= p2; + s += p2 >> 4; + s += (p1 * -3) >> 6; + } break; + + case 3: { + //s += p1 * 0.8984375 - p2 * 0.40625 + s += p1; + s -= p2; + s += (p1 * -13) >> 7; + s += (p2 * 3) >> 4; + } break; + } + + //adjust and write sample + s = sclamp<16>(s); + s = (int16)(s << 1); + v.buffer.write(v.buf_pos++, s); + if(v.buf_pos >= brr_buf_size) v.buf_pos = 0; + } +} + +#endif diff --git a/mednafen/snes/src/dsp/sdsp/counter.cpp b/mednafen/snes/src/dsp/sdsp/counter.cpp new file mode 100755 index 0000000..577e15f --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/counter.cpp @@ -0,0 +1,52 @@ +#ifdef SDSP_CPP + +//counter_rate = number of samples per counter event +//all rates are evenly divisible by counter_range (0x7800, 30720, or 2048 * 5 * 3) +//note that rate[0] is a special case, which never triggers + +const uint16 sDSP::counter_rate[32] = { + 0, 2048, 1536, + 1280, 1024, 768, + 640, 512, 384, + 320, 256, 192, + 160, 128, 96, + 80, 64, 48, + 40, 32, 24, + 20, 16, 12, + 10, 8, 6, + 5, 4, 3, + 2, + 1, +}; + +//counter_offset = counter offset from zero +//counters do not appear to be aligned at zero for all rates + +const uint16 sDSP::counter_offset[32] = { + 0, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 0, + 0, +}; + +inline void sDSP::counter_tick() { + state.counter--; + if(state.counter < 0) state.counter = counter_range - 1; +} + +//return true if counter event should trigger + +inline bool sDSP::counter_poll(unsigned rate) { + if(rate == 0) return false; + return (((unsigned)state.counter + counter_offset[rate]) % counter_rate[rate]) == 0; +} + +#endif diff --git a/mednafen/snes/src/dsp/sdsp/debugger/debugger.cpp b/mednafen/snes/src/dsp/sdsp/debugger/debugger.cpp new file mode 100755 index 0000000..1debefa --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/debugger/debugger.cpp @@ -0,0 +1,3 @@ +#ifdef SDSP_CPP + +#endif diff --git a/mednafen/snes/src/dsp/sdsp/debugger/debugger.hpp b/mednafen/snes/src/dsp/sdsp/debugger/debugger.hpp new file mode 100755 index 0000000..5f152b4 --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/debugger/debugger.hpp @@ -0,0 +1,3 @@ +class sDSPDebugger : public sDSP { +public: +}; diff --git a/mednafen/snes/src/dsp/sdsp/echo.cpp b/mednafen/snes/src/dsp/sdsp/echo.cpp new file mode 100755 index 0000000..f28babc --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/echo.cpp @@ -0,0 +1,135 @@ +#ifdef SDSP_CPP + +int sDSP::calc_fir(int i, bool channel) { + int s = state.echo_hist[channel][state.echo_hist_pos + i + 1]; + return (s * (int8)REG(fir + i * 0x10)) >> 6; +} + +int sDSP::echo_output(bool channel) { + int output = (int16)((state.t_main_out[channel] * (int8)REG(mvoll + channel * 0x10)) >> 7) + + (int16)((state.t_echo_in [channel] * (int8)REG(evoll + channel * 0x10)) >> 7); + return sclamp<16>(output); +} + +void sDSP::echo_read(bool channel) { + unsigned addr = state.t_echo_ptr + channel * 2; + uint8 lo = memory::apuram[(uint16)(addr + 0)]; + uint8 hi = memory::apuram[(uint16)(addr + 1)]; + int s = (int16)((hi << 8) + lo); + state.echo_hist[channel].write(state.echo_hist_pos, s >> 1); +} + +void sDSP::echo_write(bool channel) { + if(!(state.t_echo_disabled & 0x20)) { + unsigned addr = state.t_echo_ptr + channel * 2; + int s = state.t_echo_out[channel]; + memory::apuram[(uint16)(addr + 0)] = s; + memory::apuram[(uint16)(addr + 1)] = s >> 8; + } + + state.t_echo_out[channel] = 0; +} + +void sDSP::echo_22() { + //history + state.echo_hist_pos++; + if(state.echo_hist_pos >= echo_hist_size) state.echo_hist_pos = 0; + + state.t_echo_ptr = (uint16)((state.t_esa << 8) + state.echo_offset); + echo_read(0); + + //FIR + int l = calc_fir(0, 0); + int r = calc_fir(0, 1); + + state.t_echo_in[0] = l; + state.t_echo_in[1] = r; +} + +void sDSP::echo_23() { + int l = calc_fir(1, 0) + calc_fir(2, 0); + int r = calc_fir(1, 1) + calc_fir(2, 1); + + state.t_echo_in[0] += l; + state.t_echo_in[1] += r; + + echo_read(1); +} + +void sDSP::echo_24() { + int l = calc_fir(3, 0) + calc_fir(4, 0) + calc_fir(5, 0); + int r = calc_fir(3, 1) + calc_fir(4, 1) + calc_fir(5, 1); + + state.t_echo_in[0] += l; + state.t_echo_in[1] += r; +} + +void sDSP::echo_25() { + int l = state.t_echo_in[0] + calc_fir(6, 0); + int r = state.t_echo_in[1] + calc_fir(6, 1); + + l = (int16)l; + r = (int16)r; + + l += (int16)calc_fir(7, 0); + r += (int16)calc_fir(7, 1); + + state.t_echo_in[0] = sclamp<16>(l) & ~1; + state.t_echo_in[1] = sclamp<16>(r) & ~1; +} + +void sDSP::echo_26() { + //left output volumes + //(save sample for next clock so we can output both together) + state.t_main_out[0] = echo_output(0); + + //echo feedback + int l = state.t_echo_out[0] + (int16)((state.t_echo_in[0] * (int8)REG(efb)) >> 7); + int r = state.t_echo_out[1] + (int16)((state.t_echo_in[1] * (int8)REG(efb)) >> 7); + + state.t_echo_out[0] = sclamp<16>(l) & ~1; + state.t_echo_out[1] = sclamp<16>(r) & ~1; +} + +void sDSP::echo_27() { + //output + int outl = state.t_main_out[0]; + int outr = echo_output(1); + state.t_main_out[0] = 0; + state.t_main_out[1] = 0; + + //TODO: global muting isn't this simple + //(turns DAC on and off or something, causing small ~37-sample pulse when first muted) + if(REG(flg) & 0x40) { + outl = 0; + outr = 0; + } + + //output sample to DAC + audio.sample(outl, outr); +} + +void sDSP::echo_28() { + state.t_echo_disabled = REG(flg); +} + +void sDSP::echo_29() { + state.t_esa = REG(esa); + + if(!state.echo_offset) state.echo_length = (REG(edl) & 0x0f) << 11; + + state.echo_offset += 4; + if(state.echo_offset >= state.echo_length) state.echo_offset = 0; + + //write left echo + echo_write(0); + + state.t_echo_disabled = REG(flg); +} + +void sDSP::echo_30() { + //write right echo + echo_write(1); +} + +#endif diff --git a/mednafen/snes/src/dsp/sdsp/envelope.cpp b/mednafen/snes/src/dsp/sdsp/envelope.cpp new file mode 100755 index 0000000..edf2de5 --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/envelope.cpp @@ -0,0 +1,62 @@ +#ifdef SDSP_CPP + +void sDSP::envelope_run(voice_t &v) { + int env = v.env; + + if(v.env_mode == env_release) { //60% + env -= 0x8; + if(env < 0) env = 0; + v.env = env; + return; + } + + int rate; + int env_data = VREG(adsr1); + if(state.t_adsr0 & 0x80) { //99% ADSR + if(v.env_mode >= env_decay) { //99% + env--; + env -= env >> 8; + rate = env_data & 0x1f; + if(v.env_mode == env_decay) { //1% + rate = ((state.t_adsr0 >> 3) & 0x0e) + 0x10; + } + } else { //env_attack + rate = ((state.t_adsr0 & 0x0f) << 1) + 1; + env += rate < 31 ? 0x20 : 0x400; + } + } else { //GAIN + env_data = VREG(gain); + int mode = env_data >> 5; + if(mode < 4) { //direct + env = env_data << 4; + rate = 31; + } else { + rate = env_data & 0x1f; + if(mode == 4) { //4: linear decrease + env -= 0x20; + } else if(mode < 6) { //5: exponential decrease + env--; + env -= env >> 8; + } else { //6, 7: linear increase + env += 0x20; + if(mode > 6 && (unsigned)v.hidden_env >= 0x600) { + env += 0x8 - 0x20; //7: two-slope linear increase + } + } + } + } + + //sustain level + if((env >> 8) == (env_data >> 5) && v.env_mode == env_decay) v.env_mode = env_sustain; + v.hidden_env = env; + + //unsigned cast because linear decrease underflowing also triggers this + if((unsigned)env > 0x7ff) { + env = (env < 0 ? 0 : 0x7ff); + if(v.env_mode == env_attack) v.env_mode = env_decay; + } + + if(counter_poll(rate) == true) v.env = env; +} + +#endif diff --git a/mednafen/snes/src/dsp/sdsp/gaussian.cpp b/mednafen/snes/src/dsp/sdsp/gaussian.cpp new file mode 100755 index 0000000..119293e --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/gaussian.cpp @@ -0,0 +1,54 @@ +#ifdef SDSP_CPP + +const int16 sDSP::gaussian_table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, + 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, + 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, + 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, + 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, + 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, + 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, + 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, + 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, + 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, + 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, + 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, + 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, + 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, + 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, + 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, + 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, + 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, + 969, 974, 978, 983, 988, 992, 997, 1001, 1005, 1010, 1014, 1019, 1023, 1027, 1032, 1036, + 1040, 1045, 1049, 1053, 1057, 1061, 1066, 1070, 1074, 1078, 1082, 1086, 1090, 1094, 1098, 1102, + 1106, 1109, 1113, 1117, 1121, 1125, 1128, 1132, 1136, 1139, 1143, 1146, 1150, 1153, 1157, 1160, + 1164, 1167, 1170, 1174, 1177, 1180, 1183, 1186, 1190, 1193, 1196, 1199, 1202, 1205, 1207, 1210, + 1213, 1216, 1219, 1221, 1224, 1227, 1229, 1232, 1234, 1237, 1239, 1241, 1244, 1246, 1248, 1251, + 1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1270, 1272, 1274, 1275, 1277, 1279, 1280, + 1282, 1283, 1284, 1286, 1287, 1288, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1297, 1298, + 1299, 1300, 1300, 1301, 1302, 1302, 1303, 1303, 1303, 1304, 1304, 1304, 1304, 1304, 1305, 1305, +}; + +int sDSP::gaussian_interpolate(const voice_t &v) { + //make pointers into gaussian table based on fractional position between samples + int offset = (v.interp_pos >> 4) & 0xff; + const int16 *fwd = gaussian_table + 255 - offset; + const int16 *rev = gaussian_table + offset; //mirror left half of gaussian table + + offset = v.buf_pos + (v.interp_pos >> 12); + int output; + output = (fwd[ 0] * v.buffer[offset + 0]) >> 11; + output += (fwd[256] * v.buffer[offset + 1]) >> 11; + output += (rev[256] * v.buffer[offset + 2]) >> 11; + output = (int16)output; + output += (rev[ 0] * v.buffer[offset + 3]) >> 11; + return sclamp<16>(output) & ~1; +} + +#endif diff --git a/mednafen/snes/src/dsp/sdsp/misc.cpp b/mednafen/snes/src/dsp/sdsp/misc.cpp new file mode 100755 index 0000000..0510b47 --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/misc.cpp @@ -0,0 +1,35 @@ +#ifdef SDSP_CPP + +void sDSP::misc_27() { + state.t_pmon = REG(pmon) & ~1; //voice 0 doesn't support PMON +} + +void sDSP::misc_28() { + state.t_non = REG(non); + state.t_eon = REG(eon); + state.t_dir = REG(dir); +} + +void sDSP::misc_29() { + state.every_other_sample ^= 1; + if(state.every_other_sample) { + state.new_kon &= ~state.kon; //clears KON 63 clocks after it was last read + } +} + +void sDSP::misc_30() { + if(state.every_other_sample) { + state.kon = state.new_kon; + state.t_koff = REG(koff); + } + + counter_tick(); + + //noise + if(counter_poll(REG(flg) & 0x1f) == true) { + int feedback = (state.noise << 13) ^ (state.noise << 14); + state.noise = (feedback & 0x4000) ^ (state.noise >> 1); + } +} + +#endif diff --git a/mednafen/snes/src/dsp/sdsp/sdsp.cpp b/mednafen/snes/src/dsp/sdsp/sdsp.cpp new file mode 100755 index 0000000..9950402 --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/sdsp.cpp @@ -0,0 +1,338 @@ +//S-DSP emulator +//note: this is basically a C++ cothreaded implementation of Shay Green's (blargg's) S-DSP emulator. +//the actual algorithms, timing information, tables, variable names, etc were all from him. + +#include <../base.hpp> + +#define SDSP_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + sDSPDebugger dsp; +#else + sDSP dsp; +#endif + +#include "serialization.cpp" + +#define REG(n) state.regs[r_##n] +#define VREG(n) state.regs[v.vidx + v_##n] + +#if !defined(DSP_STATE_MACHINE) + #define phase_start() while(true) { \ + while(scheduler.sync == Scheduler::SyncAll) { \ + scheduler.exit(Scheduler::SynchronizeEvent); \ + } + #define phase(n) + #define tick() scheduler.addclocks_dsp(3 * 8); scheduler.sync_dspsmp() + #define phase_end() } +#else + #define phase_start() switch(phase_index) { + #define phase(n) case n: + #define tick() scheduler.addclocks_dsp(3 * 8); break + #define phase_end() } phase_index = (phase_index + 1) & 31; +#endif + +#include "gaussian.cpp" +#include "counter.cpp" +#include "envelope.cpp" +#include "brr.cpp" +#include "misc.cpp" +#include "voice.cpp" +#include "echo.cpp" + +/* timing */ + +void sDSP::enter() { + phase_start() + + phase(0) + voice_5(voice[0]); + voice_2(voice[1]); + tick(); + + phase(1) + voice_6(voice[0]); + voice_3(voice[1]); + tick(); + + phase(2) + voice_7(voice[0]); + voice_4(voice[1]); + voice_1(voice[3]); + tick(); + + phase(3) + voice_8(voice[0]); + voice_5(voice[1]); + voice_2(voice[2]); + tick(); + + phase(4) + voice_9(voice[0]); + voice_6(voice[1]); + voice_3(voice[2]); + tick(); + + phase(5) + voice_7(voice[1]); + voice_4(voice[2]); + voice_1(voice[4]); + tick(); + + phase(6) + voice_8(voice[1]); + voice_5(voice[2]); + voice_2(voice[3]); + tick(); + + phase(7) + voice_9(voice[1]); + voice_6(voice[2]); + voice_3(voice[3]); + tick(); + + phase(8) + voice_7(voice[2]); + voice_4(voice[3]); + voice_1(voice[5]); + tick(); + + phase(9) + voice_8(voice[2]); + voice_5(voice[3]); + voice_2(voice[4]); + tick(); + + phase(10) + voice_9(voice[2]); + voice_6(voice[3]); + voice_3(voice[4]); + tick(); + + phase(11) + voice_7(voice[3]); + voice_4(voice[4]); + voice_1(voice[6]); + tick(); + + phase(12) + voice_8(voice[3]); + voice_5(voice[4]); + voice_2(voice[5]); + tick(); + + phase(13) + voice_9(voice[3]); + voice_6(voice[4]); + voice_3(voice[5]); + tick(); + + phase(14) + voice_7(voice[4]); + voice_4(voice[5]); + voice_1(voice[7]); + tick(); + + phase(15) + voice_8(voice[4]); + voice_5(voice[5]); + voice_2(voice[6]); + tick(); + + phase(16) + voice_9(voice[4]); + voice_6(voice[5]); + voice_3(voice[6]); + tick(); + + phase(17) + voice_1(voice[0]); + voice_7(voice[5]); + voice_4(voice[6]); + tick(); + + phase(18) + voice_8(voice[5]); + voice_5(voice[6]); + voice_2(voice[7]); + tick(); + + phase(19) + voice_9(voice[5]); + voice_6(voice[6]); + voice_3(voice[7]); + tick(); + + phase(20) + voice_1(voice[1]); + voice_7(voice[6]); + voice_4(voice[7]); + tick(); + + phase(21) + voice_8(voice[6]); + voice_5(voice[7]); + voice_2(voice[0]); + tick(); + + phase(22) + voice_3a(voice[0]); + voice_9(voice[6]); + voice_6(voice[7]); + echo_22(); + tick(); + + phase(23) + voice_7(voice[7]); + echo_23(); + tick(); + + phase(24) + voice_8(voice[7]); + echo_24(); + tick(); + + phase(25) + voice_3b(voice[0]); + voice_9(voice[7]); + echo_25(); + tick(); + + phase(26) + echo_26(); + tick(); + + phase(27) + misc_27(); + echo_27(); + tick(); + + phase(28) + misc_28(); + echo_28(); + tick(); + + phase(29) + misc_29(); + echo_29(); + tick(); + + phase(30) + misc_30(); + voice_3c(voice[0]); + echo_30(); + tick(); + + phase(31) + voice_4(voice[0]); + voice_1(voice[2]); + tick(); + + phase_end() +} + +/* register interface for S-SMP $00f2,$00f3 */ + +uint8 sDSP::read(uint8 addr) { + return state.regs[addr]; +} + +void sDSP::write(uint8 addr, uint8 data) { + state.regs[addr] = data; + + if((addr & 0x0f) == v_envx) { + state.envx_buf = data; + } else if((addr & 0x0f) == v_outx) { + state.outx_buf = data; + } else if(addr == r_kon) { + state.new_kon = data; + } else if(addr == r_endx) { + //always cleared, regardless of data written + state.endx_buf = 0; + state.regs[r_endx] = 0; + } +} + +/* initialization */ + +void sDSP::power() { + memset(&state.regs, 0, sizeof state.regs); + state.echo_hist_pos = 0; + state.every_other_sample = false; + state.kon = 0; + state.noise = 0; + state.counter = 0; + state.echo_offset = 0; + state.echo_length = 0; + state.new_kon = 0; + state.endx_buf = 0; + state.envx_buf = 0; + state.outx_buf = 0; + state.t_pmon = 0; + state.t_non = 0; + state.t_eon = 0; + state.t_dir = 0; + state.t_koff = 0; + state.t_brr_next_addr = 0; + state.t_adsr0 = 0; + state.t_brr_header = 0; + state.t_brr_byte = 0; + state.t_srcn = 0; + state.t_esa = 0; + state.t_echo_disabled = 0; + state.t_dir_addr = 0; + state.t_pitch = 0; + state.t_output = 0; + state.t_looped = 0; + state.t_echo_ptr = 0; + state.t_main_out[0] = state.t_main_out[1] = 0; + state.t_echo_out[0] = state.t_echo_out[1] = 0; + state.t_echo_in[0] = state.t_echo_in[1] = 0; + + for(unsigned i = 0; i < 8; i++) { + voice[i].buf_pos = 0; + voice[i].interp_pos = 0; + voice[i].brr_addr = 0; + voice[i].brr_offset = 1; + voice[i].vbit = 1 << i; + voice[i].vidx = i * 0x10; + voice[i].kon_delay = 0; + voice[i].env_mode = env_release; + voice[i].env = 0; + voice[i].t_envx_out = 0; + voice[i].hidden_env = 0; + } + + reset(); +} + +void sDSP::reset() { + REG(flg) = 0xe0; + + state.noise = 0x4000; + state.echo_hist_pos = 0; + state.every_other_sample = 1; + state.echo_offset = 0; + state.counter = 0; + + phase_index = 0; +} + +sDSP::sDSP() { + nall_static_assert= 32 / 8>(); //int >= 32-bits + nall_static_assert<(int8)0x80 == -0x80>(); //8-bit sign extension + nall_static_assert<(int16)0x8000 == -0x8000>(); //16-bit sign extension + nall_static_assert<(uint16)0xffff0000 == 0>(); //16-bit unsigned clip + nall_static_assert<(-1 >> 1) == -1>(); //arithmetic shift right + + //-0x8000 <= n <= +0x7fff + assert(sclamp<16>(+0x8000) == +0x7fff); + assert(sclamp<16>(-0x8001) == -0x8000); +} + +sDSP::~sDSP() { +} + +}; diff --git a/mednafen/snes/src/dsp/sdsp/sdsp.hpp b/mednafen/snes/src/dsp/sdsp/sdsp.hpp new file mode 100755 index 0000000..f9b17f0 --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/sdsp.hpp @@ -0,0 +1,176 @@ +class sDSP : public DSP { +public: + void enter(); + + uint8 read(uint8 addr); + void write(uint8 addr, uint8 data); + + void power(); + void reset(); + + void serialize(serializer&); + sDSP(); + ~sDSP(); + +private: + //DSP_STATE_MACHINE variable + unsigned phase_index; + + //global registers + enum global_reg_t { + r_mvoll = 0x0c, r_mvolr = 0x1c, + r_evoll = 0x2c, r_evolr = 0x3c, + r_kon = 0x4c, r_koff = 0x5c, + r_flg = 0x6c, r_endx = 0x7c, + r_efb = 0x0d, r_pmon = 0x2d, + r_non = 0x3d, r_eon = 0x4d, + r_dir = 0x5d, r_esa = 0x6d, + r_edl = 0x7d, r_fir = 0x0f, //8 coefficients at 0x0f, 0x1f, ... 0x7f + }; + + //voice registers + enum voice_reg_t { + v_voll = 0x00, v_volr = 0x01, + v_pitchl = 0x02, v_pitchh = 0x03, + v_srcn = 0x04, v_adsr0 = 0x05, + v_adsr1 = 0x06, v_gain = 0x07, + v_envx = 0x08, v_outx = 0x09, + }; + + //internal envelope modes + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; + + //internal constants + enum { echo_hist_size = 8 }; + enum { brr_buf_size = 12 }; + enum { brr_block_size = 9 }; + + //global state + struct state_t { + uint8 regs[128]; + + modulo_array echo_hist[2]; //echo history keeps most recent 8 samples + int echo_hist_pos; + + bool every_other_sample; //toggles every sample + int kon; //KON value when last checked + int noise; + int counter; + int echo_offset; //offset from ESA in echo buffer + int echo_length; //number of bytes that echo_offset will stop at + + //hidden registers also written to when main register is written to + int new_kon; + int endx_buf; + int envx_buf; + int outx_buf; + + //temporary state between clocks + + //read once per sample + int t_pmon; + int t_non; + int t_eon; + int t_dir; + int t_koff; + + //read a few clocks ahead before used + int t_brr_next_addr; + int t_adsr0; + int t_brr_header; + int t_brr_byte; + int t_srcn; + int t_esa; + int t_echo_disabled; + + //internal state that is recalculated every sample + int t_dir_addr; + int t_pitch; + int t_output; + int t_looped; + int t_echo_ptr; + + //left/right sums + int t_main_out[2]; + int t_echo_out[2]; + int t_echo_in [2]; + } state; + + //voice state + struct voice_t { + modulo_array buffer; //decoded samples + int buf_pos; //place in buffer where next samples will be decoded + int interp_pos; //relative fractional position in sample (0x1000 = 1.0) + int brr_addr; //address of current BRR block + int brr_offset; //current decoding offset in BRR block + int vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc + int vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc + int kon_delay; //KON delay/current setup phase + int env_mode; + int env; //current envelope level + int t_envx_out; + int hidden_env; //used by GAIN mode 7, very obscure quirk + } voice[8]; + + //gaussian + static const int16 gaussian_table[512]; + int gaussian_interpolate(const voice_t &v); + + //counter + enum { counter_range = 2048 * 5 * 3 }; //30720 (0x7800) + static const uint16 counter_rate[32]; + static const uint16 counter_offset[32]; + void counter_tick(); + bool counter_poll(unsigned rate); + + //envelope + void envelope_run(voice_t &v); + + //brr + void brr_decode(voice_t &v); + + //misc + void misc_27(); + void misc_28(); + void misc_29(); + void misc_30(); + + //voice + void voice_output(voice_t &v, bool channel); + void voice_1 (voice_t &v); + void voice_2 (voice_t &v); + void voice_3 (voice_t &v); + void voice_3a(voice_t &v); + void voice_3b(voice_t &v); + void voice_3c(voice_t &v); + void voice_4 (voice_t &v); + void voice_5 (voice_t &v); + void voice_6 (voice_t &v); + void voice_7 (voice_t &v); + void voice_8 (voice_t &v); + void voice_9 (voice_t &v); + + //echo + int calc_fir(int i, bool channel); + int echo_output(bool channel); + void echo_read(bool channel); + void echo_write(bool channel); + void echo_22(); + void echo_23(); + void echo_24(); + void echo_25(); + void echo_26(); + void echo_27(); + void echo_28(); + void echo_29(); + void echo_30(); + + friend class sDSPDebug; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern sDSPDebugger dsp; +#else + extern sDSP dsp; +#endif diff --git a/mednafen/snes/src/dsp/sdsp/serialization.cpp b/mednafen/snes/src/dsp/sdsp/serialization.cpp new file mode 100755 index 0000000..72c4e67 --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/serialization.cpp @@ -0,0 +1,68 @@ +#ifdef SDSP_CPP + +void sDSP::serialize(serializer &s) { + DSP::serialize(s); + + s.integer(phase_index); + + s.array(state.regs, 128); + state.echo_hist[0].serialize(s); + state.echo_hist[1].serialize(s); + s.integer(state.echo_hist_pos); + + s.integer(state.every_other_sample); + s.integer(state.kon); + s.integer(state.noise); + s.integer(state.counter); + s.integer(state.echo_offset); + s.integer(state.echo_length); + + s.integer(state.new_kon); + s.integer(state.endx_buf); + s.integer(state.envx_buf); + s.integer(state.outx_buf); + + s.integer(state.t_pmon); + s.integer(state.t_non); + s.integer(state.t_eon); + s.integer(state.t_dir); + s.integer(state.t_koff); + + s.integer(state.t_brr_next_addr); + s.integer(state.t_adsr0); + s.integer(state.t_brr_header); + s.integer(state.t_brr_byte); + s.integer(state.t_srcn); + s.integer(state.t_esa); + s.integer(state.t_echo_disabled); + + s.integer(state.t_dir_addr); + s.integer(state.t_pitch); + s.integer(state.t_output); + s.integer(state.t_looped); + s.integer(state.t_echo_ptr); + + s.integer(state.t_main_out[0]); + s.integer(state.t_main_out[1]); + s.integer(state.t_echo_out[0]); + s.integer(state.t_echo_out[1]); + s.integer(state.t_echo_in [0]); + s.integer(state.t_echo_in [1]); + + for(unsigned n = 0; n < 8; n++) { + voice[n].buffer.serialize(s); + s.integer(voice[n].buf_pos); + s.integer(voice[n].interp_pos); + s.integer(voice[n].brr_addr); + s.integer(voice[n].brr_offset); + s.integer(voice[n].vbit); + s.integer(voice[n].vidx); + s.integer(voice[n].kon_delay); + s.integer(voice[n].env_mode); + s.integer(voice[n].env); + s.integer(voice[n].t_envx_out); + s.integer(voice[n].hidden_env); + } +} + +#endif diff --git a/mednafen/snes/src/dsp/sdsp/voice.cpp b/mednafen/snes/src/dsp/sdsp/voice.cpp new file mode 100755 index 0000000..8c7f526 --- /dev/null +++ b/mednafen/snes/src/dsp/sdsp/voice.cpp @@ -0,0 +1,174 @@ +#ifdef SDSP_CPP + +inline void sDSP::voice_output(voice_t &v, bool channel) { + //apply left/right volume + int amp = (state.t_output * (int8)VREG(voll + channel)) >> 7; + + //add to output total + state.t_main_out[channel] += amp; + state.t_main_out[channel] = sclamp<16>(state.t_main_out[channel]); + + //optionally add to echo total + if(state.t_eon & v.vbit) { + state.t_echo_out[channel] += amp; + state.t_echo_out[channel] = sclamp<16>(state.t_echo_out[channel]); + } +} + +void sDSP::voice_1(voice_t &v) { + state.t_dir_addr = (state.t_dir << 8) + (state.t_srcn << 2); + state.t_srcn = VREG(srcn); +} + +void sDSP::voice_2(voice_t &v) { + //read sample pointer (ignored if not needed) + uint16 addr = state.t_dir_addr; + if(!v.kon_delay) addr += 2; + uint8 lo = memory::apuram[(uint16)(addr + 0)]; + uint8 hi = memory::apuram[(uint16)(addr + 1)]; + state.t_brr_next_addr = ((hi << 8) + lo); + + state.t_adsr0 = VREG(adsr0); + + //read pitch, spread over two clocks + state.t_pitch = VREG(pitchl); +} + +void sDSP::voice_3(voice_t &v) { + voice_3a(v); + voice_3b(v); + voice_3c(v); +} + +void sDSP::voice_3a(voice_t &v) { + state.t_pitch += (VREG(pitchh) & 0x3f) << 8; +} + +void sDSP::voice_3b(voice_t &v) { + state.t_brr_byte = memory::apuram[(uint16)(v.brr_addr + v.brr_offset)]; + state.t_brr_header = memory::apuram[(uint16)(v.brr_addr)]; +} + +void sDSP::voice_3c(voice_t &v) { + //pitch modulation using previous voice's output + + if(state.t_pmon & v.vbit) { + state.t_pitch += ((state.t_output >> 5) * state.t_pitch) >> 10; + } + + if(v.kon_delay) { + //get ready to start BRR decoding on next sample + if(v.kon_delay == 5) { + v.brr_addr = state.t_brr_next_addr; + v.brr_offset = 1; + v.buf_pos = 0; + state.t_brr_header = 0; //header is ignored on this sample + } + + //envelope is never run during KON + v.env = 0; + v.hidden_env = 0; + + //disable BRR decoding until last three samples + v.interp_pos = 0; + v.kon_delay--; + if(v.kon_delay & 3) v.interp_pos = 0x4000; + + //pitch is never added during KON + state.t_pitch = 0; + } + + //gaussian interpolation + int output = gaussian_interpolate(v); + + //noise + if(state.t_non & v.vbit) { + output = (int16)(state.noise << 1); + } + + //apply envelope + state.t_output = ((output * v.env) >> 11) & ~1; + v.t_envx_out = v.env >> 4; + + //immediate silence due to end of sample or soft reset + if(REG(flg) & 0x80 || (state.t_brr_header & 3) == 1) { + v.env_mode = env_release; + v.env = 0; + } + + if(state.every_other_sample) { + //KOFF + if(state.t_koff & v.vbit) { + v.env_mode = env_release; + } + + //KON + if(state.kon & v.vbit) { + v.kon_delay = 5; + v.env_mode = env_attack; + } + } + + //run envelope for next sample + if(!v.kon_delay) envelope_run(v); +} + +void sDSP::voice_4(voice_t &v) { + //decode BRR + state.t_looped = 0; + if(v.interp_pos >= 0x4000) { + brr_decode(v); + v.brr_offset += 2; + if(v.brr_offset >= 9) { + //start decoding next BRR block + v.brr_addr = (uint16)(v.brr_addr + 9); + if(state.t_brr_header & 1) { + v.brr_addr = state.t_brr_next_addr; + state.t_looped = v.vbit; + } + v.brr_offset = 1; + } + } + + //apply pitch + v.interp_pos = (v.interp_pos & 0x3fff) + state.t_pitch; + + //keep from getting too far ahead (when using pitch modulation) + if(v.interp_pos > 0x7fff) v.interp_pos = 0x7fff; + + //output left + voice_output(v, 0); +} + +void sDSP::voice_5(voice_t &v) { + //output right + voice_output(v, 1); + + //ENDX, OUTX and ENVX won't update if you wrote to them 1-2 clocks earlier + state.endx_buf = REG(endx) | state.t_looped; + + //clear bit in ENDX if KON just began + if(v.kon_delay == 5) state.endx_buf &= ~v.vbit; +} + +void sDSP::voice_6(voice_t &v) { + state.outx_buf = state.t_output >> 8; +} + +void sDSP::voice_7(voice_t &v) { + //update ENDX + REG(endx) = (uint8)state.endx_buf; + state.envx_buf = v.t_envx_out; +} + +void sDSP::voice_8(voice_t &v) { + //update OUTX + VREG(outx) = (uint8)state.outx_buf; +} + +void sDSP::voice_9(voice_t &v) { + //update ENVX + VREG(envx) = (uint8)state.envx_buf; +} + +#endif diff --git a/mednafen/snes/src/interface.hpp b/mednafen/snes/src/interface.hpp new file mode 100755 index 0000000..136be56 --- /dev/null +++ b/mednafen/snes/src/interface.hpp @@ -0,0 +1,39 @@ +#ifdef DEBUGGER + #define debugvirtual virtual +#else + #define debugvirtual +#endif + +namespace SNES { + struct ChipDebugger { + virtual bool property(unsigned id, string &name, string &value) = 0; + }; + + #include "memory/memory.hpp" + #include "memory/smemory/smemory.hpp" + + #include "ppu/ppu.hpp" + #include "ppu/bppu/bppu.hpp" + + #include "cpu/cpu.hpp" + #include "cpu/core/core.hpp" + #include "cpu/scpu/scpu.hpp" + + #include "smp/smp.hpp" + #include "smp/core/core.hpp" + #include "smp/ssmp/ssmp.hpp" + + #include "dsp/dsp.hpp" + #include "dsp/sdsp/sdsp.hpp" + + #include "system/system.hpp" + #include "chip/chip.hpp" + #include "cartridge/cartridge.hpp" + #include "cheat/cheat.hpp" + + #include "memory/memory-inline.hpp" + #include "ppu/ppu-inline.hpp" + #include "cheat/cheat-inline.hpp" +}; + +#undef debugvirtual diff --git a/mednafen/snes/src/lib/libco/amd64.c b/mednafen/snes/src/lib/libco/amd64.c new file mode 100755 index 0000000..5f1cfca --- /dev/null +++ b/mednafen/snes/src/lib/libco/amd64.c @@ -0,0 +1,104 @@ +/* + libco.amd64 (2009-10-12) + author: byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local long long co_active_buffer[64]; +static thread_local cothread_t co_active_handle = 0; +static void (*co_swap)(cothread_t, cothread_t) = 0; + +#ifdef _WIN32 + //ABI: Win64 + static unsigned char co_swap_function[] = { + 0x48, 0x89, 0x22, 0x48, 0x8B, 0x21, 0x58, 0x48, 0x89, 0x6A, 0x08, 0x48, 0x89, 0x72, 0x10, 0x48, + 0x89, 0x7A, 0x18, 0x48, 0x89, 0x5A, 0x20, 0x4C, 0x89, 0x62, 0x28, 0x4C, 0x89, 0x6A, 0x30, 0x4C, + 0x89, 0x72, 0x38, 0x4C, 0x89, 0x7A, 0x40, 0x48, 0x81, 0xC2, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83, + 0xE2, 0xF0, 0x0F, 0x29, 0x32, 0x0F, 0x29, 0x7A, 0x10, 0x44, 0x0F, 0x29, 0x42, 0x20, 0x44, 0x0F, + 0x29, 0x4A, 0x30, 0x44, 0x0F, 0x29, 0x52, 0x40, 0x44, 0x0F, 0x29, 0x5A, 0x50, 0x44, 0x0F, 0x29, + 0x62, 0x60, 0x44, 0x0F, 0x29, 0x6A, 0x70, 0x44, 0x0F, 0x29, 0xB2, 0x80, 0x00, 0x00, 0x00, 0x44, + 0x0F, 0x29, 0xBA, 0x90, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x69, 0x08, 0x48, 0x8B, 0x71, 0x10, 0x48, + 0x8B, 0x79, 0x18, 0x48, 0x8B, 0x59, 0x20, 0x4C, 0x8B, 0x61, 0x28, 0x4C, 0x8B, 0x69, 0x30, 0x4C, + 0x8B, 0x71, 0x38, 0x4C, 0x8B, 0x79, 0x40, 0x48, 0x81, 0xC1, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83, + 0xE1, 0xF0, 0x0F, 0x29, 0x31, 0x0F, 0x29, 0x79, 0x10, 0x44, 0x0F, 0x29, 0x41, 0x20, 0x44, 0x0F, + 0x29, 0x49, 0x30, 0x44, 0x0F, 0x29, 0x51, 0x40, 0x44, 0x0F, 0x29, 0x59, 0x50, 0x44, 0x0F, 0x29, + 0x61, 0x60, 0x44, 0x0F, 0x29, 0x69, 0x70, 0x44, 0x0F, 0x29, 0xB1, 0x80, 0x00, 0x00, 0x00, 0x44, + 0x0F, 0x29, 0xB9, 0x90, 0x00, 0x00, 0x00, 0xFF, 0xE0, + }; + + #include + + void co_init() { + DWORD old_privileges; + VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges); + } +#else + //ABI: SystemV + static unsigned char co_swap_function[] = { + 0x48, 0x89, 0x26, 0x48, 0x8B, 0x27, 0x58, 0x48, 0x89, 0x6E, 0x08, 0x48, 0x89, 0x5E, 0x10, 0x4C, + 0x89, 0x66, 0x18, 0x4C, 0x89, 0x6E, 0x20, 0x4C, 0x89, 0x76, 0x28, 0x4C, 0x89, 0x7E, 0x30, 0x48, + 0x8B, 0x6F, 0x08, 0x48, 0x8B, 0x5F, 0x10, 0x4C, 0x8B, 0x67, 0x18, 0x4C, 0x8B, 0x6F, 0x20, 0x4C, + 0x8B, 0x77, 0x28, 0x4C, 0x8B, 0x7F, 0x30, 0xFF, 0xE0, + }; + + #include + #include + + void co_init() { + unsigned long long addr = (unsigned long long)co_swap_function; + unsigned long long base = addr - (addr % sysconf(_SC_PAGESIZE)); + unsigned long long size = (addr - base) + sizeof co_swap_function; + mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC); + } +#endif + +static void crash() { + assert(0); /* called only if cothread_t entrypoint returns */ +} + +cothread_t co_active() { + if(!co_active_handle) co_active_handle = &co_active_buffer; + return co_active_handle; +} + +cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + if(!co_swap) { + co_init(); + co_swap = (void (*)(cothread_t, cothread_t))co_swap_function; + } + if(!co_active_handle) co_active_handle = &co_active_buffer; + size += 512; /* allocate additional space for storage */ + size &= ~15; /* align stack to 16-byte boundary */ + + if(handle = (cothread_t)malloc(size)) { + long long *p = (long long*)((char*)handle + size); /* seek to top of stack */ + *--p = (long long)crash; /* crash if entrypoint returns */ + *--p = (long long)entrypoint; /* start of function */ + *(long long*)handle = (long long)p; /* stack pointer */ + } + + return handle; +} + +void co_delete(cothread_t handle) { + free(handle); +} + +void co_switch(cothread_t handle) { + register cothread_t co_previous_handle = co_active_handle; + co_swap(co_active_handle = handle, co_previous_handle); +} + +#ifdef __cplusplus +} +#endif diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/benchmark.c b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/benchmark.c new file mode 100644 index 0000000..ea2f899 --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/benchmark.c @@ -0,0 +1,123 @@ +/* Measures absolute and relative performance of co_switch() +versus function call. */ + +#include "libco/libco.h" + +#include +#include +#include +#include +#include "testing.h" + +/* Keep calling this until it returns non-zero value. Result +is number of calls made per second. */ +double calls_per_sec( void ); + +/* Something to do in addition to func call/co_switch */ +static volatile int counter; +#define DO_SOMETHING() counter++ + + +void benchmark_func( void ); + +static double time_func( void ) +{ + int const iter = 100000; + double rate; + while ( !(rate = calls_per_sec()) ) + { + int n; + for ( n = iter / 4; n--; ) + { + benchmark_func(); + benchmark_func(); + benchmark_func(); + benchmark_func(); + } + } + return rate * iter; +} + + +/* co_switch timing */ +static cothread_t main_thread; + +static void thread_func( void ) +{ + while ( 1 ) + { + co_switch( main_thread ); DO_SOMETHING(); + co_switch( main_thread ); DO_SOMETHING(); + co_switch( main_thread ); DO_SOMETHING(); + co_switch( main_thread ); DO_SOMETHING(); + } +} + +static double time_co_switch( void ) +{ + int const iter = 1000; + double rate; + cothread_t thread; + + main_thread = co_active(); + thread = co_create( 16 * 1024, thread_func ); + assert( thread && main_thread ); + + while ( !(rate = calls_per_sec()) ) + { + int n; + for ( n = iter / 4; n--; ) + { + co_switch( thread ); + co_switch( thread ); + co_switch( thread ); + co_switch( thread ); + } + } + return rate * iter; +} + + +/* Time both and compare */ +int main( void ) +{ + double func_rate = time_func(); + double co_rate = time_co_switch(); + + print_libco_opts(); + + printf( "%6.2f M function calls per sec\n", func_rate / 1000000 ); + printf( "%6.2f M co_switch()x2 per sec\n", co_rate / 1000000 ); + + printf( "Function call is %.1fX faster than two co_switch() calls.\n\n", func_rate / co_rate ); + + return 0; +} + + +/* Utility */ +double calls_per_sec( void ) +{ + #define DURATION CLOCKS_PER_SEC + + static clock_t s_end; + static int s_iter; + clock_t const present = clock(); + int const iter = s_iter++; + if ( iter <= 1 ) /* throw away first iteration since it'll be slower due to setup */ + { + if ( CLOCKS_PER_SEC < 10 ) + { + fprintf( stderr, "Not enough clock() resolution\n" ); + exit( EXIT_FAILURE ); + } + while ( clock() == present ) { } + s_end = clock() + DURATION; + } + else if ( present >= s_end ) + { + s_iter = 0; + return (double) (iter - 1) * CLOCKS_PER_SEC / (present - s_end + DURATION); + } + return 0; +} diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/benchmark_impl.c b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/benchmark_impl.c new file mode 100644 index 0000000..46aa892 --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/benchmark_impl.c @@ -0,0 +1,5 @@ + +/* Function timing */ +void benchmark_func( void ) +{ +} diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/libco/libco.c b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/libco/libco.c new file mode 100644 index 0000000..5567626 --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/libco/libco.c @@ -0,0 +1,23 @@ +/* + libco + auto-selection module + license: public domain +*/ + +#if defined(__GNUC__) && defined(__i386__) + #include "x86.c" +#elif defined(__GNUC__) && defined(__amd64__) + #include "amd64.c" +#elif defined(__GNUC__) && defined(_ARCH_PPC) + #include "ppc.c" +#elif defined(__GNUC__) + #include "sjlj.c" +#elif defined(_MSC_VER) && defined(_M_IX86) + #include "x86.c" +#elif defined(_MSC_VER) && defined(_M_AMD64) + #include "amd64.c" +#elif defined(_MSC_VER) + #include "fiber.c" +#else + #error "libco: unsupported processor, compiler or operating system" +#endif diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/libco/libco.h b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/libco/libco.h new file mode 100644 index 0000000..b1b49a2 --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/libco/libco.h @@ -0,0 +1,34 @@ +/* + libco + version: 0.15 (2009-10-12) + license: public domain +*/ + +#ifndef LIBCO_H +#define LIBCO_H + +#ifdef LIBCO_C + #ifdef LIBCO_MP + #define thread_local __thread + #else + #define thread_local + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* cothread_t; + +cothread_t co_active(); +cothread_t co_create(unsigned int, void (*)(void)); +void co_delete(cothread_t); +void co_switch(cothread_t); + +#ifdef __cplusplus +} +#endif + +/* ifndef LIBCO_H */ +#endif diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/libco/ppc.c b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/libco/ppc.c new file mode 100644 index 0000000..a6028fd --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/libco/ppc.c @@ -0,0 +1,407 @@ +/* + libco.ppc (2010-10-17) + author: blargg + license: public domain +*/ + +/* PowerPC 32/64 using embedded or external asm, with optional +floating-point and AltiVec save/restore */ + +#define LIBCO_C +#include "libco.h" +#include +#include +#include + +#define LIBCO_MPROTECT (__unix__ && !LIBCO_PPC_ASM) + +#if LIBCO_MPROTECT + #include + #include +#endif + +/* State format (offsets in 32-bit words) + ++0 Pointer to swap code + Rest of function descriptor for entry function ++8 PC ++10 SP + Special regs + GPRs + FPRs + VRs + stack +*/ + +enum { state_size = 1024 }; +enum { above_stack = 2048 }; +enum { stack_align = 256 }; + +static thread_local cothread_t co_active_handle = 0; + +/**** Determine environment ****/ + +#define LIBCO_PPC64 (_ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__) + +/* Whether function calls are indirect through a descriptor, +or are directly to function */ +#ifndef LIBCO_PPCDESC + #if !_CALL_SYSV && (_CALL_AIX || _CALL_AIXDESC || LIBCO_PPC64) + #define LIBCO_PPCDESC 1 + #endif +#endif + +#ifdef LIBCO_PPC_ASM + + #ifdef __cplusplus + extern "C" + #endif + + /* Swap code is in ppc.S */ + void co_swap_asm( cothread_t, cothread_t ); + #define CO_SWAP_ASM( x, y ) co_swap_asm( x, y ) + +#else + +/* Swap code is here in array. Please leave dieassembly comments, +as they make it easy to see what it does, and reorder instructions +if one wants to see whether that improves performance. */ +static const uint32_t libco_ppc_code [] = { +#if LIBCO_PPC64 + 0x7d000026, /* mfcr r8 */ + 0xf8240028, /* std r1,40(r4) */ + 0x7d2802a6, /* mflr r9 */ + 0xf9c40048, /* std r14,72(r4) */ + 0xf9e40050, /* std r15,80(r4) */ + 0xfa040058, /* std r16,88(r4) */ + 0xfa240060, /* std r17,96(r4) */ + 0xfa440068, /* std r18,104(r4) */ + 0xfa640070, /* std r19,112(r4) */ + 0xfa840078, /* std r20,120(r4) */ + 0xfaa40080, /* std r21,128(r4) */ + 0xfac40088, /* std r22,136(r4) */ + 0xfae40090, /* std r23,144(r4) */ + 0xfb040098, /* std r24,152(r4) */ + 0xfb2400a0, /* std r25,160(r4) */ + 0xfb4400a8, /* std r26,168(r4) */ + 0xfb6400b0, /* std r27,176(r4) */ + 0xfb8400b8, /* std r28,184(r4) */ + 0xfba400c0, /* std r29,192(r4) */ + 0xfbc400c8, /* std r30,200(r4) */ + 0xfbe400d0, /* std r31,208(r4) */ + 0xf9240020, /* std r9,32(r4) */ + 0xe8e30020, /* ld r7,32(r3) */ + 0xe8230028, /* ld r1,40(r3) */ + 0x48000009, /* bl 1 */ + 0x7fe00008, /* trap */ + 0x91040030,/*1:stw r8,48(r4) */ + 0x80c30030, /* lwz r6,48(r3) */ + 0x7ce903a6, /* mtctr r7 */ + 0xe9c30048, /* ld r14,72(r3) */ + 0xe9e30050, /* ld r15,80(r3) */ + 0xea030058, /* ld r16,88(r3) */ + 0xea230060, /* ld r17,96(r3) */ + 0xea430068, /* ld r18,104(r3) */ + 0xea630070, /* ld r19,112(r3) */ + 0xea830078, /* ld r20,120(r3) */ + 0xeaa30080, /* ld r21,128(r3) */ + 0xeac30088, /* ld r22,136(r3) */ + 0xeae30090, /* ld r23,144(r3) */ + 0xeb030098, /* ld r24,152(r3) */ + 0xeb2300a0, /* ld r25,160(r3) */ + 0xeb4300a8, /* ld r26,168(r3) */ + 0xeb6300b0, /* ld r27,176(r3) */ + 0xeb8300b8, /* ld r28,184(r3) */ + 0xeba300c0, /* ld r29,192(r3) */ + 0xebc300c8, /* ld r30,200(r3) */ + 0xebe300d0, /* ld r31,208(r3) */ + 0x7ccff120, /* mtcr r6 */ +#else + 0x7d000026, /* mfcr r8 */ + 0x90240028, /* stw r1,40(r4) */ + 0x7d2802a6, /* mflr r9 */ + 0x91a4003c, /* stw r13,60(r4) */ + 0x91c40040, /* stw r14,64(r4) */ + 0x91e40044, /* stw r15,68(r4) */ + 0x92040048, /* stw r16,72(r4) */ + 0x9224004c, /* stw r17,76(r4) */ + 0x92440050, /* stw r18,80(r4) */ + 0x92640054, /* stw r19,84(r4) */ + 0x92840058, /* stw r20,88(r4) */ + 0x92a4005c, /* stw r21,92(r4) */ + 0x92c40060, /* stw r22,96(r4) */ + 0x92e40064, /* stw r23,100(r4) */ + 0x93040068, /* stw r24,104(r4) */ + 0x9324006c, /* stw r25,108(r4) */ + 0x93440070, /* stw r26,112(r4) */ + 0x93640074, /* stw r27,116(r4) */ + 0x93840078, /* stw r28,120(r4) */ + 0x93a4007c, /* stw r29,124(r4) */ + 0x93c40080, /* stw r30,128(r4) */ + 0x93e40084, /* stw r31,132(r4) */ + 0x91240020, /* stw r9,32(r4) */ + 0x80e30020, /* lwz r7,32(r3) */ + 0x80230028, /* lwz r1,40(r3) */ + 0x48000009, /* bl 1 */ + 0x7fe00008, /* trap */ + 0x91040030,/*1:stw r8,48(r4) */ + 0x80c30030, /* lwz r6,48(r3) */ + 0x7ce903a6, /* mtctr r7 */ + 0x81a3003c, /* lwz r13,60(r3) */ + 0x81c30040, /* lwz r14,64(r3) */ + 0x81e30044, /* lwz r15,68(r3) */ + 0x82030048, /* lwz r16,72(r3) */ + 0x8223004c, /* lwz r17,76(r3) */ + 0x82430050, /* lwz r18,80(r3) */ + 0x82630054, /* lwz r19,84(r3) */ + 0x82830058, /* lwz r20,88(r3) */ + 0x82a3005c, /* lwz r21,92(r3) */ + 0x82c30060, /* lwz r22,96(r3) */ + 0x82e30064, /* lwz r23,100(r3) */ + 0x83030068, /* lwz r24,104(r3) */ + 0x8323006c, /* lwz r25,108(r3) */ + 0x83430070, /* lwz r26,112(r3) */ + 0x83630074, /* lwz r27,116(r3) */ + 0x83830078, /* lwz r28,120(r3) */ + 0x83a3007c, /* lwz r29,124(r3) */ + 0x83c30080, /* lwz r30,128(r3) */ + 0x83e30084, /* lwz r31,132(r3) */ + 0x7ccff120, /* mtcr r6 */ +#endif + +#ifndef LIBCO_PPC_NOFP + 0xd9c400e0, /* stfd f14,224(r4) */ + 0xd9e400e8, /* stfd f15,232(r4) */ + 0xda0400f0, /* stfd f16,240(r4) */ + 0xda2400f8, /* stfd f17,248(r4) */ + 0xda440100, /* stfd f18,256(r4) */ + 0xda640108, /* stfd f19,264(r4) */ + 0xda840110, /* stfd f20,272(r4) */ + 0xdaa40118, /* stfd f21,280(r4) */ + 0xdac40120, /* stfd f22,288(r4) */ + 0xdae40128, /* stfd f23,296(r4) */ + 0xdb040130, /* stfd f24,304(r4) */ + 0xdb240138, /* stfd f25,312(r4) */ + 0xdb440140, /* stfd f26,320(r4) */ + 0xdb640148, /* stfd f27,328(r4) */ + 0xdb840150, /* stfd f28,336(r4) */ + 0xdba40158, /* stfd f29,344(r4) */ + 0xdbc40160, /* stfd f30,352(r4) */ + 0xdbe40168, /* stfd f31,360(r4) */ + 0xc9c300e0, /* lfd f14,224(r3) */ + 0xc9e300e8, /* lfd f15,232(r3) */ + 0xca0300f0, /* lfd f16,240(r3) */ + 0xca2300f8, /* lfd f17,248(r3) */ + 0xca430100, /* lfd f18,256(r3) */ + 0xca630108, /* lfd f19,264(r3) */ + 0xca830110, /* lfd f20,272(r3) */ + 0xcaa30118, /* lfd f21,280(r3) */ + 0xcac30120, /* lfd f22,288(r3) */ + 0xcae30128, /* lfd f23,296(r3) */ + 0xcb030130, /* lfd f24,304(r3) */ + 0xcb230138, /* lfd f25,312(r3) */ + 0xcb430140, /* lfd f26,320(r3) */ + 0xcb630148, /* lfd f27,328(r3) */ + 0xcb830150, /* lfd f28,336(r3) */ + 0xcba30158, /* lfd f29,344(r3) */ + 0xcbc30160, /* lfd f30,352(r3) */ + 0xcbe30168, /* lfd f31,360(r3) */ +#endif + +#ifdef __ALTIVEC__ + 0x7ca042a6, /* mfvrsave r5 */ + 0x39040180, /* addi r8,r4,384 */ + 0x39240190, /* addi r9,r4,400 */ + 0x70a00fff, /* andi. r0,r5,4095 */ + 0x90a40034, /* stw r5,52(r4) */ + 0x4182005c, /* beq- 2 */ + 0x7e8041ce, /* stvx v20,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ea049ce, /* stvx v21,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7ec041ce, /* stvx v22,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ee049ce, /* stvx v23,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f0041ce, /* stvx v24,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f2049ce, /* stvx v25,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f4041ce, /* stvx v26,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f6049ce, /* stvx v27,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f8041ce, /* stvx v28,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7fa049ce, /* stvx v29,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7fc041ce, /* stvx v30,r0,r8 */ + 0x7fe049ce, /* stvx v31,r0,r9 */ + 0x80a30034,/*2:lwz r5,52(r3) */ + 0x39030180, /* addi r8,r3,384 */ + 0x39230190, /* addi r9,r3,400 */ + 0x70a00fff, /* andi. r0,r5,4095 */ + 0x7ca043a6, /* mtvrsave r5 */ + 0x4d820420, /* beqctr */ + 0x7e8040ce, /* lvx v20,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ea048ce, /* lvx v21,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7ec040ce, /* lvx v22,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ee048ce, /* lvx v23,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f0040ce, /* lvx v24,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f2048ce, /* lvx v25,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f4040ce, /* lvx v26,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f6048ce, /* lvx v27,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f8040ce, /* lvx v28,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7fa048ce, /* lvx v29,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7fc040ce, /* lvx v30,r0,r8 */ + 0x7fe048ce, /* lvx v31,r0,r9 */ +#endif + + 0x4e800420, /* bctr */ +}; + + #if LIBCO_PPCDESC + /* Function call goes through indirect descriptor */ + #define CO_SWAP_ASM( x, y ) \ + ((void (*)( cothread_t, cothread_t )) (uintptr_t) x)( x, y ) + #else + /* Function call goes directly to code */ + #define CO_SWAP_ASM( x, y ) \ + ((void (*)( cothread_t, cothread_t )) (uintptr_t) libco_ppc_code)( x, y ) + #endif + +#endif + +static uint32_t* co_create_( unsigned size, uintptr_t entry ) +{ + uint32_t* t = (uint32_t*) malloc( size ); + + (void) entry; + + #if LIBCO_PPCDESC + if ( t ) + { + /* Copy entry's descriptor */ + memcpy( t, (void*) entry, sizeof (void*) * 3 ); + + /* Set function pointer to swap routine */ + #ifdef LIBCO_PPC_ASM + *(const void**) t = *(void**) &co_swap_asm; + #else + *(const void**) t = libco_ppc_code; + #endif + } + #endif + + return t; +} + +cothread_t co_create( unsigned int size, void (*entry_)( void ) ) +{ + uintptr_t entry = (uintptr_t) entry_; + uint32_t* t = NULL; + + /* Be sure main thread was successfully allocated */ + if ( co_active() ) + { + size += state_size + above_stack + stack_align; + t = co_create_( size, entry ); + } + + if ( t ) + { + uintptr_t sp; + int shift; + + /* Save current registers into new thread, so that any special ones will + have proper values when thread is begun */ + CO_SWAP_ASM( t, t ); + + #if LIBCO_PPCDESC + /* Get real address */ + entry = (uintptr_t) *(void**) entry; + #endif + + /* Put stack near end of block, and align */ + sp = (uintptr_t) t + size - above_stack; + sp -= sp % stack_align; + + /* On PPC32, we save and restore GPRs as 32 bits. For PPC64, we + save and restore them as 64 bits, regardless of the size the ABI + uses. So, we manually write pointers at the proper size. We always + save and restore at the same address, and since PPC is big-endian, + we must put the low byte first on PPC32. */ + + /* If uintptr_t is 32 bits, >>32 is undefined behavior, so we do two shifts + and don't have to care how many bits uintptr_t is. */ + #if LIBCO_PPC64 + shift = 16; + #else + shift = 0; + #endif + + /* Set up so entry will be called on next swap */ + t [8] = (uint32_t) (entry >> shift >> shift); + t [9] = (uint32_t) entry; + + t [10] = (uint32_t) (sp >> shift >> shift); + t [11] = (uint32_t) sp; + } + + return t; +} + +void co_delete( cothread_t t ) +{ + free( t ); +} + +static void co_init_( void ) +{ + #if LIBCO_MPROTECT + /* TODO: pre- and post-pad PPC code so that this doesn't make other + data executable and writable */ + long page_size = sysconf( _SC_PAGESIZE ); + if ( page_size > 0 ) + { + uintptr_t align = page_size; + uintptr_t begin = (uintptr_t) libco_ppc_code; + uintptr_t end = begin + sizeof libco_ppc_code; + + /* Align beginning and end */ + end += align - 1; + end -= end % align; + begin -= begin % align; + + mprotect( (void*) begin, end - begin, PROT_READ | PROT_WRITE | PROT_EXEC ); + } + #endif + + co_active_handle = co_create_( state_size, (uintptr_t) &co_switch ); +} + +cothread_t co_active() +{ + if ( !co_active_handle ) + co_init_(); + + return co_active_handle; +} + +void co_switch( cothread_t t ) +{ + cothread_t old = co_active_handle; + co_active_handle = t; + + CO_SWAP_ASM( t, old ); +} diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/ppc.S b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/ppc.S new file mode 100644 index 0000000..268deb4 --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/ppc.S @@ -0,0 +1,265 @@ +// Core processor state swap function. +// Only used by ppc.c if LIBCO_PPC_ASM is defined. +// Main purpose of this asm file is to allow easy development of improvements. + +// If this isn't available, you can just do -mregnames when compiling +#include "ppc-asm.h" + +.section ".text" +.align 5 +.globl co_swap_asm +.type co_swap_asm, @function + +// Wrap GPR load/stores so that we can use the same code for +// PPC and PPC64. +#if _ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__ + #define STWD std + #define LWZD ld + #define GPR_SIZE 8 +#else + #define LIBCO_PPC32 1 + #define STWD stw + #define LWZD lwz + #define GPR_SIZE 4 +#endif + +// Offsets of values in memory +#define PC_OFF 32 +#define SP_OFF 40 +#define CR_OFF 48 +#define VRSAVE_OFF 52 +#define GPR_OFF 56 +#define FPR_OFF 224 +#define VR_OFF 384 + +// Avoid stupid coding errors by letting the preprocessor +// keep things consistent for us. +#define FPR(n) FPR_OFF - 8*14 + 8*n +#define GPR(n) GPR_OFF - GPR_SIZE*12 + GPR_SIZE*n + +#define FPR_NAME(n) f##n +#define GPR_NAME(n) r##n + +// REG_MEM(FPR,31) turns into f31,FPR_OFF+8*(31-14) +#define REG_MEM( reg, n ) reg##_NAME(n), reg(n) + +// void co_swap_asm( cothread_t new, cothread_t old ) +co_swap_asm: + // We must save current state into old, and load + // new state from new. Might be called with old=new, + // so we must not load a particular register before + // having saved it. I don't think this hurts + // performance, since there are enough instructions + // to mix in. + + // Improve performance by reordering things somewhat. + // Try to keep GPR saves/loads in block, for clarity. + + /* new old + r3 pointer + r4 pointer + r5 flags flags + r6 CR + r7 PC + r8 CR + r9 PC + */ + + mfcr r8 + STWD sp, SP_OFF (r4) + mflr r9 + + // Save GPRs +#if LIBCO_PPC32 + STWD REG_MEM(GPR,13) (r4) +#endif + STWD REG_MEM(GPR,14) (r4) + STWD REG_MEM(GPR,15) (r4) + STWD REG_MEM(GPR,16) (r4) + STWD REG_MEM(GPR,17) (r4) + STWD REG_MEM(GPR,18) (r4) + STWD REG_MEM(GPR,19) (r4) + STWD REG_MEM(GPR,20) (r4) + STWD REG_MEM(GPR,21) (r4) + STWD REG_MEM(GPR,22) (r4) + STWD REG_MEM(GPR,23) (r4) + STWD REG_MEM(GPR,24) (r4) + STWD REG_MEM(GPR,25) (r4) + STWD REG_MEM(GPR,26) (r4) + STWD REG_MEM(GPR,27) (r4) + STWD REG_MEM(GPR,28) (r4) + STWD REG_MEM(GPR,29) (r4) + STWD REG_MEM(GPR,30) (r4) + STWD REG_MEM(GPR,31) (r4) + + STWD r9, PC_OFF (r4) + LWZD r7, PC_OFF (r3) + LWZD sp, SP_OFF (r3) + bl 1f // Crash if entry function returns + trap +1: stw r8, CR_OFF (r4) + lwz r6, CR_OFF (r3) + mtctr r7 + + // Load GPRs +#if LIBCO_PPC32 + LWZD REG_MEM(GPR,13) (r3) +#endif + LWZD REG_MEM(GPR,14) (r3) + LWZD REG_MEM(GPR,15) (r3) + LWZD REG_MEM(GPR,16) (r3) + LWZD REG_MEM(GPR,17) (r3) + LWZD REG_MEM(GPR,18) (r3) + LWZD REG_MEM(GPR,19) (r3) + LWZD REG_MEM(GPR,20) (r3) + LWZD REG_MEM(GPR,21) (r3) + LWZD REG_MEM(GPR,22) (r3) + LWZD REG_MEM(GPR,23) (r3) + LWZD REG_MEM(GPR,24) (r3) + LWZD REG_MEM(GPR,25) (r3) + LWZD REG_MEM(GPR,26) (r3) + LWZD REG_MEM(GPR,27) (r3) + LWZD REG_MEM(GPR,28) (r3) + LWZD REG_MEM(GPR,29) (r3) + LWZD REG_MEM(GPR,30) (r3) + LWZD REG_MEM(GPR,31) (r3) + + mtcr r6 + +#ifndef LIBCO_PPC_NOFP + // Save FPRs + stfd REG_MEM(FPR,14) (r4) + stfd REG_MEM(FPR,15) (r4) + stfd REG_MEM(FPR,16) (r4) + stfd REG_MEM(FPR,17) (r4) + stfd REG_MEM(FPR,18) (r4) + stfd REG_MEM(FPR,19) (r4) + stfd REG_MEM(FPR,20) (r4) + stfd REG_MEM(FPR,21) (r4) + stfd REG_MEM(FPR,22) (r4) + stfd REG_MEM(FPR,23) (r4) + stfd REG_MEM(FPR,24) (r4) + stfd REG_MEM(FPR,25) (r4) + stfd REG_MEM(FPR,26) (r4) + stfd REG_MEM(FPR,27) (r4) + stfd REG_MEM(FPR,28) (r4) + stfd REG_MEM(FPR,29) (r4) + stfd REG_MEM(FPR,30) (r4) + stfd REG_MEM(FPR,31) (r4) + + // Load FPRs + lfd REG_MEM(FPR,14) (r3) + lfd REG_MEM(FPR,15) (r3) + lfd REG_MEM(FPR,16) (r3) + lfd REG_MEM(FPR,17) (r3) + lfd REG_MEM(FPR,18) (r3) + lfd REG_MEM(FPR,19) (r3) + lfd REG_MEM(FPR,20) (r3) + lfd REG_MEM(FPR,21) (r3) + lfd REG_MEM(FPR,22) (r3) + lfd REG_MEM(FPR,23) (r3) + lfd REG_MEM(FPR,24) (r3) + lfd REG_MEM(FPR,25) (r3) + lfd REG_MEM(FPR,26) (r3) + lfd REG_MEM(FPR,27) (r3) + lfd REG_MEM(FPR,28) (r3) + lfd REG_MEM(FPR,29) (r3) + lfd REG_MEM(FPR,30) (r3) + lfd REG_MEM(FPR,31) (r3) +#endif + +#ifdef __ALTIVEC__ + // Performance testing showed that conditionally saving/restoring + // individual registers was worse than just doing them all. + + // Save VRSAVE + mfspr r5, 256 // get VRSAVE + addi r8, r4,VR_OFF + addi r9, r4,VR_OFF + 16 + andi. r0, r5,0x0FFF // see whether anything to save + stw r5, VRSAVE_OFF(r4) + beq 2f // skip if nothing to save + + // Save VRs + + // we use two pointers to avoid stalls + stvx v20,0,r8 + addi r8,r8,32 + + stvx v21,0,r9 + addi r9,r9,32 + + stvx v22,0,r8 + addi r8,r8,32 + + stvx v23,0,r9 + addi r9,r9,32 + + stvx v24,0,r8 + addi r8,r8,32 + + stvx v25,0,r9 + addi r9,r9,32 + + stvx v26,0,r8 + addi r8,r8,32 + + stvx v27,0,r9 + addi r9,r9,32 + + stvx v28,0,r8 + addi r8,r8,32 + + stvx v29,0,r9 + addi r9,r9,32 + + stvx v30,0,r8 + + stvx v31,0,r9 + +2: + // Load VRSAVE + lwz r5, VRSAVE_OFF(r3) + addi r8, r3,VR_OFF + addi r9, r3,VR_OFF + 16 + andi. r0, r5,0x0FFF + mtspr 256,r5 // set VRSAVE + beqctr // return if nothing to restore + + // Load VRs + lvx v20,0,r8 + addi r8,r8,32 + + lvx v21,0,r9 + addi r9,r9,32 + + lvx v22,0,r8 + addi r8,r8,32 + + lvx v23,0,r9 + addi r9,r9,32 + + lvx v24,0,r8 + addi r8,r8,32 + + lvx v25,0,r9 + addi r9,r9,32 + + lvx v26,0,r8 + addi r8,r8,32 + + lvx v27,0,r9 + addi r9,r9,32 + + lvx v28,0,r8 + addi r8,r8,32 + + lvx v29,0,r9 + addi r9,r9,32 + + lvx v30,0,r8 + + lvx v31,0,r9 +#endif + + bctr diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/ppc_regs.S b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/ppc_regs.S new file mode 100644 index 0000000..2a769ee --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/ppc_regs.S @@ -0,0 +1,329 @@ +.section ".text" +.align 5 +.globl save_ppc_regs +.globl load_ppc_regs + +.type save_ppc_regs, @function +.type load_ppc_regs, @function + +#if _ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__ + #define STWD std + #define LWZD ld + #define GPR_EXTRA 0 +#else + #define STWD stw + #define LWZD lwz + #define GPR_EXTRA 4 +#endif + +#define VR_OFF 0 +#define FPR_OFF 512 +#define GPR_OFF 768 +#define SPR_OFF 1024 + +#define FPR(n) FPR_OFF + 8*n +#define GPR(n) GPR_OFF + GPR_EXTRA + 8*n +#define SPR(n) SPR_OFF + 4*n + +#define FPR_NAME(n) f##n +#define GPR_NAME(n) r##n + +#define REG_MEM( reg, n ) reg##_NAME(n), reg(n) + +save_ppc_regs: + mfcr r0 + stw r0, SPR(0) (r3) + + mfspr r0,256 /* VRSAVE */ + stw r0, SPR(1) (r3) + + mfctr r0 + stw r0, SPR(2) (r3) + + mfxer r0 + stw r0, SPR(3) (r3) + + STWD REG_MEM(GPR, 0) (r3) + STWD REG_MEM(GPR, 1) (r3) + STWD REG_MEM(GPR, 2) (r3) + STWD REG_MEM(GPR, 3) (r3) + STWD REG_MEM(GPR, 4) (r3) + STWD REG_MEM(GPR, 5) (r3) + STWD REG_MEM(GPR, 6) (r3) + STWD REG_MEM(GPR, 7) (r3) + STWD REG_MEM(GPR, 8) (r3) + STWD REG_MEM(GPR, 9) (r3) + STWD REG_MEM(GPR,10) (r3) + STWD REG_MEM(GPR,11) (r3) + STWD REG_MEM(GPR,12) (r3) + STWD REG_MEM(GPR,13) (r3) + STWD REG_MEM(GPR,14) (r3) + STWD REG_MEM(GPR,15) (r3) + STWD REG_MEM(GPR,16) (r3) + STWD REG_MEM(GPR,17) (r3) + STWD REG_MEM(GPR,18) (r3) + STWD REG_MEM(GPR,19) (r3) + STWD REG_MEM(GPR,20) (r3) + STWD REG_MEM(GPR,21) (r3) + STWD REG_MEM(GPR,22) (r3) + STWD REG_MEM(GPR,23) (r3) + STWD REG_MEM(GPR,24) (r3) + STWD REG_MEM(GPR,25) (r3) + STWD REG_MEM(GPR,26) (r3) + STWD REG_MEM(GPR,27) (r3) + STWD REG_MEM(GPR,28) (r3) + STWD REG_MEM(GPR,29) (r3) + STWD REG_MEM(GPR,30) (r3) + STWD REG_MEM(GPR,31) (r3) + + stfd REG_MEM(FPR, 0) (r3) + stfd REG_MEM(FPR, 1) (r3) + stfd REG_MEM(FPR, 2) (r3) + stfd REG_MEM(FPR, 3) (r3) + stfd REG_MEM(FPR, 4) (r3) + stfd REG_MEM(FPR, 5) (r3) + stfd REG_MEM(FPR, 6) (r3) + stfd REG_MEM(FPR, 7) (r3) + stfd REG_MEM(FPR, 8) (r3) + stfd REG_MEM(FPR, 9) (r3) + stfd REG_MEM(FPR,10) (r3) + stfd REG_MEM(FPR,11) (r3) + stfd REG_MEM(FPR,12) (r3) + stfd REG_MEM(FPR,13) (r3) + stfd REG_MEM(FPR,14) (r3) + stfd REG_MEM(FPR,15) (r3) + stfd REG_MEM(FPR,16) (r3) + stfd REG_MEM(FPR,17) (r3) + stfd REG_MEM(FPR,18) (r3) + stfd REG_MEM(FPR,19) (r3) + stfd REG_MEM(FPR,20) (r3) + stfd REG_MEM(FPR,21) (r3) + stfd REG_MEM(FPR,22) (r3) + stfd REG_MEM(FPR,23) (r3) + stfd REG_MEM(FPR,24) (r3) + stfd REG_MEM(FPR,25) (r3) + stfd REG_MEM(FPR,26) (r3) + stfd REG_MEM(FPR,27) (r3) + stfd REG_MEM(FPR,28) (r3) + stfd REG_MEM(FPR,29) (r3) + stfd REG_MEM(FPR,30) (r3) + stfd REG_MEM(FPR,31) (r3) + + mffs f0 + stfd f0, SPR(4) (r3) + + addi r5, r3, VR_OFF + stvx v0, 0, r5 + addi r5, r5, 16 + stvx v1, 0, r5 + addi r5, r5, 16 + stvx v2, 0, r5 + addi r5, r5, 16 + stvx v3, 0, r5 + addi r5, r5, 16 + stvx v4, 0, r5 + addi r5, r5, 16 + stvx v5, 0, r5 + addi r5, r5, 16 + stvx v6, 0, r5 + addi r5, r5, 16 + stvx v7, 0, r5 + addi r5, r5, 16 + stvx v8, 0, r5 + addi r5, r5, 16 + stvx v9, 0, r5 + addi r5, r5, 16 + stvx v10, 0, r5 + addi r5, r5, 16 + stvx v11, 0, r5 + addi r5, r5, 16 + stvx v12, 0, r5 + addi r5, r5, 16 + stvx v13, 0, r5 + addi r5, r5, 16 + stvx v14, 0, r5 + addi r5, r5, 16 + stvx v15, 0, r5 + addi r5, r5, 16 + stvx v16, 0, r5 + addi r5, r5, 16 + stvx v17, 0, r5 + addi r5, r5, 16 + stvx v18, 0, r5 + addi r5, r5, 16 + stvx v19, 0, r5 + addi r5, r5, 16 + stvx v20, 0, r5 + addi r5, r5, 16 + stvx v21, 0, r5 + addi r5, r5, 16 + stvx v22, 0, r5 + addi r5, r5, 16 + stvx v23, 0, r5 + addi r5, r5, 16 + stvx v24, 0, r5 + addi r5, r5, 16 + stvx v25, 0, r5 + addi r5, r5, 16 + stvx v26, 0, r5 + addi r5, r5, 16 + stvx v27, 0, r5 + addi r5, r5, 16 + stvx v28, 0, r5 + addi r5, r5, 16 + stvx v29, 0, r5 + addi r5, r5, 16 + stvx v30, 0, r5 + addi r5, r5, 16 + stvx v31, 0, r5 + + blr + +load_ppc_regs: + lwz r0, SPR(0) (r3) + mtcr r0 + + lwz r0, SPR(1) (r3) + mtspr 256, r0 + + lwz r0, SPR(2) (r3) + mtctr r0 + + lwz r0, SPR(3) (r3) + mtxer r0 + + lfd f0, SPR(4) (r3) + mtfsf 255, f0 + + LWZD REG_MEM(GPR, 0) (r3) + + + + LWZD REG_MEM(GPR, 4) (r3) + LWZD REG_MEM(GPR, 5) (r3) + LWZD REG_MEM(GPR, 6) (r3) + LWZD REG_MEM(GPR, 7) (r3) + LWZD REG_MEM(GPR, 8) (r3) + LWZD REG_MEM(GPR, 9) (r3) + LWZD REG_MEM(GPR,10) (r3) + LWZD REG_MEM(GPR,11) (r3) + LWZD REG_MEM(GPR,12) (r3) + LWZD REG_MEM(GPR,13) (r3) + LWZD REG_MEM(GPR,14) (r3) + LWZD REG_MEM(GPR,15) (r3) + LWZD REG_MEM(GPR,16) (r3) + LWZD REG_MEM(GPR,17) (r3) + LWZD REG_MEM(GPR,18) (r3) + LWZD REG_MEM(GPR,19) (r3) + LWZD REG_MEM(GPR,20) (r3) + LWZD REG_MEM(GPR,21) (r3) + LWZD REG_MEM(GPR,22) (r3) + LWZD REG_MEM(GPR,23) (r3) + LWZD REG_MEM(GPR,24) (r3) + LWZD REG_MEM(GPR,25) (r3) + LWZD REG_MEM(GPR,26) (r3) + LWZD REG_MEM(GPR,27) (r3) + LWZD REG_MEM(GPR,28) (r3) + LWZD REG_MEM(GPR,29) (r3) + LWZD REG_MEM(GPR,30) (r3) + LWZD REG_MEM(GPR,31) (r3) + + lfd REG_MEM(FPR, 0) (r3) + lfd REG_MEM(FPR, 1) (r3) + lfd REG_MEM(FPR, 2) (r3) + lfd REG_MEM(FPR, 3) (r3) + lfd REG_MEM(FPR, 4) (r3) + lfd REG_MEM(FPR, 5) (r3) + lfd REG_MEM(FPR, 6) (r3) + lfd REG_MEM(FPR, 7) (r3) + lfd REG_MEM(FPR, 8) (r3) + lfd REG_MEM(FPR, 9) (r3) + lfd REG_MEM(FPR,10) (r3) + lfd REG_MEM(FPR,11) (r3) + lfd REG_MEM(FPR,12) (r3) + lfd REG_MEM(FPR,13) (r3) + lfd REG_MEM(FPR,14) (r3) + lfd REG_MEM(FPR,15) (r3) + lfd REG_MEM(FPR,16) (r3) + lfd REG_MEM(FPR,17) (r3) + lfd REG_MEM(FPR,18) (r3) + lfd REG_MEM(FPR,19) (r3) + lfd REG_MEM(FPR,20) (r3) + lfd REG_MEM(FPR,21) (r3) + lfd REG_MEM(FPR,22) (r3) + lfd REG_MEM(FPR,23) (r3) + lfd REG_MEM(FPR,24) (r3) + lfd REG_MEM(FPR,25) (r3) + lfd REG_MEM(FPR,26) (r3) + lfd REG_MEM(FPR,27) (r3) + lfd REG_MEM(FPR,28) (r3) + lfd REG_MEM(FPR,29) (r3) + lfd REG_MEM(FPR,30) (r3) + lfd REG_MEM(FPR,31) (r3) + + addi r5, r3, VR_OFF + lvx v0, 0, r5 + addi r5, r5, 16 + lvx v1, 0, r5 + addi r5, r5, 16 + lvx v2, 0, r5 + addi r5, r5, 16 + lvx v3, 0, r5 + addi r5, r5, 16 + lvx v4, 0, r5 + addi r5, r5, 16 + lvx v5, 0, r5 + addi r5, r5, 16 + lvx v6, 0, r5 + addi r5, r5, 16 + lvx v7, 0, r5 + addi r5, r5, 16 + lvx v8, 0, r5 + addi r5, r5, 16 + lvx v9, 0, r5 + addi r5, r5, 16 + lvx v10, 0, r5 + addi r5, r5, 16 + lvx v11, 0, r5 + addi r5, r5, 16 + lvx v12, 0, r5 + addi r5, r5, 16 + lvx v13, 0, r5 + addi r5, r5, 16 + lvx v14, 0, r5 + addi r5, r5, 16 + lvx v15, 0, r5 + addi r5, r5, 16 + lvx v16, 0, r5 + addi r5, r5, 16 + lvx v17, 0, r5 + addi r5, r5, 16 + lvx v18, 0, r5 + addi r5, r5, 16 + lvx v19, 0, r5 + addi r5, r5, 16 + lvx v20, 0, r5 + addi r5, r5, 16 + lvx v21, 0, r5 + addi r5, r5, 16 + lvx v22, 0, r5 + addi r5, r5, 16 + lvx v23, 0, r5 + addi r5, r5, 16 + lvx v24, 0, r5 + addi r5, r5, 16 + lvx v25, 0, r5 + addi r5, r5, 16 + lvx v26, 0, r5 + addi r5, r5, 16 + lvx v27, 0, r5 + addi r5, r5, 16 + lvx v28, 0, r5 + addi r5, r5, 16 + lvx v29, 0, r5 + addi r5, r5, 16 + lvx v30, 0, r5 + addi r5, r5, 16 + lvx v31, 0, r5 + + blr diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/ppc_regs.h b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/ppc_regs.h new file mode 100644 index 0000000..8a9cc27 --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/ppc_regs.h @@ -0,0 +1,32 @@ +/* Saves/loads most user-level PowerPC registers */ + +#ifndef PPC_REGS_H +#define PPC_REGS_H + +#include + +typedef __attribute__((aligned(16))) struct ppc_regs_t +{ + uint64_t vr [32] [2]; + double fpr [32]; + uint64_t gpr [32]; + uint32_t cr; + uint32_t vrsave; + uint32_t ctr; + uint32_t xer; + uint32_t garbage; + uint32_t fpscr; +} ppc_regs_t; + +#ifdef __cplusplus + extern "C" { +#endif + +void save_ppc_regs( ppc_regs_t* ); +void load_ppc_regs( ppc_regs_t const* ); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/readme.txt b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/readme.txt new file mode 100644 index 0000000..9054c76 --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/readme.txt @@ -0,0 +1,34 @@ +Run run_tests to test things fairly well, including the embedded and +separate versions, 32-bit and 64-bit, and with or without FP and vector +support. It will stop on the first error. Verifies that proper registers +are saved/restored, and that no others are modified, and lists incorrect +ones. Runs a bunch of threads and randomly creates/destroys, verifying +that they all switch properly. Then runs benchmark. I'd have included +one of the other portable versions, but they all crash. + +* Supports PowerPC 32-bit and 64-bit. Detects based on compiler +settings. + +* Uses embedded PowerPC code. To use separate ppc.S, define +LIBCO_PPC_ASM. + +* Saves floating-point registers by default. To avoid this and improve +performance, define LIBCO_PPC_NOFP. + +* Saves Altivec registers only if compiler is generating Altivec code. + +* Assembly code merely saves current state to buffer pointed to by r4, +then restores from r3. This eliminates almost all dependency on the ABI, +stack format, etc. and is easier to get right. C code does all the +setup. + +* C code attempts to avoid depending on whether ABI stores pointers as +32-bit or 64-bit (independently from whether it's a 64-bit PPC). + +* Everything outside of libco/ is just for +developing/testing/benchmarking. + +* Code even works on my old Mac OS Classic machine. + +-- +Shay Green diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/run_tests b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/run_tests new file mode 100644 index 0000000..e4edaeb --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/run_tests @@ -0,0 +1,41 @@ +#!/bin/bash + +# Enable exit on error and error on use of undefined variables +set -e -u + +echo "32-bit tests" + +# General +gcc -O3 libco/libco.c test_libco.c -mno-powerpc64 -maltivec ; ./a.out + +# Stress +gcc -O3 libco/libco.c test_threads.c -mno-powerpc64 -DLIBCO_PPC_NOFP ; ./a.out +gcc -O3 libco/libco.c test_threads.c -mno-powerpc64 -maltivec ; ./a.out + +# Benchmark +gcc -O3 libco/libco.c benchmark*.c -mno-powerpc64 -DLIBCO_PPC_NOFP ; ./a.out +gcc -O3 libco/libco.c benchmark*.c -mno-powerpc64 ; ./a.out +gcc -O3 libco/libco.c benchmark*.c -mno-powerpc64 -maltivec ; ./a.out + +# Register preservation +gcc -mregnames --omit-frame-pointer libco/libco.c ppc_regs.S test_regs.c -mno-powerpc64 -DLIBCO_PPC_NOFP ; ./a.out +gcc -mregnames --omit-frame-pointer libco/libco.c ppc_regs.S test_regs.c -mno-powerpc64 -D__ALTIVEC__ ; ./a.out + +echo +echo "64-bit tests" + +# General +gcc -O3 libco/libco.c test_libco.c -mpowerpc64 -D__ALTIVEC__ ; ./a.out + +# Stress +gcc -O3 libco/libco.c test_threads.c -mpowerpc64 -DLIBCO_PPC_NOFP ; ./a.out +gcc -O3 libco/libco.c test_threads.c -mpowerpc64 -maltivec ; ./a.out + +# Benchmark +gcc -O3 libco/libco.c benchmark*.c -mpowerpc64 -DLIBCO_PPC_NOFP ; ./a.out +gcc -O3 libco/libco.c benchmark*.c -mpowerpc64 ; ./a.out +gcc -O3 libco/libco.c benchmark*.c -mpowerpc64 -maltivec ; ./a.out + +# Register preservation +gcc -mregnames --omit-frame-pointer libco/libco.c ppc_regs.S test_regs.c -mpowerpc64 -DLIBCO_PPC_NOFP ; ./a.out +gcc -mregnames --omit-frame-pointer libco/libco.c ppc_regs.S test_regs.c -mpowerpc64 -D__ALTIVEC__ ; ./a.out diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/run_tests.txt b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/run_tests.txt new file mode 100644 index 0000000..392e8a7 --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/run_tests.txt @@ -0,0 +1,106 @@ +$ cat /proc/cpuinfo +processor : 0 +cpu : PPC970, altivec supported +clock : 2000.000000MHz +revision : 2.2 (pvr 0039 0202) + +processor : 1 +cpu : PPC970, altivec supported +clock : 2000.000000MHz +revision : 2.2 (pvr 0039 0202) + +timebase : 33333333 +platform : PowerMac +model : PowerMac7,2 +machine : PowerMac7,2 +motherboard : PowerMac7,2 MacRISC4 Power Macintosh +detected as : 336 (PowerMac G5) +pmac flags : 00000000 +L2 cache : 512K unified +pmac-generation : NewWorld + +$ ./run_tests +32-bit tests +Basic test +PPC32 FP ALTIVEC +Passed + +Thread test +PPC32 +Passed + +Thread test +PPC32 FP ALTIVEC +Passed + +PPC32 +291.30 M function calls per sec + 18.93 M co_switch()x2 per sec +Function call is 15.4X faster than two co_switch() calls. + +PPC32 FP +291.20 M function calls per sec + 12.74 M co_switch()x2 per sec +Function call is 22.9X faster than two co_switch() calls. + +PPC32 FP ALTIVEC +291.90 M function calls per sec + 9.64 M co_switch()x2 per sec +Function call is 30.3X faster than two co_switch() calls. + +Register test +PPC32 +Checking for modified system registers +Checking for unpreserved non-volatiles +Checking for unnecessarily preserved volatiles +Passed + +Register test +PPC32 FP ALTIVEC +Checking for modified system registers +Checking for unpreserved non-volatiles +Checking for unnecessarily preserved volatiles +Passed + + +64-bit tests +Basic test +PPC64 FP ALTIVEC +Passed + +Thread test +PPC64 +Passed + +Thread test +PPC64 FP ALTIVEC +Passed + +PPC64 +291.70 M function calls per sec + 15.25 M co_switch()x2 per sec +Function call is 19.1X faster than two co_switch() calls. + +PPC64 FP +292.00 M function calls per sec + 12.69 M co_switch()x2 per sec +Function call is 23.0X faster than two co_switch() calls. + +PPC64 FP ALTIVEC +291.60 M function calls per sec + 9.57 M co_switch()x2 per sec +Function call is 30.5X faster than two co_switch() calls. + +Register test +PPC64 +Checking for modified system registers +Checking for unpreserved non-volatiles +Checking for unnecessarily preserved volatiles +Passed + +Register test +PPC64 FP ALTIVEC +Checking for modified system registers +Checking for unpreserved non-volatiles +Checking for unnecessarily preserved volatiles +Passed diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/test_libco.c b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/test_libco.c new file mode 100644 index 0000000..2cbd510 --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/test_libco.c @@ -0,0 +1,134 @@ +#include "libco/libco.h" + +#include +#include +#include +#include "testing.h" + +/* Verifies co_active() initial behavior */ +static void test_initial( void ) +{ + int n; + + if ( !co_active() ) + assert( 0 ); + + for ( n = 1000; n--; ) + co_switch( co_active() ); +} + +static void thread_func( void ) +{ } + +/* Creates and deletes threads many times to ensure nothing is leaked */ +static void test_leaks( void ) +{ + enum { thread_count = 10 }; + cothread_t threads [thread_count] = { 0 }; + int i = 0; + int n; + + for ( n = 10000; n--; ) + { + if ( threads [i] ) + co_delete( threads [i] ); + + threads [i] = co_create( 256 * 1024, thread_func ); + assert( threads [i] ); + + i = (i + 1) % thread_count; + } + + for ( i = 0; i < thread_count; i++ ) + if ( threads [i] ) + co_delete( threads [i] ); +} + +static cothread_t threads [10]; +static int next; + +static void next_thread( void ) +{ + co_switch( threads [next++] ); +} + +static void preserved_func( void ) +{ + int seed = next; + + #define INIT(type,n) \ + register type type##n = rand(); + + #define CHECK(type,n) \ + assert( type##n == rand() ); + + #define REGS_(type,op,base) \ + op(type,base##0)\ + op(type,base##1)\ + op(type,base##2)\ + op(type,base##3)\ + op(type,base##4)\ + op(type,base##5)\ + op(type,base##6)\ + op(type,base##7)\ + op(type,base##8)\ + op(type,base##9) + + /* Invokes op(type,n), with n going from 00 through 49 */ + #define REGS(type,op) \ + REGS_(type,op,0)\ + REGS_(type,op,1)\ + REGS_(type,op,2)\ + REGS_(type,op,3)\ + REGS_(type,op,4) + + srand(seed); + { + /* Fill int and FP registers with random values */ + REGS(size_t,INIT) + + #if LIBCO_PPC_FP + REGS(double,INIT) + #endif + + /* Run second copy of this func that puts different random values + in registers, and then switches back to us */ + next_thread(); + + /* Verify that registers match random values */ + srand(seed); + + REGS(size_t,CHECK) + + #if LIBCO_PPC_FP + REGS(double,CHECK) + #endif + } + + next_thread(); +} + +/* Verifies that registers are saved and restored by co_switch() */ +static void test_preserved( void ) +{ + threads [0] = co_create( 64 * 1024, preserved_func ); + threads [1] = co_create( 64 * 1024, preserved_func ); + threads [2] = threads [0]; + threads [3] = co_active(); + + next = 0; + next_thread(); +} + +int main( void ) +{ + printf( "Basic test\n" ); + print_libco_opts(); + + test_initial(); + test_leaks(); + test_preserved(); + + printf( "Passed\n\n" ); + return 0; +} diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/test_regs.c b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/test_regs.c new file mode 100644 index 0000000..754252e --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/test_regs.c @@ -0,0 +1,274 @@ +/* Verifies that correct registers are saved and restored by co_switch(), +that it doesn't unnecessarily preserve volatile registers, and that it +never touches system registers. MUST be compiled with --omit-frame-pointer */ + +#include "libco/libco.h" +#include "ppc_regs.h" + +#include +#include +#include +#include +#include "testing.h" + +enum { + spr_cr = 1 << 0, + spr_xer = 1 << 8, + spr_ctr = 1 << 9, + spr_fpscr = 1 << 10, + spr_vrsave = 1 << 11, +}; + +typedef struct { + unsigned spr; + unsigned gpr; + unsigned fpr; + unsigned vr; +} regmask_t; + +enum { fpscr_mask = ~0x60000800 }; + +/* Single register and range of registers (inclusive) */ +#define REG( b ) (1 << (b)) +#define RANGE( start, end ) ((~0 << (start)) & ~(~0 << (end) << 1)) + +/* Error if modified. FPRs and VRs included here since they shouldn't be +modified or needed during switching. */ +static const regmask_t system_regs = { + spr_fpscr | (spr_vrsave*!LIBCO_PPC_ALTIVEC), + REG(2) | (REG(13)*LIBCO_PPC32), + RANGE(0,LIBCO_PPC_FP ? 12 : 31), + RANGE(0,LIBCO_PPC_ALTIVEC ? 19 : 31) +}; + +/* Error if not saved and restored */ +static const regmask_t switched_regs = { + 0x7E | (spr_vrsave*LIBCO_PPC_ALTIVEC), + REG(1) | RANGE(14-LIBCO_PPC32,31), + RANGE(14,31)*LIBCO_PPC_FP, + RANGE(20,31)*LIBCO_PPC_ALTIVEC +}; + +/* Error if saved and restored; fine if modified */ +static const regmask_t volatile_regs = { + spr_xer | spr_ctr, + RANGE(3,13-LIBCO_PPC32) | REG(0), + 0, + 0 +}; + +#undef RANGE +#undef REG + +/* Everything is broken into a separate function to avoid the compiler loading +addresses into registers between operations, since we clear all registers. */ + +static int zero_enabled; + +static ppc_regs_t regs_saved; +static ppc_regs_t regs_in; +static ppc_regs_t regs_zero; +static ppc_regs_t regs_out; + +static void save_regs_saved() +{ + save_ppc_regs( ®s_saved ); + + /* We must not trash these when restoring registers */ + regs_in.gpr [1] = regs_saved.gpr [1]; + regs_in.gpr [2] = regs_saved.gpr [2]; + + regs_out = regs_in; +} + +static void load_regs_saved() { load_ppc_regs( ®s_saved ); } +static void load_regs_in() { load_ppc_regs( ®s_in ); } +static void load_regs_zero() { load_ppc_regs( ®s_zero ); } +static void save_regs_out() { save_ppc_regs( ®s_out ); } + +static cothread_t main_thread; +static cothread_t test_thread; +static cothread_t zero_thread; + +static void switch_main_thread() { co_switch( main_thread ); } +static void switch_test_thread() { co_switch( test_thread ); } +static void switch_zero_thread() { if ( zero_enabled ) co_switch( zero_thread ); } + +static void test_thread_func() +{ + for ( ;; ) + { + /* Save current registers, load random registers, switch to another + thread which then loads zero into registers and switches back to us, + save our registers to see whether they match, then restore originals. */ + save_regs_saved(); + load_regs_in(); + switch_zero_thread(); + save_regs_out(); + load_regs_saved(); + switch_main_thread(); + } +} + +static void zero_thread_func() +{ + for ( ;; ) + { + load_regs_zero(); + switch_test_thread(); + } +} + +/* Determines which registers are modified by co_switch(). If enable_zero_fill +is true, clears registers between two co_switch() calls. ORs result to +*out. */ +static void find_modified( regmask_t* out, int enable_zero_fill ) +{ + int i; + + zero_enabled = enable_zero_fill; + + for ( i = 0; i < sizeof regs_in; i++ ) + ((unsigned char*) ®s_in) [i] = rand() >> 4; + + switch_test_thread(); + + regs_out.fpscr = (regs_out.fpscr & fpscr_mask) | (regs_in.fpscr & ~fpscr_mask); + + #define SPR( name ) \ + if ( regs_out.name != regs_in.name )\ + out->spr |= spr_##name; + + SPR( xer ); + SPR( ctr ); + SPR( fpscr ); + SPR( vrsave ); + + #undef SPR + + for ( i = 0; i < 8; i++ ) + if ( (regs_out.cr ^ regs_in.cr) >> (i*4) & 0x0F ) + out->spr |= spr_cr << i; + + #define REG( name ) \ + {\ + for ( i = 0; i < 32; i++ )\ + if ( memcmp( ®s_out.name [i], ®s_in.name [i], sizeof regs_in.name [i] ) )\ + out->name |= 1 << i;\ + } + + REG( gpr ); + REG( fpr ); + REG( vr ); + + #undef REG +} + +static void print_diffs( unsigned diff, const char name [] ) +{ + if ( diff ) + { + int i; + + printf( "%s: ", name ); + + for ( i = 0; i < 32; i++ ) + if ( diff >> i & 1 ) + printf( "%d ", i ); + + printf( "\n" ); + } +} + +static unsigned check_regs( regmask_t regs, regmask_t mask, regmask_t cmp ) +{ + unsigned any_diffs = 0; + unsigned diff; + + diff = (regs.spr & mask.spr) ^ cmp.spr; + any_diffs |= diff; + if ( diff ) + { + int i; + for ( i = 0; i < 8; i++ ) + if ( diff >> i & 1 ) + printf( "CR%d ", 7 - i ); + + #define SPR( name ) \ + if ( diff & spr_##name )\ + printf( #name " " ); + + SPR( xer ); + SPR( ctr ); + SPR( fpscr ); + SPR( vrsave ); + + #undef SPR + + printf( "\n" ); + } + + diff = (regs.gpr & mask.gpr) ^ cmp.gpr; + any_diffs |= diff; + print_diffs( diff, "GPR" ); + + diff = (regs.fpr & mask.fpr) ^ cmp.fpr; + any_diffs |= diff; + print_diffs( diff, "FPR" ); + + diff = (regs.vr & mask.vr) ^ cmp.vr; + any_diffs |= diff; + print_diffs( diff, "VR " ); + + return any_diffs; +} + +static void test( void ) +{ + /* Run a bunch of trials and accumulate which registers are modified by co_switch(), + and which aren't swapped by it. */ + regmask_t modified = { 0 }; + regmask_t unswitched = { 0 }; + int n; + + for ( n = 2000; n--; ) + { + find_modified( &modified, 0 ); + find_modified( &unswitched, 1 ); + } + + { + regmask_t zero = { 0 }; + unsigned error = 0; + + printf( "Checking for modified system registers\n" ); + error |= check_regs( modified, system_regs, zero ); + + printf( "Checking for unpreserved non-volatiles\n" ); + error |= check_regs( unswitched, switched_regs, zero ); + + printf( "Checking for unnecessarily preserved volatiles\n" ); + error |= check_regs( unswitched, volatile_regs, volatile_regs ); + + if ( error ) + { + printf( "Failed\n" ); + exit( EXIT_FAILURE ); + } + } +} + +int main( void ) +{ + printf( "Register test\n" ); + print_libco_opts(); + + main_thread = co_active(); + test_thread = co_create( 32 * 1024, test_thread_func ); + zero_thread = co_create( 32 * 1024, zero_thread_func ); + + test(); + + printf( "Passed\n\n" ); + return 0; +} diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/test_threads.c b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/test_threads.c new file mode 100644 index 0000000..ffa4c23 --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/test_threads.c @@ -0,0 +1,188 @@ +/* Stress tests cothreads + +Randomly creates, deletes, switches threads, and randomly +shuffles random data between locals and shared globals, +and randomly calls deeper and returns shallower in call +chain of each thread. After running for a while, prints +XOR of all the global data, and verifies that this matches +correct value. + +STRESS_EXCEPTIONS uses C++ exceptions rather than longjmp. +*/ + +#include "libco/libco.h" + +#include +#include +#include +#include +#include +#include "testing.h" + +#if BLARGG_DEV + int const iter = 300; + unsigned const final_data = 0x4E914B5A; +#else + int const iter = 100000; + unsigned const final_data = 0x1FED4BE2; +#endif + +int const stack_size = 256 * 1024; + +enum { max_threads = 16 }; +static cothread_t threads [max_threads]; + +static unsigned shared [16]; + +static unsigned rnd() +{ + static unsigned n = 1; + return n = (n >> 1) ^ (0xEDB88320 & -(n & 1)); +} + +static void run( jmp_buf, int depth ); + +static void entry( void ) +{ + run( NULL, 0 ); +} + +static int max_depth = 0; + +static void run( jmp_buf jb, int depth ) +{ + unsigned local [16]; + memcpy( local, shared, sizeof local ); + + if ( depth > max_depth ) + max_depth = depth; + + while ( 1 ) + { + co_switch( threads [0] ); + + switch ( rnd() & 7 ) + { + case 0: + if ( depth > 0 ) + { + if ( rnd() & 1 ) + #if STRESS_EXCEPTIONS + throw 0; + #else + longjmp( jb, 1 ); + #endif + goto ret; + } + break; + + case 1: + if ( depth < 50 ) + { + #if STRESS_EXCEPTIONS + { + try { + run( jb, depth + 1 ); + } + catch ( ... ) + { } + } + #else + { + jmp_buf jb2; + if ( !setjmp( jb2 ) ) + run( jb2, depth + 1 ); + } + #endif + } + break; + + case 2: { + int i; + for ( i = 0; i < max_threads; i++ ) + { + if ( !threads [i] ) + { + threads [i] = co_create( stack_size, entry ); + assert( threads [i] ); + break; + } + } + break; + } + + case 3: + { + int i = rnd() & (max_threads - 1); + if ( i > 0 && threads [i] && threads [i] != co_active() ) + { + co_delete( threads [i] ); + threads [i] = 0; + } + break; + } + + case 4: + case 5: + case 6: + case 7: { + int n; + for ( n = 10; n--; ) + { + unsigned r; + r = rnd(); local [r & 15] += rnd(); + r = rnd(); shared [r & 15] += rnd(); + r = rnd(); local [r & 15] ^= shared [rnd() & 15]; + r = rnd(); shared [r & 15] ^= local [rnd() & 15]; + } + break; + } + + } + + { + int i = rnd() & (max_threads - 1); + if ( threads [i] && threads [i] != co_active() ) + co_switch( threads [i] ); + } + } +ret:; +} + +int main( void ) +{ + int i, n; + + printf( "Thread test\n" ); + print_libco_opts(); + + threads [0] = co_active(); + threads [1] = co_create( stack_size, entry ); + assert( threads [1] ); + for ( n = 0; n < iter; n++ ) + { + /* + if ( !(n & (n - 1)) ) + printf( "%d\n", n );*/ + + for ( i = 1; i < max_threads; i++ ) + if ( threads [i] ) + co_switch( threads [i] ); + } + + { + unsigned all = 0; + for ( i = 0; i < 16; i++ ) + all ^= shared [i]; + + if ( all != final_data ) + { + printf( "0x%08X\n", all ); + printf( "Incorrect CRC\n" ); + return EXIT_FAILURE; + } + } + + printf( "Passed\n\n" ); + return 0; +} diff --git a/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/testing.h b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/testing.h new file mode 100644 index 0000000..a7d09fd --- /dev/null +++ b/mednafen/snes/src/lib/libco/blargg_libco_ppc64-5/testing.h @@ -0,0 +1,36 @@ +#if _ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__ + #define LIBCO_PPC32 0 +#else + #define LIBCO_PPC32 1 +#endif + +#ifdef LIBCO_PPC_NOFP + #define LIBCO_PPC_FP 0 +#else + #define LIBCO_PPC_FP 1 +#endif + +#ifdef __ALTIVEC__ + #define LIBCO_PPC_ALTIVEC 1 +#else + #define LIBCO_PPC_ALTIVEC 0 +#endif + +static void print_libco_opts( void ) +{ + #if LIBCO_PPC32 + printf( "PPC32 " ); + #else + printf( "PPC64 " ); + #endif + + #if LIBCO_PPC_FP + printf( "FP " ); + #endif + + #if LIBCO_PPC_ALTIVEC + printf( "ALTIVEC " ); + #endif + + printf( "\n" ); +} diff --git a/mednafen/snes/src/lib/libco/fiber.c b/mednafen/snes/src/lib/libco/fiber.c new file mode 100755 index 0000000..02ef5bc --- /dev/null +++ b/mednafen/snes/src/lib/libco/fiber.c @@ -0,0 +1,51 @@ +/* + libco.win (2008-01-28) + authors: Nach, byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#define WINVER 0x0400 +#define _WIN32_WINNT 0x0400 +#define WIN32_LEAN_AND_MEAN +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local cothread_t co_active_ = 0; + +static void __stdcall co_thunk(void *coentry) { + ((void (*)(void))coentry)(); +} + +cothread_t co_active() { + if(!co_active_) { + ConvertThreadToFiber(0); + co_active_ = GetCurrentFiber(); + } + return co_active_; +} + +cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { + if(!co_active_) { + ConvertThreadToFiber(0); + co_active_ = GetCurrentFiber(); + } + return (cothread_t)CreateFiber(heapsize, co_thunk, (void*)coentry); +} + +void co_delete(cothread_t cothread) { + DeleteFiber(cothread); +} + +void co_switch(cothread_t cothread) { + co_active_ = cothread; + SwitchToFiber(cothread); +} + +#ifdef __cplusplus +} +#endif diff --git a/mednafen/snes/src/lib/libco/libco.c b/mednafen/snes/src/lib/libco/libco.c new file mode 100755 index 0000000..5567626 --- /dev/null +++ b/mednafen/snes/src/lib/libco/libco.c @@ -0,0 +1,23 @@ +/* + libco + auto-selection module + license: public domain +*/ + +#if defined(__GNUC__) && defined(__i386__) + #include "x86.c" +#elif defined(__GNUC__) && defined(__amd64__) + #include "amd64.c" +#elif defined(__GNUC__) && defined(_ARCH_PPC) + #include "ppc.c" +#elif defined(__GNUC__) + #include "sjlj.c" +#elif defined(_MSC_VER) && defined(_M_IX86) + #include "x86.c" +#elif defined(_MSC_VER) && defined(_M_AMD64) + #include "amd64.c" +#elif defined(_MSC_VER) + #include "fiber.c" +#else + #error "libco: unsupported processor, compiler or operating system" +#endif diff --git a/mednafen/snes/src/lib/libco/libco.h b/mednafen/snes/src/lib/libco/libco.h new file mode 100755 index 0000000..b1b49a2 --- /dev/null +++ b/mednafen/snes/src/lib/libco/libco.h @@ -0,0 +1,34 @@ +/* + libco + version: 0.15 (2009-10-12) + license: public domain +*/ + +#ifndef LIBCO_H +#define LIBCO_H + +#ifdef LIBCO_C + #ifdef LIBCO_MP + #define thread_local __thread + #else + #define thread_local + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* cothread_t; + +cothread_t co_active(); +cothread_t co_create(unsigned int, void (*)(void)); +void co_delete(cothread_t); +void co_switch(cothread_t); + +#ifdef __cplusplus +} +#endif + +/* ifndef LIBCO_H */ +#endif diff --git a/mednafen/snes/src/lib/libco/ppc-elf.c b/mednafen/snes/src/lib/libco/ppc-elf.c new file mode 100755 index 0000000..5740f77 --- /dev/null +++ b/mednafen/snes/src/lib/libco/ppc-elf.c @@ -0,0 +1,325 @@ +/* + * libco.ppc-elf + * author: Kernigh + * license: public domain + * + * PowerPC 32-bit ELF implementation of libco (for compile with GCC), + * ported from PowerPC Mac OS X implementation (ppc.s) by Vas Crabb. + * This ELF version works for OpenBSD, and might also work for FreeBSD, + * NetBSD and Linux. + * + * Note 1: This implementation does not handle the AltiVec/VMX + * registers, because the ELF ABI does not mention them, + * and my OpenBSD system is not using them. + * + * Note 2: If you want position-independent code, then you must + * define __PIC__. gcc -fpic or -fPIC defines __PIC__, but + * gcc -fpie or -fPIE might not. If you want to use -fpie + * or -fPIE, then you might need a manual definition: + * gcc -fpie -D__PIC__=1 + * gcc -fPIE -D__PIC__=2 + * + * The ELF ABI is "System V Application Binary Interface, PowerPC + * Processor Supplement", which you can get from + * + * (PDF file, hosted by Linux Foundation). + * + * ELF and Mac OS X use similar conventions to allocate the registers, + * and to pass arguments and return values through registers. The main + * differences are that ELF has a slightly different stack format, that + * symbols are different (and without an extra underscore at the start), + * and that the assembly syntax is different. + * + * A function may destroy the values of volatile registers, but must + * preserve the values of nonvolatile registers. So the co_switch() + * function only saves the nonvolatile registers. + * + * [nonvolatile registers in ELF] + * %r1, %r14..%r31 + * %f14..%f31 + * %cr2..%cr4 in cr + * + * [volatile registers in ELF] + * %r0, %r3..%r10 + * %f0..%f13 + * %cr0, %cr1, %cr5..%cr7 in cr + * ctr, lr, xer + * + * lr (link register) is volatile, but it contains the return address, + * so co_switch must save lr. + * + * %r13 is the small data pointer. This is constant across threads, so + * co_switch() does not touch %r13. + * + * %r2 is a reserved register, so co_switch() does not touch %r2. Some + * systems might borrow an idea from the PowerPC Embedded ABI, and might + * use %r2 as a small read-only data pointer, which is constant across + * threads. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void * cothread_t; + +/* + * co_active_context is either in a global offset table (if we are + * compiling -fPIC or -fPIE) or has an absolute position. + */ +static void *co_main_stack_pointer; +static cothread_t co_active_context = &co_main_stack_pointer; + +extern cothread_t co_active() { + return co_active_context; +} + +/* + * Embedded assembly. + * + * We are not using the percent-sign substitution feature, + * so we must write "%r1", not "%%r1". + * + * We always write 'bl malloc@plt', not 'bl malloc'. The '@plt' + * is necessary in position-indepent code and seems to have no + * significant effect in fixed-position code. + * + * We never use the 'lmw' or 'stmw' instructions. The ELF ABI + * mentions that these instructions "are usually slower than + * a sequence of other instructions that have the same effect." + * We instead use sequences of 'lwz' or 'stz' instructions. + */ +__asm__("\n" +"### embedded assembly \n" +".section \".text\" \n" +" .balign 4 \n" +" \n" +/* + * void co_switch(co_thread to %r3) + * + * Allocate our stack frame of 240 bytes: + * Old New Value + * 4(%r1) 244(%r1) return address, used by us + * 0(%r1) 240(%r1) frame pointer + * 232(%r1) %f31 + * 224(%r1) %f30 + * ... + * 96(%r1) %f14 + * 92(%r1) %r31 + * 88(%r1) %r30 + * ... + * 24(%r1) %r14 + * 20(%r1) condition register + * 8(%r1) padding of 12 bytes + * 4(%r1) return address, never used + * 0(%r1) frame pointer + * + * Save our registers in our stack frame. + * Save our stack pointer in 0(%r4). + * Switch to the stack of the other thread. + * Restore registers and return. + */ +" .globl co_switch \n" +" .type co_switch, @function \n" +"co_switch: \n" +" mflr %r0 # %r0 = return address \n" +" mfcr %r9 # %r9 = condition register \n" +" stwu %r1, -240(%r1) # allocate stack frame \n" +" \n" +" stw %r0, 244(%r1) # save return address \n" +" stfd %f31, 232(%r1) # save floating-point regs \n" +" stfd %f30, 224(%r1) \n" +" stfd %f29, 216(%r1) \n" +" stfd %f28, 208(%r1) \n" +" stfd %f27, 200(%r1) \n" +" stfd %f26, 192(%r1) \n" +" stfd %f25, 184(%r1) \n" +" stfd %f24, 176(%r1) \n" +" stfd %f23, 168(%r1) \n" +" stfd %f22, 160(%r1) \n" +" stfd %f21, 152(%r1) \n" +" stfd %f20, 144(%r1) \n" +" stfd %f19, 136(%r1) \n" +" stfd %f18, 128(%r1) \n" +" stfd %f17, 120(%r1) \n" +" stfd %f16, 112(%r1) \n" +" stfd %f16, 104(%r1) \n" +" stfd %f14, 96(%r1) \n" +" stw %r31, 92(%r1) # save general-purpose regs \n" +" stw %r30, 88(%r1) \n" +" stw %r29, 84(%r1) \n" +" stw %r28, 80(%r1) \n" +" stw %r27, 76(%r1) \n" +" stw %r26, 72(%r1) \n" +" stw %r25, 68(%r1) \n" +" stw %r24, 64(%r1) \n" +" stw %r23, 60(%r1) \n" +" stw %r22, 56(%r1) \n" +" stw %r21, 52(%r1) \n" +" stw %r20, 48(%r1) \n" +" stw %r19, 44(%r1) \n" +" stw %r18, 40(%r1) \n" +" stw %r17, 36(%r1) \n" +" stw %r16, 32(%r1) \n" +" stw %r15, 28(%r1) \n" +" stw %r14, 24(%r1) \n" +" stw %r9, 20(%r1) # save condition reg \n" +" \n" +" # save current context, set new context \n" +" # %r4 = co_active_context \n" +" # co_active_context = %r3 \n" +#if __PIC__ == 2 +" # position-independent code, large model (-fPIC) \n" +" bl _GLOBAL_OFFSET_TABLE_@local-4 \n" +" mflr %r8 # %r8 = address of got \n" +" addis %r7, %r8, co_active_context@got@ha \n" +" lwz %r6, co_active_context@got@l(%r7) \n" +" lwz %r4, 0(%r6) \n" +" stw %r3, 0(%r6) \n" +#elif __PIC__ == 1 +" # position-independent code, small model (-fpic) \n" +" bl _GLOBAL_OFFSET_TABLE_@local-4 \n" +" mflr %r8 # %r8 = address of got \n" +" lwz %r7, co_active_context@got(%r8) \n" +" lwz %r4, 0(%r7) \n" +" stw %r3, 0(%r7) \n" +#else +" # fixed-position code \n" +" lis %r8, co_active_context@ha \n" +" lwz %r4, co_active_context@l(%r8) \n" +" stw %r3, co_active_context@l(%r8) \n" +#endif +" \n" +" # save current stack pointer \n" +" stw %r1, 0(%r4) \n" +" # get new stack pointer \n" +" lwz %r1, 0(%r3) \n" +" \n" +" lwz %r0, 244(%r1) # get return address \n" +" lfd %f31, 232(%r1) # restore floating-point regs \n" +" lfd %f30, 224(%r1) \n" +" lfd %f29, 216(%r1) \n" +" lfd %f28, 208(%r1) \n" +" lfd %f27, 200(%r1) \n" +" lfd %f26, 192(%r1) \n" +" lfd %f25, 184(%r1) \n" +" lfd %f24, 176(%r1) \n" +" lfd %f23, 168(%r1) \n" +" lfd %f22, 160(%r1) \n" +" lfd %f21, 152(%r1) \n" +" lfd %f20, 144(%r1) \n" +" lfd %f19, 136(%r1) \n" +" lfd %f18, 128(%r1) \n" +" lfd %f17, 120(%r1) \n" +" lfd %f16, 112(%r1) \n" +" lfd %f16, 104(%r1) \n" +" lfd %f14, 96(%r1) \n" +" lwz %r31, 92(%r1) # restore general-purpose regs \n" +" lwz %r30, 88(%r1) \n" +" lwz %r29, 84(%r1) \n" +" lwz %r28, 80(%r1) \n" +" lwz %r27, 76(%r1) \n" +" lwz %r26, 72(%r1) \n" +" lwz %r25, 68(%r1) \n" +" lwz %r24, 64(%r1) \n" +" lwz %r23, 60(%r1) \n" +" lwz %r22, 56(%r1) \n" +" lwz %r21, 52(%r1) \n" +" lwz %r20, 48(%r1) \n" +" lwz %r19, 44(%r1) \n" +" lwz %r18, 40(%r1) \n" +" lwz %r17, 36(%r1) \n" +" lwz %r16, 32(%r1) \n" +" lwz %r15, 28(%r1) \n" +" lwz %r14, 24(%r1) \n" +" lwz %r9, 20(%r1) # get condition reg \n" +" \n" +" addi %r1, %r1, 240 # free stack frame \n" +" mtlr %r0 # restore return address \n" +" mtcr %r9 # restore condition register \n" +" blr # return \n" +" .size co_switch, . - co_switch \n" +" \n" +/* + * cothread_t %r3 co_create(unsigned int stack_size %r3, + * void (*coentry %r4)()) + * + * Allocate a new stack, such that when you co_switch to that + * stack, then co_switch returns to coentry. + */ +" .globl co_create \n" +" .type co_create, @function \n" +"co_create: \n" +" mflr %r0 # %r0 = return address \n" +" stwu %r1, -16(%r1) # allocate my stack frame \n" +" stw %r0, 20(%r1) # save return address \n" +" stw %r31, 12(%r1) # save %r31 \n" +" stw %r30, 8(%r1) # save %r30 \n" +" \n" +" mr %r30, %r3 # %r30 = stack_size \n" +" mr %r31, %r4 # %r31 = coentry \n" +" \n" +" # Call malloc(stack_size %r3) to allocate stack; \n" +" # malloc() probably uses good alignment. \n" +" # \n" +" bl malloc@plt # returns %r3 = low end \n" +" cmpwi %r3, 0 # if returned NULL, \n" +" beq- 1f # then abort \n" +" \n" +" # we return %r3 = low end of stack \n" +" add %r4, %r3, %r30 # %r4 = high end of stack \n" +" \n" +" # uncomment if malloc() uses wrong alignment \n" +" #rlwinm %r4,%r4,0,0,27 # force 16-byte alignment \n" +" \n" + /* + * Allocate two stack frames: + * 16 bytes for stack frame with return address + * 240 bytes for co_switch stack frame + * + * Old New Value + * -8(%r4) 248(%r5) padding of 8 bytes + * -12(%r4) 244(%r5) return address = coentry + * -16(%r4) 240(%r5) frame pointer = NULL + * 232(%r5) %f31 = 0 + * ... + * 20(%r5) condition register = 0 + * 0(%r5) frame pointer + */ +" li %r9, (240-20)/4+1 \n" +" addi %r5, %r4, -16 # allocate first stack frame \n" +" li %r0, 0 \n" +" stwu %r5, -240(%r5) # allocate second stack frame \n" +" li %r8, 20 \n" +" mtctr %r9 # loop %r9 times \n" +"2: # loop to store zero to 20(%r5) through 240(%r5) \n" +" stwx %r0, %r5, %r8 \n" +" addi %r8, %r8, 4 # index += 4 \n" +" bdnz+ 2b # ctr -= 1, branch if nonzero \n" +" \n" +" stw %r31, 244(%r5) # return address = coentry \n" +" stw %r5, 0(%r3) # save stack pointer \n" +" \n" +" lwz %r0, 20(%r1) # get return address \n" +" lwz %r31, 12(%r1) # restore %r31 \n" +" lwz %r30, 8(%r1) # restore %r30 \n" +" mtlr %r0 # restore return address \n" +" addi %r1, %r1, 16 # free stack frame \n" +" blr # return \n" +" \n" +"1: b abort@plt # branch 1f to abort \n" +" .size co_create, . - co_create \n" +" \n" +/* + * void co_delete(cothread_t) => void free(void *) + */ +" .globl co_delete \n" +" .type co_delete, @function \n" +"co_delete: \n" +" b free@plt \n" +" \n" +); + +#ifdef __cplusplus +} +#endif diff --git a/mednafen/snes/src/lib/libco/ppc.c b/mednafen/snes/src/lib/libco/ppc.c new file mode 100644 index 0000000..a6028fd --- /dev/null +++ b/mednafen/snes/src/lib/libco/ppc.c @@ -0,0 +1,407 @@ +/* + libco.ppc (2010-10-17) + author: blargg + license: public domain +*/ + +/* PowerPC 32/64 using embedded or external asm, with optional +floating-point and AltiVec save/restore */ + +#define LIBCO_C +#include "libco.h" +#include +#include +#include + +#define LIBCO_MPROTECT (__unix__ && !LIBCO_PPC_ASM) + +#if LIBCO_MPROTECT + #include + #include +#endif + +/* State format (offsets in 32-bit words) + ++0 Pointer to swap code + Rest of function descriptor for entry function ++8 PC ++10 SP + Special regs + GPRs + FPRs + VRs + stack +*/ + +enum { state_size = 1024 }; +enum { above_stack = 2048 }; +enum { stack_align = 256 }; + +static thread_local cothread_t co_active_handle = 0; + +/**** Determine environment ****/ + +#define LIBCO_PPC64 (_ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__) + +/* Whether function calls are indirect through a descriptor, +or are directly to function */ +#ifndef LIBCO_PPCDESC + #if !_CALL_SYSV && (_CALL_AIX || _CALL_AIXDESC || LIBCO_PPC64) + #define LIBCO_PPCDESC 1 + #endif +#endif + +#ifdef LIBCO_PPC_ASM + + #ifdef __cplusplus + extern "C" + #endif + + /* Swap code is in ppc.S */ + void co_swap_asm( cothread_t, cothread_t ); + #define CO_SWAP_ASM( x, y ) co_swap_asm( x, y ) + +#else + +/* Swap code is here in array. Please leave dieassembly comments, +as they make it easy to see what it does, and reorder instructions +if one wants to see whether that improves performance. */ +static const uint32_t libco_ppc_code [] = { +#if LIBCO_PPC64 + 0x7d000026, /* mfcr r8 */ + 0xf8240028, /* std r1,40(r4) */ + 0x7d2802a6, /* mflr r9 */ + 0xf9c40048, /* std r14,72(r4) */ + 0xf9e40050, /* std r15,80(r4) */ + 0xfa040058, /* std r16,88(r4) */ + 0xfa240060, /* std r17,96(r4) */ + 0xfa440068, /* std r18,104(r4) */ + 0xfa640070, /* std r19,112(r4) */ + 0xfa840078, /* std r20,120(r4) */ + 0xfaa40080, /* std r21,128(r4) */ + 0xfac40088, /* std r22,136(r4) */ + 0xfae40090, /* std r23,144(r4) */ + 0xfb040098, /* std r24,152(r4) */ + 0xfb2400a0, /* std r25,160(r4) */ + 0xfb4400a8, /* std r26,168(r4) */ + 0xfb6400b0, /* std r27,176(r4) */ + 0xfb8400b8, /* std r28,184(r4) */ + 0xfba400c0, /* std r29,192(r4) */ + 0xfbc400c8, /* std r30,200(r4) */ + 0xfbe400d0, /* std r31,208(r4) */ + 0xf9240020, /* std r9,32(r4) */ + 0xe8e30020, /* ld r7,32(r3) */ + 0xe8230028, /* ld r1,40(r3) */ + 0x48000009, /* bl 1 */ + 0x7fe00008, /* trap */ + 0x91040030,/*1:stw r8,48(r4) */ + 0x80c30030, /* lwz r6,48(r3) */ + 0x7ce903a6, /* mtctr r7 */ + 0xe9c30048, /* ld r14,72(r3) */ + 0xe9e30050, /* ld r15,80(r3) */ + 0xea030058, /* ld r16,88(r3) */ + 0xea230060, /* ld r17,96(r3) */ + 0xea430068, /* ld r18,104(r3) */ + 0xea630070, /* ld r19,112(r3) */ + 0xea830078, /* ld r20,120(r3) */ + 0xeaa30080, /* ld r21,128(r3) */ + 0xeac30088, /* ld r22,136(r3) */ + 0xeae30090, /* ld r23,144(r3) */ + 0xeb030098, /* ld r24,152(r3) */ + 0xeb2300a0, /* ld r25,160(r3) */ + 0xeb4300a8, /* ld r26,168(r3) */ + 0xeb6300b0, /* ld r27,176(r3) */ + 0xeb8300b8, /* ld r28,184(r3) */ + 0xeba300c0, /* ld r29,192(r3) */ + 0xebc300c8, /* ld r30,200(r3) */ + 0xebe300d0, /* ld r31,208(r3) */ + 0x7ccff120, /* mtcr r6 */ +#else + 0x7d000026, /* mfcr r8 */ + 0x90240028, /* stw r1,40(r4) */ + 0x7d2802a6, /* mflr r9 */ + 0x91a4003c, /* stw r13,60(r4) */ + 0x91c40040, /* stw r14,64(r4) */ + 0x91e40044, /* stw r15,68(r4) */ + 0x92040048, /* stw r16,72(r4) */ + 0x9224004c, /* stw r17,76(r4) */ + 0x92440050, /* stw r18,80(r4) */ + 0x92640054, /* stw r19,84(r4) */ + 0x92840058, /* stw r20,88(r4) */ + 0x92a4005c, /* stw r21,92(r4) */ + 0x92c40060, /* stw r22,96(r4) */ + 0x92e40064, /* stw r23,100(r4) */ + 0x93040068, /* stw r24,104(r4) */ + 0x9324006c, /* stw r25,108(r4) */ + 0x93440070, /* stw r26,112(r4) */ + 0x93640074, /* stw r27,116(r4) */ + 0x93840078, /* stw r28,120(r4) */ + 0x93a4007c, /* stw r29,124(r4) */ + 0x93c40080, /* stw r30,128(r4) */ + 0x93e40084, /* stw r31,132(r4) */ + 0x91240020, /* stw r9,32(r4) */ + 0x80e30020, /* lwz r7,32(r3) */ + 0x80230028, /* lwz r1,40(r3) */ + 0x48000009, /* bl 1 */ + 0x7fe00008, /* trap */ + 0x91040030,/*1:stw r8,48(r4) */ + 0x80c30030, /* lwz r6,48(r3) */ + 0x7ce903a6, /* mtctr r7 */ + 0x81a3003c, /* lwz r13,60(r3) */ + 0x81c30040, /* lwz r14,64(r3) */ + 0x81e30044, /* lwz r15,68(r3) */ + 0x82030048, /* lwz r16,72(r3) */ + 0x8223004c, /* lwz r17,76(r3) */ + 0x82430050, /* lwz r18,80(r3) */ + 0x82630054, /* lwz r19,84(r3) */ + 0x82830058, /* lwz r20,88(r3) */ + 0x82a3005c, /* lwz r21,92(r3) */ + 0x82c30060, /* lwz r22,96(r3) */ + 0x82e30064, /* lwz r23,100(r3) */ + 0x83030068, /* lwz r24,104(r3) */ + 0x8323006c, /* lwz r25,108(r3) */ + 0x83430070, /* lwz r26,112(r3) */ + 0x83630074, /* lwz r27,116(r3) */ + 0x83830078, /* lwz r28,120(r3) */ + 0x83a3007c, /* lwz r29,124(r3) */ + 0x83c30080, /* lwz r30,128(r3) */ + 0x83e30084, /* lwz r31,132(r3) */ + 0x7ccff120, /* mtcr r6 */ +#endif + +#ifndef LIBCO_PPC_NOFP + 0xd9c400e0, /* stfd f14,224(r4) */ + 0xd9e400e8, /* stfd f15,232(r4) */ + 0xda0400f0, /* stfd f16,240(r4) */ + 0xda2400f8, /* stfd f17,248(r4) */ + 0xda440100, /* stfd f18,256(r4) */ + 0xda640108, /* stfd f19,264(r4) */ + 0xda840110, /* stfd f20,272(r4) */ + 0xdaa40118, /* stfd f21,280(r4) */ + 0xdac40120, /* stfd f22,288(r4) */ + 0xdae40128, /* stfd f23,296(r4) */ + 0xdb040130, /* stfd f24,304(r4) */ + 0xdb240138, /* stfd f25,312(r4) */ + 0xdb440140, /* stfd f26,320(r4) */ + 0xdb640148, /* stfd f27,328(r4) */ + 0xdb840150, /* stfd f28,336(r4) */ + 0xdba40158, /* stfd f29,344(r4) */ + 0xdbc40160, /* stfd f30,352(r4) */ + 0xdbe40168, /* stfd f31,360(r4) */ + 0xc9c300e0, /* lfd f14,224(r3) */ + 0xc9e300e8, /* lfd f15,232(r3) */ + 0xca0300f0, /* lfd f16,240(r3) */ + 0xca2300f8, /* lfd f17,248(r3) */ + 0xca430100, /* lfd f18,256(r3) */ + 0xca630108, /* lfd f19,264(r3) */ + 0xca830110, /* lfd f20,272(r3) */ + 0xcaa30118, /* lfd f21,280(r3) */ + 0xcac30120, /* lfd f22,288(r3) */ + 0xcae30128, /* lfd f23,296(r3) */ + 0xcb030130, /* lfd f24,304(r3) */ + 0xcb230138, /* lfd f25,312(r3) */ + 0xcb430140, /* lfd f26,320(r3) */ + 0xcb630148, /* lfd f27,328(r3) */ + 0xcb830150, /* lfd f28,336(r3) */ + 0xcba30158, /* lfd f29,344(r3) */ + 0xcbc30160, /* lfd f30,352(r3) */ + 0xcbe30168, /* lfd f31,360(r3) */ +#endif + +#ifdef __ALTIVEC__ + 0x7ca042a6, /* mfvrsave r5 */ + 0x39040180, /* addi r8,r4,384 */ + 0x39240190, /* addi r9,r4,400 */ + 0x70a00fff, /* andi. r0,r5,4095 */ + 0x90a40034, /* stw r5,52(r4) */ + 0x4182005c, /* beq- 2 */ + 0x7e8041ce, /* stvx v20,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ea049ce, /* stvx v21,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7ec041ce, /* stvx v22,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ee049ce, /* stvx v23,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f0041ce, /* stvx v24,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f2049ce, /* stvx v25,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f4041ce, /* stvx v26,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f6049ce, /* stvx v27,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f8041ce, /* stvx v28,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7fa049ce, /* stvx v29,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7fc041ce, /* stvx v30,r0,r8 */ + 0x7fe049ce, /* stvx v31,r0,r9 */ + 0x80a30034,/*2:lwz r5,52(r3) */ + 0x39030180, /* addi r8,r3,384 */ + 0x39230190, /* addi r9,r3,400 */ + 0x70a00fff, /* andi. r0,r5,4095 */ + 0x7ca043a6, /* mtvrsave r5 */ + 0x4d820420, /* beqctr */ + 0x7e8040ce, /* lvx v20,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ea048ce, /* lvx v21,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7ec040ce, /* lvx v22,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ee048ce, /* lvx v23,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f0040ce, /* lvx v24,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f2048ce, /* lvx v25,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f4040ce, /* lvx v26,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f6048ce, /* lvx v27,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f8040ce, /* lvx v28,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7fa048ce, /* lvx v29,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7fc040ce, /* lvx v30,r0,r8 */ + 0x7fe048ce, /* lvx v31,r0,r9 */ +#endif + + 0x4e800420, /* bctr */ +}; + + #if LIBCO_PPCDESC + /* Function call goes through indirect descriptor */ + #define CO_SWAP_ASM( x, y ) \ + ((void (*)( cothread_t, cothread_t )) (uintptr_t) x)( x, y ) + #else + /* Function call goes directly to code */ + #define CO_SWAP_ASM( x, y ) \ + ((void (*)( cothread_t, cothread_t )) (uintptr_t) libco_ppc_code)( x, y ) + #endif + +#endif + +static uint32_t* co_create_( unsigned size, uintptr_t entry ) +{ + uint32_t* t = (uint32_t*) malloc( size ); + + (void) entry; + + #if LIBCO_PPCDESC + if ( t ) + { + /* Copy entry's descriptor */ + memcpy( t, (void*) entry, sizeof (void*) * 3 ); + + /* Set function pointer to swap routine */ + #ifdef LIBCO_PPC_ASM + *(const void**) t = *(void**) &co_swap_asm; + #else + *(const void**) t = libco_ppc_code; + #endif + } + #endif + + return t; +} + +cothread_t co_create( unsigned int size, void (*entry_)( void ) ) +{ + uintptr_t entry = (uintptr_t) entry_; + uint32_t* t = NULL; + + /* Be sure main thread was successfully allocated */ + if ( co_active() ) + { + size += state_size + above_stack + stack_align; + t = co_create_( size, entry ); + } + + if ( t ) + { + uintptr_t sp; + int shift; + + /* Save current registers into new thread, so that any special ones will + have proper values when thread is begun */ + CO_SWAP_ASM( t, t ); + + #if LIBCO_PPCDESC + /* Get real address */ + entry = (uintptr_t) *(void**) entry; + #endif + + /* Put stack near end of block, and align */ + sp = (uintptr_t) t + size - above_stack; + sp -= sp % stack_align; + + /* On PPC32, we save and restore GPRs as 32 bits. For PPC64, we + save and restore them as 64 bits, regardless of the size the ABI + uses. So, we manually write pointers at the proper size. We always + save and restore at the same address, and since PPC is big-endian, + we must put the low byte first on PPC32. */ + + /* If uintptr_t is 32 bits, >>32 is undefined behavior, so we do two shifts + and don't have to care how many bits uintptr_t is. */ + #if LIBCO_PPC64 + shift = 16; + #else + shift = 0; + #endif + + /* Set up so entry will be called on next swap */ + t [8] = (uint32_t) (entry >> shift >> shift); + t [9] = (uint32_t) entry; + + t [10] = (uint32_t) (sp >> shift >> shift); + t [11] = (uint32_t) sp; + } + + return t; +} + +void co_delete( cothread_t t ) +{ + free( t ); +} + +static void co_init_( void ) +{ + #if LIBCO_MPROTECT + /* TODO: pre- and post-pad PPC code so that this doesn't make other + data executable and writable */ + long page_size = sysconf( _SC_PAGESIZE ); + if ( page_size > 0 ) + { + uintptr_t align = page_size; + uintptr_t begin = (uintptr_t) libco_ppc_code; + uintptr_t end = begin + sizeof libco_ppc_code; + + /* Align beginning and end */ + end += align - 1; + end -= end % align; + begin -= begin % align; + + mprotect( (void*) begin, end - begin, PROT_READ | PROT_WRITE | PROT_EXEC ); + } + #endif + + co_active_handle = co_create_( state_size, (uintptr_t) &co_switch ); +} + +cothread_t co_active() +{ + if ( !co_active_handle ) + co_init_(); + + return co_active_handle; +} + +void co_switch( cothread_t t ) +{ + cothread_t old = co_active_handle; + co_active_handle = t; + + CO_SWAP_ASM( t, old ); +} diff --git a/mednafen/snes/src/lib/libco/ppc.s b/mednafen/snes/src/lib/libco/ppc.s new file mode 100755 index 0000000..d7f6b75 --- /dev/null +++ b/mednafen/snes/src/lib/libco/ppc.s @@ -0,0 +1,478 @@ +;***** +;libco.ppc (2007-11-29) +;author: Vas Crabb +;license: public domain +; +;cross-platform PowerPC implementation of libco +;special thanks to byuu for writing the original version +; +;[ABI compatibility] +;- gcc; mac os x; ppc +; +;[nonvolatile registers] +;- GPR1, GPR13 - GPR31 +;- FPR14 - FPR31 +;- V20 - V31 +;- VRSAVE, CR2 - CR4 +; +;[volatile registers] +;- GPR0, GPR2 - GPR12 +;- FPR0 - FPR13 +;- V0 - V19 +;- LR, CTR, XER, CR0, CR1, CR5 - CR7 +;***** + + +;Declare some target-specific stuff + + .section __TEXT,__text,regular,pure_instructions + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .machine ppc + + +;Constants + + .cstring + .align 2 + +_sysctl_altivec: + .ascii "hw.optional.altivec\0" + + +;Declare space for variables + +.lcomm _co_environ,4,2 ;bit 0 = initialised, bit 1 = have Altivec/VMX +.lcomm _co_primary_buffer,1024,2 ;buffer (will be zeroed by loader) + + .data + .align 2 + +_co_active_context: + .long _co_primary_buffer + + + .text + .align 2 + + +;Declare exported names + +.globl _co_active +.globl _co_create +.globl _co_delete +.globl _co_switch + + +;***** +;extern "C" cothread_t co_active(); +;return = GPR3 +;***** + +_co_active: + mflr r0 ;GPR0 = return address + bcl 20,31,L_co_active$spb +L_co_active$spb: + mflr r2 ;GPR2 set for position-independance + addis r3,r2,ha16(_co_active_context-L_co_active$spb) ;get value in GPR3 + lwz r3,lo16(_co_active_context-L_co_active$spb)(r3) + mtlr r0 ;LR = return address + blr ;return + + +;***** +;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)()); +;GPR3 = heapsize +;GPR4 = coentry +;return = GPR3 +;***** + +_co_create: + mflr r0 ;GPR0 = return address + stmw r30,-8(r1) ;save GPR30 and GPR31 + stw r0,8(r1) ;save return address + stwu r1,-(2*4+16+24)(r1) ;allocate 16 bytes for locals/parameters + +;create heap space (stack + register storage) + addi r31,r3,1024-24 ;subtract space for linkage + mr r30,r4 ;GPR30 = coentry + addi r3,r3,1024 ;allocate extra memory for contextual info + bl L_malloc$stub ;GPR3 = malloc(heapsize + 1024) + add r4,r3,r31 ;GPR4 points to top-of-stack + rlwinm r5,r4,0,0,27 ;force 16-byte alignment + +;store thread entry point + registers, so that first call to co_switch will execute coentry + stw r30,8(r5) ;store entry point + addi r6,0,2+19+18*2+12*4+1 ;clear for CR, old GPR1, 19 GPRs, 18 FPRs, 12 VRs, VRSAVE + addi r0,0,0 + addi r7,0,4 ;start at 4(GPR5) + mtctr r6 +L_co_create$clear_loop: + stwx r0,r5,r7 ;clear a word + addi r7,r7,-4 ;increment pointer + bdnz L_co_create$clear_loop ;loop + stwu r5,-448(r5) ;store top of stack + +;initialize context memory heap and return + stw r5,0(r3) ;*cothread_t = stack heap pointer (GPR1) + lwz r1,0(r1) ;deallocate stack frame + lwz r8,8(r1) ;fetch return address + lmw r30,-8(r1) ;restore GPR30 and GPR31 + mtlr r8 ;return address in LR + blr ;return + + +;***** +;extern "C" void co_delete(cothread_t cothread); +;GPR3 = cothread +;***** + +_co_delete: + b L_free$stub ;free(GPR3) + + +;***** +;extern "C" void co_switch(cothread_t cothread); +;GPR3 = cothread +;***** +; +;Frame looks like: +; +;Old New Value +; 8(r1) 456(r1) Saved LR +; 4(r1) 452(r1) Saved CR +; 0(r1) 448(r1) Old GPR1 +; -4(r1) 444(r1) Saved GPR31 +; -8(r1) 440(r1) Saved GPR30 +;... ... ... +; -72(r1) 376(r1) Saved GPR14 +; -76(r1) 372(r1) Saved GPR13 +; -80(r1) 368(r1) Saved VRSAVE +; -84(r1) 364(r1) +++ +; -88(r1) 360(r1) Saved FPR31 +; -92(r1) 356(r1) +++ +; -96(r1) 352(r1) Saved FPR30 +;... ... ... +;-212(r1) 236(r1) +++ +;-216(r1) 232(r1) Saved FPR15 +;-220(r1) 228(r1) +++ +;-224(r1) 224(r1) Saved FPR14 +;-228(r1) 220(r1) +++ value +;-232(r1) 216(r1) +++ len +;-236(r1) 212(r1) +++ +;-240(r1) 208(r1) Saved VR31 +;-244(r1) 204(r1) +++ +;-248(r1) 200(r1) +++ +;-252(r1) 196(r1) +++ +;-256(r1) 192(r1) Saved VR30 +;... ... ... +;-388(r1) 60(r1) +++ +;-392(r1) 56(r1) +++ +;-396(r1) 52(r1) +++ +;-400(r1) 48(r1) Saved VR21 +;-404(r1) 44(r1) +++ +;-408(r1) 40(r1) +++ Param 5 (GPR7) +;-412(r1) 36(r1) +++ Param 4 (GPR6) +;-416(r1) 32(r1) Saved VR20 Param 3 (GPR5) +;-420(r1) 28(r1) - Param 2 (GPR4) +;-424(r1) 24(r1) - Param 1 (GPR3) +;-428(r1) 20(r1) - Reserved +;-432(r1) 16(r1) - Reserved +;-436(r1) 12(r1) - Reserved +;-440(r1) 8(r1) - New LR +;-444(r1) 4(r1) - New CR +;-448(r1) 0(r1) Saved GPR1 + + +_co_switch: + stmw r13,-76(r1) ;save preserved GPRs + stfd f14,-224(r1) ;save preserved FPRs + stfd f15,-216(r1) + stfd f16,-208(r1) + stfd f17,-200(r1) + stfd f18,-192(r1) + stfd f19,-184(r1) + stfd f20,-176(r1) + stfd f21,-168(r1) + stfd f22,-160(r1) + stfd f23,-152(r1) + stfd f24,-144(r1) + stfd f25,-136(r1) + stfd f26,-128(r1) + stfd f27,-120(r1) + stfd f28,-112(r1) + stfd f29,-104(r1) + stfd f30,-96(r1) + stfd f31,-88(r1) + mflr r0 ;save return address + stw r0,8(r1) + mfcr r2 ;save condition codes + stw r2,4(r1) + stwu r1,-448(r1) ;create stack frame (save 19 GPRs, 18 FRPs, 12 VRs, VRSAVE) + + mr r30,r3 ;save new context pointer + bcl 20,31,L_co_switch$spb ;get address of co_active_context +L_co_switch$spb: + mflr r31 + + addis r29,r31,ha16(_co_environ-L_co_switch$spb) ;get environment flags + lwz r8,lo16(_co_environ-L_co_switch$spb)(r29) + andis. r9,r8,0x8000 ;is it initialised? + bne+ L_co_switch$initialised + + addi r0,0,4 ;len = sizeof(int) + stw r0,216(r1) + addis r3,r31,ha16(_sysctl_altivec-L_co_switch$spb) ;GPR3 = "hw.optional.altivec" + addi r3,r3,lo16(_sysctl_altivec-L_co_switch$spb) + addi r4,r1,220 ;GPR4 = &value + addi r5,r1,216 ;GPR5 = &len + addi r6,0,0 ;newp = 0 + addi r7,0,0 ;newlen = 0 + bl L_sysctlbyname$stub ;call sysctlbyname + lwz r2,220(r1) ;fetch result + addis r8,0,0x8000 ;set initialised bit + cmpwi cr5,r3,0 ;assume error means not present + cmpwi cr6,r2,0 ;test result + blt- cr5,L_co_switch$store_environ + beq cr6,L_co_switch$store_environ + oris r8,r8,0x4000 ;set the flag to say we have it! +L_co_switch$store_environ: + stw r8,lo16(_co_environ-L_co_switch$spb)(r29) ;store environment flags +L_co_switch$initialised: + + andis. r10,r8,0x4000 ;do we have Altivec/VMX? + beq L_co_switch$save_no_vmx + mfspr r11,256 ;save VRSAVE + andi. r0,r11,0x0FFF ;short-circuit if it's zero + stw r11,368(r1) + beq L_co_switch$save_no_vmx + andi. r0,r11,0x0800 ;check bit 20 + addi r2,0,32 ;starting index + beq L_co_switch$save_skip_vr20 + stvx v20,r1,r2 ;save VR20 +L_co_switch$save_skip_vr20: + addi r2,r2,16 ;stride + andi. r0,r11,0x0400 ;check bit 21 + beq L_co_switch$save_skip_vr21 + stvx v21,r1,r2 ;save VR21 +L_co_switch$save_skip_vr21: + addi r2,r2,16 ;stride + andi. r0,r11,0x0200 ;check bit 22 + beq L_co_switch$save_skip_vr22 + stvx v22,r1,r2 ;save VR22 +L_co_switch$save_skip_vr22: + addi r2,r2,16 ;stride + andi. r0,r11,0x0100 ;check bit 23 + beq L_co_switch$save_skip_vr23 + stvx v23,r1,r2 ;save VR23 +L_co_switch$save_skip_vr23: + addi r2,r2,16 ;stride + andi. r0,r11,0x0080 ;check bit 24 + beq L_co_switch$save_skip_vr24 + stvx v24,r1,r2 ;save VR24 +L_co_switch$save_skip_vr24: + addi r2,r2,16 ;stride + andi. r0,r11,0x0040 ;check bit 25 + beq L_co_switch$save_skip_vr25 + stvx v25,r1,r2 ;save VR25 +L_co_switch$save_skip_vr25: + addi r2,r2,16 ;stride + andi. r0,r11,0x0020 ;check bit 26 + beq L_co_switch$save_skip_vr26 + stvx v26,r1,r2 ;save VR26 +L_co_switch$save_skip_vr26: + addi r2,r2,16 ;stride + andi. r0,r11,0x0010 ;check bit 27 + beq L_co_switch$save_skip_vr27 + stvx v27,r1,r2 ;save VR27 +L_co_switch$save_skip_vr27: + addi r2,r2,16 ;stride + andi. r0,r11,0x0008 ;check bit 28 + beq L_co_switch$save_skip_vr28 + stvx v28,r1,r2 ;save VR28 +L_co_switch$save_skip_vr28: + addi r2,r2,16 ;stride + andi. r0,r11,0x0004 ;check bit 29 + beq L_co_switch$save_skip_vr29 + stvx v29,r1,r2 ;save VR29 +L_co_switch$save_skip_vr29: + addi r2,r2,16 ;stride + andi. r0,r11,0x0002 ;check bit 30 + beq L_co_switch$save_skip_vr30 + stvx v30,r1,r2 ;save VR30 +L_co_switch$save_skip_vr30: + addi r2,r2,16 ;stride + andi. r0,r11,0x0001 ;check bit 31 + beq L_co_switch$save_skip_vr31 + stvx v31,r1,r2 ;save VR31 +L_co_switch$save_skip_vr31: +L_co_switch$save_no_vmx: + + addis r4,r31,ha16(_co_active_context-L_co_switch$spb) ;save current context + lwz r5,lo16(_co_active_context-L_co_switch$spb)(r4) + stw r30,lo16(_co_active_context-L_co_switch$spb)(r4);set new context + stw r1,0(r5) ;save current stack pointer + lwz r1,0(r30) ;get new stack pointer + + andis. r10,r8,0x4000 ;do we have Altivec/VMX? + beq L_co_switch$restore_no_vmx + lwz r11,368(r1) ;restore VRSAVE + andi. r0,r11,0x0FFF ;short-circuit if it's zero + mtspr 256,r11 + beq L_co_switch$restore_no_vmx + andi. r0,r11,0x0800 ;check bit 20 + addi r2,0,32 ;starting index + beq L_co_switch$restore_skip_vr20 + lvx v20,r1,r2 ;restore VR20 +L_co_switch$restore_skip_vr20: + addi r2,r2,16 ;stride + andi. r0,r11,0x0400 ;check bit 21 + beq L_co_switch$restore_skip_vr21 + lvx v21,r1,r2 ;restore VR21 +L_co_switch$restore_skip_vr21: + addi r2,r2,16 ;stride + andi. r0,r11,0x0200 ;check bit 22 + beq L_co_switch$restore_skip_vr22 + lvx v22,r1,r2 ;restore VR22 +L_co_switch$restore_skip_vr22: + addi r2,r2,16 ;stride + andi. r0,r11,0x0100 ;check bit 23 + beq L_co_switch$restore_skip_vr23 + lvx v23,r1,r2 ;restore VR23 +L_co_switch$restore_skip_vr23: + addi r2,r2,16 ;stride + andi. r0,r11,0x0080 ;check bit 24 + beq L_co_switch$restore_skip_vr24 + lvx v24,r1,r2 ;restore VR24 +L_co_switch$restore_skip_vr24: + addi r2,r2,16 ;stride + andi. r0,r11,0x0040 ;check bit 25 + beq L_co_switch$restore_skip_vr25 + lvx v25,r1,r2 ;restore VR25 +L_co_switch$restore_skip_vr25: + addi r2,r2,16 ;stride + andi. r0,r11,0x0020 ;check bit 26 + beq L_co_switch$restore_skip_vr26 + lvx v26,r1,r2 ;restore VR26 +L_co_switch$restore_skip_vr26: + addi r2,r2,16 ;stride + andi. r0,r11,0x0010 ;check bit 27 + beq L_co_switch$restore_skip_vr27 + lvx v27,r1,r2 ;restore VR27 +L_co_switch$restore_skip_vr27: + addi r2,r2,16 ;stride + andi. r0,r11,0x0008 ;check bit 28 + beq L_co_switch$restore_skip_vr28 + lvx v28,r1,r2 ;restore VR28 +L_co_switch$restore_skip_vr28: + addi r2,r2,16 ;stride + andi. r0,r11,0x0004 ;check bit 29 + beq L_co_switch$restore_skip_vr29 + lvx v29,r1,r2 ;restore VR29 +L_co_switch$restore_skip_vr29: + addi r2,r2,16 ;stride + andi. r0,r11,0x0002 ;check bit 30 + beq L_co_switch$restore_skip_vr30 + lvx v30,r1,r2 ;restore VR30 +L_co_switch$restore_skip_vr30: + addi r2,r2,16 ;stride + andi. r0,r11,0x0001 ;check bit 31 + beq L_co_switch$restore_skip_vr31 + lvx v31,r1,r2 ;restore VR31 +L_co_switch$restore_skip_vr31: +L_co_switch$restore_no_vmx: + + lwz r1,0(r1) ;deallocate stack frame + lwz r6,8(r1) ;return address in GPR6 + lwz r7,4(r1) ;condition codes in GPR7 + addi r0,0,0 ;make thread main crash if it returns + lmw r13,-76(r1) ;restore preserved GPRs + lfd f14,-224(r1) ;restore preserved FPRs + lfd f15,-216(r1) + lfd f16,-208(r1) + lfd f17,-200(r1) + lfd f18,-192(r1) + lfd f19,-184(r1) + lfd f20,-176(r1) + lfd f21,-168(r1) + lfd f22,-160(r1) + lfd f23,-152(r1) + lfd f24,-144(r1) + lfd f25,-136(r1) + lfd f26,-128(r1) + lfd f27,-120(r1) + lfd f28,-112(r1) + lfd f29,-104(r1) + lfd f30,-96(r1) + lfd f31,-88(r1) + mtlr r0 + mtctr r6 ;restore return address + mtcrf 32,r7 ;restore preserved condition codes + mtcrf 16,r7 + mtcrf 8,r7 + bctr ;return + + + +;Import external functions + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_malloc$stub: + .indirect_symbol _malloc + mflr r0 + bcl 20,31,L_malloc$spb +L_malloc$spb: + mflr r11 + addis r11,r11,ha16(L_malloc$lazy_ptr-L_malloc$spb) + mtlr r0 + lwzu r12,lo16(L_malloc$lazy_ptr-L_malloc$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_malloc$lazy_ptr: + .indirect_symbol _malloc + .long dyld_stub_binding_helper + + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_free$stub: + .indirect_symbol _free + mflr r0 + bcl 20,31,L_free$spb +L_free$spb: + mflr r11 + addis r11,r11,ha16(L_free$lazy_ptr-L_free$spb) + mtlr r0 + lwzu r12,lo16(L_free$lazy_ptr-L_free$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_free$lazy_ptr: + .indirect_symbol _free + .long dyld_stub_binding_helper + + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_sysctlbyname$stub: + .indirect_symbol _sysctlbyname + mflr r0 + bcl 20,31,L_sysctlbyname$spb +L_sysctlbyname$spb: + mflr r11 + addis r11,r11,ha16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb) + mtlr r0 + lwzu r12,lo16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_sysctlbyname$lazy_ptr: + .indirect_symbol _sysctlbyname + .long dyld_stub_binding_helper + + +;This needs to be here! + + .subsections_via_symbols + diff --git a/mednafen/snes/src/lib/libco/ppc64.s b/mednafen/snes/src/lib/libco/ppc64.s new file mode 100755 index 0000000..2fb048d --- /dev/null +++ b/mednafen/snes/src/lib/libco/ppc64.s @@ -0,0 +1,513 @@ +;***** +;libco.ppc64 (2007-12-05) +;author: Vas Crabb +;license: public domain +; +;cross-platform 64-bit PowerPC implementation of libco +;special thanks to byuu for writing the original version +; +;[ABI compatibility] +;- gcc; mac os x; ppc64 +; +;[nonvolatile registers] +;- GPR1, GPR13 - GPR31 +;- FPR14 - FPR31 +;- V20 - V31 +;- VRSAVE, CR2 - CR4 +; +;[volatile registers] +;- GPR0, GPR2 - GPR12 +;- FPR0 - FPR13 +;- V0 - V19 +;- LR, CTR, XER, CR0, CR1, CR5 - CR7 +;***** + + +;Declare some target-specific stuff + + .section __TEXT,__text,regular,pure_instructions + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .machine ppc64 + + +;Constants + + .cstring + .align 3 + +_sysctl_altivec: + .ascii "hw.optional.altivec\0" + + +;Declare space for variables + +.lcomm _co_environ,4,2 ;bit 0 = initialised, bit 1 = have Altivec/VMX +.lcomm _co_primary_buffer,1024,3 ;buffer (will be zeroed by loader) + + .data + .align 3 + +_co_active_context: + .quad _co_primary_buffer + + + .text + .align 2 + + +;Declare exported names + +.globl _co_active +.globl _co_create +.globl _co_delete +.globl _co_switch + + +;***** +;extern "C" cothread_t co_active(); +;return = GPR3 +;***** + +_co_active: + mflr r0 ;GPR0 = return address + bcl 20,31,L_co_active$spb +L_co_active$spb: + mflr r2 ;GPR2 set for position-independance + addis r3,r2,ha16(_co_active_context-L_co_active$spb) ;get value in GPR3 + ld r3,lo16(_co_active_context-L_co_active$spb)(r3) + mtlr r0 ;LR = return address + blr ;return + + +;***** +;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)()); +;GPR3 = heapsize +;GPR4 = coentry +;return = GPR3 +;***** + +_co_create: + mflr r0 ;GPR0 = return address + std r30,-16(r1) ;save GPR30 and GPR31 + std r31,-8(r1) + std r0,16(r1) ;save return address + stdu r1,-(2*8+16+48)(r1) ;allocate 16 bytes for locals/parameters + +;create heap space (stack + register storage) + addi r31,r3,1024-48 ;subtract space for linkage + mr r30,r4 ;GPR30 = coentry + addi r3,r3,1024 ;allocate extra memory for contextual info + bl L_malloc$stub ;GPR3 = malloc(heapsize + 1024) + add r4,r3,r31 ;GPR4 points to top-of-stack + rldicr r5,r4,0,59 ;force 16-byte alignment + +;store thread entry point + registers, so that first call to co_switch will execute coentry + std r30,16(r5) ;store entry point + addi r6,0,2+19+18+12*2+1 ;clear for CR, old GPR1, 19 GPRs, 18 FPRs, 12 VRs, VRSAVE + addi r0,0,0 + addi r7,0,8 ;start at 8(GPR5) + mtctr r6 +L_co_create$clear_loop: + stdx r0,r5,r7 ;clear a double + addi r7,r7,-8 ;increment pointer + bdnz L_co_create$clear_loop ;loop + stdu r5,-544(r5) ;store top of stack + +;initialize context memory heap and return + addis r9,0,0x8000 ;GPR13 not set (system TLS) + std r5,0(r3) ;*cothread_t = stack heap pointer (GPR1) + stw r9,8(r3) ;this is a flag word + ld r1,0(r1) ;deallocate stack frame + ld r8,16(r1) ;fetch return address + ld r30,-16(r1) ;restore GPR30 and GPR31 + ld r31,-8(r1) + mtlr r8 ;return address in LR + blr ;return + + +;***** +;extern "C" void co_delete(cothread_t cothread); +;GPR3 = cothread +;***** + +_co_delete: + b L_free$stub ;free(GPR3) + + +;***** +;extern "C" void co_switch(cothread_t cothread); +;GPR3 = cothread +;***** +; +;Frame looks like: +; +;Old New Value +; 16(r1) 560(r1) Saved LR +; 8(r1) 552(r1) Saved CR +; 0(r1) 544(r1) Old GPR1 +; -8(r1) 536(r1) Saved GPR31 +; -16(r1) 528(r1) Saved GPR30 +;... ... ... +;-144(r1) 400(r1) Saved GPR14 +;-152(r1) 392(r1) Saved GPR13 +;-160(r1) 384(r1) Saved FPR31 +;-168(r1) 376(r1) Saved FPR30 +;... ... ... +;-288(r1) 256(r1) Saved FPR15 +;-296(r1) 248(r1) Saved FPR14 +;-304(r1) 240(r1) Saved VRSAVE +;-312(r1) 232(r1) +++ value +;-320(r1) 224(r1) Saved VR31 len +;-328(r1) 216(r1) +++ +;-336(r1) 208(r1) Saved VR30 +;... ... ... +;-456(r1) 88(r1) +++ +;-464(r1) 80(r1) Saved VR22 Param 5 (GPR7) +;-472(r1) 72(r1) +++ Param 4 (GPR6) +;-480(r1) 64(r1) Saved VR21 Param 3 (GPR5) +;-488(r1) 56(r1) +++ Param 2 (GPR4) +;-496(r1) 48(r1) Saved VR20 Param 1 (GPR3) +;-504(r1) 40(r1) - Reserved +;-512(r1) 32(r1) - Reserved +;-520(r1) 24(r1) - Reserved +;-528(r1) 16(r1) - New LR +;-536(r1) 8(r1) - New CR +;-544(r1) 0(r1) Saved GPR1 + + +_co_switch: + std r13,-152(r1) ;save preserved GPRs + std r14,-144(r1) + std r15,-136(r1) + std r16,-128(r1) + std r17,-120(r1) + std r18,-112(r1) + std r19,-104(r1) + std r20,-96(r1) + std r21,-88(r1) + std r22,-80(r1) + std r23,-72(r1) + std r24,-64(r1) + std r25,-56(r1) + std r26,-48(r1) + std r27,-40(r1) + std r28,-32(r1) + std r29,-24(r1) + std r30,-16(r1) + std r31,-8(r1) + mflr r0 ;save return address + std r0,16(r1) + mfcr r2 ;save condition codes + stw r2,8(r1) + stdu r1,-544(r1) ;create stack frame (save 19 GPRs, 18 FRPs, 12 VRs, VRSAVE) + stfd f14,248(r1) ;save preserved FPRs + stfd f15,256(r1) + stfd f16,264(r1) + stfd f17,272(r1) + stfd f18,280(r1) + stfd f19,288(r1) + stfd f20,296(r1) + stfd f21,304(r1) + stfd f22,312(r1) + stfd f23,320(r1) + stfd f24,328(r1) + stfd f25,336(r1) + stfd f26,344(r1) + stfd f27,352(r1) + stfd f28,360(r1) + stfd f29,368(r1) + stfd f30,376(r1) + stfd f31,384(r1) + + mr r30,r3 ;save new context pointer + bcl 20,31,L_co_switch$spb ;get address of co_active_context +L_co_switch$spb: + mflr r31 + + addis r29,r31,ha16(_co_environ-L_co_switch$spb) ;get environment flags + lwz r8,lo16(_co_environ-L_co_switch$spb)(r29) + andis. r9,r8,0x8000 ;is it initialised? + bne+ L_co_switch$initialised + + addi r0,0,4 ;len = sizeof(int) + std r0,224(r1) + addis r3,r31,ha16(_sysctl_altivec-L_co_switch$spb) ;GPR3 = "hw.optional.altivec" + addi r3,r3,lo16(_sysctl_altivec-L_co_switch$spb) + addi r4,r1,232 ;GPR4 = &value + addi r5,r1,224 ;GPR5 = &len + addi r6,0,0 ;newp = 0 + addi r7,0,0 ;newlen = 0 + bl L_sysctlbyname$stub ;call sysctlbyname + lwz r2,232(r1) ;fetch result + addis r8,0,0x8000 ;set initialised bit + cmpdi cr5,r3,0 ;assume error means not present + cmpwi cr6,r2,0 ;test result + blt- cr5,L_co_switch$store_environ + beq cr6,L_co_switch$store_environ + oris r8,r8,0x4000 ;set the flag to say we have it! +L_co_switch$store_environ: + stw r8,lo16(_co_environ-L_co_switch$spb)(r29) ;store environment flags +L_co_switch$initialised: + + andis. r10,r8,0x4000 ;do we have Altivec/VMX? + beq L_co_switch$save_no_vmx + mfspr r11,256 ;save VRSAVE + andi. r0,r11,0x0FFF ;short-circuit if it's zero + stw r11,240(r1) + beq L_co_switch$save_no_vmx + andi. r0,r11,0x0800 ;check bit 20 + addi r2,0,48 ;starting index + beq L_co_switch$save_skip_vr20 + stvx v20,r1,r2 ;save VR20 +L_co_switch$save_skip_vr20: + addi r2,r2,16 ;stride + andi. r0,r11,0x0400 ;check bit 21 + beq L_co_switch$save_skip_vr21 + stvx v21,r1,r2 ;save VR21 +L_co_switch$save_skip_vr21: + addi r2,r2,16 ;stride + andi. r0,r11,0x0200 ;check bit 22 + beq L_co_switch$save_skip_vr22 + stvx v22,r1,r2 ;save VR22 +L_co_switch$save_skip_vr22: + addi r2,r2,16 ;stride + andi. r0,r11,0x0100 ;check bit 23 + beq L_co_switch$save_skip_vr23 + stvx v23,r1,r2 ;save VR23 +L_co_switch$save_skip_vr23: + addi r2,r2,16 ;stride + andi. r0,r11,0x0080 ;check bit 24 + beq L_co_switch$save_skip_vr24 + stvx v24,r1,r2 ;save VR24 +L_co_switch$save_skip_vr24: + addi r2,r2,16 ;stride + andi. r0,r11,0x0040 ;check bit 25 + beq L_co_switch$save_skip_vr25 + stvx v25,r1,r2 ;save VR25 +L_co_switch$save_skip_vr25: + addi r2,r2,16 ;stride + andi. r0,r11,0x0020 ;check bit 26 + beq L_co_switch$save_skip_vr26 + stvx v26,r1,r2 ;save VR26 +L_co_switch$save_skip_vr26: + addi r2,r2,16 ;stride + andi. r0,r11,0x0010 ;check bit 27 + beq L_co_switch$save_skip_vr27 + stvx v27,r1,r2 ;save VR27 +L_co_switch$save_skip_vr27: + addi r2,r2,16 ;stride + andi. r0,r11,0x0008 ;check bit 28 + beq L_co_switch$save_skip_vr28 + stvx v28,r1,r2 ;save VR28 +L_co_switch$save_skip_vr28: + addi r2,r2,16 ;stride + andi. r0,r11,0x0004 ;check bit 29 + beq L_co_switch$save_skip_vr29 + stvx v29,r1,r2 ;save VR29 +L_co_switch$save_skip_vr29: + addi r2,r2,16 ;stride + andi. r0,r11,0x0002 ;check bit 30 + beq L_co_switch$save_skip_vr30 + stvx v30,r1,r2 ;save VR30 +L_co_switch$save_skip_vr30: + addi r2,r2,16 ;stride + andi. r0,r11,0x0001 ;check bit 31 + beq L_co_switch$save_skip_vr31 + stvx v31,r1,r2 ;save VR31 +L_co_switch$save_skip_vr31: +L_co_switch$save_no_vmx: + + addis r4,r31,ha16(_co_active_context-L_co_switch$spb) ;save current context + ld r5,lo16(_co_active_context-L_co_switch$spb)(r4) + std r30,lo16(_co_active_context-L_co_switch$spb)(r4);set new context + std r1,0(r5) ;save current stack pointer + ld r1,0(r30) ;get new stack pointer + lwz r12,8(r30) ;have we already set GPR13 (system TLS)? + andis. r0,r12,0x8000 + beq+ L_co_switch$gpr13_set + std r13,392(r1) + xoris r12,r12,0x8000 + stw r12,8(r30) +L_co_switch$gpr13_set: + + andis. r10,r8,0x4000 ;do we have Altivec/VMX? + beq L_co_switch$restore_no_vmx + lwz r11,240(r1) ;restore VRSAVE + andi. r0,r11,0x0FFF ;short-circuit if it's zero + mtspr 256,r11 + beq L_co_switch$restore_no_vmx + andi. r0,r11,0x0800 ;check bit 20 + addi r2,0,48 ;starting index + beq L_co_switch$restore_skip_vr20 + lvx v20,r1,r2 ;restore VR20 +L_co_switch$restore_skip_vr20: + addi r2,r2,16 ;stride + andi. r0,r11,0x0400 ;check bit 21 + beq L_co_switch$restore_skip_vr21 + lvx v21,r1,r2 ;restore VR21 +L_co_switch$restore_skip_vr21: + addi r2,r2,16 ;stride + andi. r0,r11,0x0200 ;check bit 22 + beq L_co_switch$restore_skip_vr22 + lvx v22,r1,r2 ;restore VR22 +L_co_switch$restore_skip_vr22: + addi r2,r2,16 ;stride + andi. r0,r11,0x0100 ;check bit 23 + beq L_co_switch$restore_skip_vr23 + lvx v23,r1,r2 ;restore VR23 +L_co_switch$restore_skip_vr23: + addi r2,r2,16 ;stride + andi. r0,r11,0x0080 ;check bit 24 + beq L_co_switch$restore_skip_vr24 + lvx v24,r1,r2 ;restore VR24 +L_co_switch$restore_skip_vr24: + addi r2,r2,16 ;stride + andi. r0,r11,0x0040 ;check bit 25 + beq L_co_switch$restore_skip_vr25 + lvx v25,r1,r2 ;restore VR25 +L_co_switch$restore_skip_vr25: + addi r2,r2,16 ;stride + andi. r0,r11,0x0020 ;check bit 26 + beq L_co_switch$restore_skip_vr26 + lvx v26,r1,r2 ;restore VR26 +L_co_switch$restore_skip_vr26: + addi r2,r2,16 ;stride + andi. r0,r11,0x0010 ;check bit 27 + beq L_co_switch$restore_skip_vr27 + lvx v27,r1,r2 ;restore VR27 +L_co_switch$restore_skip_vr27: + addi r2,r2,16 ;stride + andi. r0,r11,0x0008 ;check bit 28 + beq L_co_switch$restore_skip_vr28 + lvx v28,r1,r2 ;restore VR28 +L_co_switch$restore_skip_vr28: + addi r2,r2,16 ;stride + andi. r0,r11,0x0004 ;check bit 29 + beq L_co_switch$restore_skip_vr29 + lvx v29,r1,r2 ;restore VR29 +L_co_switch$restore_skip_vr29: + addi r2,r2,16 ;stride + andi. r0,r11,0x0002 ;check bit 30 + beq L_co_switch$restore_skip_vr30 + lvx v30,r1,r2 ;restore VR30 +L_co_switch$restore_skip_vr30: + addi r2,r2,16 ;stride + andi. r0,r11,0x0001 ;check bit 31 + beq L_co_switch$restore_skip_vr31 + lvx v31,r1,r2 ;restore VR31 +L_co_switch$restore_skip_vr31: +L_co_switch$restore_no_vmx: + + lfd f14,248(r1) ;restore preserved FPRs + lfd f15,256(r1) + lfd f16,264(r1) + lfd f17,272(r1) + lfd f18,280(r1) + lfd f19,288(r1) + lfd f20,296(r1) + lfd f21,304(r1) + lfd f22,312(r1) + lfd f23,320(r1) + lfd f24,328(r1) + lfd f25,336(r1) + lfd f26,344(r1) + lfd f27,352(r1) + lfd f28,360(r1) + lfd f29,368(r1) + lfd f30,376(r1) + lfd f31,384(r1) + addi r0,0,0 ;make thread main crash if it returns + ld r1,0(r1) ;deallocate stack frame + ld r6,16(r1) ;return address in GPR6 + lwz r7,8(r1) ;condition codes in GPR7 + ld r13,-152(r1) ;restore preserved GPRs + ld r14,-144(r1) + ld r15,-136(r1) + ld r16,-128(r1) + ld r17,-120(r1) + ld r18,-112(r1) + ld r19,-104(r1) + ld r20,-96(r1) + ld r21,-88(r1) + ld r22,-80(r1) + ld r23,-72(r1) + ld r24,-64(r1) + ld r25,-56(r1) + ld r26,-48(r1) + ld r27,-40(r1) + ld r28,-32(r1) + ld r29,-24(r1) + ld r30,-16(r1) + ld r31,-8(r1) + mtlr r0 + mtctr r6 ;restore return address + mtcrf 32,r7 ;restore preserved condition codes + mtcrf 16,r7 + mtcrf 8,r7 + bctr ;return + + + +;Import external functions + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_malloc$stub: + .indirect_symbol _malloc + mflr r0 + bcl 20,31,L_malloc$spb +L_malloc$spb: + mflr r11 + addis r11,r11,ha16(L_malloc$lazy_ptr-L_malloc$spb) + mtlr r0 + ldu r12,lo16(L_malloc$lazy_ptr-L_malloc$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_malloc$lazy_ptr: + .indirect_symbol _malloc + .quad dyld_stub_binding_helper + + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_free$stub: + .indirect_symbol _free + mflr r0 + bcl 20,31,L_free$spb +L_free$spb: + mflr r11 + addis r11,r11,ha16(L_free$lazy_ptr-L_free$spb) + mtlr r0 + ldu r12,lo16(L_free$lazy_ptr-L_free$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_free$lazy_ptr: + .indirect_symbol _free + .quad dyld_stub_binding_helper + + + .section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32 + .align 5 +L_sysctlbyname$stub: + .indirect_symbol _sysctlbyname + mflr r0 + bcl 20,31,L_sysctlbyname$spb +L_sysctlbyname$spb: + mflr r11 + addis r11,r11,ha16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb) + mtlr r0 + ldu r12,lo16(L_sysctlbyname$lazy_ptr-L_sysctlbyname$spb)(r11) + mtctr r12 + bctr + .lazy_symbol_pointer +L_sysctlbyname$lazy_ptr: + .indirect_symbol _sysctlbyname + .quad dyld_stub_binding_helper + + +;This needs to be here! + + .subsections_via_symbols + diff --git a/mednafen/snes/src/lib/libco/sjlj.c b/mednafen/snes/src/lib/libco/sjlj.c new file mode 100755 index 0000000..8b72b61 --- /dev/null +++ b/mednafen/snes/src/lib/libco/sjlj.c @@ -0,0 +1,102 @@ +/* + libco.sjlj (2008-01-28) + author: Nach + license: public domain +*/ + +/* + * Note this was designed for UNIX systems. Based on ideas expressed in a paper + * by Ralf Engelschall. + * For SJLJ on other systems, one would want to rewrite springboard() and + * co_create() and hack the jmb_buf stack pointer. + */ + +#define LIBCO_C +#include "libco.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + sigjmp_buf context; + void (*coentry)(void); + void *stack; +} cothread_struct; + +static thread_local cothread_struct co_primary; +static thread_local cothread_struct *creating, *co_running = 0; + +static void springboard(int ignored) { + if(sigsetjmp(creating->context, 0)) { + co_running->coentry(); + } +} + +cothread_t co_active() { + if(!co_running) co_running = &co_primary; + return (cothread_t)co_running; +} + +cothread_t co_create(unsigned int size, void (*coentry)(void)) { + if(!co_running) co_running = &co_primary; + + cothread_struct *thread = (cothread_struct*)malloc(sizeof(cothread_struct)); + if(thread) { + struct sigaction handler; + struct sigaction old_handler; + + stack_t stack; + stack_t old_stack; + + thread->coentry = thread->stack = 0; + + stack.ss_flags = 0; + stack.ss_size = size; + thread->stack = stack.ss_sp = malloc(size); + if(stack.ss_sp && !sigaltstack(&stack, &old_stack)) { + handler.sa_handler = springboard; + handler.sa_flags = SA_ONSTACK; + sigemptyset(&handler.sa_mask); + creating = thread; + + if(!sigaction(SIGUSR1, &handler, &old_handler)) { + if(!raise(SIGUSR1)) { + thread->coentry = coentry; + } + sigaltstack(&old_stack, 0); + sigaction(SIGUSR1, &old_handler, 0); + } + } + + if(thread->coentry != coentry) { + co_delete(thread); + thread = 0; + } + } + + return (cothread_t)thread; +} + +void co_delete(cothread_t cothread) { + if(cothread) { + if(((cothread_struct*)cothread)->stack) { + free(((cothread_struct*)cothread)->stack); + } + free(cothread); + } +} + +void co_switch(cothread_t cothread) { + if(!sigsetjmp(co_running->context, 0)) { + co_running = (cothread_struct*)cothread; + siglongjmp(co_running->context, 1); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/mednafen/snes/src/lib/libco/ucontext.c b/mednafen/snes/src/lib/libco/ucontext.c new file mode 100755 index 0000000..17472f6 --- /dev/null +++ b/mednafen/snes/src/lib/libco/ucontext.c @@ -0,0 +1,67 @@ +/* + libco.ucontext (2008-01-28) + author: Nach + license: public domain +*/ + +/* + * WARNING: the overhead of POSIX ucontext is very high, + * assembly versions of libco or libco_sjlj should be much faster + * + * This library only exists for two reasons: + * 1 - as an initial test for the viability of a ucontext implementation + * 2 - to demonstrate the power and speed of libco over existing implementations, + * such as pth (which defaults to wrapping ucontext on unix targets) + * + * Use this library only as a *last resort* + */ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local ucontext_t co_primary; +static thread_local ucontext_t *co_running = 0; + +cothread_t co_active() { + if(!co_running) co_running = &co_primary; + return (cothread_t)co_running; +} + +cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { + if(!co_running) co_running = &co_primary; + ucontext_t *thread = (ucontext_t*)malloc(sizeof(ucontext_t)); + if(thread) { + if((!getcontext(thread) && !(thread->uc_stack.ss_sp = 0)) && (thread->uc_stack.ss_sp = malloc(heapsize))) { + thread->uc_link = co_running; + thread->uc_stack.ss_size = heapsize; + makecontext(thread, coentry, 0); + } else { + co_delete((cothread_t)thread); + thread = 0; + } + } + return (cothread_t)thread; +} + +void co_delete(cothread_t cothread) { + if(cothread) { + if(((ucontext_t*)cothread)->uc_stack.ss_sp) { free(((ucontext_t*)cothread)->uc_stack.ss_sp); } + free(cothread); + } +} + +void co_switch(cothread_t cothread) { + ucontext_t *old_thread = co_running; + co_running = (ucontext_t*)cothread; + swapcontext(old_thread, co_running); +} + +#ifdef __cplusplus +} +#endif diff --git a/mednafen/snes/src/lib/libco/x86.c b/mednafen/snes/src/lib/libco/x86.c new file mode 100755 index 0000000..d8f820b --- /dev/null +++ b/mednafen/snes/src/lib/libco/x86.c @@ -0,0 +1,93 @@ +/* + libco.x86 (2009-10-12) + author: byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) + #define fastcall __fastcall +#elif defined(__GNUC__) + #define fastcall __attribute__((fastcall)) +#else + #error "libco: please define fastcall macro" +#endif + +static thread_local long co_active_buffer[64]; +static thread_local cothread_t co_active_handle = 0; +static void (fastcall *co_swap)(cothread_t, cothread_t) = 0; + +//ABI: fastcall +static unsigned char co_swap_function[] = { + 0x89, 0x22, 0x8B, 0x21, 0x58, 0x89, 0x6A, 0x04, 0x89, 0x72, 0x08, 0x89, 0x7A, 0x0C, 0x89, 0x5A, + 0x10, 0x8B, 0x69, 0x04, 0x8B, 0x71, 0x08, 0x8B, 0x79, 0x0C, 0x8B, 0x59, 0x10, 0xFF, 0xE0, +}; + +#ifdef _WIN32 + #include + + void co_init() { + DWORD old_privileges; + VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges); + } +#else + #include + #include + + void co_init() { + unsigned long addr = (unsigned long)co_swap_function; + unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE)); + unsigned long size = (addr - base) + sizeof co_swap_function; + mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC); + } +#endif + +static void crash() { + assert(0); /* called only if cothread_t entrypoint returns */ +} + +cothread_t co_active() { + if(!co_active_handle) co_active_handle = &co_active_buffer; + return co_active_handle; +} + +cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + if(!co_swap) { + co_init(); + co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function; + } + if(!co_active_handle) co_active_handle = &co_active_buffer; + size += 256; /* allocate additional space for storage */ + size &= ~15; /* align stack to 16-byte boundary */ + + if(handle = (cothread_t)malloc(size)) { + long *p = (long*)((char*)handle + size); /* seek to top of stack */ + *--p = (long)crash; /* crash if entrypoint returns */ + *--p = (long)entrypoint; /* start of function */ + *(long*)handle = (long)p; /* stack pointer */ + } + + return handle; +} + +void co_delete(cothread_t handle) { + free(handle); +} + +void co_switch(cothread_t handle) { + register cothread_t co_previous_handle = co_active_handle; + co_swap(co_active_handle = handle, co_previous_handle); +} + +#ifdef __cplusplus +} +#endif diff --git a/mednafen/snes/src/lib/nall/Makefile b/mednafen/snes/src/lib/nall/Makefile new file mode 100755 index 0000000..82dc00a --- /dev/null +++ b/mednafen/snes/src/lib/nall/Makefile @@ -0,0 +1,109 @@ +# Makefile +# author: byuu +# license: public domain + +[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z +[0-9] = 0 1 2 3 4 5 6 7 8 9 +[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ? +[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup]) +[space] := +[space] += + +##### +# platform detection +##### + +ifeq ($(platform),) + uname := $(shell uname -a) + ifeq ($(uname),) + platform := win + delete = del $(subst /,\,$1) + else ifneq ($(findstring Darwin,$(uname)),) + platform := osx + delete = rm -f $1 + else + platform := x + delete = rm -f $1 + endif +endif + +ifeq ($(compiler),) + compiler := gcc +endif + +ifeq ($(prefix),) + prefix := /usr/local +endif + +##### +# function rwildcard(directory, pattern) +##### +rwildcard = \ + $(strip \ + $(filter $(if $2,$2,%), \ + $(foreach f, \ + $(wildcard $1*), \ + $(eval t = $(call rwildcard,$f/)) \ + $(if $t,$t,$f) \ + ) \ + ) \ + ) + +##### +# function strtr(source, from, to) +##### +strtr = \ + $(eval __temp := $1) \ + $(strip \ + $(foreach c, \ + $(join $(addsuffix :,$2),$3), \ + $(eval __temp := \ + $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) + +##### +# function strupper(source) +##### +strupper = $(call strtr,$1,$([a-z]),$([A-Z])) + +##### +# function strlower(source) +##### +strlower = $(call strtr,$1,$([A-Z]),$([a-z])) + +##### +# function strlen(source) +##### +strlen = \ + $(eval __temp := $(subst $([space]),_,$1)) \ + $(words \ + $(strip \ + $(foreach c, \ + $([all]), \ + $(eval __temp := \ + $(subst $c,$c ,$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) \ + ) + +##### +# function streq(source) +##### +streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1) + +##### +# function strne(source) +##### +strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,) + +##### +# function ifhas(needle, haystack, true, false) +##### +ifhas = $(if $(findstring $1,$2),$3,$4) + diff --git a/mednafen/snes/src/lib/nall/Makefile-qt b/mednafen/snes/src/lib/nall/Makefile-qt new file mode 100755 index 0000000..7cf21c9 --- /dev/null +++ b/mednafen/snes/src/lib/nall/Makefile-qt @@ -0,0 +1,55 @@ +# requires nall/Makefile + +# exports the following symbols: +# $(moc) -- meta-object compiler +# $(rcc) -- resource compiler +# $(qtinc) -- includes for compiling +# $(qtlib) -- libraries for linking + +ifeq ($(moc),) +moc := moc +endif + +ifeq ($(rcc),) +rcc := rcc +endif + +ifeq ($(platform),x) + qtinc := `pkg-config --cflags QtCore QtGui` + qtlib := `pkg-config --libs QtCore QtGui` +else ifeq ($(platform),osx) + qtinc := -I/Library/Frameworks/QtCore.framework/Versions/4/Headers + qtinc += -I/Library/Frameworks/QtGui.framework/Versions/4/Headers + qtinc += -I/Library/Frameworks/QtOpenGL.framework/Versions/4/Headers + + qtlib := -L/Library/Frameworks + qtlib += -framework QtCore + qtlib += -framework QtGui + qtlib += -framework QtOpenGL + qtlib += -framework Carbon + qtlib += -framework Cocoa + qtlib += -framework OpenGL + qtlib += -framework AppKit + qtlib += -framework ApplicationServices +else ifeq ($(platform),win) + ifeq ($(qtpath),) + # find Qt install directory from PATH environment variable + qtpath := $(foreach path,$(subst ;, ,$(PATH)),$(if $(wildcard $(path)/$(moc).exe),$(path))) + qtpath := $(strip $(qtpath)) + qtpath := $(subst \,/,$(qtpath)) + qtpath := $(patsubst %/bin,%,$(qtpath)) + endif + + qtinc := -I$(qtpath)/include + qtinc += -I$(qtpath)/include/QtCore + qtinc += -I$(qtpath)/include/QtGui + + qtlib := -L$(qtpath)/lib + qtlib += -L$(qtpath)/plugins/imageformats + + qtlib += -lmingw32 -lqtmain -lQtGui4 -lcomdlg32 -loleaut32 -limm32 -lwinmm + qtlib += -lwinspool -lmsimg32 -lQtCore4 -lole32 -ladvapi32 -lws2_32 -luuid -lgdi32 + + # optional image-file support: + # qtlib += -lqjpeg -lqmng +endif diff --git a/mednafen/snes/src/lib/nall/algorithm.hpp b/mednafen/snes/src/lib/nall/algorithm.hpp new file mode 100755 index 0000000..98b3952 --- /dev/null +++ b/mednafen/snes/src/lib/nall/algorithm.hpp @@ -0,0 +1,23 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + template T min(const T& t, const U& u) { + return t < u ? t : u; + } + + template T max(const T& t, const U& u) { + return t > u ? t : u; + } + + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } +} + +#endif diff --git a/mednafen/snes/src/lib/nall/any.hpp b/mednafen/snes/src/lib/nall/any.hpp new file mode 100755 index 0000000..9689af1 --- /dev/null +++ b/mednafen/snes/src/lib/nall/any.hpp @@ -0,0 +1,74 @@ +#ifndef NALL_ANY_HPP +#define NALL_ANY_HPP + +#include +#include +#include + +namespace nall { + class any { + public: + bool empty() const { return container; } + const std::type_info& type() const { return container ? container->type() : typeid(void); } + + template any& operator=(const T& value_) { + typedef typename static_if< + is_array::value, + typename remove_extent::type>::type*, + T + >::type auto_t; + + if(type() == typeid(auto_t)) { + static_cast*>(container)->value = (auto_t)value_; + } else { + if(container) delete container; + container = new holder((auto_t)value_); + } + + return *this; + } + + any() : container(0) {} + template any(const T& value_) : container(0) { operator=(value_); } + + private: + struct placeholder { + virtual const std::type_info& type() const = 0; + } *container; + + template struct holder : placeholder { + T value; + const std::type_info& type() const { return typeid(T); } + holder(const T& value_) : value(value_) {} + }; + + template friend T any_cast(any&); + template friend T any_cast(const any&); + template friend T* any_cast(any*); + template friend const T* any_cast(const any*); + }; + + template T any_cast(any &value) { + typedef typename remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T any_cast(const any &value) { + typedef const typename remove_reference::type nonref; + if(value.type() != typeid(nonref)) throw; + return static_cast*>(value.container)->value; + } + + template T* any_cast(any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } + + template const T* any_cast(const any *value) { + if(!value || value->type() != typeid(T)) return 0; + return &static_cast*>(value->container)->value; + } +} + +#endif diff --git a/mednafen/snes/src/lib/nall/array.hpp b/mednafen/snes/src/lib/nall/array.hpp new file mode 100755 index 0000000..5fa5405 --- /dev/null +++ b/mednafen/snes/src/lib/nall/array.hpp @@ -0,0 +1,95 @@ +#ifndef NALL_ARRAY_HPP +#define NALL_ARRAY_HPP + +#include +#include +#include +#include + +namespace nall { + //dynamic vector array + //neither constructor nor destructor is ever invoked; + //thus, this should only be used for POD objects. + template class array { + protected: + T *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } + + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } + + void add(const T data) { + operator[](buffersize) = data; + } + + signed find(const T data) { + for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return i; + return -1; //not found + } + + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } + + array() { + pool = 0; + poolsize = 0; + buffersize = 0; + } + + ~array() { reset(); } + + array(const array &source) : pool(0) { + operator=(source); + } + + array& operator=(const array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size, + memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects + return *this; + } + + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/base64.hpp b/mednafen/snes/src/lib/nall/base64.hpp new file mode 100755 index 0000000..e41c87b --- /dev/null +++ b/mednafen/snes/src/lib/nall/base64.hpp @@ -0,0 +1,90 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include + +namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new char[inlength * 8 / 6 + 6](); + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new uint8_t[inlength](); + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/bit.hpp b/mednafen/snes/src/lib/nall/bit.hpp new file mode 100755 index 0000000..169fc14 --- /dev/null +++ b/mednafen/snes/src/lib/nall/bit.hpp @@ -0,0 +1,51 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +namespace nall { + template inline unsigned uclamp(const unsigned x) { + enum { y = (1U << bits) - 1 }; + return y + ((x - y) & -(x < y)); //min(x, y); + } + + template inline unsigned uclip(const unsigned x) { + enum { m = (1U << bits) - 1 }; + return (x & m); + } + + template inline signed sclamp(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; + } + + template inline signed sclip(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << bits) - 1 }; + return ((x & m) ^ b) - b; + } + + namespace bit { + //lowest(0b1110) == 0b0010 + template inline T lowest(const T x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + template inline T clear_lowest(const T x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + template inline T set_lowest(const T x) { + return x | (x + 1); + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } + } +} + +#endif diff --git a/mednafen/snes/src/lib/nall/config.hpp b/mednafen/snes/src/lib/nall/config.hpp new file mode 100755 index 0000000..10494e6 --- /dev/null +++ b/mednafen/snes/src/lib/nall/config.hpp @@ -0,0 +1,124 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { + namespace configuration_traits { + template struct is_boolean { enum { value = false }; }; + template<> struct is_boolean { enum { value = true }; }; + + template struct is_signed { enum { value = false }; }; + template<> struct is_signed { enum { value = true }; }; + + template struct is_unsigned { enum { value = false }; }; + template<> struct is_unsigned { enum { value = true }; }; + + template struct is_double { enum { value = false }; }; + template<> struct is_double { enum { value = true }; }; + + template struct is_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() const { + switch(type) { + case boolean_t: return string() << *(bool*)data; + case signed_t: return string() << *(signed*)data; + case unsigned_t: return string() << *(unsigned*)data; + case double_t: return string() << *(double*)data; + case string_t: return string() << "\"" << *(string*)data << "\""; + } + return "???"; + } + + void set(string s) { + switch(type) { + case boolean_t: *(bool*)data = (s == "true"); break; + case signed_t: *(signed*)data = strsigned(s); break; + case unsigned_t: *(unsigned*)data = strunsigned(s); break; + case double_t: *(double*)data = strdouble(s); break; + case string_t: trim(s, "\""); *(string*)data = s; break; + } + } + }; + vector list; + + template + void attach(T &data, const char *name, const char *desc = "") { + unsigned n = list.size(); + list[n].data = (uintptr_t)&data; + list[n].name = name; + list[n].desc = desc; + + if(configuration_traits::is_boolean::value) list[n].type = boolean_t; + else if(configuration_traits::is_signed::value) list[n].type = signed_t; + else if(configuration_traits::is_unsigned::value) list[n].type = unsigned_t; + else if(configuration_traits::is_double::value) list[n].type = double_t; + else if(configuration_traits::is_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + virtual bool load(const char *filename) { + string data; + if(data.readfile(filename) == true) { + data.replace("\r", ""); + lstring line; + line.split("\n", data); + + for(unsigned i = 0; i < line.size(); i++) { + int position = qstrpos(line[i], "#"); + if(position >= 0) line[i][position] = 0; + if(qstrpos(line[i], " = ") < 0) continue; + + lstring part; + part.qsplit(" = ", line[i]); + trim(part[0]); + trim(part[1]); + + for(unsigned n = 0; n < list.size(); n++) { + if(part[0] == list[n].name) { + list[n].set(part[1]); + break; + } + } + } + + return true; + } else { + return false; + } + } + + virtual bool save(const char *filename) const { + file fp; + if(fp.open(filename, file::mode_write)) { + for(unsigned i = 0; i < list.size(); i++) { + string output; + output << list[i].name << " = " << list[i].get(); + if(list[i].desc != "") output << " # " << list[i].desc; + output << "\r\n"; + fp.print(output); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/crc32.hpp b/mednafen/snes/src/lib/nall/crc32.hpp new file mode 100755 index 0000000..ad36fbf --- /dev/null +++ b/mednafen/snes/src/lib/nall/crc32.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; + } + + inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) { + uint32_t crc32 = ~0; + for(unsigned i = 0; i < length; i++) { + crc32 = crc32_adjust(crc32, data[i]); + } + return ~crc32; + } +} + +#endif diff --git a/mednafen/snes/src/lib/nall/detect.hpp b/mednafen/snes/src/lib/nall/detect.hpp new file mode 100755 index 0000000..b4991aa --- /dev/null +++ b/mednafen/snes/src/lib/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__) + #define ARCH_MSB +#endif + +#endif diff --git a/mednafen/snes/src/lib/nall/dictionary.hpp b/mednafen/snes/src/lib/nall/dictionary.hpp new file mode 100755 index 0000000..35128c2 --- /dev/null +++ b/mednafen/snes/src/lib/nall/dictionary.hpp @@ -0,0 +1,73 @@ +#ifndef NALL_DICTIONARY_HPP +#define NALL_DICTIONARY_HPP + +#include +#include +#include + +namespace nall { + class dictionary : noncopyable { + public: + string operator[](const char *input) { + for(unsigned i = 0; i < index_input.size(); i++) { + if(index_input[i] == input) return index_output[i]; + } + + //no match, use input; remove input identifier, if one exists + if(strbegin(input, "{{")) { + int pos = strpos(input, "}}"); + if(pos >= 0) { + string temp = substr(input, pos + 2); + return temp; + } + } + + return input; + } + + bool import(const char *filename) { + string data; + if(data.readfile(filename) == false) return false; + ltrim_once(data, "\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + data.replace("\r", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + //format: "Input" = "Output" + part.qsplit("=", line[i]); + if(part.size() != 2) continue; + + //remove whitespace + trim(part[0]); + trim(part[1]); + + //remove quotes + trim_once(part[0], "\""); + trim_once(part[1], "\""); + + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } + + return true; + } + + void reset() { + index_input.reset(); + index_output.reset(); + } + + ~dictionary() { + reset(); + } + + protected: + lstring index_input; + lstring index_output; + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/dl.hpp b/mednafen/snes/src/lib/nall/dl.hpp new file mode 100755 index 0000000..088b91e --- /dev/null +++ b/mednafen/snes/src/lib/nall/dl.hpp @@ -0,0 +1,116 @@ +#ifndef NALL_DL_HPP +#define NALL_DL_HPP + +//dynamic linking support + +#include +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_OSX) + #include +#elif defined(PLATFORM_WIN) + #include + #include +#endif + +namespace nall { + struct library : noncopyable { + bool opened() const { return handle; } + bool open(const char*); + void* sym(const char*); + void close(); + + library() : handle(0) {} + ~library() { close(); } + + private: + uintptr_t handle; + }; + + #if defined(PLATFORM_X) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 256]; + strcpy(t, "lib"); + strcat(t, name); + strcat(t, ".so"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + if(!handle) { + strcpy(t, "/usr/local/lib/lib"); + strcat(t, name); + strcat(t, ".so"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + } + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_OSX) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 256]; + strcpy(t, "lib"); + strcat(t, name); + strcat(t, ".dylib"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + if(!handle) { + strcpy(t, "/usr/local/lib/lib"); + strcat(t, name); + strcat(t, ".dylib"); + handle = (uintptr_t)dlopen(t, RTLD_LAZY); + } + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_WIN) + inline bool library::open(const char *name) { + if(handle) close(); + char *t = new char[strlen(name) + 8]; + strcpy(t, name); + strcat(t, ".dll"); + handle = (uintptr_t)LoadLibraryW(utf16_t(t)); + delete[] t; + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return (void*)GetProcAddress((HMODULE)handle, name); + } + + inline void library::close() { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; + } + #else + inline bool library::open(const char*) { return false; } + inline void* library::sym(const char*) { return 0; } + inline void library::close() {} + #endif +}; + +#endif diff --git a/mednafen/snes/src/lib/nall/endian.hpp b/mednafen/snes/src/lib/nall/endian.hpp new file mode 100755 index 0000000..40d1563 --- /dev/null +++ b/mednafen/snes/src/lib/nall/endian.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#if !defined(ARCH_MSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#endif + +#endif diff --git a/mednafen/snes/src/lib/nall/file.hpp b/mednafen/snes/src/lib/nall/file.hpp new file mode 100755 index 0000000..29be589 --- /dev/null +++ b/mednafen/snes/src/lib/nall/file.hpp @@ -0,0 +1,256 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include + +#if !defined(_WIN32) + #include +#else + #include +#endif + +#include +#include +#include + +namespace nall { + inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) { + #if !defined(_WIN32) + return fopen(utf8_filename, mode); + #else + return _wfopen(utf16_t(utf8_filename), utf16_t(mode)); + #endif + } + + class file : noncopyable { + public: + enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread }; + enum SeekMode { seek_absolute, seek_relative }; + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode_write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t *buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } + + void print(const char *string) { + if(!string) return; + while(*string) write(*string++); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, SeekMode mode = seek_absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(mode) { + case seek_absolute: req_offset = offset; break; + case seek_relative: req_offset += offset; break; + } + + if(req_offset < 0) req_offset = 0; //cannot seek before start of file + if(req_offset > file_size) { + if(file_mode == mode_read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + int offset() { + if(!fp) return -1; //file not open + return file_offset; + } + + int size() { + if(!fp) return -1; //file not open + return file_size; + } + + bool truncate(unsigned size) { + if(!fp) return false; //file not open + #if !defined(_WIN32) + return ftruncate(fileno(fp), size) == 0; + #else + return _chsize(fileno(fp), size) == 0; + #endif + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + static unsigned size(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + unsigned filesize = 0; + if(fp) { + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fclose(fp); + } + return filesize; + } + + bool open() { + return fp; + } + + bool open(const char *fn, FileMode mode) { + if(fp) return false; + + switch(file_mode = mode) { + #if !defined(_WIN32) + case mode_read: fp = fopen(fn, "rb"); break; + case mode_write: fp = fopen(fn, "wb+"); break; //need read permission for buffering + case mode_readwrite: fp = fopen(fn, "rb+"); break; + case mode_writeread: fp = fopen(fn, "wb+"); break; + #else + case mode_read: fp = _wfopen(utf16_t(fn), L"rb"); break; + case mode_write: fp = _wfopen(utf16_t(fn), L"wb+"); break; + case mode_readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break; + case mode_writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = 0; + } + + file() { + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode_read; + } + + ~file() { + close(); + } + + private: + enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 }; + char buffer[buffer_size]; + int buffer_offset; + bool buffer_dirty; + FILE *fp; + unsigned file_offset; + unsigned file_size; + FileMode file_mode; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/filemap.hpp b/mednafen/snes/src/lib/nall/filemap.hpp new file mode 100755 index 0000000..a05f0eb --- /dev/null +++ b/mednafen/snes/src/lib/nall/filemap.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + class filemap { + public: + enum filemode { mode_read, mode_write, mode_readwrite, mode_writeread }; + + bool open(const char *filename, filemode mode) { return p_open(filename, mode); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* handle() { return p_handle; } + const uint8_t* handle() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + ~filemap() { p_dtor(); } + + private: + unsigned p_size; + uint8_t *p_handle; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_open(const char *filename, filemode mode) { + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode) { + default: return false; + case mode_read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode_write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode_readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode_writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL, + creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, NULL); + + p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_open(const char *filename, filemode mode) { + int open_flags, mmap_flags; + + switch(mode) { + default: return false; + case mode_read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode_write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode_readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode_writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = 0; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/function.hpp b/mednafen/snes/src/lib/nall/function.hpp new file mode 100755 index 0000000..58fe349 --- /dev/null +++ b/mednafen/snes/src/lib/nall/function.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +#include + +//prologue + +#define TN typename + +namespace nall { + template class function; +} + +//parameters = 0 + +#define cat(n) n +#define TL typename R +#define PL +#define CL + +#include "function.hpp" + +//parameters = 1 + +#define cat(n) , n +#define TL TN R, TN P1 +#define PL P1 p1 +#define CL p1 + +#include "function.hpp" + +//parameters = 2 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2 +#define PL P1 p1, P2 p2 +#define CL p1, p2 + +#include "function.hpp" + +//parameters = 3 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3 +#define PL P1 p1, P2 p2, P3 p3 +#define CL p1, p2, p3 + +#include "function.hpp" + +//parameters = 4 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4 +#define PL P1 p1, P2 p2, P3 p3, P4 p4 +#define CL p1, p2, p3, p4 + +#include "function.hpp" + +//parameters = 5 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5 +#define CL p1, p2, p3, p4, p5 + +#include "function.hpp" + +//parameters = 6 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6 +#define CL p1, p2, p3, p4, p5, p6 + +#include "function.hpp" + +//parameters = 7 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6, TN P7 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7 +#define CL p1, p2, p3, p4, p5, p6, p7 + +#include "function.hpp" + +//parameters = 8 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6, TN P7, TN P8 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8 +#define CL p1, p2, p3, p4, p5, p6, p7, p8 + +#include "function.hpp" + +//epilogue + +#undef TN +#define NALL_FUNCTION_T + +#elif !defined(NALL_FUNCTION_T) + +//function implementation template class + +namespace nall { + template + class function { + private: + struct base1 { virtual void func1(PL) {} }; + struct base2 { virtual void func2(PL) {} }; + struct derived : base1, virtual base2 {}; + + struct data_t { + R (*fn_call)(const data_t& cat(PL)); + union { + R (*fn_global)(PL); + struct { + R (derived::*fn_member)(PL); + void *object; + }; + }; + } data; + + static R fn_call_global(const data_t &d cat(PL)) { + return d.fn_global(CL); + } + + template + static R fn_call_member(const data_t &d cat(PL)) { + return (((C*)d.object)->*((R (C::*&)(PL))d.fn_member))(CL); + } + + public: + R operator()(PL) const { return data.fn_call(data cat(CL)); } + operator bool() const { return data.fn_call; } + + function() { data.fn_call = 0; } + + function(void *fn) { + data.fn_call = fn ? &fn_call_global : 0; + data.fn_global = (R (*)(PL))fn; + } + + function(R (*fn)(PL)) { + data.fn_call = &fn_call_global; + data.fn_global = fn; + } + + template + function(R (C::*fn)(PL), C *obj) { + data.fn_call = &fn_call_member; + (R (C::*&)(PL))data.fn_member = fn; + assert(sizeof data.fn_member >= sizeof fn); + data.object = obj; + } + + template + function(R (C::*fn)(PL) const, C *obj) { + data.fn_call = &fn_call_member; + (R (C::*&)(PL))data.fn_member = (R (C::*&)(PL))fn; + assert(sizeof data.fn_member >= sizeof fn); + data.object = obj; + } + + function& operator=(void *fn) { return operator=(function(fn)); } + function& operator=(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); return *this; } + function(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); } + }; + + template + function bind(R (*fn)(PL)) { + return function(fn); + } + + template + function bind(R (C::*fn)(PL), C *obj) { + return function(fn, obj); + } + + template + function bind(R (C::*fn)(PL) const, C *obj) { + return function(fn, obj); + } +} + +#undef cat +#undef TL +#undef PL +#undef CL + +#endif diff --git a/mednafen/snes/src/lib/nall/input.hpp b/mednafen/snes/src/lib/nall/input.hpp new file mode 100755 index 0000000..b3ce9eb --- /dev/null +++ b/mednafen/snes/src/lib/nall/input.hpp @@ -0,0 +1,386 @@ +#ifndef NALL_INPUT_HPP +#define NALL_INPUT_HPP + +#include +#include +#include + +#include +#include + +namespace nall { + +struct Keyboard; +Keyboard& keyboard(unsigned = 0); + +static const char KeyboardScancodeName[][64] = { + "Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "PrintScreen", "ScrollLock", "Pause", "Tilde", + "Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0", + "Dash", "Equal", "Backspace", + "Insert", "Delete", "Home", "End", "PageUp", "PageDown", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash", + "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0", + "Point", "Enter", "Add", "Subtract", "Multiply", "Divide", + "NumLock", "CapsLock", + "Up", "Down", "Left", "Right", + "Tab", "Return", "Spacebar", "Menu", + "Shift", "Control", "Alt", "Super", +}; + +struct Keyboard { + const unsigned ID; + enum { Base = 1 }; + enum { Count = 8, Size = 128 }; + + enum Scancode { + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + PrintScreen, ScrollLock, Pause, Tilde, + Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, + Dash, Equal, Backspace, + Insert, Delete, Home, End, PageUp, PageDown, + A, B, C, D, E, F, G, H, I, J, K, L, M, + N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, + Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, + Point, Enter, Add, Subtract, Multiply, Divide, + NumLock, CapsLock, + Up, Down, Left, Right, + Tab, Return, Spacebar, Menu, + Shift, Control, Alt, Super, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed keyDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape); + } + return -1; + } + + static signed modifierDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift); + } + return -1; + } + + static bool isAnyKey(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return true; + } + return false; + } + + static bool isAnyModifier(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "KB")) return 0; + ltrim(s, "KB"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == KeyboardScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "KB" << ID << "::" << KeyboardScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t key(unsigned id) const { return Base + Size * ID + id; } + bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); } + bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); } + bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); } + + Keyboard(unsigned ID_) : ID(ID_) {} +}; + +inline Keyboard& keyboard(unsigned id) { + static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7); + switch(id) { default: + case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3; + case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7; + } +} + +static const char MouseScancodeName[][64] = { + "Xaxis", "Yaxis", "Zaxis", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", +}; + +struct Mouse; +Mouse& mouse(unsigned = 0); + +struct Mouse { + const unsigned ID; + enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count }; + enum { Count = 8, Size = 16 }; + enum { Axes = 3, Buttons = 8 }; + + enum Scancode { + Xaxis, Yaxis, Zaxis, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0); + } + return -1; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "MS")) return 0; + ltrim(s, "MS"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == MouseScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "MS" << ID << "::" << MouseScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); } + bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); } + + Mouse(unsigned ID_) : ID(ID_) {} +}; + +inline Mouse& mouse(unsigned id) { + static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7); + switch(id) { default: + case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3; + case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7; + } +} + +static const char JoypadScancodeName[][64] = { + "Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7", + "Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7", + "Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", + "Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15", + "Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23", + "Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31", +}; + +struct Joypad; +Joypad& joypad(unsigned = 0); + +struct Joypad { + const unsigned ID; + enum { Base = Mouse::Base + Mouse::Size * Mouse::Count }; + enum { Count = 8, Size = 64 }; + enum { Hats = 8, Axes = 16, Buttons = 32 }; + + enum Scancode { + Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7, + Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7, + Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15, + Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23, + Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31, + Limit, + }; + + enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed hatDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0); + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0); + } + return -1; + } + + static bool isAnyHat(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return true; + } + return false; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "JP")) return 0; + ltrim(s, "JP"); + unsigned id = strunsigned(s); + int pos = strpos(s, "::"); + if(pos < 0) return 0; + s = substr(s, pos + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == JoypadScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + } + } + return string() << "JP" << ID << "::" << JoypadScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); } + bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); } + + Joypad(unsigned ID_) : ID(ID_) {} +}; + +inline Joypad& joypad(unsigned id) { + static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7); + switch(id) { default: + case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3; + case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7; + } +} + +struct Scancode { + enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count }; + + static uint16_t decode(const char *name) { + uint16_t code; + code = Keyboard::decode(name); + if(code) return code; + code = Mouse::decode(name); + if(code) return code; + code = Joypad::decode(name); + if(code) return code; + return None; + } + + static string encode(uint16_t code) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code); + } + for(unsigned i = 0; i < Mouse::Count; i++) { + if(mouse(i).belongsTo(code)) return mouse(i).encode(code); + } + for(unsigned i = 0; i < Joypad::Count; i++) { + if(joypad(i).belongsTo(code)) return joypad(i).encode(code); + } + return "None"; + } +}; + +} + +#endif diff --git a/mednafen/snes/src/lib/nall/lzss.hpp b/mednafen/snes/src/lib/nall/lzss.hpp new file mode 100755 index 0000000..202bc81 --- /dev/null +++ b/mednafen/snes/src/lib/nall/lzss.hpp @@ -0,0 +1,81 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + class lzss { + public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/moduloarray.hpp b/mednafen/snes/src/lib/nall/moduloarray.hpp new file mode 100755 index 0000000..be549ae --- /dev/null +++ b/mednafen/snes/src/lib/nall/moduloarray.hpp @@ -0,0 +1,40 @@ +#ifndef NALL_MODULO_HPP +#define NALL_MODULO_HPP + +#include + +namespace nall { + template class modulo_array { + public: + inline T operator[](int index) const { + return buffer[size + index]; + } + + inline T read(int index) const { + return buffer[size + index]; + } + + inline void write(unsigned index, const T value) { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + void serialize(serializer &s) { + s.array(buffer, size * 3); + } + + modulo_array() { + buffer = new T[size * 3](); + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/platform.hpp b/mednafen/snes/src/lib/nall/platform.hpp new file mode 100755 index 0000000..0bd6289 --- /dev/null +++ b/mednafen/snes/src/lib/nall/platform.hpp @@ -0,0 +1,77 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +//========================= +//standard platform headers +//========================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #include + #include + #include + #undef interface +#else + #include + #include + #include +#endif + +//================== +//warning supression +//================== + +//Visual C++ +#if defined(_MSC_VER) + //disable libc "deprecation" warnings + #pragma warning(disable:4996) +#endif + +//================ +//POSIX compliance +//================ + +#if defined(_MSC_VER) + #define PATH_MAX _MAX_PATH + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + #define getcwd _getcwd + #define ftruncate _chsize + #define putenv _putenv + #define rmdir _rmdir + #define vsnprintf _vsnprintf + #define usleep(n) Sleep(n / 1000) +#endif + +//================ +//inline expansion +//================ + +#if defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define inline inline + #define alwaysinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define inline inline + #define alwaysinline inline __forceinline +#else + #define noinline + #define inline inline + #define alwaysinline inline +#endif + +#endif + diff --git a/mednafen/snes/src/lib/nall/priorityqueue.hpp b/mednafen/snes/src/lib/nall/priorityqueue.hpp new file mode 100755 index 0000000..7f33e23 --- /dev/null +++ b/mednafen/snes/src/lib/nall/priorityqueue.hpp @@ -0,0 +1,106 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#include +#include +#include +#include + +namespace nall { + template void priority_queue_nocallback(type_t) {} + + //priority queue implementation using binary min-heap array; + //does not require normalize() function. + //O(1) find (tick) + //O(log n) insert (enqueue) + //O(log n) remove (dequeue) + template class priority_queue : noncopyable { + public: + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + void serialize(serializer &s) { + s.integer(basecounter); + s.integer(heapsize); + for(unsigned n = 0; n < heapcapacity; n++) { + s.integer(heap[n].counter); + s.integer(heap[n].event); + } + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + heapcapacity = size; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + unsigned heapcapacity; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/property.hpp b/mednafen/snes/src/lib/nall/property.hpp new file mode 100755 index 0000000..6fd33ac --- /dev/null +++ b/mednafen/snes/src/lib/nall/property.hpp @@ -0,0 +1,91 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +//nall::property implements ownership semantics into container classes +//example: property::readonly implies that only owner has full +//access to type; and all other code has readonly access. +// +//this code relies on extended friend semantics from C++0x to work, as it +//declares a friend class via a template paramter. it also exploits a bug in +//G++ 4.x to work even in C++98 mode. +// +//if compiling elsewhere, simply remove the friend class and private semantics + +//property can be used either of two ways: +//struct foo { +// property::readonly x; +// property::readwrite y; +//}; +//-or- +//struct foo : property { +// readonly x; +// readwrite y; +//}; + +//return types are const T& (byref) instead fo T (byval) to avoid major speed +//penalties for objects with expensive copy constructors + +//operator-> provides access to underlying object type: +//readonly foo; +//foo->bar(); +//... will call Object::bar(); + +//operator='s reference is constant so as to avoid leaking a reference handle +//that could bypass access restrictions + +//both constant and non-constant operators are provided, though it may be +//necessary to cast first, for instance: +//struct foo : property { readonly bar; } object; +//int main() { int value = const_cast(object); } + +//writeonly is useful for objects that have non-const reads, but const writes. +//however, to avoid leaking handles, the interface is very restricted. the only +//way to write is via operator=, which requires conversion via eg copy +//constructor. example: +//struct foo { +// foo(bool value) { ... } +//}; +//writeonly bar; +//bar = true; + +namespace nall { + template struct property { + template struct traits { typedef T type; }; + + template struct readonly { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + private: + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + friend class traits::type; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + friend class traits::type; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + }; + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/serial.hpp b/mednafen/snes/src/lib/nall/serial.hpp new file mode 100755 index 0000000..6f5cf6d --- /dev/null +++ b/mednafen/snes/src/lib/nall/serial.hpp @@ -0,0 +1,80 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + class serial { + public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB); + attr.c_cflag |= (CS8 | CREAD | CLOCAL); + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + + private: + int port; + bool port_open; + termios original_attr; + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/serializer.hpp b/mednafen/snes/src/lib/nall/serializer.hpp new file mode 100755 index 0000000..3ac3c9f --- /dev/null +++ b/mednafen/snes/src/lib/nall/serializer.hpp @@ -0,0 +1,126 @@ +#ifndef NALL_SERIALIZER_HPP +#define NALL_SERIALIZER_HPP + +#include +#include +#include + +namespace nall { + + //serializer: a class designed to save and restore the state of classes. + // + //benefits: + //- data() will be portable in size (it is not necessary to specify type sizes.) + //- data() will be portable in endianness (always stored internally as little-endian.) + //- one serialize function can both save and restore class states. + // + //caveats: + //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); + //- floating-point usage is not portable across platforms + + class serializer { + public: + enum mode_t { Load, Save, Size }; + + mode_t mode() const { + return imode; + } + + const uint8_t* data() const { + return idata; + } + + unsigned size() const { + return isize; + } + + unsigned capacity() const { + return icapacity; + } + + template void floatingpoint(T &value) { + enum { size = sizeof(T) }; + //this is rather dangerous, and not cross-platform safe; + //but there is no standardized way to export FP-values + uint8_t *p = (uint8_t*)&value; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = p[n]; + } else if(imode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = idata[isize++]; + } else { + isize += size; + } + } + + template void integer(T &value) { + enum { size = is_bool::value ? 1 : sizeof(T) }; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = (uint64_t)value >> (n << 3); + } else if(imode == Load) { + value = 0; + for(unsigned n = 0; n < size; n++) value |= (uint64_t)idata[isize++] << (n << 3); + } else if(imode == Size) { + isize += size; + } + } + + template void array(T &array) { + enum { size = sizeof(T) / sizeof(typename remove_extent::type) }; + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + template void array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + serializer& operator=(const serializer &s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = new uint8_t[s.icapacity]; + isize = s.isize; + icapacity = s.icapacity; + + memcpy(idata, s.idata, s.icapacity); + return *this; + } + + serializer(const serializer &s) : idata(0) { + operator=(s); + } + + serializer() { + imode = Size; + idata = 0; + isize = 0; + } + + serializer(unsigned capacity) { + imode = Save; + idata = new uint8_t[capacity](); + isize = 0; + icapacity = capacity; + } + + serializer(const uint8_t *data, unsigned capacity) { + imode = Load; + idata = new uint8_t[capacity]; + isize = 0; + icapacity = capacity; + memcpy(idata, data, capacity); + } + + ~serializer() { + if(idata) delete[] idata; + } + + private: + mode_t imode; + uint8_t *idata; + unsigned isize; + unsigned icapacity; + }; + +}; + +#endif diff --git a/mednafen/snes/src/lib/nall/sha256.hpp b/mednafen/snes/src/lib/nall/sha256.hpp new file mode 100755 index 0000000..7f41f04 --- /dev/null +++ b/mednafen/snes/src/lib/nall/sha256.hpp @@ -0,0 +1,143 @@ +#ifndef NALL_SHA256_HPP +#define NALL_SHA256_HPP + +//author: vladitx + +namespace nall { + #define PTR(t, a) ((t*)(a)) + + #define SWAP32(x) ((uint32_t)( \ + (((uint32_t)(x) & 0x000000ff) << 24) | \ + (((uint32_t)(x) & 0x0000ff00) << 8) | \ + (((uint32_t)(x) & 0x00ff0000) >> 8) | \ + (((uint32_t)(x) & 0xff000000) >> 24) \ + )) + + #define ST32(a, d) *PTR(uint32_t, a) = (d) + #define ST32BE(a, d) ST32(a, SWAP32(d)) + + #define LD32(a) *PTR(uint32_t, a) + #define LD32BE(a) SWAP32(LD32(a)) + + #define LSL32(x, n) ((uint32_t)(x) << (n)) + #define LSR32(x, n) ((uint32_t)(x) >> (n)) + #define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n))) + + //first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19 + static const uint32_t T_H[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, + }; + + //first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311 + static const uint32_t T_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + }; + + struct sha256_ctx { + uint8_t in[64]; + unsigned inlen; + + uint32_t w[64]; + uint32_t h[8]; + uint64_t len; + }; + + void sha256_init(sha256_ctx *p) { + memset(p, 0, sizeof(sha256_ctx)); + memcpy(p->h, T_H, sizeof(T_H)); + } + + static void sha256_block(sha256_ctx *p) { + unsigned i; + uint32_t s0, s1; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2, maj, ch; + + for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4); + + for(i = 16; i < 64; i++) { + s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3); + s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10); + p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1; + } + + a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3]; + e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7]; + + for(i = 0; i < 64; i++) { + s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22); + maj = (a & b) ^ (a & c) ^ (b & c); + t2 = s0 + maj; + s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25); + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + T_K[i] + p->w[i]; + + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d; + p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h; + + //next block + p->inlen = 0; + } + + void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) { + unsigned l; + p->len += len; + + while(len) { + l = 64 - p->inlen; + l = (len < l) ? len : l; + + memcpy(p->in + p->inlen, s, l); + s += l; + p->inlen += l; + len -= l; + + if(p->inlen == 64) sha256_block(p); + } + } + + void sha256_final(sha256_ctx *p) { + uint64_t len; + p->in[p->inlen++] = 0x80; + + if(p->inlen > 56) { + memset(p->in + p->inlen, 0, 64 - p->inlen); + sha256_block(p); + } + + memset(p->in + p->inlen, 0, 56 - p->inlen); + + len = p->len << 3; + ST32BE(p->in + 56, len >> 32); + ST32BE(p->in + 60, len); + sha256_block(p); + } + + void sha256_hash(sha256_ctx *p, uint8_t *s) { + uint32_t *t = (uint32_t*)s; + for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]); + } + + #undef PTR + #undef SWAP32 + #undef ST32 + #undef ST32BE + #undef LD32 + #undef LD32BE + #undef LSL32 + #undef LSR32 + #undef ROR32 +} + +#endif diff --git a/mednafen/snes/src/lib/nall/sort.hpp b/mednafen/snes/src/lib/nall/sort.hpp new file mode 100755 index 0000000..23c317a --- /dev/null +++ b/mednafen/snes/src/lib/nall/sort.hpp @@ -0,0 +1,62 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//notes: +//there are two primary reasons for choosing merge sort +//over the (usually) faster quick sort*: +//1: it is a stable sort. +//2: it lacks O(n^2) worst-case overhead. +//(* which is also O(n log n) in the average case.) + +namespace nall { + template + void sort(T list[], unsigned length) { + if(length <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(length < 64) { + for(unsigned i = 0; i < length; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < length; j++) { + if(list[j] < list[min]) min = j; + } + if(min != i) swap(list[i], list[min]); + } + return; + } + + //split list in half and recursively sort both + unsigned middle = length / 2; + sort(list, middle); + sort(list + middle, length - middle); + + //left and right are sorted here; perform merge sort + T *buffer = new T[length]; + unsigned offset = 0; + unsigned left = 0; + unsigned right = middle; + while(left < middle && right < length) { + if(list[left] < list[right]) { + buffer[offset++] = list[left++]; + } else { + buffer[offset++] = list[right++]; + } + } + while(left < middle) buffer[offset++] = list[left++]; + while(right < length) buffer[offset++] = list[right++]; + + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } +} + +#endif diff --git a/mednafen/snes/src/lib/nall/static.hpp b/mednafen/snes/src/lib/nall/static.hpp new file mode 100755 index 0000000..05b85b8 --- /dev/null +++ b/mednafen/snes/src/lib/nall/static.hpp @@ -0,0 +1,17 @@ +#ifndef NALL_STATIC_HPP +#define NALL_STATIC_HPP + +namespace nall { + template struct nall_static_assert; + template<> struct nall_static_assert {}; + + template struct static_if { + typedef true_type type; + }; + + template struct static_if { + typedef false_type type; + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/stdint.hpp b/mednafen/snes/src/lib/nall/stdint.hpp new file mode 100755 index 0000000..e08cd33 --- /dev/null +++ b/mednafen/snes/src/lib/nall/stdint.hpp @@ -0,0 +1,44 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#include + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +namespace nall { + static nall_static_assert int8_t_assert; + static nall_static_assert int16_t_assert; + static nall_static_assert int32_t_assert; + static nall_static_assert int64_t_assert; + + static nall_static_assert uint8_t_assert; + static nall_static_assert uint16_t_assert; + static nall_static_assert uint32_t_assert; + static nall_static_assert uint64_t_assert; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/string.hpp b/mednafen/snes/src/lib/nall/string.hpp new file mode 100755 index 0000000..94e65b6 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string.hpp @@ -0,0 +1,18 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/mednafen/snes/src/lib/nall/string/base.hpp b/mednafen/snes/src/lib/nall/string/base.hpp new file mode 100755 index 0000000..e5851b0 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/base.hpp @@ -0,0 +1,122 @@ +#ifndef NALL_STRING_BASE_HPP +#define NALL_STRING_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include + +inline char chrlower(char c); +inline char chrupper(char c); +inline int stricmp(const char *dest, const char *src); +inline int strpos (const char *str, const char *key); +inline int qstrpos(const char *str, const char *key); +inline bool strbegin (const char *str, const char *key); +inline bool stribegin(const char *str, const char *key); +inline bool strend (const char *str, const char *key); +inline bool striend(const char *str, const char *key); +inline char* strlower(char *str); +inline char* strupper(char *str); +inline char* strtr(char *dest, const char *before, const char *after); +inline uintmax_t strhex (const char *str); +inline intmax_t strsigned (const char *str); +inline uintmax_t strunsigned(const char *str); +inline uintmax_t strbin (const char *str); +inline double strdouble (const char *str); +inline size_t strhex (char *str, uintmax_t value, size_t length = 0); +inline size_t strsigned (char *str, intmax_t value, size_t length = 0); +inline size_t strunsigned(char *str, uintmax_t value, size_t length = 0); +inline size_t strbin (char *str, uintmax_t value, size_t length = 0); +inline size_t strdouble (char *str, double value, size_t length = 0); +inline bool match(const char *pattern, const char *str); +inline bool strint (const char *str, int &result); +inline bool strmath(const char *str, int &result); +inline size_t strlcpy(char *dest, const char *src, size_t length); +inline size_t strlcat(char *dest, const char *src, size_t length); +inline char* ltrim(char *str, const char *key = " "); +inline char* rtrim(char *str, const char *key = " "); +inline char* trim (char *str, const char *key = " "); +inline char* ltrim_once(char *str, const char *key = " "); +inline char* rtrim_once(char *str, const char *key = " "); +inline char* trim_once (char *str, const char *key = " "); + +namespace nall { + class string; + template inline string to_string(T); + + class string { + public: + static string printf(const char*, ...); + + inline void reserve(size_t); + inline unsigned length() const; + + inline string& assign(const char*); + inline string& append(const char*); + template inline string& operator= (T value); + template inline string& operator<<(T value); + + inline operator const char*() const; + inline char* operator()(); + inline char& operator[](int); + + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline bool operator< (const char*) const; + inline bool operator<=(const char*) const; + inline bool operator> (const char*) const; + inline bool operator>=(const char*) const; + + inline string(); + inline string(const char*); + inline string(const string&); + inline string& operator=(const string&); + inline ~string(); + + inline bool readfile(const char*); + inline string& replace (const char*, const char*); + inline string& qreplace(const char*, const char*); + + protected: + char *data; + size_t size; + + #if defined(QT_CORE_LIB) + public: + inline operator QString() const; + #endif + }; + + class lstring : public vector { + public: + template inline lstring& operator<<(T value); + + inline int find(const char*); + inline void split (const char*, const char*, unsigned = 0); + inline void qsplit(const char*, const char*, unsigned = 0); + }; +}; + +inline size_t strlcpy(nall::string &dest, const char *src, size_t length); +inline size_t strlcat(nall::string &dest, const char *src, size_t length); +inline nall::string& strlower(nall::string &str); +inline nall::string& strupper(nall::string &str); +inline nall::string& strtr(nall::string &dest, const char *before, const char *after); +inline nall::string& ltrim(nall::string &str, const char *key = " "); +inline nall::string& rtrim(nall::string &str, const char *key = " "); +inline nall::string& trim (nall::string &str, const char *key = " "); +inline nall::string& ltrim_once(nall::string &str, const char *key = " "); +inline nall::string& rtrim_once(nall::string &str, const char *key = " "); +inline nall::string& trim_once (nall::string &str, const char *key = " "); + +inline nall::string substr(const char *src, size_t start = 0, size_t length = 0); +inline nall::string strhex (uintmax_t value); +inline nall::string strsigned (intmax_t value); +inline nall::string strunsigned(uintmax_t value); +inline nall::string strbin (uintmax_t value); +inline nall::string strdouble (double value); + +#endif diff --git a/mednafen/snes/src/lib/nall/string/cast.hpp b/mednafen/snes/src/lib/nall/string/cast.hpp new file mode 100755 index 0000000..0b49091 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/cast.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_STRING_CAST_HPP +#define NALL_STRING_CAST_HPP + +namespace nall { + //this is needed, as C++98 does not support explicit template specialization inside classes; + //redundant memory allocation should hopefully be avoided via compiler optimizations. + template<> inline string to_string (bool v) { return v ? "true" : "false"; } + template<> inline string to_string (signed int v) { return strsigned(v); } + template<> inline string to_string (unsigned int v) { return strunsigned(v); } + template<> inline string to_string (double v) { return strdouble(v); } + template<> inline string to_string (char *v) { return v; } + template<> inline string to_string (const char *v) { return v; } + template<> inline string to_string (string v) { return v; } + template<> inline string to_string(const string &v) { return v; } + + template string& string::operator= (T value) { return assign(to_string(value)); } + template string& string::operator<<(T value) { return append(to_string(value)); } + + template lstring& lstring::operator<<(T value) { + operator[](size()).assign(to_string(value)); + return *this; + } + + #if defined(QT_CORE_LIB) + template<> inline string to_string(const QString &v) { return v.toUtf8().constData(); } + string::operator QString() const { return QString::fromUtf8(*this); } + #endif +}; + +#endif diff --git a/mednafen/snes/src/lib/nall/string/compare.hpp b/mednafen/snes/src/lib/nall/string/compare.hpp new file mode 100755 index 0000000..23d2ee3 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/compare.hpp @@ -0,0 +1,100 @@ +#ifndef NALL_STRING_COMPARE_HPP +#define NALL_STRING_COMPARE_HPP + +char chrlower(char c) { + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char chrupper(char c) { + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + +int stricmp(const char *dest, const char *src) { + while(*dest) { + if(chrlower(*dest) != chrlower(*src)) break; + dest++; + src++; + } + + return (int)chrlower(*dest) - (int)chrlower(*src); +} + +int strpos(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return -1; + for(int i = 0; i <= ssl - ksl; i++) { + if(!memcmp(str + i, key, ksl)) { + return i; + } + } + return -1; +} + +int qstrpos(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return -1; + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = str[i]; + if(x == '\"' || x == '\'') { + uint8_t z = i++; + while(str[i] != x && i < ssl) i++; + if(i >= ssl) i = z; + } + if(!memcmp(str + i, key, ksl)) { + return i; + } else { + i++; + } + } + return -1; +} + +bool strbegin(const char *str, const char *key) { + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +bool stribegin(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = 0; i < ksl; i++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[i] && str[i]+0x20 != key[i])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[i] && str[i]-0x20 != key[i])return false; + } else { + if(str[i] != key[i])return false; + } + } + return true; +} + +bool strend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +bool striend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[z] && str[i]+0x20 != key[z])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[z] && str[i]-0x20 != key[z])return false; + } else { + if(str[i] != key[z])return false; + } + } + return true; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/string/convert.hpp b/mednafen/snes/src/lib/nall/string/convert.hpp new file mode 100755 index 0000000..da90bb0 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/convert.hpp @@ -0,0 +1,285 @@ +#ifndef NALL_STRING_CONVERT_HPP +#define NALL_STRING_CONVERT_HPP + +char* strlower(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +char* strupper(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +char* strtr(char *dest, const char *before, const char *after) { + if(!dest || !before || !after) return dest; + int sl = strlen(dest), bsl = strlen(before), asl = strlen(after); + + if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace + for(unsigned i = 0; i < sl; i++) { + for(unsigned l = 0; l < bsl; l++) { + if(dest[i] == before[l]) { + dest[i] = after[l]; + break; + } + } + } + + return dest; +} + +uintmax_t strhex(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip hex identifiers 0x and $, if present + if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2; + else if(*str == '$') str++; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x >= 'A' && x <= 'F') x -= 'A' - 10; + else if(x >= 'a' && x <= 'f') x -= 'a' - 10; + else break; //stop at first invalid character + result = result * 16 + x; + } + + return result; +} + +intmax_t strsigned(const char *str) { + if(!str) return 0; + intmax_t result = 0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return !negate ? result : -result; +} + +uintmax_t strunsigned(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return result; +} + +uintmax_t strbin(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip bin identifiers 0b and %, if present + if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2; + else if(*str == '%') str++; + + while(*str) { + uint8_t x = *str++; + if(x == '0' || x == '1') x -= '0'; + else break; //stop at first invalid character + result = result * 2 + x; + } + + return result; +} + +double strdouble(const char *str) { + if(!str) return 0.0; + bool negate = false; + + //check for negation + if(*str == '-') { + negate = true; + str++; + } + + intmax_t result_integral = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x == '.') break; //break loop and read fractional part + else return (double)result_integral; //invalid value, assume no fractional part + result_integral = result_integral * 10 + x; + } + + intmax_t result_fractional = 0; + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result_fractional = result_fractional * 10 + x; + } + + //calculate fractional portion + double result = (double)result_fractional; + while((uintmax_t)result > 0) result /= 10.0; + result += (double)result_integral; + + return !negate ? result : -result; +} + +// + +size_t strhex(char *str, uintmax_t value, size_t length /* = 0 */) { + if(length == 0) length -= 1U; //"infinite" length + size_t initial_length = length; + + //count number of digits in value + int digits_integral = 1; + uintmax_t digits_integral_ = value; + while(digits_integral_ /= 16) digits_integral++; + + int digits = digits_integral; + if(!str) return digits + 1; //only computing required length? + + length = nall::min(digits, length - 1); + str += length; //seek to end of target string + *str = 0; //set null terminator + + while(length--) { + uint8_t x = value % 16; + value /= 16; + *--str = x < 10 ? (x + '0') : (x + 'a' - 10); //iterate backwards to write string + } + + return nall::min(initial_length, digits + 1); +} + +size_t strsigned(char *str, intmax_t value_, size_t length /* = 0 */) { + if(length == 0) length = -1U; //"infinite" length + size_t initial_length = length; + + bool negate = value_ < 0; + uintmax_t value = value_ >= 0 ? value_ : -value_; + + //count number of digits in value + int digits_integral = 1; + uintmax_t digits_integral_ = value; + while(digits_integral_ /= 10) digits_integral++; + + int digits = (negate ? 1 : 0) + digits_integral; + if(!str) return digits + 1; //only computing required length? + + length = nall::min(digits, length - 1); + str += length; //seek to end of target string + *str = 0; //set null terminator + while(length && digits_integral--) { + uint8_t x = '0' + (value % 10); + value /= 10; + *--str = x; //iterate backwards to write string + length--; + } + + if(length && negate) { + *--str = '-'; + } + + return nall::min(initial_length, digits + 1); +} + +size_t strunsigned(char *str, uintmax_t value, size_t length /* = 0 */) { + if(length == 0) length = -1U; //"infinite" length + size_t initial_length = length; + + //count number of digits in value + int digits_integral = 1; + uintmax_t digits_integral_ = value; + while(digits_integral_ /= 10) digits_integral++; + + int digits = digits_integral; + if(!str) return digits_integral + 1; //only computing required length? + + length = nall::min(digits, length - 1); + str += length; //seek to end of target string + *str = 0; //set null terminator + + while(length--) { + uint8_t x = '0' + (value % 10); + value /= 10; + *--str = x; //iterate backwards to write string + } + + return nall::min(initial_length, digits + 1); +} + +size_t strbin(char *str, uintmax_t value, size_t length /* = 0 */) { + if(length == 0) length = -1U; //"infinite" length + size_t initial_length = length; + + //count number of digits in value + int digits_integral = 1; + uintmax_t digits_integral_ = value; + while(digits_integral_ /= 2) digits_integral++; + + int digits = digits_integral; + if(!str) return digits + 1; //only computing required length? + + length = nall::min(digits, length - 1); + str += length; //seek to end of target string + *str = 0; //set null terminator + + while(length--) { + uint8_t x = '0' + (value % 2); + value /= 2; + *--str = x; //iterate backwards to write string + } + + return nall::min(initial_length, digits + 1); +} + +//using sprintf is certainly not the most ideal method to convert +//a double to a string ... but attempting to parse a double by +//hand, digit-by-digit, results in subtle rounding errors. +// +//note: length parameter is currently ignored. +//it remains for consistency and possible future support. +size_t strdouble(char *str, double value, size_t length /* = 0 */) { + char buffer[256]; + sprintf(buffer, "%f", value); + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char *p = buffer; *p; p++) { + if(*p == '.') { + char *p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/string/core.hpp b/mednafen/snes/src/lib/nall/string/core.hpp new file mode 100755 index 0000000..29ca717 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/core.hpp @@ -0,0 +1,118 @@ +#ifndef NALL_STRING_CORE_HPP +#define NALL_STRING_CORE_HPP + +namespace nall { + +inline string string::printf(const char *fmt, ...) { + static char text[4096]; + va_list args; + va_start(args, fmt); + vsprintf(text, fmt, args); + va_end(args); + return text; +} + +void string::reserve(size_t size_) { + if(size_ > size) { + size = size_; + data = (char*)realloc(data, size + 1); + data[size] = 0; + } +} + +unsigned string::length() const { + return strlen(data); +} + +string& string::assign(const char *s) { + unsigned length = strlen(s); + reserve(length); + strcpy(data, s); + return *this; +} + +string& string::append(const char *s) { + unsigned length = strlen(data) + strlen(s); + reserve(length); + strcat(data, s); + return *this; +} + +string::operator const char*() const { + return data; +} + +char* string::operator()() { + return data; +} + +char& string::operator[](int index) { + reserve(index); + return data[index]; +} + +bool string::operator==(const char *str) const { return strcmp(data, str) == 0; } +bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; } +bool string::operator< (const char *str) const { return strcmp(data, str) < 0; } +bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; } +bool string::operator> (const char *str) const { return strcmp(data, str) > 0; } +bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } + +string::string() { + size = 64; + data = (char*)malloc(size + 1); + *data = 0; +} + +string::string(const char *value) { + size = strlen(value); + data = strdup(value); +} + +string::string(const string &value) { + size = strlen(value); + data = strdup(value); +} + +string& string::operator=(const string &value) { + assign(value); + return *this; +} + +string::~string() { + free(data); +} + +bool string::readfile(const char *filename) { + assign(""); + + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(nall::utf16_t(filename), L"rb"); + #endif + if(!fp) return false; + + fseek(fp, 0, SEEK_END); + size_t size = ftell(fp); + rewind(fp); + char *fdata = new char[size + 1]; + unsigned unused = fread(fdata, 1, size, fp); + fclose(fp); + fdata[size] = 0; + assign(fdata); + delete[] fdata; + + return true; +} + +int lstring::find(const char *key) { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return i; + } + return -1; +} + +}; + +#endif diff --git a/mednafen/snes/src/lib/nall/string/filename.hpp b/mednafen/snes/src/lib/nall/string/filename.hpp new file mode 100755 index 0000000..e26493c --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/filename.hpp @@ -0,0 +1,60 @@ +#ifndef NALL_FILENAME_HPP +#define NALL_FILENAME_HPP + +namespace nall { + // "foo/bar.c" -> "foo/", "bar.c" -> "./" + inline string dir(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + result[i + 1] = 0; + break; + } + if(i == 0) result = "./"; + } + return result; + } + + // "foo/bar.c" -> "bar.c" + inline string notdir(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + name += i + 1; + break; + } + } + string result = name; + return result; + } + + // "foo/bar.c" -> "foo/bar" + inline string basename(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + //file has no extension + break; + } + + if(result[i] == '.') { + result[i] = 0; + break; + } + } + return result; + } + + // "foo/bar.c" -> "c" + inline string extension(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '.') { + name += i + 1; + break; + } + } + string result = name; + return result; + } +} + +#endif diff --git a/mednafen/snes/src/lib/nall/string/match.hpp b/mednafen/snes/src/lib/nall/string/match.hpp new file mode 100755 index 0000000..70184fe --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/match.hpp @@ -0,0 +1,72 @@ +#ifndef NALL_STRING_MATCH_HPP +#define NALL_STRING_MATCH_HPP + +bool match(const char *p, const char *s) { + const char *p_ = 0, *s_ = 0; + + for(;;) { + if(!*s) { + while(*p == '*') p++; + return !*p; + } + + //wildcard match + if(*p == '*') { + p_ = p++, s_ = s; + continue; + } + + //any match + if(*p == '?') { + p++, s++; + continue; + } + + //ranged match + if(*p == '{') { + #define pattern(name_, rule_) \ + if(strbegin(p, name_)) { \ + if(rule_) { \ + p += sizeof(name_) - 1, s++; \ + continue; \ + } \ + goto failure; \ + } + + pattern("{alpha}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) + pattern("{alphanumeric}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') || (*s >= '0' && *s <= '9')) + pattern("{binary}", (*s == '0' || *s == '1')) + pattern("{hex}", (*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'F') || (*s >= 'a' && *s <= 'f')) + pattern("{lowercase}", (*s >= 'a' && *s <= 'z')) + pattern("{numeric}", (*s >= '0' && *s <= '9')) + pattern("{uppercase}", (*s >= 'A' && *s <= 'Z')) + pattern("{whitespace}", (*s == ' ' || *s == '\t')) + + #undef pattern + goto failure; + } + + //reserved character match + if(*p == '\\') { + p++; + //fallthrough + } + + //literal match + if(*p == *s) { + p++, *s++; + continue; + } + + //attempt wildcard rematch + failure: + if(p_) { + p = p_, s = s_ + 1; + continue; + } + + return false; + } +} + +#endif diff --git a/mednafen/snes/src/lib/nall/string/math.hpp b/mednafen/snes/src/lib/nall/string/math.hpp new file mode 100755 index 0000000..604b083 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/math.hpp @@ -0,0 +1,160 @@ +#ifndef NALL_STRING_MATH_HPP +#define NALL_STRING_MATH_HPP + +static int eval_integer(const char *&s) { + if(!*s) throw "unrecognized_integer"; + int value = 0, x = *s, y = *(s + 1); + + //hexadecimal + if(x == '0' && (y == 'X' || y == 'x')) { + s += 2; + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } + if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } + if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } + return value; + } + } + + //binary + if(x == '0' && (y == 'B' || y == 'b')) { + s += 2; + while(true) { + if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } + return value; + } + } + + //octal (or decimal '0') + if(x == '0') { + s += 1; + while(true) { + if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } + return value; + } + } + + //decimal + if(x >= '0' && x <= '9') { + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } + return value; + } + } + + //char + if(x == '\'' && y != '\'') { + s += 1; + while(true) { + value = value * 256 + *s++; + if(*s == '\'') { s += 1; return value; } + if(!*s) throw "mismatched_char"; + } + } + + throw "unrecognized_integer"; +} + +static int eval(const char *&s, int depth = 0) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) throw "unrecognized_token"; + int value = 0, x = *s, y = *(s + 1); + + if(*s == '(') { + value = eval(++s, 1); + if(*s++ != ')') throw "mismatched_group"; + } + + else if(x == '!') value = !eval(++s, 13); + else if(x == '~') value = ~eval(++s, 13); + else if(x == '+') value = +eval(++s, 13); + else if(x == '-') value = -eval(++s, 13); + + else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); + + else throw "unrecognized_token"; + + while(true) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) break; + x = *s, y = *(s + 1); + + if(depth >= 13) break; + if(x == '*') { value *= eval(++s, 13); continue; } + if(x == '/') { value /= eval(++s, 13); continue; } + if(x == '%') { value %= eval(++s, 13); continue; } + + if(depth >= 12) break; + if(x == '+') { value += eval(++s, 12); continue; } + if(x == '-') { value -= eval(++s, 12); continue; } + + if(depth >= 11) break; + if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } + if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } + + if(depth >= 10) break; + if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } + if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } + if(x == '<') { value = value < eval(++s, 10); continue; } + if(x == '>') { value = value > eval(++s, 10); continue; } + + if(depth >= 9) break; + if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } + if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } + + if(depth >= 8) break; + if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } + + if(depth >= 7) break; + if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } + + if(depth >= 6) break; + if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } + + if(depth >= 5) break; + if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } + + if(depth >= 4) break; + if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } + + if(depth >= 3) break; + if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } + + if(x == '?') { + int lhs = eval(++s, 2); + if(*s != ':') throw "mismatched_ternary"; + int rhs = eval(++s, 2); + value = value ? lhs : rhs; + continue; + } + if(depth >= 2) break; + + if(depth > 0 && x == ')') break; + + throw "unrecognized_token"; + } + + return value; +} + +bool strint(const char *s, int &result) { + try { + result = eval_integer(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +bool strmath(const char *s, int &result) { + try { + result = eval(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +#endif diff --git a/mednafen/snes/src/lib/nall/string/replace.hpp b/mednafen/snes/src/lib/nall/string/replace.hpp new file mode 100755 index 0000000..db405a9 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/replace.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_STRING_REPLACE_HPP +#define NALL_STRING_REPLACE_HPP + +namespace nall { + +string& string::replace(const char *key, const char *token) { + int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { //the new string may be longer than the old string... + for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +string& string::qreplace(const char *key, const char *token) { + int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + uint8_t x; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { + for(i = 0; i <= ssl - ksl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(data[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(data[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(buffer + z, data + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +}; + +#endif diff --git a/mednafen/snes/src/lib/nall/string/split.hpp b/mednafen/snes/src/lib/nall/string/split.hpp new file mode 100755 index 0000000..bb77dfc --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/split.hpp @@ -0,0 +1,56 @@ +#ifndef NALL_STRING_SPLIT_HPP +#define NALL_STRING_SPLIT_HPP + +namespace nall { + +void lstring::split(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +void lstring::qsplit(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = src[i]; + + if(x == '\"' || x == '\'') { + int z = i++; //skip opening quote + while(i < ssl && src[i] != x) i++; + if(i >= ssl) i = z; //failed match, rewind i + else { + i++; //skip closing quote + continue; //restart in case next char is also a quote + } + } + + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +}; + +#endif diff --git a/mednafen/snes/src/lib/nall/string/strl.hpp b/mednafen/snes/src/lib/nall/string/strl.hpp new file mode 100755 index 0000000..329e852 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/strl.hpp @@ -0,0 +1,48 @@ +#ifndef NALL_STRING_STRL_HPP +#define NALL_STRING_STRL_HPP + +//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller + +//return = strlen(src) +size_t strlcpy(char *dest, const char *src, size_t length) { + char *d = dest; + const char *s = src; + size_t n = length; + + if(n) { + while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached + } + + if(!n) { + if(length) *d = 0; + while(*s++); //traverse rest of s, so that s - src == strlen(src) + } + + return (s - src - 1); //return length of copied string, sans null terminator +} + +//return = strlen(src) + min(length, strlen(dest)) +size_t strlcat(char *dest, const char *src, size_t length) { + char *d = dest; + const char *s = src; + size_t n = length; + + while(n-- && *d) d++; //find end of dest + size_t dlength = d - dest; + n = length - dlength; //subtract length of dest from maximum string length + + if(!n) return dlength + strlen(s); + + while(*s) { + if(n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = 0; + + return dlength + (s - src); //return length of resulting string, sans null terminator +} + +#endif diff --git a/mednafen/snes/src/lib/nall/string/trim.hpp b/mednafen/snes/src/lib/nall/string/trim.hpp new file mode 100755 index 0000000..8f218c5 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/trim.hpp @@ -0,0 +1,50 @@ +#ifndef NALL_STRING_TRIM_HPP +#define NALL_STRING_TRIM_HPP + +char* ltrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + } + return str; +} + +char* rtrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +char* trim(char *str, const char *key) { + return ltrim(rtrim(str, key), key); +} + +char* ltrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + } + return str; +} + +char* rtrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +char* trim_once(char *str, const char *key) { + return ltrim_once(rtrim_once(str, key), key); +} + +#endif diff --git a/mednafen/snes/src/lib/nall/string/utility.hpp b/mednafen/snes/src/lib/nall/string/utility.hpp new file mode 100755 index 0000000..1300d32 --- /dev/null +++ b/mednafen/snes/src/lib/nall/string/utility.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_STRING_UTILITY_HPP +#define NALL_STRING_UTILITY_HPP + +size_t strlcpy(nall::string &dest, const char *src, size_t length) { + dest.reserve(length); + return strlcpy(dest(), src, length); +} + +size_t strlcat(nall::string &dest, const char *src, size_t length) { + dest.reserve(length); + return strlcat(dest(), src, length); +} + +nall::string substr(const char *src, size_t start, size_t length) { + nall::string dest; + if(length == 0) { + //copy entire string + dest = src + start; + } else { + //copy partial string + strlcpy(dest, src + start, length + 1); + } + return dest; +} + +/* very simplistic wrappers to return nall::string& instead of char* type */ + +nall::string& strlower(nall::string &str) { strlower(str()); return str; } +nall::string& strupper(nall::string &str) { strupper(str()); return str; } +nall::string& strtr(nall::string &dest, const char *before, const char *after) { strtr(dest(), before, after); return dest; } +nall::string& ltrim(nall::string &str, const char *key) { ltrim(str(), key); return str; } +nall::string& rtrim(nall::string &str, const char *key) { rtrim(str(), key); return str; } +nall::string& trim (nall::string &str, const char *key) { trim (str(), key); return str; } +nall::string& ltrim_once(nall::string &str, const char *key) { ltrim_once(str(), key); return str; } +nall::string& rtrim_once(nall::string &str, const char *key) { rtrim_once(str(), key); return str; } +nall::string& trim_once (nall::string &str, const char *key) { trim_once (str(), key); return str; } + +/* arithmetic <> string */ + +nall::string strhex(uintmax_t value) { + nall::string temp; + temp.reserve(strhex(0, value)); + strhex(temp(), value); + return temp; +} + +nall::string strsigned(intmax_t value) { + nall::string temp; + temp.reserve(strsigned(0, value)); + strsigned(temp(), value); + return temp; +} + +nall::string strunsigned(uintmax_t value) { + nall::string temp; + temp.reserve(strunsigned(0, value)); + strunsigned(temp(), value); + return temp; +} + +nall::string strbin(uintmax_t value) { + nall::string temp; + temp.reserve(strbin(0, value)); + strbin(temp(), value); + return temp; +} + +nall::string strdouble(double value) { + nall::string temp; + temp.reserve(strdouble(0, value)); + strdouble(temp(), value); + return temp; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/traits.hpp b/mednafen/snes/src/lib/nall/traits.hpp new file mode 100755 index 0000000..0e7e0db --- /dev/null +++ b/mednafen/snes/src/lib/nall/traits.hpp @@ -0,0 +1,97 @@ +#ifndef NALL_TRAITS_HPP +#define NALL_TRAITS_HPP + +namespace nall { + //== + //is + //== + + template struct is_integral { enum { value = false }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + template<> struct is_integral { enum { value = true }; }; + + template struct is_floating_point { enum { value = false }; }; + template<> struct is_floating_point { enum { value = true }; }; + template<> struct is_floating_point { enum { value = true }; }; + template<> struct is_floating_point { enum { value = true }; }; + + template struct is_bool { enum { value = false }; }; + template<> struct is_bool { enum { value = true }; }; + + template struct is_void { enum { value = false }; }; + template<> struct is_void { enum { value = true }; }; + + template struct is_arithmetic { + enum { value = is_integral::value || is_floating_point::value }; + }; + + template struct is_fundamental { + enum { value = is_integral::value || is_floating_point::value || is_void::value }; + }; + + template struct is_compound { + enum { value = !is_fundamental::value }; + }; + + template struct is_array { enum { value = false }; }; + template struct is_array { enum { value = true }; }; + template struct is_array { enum { value = true }; }; + + template struct is_const { enum { value = false }; }; + template struct is_const { enum { value = true }; }; + template struct is_const { enum { value = true }; }; + + template struct is_pointer { enum { value = false }; }; + template struct is_pointer { enum { value = true }; }; + + template struct is_reference { enum { value = false }; }; + template struct is_reference { enum { value = true }; }; + + template struct is_same { enum { value = false }; }; + template struct is_same { enum { value = true }; }; + + //=== + //add + //=== + + template struct add_const { typedef const T type; }; + template struct add_const { typedef const T type; }; + template struct add_const { typedef const T& type; }; + + template struct add_pointer { typedef T* type; }; + template struct add_pointer { typedef T** type; }; + + template struct add_reference { typedef T& type; }; + template struct add_reference { typedef T& type; }; + + //====== + //remove + //====== + + template struct remove_const { typedef T type; }; + template struct remove_const { typedef T type; }; + template struct remove_const { typedef T type; }; + + template struct remove_extent { typedef T type; }; + template struct remove_extent { typedef T type; }; + template struct remove_extent { typedef T type; }; + + template struct remove_pointer { typedef T type; }; + template struct remove_pointer { typedef T type; }; + + template struct remove_reference { typedef T type; }; + template struct remove_reference { typedef T type; }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/ups.hpp b/mednafen/snes/src/lib/nall/ups.hpp new file mode 100755 index 0000000..f255ecb --- /dev/null +++ b/mednafen/snes/src/lib/nall/ups.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include + +#include +#include +#include +#include + +namespace nall { + class ups { + public: + enum result { + ok, + patch_unreadable, + patch_unwritable, + patch_invalid, + input_invalid, + output_invalid, + patch_crc32_invalid, + input_crc32_invalid, + output_crc32_invalid, + }; + + ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) { + if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable; + + crc32 = ~0; + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + //header + write('U'); + write('P'); + write('S'); + write('1'); + encptr(x_size); + encptr(y_size); + + //body + unsigned max_size = max(x_size, y_size); + unsigned relative = 0; + for(unsigned i = 0; i < max_size;) { + uint8_t x = i < x_size ? x_data[i] : 0x00; + uint8_t y = i < y_size ? y_data[i] : 0x00; + + if(x == y) { + i++; + continue; + } + + encptr(i++ - relative); + write(x ^ y); + + while(true) { + if(i >= max_size) { + write(0x00); + break; + } + + x = i < x_size ? x_data[i] : 0x00; + y = i < y_size ? y_data[i] : 0x00; + i++; + write(x ^ y); + if(x == y) break; + } + + relative = i; + } + + //footer + for(unsigned i = 0; i < 4; i++) write(x_crc32 >> (i << 3)); + for(unsigned i = 0; i < 4; i++) write(y_crc32 >> (i << 3)); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3)); + + fp.close(); + return ok; + } + + ups::result apply(const uint8_t *p_data, unsigned p_size, const uint8_t *x_data, unsigned x_size, uint8_t *&y_data, unsigned &y_size) { + if(p_size < 18) return patch_invalid; + p_buffer = p_data; + + crc32 = ~0; + + //header + if(read() != 'U') return patch_invalid; + if(read() != 'P') return patch_invalid; + if(read() != 'S') return patch_invalid; + if(read() != '1') return patch_invalid; + + unsigned px_size = decptr(); + unsigned py_size = decptr(); + + //mirror + if(x_size != px_size && x_size != py_size) return input_invalid; + y_size = (x_size == px_size) ? py_size : px_size; + y_data = new uint8_t[y_size](); + + for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i]; + for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00; + + //body + unsigned relative = 0; + while(p_buffer < p_data + p_size - 12) { + relative += decptr(); + + while(true) { + uint8_t x = read(); + if(x && relative < y_size) { + uint8_t y = relative < x_size ? x_data[relative] : 0x00; + y_data[relative] = x ^ y; + } + relative++; + if(!x) break; + } + } + + //footer + unsigned px_crc32 = 0, py_crc32 = 0, pp_crc32 = 0; + for(unsigned i = 0; i < 4; i++) px_crc32 |= read() << (i << 3); + for(unsigned i = 0; i < 4; i++) py_crc32 |= read() << (i << 3); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) pp_crc32 |= read() << (i << 3); + + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + if(px_size != py_size) { + if(x_size == px_size && x_crc32 != px_crc32) return input_crc32_invalid; + if(x_size == py_size && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_size == px_size && y_crc32 != px_crc32) return output_crc32_invalid; + if(y_size == py_size && y_crc32 != py_crc32) return output_crc32_invalid; + } else { + if(x_crc32 != px_crc32 && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_crc32 != px_crc32 && y_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 == y_crc32 && px_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 != y_crc32 && px_crc32 == py_crc32) return output_crc32_invalid; + } + + if(p_crc32 != pp_crc32) return patch_crc32_invalid; + return ok; + } + + private: + file fp; + uint32_t crc32; + const uint8_t *p_buffer; + + uint8_t read() { + uint8_t n = *p_buffer++; + crc32 = crc32_adjust(crc32, n); + return n; + } + + void write(uint8_t n) { + fp.write(n); + crc32 = crc32_adjust(crc32, n); + } + + void encptr(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + write(0x80 | x); + break; + } + write(x); + offset--; + } + } + + uint64_t decptr() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/utf8.hpp b/mednafen/snes/src/lib/nall/utf8.hpp new file mode 100755 index 0000000..c66c341 --- /dev/null +++ b/mednafen/snes/src/lib/nall/utf8.hpp @@ -0,0 +1,72 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#undef NOMINMAX +#define NOMINMAX +#include +#undef interface + +namespace nall { + //UTF-8 to UTF-16 + class utf16_t { + public: + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new wchar_t[length + 1](); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t *buffer; + }; + + //UTF-16 to UTF-8 + class utf8_t { + public: + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t *s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0); + buffer = new char[length + 1](); + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } + + ~utf8_t() { + delete[] buffer; + } + + private: + char *buffer; + }; +} + +#endif //if defined(_WIN32) + +#endif diff --git a/mednafen/snes/src/lib/nall/utility.hpp b/mednafen/snes/src/lib/nall/utility.hpp new file mode 100755 index 0000000..fa73f8c --- /dev/null +++ b/mednafen/snes/src/lib/nall/utility.hpp @@ -0,0 +1,36 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +namespace nall { + template + inline void swap(T &x, T &y) { + T temp(x); + x = y; + y = temp; + } + + template + struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + class noncopyable { + protected: + noncopyable() {} + ~noncopyable() {} + + private: + noncopyable(const noncopyable&); + const noncopyable& operator=(const noncopyable&); + }; + + template + inline T* allocate(size_t size, const T &value) { + T *array = new T[size]; + for(size_t i = 0; i < size; i++) array[i] = value; + return array; + } +} + +#endif diff --git a/mednafen/snes/src/lib/nall/varint.hpp b/mednafen/snes/src/lib/nall/varint.hpp new file mode 100755 index 0000000..c17cb89 --- /dev/null +++ b/mednafen/snes/src/lib/nall/varint.hpp @@ -0,0 +1,92 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include +#include + +namespace nall { + template class uint_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + unsigned int, + typename static_if< + sizeof(long) >= bytes, + unsigned long, + typename static_if< + sizeof(long long) >= bytes, + unsigned long long, + void + >::type + >::type + >::type T; + nall_static_assert::value> uint_assert; + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = uclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = uclip(data - 1); return r; } + inline T operator ++() { return data = uclip(data + 1); } + inline T operator --() { return data = uclip(data - 1); } + inline T operator =(const T i) { return data = uclip(i); } + inline T operator |=(const T i) { return data = uclip(data | i); } + inline T operator ^=(const T i) { return data = uclip(data ^ i); } + inline T operator &=(const T i) { return data = uclip(data & i); } + inline T operator<<=(const T i) { return data = uclip(data << i); } + inline T operator>>=(const T i) { return data = uclip(data >> i); } + inline T operator +=(const T i) { return data = uclip(data + i); } + inline T operator -=(const T i) { return data = uclip(data - i); } + inline T operator *=(const T i) { return data = uclip(data * i); } + inline T operator /=(const T i) { return data = uclip(data / i); } + inline T operator %=(const T i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const T i) : data(uclip(i)) {} + }; + + template class int_t { + private: + enum { bytes = (bits + 7) >> 3 }; //minimum number of bytes needed to store value + typedef typename static_if< + sizeof(int) >= bytes, + signed int, + typename static_if< + sizeof(long) >= bytes, + signed long, + typename static_if< + sizeof(long long) >= bytes, + signed long long, + void + >::type + >::type + >::type T; + nall_static_assert::value> int_assert; + T data; + + public: + inline operator T() const { return data; } + inline T operator ++(int) { T r = data; data = sclip(data + 1); return r; } + inline T operator --(int) { T r = data; data = sclip(data - 1); return r; } + inline T operator ++() { return data = sclip(data + 1); } + inline T operator --() { return data = sclip(data - 1); } + inline T operator =(const T i) { return data = sclip(i); } + inline T operator |=(const T i) { return data = sclip(data | i); } + inline T operator ^=(const T i) { return data = sclip(data ^ i); } + inline T operator &=(const T i) { return data = sclip(data & i); } + inline T operator<<=(const T i) { return data = sclip(data << i); } + inline T operator>>=(const T i) { return data = sclip(data >> i); } + inline T operator +=(const T i) { return data = sclip(data + i); } + inline T operator -=(const T i) { return data = sclip(data - i); } + inline T operator *=(const T i) { return data = sclip(data * i); } + inline T operator /=(const T i) { return data = sclip(data / i); } + inline T operator %=(const T i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const T i) : data(sclip(i)) {} + }; +} + +#endif diff --git a/mednafen/snes/src/lib/nall/vector.hpp b/mednafen/snes/src/lib/nall/vector.hpp new file mode 100755 index 0000000..aeff81c --- /dev/null +++ b/mednafen/snes/src/lib/nall/vector.hpp @@ -0,0 +1,162 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include + +namespace nall { + //linear_vector + //memory: O(capacity * 2) + // + //linear_vector uses placement new + manual destructor calls to create a + //contiguous block of memory for all objects. accessing individual elements + //is fast, though resizing the array incurs significant overhead. + //reserve() overhead is reduced from quadratic time to amortized constant time + //by resizing twice as much as requested. + // + //if objects hold memory address references to themselves (introspection), a + //valid copy constructor will be needed to keep pointers valid. + + template class linear_vector : noncopyable { + protected: + T *pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + T *poolcopy = (T*)malloc(newsize * sizeof(T)); + for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]); + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + if(newsize < objectsize) { + //vector is shrinking; destroy excess objects + for(unsigned i = newsize; i < objectsize; i++) pool[i].~T(); + } else if(newsize > objectsize) { + //vector is expanding; allocate new objects + for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T; + } + + objectsize = newsize; + } + + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } + + linear_vector() : pool(0), poolsize(0), objectsize(0) {} + ~linear_vector() { reset(); } + }; + + //pointer_vector + //memory: O(1) + // + //pointer_vector keeps an array of pointers to each vector object. this adds + //significant overhead to individual accesses, but allows for optimal memory + //utilization. + // + //by guaranteeing that the base memory address of each objects never changes, + //this avoids the need for an object to have a valid copy constructor. + + template class pointer_vector : noncopyable { + protected: + T **pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; } + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + pool = (T**)realloc(pool, newsize * sizeof(T*)); + for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + objectsize = newsize; + } + + void add(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + if(!pool[index]) pool[index] = new T; + return *pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; + return *pool[index]; + } + + pointer_vector() : pool(0), poolsize(0), objectsize(0) {} + ~pointer_vector() { reset(); } + }; + + //default vector type + template class vector : public linear_vector {}; +} + +#endif diff --git a/mednafen/snes/src/lib/ruby/audio.hpp b/mednafen/snes/src/lib/ruby/audio.hpp new file mode 100755 index 0000000..aaf43ff --- /dev/null +++ b/mednafen/snes/src/lib/ruby/audio.hpp @@ -0,0 +1,23 @@ +class Audio { +public: + static const char *Volume; + static const char *Resample; + static const char *ResampleRatio; + + static const char *Handle; + static const char *Synchronize; + static const char *Frequency; + static const char *Latency; + + virtual bool cap(const nall::string& name) { return false; } + virtual nall::any get(const nall::string& name) { return false; } + virtual bool set(const nall::string& name, const nall::any& value) { return false; } + + virtual void sample(uint16_t left, uint16_t right) {} + virtual void clear() {} + virtual bool init() { return true; } + virtual void term() {} + + Audio() {} + virtual ~Audio() {} +}; diff --git a/mednafen/snes/src/lib/ruby/audio/alsa.cpp b/mednafen/snes/src/lib/ruby/audio/alsa.cpp new file mode 100755 index 0000000..45b4a8b --- /dev/null +++ b/mednafen/snes/src/lib/ruby/audio/alsa.cpp @@ -0,0 +1,240 @@ +//audio.alsa (2009-11-30) +//authors: BearOso, byuu, Nach, RedDwarf + +#include + +namespace ruby { + +class pAudioALSA { +public: + struct { + snd_pcm_t *handle; + snd_pcm_format_t format; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; + int channels; + const char *name; + } device; + + struct { + uint32_t *data; + unsigned length; + } buffer; + + struct { + bool synchronize; + unsigned frequency; + unsigned latency; + } settings; + + bool cap(const string& name) { + if(name == Audio::Synchronize) return true; + if(name == Audio::Frequency) return true; + if(name == Audio::Latency) return true; + return false; + } + + any get(const string& name) { + if(name == Audio::Synchronize) return settings.synchronize; + if(name == Audio::Frequency) return settings.frequency; + if(name == Audio::Latency) return settings.latency; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Audio::Synchronize) { + if(settings.synchronize != any_cast(value)) { + settings.synchronize = any_cast(value); + if(device.handle) init(); + } + return true; + } + + if(name == Audio::Frequency) { + if(settings.frequency != any_cast(value)) { + settings.frequency = any_cast(value); + if(device.handle) init(); + } + return true; + } + + if(name == Audio::Latency) { + if(settings.latency != any_cast(value)) { + settings.latency = any_cast(value); + if(device.handle) init(); + } + return true; + } + + return false; + } + + void sample(uint16_t left, uint16_t right) { + if(!device.handle) return; + + buffer.data[buffer.length++] = left + (right << 16); + if(buffer.length < device.period_size) return; + + snd_pcm_sframes_t avail; + do { + avail = snd_pcm_avail_update(device.handle); + if(avail < 0) snd_pcm_recover(device.handle, avail, 1); + if(avail < buffer.length) { + if(settings.synchronize == false) { + buffer.length = 0; + return; + } + int error = snd_pcm_wait(device.handle, -1); + if(error < 0) snd_pcm_recover(device.handle, error, 1); + } + } while(avail < buffer.length); + + //below code has issues with PulseAudio sound server + #if 0 + if(settings.synchronize == false) { + snd_pcm_sframes_t avail = snd_pcm_avail_update(device.handle); + if(avail < device.period_size) { + buffer.length = 0; + return; + } + } + #endif + + uint32_t *buffer_ptr = buffer.data; + int i = 4; + + while((buffer.length > 0) && i--) { + snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer_ptr, buffer.length); + if(written < 0) { + //no samples written + snd_pcm_recover(device.handle, written, 1); + } else if(written <= buffer.length) { + buffer.length -= written; + buffer_ptr += written; + } + } + + if(i < 0) { + if(buffer.data == buffer_ptr) { + buffer.length--; + buffer_ptr++; + } + memmove(buffer.data, buffer_ptr, buffer.length * sizeof(uint32_t)); + } + } + + void clear() { + } + + bool init() { + term(); + + if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) { + term(); + return false; + } + + //below code will not work with 24khz frequency rate (ALSA library bug) + #if 0 + if(snd_pcm_set_params(device.handle, device.format, SND_PCM_ACCESS_RW_INTERLEAVED, + device.channels, settings.frequency, 1, settings.latency * 1000) < 0) { + //failed to set device parameters + term(); + return false; + } + + if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) { + device.period_size = settings.latency * 1000 * 1e-6 * settings.frequency / 4; + } + #endif + + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + unsigned rate = settings.frequency; + unsigned buffer_time = settings.latency * 1000; + unsigned period_time = settings.latency * 1000 / 4; + + snd_pcm_hw_params_alloca(&hwparams); + if(snd_pcm_hw_params_any(device.handle, hwparams) < 0) { + term(); + return false; + } + + if(snd_pcm_hw_params_set_access(device.handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 + || snd_pcm_hw_params_set_format(device.handle, hwparams, device.format) < 0 + || snd_pcm_hw_params_set_channels(device.handle, hwparams, device.channels) < 0 + || snd_pcm_hw_params_set_rate_near(device.handle, hwparams, &rate, 0) < 0 + || snd_pcm_hw_params_set_period_time_near(device.handle, hwparams, &period_time, 0) < 0 + || snd_pcm_hw_params_set_buffer_time_near(device.handle, hwparams, &buffer_time, 0) < 0 + ) { + term(); + return false; + } + + if(snd_pcm_hw_params(device.handle, hwparams) < 0) { + term(); + return false; + } + + if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) { + term(); + return false; + } + + snd_pcm_sw_params_alloca(&swparams); + if(snd_pcm_sw_params_current(device.handle, swparams) < 0) { + term(); + return false; + } + + if(snd_pcm_sw_params_set_start_threshold(device.handle, swparams, + (device.buffer_size / device.period_size) * device.period_size) < 0 + ) { + term(); + return false; + } + + if(snd_pcm_sw_params(device.handle, swparams) < 0) { + term(); + return false; + } + + buffer.data = new uint32_t[device.period_size]; + return true; + } + + void term() { + if(device.handle) { + snd_pcm_drain(device.handle); + snd_pcm_close(device.handle); + device.handle = 0; + } + + if(buffer.data) { + delete[] buffer.data; + buffer.data = 0; + } + } + + pAudioALSA() { + device.handle = 0; + device.format = SND_PCM_FORMAT_S16_LE; + device.channels = 2; + device.name = "default"; + + buffer.data = 0; + buffer.length = 0; + + settings.synchronize = false; + settings.frequency = 22050; + settings.latency = 60; + } + + ~pAudioALSA() { + term(); + } +}; + +DeclareAudio(ALSA) + +}; diff --git a/mednafen/snes/src/lib/ruby/audio/ao.cpp b/mednafen/snes/src/lib/ruby/audio/ao.cpp new file mode 100755 index 0000000..0cfe670 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/audio/ao.cpp @@ -0,0 +1,94 @@ +/* + audio.ao (2008-06-01) + authors: Nach, RedDwarf +*/ + +#include + +namespace ruby { + +class pAudioAO { +public: + int driver_id; + ao_sample_format driver_format; + ao_device *audio_device; + + struct { + unsigned frequency; + } settings; + + bool cap(const string& name) { + if(name == Audio::Frequency) return true; + return false; + } + + any get(const string& name) { + if(name == Audio::Frequency) return settings.frequency; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + if(audio_device) init(); + return true; + } + + return false; + } + + void sample(uint16_t l_sample, uint16_t r_sample) { + uint32_t samp = (l_sample << 0) + (r_sample << 16); + ao_play(audio_device, (char*)&samp, 4); //This may need to be byte swapped for Big Endian + } + + void clear() { + } + + bool init() { + term(); + + driver_id = ao_default_driver_id(); //ao_driver_id((const char*)driver) + if(driver_id < 0) return false; + + driver_format.bits = 16; + driver_format.channels = 2; + driver_format.rate = settings.frequency; + driver_format.byte_format = AO_FMT_LITTLE; + + ao_option *options = 0; + ao_info *di = ao_driver_info(driver_id); + if(!di) return false; + if(!strcmp(di->short_name, "alsa")) { + ao_append_option(&options, "buffer_time", "100000"); //100ms latency (default was 500ms) + } + + audio_device = ao_open_live(driver_id, &driver_format, options); + if(!audio_device) return false; + + return true; + } + + void term() { + if(audio_device) { + ao_close(audio_device); + audio_device = 0; + } + } + + pAudioAO() { + audio_device = 0; + ao_initialize(); + + settings.frequency = 22050; + } + + ~pAudioAO() { + term(); + //ao_shutdown(); //FIXME: this is causing a segfault for some reason when called ... + } +}; + +DeclareAudio(AO) + +}; diff --git a/mednafen/snes/src/lib/ruby/audio/directsound.cpp b/mednafen/snes/src/lib/ruby/audio/directsound.cpp new file mode 100755 index 0000000..17d09e2 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/audio/directsound.cpp @@ -0,0 +1,212 @@ +/* + audio.directsound (2007-12-26) + author: byuu +*/ + +#include + +namespace ruby { + +class pAudioDS { +public: + LPDIRECTSOUND ds; + LPDIRECTSOUNDBUFFER dsb_p, dsb_b; + DSBUFFERDESC dsbd; + WAVEFORMATEX wfx; + + struct { + unsigned rings; + unsigned latency; + + uint32_t *buffer; + unsigned bufferoffset; + + unsigned readring; + unsigned writering; + int distance; + } device; + + struct { + HWND handle; + bool synchronize; + unsigned frequency; + unsigned latency; + } settings; + + bool cap(const string& name) { + if(name == Audio::Handle) return true; + if(name == Audio::Synchronize) return true; + if(name == Audio::Frequency) return true; + if(name == Audio::Latency) return true; + return false; + } + + any get(const string& name) { + if(name == Audio::Handle) return (uintptr_t)settings.handle; + if(name == Audio::Synchronize) return settings.synchronize; + if(name == Audio::Frequency) return settings.frequency; + if(name == Audio::Latency) return settings.latency; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Audio::Handle) { + settings.handle = (HWND)any_cast(value); + return true; + } + + if(name == Audio::Synchronize) { + settings.synchronize = any_cast(value); + if(ds) clear(); + return true; + } + + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + if(ds) init(); + return true; + } + + if(name == Audio::Latency) { + settings.latency = any_cast(value); + if(ds) init(); + return true; + } + + return false; + } + + void sample(uint16_t left, uint16_t right) { + device.buffer[device.bufferoffset++] = left + (right << 16); + if(device.bufferoffset < device.latency) return; + device.bufferoffset = 0; + + DWORD pos, size; + void *output; + + if(settings.synchronize == true) { + //wait until playback buffer has an empty ring to write new audio data to + while(device.distance >= device.rings - 1) { + dsb_b->GetCurrentPosition(&pos, 0); + unsigned activering = pos / (device.latency * 4); + if(activering == device.readring) { + if(settings.synchronize == false) Sleep(1); + continue; + } + + //subtract number of played rings from ring distance counter + device.distance -= (device.rings + activering - device.readring) % device.rings; + device.readring = activering; + + if(device.distance < 2) { + //buffer underflow; set max distance to recover quickly + device.distance = device.rings - 1; + device.writering = (device.rings + device.readring - 1) % device.rings; + break; + } + } + } + + device.writering = (device.writering + 1) % device.rings; + device.distance = (device.distance + 1) % device.rings; + + if(dsb_b->Lock(device.writering * device.latency * 4, device.latency * 4, &output, &size, 0, 0, 0) == DS_OK) { + memcpy(output, device.buffer, device.latency * 4); + dsb_b->Unlock(output, size, 0, 0); + } + } + + void clear() { + device.readring = 0; + device.writering = device.rings - 1; + device.distance = device.rings - 1; + + device.bufferoffset = 0; + if(device.buffer) memset(device.buffer, 0, device.latency * device.rings * 4); + + if(!dsb_b) return; + dsb_b->Stop(); + dsb_b->SetCurrentPosition(0); + + DWORD size; + void *output; + dsb_b->Lock(0, device.latency * device.rings * 4, &output, &size, 0, 0, 0); + memset(output, 0, size); + dsb_b->Unlock(output, size, 0, 0); + + dsb_b->Play(0, 0, DSBPLAY_LOOPING); + } + + bool init() { + term(); + + device.rings = 8; + device.latency = settings.frequency * settings.latency / device.rings / 1000.0 + 0.5; + device.buffer = new uint32_t[device.latency * device.rings]; + device.bufferoffset = 0; + + DirectSoundCreate(0, &ds, 0); + ds->SetCooperativeLevel((HWND)settings.handle, DSSCL_PRIORITY); + + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = 0; + ds->CreateSoundBuffer(&dsbd, &dsb_p, 0); + + memset(&wfx, 0, sizeof(wfx)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = settings.frequency; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + dsb_p->SetFormat(&wfx); + + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE; + dsbd.dwBufferBytes = device.latency * device.rings * sizeof(uint32_t); + dsbd.guid3DAlgorithm = GUID_NULL; + dsbd.lpwfxFormat = &wfx; + ds->CreateSoundBuffer(&dsbd, &dsb_b, 0); + dsb_b->SetFrequency(settings.frequency); + dsb_b->SetCurrentPosition(0); + + clear(); + return true; + } + + void term() { + if(device.buffer) { + delete[] device.buffer; + device.buffer = 0; + } + + if(dsb_b) { dsb_b->Stop(); dsb_b->Release(); dsb_b = 0; } + if(dsb_p) { dsb_p->Stop(); dsb_p->Release(); dsb_p = 0; } + if(ds) { ds->Release(); ds = 0; } + } + + pAudioDS() { + ds = 0; + dsb_p = 0; + dsb_b = 0; + + device.buffer = 0; + device.bufferoffset = 0; + device.readring = 0; + device.writering = 0; + device.distance = 0; + + settings.handle = GetDesktopWindow(); + settings.synchronize = false; + settings.frequency = 22050; + settings.latency = 120; + } +}; + +DeclareAudio(DS) + +}; diff --git a/mednafen/snes/src/lib/ruby/audio/openal.cpp b/mednafen/snes/src/lib/ruby/audio/openal.cpp new file mode 100755 index 0000000..a5be2aa --- /dev/null +++ b/mednafen/snes/src/lib/ruby/audio/openal.cpp @@ -0,0 +1,210 @@ +/* + audio.openal (2007-12-26) + author: Nach + contributors: byuu, wertigon, _willow_ +*/ + +#if defined(PLATFORM_OSX) + #include + #include +#else + #include + #include +#endif + +namespace ruby { + +class pAudioOpenAL { +public: + struct { + ALCdevice *handle; + ALCcontext *context; + ALuint source; + ALenum format; + unsigned latency; + unsigned queue_length; + } device; + + struct { + uint32_t *data; + unsigned length; + unsigned size; + } buffer; + + struct { + bool synchronize; + unsigned frequency; + unsigned latency; + } settings; + + bool cap(const string& name) { + if(name == Audio::Synchronize) return true; + if(name == Audio::Frequency) return true; + if(name == Audio::Latency) return true; + return false; + } + + any get(const string& name) { + if(name == Audio::Synchronize) return settings.synchronize; + if(name == Audio::Frequency) return settings.frequency; + if(name == Audio::Latency) return settings.latency; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Audio::Synchronize) { + settings.synchronize = any_cast(value); + return true; + } + + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + return true; + } + + if(name == Audio::Latency) { + if(settings.latency != any_cast(value)) { + settings.latency = any_cast(value); + update_latency(); + } + return true; + } + + return false; + } + + void sample(uint16_t sl, uint16_t sr) { + buffer.data[buffer.length++] = sl + (sr << 16); + if(buffer.length < buffer.size) return; + + ALuint albuffer = 0; + int processed = 0; + while(true) { + alGetSourcei(device.source, AL_BUFFERS_PROCESSED, &processed); + while(processed--) { + alSourceUnqueueBuffers(device.source, 1, &albuffer); + alDeleteBuffers(1, &albuffer); + device.queue_length--; + } + //wait for buffer playback to catch up to sample generation if not synchronizing + if(settings.synchronize == false || device.queue_length < 3) break; + } + + if(device.queue_length < 3) { + alGenBuffers(1, &albuffer); + alBufferData(albuffer, device.format, buffer.data, buffer.size * 4, settings.frequency); + alSourceQueueBuffers(device.source, 1, &albuffer); + device.queue_length++; + } + + ALint playing; + alGetSourcei(device.source, AL_SOURCE_STATE, &playing); + if(playing != AL_PLAYING) alSourcePlay(device.source); + buffer.length = 0; + } + + void clear() { + } + + void update_latency() { + if(buffer.data) delete[] buffer.data; + buffer.size = settings.frequency * settings.latency / 1000.0 + 0.5; + buffer.data = new uint32_t[buffer.size]; + } + + bool init() { + update_latency(); + device.queue_length = 0; + + bool success = false; + if(device.handle = alcOpenDevice(NULL)) { + if(device.context = alcCreateContext(device.handle, NULL)) { + alcMakeContextCurrent(device.context); + alGenSources(1, &device.source); + + //alSourcef (device.source, AL_PITCH, 1.0); + //alSourcef (device.source, AL_GAIN, 1.0); + //alSource3f(device.source, AL_POSITION, 0.0, 0.0, 0.0); + //alSource3f(device.source, AL_VELOCITY, 0.0, 0.0, 0.0); + //alSource3f(device.source, AL_DIRECTION, 0.0, 0.0, 0.0); + //alSourcef (device.source, AL_ROLLOFF_FACTOR, 0.0); + //alSourcei (device.source, AL_SOURCE_RELATIVE, AL_TRUE); + + alListener3f(AL_POSITION, 0.0, 0.0, 0.0); + alListener3f(AL_VELOCITY, 0.0, 0.0, 0.0); + ALfloat listener_orientation[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + alListenerfv(AL_ORIENTATION, listener_orientation); + + success = true; + } + } + + if(success == false) { + term(); + return false; + } + + return true; + } + + void term() { + if(alIsSource(device.source) == AL_TRUE) { + int playing = 0; + alGetSourcei(device.source, AL_SOURCE_STATE, &playing); + if(playing == AL_PLAYING) { + alSourceStop(device.source); + int queued = 0; + alGetSourcei(device.source, AL_BUFFERS_QUEUED, &queued); + while(queued--) { + ALuint albuffer = 0; + alSourceUnqueueBuffers(device.source, 1, &albuffer); + alDeleteBuffers(1, &albuffer); + device.queue_length--; + } + } + + alDeleteSources(1, &device.source); + device.source = 0; + } + + if(device.context) { + alcMakeContextCurrent(NULL); + alcDestroyContext(device.context); + device.context = 0; + } + + if(device.handle) { + alcCloseDevice(device.handle); + device.handle = 0; + } + + if(buffer.data) { + delete[] buffer.data; + buffer.data = 0; + } + } + + pAudioOpenAL() { + device.source = 0; + device.handle = 0; + device.context = 0; + device.format = AL_FORMAT_STEREO16; + device.queue_length = 0; + + buffer.data = 0; + buffer.length = 0; + buffer.size = 0; + + settings.synchronize = true; + settings.frequency = 22050; + settings.latency = 40; + } + + ~pAudioOpenAL() { + term(); + } +}; + +DeclareAudio(OpenAL) + +}; diff --git a/mednafen/snes/src/lib/ruby/audio/oss.cpp b/mednafen/snes/src/lib/ruby/audio/oss.cpp new file mode 100755 index 0000000..dcb8115 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/audio/oss.cpp @@ -0,0 +1,113 @@ +/* + audio.oss (2007-12-26) + author: Nach +*/ + +#include +#include +#include +#include + +//OSS4 soundcard.h includes below SNDCTL defines, but OSS3 does not +//However, OSS4 soundcard.h does not reside in +//Therefore, attempt to manually define SNDCTL values if using OSS3 header +//Note that if the defines below fail to work on any specific platform, one can point soundcard.h +//above to the correct location for OSS4 (usually /usr/lib/oss/include/sys/soundcard.h) +//Failing that, one can disable OSS4 ioctl calls inside init() and remove the below defines + +#ifndef SNDCTL_DSP_COOKEDMODE + #define SNDCTL_DSP_COOKEDMODE _IOW('P', 30, int) +#endif + +#ifndef SNDCTL_DSP_POLICY + #define SNDCTL_DSP_POLICY _IOW('P', 45, int) +#endif + +namespace ruby { + +class pAudioOSS { +public: + struct { + int fd; + int format; + int channels; + const char *name; + } device; + + struct { + unsigned frequency; + } settings; + + bool cap(const string& name) { + if(name == Audio::Frequency) return true; + return false; + } + + any get(const string& name) { + if(name == Audio::Frequency) return settings.frequency; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + if(device.fd > 0) init(); + return true; + } + + return false; + } + + void sample(uint16_t sl, uint16_t sr) { + uint32_t sample = sl + (sr << 16); + unsigned unused = write(device.fd, &sample, 4); + } + + void clear() { + } + + bool init() { + term(); + + device.fd = open(device.name, O_WRONLY, O_NONBLOCK); + if(device.fd < 0) return false; + + #if 1 //SOUND_VERSION >= 0x040000 + //attempt to enable OSS4-specific features regardless of version + //OSS3 ioctl calls will silently fail, but sound will still work + int cooked = 1, policy = 4; //policy should be 0 - 10, lower = less latency, more CPU usage + ioctl(device.fd, SNDCTL_DSP_COOKEDMODE, &cooked); + ioctl(device.fd, SNDCTL_DSP_POLICY, &policy); + #endif + int freq = settings.frequency; + ioctl(device.fd, SNDCTL_DSP_CHANNELS, &device.channels); + ioctl(device.fd, SNDCTL_DSP_SETFMT, &device.format); + ioctl(device.fd, SNDCTL_DSP_SPEED, &freq); + + return true; + } + + void term() { + if(device.fd > 0) { + close(device.fd); + device.fd = -1; + } + } + + pAudioOSS() { + device.fd = -1; + device.format = AFMT_S16_LE; + device.channels = 2; + device.name = "/dev/dsp"; + + settings.frequency = 22050; + } + + ~pAudioOSS() { + term(); + } +}; + +DeclareAudio(OSS) + +}; diff --git a/mednafen/snes/src/lib/ruby/audio/pulseaudio.cpp b/mednafen/snes/src/lib/ruby/audio/pulseaudio.cpp new file mode 100755 index 0000000..bdd5f68 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/audio/pulseaudio.cpp @@ -0,0 +1,177 @@ +//audio.pulseaudio (2010-01-05) +//author: RedDwarf + +#include + +namespace ruby { + +class pAudioPulseAudio { +public: + struct { + pa_mainloop *mainloop; + pa_context *context; + pa_stream *stream; + pa_sample_spec spec; + pa_buffer_attr buffer_attr; + bool first; + } device; + + struct { + uint32_t *data; + size_t size; + unsigned offset; + } buffer; + + struct { + bool synchronize; + unsigned frequency; + unsigned latency; + } settings; + + bool cap(const string& name) { + if(name == Audio::Synchronize) return true; + if(name == Audio::Frequency) return true; + if(name == Audio::Latency) return true; + } + + any get(const string& name) { + if(name == Audio::Synchronize) return settings.synchronize; + if(name == Audio::Frequency) return settings.frequency; + if(name == Audio::Latency) return settings.latency; + } + + bool set(const string& name, const any& value) { + if(name == Audio::Synchronize) { + settings.synchronize = any_cast(value); + return true; + } + + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + if(device.stream) { + pa_operation_unref(pa_stream_update_sample_rate(device.stream, settings.frequency, NULL, NULL)); + } + return true; + } + + if(name == Audio::Latency) { + settings.latency = any_cast(value); + if(device.stream) { + device.buffer_attr.tlength = pa_usec_to_bytes(settings.latency * PA_USEC_PER_MSEC, &device.spec); + pa_stream_set_buffer_attr(device.stream, &device.buffer_attr, NULL, NULL); + } + return true; + } + } + + void sample(uint16_t left, uint16_t right) { + pa_stream_begin_write(device.stream, (void**)&buffer.data, &buffer.size); + buffer.data[buffer.offset++] = left + (right << 16); + if((buffer.offset + 1) * pa_frame_size(&device.spec) <= buffer.size) return; + + while(true) { + if(device.first) { + device.first = false; + pa_mainloop_iterate(device.mainloop, 0, NULL); + } else { + pa_mainloop_iterate(device.mainloop, 1, NULL); + } + unsigned length = pa_stream_writable_size(device.stream); + if(length >= buffer.offset * pa_frame_size(&device.spec)) break; + if(settings.synchronize == false) { + buffer.offset = 0; + return; + } + } + + pa_stream_write(device.stream, (const void*)buffer.data, buffer.offset * pa_frame_size(&device.spec), NULL, 0LL, PA_SEEK_RELATIVE); + buffer.data = 0; + buffer.offset = 0; + } + + void clear() { + } + + bool init() { + device.mainloop = pa_mainloop_new(); + + device.context = pa_context_new(pa_mainloop_get_api(device.mainloop), "ruby::pulseaudio"); + pa_context_connect(device.context, NULL, PA_CONTEXT_NOFLAGS, NULL); + + pa_context_state_t cstate; + do { + pa_mainloop_iterate(device.mainloop, 1, NULL); + cstate = pa_context_get_state(device.context); + if(!PA_CONTEXT_IS_GOOD(cstate)) return false; + } while(cstate != PA_CONTEXT_READY); + + device.spec.format = PA_SAMPLE_S16LE; + device.spec.channels = 2; + device.spec.rate = settings.frequency; + device.stream = pa_stream_new(device.context, "audio", &device.spec, NULL); + + device.buffer_attr.maxlength = -1; + device.buffer_attr.tlength = pa_usec_to_bytes(settings.latency * PA_USEC_PER_MSEC, &device.spec); + device.buffer_attr.prebuf = -1; + device.buffer_attr.minreq = -1; + device.buffer_attr.fragsize = -1; + + pa_stream_flags_t flags = (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY | PA_STREAM_VARIABLE_RATE); + pa_stream_connect_playback(device.stream, NULL, &device.buffer_attr, flags, NULL, NULL); + + pa_stream_state_t sstate; + do { + pa_mainloop_iterate(device.mainloop, 1, NULL); + sstate = pa_stream_get_state(device.stream); + if(!PA_STREAM_IS_GOOD(sstate)) return false; + } while(sstate != PA_STREAM_READY); + + buffer.size = 960; + buffer.offset = 0; + device.first = true; + + return true; + } + + void term() { + if(buffer.data) { + pa_stream_cancel_write(device.stream); + buffer.data = 0; + } + + if(device.stream) { + pa_stream_disconnect(device.stream); + pa_stream_unref(device.stream); + device.stream = 0; + } + + if(device.context) { + pa_context_disconnect(device.context); + pa_context_unref(device.context); + device.context = 0; + } + + if(device.mainloop) { + pa_mainloop_free(device.mainloop); + device.mainloop = 0; + } + } + + pAudioPulseAudio() { + device.mainloop = 0; + device.context = 0; + device.stream = 0; + buffer.data = 0; + settings.synchronize = false; + settings.frequency = 22050; + settings.latency = 60; + } + + ~pAudioPulseAudio() { + term(); + } +}; + +DeclareAudio(PulseAudio) + +} diff --git a/mednafen/snes/src/lib/ruby/audio/pulseaudiosimple.cpp b/mednafen/snes/src/lib/ruby/audio/pulseaudiosimple.cpp new file mode 100755 index 0000000..cdd6e43 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/audio/pulseaudiosimple.cpp @@ -0,0 +1,115 @@ +//audio.pulseaudiosimple (2010-01-05) +//author: byuu + +#include +#include + +namespace ruby { + +class pAudioPulseAudioSimple { +public: + struct { + pa_simple *handle; + pa_sample_spec spec; + } device; + + struct { + uint32_t *data; + unsigned offset; + } buffer; + + struct { + unsigned frequency; + } settings; + + bool cap(const string& name) { + if(name == Audio::Frequency) return true; + return false; + } + + any get(const string& name) { + if(name == Audio::Frequency) return settings.frequency; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Audio::Frequency) { + settings.frequency = any_cast(value); + if(device.handle) init(); + return true; + } + + return false; + } + + void sample(uint16_t left, uint16_t right) { + if(!device.handle) return; + + buffer.data[buffer.offset++] = left + (right << 16); + if(buffer.offset >= 64) { + int error; + pa_simple_write(device.handle, (const void*)buffer.data, buffer.offset * sizeof(uint32_t), &error); + buffer.offset = 0; + } + } + + void clear() { + } + + bool init() { + term(); + + device.spec.format = PA_SAMPLE_S16LE; + device.spec.channels = 2; + device.spec.rate = settings.frequency; + + int error = 0; + device.handle = pa_simple_new( + 0, //default server + "ruby::pulseaudiosimple", //application name + PA_STREAM_PLAYBACK, //direction + 0, //default device + "audio", //stream description + &device.spec, //sample format + 0, //default channel map + 0, //default buffering attributes + &error //error code + ); + if(!device.handle) { + fprintf(stderr, "ruby::pulseaudiosimple failed to initialize - %s\n", pa_strerror(error)); + return false; + } + + buffer.data = new uint32_t[64]; + buffer.offset = 0; + return true; + } + + void term() { + if(device.handle) { + int error; + pa_simple_flush(device.handle, &error); + pa_simple_free(device.handle); + device.handle = 0; + } + + if(buffer.data) { + delete[] buffer.data; + buffer.data = 0; + } + } + + pAudioPulseAudioSimple() { + device.handle = 0; + buffer.data = 0; + settings.frequency = 22050; + } + + ~pAudioPulseAudioSimple() { + term(); + } +}; + +DeclareAudio(PulseAudioSimple) + +}; diff --git a/mednafen/snes/src/lib/ruby/input.hpp b/mednafen/snes/src/lib/ruby/input.hpp new file mode 100755 index 0000000..5334c4d --- /dev/null +++ b/mednafen/snes/src/lib/ruby/input.hpp @@ -0,0 +1,22 @@ +class Input { +public: + static const char *Handle; + static const char *KeyboardSupport; + static const char *MouseSupport; + static const char *JoypadSupport; + + virtual bool cap(const nall::string& name) { return false; } + virtual nall::any get(const nall::string& name) { return false; } + virtual bool set(const nall::string& name, const nall::any& value) { return false; } + + virtual bool acquire() { return false; } + virtual bool unacquire() { return false; } + virtual bool acquired() { return false; } + + virtual bool poll(int16_t *table) { return false; } + virtual bool init() { return true; } + virtual void term() {} + + Input() {} + virtual ~Input() {} +}; diff --git a/mednafen/snes/src/lib/ruby/input/carbon.cpp b/mednafen/snes/src/lib/ruby/input/carbon.cpp new file mode 100755 index 0000000..c5191f4 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/input/carbon.cpp @@ -0,0 +1,157 @@ +namespace ruby { + +class pInputCarbon { +public: + bool cap(const string& name) { + return false; + } + + any get(const string& name) { + return false; + } + + bool set(const string& name, const any& value) { + return false; + } + + bool acquire() { return false; } + bool unacquire() { return false; } + bool acquired() { return false; } + + bool poll(int16_t *table) { + memset(table, 0, Scancode::Limit * sizeof(int16_t)); + + KeyMap keys; + GetKeys(keys); + uint8_t *keymap = (uint8_t*)keys; + + #define map(id, name) table[keyboard(0)[name]] = (bool)(keymap[id >> 3] & (1 << (id & 7))) + map(0x35, Keyboard::Escape); + + map(0x7a, Keyboard::F1); + map(0x78, Keyboard::F2); + map(0x63, Keyboard::F3); + map(0x76, Keyboard::F4); + map(0x60, Keyboard::F5); + map(0x61, Keyboard::F6); + map(0x62, Keyboard::F7); + map(0x64, Keyboard::F8); + map(0x65, Keyboard::F9); + map(0x6d, Keyboard::F10); + map(0x67, Keyboard::F11); + //map(0x??, Keyboard::F12); + + map(0x69, Keyboard::PrintScreen); + //map(0x??, Keyboard::ScrollLock); + map(0x71, Keyboard::Pause); + + map(0x32, Keyboard::Tilde); + map(0x12, Keyboard::Num1); + map(0x13, Keyboard::Num2); + map(0x14, Keyboard::Num3); + map(0x15, Keyboard::Num4); + map(0x17, Keyboard::Num5); + map(0x16, Keyboard::Num6); + map(0x1a, Keyboard::Num7); + map(0x1c, Keyboard::Num8); + map(0x19, Keyboard::Num9); + map(0x1d, Keyboard::Num0); + + map(0x1b, Keyboard::Dash); + map(0x18, Keyboard::Equal); + map(0x33, Keyboard::Backspace); + + map(0x72, Keyboard::Insert); + map(0x75, Keyboard::Delete); + map(0x73, Keyboard::Home); + map(0x77, Keyboard::End); + map(0x74, Keyboard::PageUp); + map(0x79, Keyboard::PageDown); + + map(0x00, Keyboard::A); + map(0x0b, Keyboard::B); + map(0x08, Keyboard::C); + map(0x02, Keyboard::D); + map(0x0e, Keyboard::E); + map(0x03, Keyboard::F); + map(0x05, Keyboard::G); + map(0x04, Keyboard::H); + map(0x22, Keyboard::I); + map(0x26, Keyboard::J); + map(0x28, Keyboard::K); + map(0x25, Keyboard::L); + map(0x2e, Keyboard::M); + map(0x2d, Keyboard::N); + map(0x1f, Keyboard::O); + map(0x23, Keyboard::P); + map(0x0c, Keyboard::Q); + map(0x0f, Keyboard::R); + map(0x01, Keyboard::S); + map(0x11, Keyboard::T); + map(0x20, Keyboard::U); + map(0x09, Keyboard::V); + map(0x0d, Keyboard::W); + map(0x07, Keyboard::X); + map(0x10, Keyboard::Y); + map(0x06, Keyboard::Z); + + map(0x21, Keyboard::LeftBracket); + map(0x1e, Keyboard::RightBracket); + map(0x2a, Keyboard::Backslash); + map(0x29, Keyboard::Semicolon); + map(0x27, Keyboard::Apostrophe); + map(0x2b, Keyboard::Comma); + map(0x2f, Keyboard::Period); + map(0x2c, Keyboard::Slash); + + map(0x53, Keyboard::Keypad1); + map(0x54, Keyboard::Keypad2); + map(0x55, Keyboard::Keypad3); + map(0x56, Keyboard::Keypad4); + map(0x57, Keyboard::Keypad5); + map(0x58, Keyboard::Keypad6); + map(0x59, Keyboard::Keypad7); + map(0x5b, Keyboard::Keypad8); + map(0x5c, Keyboard::Keypad9); + map(0x52, Keyboard::Keypad0); + + //map(0x??, Keyboard::Point); + map(0x4c, Keyboard::Enter); + map(0x45, Keyboard::Add); + map(0x4e, Keyboard::Subtract); + map(0x43, Keyboard::Multiply); + map(0x4b, Keyboard::Divide); + + map(0x47, Keyboard::NumLock); + //map(0x39, Keyboard::CapsLock); + + map(0x7e, Keyboard::Up); + map(0x7d, Keyboard::Down); + map(0x7b, Keyboard::Left); + map(0x7c, Keyboard::Right); + + map(0x30, Keyboard::Tab); + map(0x24, Keyboard::Return); + map(0x31, Keyboard::Spacebar); + //map(0x??, Keyboard::Menu); + + map(0x38, Keyboard::Shift); + map(0x3b, Keyboard::Control); + map(0x3a, Keyboard::Alt); + map(0x37, Keyboard::Super); + #undef map + + return true; + } + + bool init() { + return true; + } + + void term() { + } +}; + +DeclareInput(Carbon) + +}; diff --git a/mednafen/snes/src/lib/ruby/input/directinput.cpp b/mednafen/snes/src/lib/ruby/input/directinput.cpp new file mode 100755 index 0000000..cb0da3c --- /dev/null +++ b/mednafen/snes/src/lib/ruby/input/directinput.cpp @@ -0,0 +1,387 @@ +#define DIRECTINPUT_VERSION 0x0800 +#include + +namespace ruby { + +static BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*); +static BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*); + +using namespace nall; + +class pInputDI { +public: + struct { + LPDIRECTINPUT8 context; + LPDIRECTINPUTDEVICE8 keyboard; + LPDIRECTINPUTDEVICE8 mouse; + LPDIRECTINPUTDEVICE8 gamepad[Joypad::Count]; + bool mouseacquired; + } device; + + struct { + HWND handle; + } settings; + + bool cap(const string& name) { + if(name == Input::Handle) return true; + if(name == Input::KeyboardSupport) return true; + if(name == Input::MouseSupport) return true; + if(name == Input::JoypadSupport) return true; + return false; + } + + any get(const string& name) { + if(name == Input::Handle) return (uintptr_t)settings.handle; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Input::Handle) { + settings.handle = (HWND)any_cast(value); + return true; + } + + return false; + } + + bool poll(int16_t *table) { + memset(table, 0, Scancode::Limit * sizeof(int16_t)); + + //======== + //Keyboard + //======== + + if(device.keyboard) { + uint8_t state[256]; + if(FAILED(device.keyboard->GetDeviceState(sizeof state, state))) { + device.keyboard->Acquire(); + if(FAILED(device.keyboard->GetDeviceState(sizeof state, state))) { + memset(state, 0, sizeof state); + } + } + + #define key(id) table[keyboard(0)[id]] + + key(Keyboard::Escape) = (bool)(state[0x01] & 0x80); + key(Keyboard::F1 ) = (bool)(state[0x3b] & 0x80); + key(Keyboard::F2 ) = (bool)(state[0x3c] & 0x80); + key(Keyboard::F3 ) = (bool)(state[0x3d] & 0x80); + key(Keyboard::F4 ) = (bool)(state[0x3e] & 0x80); + key(Keyboard::F5 ) = (bool)(state[0x3f] & 0x80); + key(Keyboard::F6 ) = (bool)(state[0x40] & 0x80); + key(Keyboard::F7 ) = (bool)(state[0x41] & 0x80); + key(Keyboard::F8 ) = (bool)(state[0x42] & 0x80); + key(Keyboard::F9 ) = (bool)(state[0x43] & 0x80); + key(Keyboard::F10 ) = (bool)(state[0x44] & 0x80); + key(Keyboard::F11 ) = (bool)(state[0x57] & 0x80); + key(Keyboard::F12 ) = (bool)(state[0x58] & 0x80); + + key(Keyboard::PrintScreen) = (bool)(state[0xb7] & 0x80); + key(Keyboard::ScrollLock ) = (bool)(state[0x46] & 0x80); + key(Keyboard::Pause ) = (bool)(state[0xc5] & 0x80); + key(Keyboard::Tilde ) = (bool)(state[0x29] & 0x80); + + key(Keyboard::Num1) = (bool)(state[0x02] & 0x80); + key(Keyboard::Num2) = (bool)(state[0x03] & 0x80); + key(Keyboard::Num3) = (bool)(state[0x04] & 0x80); + key(Keyboard::Num4) = (bool)(state[0x05] & 0x80); + key(Keyboard::Num5) = (bool)(state[0x06] & 0x80); + key(Keyboard::Num6) = (bool)(state[0x07] & 0x80); + key(Keyboard::Num7) = (bool)(state[0x08] & 0x80); + key(Keyboard::Num8) = (bool)(state[0x09] & 0x80); + key(Keyboard::Num9) = (bool)(state[0x0a] & 0x80); + key(Keyboard::Num0) = (bool)(state[0x0b] & 0x80); + + key(Keyboard::Dash ) = (bool)(state[0x0c] & 0x80); + key(Keyboard::Equal ) = (bool)(state[0x0d] & 0x80); + key(Keyboard::Backspace) = (bool)(state[0x0e] & 0x80); + + key(Keyboard::Insert ) = (bool)(state[0xd2] & 0x80); + key(Keyboard::Delete ) = (bool)(state[0xd3] & 0x80); + key(Keyboard::Home ) = (bool)(state[0xc7] & 0x80); + key(Keyboard::End ) = (bool)(state[0xcf] & 0x80); + key(Keyboard::PageUp ) = (bool)(state[0xc9] & 0x80); + key(Keyboard::PageDown) = (bool)(state[0xd1] & 0x80); + + key(Keyboard::A) = (bool)(state[0x1e] & 0x80); + key(Keyboard::B) = (bool)(state[0x30] & 0x80); + key(Keyboard::C) = (bool)(state[0x2e] & 0x80); + key(Keyboard::D) = (bool)(state[0x20] & 0x80); + key(Keyboard::E) = (bool)(state[0x12] & 0x80); + key(Keyboard::F) = (bool)(state[0x21] & 0x80); + key(Keyboard::G) = (bool)(state[0x22] & 0x80); + key(Keyboard::H) = (bool)(state[0x23] & 0x80); + key(Keyboard::I) = (bool)(state[0x17] & 0x80); + key(Keyboard::J) = (bool)(state[0x24] & 0x80); + key(Keyboard::K) = (bool)(state[0x25] & 0x80); + key(Keyboard::L) = (bool)(state[0x26] & 0x80); + key(Keyboard::M) = (bool)(state[0x32] & 0x80); + key(Keyboard::N) = (bool)(state[0x31] & 0x80); + key(Keyboard::O) = (bool)(state[0x18] & 0x80); + key(Keyboard::P) = (bool)(state[0x19] & 0x80); + key(Keyboard::Q) = (bool)(state[0x10] & 0x80); + key(Keyboard::R) = (bool)(state[0x13] & 0x80); + key(Keyboard::S) = (bool)(state[0x1f] & 0x80); + key(Keyboard::T) = (bool)(state[0x14] & 0x80); + key(Keyboard::U) = (bool)(state[0x16] & 0x80); + key(Keyboard::V) = (bool)(state[0x2f] & 0x80); + key(Keyboard::W) = (bool)(state[0x11] & 0x80); + key(Keyboard::X) = (bool)(state[0x2d] & 0x80); + key(Keyboard::Y) = (bool)(state[0x15] & 0x80); + key(Keyboard::Z) = (bool)(state[0x2c] & 0x80); + + key(Keyboard::LeftBracket ) = (bool)(state[0x1a] & 0x80); + key(Keyboard::RightBracket) = (bool)(state[0x1b] & 0x80); + key(Keyboard::Backslash ) = (bool)(state[0x2b] & 0x80); + key(Keyboard::Semicolon ) = (bool)(state[0x27] & 0x80); + key(Keyboard::Apostrophe ) = (bool)(state[0x28] & 0x80); + key(Keyboard::Comma ) = (bool)(state[0x33] & 0x80); + key(Keyboard::Period ) = (bool)(state[0x34] & 0x80); + key(Keyboard::Slash ) = (bool)(state[0x35] & 0x80); + + key(Keyboard::Keypad0) = (bool)(state[0x4f] & 0x80); + key(Keyboard::Keypad1) = (bool)(state[0x50] & 0x80); + key(Keyboard::Keypad2) = (bool)(state[0x51] & 0x80); + key(Keyboard::Keypad3) = (bool)(state[0x4b] & 0x80); + key(Keyboard::Keypad4) = (bool)(state[0x4c] & 0x80); + key(Keyboard::Keypad5) = (bool)(state[0x4d] & 0x80); + key(Keyboard::Keypad6) = (bool)(state[0x47] & 0x80); + key(Keyboard::Keypad7) = (bool)(state[0x48] & 0x80); + key(Keyboard::Keypad8) = (bool)(state[0x49] & 0x80); + key(Keyboard::Keypad9) = (bool)(state[0x52] & 0x80); + key(Keyboard::Point ) = (bool)(state[0x53] & 0x80); + + key(Keyboard::Add ) = (bool)(state[0x4e] & 0x80); + key(Keyboard::Subtract) = (bool)(state[0x4a] & 0x80); + key(Keyboard::Multiply) = (bool)(state[0x37] & 0x80); + key(Keyboard::Divide ) = (bool)(state[0xb5] & 0x80); + key(Keyboard::Enter ) = (bool)(state[0x9c] & 0x80); + + key(Keyboard::NumLock ) = (bool)(state[0x45] & 0x80); + key(Keyboard::CapsLock) = (bool)(state[0x3a] & 0x80); + + key(Keyboard::Up ) = (bool)(state[0xc8] & 0x80); + key(Keyboard::Down ) = (bool)(state[0xd0] & 0x80); + key(Keyboard::Left ) = (bool)(state[0xcb] & 0x80); + key(Keyboard::Right) = (bool)(state[0xcd] & 0x80); + + key(Keyboard::Tab ) = (bool)(state[0x0f] & 0x80); + key(Keyboard::Return ) = (bool)(state[0x1c] & 0x80); + key(Keyboard::Spacebar) = (bool)(state[0x39] & 0x80); + key(Keyboard::Menu ) = (bool)(state[0xdd] & 0x80); + + key(Keyboard::Shift ) = (bool)(state[0x2a] & 0x80) || (bool)(state[0x36] & 0x80); + key(Keyboard::Control) = (bool)(state[0x1d] & 0x80) || (bool)(state[0x9d] & 0x80); + key(Keyboard::Alt ) = (bool)(state[0x38] & 0x80) || (bool)(state[0xb8] & 0x80); + key(Keyboard::Super ) = (bool)(state[0xdb] & 0x80) || (bool)(state[0xdc] & 0x80); + + #undef key + } + + //===== + //Mouse + //===== + + if(device.mouse) { + DIMOUSESTATE2 state; + if(FAILED(device.mouse->GetDeviceState(sizeof(DIMOUSESTATE2), (void*)&state))) { + device.mouse->Acquire(); + if(FAILED(device.mouse->GetDeviceState(sizeof(DIMOUSESTATE2), (void*)&state))) { + memset(&state, 0, sizeof(DIMOUSESTATE2)); + } + } + + table[mouse(0).axis(0)] = state.lX; + table[mouse(0).axis(1)] = state.lY; + table[mouse(0).axis(2)] = state.lZ / WHEEL_DELTA; + for(unsigned n = 0; n < Mouse::Buttons; n++) { + table[mouse(0).button(n)] = (bool)state.rgbButtons[n]; + } + + //on Windows, 0 = left, 1 = right, 2 = middle + //swap middle and right buttons for consistency with Linux + int16_t temp = table[mouse(0).button(1)]; + table[mouse(0).button(1)] = table[mouse(0).button(2)]; + table[mouse(0).button(2)] = temp; + } + + //========= + //Joypad(s) + //========= + + for(unsigned i = 0; i < Joypad::Count; i++) { + if(!device.gamepad[i]) continue; + + if(FAILED(device.gamepad[i]->Poll())) { + device.gamepad[i]->Acquire(); + continue; + } + + DIJOYSTATE2 state; + device.gamepad[i]->GetDeviceState(sizeof(DIJOYSTATE2), &state); + + //POV hats + for(unsigned n = 0; n < min((unsigned)Joypad::Hats, 4); n++) { + //POV value is in clockwise-hundredth degree units. + unsigned pov = state.rgdwPOV[n]; + //some drivers report a centered POV hat as -1U, others as 65535U. + //>= 36000 will match both, as well as invalid ranges. + if(pov < 36000) { + if(pov >= 31500 || pov <= 4500) table[joypad(i).hat(n)] |= Joypad::HatUp; + if(pov >= 4500 && pov <= 13500) table[joypad(i).hat(n)] |= Joypad::HatRight; + if(pov >= 13500 && pov <= 22500) table[joypad(i).hat(n)] |= Joypad::HatDown; + if(pov >= 22500 && pov <= 31500) table[joypad(i).hat(n)] |= Joypad::HatLeft; + } + } + + //axes + table[joypad(i).axis(0)] = state.lX; + table[joypad(i).axis(1)] = state.lY; + table[joypad(i).axis(2)] = state.lZ; + table[joypad(i).axis(3)] = state.lRx; + table[joypad(i).axis(4)] = state.lRy; + table[joypad(i).axis(5)] = state.lRz; + + //buttons + for(unsigned n = 0; n < min((unsigned)Joypad::Buttons, 128); n++) { + table[joypad(i).button(n)] = (bool)state.rgbButtons[n]; + } + } + + return true; + } + + bool init_joypad(const DIDEVICEINSTANCE *instance) { + unsigned n; + for(n = 0; n < Joypad::Count; n++) { if(!device.gamepad[n]) break; } + if(n >= Joypad::Count) return DIENUM_STOP; + + if(FAILED(device.context->CreateDevice(instance->guidInstance, &device.gamepad[n], 0))) { + return DIENUM_CONTINUE; //continue and try next gamepad + } + + device.gamepad[n]->SetDataFormat(&c_dfDIJoystick2); + device.gamepad[n]->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + device.gamepad[n]->EnumObjects(DI_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS); + + return DIENUM_CONTINUE; + } + + bool init_axis(const DIDEVICEOBJECTINSTANCE *instance) { + signed n; + for(n = Joypad::Count - 1; n >= 0; n--) { if(device.gamepad[n]) break; } + if(n < 0) return DIENUM_STOP; + + DIPROPRANGE range; + range.diph.dwSize = sizeof(DIPROPRANGE); + range.diph.dwHeaderSize = sizeof(DIPROPHEADER); + range.diph.dwHow = DIPH_BYID; + range.diph.dwObj = instance->dwType; + range.lMin = -32768; + range.lMax = +32767; + device.gamepad[n]->SetProperty(DIPROP_RANGE, &range.diph); + + return DIENUM_CONTINUE; + } + + bool init() { + device.context = 0; + device.keyboard = 0; + device.mouse = 0; + for(unsigned i = 0; i < Joypad::Count; i++) device.gamepad[i] = 0; + device.mouseacquired = false; + + DirectInput8Create(GetModuleHandle(0), 0x0800, IID_IDirectInput8, (void**)&device.context, 0); + + device.context->CreateDevice(GUID_SysKeyboard, &device.keyboard, 0); + device.keyboard->SetDataFormat(&c_dfDIKeyboard); + device.keyboard->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + device.keyboard->Acquire(); + + device.context->CreateDevice(GUID_SysMouse, &device.mouse, 0); + device.mouse->SetDataFormat(&c_dfDIMouse2); + HRESULT hr = device.mouse->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + device.mouse->Acquire(); + + device.context->EnumDevices(DI8DEVCLASS_GAMECTRL, DI_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY); + + return true; + } + + void term() { + if(device.keyboard) { + device.keyboard->Unacquire(); + device.keyboard->Release(); + device.keyboard = 0; + } + + if(device.mouse) { + device.mouse->Unacquire(); + device.mouse->Release(); + device.mouse = 0; + } + + for(unsigned i = 0; i < Joypad::Count; i++) { + if(device.gamepad[i]) { + device.gamepad[i]->Unacquire(); + device.gamepad[i]->Release(); + device.gamepad[i] = 0; + } + } + + if(device.context) { + device.context->Release(); + device.context = 0; + } + } + + bool acquire() { + if(!device.mouse) return false; + if(acquired() == false) { + device.mouse->Unacquire(); + device.mouse->SetCooperativeLevel(settings.handle, DISCL_EXCLUSIVE | DISCL_FOREGROUND); + device.mouse->Acquire(); + device.mouseacquired = true; + } + return true; + } + + bool unacquire() { + if(!device.mouse) return false; + if(acquired() == true) { + device.mouse->Unacquire(); + device.mouse->SetCooperativeLevel(settings.handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + device.mouse->Acquire(); + device.mouseacquired = false; + } + return true; + } + + bool acquired() { + return device.mouseacquired; + } + + pInputDI() { + device.context = 0; + device.keyboard = 0; + device.mouse = 0; + for(unsigned i = 0; i < Joypad::Count; i++) device.gamepad[i] = 0; + device.mouseacquired = false; + + settings.handle = 0; + } + + ~pInputDI() { term(); } +}; + +BOOL CALLBACK DI_EnumJoypadsCallback(const DIDEVICEINSTANCE *instance, void *p) { + return ((pInputDI*)p)->init_joypad(instance); +} + +BOOL CALLBACK DI_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE *instance, void *p) { + return ((pInputDI*)p)->init_axis(instance); +} + +DeclareInput(DI) + +}; diff --git a/mednafen/snes/src/lib/ruby/input/rawinput.cpp b/mednafen/snes/src/lib/ruby/input/rawinput.cpp new file mode 100755 index 0000000..fa3951f --- /dev/null +++ b/mednafen/snes/src/lib/ruby/input/rawinput.cpp @@ -0,0 +1,797 @@ +//RawInput driver +//author: byuu + +//this driver utilizes RawInput (WM_INPUT) to capture keyboard and mouse input. +//although this requires WinXP or newer, it is the only way to uniquely identify +//and independently map multiple keyboards and mice. DirectInput merges all +//keyboards and mice into one device per. +// +//as WM_INPUT lacks specific RAWINPUT structures for gamepads, giving only raw +//data, and because DirectInput supports up to 16 joypads, DirectInput is used +//for joypad mapping. +// +//further, Xbox 360 controllers are explicitly detected and supported through +//XInput. this is because under DirectInput, the LT / RT (trigger) buttons are +//merged into a single Z-axis -- making it impossible to detect both buttons +//being pressed at the same time. with XInput, the state of both trigger +//buttons can be read independently. +// +//so in essence, this is actually more of a hybrid driver. + +#define DIRECTINPUT_VERSION 0x0800 +#include +#include + +namespace ruby { + +static DWORD WINAPI RawInputThreadProc(void*); +static LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM); + +class RawInput { +public: + HANDLE mutex; + HWND hwnd; + bool initialized; + bool ready; + + struct Device { + HANDLE handle; + }; + + struct Keyboard : Device { + bool state[nall::Keyboard::Size]; + + void update(RAWINPUT *input) { + unsigned code = input->data.keyboard.MakeCode; + unsigned flags = input->data.keyboard.Flags; + + #define map(id, flag, name) if(code == id) state[name] = (bool)(flags == flag); + map(0x0001, 0, nall::Keyboard::Escape) + map(0x003b, 0, nall::Keyboard::F1) + map(0x003c, 0, nall::Keyboard::F2) + map(0x003d, 0, nall::Keyboard::F3) + map(0x003e, 0, nall::Keyboard::F4) + map(0x003f, 0, nall::Keyboard::F5) + map(0x0040, 0, nall::Keyboard::F6) + map(0x0041, 0, nall::Keyboard::F7) + map(0x0042, 0, nall::Keyboard::F8) + map(0x0043, 0, nall::Keyboard::F9) + map(0x0044, 0, nall::Keyboard::F10) + map(0x0057, 0, nall::Keyboard::F11) + map(0x0058, 0, nall::Keyboard::F12) + + map(0x0037, 2, nall::Keyboard::PrintScreen) + map(0x0046, 0, nall::Keyboard::ScrollLock) + map(0x001d, 4, nall::Keyboard::Pause) + map(0x0029, 0, nall::Keyboard::Tilde) + + map(0x0002, 0, nall::Keyboard::Num1) + map(0x0003, 0, nall::Keyboard::Num2) + map(0x0004, 0, nall::Keyboard::Num3) + map(0x0005, 0, nall::Keyboard::Num4) + map(0x0006, 0, nall::Keyboard::Num5) + map(0x0007, 0, nall::Keyboard::Num6) + map(0x0008, 0, nall::Keyboard::Num7) + map(0x0009, 0, nall::Keyboard::Num8) + map(0x000a, 0, nall::Keyboard::Num9) + map(0x000b, 0, nall::Keyboard::Num0) + + map(0x000c, 0, nall::Keyboard::Dash) + map(0x000d, 0, nall::Keyboard::Equal) + map(0x000e, 0, nall::Keyboard::Backspace) + + map(0x0052, 2, nall::Keyboard::Insert) + map(0x0053, 2, nall::Keyboard::Delete) + map(0x0047, 2, nall::Keyboard::Home) + map(0x004f, 2, nall::Keyboard::End) + map(0x0049, 2, nall::Keyboard::PageUp) + map(0x0051, 2, nall::Keyboard::PageDown) + + map(0x001e, 0, nall::Keyboard::A) + map(0x0030, 0, nall::Keyboard::B) + map(0x002e, 0, nall::Keyboard::C) + map(0x0020, 0, nall::Keyboard::D) + map(0x0012, 0, nall::Keyboard::E) + map(0x0021, 0, nall::Keyboard::F) + map(0x0022, 0, nall::Keyboard::G) + map(0x0023, 0, nall::Keyboard::H) + map(0x0017, 0, nall::Keyboard::I) + map(0x0024, 0, nall::Keyboard::J) + map(0x0025, 0, nall::Keyboard::K) + map(0x0026, 0, nall::Keyboard::L) + map(0x0032, 0, nall::Keyboard::M) + map(0x0031, 0, nall::Keyboard::N) + map(0x0018, 0, nall::Keyboard::O) + map(0x0019, 0, nall::Keyboard::P) + map(0x0010, 0, nall::Keyboard::Q) + map(0x0013, 0, nall::Keyboard::R) + map(0x001f, 0, nall::Keyboard::S) + map(0x0014, 0, nall::Keyboard::T) + map(0x0016, 0, nall::Keyboard::U) + map(0x002f, 0, nall::Keyboard::V) + map(0x0011, 0, nall::Keyboard::W) + map(0x002d, 0, nall::Keyboard::X) + map(0x0015, 0, nall::Keyboard::Y) + map(0x002c, 0, nall::Keyboard::Z) + + map(0x001a, 0, nall::Keyboard::LeftBracket) + map(0x001b, 0, nall::Keyboard::RightBracket) + map(0x002b, 0, nall::Keyboard::Backslash) + map(0x0027, 0, nall::Keyboard::Semicolon) + map(0x0028, 0, nall::Keyboard::Apostrophe) + map(0x0033, 0, nall::Keyboard::Comma) + map(0x0034, 0, nall::Keyboard::Period) + map(0x0035, 0, nall::Keyboard::Slash) + + map(0x004f, 0, nall::Keyboard::Keypad1) + map(0x0050, 0, nall::Keyboard::Keypad2) + map(0x0051, 0, nall::Keyboard::Keypad3) + map(0x004b, 0, nall::Keyboard::Keypad4) + map(0x004c, 0, nall::Keyboard::Keypad5) + map(0x004d, 0, nall::Keyboard::Keypad6) + map(0x0047, 0, nall::Keyboard::Keypad7) + map(0x0048, 0, nall::Keyboard::Keypad8) + map(0x0049, 0, nall::Keyboard::Keypad9) + map(0x0052, 0, nall::Keyboard::Keypad0) + + map(0x0053, 0, nall::Keyboard::Point) + map(0x001c, 2, nall::Keyboard::Enter) + map(0x004e, 0, nall::Keyboard::Add) + map(0x004a, 0, nall::Keyboard::Subtract) + map(0x0037, 0, nall::Keyboard::Multiply) + map(0x0035, 2, nall::Keyboard::Divide) + + map(0x0045, 0, nall::Keyboard::NumLock) + map(0x003a, 0, nall::Keyboard::CapsLock) + + //Pause signals 0x1d:4 + 0x45:0, whereas NumLock signals only 0x45:0. + //this makes it impractical to detect both Pause+NumLock independently. + //workaround: always detect Pause; detect NumLock only when Pause is released. + if(state[nall::Keyboard::Pause]) state[nall::Keyboard::NumLock] = false; + + map(0x0048, 2, nall::Keyboard::Up) + map(0x0050, 2, nall::Keyboard::Down) + map(0x004b, 2, nall::Keyboard::Left) + map(0x004d, 2, nall::Keyboard::Right) + + map(0x000f, 0, nall::Keyboard::Tab) + map(0x001c, 0, nall::Keyboard::Return) + map(0x0039, 0, nall::Keyboard::Spacebar) + map(0x005d, 2, nall::Keyboard::Menu) + + //merge left and right modifiers to one ID + if(code == 0x002a && flags == 0) state[nall::Keyboard::Shift] = 1; //left shift + if(code == 0x002a && flags == 1) state[nall::Keyboard::Shift] = 0; + if(code == 0x0036 && flags == 0) state[nall::Keyboard::Shift] = 1; //right shift + if(code == 0x0036 && flags == 1) state[nall::Keyboard::Shift] = 0; + + if(code == 0x001d && flags == 0) state[nall::Keyboard::Control] = 1; //left control + if(code == 0x001d && flags == 1) state[nall::Keyboard::Control] = 0; + if(code == 0x001d && flags == 2) state[nall::Keyboard::Control] = 1; //right control + if(code == 0x001d && flags == 3) state[nall::Keyboard::Control] = 0; + + if(code == 0x0038 && flags == 0) state[nall::Keyboard::Alt] = 1; //left alt + if(code == 0x0038 && flags == 1) state[nall::Keyboard::Alt] = 0; + if(code == 0x0038 && flags == 2) state[nall::Keyboard::Alt] = 1; //right alt + if(code == 0x0038 && flags == 3) state[nall::Keyboard::Alt] = 0; + + if(code == 0x005b && flags == 2) state[nall::Keyboard::Super] = 1; //left super + if(code == 0x005b && flags == 3) state[nall::Keyboard::Super] = 0; + if(code == 0x005c && flags == 2) state[nall::Keyboard::Super] = 1; //right super + if(code == 0x005c && flags == 3) state[nall::Keyboard::Super] = 0; + #undef map + } + + Keyboard() { + for(unsigned i = 0; i < nall::Keyboard::Size; i++) state[i] = false; + } + }; + + struct Mouse : Device { + signed xDistance; + signed yDistance; + signed zDistance; + unsigned buttonState; + + void sync() { + xDistance = 0; + yDistance = 0; + zDistance = 0; + } + + void update(RAWINPUT *input) { + if((input->data.mouse.usFlags & 1) == MOUSE_MOVE_RELATIVE) { + xDistance += input->data.mouse.lLastX; + yDistance += input->data.mouse.lLastY; + } + + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) buttonState |= 1 << 0; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP ) buttonState &=~ 1 << 0; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) buttonState |= 1 << 2; //swap middle and right buttons, + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP ) buttonState &=~ 1 << 2; //for consistency with Linux: + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) buttonState |= 1 << 1; //left = 0, middle = 1, right = 2 + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP ) buttonState &=~ 1 << 1; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) buttonState |= 1 << 3; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP ) buttonState &=~ 1 << 3; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) buttonState |= 1 << 4; + if(input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP ) buttonState &=~ 1 << 4; + + if(input->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) { + zDistance += (int16_t)input->data.mouse.usButtonData; + } + } + + Mouse() { + xDistance = yDistance = zDistance = 0; + buttonState = 0; + } + }; + + //keep track of gamepads for the sole purpose of distinguishing XInput devices + //from all other devices. this is necessary, as DirectInput does not provide + //a way to retrieve the necessary RIDI_DEVICENAME string. + struct Gamepad : Device { + bool isXInputDevice; + uint16_t vendorId; + uint16_t productId; + }; + + vector lkeyboard; + vector lmouse; + vector lgamepad; + + LRESULT window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + if(msg == WM_INPUT) { + unsigned size = 0; + GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); + RAWINPUT *input = new RAWINPUT[size]; + GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER)); + WaitForSingleObject(mutex, INFINITE); + + if(input->header.dwType == RIM_TYPEKEYBOARD) { + for(unsigned i = 0; i < lkeyboard.size(); i++) { + if(input->header.hDevice == lkeyboard[i].handle) { + lkeyboard[i].update(input); + break; + } + } + } else if(input->header.dwType == RIM_TYPEMOUSE) { + for(unsigned i = 0; i < lmouse.size(); i++) { + if(input->header.hDevice == lmouse[i].handle) { + lmouse[i].update(input); + break; + } + } + } + + ReleaseMutex(mutex); + //allow propogation of WM_INPUT message + LRESULT result = DefRawInputProc(&input, size, sizeof(RAWINPUTHEADER)); + delete[] input; + return result; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); + } + + //this is used to sort device IDs + struct DevicePool { + HANDLE handle; + char name[4096]; + bool operator<(const DevicePool &pool) const { return strcmp(name, pool.name) < 0; } + }; + + int main() { + //create an invisible window to act as a sink, capturing all WM_INPUT messages + WNDCLASS wc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = RawInputWindowProc; + wc.lpszClassName = "RawInputClass"; + wc.lpszMenuName = 0; + wc.style = CS_VREDRAW | CS_HREDRAW; + RegisterClass(&wc); + + hwnd = CreateWindow("RawInputClass", "RawInputClass", WS_POPUP, + 0, 0, 64, 64, 0, 0, GetModuleHandle(0), 0); + + //enumerate all HID devices + unsigned devices = 0; + GetRawInputDeviceList(NULL, &devices, sizeof(RAWINPUTDEVICELIST)); + RAWINPUTDEVICELIST *list = new RAWINPUTDEVICELIST[devices]; + GetRawInputDeviceList(list, &devices, sizeof(RAWINPUTDEVICELIST)); + + //sort all devices by name. this has two important properties: + //1) it consistently orders peripherals, so mapped IDs remain constant + //2) it sorts the virtual keyboard and mouse to the bottom of the list + // (real devices start with \\?\HID#, virtual with \\?\Root#) + DevicePool pool[devices]; + for(unsigned i = 0; i < devices; i++) { + pool[i].handle = list[i].hDevice; + unsigned size = sizeof(pool[i].name) - 1; + GetRawInputDeviceInfo(list[i].hDevice, RIDI_DEVICENAME, &pool[i].name, &size); + } + nall::sort(pool, devices); + delete[] list; + + for(unsigned i = 0; i < devices; i++) { + RID_DEVICE_INFO info; + info.cbSize = sizeof(RID_DEVICE_INFO); + + unsigned size = info.cbSize; + GetRawInputDeviceInfo(pool[i].handle, RIDI_DEVICEINFO, &info, &size); + + if(info.dwType == RIM_TYPEKEYBOARD) { + unsigned n = lkeyboard.size(); + lkeyboard[n].handle = pool[i].handle; + } else if(info.dwType == RIM_TYPEMOUSE) { + unsigned n = lmouse.size(); + lmouse[n].handle = pool[i].handle; + } else if(info.dwType == RIM_TYPEHID) { + //if this is a gamepad or joystick device ... + if(info.hid.usUsagePage == 1 && (info.hid.usUsage == 4 || info.hid.usUsage == 5)) { + //... then cache device information for later use + unsigned n = lgamepad.size(); + lgamepad[n].handle = pool[i].handle; + lgamepad[n].vendorId = (uint16_t)info.hid.dwVendorId; + lgamepad[n].productId = (uint16_t)info.hid.dwProductId; + + //per MSDN: XInput devices have "IG_" in their device strings, + //which is how they should be identified. + const char *p = strstr(pool[i].name, "IG_"); + lgamepad[n].isXInputDevice = (bool)p; + } + } + } + + RAWINPUTDEVICE device[2]; + //capture all keyboard input + device[0].usUsagePage = 1; + device[0].usUsage = 6; + device[0].dwFlags = RIDEV_INPUTSINK; + device[0].hwndTarget = hwnd; + //capture all mouse input + device[1].usUsagePage = 1; + device[1].usUsage = 2; + device[1].dwFlags = RIDEV_INPUTSINK; + device[1].hwndTarget = hwnd; + RegisterRawInputDevices(device, 2, sizeof(RAWINPUTDEVICE)); + + WaitForSingleObject(mutex, INFINITE); + ready = true; + ReleaseMutex(mutex); + + while(true) { + MSG msg; + GetMessage(&msg, hwnd, 0, 0); + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; + } + + RawInput() : initialized(false), ready(false) { + } +}; + +static RawInput rawinput; + +DWORD WINAPI RawInputThreadProc(void*) { + return rawinput.main(); +} + +LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + return rawinput.window_proc(hwnd, msg, wparam, lparam); +} + +class XInput { +public: + HMODULE libxinput; + DWORD WINAPI (*pXInputGetState)(DWORD, XINPUT_STATE*); + + struct Gamepad { + unsigned id; + + int16_t hat; + int16_t axis[6]; + bool button[10]; + + void poll(XINPUT_STATE &state) { + hat = Joypad::HatCenter; + if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) hat |= Joypad::HatUp; + if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) hat |= Joypad::HatRight; + if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) hat |= Joypad::HatDown; + if(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) hat |= Joypad::HatLeft; + + axis[0] = (int16_t)state.Gamepad.sThumbLX; + axis[1] = (int16_t)state.Gamepad.sThumbLY; + axis[2] = (int16_t)state.Gamepad.sThumbRX; + axis[3] = (int16_t)state.Gamepad.sThumbRY; + + //transform left and right trigger ranges: + //from: 0 (low, eg released) to 255 (high, eg pressed all the way down) + //to: +32767 (low) to -32768 (high) + uint16_t triggerX = state.Gamepad.bLeftTrigger; + uint16_t triggerY = state.Gamepad.bRightTrigger; + + triggerX = (triggerX << 8) | triggerX; + triggerY = (triggerY << 8) | triggerY; + + axis[4] = (~triggerX) - 32768; + axis[5] = (~triggerY) - 32768; + + button[0] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_A); + button[1] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_B); + button[2] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_X); + button[3] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y); + button[4] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK); + button[5] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_START); + button[6] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER); + button[7] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER); + button[8] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB); + button[9] = (bool)(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB); + } + + Gamepad() { + hat = Joypad::HatCenter; + for(unsigned n = 0; n < 6; n++) axis[n] = 0; + for(unsigned n = 0; n < 10; n++) button[n] = false; + } + }; + + vector lgamepad; + + void poll() { + if(!pXInputGetState) return; + + for(unsigned i = 0; i < lgamepad.size(); i++) { + XINPUT_STATE state; + DWORD result = pXInputGetState(lgamepad[i].id, &state); + if(result == ERROR_SUCCESS) lgamepad[i].poll(state); + } + } + + void init() { + if(!pXInputGetState) return; + + //XInput only supports up to four controllers + for(unsigned i = 0; i <= 3; i++) { + XINPUT_STATE state; + DWORD result = pXInputGetState(i, &state); + if(result == ERROR_SUCCESS) { + //valid controller detected, add to gamepad list + unsigned n = lgamepad.size(); + lgamepad[n].id = i; + } + } + } + + XInput() : pXInputGetState(0) { + //bind xinput1 dynamically, as it does not ship with Windows Vista or below + libxinput = LoadLibraryA("xinput1_3.dll"); + if(!libxinput) libxinput = LoadLibraryA("xinput1_2.dll"); + if(!libxinput) libxinput = LoadLibraryA("xinput1_1.dll"); + if(!libxinput) return; + pXInputGetState = (DWORD WINAPI (*)(DWORD, XINPUT_STATE*))GetProcAddress(libxinput, "XInputGetState"); + } + + ~XInput() { + if(libxinput) FreeLibrary(libxinput); + } +}; + +static BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*); +static BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE*, void*); + +class DirectInput { +public: + HWND handle; + LPDIRECTINPUT8 context; + struct Gamepad { + LPDIRECTINPUTDEVICE8 handle; + + int16_t hat[4]; + int16_t axis[6]; + bool button[128]; + + void poll(DIJOYSTATE2 &state) { + //POV hats + for(unsigned n = 0; n < 4; n++) { + hat[n] = Joypad::HatCenter; + + //POV value is in clockwise-hundredth degree units + unsigned pov = state.rgdwPOV[n]; + + //some drivers report a centered POV hat as -1U, others as 65535U. + //>= 36000 will match both, as well as invalid ranges. + if(pov >= 36000) continue; + + if(pov >= 31500 || pov <= 4500) hat[n] |= Joypad::HatUp; + if(pov >= 4500 && pov <= 13500) hat[n] |= Joypad::HatRight; + if(pov >= 13500 && pov <= 22500) hat[n] |= Joypad::HatDown; + if(pov >= 22500 && pov <= 31500) hat[n] |= Joypad::HatLeft; + } + + //axes + axis[0] = state.lX; + axis[1] = state.lY; + axis[2] = state.lZ; + axis[3] = state.lRx; + axis[4] = state.lRy; + axis[5] = state.lRz; + + //buttons + for(unsigned n = 0; n < 128; n++) { + button[n] = (bool)state.rgbButtons[n]; + } + } + + Gamepad() { + handle = 0; + for(unsigned n = 0; n < 4; n++) hat[n] = Joypad::HatCenter; + for(unsigned n = 0; n < 6; n++) axis[n] = 0; + for(unsigned n = 0; n < 128; n++) button[n] = false; + } + }; + vector lgamepad; + + void poll() { + for(unsigned i = 0; i < lgamepad.size(); i++) { + if(FAILED(lgamepad[i].handle->Poll())) { + lgamepad[i].handle->Acquire(); + continue; + } + + DIJOYSTATE2 state; + lgamepad[i].handle->GetDeviceState(sizeof(DIJOYSTATE2), &state); + lgamepad[i].poll(state); + } + } + + bool init_joypad(const DIDEVICEINSTANCE *instance) { + //if this is an XInput device, do not acquire it via DirectInput ... + //the XInput driver above will handle said device. + for(unsigned i = 0; i < rawinput.lgamepad.size(); i++) { + uint32_t guid = MAKELONG(rawinput.lgamepad[i].vendorId, rawinput.lgamepad[i].productId); + if(guid == instance->guidProduct.Data1) { + if(rawinput.lgamepad[i].isXInputDevice == true) { + return DIENUM_CONTINUE; + } + } + } + + if(FAILED(context->CreateDevice(instance->guidInstance, &device, 0))) { + return DIENUM_CONTINUE; + } + + device->SetDataFormat(&c_dfDIJoystick2); + device->SetCooperativeLevel(handle, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + device->EnumObjects(DirectInput_EnumJoypadAxesCallback, (void*)this, DIDFT_ABSAXIS); + unsigned n = lgamepad.size(); + lgamepad[n].handle = device; + return DIENUM_CONTINUE; + } + + bool init_axis(const DIDEVICEOBJECTINSTANCE *instance) { + DIPROPRANGE range; + range.diph.dwSize = sizeof(DIPROPRANGE); + range.diph.dwHeaderSize = sizeof(DIPROPHEADER); + range.diph.dwHow = DIPH_BYID; + range.diph.dwObj = instance->dwType; + range.lMin = -32768; + range.lMax = +32767; + device->SetProperty(DIPROP_RANGE, &range.diph); + return DIENUM_CONTINUE; + } + + void init(HWND handle_) { + handle = handle_; + DirectInput8Create(GetModuleHandle(0), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&context, 0); + context->EnumDevices(DI8DEVCLASS_GAMECTRL, DirectInput_EnumJoypadsCallback, (void*)this, DIEDFL_ATTACHEDONLY); + } + + void term() { + for(unsigned i = 0; i < lgamepad.size(); i++) { + lgamepad[i].handle->Unacquire(); + lgamepad[i].handle->Release(); + } + lgamepad.reset(); + + if(context) { + context->Release(); + context = 0; + } + } + +private: + LPDIRECTINPUTDEVICE8 device; +}; + +BOOL CALLBACK DirectInput_EnumJoypadsCallback(const DIDEVICEINSTANCE *instance, void *p) { + return ((DirectInput*)p)->init_joypad(instance); +} + +BOOL CALLBACK DirectInput_EnumJoypadAxesCallback(const DIDEVICEOBJECTINSTANCE *instance, void *p) { + return ((DirectInput*)p)->init_axis(instance); +} + +class pInputRaw { +public: + XInput xinput; + DirectInput dinput; + + bool acquire_mouse; + bool cursor_visible; + + struct { + HWND handle; + } settings; + + bool cap(const string& name) { + if(name == Input::Handle) return true; + if(name == Input::KeyboardSupport) return true; + if(name == Input::MouseSupport) return true; + if(name == Input::JoypadSupport) return true; + return false; + } + + any get(const string& name) { + if(name == Input::Handle) return (uintptr_t)settings.handle; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Input::Handle) { + settings.handle = (HWND)any_cast(value); + return true; + } + + return false; + } + + bool acquire() { + acquire_mouse = true; + if(cursor_visible == true) { + ShowCursor(cursor_visible = false); + } + return acquired(); + } + + bool unacquire() { + acquire_mouse = false; + ReleaseCapture(); + ClipCursor(NULL); + if(cursor_visible == false) { + ShowCursor(cursor_visible = true); + } + return true; + } + + bool acquired() { + if(acquire_mouse == true) { + SetFocus(settings.handle); + SetCapture(settings.handle); + RECT rc; + GetWindowRect(settings.handle, &rc); + ClipCursor(&rc); + } + return GetCapture() == settings.handle; + } + + bool poll(int16_t *table) { + memset(table, 0, Scancode::Limit * sizeof(int16_t)); + + WaitForSingleObject(rawinput.mutex, INFINITE); + + //========= + //Keyboards + //========= + for(unsigned i = 0; i < min(rawinput.lkeyboard.size(), (unsigned)Keyboard::Count); i++) { + for(unsigned n = 0; n < nall::Keyboard::Size; n++) { + table[keyboard(i).key(n)] = rawinput.lkeyboard[i].state[n]; + } + } + + //==== + //Mice + //==== + for(unsigned i = 0; i < min(rawinput.lmouse.size(), (unsigned)Mouse::Count); i++) { + table[mouse(i).axis(0)] = rawinput.lmouse[i].xDistance; + table[mouse(i).axis(1)] = rawinput.lmouse[i].yDistance; + table[mouse(i).axis(2)] = rawinput.lmouse[i].zDistance; + + for(unsigned n = 0; n < min(5U, (unsigned)Mouse::Buttons); n++) { + table[mouse(i).button(n)] = (bool)(rawinput.lmouse[i].buttonState & (1 << n)); + } + + rawinput.lmouse[i].sync(); + } + + ReleaseMutex(rawinput.mutex); + + unsigned joy = 0; + + //================== + //XInput controllers + //================== + xinput.poll(); + for(unsigned i = 0; i < xinput.lgamepad.size(); i++) { + if(joy >= Joypad::Count) break; + + table[joypad(i).hat(0)] = xinput.lgamepad[i].hat; + + for(unsigned axis = 0; axis < min(6U, (unsigned)Joypad::Axes); axis++) { + table[joypad(i).axis(axis)] = xinput.lgamepad[i].axis[axis]; + } + + for(unsigned button = 0; button < min(10U, (unsigned)Joypad::Buttons); button++) { + table[joypad(i).button(button)] = xinput.lgamepad[i].button[button]; + } + } + + //======================= + //DirectInput controllers + //======================= + dinput.poll(); + for(unsigned i = 0; i < dinput.lgamepad.size(); i++) { + if(joy >= Joypad::Count) break; + + for(unsigned hat = 0; hat < min(4U, (unsigned)Joypad::Hats); hat++) { + table[joypad(i).hat(hat)] = dinput.lgamepad[i].hat[hat]; + } + + for(unsigned axis = 0; axis < min(6U, (unsigned)Joypad::Axes); axis++) { + table[joypad(i).axis(axis)] = dinput.lgamepad[i].axis[axis]; + } + + for(unsigned button = 0; button < min(128U, (unsigned)Joypad::Buttons); button++) { + table[joypad(i).button(button)] = dinput.lgamepad[i].button[button]; + } + } + + return true; + } + + bool init() { + //only spawn RawInput processing thread one time + if(rawinput.initialized == false) { + rawinput.initialized = true; + rawinput.mutex = CreateMutex(NULL, FALSE, NULL); + CreateThread(NULL, 0, RawInputThreadProc, 0, 0, NULL); + + //RawInput device calibration needs to finish before initializing DirectInput; + //as it needs device GUIDs to distinguish XInput devices from ordinary joypads. + bool ready = false; + do { + Sleep(10); + WaitForSingleObject(rawinput.mutex, INFINITE); + ready = rawinput.ready; + ReleaseMutex(rawinput.mutex); + } while(ready == false); + } + + xinput.init(); + dinput.init(settings.handle); + + acquire_mouse = false; + cursor_visible = true; + return true; + } + + void term() { + unacquire(); + dinput.term(); + } + + pInputRaw() { + } +}; + +DeclareInput(Raw) + +}; diff --git a/mednafen/snes/src/lib/ruby/input/sdl.cpp b/mednafen/snes/src/lib/ruby/input/sdl.cpp new file mode 100755 index 0000000..9986a00 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/input/sdl.cpp @@ -0,0 +1,230 @@ +//================ +//SDL input driver +//================ +//Keyboard and mouse are controlled directly via Xlib, +//as SDL cannot capture input from windows it does not create itself. +//SDL is used only to handle joysticks / gamepads. + +#include +#include +#include + +namespace ruby { + +struct pInputSDL { + #include "xlibkeys.hpp" + + struct { + Display *display; + Window rootwindow; + Cursor InvisibleCursor; + SDL_Joystick *gamepad[Joypad::Count]; + + unsigned screenwidth, screenheight; + unsigned relativex, relativey; + bool mouseacquired; + + //mouse device settings + int accel_numerator; + int accel_denominator; + int threshold; + } device; + + struct { + uintptr_t handle; + } settings; + + bool cap(const string& name) { + if(name == Input::Handle) return true; + if(name == Input::KeyboardSupport) return true; + if(name == Input::MouseSupport) return true; + if(name == Input::JoypadSupport) return true; + return false; + } + + any get(const string& name) { + if(name == Input::Handle) return (uintptr_t)settings.handle; + return false; + } + + bool set(const string& name, const any &value) { + if(name == Input::Handle) { + settings.handle = any_cast(value); + return true; + } + + return false; + } + + bool acquire() { + if(acquired()) return true; + + if(XGrabPointer(device.display, settings.handle, True, 0, GrabModeAsync, GrabModeAsync, + device.rootwindow, device.InvisibleCursor, CurrentTime) == GrabSuccess) { + //backup existing cursor acceleration settings + XGetPointerControl(device.display, &device.accel_numerator, &device.accel_denominator, &device.threshold); + + //disable cursor acceleration + XChangePointerControl(device.display, True, False, 1, 1, 0); + + //center cursor (so that first relative poll returns 0, 0 if mouse has not moved) + XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2); + + return device.mouseacquired = true; + } else { + return device.mouseacquired = false; + } + } + + bool unacquire() { + if(acquired()) { + //restore cursor acceleration and release cursor + XChangePointerControl(device.display, True, True, device.accel_numerator, device.accel_denominator, device.threshold); + XUngrabPointer(device.display, CurrentTime); + device.mouseacquired = false; + } + return true; + } + + bool acquired() { + return device.mouseacquired; + } + + bool poll(int16_t *table) { + memset(table, 0, Scancode::Limit * sizeof(int16_t)); + + //======== + //Keyboard + //======== + + x_poll(table); + + //===== + //Mouse + //===== + + Window root_return, child_return; + int root_x_return = 0, root_y_return = 0; + int win_x_return = 0, win_y_return = 0; + unsigned int mask_return = 0; + XQueryPointer(device.display, settings.handle, + &root_return, &child_return, &root_x_return, &root_y_return, + &win_x_return, &win_y_return, &mask_return); + + if(acquired()) { + XWindowAttributes attributes; + XGetWindowAttributes(device.display, settings.handle, &attributes); + + //absolute -> relative conversion + table[mouse(0).axis(0)] = (int16_t)(root_x_return - device.screenwidth / 2); + table[mouse(0).axis(1)] = (int16_t)(root_y_return - device.screenheight / 2); + + if(table[mouse(0).axis(0)] != 0 || table[mouse(0).axis(1)] != 0) { + //if mouse movement occurred, re-center mouse for next poll + XWarpPointer(device.display, None, device.rootwindow, 0, 0, 0, 0, device.screenwidth / 2, device.screenheight / 2); + } + } else { + table[mouse(0).axis(0)] = (int16_t)(root_x_return - device.relativex); + table[mouse(0).axis(1)] = (int16_t)(root_y_return - device.relativey); + + device.relativex = root_x_return; + device.relativey = root_y_return; + } + + //manual device polling is limited to only five buttons ... + table[mouse(0).button(0)] = (bool)(mask_return & Button1Mask); + table[mouse(0).button(1)] = (bool)(mask_return & Button2Mask); + table[mouse(0).button(2)] = (bool)(mask_return & Button3Mask); + table[mouse(0).button(3)] = (bool)(mask_return & Button4Mask); + table[mouse(0).button(4)] = (bool)(mask_return & Button5Mask); + + //========= + //Joypad(s) + //========= + + SDL_JoystickUpdate(); + for(unsigned i = 0; i < Joypad::Count; i++) { + if(!device.gamepad[i]) continue; + + //POV hats + unsigned hats = min((unsigned)Joypad::Hats, SDL_JoystickNumHats(device.gamepad[i])); + for(unsigned hat = 0; hat < hats; hat++) { + uint8_t state = SDL_JoystickGetHat(device.gamepad[i], hat); + if(state & SDL_HAT_UP ) table[joypad(i).hat(hat)] |= Joypad::HatUp; + if(state & SDL_HAT_RIGHT) table[joypad(i).hat(hat)] |= Joypad::HatRight; + if(state & SDL_HAT_DOWN ) table[joypad(i).hat(hat)] |= Joypad::HatDown; + if(state & SDL_HAT_LEFT ) table[joypad(i).hat(hat)] |= Joypad::HatLeft; + } + + //axes + unsigned axes = min((unsigned)Joypad::Axes, SDL_JoystickNumAxes(device.gamepad[i])); + for(unsigned axis = 0; axis < axes; axis++) { + table[joypad(i).axis(axis)] = (int16_t)SDL_JoystickGetAxis(device.gamepad[i], axis); + } + + //buttons + for(unsigned button = 0; button < Joypad::Buttons; button++) { + table[joypad(i).button(button)] = (bool)SDL_JoystickGetButton(device.gamepad[i], button); + } + } + + return true; + } + + bool init() { + x_init(); + SDL_InitSubSystem(SDL_INIT_JOYSTICK); + SDL_JoystickEventState(SDL_IGNORE); + + device.display = XOpenDisplay(0); + device.rootwindow = DefaultRootWindow(device.display); + XWindowAttributes attributes; + XGetWindowAttributes(device.display, device.rootwindow, &attributes); + device.screenwidth = attributes.width; + device.screenheight = attributes.height; + + //Xlib: "because XShowCursor(false) would be too easy." + //create a fully transparent cursor named InvisibleCursor, + //for use while acquire() / XGrabPointer() is active. + Pixmap pixmap; + XColor black, unused; + static char invisible_data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + Colormap colormap = DefaultColormap(device.display, DefaultScreen(device.display)); + XAllocNamedColor(device.display, colormap, "black", &black, &unused); + pixmap = XCreateBitmapFromData(device.display, settings.handle, invisible_data, 8, 8); + device.InvisibleCursor = XCreatePixmapCursor(device.display, pixmap, pixmap, &black, &black, 0, 0); + XFreePixmap(device.display, pixmap); + XFreeColors(device.display, colormap, &black.pixel, 1, 0); + + device.mouseacquired = false; + device.relativex = 0; + device.relativey = 0; + + unsigned joypads = min((unsigned)Joypad::Count, SDL_NumJoysticks()); + for(unsigned i = 0; i < joypads; i++) device.gamepad[i] = SDL_JoystickOpen(i); + + return true; + } + + void term() { + unacquire(); + XFreeCursor(device.display, device.InvisibleCursor); + + for(unsigned i = 0; i < Joypad::Count; i++) { + if(device.gamepad[i]) SDL_JoystickClose(device.gamepad[i]); + device.gamepad[i] = 0; + } + + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + XCloseDisplay(device.display); + } + + pInputSDL() { + for(unsigned i = 0; i < Joypad::Count; i++) device.gamepad[i] = 0; + settings.handle = 0; + } +}; + +DeclareInput(SDL) + +}; diff --git a/mednafen/snes/src/lib/ruby/input/x.cpp b/mednafen/snes/src/lib/ruby/input/x.cpp new file mode 100755 index 0000000..fe1440a --- /dev/null +++ b/mednafen/snes/src/lib/ruby/input/x.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include + +namespace ruby { + +class pInputX { +public: + Display *display; + #include "xlibkeys.hpp" + + bool cap(const string& name) { + if(name == Input::KeyboardSupport) return true; + return false; + } + + any get(const string& name) { + return false; + } + + bool set(const string& name, const any &value) { + return false; + } + + bool acquire() { return false; } + bool unacquire() { return false; } + bool acquired() { return false; } + + bool poll(int16_t *table) { + memset(table, 0, Scancode::Limit * sizeof(int16_t)); + x_poll(table); + return true; + } + + bool init() { + x_init(); + display = XOpenDisplay(0); + return true; + } + + void term() { + XCloseDisplay(display); + } +}; + +DeclareInput(X) + +}; diff --git a/mednafen/snes/src/lib/ruby/input/xlibkeys.hpp b/mednafen/snes/src/lib/ruby/input/xlibkeys.hpp new file mode 100755 index 0000000..770de16 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/input/xlibkeys.hpp @@ -0,0 +1,264 @@ +uint8_t scancode[256]; + +enum XScancode { + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + ScrollLock, Pause, Tilde, + Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, + Dash, Equal, Backspace, + Insert, Delete, Home, End, PageUp, PageDown, + A, B, C, D, E, F, G, H, I, J, K, L, M, + N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, + Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, + Point, Enter, Add, Subtract, Multiply, Divide, + Up, Down, Left, Right, + Tab, Return, Spacebar, Menu, + LeftShift, RightShift, LeftControl, RightControl, LeftAlt, RightAlt, LeftSuper, RightSuper, +}; + +void x_poll(int16_t *table) { + char state[32]; + Display *display = XOpenDisplay(0); + XQueryKeymap(display, state); + XCloseDisplay(display); + + #define key(id) table[keyboard(0)[id]] + #define pressed(id) (bool)(state[scancode[id] >> 3] & (1 << (scancode[id] & 7))) + + key(Keyboard::Escape) = pressed(Escape); + + key(Keyboard::F1) = pressed(F1); + key(Keyboard::F2) = pressed(F2); + key(Keyboard::F3) = pressed(F3); + key(Keyboard::F4) = pressed(F4); + key(Keyboard::F5) = pressed(F5); + key(Keyboard::F6) = pressed(F6); + key(Keyboard::F7) = pressed(F7); + key(Keyboard::F8) = pressed(F8); + key(Keyboard::F9) = pressed(F9); + key(Keyboard::F10) = pressed(F10); + key(Keyboard::F11) = pressed(F11); + key(Keyboard::F12) = pressed(F12); + + key(Keyboard::ScrollLock) = pressed(ScrollLock); + key(Keyboard::Pause) = pressed(Pause); + key(Keyboard::Tilde) = pressed(Tilde); + + key(Keyboard::Num1) = pressed(Num1); + key(Keyboard::Num2) = pressed(Num2); + key(Keyboard::Num3) = pressed(Num3); + key(Keyboard::Num4) = pressed(Num4); + key(Keyboard::Num5) = pressed(Num5); + key(Keyboard::Num6) = pressed(Num6); + key(Keyboard::Num7) = pressed(Num7); + key(Keyboard::Num8) = pressed(Num8); + key(Keyboard::Num9) = pressed(Num9); + key(Keyboard::Num0) = pressed(Num0); + + key(Keyboard::Dash) = pressed(Dash); + key(Keyboard::Equal) = pressed(Equal); + key(Keyboard::Backspace) = pressed(Backspace); + + key(Keyboard::Insert) = pressed(Insert); + key(Keyboard::Delete) = pressed(Delete); + key(Keyboard::Home) = pressed(Home); + key(Keyboard::End) = pressed(End); + key(Keyboard::PageUp) = pressed(PageUp); + key(Keyboard::PageDown) = pressed(PageDown); + + key(Keyboard::A) = pressed(A); + key(Keyboard::B) = pressed(B); + key(Keyboard::C) = pressed(C); + key(Keyboard::D) = pressed(D); + key(Keyboard::E) = pressed(E); + key(Keyboard::F) = pressed(F); + key(Keyboard::G) = pressed(G); + key(Keyboard::H) = pressed(H); + key(Keyboard::I) = pressed(I); + key(Keyboard::J) = pressed(J); + key(Keyboard::K) = pressed(K); + key(Keyboard::L) = pressed(L); + key(Keyboard::M) = pressed(M); + key(Keyboard::N) = pressed(N); + key(Keyboard::O) = pressed(O); + key(Keyboard::P) = pressed(P); + key(Keyboard::Q) = pressed(Q); + key(Keyboard::R) = pressed(R); + key(Keyboard::S) = pressed(S); + key(Keyboard::T) = pressed(T); + key(Keyboard::U) = pressed(U); + key(Keyboard::V) = pressed(V); + key(Keyboard::W) = pressed(W); + key(Keyboard::X) = pressed(X); + key(Keyboard::Y) = pressed(Y); + key(Keyboard::Z) = pressed(Z); + + key(Keyboard::LeftBracket) = pressed(LeftBracket); + key(Keyboard::RightBracket) = pressed(RightBracket); + key(Keyboard::Backslash) = pressed(Backslash); + key(Keyboard::Semicolon) = pressed(Semicolon); + key(Keyboard::Apostrophe) = pressed(Apostrophe); + key(Keyboard::Comma) = pressed(Comma); + key(Keyboard::Period) = pressed(Period); + key(Keyboard::Slash) = pressed(Slash); + + key(Keyboard::Keypad1) = pressed(Keypad1); + key(Keyboard::Keypad2) = pressed(Keypad2); + key(Keyboard::Keypad3) = pressed(Keypad3); + key(Keyboard::Keypad4) = pressed(Keypad4); + key(Keyboard::Keypad5) = pressed(Keypad5); + key(Keyboard::Keypad6) = pressed(Keypad6); + key(Keyboard::Keypad7) = pressed(Keypad7); + key(Keyboard::Keypad8) = pressed(Keypad8); + key(Keyboard::Keypad9) = pressed(Keypad9); + key(Keyboard::Keypad0) = pressed(Keypad0); + + key(Keyboard::Point) = pressed(Point); + key(Keyboard::Enter) = pressed(Enter); + key(Keyboard::Add) = pressed(Add); + key(Keyboard::Subtract) = pressed(Subtract); + key(Keyboard::Multiply) = pressed(Multiply); + key(Keyboard::Divide) = pressed(Divide); + + key(Keyboard::Up) = pressed(Up); + key(Keyboard::Down) = pressed(Down); + key(Keyboard::Left) = pressed(Left); + key(Keyboard::Right) = pressed(Right); + + key(Keyboard::Tab) = pressed(Tab); + key(Keyboard::Return) = pressed(Return); + key(Keyboard::Spacebar) = pressed(Spacebar); + key(Keyboard::Menu) = pressed(Menu); + + key(Keyboard::Shift) = pressed(LeftShift) || pressed(RightShift); + key(Keyboard::Control) = pressed(LeftControl) || pressed(RightControl); + key(Keyboard::Alt) = pressed(LeftAlt) || pressed(RightAlt); + key(Keyboard::Super) = pressed(LeftSuper) || pressed(RightSuper); + + #undef key + #undef pressed +} + +void x_init() { + Display *display = XOpenDisplay(0); + memset(&scancode, 0, sizeof scancode); + + #define assign(x, y) scancode[x] = XKeysymToKeycode(display, y) + assign(Escape, XK_Escape); + + assign(F1, XK_F1); + assign(F2, XK_F2); + assign(F3, XK_F3); + assign(F4, XK_F4); + assign(F5, XK_F5); + assign(F6, XK_F6); + assign(F7, XK_F7); + assign(F8, XK_F8); + assign(F9, XK_F9); + assign(F10, XK_F10); + assign(F11, XK_F11); + assign(F12, XK_F12); + + assign(ScrollLock, XK_Scroll_Lock); + assign(Pause, XK_Pause); + + assign(Tilde, XK_asciitilde); + + assign(Num0, XK_0); + assign(Num1, XK_1); + assign(Num2, XK_2); + assign(Num3, XK_3); + assign(Num4, XK_4); + assign(Num5, XK_5); + assign(Num6, XK_6); + assign(Num7, XK_7); + assign(Num8, XK_8); + assign(Num9, XK_9); + + assign(Dash, XK_minus); + assign(Equal, XK_equal); + assign(Backspace, XK_BackSpace); + + assign(Insert, XK_Insert); + assign(Delete, XK_Delete); + assign(Home, XK_Home); + assign(End, XK_End); + assign(PageUp, XK_Prior); + assign(PageDown, XK_Next); + + assign(A, XK_A); + assign(B, XK_B); + assign(C, XK_C); + assign(D, XK_D); + assign(E, XK_E); + assign(F, XK_F); + assign(G, XK_G); + assign(H, XK_H); + assign(I, XK_I); + assign(J, XK_J); + assign(K, XK_K); + assign(L, XK_L); + assign(M, XK_M); + assign(N, XK_N); + assign(O, XK_O); + assign(P, XK_P); + assign(Q, XK_Q); + assign(R, XK_R); + assign(S, XK_S); + assign(T, XK_T); + assign(U, XK_U); + assign(V, XK_V); + assign(W, XK_W); + assign(X, XK_X); + assign(Y, XK_Y); + assign(Z, XK_Z); + + assign(LeftBracket, XK_bracketleft); + assign(RightBracket, XK_bracketright); + assign(Backslash, XK_backslash); + assign(Semicolon, XK_semicolon); + assign(Apostrophe, XK_apostrophe); + assign(Comma, XK_comma); + assign(Period, XK_period); + assign(Slash, XK_slash); + + assign(Keypad0, XK_KP_0); + assign(Keypad1, XK_KP_1); + assign(Keypad2, XK_KP_2); + assign(Keypad3, XK_KP_3); + assign(Keypad4, XK_KP_4); + assign(Keypad5, XK_KP_5); + assign(Keypad6, XK_KP_6); + assign(Keypad7, XK_KP_7); + assign(Keypad8, XK_KP_8); + assign(Keypad9, XK_KP_9); + + assign(Add, XK_KP_Add); + assign(Subtract, XK_KP_Subtract); + assign(Multiply, XK_KP_Multiply); + assign(Divide, XK_KP_Divide); + assign(Enter, XK_KP_Enter); + + assign(Up, XK_Up); + assign(Down, XK_Down); + assign(Left, XK_Left); + assign(Right, XK_Right); + + assign(Tab, XK_Tab); + assign(Return, XK_Return); + assign(Spacebar, XK_space); + + assign(LeftControl, XK_Control_L); + assign(RightControl, XK_Control_R); + assign(LeftAlt, XK_Alt_L); + assign(RightAlt, XK_Alt_R); + assign(LeftShift, XK_Shift_L); + assign(RightShift, XK_Shift_R); + assign(LeftSuper, XK_Super_L); + assign(RightSuper, XK_Super_R); + assign(Menu, XK_Menu); + + #undef assign + + XCloseDisplay(display); +} diff --git a/mednafen/snes/src/lib/ruby/ruby.cpp b/mednafen/snes/src/lib/ruby/ruby.cpp new file mode 100755 index 0000000..5239597 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/ruby.cpp @@ -0,0 +1,370 @@ +#include +using namespace nall; + +#include + +namespace ruby { + +VideoInterface video; +AudioInterface audio; +InputInterface input; + +/* VideoInterface */ + +const char *Video::Handle = "Handle"; +const char *Video::Synchronize = "Synchronize"; +const char *Video::Filter = "Filter"; +const char *Video::FragmentShader = "FragmentShader"; +const char *Video::VertexShader = "VertexShader"; + +void VideoInterface::driver(const char *driver) { + if(p) term(); + + if(!driver || !*driver) driver = default_driver(); + + if(0); + + #ifdef VIDEO_DIRECT3D + else if(!strcmp(driver, "Direct3D")) p = new VideoD3D(); + #endif + + #ifdef VIDEO_DIRECTDRAW + else if(!strcmp(driver, "DirectDraw")) p = new VideoDD(); + #endif + + #ifdef VIDEO_GDI + else if(!strcmp(driver, "GDI")) p = new VideoGDI(); + #endif + + #ifdef VIDEO_GLX + else if(!strcmp(driver, "OpenGL")) p = new VideoGLX(); + #endif + + #ifdef VIDEO_QTOPENGL + else if(!strcmp(driver, "Qt-OpenGL")) p = new VideoQtOpenGL(); + #endif + + #ifdef VIDEO_QTRASTER + else if(!strcmp(driver, "Qt-Raster")) p = new VideoQtRaster(); + #endif + + #ifdef VIDEO_SDL + else if(!strcmp(driver, "SDL")) p = new VideoSDL(); + #endif + + #ifdef VIDEO_WGL + else if(!strcmp(driver, "OpenGL")) p = new VideoWGL(); + #endif + + #ifdef VIDEO_XV + else if(!strcmp(driver, "X-Video")) p = new VideoXv(); + #endif + + else p = new Video(); +} + +//select the *safest* available driver, not the fastest +const char* VideoInterface::default_driver() { + #if defined(VIDEO_DIRECT3D) + return "Direct3D"; + #elif defined(VIDEO_WGL) + return "OpenGL"; + #elif defined(VIDEO_DIRECTDRAW) + return "DirectDraw"; + #elif defined(VIDEO_GDI) + return "GDI"; + #elif defined(VIDEO_QTOPENGL) + return "Qt-OpenGL"; + #elif defined(VIDEO_QTRASTER) + return "Qt-Raster"; + #elif defined(VIDEO_SDL) + return "SDL"; + #elif defined(VIDEO_XV) + return "X-Video"; + #elif defined(VIDEO_GLX) + return "OpenGL"; + #else + return "None"; + #endif +} + +//returns list of available drivers, sorted from most to least optimal +const char* VideoInterface::driver_list() { + return + + //Windows + + #if defined(VIDEO_DIRECT3D) + "Direct3D;" + #endif + + #if defined(VIDEO_WGL) + "OpenGL;" + #endif + + #if defined(VIDEO_DIRECTDRAW) + "DirectDraw;" + #endif + + #if defined(VIDEO_GDI) + "GDI;" + #endif + + //Linux + + #if defined(VIDEO_GLX) + "OpenGL;" + #endif + + #if defined(VIDEO_QTOPENGL) + "Qt-OpenGL;" + #endif + + #if defined(VIDEO_XV) + "X-Video;" + #endif + + #if defined(VIDEO_QTRASTER) + "Qt-Raster;" + #endif + + #if defined(VIDEO_SDL) + "SDL;" + #endif + + "None"; +} + +bool VideoInterface::init() { + if(!p) driver(); + return p->init(); +} + +void VideoInterface::term() { + if(p) { + delete p; + p = 0; + } +} + +bool VideoInterface::cap(const string& name) { return p ? p->cap(name) : false; } +any VideoInterface::get(const string& name) { return p ? p->get(name) : false; } +bool VideoInterface::set(const string& name, const any& value) { return p ? p->set(name, value) : false; } +bool VideoInterface::lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { return p ? p->lock(data, pitch, width, height) : false; } +void VideoInterface::unlock() { if(p) p->unlock(); } +void VideoInterface::clear() { if(p) p->clear(); } +void VideoInterface::refresh() { if(p) p->refresh(); } +VideoInterface::VideoInterface() : p(0) {} +VideoInterface::~VideoInterface() { term(); } + +/* AudioInterface */ + +void AudioInterface::driver(const char *driver) { + if(p) term(); + + if(!driver || !*driver) driver = default_driver(); + + if(0); + + #ifdef AUDIO_ALSA + else if(!strcmp(driver, "ALSA")) p = new AudioALSA(); + #endif + + #ifdef AUDIO_AO + else if(!strcmp(driver, "libao")) p = new AudioAO(); + #endif + + #ifdef AUDIO_DIRECTSOUND + else if(!strcmp(driver, "DirectSound")) p = new AudioDS(); + #endif + + #ifdef AUDIO_OPENAL + else if(!strcmp(driver, "OpenAL")) p = new AudioOpenAL(); + #endif + + #ifdef AUDIO_OSS + else if(!strcmp(driver, "OSS")) p = new AudioOSS(); + #endif + + #ifdef AUDIO_PULSEAUDIO + else if(!strcmp(driver, "PulseAudio")) p = new AudioPulseAudio(); + #endif + + #ifdef AUDIO_PULSEAUDIOSIMPLE + else if(!strcmp(driver, "PulseAudioSimple")) p = new AudioPulseAudioSimple(); + #endif + + else p = new Audio(); +} + +//select the *safest* available driver, not the fastest +const char* AudioInterface::default_driver() { + #if defined(AUDIO_DIRECTSOUND) + return "DirectSound"; + #elif defined(AUDIO_ALSA) + return "ALSA"; + #elif defined(AUDIO_OPENAL) + return "OpenAL"; + #elif defined(AUDIO_PULSEAUDIO) + return "PulseAudio"; + #elif defined(AUDIO_PULSEAUDIOSIMPLE) + return "PulseAudioSimple"; + #elif defined(AUDIO_AO) + return "libao"; + #elif defined(AUDIO_OSS) + return "OSS"; + #else + return "None"; + #endif +} + +//returns list of available drivers, sorted from most to least optimal +const char* AudioInterface::driver_list() { + return + + //Windows + + #if defined(AUDIO_DIRECTSOUND) + "DirectSound;" + #endif + + //Linux + + #if defined(AUDIO_ALSA) + "ALSA;" + #endif + + #if defined(AUDIO_OPENAL) + "OpenAL;" + #endif + + #if defined(AUDIO_OSS) + "OSS;" + #endif + + #if defined(AUDIO_PULSEAUDIO) + "PulseAudio;" + #endif + + #if defined(AUDIO_PULSEAUDIOSIMPLE) + "PulseAudioSimple;" + #endif + + #if defined(AUDIO_AO) + "libao;" + #endif + + "None"; +} + +#include "ruby_audio.cpp" + +/* InputInterface */ + +const char *Input::Handle = "Handle"; +const char *Input::KeyboardSupport = "KeyboardSupport"; +const char *Input::MouseSupport = "MouseSupport"; +const char *Input::JoypadSupport = "JoypadSupport"; + +void InputInterface::driver(const char *driver) { + if(p) term(); + + if(!driver || !*driver) driver = default_driver(); + + if(0); + + #ifdef INPUT_DIRECTINPUT + else if(!strcmp(driver, "DirectInput")) p = new InputDI(); + #endif + + #ifdef INPUT_RAWINPUT + else if(!strcmp(driver, "RawInput")) p = new InputRaw(); + #endif + + #ifdef INPUT_SDL + else if(!strcmp(driver, "SDL")) p = new InputSDL(); + #endif + + #ifdef INPUT_X + else if(!strcmp(driver, "X-Windows")) p = new InputX(); + #endif + + #ifdef INPUT_CARBON + else if(!strcmp(driver, "Carbon")) p = new InputCarbon(); + #endif + + else p = new Input(); +} + +//select the *safest* available driver, not the fastest +const char* InputInterface::default_driver() { + #if defined(INPUT_RAWINPUT) + return "RawInput"; + #elif defined(INPUT_DIRECTINPUT) + return "DirectInput"; + #elif defined(INPUT_SDL) + return "SDL"; + #elif defined(INPUT_X) + return "X-Windows"; + #elif defined(INPUT_CARBON) + return "Carbon"; + #else + return "none"; + #endif +} + +const char* InputInterface::driver_list() { + return + + //Windows + + #if defined(INPUT_RAWINPUT) + "RawInput;" + #endif + + #if defined(INPUT_DIRECTINPUT) + "DirectInput;" + #endif + + //Linux + + #if defined(INPUT_SDL) + "SDL;" + #endif + + #if defined(INPUT_X) + "X-Windows;" + #endif + + //OS X + + #if defined(INPUT_CARBON) + "Carbon;" + #endif + + "None"; +} + +bool InputInterface::init() { + if(!p) driver(); + return p->init(); +} + +void InputInterface::term() { + if(p) { + delete p; + p = 0; + } +} + +bool InputInterface::cap(const string& name) { return p ? p->cap(name) : false; } +any InputInterface::get(const string& name) { return p ? p->get(name) : false; } +bool InputInterface::set(const string& name, const any& value) { return p ? p->set(name, value) : false; } +bool InputInterface::acquire() { return p ? p->acquire() : false; } +bool InputInterface::unacquire() { return p ? p->unacquire() : false; } +bool InputInterface::acquired() { return p ? p->acquired() : false; } +bool InputInterface::poll(int16_t *table) { return p ? p->poll(table) : false; } +InputInterface::InputInterface() : p(0) {} +InputInterface::~InputInterface() { term(); } + +}; diff --git a/mednafen/snes/src/lib/ruby/ruby.hpp b/mednafen/snes/src/lib/ruby/ruby.hpp new file mode 100755 index 0000000..4d1b1a3 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/ruby.hpp @@ -0,0 +1,109 @@ +/* + ruby + version: 0.06 (2009-05-22) + license: public domain +*/ + +#ifndef RUBY_H +#define RUBY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ruby { + +#include +#include +#include + +class VideoInterface { +public: + void driver(const char *driver = ""); + const char* default_driver(); + const char* driver_list(); + bool init(); + void term(); + + bool cap(const nall::string& name); + nall::any get(const nall::string& name); + bool set(const nall::string& name, const nall::any& value); + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height); + void unlock(); + void clear(); + void refresh(); + VideoInterface(); + ~VideoInterface(); + +private: + Video *p; +}; + +class AudioInterface { +public: + void driver(const char *driver = ""); + const char* default_driver(); + const char* driver_list(); + bool init(); + void term(); + + bool cap(const nall::string& name); + nall::any get(const nall::string& name); + bool set(const nall::string& name, const nall::any& value); + + void sample(uint16_t left, uint16_t right); + void clear(); + AudioInterface(); + ~AudioInterface(); + +private: + Audio *p; + + unsigned volume; + + //resample unit + double hermite(double mu, double a, double b, double c, double d); + bool resample_enabled; + double r_step, r_frac; + int r_left[4], r_right[4]; +}; + +class InputInterface { +public: + void driver(const char *driver = ""); + const char* default_driver(); + const char* driver_list(); + bool init(); + void term(); + + bool cap(const nall::string& name); + nall::any get(const nall::string& name); + bool set(const nall::string& name, const nall::any& value); + + bool acquire(); + bool unacquire(); + bool acquired(); + + bool poll(int16_t *table); + InputInterface(); + ~InputInterface(); + +private: + Input *p; +}; + +extern VideoInterface video; +extern AudioInterface audio; +extern InputInterface input; + +}; + +#endif diff --git a/mednafen/snes/src/lib/ruby/ruby_audio.cpp b/mednafen/snes/src/lib/ruby/ruby_audio.cpp new file mode 100755 index 0000000..4afe1f5 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/ruby_audio.cpp @@ -0,0 +1,133 @@ +const char *Audio::Volume = "Volume"; +const char *Audio::Resample = "Resample"; +const char *Audio::ResampleRatio = "ResampleRatio"; + +const char *Audio::Handle = "Handle"; +const char *Audio::Synchronize = "Synchronize"; +const char *Audio::Frequency = "Frequency"; +const char *Audio::Latency = "Latency"; + +bool AudioInterface::init() { + if(!p) driver(); + return p->init(); +} + +void AudioInterface::term() { + if(p) { + delete p; + p = 0; + } +} + +bool AudioInterface::cap(const string& name) { + if(name == Audio::Volume) return true; + if(name == Audio::Resample) return true; + if(name == Audio::ResampleRatio) return true; + + return p ? p->cap(name) : false; +} + +any AudioInterface::get(const string& name) { + if(name == Audio::Volume) return volume; + if(name == Audio::Resample) return resample_enabled; + if(name == Audio::ResampleRatio); + + return p ? p->get(name) : false; +} + +bool AudioInterface::set(const string& name, const any& value) { + if(name == Audio::Volume) { + volume = any_cast(value); + return true; + } + + if(name == Audio::Resample) { + resample_enabled = any_cast(value); + return true; + } + + if(name == Audio::ResampleRatio) { + r_step = any_cast(value); + r_frac = 0; + return true; + } + + return p ? p->set(name, value) : false; +} + +//4-tap hermite interpolation +double AudioInterface::hermite(double mu1, double a, double b, double c, double d) { + const double tension = 0.0; //-1 = low, 0 = normal, 1 = high + const double bias = 0.0; //-1 = left, 0 = even, 1 = right + + double mu2, mu3, m0, m1, a0, a1, a2, a3; + + mu2 = mu1 * mu1; + mu3 = mu2 * mu1; + + m0 = (b - a) * (1 + bias) * (1 - tension) / 2; + m0 += (c - b) * (1 - bias) * (1 - tension) / 2; + m1 = (c - b) * (1 + bias) * (1 - tension) / 2; + m1 += (d - c) * (1 - bias) * (1 - tension) / 2; + + a0 = +2 * mu3 - 3 * mu2 + 1; + a1 = mu3 - 2 * mu2 + mu1; + a2 = mu3 - mu2; + a3 = -2 * mu3 + 3 * mu2; + + return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); +} + +void AudioInterface::sample(uint16_t left, uint16_t right) { + int s_left = (int16_t)left; + int s_right = (int16_t)right; + + if(volume != 100) { + s_left = sclamp<16>((double)s_left * (double)volume / 100.0); + s_right = sclamp<16>((double)s_right * (double)volume / 100.0); + } + + r_left [0] = r_left [1]; + r_left [1] = r_left [2]; + r_left [2] = r_left [3]; + r_left [3] = s_left; + + r_right[0] = r_right[1]; + r_right[1] = r_right[2]; + r_right[2] = r_right[3]; + r_right[3] = s_right; + + if(resample_enabled == false) { + if(p) p->sample(left, right); + return; + } + + while(r_frac <= 1.0) { + int output_left = sclamp<16>(hermite(r_frac, r_left [0], r_left [1], r_left [2], r_left [3])); + int output_right = sclamp<16>(hermite(r_frac, r_right[0], r_right[1], r_right[2], r_right[3])); + r_frac += r_step; + if(p) p->sample(output_left, output_right); + } + + r_frac -= 1.0; +} + +void AudioInterface::clear() { + r_frac = 0; + r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0; + r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0; + if(p) p->clear(); +} + +AudioInterface::AudioInterface() { + p = 0; + volume = 100; + resample_enabled = false; + r_step = r_frac = 0; + r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0; + r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0; +} + +AudioInterface::~AudioInterface() { + term(); +} diff --git a/mednafen/snes/src/lib/ruby/ruby_impl.cpp b/mednafen/snes/src/lib/ruby/ruby_impl.cpp new file mode 100755 index 0000000..d5be3c3 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/ruby_impl.cpp @@ -0,0 +1,177 @@ +/* Global Headers */ + +#if defined(VIDEO_QTOPENGL) || defined(VIDEO_QTRASTER) + #include + #include +#endif + +#if defined(VIDEO_QTOPENGL) + #include + #if defined(PLATFORM_WIN) + #include + #endif +#endif + +#if defined(PLATFORM_X) + #include + #include + #include +#elif defined(PLATFORM_OSX) + #include +#elif defined(PLATFORM_WIN) + #define _WIN32_WINNT 0x0501 + #include +#endif + +/* Video */ + +#define DeclareVideo(Name) \ + class Video##Name : public Video { \ + public: \ + bool cap(const string& name) { return p.cap(name); } \ + any get(const string& name) { return p.get(name); } \ + bool set(const string& name, const any& value) { return p.set(name, value); } \ + \ + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { return p.lock(data, pitch, width, height); } \ + void unlock() { p.unlock(); } \ + \ + void clear() { p.clear(); } \ + void refresh() { p.refresh(); } \ + bool init() { return p.init(); } \ + void term() { p.term(); } \ + \ + Video##Name() : p(*new pVideo##Name) {} \ + ~Video##Name() { delete &p; } \ + \ + private: \ + pVideo##Name &p; \ + }; + +#ifdef VIDEO_DIRECT3D + #include +#endif + +#ifdef VIDEO_DIRECTDRAW + #include +#endif + +#ifdef VIDEO_GDI + #include +#endif + +#ifdef VIDEO_GLX + #include +#endif + +#ifdef VIDEO_QTOPENGL + #include +#endif + +#ifdef VIDEO_QTRASTER + #include +#endif + +#ifdef VIDEO_SDL + #include +#endif + +#ifdef VIDEO_WGL + #include +#endif + +#ifdef VIDEO_XV + #include +#endif + +/* Audio */ + +#define DeclareAudio(Name) \ + class Audio##Name : public Audio { \ + public: \ + bool cap(const string& name) { return p.cap(name); } \ + any get(const string& name) { return p.get(name); } \ + bool set(const string& name, const any& value) { return p.set(name, value); } \ + \ + void sample(uint16_t left, uint16_t right) { p.sample(left, right); } \ + void clear() { p.clear(); } \ + bool init() { return p.init(); } \ + void term() { p.term(); } \ + \ + Audio##Name() : p(*new pAudio##Name) {} \ + ~Audio##Name() { delete &p; } \ + \ + private: \ + pAudio##Name &p; \ + }; + +#ifdef AUDIO_ALSA + #include +#endif + +#ifdef AUDIO_AO + #include +#endif + +#ifdef AUDIO_DIRECTSOUND + #include +#endif + +#ifdef AUDIO_OPENAL + #include +#endif + +#ifdef AUDIO_OSS + #include +#endif + +#ifdef AUDIO_PULSEAUDIO + #include +#endif + +#ifdef AUDIO_PULSEAUDIOSIMPLE + #include +#endif + +/* Input */ + +#define DeclareInput(Name) \ + class Input##Name : public Input { \ + public: \ + bool cap(const string& name) { return p.cap(name); } \ + any get(const string& name) { return p.get(name); } \ + bool set(const string& name, const any& value) { return p.set(name, value); } \ + \ + bool acquire() { return p.acquire(); } \ + bool unacquire() { return p.unacquire(); } \ + bool acquired() { return p.acquired(); } \ + \ + bool poll(int16_t *table) { return p.poll(table); } \ + bool init() { return p.init(); } \ + void term() { p.term(); } \ + \ + Input##Name() : p(*new pInput##Name) {} \ + ~Input##Name() { delete &p; } \ + \ + private: \ + pInput##Name &p; \ + }; + +#ifdef INPUT_DIRECTINPUT + #include +#endif + +#ifdef INPUT_RAWINPUT + #include +#endif + +#ifdef INPUT_SDL + #include +#endif + +#ifdef INPUT_X + #include +#endif + +#ifdef INPUT_CARBON + #include +#endif diff --git a/mednafen/snes/src/lib/ruby/video.hpp b/mednafen/snes/src/lib/ruby/video.hpp new file mode 100755 index 0000000..e7f0f82 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video.hpp @@ -0,0 +1,28 @@ +class Video { +public: + static const char *Handle; + static const char *Synchronize; + static const char *Filter; + static const char *FragmentShader; + static const char *VertexShader; + + enum Filter { + FilterPoint, + FilterLinear, + }; + + virtual bool cap(const nall::string& name) { return false; } + virtual nall::any get(const nall::string& name) { return false; } + virtual bool set(const nall::string& name, const nall::any& value) { return false; } + + virtual bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { return false; } + virtual void unlock() {} + + virtual void clear() {} + virtual void refresh() {} + virtual bool init() { return true; } + virtual void term() {} + + Video() {} + virtual ~Video() {} +}; diff --git a/mednafen/snes/src/lib/ruby/video/direct3d.cpp b/mednafen/snes/src/lib/ruby/video/direct3d.cpp new file mode 100755 index 0000000..b138b3b --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video/direct3d.cpp @@ -0,0 +1,388 @@ +#undef interface +#define interface struct +#include +#undef interface + +#define D3DVERTEX (D3DFVF_XYZRHW | D3DFVF_TEX1) + +namespace ruby { + +class pVideoD3D { +public: + LPDIRECT3D9 lpd3d; + LPDIRECT3DDEVICE9 device; + LPDIRECT3DVERTEXBUFFER9 vertex_buffer, *vertex_ptr; + D3DPRESENT_PARAMETERS presentation; + D3DSURFACE_DESC d3dsd; + D3DLOCKED_RECT d3dlr; + D3DRASTER_STATUS d3drs; + D3DCAPS9 d3dcaps; + LPDIRECT3DTEXTURE9 texture; + LPDIRECT3DSURFACE9 surface; + bool lost; + unsigned iwidth, iheight; + + struct d3dvertex { + float x, y, z, rhw; //screen coords + float u, v; //texture coords + }; + + struct { + uint32_t t_usage, v_usage; + uint32_t t_pool, v_pool; + uint32_t lock; + uint32_t filter; + } flags; + + struct { + bool dynamic; //device supports dynamic textures + bool stretchrect; //device supports StretchRect + } caps; + + struct { + HWND handle; + bool synchronize; + unsigned filter; + + unsigned width; + unsigned height; + } settings; + + struct { + unsigned width; + unsigned height; + } state; + + bool cap(const string& name) { + if(name == Video::Handle) return true; + if(name == Video::Synchronize) return true; + if(name == Video::Filter) return true; + return false; + } + + any get(const string& name) { + if(name == Video::Handle) return (uintptr_t)settings.handle; + if(name == Video::Synchronize) return settings.synchronize; + if(name == Video::Filter) return settings.filter; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Video::Handle) { + settings.handle = (HWND)any_cast(value); + return true; + } + + if(name == Video::Synchronize) { + settings.synchronize = any_cast(value); + return true; + } + + if(name == Video::Filter) { + settings.filter = any_cast(value); + if(lpd3d) update_filter(); + return true; + } + + return false; + } + + bool recover() { + if(!device) return false; + + if(lost) { + release_resources(); + if(device->Reset(&presentation) != D3D_OK) return false; + } + + lost = false; + + device->SetDialogBoxMode(false); + + device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); + device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + + device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + + device->SetRenderState(D3DRS_LIGHTING, false); + device->SetRenderState(D3DRS_ZENABLE, false); + device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); + + device->SetVertexShader(NULL); + device->SetFVF(D3DVERTEX); + + device->CreateVertexBuffer(sizeof(d3dvertex) * 4, flags.v_usage, D3DVERTEX, + static_cast(flags.v_pool), &vertex_buffer, NULL); + iwidth = 0; + iheight = 0; + resize(settings.width = 256, settings.height = 256); + update_filter(); + clear(); + return true; + } + + unsigned rounded_power_of_two(unsigned n) { + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + return n + 1; + } + + void resize(unsigned width, unsigned height) { + if(iwidth >= width && iheight >= height) return; + + iwidth = rounded_power_of_two(max(width, iwidth )); + iheight = rounded_power_of_two(max(height, iheight)); + + if(d3dcaps.MaxTextureWidth < iwidth || d3dcaps.MaxTextureWidth < iheight) { + //TODO: attempt to handle this more gracefully + return; + } + + if(caps.stretchrect == true) { + if(surface) surface->Release(); + device->CreateOffscreenPlainSurface(iwidth, iheight, D3DFMT_X8R8G8B8, + D3DPOOL_DEFAULT, &surface, NULL); + } else { + if(texture) texture->Release(); + device->CreateTexture(iwidth, iheight, 1, flags.t_usage, D3DFMT_X8R8G8B8, + static_cast(flags.t_pool), &texture, NULL); + } + } + + void update_filter() { + if(!device) return; + if(lost && !recover()) return; + + switch(settings.filter) { default: + case Video::FilterPoint: flags.filter = D3DTEXF_POINT; break; + case Video::FilterLinear: flags.filter = D3DTEXF_LINEAR; break; + } + + device->SetSamplerState(0, D3DSAMP_MINFILTER, flags.filter); + device->SetSamplerState(0, D3DSAMP_MAGFILTER, flags.filter); + } + + // Vertex format: + // + // 0----------1 + // | /| + // | / | + // | / | + // | / | + // | / | + // 2----------3 + // + // (x,y) screen coords, in pixels + // (u,v) texture coords, betweeen 0.0 (top, left) to 1.0 (bottom, right) + void set_vertex( + uint32_t px, uint32_t py, uint32_t pw, uint32_t ph, + uint32_t tw, uint32_t th, + uint32_t x, uint32_t y, uint32_t w, uint32_t h + ) { + d3dvertex vertex[4]; + vertex[0].x = vertex[2].x = (double)(x - 0.5); + vertex[1].x = vertex[3].x = (double)(x + w - 0.5); + vertex[0].y = vertex[1].y = (double)(y - 0.5); + vertex[2].y = vertex[3].y = (double)(y + h - 0.5); + + //Z-buffer and RHW are unused for 2D blit, set to normal values + vertex[0].z = vertex[1].z = vertex[2].z = vertex[3].z = 0.0; + vertex[0].rhw = vertex[1].rhw = vertex[2].rhw = vertex[3].rhw = 1.0; + + double rw = (double)w / (double)pw * (double)tw; + double rh = (double)h / (double)ph * (double)th; + vertex[0].u = vertex[2].u = (double)(px ) / rw; + vertex[1].u = vertex[3].u = (double)(px + w) / rw; + vertex[0].v = vertex[1].v = (double)(py ) / rh; + vertex[2].v = vertex[3].v = (double)(py + h) / rh; + + vertex_buffer->Lock(0, sizeof(d3dvertex) * 4, (void**)&vertex_ptr, 0); + memcpy(vertex_ptr, vertex, sizeof(d3dvertex) * 4); + vertex_buffer->Unlock(); + + device->SetStreamSource(0, vertex_buffer, 0, sizeof(d3dvertex)); + } + + void clear() { + if(lost && !recover()) return; + + if(caps.stretchrect == false) { + texture->GetLevelDesc(0, &d3dsd); + texture->GetSurfaceLevel(0, &surface); + } + + if(surface) { + device->ColorFill(surface, 0, D3DCOLOR_XRGB(0x00, 0x00, 0x00)); + if(caps.stretchrect == false) surface->Release(); + } + + //clear primary display and all backbuffers + for(unsigned i = 0; i < 3; i++) { + device->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0x00, 0x00, 0x00), 1.0f, 0); + device->Present(0, 0, 0, 0); + } + } + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { + if(lost && !recover()) return false; + + if(width != settings.width || height != settings.height) { + resize(settings.width = width, settings.height = height); + } + + if(caps.stretchrect == false) { + texture->GetLevelDesc(0, &d3dsd); + texture->GetSurfaceLevel(0, &surface); + } + + surface->LockRect(&d3dlr, 0, flags.lock); + pitch = d3dlr.Pitch; + return data = (uint32_t*)d3dlr.pBits; + } + + void unlock() { + surface->UnlockRect(); + if(caps.stretchrect == false) surface->Release(); + } + + void refresh() { + if(lost && !recover()) return; + + RECT rd, rs; //dest, source rectangles + GetClientRect(settings.handle, &rd); + SetRect(&rs, 0, 0, settings.width, settings.height); + + //if output size changed, driver must be re-initialized. + //failure to do so causes scaling issues on some video drivers. + if(state.width != rd.right || state.height != rd.bottom) { + init(); + return; + } + + device->BeginScene(); + + if(caps.stretchrect == true) { + LPDIRECT3DSURFACE9 temp; + device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &temp); + device->StretchRect(surface, &rs, temp, 0, static_cast(flags.filter)); + temp->Release(); + } else { + set_vertex(0, 0, settings.width, settings.height, iwidth, iheight, 0, 0, rd.right, rd.bottom); + device->SetTexture(0, texture); + device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + } + + device->EndScene(); + + if(settings.synchronize) { + while(true) { + D3DRASTER_STATUS status; + device->GetRasterStatus(0, &status); + if(status.InVBlank == true) break; + } + } + + if(device->Present(0, 0, 0, 0) == D3DERR_DEVICELOST) lost = true; + } + + bool init() { + term(); + + RECT rd; + GetClientRect(settings.handle, &rd); + state.width = rd.right; + state.height = rd.bottom; + + lpd3d = Direct3DCreate9(D3D_SDK_VERSION); + if(!lpd3d) return false; + + memset(&presentation, 0, sizeof(presentation)); + presentation.Flags = D3DPRESENTFLAG_VIDEO; + presentation.SwapEffect = D3DSWAPEFFECT_FLIP; + presentation.hDeviceWindow = settings.handle; + presentation.BackBufferCount = 1; + presentation.MultiSampleType = D3DMULTISAMPLE_NONE; + presentation.MultiSampleQuality = 0; + presentation.EnableAutoDepthStencil = false; + presentation.AutoDepthStencilFormat = D3DFMT_UNKNOWN; + presentation.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + presentation.Windowed = true; + presentation.BackBufferFormat = D3DFMT_UNKNOWN; + presentation.BackBufferWidth = 0; + presentation.BackBufferHeight = 0; + + if(lpd3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, settings.handle, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentation, &device) != D3D_OK) { + return false; + } + + device->GetDeviceCaps(&d3dcaps); + + caps.dynamic = bool(d3dcaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES); + caps.stretchrect = (d3dcaps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES) && + (d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT) && + (d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT) && + (d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) && + (d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR); + + if(caps.dynamic == true) { + flags.t_usage = D3DUSAGE_DYNAMIC; + flags.v_usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC; + flags.t_pool = D3DPOOL_DEFAULT; + flags.v_pool = D3DPOOL_DEFAULT; + flags.lock = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD; + } else { + flags.t_usage = 0; + flags.v_usage = D3DUSAGE_WRITEONLY; + flags.t_pool = D3DPOOL_MANAGED; + flags.v_pool = D3DPOOL_MANAGED; + flags.lock = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD; + } + + lost = false; + recover(); + return true; + } + + void release_resources() { + if(vertex_buffer) { vertex_buffer->Release(); vertex_buffer = 0; } + if(surface) { surface->Release(); surface = 0; } + if(texture) { texture->Release(); texture = 0; } + } + + void term() { + release_resources(); + if(device) { device->Release(); device = 0; } + if(lpd3d) { lpd3d->Release(); lpd3d = 0; } + } + + pVideoD3D() { + vertex_buffer = 0; + surface = 0; + texture = 0; + device = 0; + lpd3d = 0; + lost = true; + + settings.handle = 0; + settings.synchronize = false; + settings.filter = Video::FilterLinear; + } +}; + +DeclareVideo(D3D) + +}; + +#undef D3DVERTEX diff --git a/mednafen/snes/src/lib/ruby/video/directdraw.cpp b/mednafen/snes/src/lib/ruby/video/directdraw.cpp new file mode 100755 index 0000000..5a77ec4 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video/directdraw.cpp @@ -0,0 +1,186 @@ +#include + +namespace ruby { + +class pVideoDD { +public: + LPDIRECTDRAW lpdd; + LPDIRECTDRAW7 lpdd7; + LPDIRECTDRAWSURFACE7 screen, raster; + LPDIRECTDRAWCLIPPER clipper; + DDSURFACEDESC2 ddsd; + DDSCAPS2 ddscaps; + unsigned iwidth, iheight; + + struct { + HWND handle; + bool synchronize; + + unsigned width; + unsigned height; + } settings; + + bool cap(const string& name) { + if(name == Video::Handle) return true; + if(name == Video::Synchronize) return true; + return false; + } + + any get(const string& name) { + if(name == Video::Handle) return (uintptr_t)settings.handle; + if(name == Video::Synchronize) return settings.synchronize; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Video::Handle) { + settings.handle = (HWND)any_cast(value); + return true; + } + + if(name == Video::Synchronize) { + settings.synchronize = any_cast(value); + return true; + } + + return false; + } + + void resize(unsigned width, unsigned height) { + if(iwidth >= width && iheight >= height) return; + + iwidth = max(width, iwidth); + iheight = max(height, iheight); + + if(raster) raster->Release(); + + screen->GetSurfaceDesc(&ddsd); + int depth = ddsd.ddpfPixelFormat.dwRGBBitCount; + if(depth == 32) goto try_native_surface; + + memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY + ddsd.dwWidth = iwidth; + ddsd.dwHeight = iheight; + + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; + ddsd.ddpfPixelFormat.dwRGBBitCount = 32; + ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000; + ddsd.ddpfPixelFormat.dwGBitMask = 0x00ff00; + ddsd.ddpfPixelFormat.dwBBitMask = 0x0000ff; + + if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return clear(); + + try_native_surface: + memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY + ddsd.dwWidth = iwidth; + ddsd.dwHeight = iheight; + + if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return clear(); + } + + void clear() { + DDBLTFX fx; + fx.dwSize = sizeof(DDBLTFX); + fx.dwFillColor = 0x00000000; + screen->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); + raster->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); + } + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { + if(width != settings.width || height != settings.height) { + resize(settings.width = width, settings.height = height); + } + + if(raster->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) { + raster->Restore(); + if(raster->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) return false; + } + pitch = ddsd.lPitch; + return data = (uint32_t*)ddsd.lpSurface; + } + + void unlock() { + raster->Unlock(0); + } + + void refresh() { + if(settings.synchronize) { + while(true) { + BOOL in_vblank; + lpdd7->GetVerticalBlankStatus(&in_vblank); + if(in_vblank == true) break; + } + } + + HRESULT hr; + RECT rd, rs; + SetRect(&rs, 0, 0, settings.width, settings.height); + + POINT p = { 0, 0 }; + ClientToScreen(settings.handle, &p); + GetClientRect(settings.handle, &rd); + OffsetRect(&rd, p.x, p.y); + + if(screen->Blt(&rd, raster, &rs, DDBLT_WAIT, 0) == DDERR_SURFACELOST) { + screen->Restore(); + raster->Restore(); + } + } + + bool init() { + term(); + + DirectDrawCreate(0, &lpdd, 0); + lpdd->QueryInterface(IID_IDirectDraw7, (void**)&lpdd7); + if(lpdd) { lpdd->Release(); lpdd = 0; } + + lpdd7->SetCooperativeLevel(settings.handle, DDSCL_NORMAL); + + memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); + ddsd.dwSize = sizeof(DDSURFACEDESC2); + + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + lpdd7->CreateSurface(&ddsd, &screen, 0); + + lpdd7->CreateClipper(0, &clipper, 0); + clipper->SetHWnd(0, settings.handle); + screen->SetClipper(clipper); + + raster = 0; + iwidth = 0; + iheight = 0; + resize(settings.width = 256, settings.height = 256); + + return true; + } + + void term() { + if(clipper) { clipper->Release(); clipper = 0; } + if(raster) { raster->Release(); raster = 0; } + if(screen) { screen->Release(); screen = 0; } + if(lpdd7) { lpdd7->Release(); lpdd7 = 0; } + if(lpdd) { lpdd->Release(); lpdd = 0; } + } + + pVideoDD() { + lpdd = 0; + lpdd7 = 0; + screen = 0; + raster = 0; + clipper = 0; + + settings.handle = 0; + } +}; + +DeclareVideo(DD) + +}; diff --git a/mednafen/snes/src/lib/ruby/video/gdi.cpp b/mednafen/snes/src/lib/ruby/video/gdi.cpp new file mode 100755 index 0000000..c79f6d6 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video/gdi.cpp @@ -0,0 +1,100 @@ +#include + +namespace ruby { + +class pVideoGDI { +public: + uint32_t *buffer; + HBITMAP bitmap; + HDC bitmapdc; + BITMAPINFO bmi; + + struct { + HWND handle; + + unsigned width; + unsigned height; + } settings; + + bool cap(const string& name) { + if(name == Video::Handle) return true; + return false; + } + + any get(const string& name) { + if(name == Video::Handle) return (uintptr_t)settings.handle; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Video::Handle) { + settings.handle = (HWND)any_cast(value); + return true; + } + + return false; + } + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { + settings.width = width; + settings.height = height; + + pitch = 1024 * 4; + return data = buffer; + } + + void unlock() {} + + void clear() {} + + void refresh() { + RECT rc; + GetClientRect(settings.handle, &rc); + + SetDIBits(bitmapdc, bitmap, 0, settings.height, (void*)buffer, &bmi, DIB_RGB_COLORS); + HDC hdc = GetDC(settings.handle); + StretchBlt(hdc, rc.left, rc.top, rc.right, rc.bottom, bitmapdc, 0, 1024 - settings.height, settings.width, settings.height, SRCCOPY); + ReleaseDC(settings.handle, hdc); + } + + bool init() { + HDC hdc = GetDC(settings.handle); + bitmapdc = CreateCompatibleDC(hdc); + assert(bitmapdc); + bitmap = CreateCompatibleBitmap(hdc, 1024, 1024); + assert(bitmap); + SelectObject(bitmapdc, bitmap); + ReleaseDC(settings.handle, hdc); + + memset(&bmi, 0, sizeof(BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = 1024; + bmi.bmiHeader.biHeight = -1024; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; //biBitCount of 15 is invalid, biBitCount of 16 is really RGB555 + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = 1024 * 1024 * sizeof(uint32_t); + + settings.width = 256; + settings.height = 256; + return true; + } + + void term() { + DeleteObject(bitmap); + DeleteDC(bitmapdc); + } + + pVideoGDI() { + buffer = (uint32_t*)malloc(1024 * 1024 * sizeof(uint32_t)); + settings.handle = 0; + } + + ~pVideoGDI() { + if(buffer) free(buffer); + } +}; + +DeclareVideo(GDI) + +}; diff --git a/mednafen/snes/src/lib/ruby/video/glx.cpp b/mednafen/snes/src/lib/ruby/video/glx.cpp new file mode 100755 index 0000000..3e1c601 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video/glx.cpp @@ -0,0 +1,231 @@ +/* + video.glx + author: byuu + license: public domain + last updated: 2010-01-05 + + Design notes: + SGI's GLX is the X11/Xlib interface to OpenGL. + At the time of this writing, there are three relevant versions of the API: versions 1.2, 1.3 and 1.4. + + Version 1.2 was released on March 4th, 1997. + Version 1.3 was released on October 19th, 1998. + Version 1.4 was released on December 16th, 2005. + + Despite version 1.3 being roughly ten years old at this time, there are still many modern X11 GLX drivers + that lack full support for the specification. Most notable would be the official video drivers from ATI. + Given this, 1.4 support is pretty much hopeless to target. + + Luckily, each version has been designed to be backwards compatible with the previous version. As well, + version 1.2 is wholly sufficient, albeit less convenient, to implement this video module. + + Therefore, for the purpose of compatibility, this driver only uses GLX 1.2 or earlier API commands. + As well, it only uses raw Xlib API commands, so that it is compatible with any toolkit. +*/ + +#include "opengl.hpp" + +namespace ruby { + +//returns true once window is mapped (created and displayed onscreen) +static Bool glx_wait_for_map_notify(Display *d, XEvent *e, char *arg) { + return (e->type == MapNotify) && (e->xmap.window == (Window)arg); +} + +class pVideoGLX : public OpenGL { +public: + int (*glSwapInterval)(int); + + Display *display; + int screen; + Window xwindow; + Colormap colormap; + GLXContext glxcontext; + GLXWindow glxwindow; + + struct { + int version_major, version_minor; + bool double_buffer; + bool is_direct; + } glx; + + struct { + Window handle; + bool synchronize; + unsigned filter; + + unsigned width; + unsigned height; + } settings; + + bool cap(const string& name) { + if(name == Video::Handle) return true; + if(name == Video::Synchronize) return true; + if(name == Video::Filter) return true; + if(name == Video::FragmentShader) return true; + if(name == Video::VertexShader) return true; + return false; + } + + any get(const string& name) { + if(name == Video::Handle) return (uintptr_t)settings.handle; + if(name == Video::Synchronize) return settings.synchronize; + if(name == Video::Filter) return settings.filter; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Video::Handle) { + settings.handle = any_cast(value); + return true; + } + + if(name == Video::Synchronize) { + if(settings.synchronize != any_cast(value)) { + settings.synchronize = any_cast(value); + if(glSwapInterval) glSwapInterval(settings.synchronize); + return true; + } + } + + if(name == Video::Filter) { + settings.filter = any_cast(value); + return true; + } + + if(name == Video::FragmentShader) { + OpenGL::set_fragment_shader(any_cast(value)); + return true; + } + + if(name == Video::VertexShader) { + OpenGL::set_vertex_shader(any_cast(value)); + return true; + } + + return false; + } + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { + resize(width, height); + settings.width = width; + settings.height = height; + return OpenGL::lock(data, pitch); + } + + void unlock() { + } + + void clear() { + OpenGL::clear(); + if(glx.double_buffer) glXSwapBuffers(display, glxwindow); + } + + void refresh() { + //we must ensure that the child window is the same size as the parent window. + //unfortunately, we cannot hook the parent window resize event notification, + //as we did not create the parent window, nor have any knowledge of the toolkit used. + //therefore, inelegant as it may be, we query each window size and resize as needed. + XWindowAttributes parent, child; + XGetWindowAttributes(display, settings.handle, &parent); + XGetWindowAttributes(display, xwindow, &child); + if(child.width != parent.width || child.height != parent.height) { + XResizeWindow(display, xwindow, parent.width, parent.height); + } + + OpenGL::refresh(settings.filter == Video::FilterLinear, + settings.width, settings.height, parent.width, parent.height); + if(glx.double_buffer) glXSwapBuffers(display, glxwindow); + } + + bool init() { + term(); + + display = XOpenDisplay(0); + screen = DefaultScreen(display); + glXQueryVersion(display, &glx.version_major, &glx.version_minor); + //require GLX 1.2+ API + if(glx.version_major < 1 || (glx.version_major == 1 && glx.version_minor < 2)) return false; + + XWindowAttributes window_attributes; + XGetWindowAttributes(display, settings.handle, &window_attributes); + + //let GLX determine the best Visual to use for GL output; provide a few hints + //note: some video drivers will override double buffering attribute + int attributelist[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None }; + XVisualInfo *vi = glXChooseVisual(display, screen, attributelist); + + //Window settings.handle has already been realized, most likely with DefaultVisual. + //GLX requires that the GL output window has the same Visual as the GLX context. + //it is not possible to change the Visual of an already realized (created) window. + //therefore a new child window, using the same GLX Visual, must be created and binded to settings.handle. + colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone); + XSetWindowAttributes attributes; + attributes.colormap = colormap; + attributes.border_pixel = 0; + attributes.event_mask = StructureNotifyMask; + xwindow = XCreateWindow(display, /* parent = */ settings.handle, + /* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height, + /* border_width = */ 0, vi->depth, InputOutput, vi->visual, + CWColormap | CWBorderPixel | CWEventMask, &attributes); + XSetWindowBackground(display, xwindow, /* color = */ 0); + XMapWindow(display, xwindow); + XEvent event; + //window must be realized (appear onscreen) before we make the context current + XIfEvent(display, &event, glx_wait_for_map_notify, (char*)xwindow); + + glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE); + glXMakeCurrent(display, glxwindow = xwindow, glxcontext); + + //read attributes of frame buffer for later use, as requested attributes from above are not always granted + int value = 0; + glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value); + glx.double_buffer = value; + glx.is_direct = glXIsDirect(display, glxcontext); + + OpenGL::init(); + settings.width = 256; + settings.height = 256; + + //vertical synchronization + if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI"); + if(!glSwapInterval) glSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA"); + if( glSwapInterval) glSwapInterval(settings.synchronize); + + return true; + } + + void term() { + OpenGL::term(); + + if(glxcontext) { + glXDestroyContext(display, glxcontext); + glxcontext = 0; + } + + if(xwindow) { + XUnmapWindow(display, xwindow); + xwindow = 0; + } + + if(colormap) { + XFreeColormap(display, colormap); + colormap = 0; + } + } + + pVideoGLX() : glSwapInterval(0) { + settings.handle = 0; + settings.synchronize = false; + xwindow = 0; + colormap = 0; + glxcontext = 0; + glxwindow = 0; + } + + ~pVideoGLX() { term(); } +}; + +DeclareVideo(GLX) + +}; diff --git a/mednafen/snes/src/lib/ruby/video/opengl.hpp b/mednafen/snes/src/lib/ruby/video/opengl.hpp new file mode 100755 index 0000000..46e5165 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video/opengl.hpp @@ -0,0 +1,225 @@ +#include + +#if defined(PLATFORM_X) + #include + #define glGetProcAddress(name) (*glXGetProcAddress)((const GLubyte*)(name)) +#elif defined(PLATFORM_WIN) + #include + #define glGetProcAddress(name) wglGetProcAddress(name) +#else + #error "ruby::OpenGL: unsupported platform" +#endif + +PFNGLCREATEPROGRAMPROC glCreateProgram = 0; +PFNGLUSEPROGRAMPROC glUseProgram = 0; +PFNGLCREATESHADERPROC glCreateShader = 0; +PFNGLDELETESHADERPROC glDeleteShader = 0; +PFNGLSHADERSOURCEPROC glShaderSource = 0; +PFNGLCOMPILESHADERPROC glCompileShader = 0; +PFNGLATTACHSHADERPROC glAttachShader = 0; +PFNGLDETACHSHADERPROC glDetachShader = 0; +PFNGLLINKPROGRAMPROC glLinkProgram = 0; +PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = 0; +PFNGLUNIFORM1IPROC glUniform1i = 0; +PFNGLUNIFORM2FVPROC glUniform2fv = 0; +PFNGLUNIFORM4FVPROC glUniform4fv = 0; + +class OpenGL { +public: + GLuint gltexture; + GLuint glprogram; + GLuint fragmentshader; + GLuint vertexshader; + bool shader_support; + + uint32_t *buffer; + unsigned iwidth, iheight; + + void resize(unsigned width, unsigned height) { + if(iwidth >= width && iheight >= height) return; + + if(gltexture) glDeleteTextures(1, &gltexture); + iwidth = max(width, iwidth ); + iheight = max(height, iheight); + if(buffer) delete[] buffer; + buffer = new uint32_t[iwidth * iheight]; + + glGenTextures(1, &gltexture); + glBindTexture(GL_TEXTURE_2D, gltexture); + glPixelStorei(GL_UNPACK_ROW_LENGTH, iwidth); + glTexImage2D(GL_TEXTURE_2D, + /* mip-map level = */ 0, /* internal format = */ GL_RGB, + iwidth, iheight, /* border = */ 0, /* format = */ GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, buffer); + } + + bool lock(uint32_t *&data, unsigned &pitch) { + pitch = iwidth * sizeof(uint32_t); + return data = buffer; + } + + void clear() { + memset(buffer, 0, iwidth * iheight * sizeof(uint32_t)); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + glFlush(); + } + + void refresh(bool smooth, unsigned inwidth, unsigned inheight, unsigned outwidth, unsigned outheight) { + if(shader_support) { + glUseProgram(glprogram); + GLint location; + + float inputSize[2] = { inwidth, inheight }; + location = glGetUniformLocation(glprogram, "rubyInputSize"); + glUniform2fv(location, 1, inputSize); + + float outputSize[2] = { outwidth, outheight }; + location = glGetUniformLocation(glprogram, "rubyOutputSize"); + glUniform2fv(location, 1, outputSize); + + float textureSize[2] = { iwidth, iheight }; + location = glGetUniformLocation(glprogram, "rubyTextureSize"); + glUniform2fv(location, 1, textureSize); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, smooth ? GL_LINEAR : GL_NEAREST); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, outwidth, 0, outheight, -1.0, 1.0); + glViewport(0, 0, outwidth, outheight); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glPixelStorei(GL_UNPACK_ROW_LENGTH, iwidth); + glTexSubImage2D(GL_TEXTURE_2D, + /* mip-map level = */ 0, /* x = */ 0, /* y = */ 0, + inwidth, inheight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); + + //OpenGL projection sets 0,0 as *bottom-left* of screen. + //therefore, below vertices flip image to support top-left source. + //texture range = x1:0.0, y1:0.0, x2:1.0, y2:1.0 + //vertex range = x1:0, y1:0, x2:width, y2:height + double w = double(inwidth) / double(iwidth); + double h = double(inheight) / double(iheight); + int u = outwidth; + int v = outheight; + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0, 0); glVertex3i(0, v, 0); + glTexCoord2f(w, 0); glVertex3i(u, v, 0); + glTexCoord2f(0, h); glVertex3i(0, 0, 0); + glTexCoord2f(w, h); glVertex3i(u, 0, 0); + glEnd(); + + glFlush(); + + if(shader_support) { + glUseProgram(0); + } + } + + void set_fragment_shader(const char *source) { + if(!shader_support) return; + + if(fragmentshader) { + glDetachShader(glprogram, fragmentshader); + glDeleteShader(fragmentshader); + fragmentshader = 0; + } + + if(source) { + fragmentshader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentshader, 1, &source, 0); + glCompileShader(fragmentshader); + glAttachShader(glprogram, fragmentshader); + } + + glLinkProgram(glprogram); + } + + void set_vertex_shader(const char *source) { + if(!shader_support) return; + + if(vertexshader) { + glDetachShader(glprogram, vertexshader); + glDeleteShader(vertexshader); + vertexshader = 0; + } + + if(source) { + vertexshader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertexshader, 1, &source, 0); + glCompileShader(vertexshader); + glAttachShader(glprogram, vertexshader); + } + + glLinkProgram(glprogram); + } + + void init() { + //disable unused features + glDisable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_POLYGON_SMOOTH); + glDisable(GL_STENCIL_TEST); + + //enable useful and required features + glEnable(GL_DITHER); + glEnable(GL_TEXTURE_2D); + + //bind shader functions + glCreateProgram = (PFNGLCREATEPROGRAMPROC)glGetProcAddress("glCreateProgram"); + glUseProgram = (PFNGLUSEPROGRAMPROC)glGetProcAddress("glUseProgram"); + glCreateShader = (PFNGLCREATESHADERPROC)glGetProcAddress("glCreateShader"); + glDeleteShader = (PFNGLDELETESHADERPROC)glGetProcAddress("glDeleteShader"); + glShaderSource = (PFNGLSHADERSOURCEPROC)glGetProcAddress("glShaderSource"); + glCompileShader = (PFNGLCOMPILESHADERPROC)glGetProcAddress("glCompileShader"); + glAttachShader = (PFNGLATTACHSHADERPROC)glGetProcAddress("glAttachShader"); + glDetachShader = (PFNGLDETACHSHADERPROC)glGetProcAddress("glDetachShader"); + glLinkProgram = (PFNGLLINKPROGRAMPROC)glGetProcAddress("glLinkProgram"); + glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)glGetProcAddress("glGetUniformLocation"); + glUniform1i = (PFNGLUNIFORM1IPROC)glGetProcAddress("glUniform1i"); + glUniform2fv = (PFNGLUNIFORM2FVPROC)glGetProcAddress("glUniform2fv"); + glUniform4fv = (PFNGLUNIFORM4FVPROC)glGetProcAddress("glUniform4fv"); + + shader_support = glCreateProgram && glUseProgram && glCreateShader + && glDeleteShader && glShaderSource && glCompileShader && glAttachShader + && glDetachShader && glLinkProgram && glGetUniformLocation + && glUniform1i && glUniform2fv && glUniform4fv; + + if(shader_support) glprogram = glCreateProgram(); + + //create surface texture + resize(256, 256); + } + + void term() { + if(gltexture) { + glDeleteTextures(1, &gltexture); + gltexture = 0; + } + + if(buffer) { + delete[] buffer; + buffer = 0; + iwidth = 0; + iheight = 0; + } + } + + OpenGL() { + gltexture = 0; + glprogram = 0; + fragmentshader = 0; + vertexshader = 0; + + buffer = 0; + iwidth = 0; + iheight = 0; + } +}; diff --git a/mednafen/snes/src/lib/ruby/video/qtopengl.cpp b/mednafen/snes/src/lib/ruby/video/qtopengl.cpp new file mode 100755 index 0000000..9f260b1 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video/qtopengl.cpp @@ -0,0 +1,174 @@ +#ifdef __APPLE__ + #include +#endif + +namespace ruby { + +class pVideoQtOpenGL { +public: + QWidget *parent; + QVBoxLayout *layout; + + class RubyGLWidget : public QGLWidget { + public: + GLuint texture; + unsigned textureWidth, textureHeight; + + uint32_t *buffer; + unsigned rasterWidth, rasterHeight; + + bool synchronize; + unsigned filter; + + void resize(unsigned width, unsigned height) { + if(width > textureWidth || height > textureHeight) { + textureWidth = max(width, textureWidth); + textureHeight = max(height, textureHeight); + + if(buffer) { + delete[] buffer; + glDeleteTextures(1, &texture); + } + + buffer = new uint32_t[textureWidth * textureHeight]; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glPixelStorei(GL_UNPACK_ROW_LENGTH, textureWidth); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureWidth, textureHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); + } + } + + void updateSynchronization() { + #ifdef __APPLE__ + makeCurrent(); + CGLContextObj context = CGLGetCurrentContext(); + GLint value = synchronize; //0 = draw immediately (no vsync), 1 = draw once per frame (vsync) + CGLSetParameter(context, kCGLCPSwapInterval, &value); + #endif + } + + void paintGL() { + unsigned outputWidth = width(); + unsigned outputHeight = height(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, outputWidth, 0, outputHeight, -1.0, 1.0); + glViewport(0, 0, outputWidth, outputHeight); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter == Video::FilterPoint) ? GL_NEAREST : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter == Video::FilterPoint) ? GL_NEAREST : GL_LINEAR); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rasterWidth, rasterHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); + + double w = (double)rasterWidth / (double)textureWidth; + double h = (double)rasterHeight / (double)textureHeight; + unsigned u = outputWidth; + unsigned v = outputHeight; + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0, 0); glVertex3i(0, v, 0); + glTexCoord2f(w, 0); glVertex3i(u, v, 0); + glTexCoord2f(0, h); glVertex3i(0, 0, 0); + glTexCoord2f(w, h); glVertex3i(u, 0, 0); + glEnd(); + } + + void initializeGL() { + format().setDoubleBuffer(true); + + texture = 0; + textureWidth = 0; + textureHeight = 0; + buffer = 0; + resize(rasterWidth = 256, rasterHeight = 256); + + glDisable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_POLYGON_SMOOTH); + glDisable(GL_STENCIL_TEST); + glEnable(GL_DITHER); + glEnable(GL_TEXTURE_2D); + glClearColor(0.0, 0.0, 0.0, 0.0); + } + } *widget; + + bool cap(const string& name) { + if(name == Video::Synchronize) return true; + if(name == Video::Filter) return true; + if(name == "QWidget") return true; + return false; + } + + any get(const string& name) { + if(name == Video::Synchronize) return widget->synchronize; + if(name == Video::Filter) return widget->filter; + if(name == "QWidget") return parent; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Video::Synchronize) { + widget->synchronize = any_cast(value); + widget->updateSynchronization(); + return true; + } + + if(name == Video::Filter) { + widget->filter = any_cast(value); + return true; + } + + if(name == "QWidget") { + parent = any_cast(value); + return true; + } + + return false; + } + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { + widget->resize(width, height); + widget->rasterWidth = width; + widget->rasterHeight = height; + + pitch = widget->textureWidth * sizeof(uint32_t); + return data = widget->buffer; + } + + void unlock() { + } + + void clear() { + memset(widget->buffer, 0, widget->textureWidth * widget->textureHeight * sizeof(uint32_t)); + widget->updateGL(); + } + + void refresh() { + widget->updateGL(); + } + + bool init() { + layout = new QVBoxLayout; + layout->setMargin(0); + + widget = new RubyGLWidget; + widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(widget); + parent->setLayout(layout); + + return true; + } + + void term() { + } +}; + +DeclareVideo(QtOpenGL) + +}; diff --git a/mednafen/snes/src/lib/ruby/video/qtraster.cpp b/mednafen/snes/src/lib/ruby/video/qtraster.cpp new file mode 100755 index 0000000..414689b --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video/qtraster.cpp @@ -0,0 +1,126 @@ +namespace ruby { + +struct VideoQtRasterContext { + QImage *image; + unsigned width, height; + unsigned filter; +} context; + +class pVideoQtRaster { +public: + QWidget *parent; + QVBoxLayout *layout; + + struct QtImage : public QWidget { + VideoQtRasterContext &context; + + void paintEvent(QPaintEvent*) { + if(context.image == 0) return; + QPainter painter(this); + + if(size().width() == context.width && size().height() == context.height) { + painter.drawImage(0, 0, *context.image); + } else { + Qt::TransformationMode mode = Qt::FastTransformation; + if(context.filter == Video::FilterLinear) mode = Qt::SmoothTransformation; + painter.drawImage(0, 0, context.image->scaled(size(), Qt::IgnoreAspectRatio, mode)); + } + } + + QtImage(QWidget *parent, VideoQtRasterContext &context_) : QWidget(parent), context(context_) {} + } *widget; + + bool cap(const string& name) { + if(name == Video::Filter) return true; + if(name == "QWidget") return true; + return false; + } + + any get(const string& name) { + if(name == Video::Filter) return context.filter; + if(name == "QWidget") return parent; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Video::Filter) { + context.filter = any_cast(value); + return true; + } + + if(name == "QWidget") { + parent = any_cast(value); + return true; + } + + return false; + } + + void resize(unsigned width, unsigned height) { + if(context.width != width || context.height != height) { + if(context.image) delete context.image; + context.image = new QImage(context.width = width, context.height = height, QImage::Format_RGB32); + } + } + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { + //if image size has changed since last lock(), re-allocate buffer to match new size + if(width != context.width || height != context.height) resize(width, height); + + pitch = width * sizeof(uint32_t); + return data = (uint32_t*)context.image->bits(); + } + + void unlock() { + } + + void clear() { + context.image->fill(0); + widget->update(); + } + + void refresh() { + widget->update(); + } + + bool init() { + term(); + + layout = new QVBoxLayout; + layout->setMargin(0); + + context.image = 0; + context.width = 0; + context.height = 0; + context.filter = Video::FilterPoint; + resize(256, 256); + + widget = new QtImage(parent, context); + widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(widget); + parent->setLayout(layout); + clear(); + + return true; + } + + void term() { + if(context.image) delete context.image; + if(widget) delete widget; + if(layout) delete layout; + + context.image = 0; + widget = 0; + layout = 0; + } + + pVideoQtRaster() { + context.image = 0; + widget = 0; + layout = 0; + } +}; + +DeclareVideo(QtRaster) + +}; diff --git a/mednafen/snes/src/lib/ruby/video/sdl.cpp b/mednafen/snes/src/lib/ruby/video/sdl.cpp new file mode 100755 index 0000000..8f70342 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video/sdl.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include + +namespace ruby { + +class pVideoSDL { +public: + Display *display; + SDL_Surface *screen, *buffer; + unsigned iwidth, iheight; + + struct { + uintptr_t handle; + + unsigned width; + unsigned height; + } settings; + + bool cap(const string& name) { + if(name == Video::Handle) return true; + return false; + } + + any get(const string& name) { + if(name == Video::Handle) return settings.handle; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Video::Handle) { + settings.handle = any_cast(value); + return true; + } + + return false; + } + + void resize(unsigned width, unsigned height) { + if(iwidth >= width && iheight >= height) return; + + iwidth = max(width, iwidth); + iheight = max(height, iheight); + + if(buffer) SDL_FreeSurface(buffer); + buffer = SDL_CreateRGBSurface( + SDL_SWSURFACE, iwidth, iheight, 32, + 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 + ); + } + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { + if(width != settings.width || height != settings.height) { + resize(settings.width = width, settings.height = height); + } + + if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer); + pitch = buffer->pitch; + return data = (uint32_t*)buffer->pixels; + } + + void unlock() { + if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer); + } + + void clear() { + if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer); + for(unsigned y = 0; y < iheight; y++) { + uint32_t *data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2); + for(unsigned x = 0; x < iwidth; x++) *data++ = 0xff000000; + } + if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer); + refresh(); + } + + void refresh() { + //ruby input is X8R8G8B8, top 8-bits are ignored. + //as SDL forces us to use a 32-bit buffer, we must set alpha to 255 (full opacity) + //to prevent blending against the window beneath when X window visual is 32-bits. + if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer); + for(unsigned y = 0; y < settings.height; y++) { + uint32_t *data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2); + for(unsigned x = 0; x < settings.width; x++) *data++ |= 0xff000000; + } + if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer); + + XWindowAttributes attributes; + XGetWindowAttributes(display, settings.handle, &attributes); + + SDL_Rect src, dest; + + src.x = 0; + src.y = 0; + src.w = settings.width; + src.h = settings.height; + + dest.x = 0; + dest.y = 0; + dest.w = attributes.width; + dest.h = attributes.height; + + SDL_SoftStretch(buffer, &src, screen, &dest); + SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h); + } + + bool init() { + display = XOpenDisplay(0); + + char env[512]; + sprintf(env, "SDL_WINDOWID=%ld", (long int)settings.handle); + putenv(env); + + SDL_InitSubSystem(SDL_INIT_VIDEO); + screen = SDL_SetVideoMode(2560, 1600, 32, SDL_HWSURFACE); + + buffer = 0; + iwidth = 0; + iheight = 0; + resize(settings.width = 256, settings.height = 256); + + return true; + } + + void term() { + XCloseDisplay(display); + SDL_FreeSurface(buffer); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + } + + pVideoSDL() { + settings.handle = 0; + } +}; + +DeclareVideo(SDL) + +}; diff --git a/mednafen/snes/src/lib/ruby/video/wgl.cpp b/mednafen/snes/src/lib/ruby/video/wgl.cpp new file mode 100755 index 0000000..23bc222 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video/wgl.cpp @@ -0,0 +1,154 @@ +/* + video.wgl + authors: byuu, krom +*/ + +#include "opengl.hpp" + +namespace ruby { + +class pVideoWGL : public OpenGL { +public: + BOOL (APIENTRY *glSwapInterval)(int); + + HDC display; + HGLRC wglcontext; + HWND window; + HINSTANCE glwindow; + + struct { + HWND handle; + bool synchronize; + unsigned filter; + + unsigned width; + unsigned height; + } settings; + + bool cap(const string& name) { + if(name == Video::Handle) return true; + if(name == Video::Synchronize) return true; + if(name == Video::Filter) return true; + if(name == Video::FragmentShader) return true; + if(name == Video::VertexShader) return true; + return false; + } + + any get(const string& name) { + if(name == Video::Handle) return (uintptr_t)settings.handle; + if(name == Video::Synchronize) return settings.synchronize; + if(name == Video::Filter) return settings.filter; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Video::Handle) { + settings.handle = (HWND)any_cast(value); + return true; + } + + if(name == Video::Synchronize) { + if(settings.synchronize != any_cast(value)) { + settings.synchronize = any_cast(value); + if(wglcontext) init(); + } + } + + if(name == Video::Filter) { + settings.filter = any_cast(value); + return true; + } + + if(name == Video::FragmentShader) { + OpenGL::set_fragment_shader(any_cast(value)); + return true; + } + + if(name == Video::VertexShader) { + OpenGL::set_vertex_shader(any_cast(value)); + return true; + } + + return false; + } + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { + resize(width, height); + settings.width = width; + settings.height = height; + return OpenGL::lock(data, pitch); + } + + void unlock() { + } + + void clear() { + OpenGL::clear(); + SwapBuffers(display); + } + + void refresh() { + RECT rc; + GetClientRect(settings.handle, &rc); + + OpenGL::refresh(settings.filter == Video::FilterLinear, + settings.width, settings.height, + rc.right - rc.left, rc.bottom - rc.top); + + SwapBuffers(display); + } + + bool init() { + term(); + + GLuint pixel_format; + PIXELFORMATDESCRIPTOR pfd; + memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + + display = GetDC(settings.handle); + pixel_format = ChoosePixelFormat(display, &pfd); + SetPixelFormat(display, pixel_format, &pfd); + + wglcontext = wglCreateContext(display); + wglMakeCurrent(display, wglcontext); + + OpenGL::init(); + settings.width = 256; + settings.height = 256; + + //vertical synchronization + if(!glSwapInterval) glSwapInterval = (BOOL (APIENTRY*)(int))glGetProcAddress("wglSwapIntervalEXT"); + if( glSwapInterval) glSwapInterval(settings.synchronize); + + return true; + } + + void term() { + OpenGL::term(); + + if(wglcontext) { + wglDeleteContext(wglcontext); + wglcontext = 0; + } + } + + pVideoWGL() : glSwapInterval(0) { + settings.handle = 0; + settings.synchronize = false; + settings.filter = 0; + + window = 0; + wglcontext = 0; + glwindow = 0; + } + + ~pVideoWGL() { term(); } +}; + +DeclareVideo(WGL) + +}; diff --git a/mednafen/snes/src/lib/ruby/video/xv.cpp b/mednafen/snes/src/lib/ruby/video/xv.cpp new file mode 100755 index 0000000..10d5164 --- /dev/null +++ b/mednafen/snes/src/lib/ruby/video/xv.cpp @@ -0,0 +1,498 @@ +#include +#include +#include +#include +#include + +extern "C" XvImage* XvShmCreateImage(Display*, XvPortID, int, char*, int, int, XShmSegmentInfo*); + +namespace ruby { + +class pVideoXv { +public: + uint32_t *buffer; + uint8_t *ytable, *utable, *vtable; + + enum XvFormat { + XvFormatRGB32, + XvFormatRGB24, + XvFormatRGB16, + XvFormatRGB15, + XvFormatYUY2, + XvFormatUYVY, + XvFormatUnknown + }; + + struct { + Display *display; + GC gc; + Window window; + Colormap colormap; + XShmSegmentInfo shminfo; + + int port; + int depth; + int visualid; + + XvImage *image; + XvFormat format; + uint32_t fourcc; + + unsigned width; + unsigned height; + } device; + + struct { + Window handle; + bool synchronize; + + unsigned width; + unsigned height; + } settings; + + bool cap(const string& name) { + if(name == Video::Handle) return true; + if(name == Video::Synchronize) { + return XInternAtom(XOpenDisplay(0), "XV_SYNC_TO_VBLANK", true) != None; + } + return false; + } + + any get(const string& name) { + if(name == Video::Handle) return settings.handle; + if(name == Video::Synchronize) return settings.synchronize; + return false; + } + + bool set(const string& name, const any& value) { + if(name == Video::Handle) { + settings.handle = any_cast(value); + return true; + } + + if(name == Video::Synchronize) { + Display *display = XOpenDisplay(0); + Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true); + if(atom != None && device.port >= 0) { + settings.synchronize = any_cast(value); + XvSetPortAttribute(display, device.port, atom, settings.synchronize); + return true; + } + return false; + } + + return false; + } + + void resize(unsigned width, unsigned height) { + if(device.width >= width && device.height >= height) return; + device.width = max(width, device.width); + device.height = max(height, device.height); + + XShmDetach(device.display, &device.shminfo); + shmdt(device.shminfo.shmaddr); + shmctl(device.shminfo.shmid, IPC_RMID, NULL); + XFree(device.image); + delete[] buffer; + + device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, device.width, device.height, &device.shminfo); + + device.shminfo.shmid = shmget(IPC_PRIVATE, device.image->data_size, IPC_CREAT | 0777); + device.shminfo.shmaddr = device.image->data = (char*)shmat(device.shminfo.shmid, 0, 0); + device.shminfo.readOnly = false; + XShmAttach(device.display, &device.shminfo); + + buffer = new uint32_t[device.width * device.height]; + } + + bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) { + if(width != settings.width || height != settings.height) { + resize(settings.width = width, settings.height = height); + } + + pitch = device.width * 4; + return data = buffer; + } + + void unlock() { + } + + void clear() { + memset(buffer, 0, device.width * device.height * sizeof(uint32_t)); + //clear twice in case video is double buffered ... + refresh(); + refresh(); + } + + void refresh() { + unsigned width = settings.width; + unsigned height = settings.height; + + XWindowAttributes target; + XGetWindowAttributes(device.display, device.window, &target); + + //we must ensure that the child window is the same size as the parent window. + //unfortunately, we cannot hook the parent window resize event notification, + //as we did not create the parent window, nor have any knowledge of the toolkit used. + //therefore, query each window size and resize as needed. + XWindowAttributes parent; + XGetWindowAttributes(device.display, settings.handle, &parent); + if(target.width != parent.width || target.height != parent.height) { + XResizeWindow(device.display, device.window, parent.width, parent.height); + } + + //update target width and height attributes + XGetWindowAttributes(device.display, device.window, &target); + + switch(device.format) { + case XvFormatRGB32: render_rgb32(width, height); break; + case XvFormatRGB24: render_rgb24(width, height); break; + case XvFormatRGB16: render_rgb16(width, height); break; + case XvFormatRGB15: render_rgb15(width, height); break; + case XvFormatYUY2: render_yuy2 (width, height); break; + case XvFormatUYVY: render_uyvy (width, height); break; + } + + XvShmPutImage(device.display, device.port, device.window, device.gc, device.image, + 0, 0, width, height, + 0, 0, target.width, target.height, + true); + } + + bool init() { + device.display = XOpenDisplay(0); + + if(!XShmQueryExtension(device.display)) { + fprintf(stderr, "VideoXv: XShm extension not found.\n"); + return false; + } + + //find an appropriate Xv port + device.port = -1; + XvAdaptorInfo *adaptor_info; + unsigned adaptor_count; + XvQueryAdaptors(device.display, DefaultRootWindow(device.display), &adaptor_count, &adaptor_info); + for(unsigned i = 0; i < adaptor_count; i++) { + //find adaptor that supports both input (memory->drawable) and image (drawable->screen) masks + if(adaptor_info[i].num_formats < 1) continue; + if(!(adaptor_info[i].type & XvInputMask)) continue; + if(!(adaptor_info[i].type & XvImageMask)) continue; + + device.port = adaptor_info[i].base_id; + device.depth = adaptor_info[i].formats->depth; + device.visualid = adaptor_info[i].formats->visual_id; + break; + } + XvFreeAdaptorInfo(adaptor_info); + if(device.port < 0) { + fprintf(stderr, "VideoXv: failed to find valid XvPort.\n"); + return false; + } + + //create child window to attach to parent window. + //this is so that even if parent window visual depth doesn't match Xv visual + //(common with composited windows), Xv can still render to child window. + XWindowAttributes window_attributes; + XGetWindowAttributes(device.display, settings.handle, &window_attributes); + + XVisualInfo visualtemplate; + visualtemplate.visualid = device.visualid; + visualtemplate.screen = DefaultScreen(device.display); + visualtemplate.depth = device.depth; + visualtemplate.visual = 0; + int visualmatches = 0; + XVisualInfo *visualinfo = XGetVisualInfo(device.display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches); + if(visualmatches < 1 || !visualinfo->visual) { + if(visualinfo) XFree(visualinfo); + fprintf(stderr, "VideoXv: unable to find Xv-compatible visual.\n"); + return false; + } + + device.colormap = XCreateColormap(device.display, settings.handle, visualinfo->visual, AllocNone); + XSetWindowAttributes attributes; + attributes.colormap = device.colormap; + attributes.border_pixel = 0; + attributes.event_mask = StructureNotifyMask; + device.window = XCreateWindow(device.display, /* parent = */ settings.handle, + /* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height, + /* border_width = */ 0, device.depth, InputOutput, visualinfo->visual, + CWColormap | CWBorderPixel | CWEventMask, &attributes); + XFree(visualinfo); + XSetWindowBackground(device.display, device.window, /* color = */ 0); + XMapWindow(device.display, device.window); + + device.gc = XCreateGC(device.display, device.window, 0, 0); + + //set colorkey to auto paint, so that Xv video output is always visible + Atom atom = XInternAtom(device.display, "XV_AUTOPAINT_COLORKEY", true); + if(atom != None) XvSetPortAttribute(device.display, device.port, atom, 1); + + //find optimal rendering format + device.format = XvFormatUnknown; + signed format_count; + XvImageFormatValues *format = XvListImageFormats(device.display, device.port, &format_count); + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvRGB && format[i].bits_per_pixel == 32) { + device.format = XvFormatRGB32; + device.fourcc = format[i].id; + break; + } + } + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvRGB && format[i].bits_per_pixel == 24) { + device.format = XvFormatRGB24; + device.fourcc = format[i].id; + break; + } + } + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvRGB && format[i].bits_per_pixel <= 16 && format[i].red_mask == 0xf800) { + device.format = XvFormatRGB16; + device.fourcc = format[i].id; + break; + } + } + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvRGB && format[i].bits_per_pixel <= 16 && format[i].red_mask == 0x7c00) { + device.format = XvFormatRGB15; + device.fourcc = format[i].id; + break; + } + } + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) { + if(format[i].component_order[0] == 'Y' && format[i].component_order[1] == 'U' + && format[i].component_order[2] == 'Y' && format[i].component_order[3] == 'V' + ) { + device.format = XvFormatYUY2; + device.fourcc = format[i].id; + break; + } + } + } + + if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) { + if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) { + if(format[i].component_order[0] == 'U' && format[i].component_order[1] == 'Y' + && format[i].component_order[2] == 'V' && format[i].component_order[3] == 'Y' + ) { + device.format = XvFormatUYVY; + device.fourcc = format[i].id; + break; + } + } + } + + free(format); + if(device.format == XvFormatUnknown) { + fprintf(stderr, "VideoXv: unable to find a supported image format.\n"); + return false; + } + + device.width = 256; + device.height = 256; + + device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, device.width, device.height, &device.shminfo); + if(!device.image) { + fprintf(stderr, "VideoXv: XShmCreateImage failed.\n"); + return false; + } + + device.shminfo.shmid = shmget(IPC_PRIVATE, device.image->data_size, IPC_CREAT | 0777); + device.shminfo.shmaddr = device.image->data = (char*)shmat(device.shminfo.shmid, 0, 0); + device.shminfo.readOnly = false; + if(!XShmAttach(device.display, &device.shminfo)) { + fprintf(stderr, "VideoXv: XShmAttach failed.\n"); + return false; + } + + buffer = new uint32_t[device.width * device.height]; + settings.width = 256; + settings.height = 256; + init_yuv_tables(); + clear(); + return true; + } + + void term() { + XShmDetach(device.display, &device.shminfo); + shmdt(device.shminfo.shmaddr); + shmctl(device.shminfo.shmid, IPC_RMID, NULL); + XFree(device.image); + + if(device.window) { + XUnmapWindow(device.display, device.window); + device.window = 0; + } + + if(device.colormap) { + XFreeColormap(device.display, device.colormap); + device.colormap = 0; + } + + if(buffer) { delete[] buffer; buffer = 0; } + if(ytable) { delete[] ytable; ytable = 0; } + if(utable) { delete[] utable; utable = 0; } + if(vtable) { delete[] vtable; vtable = 0; } + } + + void render_rgb32(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint32_t *output = (uint32_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + memcpy(output, input, width * 4); + input += device.width; + output += device.width; + } + } + + void render_rgb24(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint8_t *output = (uint8_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width; x++) { + uint32_t p = *input++; + *output++ = p; + *output++ = p >> 8; + *output++ = p >> 16; + } + + input += (device.width - width); + output += (device.width - width) * 3; + } + } + + void render_rgb16(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint16_t *output = (uint16_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width; x++) { + uint32_t p = *input++; + *output++ = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x001f); //RGB32->RGB16 + } + + input += device.width - width; + output += device.width - width; + } + } + + void render_rgb15(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint16_t *output = (uint16_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width; x++) { + uint32_t p = *input++; + *output++ = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x001f); //RGB32->RGB15 + } + + input += device.width - width; + output += device.width - width; + } + } + + void render_yuy2(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint16_t *output = (uint16_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width >> 1; x++) { + uint32_t p0 = *input++; + uint32_t p1 = *input++; + p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f); //RGB32->RGB16 + p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f); //RGB32->RGB16 + + uint8_t u = (utable[p0] + utable[p1]) >> 1; + uint8_t v = (vtable[p0] + vtable[p1]) >> 1; + + *output++ = (u << 8) | ytable[p0]; + *output++ = (v << 8) | ytable[p1]; + } + + input += device.width - width; + output += device.width - width; + } + } + + void render_uyvy(unsigned width, unsigned height) { + uint32_t *input = (uint32_t*)buffer; + uint16_t *output = (uint16_t*)device.image->data; + + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width >> 1; x++) { + uint32_t p0 = *input++; + uint32_t p1 = *input++; + p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f); + p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f); + + uint8_t u = (utable[p0] + utable[p1]) >> 1; + uint8_t v = (vtable[p0] + vtable[p1]) >> 1; + + *output++ = (ytable[p0] << 8) | u; + *output++ = (ytable[p1] << 8) | v; + } + + input += device.width - width; + output += device.width - width; + } + } + + void init_yuv_tables() { + ytable = new uint8_t[65536]; + utable = new uint8_t[65536]; + vtable = new uint8_t[65536]; + + for(unsigned i = 0; i < 65536; i++) { + //extract RGB565 color data from i + uint8_t r = (i >> 11) & 31, g = (i >> 5) & 63, b = (i) & 31; + r = (r << 3) | (r >> 2); //R5->R8 + g = (g << 2) | (g >> 4); //G6->G8 + b = (b << 3) | (b >> 2); //B5->B8 + + //ITU-R Recommendation BT.601 + //double lr = 0.299, lg = 0.587, lb = 0.114; + int y = int( +(double(r) * 0.257) + (double(g) * 0.504) + (double(b) * 0.098) + 16.0 ); + int u = int( -(double(r) * 0.148) - (double(g) * 0.291) + (double(b) * 0.439) + 128.0 ); + int v = int( +(double(r) * 0.439) - (double(g) * 0.368) - (double(b) * 0.071) + 128.0 ); + + //ITU-R Recommendation BT.709 + //double lr = 0.2126, lg = 0.7152, lb = 0.0722; + //int y = int( double(r) * lr + double(g) * lg + double(b) * lb ); + //int u = int( (double(b) - y) / (2.0 - 2.0 * lb) + 128.0 ); + //int v = int( (double(r) - y) / (2.0 - 2.0 * lr) + 128.0 ); + + ytable[i] = y < 0 ? 0 : y > 255 ? 255 : y; + utable[i] = u < 0 ? 0 : u > 255 ? 255 : u; + vtable[i] = v < 0 ? 0 : v > 255 ? 255 : v; + } + } + + pVideoXv() { + device.window = 0; + device.colormap = 0; + device.port = -1; + + ytable = 0; + utable = 0; + vtable = 0; + + settings.handle = 0; + settings.synchronize = false; + } + + ~pVideoXv() { + term(); + } +}; + +DeclareVideo(Xv) + +}; diff --git a/mednafen/snes/src/lib/sync.bat b/mednafen/snes/src/lib/sync.bat new file mode 100755 index 0000000..f2c5013 --- /dev/null +++ b/mednafen/snes/src/lib/sync.bat @@ -0,0 +1,7 @@ +rmdir /Q /S nall +rmdir /Q /S ruby + +mkdir nall +mkdir ruby +xcopy /E ..\..\..\nall nall +xcopy /E ..\..\..\ruby ruby diff --git a/mednafen/snes/src/lib/sync.sh b/mednafen/snes/src/lib/sync.sh new file mode 100755 index 0000000..7d0fa7e --- /dev/null +++ b/mednafen/snes/src/lib/sync.sh @@ -0,0 +1,10 @@ +rm -r libco +rm -r nall +rm -r ruby + +cp -r ../../../libco ./libco +cp -r ../../../nall ./nall +cp -r ../../../ruby ./ruby + +rm -r libco/doc +rm -r libco/test diff --git a/mednafen/snes/src/memory/memory-inline.hpp b/mednafen/snes/src/memory/memory-inline.hpp new file mode 100755 index 0000000..d0a9d32 --- /dev/null +++ b/mednafen/snes/src/memory/memory-inline.hpp @@ -0,0 +1,75 @@ +//Memory + +unsigned Memory::size() const { return 0; } + +//StaticRAM + +uint8* StaticRAM::data() { return data_; } +unsigned StaticRAM::size() const { return size_; } + +uint8 StaticRAM::read(unsigned addr) { return data_[addr]; } +void StaticRAM::write(unsigned addr, uint8 n) { data_[addr] = n; } +uint8& StaticRAM::operator[](unsigned addr) { return data_[addr]; } +const uint8& StaticRAM::operator[](unsigned addr) const { return data_[addr]; } + +StaticRAM::StaticRAM(unsigned n) : size_(n) { data_ = new uint8[size_]; } +StaticRAM::~StaticRAM() { delete[] data_; } + +//MappedRAM + +void MappedRAM::reset() { + if(data_) { + delete[] data_; + data_ = 0; + } + size_ = -1U; + write_protect_ = false; +} + +void MappedRAM::map(uint8 *source, unsigned length) { + reset(); + data_ = source; + size_ = data_ && length > 0 ? length : -1U; +} + +void MappedRAM::copy(uint8 *data, unsigned size) { + if(!data_) { + size_ = (size & ~255) + ((bool)(size & 255) << 8); + data_ = new uint8[size_](); + } + memcpy(data_, data, min(size_, size)); +} + +void MappedRAM::write_protect(bool status) { write_protect_ = status; } +uint8* MappedRAM::data() { return data_; } +unsigned MappedRAM::size() const { return size_; } + +uint8 MappedRAM::read(unsigned addr) { return data_[addr]; } +void MappedRAM::write(unsigned addr, uint8 n) { if(!write_protect_) data_[addr] = n; } +const uint8 MappedRAM::operator[](unsigned addr) const { return data_[addr]; } +MappedRAM::MappedRAM() : data_(0), size_(-1U), write_protect_(false) {} + +//Bus + +uint8 Bus::read(unsigned addr) { + #if defined(CHEAT_SYSTEM) + if(cheat.active() && cheat.exists(addr)) { + uint8 r; + if(cheat.read(addr, r)) return r; + } + #endif + + Page &p = page[addr >> 8]; + return p.access->read(p.offset + addr); +} + +void Bus::write(unsigned addr, uint8 data) { + Page &p = page[addr >> 8]; + return p.access->write(p.offset + addr, data); +} + +bool Bus::load_cart() { return false; } +void Bus::unload_cart() {} + +void Bus::power() {} +void Bus::reset() {} diff --git a/mednafen/snes/src/memory/memory.cpp b/mednafen/snes/src/memory/memory.cpp new file mode 100755 index 0000000..14dc996 --- /dev/null +++ b/mednafen/snes/src/memory/memory.cpp @@ -0,0 +1,112 @@ +#include <../base.hpp> + +#define MEMORY_CPP +namespace SNES { + +namespace memory { + MMIOAccess mmio; + StaticRAM wram(128 * 1024); + StaticRAM apuram(64 * 1024); + StaticRAM vram(64 * 1024); + StaticRAM oam(544); + StaticRAM cgram(512); + + UnmappedMemory memory_unmapped; + UnmappedMMIO mmio_unmapped; +}; + +unsigned UnmappedMemory::size() const { return 16 * 1024 * 1024; } +uint8 UnmappedMemory::read(unsigned) { return cpu.regs.mdr; } +void UnmappedMemory::write(unsigned, uint8) {} + +uint8 UnmappedMMIO::mmio_read(unsigned) { return cpu.regs.mdr; } +void UnmappedMMIO::mmio_write(unsigned, uint8) {} + +void MMIOAccess::map(unsigned addr, MMIO &access) { + //MMIO: $[00-3f]:[2000-5fff] + mmio[(addr - 0x2000) & 0x3fff] = &access; +} + +uint8 MMIOAccess::read(unsigned addr) { + return mmio[(addr - 0x2000) & 0x3fff]->mmio_read(addr); +} + +void MMIOAccess::write(unsigned addr, uint8 data) { + mmio[(addr - 0x2000) & 0x3fff]->mmio_write(addr, data); +} + +unsigned Bus::mirror(unsigned addr, unsigned size) { + unsigned base = 0; + if(size) { + unsigned mask = 1 << 23; + while(addr >= size) { + while(!(addr & mask)) mask >>= 1; + addr -= mask; + if(size > mask) { + size -= mask; + base += mask; + } + mask >>= 1; + } + base += addr; + } + return base; +} + +void Bus::map(unsigned addr, Memory &access, unsigned offset) { + page[addr >> 8].access = &access; + page[addr >> 8].offset = offset - addr; +} + +void Bus::map( + MapMode mode, + uint8 bank_lo, uint8 bank_hi, + uint16 addr_lo, uint16 addr_hi, + Memory &access, unsigned offset, unsigned size +) { + assert(bank_lo <= bank_hi); + assert(addr_lo <= addr_hi); + if(access.size() == -1U) return; + + uint8 page_lo = addr_lo >> 8; + uint8 page_hi = addr_hi >> 8; + unsigned index = 0; + + switch(mode) { + case MapDirect: { + for(unsigned bank = bank_lo; bank <= bank_hi; bank++) { + for(unsigned page = page_lo; page <= page_hi; page++) { + map((bank << 16) + (page << 8), access, (bank << 16) + (page << 8)); + } + } + } break; + + case MapLinear: { + for(unsigned bank = bank_lo; bank <= bank_hi; bank++) { + for(unsigned page = page_lo; page <= page_hi; page++) { + map((bank << 16) + (page << 8), access, mirror(offset + index, access.size())); + index += 256; + if(size) index %= size; + } + } + } break; + + case MapShadow: { + for(unsigned bank = bank_lo; bank <= bank_hi; bank++) { + index += page_lo * 256; + if(size) index %= size; + + for(unsigned page = page_lo; page <= page_hi; page++) { + map((bank << 16) + (page << 8), access, mirror(offset + index, access.size())); + index += 256; + if(size) index %= size; + } + + index += (255 - page_hi) * 256; + if(size) index %= size; + } + } break; + } +} + +}; diff --git a/mednafen/snes/src/memory/memory.hpp b/mednafen/snes/src/memory/memory.hpp new file mode 100755 index 0000000..745f62a --- /dev/null +++ b/mednafen/snes/src/memory/memory.hpp @@ -0,0 +1,104 @@ +struct Memory { + virtual inline unsigned size() const; + virtual uint8 read(unsigned addr) = 0; + virtual void write(unsigned addr, uint8 data) = 0; +}; + +struct MMIO { + virtual uint8 mmio_read(unsigned addr) = 0; + virtual void mmio_write(unsigned addr, uint8 data) = 0; +}; + +struct UnmappedMemory : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +struct UnmappedMMIO : MMIO { + uint8 mmio_read(unsigned); + void mmio_write(unsigned, uint8); +}; + +struct StaticRAM : Memory { + inline uint8* data(); + inline unsigned size() const; + + inline uint8 read(unsigned addr); + inline void write(unsigned addr, uint8 n); + inline uint8& operator[](unsigned addr); + inline const uint8& operator[](unsigned addr) const; + + inline StaticRAM(unsigned size); + inline ~StaticRAM(); + +private: + uint8 *data_; + unsigned size_; +}; + +struct MappedRAM : Memory { + inline void reset(); + inline void map(uint8*, unsigned); + inline void copy(uint8*, unsigned); + + inline void write_protect(bool status); + inline uint8* data(); + inline unsigned size() const; + + inline uint8 read(unsigned addr); + inline void write(unsigned addr, uint8 n); + inline const uint8 operator[](unsigned addr) const; + inline MappedRAM(); + +private: + uint8 *data_; + unsigned size_; + bool write_protect_; +}; + +struct MMIOAccess : Memory { + void map(unsigned addr, MMIO &access); + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + MMIO *mmio[0x4000]; +}; + +struct Bus { + unsigned mirror(unsigned addr, unsigned size); + void map(unsigned addr, Memory &access, unsigned offset); + enum MapMode { MapDirect, MapLinear, MapShadow }; + void map(MapMode mode, + uint8 bank_lo, uint8 bank_hi, + uint16 addr_lo, uint16 addr_hi, + Memory &access, unsigned offset = 0, unsigned size = 0); + + alwaysinline uint8 read(unsigned addr); + alwaysinline void write(unsigned addr, uint8 data); + + virtual inline bool load_cart(); + virtual inline void unload_cart(); + + virtual inline void power(); + virtual inline void reset(); + + struct Page { + Memory *access; + unsigned offset; + } page[65536]; + + virtual void serialize(serializer&) {} +}; + +namespace memory { + extern MMIOAccess mmio; //S-CPU, S-PPU + extern StaticRAM wram; //S-CPU + extern StaticRAM apuram; //S-SMP, S-DSP + extern StaticRAM vram; //S-PPU + extern StaticRAM oam; //S-PPU + extern StaticRAM cgram; //S-PPU + + extern UnmappedMemory memory_unmapped; + extern UnmappedMMIO mmio_unmapped; +}; diff --git a/mednafen/snes/src/memory/smemory/generic.cpp b/mednafen/snes/src/memory/smemory/generic.cpp new file mode 100755 index 0000000..92c4588 --- /dev/null +++ b/mednafen/snes/src/memory/smemory/generic.cpp @@ -0,0 +1,112 @@ +#ifdef SMEMORY_CPP + +void sBus::map_generic() { + switch(cartridge.mapper()) { + case Cartridge::LoROM: { + map(MapLinear, 0x00, 0x7f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x80, 0xff, 0x8000, 0xffff, memory::cartrom); + map_generic_sram(); + } break; + + case Cartridge::HiROM: { + map(MapShadow, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x40, 0x7f, 0x0000, 0xffff, memory::cartrom); + map(MapShadow, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom); + map_generic_sram(); + } break; + + case Cartridge::ExLoROM: { + map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x40, 0x7f, 0x0000, 0xffff, memory::cartrom); + map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom); + map_generic_sram(); + } break; + + case Cartridge::ExHiROM: { + map(MapShadow, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom, 0x400000); + map(MapLinear, 0x40, 0x7f, 0x0000, 0xffff, memory::cartrom, 0x400000); + map(MapShadow, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom, 0x000000); + map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom, 0x000000); + map_generic_sram(); + } break; + + case Cartridge::SuperFXROM: { + //mapped via SuperFXBus::init(); + } break; + + case Cartridge::SA1ROM: { + //mapped via SA1Bus::init(); + } break; + + case Cartridge::SPC7110ROM: { + map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic + map(MapShadow, 0x00, 0x0f, 0x8000, 0xffff, memory::cartrom); //program ROM + map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic + map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110); //decompression MMIO port + map(MapShadow, 0x80, 0x8f, 0x8000, 0xffff, memory::cartrom); //program ROM + map(MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom); //program ROM + map(MapDirect, 0xd0, 0xff, 0x0000, 0xffff, spc7110); //MMC-controlled data ROM + } break; + + case Cartridge::BSXROM: { + //full map is dynamically mapped by: + //src/chip/bsx/bsx_cart.cpp : BSXCart::update_memory_map(); + map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); + } break; + + case Cartridge::BSCLoROM: { + map(MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, 0x000000); + map(MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, 0x100000); + map(MapLinear, 0x70, 0x7f, 0x0000, 0x7fff, memory::cartram, 0x000000); + map(MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, 0x200000); + map(MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, 0x100000); + map(MapLinear, 0xc0, 0xef, 0x0000, 0xffff, bsxflash); + map(MapLinear, 0xf0, 0xff, 0x0000, 0x7fff, memory::cartram, 0x000000); + } break; + + case Cartridge::BSCHiROM: { + map(MapShadow, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x20, 0x3f, 0x6000, 0x7fff, memory::cartram); + map(MapShadow, 0x20, 0x3f, 0x8000, 0xffff, bsxflash); + map(MapLinear, 0x40, 0x5f, 0x0000, 0xffff, memory::cartrom); + map(MapLinear, 0x60, 0x7f, 0x0000, 0xffff, bsxflash); + map(MapShadow, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0xa0, 0xbf, 0x6000, 0x7fff, memory::cartram); + map(MapShadow, 0xa0, 0xbf, 0x8000, 0xffff, bsxflash); + map(MapLinear, 0xc0, 0xdf, 0x0000, 0xffff, memory::cartrom); + map(MapLinear, 0xe0, 0xff, 0x0000, 0xffff, bsxflash); + } break; + + case Cartridge::STROM: { + map(MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::stArom); + map(MapLinear, 0x40, 0x5f, 0x8000, 0xffff, memory::stBrom); + map(MapLinear, 0x60, 0x63, 0x8000, 0xffff, memory::stAram); + map(MapLinear, 0x70, 0x73, 0x8000, 0xffff, memory::stBram); + map(MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom); + map(MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::stArom); + map(MapLinear, 0xc0, 0xdf, 0x8000, 0xffff, memory::stBrom); + map(MapLinear, 0xe0, 0xe3, 0x8000, 0xffff, memory::stAram); + map(MapLinear, 0xf0, 0xf3, 0x8000, 0xffff, memory::stBram); + } break; + } +} + +void sBus::map_generic_sram() { + if(memory::cartram.size() == 0 || memory::cartram.size() == -1U) { return; } + + map(MapLinear, 0x20, 0x3f, 0x6000, 0x7fff, memory::cartram); + map(MapLinear, 0xa0, 0xbf, 0x6000, 0x7fff, memory::cartram); + + //research shows only games with very large ROM/RAM sizes require MAD-1 memory mapping of RAM + //otherwise, default to safer, larger RAM address window + uint16 addr_hi = (memory::cartrom.size() > 0x200000 || memory::cartram.size() > 32 * 1024) ? 0x7fff : 0xffff; + map(MapLinear, 0x70, 0x7f, 0x0000, addr_hi, memory::cartram); + if(cartridge.mapper() != Cartridge::LoROM) return; + map(MapLinear, 0xf0, 0xff, 0x0000, addr_hi, memory::cartram); +} + +#endif diff --git a/mednafen/snes/src/memory/smemory/serialization.cpp b/mednafen/snes/src/memory/smemory/serialization.cpp new file mode 100755 index 0000000..0c0ff6a --- /dev/null +++ b/mednafen/snes/src/memory/smemory/serialization.cpp @@ -0,0 +1,11 @@ +#ifdef SMEMORY_CPP + +void sBus::serialize(serializer &s) { + s.array(memory::wram.data(), memory::wram.size()); + s.array(memory::apuram.data(), memory::apuram.size()); + s.array(memory::vram.data(), memory::vram.size()); + s.array(memory::oam.data(), memory::oam.size()); + s.array(memory::cgram.data(), memory::cgram.size()); +} + +#endif diff --git a/mednafen/snes/src/memory/smemory/smemory.cpp b/mednafen/snes/src/memory/smemory/smemory.cpp new file mode 100755 index 0000000..c14a38a --- /dev/null +++ b/mednafen/snes/src/memory/smemory/smemory.cpp @@ -0,0 +1,38 @@ +#include <../base.hpp> + +#define SMEMORY_CPP +namespace SNES { + +sBus bus; + +#include "system.cpp" +#include "generic.cpp" +#include "serialization.cpp" + +void sBus::power() { + for(unsigned i = 0x2000; i <= 0x5fff; i++) memory::mmio.map(i, memory::mmio_unmapped); + for(unsigned i = 0; i < memory::wram.size(); i++) memory::wram[i] = config.cpu.wram_init_value; +} + +void sBus::reset() { +} + +bool sBus::load_cart() { + if(cartridge.loaded() == true) return false; + + map_reset(); + map_generic(); + map_system(); + return true; +} + +void sBus::unload_cart() { +} + +sBus::sBus() { +} + +sBus::~sBus() { +} + +}; diff --git a/mednafen/snes/src/memory/smemory/smemory.hpp b/mednafen/snes/src/memory/smemory/smemory.hpp new file mode 100755 index 0000000..5b5d1a1 --- /dev/null +++ b/mednafen/snes/src/memory/smemory/smemory.hpp @@ -0,0 +1,20 @@ +class sBus : public Bus { +public: + bool load_cart(); + void unload_cart(); + + void power(); + void reset(); + + void serialize(serializer&); + sBus(); + ~sBus(); + +private: + void map_reset(); + void map_system(); + void map_generic(); + void map_generic_sram(); +}; + +extern sBus bus; diff --git a/mednafen/snes/src/memory/smemory/system.cpp b/mednafen/snes/src/memory/smemory/system.cpp new file mode 100755 index 0000000..1612b9f --- /dev/null +++ b/mednafen/snes/src/memory/smemory/system.cpp @@ -0,0 +1,18 @@ +#ifdef SMEMORY_CPP + +void sBus::map_reset() { + map(MapDirect, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped); + for(unsigned i = 0x2000; i <= 0x5fff; i++) memory::mmio.map(i, memory::mmio_unmapped); +} + +void sBus::map_system() { + map(MapDirect, 0x00, 0x3f, 0x2000, 0x5fff, memory::mmio); + map(MapDirect, 0x80, 0xbf, 0x2000, 0x5fff, memory::mmio); + + map(MapLinear, 0x00, 0x3f, 0x0000, 0x1fff, memory::wram, 0x000000, 0x002000); + map(MapLinear, 0x80, 0xbf, 0x0000, 0x1fff, memory::wram, 0x000000, 0x002000); + + map(MapLinear, 0x7e, 0x7f, 0x0000, 0xffff, memory::wram); +} + +#endif diff --git a/mednafen/snes/src/ppu/bppu/bppu.cpp b/mednafen/snes/src/ppu/bppu/bppu.cpp new file mode 100755 index 0000000..58dab85 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/bppu.cpp @@ -0,0 +1,378 @@ +#include <../base.hpp> + +#define BPPU_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + bPPUDebugger ppu; +#else + bPPU ppu; +#endif + +#include "memory/memory.cpp" +#include "mmio/mmio.cpp" +#include "render/render.cpp" +#include "serialization.cpp" + +void bPPU::enter() { + while(true) { + while(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + + //H = 0 (initialize) + scanline(); + add_clocks(10); + + //H = 10 (cache mode7 registers + OAM address reset) + cache.m7_hofs = regs.m7_hofs; + cache.m7_vofs = regs.m7_vofs; + cache.m7a = regs.m7a; + cache.m7b = regs.m7b; + cache.m7c = regs.m7c; + cache.m7d = regs.m7d; + cache.m7x = regs.m7x; + cache.m7y = regs.m7y; + if(vcounter() == (!overscan() ? 225 : 240)) { + if(regs.display_disabled == false) { + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; + } + } + add_clocks(502); + + //H = 512 (render) + render_scanline(); + add_clocks(640); + + //H = 1152 (cache OBSEL) + if(cache.oam_basesize != regs.oam_basesize) { + cache.oam_basesize = regs.oam_basesize; + sprite_list_valid = false; + } + cache.oam_nameselect = regs.oam_nameselect; + cache.oam_tdaddr = regs.oam_tdaddr; + add_clocks(lineclocks() - 1152); //seek to start of next scanline + } +} + +void bPPU::add_clocks(unsigned clocks) { + tick(clocks); + scheduler.addclocks_ppu(clocks); + scheduler.sync_ppucpu(); +} + +void bPPU::scanline() { + line = vcounter(); + + if(line == 0) { + frame(); + + //RTO flag reset + regs.time_over = false; + regs.range_over = false; + } + + if(line == 1) { + //mosaic reset + for(int bg = BG1; bg <= BG4; bg++) regs.bg_y[bg] = 1; + regs.mosaic_countdown = regs.mosaic_size + 1; + regs.mosaic_countdown--; + } else { + for(int bg = BG1; bg <= BG4; bg++) { + if(!regs.mosaic_enabled[bg] || !regs.mosaic_countdown) regs.bg_y[bg] = line; + } + if(!regs.mosaic_countdown) regs.mosaic_countdown = regs.mosaic_size + 1; + regs.mosaic_countdown--; + } +} + +void bPPU::render_scanline() { + #ifdef FAST_FRAMESKIP + //note: this bypasses RTO status flag calculations, which is observable by software + if(status.render_output == false) return; + #endif + + if(line >= 1 && line < (!overscan() ? 225 : 240)) { + render_line_oam_rto(); + render_line(); + } +} + +void bPPU::frame() { + PPU::frame(); + system.frame(); + + if(field() == 0) { + display.interlace = regs.interlace; + regs.scanlines = (regs.overscan == false) ? 224 : 239; + } +} + +void bPPU::power() { + PPU::power(); + + for(unsigned i = 0; i < memory::vram.size(); i++) memory::vram[i] = 0x00; + for(unsigned i = 0; i < memory::oam.size(); i++) memory::oam[i] = 0x00; + for(unsigned i = 0; i < memory::cgram.size(); i++) memory::cgram[i] = 0x00; + flush_tiledata_cache(); + + region = (system.region() == System::NTSC ? 0 : 1); //0 = NTSC, 1 = PAL + + //$2100 + regs.display_disabled = true; + regs.display_brightness = 15; + + //$2101 + regs.oam_basesize = 0; + regs.oam_nameselect = 0; + regs.oam_tdaddr = 0x0000; + + cache.oam_basesize = 0; + cache.oam_nameselect = 0; + cache.oam_tdaddr = 0x0000; + + //$2102-$2103 + regs.oam_baseaddr = 0x0000; + regs.oam_addr = 0x0000; + regs.oam_priority = false; + regs.oam_firstsprite = 0; + + //$2104 + regs.oam_latchdata = 0x00; + + //$2105 + regs.bg_tilesize[BG1] = 0; + regs.bg_tilesize[BG2] = 0; + regs.bg_tilesize[BG3] = 0; + regs.bg_tilesize[BG4] = 0; + regs.bg3_priority = 0; + regs.bg_mode = 0; + + //$2106 + regs.mosaic_size = 0; + regs.mosaic_enabled[BG1] = false; + regs.mosaic_enabled[BG2] = false; + regs.mosaic_enabled[BG3] = false; + regs.mosaic_enabled[BG4] = false; + regs.mosaic_countdown = 0; + + //$2107-$210a + regs.bg_scaddr[BG1] = 0x0000; + regs.bg_scaddr[BG2] = 0x0000; + regs.bg_scaddr[BG3] = 0x0000; + regs.bg_scaddr[BG4] = 0x0000; + regs.bg_scsize[BG1] = SC_32x32; + regs.bg_scsize[BG2] = SC_32x32; + regs.bg_scsize[BG3] = SC_32x32; + regs.bg_scsize[BG4] = SC_32x32; + + //$210b-$210c + regs.bg_tdaddr[BG1] = 0x0000; + regs.bg_tdaddr[BG2] = 0x0000; + regs.bg_tdaddr[BG3] = 0x0000; + regs.bg_tdaddr[BG4] = 0x0000; + + //$210d-$2114 + regs.bg_ofslatch = 0x00; + regs.m7_hofs = regs.m7_vofs = 0x0000; + regs.bg_hofs[BG1] = regs.bg_vofs[BG1] = 0x0000; + regs.bg_hofs[BG2] = regs.bg_vofs[BG2] = 0x0000; + regs.bg_hofs[BG3] = regs.bg_vofs[BG3] = 0x0000; + regs.bg_hofs[BG4] = regs.bg_vofs[BG4] = 0x0000; + + //$2115 + regs.vram_incmode = 1; + regs.vram_mapping = 0; + regs.vram_incsize = 1; + + //$2116-$2117 + regs.vram_addr = 0x0000; + + //$211a + regs.mode7_repeat = 0; + regs.mode7_vflip = false; + regs.mode7_hflip = false; + + //$211b-$2120 + regs.m7_latch = 0x00; + regs.m7a = 0x0000; + regs.m7b = 0x0000; + regs.m7c = 0x0000; + regs.m7d = 0x0000; + regs.m7x = 0x0000; + regs.m7y = 0x0000; + + //$2121 + regs.cgram_addr = 0x0000; + + //$2122 + regs.cgram_latchdata = 0x00; + + //$2123-$2125 + regs.window1_enabled[BG1] = false; + regs.window1_enabled[BG2] = false; + regs.window1_enabled[BG3] = false; + regs.window1_enabled[BG4] = false; + regs.window1_enabled[OAM] = false; + regs.window1_enabled[COL] = false; + + regs.window1_invert [BG1] = false; + regs.window1_invert [BG2] = false; + regs.window1_invert [BG3] = false; + regs.window1_invert [BG4] = false; + regs.window1_invert [OAM] = false; + regs.window1_invert [COL] = false; + + regs.window2_enabled[BG1] = false; + regs.window2_enabled[BG2] = false; + regs.window2_enabled[BG3] = false; + regs.window2_enabled[BG4] = false; + regs.window2_enabled[OAM] = false; + regs.window2_enabled[COL] = false; + + regs.window2_invert [BG1] = false; + regs.window2_invert [BG2] = false; + regs.window2_invert [BG3] = false; + regs.window2_invert [BG4] = false; + regs.window2_invert [OAM] = false; + regs.window2_invert [COL] = false; + + //$2126-$2129 + regs.window1_left = 0x00; + regs.window1_right = 0x00; + regs.window2_left = 0x00; + regs.window2_right = 0x00; + + //$212a-$212b + regs.window_mask[BG1] = 0; + regs.window_mask[BG2] = 0; + regs.window_mask[BG3] = 0; + regs.window_mask[BG4] = 0; + regs.window_mask[OAM] = 0; + regs.window_mask[COL] = 0; + + //$212c-$212d + regs.bg_enabled[BG1] = false; + regs.bg_enabled[BG2] = false; + regs.bg_enabled[BG3] = false; + regs.bg_enabled[BG4] = false; + regs.bg_enabled[OAM] = false; + regs.bgsub_enabled[BG1] = false; + regs.bgsub_enabled[BG2] = false; + regs.bgsub_enabled[BG3] = false; + regs.bgsub_enabled[BG4] = false; + regs.bgsub_enabled[OAM] = false; + + //$212e-$212f + regs.window_enabled[BG1] = false; + regs.window_enabled[BG2] = false; + regs.window_enabled[BG3] = false; + regs.window_enabled[BG4] = false; + regs.window_enabled[OAM] = false; + regs.sub_window_enabled[BG1] = false; + regs.sub_window_enabled[BG2] = false; + regs.sub_window_enabled[BG3] = false; + regs.sub_window_enabled[BG4] = false; + regs.sub_window_enabled[OAM] = false; + + //$2130 + regs.color_mask = 0; + regs.colorsub_mask = 0; + regs.addsub_mode = false; + regs.direct_color = false; + + //$2131 + regs.color_mode = 0; + regs.color_halve = false; + regs.color_enabled[BACK] = false; + regs.color_enabled[OAM] = false; + regs.color_enabled[BG4] = false; + regs.color_enabled[BG3] = false; + regs.color_enabled[BG2] = false; + regs.color_enabled[BG1] = false; + + //$2132 + regs.color_r = 0x00; + regs.color_g = 0x00; + regs.color_b = 0x00; + regs.color_rgb = 0x0000; + + //$2133 + regs.mode7_extbg = false; + regs.pseudo_hires = false; + regs.overscan = false; + regs.scanlines = 224; + regs.oam_interlace = false; + regs.interlace = false; + + //$2137 + regs.hcounter = 0; + regs.vcounter = 0; + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + regs.counters_latched = false; + + //$2139-$213a + regs.vram_readbuffer = 0x0000; + + //$213e + regs.time_over = false; + regs.range_over = false; + + reset(); +} + +void bPPU::reset() { + PPU::reset(); + PPU::frame(); + + //$2100 + regs.display_disabled = true; + + display.interlace = false; + display.overscan = false; + regs.scanlines = 224; + + memset(sprite_list, 0, sizeof(sprite_list)); + sprite_list_valid = false; + + //open bus support + regs.ppu1_mdr = 0xff; + regs.ppu2_mdr = 0xff; + + //bg line counters + regs.bg_y[0] = 0; + regs.bg_y[1] = 0; + regs.bg_y[2] = 0; + regs.bg_y[3] = 0; +} + +bPPU::bPPU() { + alloc_tiledata_cache(); + + for(int l = 0; l < 16; l++) { + for(int i = 0; i < 4096; i++) { + mosaic_table[l][i] = (i / (l + 1)) * (l + 1); + } + } + + for(int l = 0; l < 16; l++) { + double m = (double)l / 15.0; + for(int i = 0; i < 32 * 32; i++) { + int r = (int)((double)((i) & 31) * m + 0.5); + int g = (int)((double)((i >> 5) & 31) * m + 0.5); + r = max(0, min(31, r)); + g = max(0, min(31, g)); + if(i < 32) light_table_b[l][i] = (r << 10); + light_table_gr[l][i] = (g << 5) | (r); + } + } +} + +bPPU::~bPPU() { + free_tiledata_cache(); +} + +}; diff --git a/mednafen/snes/src/ppu/bppu/bppu.hpp b/mednafen/snes/src/ppu/bppu/bppu.hpp new file mode 100755 index 0000000..7a4860c --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/bppu.hpp @@ -0,0 +1,65 @@ +class bPPU : public PPU { +public: + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + #include "render/render.hpp" + + void enter(); + void add_clocks(unsigned clocks); + + uint8 region; + unsigned line; + + enum { NTSC = 0, PAL = 1 }; + enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 }; + enum { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 }; + + struct { + bool interlace; + bool overscan; + } display; + + struct { + //$2101 + uint8 oam_basesize; + uint8 oam_nameselect; + uint16 oam_tdaddr; + + //$210d-$210e + uint16 m7_hofs, m7_vofs; + + //$211b-$2120 + uint16 m7a, m7b, m7c, m7d, m7x, m7y; + } cache; + + alwaysinline bool interlace() const { return display.interlace; } + alwaysinline bool overscan() const { return display.overscan; } + alwaysinline bool hires() const { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); } + + uint16 light_table_b[16][32]; + uint16 light_table_gr[16][32 * 32]; + uint16 mosaic_table[16][4096]; + void render_line(); + + void update_oam_status(); + //required functions + void run(); + void scanline(); + void render_scanline(); + void frame(); + void power(); + void reset(); + + void serialize(serializer&); + bPPU(); + ~bPPU(); + + friend class bPPUDebug; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern bPPUDebugger ppu; +#else + extern bPPU ppu; +#endif diff --git a/mednafen/snes/src/ppu/bppu/debugger/debugger.cpp b/mednafen/snes/src/ppu/bppu/debugger/debugger.cpp new file mode 100755 index 0000000..2d015e9 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/debugger/debugger.cpp @@ -0,0 +1,290 @@ +#ifdef BPPU_CPP + +#include "render.cpp" + +uint8 bPPUDebugger::vram_mmio_read(uint16 addr) { + uint8 data = bPPU::vram_mmio_read(addr); + debugger.breakpoint_test(Debugger::Breakpoint::VRAM, Debugger::Breakpoint::Read, addr, data); + return data; +} + +void bPPUDebugger::vram_mmio_write(uint16 addr, uint8 data) { + bPPU::vram_mmio_write(addr, data); + debugger.breakpoint_test(Debugger::Breakpoint::VRAM, Debugger::Breakpoint::Write, addr, data); +} + +uint8 bPPUDebugger::oam_mmio_read(uint16 addr) { + uint8 data = bPPU::oam_mmio_read(addr); + debugger.breakpoint_test(Debugger::Breakpoint::OAM, Debugger::Breakpoint::Read, addr, data); + return data; +} + +void bPPUDebugger::oam_mmio_write(uint16 addr, uint8 data) { + bPPU::oam_mmio_write(addr, data); + debugger.breakpoint_test(Debugger::Breakpoint::OAM, Debugger::Breakpoint::Write, addr, data); +} + +uint8 bPPUDebugger::cgram_mmio_read(uint16 addr) { + uint8 data = bPPU::cgram_mmio_read(addr); + debugger.breakpoint_test(Debugger::Breakpoint::CGRAM, Debugger::Breakpoint::Read, addr, data); + return data; +} + +void bPPUDebugger::cgram_mmio_write(uint16 addr, uint8 data) { + bPPU::cgram_mmio_write(addr, data); + debugger.breakpoint_test(Debugger::Breakpoint::CGRAM, Debugger::Breakpoint::Write, addr, data); +} + +bPPUDebugger::bPPUDebugger() { + bg1_enabled[0] = bg1_enabled[1] = true; + bg2_enabled[0] = bg2_enabled[1] = true; + bg3_enabled[0] = bg3_enabled[1] = true; + bg4_enabled[0] = bg4_enabled[1] = true; + oam_enabled[0] = oam_enabled[1] = oam_enabled[2] = oam_enabled[3] = true; +} + +//=========== +//PPUDebugger +//=========== + +//internal +unsigned bPPUDebugger::ppu1_mdr() { return regs.ppu1_mdr; } +unsigned bPPUDebugger::ppu2_mdr() { return regs.ppu2_mdr; } + +//$2100 +bool bPPUDebugger::display_disable() { return regs.display_disabled; } +unsigned bPPUDebugger::display_brightness() { return regs.display_brightness; } + +//$2101 +unsigned bPPUDebugger::oam_base_size() { return regs.oam_basesize; } +unsigned bPPUDebugger::oam_name_select() { return regs.oam_nameselect; } +unsigned bPPUDebugger::oam_name_base_address() { return regs.oam_tdaddr; } + +//$2102-$2103 +unsigned bPPUDebugger::oam_base_address() { return regs.oam_baseaddr; } +bool bPPUDebugger::oam_priority() { return regs.oam_priority; } + +//$2105 +bool bPPUDebugger::bg1_tile_size() { return regs.bg_tilesize[BG1]; } +bool bPPUDebugger::bg2_tile_size() { return regs.bg_tilesize[BG2]; } +bool bPPUDebugger::bg3_tile_size() { return regs.bg_tilesize[BG3]; } +bool bPPUDebugger::bg4_tile_size() { return regs.bg_tilesize[BG4]; } +bool bPPUDebugger::bg3_priority() { return regs.bg3_priority; } +unsigned bPPUDebugger::bg_mode() { return regs.bg_mode; } + +//$2106 +unsigned bPPUDebugger::mosaic_size() { return regs.mosaic_size; } +bool bPPUDebugger::bg1_mosaic_enable() { return regs.mosaic_enabled[BG1]; } +bool bPPUDebugger::bg2_mosaic_enable() { return regs.mosaic_enabled[BG2]; } +bool bPPUDebugger::bg3_mosaic_enable() { return regs.mosaic_enabled[BG3]; } +bool bPPUDebugger::bg4_mosaic_enable() { return regs.mosaic_enabled[BG4]; } + +//$2107 +unsigned bPPUDebugger::bg1_screen_address() { return regs.bg_scaddr[BG1]; } +unsigned bPPUDebugger::bg1_screen_size() { return regs.bg_scsize[BG1]; } + +//$2108 +unsigned bPPUDebugger::bg2_screen_address() { return regs.bg_scaddr[BG2]; } +unsigned bPPUDebugger::bg2_screen_size() { return regs.bg_scsize[BG2]; } + +//$2109 +unsigned bPPUDebugger::bg3_screen_address() { return regs.bg_scaddr[BG3]; } +unsigned bPPUDebugger::bg3_screen_size() { return regs.bg_scsize[BG3]; } + +//$210a +unsigned bPPUDebugger::bg4_screen_address() { return regs.bg_scaddr[BG4]; } +unsigned bPPUDebugger::bg4_screen_size() { return regs.bg_scsize[BG4]; } + +//$210b +unsigned bPPUDebugger::bg1_name_base_address() { return regs.bg_tdaddr[BG1]; } +unsigned bPPUDebugger::bg2_name_base_address() { return regs.bg_tdaddr[BG2]; } + +//$210c +unsigned bPPUDebugger::bg3_name_base_address() { return regs.bg_tdaddr[BG3]; } +unsigned bPPUDebugger::bg4_name_base_address() { return regs.bg_tdaddr[BG4]; } + +//$210d +unsigned bPPUDebugger::mode7_hoffset() { return regs.m7_hofs & 0x1fff; } +unsigned bPPUDebugger::bg1_hoffset() { return regs.bg_hofs[BG1] & 0x03ff; } + +//$210e +unsigned bPPUDebugger::mode7_voffset() { return regs.m7_vofs & 0x1fff; } +unsigned bPPUDebugger::bg1_voffset() { return regs.bg_vofs[BG1] & 0x03ff; } + +//$210f +unsigned bPPUDebugger::bg2_hoffset() { return regs.bg_hofs[BG2] & 0x03ff; } + +//$2110 +unsigned bPPUDebugger::bg2_voffset() { return regs.bg_vofs[BG2] & 0x03ff; } + +//$2111 +unsigned bPPUDebugger::bg3_hoffset() { return regs.bg_hofs[BG3] & 0x03ff; } + +//$2112 +unsigned bPPUDebugger::bg3_voffset() { return regs.bg_vofs[BG3] & 0x03ff; } + +//$2113 +unsigned bPPUDebugger::bg4_hoffset() { return regs.bg_hofs[BG4] & 0x03ff; } + +//$2114 +unsigned bPPUDebugger::bg4_voffset() { return regs.bg_vofs[BG4] & 0x03ff; } + +//$2115 +bool bPPUDebugger::vram_increment_mode() { return regs.vram_incmode; } +unsigned bPPUDebugger::vram_increment_formation() { return regs.vram_mapping; } +unsigned bPPUDebugger::vram_increment_size() { return regs.vram_incsize; } + +//$2116-$2117 +unsigned bPPUDebugger::vram_address() { return regs.vram_addr; } + +//$211a +unsigned bPPUDebugger::mode7_repeat() { return regs.mode7_repeat; } +bool bPPUDebugger::mode7_vflip() { return regs.mode7_vflip; } +bool bPPUDebugger::mode7_hflip() { return regs.mode7_hflip; } + +//$211b +unsigned bPPUDebugger::mode7_a() { return regs.m7a; } + +//$211c +unsigned bPPUDebugger::mode7_b() { return regs.m7b; } + +//$211d +unsigned bPPUDebugger::mode7_c() { return regs.m7c; } + +//$211e +unsigned bPPUDebugger::mode7_d() { return regs.m7d; } + +//$211f +unsigned bPPUDebugger::mode7_x() { return regs.m7x; } + +//$2120 +unsigned bPPUDebugger::mode7_y() { return regs.m7y; } + +//$2121 +unsigned bPPUDebugger::cgram_address() { return regs.cgram_addr; } + +//$2123 +bool bPPUDebugger::bg1_window1_enable() { return regs.window1_enabled[BG1]; } +bool bPPUDebugger::bg1_window1_invert() { return regs.window1_invert [BG1]; } +bool bPPUDebugger::bg1_window2_enable() { return regs.window2_enabled[BG1]; } +bool bPPUDebugger::bg1_window2_invert() { return regs.window2_invert [BG1]; } +bool bPPUDebugger::bg2_window1_enable() { return regs.window1_enabled[BG2]; } +bool bPPUDebugger::bg2_window1_invert() { return regs.window1_invert [BG2]; } +bool bPPUDebugger::bg2_window2_enable() { return regs.window2_enabled[BG2]; } +bool bPPUDebugger::bg2_window2_invert() { return regs.window2_invert [BG2]; } + +//$2124 +bool bPPUDebugger::bg3_window1_enable() { return regs.window1_enabled[BG3]; } +bool bPPUDebugger::bg3_window1_invert() { return regs.window1_invert [BG3]; } +bool bPPUDebugger::bg3_window2_enable() { return regs.window2_enabled[BG3]; } +bool bPPUDebugger::bg3_window2_invert() { return regs.window2_invert [BG3]; } +bool bPPUDebugger::bg4_window1_enable() { return regs.window1_enabled[BG4]; } +bool bPPUDebugger::bg4_window1_invert() { return regs.window1_invert [BG4]; } +bool bPPUDebugger::bg4_window2_enable() { return regs.window2_enabled[BG4]; } +bool bPPUDebugger::bg4_window2_invert() { return regs.window2_invert [BG4]; } + +//$2125 +bool bPPUDebugger::oam_window1_enable() { return regs.window1_enabled[OAM]; } +bool bPPUDebugger::oam_window1_invert() { return regs.window1_invert [OAM]; } +bool bPPUDebugger::oam_window2_enable() { return regs.window2_enabled[OAM]; } +bool bPPUDebugger::oam_window2_invert() { return regs.window2_invert [OAM]; } +bool bPPUDebugger::color_window1_enable() { return regs.window1_enabled[COL]; } +bool bPPUDebugger::color_window1_invert() { return regs.window1_invert [COL]; } +bool bPPUDebugger::color_window2_enable() { return regs.window2_enabled[COL]; } +bool bPPUDebugger::color_window2_invert() { return regs.window2_enabled[COL]; } + +//$2126 +unsigned bPPUDebugger::window1_left() { return regs.window1_left; } + +//$2127 +unsigned bPPUDebugger::window1_right() { return regs.window1_right; } + +//$2128 +unsigned bPPUDebugger::window2_left() { return regs.window2_left; } + +//$2129 +unsigned bPPUDebugger::window2_right() { return regs.window2_right; } + +//$212a +unsigned bPPUDebugger::bg1_window_mask() { return regs.window_mask[BG1]; } +unsigned bPPUDebugger::bg2_window_mask() { return regs.window_mask[BG2]; } +unsigned bPPUDebugger::bg3_window_mask() { return regs.window_mask[BG3]; } +unsigned bPPUDebugger::bg4_window_mask() { return regs.window_mask[BG4]; } + +//$212b +unsigned bPPUDebugger::oam_window_mask() { return regs.window_mask[OAM]; } +unsigned bPPUDebugger::color_window_mask() { return regs.window_mask[COL]; } + +//$212c +bool bPPUDebugger::bg1_mainscreen_enable() { return regs.bg_enabled[BG1]; } +bool bPPUDebugger::bg2_mainscreen_enable() { return regs.bg_enabled[BG2]; } +bool bPPUDebugger::bg3_mainscreen_enable() { return regs.bg_enabled[BG3]; } +bool bPPUDebugger::bg4_mainscreen_enable() { return regs.bg_enabled[BG4]; } +bool bPPUDebugger::oam_mainscreen_enable() { return regs.bg_enabled[OAM]; } + +//$212d +bool bPPUDebugger::bg1_subscreen_enable() { return regs.bgsub_enabled[BG1]; } +bool bPPUDebugger::bg2_subscreen_enable() { return regs.bgsub_enabled[BG2]; } +bool bPPUDebugger::bg3_subscreen_enable() { return regs.bgsub_enabled[BG3]; } +bool bPPUDebugger::bg4_subscreen_enable() { return regs.bgsub_enabled[BG4]; } +bool bPPUDebugger::oam_subscreen_enable() { return regs.bgsub_enabled[OAM]; } + +//$212e +bool bPPUDebugger::bg1_mainscreen_window_enable() { return regs.window_enabled[BG1]; } +bool bPPUDebugger::bg2_mainscreen_window_enable() { return regs.window_enabled[BG2]; } +bool bPPUDebugger::bg3_mainscreen_window_enable() { return regs.window_enabled[BG3]; } +bool bPPUDebugger::bg4_mainscreen_window_enable() { return regs.window_enabled[BG4]; } +bool bPPUDebugger::oam_mainscreen_window_enable() { return regs.window_enabled[OAM]; } + +//$212f +bool bPPUDebugger::bg1_subscreen_window_enable() { return regs.sub_window_enabled[BG1]; } +bool bPPUDebugger::bg2_subscreen_window_enable() { return regs.sub_window_enabled[BG2]; } +bool bPPUDebugger::bg3_subscreen_window_enable() { return regs.sub_window_enabled[BG3]; } +bool bPPUDebugger::bg4_subscreen_window_enable() { return regs.sub_window_enabled[BG4]; } +bool bPPUDebugger::oam_subscreen_window_enable() { return regs.sub_window_enabled[OAM]; } + +//$2130 +unsigned bPPUDebugger::color_mainscreen_window_mask() { return regs.color_mask; } +unsigned bPPUDebugger::color_subscreen_window_mask() { return regs.colorsub_mask; } +bool bPPUDebugger::color_add_subtract_mode() { return regs.addsub_mode; } +bool bPPUDebugger::direct_color() { return regs.direct_color; } + +//$2131 +bool bPPUDebugger::color_mode() { return regs.color_mode; } +bool bPPUDebugger::color_halve() { return regs.color_halve; } +bool bPPUDebugger::bg1_color_enable() { return regs.color_enabled[BG1]; } +bool bPPUDebugger::bg2_color_enable() { return regs.color_enabled[BG2]; } +bool bPPUDebugger::bg3_color_enable() { return regs.color_enabled[BG3]; } +bool bPPUDebugger::bg4_color_enable() { return regs.color_enabled[BG4]; } +bool bPPUDebugger::oam_color_enable() { return regs.color_enabled[OAM]; } +bool bPPUDebugger::back_color_enable() { return regs.color_enabled[BACK]; } + +//$2132 +unsigned bPPUDebugger::color_constant_blue() { return regs.color_b; } +unsigned bPPUDebugger::color_constant_green() { return regs.color_g; } +unsigned bPPUDebugger::color_constant_red() { return regs.color_r; } + +//$2133 +bool bPPUDebugger::mode7_extbg() { return regs.mode7_extbg; } +bool bPPUDebugger::pseudo_hires() { return regs.pseudo_hires; } +bool bPPUDebugger::overscan() { return regs.overscan; } +bool bPPUDebugger::oam_interlace() { return regs.oam_interlace; } +bool bPPUDebugger::interlace() { return regs.interlace; } + +//$213c +unsigned bPPUDebugger::hcounter() { return bPPU::hcounter(); } + +//$213d +unsigned bPPUDebugger::vcounter() { return bPPU::vcounter(); } + +//$213e +bool bPPUDebugger::range_over() { return regs.range_over; } +bool bPPUDebugger::time_over() { return regs.time_over; } +unsigned bPPUDebugger::ppu1_version() { return PPU::ppu1_version; } + +//$213f +bool bPPUDebugger::field() { return cpu.field(); } +bool bPPUDebugger::region() { return bPPU::region; } +unsigned bPPUDebugger::ppu2_version() { return PPU::ppu2_version; } + +#endif diff --git a/mednafen/snes/src/ppu/bppu/debugger/debugger.hpp b/mednafen/snes/src/ppu/bppu/debugger/debugger.hpp new file mode 100755 index 0000000..8d254c5 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/debugger/debugger.hpp @@ -0,0 +1,272 @@ +class bPPUDebugger : public bPPU, public PPUDebugger { +public: + bool bg1_enabled[2]; + bool bg2_enabled[2]; + bool bg3_enabled[2]; + bool bg4_enabled[2]; + bool oam_enabled[4]; + + uint8 vram_mmio_read(uint16 addr); + void vram_mmio_write(uint16 addr, uint8 data); + + uint8 oam_mmio_read(uint16 addr); + void oam_mmio_write(uint16 addr, uint8 data); + + uint8 cgram_mmio_read(uint16 addr); + void cgram_mmio_write(uint16 addr, uint8 data); + + void render_line_mode0(); + void render_line_mode1(); + void render_line_mode2(); + void render_line_mode3(); + void render_line_mode4(); + void render_line_mode5(); + void render_line_mode6(); + void render_line_mode7(); + + bPPUDebugger(); + + //=========== + //PPUDebugger + //=========== + + //internal + unsigned ppu1_mdr(); + unsigned ppu2_mdr(); + + //$2100 + bool display_disable(); + unsigned display_brightness(); + + //$2101 + unsigned oam_base_size(); + unsigned oam_name_select(); + unsigned oam_name_base_address(); + + //$2102-$2103 + unsigned oam_base_address(); + bool oam_priority(); + + //$2105 + bool bg1_tile_size(); + bool bg2_tile_size(); + bool bg3_tile_size(); + bool bg4_tile_size(); + bool bg3_priority(); + unsigned bg_mode(); + + //$2106 + unsigned mosaic_size(); + bool bg1_mosaic_enable(); + bool bg2_mosaic_enable(); + bool bg3_mosaic_enable(); + bool bg4_mosaic_enable(); + + //$2107 + unsigned bg1_screen_address(); + unsigned bg1_screen_size(); + + //$2108 + unsigned bg2_screen_address(); + unsigned bg2_screen_size(); + + //$2109 + unsigned bg3_screen_address(); + unsigned bg3_screen_size(); + + //$210a + unsigned bg4_screen_address(); + unsigned bg4_screen_size(); + + //$210b + unsigned bg1_name_base_address(); + unsigned bg2_name_base_address(); + + //$210c + unsigned bg3_name_base_address(); + unsigned bg4_name_base_address(); + + //$210d + unsigned mode7_hoffset(); + unsigned bg1_hoffset(); + + //$210e + unsigned mode7_voffset(); + unsigned bg1_voffset(); + + //$210f + unsigned bg2_hoffset(); + + //$2110 + unsigned bg2_voffset(); + + //$2111 + unsigned bg3_hoffset(); + + //$2112 + unsigned bg3_voffset(); + + //$2113 + unsigned bg4_hoffset(); + + //$2114 + unsigned bg4_voffset(); + + //$2115 + bool vram_increment_mode(); + unsigned vram_increment_formation(); + unsigned vram_increment_size(); + + //$2116-$2117 + unsigned vram_address(); + + //$211a + unsigned mode7_repeat(); + bool mode7_vflip(); + bool mode7_hflip(); + + //$211b + unsigned mode7_a(); + + //$211c + unsigned mode7_b(); + + //$211d + unsigned mode7_c(); + + //$211e + unsigned mode7_d(); + + //$211f + unsigned mode7_x(); + + //$2120 + unsigned mode7_y(); + + //$2121 + unsigned cgram_address(); + + //$2123 + bool bg1_window1_enable(); + bool bg1_window1_invert(); + bool bg1_window2_enable(); + bool bg1_window2_invert(); + bool bg2_window1_enable(); + bool bg2_window1_invert(); + bool bg2_window2_enable(); + bool bg2_window2_invert(); + + //$2124 + bool bg3_window1_enable(); + bool bg3_window1_invert(); + bool bg3_window2_enable(); + bool bg3_window2_invert(); + bool bg4_window1_enable(); + bool bg4_window1_invert(); + bool bg4_window2_enable(); + bool bg4_window2_invert(); + + //$2125 + bool oam_window1_enable(); + bool oam_window1_invert(); + bool oam_window2_enable(); + bool oam_window2_invert(); + bool color_window1_enable(); + bool color_window1_invert(); + bool color_window2_enable(); + bool color_window2_invert(); + + //$2126 + unsigned window1_left(); + + //$2127 + unsigned window1_right(); + + //$2128 + unsigned window2_left(); + + //$2129 + unsigned window2_right(); + + //$212a + unsigned bg1_window_mask(); + unsigned bg2_window_mask(); + unsigned bg3_window_mask(); + unsigned bg4_window_mask(); + + //$212b + unsigned oam_window_mask(); + unsigned color_window_mask(); + + //$212c + bool bg1_mainscreen_enable(); + bool bg2_mainscreen_enable(); + bool bg3_mainscreen_enable(); + bool bg4_mainscreen_enable(); + bool oam_mainscreen_enable(); + + //$212d + bool bg1_subscreen_enable(); + bool bg2_subscreen_enable(); + bool bg3_subscreen_enable(); + bool bg4_subscreen_enable(); + bool oam_subscreen_enable(); + + //$212e + bool bg1_mainscreen_window_enable(); + bool bg2_mainscreen_window_enable(); + bool bg3_mainscreen_window_enable(); + bool bg4_mainscreen_window_enable(); + bool oam_mainscreen_window_enable(); + + //$212f + bool bg1_subscreen_window_enable(); + bool bg2_subscreen_window_enable(); + bool bg3_subscreen_window_enable(); + bool bg4_subscreen_window_enable(); + bool oam_subscreen_window_enable(); + + //$2130 + unsigned color_mainscreen_window_mask(); + unsigned color_subscreen_window_mask(); + bool color_add_subtract_mode(); + bool direct_color(); + + //$2131 + bool color_mode(); + bool color_halve(); + bool bg1_color_enable(); + bool bg2_color_enable(); + bool bg3_color_enable(); + bool bg4_color_enable(); + bool oam_color_enable(); + bool back_color_enable(); + + //$2132 + unsigned color_constant_blue(); + unsigned color_constant_green(); + unsigned color_constant_red(); + + //$2133 + bool mode7_extbg(); + bool pseudo_hires(); + bool overscan(); + bool oam_interlace(); + bool interlace(); + + //$213c + unsigned hcounter(); + + //$213d + unsigned vcounter(); + + //$213e + bool range_over(); + bool time_over(); + unsigned ppu1_version(); + + //$213f + bool field(); + bool region(); + unsigned ppu2_version(); +}; diff --git a/mednafen/snes/src/ppu/bppu/debugger/render.cpp b/mednafen/snes/src/ppu/bppu/debugger/render.cpp new file mode 100755 index 0000000..7d2e26a --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/debugger/render.cpp @@ -0,0 +1,194 @@ +#ifdef BPPU_CPP + +//render_line_modeN() taken from src/ppu/bppu/render/render.cpp +//modified to support layer disable; accomplished by setting priority to zero +//a priority of zero won't override the back layer, effectively nullifying it +//for speed, rendering loop is skipped entirely if all priorities are disabled +// +//note: render_line_(bg|oam|mode7) cannot be virtualized as they are templates + +void bPPUDebugger::render_line_mode0() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 8 : 0; + pri1 = bg1_enabled[1] ? 11 : 0; + if(pri0 | pri1) bPPU::render_line_bg<0, BG1, COLORDEPTH_4>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 7 : 0; + pri1 = bg2_enabled[1] ? 10 : 0; + if(pri0 | pri1) bPPU::render_line_bg<0, BG2, COLORDEPTH_4>(pri0, pri1); + + pri0 = bg3_enabled[0] ? 2 : 0; + pri1 = bg3_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<0, BG3, COLORDEPTH_4>(pri0, pri1); + + pri0 = bg4_enabled[0] ? 1 : 0; + pri1 = bg4_enabled[1] ? 4 : 0; + if(pri0 | pri1) bPPU::render_line_bg<0, BG4, COLORDEPTH_4>(pri0, pri1); + + pri0 = oam_enabled[0] ? 3 : 0; + pri1 = oam_enabled[1] ? 6 : 0; + pri2 = oam_enabled[2] ? 9 : 0; + pri3 = oam_enabled[3] ? 12 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode1() { + unsigned pri0, pri1, pri2, pri3; + + if(regs.bg3_priority) { + pri0 = bg1_enabled[0] ? 5 : 0; + pri1 = bg1_enabled[1] ? 8 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG1, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 4 : 0; + pri1 = bg2_enabled[1] ? 7 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG2, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg3_enabled[0] ? 1 : 0; + pri1 = bg3_enabled[1] ? 10 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG3, COLORDEPTH_4>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 3 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 9 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); + } else { + pri0 = bg1_enabled[0] ? 6 : 0; + pri1 = bg1_enabled[1] ? 9 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG1, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 5 : 0; + pri1 = bg2_enabled[1] ? 8 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG2, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg3_enabled[0] ? 1 : 0; + pri1 = bg3_enabled[1] ? 3 : 0; + if(pri0 | pri1) bPPU::render_line_bg<1, BG3, COLORDEPTH_4>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 7 : 0; + pri3 = oam_enabled[3] ? 10 : 0; + bPPU::render_line_oam(pri0, pri1, pri2, pri3); + } +} + +void bPPUDebugger::render_line_mode2() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 3 : 0; + pri1 = bg1_enabled[1] ? 7 : 0; + if(pri0 | pri1) bPPU::render_line_bg<2, BG1, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 1 : 0; + pri1 = bg2_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<2, BG2, COLORDEPTH_16>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 8 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode3() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 3 : 0; + pri1 = bg1_enabled[1] ? 7 : 0; + if(pri0 | pri1) bPPU::render_line_bg<3, BG1, COLORDEPTH_256>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 1 : 0; + pri1 = bg2_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<3, BG2, COLORDEPTH_16>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 8 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode4() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 3 : 0; + pri1 = bg1_enabled[1] ? 7 : 0; + if(pri0 | pri1) bPPU::render_line_bg<4, BG1, COLORDEPTH_256>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 1 : 0; + pri1 = bg2_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<4, BG2, COLORDEPTH_4>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 8 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode5() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 3 : 0; + pri1 = bg1_enabled[1] ? 7 : 0; + if(pri0 | pri1) bPPU::render_line_bg<5, BG1, COLORDEPTH_16>(pri0, pri1); + + pri0 = bg2_enabled[0] ? 1 : 0; + pri1 = bg2_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<5, BG2, COLORDEPTH_4>(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 8 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode6() { + unsigned pri0, pri1, pri2, pri3; + + pri0 = bg1_enabled[0] ? 2 : 0; + pri1 = bg1_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_bg<6, BG1, COLORDEPTH_16>(pri0, pri1); + + pri0 = oam_enabled[0] ? 1 : 0; + pri1 = oam_enabled[1] ? 3 : 0; + pri2 = oam_enabled[2] ? 4 : 0; + pri3 = oam_enabled[3] ? 6 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); +} + +void bPPUDebugger::render_line_mode7() { + unsigned pri0, pri1, pri2, pri3; + + if(regs.mode7_extbg == false) { + pri0 = bg1_enabled[0] ? 2 : 0; + pri1 = bg1_enabled[1] ? 2 : 0; + if(pri0 | pri1) bPPU::render_line_mode7(pri0, pri1); + + pri0 = oam_enabled[0] ? 1 : 0; + pri1 = oam_enabled[1] ? 3 : 0; + pri2 = oam_enabled[2] ? 4 : 0; + pri3 = oam_enabled[3] ? 5 : 0; + if(pri0 | pri1 | pri2 | pri3) bPPU::render_line_oam(pri0, pri1, pri2, pri3); + } else { + pri0 = bg1_enabled[0] ? 3 : 0; + pri1 = bg1_enabled[1] ? 3 : 0; + if(pri0 | pri1) bPPU::render_line_mode7(pri0, pri1); + + pri0 = bg2_enabled[0] ? 1 : 0; + pri1 = bg2_enabled[1] ? 5 : 0; + if(pri0 | pri1) bPPU::render_line_mode7(pri0, pri1); + + pri0 = oam_enabled[0] ? 2 : 0; + pri1 = oam_enabled[1] ? 4 : 0; + pri2 = oam_enabled[2] ? 6 : 0; + pri3 = oam_enabled[3] ? 7 : 0; + bPPU::render_line_oam(pri0, pri1, pri2, pri3); + } +} + +#endif diff --git a/mednafen/snes/src/ppu/bppu/memory/memory.cpp b/mednafen/snes/src/ppu/bppu/memory/memory.cpp new file mode 100755 index 0000000..36becbc --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/memory/memory.cpp @@ -0,0 +1,173 @@ +#ifdef BPPU_CPP + +void bPPU::latch_counters() { + regs.hcounter = cpu.hdot(); + regs.vcounter = cpu.vcounter(); + regs.counters_latched = true; +} + +uint16 bPPU::get_vram_address() { + uint16 addr = regs.vram_addr; + switch(regs.vram_mapping) { + case 0: break; //direct mapping + case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break; + case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break; + case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break; + } + return (addr << 1); +} + +//NOTE: all VRAM writes during active display are invalid. Unlike OAM and CGRAM, they will +//not be written anywhere at all. The below address ranges for where writes are invalid have +//been validated on hardware, as has the edge case where the S-CPU MDR can be written if the +//write occurs during the very last clock cycle of vblank. + +uint8 bPPU::vram_mmio_read(uint16 addr) { + uint8 data; + + if(regs.display_disabled == true) { + data = memory::vram[addr]; + } else { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + uint16 ls = ((system.region() == System::NTSC ? 525 : 625) >> 1) - 1; + if(interlace() && !cpu.field()) ls++; + + if(v == ls && h == 1362) { + data = 0x00; + } else if(v < (!overscan() ? 224 : 239)) { + data = 0x00; + } else if(v == (!overscan() ? 224 : 239)) { + if(h == 1362) { + data = memory::vram[addr]; + } else { + data = 0x00; + } + } else { + data = memory::vram[addr]; + } + } + + return data; +} + +void bPPU::vram_mmio_write(uint16 addr, uint8 data) { + if(regs.display_disabled == true) { + memory::vram[addr] = data; + } else { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + if(v == 0) { + if(h <= 4) { + memory::vram[addr] = data; + } else if(h == 6) { + memory::vram[addr] = cpu.regs.mdr; + } else { + //no write + } + } else if(v < (!overscan() ? 225 : 240)) { + //no write + } else if(v == (!overscan() ? 225 : 240)) { + if(h <= 4) { + //no write + } else { + memory::vram[addr] = data; + } + } else { + memory::vram[addr] = data; + } + } +} + +//NOTE: OAM accesses during active display are rerouted to 0x0218 ... this can be considered +//a hack. The actual address varies during rendering, as the S-PPU reads in data itself for +//processing. Unfortunately, we have yet to determine how this works. The algorithm cannot be +//reverse engineered using a scanline renderer such as this, and at this time, there does not +//exist a more accurate SNES PPU emulator to work from. The only known game to actually access +//OAM during active display is Uniracers. It expects accesses to map to offset 0x0218. +//It was decided by public consensus to map writes to this address to match Uniracers, primarily +//because it is the only game observed to do this, but also because mapping to this address does +//not contradict any of our findings, because we have no findings whatsoever on this behavior. +//Think of this what you will, I openly admit that this is a hack. But it is more accurate than +//writing to the 'expected' address set by $2102,$2103, and will catch problems in software that +//accidentally accesses OAM during active display by virtue of not returning the expected data. + +uint8 bPPU::oam_mmio_read(uint16 addr) { + addr &= 0x03ff; + if(addr & 0x0200) addr &= 0x021f; + uint8 data; + + if(regs.display_disabled == true) { + data = memory::oam[addr]; + } else { + if(cpu.vcounter() < (!overscan() ? 225 : 240)) { + data = memory::oam[0x0218]; + } else { + data = memory::oam[addr]; + } + } + + return data; +} + +void bPPU::oam_mmio_write(uint16 addr, uint8 data) { + addr &= 0x03ff; + if(addr & 0x0200) addr &= 0x021f; + + sprite_list_valid = false; + + if(regs.display_disabled == true) { + memory::oam[addr] = data; + } else { + if(cpu.vcounter() < (!overscan() ? 225 : 240)) { + memory::oam[0x0218] = data; + } else { + memory::oam[addr] = data; + } + } +} + +//NOTE: CGRAM writes during hblank are valid. During active display, the actual address the +//data is written to varies, as the S-PPU itself changes the address. Like OAM, we do not know +//the exact algorithm used, but we have zero known examples of any commercial software that +//attempts to do this. Therefore, the addresses are mapped to 0x01ff. There is nothing special +//about this address, it is simply more accurate to invalidate the 'expected' address than not. + +uint8 bPPU::cgram_mmio_read(uint16 addr) { + addr &= 0x01ff; + uint8 data; + + if(regs.display_disabled == true) { + data = memory::cgram[addr]; + } else { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) { + data = memory::cgram[0x01ff] & 0x7f; + } else { + data = memory::cgram[addr]; + } + } + + if(addr & 1) data &= 0x7f; + return data; +} + +void bPPU::cgram_mmio_write(uint16 addr, uint8 data) { + addr &= 0x01ff; + if(addr & 1) data &= 0x7f; + + if(regs.display_disabled == true) { + memory::cgram[addr] = data; + } else { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) { + memory::cgram[0x01ff] = data & 0x7f; + } else { + memory::cgram[addr] = data; + } + } +} + +#endif diff --git a/mednafen/snes/src/ppu/bppu/memory/memory.hpp b/mednafen/snes/src/ppu/bppu/memory/memory.hpp new file mode 100755 index 0000000..c2979ae --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/memory/memory.hpp @@ -0,0 +1,10 @@ +uint16 get_vram_address(); + +debugvirtual uint8 vram_mmio_read(uint16 addr); +debugvirtual void vram_mmio_write(uint16 addr, uint8 data); + +debugvirtual uint8 oam_mmio_read(uint16 addr); +debugvirtual void oam_mmio_write(uint16 addr, uint8 data); + +debugvirtual uint8 cgram_mmio_read(uint16 addr); +debugvirtual void cgram_mmio_write(uint16 addr, uint8 data); diff --git a/mednafen/snes/src/ppu/bppu/mmio/mmio.cpp b/mednafen/snes/src/ppu/bppu/mmio/mmio.cpp new file mode 100755 index 0000000..ce66c9b --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/mmio/mmio.cpp @@ -0,0 +1,671 @@ +#ifdef BPPU_CPP + +//INIDISP +void bPPU::mmio_w2100(uint8 value) { + if(regs.display_disabled == true && cpu.vcounter() == (!overscan() ? 225 : 240)) { + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; + } + + regs.display_disabled = !!(value & 0x80); + regs.display_brightness = value & 15; +} + +//OBSEL +void bPPU::mmio_w2101(uint8 value) { + regs.oam_basesize = (value >> 5) & 7; + regs.oam_nameselect = (value >> 3) & 3; + regs.oam_tdaddr = (value & 3) << 14; +} + +//OAMADDL +void bPPU::mmio_w2102(uint8 data) { + regs.oam_baseaddr = (regs.oam_baseaddr & ~0xff) | (data << 0); + regs.oam_baseaddr &= 0x01ff; + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; +} + +//OAMADDH +void bPPU::mmio_w2103(uint8 data) { + regs.oam_priority = !!(data & 0x80); + regs.oam_baseaddr = (regs.oam_baseaddr & 0xff) | (data << 8); + regs.oam_baseaddr &= 0x01ff; + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; +} + +//OAMDATA +void bPPU::mmio_w2104(uint8 data) { + if(regs.oam_addr & 0x0200) { + oam_mmio_write(regs.oam_addr, data); + } else if((regs.oam_addr & 1) == 0) { + regs.oam_latchdata = data; + } else { + oam_mmio_write((regs.oam_addr & ~1) + 0, regs.oam_latchdata); + oam_mmio_write((regs.oam_addr & ~1) + 1, data); + } + + regs.oam_addr++; + regs.oam_addr &= 0x03ff; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; +} + +//BGMODE +void bPPU::mmio_w2105(uint8 value) { + regs.bg_tilesize[BG4] = !!(value & 0x80); + regs.bg_tilesize[BG3] = !!(value & 0x40); + regs.bg_tilesize[BG2] = !!(value & 0x20); + regs.bg_tilesize[BG1] = !!(value & 0x10); + regs.bg3_priority = !!(value & 0x08); + regs.bg_mode = (value & 7); +} + +//MOSAIC +void bPPU::mmio_w2106(uint8 value) { + regs.mosaic_size = (value >> 4) & 15; + regs.mosaic_enabled[BG4] = !!(value & 0x08); + regs.mosaic_enabled[BG3] = !!(value & 0x04); + regs.mosaic_enabled[BG2] = !!(value & 0x02); + regs.mosaic_enabled[BG1] = !!(value & 0x01); +} + +//BG1SC +void bPPU::mmio_w2107(uint8 value) { + regs.bg_scaddr[BG1] = (value & 0x7c) << 9; + regs.bg_scsize[BG1] = value & 3; +} + +//BG2SC +void bPPU::mmio_w2108(uint8 value) { + regs.bg_scaddr[BG2] = (value & 0x7c) << 9; + regs.bg_scsize[BG2] = value & 3; +} + +//BG3SC +void bPPU::mmio_w2109(uint8 value) { + regs.bg_scaddr[BG3] = (value & 0x7c) << 9; + regs.bg_scsize[BG3] = value & 3; +} + +//BG4SC +void bPPU::mmio_w210a(uint8 value) { + regs.bg_scaddr[BG4] = (value & 0x7c) << 9; + regs.bg_scsize[BG4] = value & 3; +} + +//BG12NBA +void bPPU::mmio_w210b(uint8 value) { + regs.bg_tdaddr[BG1] = (value & 0x07) << 13; + regs.bg_tdaddr[BG2] = (value & 0x70) << 9; +} + +//BG34NBA +void bPPU::mmio_w210c(uint8 value) { + regs.bg_tdaddr[BG3] = (value & 0x07) << 13; + regs.bg_tdaddr[BG4] = (value & 0x70) << 9; +} + +//BG1HOFS +void bPPU::mmio_w210d(uint8 value) { + regs.m7_hofs = (value << 8) | regs.m7_latch; + regs.m7_latch = value; + + regs.bg_hofs[BG1] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG1] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG1VOFS +void bPPU::mmio_w210e(uint8 value) { + regs.m7_vofs = (value << 8) | regs.m7_latch; + regs.m7_latch = value; + + regs.bg_vofs[BG1] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//BG2HOFS +void bPPU::mmio_w210f(uint8 value) { + regs.bg_hofs[BG2] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG2] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG2VOFS +void bPPU::mmio_w2110(uint8 value) { + regs.bg_vofs[BG2] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//BG3HOFS +void bPPU::mmio_w2111(uint8 value) { + regs.bg_hofs[BG3] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG3] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG3VOFS +void bPPU::mmio_w2112(uint8 value) { + regs.bg_vofs[BG3] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//BG4HOFS +void bPPU::mmio_w2113(uint8 value) { + regs.bg_hofs[BG4] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG4] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG4VOFS +void bPPU::mmio_w2114(uint8 value) { + regs.bg_vofs[BG4] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//VMAIN +void bPPU::mmio_w2115(uint8 value) { + regs.vram_incmode = !!(value & 0x80); + regs.vram_mapping = (value >> 2) & 3; + switch(value & 3) { + case 0: regs.vram_incsize = 1; break; + case 1: regs.vram_incsize = 32; break; + case 2: regs.vram_incsize = 128; break; + case 3: regs.vram_incsize = 128; break; + } +} + +//VMADDL +void bPPU::mmio_w2116(uint8 value) { + regs.vram_addr = (regs.vram_addr & 0xff00) | value; + uint16 addr = get_vram_address(); + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; +} + +//VMADDH +void bPPU::mmio_w2117(uint8 value) { + regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff); + uint16 addr = get_vram_address(); + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; +} + +//VMDATAL +void bPPU::mmio_w2118(uint8 value) { +uint16 addr = get_vram_address(); + vram_mmio_write(addr, value); + bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1; + bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1; + bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1; + + if(regs.vram_incmode == 0) { + regs.vram_addr += regs.vram_incsize; + } +} + +//VMDATAH +void bPPU::mmio_w2119(uint8 value) { +uint16 addr = get_vram_address() + 1; + vram_mmio_write(addr, value); + bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1; + bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1; + bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1; + + if(regs.vram_incmode == 1) { + regs.vram_addr += regs.vram_incsize; + } +} + +//M7SEL +void bPPU::mmio_w211a(uint8 value) { + regs.mode7_repeat = (value >> 6) & 3; + regs.mode7_vflip = !!(value & 0x02); + regs.mode7_hflip = !!(value & 0x01); +} + +//M7A +void bPPU::mmio_w211b(uint8 value) { + regs.m7a = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7B +void bPPU::mmio_w211c(uint8 value) { + regs.m7b = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7C +void bPPU::mmio_w211d(uint8 value) { + regs.m7c = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7D +void bPPU::mmio_w211e(uint8 value) { + regs.m7d = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7X +void bPPU::mmio_w211f(uint8 value) { + regs.m7x = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7Y +void bPPU::mmio_w2120(uint8 value) { + regs.m7y = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//CGADD +void bPPU::mmio_w2121(uint8 value) { + regs.cgram_addr = value << 1; +} + +//CGDATA +//note: CGRAM palette data format is 15-bits +//(0,bbbbb,ggggg,rrrrr). Highest bit is ignored, +//as evidenced by $213b CGRAM data reads. +// +//anomie indicates writes to CGDATA work the same +//as writes to OAMDATA's low table. need to verify +//this on hardware. +void bPPU::mmio_w2122(uint8 value) { + if(!(regs.cgram_addr & 1)) { + regs.cgram_latchdata = value; + } else { + cgram_mmio_write((regs.cgram_addr & 0x01fe), regs.cgram_latchdata); + cgram_mmio_write((regs.cgram_addr & 0x01fe) + 1, value & 0x7f); + } + regs.cgram_addr++; + regs.cgram_addr &= 0x01ff; +} + +//W12SEL +void bPPU::mmio_w2123(uint8 value) { + regs.window2_enabled[BG2] = !!(value & 0x80); + regs.window2_invert [BG2] = !!(value & 0x40); + regs.window1_enabled[BG2] = !!(value & 0x20); + regs.window1_invert [BG2] = !!(value & 0x10); + regs.window2_enabled[BG1] = !!(value & 0x08); + regs.window2_invert [BG1] = !!(value & 0x04); + regs.window1_enabled[BG1] = !!(value & 0x02); + regs.window1_invert [BG1] = !!(value & 0x01); +} + +//W34SEL +void bPPU::mmio_w2124(uint8 value) { + regs.window2_enabled[BG4] = !!(value & 0x80); + regs.window2_invert [BG4] = !!(value & 0x40); + regs.window1_enabled[BG4] = !!(value & 0x20); + regs.window1_invert [BG4] = !!(value & 0x10); + regs.window2_enabled[BG3] = !!(value & 0x08); + regs.window2_invert [BG3] = !!(value & 0x04); + regs.window1_enabled[BG3] = !!(value & 0x02); + regs.window1_invert [BG3] = !!(value & 0x01); +} + +//WOBJSEL +void bPPU::mmio_w2125(uint8 value) { + regs.window2_enabled[COL] = !!(value & 0x80); + regs.window2_invert [COL] = !!(value & 0x40); + regs.window1_enabled[COL] = !!(value & 0x20); + regs.window1_invert [COL] = !!(value & 0x10); + regs.window2_enabled[OAM] = !!(value & 0x08); + regs.window2_invert [OAM] = !!(value & 0x04); + regs.window1_enabled[OAM] = !!(value & 0x02); + regs.window1_invert [OAM] = !!(value & 0x01); +} + +//WH0 +void bPPU::mmio_w2126(uint8 value) { + regs.window1_left = value; +} + +//WH1 +void bPPU::mmio_w2127(uint8 value) { + regs.window1_right = value; +} + +//WH2 +void bPPU::mmio_w2128(uint8 value) { + regs.window2_left = value; +} + +//WH3 +void bPPU::mmio_w2129(uint8 value) { + regs.window2_right = value; +} + +//WBGLOG +void bPPU::mmio_w212a(uint8 value) { + regs.window_mask[BG4] = (value >> 6) & 3; + regs.window_mask[BG3] = (value >> 4) & 3; + regs.window_mask[BG2] = (value >> 2) & 3; + regs.window_mask[BG1] = (value ) & 3; +} + +//WOBJLOG +void bPPU::mmio_w212b(uint8 value) { + regs.window_mask[COL] = (value >> 2) & 3; + regs.window_mask[OAM] = (value ) & 3; +} + +//TM +void bPPU::mmio_w212c(uint8 value) { + regs.bg_enabled[OAM] = !!(value & 0x10); + regs.bg_enabled[BG4] = !!(value & 0x08); + regs.bg_enabled[BG3] = !!(value & 0x04); + regs.bg_enabled[BG2] = !!(value & 0x02); + regs.bg_enabled[BG1] = !!(value & 0x01); +} + +//TS +void bPPU::mmio_w212d(uint8 value) { + regs.bgsub_enabled[OAM] = !!(value & 0x10); + regs.bgsub_enabled[BG4] = !!(value & 0x08); + regs.bgsub_enabled[BG3] = !!(value & 0x04); + regs.bgsub_enabled[BG2] = !!(value & 0x02); + regs.bgsub_enabled[BG1] = !!(value & 0x01); +} + +//TMW +void bPPU::mmio_w212e(uint8 value) { + regs.window_enabled[OAM] = !!(value & 0x10); + regs.window_enabled[BG4] = !!(value & 0x08); + regs.window_enabled[BG3] = !!(value & 0x04); + regs.window_enabled[BG2] = !!(value & 0x02); + regs.window_enabled[BG1] = !!(value & 0x01); +} + +//TSW +void bPPU::mmio_w212f(uint8 value) { + regs.sub_window_enabled[OAM] = !!(value & 0x10); + regs.sub_window_enabled[BG4] = !!(value & 0x08); + regs.sub_window_enabled[BG3] = !!(value & 0x04); + regs.sub_window_enabled[BG2] = !!(value & 0x02); + regs.sub_window_enabled[BG1] = !!(value & 0x01); +} + +//CGWSEL +void bPPU::mmio_w2130(uint8 value) { + regs.color_mask = (value >> 6) & 3; + regs.colorsub_mask = (value >> 4) & 3; + regs.addsub_mode = !!(value & 0x02); + regs.direct_color = !!(value & 0x01); +} + +//CGADDSUB +void bPPU::mmio_w2131(uint8 value) { + regs.color_mode = !!(value & 0x80); + regs.color_halve = !!(value & 0x40); + regs.color_enabled[BACK] = !!(value & 0x20); + regs.color_enabled[OAM] = !!(value & 0x10); + regs.color_enabled[BG4] = !!(value & 0x08); + regs.color_enabled[BG3] = !!(value & 0x04); + regs.color_enabled[BG2] = !!(value & 0x02); + regs.color_enabled[BG1] = !!(value & 0x01); +} + +//COLDATA +void bPPU::mmio_w2132(uint8 value) { + if(value & 0x80) regs.color_b = value & 0x1f; + if(value & 0x40) regs.color_g = value & 0x1f; + if(value & 0x20) regs.color_r = value & 0x1f; + + regs.color_rgb = (regs.color_r) + | (regs.color_g << 5) + | (regs.color_b << 10); +} + +//SETINI +void bPPU::mmio_w2133(uint8 value) { + regs.mode7_extbg = !!(value & 0x40); + regs.pseudo_hires = !!(value & 0x08); + regs.overscan = !!(value & 0x04); + regs.oam_interlace = !!(value & 0x02); + regs.interlace = !!(value & 0x01); + + display.overscan = regs.overscan; + sprite_list_valid = false; +} + +//MPYL +uint8 bPPU::mmio_r2134() { +uint32 r; + r = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = r; + return regs.ppu1_mdr; +} + +//MPYM +uint8 bPPU::mmio_r2135() { +uint32 r; + r = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = r >> 8; + return regs.ppu1_mdr; +} + +//MPYH +uint8 bPPU::mmio_r2136() { +uint32 r; + r = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = r >> 16; + return regs.ppu1_mdr; +} + +//SLHV +uint8 bPPU::mmio_r2137() { + if(cpu.pio() & 0x80) { + latch_counters(); + } + return cpu.regs.mdr; +} + +//OAMDATAREAD +uint8 bPPU::mmio_r2138() { + regs.ppu1_mdr = oam_mmio_read(regs.oam_addr); + + regs.oam_addr++; + regs.oam_addr &= 0x03ff; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; + + return regs.ppu1_mdr; +} + +//VMDATALREAD +uint8 bPPU::mmio_r2139() { +uint16 addr = get_vram_address(); + regs.ppu1_mdr = regs.vram_readbuffer; + if(regs.vram_incmode == 0) { + addr &= 0xfffe; + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; + regs.vram_addr += regs.vram_incsize; + } + return regs.ppu1_mdr; +} + +//VMDATAHREAD +uint8 bPPU::mmio_r213a() { +uint16 addr = get_vram_address() + 1; + regs.ppu1_mdr = regs.vram_readbuffer >> 8; + if(regs.vram_incmode == 1) { + addr &= 0xfffe; + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; + regs.vram_addr += regs.vram_incsize; + } + return regs.ppu1_mdr; +} + +//CGDATAREAD +//note: CGRAM palette data is 15-bits (0,bbbbb,ggggg,rrrrr) +//therefore, the high byte read from each color does not +//update bit 7 of the PPU2 MDR. +uint8 bPPU::mmio_r213b() { + if(!(regs.cgram_addr & 1)) { + regs.ppu2_mdr = cgram_mmio_read(regs.cgram_addr) & 0xff; + } else { + regs.ppu2_mdr &= 0x80; + regs.ppu2_mdr |= cgram_mmio_read(regs.cgram_addr) & 0x7f; + } + regs.cgram_addr++; + regs.cgram_addr &= 0x01ff; + return regs.ppu2_mdr; +} + +//OPHCT +uint8 bPPU::mmio_r213c() { + if(!regs.latch_hcounter) { + regs.ppu2_mdr = regs.hcounter & 0xff; + } else { + regs.ppu2_mdr &= 0xfe; + regs.ppu2_mdr |= (regs.hcounter >> 8) & 1; + } + regs.latch_hcounter ^= 1; + return regs.ppu2_mdr; +} + +//OPVCT +uint8 bPPU::mmio_r213d() { + if(!regs.latch_vcounter) { + regs.ppu2_mdr = regs.vcounter & 0xff; + } else { + regs.ppu2_mdr &= 0xfe; + regs.ppu2_mdr |= (regs.vcounter >> 8) & 1; + } + regs.latch_vcounter ^= 1; + return regs.ppu2_mdr; +} + +//STAT77 +uint8 bPPU::mmio_r213e() { +uint8 r = 0x00; + r |= (regs.time_over) ? 0x80 : 0x00; + r |= (regs.range_over) ? 0x40 : 0x00; + r |= (regs.ppu1_mdr & 0x10); + r |= (ppu1_version & 0x0f); + regs.ppu1_mdr = r; + return regs.ppu1_mdr; +} + +//STAT78 +uint8 bPPU::mmio_r213f() { +uint8 r = 0x00; + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + + r |= cpu.field() << 7; + if(!(cpu.pio() & 0x80)) { + r |= 0x40; + } else if(regs.counters_latched == true) { + r |= 0x40; + regs.counters_latched = false; + } + r |= (regs.ppu2_mdr & 0x20); + r |= (region << 4); //0 = NTSC, 1 = PAL + r |= (ppu2_version & 0x0f); + regs.ppu2_mdr = r; + return regs.ppu2_mdr; +} + +uint8 bPPU::mmio_read(unsigned addr) { + scheduler.sync_cpuppu(); + + switch(addr & 0xffff) { + case 0x2104: + case 0x2105: + case 0x2106: + case 0x2108: + case 0x2109: + case 0x210a: + case 0x2114: + case 0x2115: + case 0x2116: + case 0x2118: + case 0x2119: + case 0x211a: + case 0x2124: + case 0x2125: + case 0x2126: + case 0x2128: + case 0x2129: + case 0x212a: return regs.ppu1_mdr; + case 0x2134: return mmio_r2134(); //MPYL + case 0x2135: return mmio_r2135(); //MPYM + case 0x2136: return mmio_r2136(); //MPYH + case 0x2137: return mmio_r2137(); //SLHV + case 0x2138: return mmio_r2138(); //OAMDATAREAD + case 0x2139: return mmio_r2139(); //VMDATALREAD + case 0x213a: return mmio_r213a(); //VMDATAHREAD + case 0x213b: return mmio_r213b(); //CGDATAREAD + case 0x213c: return mmio_r213c(); //OPHCT + case 0x213d: return mmio_r213d(); //OPVCT + case 0x213e: return mmio_r213e(); //STAT77 + case 0x213f: return mmio_r213f(); //STAT78 + } + + return cpu.regs.mdr; +} + +void bPPU::mmio_write(unsigned addr, uint8 data) { + scheduler.sync_cpuppu(); + + switch(addr & 0xffff) { + case 0x2100: return mmio_w2100(data); //INIDISP + case 0x2101: return mmio_w2101(data); //OBSEL + case 0x2102: return mmio_w2102(data); //OAMADDL + case 0x2103: return mmio_w2103(data); //OAMADDH + case 0x2104: return mmio_w2104(data); //OAMDATA + case 0x2105: return mmio_w2105(data); //BGMODE + case 0x2106: return mmio_w2106(data); //MOSAIC + case 0x2107: return mmio_w2107(data); //BG1SC + case 0x2108: return mmio_w2108(data); //BG2SC + case 0x2109: return mmio_w2109(data); //BG3SC + case 0x210a: return mmio_w210a(data); //BG4SC + case 0x210b: return mmio_w210b(data); //BG12NBA + case 0x210c: return mmio_w210c(data); //BG34NBA + case 0x210d: return mmio_w210d(data); //BG1HOFS + case 0x210e: return mmio_w210e(data); //BG1VOFS + case 0x210f: return mmio_w210f(data); //BG2HOFS + case 0x2110: return mmio_w2110(data); //BG2VOFS + case 0x2111: return mmio_w2111(data); //BG3HOFS + case 0x2112: return mmio_w2112(data); //BG3VOFS + case 0x2113: return mmio_w2113(data); //BG4HOFS + case 0x2114: return mmio_w2114(data); //BG4VOFS + case 0x2115: return mmio_w2115(data); //VMAIN + case 0x2116: return mmio_w2116(data); //VMADDL + case 0x2117: return mmio_w2117(data); //VMADDH + case 0x2118: return mmio_w2118(data); //VMDATAL + case 0x2119: return mmio_w2119(data); //VMDATAH + case 0x211a: return mmio_w211a(data); //M7SEL + case 0x211b: return mmio_w211b(data); //M7A + case 0x211c: return mmio_w211c(data); //M7B + case 0x211d: return mmio_w211d(data); //M7C + case 0x211e: return mmio_w211e(data); //M7D + case 0x211f: return mmio_w211f(data); //M7X + case 0x2120: return mmio_w2120(data); //M7Y + case 0x2121: return mmio_w2121(data); //CGADD + case 0x2122: return mmio_w2122(data); //CGDATA + case 0x2123: return mmio_w2123(data); //W12SEL + case 0x2124: return mmio_w2124(data); //W34SEL + case 0x2125: return mmio_w2125(data); //WOBJSEL + case 0x2126: return mmio_w2126(data); //WH0 + case 0x2127: return mmio_w2127(data); //WH1 + case 0x2128: return mmio_w2128(data); //WH2 + case 0x2129: return mmio_w2129(data); //WH3 + case 0x212a: return mmio_w212a(data); //WBGLOG + case 0x212b: return mmio_w212b(data); //WOBJLOG + case 0x212c: return mmio_w212c(data); //TM + case 0x212d: return mmio_w212d(data); //TS + case 0x212e: return mmio_w212e(data); //TMW + case 0x212f: return mmio_w212f(data); //TSW + case 0x2130: return mmio_w2130(data); //CGWSEL + case 0x2131: return mmio_w2131(data); //CGADDSUB + case 0x2132: return mmio_w2132(data); //COLDATA + case 0x2133: return mmio_w2133(data); //SETINI + } +} + +#endif diff --git a/mednafen/snes/src/ppu/bppu/mmio/mmio.hpp b/mednafen/snes/src/ppu/bppu/mmio/mmio.hpp new file mode 100755 index 0000000..ee904c9 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/mmio/mmio.hpp @@ -0,0 +1,198 @@ +struct { + //open bus support + uint8 ppu1_mdr, ppu2_mdr; + + //bg line counters + uint16 bg_y[4]; + + //$2100 + bool display_disabled; + uint8 display_brightness; + + //$2101 + uint8 oam_basesize; + uint8 oam_nameselect; + uint16 oam_tdaddr; + + //$2102-$2103 + uint16 oam_baseaddr; + uint16 oam_addr; + bool oam_priority; + uint8 oam_firstsprite; + + //$2104 + uint8 oam_latchdata; + + //$2105 + bool bg_tilesize[4]; + bool bg3_priority; + uint8 bg_mode; + + //$2106 + uint8 mosaic_size; + bool mosaic_enabled[4]; + uint16 mosaic_countdown; + + //$2107-$210a + uint16 bg_scaddr[4]; + uint8 bg_scsize[4]; + + //$210b-$210c + uint16 bg_tdaddr[4]; + + //$210d-$2114 + uint8 bg_ofslatch; + uint16 m7_hofs, m7_vofs; + uint16 bg_hofs[4]; + uint16 bg_vofs[4]; + + //$2115 + bool vram_incmode; + uint8 vram_mapping; + uint8 vram_incsize; + + //$2116-$2117 + uint16 vram_addr; + + //$211a + uint8 mode7_repeat; + bool mode7_vflip; + bool mode7_hflip; + + //$211b-$2120 + uint8 m7_latch; + uint16 m7a, m7b, m7c, m7d, m7x, m7y; + + //$2121 + uint16 cgram_addr; + + //$2122 + uint8 cgram_latchdata; + + //$2123-$2125 + bool window1_enabled[6]; + bool window1_invert [6]; + bool window2_enabled[6]; + bool window2_invert [6]; + + //$2126-$2129 + uint8 window1_left, window1_right; + uint8 window2_left, window2_right; + + //$212a-$212b + uint8 window_mask[6]; + + //$212c-$212d + bool bg_enabled[5], bgsub_enabled[5]; + + //$212e-$212f + bool window_enabled[5], sub_window_enabled[5]; + + //$2130 + uint8 color_mask, colorsub_mask; + bool addsub_mode; + bool direct_color; + + //$2131 + bool color_mode, color_halve; + bool color_enabled[6]; + + //$2132 + uint8 color_r, color_g, color_b; + uint16 color_rgb; + + //$2133 + //overscan and interlace are checked once per frame to + //determine if entire frame should be interlaced/non-interlace + //and overscan adjusted. therefore, the variables act sort of + //like a buffer, but they do still affect internal rendering + bool mode7_extbg; + bool pseudo_hires; + bool overscan; + uint16 scanlines; + bool oam_interlace; + bool interlace; + + //$2137 + uint16 hcounter, vcounter; + bool latch_hcounter, latch_vcounter; + bool counters_latched; + + //$2139-$213a + uint16 vram_readbuffer; + + //$213e + bool time_over, range_over; + uint16 oam_itemcount, oam_tilecount; +} regs; + +void mmio_w2100(uint8 value); //INIDISP +void mmio_w2101(uint8 value); //OBSEL +void mmio_w2102(uint8 value); //OAMADDL +void mmio_w2103(uint8 value); //OAMADDH +void mmio_w2104(uint8 value); //OAMDATA +void mmio_w2105(uint8 value); //BGMODE +void mmio_w2106(uint8 value); //MOSAIC +void mmio_w2107(uint8 value); //BG1SC +void mmio_w2108(uint8 value); //BG2SC +void mmio_w2109(uint8 value); //BG3SC +void mmio_w210a(uint8 value); //BG4SC +void mmio_w210b(uint8 value); //BG12NBA +void mmio_w210c(uint8 value); //BG34NBA +void mmio_w210d(uint8 value); //BG1HOFS +void mmio_w210e(uint8 value); //BG1VOFS +void mmio_w210f(uint8 value); //BG2HOFS +void mmio_w2110(uint8 value); //BG2VOFS +void mmio_w2111(uint8 value); //BG3HOFS +void mmio_w2112(uint8 value); //BG3VOFS +void mmio_w2113(uint8 value); //BG4HOFS +void mmio_w2114(uint8 value); //BG4VOFS +void mmio_w2115(uint8 value); //VMAIN +void mmio_w2116(uint8 value); //VMADDL +void mmio_w2117(uint8 value); //VMADDH +void mmio_w2118(uint8 value); //VMDATAL +void mmio_w2119(uint8 value); //VMDATAH +void mmio_w211a(uint8 value); //M7SEL +void mmio_w211b(uint8 value); //M7A +void mmio_w211c(uint8 value); //M7B +void mmio_w211d(uint8 value); //M7C +void mmio_w211e(uint8 value); //M7D +void mmio_w211f(uint8 value); //M7X +void mmio_w2120(uint8 value); //M7Y +void mmio_w2121(uint8 value); //CGADD +void mmio_w2122(uint8 value); //CGDATA +void mmio_w2123(uint8 value); //W12SEL +void mmio_w2124(uint8 value); //W34SEL +void mmio_w2125(uint8 value); //WOBJSEL +void mmio_w2126(uint8 value); //WH0 +void mmio_w2127(uint8 value); //WH1 +void mmio_w2128(uint8 value); //WH2 +void mmio_w2129(uint8 value); //WH3 +void mmio_w212a(uint8 value); //WBGLOG +void mmio_w212b(uint8 value); //WOBJLOG +void mmio_w212c(uint8 value); //TM +void mmio_w212d(uint8 value); //TS +void mmio_w212e(uint8 value); //TMW +void mmio_w212f(uint8 value); //TSW +void mmio_w2130(uint8 value); //CGWSEL +void mmio_w2131(uint8 value); //CGADDSUB +void mmio_w2132(uint8 value); //COLDATA +void mmio_w2133(uint8 value); //SETINI + +uint8 mmio_r2134(); //MPYL +uint8 mmio_r2135(); //MPYM +uint8 mmio_r2136(); //MPYH +uint8 mmio_r2137(); //SLHV +uint8 mmio_r2138(); //OAMDATAREAD +uint8 mmio_r2139(); //VMDATALREAD +uint8 mmio_r213a(); //VMDATAHREAD +uint8 mmio_r213b(); //CGDATAREAD +uint8 mmio_r213c(); //OPHCT +uint8 mmio_r213d(); //OPVCT +uint8 mmio_r213e(); //STAT77 +uint8 mmio_r213f(); //STAT78 + +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); + +void latch_counters(); diff --git a/mednafen/snes/src/ppu/bppu/render/addsub.cpp b/mednafen/snes/src/ppu/bppu/render/addsub.cpp new file mode 100755 index 0000000..1a561ab --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/render/addsub.cpp @@ -0,0 +1,25 @@ +#ifdef BPPU_CPP + +//color addition / subtraction +//thanks go to blargg for the optimized algorithms +inline uint16 bPPU::addsub(uint32 x, uint32 y, bool halve) { + if(!regs.color_mode) { + if(!halve) { + unsigned sum = x + y; + unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420; + return (sum - carry) | (carry - (carry >> 5)); + } else { + return (x + y - ((x ^ y) & 0x0421)) >> 1; + } + } else { + unsigned diff = x - y + 0x8420; + unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420; + if(!halve) { + return (diff - borrow) & (borrow - (borrow >> 5)); + } else { + return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1; + } + } +} + +#endif diff --git a/mednafen/snes/src/ppu/bppu/render/bg.cpp b/mednafen/snes/src/ppu/bppu/render/bg.cpp new file mode 100755 index 0000000..f328277 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/render/bg.cpp @@ -0,0 +1,205 @@ +#ifdef BPPU_CPP + +//called once at the start of every rendered scanline +void bPPU::update_bg_info() { + const unsigned hires = (regs.bg_mode == 5 || regs.bg_mode == 6); + const unsigned width = (!hires ? 256 : 512); + + for(unsigned bg = 0; bg < 4; bg++) { + bg_info[bg].th = (regs.bg_tilesize[bg] ? 4 : 3); + bg_info[bg].tw = (hires ? 4 : bg_info[bg].th); + + bg_info[bg].mx = (bg_info[bg].th == 4 ? (width << 1) : width); + bg_info[bg].my = bg_info[bg].mx; + if(regs.bg_scsize[bg] & 0x01) bg_info[bg].mx <<= 1; + if(regs.bg_scsize[bg] & 0x02) bg_info[bg].my <<= 1; + bg_info[bg].mx--; + bg_info[bg].my--; + + bg_info[bg].scy = (regs.bg_scsize[bg] & 0x02) ? (32 << 5) : 0; + bg_info[bg].scx = (regs.bg_scsize[bg] & 0x01) ? (32 << 5) : 0; + if(regs.bg_scsize[bg] == 3) bg_info[bg].scy <<= 1; + } +} + +template +uint16 bPPU::bg_get_tile(uint16 x, uint16 y) { + x = (x & bg_info[bg].mx) >> bg_info[bg].tw; + y = (y & bg_info[bg].my) >> bg_info[bg].th; + + uint16 pos = ((y & 0x1f) << 5) + (x & 0x1f); + if(y & 0x20) pos += bg_info[bg].scy; + if(x & 0x20) pos += bg_info[bg].scx; + + const uint16 addr = regs.bg_scaddr[bg] + (pos << 1); + return memory::vram[addr] + (memory::vram[addr + 1] << 8); +} + +#define setpixel_main(x) \ + if(pixel_cache[x].pri_main < tile_pri) { \ + pixel_cache[x].pri_main = tile_pri; \ + pixel_cache[x].bg_main = bg; \ + pixel_cache[x].src_main = col; \ + pixel_cache[x].ce_main = false; \ + } + +#define setpixel_sub(x) \ + if(pixel_cache[x].pri_sub < tile_pri) { \ + pixel_cache[x].pri_sub = tile_pri; \ + pixel_cache[x].bg_sub = bg; \ + pixel_cache[x].src_sub = col; \ + pixel_cache[x].ce_sub = false; \ + } + +template +void bPPU::render_line_bg(uint8 pri0_pos, uint8 pri1_pos) { + if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return; + + const bool bg_enabled = regs.bg_enabled[bg]; + const bool bgsub_enabled = regs.bgsub_enabled[bg]; + + const uint16 opt_valid_bit = (bg == BG1) ? 0x2000 : (bg == BG2) ? 0x4000 : 0x0000; + const uint8 bgpal_index = (mode == 0 ? (bg << 5) : 0); + + const uint8 pal_size = 2 << color_depth; //<<2 (*4), <<4 (*16), <<8 (*256) + const uint16 tile_mask = 0x0fff >> color_depth; //0x0fff, 0x07ff, 0x03ff + //4 + color_depth = >>(4-6) -- / {16, 32, 64 } bytes/tile + //index is a tile number count to add to base tile number + const unsigned tiledata_index = regs.bg_tdaddr[bg] >> (4 + color_depth); + + const uint8 *bg_td = bg_tiledata[color_depth]; + const uint8 *bg_td_state = bg_tiledata_state[color_depth]; + + const uint8 tile_width = bg_info[bg].tw; + const uint8 tile_height = bg_info[bg].th; + const uint16 mask_x = bg_info[bg].mx; //screen width mask + const uint16 mask_y = bg_info[bg].my; //screen height mask + + uint16 y = regs.bg_y[bg]; + uint16 hscroll = regs.bg_hofs[bg]; + uint16 vscroll = regs.bg_vofs[bg]; + + const unsigned hires = (mode == 5 || mode == 6); + const unsigned width = (!hires ? 256 : 512); + + if(hires) { + hscroll <<= 1; + if(regs.interlace) y = (y << 1) + field(); + } + + uint16 hval, vval; + uint16 tile_pri, tile_num; + uint8 pal_index, pal_num; + uint16 hoffset, voffset, opt_x, col; + bool mirror_x, mirror_y; + + const uint8 *tile_ptr; + const uint16 *mtable = mosaic_table[regs.mosaic_enabled[bg] ? regs.mosaic_size : 0]; + const bool is_opt_mode = (mode == 2 || mode == 4 || mode == 6); + const bool is_direct_color_mode = (regs.direct_color == true && bg == BG1 && (mode == 3 || mode == 4)); + + build_window_tables(bg); + const uint8 *wt_main = window[bg].main; + const uint8 *wt_sub = window[bg].sub; + + uint16 prev_x = 0xffff, prev_y = 0xffff, prev_optx = 0xffff; + for(uint16 x = 0; x < width; x++) { + hoffset = mtable[x] + hscroll; + voffset = y + vscroll; + + if(is_opt_mode) { + opt_x = (x + (hscroll & 7)); + + //tile 0 is unaffected by OPT mode... + if(opt_x >= 8) { + //cache tile data in hval, vval if possible + if((opt_x >> 3) != (prev_optx >> 3)) { + prev_optx = opt_x; + + hval = bg_get_tile((opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3]); + if(mode != 4) { + vval = bg_get_tile((opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3] + 8); + } + } + + if(mode == 4) { + if(hval & opt_valid_bit) { + if(!(hval & 0x8000)) { + hoffset = opt_x + (hval & ~7); + } else { + voffset = y + hval; + } + } + } else { + if(hval & opt_valid_bit) { + hoffset = opt_x + (hval & ~7); + } + if(vval & opt_valid_bit) { + voffset = y + vval; + } + } + } + } + + hoffset &= mask_x; + voffset &= mask_y; + + if((hoffset >> 3) != prev_x || (voffset >> 3) != prev_y) { + prev_x = (hoffset >> 3); + prev_y = (voffset >> 3); + + tile_num = bg_get_tile(hoffset, voffset); //format = vhopppcc cccccccc + mirror_y = (tile_num & 0x8000); + mirror_x = (tile_num & 0x4000); + tile_pri = (tile_num & 0x2000) ? pri1_pos : pri0_pos; + pal_num = ((tile_num >> 10) & 7); + pal_index = bgpal_index + (pal_num << pal_size); + + if(tile_width == 4) { //16x16 horizontal tile mirroring + if((bool)(hoffset & 8) != mirror_x) tile_num++; + } + + if(tile_height == 4) { //16x16 vertical tile mirroring + if((bool)(voffset & 8) != mirror_y) tile_num += 16; + } + + tile_num &= 0x03ff; + tile_num += tiledata_index; + tile_num &= tile_mask; + + if(bg_td_state[tile_num] == 1) { + render_bg_tile(tile_num); + } + + if(mirror_y) voffset ^= 7; //invert y tile pos + tile_ptr = bg_td + (tile_num * 64) + ((voffset & 7) * 8); + } + + if(mirror_x) hoffset ^= 7; //invert x tile pos + col = *(tile_ptr + (hoffset & 7)); + if(col) { + if(is_direct_color_mode) { + col = get_direct_color(pal_num, col); + } else { + col = get_palette(col + pal_index); + } + + if(!hires) { + if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); } + if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); } + } else { + int hx = x >> 1; + if(x & 1) { + if(bg_enabled == true && !wt_main[hx]) { setpixel_main(hx); } + } else { + if(bgsub_enabled == true && !wt_sub[hx]) { setpixel_sub(hx); } + } + } + } + } +} + +#undef setpixel_main +#undef setpixel_sub + +#endif diff --git a/mednafen/snes/src/ppu/bppu/render/cache.cpp b/mednafen/snes/src/ppu/bppu/render/cache.cpp new file mode 100755 index 0000000..2be2469 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/render/cache.cpp @@ -0,0 +1,147 @@ +#ifdef BPPU_CPP + +#define render_bg_tile_line_2bpp(mask) \ + col = !!(d0 & mask) << 0; \ + col += !!(d1 & mask) << 1; \ + *dest++ = col + +#define render_bg_tile_line_4bpp(mask) \ + col = !!(d0 & mask) << 0; \ + col += !!(d1 & mask) << 1; \ + col += !!(d2 & mask) << 2; \ + col += !!(d3 & mask) << 3; \ + *dest++ = col + +#define render_bg_tile_line_8bpp(mask) \ + col = !!(d0 & mask) << 0; \ + col += !!(d1 & mask) << 1; \ + col += !!(d2 & mask) << 2; \ + col += !!(d3 & mask) << 3; \ + col += !!(d4 & mask) << 4; \ + col += !!(d5 & mask) << 5; \ + col += !!(d6 & mask) << 6; \ + col += !!(d7 & mask) << 7; \ + *dest++ = col + +template +void bPPU::render_bg_tile(uint16 tile_num) { + uint8 col, d0, d1, d2, d3, d4, d5, d6, d7; + + if(color_depth == COLORDEPTH_4) { + uint8 *dest = (uint8*)bg_tiledata[TILE_2BIT] + tile_num * 64; + unsigned pos = tile_num * 16; + unsigned y = 8; + while(y--) { + d0 = memory::vram[pos ]; + d1 = memory::vram[pos + 1]; + render_bg_tile_line_2bpp(0x80); + render_bg_tile_line_2bpp(0x40); + render_bg_tile_line_2bpp(0x20); + render_bg_tile_line_2bpp(0x10); + render_bg_tile_line_2bpp(0x08); + render_bg_tile_line_2bpp(0x04); + render_bg_tile_line_2bpp(0x02); + render_bg_tile_line_2bpp(0x01); + pos += 2; + } + bg_tiledata_state[TILE_2BIT][tile_num] = 0; + } + + if(color_depth == COLORDEPTH_16) { + uint8 *dest = (uint8*)bg_tiledata[TILE_4BIT] + tile_num * 64; + unsigned pos = tile_num * 32; + unsigned y = 8; + while(y--) { + d0 = memory::vram[pos ]; + d1 = memory::vram[pos + 1]; + d2 = memory::vram[pos + 16]; + d3 = memory::vram[pos + 17]; + render_bg_tile_line_4bpp(0x80); + render_bg_tile_line_4bpp(0x40); + render_bg_tile_line_4bpp(0x20); + render_bg_tile_line_4bpp(0x10); + render_bg_tile_line_4bpp(0x08); + render_bg_tile_line_4bpp(0x04); + render_bg_tile_line_4bpp(0x02); + render_bg_tile_line_4bpp(0x01); + pos += 2; + } + bg_tiledata_state[TILE_4BIT][tile_num] = 0; + } + + if(color_depth == COLORDEPTH_256) { + uint8 *dest = (uint8*)bg_tiledata[TILE_8BIT] + tile_num * 64; + unsigned pos = tile_num * 64; + unsigned y = 8; + while(y--) { + d0 = memory::vram[pos ]; + d1 = memory::vram[pos + 1]; + d2 = memory::vram[pos + 16]; + d3 = memory::vram[pos + 17]; + d4 = memory::vram[pos + 32]; + d5 = memory::vram[pos + 33]; + d6 = memory::vram[pos + 48]; + d7 = memory::vram[pos + 49]; + render_bg_tile_line_8bpp(0x80); + render_bg_tile_line_8bpp(0x40); + render_bg_tile_line_8bpp(0x20); + render_bg_tile_line_8bpp(0x10); + render_bg_tile_line_8bpp(0x08); + render_bg_tile_line_8bpp(0x04); + render_bg_tile_line_8bpp(0x02); + render_bg_tile_line_8bpp(0x01); + pos += 2; + } + bg_tiledata_state[TILE_8BIT][tile_num] = 0; + } +} + +#undef render_bg_tile_line_2bpp +#undef render_bg_tile_line_4bpp +#undef render_bg_tile_line_8bpp + +void bPPU::flush_pixel_cache() { + uint16 main = get_palette(0); + uint16 sub = (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6) + ? main + : regs.color_rgb; + + unsigned i = 255; + do { + pixel_cache[i].src_main = main; + pixel_cache[i].src_sub = sub; + pixel_cache[i].bg_main = BACK; + pixel_cache[i].bg_sub = BACK; + pixel_cache[i].ce_main = false; + pixel_cache[i].ce_sub = false; + pixel_cache[i].pri_main = 0; + pixel_cache[i].pri_sub = 0; + } while(i--); +} + +void bPPU::alloc_tiledata_cache() { + bg_tiledata[TILE_2BIT] = new uint8_t[262144](); + bg_tiledata[TILE_4BIT] = new uint8_t[131072](); + bg_tiledata[TILE_8BIT] = new uint8_t[ 65536](); + bg_tiledata_state[TILE_2BIT] = new uint8_t[ 4096](); + bg_tiledata_state[TILE_4BIT] = new uint8_t[ 2048](); + bg_tiledata_state[TILE_8BIT] = new uint8_t[ 1024](); +} + +//marks all tiledata cache entries as dirty +void bPPU::flush_tiledata_cache() { + for(unsigned i = 0; i < 4096; i++) bg_tiledata_state[TILE_2BIT][i] = 1; + for(unsigned i = 0; i < 2048; i++) bg_tiledata_state[TILE_4BIT][i] = 1; + for(unsigned i = 0; i < 1024; i++) bg_tiledata_state[TILE_8BIT][i] = 1; +} + +void bPPU::free_tiledata_cache() { + delete[] bg_tiledata[TILE_2BIT]; + delete[] bg_tiledata[TILE_4BIT]; + delete[] bg_tiledata[TILE_8BIT]; + delete[] bg_tiledata_state[TILE_2BIT]; + delete[] bg_tiledata_state[TILE_4BIT]; + delete[] bg_tiledata_state[TILE_8BIT]; +} + +#endif diff --git a/mednafen/snes/src/ppu/bppu/render/line.cpp b/mednafen/snes/src/ppu/bppu/render/line.cpp new file mode 100755 index 0000000..14a8c56 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/render/line.cpp @@ -0,0 +1,139 @@ +#ifdef BPPU_CPP + +inline uint16 bPPU::get_palette(uint8 index) { + const unsigned addr = index << 1; + return memory::cgram[addr] + (memory::cgram[addr + 1] << 8); +} + +//p = 00000bgr +//t = BBGGGRRR +//r = 0BBb00GGGg0RRRr0 +inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) { + return ((t & 7) << 2) | ((p & 1) << 1) | + (((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) | + ((t >> 6) << 13) | ((p >> 2) << 12); +} + +inline uint16 bPPU::get_pixel_normal(uint32 x) { + pixel_t &p = pixel_cache[x]; + uint16 src_main, src_sub; + uint8 bg_sub; + src_main = p.src_main; + + if(!regs.addsub_mode) { + bg_sub = BACK; + src_sub = regs.color_rgb; + } else { + bg_sub = p.bg_sub; + src_sub = p.src_sub; + } + + if(!window[COL].main[x]) { + if(!window[COL].sub[x]) { + return 0x0000; + } + src_main = 0x0000; + } + + if(!p.ce_main && regs.color_enabled[p.bg_main] && window[COL].sub[x]) { + bool halve = false; + if(regs.color_halve && window[COL].main[x]) { + if(regs.addsub_mode && bg_sub == BACK); + else { + halve = true; + } + } + return addsub(src_main, src_sub, halve); + } + + return src_main; +} + +inline uint16 bPPU::get_pixel_swap(uint32 x) { + pixel_t &p = pixel_cache[x]; + uint16 src_main, src_sub; + uint8 bg_sub; + src_main = p.src_sub; + + if(!regs.addsub_mode) { + bg_sub = BACK; + src_sub = regs.color_rgb; + } else { + bg_sub = p.bg_main; + src_sub = p.src_main; + } + + if(!window[COL].main[x]) { + if(!window[COL].sub[x]) { + return 0x0000; + } + src_main = 0x0000; + } + + if(!p.ce_sub && regs.color_enabled[p.bg_sub] && window[COL].sub[x]) { + bool halve = false; + if(regs.color_halve && window[COL].main[x]) { + if(regs.addsub_mode && bg_sub == BACK); + else { + halve = true; + } + } + return addsub(src_main, src_sub, halve); + } + + return src_main; +} + +inline void bPPU::render_line_output() { + uint16 *ptr = (uint16*)output + (line * 1024) + + ((interlace() && field()) ? 512 : 0); + uint16 *luma_b = light_table_b [regs.display_brightness]; + uint16 *luma_gr = light_table_gr[regs.display_brightness]; + uint16 curr, prev; + + if(!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) { + if(regs.display_brightness == 15) { + for(unsigned x = 0; x < 256; x++) { + *ptr++ = get_pixel_normal(x); + } + } else { + for(unsigned x = 0; x < 256; x++) { + curr = get_pixel_normal(x); + *ptr++ = luma_b[curr >> 10] + luma_gr[curr & 0x3ff]; + } + } + } else { + if(regs.display_brightness == 15) { + for(unsigned x = 0, prev = 0; x < 256; x++) { + curr = get_pixel_swap(x); + *ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1; + prev = curr; + + curr = get_pixel_normal(x); + *ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1; + prev = curr; + + } + } else { + for(unsigned x = 0, prev = 0; x < 256; x++) { + curr = get_pixel_swap(x); + curr = luma_b[curr >> 10] + luma_gr[curr & 0x3ff]; + *ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1; + prev = curr; + + curr = get_pixel_normal(x); + curr = luma_b[curr >> 10] + luma_gr[curr & 0x3ff]; + *ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1; + prev = curr; + } + } + } +} + +inline void bPPU::render_line_clear() { + uint16 *ptr = (uint16*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0); + uint16 width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512; + memset(ptr, 0, width * 2 * sizeof(uint16)); +} + +#endif diff --git a/mednafen/snes/src/ppu/bppu/render/mode7.cpp b/mednafen/snes/src/ppu/bppu/render/mode7.cpp new file mode 100755 index 0000000..e012a10 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/render/mode7.cpp @@ -0,0 +1,139 @@ +#ifdef BPPU_CPP + +//bsnes mode7 renderer +// +//base algorithm written by anomie +//bsnes implementation written by byuu +// +//supports mode 7 + extbg + rotate + zoom + direct color + scrolling + m7sel + windowing + mosaic +//interlace and pseudo-hires support are automatic via main rendering routine + +//13-bit sign extend +//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv +#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) ) + +template +void bPPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) { + if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return; + + int32 px, py; + int32 tx, ty, tile, palette; + + int32 a = sclip<16>(cache.m7a); + int32 b = sclip<16>(cache.m7b); + int32 c = sclip<16>(cache.m7c); + int32 d = sclip<16>(cache.m7d); + + int32 cx = sclip<13>(cache.m7x); + int32 cy = sclip<13>(cache.m7y); + int32 hofs = sclip<13>(cache.m7_hofs); + int32 vofs = sclip<13>(cache.m7_vofs); + + int _pri, _x; + bool _bg_enabled = regs.bg_enabled[bg]; + bool _bgsub_enabled = regs.bgsub_enabled[bg]; + + build_window_tables(bg); + uint8 *wt_main = window[bg].main; + uint8 *wt_sub = window[bg].sub; + + int32 y = (regs.mode7_vflip == false ? line : 255 - line); + + uint16 *mtable_x, *mtable_y; + if(bg == BG1) { + mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0]; + mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0]; + } else { //bg == BG2 + //Mode7 EXTBG BG2 uses BG1 mosaic enable to control vertical mosaic, + //and BG2 mosaic enable to control horizontal mosaic... + mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG2] == true) ? regs.mosaic_size : 0]; + mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0]; + } + + int32 psx = ((a * CLIP(hofs - cx)) & ~63) + ((b * CLIP(vofs - cy)) & ~63) + ((b * mtable_y[y]) & ~63) + (cx << 8); + int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d * mtable_y[y]) & ~63) + (cy << 8); + for(int32 x = 0; x < 256; x++) { + px = psx + (a * mtable_x[x]); + py = psy + (c * mtable_x[x]); + + //mask floating-point bits (low 8 bits) + px >>= 8; + py >>= 8; + + switch(regs.mode7_repeat) { + case 0: //screen repetition outside of screen area + case 1: { //same as case 0 + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = memory::vram[(ty * 128 + tx) << 1]; + palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } break; + case 2: { //palette color 0 outside of screen area + if(px < 0 || px > 1023 || py < 0 || py > 1023) { + palette = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = memory::vram[(ty * 128 + tx) << 1]; + palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } + } break; + case 3: { //character 0 repetition outside of screen area + if(px < 0 || px > 1023 || py < 0 || py > 1023) { + tile = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = memory::vram[(ty * 128 + tx) << 1]; + } + palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } break; + } + + if(bg == BG1) { + _pri = pri0_pos; + } else { + _pri = (palette >> 7) ? pri1_pos : pri0_pos; + palette &= 0x7f; + } + + if(!palette) continue; + + _x = (regs.mode7_hflip == false) ? (x) : (255 - x); + + uint32 col; + if(regs.direct_color == true && bg == BG1) { + //direct color mode does not apply to bg2, as it is only 128 colors... + col = get_direct_color(0, palette); + } else { + col = get_palette(palette); + } + + if(regs.bg_enabled[bg] == true && !wt_main[_x]) { + if(pixel_cache[_x].pri_main < _pri) { + pixel_cache[_x].pri_main = _pri; + pixel_cache[_x].bg_main = bg; + pixel_cache[_x].src_main = col; + pixel_cache[_x].ce_main = false; + } + } + if(regs.bgsub_enabled[bg] == true && !wt_sub[_x]) { + if(pixel_cache[_x].pri_sub < _pri) { + pixel_cache[_x].pri_sub = _pri; + pixel_cache[_x].bg_sub = bg; + pixel_cache[_x].src_sub = col; + pixel_cache[_x].ce_sub = false; + } + } + } +} + +#undef CLIP + +#endif diff --git a/mednafen/snes/src/ppu/bppu/render/oam.cpp b/mednafen/snes/src/ppu/bppu/render/oam.cpp new file mode 100755 index 0000000..59c7845 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/render/oam.cpp @@ -0,0 +1,217 @@ +#ifdef BPPU_CPP + +void bPPU::build_sprite_list() { + if(sprite_list_valid == true) return; + sprite_list_valid = true; + + const uint8 *tableA = memory::oam.data(); + const uint8 *tableB = memory::oam.data() + 512; + + for(unsigned i = 0; i < 128; i++) { + const bool x = *tableB & (1 << ((i & 3) << 1)); //0x01, 0x04, 0x10, 0x40 + const bool size = *tableB & (2 << ((i & 3) << 1)); //0x02, 0x08, 0x20, 0x80 + + switch(cache.oam_basesize) { + case 0: sprite_list[i].width = (!size) ? 8 : 16; + sprite_list[i].height = (!size) ? 8 : 16; + break; + case 1: sprite_list[i].width = (!size) ? 8 : 32; + sprite_list[i].height = (!size) ? 8 : 32; + break; + case 2: sprite_list[i].width = (!size) ? 8 : 64; + sprite_list[i].height = (!size) ? 8 : 64; + break; + case 3: sprite_list[i].width = (!size) ? 16 : 32; + sprite_list[i].height = (!size) ? 16 : 32; + break; + case 4: sprite_list[i].width = (!size) ? 16 : 64; + sprite_list[i].height = (!size) ? 16 : 64; + break; + case 5: sprite_list[i].width = (!size) ? 32 : 64; + sprite_list[i].height = (!size) ? 32 : 64; + break; + case 6: sprite_list[i].width = (!size) ? 16 : 32; + sprite_list[i].height = (!size) ? 32 : 64; + if(regs.oam_interlace && !size) sprite_list[i].height = 16; + //32x64 height is not affected by oam_interlace setting + break; + case 7: sprite_list[i].width = (!size) ? 16 : 32; + sprite_list[i].height = (!size) ? 32 : 32; + if(regs.oam_interlace && !size) sprite_list[i].height = 16; + break; + } + + sprite_list[i].x = (x << 8) + tableA[0]; + sprite_list[i].y = (tableA[1] + 1) & 0xff; + sprite_list[i].character = tableA[2]; + sprite_list[i].vflip = tableA[3] & 0x80; + sprite_list[i].hflip = tableA[3] & 0x40; + sprite_list[i].priority = (tableA[3] >> 4) & 3; + sprite_list[i].palette = (tableA[3] >> 1) & 7; + sprite_list[i].use_nameselect = tableA[3] & 1; + + tableA += 4; + if((i & 3) == 3) tableB++; + } +} + +bool bPPU::is_sprite_on_scanline() { + //if sprite is entirely offscreen and doesn't wrap around to the left side of the screen, + //then it is not counted. this *should* be 256, and not 255, even though dot 256 is offscreen. + sprite_item *spr = &sprite_list[active_sprite]; + if(spr->x > 256 && (spr->x + spr->width - 1) < 512) return false; + + int spr_height = (regs.oam_interlace == false) ? (spr->height) : (spr->height >> 1); + if(line >= spr->y && line < (spr->y + spr_height)) return true; + if((spr->y + spr_height) >= 256 && line < ((spr->y + spr_height) & 255)) return true; + return false; +} + +void bPPU::load_oam_tiles() { + sprite_item *spr = &sprite_list[active_sprite]; + uint16 tile_width = spr->width >> 3; + int x = spr->x; + int y = (line - spr->y) & 0xff; + if(regs.oam_interlace == true) { + y <<= 1; + } + + if(spr->vflip == true) { + if(spr->width == spr->height) { + y = (spr->height - 1) - y; + } else { + y = (y < spr->width) ? ((spr->width - 1) - y) : (spr->width + ((spr->width - 1) - (y - spr->width))); + } + } + + if(regs.oam_interlace == true) { + y = (spr->vflip == false) ? (y + field()) : (y - field()); + } + + x &= 511; + y &= 255; + + uint16 tdaddr = cache.oam_tdaddr; + uint16 chrx = (spr->character ) & 15; + uint16 chry = (spr->character >> 4) & 15; + if(spr->use_nameselect == true) { + tdaddr += (256 * 32) + (cache.oam_nameselect << 13); + } + chry += (y >> 3); + chry &= 15; + chry <<= 4; + + for(unsigned tx = 0; tx < tile_width; tx++) { + unsigned sx = (x + (tx << 3)) & 511; + //ignore sprites that are offscreen, x==256 is a special case that loads all tiles in OBJ + if(x != 256 && sx >= 256 && (sx + 7) < 512) continue; + + if(regs.oam_tilecount++ > 34) break; + unsigned n = regs.oam_tilecount - 1; + oam_tilelist[n].x = sx; + oam_tilelist[n].y = y; + oam_tilelist[n].pri = spr->priority; + oam_tilelist[n].pal = 128 + (spr->palette << 4); + oam_tilelist[n].hflip = spr->hflip; + + unsigned mx = (spr->hflip == false) ? tx : ((tile_width - 1) - tx); + unsigned pos = tdaddr + ((chry + ((chrx + mx) & 15)) << 5); + oam_tilelist[n].tile = (pos >> 5) & 0x07ff; + } +} + +void bPPU::render_oam_tile(int tile_num) { + oam_tileitem *t = &oam_tilelist[tile_num]; + uint8 *oam_td = (uint8*)bg_tiledata[COLORDEPTH_16]; + uint8 *oam_td_state = (uint8*)bg_tiledata_state[COLORDEPTH_16]; + + if(oam_td_state[t->tile] == 1) { + render_bg_tile(t->tile); + } + + unsigned sx = t->x; + uint8 *tile_ptr = (uint8*)oam_td + (t->tile << 6) + ((t->y & 7) << 3); + for(unsigned x = 0; x < 8; x++) { + sx &= 511; + if(sx < 256) { + unsigned col = *(tile_ptr + ((t->hflip == false) ? x : (7 - x))); + if(col) { + col += t->pal; + oam_line_pal[sx] = col; + oam_line_pri[sx] = t->pri; + } + } + sx++; + } +} + +void bPPU::render_line_oam_rto() { + build_sprite_list(); + + regs.oam_itemcount = 0; + regs.oam_tilecount = 0; + memset(oam_line_pri, OAM_PRI_NONE, 256); + memset(oam_itemlist, 0xff, 32); + for(int s = 0; s < 34; s++) oam_tilelist[s].tile = 0xffff; + + for(int s = 0; s < 128; s++) { + active_sprite = (s + regs.oam_firstsprite) & 127; + if(is_sprite_on_scanline() == false) continue; + if(regs.oam_itemcount++ > 32) break; + oam_itemlist[regs.oam_itemcount - 1] = (s + regs.oam_firstsprite) & 127; + } + + for(int s = 31; s >= 0; s--) { + if(oam_itemlist[s] == 0xff) continue; + active_sprite = oam_itemlist[s]; + load_oam_tiles(); + } + + regs.time_over |= (regs.oam_tilecount > 34); + regs.range_over |= (regs.oam_itemcount > 32); +} + +#define setpixel_main(x) \ + if(pixel_cache[x].pri_main < pri) { \ + pixel_cache[x].pri_main = pri; \ + pixel_cache[x].bg_main = OAM; \ + pixel_cache[x].src_main = get_palette(oam_line_pal[x]); \ + pixel_cache[x].ce_main = (oam_line_pal[x] < 192); \ + } +#define setpixel_sub(x) \ + if(pixel_cache[x].pri_sub < pri) { \ + pixel_cache[x].pri_sub = pri; \ + pixel_cache[x].bg_sub = OAM; \ + pixel_cache[x].src_sub = get_palette(oam_line_pal[x]); \ + pixel_cache[x].ce_sub = (oam_line_pal[x] < 192); \ + } + +void bPPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) { + if(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false) return; + + for(unsigned s = 0; s < 34; s++) { + if(oam_tilelist[s].tile == 0xffff) continue; + render_oam_tile(s); + } + + bool bg_enabled = regs.bg_enabled[OAM]; + bool bgsub_enabled = regs.bgsub_enabled[OAM]; + + build_window_tables(OAM); + uint8 *wt_main = window[OAM].main; + uint8 *wt_sub = window[OAM].sub; + + unsigned pri_tbl[4] = { pri0_pos, pri1_pos, pri2_pos, pri3_pos }; + for(int x = 0; x < 256; x++) { + if(oam_line_pri[x] == OAM_PRI_NONE) continue; + + unsigned pri = pri_tbl[oam_line_pri[x]]; + if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); } + if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); } + } +} + +#undef setpixel_main +#undef setpixel_sub + +#endif diff --git a/mednafen/snes/src/ppu/bppu/render/render.cpp b/mednafen/snes/src/ppu/bppu/render/render.cpp new file mode 100755 index 0000000..4667987 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/render/render.cpp @@ -0,0 +1,129 @@ +#ifdef BPPU_CPP + +#include "cache.cpp" +#include "windows.cpp" +#include "bg.cpp" +#include "oam.cpp" +#include "mode7.cpp" +#include "addsub.cpp" +#include "line.cpp" + +//Mode 0: -> +// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 +// BG4B, BG3B, OAM0, BG4A, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3 +void bPPU::render_line_mode0() { + render_line_bg<0, BG1, COLORDEPTH_4>(8, 11); + render_line_bg<0, BG2, COLORDEPTH_4>(7, 10); + render_line_bg<0, BG3, COLORDEPTH_4>(2, 5); + render_line_bg<0, BG4, COLORDEPTH_4>(1, 4); + render_line_oam(3, 6, 9, 12); +} + +//Mode 1 (pri=1): -> +// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// BG3B, OAM0, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3, BG3A +// +//Mode 1 (pri=0): -> +// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// BG3B, OAM0, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3 +void bPPU::render_line_mode1() { + if(regs.bg3_priority) { + render_line_bg<1, BG1, COLORDEPTH_16>(5, 8); + render_line_bg<1, BG2, COLORDEPTH_16>(4, 7); + render_line_bg<1, BG3, COLORDEPTH_4 >(1, 10); + render_line_oam(2, 3, 6, 9); + } else { + render_line_bg<1, BG1, COLORDEPTH_16>(6, 9); + render_line_bg<1, BG2, COLORDEPTH_16>(5, 8); + render_line_bg<1, BG3, COLORDEPTH_4 >(1, 3); + render_line_oam(2, 4, 7, 10); + } +} + +//Mode 2: -> +// 1, 2, 3, 4, 5, 6, 7, 8 +// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +void bPPU::render_line_mode2() { + render_line_bg<2, BG1, COLORDEPTH_16>(3, 7); + render_line_bg<2, BG2, COLORDEPTH_16>(1, 5); + render_line_oam(2, 4, 6, 8); +} + +//Mode 3: -> +// 1, 2, 3, 4, 5, 6, 7, 8 +// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +void bPPU::render_line_mode3() { + render_line_bg<3, BG1, COLORDEPTH_256>(3, 7); + render_line_bg<3, BG2, COLORDEPTH_16 >(1, 5); + render_line_oam(2, 4, 6, 8); +} + +//Mode 4: -> +// 1, 2, 3, 4, 5, 6, 7, 8 +// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +void bPPU::render_line_mode4() { + render_line_bg<4, BG1, COLORDEPTH_256>(3, 7); + render_line_bg<4, BG2, COLORDEPTH_4 >(1, 5); + render_line_oam(2, 4, 6, 8); +} + +//Mode 5: -> +// 1, 2, 3, 4, 5, 6, 7, 8 +// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +void bPPU::render_line_mode5() { + render_line_bg<5, BG1, COLORDEPTH_16>(3, 7); + render_line_bg<5, BG2, COLORDEPTH_4 >(1, 5); + render_line_oam(2, 4, 6, 8); +} + +//Mode 6: -> +// 1, 2, 3, 4, 5, 6 +// OAM0, BG1B, OAM1, OAM2, BG1A, OAM3 +void bPPU::render_line_mode6() { + render_line_bg<6, BG1, COLORDEPTH_16>(2, 5); + render_line_oam(1, 3, 4, 6); +} + +//Mode7: -> +// 1, 2, 3, 4, 5 +// OAM0, BG1n, OAM1, OAM2, OAM3 + +//Mode 7 EXTBG: -> +// 1, 2, 3, 4, 5, 6, 7 +// BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3 +void bPPU::render_line_mode7() { + if(regs.mode7_extbg == false) { + render_line_mode7(2, 2); + render_line_oam(1, 3, 4, 5); + } else { + render_line_mode7(3, 3); + render_line_mode7(1, 5); + render_line_oam(2, 4, 6, 7); + } +} + +void bPPU::render_line() { + if(regs.display_disabled == true) { + render_line_clear(); + return; + } + + flush_pixel_cache(); + build_window_tables(COL); + update_bg_info(); + + switch(regs.bg_mode) { + case 0: render_line_mode0(); break; + case 1: render_line_mode1(); break; + case 2: render_line_mode2(); break; + case 3: render_line_mode3(); break; + case 4: render_line_mode4(); break; + case 5: render_line_mode5(); break; + case 6: render_line_mode6(); break; + case 7: render_line_mode7(); break; + } + + render_line_output(); +} + +#endif diff --git a/mednafen/snes/src/ppu/bppu/render/render.hpp b/mednafen/snes/src/ppu/bppu/render/render.hpp new file mode 100755 index 0000000..6de43e4 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/render/render.hpp @@ -0,0 +1,97 @@ +//render.cpp +debugvirtual inline void render_line_mode0(); +debugvirtual inline void render_line_mode1(); +debugvirtual inline void render_line_mode2(); +debugvirtual inline void render_line_mode3(); +debugvirtual inline void render_line_mode4(); +debugvirtual inline void render_line_mode5(); +debugvirtual inline void render_line_mode6(); +debugvirtual inline void render_line_mode7(); + +//cache.cpp +enum { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 }; +enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 }; + +struct pixel_t { + //bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0 + //needs to be bgr555 instead of palette index for direct color mode ($2130 bit 0) to work + uint16 src_main, src_sub; + //indicates source of palette # for main/subscreen (BG1-4, OAM, or back) + uint8 bg_main, bg_sub; + //color_exemption -- true when bg == OAM && palette index >= 192, disables color add/sub effects + uint8 ce_main, ce_sub; + //priority level of src_n. to set src_n, + //the priority of the pixel must be >pri_n + uint8 pri_main, pri_sub; +} pixel_cache[256]; + +uint8 *bg_tiledata[3]; +uint8 *bg_tiledata_state[3]; //0 = valid, 1 = dirty + +template void render_bg_tile(uint16 tile_num); +inline void flush_pixel_cache(); +void alloc_tiledata_cache(); +void flush_tiledata_cache(); +void free_tiledata_cache(); + +//windows.cpp +struct window_t { + uint8 main[256], sub[256]; +} window[6]; + +void build_window_table(uint8 bg, bool mainscreen); +void build_window_tables(uint8 bg); + +//bg.cpp +struct { + uint16 tw, th; //tile width, height + uint16 mx, my; //screen mask x, y + uint16 scx, scy; //sc index offsets +} bg_info[4]; +void update_bg_info(); + +template uint16 bg_get_tile(uint16 x, uint16 y); +template void render_line_bg(uint8 pri0_pos, uint8 pri1_pos); + +//oam.cpp +struct sprite_item { + uint8 width, height; + uint16 x, y; + uint8 character; + bool use_nameselect; + bool vflip, hflip; + uint8 palette; + uint8 priority; +} sprite_list[128]; +bool sprite_list_valid; +unsigned active_sprite; + +uint8 oam_itemlist[32]; +struct oam_tileitem { + uint16 x, y, pri, pal, tile; + bool hflip; +} oam_tilelist[34]; + +enum { OAM_PRI_NONE = 4 }; +uint8 oam_line_pal[256], oam_line_pri[256]; + +void build_sprite_list(); +bool is_sprite_on_scanline(); +void load_oam_tiles(); +void render_oam_tile(int tile_num); +void render_line_oam_rto(); +void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos); + +//mode7.cpp +template void render_line_mode7(uint8 pri0_pos, uint8 pri1_pos); + +//addsub.cpp +inline uint16 addsub(uint32 x, uint32 y, bool halve); + +//line.cpp +inline uint16 get_palette(uint8 index); +inline uint16 get_direct_color(uint8 p, uint8 t); +inline uint16 get_pixel_normal(uint32 x); +inline uint16 get_pixel_swap(uint32 x); +void render_line_output(); +void render_line_clear(); diff --git a/mednafen/snes/src/ppu/bppu/render/windows.cpp b/mednafen/snes/src/ppu/bppu/render/windows.cpp new file mode 100755 index 0000000..b7f60a4 --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/render/windows.cpp @@ -0,0 +1,70 @@ +#ifdef BPPU_CPP + +//screen: 0 = main, 1 = sub +void bPPU::build_window_table(uint8 bg, bool screen) { + bool set = 1, clr = 0; + uint8 *table = (screen == 0 ? window[bg].main : window[bg].sub); + + if(bg != COL) { + if(screen == 0 && regs.window_enabled[bg] == false) { + memset(table, 0, 256); + return; + } + if(screen == 1 && regs.sub_window_enabled[bg] == false) { + memset(table, 0, 256); + return; + } + } else { + switch(screen == 0 ? regs.color_mask : regs.colorsub_mask) { + case 0: memset(table, 1, 256); return; //always + case 3: memset(table, 0, 256); return; //never + case 1: set = 1, clr = 0; break; //inside window only + case 2: set = 0, clr = 1; break; //outside window only + } + } + + const uint16 window1_left = regs.window1_left; + const uint16 window1_right = regs.window1_right; + const uint16 window2_left = regs.window2_left; + const uint16 window2_right = regs.window2_right; + + if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) { + memset(table, clr, 256); + return; + } + + if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) { + if(regs.window1_invert[bg] == true) set ^= clr ^= set ^= clr; + for(unsigned x = 0; x < 256; x++) { + table[x] = (x >= window1_left && x <= window1_right) ? set : clr; + } + return; + } + + if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) { + if(regs.window2_invert[bg] == true) set ^= clr ^= set ^= clr; + for(unsigned x = 0; x < 256; x++) { + table[x] = (x >= window2_left && x <= window2_right) ? set : clr; + } + return; + } + + for(unsigned x = 0; x < 256; x++) { + bool w1_mask = (x >= window1_left && x <= window1_right) ^ regs.window1_invert[bg]; + bool w2_mask = (x >= window2_left && x <= window2_right) ^ regs.window2_invert[bg]; + + switch(regs.window_mask[bg]) { + case 0: table[x] = (w1_mask | w2_mask) == 1 ? set : clr; break; //or + case 1: table[x] = (w1_mask & w2_mask) == 1 ? set : clr; break; //and + case 2: table[x] = (w1_mask ^ w2_mask) == 1 ? set : clr; break; //xor + case 3: table[x] = (w1_mask ^ w2_mask) == 0 ? set : clr; break; //xnor + } + } +} + +void bPPU::build_window_tables(uint8 bg) { + build_window_table(bg, 0); + build_window_table(bg, 1); +} + +#endif diff --git a/mednafen/snes/src/ppu/bppu/serialization.cpp b/mednafen/snes/src/ppu/bppu/serialization.cpp new file mode 100755 index 0000000..593d5db --- /dev/null +++ b/mednafen/snes/src/ppu/bppu/serialization.cpp @@ -0,0 +1,183 @@ +#ifdef BPPU_CPP + +void bPPU::serialize(serializer &s) { + PPU::serialize(s); + + s.integer(region); + s.integer(line); + + s.integer(display.interlace); + s.integer(display.overscan); + + s.integer(cache.oam_basesize); + s.integer(cache.oam_nameselect); + s.integer(cache.oam_tdaddr); + + s.integer(regs.ppu1_mdr); + s.integer(regs.ppu2_mdr); + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_y[n]); + + s.integer(regs.display_disabled); + s.integer(regs.display_brightness); + + s.integer(regs.oam_basesize); + s.integer(regs.oam_nameselect); + s.integer(regs.oam_tdaddr); + + s.integer(regs.oam_baseaddr); + s.integer(regs.oam_addr); + s.integer(regs.oam_priority); + s.integer(regs.oam_firstsprite); + + s.integer(regs.oam_latchdata); + + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_tilesize[n]); + s.integer(regs.bg3_priority); + s.integer(regs.bg_mode); + + s.integer(regs.mosaic_size); + for(unsigned n = 0; n < 4; n++) s.integer(regs.mosaic_enabled[n]); + s.integer(regs.mosaic_countdown); + + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_scaddr[n]); + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_scsize[n]); + + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_tdaddr[n]); + + s.integer(regs.bg_ofslatch); + s.integer(regs.m7_hofs); + s.integer(regs.m7_vofs); + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_hofs[n]); + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_vofs[n]); + + s.integer(regs.vram_incmode); + s.integer(regs.vram_mapping); + s.integer(regs.vram_incsize); + + s.integer(regs.vram_addr); + + s.integer(regs.mode7_repeat); + s.integer(regs.mode7_vflip); + s.integer(regs.mode7_hflip); + + s.integer(regs.m7_latch); + s.integer(regs.m7a); + s.integer(regs.m7b); + s.integer(regs.m7c); + s.integer(regs.m7d); + s.integer(regs.m7x); + s.integer(regs.m7y); + + s.integer(regs.cgram_addr); + + s.integer(regs.cgram_latchdata); + + for(unsigned n = 0; n < 6; n++) s.integer(regs.window1_enabled[n]); + for(unsigned n = 0; n < 6; n++) s.integer(regs.window1_invert [n]); + for(unsigned n = 0; n < 6; n++) s.integer(regs.window2_enabled[n]); + for(unsigned n = 0; n < 6; n++) s.integer(regs.window2_invert [n]); + + s.integer(regs.window1_left); + s.integer(regs.window1_right); + s.integer(regs.window2_left); + s.integer(regs.window2_right); + + for(unsigned n = 0; n < 6; n++) s.integer(regs.window_mask[n]); + for(unsigned n = 0; n < 5; n++) s.integer(regs.bg_enabled[n]); + for(unsigned n = 0; n < 5; n++) s.integer(regs.bgsub_enabled[n]); + for(unsigned n = 0; n < 5; n++) s.integer(regs.window_enabled[n]); + for(unsigned n = 0; n < 5; n++) s.integer(regs.sub_window_enabled[n]); + + s.integer(regs.color_mask); + s.integer(regs.colorsub_mask); + s.integer(regs.addsub_mode); + s.integer(regs.direct_color); + + s.integer(regs.color_mode); + s.integer(regs.color_halve); + for(unsigned n = 0; n < 6; n++) s.integer(regs.color_enabled[n]); + + s.integer(regs.color_r); + s.integer(regs.color_g); + s.integer(regs.color_b); + s.integer(regs.color_rgb); + + s.integer(regs.mode7_extbg); + s.integer(regs.pseudo_hires); + s.integer(regs.overscan); + s.integer(regs.scanlines); + s.integer(regs.oam_interlace); + s.integer(regs.interlace); + + s.integer(regs.hcounter); + s.integer(regs.vcounter); + s.integer(regs.latch_hcounter); + s.integer(regs.latch_vcounter); + s.integer(regs.counters_latched); + + s.integer(regs.vram_readbuffer); + + s.integer(regs.time_over); + s.integer(regs.range_over); + s.integer(regs.oam_itemcount); + s.integer(regs.oam_tilecount); + + for(unsigned n = 0; n < 256; n++) { + s.integer(pixel_cache[n].src_main); + s.integer(pixel_cache[n].src_sub); + s.integer(pixel_cache[n].bg_main); + s.integer(pixel_cache[n].bg_sub); + s.integer(pixel_cache[n].ce_main); + s.integer(pixel_cache[n].ce_sub); + s.integer(pixel_cache[n].pri_main); + s.integer(pixel_cache[n].pri_sub); + } + + //better to just take a small speed hit than store all of bg_tiledata[3][] ... + flush_tiledata_cache(); + + for(unsigned n = 0; n < 6; n++) { + s.array(window[n].main, 256); + s.array(window[n].sub, 256); + } + + for(unsigned n = 0; n < 4; n++) { + s.integer(bg_info[n].tw); + s.integer(bg_info[n].th); + s.integer(bg_info[n].mx); + s.integer(bg_info[n].my); + s.integer(bg_info[n].scx); + s.integer(bg_info[n].scy); + } + + for(unsigned n = 0; n < 128; n++) { + s.integer(sprite_list[n].width); + s.integer(sprite_list[n].height); + s.integer(sprite_list[n].x); + s.integer(sprite_list[n].y); + s.integer(sprite_list[n].character); + s.integer(sprite_list[n].use_nameselect); + s.integer(sprite_list[n].vflip); + s.integer(sprite_list[n].hflip); + s.integer(sprite_list[n].palette); + s.integer(sprite_list[n].priority); + } + s.integer(sprite_list_valid); + s.integer(active_sprite); + + s.array(oam_itemlist, 32); + + for(unsigned n = 0; n < 34; n++) { + s.integer(oam_tilelist[n].x); + s.integer(oam_tilelist[n].y); + s.integer(oam_tilelist[n].pri); + s.integer(oam_tilelist[n].pal); + s.integer(oam_tilelist[n].tile); + s.integer(oam_tilelist[n].hflip); + } + + s.array(oam_line_pal, 256); + s.array(oam_line_pri, 256); +} + +#endif diff --git a/mednafen/snes/src/ppu/ppu-debugger.cpp b/mednafen/snes/src/ppu/ppu-debugger.cpp new file mode 100755 index 0000000..df06f22 --- /dev/null +++ b/mednafen/snes/src/ppu/ppu-debugger.cpp @@ -0,0 +1,305 @@ +#ifdef PPU_CPP + +bool PPUDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + //internal + if(id == n++) { name = "S-PPU1 MDR"; value = string::printf("0x%.2x", ppu1_mdr()); return true; } + if(id == n++) { name = "S-PPU2 MDR"; value = string::printf("0x%.2x", ppu2_mdr()); return true; } + + //$2100 + if(id == n++) { name = "$2100"; value = ""; return true; } + if(id == n++) { name = "Display Disable"; value = display_disable(); return true; } + if(id == n++) { name = "Display Brightness"; value = display_brightness(); return true; } + + //$2101 + if(id == n++) { name = "$2101"; value = ""; return true; } + if(id == n++) { name = "OAM Base Size"; value = oam_base_size(); return true; } + if(id == n++) { name = "OAM Name Select"; value = oam_name_select(); return true; } + if(id == n++) { name = "OAM Name Base Address"; value = string::printf("0x%.4x", oam_name_base_address()); return true; } + + //$2102-$2103 + if(id == n++) { name = "$2102-$2103"; value = ""; return true; } + if(id == n++) { name = "OAM Base Address"; value = string::printf("0x%.4x", oam_base_address()); return true; } + if(id == n++) { name = "OAM Priority"; value = oam_priority(); return true; } + + //$2105 + if(id == n++) { name = "$2105"; value = ""; return true; } + if(id == n++) { name = "BG1 Tile Size"; value = bg1_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG2 Tile Size"; value = bg2_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG3 Tile Size"; value = bg3_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG4 Tile Size"; value = bg4_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG3 Priority"; value = bg3_priority(); return true; } + if(id == n++) { name = "BG Mode"; value = bg_mode(); return true; } + + //$2106 + if(id == n++) { name = "$2106"; value = ""; return true; } + if(id == n++) { name = "Mosaic Size"; value = mosaic_size(); return true; } + if(id == n++) { name = "BG1 Mosaic Enable"; value = bg1_mosaic_enable(); return true; } + if(id == n++) { name = "BG2 Mosaic Enable"; value = bg2_mosaic_enable(); return true; } + if(id == n++) { name = "BG3 Mosaic Enable"; value = bg3_mosaic_enable(); return true; } + if(id == n++) { name = "BG4 Mosaic Enable"; value = bg4_mosaic_enable(); return true; } + + static char screen_size[4][8] = { "32x32", "32x64", "64x32", "64x64" }; + + //$2107 + if(id == n++) { name = "$2107"; value = ""; return true; } + if(id == n++) { name = "BG1 Screen Address"; value = string::printf("0x%.4x", bg1_screen_address()); return true; } + if(id == n++) { name = "BG1 Screen Size"; value = screen_size[bg1_screen_size()]; return true; } + + //$2108 + if(id == n++) { name = "$2108"; value = ""; return true; } + if(id == n++) { name = "BG2 Screen Address"; value = string::printf("0x%.4x", bg2_screen_address()); return true; } + if(id == n++) { name = "BG2 Screen Size"; value = screen_size[bg2_screen_size()]; return true; } + + //$2109 + if(id == n++) { name = "$2109"; value = ""; return true; } + if(id == n++) { name = "BG3 Screen Address"; value = string::printf("0x%.4x", bg3_screen_address()); return true; } + if(id == n++) { name = "BG3 Screen Size"; value = screen_size[bg3_screen_size()]; return true; } + + //$210a + if(id == n++) { name = "$210a"; value = ""; return true; } + if(id == n++) { name = "BG4 Screen Address"; value = string::printf("0x%.4x", bg4_screen_address()); return true; } + if(id == n++) { name = "BG4 Screen Size"; value = screen_size[bg4_screen_size()]; return true; } + + //$210b + if(id == n++) { name = "$210b"; value = ""; return true; } + if(id == n++) { name = "BG1 Name Base Address"; value = string::printf("0x%.4x", bg1_name_base_address()); return true; } + if(id == n++) { name = "BG2 Name Base Address"; value = string::printf("0x%.4x", bg2_name_base_address()); return true; } + + //$210c + if(id == n++) { name = "$210c"; value = ""; return true; } + if(id == n++) { name = "BG3 Name Base Address"; value = string::printf("0x%.4x", bg3_name_base_address()); return true; } + if(id == n++) { name = "BG4 Name Base Address"; value = string::printf("0x%.4x", bg4_name_base_address()); return true; } + + //$210d + if(id == n++) { name = "$210d"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Scroll H-offset"; value = mode7_hoffset(); return true; } + if(id == n++) { name = "BG1 Scroll H-offset"; value = bg1_hoffset(); return true; } + + //$210e + if(id == n++) { name = "$210e"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Scroll V-offset"; value = mode7_voffset(); return true; } + if(id == n++) { name = "BG1 Scroll V-offset"; value = bg1_voffset(); return true; } + + //$210f + if(id == n++) { name = "$210f"; value = ""; return true; } + if(id == n++) { name = "BG2 Scroll H-offset"; value = bg2_hoffset(); return true; } + + //$2110 + if(id == n++) { name = "$2110"; value = ""; return true; } + if(id == n++) { name = "BG2 Scroll V-offset"; value = bg2_voffset(); return true; } + + //$2111 + if(id == n++) { name = "$2111"; value = ""; return true; } + if(id == n++) { name = "BG3 Scroll H-offset"; value = bg3_hoffset(); return true; } + + //$2112 + if(id == n++) { name = "$2112"; value = ""; return true; } + if(id == n++) { name = "BG3 Scroll V-offset"; value = bg3_voffset(); return true; } + + //$2113 + if(id == n++) { name = "$2113"; value = ""; return true; } + if(id == n++) { name = "BG4 Scroll H-offset"; value = bg4_hoffset(); return true; } + + //$2114 + if(id == n++) { name = "$2114"; value = ""; return true; } + if(id == n++) { name = "BG4 Scroll V-offset"; value = bg4_voffset(); return true; } + + //$2115 + if(id == n++) { name = "$2115"; value = ""; return true; } + if(id == n++) { name = "VRAM Increment Mode"; value = (unsigned)vram_increment_mode(); return true; } + if(id == n++) { name = "VRAM Increment Formation"; value = vram_increment_formation(); return true; } + if(id == n++) { name = "VRAM Increment Size"; value = vram_increment_size(); return true; } + + //$2116-$2117 + if(id == n++) { name = "$2116-$2117"; value = ""; return true; } + if(id == n++) { name = "VRAM Address"; value = string::printf("0x%.4x", vram_address()); return true; } + + //$211a + if(id == n++) { name = "$211a"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Repeat"; value = mode7_repeat(); return true; } + if(id == n++) { name = "Mode 7 V-flip"; value = mode7_vflip(); return true; } + if(id == n++) { name = "Mode 7 H-flip"; value = mode7_hflip(); return true; } + + //$211b + if(id == n++) { name = "$211b"; value = ""; return true; } + if(id == n++) { name = "Mode 7 A"; value = mode7_a(); return true; } + + //$211c + if(id == n++) { name = "$211c"; value = ""; return true; } + if(id == n++) { name = "Mode 7 B"; value = mode7_b(); return true; } + + //$211d + if(id == n++) { name = "$211d"; value = ""; return true; } + if(id == n++) { name = "Mode 7 C"; value = mode7_c(); return true; } + + //$211e + if(id == n++) { name = "$211e"; value = ""; return true; } + if(id == n++) { name = "Mode 7 D"; value = mode7_d(); return true; } + + //$211f + if(id == n++) { name = "$211f"; value = ""; return true; } + if(id == n++) { name = "Mode 7 X"; value = mode7_x(); return true; } + + //$2120 + if(id == n++) { name = "$2120"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Y"; value = mode7_y(); return true; } + + //$2121 + if(id == n++) { name = "$2121"; value = ""; return true; } + if(id == n++) { name = "CGRAM Address"; value = string::printf("0x%.4x", cgram_address()); return true; } + + //$2123 + if(id == n++) { name = "$2123"; value = ""; return true; } + if(id == n++) { name = "BG1 Window 1 Enable"; value = bg1_window1_enable(); return true; } + if(id == n++) { name = "BG1 Window 1 Invert"; value = bg1_window1_invert(); return true; } + if(id == n++) { name = "BG1 Window 2 Enable"; value = bg1_window2_enable(); return true; } + if(id == n++) { name = "BG1 Window 2 Invert"; value = bg1_window2_invert(); return true; } + if(id == n++) { name = "BG2 Window 1 Enable"; value = bg2_window1_enable(); return true; } + if(id == n++) { name = "BG2 Window 1 Invert"; value = bg2_window1_invert(); return true; } + if(id == n++) { name = "BG2 Window 2 Enable"; value = bg2_window2_enable(); return true; } + if(id == n++) { name = "BG2 Window 2 Invert"; value = bg2_window2_invert(); return true; } + + //$2124 + if(id == n++) { name = "$2124"; value = ""; return true; } + if(id == n++) { name = "BG3 Window 1 Enable"; value = bg3_window1_enable(); return true; } + if(id == n++) { name = "BG3 Window 1 Invert"; value = bg3_window1_invert(); return true; } + if(id == n++) { name = "BG3 Window 2 Enable"; value = bg3_window2_enable(); return true; } + if(id == n++) { name = "BG3 Window 2 Invert"; value = bg3_window2_invert(); return true; } + if(id == n++) { name = "BG4 Window 1 Enable"; value = bg4_window1_enable(); return true; } + if(id == n++) { name = "BG4 Window 1 Invert"; value = bg4_window1_invert(); return true; } + if(id == n++) { name = "BG4 Window 2 Enable"; value = bg4_window2_enable(); return true; } + if(id == n++) { name = "BG4 Window 2 Invert"; value = bg4_window2_invert(); return true; } + + //$2125 + if(id == n++) { name = "$2125"; value = ""; return true; } + if(id == n++) { name = "OAM Window 1 Enable"; value = oam_window1_enable(); return true; } + if(id == n++) { name = "OAM Window 1 Invert"; value = oam_window1_invert(); return true; } + if(id == n++) { name = "OAM Window 2 Enable"; value = oam_window2_enable(); return true; } + if(id == n++) { name = "OAM Window 2 Invert"; value = oam_window2_invert(); return true; } + if(id == n++) { name = "Color Window 1 Enable"; value = color_window1_enable(); return true; } + if(id == n++) { name = "Color Window 1 Invert"; value = color_window1_invert(); return true; } + if(id == n++) { name = "Color Window 2 Enable"; value = color_window2_enable(); return true; } + if(id == n++) { name = "Color Window 2 Invert"; value = color_window2_invert(); return true; } + + //$2126 + if(id == n++) { name = "$2126"; value = ""; return true; } + if(id == n++) { name = "Window 1 Left"; value = window1_left(); return true; } + + //$2127 + if(id == n++) { name = "$2127"; value = ""; return true; } + if(id == n++) { name = "Window 1 Right"; value = window1_right(); return true; } + + //$2128 + if(id == n++) { name = "$2128"; value = ""; return true; } + if(id == n++) { name = "Window 2 Left"; value = window2_left(); return true; } + + //$2129 + if(id == n++) { name = "$2129"; value = ""; return true; } + if(id == n++) { name = "Window 2 Right"; value = window2_right(); return true; } + + static char window_mask_mode[4][8] = { "OR", "AND", "XOR", "XNOR" }; + + //$212a + if(id == n++) { name = "$212a"; value = ""; return true; } + if(id == n++) { name = "BG1 Window Mask"; value = window_mask_mode[bg1_window_mask()]; return true; } + if(id == n++) { name = "BG2 Window Mask"; value = window_mask_mode[bg2_window_mask()]; return true; } + if(id == n++) { name = "BG3 Window Mask"; value = window_mask_mode[bg3_window_mask()]; return true; } + if(id == n++) { name = "BG4 Window Mask"; value = window_mask_mode[bg4_window_mask()]; return true; } + + //$212b + if(id == n++) { name = "$212b"; value = ""; return true; } + if(id == n++) { name = "OAM Window Mask"; value = window_mask_mode[oam_window_mask()]; return true; } + if(id == n++) { name = "Color Window Mask"; value = window_mask_mode[color_window_mask()]; return true; } + + //$212c + if(id == n++) { name = "$212c"; value = ""; return true; } + if(id == n++) { name = "BG1 Mainscreen Enable"; value = bg1_mainscreen_enable(); return true; } + if(id == n++) { name = "BG2 Mainscreen Enable"; value = bg2_mainscreen_enable(); return true; } + if(id == n++) { name = "BG3 Mainscreen Enable"; value = bg3_mainscreen_enable(); return true; } + if(id == n++) { name = "BG4 Mainscreen Enable"; value = bg4_mainscreen_enable(); return true; } + if(id == n++) { name = "OAM Mainscreen Enable"; value = oam_mainscreen_enable(); return true; } + + //$212d + if(id == n++) { name = "$212d"; value = ""; return true; } + if(id == n++) { name = "BG1 Subscreen Enable"; value = bg1_subscreen_enable(); return true; } + if(id == n++) { name = "BG2 Subscreen Enable"; value = bg2_subscreen_enable(); return true; } + if(id == n++) { name = "BG3 Subscreen Enable"; value = bg3_subscreen_enable(); return true; } + if(id == n++) { name = "BG4 Subscreen Enable"; value = bg4_subscreen_enable(); return true; } + if(id == n++) { name = "OAM Subscreen Enable"; value = oam_subscreen_enable(); return true; } + + //$212e + if(id == n++) { name = "$212e"; value = ""; return true; } + if(id == n++) { name = "BG1 Mainscreen Window Enable"; value = bg1_mainscreen_window_enable(); return true; } + if(id == n++) { name = "BG2 Mainscreen Window Enable"; value = bg2_mainscreen_window_enable(); return true; } + if(id == n++) { name = "BG3 Mainscreen Window Enable"; value = bg3_mainscreen_window_enable(); return true; } + if(id == n++) { name = "BG4 Mainscreen Window Enable"; value = bg4_mainscreen_window_enable(); return true; } + if(id == n++) { name = "OAM Mainscreen Window Enable"; value = oam_mainscreen_window_enable(); return true; } + + //$212f + if(id == n++) { name = "$212f"; value = ""; return true; } + if(id == n++) { name = "BG1 Subscreen Window Enable"; value = bg1_subscreen_window_enable(); return true; } + if(id == n++) { name = "BG2 Subscreen Window Enable"; value = bg2_subscreen_window_enable(); return true; } + if(id == n++) { name = "BG3 Subscreen Window Enable"; value = bg3_subscreen_window_enable(); return true; } + if(id == n++) { name = "BG4 Subscreen Window Enable"; value = bg4_subscreen_window_enable(); return true; } + if(id == n++) { name = "OAM Subscreen Window Enable"; value = oam_subscreen_window_enable(); return true; } + + static char color_window_mask_mode[4][32] = { "Always", "Never", "Inside Window Only", "Outside Window Only" }; + + //$2130 + if(id == n++) { name = "$2130"; value = ""; return true; } + if(id == n++) { name = "Color Mainscreen Window Mask"; value = color_window_mask_mode[color_mainscreen_window_mask()]; return true; } + if(id == n++) { name = "Color Subscreen Window Mask"; value = color_window_mask_mode[color_subscreen_window_mask()]; return true; } + if(id == n++) { name = "Color Add/Subtract Mode"; value = !color_add_subtract_mode() ? "Fixed Color" : "Subscreen"; return true; } + if(id == n++) { name = "Direct Color"; value = direct_color(); return true; } + + //$2131 + if(id == n++) { name = "$2131"; value = ""; return true; } + if(id == n++) { name = "Color Mode"; value = !color_mode() ? "Add" : "Subtract"; return true; } + if(id == n++) { name = "Color Halve"; value = color_halve(); return true; } + if(id == n++) { name = "BG1 Color Enable"; value = bg1_color_enable(); return true; } + if(id == n++) { name = "BG2 Color Enable"; value = bg2_color_enable(); return true; } + if(id == n++) { name = "BG3 Color Enable"; value = bg3_color_enable(); return true; } + if(id == n++) { name = "BG4 Color Enable"; value = bg4_color_enable(); return true; } + if(id == n++) { name = "OAM Color Enable"; value = oam_color_enable(); return true; } + if(id == n++) { name = "Back Color Enable"; value = back_color_enable(); return true; } + + //$2132 + if(id == n++) { name = "$2132"; value = ""; return true; } + if(id == n++) { name = "Color Constant - Blue"; value = color_constant_blue(); return true; } + if(id == n++) { name = "Color Constant - Green"; value = color_constant_green(); return true; } + if(id == n++) { name = "Color Constant - Red"; value = color_constant_red(); return true; } + + //$2133 + if(id == n++) { name = "$2133"; value = ""; return true; } + if(id == n++) { name = "Mode 7 EXTBG"; value = mode7_extbg(); return true; } + if(id == n++) { name = "Pseudo Hires"; value = pseudo_hires(); return true; } + if(id == n++) { name = "Overscan"; value = overscan(); return true; } + if(id == n++) { name = "OAM Interlace"; value = oam_interlace(); return true; } + if(id == n++) { name = "Interlace"; value = interlace(); return true; } + + //$213c + if(id == n++) { name = "$213c"; value = ""; return true; } + if(id == n++) { name = "H-counter"; value = hcounter(); return true; } + + //$213d + if(id == n++) { name = "$213d"; value = ""; return true; } + if(id == n++) { name = "V-counter"; value = vcounter(); return true; } + + //$213e + if(id == n++) { name = "$213e"; value = ""; return true; } + if(id == n++) { name = "Range Over"; value = range_over(); return true; } + if(id == n++) { name = "Time Over"; value = time_over(); return true; } + if(id == n++) { name = "S-PPU1 Version"; value = ppu1_version(); return true; } + + //$213f + if(id == n++) { name = "$213f"; value = ""; return true; } + if(id == n++) { name = "Field"; value = field(); return true; } + if(id == n++) { name = "Region"; value = !region() ? "NTSC" : "PAL"; return true; } + if(id == n++) { name = "S-PPU2 Version"; value = ppu2_version(); return true; } + + return false; +} + +#endif diff --git a/mednafen/snes/src/ppu/ppu-debugger.hpp b/mednafen/snes/src/ppu/ppu-debugger.hpp new file mode 100755 index 0000000..800bedd --- /dev/null +++ b/mednafen/snes/src/ppu/ppu-debugger.hpp @@ -0,0 +1,243 @@ +struct PPUDebugger : ChipDebugger { + bool property(unsigned id, string &name, string &value); + + //internal + virtual unsigned ppu1_mdr() { return 0; } + virtual unsigned ppu2_mdr() { return 0; } + + //$2100 + virtual bool display_disable() { return 0; } + virtual unsigned display_brightness() { return 0; } + + //$2101 + virtual unsigned oam_base_size() { return 0; } + virtual unsigned oam_name_select() { return 0; } + virtual unsigned oam_name_base_address() { return 0; } + + //$2102-$2103 + virtual unsigned oam_base_address() { return 0; } + virtual bool oam_priority() { return 0; } + + //$2105 + virtual bool bg1_tile_size() { return 0; } + virtual bool bg2_tile_size() { return 0; } + virtual bool bg3_tile_size() { return 0; } + virtual bool bg4_tile_size() { return 0; } + virtual bool bg3_priority() { return 0; } + virtual unsigned bg_mode() { return 0; } + + //$2106 + virtual unsigned mosaic_size() { return 0; } + virtual bool bg1_mosaic_enable() { return 0; } + virtual bool bg2_mosaic_enable() { return 0; } + virtual bool bg3_mosaic_enable() { return 0; } + virtual bool bg4_mosaic_enable() { return 0; } + + //$2107 + virtual unsigned bg1_screen_address() { return 0; } + virtual unsigned bg1_screen_size() { return 0; } + + //$2108 + virtual unsigned bg2_screen_address() { return 0; } + virtual unsigned bg2_screen_size() { return 0; } + + //$2109 + virtual unsigned bg3_screen_address() { return 0; } + virtual unsigned bg3_screen_size() { return 0; } + + //$210a + virtual unsigned bg4_screen_address() { return 0; } + virtual unsigned bg4_screen_size() { return 0; } + + //$210b + virtual unsigned bg1_name_base_address() { return 0; } + virtual unsigned bg2_name_base_address() { return 0; } + + //$210c + virtual unsigned bg3_name_base_address() { return 0; } + virtual unsigned bg4_name_base_address() { return 0; } + + //$210d + virtual unsigned mode7_hoffset() { return 0; } + virtual unsigned bg1_hoffset() { return 0; } + + //$210e + virtual unsigned mode7_voffset() { return 0; } + virtual unsigned bg1_voffset() { return 0; } + + //$210f + virtual unsigned bg2_hoffset() { return 0; } + + //$2110 + virtual unsigned bg2_voffset() { return 0; } + + //$2111 + virtual unsigned bg3_hoffset() { return 0; } + + //$2112 + virtual unsigned bg3_voffset() { return 0; } + + //$2113 + virtual unsigned bg4_hoffset() { return 0; } + + //$2114 + virtual unsigned bg4_voffset() { return 0; } + + //$2115 + virtual bool vram_increment_mode() { return 0; } + virtual unsigned vram_increment_formation() { return 0; } + virtual unsigned vram_increment_size() { return 0; } + + //$2116-$2117 + virtual unsigned vram_address() { return 0; } + + //$211a + virtual unsigned mode7_repeat() { return 0; } + virtual bool mode7_vflip() { return 0; } + virtual bool mode7_hflip() { return 0; } + + //$211b + virtual unsigned mode7_a() { return 0; } + + //$211c + virtual unsigned mode7_b() { return 0; } + + //$211d + virtual unsigned mode7_c() { return 0; } + + //$211e + virtual unsigned mode7_d() { return 0; } + + //$211f + virtual unsigned mode7_x() { return 0; } + + //$2120 + virtual unsigned mode7_y() { return 0; } + + //$2121 + virtual unsigned cgram_address() { return 0; } + + //$2123 + virtual bool bg1_window1_enable() { return 0; } + virtual bool bg1_window1_invert() { return 0; } + virtual bool bg1_window2_enable() { return 0; } + virtual bool bg1_window2_invert() { return 0; } + virtual bool bg2_window1_enable() { return 0; } + virtual bool bg2_window1_invert() { return 0; } + virtual bool bg2_window2_enable() { return 0; } + virtual bool bg2_window2_invert() { return 0; } + + //$2124 + virtual bool bg3_window1_enable() { return 0; } + virtual bool bg3_window1_invert() { return 0; } + virtual bool bg3_window2_enable() { return 0; } + virtual bool bg3_window2_invert() { return 0; } + virtual bool bg4_window1_enable() { return 0; } + virtual bool bg4_window1_invert() { return 0; } + virtual bool bg4_window2_enable() { return 0; } + virtual bool bg4_window2_invert() { return 0; } + + //$2125 + virtual bool oam_window1_enable() { return 0; } + virtual bool oam_window1_invert() { return 0; } + virtual bool oam_window2_enable() { return 0; } + virtual bool oam_window2_invert() { return 0; } + virtual bool color_window1_enable() { return 0; } + virtual bool color_window1_invert() { return 0; } + virtual bool color_window2_enable() { return 0; } + virtual bool color_window2_invert() { return 0; } + + //$2126 + virtual unsigned window1_left() { return 0; } + + //$2127 + virtual unsigned window1_right() { return 0; } + + //$2128 + virtual unsigned window2_left() { return 0; } + + //$2129 + virtual unsigned window2_right() { return 0; } + + //$212a + virtual unsigned bg1_window_mask() { return 0; } + virtual unsigned bg2_window_mask() { return 0; } + virtual unsigned bg3_window_mask() { return 0; } + virtual unsigned bg4_window_mask() { return 0; } + + //$212b + virtual unsigned oam_window_mask() { return 0; } + virtual unsigned color_window_mask() { return 0; } + + //$212c + virtual bool bg1_mainscreen_enable() { return 0; } + virtual bool bg2_mainscreen_enable() { return 0; } + virtual bool bg3_mainscreen_enable() { return 0; } + virtual bool bg4_mainscreen_enable() { return 0; } + virtual bool oam_mainscreen_enable() { return 0; } + + //$212d + virtual bool bg1_subscreen_enable() { return 0; } + virtual bool bg2_subscreen_enable() { return 0; } + virtual bool bg3_subscreen_enable() { return 0; } + virtual bool bg4_subscreen_enable() { return 0; } + virtual bool oam_subscreen_enable() { return 0; } + + //$212e + virtual bool bg1_mainscreen_window_enable() { return 0; } + virtual bool bg2_mainscreen_window_enable() { return 0; } + virtual bool bg3_mainscreen_window_enable() { return 0; } + virtual bool bg4_mainscreen_window_enable() { return 0; } + virtual bool oam_mainscreen_window_enable() { return 0; } + + //$212f + virtual bool bg1_subscreen_window_enable() { return 0; } + virtual bool bg2_subscreen_window_enable() { return 0; } + virtual bool bg3_subscreen_window_enable() { return 0; } + virtual bool bg4_subscreen_window_enable() { return 0; } + virtual bool oam_subscreen_window_enable() { return 0; } + + //$2130 + virtual unsigned color_mainscreen_window_mask() { return 0; } + virtual unsigned color_subscreen_window_mask() { return 0; } + virtual bool color_add_subtract_mode() { return 0; } + virtual bool direct_color() { return 0; } + + //$2131 + virtual bool color_mode() { return 0; } + virtual bool color_halve() { return 0; } + virtual bool bg1_color_enable() { return 0; } + virtual bool bg2_color_enable() { return 0; } + virtual bool bg3_color_enable() { return 0; } + virtual bool bg4_color_enable() { return 0; } + virtual bool oam_color_enable() { return 0; } + virtual bool back_color_enable() { return 0; } + + //$2132 + virtual unsigned color_constant_blue() { return 0; } + virtual unsigned color_constant_green() { return 0; } + virtual unsigned color_constant_red() { return 0; } + + //$2133 + virtual bool mode7_extbg() { return 0; } + virtual bool pseudo_hires() { return 0; } + virtual bool overscan() { return 0; } + virtual bool oam_interlace() { return 0; } + virtual bool interlace() { return 0; } + + //$213c + virtual unsigned hcounter() { return 0; } + + //$213d + virtual unsigned vcounter() { return 0; } + + //$213e + virtual bool range_over() { return 0; } + virtual bool time_over() { return 0; } + virtual unsigned ppu1_version() { return 0; } + + //$213f + virtual bool field() { return 0; } + virtual bool region() { return 0; } + virtual unsigned ppu2_version() { return 0; } +}; diff --git a/mednafen/snes/src/ppu/ppu-inline.hpp b/mednafen/snes/src/ppu/ppu-inline.hpp new file mode 100755 index 0000000..489b429 --- /dev/null +++ b/mednafen/snes/src/ppu/ppu-inline.hpp @@ -0,0 +1,85 @@ +//this should only be called by CPU::PPUcounter::tick(); +//keeps track of previous counter positions in history table +void PPUcounter::tick() { + status.hcounter += 2; //increment by smallest unit of time + if(status.hcounter >= 1360 && status.hcounter == lineclocks()) { + status.hcounter = 0; + vcounter_tick(); + } + + history.index = (history.index + 1) & 2047; + history.field [history.index] = status.field; + history.vcounter[history.index] = status.vcounter; + history.hcounter[history.index] = status.hcounter; +} + +//this should only be called by PPU::PPUcounter::tick(n); +//allows stepping by more than the smallest unit of time +void PPUcounter::tick(unsigned clocks) { + status.hcounter += clocks; + if(status.hcounter >= lineclocks()) { + status.hcounter -= lineclocks(); + vcounter_tick(); + } +} + +//internal +void PPUcounter::vcounter_tick() { + if(++status.vcounter == 128) status.interlace = ppu.interlace(); + + if((system.region() == System::NTSC && status.interlace == false && status.vcounter == 262) + || (system.region() == System::NTSC && status.interlace == true && status.vcounter == 263) + || (system.region() == System::NTSC && status.interlace == true && status.vcounter == 262 && status.field == 1) + || (system.region() == System::PAL && status.interlace == false && status.vcounter == 312) + || (system.region() == System::PAL && status.interlace == true && status.vcounter == 313) + || (system.region() == System::PAL && status.interlace == true && status.vcounter == 312 && status.field == 1) + ) { + status.vcounter = 0; + status.field = !status.field; + } + if(scanline) scanline(); +} + +bool PPUcounter::field () const { return status.field; } +uint16 PPUcounter::vcounter() const { return status.vcounter; } +uint16 PPUcounter::hcounter() const { return status.hcounter; } + +bool PPUcounter::field (unsigned offset) const { return history.field [(history.index - (offset >> 1)) & 2047]; } +uint16 PPUcounter::vcounter(unsigned offset) const { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } +uint16 PPUcounter::hcounter(unsigned offset) const { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } + +//one PPU dot = 4 CPU clocks +// +//PPU dots 323 and 327 are 6 CPU clocks long. +//this does not apply to NTSC non-interlace scanline 240 on odd fields. this is +//because the PPU skips one dot to alter the color burst phase of the video signal. +// +//dot 323 range = { 1292, 1294, 1296 } +//dot 327 range = { 1310, 1312, 1314 } + +uint16 PPUcounter::hdot() const { + if(system.region() == System::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) { + return (hcounter() >> 2); + } else { + return (hcounter() - ((hcounter() > 1292) << 1) - ((hcounter() > 1310) << 1)) >> 2; + } +} + +uint16 PPUcounter::lineclocks() const { + if(system.region() == System::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) return 1360; + return 1364; +} + +void PPUcounter::reset() { + status.interlace = false; + status.field = 0; + status.vcounter = 0; + status.hcounter = 0; + history.index = 0; + + for(unsigned i = 0; i < 2048; i++) { + history.field [i] = 0; + history.vcounter[i] = 0; + history.hcounter[i] = 0; + } +} diff --git a/mednafen/snes/src/ppu/ppu.cpp b/mednafen/snes/src/ppu/ppu.cpp new file mode 100755 index 0000000..c327601 --- /dev/null +++ b/mednafen/snes/src/ppu/ppu.cpp @@ -0,0 +1,56 @@ +#include <../base.hpp> + +#define PPU_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "ppu-debugger.cpp" +#endif + +#include "serialization.cpp" + +void PPU::enable_renderer(bool r) { status.render_output = r; } +bool PPU::renderer_enabled() { return status.render_output; } + +void PPU::frame() { + status.frame_executed = true; + + static int32 fr = 0, fe = 0; + static time_t prev, curr; + fe++; + if(status.render_output)fr++; + + time(&curr); + if(curr != prev) { + status.frames_updated = true; + status.frames_rendered = fr; + status.frames_executed = fe; + fr = fe = 0; + } + prev = curr; +} + +void PPU::power() { + ppu1_version = config.ppu1.version; + ppu2_version = config.ppu2.version; +} + +void PPU::reset() { + PPUcounter::reset(); + memset(output, 0, 512 * 480 * sizeof(uint16)); +} + +PPU::PPU() { + output = new uint16[512 * 480]; + + status.render_output = true; + status.frames_updated = false; + status.frames_rendered = 0; + status.frames_executed = 0; +} + +PPU::~PPU() { + delete[] output; +} + +}; diff --git a/mednafen/snes/src/ppu/ppu.hpp b/mednafen/snes/src/ppu/ppu.hpp new file mode 100755 index 0000000..c4470b9 --- /dev/null +++ b/mednafen/snes/src/ppu/ppu.hpp @@ -0,0 +1,94 @@ +#if defined(DEBUGGER) + #include "ppu-debugger.hpp" +#endif + +//PPUcounter emulates the H/V latch counters of the S-PPU2. +// +//real hardware has the S-CPU maintain its own copy of these counters that are +//updated based on the state of the S-PPU Vblank and Hblank pins. emulating this +//would require full lock-step synchronization for every clock tick. +//to bypass this and allow the two to run out-of-order, both the CPU and PPU +//classes inherit PPUcounter and keep their own counters. +//the timers are kept in sync, as the only differences occur on V=240 and V=261, +//based on interlace. thus, we need only synchronize and fetch interlace at any +//point before this in the frame, which is handled internally by this class at +//V=128. + +class PPUcounter { +public: + alwaysinline void tick(); + alwaysinline void tick(unsigned clocks); + + alwaysinline bool field () const; + alwaysinline uint16 vcounter() const; + alwaysinline uint16 hcounter() const; + inline uint16 hdot() const; + inline uint16 lineclocks() const; + + alwaysinline bool field (unsigned offset) const; + alwaysinline uint16 vcounter(unsigned offset) const; + alwaysinline uint16 hcounter(unsigned offset) const; + + inline void reset(); + function scanline; + void serialize(serializer&); + +private: + inline void vcounter_tick(); + + struct { + bool interlace; + bool field; + uint16 vcounter; + uint16 hcounter; + } status; + + struct { + bool field[2048]; + uint16 vcounter[2048]; + uint16 hcounter[2048]; + + int32 index; + } history; +}; + +class PPU : public PPUcounter, public MMIO { +public: + virtual void enter() = 0; + + uint16 *output; + + struct { + bool render_output; + bool frame_executed; + bool frames_updated; + unsigned frames_rendered; + unsigned frames_executed; + } status; + + //PPU1 version number + //* 1 is known + //* reported by $213e + uint8 ppu1_version; + + //PPU2 version number + //* 1 and 3 are known + //* reported by $213f + uint8 ppu2_version; + + virtual bool interlace() const = 0; + virtual bool overscan() const = 0; + virtual bool hires() const = 0; + + virtual void latch_counters() = 0; + + virtual void frame(); + virtual void power(); + virtual void reset(); + virtual void enable_renderer(bool r); + virtual bool renderer_enabled(); + + virtual void serialize(serializer&); + PPU(); + virtual ~PPU(); +}; diff --git a/mednafen/snes/src/ppu/serialization.cpp b/mednafen/snes/src/ppu/serialization.cpp new file mode 100755 index 0000000..2a5ef31 --- /dev/null +++ b/mednafen/snes/src/ppu/serialization.cpp @@ -0,0 +1,28 @@ +#ifdef PPU_CPP + +void PPUcounter::serialize(serializer &s) { + s.integer(status.interlace); + s.integer(status.field); + s.integer(status.vcounter); + s.integer(status.hcounter); + + s.array(history.field); + s.array(history.vcounter); + s.array(history.hcounter); + s.integer(history.index); +} + +void PPU::serialize(serializer &s) { + PPUcounter::serialize(s); + + s.integer(status.render_output); + s.integer(status.frame_executed); + s.integer(status.frames_updated); + s.integer(status.frames_rendered); + s.integer(status.frames_executed); + + s.integer(ppu1_version); + s.integer(ppu2_version); +} + +#endif diff --git a/mednafen/snes/src/smp/core/algorithms.cpp b/mednafen/snes/src/smp/core/algorithms.cpp new file mode 100755 index 0000000..30a2107 --- /dev/null +++ b/mednafen/snes/src/smp/core/algorithms.cpp @@ -0,0 +1,126 @@ +#ifdef SMPCORE_CPP + +uint8 SMPcore::op_adc(uint8 x, uint8 y) { + int r = x + y + regs.p.c; + regs.p.n = r & 0x80; + regs.p.v = ~(x ^ y) & (x ^ r) & 0x80; + regs.p.h = (x ^ y ^ r) & 0x10; + regs.p.z = (uint8)r == 0; + regs.p.c = r > 0xff; + return r; +} + +uint16 SMPcore::op_addw(uint16 x, uint16 y) { + uint16 r; + regs.p.c = 0; + r = op_adc(x, y); + r |= op_adc(x >> 8, y >> 8) << 8; + regs.p.z = r == 0; + return r; +} + +uint8 SMPcore::op_and(uint8 x, uint8 y) { + x &= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_cmp(uint8 x, uint8 y) { + int r = x - y; + regs.p.n = r & 0x80; + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; + return x; +} + +uint16 SMPcore::op_cmpw(uint16 x, uint16 y) { + int r = x - y; + regs.p.n = r & 0x8000; + regs.p.z = (uint16)r == 0; + regs.p.c = r >= 0; + return x; +} + +uint8 SMPcore::op_eor(uint8 x, uint8 y) { + x ^= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_or(uint8 x, uint8 y) { + x |= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_sbc(uint8 x, uint8 y) { + int r = x - y - !regs.p.c; + regs.p.n = r & 0x80; + regs.p.v = (x ^ y) & (x ^ r) & 0x80; + regs.p.h = !((x ^ y ^ r) & 0x10); + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; + return r; +} + +uint16 SMPcore::op_subw(uint16 x, uint16 y) { + uint16 r; + regs.p.c = 1; + r = op_sbc(x, y); + r |= op_sbc(x >> 8, y >> 8) << 8; + regs.p.z = r == 0; + return r; +} + +uint8 SMPcore::op_inc(uint8 x) { + x++; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_dec(uint8 x) { + x--; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_asl(uint8 x) { + regs.p.c = x & 0x80; + x <<= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_lsr(uint8 x) { + regs.p.c = x & 0x01; + x >>= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_rol(uint8 x) { + unsigned carry = (unsigned)regs.p.c; + regs.p.c = x & 0x80; + x = (x << 1) | carry; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_ror(uint8 x) { + unsigned carry = (unsigned)regs.p.c << 7; + regs.p.c = x & 0x01; + x = carry | (x >> 1); + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +#endif diff --git a/mednafen/snes/src/smp/core/core.cpp b/mednafen/snes/src/smp/core/core.cpp new file mode 100755 index 0000000..fe6b1f5 --- /dev/null +++ b/mednafen/snes/src/smp/core/core.cpp @@ -0,0 +1,31 @@ +#include <../base.hpp> + +#define SMPCORE_CPP +namespace SNES { + +#include "serialization.cpp" +#include "algorithms.cpp" +#include "disassembler/disassembler.cpp" + +#define A 0 +#define X 1 +#define Y 2 +#define SP 3 + +#include "opcode_mov.cpp" +#include "opcode_pc.cpp" +#include "opcode_read.cpp" +#include "opcode_rmw.cpp" +#include "opcode_misc.cpp" +#include "table.cpp" + +#undef A +#undef X +#undef Y +#undef SP + +SMPcore::SMPcore() { + initialize_opcode_table(); +} + +}; diff --git a/mednafen/snes/src/smp/core/core.hpp b/mednafen/snes/src/smp/core/core.hpp new file mode 100755 index 0000000..85e8c74 --- /dev/null +++ b/mednafen/snes/src/smp/core/core.hpp @@ -0,0 +1,118 @@ +class SMPcore { +public: + #include "registers.hpp" + #include "memory.hpp" + #include "disassembler/disassembler.hpp" + + regs_t regs; + uint16 dp, sp, rd, wr, bit, ya; + + virtual void op_io() = 0; + virtual uint8 op_read(uint16 addr) = 0; + virtual void op_write(uint16 addr, uint8 data) = 0; + + uint8 op_adc (uint8 x, uint8 y); + uint16 op_addw(uint16 x, uint16 y); + uint8 op_and (uint8 x, uint8 y); + uint8 op_cmp (uint8 x, uint8 y); + uint16 op_cmpw(uint16 x, uint16 y); + uint8 op_eor (uint8 x, uint8 y); + uint8 op_inc (uint8 x); + uint8 op_dec (uint8 x); + uint8 op_or (uint8 x, uint8 y); + uint8 op_sbc (uint8 x, uint8 y); + uint16 op_subw(uint16 x, uint16 y); + uint8 op_asl (uint8 x); + uint8 op_lsr (uint8 x); + uint8 op_rol (uint8 x); + uint8 op_ror (uint8 x); + + template void op_mov_reg_reg(); + void op_mov_sp_x(); + template void op_mov_reg_const(); + void op_mov_a_ix(); + void op_mov_a_ixinc(); + template void op_mov_reg_dp(); + template void op_mov_reg_dpr(); + template void op_mov_reg_addr(); + template void op_mov_a_addrr(); + void op_mov_a_idpx(); + void op_mov_a_idpy(); + void op_mov_dp_dp(); + void op_mov_dp_const(); + void op_mov_ix_a(); + void op_mov_ixinc_a(); + template void op_mov_dp_reg(); + template void op_mov_dpr_reg(); + template void op_mov_addr_reg(); + template void op_mov_addrr_a(); + void op_mov_idpx_a(); + void op_mov_idpy_a(); + void op_movw_ya_dp(); + void op_movw_dp_ya(); + void op_mov1_c_bit(); + void op_mov1_bit_c(); + + void op_bra(); + template void op_branch(); + template void op_bitbranch(); + void op_cbne_dp(); + void op_cbne_dpx(); + void op_dbnz_dp(); + void op_dbnz_y(); + void op_jmp_addr(); + void op_jmp_iaddrx(); + void op_call(); + void op_pcall(); + template void op_tcall(); + void op_brk(); + void op_ret(); + void op_reti(); + + template void op_read_reg_const(); + template void op_read_a_ix(); + template void op_read_reg_dp(); + template void op_read_a_dpx(); + template void op_read_reg_addr(); + template void op_read_a_addrr(); + template void op_read_a_idpx(); + template void op_read_a_idpy(); + template void op_read_ix_iy(); + template void op_read_dp_dp(); + template void op_read_dp_const(); + template void op_read_ya_dp(); + void op_cmpw_ya_dp(); + template void op_and1_bit(); + void op_eor1_bit(); + void op_not1_bit(); + template void op_or1_bit(); + + template void op_adjust_reg(); + template void op_adjust_dp(); + template void op_adjust_dpx(); + template void op_adjust_addr(); + template void op_adjust_addr_a(); + template void op_adjustw_dp(); + + void op_nop(); + void op_wait(); + void op_xcn(); + void op_daa(); + void op_das(); + template void op_setbit(); + void op_notc(); + template void op_seti(); + template void op_setbit_dp(); + template void op_push_reg(); + void op_push_p(); + template void op_pop_reg(); + void op_pop_p(); + void op_mul_ya(); + void op_div_ya_x(); + + void (SMPcore::*opcode_table[256])(); + void initialize_opcode_table(); + + void core_serialize(serializer&); + SMPcore(); +}; diff --git a/mednafen/snes/src/smp/core/disassembler/disassembler.cpp b/mednafen/snes/src/smp/core/disassembler/disassembler.cpp new file mode 100755 index 0000000..f636144 --- /dev/null +++ b/mednafen/snes/src/smp/core/disassembler/disassembler.cpp @@ -0,0 +1,304 @@ +#ifdef SMPCORE_CPP + +uint16 SMPcore::__relb(int8 offset, int op_len) { + uint16 pc = regs.pc + op_len; + return pc + offset; +} + +void SMPcore::disassemble_opcode(char *output, uint16 addr) { + char *s, t[512]; + uint8 op, op0, op1; + uint16 opw, opdp0, opdp1; + s = output; + + sprintf(s, "..%.4x ", addr); + + //TODO: read from IPLROM when applicable + op = memory::apuram[(uint16)(addr + 0)]; + op0 = memory::apuram[(uint16)(addr + 1)]; + op1 = memory::apuram[(uint16)(addr + 2)]; + opw = (op0) | (op1 << 8); + opdp0 = ((unsigned)regs.p.p << 8) + op0; + opdp1 = ((unsigned)regs.p.p << 8) + op1; + + strcpy(t, " "); + + switch(op) { + case 0x00: sprintf(t, "nop"); break; + case 0x01: sprintf(t, "tcall 0"); break; + case 0x02: sprintf(t, "set0 $%.3x", opdp0); break; + case 0x03: sprintf(t, "bbs0 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x04: sprintf(t, "or a,$%.3x", opdp0); break; + case 0x05: sprintf(t, "or a,$%.4x", opw); break; + case 0x06: sprintf(t, "or a,(x)"); break; + case 0x07: sprintf(t, "or a,($%.3x+x)", opdp0); break; + case 0x08: sprintf(t, "or a,#$%.2x", op0); break; + case 0x09: sprintf(t, "or $%.3x,$%.3x", opdp1, opdp0); break; + case 0x0a: sprintf(t, "or1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0x0b: sprintf(t, "asl $%.3x", opdp0); break; + case 0x0c: sprintf(t, "asl $%.4x", opw); break; + case 0x0d: sprintf(t, "push p"); break; + case 0x0e: sprintf(t, "tset $%.4x,a", opw); break; + case 0x0f: sprintf(t, "brk"); break; + case 0x10: sprintf(t, "bpl $%.4x", __relb(op0, 2)); break; + case 0x11: sprintf(t, "tcall 1"); break; + case 0x12: sprintf(t, "clr0 $%.3x", opdp0); break; + case 0x13: sprintf(t, "bbc0 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x14: sprintf(t, "or a,$%.3x+x", opdp0); break; + case 0x15: sprintf(t, "or a,$%.4x+x", opw); break; + case 0x16: sprintf(t, "or a,$%.4x+y", opw); break; + case 0x17: sprintf(t, "or a,($%.3x)+y", opdp0); break; + case 0x18: sprintf(t, "or $%.3x,#$%.2x", opdp1, op0); break; + case 0x19: sprintf(t, "or (x),(y)"); break; + case 0x1a: sprintf(t, "decw $%.3x", opdp0); break; + case 0x1b: sprintf(t, "asl $%.3x+x", opdp0); break; + case 0x1c: sprintf(t, "asl a"); break; + case 0x1d: sprintf(t, "dec x"); break; + case 0x1e: sprintf(t, "cmp x,$%.4x", opw); break; + case 0x1f: sprintf(t, "jmp ($%.4x+x)", opw); break; + case 0x20: sprintf(t, "clrp"); break; + case 0x21: sprintf(t, "tcall 2"); break; + case 0x22: sprintf(t, "set1 $%.3x", opdp0); break; + case 0x23: sprintf(t, "bbs1 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x24: sprintf(t, "and a,$%.3x", opdp0); break; + case 0x25: sprintf(t, "and a,$%.4x", opw); break; + case 0x26: sprintf(t, "and a,(x)"); break; + case 0x27: sprintf(t, "and a,($%.3x+x)", opdp0); break; + case 0x28: sprintf(t, "and a,#$%.2x", op0); break; + case 0x29: sprintf(t, "and $%.3x,$%.3x", opdp1, opdp0); break; + case 0x2a: sprintf(t, "or1 c,!$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0x2b: sprintf(t, "rol $%.3x", opdp0); break; + case 0x2c: sprintf(t, "rol $%.4x", opw); break; + case 0x2d: sprintf(t, "push a"); break; + case 0x2e: sprintf(t, "cbne $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x2f: sprintf(t, "bra $%.4x", __relb(op0, 2)); break; + case 0x30: sprintf(t, "bmi $%.4x", __relb(op0, 2)); break; + case 0x31: sprintf(t, "tcall 3"); break; + case 0x32: sprintf(t, "clr1 $%.3x", opdp0); break; + case 0x33: sprintf(t, "bbc1 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x34: sprintf(t, "and a,$%.3x+x", opdp0); break; + case 0x35: sprintf(t, "and a,$%.4x+x", opw); break; + case 0x36: sprintf(t, "and a,$%.4x+y", opw); break; + case 0x37: sprintf(t, "and a,($%.3x)+y", opdp0); break; + case 0x38: sprintf(t, "and $%.3x,#$%.2x", opdp1, op0); break; + case 0x39: sprintf(t, "and (x),(y)"); break; + case 0x3a: sprintf(t, "incw $%.3x", opdp0); break; + case 0x3b: sprintf(t, "rol $%.3x+x", opdp0); break; + case 0x3c: sprintf(t, "rol a"); break; + case 0x3d: sprintf(t, "inc x"); break; + case 0x3e: sprintf(t, "cmp x,$%.3x", opdp0); break; + case 0x3f: sprintf(t, "call $%.4x", opw); break; + case 0x40: sprintf(t, "setp"); break; + case 0x41: sprintf(t, "tcall 4"); break; + case 0x42: sprintf(t, "set2 $%.3x", opdp0); break; + case 0x43: sprintf(t, "bbs2 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x44: sprintf(t, "eor a,$%.3x", opdp0); break; + case 0x45: sprintf(t, "eor a,$%.4x", opw); break; + case 0x46: sprintf(t, "eor a,(x)"); break; + case 0x47: sprintf(t, "eor a,($%.3x+x)", opdp0); break; + case 0x48: sprintf(t, "eor a,#$%.2x", op0); break; + case 0x49: sprintf(t, "eor $%.3x,$%.3x", opdp1, opdp0); break; + case 0x4a: sprintf(t, "and1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0x4b: sprintf(t, "lsr $%.3x", opdp0); break; + case 0x4c: sprintf(t, "lsr $%.4x", opw); break; + case 0x4d: sprintf(t, "push x"); break; + case 0x4e: sprintf(t, "tclr $%.4x,a", opw); break; + case 0x4f: sprintf(t, "pcall $ff%.2x", op0); break; + case 0x50: sprintf(t, "bvc $%.4x", __relb(op0, 2)); break; + case 0x51: sprintf(t, "tcall 5"); break; + case 0x52: sprintf(t, "clr2 $%.3x", opdp0); break; + case 0x53: sprintf(t, "bbc2 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x54: sprintf(t, "eor a,$%.3x+x", opdp0); break; + case 0x55: sprintf(t, "eor a,$%.4x+x", opw); break; + case 0x56: sprintf(t, "eor a,$%.4x+y", opw); break; + case 0x57: sprintf(t, "eor a,($%.3x)+y", opdp0); break; + case 0x58: sprintf(t, "eor $%.3x,#$%.2x", opdp1, op0); break; + case 0x59: sprintf(t, "eor (x),(y)"); break; + case 0x5a: sprintf(t, "cmpw ya,$%.3x", opdp0); break; + case 0x5b: sprintf(t, "lsr $%.3x+x", opdp0); break; + case 0x5c: sprintf(t, "lsr a"); break; + case 0x5d: sprintf(t, "mov x,a"); break; + case 0x5e: sprintf(t, "cmp y,$%.4x", opw); break; + case 0x5f: sprintf(t, "jmp $%.4x", opw); break; + case 0x60: sprintf(t, "clrc"); break; + case 0x61: sprintf(t, "tcall 6"); break; + case 0x62: sprintf(t, "set3 $%.3x", opdp0); break; + case 0x63: sprintf(t, "bbs3 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x64: sprintf(t, "cmp a,$%.3x", opdp0); break; + case 0x65: sprintf(t, "cmp a,$%.4x", opw); break; + case 0x66: sprintf(t, "cmp a,(x)"); break; + case 0x67: sprintf(t, "cmp a,($%.3x+x)", opdp0); break; + case 0x68: sprintf(t, "cmp a,#$%.2x", op0); break; + case 0x69: sprintf(t, "cmp $%.3x,$%.3x", opdp1, opdp0); break; + case 0x6a: sprintf(t, "and1 c,!$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0x6b: sprintf(t, "ror $%.3x", opdp0); break; + case 0x6c: sprintf(t, "ror $%.4x", opw); break; + case 0x6d: sprintf(t, "push y"); break; + case 0x6e: sprintf(t, "dbnz $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x6f: sprintf(t, "ret"); break; + case 0x70: sprintf(t, "bvs $%.4x", __relb(op0, 2)); break; + case 0x71: sprintf(t, "tcall 7"); break; + case 0x72: sprintf(t, "clr3 $%.3x", opdp0); break; + case 0x73: sprintf(t, "bbc3 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x74: sprintf(t, "cmp a,$%.3x+x", opdp0); break; + case 0x75: sprintf(t, "cmp a,$%.4x+x", opw); break; + case 0x76: sprintf(t, "cmp a,$%.4x+y", opw); break; + case 0x77: sprintf(t, "cmp a,($%.3x)+y", opdp0); break; + case 0x78: sprintf(t, "cmp $%.3x,#$%.2x", opdp1, op0); break; + case 0x79: sprintf(t, "cmp (x),(y)"); break; + case 0x7a: sprintf(t, "addw ya,$%.3x", opdp0); break; + case 0x7b: sprintf(t, "ror $%.3x+x", opdp0); break; + case 0x7c: sprintf(t, "ror a"); break; + case 0x7d: sprintf(t, "mov a,x"); break; + case 0x7e: sprintf(t, "cmp y,$%.3x", opdp0); break; + case 0x7f: sprintf(t, "reti"); break; + case 0x80: sprintf(t, "setc"); break; + case 0x81: sprintf(t, "tcall 8"); break; + case 0x82: sprintf(t, "set4 $%.3x", opdp0); break; + case 0x83: sprintf(t, "bbs4 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x84: sprintf(t, "adc a,$%.3x", opdp0); break; + case 0x85: sprintf(t, "adc a,$%.4x", opw); break; + case 0x86: sprintf(t, "adc a,(x)"); break; + case 0x87: sprintf(t, "adc a,($%.3x+x)", opdp0); break; + case 0x88: sprintf(t, "adc a,#$%.2x", op0); break; + case 0x89: sprintf(t, "adc $%.3x,$%.3x", opdp1, opdp0); break; + case 0x8a: sprintf(t, "eor1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0x8b: sprintf(t, "dec $%.3x", opdp0); break; + case 0x8c: sprintf(t, "dec $%.4x", opw); break; + case 0x8d: sprintf(t, "mov y,#$%.2x", op0); break; + case 0x8e: sprintf(t, "pop p"); break; + case 0x8f: sprintf(t, "mov $%.3x,#$%.2x", opdp1, op0); break; + case 0x90: sprintf(t, "bcc $%.4x", __relb(op0, 2)); break; + case 0x91: sprintf(t, "tcall 9"); break; + case 0x92: sprintf(t, "clr4 $%.3x", opdp0); break; + case 0x93: sprintf(t, "bbc4 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0x94: sprintf(t, "adc a,$%.3x+x", opdp0); break; + case 0x95: sprintf(t, "adc a,$%.4x+x", opw); break; + case 0x96: sprintf(t, "adc a,$%.4x+y", opw); break; + case 0x97: sprintf(t, "adc a,($%.3x)+y", opdp0); break; + case 0x98: sprintf(t, "adc $%.3x,#$%.2x", opdp1, op0); break; + case 0x99: sprintf(t, "adc (x),(y)"); break; + case 0x9a: sprintf(t, "subw ya,$%.3x", opdp0); break; + case 0x9b: sprintf(t, "dec $%.3x+x", opdp0); break; + case 0x9c: sprintf(t, "dec a"); break; + case 0x9d: sprintf(t, "mov x,sp"); break; + case 0x9e: sprintf(t, "div ya,x"); break; + case 0x9f: sprintf(t, "xcn a"); break; + case 0xa0: sprintf(t, "ei"); break; + case 0xa1: sprintf(t, "tcall 10"); break; + case 0xa2: sprintf(t, "set5 $%.3x", opdp0); break; + case 0xa3: sprintf(t, "bbs5 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0xa4: sprintf(t, "sbc a,$%.3x", opdp0); break; + case 0xa5: sprintf(t, "sbc a,$%.4x", opw); break; + case 0xa6: sprintf(t, "sbc a,(x)"); break; + case 0xa7: sprintf(t, "sbc a,($%.3x+x)", opdp0); break; + case 0xa8: sprintf(t, "sbc a,#$%.2x", op0); break; + case 0xa9: sprintf(t, "sbc $%.3x,$%.3x", opdp1, opdp0); break; + case 0xaa: sprintf(t, "mov1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0xab: sprintf(t, "inc $%.3x", opdp0); break; + case 0xac: sprintf(t, "inc $%.4x", opw); break; + case 0xad: sprintf(t, "cmp y,#$%.2x", op0); break; + case 0xae: sprintf(t, "pop a"); break; + case 0xaf: sprintf(t, "mov (x)+,a"); break; + case 0xb0: sprintf(t, "bcs $%.4x", __relb(op0, 2)); break; + case 0xb1: sprintf(t, "tcall 11"); break; + case 0xb2: sprintf(t, "clr5 $%.3x", opdp0); break; + case 0xb3: sprintf(t, "bbc5 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0xb4: sprintf(t, "sbc a,$%.3x+x", opdp0); break; + case 0xb5: sprintf(t, "sbc a,$%.4x+x", opw); break; + case 0xb6: sprintf(t, "sbc a,$%.4x+y", opw); break; + case 0xb7: sprintf(t, "sbc a,($%.3x)+y", opdp0); break; + case 0xb8: sprintf(t, "sbc $%.3x,#$%.2x", opdp1, op0); break; + case 0xb9: sprintf(t, "sbc (x),(y)"); break; + case 0xba: sprintf(t, "movw ya,$%.3x", opdp0); break; + case 0xbb: sprintf(t, "inc $%.3x+x", opdp0); break; + case 0xbc: sprintf(t, "inc a"); break; + case 0xbd: sprintf(t, "mov sp,x"); break; + case 0xbe: sprintf(t, "das a"); break; + case 0xbf: sprintf(t, "mov a,(x)+"); break; + case 0xc0: sprintf(t, "di"); break; + case 0xc1: sprintf(t, "tcall 12"); break; + case 0xc2: sprintf(t, "set6 $%.3x", opdp0); break; + case 0xc3: sprintf(t, "bbs6 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0xc4: sprintf(t, "mov $%.3x,a", opdp0); break; + case 0xc5: sprintf(t, "mov $%.4x,a", opw); break; + case 0xc6: sprintf(t, "mov (x),a"); break; + case 0xc7: sprintf(t, "mov ($%.3x+x),a", opdp0); break; + case 0xc8: sprintf(t, "cmp x,#$%.2x", op0); break; + case 0xc9: sprintf(t, "mov $%.4x,x", opw); break; + case 0xca: sprintf(t, "mov1 $%.4x:%d,c", opw & 0x1fff, opw >> 13); break; + case 0xcb: sprintf(t, "mov $%.3x,y", opdp0); break; + case 0xcc: sprintf(t, "mov $%.4x,y", opw); break; + case 0xcd: sprintf(t, "mov x,#$%.2x", op0); break; + case 0xce: sprintf(t, "pop x"); break; + case 0xcf: sprintf(t, "mul ya"); break; + case 0xd0: sprintf(t, "bne $%.4x", __relb(op0, 2)); break; + case 0xd1: sprintf(t, "tcall 13"); break; + case 0xd2: sprintf(t, "clr6 $%.3x", opdp0); break; + case 0xd3: sprintf(t, "bbc6 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0xd4: sprintf(t, "mov $%.3x+x,a", opdp0); break; + case 0xd5: sprintf(t, "mov $%.4x+x,a", opw); break; + case 0xd6: sprintf(t, "mov $%.4x+y,a", opw); break; + case 0xd7: sprintf(t, "mov ($%.3x)+y,a", opdp0); break; + case 0xd8: sprintf(t, "mov $%.3x,x", opdp0); break; + case 0xd9: sprintf(t, "mov $%.3x+y,x", opdp0); break; + case 0xda: sprintf(t, "movw $%.3x,ya", opdp0); break; + case 0xdb: sprintf(t, "mov $%.3x+x,y", opdp0); break; + case 0xdc: sprintf(t, "dec y"); break; + case 0xdd: sprintf(t, "mov a,y"); break; + case 0xde: sprintf(t, "cbne $%.3x+x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0xdf: sprintf(t, "daa a"); break; + case 0xe0: sprintf(t, "clrv"); break; + case 0xe1: sprintf(t, "tcall 14"); break; + case 0xe2: sprintf(t, "set7 $%.3x", opdp0); break; + case 0xe3: sprintf(t, "bbs7 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0xe4: sprintf(t, "mov a,$%.3x", opdp0); break; + case 0xe5: sprintf(t, "mov a,$%.4x", opw); break; + case 0xe6: sprintf(t, "mov a,(x)"); break; + case 0xe7: sprintf(t, "mov a,($%.3x+x)", opdp0); break; + case 0xe8: sprintf(t, "mov a,#$%.2x", op0); break; + case 0xe9: sprintf(t, "mov x,$%.4x", opw); break; + case 0xea: sprintf(t, "not1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0xeb: sprintf(t, "mov y,$%.3x", opdp0); break; + case 0xec: sprintf(t, "mov y,$%.4x", opw); break; + case 0xed: sprintf(t, "notc"); break; + case 0xee: sprintf(t, "pop y"); break; + case 0xef: sprintf(t, "sleep"); break; + case 0xf0: sprintf(t, "beq $%.4x", __relb(op0, 2)); break; + case 0xf1: sprintf(t, "tcall 15"); break; + case 0xf2: sprintf(t, "clr7 $%.3x", opdp0); break; + case 0xf3: sprintf(t, "bbc7 $%.3x,$%.4x", opdp0, __relb(op1, 3)); break; + case 0xf4: sprintf(t, "mov a,$%.3x+x", opdp0); break; + case 0xf5: sprintf(t, "mov a,$%.4x+x", opw); break; + case 0xf6: sprintf(t, "mov a,$%.4x+y", opw); break; + case 0xf7: sprintf(t, "mov a,($%.3x)+y", opdp0); break; + case 0xf8: sprintf(t, "mov x,$%.3x", opdp0); break; + case 0xf9: sprintf(t, "mov x,$%.3x+y", opdp0); break; + case 0xfa: sprintf(t, "mov $%.3x,$%.3x", opdp1, opdp0); break; + case 0xfb: sprintf(t, "mov y,$%.3x+x", opdp0); break; + case 0xfc: sprintf(t, "inc y"); break; + case 0xfd: sprintf(t, "mov y,a"); break; + case 0xfe: sprintf(t, "dbnz y,$%.4x", __relb(op0, 2)); break; + case 0xff: sprintf(t, "stop"); break; + } + + t[strlen(t)] = ' '; + strcat(s, t); + + sprintf(t, "A:%.2x X:%.2x Y:%.2x SP:01%.2x YA:%.4x ", + regs.a, regs.x, regs.y, regs.sp, (uint16)regs.ya); + strcat(s, t); + + sprintf(t, "%c%c%c%c%c%c%c%c", + regs.p.n ? 'N' : 'n', + regs.p.v ? 'V' : 'v', + regs.p.p ? 'P' : 'p', + regs.p.b ? 'B' : 'b', + regs.p.h ? 'H' : 'h', + regs.p.i ? 'I' : 'i', + regs.p.z ? 'Z' : 'z', + regs.p.c ? 'C' : 'c'); + strcat(s, t); +} + +#endif diff --git a/mednafen/snes/src/smp/core/disassembler/disassembler.hpp b/mednafen/snes/src/smp/core/disassembler/disassembler.hpp new file mode 100755 index 0000000..abb8982 --- /dev/null +++ b/mednafen/snes/src/smp/core/disassembler/disassembler.hpp @@ -0,0 +1,2 @@ +void disassemble_opcode(char *output, uint16 addr); +inline uint16 __relb(int8 offset, int op_len); diff --git a/mednafen/snes/src/smp/core/memory.hpp b/mednafen/snes/src/smp/core/memory.hpp new file mode 100755 index 0000000..9af3295 --- /dev/null +++ b/mednafen/snes/src/smp/core/memory.hpp @@ -0,0 +1,27 @@ +alwaysinline uint8_t op_readpc() { + return op_read(regs.pc++); +} + +alwaysinline uint8_t op_readstack() { + return op_read(0x0100 | ++regs.sp); +} + +alwaysinline void op_writestack(uint8_t data) { + op_write(0x0100 | regs.sp--, data); +} + +alwaysinline uint8_t op_readaddr(uint16_t addr) { + return op_read(addr); +} + +alwaysinline void op_writeaddr(uint16_t addr, uint8_t data) { + op_write(addr, data); +} + +alwaysinline uint8_t op_readdp(uint8_t addr) { + return op_read(((unsigned)regs.p.p << 8) + addr); +} + +alwaysinline void op_writedp(uint8_t addr, uint8_t data) { + op_write(((unsigned)regs.p.p << 8) + addr, data); +} diff --git a/mednafen/snes/src/smp/core/opcode_misc.cpp b/mednafen/snes/src/smp/core/opcode_misc.cpp new file mode 100755 index 0000000..0f4d4ce --- /dev/null +++ b/mednafen/snes/src/smp/core/opcode_misc.cpp @@ -0,0 +1,148 @@ +#ifdef SMPCORE_CPP + +void SMPcore::op_nop() { + op_io(); +} + +void SMPcore::op_wait() { + while(true) { + op_io(); + op_io(); + } +} + +void SMPcore::op_xcn() { + op_io(); + op_io(); + op_io(); + op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_daa() { + op_io(); + op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_das() { + op_io(); + op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +template void SMPcore::op_setbit() { + op_io(); + regs.p = (regs.p & ~mask) | value; +} + +void SMPcore::op_notc() { + op_io(); + op_io(); + regs.p.c = !regs.p.c; +} + +template void SMPcore::op_seti() { + op_io(); + op_io(); + regs.p.i = value; +} + +template void SMPcore::op_setbit_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = (op ? rd | value : rd & ~value); + op_writedp(dp, rd); +} + +template void SMPcore::op_push_reg() { + op_io(); + op_io(); + op_writestack(regs.r[n]); +} + +void SMPcore::op_push_p() { + op_io(); + op_io(); + op_writestack(regs.p); +} + +template void SMPcore::op_pop_reg() { + op_io(); + op_io(); + regs.r[n] = op_readstack(); +} + +void SMPcore::op_pop_p() { + op_io(); + op_io(); + regs.p = op_readstack(); +} + +void SMPcore::op_mul_ya() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; + //result is set based on y (high-byte) only + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); +} + +void SMPcore::op_div_ya_x() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.ya; + //overflow set if quotient >= 256 + regs.p.v = !!(regs.y >= regs.x); + regs.p.h = !!((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } + //result is set based on a (quotient) only + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +#endif diff --git a/mednafen/snes/src/smp/core/opcode_mov.cpp b/mednafen/snes/src/smp/core/opcode_mov.cpp new file mode 100755 index 0000000..e21593c --- /dev/null +++ b/mednafen/snes/src/smp/core/opcode_mov.cpp @@ -0,0 +1,200 @@ +#ifdef SMPCORE_CPP + +template void SMPcore::op_mov_reg_reg() { + op_io(); + regs.r[to] = regs.r[from]; + regs.p.n = (regs.r[to] & 0x80); + regs.p.z = (regs.r[to] == 0); +} + +void SMPcore::op_mov_sp_x() { + op_io(); + regs.sp = regs.x; +} + +template void SMPcore::op_mov_reg_const() { + regs.r[n] = op_readpc(); + regs.p.n = (regs.r[n] & 0x80); + regs.p.z = (regs.r[n] == 0); +} + +void SMPcore::op_mov_a_ix() { + op_io(); + regs.a = op_readdp(regs.x); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_mov_a_ixinc() { + op_io(); + regs.a = op_readdp(regs.x++); + op_io(); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +template void SMPcore::op_mov_reg_dp() { + sp = op_readpc(); + regs.r[n] = op_readdp(sp); + regs.p.n = (regs.r[n] & 0x80); + regs.p.z = (regs.r[n] == 0); +} + +template void SMPcore::op_mov_reg_dpr() { + sp = op_readpc(); + op_io(); + regs.r[n] = op_readdp(sp + regs.r[i]); + regs.p.n = (regs.r[n] & 0x80); + regs.p.z = (regs.r[n] == 0); +} + +template void SMPcore::op_mov_reg_addr() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + regs.r[n] = op_readaddr(sp); + regs.p.n = (regs.r[n] & 0x80); + regs.p.z = (regs.r[n] == 0); +} + +template void SMPcore::op_mov_a_addrr() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + op_io(); + regs.a = op_readaddr(sp + regs.r[i]); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_mov_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + regs.a = op_readaddr(sp); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_mov_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + regs.a = op_readaddr(sp + regs.y); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_mov_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + op_writedp(dp, rd); +} + +void SMPcore::op_mov_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, rd); +} + +void SMPcore::op_mov_ix_a() { + op_io(); + op_readdp(regs.x); + op_writedp(regs.x, regs.a); +} + +void SMPcore::op_mov_ixinc_a() { + op_io(); + op_io(); + op_writedp(regs.x++, regs.a); +} + +template void SMPcore::op_mov_dp_reg() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.r[n]); +} + +template void SMPcore::op_mov_dpr_reg() { + dp = op_readpc(); + op_io(); + dp += regs.r[i]; + op_readdp(dp); + op_writedp(dp, regs.r[n]); +} + +template void SMPcore::op_mov_addr_reg() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.r[n]); +} + +template void SMPcore::op_mov_addrr_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + dp += regs.r[i]; + op_readaddr(dp); + op_writeaddr(dp, regs.a); +} + +void SMPcore::op_mov_idpx_a() { + sp = op_readpc(); + op_io(); + sp += regs.x; + dp = op_readdp(sp + 0) << 0; + dp |= op_readdp(sp + 1) << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.a); +} + +void SMPcore::op_mov_idpy_a() { + sp = op_readpc(); + dp = op_readdp(sp + 0) << 0; + dp |= op_readdp(sp + 1) << 8; + op_io(); + dp += regs.y; + op_readaddr(dp); + op_writeaddr(dp, regs.a); +} + +void SMPcore::op_movw_ya_dp() { + sp = op_readpc(); + regs.a = op_readdp(sp + 0); + op_io(); + regs.y = op_readdp(sp + 1); + regs.p.n = (regs.ya & 0x8000); + regs.p.z = (regs.ya == 0); +} + +void SMPcore::op_movw_dp_ya() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp + 0, regs.a); + op_writedp(dp + 1, regs.y); +} + +void SMPcore::op_mov1_c_bit() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + bit = sp >> 13; + sp &= 0x1fff; + rd = op_readaddr(sp); + regs.p.c = (rd & (1 << bit)); +} + +void SMPcore::op_mov1_bit_c() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + (regs.p.c) ? rd |= (1 << bit) : rd &= ~(1 << bit); + op_io(); + op_writeaddr(dp, rd); +} + +#endif diff --git a/mednafen/snes/src/smp/core/opcode_pc.cpp b/mednafen/snes/src/smp/core/opcode_pc.cpp new file mode 100755 index 0000000..b69d841 --- /dev/null +++ b/mednafen/snes/src/smp/core/opcode_pc.cpp @@ -0,0 +1,152 @@ +#ifdef SMPCORE_CPP + +void SMPcore::op_bra() { + rd = op_readpc(); + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +template void SMPcore::op_branch() { + rd = op_readpc(); + if((bool)(regs.p & flag) != value) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +template void SMPcore::op_bitbranch() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((bool)(sp & mask) != value) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_cbne_dp() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_cbne_dpx() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + regs.x); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_dbnz_dp() { + dp = op_readpc(); + wr = op_readdp(dp); + op_writedp(dp, --wr); + rd = op_readpc(); + if(wr == 0) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_dbnz_y() { + rd = op_readpc(); + op_io(); + regs.y--; + op_io(); + if(regs.y == 0) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_jmp_addr() { + rd = op_readpc() << 0; + rd |= op_readpc() << 8; + regs.pc = rd; +} + +void SMPcore::op_jmp_iaddrx() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + dp += regs.x; + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + regs.pc = rd; +} + +void SMPcore::op_call() { + rd = op_readpc() << 0; + rd |= op_readpc() << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; +} + +void SMPcore::op_pcall() { + rd = op_readpc(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = 0xff00 | rd; +} + +template void SMPcore::op_tcall() { + dp = 0xffde - (n << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; +} + +void SMPcore::op_brk() { + rd = op_readaddr(0xffde) << 0; + rd |= op_readaddr(0xffdf) << 8; + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + op_writestack(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; +} + +void SMPcore::op_ret() { + rd = op_readstack() << 0; + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; +} + +void SMPcore::op_reti() { + regs.p = op_readstack(); + rd = op_readstack() << 0; + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; +} + +#endif diff --git a/mednafen/snes/src/smp/core/opcode_read.cpp b/mednafen/snes/src/smp/core/opcode_read.cpp new file mode 100755 index 0000000..2058cff --- /dev/null +++ b/mednafen/snes/src/smp/core/opcode_read.cpp @@ -0,0 +1,154 @@ +#ifdef SMPCORE_CPP + +template +void SMPcore::op_read_reg_const() { + rd = op_readpc(); + regs.r[n] = (this->*op)(regs.r[n], rd); +} + +template +void SMPcore::op_read_a_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = (this->*op)(regs.a, rd); +} + +template +void SMPcore::op_read_reg_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.r[n] = (this->*op)(regs.r[n], rd); +} + +template +void SMPcore::op_read_a_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = (this->*op)(regs.a, rd); +} + +template +void SMPcore::op_read_reg_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.r[n] = (this->*op)(regs.r[n], rd); +} + +template +void SMPcore::op_read_a_addrr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.r[i]); + regs.a = (this->*op)(regs.a, rd); +} + +template +void SMPcore::op_read_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = (this->*op)(regs.a, rd); +} + +template +void SMPcore::op_read_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = (this->*op)(regs.a, rd); +} + +template +void SMPcore::op_read_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = (this->*op)(wr, rd); + static uint8 (SMPcore::*cmp)(uint8, uint8) = &SMPcore::op_cmp; + (op != cmp) ? op_writedp(regs.x, wr) : op_io(); +} + +template +void SMPcore::op_read_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = (this->*op)(wr, rd); + static uint8 (SMPcore::*cmp)(uint8, uint8) = &SMPcore::op_cmp; + (op != cmp) ? op_writedp(dp, wr) : op_io(); +} + +template +void SMPcore::op_read_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = (this->*op)(wr, rd); + static uint8 (SMPcore::*cmp)(uint8, uint8) = &SMPcore::op_cmp; + (op != cmp) ? op_writedp(dp, wr) : op_io(); +} + +template +void SMPcore::op_read_ya_dp() { + dp = op_readpc(); + rd = op_readdp(dp + 0) << 0; + op_io(); + rd |= op_readdp(dp + 1) << 8; + regs.ya = (this->*op)(regs.ya, rd); +} + +void SMPcore::op_cmpw_ya_dp() { + dp = op_readpc(); + rd = op_readdp(dp + 0) << 0; + rd |= op_readdp(dp + 1) << 8; + op_cmpw(regs.ya, rd); +} + +template void SMPcore::op_and1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & ((bool)(rd & (1 << bit)) ^ op); +} + +void SMPcore::op_eor1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c ^ (bool)(rd & (1 << bit)); +} + +void SMPcore::op_not1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + rd ^= 1 << bit; + op_writeaddr(dp, rd); +} + +template void SMPcore::op_or1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c | ((bool)(rd & (1 << bit)) ^ op); +} + +#endif diff --git a/mednafen/snes/src/smp/core/opcode_rmw.cpp b/mednafen/snes/src/smp/core/opcode_rmw.cpp new file mode 100755 index 0000000..103bd2d --- /dev/null +++ b/mednafen/snes/src/smp/core/opcode_rmw.cpp @@ -0,0 +1,58 @@ +#ifdef SMPCORE_CPP + +template +void SMPcore::op_adjust_reg() { + op_io(); + regs.r[n] = (this->*op)(regs.r[n]); +} + +template +void SMPcore::op_adjust_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = (this->*op)(rd); + op_writedp(dp, rd); +} + +template +void SMPcore::op_adjust_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = (this->*op)(rd); + op_writedp(dp + regs.x, rd); +} + +template +void SMPcore::op_adjust_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = (this->*op)(rd); + op_writeaddr(dp, rd); +} + +template +void SMPcore::op_adjust_addr_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.p.n = ((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + op_readaddr(dp); + op_writeaddr(dp, (op ? rd | regs.a : rd & ~regs.a)); +} + +template +void SMPcore::op_adjustw_dp() { + dp = op_readpc(); + rd = op_readdp(dp) << 0; + rd += adjust; + op_writedp(dp++, rd); + rd += op_readdp(dp) << 8; + op_writedp(dp, rd >> 8); + regs.p.n = (rd & 0x8000); + regs.p.z = (rd == 0); +} + +#endif diff --git a/mednafen/snes/src/smp/core/registers.hpp b/mednafen/snes/src/smp/core/registers.hpp new file mode 100755 index 0000000..baed447 --- /dev/null +++ b/mednafen/snes/src/smp/core/registers.hpp @@ -0,0 +1,44 @@ +struct regya_t { + uint8_t &hi, &lo; + + inline operator uint16_t() const { + return (hi << 8) + lo; + } + + inline regya_t& operator=(uint16_t data) { + hi = data >> 8; + lo = data; + return *this; + } + + regya_t(uint8_t &hi_, uint8_t &lo_) : hi(hi_), lo(lo_) {} +}; + +struct flag_t { + bool n, v, p, b, h, i, z, c; + + inline operator unsigned() const { + return (n << 7) + (v << 6) + (p << 5) + (b << 4) + + (h << 3) + (i << 2) + (z << 1) + (c << 0); + } + + inline unsigned operator=(uint8_t data) { + n = data & 0x80; v = data & 0x40; p = data & 0x20; b = data & 0x10; + h = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01; + return data; + } + + inline unsigned operator|=(unsigned data) { return operator=(operator unsigned() | data); } + inline unsigned operator^=(unsigned data) { return operator=(operator unsigned() ^ data); } + inline unsigned operator&=(unsigned data) { return operator=(operator unsigned() & data); } + + flag_t() : n(0), v(0), p(0), b(0), h(0), i(0), z(0), c(0) {} +}; + +struct regs_t { + uint16_t pc; + uint8_t r[4], &a, &x, &y, &sp; + regya_t ya; + flag_t p; + regs_t() : a(r[0]), x(r[1]), y(r[2]), sp(r[3]), ya(r[2], r[0]) {} +}; diff --git a/mednafen/snes/src/smp/core/serialization.cpp b/mednafen/snes/src/smp/core/serialization.cpp new file mode 100755 index 0000000..883d013 --- /dev/null +++ b/mednafen/snes/src/smp/core/serialization.cpp @@ -0,0 +1,26 @@ +#ifdef SMPCORE_CPP + +void SMPcore::core_serialize(serializer &s) { + s.integer(regs.pc); + s.integer(regs.a); + s.integer(regs.x); + s.integer(regs.y); + s.integer(regs.sp); + s.integer(regs.p.n); + s.integer(regs.p.v); + s.integer(regs.p.p); + s.integer(regs.p.b); + s.integer(regs.p.h); + s.integer(regs.p.i); + s.integer(regs.p.z); + s.integer(regs.p.c); + + s.integer(dp); + s.integer(sp); + s.integer(rd); + s.integer(wr); + s.integer(bit); + s.integer(ya); +} + +#endif diff --git a/mednafen/snes/src/smp/core/table.cpp b/mednafen/snes/src/smp/core/table.cpp new file mode 100755 index 0000000..dc0f633 --- /dev/null +++ b/mednafen/snes/src/smp/core/table.cpp @@ -0,0 +1,264 @@ +#ifdef SMPCORE_CPP + +void SMPcore::initialize_opcode_table() { + #define op opcode_table + op[0x00] = &SMPcore::op_nop; + op[0x01] = &SMPcore::op_tcall<0>; + op[0x02] = &SMPcore::op_setbit_dp<1, 0x01>; + op[0x03] = &SMPcore::op_bitbranch<0x01, true>; + op[0x04] = &SMPcore::op_read_reg_dp<&SMPcore::op_or, A>; + op[0x05] = &SMPcore::op_read_reg_addr<&SMPcore::op_or, A>; + op[0x06] = &SMPcore::op_read_a_ix<&SMPcore::op_or>; + op[0x07] = &SMPcore::op_read_a_idpx<&SMPcore::op_or>; + op[0x08] = &SMPcore::op_read_reg_const<&SMPcore::op_or, A>; + op[0x09] = &SMPcore::op_read_dp_dp<&SMPcore::op_or>; + op[0x0a] = &SMPcore::op_or1_bit<0>; + op[0x0b] = &SMPcore::op_adjust_dp<&SMPcore::op_asl>; + op[0x0c] = &SMPcore::op_adjust_addr<&SMPcore::op_asl>; + op[0x0d] = &SMPcore::op_push_p; + op[0x0e] = &SMPcore::op_adjust_addr_a<1>; + op[0x0f] = &SMPcore::op_brk; + op[0x10] = &SMPcore::op_branch<0x80, false>; + op[0x11] = &SMPcore::op_tcall<1>; + op[0x12] = &SMPcore::op_setbit_dp<0, 0x01>; + op[0x13] = &SMPcore::op_bitbranch<0x01, false>; + op[0x14] = &SMPcore::op_read_a_dpx<&SMPcore::op_or>; + op[0x15] = &SMPcore::op_read_a_addrr<&SMPcore::op_or, X>; + op[0x16] = &SMPcore::op_read_a_addrr<&SMPcore::op_or, Y>; + op[0x17] = &SMPcore::op_read_a_idpy<&SMPcore::op_or>; + op[0x18] = &SMPcore::op_read_dp_const<&SMPcore::op_or>; + op[0x19] = &SMPcore::op_read_ix_iy<&SMPcore::op_or>; + op[0x1a] = &SMPcore::op_adjustw_dp<-1>; + op[0x1b] = &SMPcore::op_adjust_dpx<&SMPcore::op_asl>; + op[0x1c] = &SMPcore::op_adjust_reg<&SMPcore::op_asl, A>; + op[0x1d] = &SMPcore::op_adjust_reg<&SMPcore::op_dec, X>; + op[0x1e] = &SMPcore::op_read_reg_addr<&SMPcore::op_cmp, X>; + op[0x1f] = &SMPcore::op_jmp_iaddrx; + op[0x20] = &SMPcore::op_setbit<0x20, 0x00>; + op[0x21] = &SMPcore::op_tcall<2>; + op[0x22] = &SMPcore::op_setbit_dp<1, 0x02>; + op[0x23] = &SMPcore::op_bitbranch<0x02, true>; + op[0x24] = &SMPcore::op_read_reg_dp<&SMPcore::op_and, A>; + op[0x25] = &SMPcore::op_read_reg_addr<&SMPcore::op_and, A>; + op[0x26] = &SMPcore::op_read_a_ix<&SMPcore::op_and>; + op[0x27] = &SMPcore::op_read_a_idpx<&SMPcore::op_and>; + op[0x28] = &SMPcore::op_read_reg_const<&SMPcore::op_and, A>; + op[0x29] = &SMPcore::op_read_dp_dp<&SMPcore::op_and>; + op[0x2a] = &SMPcore::op_or1_bit<1>; + op[0x2b] = &SMPcore::op_adjust_dp<&SMPcore::op_rol>; + op[0x2c] = &SMPcore::op_adjust_addr<&SMPcore::op_rol>; + op[0x2d] = &SMPcore::op_push_reg; + op[0x2e] = &SMPcore::op_cbne_dp; + op[0x2f] = &SMPcore::op_bra; + op[0x30] = &SMPcore::op_branch<0x80, true>; + op[0x31] = &SMPcore::op_tcall<3>; + op[0x32] = &SMPcore::op_setbit_dp<0, 0x02>; + op[0x33] = &SMPcore::op_bitbranch<0x02, false>; + op[0x34] = &SMPcore::op_read_a_dpx<&SMPcore::op_and>; + op[0x35] = &SMPcore::op_read_a_addrr<&SMPcore::op_and, X>; + op[0x36] = &SMPcore::op_read_a_addrr<&SMPcore::op_and, Y>; + op[0x37] = &SMPcore::op_read_a_idpy<&SMPcore::op_and>; + op[0x38] = &SMPcore::op_read_dp_const<&SMPcore::op_and>; + op[0x39] = &SMPcore::op_read_ix_iy<&SMPcore::op_and>; + op[0x3a] = &SMPcore::op_adjustw_dp<+1>; + op[0x3b] = &SMPcore::op_adjust_dpx<&SMPcore::op_rol>; + op[0x3c] = &SMPcore::op_adjust_reg<&SMPcore::op_rol, A>; + op[0x3d] = &SMPcore::op_adjust_reg<&SMPcore::op_inc, X>; + op[0x3e] = &SMPcore::op_read_reg_dp<&SMPcore::op_cmp, X>; + op[0x3f] = &SMPcore::op_call; + op[0x40] = &SMPcore::op_setbit<0x20, 0x20>; + op[0x41] = &SMPcore::op_tcall<4>; + op[0x42] = &SMPcore::op_setbit_dp<1, 0x04>; + op[0x43] = &SMPcore::op_bitbranch<0x04, true>; + op[0x44] = &SMPcore::op_read_reg_dp<&SMPcore::op_eor, A>; + op[0x45] = &SMPcore::op_read_reg_addr<&SMPcore::op_eor, A>; + op[0x46] = &SMPcore::op_read_a_ix<&SMPcore::op_eor>; + op[0x47] = &SMPcore::op_read_a_idpx<&SMPcore::op_eor>; + op[0x48] = &SMPcore::op_read_reg_const<&SMPcore::op_eor, A>; + op[0x49] = &SMPcore::op_read_dp_dp<&SMPcore::op_eor>; + op[0x4a] = &SMPcore::op_and1_bit<0>; + op[0x4b] = &SMPcore::op_adjust_dp<&SMPcore::op_lsr>; + op[0x4c] = &SMPcore::op_adjust_addr<&SMPcore::op_lsr>; + op[0x4d] = &SMPcore::op_push_reg; + op[0x4e] = &SMPcore::op_adjust_addr_a<0>; + op[0x4f] = &SMPcore::op_pcall; + op[0x50] = &SMPcore::op_branch<0x40, false>; + op[0x51] = &SMPcore::op_tcall<5>; + op[0x52] = &SMPcore::op_setbit_dp<0, 0x04>; + op[0x53] = &SMPcore::op_bitbranch<0x04, false>; + op[0x54] = &SMPcore::op_read_a_dpx<&SMPcore::op_eor>; + op[0x55] = &SMPcore::op_read_a_addrr<&SMPcore::op_eor, X>; + op[0x56] = &SMPcore::op_read_a_addrr<&SMPcore::op_eor, Y>; + op[0x57] = &SMPcore::op_read_a_idpy<&SMPcore::op_eor>; + op[0x58] = &SMPcore::op_read_dp_const<&SMPcore::op_eor>; + op[0x59] = &SMPcore::op_read_ix_iy<&SMPcore::op_eor>; + op[0x5a] = &SMPcore::op_cmpw_ya_dp; + op[0x5b] = &SMPcore::op_adjust_dpx<&SMPcore::op_lsr>; + op[0x5c] = &SMPcore::op_adjust_reg<&SMPcore::op_lsr, A>; + op[0x5d] = &SMPcore::op_mov_reg_reg; + op[0x5e] = &SMPcore::op_read_reg_addr<&SMPcore::op_cmp, Y>; + op[0x5f] = &SMPcore::op_jmp_addr; + op[0x60] = &SMPcore::op_setbit<0x01, 0x00>; + op[0x61] = &SMPcore::op_tcall<6>; + op[0x62] = &SMPcore::op_setbit_dp<1, 0x08>; + op[0x63] = &SMPcore::op_bitbranch<0x08, true>; + op[0x64] = &SMPcore::op_read_reg_dp<&SMPcore::op_cmp, A>; + op[0x65] = &SMPcore::op_read_reg_addr<&SMPcore::op_cmp, A>; + op[0x66] = &SMPcore::op_read_a_ix<&SMPcore::op_cmp>; + op[0x67] = &SMPcore::op_read_a_idpx<&SMPcore::op_cmp>; + op[0x68] = &SMPcore::op_read_reg_const<&SMPcore::op_cmp, A>; + op[0x69] = &SMPcore::op_read_dp_dp<&SMPcore::op_cmp>; + op[0x6a] = &SMPcore::op_and1_bit<1>; + op[0x6b] = &SMPcore::op_adjust_dp<&SMPcore::op_ror>; + op[0x6c] = &SMPcore::op_adjust_addr<&SMPcore::op_ror>; + op[0x6d] = &SMPcore::op_push_reg; + op[0x6e] = &SMPcore::op_dbnz_dp; + op[0x6f] = &SMPcore::op_ret; + op[0x70] = &SMPcore::op_branch<0x40, true>; + op[0x71] = &SMPcore::op_tcall<7>; + op[0x72] = &SMPcore::op_setbit_dp<0, 0x08>; + op[0x73] = &SMPcore::op_bitbranch<0x08, false>; + op[0x74] = &SMPcore::op_read_a_dpx<&SMPcore::op_cmp>; + op[0x75] = &SMPcore::op_read_a_addrr<&SMPcore::op_cmp, X>; + op[0x76] = &SMPcore::op_read_a_addrr<&SMPcore::op_cmp, Y>; + op[0x77] = &SMPcore::op_read_a_idpy<&SMPcore::op_cmp>; + op[0x78] = &SMPcore::op_read_dp_const<&SMPcore::op_cmp>; + op[0x79] = &SMPcore::op_read_ix_iy<&SMPcore::op_cmp>; + op[0x7a] = &SMPcore::op_read_ya_dp<&SMPcore::op_addw>; + op[0x7b] = &SMPcore::op_adjust_dpx<&SMPcore::op_ror>; + op[0x7c] = &SMPcore::op_adjust_reg<&SMPcore::op_ror, A>; + op[0x7d] = &SMPcore::op_mov_reg_reg; + op[0x7e] = &SMPcore::op_read_reg_dp<&SMPcore::op_cmp, Y>; + op[0x7f] = &SMPcore::op_reti; + op[0x80] = &SMPcore::op_setbit<0x01, 0x01>; + op[0x81] = &SMPcore::op_tcall<8>; + op[0x82] = &SMPcore::op_setbit_dp<1, 0x10>; + op[0x83] = &SMPcore::op_bitbranch<0x10, true>; + op[0x84] = &SMPcore::op_read_reg_dp<&SMPcore::op_adc, A>; + op[0x85] = &SMPcore::op_read_reg_addr<&SMPcore::op_adc, A>; + op[0x86] = &SMPcore::op_read_a_ix<&SMPcore::op_adc>; + op[0x87] = &SMPcore::op_read_a_idpx<&SMPcore::op_adc>; + op[0x88] = &SMPcore::op_read_reg_const<&SMPcore::op_adc, A>; + op[0x89] = &SMPcore::op_read_dp_dp<&SMPcore::op_adc>; + op[0x8a] = &SMPcore::op_eor1_bit; + op[0x8b] = &SMPcore::op_adjust_dp<&SMPcore::op_dec>; + op[0x8c] = &SMPcore::op_adjust_addr<&SMPcore::op_dec>; + op[0x8d] = &SMPcore::op_mov_reg_const; + op[0x8e] = &SMPcore::op_pop_p; + op[0x8f] = &SMPcore::op_mov_dp_const; + op[0x90] = &SMPcore::op_branch<0x01, false>; + op[0x91] = &SMPcore::op_tcall<9>; + op[0x92] = &SMPcore::op_setbit_dp<0, 0x10>; + op[0x93] = &SMPcore::op_bitbranch<0x10, false>; + op[0x94] = &SMPcore::op_read_a_dpx<&SMPcore::op_adc>; + op[0x95] = &SMPcore::op_read_a_addrr<&SMPcore::op_adc, X>; + op[0x96] = &SMPcore::op_read_a_addrr<&SMPcore::op_adc, Y>; + op[0x97] = &SMPcore::op_read_a_idpy<&SMPcore::op_adc>; + op[0x98] = &SMPcore::op_read_dp_const<&SMPcore::op_adc>; + op[0x99] = &SMPcore::op_read_ix_iy<&SMPcore::op_adc>; + op[0x9a] = &SMPcore::op_read_ya_dp<&SMPcore::op_subw>; + op[0x9b] = &SMPcore::op_adjust_dpx<&SMPcore::op_dec>; + op[0x9c] = &SMPcore::op_adjust_reg<&SMPcore::op_dec, A>; + op[0x9d] = &SMPcore::op_mov_reg_reg; + op[0x9e] = &SMPcore::op_div_ya_x; + op[0x9f] = &SMPcore::op_xcn; + op[0xa0] = &SMPcore::op_seti<1>; + op[0xa1] = &SMPcore::op_tcall<10>; + op[0xa2] = &SMPcore::op_setbit_dp<1, 0x20>; + op[0xa3] = &SMPcore::op_bitbranch<0x20, true>; + op[0xa4] = &SMPcore::op_read_reg_dp<&SMPcore::op_sbc, A>; + op[0xa5] = &SMPcore::op_read_reg_addr<&SMPcore::op_sbc, A>; + op[0xa6] = &SMPcore::op_read_a_ix<&SMPcore::op_sbc>; + op[0xa7] = &SMPcore::op_read_a_idpx<&SMPcore::op_sbc>; + op[0xa8] = &SMPcore::op_read_reg_const<&SMPcore::op_sbc, A>; + op[0xa9] = &SMPcore::op_read_dp_dp<&SMPcore::op_sbc>; + op[0xaa] = &SMPcore::op_mov1_c_bit; + op[0xab] = &SMPcore::op_adjust_dp<&SMPcore::op_inc>; + op[0xac] = &SMPcore::op_adjust_addr<&SMPcore::op_inc>; + op[0xad] = &SMPcore::op_read_reg_const<&SMPcore::op_cmp, Y>; + op[0xae] = &SMPcore::op_pop_reg; + op[0xaf] = &SMPcore::op_mov_ixinc_a; + op[0xb0] = &SMPcore::op_branch<0x01, true>; + op[0xb1] = &SMPcore::op_tcall<11>; + op[0xb2] = &SMPcore::op_setbit_dp<0, 0x20>; + op[0xb3] = &SMPcore::op_bitbranch<0x20, false>; + op[0xb4] = &SMPcore::op_read_a_dpx<&SMPcore::op_sbc>; + op[0xb5] = &SMPcore::op_read_a_addrr<&SMPcore::op_sbc, X>; + op[0xb6] = &SMPcore::op_read_a_addrr<&SMPcore::op_sbc, Y>; + op[0xb7] = &SMPcore::op_read_a_idpy<&SMPcore::op_sbc>; + op[0xb8] = &SMPcore::op_read_dp_const<&SMPcore::op_sbc>; + op[0xb9] = &SMPcore::op_read_ix_iy<&SMPcore::op_sbc>; + op[0xba] = &SMPcore::op_movw_ya_dp; + op[0xbb] = &SMPcore::op_adjust_dpx<&SMPcore::op_inc>; + op[0xbc] = &SMPcore::op_adjust_reg<&SMPcore::op_inc, A>; + op[0xbd] = &SMPcore::op_mov_sp_x; + op[0xbe] = &SMPcore::op_das; + op[0xbf] = &SMPcore::op_mov_a_ixinc; + op[0xc0] = &SMPcore::op_seti<0>; + op[0xc1] = &SMPcore::op_tcall<12>; + op[0xc2] = &SMPcore::op_setbit_dp<1, 0x40>; + op[0xc3] = &SMPcore::op_bitbranch<0x40, true>; + op[0xc4] = &SMPcore::op_mov_dp_reg; + op[0xc5] = &SMPcore::op_mov_addr_reg; + op[0xc6] = &SMPcore::op_mov_ix_a; + op[0xc7] = &SMPcore::op_mov_idpx_a; + op[0xc8] = &SMPcore::op_read_reg_const<&SMPcore::op_cmp, X>; + op[0xc9] = &SMPcore::op_mov_addr_reg; + op[0xca] = &SMPcore::op_mov1_bit_c; + op[0xcb] = &SMPcore::op_mov_dp_reg; + op[0xcc] = &SMPcore::op_mov_addr_reg; + op[0xcd] = &SMPcore::op_mov_reg_const; + op[0xce] = &SMPcore::op_pop_reg; + op[0xcf] = &SMPcore::op_mul_ya; + op[0xd0] = &SMPcore::op_branch<0x02, false>; + op[0xd1] = &SMPcore::op_tcall<13>; + op[0xd2] = &SMPcore::op_setbit_dp<0, 0x40>; + op[0xd3] = &SMPcore::op_bitbranch<0x40, false>; + op[0xd4] = &SMPcore::op_mov_dpr_reg; + op[0xd5] = &SMPcore::op_mov_addrr_a; + op[0xd6] = &SMPcore::op_mov_addrr_a; + op[0xd7] = &SMPcore::op_mov_idpy_a; + op[0xd8] = &SMPcore::op_mov_dp_reg; + op[0xd9] = &SMPcore::op_mov_dpr_reg; + op[0xda] = &SMPcore::op_movw_dp_ya; + op[0xdb] = &SMPcore::op_mov_dpr_reg; + op[0xdc] = &SMPcore::op_adjust_reg<&SMPcore::op_dec, Y>; + op[0xdd] = &SMPcore::op_mov_reg_reg; + op[0xde] = &SMPcore::op_cbne_dpx; + op[0xdf] = &SMPcore::op_daa; + op[0xe0] = &SMPcore::op_setbit<0x48, 0x00>; + op[0xe1] = &SMPcore::op_tcall<14>; + op[0xe2] = &SMPcore::op_setbit_dp<1, 0x80>; + op[0xe3] = &SMPcore::op_bitbranch<0x80, true>; + op[0xe4] = &SMPcore::op_mov_reg_dp; + op[0xe5] = &SMPcore::op_mov_reg_addr; + op[0xe6] = &SMPcore::op_mov_a_ix; + op[0xe7] = &SMPcore::op_mov_a_idpx; + op[0xe8] = &SMPcore::op_mov_reg_const; + op[0xe9] = &SMPcore::op_mov_reg_addr; + op[0xea] = &SMPcore::op_not1_bit; + op[0xeb] = &SMPcore::op_mov_reg_dp; + op[0xec] = &SMPcore::op_mov_reg_addr; + op[0xed] = &SMPcore::op_notc; + op[0xee] = &SMPcore::op_pop_reg; + op[0xef] = &SMPcore::op_wait; + op[0xf0] = &SMPcore::op_branch<0x02, true>; + op[0xf1] = &SMPcore::op_tcall<15>; + op[0xf2] = &SMPcore::op_setbit_dp<0, 0x80>; + op[0xf3] = &SMPcore::op_bitbranch<0x80, false>; + op[0xf4] = &SMPcore::op_mov_reg_dpr; + op[0xf5] = &SMPcore::op_mov_a_addrr; + op[0xf6] = &SMPcore::op_mov_a_addrr; + op[0xf7] = &SMPcore::op_mov_a_idpy; + op[0xf8] = &SMPcore::op_mov_reg_dp; + op[0xf9] = &SMPcore::op_mov_reg_dpr; + op[0xfa] = &SMPcore::op_mov_dp_dp; + op[0xfb] = &SMPcore::op_mov_reg_dpr; + op[0xfc] = &SMPcore::op_adjust_reg<&SMPcore::op_inc, Y>; + op[0xfd] = &SMPcore::op_mov_reg_reg; + op[0xfe] = &SMPcore::op_dbnz_y; + op[0xff] = &SMPcore::op_wait; + #undef op +} + +#endif diff --git a/mednafen/snes/src/smp/smp.cpp b/mednafen/snes/src/smp/smp.cpp new file mode 100755 index 0000000..a7460e2 --- /dev/null +++ b/mednafen/snes/src/smp/smp.cpp @@ -0,0 +1,47 @@ +#include <../base.hpp> + +#define SMP_CPP +namespace SNES { + +//this is the IPLROM for the S-SMP coprocessor. +//the S-SMP does not allow writing to the IPLROM. +//all writes are instead mapped to the extended +//RAM region, accessible when $f1.d7 is clear. + +const uint8_t SMP::iplrom[64] = { +/*ffc0*/ 0xcd, 0xef, //mov x,#$ef +/*ffc2*/ 0xbd, //mov sp,x +/*ffc3*/ 0xe8, 0x00, //mov a,#$00 +/*ffc5*/ 0xc6, //mov (x),a +/*ffc6*/ 0x1d, //dec x +/*ffc7*/ 0xd0, 0xfc, //bne $ffc5 +/*ffc9*/ 0x8f, 0xaa, 0xf4, //mov $f4,#$aa +/*ffcc*/ 0x8f, 0xbb, 0xf5, //mov $f5,#$bb +/*ffcf*/ 0x78, 0xcc, 0xf4, //cmp $f4,#$cc +/*ffd2*/ 0xd0, 0xfb, //bne $ffcf +/*ffd4*/ 0x2f, 0x19, //bra $ffef +/*ffd6*/ 0xeb, 0xf4, //mov y,$f4 +/*ffd8*/ 0xd0, 0xfc, //bne $ffd6 +/*ffda*/ 0x7e, 0xf4, //cmp y,$f4 +/*ffdc*/ 0xd0, 0x0b, //bne $ffe9 +/*ffde*/ 0xe4, 0xf5, //mov a,$f5 +/*ffe0*/ 0xcb, 0xf4, //mov $f4,y +/*ffe2*/ 0xd7, 0x00, //mov ($00)+y,a +/*ffe4*/ 0xfc, //inc y +/*ffe5*/ 0xd0, 0xf3, //bne $ffda +/*ffe7*/ 0xab, 0x01, //inc $01 +/*ffe9*/ 0x10, 0xef, //bpl $ffda +/*ffeb*/ 0x7e, 0xf4, //cmp y,$f4 +/*ffed*/ 0x10, 0xeb, //bpl $ffda +/*ffef*/ 0xba, 0xf6, //movw ya,$f6 +/*fff1*/ 0xda, 0x00, //movw $00,ya +/*fff3*/ 0xba, 0xf4, //movw ya,$f4 +/*fff5*/ 0xc4, 0xf4, //mov $f4,a +/*fff7*/ 0xdd, //mov a,y +/*fff8*/ 0x5d, //mov x,a +/*fff9*/ 0xd0, 0xdb, //bne $ffd6 +/*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x) +/*fffe*/ 0xc0, 0xff //reset vector location ($ffc0) +}; + +}; diff --git a/mednafen/snes/src/smp/smp.hpp b/mednafen/snes/src/smp/smp.hpp new file mode 100755 index 0000000..8622d6c --- /dev/null +++ b/mednafen/snes/src/smp/smp.hpp @@ -0,0 +1,19 @@ +class SMP { +public: + virtual void enter() = 0; + static const uint8_t iplrom[64]; + + virtual uint8 ram_read(uint16 addr) = 0; + virtual void ram_write(uint16 addr, uint8 value) = 0; + + //$f4-$f7 + virtual uint8 port_read(uint8 port) = 0; + virtual void port_write(uint8 port, uint8 value) = 0; + + virtual void power() = 0; + virtual void reset() = 0; + + virtual void serialize(serializer&) {} + SMP() {} + virtual ~SMP() {} +}; diff --git a/mednafen/snes/src/smp/ssmp/debugger/debugger.cpp b/mednafen/snes/src/smp/ssmp/debugger/debugger.cpp new file mode 100755 index 0000000..6db906c --- /dev/null +++ b/mednafen/snes/src/smp/ssmp/debugger/debugger.cpp @@ -0,0 +1,44 @@ +#ifdef SSMP_CPP + +void sSMPDebugger::op_step() { + bool break_event = false; + + usage[regs.pc] |= UsageExec; + opcode_pc = regs.pc; + + if(debugger.step_smp) { + debugger.break_event = Debugger::SMPStep; + scheduler.exit(Scheduler::DebuggerEvent); + } else { + debugger.breakpoint_test(Debugger::Breakpoint::APURAM, Debugger::Breakpoint::Exec, regs.pc, 0x00); + } + + if(step_event) step_event(); + sSMP::op_step(); + scheduler.sync_smpcpu(); +} + +uint8 sSMPDebugger::op_read(uint16 addr) { + uint8 data = sSMP::op_read(addr); + usage[addr] |= UsageRead; + debugger.breakpoint_test(Debugger::Breakpoint::APURAM, Debugger::Breakpoint::Read, addr, data); + return data; +} + +void sSMPDebugger::op_write(uint16 addr, uint8 data) { + sSMP::op_write(addr, data); + usage[addr] |= UsageWrite; + usage[addr] &= ~UsageExec; + debugger.breakpoint_test(Debugger::Breakpoint::APURAM, Debugger::Breakpoint::Write, addr, data); +} + +sSMPDebugger::sSMPDebugger() { + usage = new uint8[1 << 16](); + opcode_pc = 0xffc0; +} + +sSMPDebugger::~sSMPDebugger() { + delete[] usage; +} + +#endif diff --git a/mednafen/snes/src/smp/ssmp/debugger/debugger.hpp b/mednafen/snes/src/smp/ssmp/debugger/debugger.hpp new file mode 100755 index 0000000..b67be97 --- /dev/null +++ b/mednafen/snes/src/smp/ssmp/debugger/debugger.hpp @@ -0,0 +1,19 @@ +class sSMPDebugger : public sSMP { +public: + function step_event; + + enum Usage { + UsageRead = 0x80, + UsageWrite = 0x40, + UsageExec = 0x20, + }; + uint8 *usage; + uint16 opcode_pc; + + void op_step(); + uint8 op_read(uint16 addr); + void op_write(uint16 addr, uint8 data); + + sSMPDebugger(); + ~sSMPDebugger(); +}; diff --git a/mednafen/snes/src/smp/ssmp/memory/memory.cpp b/mednafen/snes/src/smp/ssmp/memory/memory.cpp new file mode 100755 index 0000000..9dd4876 --- /dev/null +++ b/mednafen/snes/src/smp/ssmp/memory/memory.cpp @@ -0,0 +1,226 @@ +#ifdef SSMP_CPP + +alwaysinline uint8 sSMP::ram_read(uint16 addr) { + if(addr < 0xffc0) return memory::apuram[addr]; + if(status.iplrom_enabled == false) return memory::apuram[addr]; + return iplrom[addr & 0x3f]; +} + +alwaysinline void sSMP::ram_write(uint16 addr, uint8 data) { + //writes to $ffc0-$ffff always go to spcram, even if the iplrom is enabled + memory::apuram[addr] = data; +} + +// + +alwaysinline uint8 sSMP::port_read(uint8 port) { + return memory::apuram[0xf4 + (port & 3)]; +} + +alwaysinline void sSMP::port_write(uint8 port, uint8 data) { + memory::apuram[0xf4 + (port & 3)] = data; +} + +// + +alwaysinline uint8 sSMP::op_busread(uint16 addr) { + uint8 r; + if((addr & 0xfff0) == 0x00f0) { + //addr >= 0x00f0 && addr <= 0x00ff + switch(addr) { + case 0xf0: { //TEST -- write-only register + r = 0x00; + } break; + + case 0xf1: { //CONTROL -- write-only register + r = 0x00; + } break; + + case 0xf2: { //DSPADDR + r = status.dsp_addr; + } break; + + case 0xf3: { //DSPDATA + //0x80-0xff are read-only mirrors of 0x00-0x7f + r = dsp.read(status.dsp_addr & 0x7f); + } break; + + case 0xf4: //CPUIO0 + case 0xf5: //CPUIO1 + case 0xf6: //CPUIO2 + case 0xf7: { //CPUIO3 + scheduler.sync_smpcpu(); + r = cpu.port_read(addr & 3); + } break; + + case 0xf8: { //??? + r = status.smp_f8; + } break; + + case 0xf9: { //??? + r = status.smp_f9; + } break; + + case 0xfa: //T0TARGET + case 0xfb: //T1TARGET + case 0xfc: { //T2TARGET -- write-only registers + r = 0x00; + } break; + + case 0xfd: { //T0OUT -- 4-bit counter value + r = t0.stage3_ticks & 15; + t0.stage3_ticks = 0; + } break; + + case 0xfe: { //T1OUT -- 4-bit counter value + r = t1.stage3_ticks & 15; + t1.stage3_ticks = 0; + } break; + + case 0xff: { //T2OUT -- 4-bit counter value + r = t2.stage3_ticks & 15; + t2.stage3_ticks = 0; + } break; + } + } else { + r = ram_read(addr); + } + + return r; +} + +alwaysinline void sSMP::op_buswrite(uint16 addr, uint8 data) { + if((addr & 0xfff0) == 0x00f0) { + //addr >= 0x00f0 && addr >= 0x00ff + if(status.mmio_disabled == true) return; + + switch(addr) { + case 0xf0: { //TEST + if(regs.p.p) break; //writes only valid when P flag is clear + + //multiplier table may not be 100% accurate, some settings crash + //the processor due to S-SMP <> S-DSP bus access misalignment + //static uint8 clock_speed_tbl[16] = + //{ 3, 5, 9, 17, 4, 6, 10, 18, 6, 8, 12, 20, 10, 12, 16, 24 }; + + //status.clock_speed = 24 * clock_speed_tbl[data >> 4] / 3; + status.mmio_disabled = !!(data & 0x04); + status.ram_writable = !!(data & 0x02); + + //if((data >> 4) != 0) { + //dprintf("* S-SMP critical warning: $00f0 (TEST) clock speed control modified!"); + //dprintf("* S-SMP may crash on hardware as a result!"); + //} + } break; + + case 0xf1: { //CONTROL + status.iplrom_enabled = !!(data & 0x80); + + if(data & 0x30) { + //one-time clearing of APU port read registers, + //emulated by simulating CPU writes of 0x00 + scheduler.sync_smpcpu(); + if(data & 0x20) { + cpu.port_write(2, 0x00); + cpu.port_write(3, 0x00); + } + if(data & 0x10) { + cpu.port_write(0, 0x00); + cpu.port_write(1, 0x00); + } + } + + //0->1 transistion resets timers + if(t2.enabled == false && (data & 0x04)) { + t2.stage2_ticks = 0; + t2.stage3_ticks = 0; + } + t2.enabled = !!(data & 0x04); + + if(t1.enabled == false && (data & 0x02)) { + t1.stage2_ticks = 0; + t1.stage3_ticks = 0; + } + t1.enabled = !!(data & 0x02); + + if(t0.enabled == false && (data & 0x01)) { + t0.stage2_ticks = 0; + t0.stage3_ticks = 0; + } + t0.enabled = !!(data & 0x01); + } break; + + case 0xf2: { //DSPADDR + status.dsp_addr = data; + } break; + + case 0xf3: { //DSPDATA + //0x80-0xff is a read-only mirror of 0x00-0x7f + if(!(status.dsp_addr & 0x80)) { + dsp.write(status.dsp_addr & 0x7f, data); + } + } break; + + case 0xf4: //CPUIO0 + case 0xf5: //CPUIO1 + case 0xf6: //CPUIO2 + case 0xf7: { //CPUIO3 + scheduler.sync_smpcpu(); + port_write(addr & 3, data); + } break; + + case 0xf8: { //??? + status.smp_f8 = data; + } break; + + case 0xf9: { //??? + status.smp_f9 = data; + } break; + + case 0xfa: { //T0TARGET + t0.target = data; + } break; + + case 0xfb: { //T1TARGET + t1.target = data; + } break; + + case 0xfc: { //T2TARGET + t2.target = data; + } break; + + case 0xfd: //T0OUT + case 0xfe: //T1OUT + case 0xff: { //T2OUT -- read-only registers + } break; + } + } + + //all writes, even to MMIO registers, appear on bus + if(status.ram_writable == true) { + ram_write(addr, data); + } +} + +// + +void sSMP::op_io() { + add_clocks(24); + tick_timers(); +} + +uint8 sSMP::op_read(uint16 addr) { + add_clocks(12); + uint8 r = op_busread(addr); + add_clocks(12); + tick_timers(); + return r; +} + +void sSMP::op_write(uint16 addr, uint8 data) { + add_clocks(24); + op_buswrite(addr, data); + tick_timers(); +} + +#endif diff --git a/mednafen/snes/src/smp/ssmp/memory/memory.hpp b/mednafen/snes/src/smp/ssmp/memory/memory.hpp new file mode 100755 index 0000000..ae16cee --- /dev/null +++ b/mednafen/snes/src/smp/ssmp/memory/memory.hpp @@ -0,0 +1,12 @@ +uint8 ram_read(uint16 addr); +void ram_write(uint16 addr, uint8 data); + +uint8 port_read(uint8 port); +void port_write(uint8 port, uint8 data); + +uint8 op_busread(uint16 addr); +void op_buswrite(uint16 addr, uint8 data); + +void op_io(); +debugvirtual uint8 op_read(uint16 addr); +debugvirtual void op_write(uint16 addr, uint8 data); diff --git a/mednafen/snes/src/smp/ssmp/serialization.cpp b/mednafen/snes/src/smp/ssmp/serialization.cpp new file mode 100755 index 0000000..d49e379 --- /dev/null +++ b/mednafen/snes/src/smp/ssmp/serialization.cpp @@ -0,0 +1,38 @@ +#ifdef SSMP_CPP + +void sSMP::serialize(serializer &s) { + SMP::serialize(s); + SMPcore::core_serialize(s); + + s.integer(status.opcode); + s.integer(status.in_opcode); + s.integer(status.clock_counter); + s.integer(status.dsp_counter); + s.integer(status.clock_speed); + s.integer(status.mmio_disabled); + s.integer(status.ram_writable); + s.integer(status.iplrom_enabled); + s.integer(status.dsp_addr); + s.integer(status.smp_f8); + s.integer(status.smp_f9); + + s.integer(t0.target); + s.integer(t0.stage1_ticks); + s.integer(t0.stage2_ticks); + s.integer(t0.stage3_ticks); + s.integer(t0.enabled); + + s.integer(t1.target); + s.integer(t1.stage1_ticks); + s.integer(t1.stage2_ticks); + s.integer(t1.stage3_ticks); + s.integer(t1.enabled); + + s.integer(t2.target); + s.integer(t2.stage1_ticks); + s.integer(t2.stage2_ticks); + s.integer(t2.stage3_ticks); + s.integer(t2.enabled); +} + +#endif diff --git a/mednafen/snes/src/smp/ssmp/ssmp.cpp b/mednafen/snes/src/smp/ssmp/ssmp.cpp new file mode 100755 index 0000000..3d243e4 --- /dev/null +++ b/mednafen/snes/src/smp/ssmp/ssmp.cpp @@ -0,0 +1,97 @@ +#include <../base.hpp> + +#define SSMP_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + sSMPDebugger smp; +#else + sSMP smp; +#endif + +#include "serialization.cpp" +#include "memory/memory.cpp" +#include "timing/timing.cpp" + +void sSMP::enter() { + while(true) { + while(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + //if(scheduler.clock.cpusmp >= 0 && scheduler.sync != Scheduler::SyncAll) { + // puts("BLAHBLAH"); + //} + + } + + op_step(); + } +} + +void sSMP::op_step() { + (this->*opcode_table[op_readpc()])(); +} + +void sSMP::power() { + //targets not initialized/changed upon reset + t0.target = 0; + t1.target = 0; + t2.target = 0; + + reset(); +} + +void sSMP::reset() { + regs.pc = 0xffc0; + regs.a = 0x00; + regs.x = 0x00; + regs.y = 0x00; + regs.sp = 0xef; + regs.p = 0x02; + + for(unsigned i = 0; i < memory::apuram.size(); i++) { + memory::apuram.write(i, 0x00); + } + + status.clock_counter = 0; + status.dsp_counter = 0; + + //$00f0 + status.clock_speed = 24 * 3 / 3; + status.mmio_disabled = false; + status.ram_writable = true; + + //$00f1 + status.iplrom_enabled = true; + + //$00f2 + status.dsp_addr = 0x00; + + //$00f8,$00f9 + status.smp_f8 = 0x00; + status.smp_f9 = 0x00; + + t0.enabled = false; + t1.enabled = false; + t2.enabled = false; + + t0.stage1_ticks = 0; + t1.stage1_ticks = 0; + t2.stage1_ticks = 0; + + t0.stage2_ticks = 0; + t1.stage2_ticks = 0; + t2.stage2_ticks = 0; + + t0.stage3_ticks = 0; + t1.stage3_ticks = 0; + t2.stage3_ticks = 0; +} + +sSMP::sSMP() { +} + +sSMP::~sSMP() { +} + +}; diff --git a/mednafen/snes/src/smp/ssmp/ssmp.hpp b/mednafen/snes/src/smp/ssmp/ssmp.hpp new file mode 100755 index 0000000..1a83c1b --- /dev/null +++ b/mednafen/snes/src/smp/ssmp/ssmp.hpp @@ -0,0 +1,49 @@ +class sSMP : public SMP, public SMPcore { +public: + void enter(); + debugvirtual void op_step(); + + #include "memory/memory.hpp" + #include "timing/timing.hpp" + + struct { + + uint8 opcode; + bool in_opcode; + + //timing + uint32 clock_counter; + uint32 dsp_counter; + + //$00f0 + uint8 clock_speed; + bool mmio_disabled; + bool ram_writable; + + //$00f1 + bool iplrom_enabled; + + //$00f2 + uint8 dsp_addr; + + //$00f8,$00f9 + uint8 smp_f8, smp_f9; + } status; + + //ssmp.cpp + void power(); + void reset(); + + void serialize(serializer&); + sSMP(); + ~sSMP(); + + friend class sSMPDebug; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern sSMPDebugger smp; +#else + extern sSMP smp; +#endif diff --git a/mednafen/snes/src/smp/ssmp/timing/timing.cpp b/mednafen/snes/src/smp/ssmp/timing/timing.cpp new file mode 100755 index 0000000..5122088 --- /dev/null +++ b/mednafen/snes/src/smp/ssmp/timing/timing.cpp @@ -0,0 +1,28 @@ +#ifdef SSMP_CPP + +void sSMP::add_clocks(unsigned clocks) { + scheduler.addclocks_smp(clocks); + #if !defined(DSP_STATE_MACHINE) + scheduler.sync_smpdsp(); + #else + while(scheduler.clock.smpdsp < 0) dsp.enter(); + #endif + + // Mednafen change. + scheduler.sync_smpcpu(); +} + +void sSMP::tick_timers() { + t0.tick(); + t1.tick(); + t2.tick(); + + //forcefully sync S-SMP to S-CPU in case chips are not communicating + //sync if S-SMP is more than 24 samples ahead of S-CPU + //if(scheduler.clock.cpusmp > +(768 * 24 * (int64)24000000)) scheduler.sync_smpcpu(); + + // Mednafen change - Make the check for only about 1 sample ahead. + //if(scheduler.clock.cpusmp > +(768 * 1 * (int64)24000000)) scheduler.sync_smpcpu(); +} + +#endif diff --git a/mednafen/snes/src/smp/ssmp/timing/timing.hpp b/mednafen/snes/src/smp/ssmp/timing/timing.hpp new file mode 100755 index 0000000..9f0781f --- /dev/null +++ b/mednafen/snes/src/smp/ssmp/timing/timing.hpp @@ -0,0 +1,34 @@ +template +class sSMPTimer { +public: + uint8 target; + uint8 stage1_ticks, stage2_ticks, stage3_ticks; + bool enabled; + + void tick() { + //stage 1 increment + stage1_ticks++; + if(stage1_ticks < cycle_frequency) return; + + stage1_ticks -= cycle_frequency; + if(enabled == false) return; + + //stage 2 increment + stage2_ticks++; + + if(stage2_ticks != target) return; + + //stage 3 increment + stage2_ticks = 0; + stage3_ticks++; + stage3_ticks &= 15; + } +}; + +sSMPTimer<128> t0; +sSMPTimer<128> t1; +sSMPTimer< 16> t2; + +alwaysinline void add_clocks(unsigned clocks); +alwaysinline void tick_timers(); +uint32 clocks_executed(); diff --git a/mednafen/snes/src/system/audio/audio.cpp b/mednafen/snes/src/system/audio/audio.cpp new file mode 100755 index 0000000..e2c6ec0 --- /dev/null +++ b/mednafen/snes/src/system/audio/audio.cpp @@ -0,0 +1,90 @@ +#ifdef SYSTEM_CPP + +Audio audio; + +void Audio::coprocessor_enable(bool state) { + coprocessor = state; + + dsp_rdoffset = cop_rdoffset = 0; + dsp_wroffset = cop_wroffset = 0; + dsp_length = cop_length = 0; + + r_sum_l = r_sum_r = 0; +} + +void Audio::coprocessor_frequency(double input_frequency) { + double output_frequency; + if(system.region() == System::NTSC) { + output_frequency = config.smp.ntsc_clock_rate / 768.0; + } else /* (system.region() == System::PAL) */ { + output_frequency = config.smp.pal_clock_rate / 768.0; + } + + r_step = input_frequency / output_frequency; + r_frac = 0; +} + +void Audio::sample(int16 left, int16 right) { + if(coprocessor == false) { + system.interface->audio_sample(left, right); + } else { + dsp_buffer[dsp_wroffset] = ((uint16)left << 0) + ((uint16)right << 16); + dsp_wroffset = (dsp_wroffset + 1) & 32767; + dsp_length = (dsp_length + 1) & 32767; + flush(); + } +} + +void Audio::coprocessor_sample(int16 left, int16 right) { + if(r_frac >= 1.0) { + r_frac -= 1.0; + r_sum_l += left; + r_sum_r += right; + return; + } + + r_sum_l += left * r_frac; + r_sum_r += right * r_frac; + + uint16 output_left = sclamp<16>(int(r_sum_l / r_step)); + uint16 output_right = sclamp<16>(int(r_sum_r / r_step)); + + double first = 1.0 - r_frac; + r_sum_l = left * first; + r_sum_r = right * first; + r_frac = r_step - first; + + cop_buffer[cop_wroffset] = (output_left << 0) + (output_right << 16); + cop_wroffset = (cop_wroffset + 1) & 32767; + cop_length = (cop_length + 1) & 32767; + flush(); +} + +void Audio::init() { +} + +void Audio::flush() { + while(dsp_length > 0 && cop_length > 0) { + uint32 dsp_sample = dsp_buffer[dsp_rdoffset]; + uint32 cop_sample = cop_buffer[cop_rdoffset]; + + dsp_rdoffset = (dsp_rdoffset + 1) & 32767; + cop_rdoffset = (cop_rdoffset + 1) & 32767; + + dsp_length--; + cop_length--; + + int dsp_left = (int16)(dsp_sample >> 0); + int dsp_right = (int16)(dsp_sample >> 16); + + int cop_left = (int16)(cop_sample >> 0); + int cop_right = (int16)(cop_sample >> 16); + + system.interface->audio_sample( + sclamp<16>((dsp_left + cop_left ) / 2), + sclamp<16>((dsp_right + cop_right) / 2) + ); + } +} + +#endif diff --git a/mednafen/snes/src/system/audio/audio.hpp b/mednafen/snes/src/system/audio/audio.hpp new file mode 100755 index 0000000..3cd522f --- /dev/null +++ b/mednafen/snes/src/system/audio/audio.hpp @@ -0,0 +1,22 @@ +class Audio { +public: + void coprocessor_enable(bool state); + void coprocessor_frequency(double frequency); + void sample(int16 left, int16 right); + void coprocessor_sample(int16 left, int16 right); + void init(); + +private: + bool coprocessor; + uint32 dsp_buffer[32768], cop_buffer[32768]; + unsigned dsp_rdoffset, cop_rdoffset; + unsigned dsp_wroffset, cop_wroffset; + unsigned dsp_length, cop_length; + + double r_step, r_frac; + int r_sum_l, r_sum_r; + + void flush(); +}; + +extern Audio audio; diff --git a/mednafen/snes/src/system/config/config.cpp b/mednafen/snes/src/system/config/config.cpp new file mode 100755 index 0000000..5c6624c --- /dev/null +++ b/mednafen/snes/src/system/config/config.cpp @@ -0,0 +1,27 @@ +#ifdef SYSTEM_CPP + +Configuration config; + +Configuration::Configuration() { + controller_port1 = Input::DeviceJoypad; + controller_port2 = Input::DeviceJoypad; + expansion_port = System::ExpansionBSX; + region = System::Autodetect; + + cpu.version = 2; + cpu.ntsc_clock_rate = 21477272; + cpu.pal_clock_rate = 21281370; + cpu.alu_mul_delay = 2; + cpu.alu_div_delay = 2; + cpu.wram_init_value = 0x55; + + smp.ntsc_clock_rate = 24607104; //32040.5 * 768 + smp.pal_clock_rate = 24607104; + + ppu1.version = 1; + ppu2.version = 3; + + superfx.speed = 0; //0 = auto-select, 1 = force 10.74MHz, 2 = force 21.48MHz +} + +#endif diff --git a/mednafen/snes/src/system/config/config.hpp b/mednafen/snes/src/system/config/config.hpp new file mode 100755 index 0000000..d28f892 --- /dev/null +++ b/mednafen/snes/src/system/config/config.hpp @@ -0,0 +1,36 @@ +struct Configuration { + unsigned controller_port1; + unsigned controller_port2; + unsigned expansion_port; + unsigned region; + + struct CPU { + unsigned version; + unsigned ntsc_clock_rate; + unsigned pal_clock_rate; + unsigned alu_mul_delay; + unsigned alu_div_delay; + unsigned wram_init_value; + } cpu; + + struct SMP { + unsigned ntsc_clock_rate; + unsigned pal_clock_rate; + } smp; + + struct PPU1 { + unsigned version; + } ppu1; + + struct PPU2 { + unsigned version; + } ppu2; + + struct SuperFX { + unsigned speed; + } superfx; + + Configuration(); +}; + +extern Configuration config; diff --git a/mednafen/snes/src/system/debugger/debugger.cpp b/mednafen/snes/src/system/debugger/debugger.cpp new file mode 100755 index 0000000..a1bb2a0 --- /dev/null +++ b/mednafen/snes/src/system/debugger/debugger.cpp @@ -0,0 +1,107 @@ +#ifdef SYSTEM_CPP + +Debugger debugger; + +void Debugger::breakpoint_test(Debugger::Breakpoint::Source source, Debugger::Breakpoint::Mode mode, unsigned addr, uint8 data) { + for(unsigned i = 0; i < Breakpoints; i++) { + if(breakpoint[i].enabled == false) continue; + + //shadow S-CPU WRAM addresses ($00-3f|80-bf:0000-1fff mirrors $7e:0000-1fff) + if(source == Debugger::Breakpoint::CPUBus && ( + ((breakpoint[i].addr & 0x40e000) == 0x000000) || //$00-3f|80-bf:0000-1fff + ((breakpoint[i].addr & 0xffe000) == 0x7e0000) //$7e:0000-1fff + ) + ) { + if((breakpoint[i].addr & 0x1fff) != (addr & 0x1fff)) continue; + } else { + if(breakpoint[i].addr != addr) continue; + } + + if(breakpoint[i].data != -1 && breakpoint[i].data != data) continue; + if(breakpoint[i].source != source) continue; + if(breakpoint[i].mode != mode) continue; + + breakpoint[i].counter++; + breakpoint_hit = i; + break_event = BreakpointHit; + scheduler.exit(Scheduler::DebuggerEvent); + break; + } +} + +uint8 Debugger::read(Debugger::MemorySource source, unsigned addr) { + switch(source) { + case CPUBus: { + //do not read from memory-mapped registers that could affect program behavior + if(((addr - 0x2000) & 0x40c000) == 0x000000) break; //$00-3f:2000-5fff MMIO + return bus.read(addr & 0xffffff); + } break; + + case APURAM: { + return memory::apuram.read(addr & 0xffff); + } break; + + case VRAM: { + return memory::vram.read(addr & 0xffff); + } break; + + case OAM: { + if(addr & 0x0200) return memory::oam.read(0x0200 + (addr & 0x1f)); + return memory::oam.read(addr & 0x01ff); + } break; + + case CGRAM: { + return memory::cgram.read(addr & 0x01ff); + } break; + } + + return 0x00; +} + +void Debugger::write(Debugger::MemorySource source, unsigned addr, uint8 data) { + switch(source) { + case CPUBus: { + //do not write to memory-mapped registers that could affect program behavior + if(((addr - 0x2000) & 0x40c000) == 0x000000) break; //$00-3f:2000-5fff MMIO + memory::cartrom.write_protect(false); + bus.write(addr & 0xffffff, data); + memory::cartrom.write_protect(true); + } break; + + case APURAM: { + memory::apuram.write(addr & 0xffff, data); + } break; + + case VRAM: { + memory::vram.write(addr & 0xffff, data); + } break; + + case OAM: { + if(addr & 0x0200) memory::oam.write(0x0200 + (addr & 0x1f), data); + else memory::oam.write(addr & 0x01ff, data); + } break; + + case CGRAM: { + memory::cgram.write(addr & 0x01ff, data); + } break; + } +} + +Debugger::Debugger() { + break_event = None; + + for(unsigned n = 0; n < Breakpoints; n++) { + breakpoint[n].enabled = false; + breakpoint[n].addr = 0; + breakpoint[n].data = -1; + breakpoint[n].mode = Breakpoint::Exec; + breakpoint[n].source = Breakpoint::CPUBus; + breakpoint[n].counter = 0; + } + breakpoint_hit = 0; + + step_cpu = false; + step_smp = false; +} + +#endif diff --git a/mednafen/snes/src/system/debugger/debugger.hpp b/mednafen/snes/src/system/debugger/debugger.hpp new file mode 100755 index 0000000..f222873 --- /dev/null +++ b/mednafen/snes/src/system/debugger/debugger.hpp @@ -0,0 +1,32 @@ +class Debugger { +public: + enum BreakEvent { + None, + BreakpointHit, + CPUStep, + SMPStep, + } break_event; + + enum { Breakpoints = 8 }; + struct Breakpoint { + bool enabled; + unsigned addr; + signed data; //-1 = unused + enum Mode { Exec, Read, Write } mode; + enum Source { CPUBus, APURAM, VRAM, OAM, CGRAM } source; + unsigned counter; //number of times breakpoint has been hit since being set + } breakpoint[Breakpoints]; + unsigned breakpoint_hit; + void breakpoint_test(Breakpoint::Source source, Breakpoint::Mode mode, unsigned addr, uint8 data); + + bool step_cpu; + bool step_smp; + + enum MemorySource { CPUBus, APURAM, VRAM, OAM, CGRAM }; + uint8 read(MemorySource, unsigned addr); + void write(MemorySource, unsigned addr, uint8 data); + + Debugger(); +}; + +extern Debugger debugger; diff --git a/mednafen/snes/src/system/input/input.cpp b/mednafen/snes/src/system/input/input.cpp new file mode 100755 index 0000000..afa60d8 --- /dev/null +++ b/mednafen/snes/src/system/input/input.cpp @@ -0,0 +1,336 @@ +#ifdef SYSTEM_CPP + +Input input; + +uint8 Input::port_read(bool portnumber) { + port_t &p = port[portnumber]; + + switch(p.device) { + case DeviceJoypad: { + if(p.counter0 >= 16) return 1; + return system.interface->input_poll(portnumber, p.device, 0, p.counter0++); + } //case DeviceJoypad + + case DeviceMultitap: { + if(cpu.joylatch()) return 2; //when latch is high -- data2 = 1, data1 = 0 + + unsigned deviceidx, deviceindex0, deviceindex1; + uint8 mask = (portnumber == 0 ? 0x40 : 0x80); + + if(cpu.pio() & mask) { + deviceidx = p.counter0; + if(deviceidx >= 16) return 3; + p.counter0++; + + deviceindex0 = 0; //controller 1 + deviceindex1 = 1; //controller 2 + } else { + deviceidx = p.counter1; + if(deviceidx >= 16) return 3; + p.counter1++; + + deviceindex0 = 2; //controller 3 + deviceindex1 = 3; //controller 4 + } + + return (system.interface->input_poll(portnumber, p.device, deviceindex0, deviceidx) << 0) + | (system.interface->input_poll(portnumber, p.device, deviceindex1, deviceidx) << 1); + } //case DeviceMultitap + + case DeviceMouse: { + if(p.counter0 >= 32) return 1; + + int position_x = system.interface->input_poll(portnumber, p.device, 0, MouseX); //-n = left, 0 = center, +n = right + int position_y = system.interface->input_poll(portnumber, p.device, 0, MouseY); //-n = up, 0 = center, +n = right + + bool direction_x = position_x < 0; //0 = right, 1 = left + bool direction_y = position_y < 0; //0 = down, 1 = up + + if(position_x < 0) position_x = -position_x; //abs(position_x) + if(position_y < 0) position_y = -position_y; //abs(position_x) + + position_x = min(127, position_x); //range = 0 - 127 + position_y = min(127, position_y); //range = 0 - 127 + + switch(p.counter0++) { default: + case 0: return 0; + case 1: return 0; + case 2: return 0; + case 3: return 0; + case 4: return 0; + case 5: return 0; + case 6: return 0; + case 7: return 0; + + case 8: return system.interface->input_poll(portnumber, p.device, 0, MouseRight); + case 9: return system.interface->input_poll(portnumber, p.device, 0, MouseLeft); + case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused) + case 11: return 0; // || + + case 12: return 0; //signature + case 13: return 0; // || + case 14: return 0; // || + case 15: return 1; // || + + case 16: return (direction_y) & 1; + case 17: return (position_y >> 6) & 1; + case 18: return (position_y >> 5) & 1; + case 19: return (position_y >> 4) & 1; + case 20: return (position_y >> 3) & 1; + case 21: return (position_y >> 2) & 1; + case 22: return (position_y >> 1) & 1; + case 23: return (position_y >> 0) & 1; + + case 24: return (direction_x) & 1; + case 25: return (position_x >> 6) & 1; + case 26: return (position_x >> 5) & 1; + case 27: return (position_x >> 4) & 1; + case 28: return (position_x >> 3) & 1; + case 29: return (position_x >> 2) & 1; + case 30: return (position_x >> 1) & 1; + case 31: return (position_x >> 0) & 1; + } + } //case DeviceMouse + + case DeviceSuperScope: { + if(portnumber == 0) break; //Super Scope in port 1 not supported ... + if(p.counter0 >= 8) return 1; + + if(p.counter0 == 0) { + //turbo is a switch; toggle is edge sensitive + bool turbo = system.interface->input_poll(portnumber, p.device, 0, SuperScopeTurbo); + if(turbo && !p.superscope.turbolock) { + p.superscope.turbo = !p.superscope.turbo; //toggle state + p.superscope.turbolock = true; + } else if(!turbo) { + p.superscope.turbolock = false; + } + + //trigger is a button + //if turbo is active, trigger is level sensitive; otherwise it is edge sensitive + p.superscope.trigger = false; + bool trigger = system.interface->input_poll(portnumber, p.device, 0, SuperScopeTrigger); + if(trigger && (p.superscope.turbo || !p.superscope.triggerlock)) { + p.superscope.trigger = true; + p.superscope.triggerlock = true; + } else if(!trigger) { + p.superscope.triggerlock = false; + } + + //cursor is a button; it is always level sensitive + p.superscope.cursor = system.interface->input_poll(portnumber, p.device, 0, SuperScopeCursor); + + //pause is a button; it is always edge sensitive + p.superscope.pause = false; + bool pause = system.interface->input_poll(portnumber, p.device, 0, SuperScopePause); + if(pause && !p.superscope.pauselock) { + p.superscope.pause = true; + p.superscope.pauselock = true; + } else if(!pause) { + p.superscope.pauselock = false; + } + + p.superscope.offscreen = + p.superscope.x < 0 || p.superscope.x >= 256 + || p.superscope.y < 0 || p.superscope.y >= (ppu.overscan() ? 240 : 225); + } + + switch(p.counter0++) { + case 0: return p.superscope.trigger; + case 1: return p.superscope.cursor; + case 2: return p.superscope.turbo; + case 3: return p.superscope.pause; + case 4: return 0; + case 5: return 0; + case 6: return p.superscope.offscreen; + case 7: return 0; //noise (1 = yes) + } + } //case DeviceSuperScope + + case DeviceJustifier: + case DeviceJustifiers: { + if(portnumber == 0) break; //Justifier in port 1 not supported ... + if(p.counter0 >= 32) return 1; + + if(p.counter0 == 0) { + p.justifier.trigger1 = system.interface->input_poll(portnumber, p.device, 0, JustifierTrigger); + p.justifier.start1 = system.interface->input_poll(portnumber, p.device, 0, JustifierStart); + + if(p.device == DeviceJustifiers) { + p.justifier.trigger2 = system.interface->input_poll(portnumber, p.device, 1, JustifierTrigger); + p.justifier.start2 = system.interface->input_poll(portnumber, p.device, 1, JustifierStart); + } else { + p.justifier.x2 = -1; + p.justifier.y2 = -1; + + p.justifier.trigger2 = false; + p.justifier.start2 = false; + } + } + + switch(p.counter0++) { + case 0: return 0; + case 1: return 0; + case 2: return 0; + case 3: return 0; + case 4: return 0; + case 5: return 0; + case 6: return 0; + case 7: return 0; + case 8: return 0; + case 9: return 0; + case 10: return 0; + case 11: return 0; + + case 12: return 1; //signature + case 13: return 1; // || + case 14: return 1; // || + case 15: return 0; // || + + case 16: return 0; + case 17: return 1; + case 18: return 0; + case 19: return 1; + case 20: return 0; + case 21: return 1; + case 22: return 0; + case 23: return 1; + + case 24: return p.justifier.trigger1; + case 25: return p.justifier.trigger2; + case 26: return p.justifier.start1; + case 27: return p.justifier.start2; + case 28: return p.justifier.active; + + case 29: return 0; + case 30: return 0; + case 31: return 0; + } + } //case DeviceJustifier(s) + } //switch(p.device) + + //no device connected + return 0; +} + +//scan all input; update cursor positions if needed +void Input::update() { + system.interface->input_poll(); + port_t &p = port[1]; + + switch(p.device) { + case DeviceSuperScope: { + int x = system.interface->input_poll(1, p.device, 0, SuperScopeX); + int y = system.interface->input_poll(1, p.device, 0, SuperScopeY); + x += p.superscope.x; + y += p.superscope.y; + p.superscope.x = max(-16, min(256 + 16, x)); + p.superscope.y = max(-16, min(240 + 16, y)); + + latchx = p.superscope.x; + latchy = p.superscope.y; + } break; + + case DeviceJustifier: + case DeviceJustifiers: { + int x1 = system.interface->input_poll(1, p.device, 0, JustifierX); + int y1 = system.interface->input_poll(1, p.device, 0, JustifierY); + x1 += p.justifier.x1; + y1 += p.justifier.y1; + p.justifier.x1 = max(-16, min(256 + 16, x1)); + p.justifier.y1 = max(-16, min(240 + 16, y1)); + + int x2 = system.interface->input_poll(1, p.device, 1, JustifierX); + int y2 = system.interface->input_poll(1, p.device, 1, JustifierY); + x2 += p.justifier.x2; + y2 += p.justifier.y2; + p.justifier.x2 = max(-16, min(256 + 16, x2)); + p.justifier.y2 = max(-16, min(240 + 16, y2)); + + if(p.justifier.active == 0) { + latchx = p.justifier.x1; + latchy = p.justifier.y1; + } else { + latchx = (p.device == DeviceJustifiers ? p.justifier.x2 : -1); + latchy = (p.device == DeviceJustifiers ? p.justifier.y2 : -1); + } + } break; + } + + if(latchy < 0 || latchy >= (ppu.overscan() ? 240 : 225) || latchx < 0 || latchx >= 256) { + //cursor is offscreen, set to invalid position so counters are not latched + latchx = ~0; + latchy = ~0; + } else { + //cursor is onscreen + latchx += 40; //offset trigger position to simulate hardware latching delay + latchx <<= 2; //dot -> clock conversion + latchx += 2; //align trigger on half-dot ala interrupts (speed optimization for sCPU::add_clocks) + } +} + +void Input::port_set_device(bool portnumber, unsigned device) { + port_t &p = port[portnumber]; + + p.device = device; + p.counter0 = 0; + p.counter1 = 0; + + //set iobit to true if device is capable of latching PPU counters + iobit = port[1].device == DeviceSuperScope + || port[1].device == DeviceJustifier + || port[1].device == DeviceJustifiers; + latchx = -1; + latchy = -1; + + if(device == DeviceSuperScope) { + p.superscope.x = 256 / 2; + p.superscope.y = 240 / 2; + + p.superscope.trigger = false; + p.superscope.cursor = false; + p.superscope.turbo = false; + p.superscope.pause = false; + p.superscope.offscreen = false; + + p.superscope.turbolock = false; + p.superscope.triggerlock = false; + p.superscope.pauselock = false; + } else if(device == DeviceJustifier) { + p.justifier.active = 0; + p.justifier.x1 = 256 / 2; + p.justifier.y1 = 240 / 2; + p.justifier.x2 = -1; + p.justifier.y2 = -1; + + p.justifier.trigger1 = false; + p.justifier.trigger2 = false; + p.justifier.start1 = false; + p.justifier.start2 = false; + } else if(device == DeviceJustifiers) { + p.justifier.active = 0; + p.justifier.x1 = 256 / 2 - 16; + p.justifier.y1 = 240 / 2; + p.justifier.x2 = 256 / 2 + 16; + p.justifier.y2 = 240 / 2; + + p.justifier.trigger1 = false; + p.justifier.trigger2 = false; + p.justifier.start1 = false; + p.justifier.start2 = false; + } +} + +void Input::poll() { + port[0].counter0 = 0; + port[0].counter1 = 0; + port[1].counter0 = 0; + port[1].counter1 = 0; + + port[1].justifier.active = !port[1].justifier.active; +} + +void Input::init() { +} + +#endif diff --git a/mednafen/snes/src/system/input/input.hpp b/mednafen/snes/src/system/input/input.hpp new file mode 100755 index 0000000..80d8d4a --- /dev/null +++ b/mednafen/snes/src/system/input/input.hpp @@ -0,0 +1,94 @@ +class Input { +public: + enum Device { + DeviceNone, + DeviceJoypad, + DeviceMultitap, + DeviceMouse, + DeviceSuperScope, + DeviceJustifier, + DeviceJustifiers, + }; + + enum JoypadID { + JoypadB = 0, JoypadY = 1, + JoypadSelect = 2, JoypadStart = 3, + JoypadUp = 4, JoypadDown = 5, + JoypadLeft = 6, JoypadRight = 7, + JoypadA = 8, JoypadX = 9, + JoypadL = 10, JoypadR = 11, + }; + + enum MouseID { + MouseX = 0, MouseY = 1, + MouseLeft = 2, MouseRight = 3, + }; + + enum SuperScopeID { + SuperScopeX = 0, SuperScopeY = 1, + SuperScopeTrigger = 2, SuperScopeCursor = 3, + SuperScopeTurbo = 4, SuperScopePause = 5, + }; + + enum JustifierID { + JustifierX = 0, JustifierY = 1, + JustifierTrigger = 2, JustifierStart = 3, + }; + + uint8 port_read(bool port); + void port_set_device(bool port, unsigned device); + void init(); + void poll(); + void update(); + + //light guns (Super Scope, Justifier(s)) strobe IOBit whenever the CRT + //beam cannon is detected. this needs to be tested at the cycle level + //(hence inlining here for speed) to avoid 'dead space' during DRAM refresh. + //iobit is updated during port_set_device(), + //latchx, latchy are updated during update() (once per frame) + alwaysinline void tick() { + //only test if Super Scope or Justifier is connected + if(iobit && cpu.vcounter() == latchy && cpu.hcounter() == latchx) { + ppu.latch_counters(); + } + } + +private: + bool iobit; + int16_t latchx, latchy; + + struct port_t { + unsigned device; + unsigned counter0; //read counters + unsigned counter1; + + struct superscope_t { + int x, y; + + bool trigger; + bool cursor; + bool turbo; + bool pause; + bool offscreen; + + bool turbolock; + bool triggerlock; + bool pauselock; + } superscope; + + struct justifier_t { + bool active; + + int x1, x2; + int y1, y2; + + bool trigger1, trigger2; + bool start1, start2; + } justifier; + } port[2]; + + friend class System; + friend class Video; +}; + +extern Input input; diff --git a/mednafen/snes/src/system/interface/interface.hpp b/mednafen/snes/src/system/interface/interface.hpp new file mode 100755 index 0000000..4951870 --- /dev/null +++ b/mednafen/snes/src/system/interface/interface.hpp @@ -0,0 +1,7 @@ +class Interface { +public: + virtual void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) {} + virtual void audio_sample(uint16_t l_sample, uint16_t r_sample) {} + virtual void input_poll() {} + virtual int16_t input_poll(bool port, unsigned device, unsigned index, unsigned id) { return 0; } +}; diff --git a/mednafen/snes/src/system/scheduler/scheduler.cpp b/mednafen/snes/src/system/scheduler/scheduler.cpp new file mode 100755 index 0000000..9c4ddce --- /dev/null +++ b/mednafen/snes/src/system/scheduler/scheduler.cpp @@ -0,0 +1,67 @@ +#ifdef SYSTEM_CPP + +Scheduler scheduler; + +void threadentry_cpu() { cpu.enter(); } +void threadentry_cop() { system.coprocessor_enter(); } +void threadentry_smp() { smp.enter(); } +void threadentry_ppu() { ppu.enter(); } +void threadentry_dsp() { dsp.enter(); } + +void Scheduler::enter() { + co_switch(thread_active); +} + +void Scheduler::exit(ExitReason reason) { + exit_reason_ = reason; + co_switch(thread_snes); +} + +Scheduler::ExitReason Scheduler::exit_reason() const { + return exit_reason_; +} + +void Scheduler::init() { + clock.cpu_freq = system.region() == System::NTSC + ? config.cpu.ntsc_clock_rate + : config.cpu.pal_clock_rate; + clock.smp_freq = system.region() == System::NTSC + ? config.smp.ntsc_clock_rate + : config.smp.pal_clock_rate; + clock.cop_freq = clock.cpu_freq; + + clock.cpucop = 0; + clock.cpuppu = 0; + clock.cpusmp = 0; + clock.smpdsp = 0; + + if(thread_cpu) co_delete(thread_cpu); + if(thread_cop) co_delete(thread_cop); + if(thread_smp) co_delete(thread_smp); + if(thread_ppu) co_delete(thread_ppu); + if(thread_dsp) co_delete(thread_dsp); + + thread_snes = co_active(); + thread_cpu = co_create(65536 * sizeof(void*), threadentry_cpu); + thread_cop = co_create(65536 * sizeof(void*), threadentry_cop); + thread_smp = co_create(65536 * sizeof(void*), threadentry_smp); + thread_ppu = co_create(65536 * sizeof(void*), threadentry_ppu); + thread_dsp = co_create(65536 * sizeof(void*), threadentry_dsp); + + //start execution with S-CPU after reset + thread_active = thread_cpu; +} + +Scheduler::Scheduler() { + thread_snes = 0; + thread_cpu = 0; + thread_cop = 0; + thread_smp = 0; + thread_ppu = 0; + thread_dsp = 0; + thread_active = 0; + + exit_reason_ = UnknownEvent; +} + +#endif diff --git a/mednafen/snes/src/system/scheduler/scheduler.hpp b/mednafen/snes/src/system/scheduler/scheduler.hpp new file mode 100755 index 0000000..ac7b8df --- /dev/null +++ b/mednafen/snes/src/system/scheduler/scheduler.hpp @@ -0,0 +1,142 @@ +//scheduler thread relationships: +//S-PPU <-> S-CPU <-> cartridge co-processor +// <|> +// S-SMP <-> S-DSP + +class Scheduler { +public: + cothread_t thread_snes; + cothread_t thread_cpu; //S-CPU (5a22) + cothread_t thread_cop; //cartridge co-processor (SuperFX, SA-1, ...) + cothread_t thread_smp; //S-SMP (SPC700) + cothread_t thread_ppu; //S-PPU + cothread_t thread_dsp; //S-DSP + cothread_t thread_active; //reference to active thread + + struct { + uint32 cpu_freq; + uint32 cop_freq; + uint32 smp_freq; + + int64 cpucop; + int64 cpuppu; + int64 cpusmp; + int64 smpdsp; + } clock; + + enum sync_t { SyncNone, SyncCpu, SyncAll } sync; + + //========== + //CPU <> COP + //========== + + alwaysinline void sync_cpucop() { + if(clock.cpucop < 0) { + thread_active = thread_cop; + co_switch(thread_cop); + } + } + + alwaysinline void sync_copcpu() { + if(clock.cpucop >= 0 && sync != SyncAll) { + thread_active = thread_cpu; + co_switch(thread_cpu); + } + } + + //========== + //CPU <> PPU + //========== + + alwaysinline void sync_cpuppu() { + if(clock.cpuppu < 0) { + thread_active = thread_ppu; + co_switch(thread_ppu); + } + } + + alwaysinline void sync_ppucpu() { + if(clock.cpuppu >= 0 && sync != SyncAll) { + thread_active = thread_cpu; + co_switch(thread_cpu); + } + } + + //========== + //CPU <> SMP + //========== + + alwaysinline void sync_cpusmp() { + if(clock.cpusmp < 0) { + thread_active = thread_smp; + co_switch(thread_smp); + } + } + + alwaysinline void sync_smpcpu() { + if(clock.cpusmp >= 0 && sync != SyncAll) { + thread_active = thread_cpu; + co_switch(thread_cpu); + } + } + + //========== + //SMP <> DSP + //========== + + alwaysinline void sync_smpdsp() { + if(clock.smpdsp < 0 && sync != SyncAll) { + thread_active = thread_dsp; + co_switch(thread_dsp); + } + } + + alwaysinline void sync_dspsmp() { + if(clock.smpdsp >= 0 && sync != SyncAll) { + thread_active = thread_smp; + co_switch(thread_smp); + } + } + + //========== + //add clocks + //========== + + alwaysinline void addclocks_cpu(unsigned clocks) { + clock.cpucop -= clocks * (uint64)clock.cop_freq; + clock.cpuppu -= clocks; + clock.cpusmp -= clocks * (uint64)clock.smp_freq; + } + + alwaysinline void addclocks_cop(unsigned clocks) { + clock.cpucop += clocks * (uint64)clock.cpu_freq; + } + + alwaysinline void addclocks_ppu(unsigned clocks) { + clock.cpuppu += clocks; + } + + alwaysinline void addclocks_smp(unsigned clocks) { + clock.cpusmp += clocks * (uint64)clock.cpu_freq; + clock.smpdsp -= clocks; + } + + alwaysinline void addclocks_dsp(unsigned clocks) { + clock.smpdsp += clocks; + } + + enum ExitReason { UnknownEvent, FrameEvent, SynchronizeEvent, DebuggerEvent }; + + void enter(); + void exit(ExitReason); + ExitReason exit_reason() const; + + void init(); + + Scheduler(); + +private: + ExitReason exit_reason_; +}; + +extern Scheduler scheduler; diff --git a/mednafen/snes/src/system/serialization.cpp b/mednafen/snes/src/system/serialization.cpp new file mode 100755 index 0000000..aa43e6b --- /dev/null +++ b/mednafen/snes/src/system/serialization.cpp @@ -0,0 +1,97 @@ +#ifdef SYSTEM_CPP + +serializer System::serialize() { + serializer s(serialize_size); + + unsigned signature = 0x31545342, version = bsnesSerializerVersion, crc32 = cartridge.crc32(); + char description[512]; + + memset(&description, 0, sizeof description); + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + + serialize_all(s); + return s; +} + +bool System::unserialize(serializer &s) { + unsigned signature, version, crc32; + char description[512]; + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + + if(signature != 0x31545342) return false; + if(version != bsnesSerializerVersion) return false; +//if(crc32 != cartridge.crc32()) return false; + scheduler.init(); + + serialize_all(s); + return true; +} + +//======== +//internal +//======== + +void System::serialize(serializer &s) { + s.integer((unsigned&)region); + s.integer((unsigned&)expansion); + + s.integer(scheduler.clock.cpu_freq); + s.integer(scheduler.clock.smp_freq); + + s.integer(scheduler.clock.cpucop); + s.integer(scheduler.clock.cpuppu); + s.integer(scheduler.clock.cpusmp); + s.integer(scheduler.clock.smpdsp); +} + +void System::serialize_all(serializer &s) { + bus.serialize(s); + cartridge.serialize(s); + system.serialize(s); + cpu.serialize(s); + smp.serialize(s); + ppu.serialize(s); + dsp.serialize(s); + + if(cartridge.mode() == Cartridge::ModeSuperGameBoy) supergameboy.serialize(s); + + if(cartridge.has_superfx()) superfx.serialize(s); + if(cartridge.has_sa1()) sa1.serialize(s); + if(cartridge.has_srtc()) srtc.serialize(s); + if(cartridge.has_sdd1()) sdd1.serialize(s); + if(cartridge.has_spc7110()) spc7110.serialize(s); + if(cartridge.has_cx4()) cx4.serialize(s); + if(cartridge.has_dsp1()) dsp1.serialize(s); + if(cartridge.has_dsp2()) dsp2.serialize(s); + if(cartridge.has_obc1()) obc1.serialize(s); + if(cartridge.has_st010()) st010.serialize(s); + if(cartridge.has_21fx()) s21fx.serialize(s); +} + +//called once upon cartridge load event: perform dry-run state save. +//determines exactly how many bytes are needed to save state for this cartridge, +//as amount varies per game (eg different RAM sizes, special chips, etc.) +void System::serialize_init() { + serializer s; + + unsigned signature = 0, version = 0, crc32 = 0; + char description[512]; + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + + serialize_all(s); + serialize_size = s.size(); +} + +#endif diff --git a/mednafen/snes/src/system/system.cpp b/mednafen/snes/src/system/system.cpp new file mode 100755 index 0000000..530e1a1 --- /dev/null +++ b/mednafen/snes/src/system/system.cpp @@ -0,0 +1,245 @@ +#include <../base.hpp> + +#define SYSTEM_CPP +namespace SNES { + +System system; + +#include "config/config.cpp" +#include "debugger/debugger.cpp" +#include "serialization.cpp" +#include "scheduler/scheduler.cpp" + +#include "video/video.cpp" +#include "audio/audio.cpp" +#include "input/input.cpp" + +void System::coprocessor_enter() { + if(cartridge.mode() == Cartridge::ModeSuperGameBoy) supergameboy.enter(); + if(cartridge.has_superfx()) superfx.enter(); + if(cartridge.has_sa1()) sa1.enter(); + if(cartridge.has_21fx()) s21fx.enter(); + + while(true) { + while(scheduler.sync == Scheduler::SyncAll) { + scheduler.exit(Scheduler::SynchronizeEvent); + } + + scheduler.addclocks_cop(64 * 1024 * 1024); + scheduler.sync_copcpu(); + } +} + +void System::run_mednafen_custom() +{ + //if(scheduler.exit_reason() == Scheduler::FrameEvent) // Commenting this out, causes latency when state rewinding is enabled. + input.update(); + + scheduler.sync = Scheduler::SyncNone; + + scheduler.enter(); + if(scheduler.exit_reason() == Scheduler::FrameEvent) { + //input.update(); + video.update(); + } +} + +void System::run() { + scheduler.sync = Scheduler::SyncNone; + + scheduler.enter(); + if(scheduler.exit_reason() == Scheduler::FrameEvent) { + input.update(); + video.update(); + } +} + +void System::runtosave() { + scheduler.sync = Scheduler::SyncCpu; + runthreadtosave(); + + scheduler.thread_active = scheduler.thread_cop; + runthreadtosave(); + + scheduler.thread_active = scheduler.thread_smp; + runthreadtosave(); + + scheduler.thread_active = scheduler.thread_ppu; + runthreadtosave(); + + #if !defined(DSP_STATE_MACHINE) + scheduler.thread_active = scheduler.thread_dsp; + runthreadtosave(); + #endif +} + +void System::runthreadtosave() { + while(true) { + scheduler.enter(); + if(scheduler.exit_reason() == Scheduler::SynchronizeEvent) break; + if(scheduler.exit_reason() == Scheduler::FrameEvent) { + input.update(); + video.update(); + } + } +} + +void System::init(Interface *interface_) { + interface = interface_; + assert(interface != 0); + + supergameboy.init(); + superfx.init(); + sa1.init(); + bsxbase.init(); + bsxcart.init(); + bsxflash.init(); + srtc.init(); + sdd1.init(); + spc7110.init(); + cx4.init(); + dsp1.init(); + dsp2.init(); + dsp3.init(); + dsp4.init(); + obc1.init(); + st010.init(); + st011.init(); + st018.init(); + s21fx.init(); + + video.init(); + audio.init(); + input.init(); +} + +void System::term() { +} + +void System::power() { + region = max(0, min(2, config.region)); + expansion = max(0, min(1, config.expansion_port)); + + if(region == Autodetect) { + region = (cartridge.region() == Cartridge::NTSC ? NTSC : PAL); + } + + audio.coprocessor_enable(false); + + scheduler.init(); + bus.power(); + + for(unsigned i = 0x2100; i <= 0x213f; i++) memory::mmio.map(i, ppu); + for(unsigned i = 0x2140; i <= 0x217f; i++) memory::mmio.map(i, cpu); + for(unsigned i = 0x2180; i <= 0x2183; i++) memory::mmio.map(i, cpu); + for(unsigned i = 0x4016; i <= 0x4017; i++) memory::mmio.map(i, cpu); + for(unsigned i = 0x4200; i <= 0x421f; i++) memory::mmio.map(i, cpu); + for(unsigned i = 0x4300; i <= 0x437f; i++) memory::mmio.map(i, cpu); + + if(expansion() == ExpansionBSX) bsxbase.enable(); + if(memory::bsxflash.data()) bsxflash.enable(); + if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.enable(); + if(cartridge.mode() == Cartridge::ModeSuperGameBoy) supergameboy.enable(); + + if(cartridge.has_superfx()) superfx.enable(); + if(cartridge.has_sa1()) sa1.enable(); + if(cartridge.has_srtc()) srtc.enable(); + if(cartridge.has_sdd1()) sdd1.enable(); + if(cartridge.has_spc7110()) spc7110.enable(); + if(cartridge.has_cx4()) cx4.enable(); + if(cartridge.has_dsp1()) dsp1.enable(); + if(cartridge.has_dsp2()) dsp2.enable(); + if(cartridge.has_dsp3()) dsp3.enable(); + if(cartridge.has_dsp4()) dsp4.enable(); + if(cartridge.has_obc1()) obc1.enable(); + if(cartridge.has_st010()) st010.enable(); + if(cartridge.has_st011()) st011.enable(); + if(cartridge.has_st018()) st018.enable(); + if(cartridge.has_21fx()) s21fx.enable(); + + if(expansion() == ExpansionBSX) bsxbase.power(); + if(memory::bsxflash.data()) bsxflash.power(); + if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.power(); + if(cartridge.mode() == Cartridge::ModeSuperGameBoy) supergameboy.power(); + + if(cartridge.has_superfx()) superfx.power(); + if(cartridge.has_sa1()) sa1.power(); + if(cartridge.has_srtc()) srtc.power(); + if(cartridge.has_sdd1()) sdd1.power(); + if(cartridge.has_spc7110()) spc7110.power(); + if(cartridge.has_cx4()) cx4.power(); + if(cartridge.has_dsp1()) dsp1.power(); + if(cartridge.has_dsp2()) dsp2.power(); + if(cartridge.has_dsp3()) dsp3.power(); + if(cartridge.has_dsp4()) dsp4.power(); + if(cartridge.has_obc1()) obc1.power(); + if(cartridge.has_st010()) st010.power(); + if(cartridge.has_st011()) st011.power(); + if(cartridge.has_st018()) st018.power(); + if(cartridge.has_21fx()) s21fx.power(); + + cpu.power(); + smp.power(); + dsp.power(); + ppu.power(); + + input.port_set_device(0, config.controller_port1); + input.port_set_device(1, config.controller_port2); + input.update(); + video.update(); +} + +void System::reset() { + scheduler.init(); + + cpu.reset(); + smp.reset(); + dsp.reset(); + ppu.reset(); + bus.reset(); + + if(expansion() == ExpansionBSX) bsxbase.reset(); + if(memory::bsxflash.data()) bsxflash.reset(); + if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.reset(); + if(cartridge.mode() == Cartridge::ModeSuperGameBoy) supergameboy.reset(); + + if(cartridge.has_superfx()) superfx.reset(); + if(cartridge.has_sa1()) sa1.reset(); + if(cartridge.has_srtc()) srtc.reset(); + if(cartridge.has_sdd1()) sdd1.reset(); + if(cartridge.has_spc7110()) spc7110.reset(); + if(cartridge.has_cx4()) cx4.reset(); + if(cartridge.has_dsp1()) dsp1.reset(); + if(cartridge.has_dsp2()) dsp2.reset(); + if(cartridge.has_dsp3()) dsp3.reset(); + if(cartridge.has_dsp4()) dsp4.reset(); + if(cartridge.has_obc1()) obc1.reset(); + if(cartridge.has_st010()) st010.reset(); + if(cartridge.has_st011()) st011.reset(); + if(cartridge.has_st018()) st018.reset(); + if(cartridge.has_21fx()) s21fx.reset(); + + input.port_set_device(0, config.controller_port1); + input.port_set_device(1, config.controller_port2); + input.update(); + video.update(); +} + +void System::unload() { + if(cartridge.mode() == Cartridge::ModeSuperGameBoy) supergameboy.unload(); +} + +void System::scanline() { + video.scanline(); + if(cpu.vcounter() == 241) scheduler.exit(Scheduler::FrameEvent); +} + +void System::frame() { +} + +System::System() : interface(0) { + region = NTSC; + expansion = ExpansionNone; +} + +}; diff --git a/mednafen/snes/src/system/system.hpp b/mednafen/snes/src/system/system.hpp new file mode 100755 index 0000000..6b7e96d --- /dev/null +++ b/mednafen/snes/src/system/system.hpp @@ -0,0 +1,58 @@ +#include "config/config.hpp" +#include "debugger/debugger.hpp" +#include "interface/interface.hpp" +#include "scheduler/scheduler.hpp" + +#include "video/video.hpp" +#include "audio/audio.hpp" +#include "input/input.hpp" + +class System : property { +public: + enum Region { NTSC = 0, PAL = 1 }; + enum RegionAutodetect { Autodetect = 2 }; + enum ExpansionPortDevice { ExpansionNone = 0, ExpansionBSX = 1 }; + + void run_mednafen_custom(); + + void run(); + void runtosave(); + + void init(Interface*); + void term(); + void power(); + void reset(); + void unload(); + + void frame(); + void scanline(); + + //return *active* system information (settings are cached upon power-on) + readonly region; + readonly expansion; + readonly serialize_size; + + serializer serialize(); + bool unserialize(serializer&); + + System(); + virtual ~System() {} + +private: + Interface *interface; + void coprocessor_enter(); + void runthreadtosave(); + + void serialize(serializer&); + void serialize_all(serializer&); + void serialize_init(); + + friend class Cartridge; + friend class Video; + friend class Audio; + friend class Input; + friend class StateManager; + friend void threadentry_cop(); +}; + +extern System system; diff --git a/mednafen/snes/src/system/video/video.cpp b/mednafen/snes/src/system/video/video.cpp new file mode 100755 index 0000000..9b97b36 --- /dev/null +++ b/mednafen/snes/src/system/video/video.cpp @@ -0,0 +1,105 @@ +#ifdef SYSTEM_CPP + +Video video; + +const uint8_t Video::cursor[15 * 15] = { + 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, + 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, + 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, + 0,0,1,2,1,1,0,1,0,1,1,2,1,0,0, + 0,1,2,1,0,0,0,1,0,0,0,1,2,1,0, + 0,1,2,1,0,0,1,2,1,0,0,1,2,1,0, + 1,2,1,0,0,1,1,2,1,1,0,0,1,2,1, + 1,2,2,1,1,2,2,2,2,2,1,1,2,2,1, + 1,2,1,0,0,1,1,2,1,1,0,0,1,2,1, + 0,1,2,1,0,0,1,2,1,0,0,1,2,1,0, + 0,1,2,1,0,0,0,1,0,0,0,1,2,1,0, + 0,0,1,2,1,1,0,1,0,1,1,2,1,0,0, + 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, + 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, + 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, +}; + +void Video::draw_cursor(uint16_t color, int x, int y) { + for(int cy = 0; cy < 15; cy++) { + int vy = y + cy - 7; + if(vy <= 0 || vy >= 240) continue; //do not draw offscreen + + bool hires = (pline_width[vy] == 512); + for(int cx = 0; cx < 15; cx++) { + int vx = x + cx - 7; + if(vx < 0 || vx >= 256) continue; //do not draw offscreen + uint8_t pixel = cursor[cy * 15 + cx]; + if(pixel == 0) continue; + uint16_t pixelcolor = (pixel == 1) ? 0 : color; + + if(hires == false) { + *((uint16_t*)ppu.output + vy * 1024 + 0 + vx) = pixelcolor; + *((uint16_t*)ppu.output + vy * 1024 + 512 + vx) = pixelcolor; + } else { + *((uint16_t*)ppu.output + vy * 1024 + 0 + vx * 2 + 0) = pixelcolor; + *((uint16_t*)ppu.output + vy * 1024 + 512 + vx * 2 + 0) = pixelcolor; + *((uint16_t*)ppu.output + vy * 1024 + 0 + vx * 2 + 1) = pixelcolor; + *((uint16_t*)ppu.output + vy * 1024 + 512 + vx * 2 + 1) = pixelcolor; + } + } + } +} + +void Video::update() { + uint16_t *data = (uint16_t*)ppu.output; + unsigned width, height; + + switch(input.port[1].device) { + case Input::DeviceSuperScope: draw_cursor(0x001f, input.port[1].superscope.x, input.port[1].superscope.y); break; + case Input::DeviceJustifiers: draw_cursor(0x02e0, input.port[1].justifier.x2, input.port[1].justifier.y2); //fallthrough + case Input::DeviceJustifier: draw_cursor(0x001f, input.port[1].justifier.x1, input.port[1].justifier.y1); break; + } + + unsigned yoffset = 1; //scanline 0 is always black, skip this line for video output + if(mode == ModeNTSC && ppu.overscan()) yoffset += 8; //NTSC overscan centers x240 height image + + switch(mode) { default: + case ModeNTSC: { width = 256; height = 224; } break; + case ModePAL: { width = 256; height = 239; } break; + } + + if(frame_hires) width <<= 1; + if(frame_interlace) height <<= 1; + + system.interface->video_refresh( + data + yoffset * 1024, + /* pitch = */ height <= 240 ? 2048 : 1024, + /* line[] = */ height <= 240 ? (pline_width + yoffset) : (iline_width + yoffset * 2), + width, height + ); + + frame_hires = false; + frame_interlace = false; +} + +void Video::scanline() { + unsigned y = cpu.vcounter(); + if(y >= 240) return; + + unsigned width = (ppu.hires() == false ? 256 : 512); + pline_width[y] = width; + iline_width[y * 2 + (int)cpu.field()] = width; + + frame_hires |= ppu.hires(); + frame_interlace |= ppu.interlace(); +} + +void Video::set_mode(Mode mode_) { + mode = mode_; +} + +void Video::init() { + for(unsigned i = 0; i < 240; i++) pline_width[i] = 256; + for(unsigned i = 0; i < 480; i++) iline_width[i] = 256; + frame_hires = false; + frame_interlace = false; + set_mode(ModeNTSC); +} + +#endif diff --git a/mednafen/snes/src/system/video/video.hpp b/mednafen/snes/src/system/video/video.hpp new file mode 100755 index 0000000..e93fa24 --- /dev/null +++ b/mednafen/snes/src/system/video/video.hpp @@ -0,0 +1,27 @@ +class Video { +public: + enum Mode { + ModeNTSC, + ModePAL, + }; + void set_mode(Mode); + +private: + Mode mode; + bool frame_hires; + bool frame_interlace; + + unsigned pline_width[240]; //progressive + unsigned iline_width[480]; //interlace + + void update(); + void scanline(); + void init(); + + static const uint8_t cursor[15 * 15]; + void draw_cursor(uint16_t color, int x, int y); + + friend class System; +}; + +extern Video video; diff --git a/mednafen/snes/src/ui_qt/Makefile b/mednafen/snes/src/ui_qt/Makefile new file mode 100755 index 0000000..32f38db --- /dev/null +++ b/mednafen/snes/src/ui_qt/Makefile @@ -0,0 +1,48 @@ +objects := ui-main ui-base ui-cartridge ui-debugger ui-input ui-movie ui-settings ui-state ui-tools $(objects) +objects += $(if $(call streq,$(platform),win),resource) +link += $(qtlib) + +headers := $(call rwildcard,$(ui)/,%.hpp) +moc_headers := $(call rwildcard,$(ui)/,%.moc.hpp) +moc_objects := $(foreach f,$(moc_headers),obj/$(notdir $(patsubst %.moc.hpp,%.moc,$f))) +qt_compile = $(call compile,-Iobj $(qtinc)) + +############# +### rules ### +############# + +# automatically run moc on all .moc.hpp (MOC header) files +%.moc: $<; $(moc) -i $< -o $@ + +# automatically generate %.moc build rules +__list = $(moc_headers) +$(foreach f,$(moc_objects), \ + $(eval __file = $(word 1,$(__list))) \ + $(eval __list = $(wordlist 2,$(words $(__list)),$(__list))) \ + $(eval $f: $(__file)) \ +) + +obj/ui-main.o: $(ui)/main.cpp $(headers) $(wildcard $(ui)/*.cpp) $(wildcard $(ui)/platform/*.cpp) $(wildcard $(ui)/utility/*.cpp); $(qt_compile) +obj/ui-base.o: $(ui)/base/base.cpp $(headers) $(wildcard $(ui)/base/*.cpp); $(qt_compile) +obj/ui-cartridge.o: $(ui)/cartridge/cartridge.cpp $(headers) $(wildcard $(ui)/cartridge/*.cpp); $(qt_compile) +obj/ui-debugger.o: $(ui)/debugger/debugger.cpp $(headers) $(call rwildcard,$(ui)/debugger/,%.cpp); $(qt_compile) +obj/ui-input.o: $(ui)/input/input.cpp $(headers) $(wildcard $(ui)/input/*.cpp); $(qt_compile) +obj/ui-movie.o: $(ui)/movie/movie.cpp $(headers) $(wildcard $(ui)/movie/*.cpp); $(qt_compile) +obj/ui-settings.o: $(ui)/settings/settings.cpp $(headers) $(wildcard $(ui)/settings/*.cpp); $(qt_compile) +obj/ui-state.o: $(ui)/state/state.cpp $(headers) $(wildcard $(ui)/state/*.cpp); $(qt_compile) +obj/ui-tools.o: $(ui)/tools/tools.cpp $(headers) $(wildcard $(ui)/tools/*.cpp); $(qt_compile) + +obj/resource.rcc: $(ui)/resource/resource.qrc data/* + $(rcc) $(ui)/resource/resource.qrc -o obj/resource.rcc + +obj/resource.o: $(ui)/resource/resource.rc + windres $(ui)/resource/resource.rc obj/resource.o + +############### +### targets ### +############### + +ui_build: obj/resource.rcc $(moc_objects); +ui_clean: + -@$(call delete,obj/*.rcc) + -@$(call delete,obj/*.moc) diff --git a/mednafen/snes/src/ui_qt/application/application.cpp b/mednafen/snes/src/ui_qt/application/application.cpp new file mode 100755 index 0000000..4883c69 --- /dev/null +++ b/mednafen/snes/src/ui_qt/application/application.cpp @@ -0,0 +1,172 @@ +#include "application.moc" +Application application; + +#include "init.cpp" +#include "qb.cpp" + +void Application::initPaths(const char *basename) { + char temp[PATH_MAX]; + + if(realpath(basename, temp)) { + //remove program name + strtr(temp, "\\", "/"); + for(signed i = strlen(temp) - 1; i >= 0; i--) { + if(temp[i] == '/') { + temp[i] = 0; + break; + } + } + + if(strend(temp, "/") == false) strcat(temp, "/"); + config().path.base = temp; + } else { + config().path.base = ""; + } + + if(userpath(temp)) { + strtr(temp, "\\", "/"); + if(strend(temp, "/") == false) strcat(temp, "/"); + config().path.user = temp; + } else { + config().path.user = ""; + } + + char cwd[PATH_MAX]; + config().path.startup = getcwd(cwd); +} + +void Application::locateFile(string &filename, bool createDataDirectory) { + //first, check if file exists in executable directory (single-user mode) + string temp = string() << config().path.base << filename; + + if(file::exists(temp) == false) { + //if not, use user data path (multi-user mode) + temp = config().path.user; + temp << ".bsnes"; + if(createDataDirectory) mkdir(temp); //ensure directory exists + temp << "/" << filename; + } + + filename = temp; +} + +int Application::main(int &argc, char **argv) { + app = new App(argc, argv); + #if !defined(PLATFORM_WIN) + //Windows port uses 256x256 icon from resource file + app->setWindowIcon(QIcon(":/bsnes.png")); + #endif + + initargs(argc, argv); //ensure argv[]s are in UTF-8 format + initPaths(argv[0]); + locateFile(configFilename = "bsnes.cfg", true); + locateFile(styleSheetFilename = "style.qss", false); + + string customStylesheet; + if(customStylesheet.readfile(styleSheetFilename) == true) { + app->setStyleSheet((const char*)customStylesheet); + } else { + app->setStyleSheet(defaultStylesheet); + } + + config().load(configFilename); + mapper().bind(); + init(); + SNES::system.init(&interface); + mainWindow->system_loadSpecial_superGameBoy->setVisible(SNES::supergameboy.opened()); + + if(argc == 2) { + //if valid file was specified on the command-line, attempt to load it now + cartridge.loadNormal(argv[1]); + } + + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(run())); + timer->start(0); + app->exec(); + + //QbWindow::hide() saves window geometry for next run + for(unsigned i = 0; i < windowList.size(); i++) { + windowList[i]->hide(); + } + + cartridge.unload(); + config().save(configFilename); + return 0; +} + +void Application::run() { + if(terminate == true) { + timer->stop(); + app->quit(); + return; + } + + QApplication::processEvents(); + utility.updateSystemState(); + mapper().poll(); + + if(config().input.focusPolicy == Configuration::Input::FocusPolicyPauseEmulation) { + bool active = mainWindow->isActive(); + if(!autopause && !active) { + autopause = true; + audio.clear(); + } else if(autopause && active) { + autopause = false; + } + } else { + autopause = false; + } + + if(SNES::cartridge.loaded() && !pause && !autopause && (!debug || debugrun)) { + SNES::system.run(); + #if defined(DEBUGGER) + if(SNES::debugger.break_event != SNES::Debugger::None) { + debug = true; + debugrun = false; + debugger->synchronize(); + debugger->event(); + SNES::debugger.break_event = SNES::Debugger::None; + } + #endif + } else { + usleep(20 * 1000); + } + + clock_t currentTime = clock(); + autosaveTime += currentTime - clockTime; + screensaverTime += currentTime - clockTime; + clockTime = currentTime; + + if(autosaveTime >= CLOCKS_PER_SEC * 60) { + //auto-save RAM once per minute in case of emulator crash + autosaveTime = 0; + if(config().system.autoSaveMemory == true) cartridge.saveMemory(); + } + + if(screensaverTime >= CLOCKS_PER_SEC * 30) { + //supress screen saver every 30 seconds so it will not trigger during gameplay + screensaverTime = 0; + supressScreenSaver(); + } +} + +Application::Application() : timer(0) { + terminate = false; + power = false; + pause = false; + autopause = false; + debug = false; + debugrun = false; + + clockTime = clock(); + autosaveTime = 0; + screensaverTime = 0; +} + +Application::~Application() { + delete timer; + + //deleting (QApplication)app will segfault the application upon exit + //delete app; +} diff --git a/mednafen/snes/src/ui_qt/application/application.moc.hpp b/mednafen/snes/src/ui_qt/application/application.moc.hpp new file mode 100755 index 0000000..db879f5 --- /dev/null +++ b/mednafen/snes/src/ui_qt/application/application.moc.hpp @@ -0,0 +1,46 @@ +#include "qb.hpp" + +class Application : public QObject { + Q_OBJECT + +public: + class App : public QApplication { + public: + #if defined(PLATFORM_WIN) + bool winEventFilter(MSG *msg, long *result); + #endif + + App(int &argc, char **argv) : QApplication(argc, argv) {} + } *app; + + QTimer *timer; + + bool terminate; //set to true to terminate main() loop and exit emulator + bool power; + bool pause; + bool autopause; + bool debug; //debugger sets this to true when entered to suspend emulation + bool debugrun; //debugger sets this to true to run emulation to a debug event + + clock_t clockTime; + clock_t autosaveTime; + clock_t screensaverTime; + + string configFilename; + string styleSheetFilename; + + array windowList; + + int main(int &argc, char **argv); + void locateFile(string &filename, bool createDataDirectory = false); + void initPaths(const char *basename); + void init(); + + Application(); + ~Application(); + +public slots: + void run(); +}; + +extern Application application; diff --git a/mednafen/snes/src/ui_qt/application/init.cpp b/mednafen/snes/src/ui_qt/application/init.cpp new file mode 100755 index 0000000..a573741 --- /dev/null +++ b/mednafen/snes/src/ui_qt/application/init.cpp @@ -0,0 +1,108 @@ +void Application::init() { + if(config().system.crashedOnLastRun == true) { + //emulator crashed on last run, disable all drivers + QMessageBox::warning(0, "bsnes Crash Notification", string() << + "

Warning:
bsnes crashed while attempting to initialize device " + "drivers the last time it was run.

" + "

To prevent this from occurring again, all drivers have been disabled. Please " + "go to Settings->Configuration->Advanced and choose new driver settings, and then " + "restart the emulator for the changes to take effect. Video, audio and input " + "will not work until you do this!

" + "

Settings that caused failure on last run:
" + << "Video driver: " << config().system.video << "
" + << "Audio driver: " << config().system.audio << "
" + << "Input driver: " << config().system.input << "

" + ); + + config().system.video = "None"; + config().system.audio = "None"; + config().system.input = "None"; + } + + if(config().system.video == "") config().system.video = video.default_driver(); + if(config().system.audio == "") config().system.audio = audio.default_driver(); + if(config().system.input == "") config().system.input = input.default_driver(); + + mainWindow = new MainWindow; + loaderWindow = new LoaderWindow; + htmlViewerWindow = new HtmlViewerWindow; + aboutWindow = new AboutWindow; + diskBrowser = new DiskBrowser; + + //window must be onscreen and visible before initializing video interface + utility.updateSystemState(); + utility.resizeMainWindow(); + utility.updateFullscreenState(); + QApplication::processEvents(); + + #if defined(DEBUGGER) + debugger = new Debugger; + #endif + settingsWindow = new SettingsWindow; + toolsWindow = new ToolsWindow; + + //if emulator crashes while initializing drivers, next run will disable them all. + //this will allow user to choose different driver settings. + config().system.crashedOnLastRun = true; + config().save(configFilename); + + video.driver(config().system.video); + video.set(Video::Handle, (uintptr_t)mainWindow->canvas->winId()); + video.set("QWidget", (QWidget*)mainWindow->canvas); + if(video.init() == false) { + QMessageBox::warning(0, "bsnes", string() << + "

Warning: " << config().system.video << " video driver failed to initialize. " + "Video driver has been disabled.

" + "

Please go to Settings->Configuration->Advanced and choose a different driver, and " + "then restart the emulator for the changes to take effect.

" + ); + video.driver("None"); + video.init(); + } + + audio.driver(config().system.audio); + audio.set(Audio::Handle, (uintptr_t)mainWindow->canvas->winId()); + audio.set(Audio::Frequency, config().audio.outputFrequency); + audio.set(Audio::Latency, config().audio.latency); + audio.set(Audio::Volume, config().audio.volume); + if(audio.init() == false) { + QMessageBox::warning(0, "bsnes", string() << + "

Warning: " << config().system.audio << " audio driver failed to initialize. " + "Audio driver has been disabled.

" + "

Please go to Settings->Configuration->Advanced and choose a different driver, and " + "then restart the emulator for the changes to take effect.

" + ); + audio.driver("None"); + audio.init(); + } + + input.driver(config().system.input); + input.set("Handle", (uintptr_t)mainWindow->canvas->winId()); + if(input.init() == false) { + QMessageBox::warning(0, "bsnes", string() << + "

Warning: " << config().system.input << " input driver failed to initialize. " + "Input driver has been disabled.

" + "

Please go to Settings->Configuration->Advanced and choose a different driver, and " + "then restart the emulator for the changes to take effect.

" + ); + input.driver("None"); + input.init(); + } + + //didn't crash, note this in the config file now in case a different kind of crash occurs later + config().system.crashedOnLastRun = false; + config().save(configFilename); + + //no sense showing unusable options ... + pixelShaderWindow->setVisible(video.cap(Video::FragmentShader) || video.cap(Video::VertexShader)); + + utility.resizeMainWindow(); + utility.updateAvSync(); + utility.updateVideoMode(); + utility.updateColorFilter(); + utility.updatePixelShader(); + utility.updateHardwareFilter(); + utility.updateSoftwareFilter(); + utility.updateEmulationSpeed(); + utility.updateControllers(); +} diff --git a/mednafen/snes/src/ui_qt/application/qb.cpp b/mednafen/snes/src/ui_qt/application/qb.cpp new file mode 100755 index 0000000..d544765 --- /dev/null +++ b/mednafen/snes/src/ui_qt/application/qb.cpp @@ -0,0 +1,101 @@ +void QbWindow::setCloseOnEscape(bool state) { + closeOnEscape = state; +} + +void QbWindow::shrink() { + if(config().video.isFullscreen == false) { + for(unsigned i = 0; i < 2; i++) { + resize(0, 0); + usleep(2000); + QApplication::processEvents(); + } + } +} + +void QbWindow::show() { + if(isVisible() == false) { + uint8_t *data; + unsigned length; + base64::decode(data, length, geometryString); + QByteArray array((const char*)data, length); + delete[] data; + restoreGeometry(array); + QWidget::show(); + } + + QApplication::processEvents(); + activateWindow(); + raise(); +} + +void QbWindow::hide() { + if(isVisible() == true) { + char *data; + QByteArray geometry = saveGeometry(); + base64::encode(data, (const uint8_t*)geometry.data(), geometry.length()); + geometryString = data; + delete[] data; + QWidget::hide(); + } +} + +void QbWindow::closeEvent(QCloseEvent *event) { + char *data; + QByteArray geometry = saveGeometry(); + base64::encode(data, (const uint8_t*)geometry.data(), geometry.length()); + geometryString = data; + delete[] data; + QWidget::hide(); +} + +void QbWindow::keyReleaseEvent(QKeyEvent *event) { + if((closeOnEscape == true) && (event->key() == Qt::Key_Escape)) close(); + QWidget::keyReleaseEvent(event); +} + +QbWindow::QbWindow(string &geometryString_) : geometryString(geometryString_) { + closeOnEscape = true; + + //keep track of all created windows (for geometry save on exit, always-on-top control, etc) + application.windowList.add(this); +} + +// + +bool QbCheckAction::isChecked() const { + return checked; +} + +void QbCheckAction::setChecked(bool checked_) { + checked = checked_; + if(checked) setIcon(QIcon(":/16x16/item-check-on.png")); + else setIcon(QIcon(":/16x16/item-check-off.png")); +} + +void QbCheckAction::toggleChecked() { + setChecked(!isChecked()); +} + +QbCheckAction::QbCheckAction(const QString &text, QObject *parent) : QAction(text, parent) { + setChecked(false); +} + +// + +bool QbRadioAction::isChecked() const { + return checked; +} + +void QbRadioAction::setChecked(bool checked_) { + checked = checked_; + if(checked) setIcon(QIcon(":/16x16/item-radio-on.png")); + else setIcon(QIcon(":/16x16/item-radio-off.png")); +} + +void QbRadioAction::toggleChecked() { + setChecked(!isChecked()); +} + +QbRadioAction::QbRadioAction(const QString &text, QObject *parent) : QAction(text, parent) { + setChecked(false); +} diff --git a/mednafen/snes/src/ui_qt/application/qb.hpp b/mednafen/snes/src/ui_qt/application/qb.hpp new file mode 100755 index 0000000..62f161c --- /dev/null +++ b/mednafen/snes/src/ui_qt/application/qb.hpp @@ -0,0 +1,36 @@ +class QbWindow : public QWidget { +public: + void setCloseOnEscape(bool); + void shrink(); + void show(); + void hide(); + void closeEvent(QCloseEvent*); + void keyReleaseEvent(QKeyEvent*); + QbWindow(string&); + +private: + string &geometryString; + bool closeOnEscape; +}; + +class QbCheckAction : public QAction { +public: + bool isChecked() const; + void setChecked(bool); + void toggleChecked(); + QbCheckAction(const QString&, QObject*); + +private: + bool checked; +}; + +class QbRadioAction : public QAction { +public: + bool isChecked() const; + void setChecked(bool); + void toggleChecked(); + QbRadioAction(const QString&, QObject*); + +private: + bool checked; +}; diff --git a/mednafen/snes/src/ui_qt/base/about.cpp b/mednafen/snes/src/ui_qt/base/about.cpp new file mode 100755 index 0000000..b2be48b --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/about.cpp @@ -0,0 +1,32 @@ +#include "about.moc" +AboutWindow *aboutWindow; + +AboutWindow::AboutWindow() : QbWindow(config().geometry.aboutWindow) { + setObjectName("about-window"); + setWindowTitle("About bsnes ..."); + + layout = new QVBoxLayout; + layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + logo = new Logo; + logo->setFixedSize(600, 106); + layout->addWidget(logo); + + info = new QLabel(string() << + "" + "" + "" + "" + "
Version: " << bsnesVersion << "
Author: byuu
Homepage: http://byuu.org/
" + ); + layout->addWidget(info); +} + +void AboutWindow::Logo::paintEvent(QPaintEvent*) { + QPainter painter(this); + QPixmap pixmap(":/logo.png"); + painter.drawPixmap(0, 0, pixmap); +} diff --git a/mednafen/snes/src/ui_qt/base/about.moc.hpp b/mednafen/snes/src/ui_qt/base/about.moc.hpp new file mode 100755 index 0000000..381bbdd --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/about.moc.hpp @@ -0,0 +1,14 @@ +class AboutWindow : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + struct Logo : public QWidget { + void paintEvent(QPaintEvent*); + } *logo; + QLabel *info; + + AboutWindow(); +}; + +extern AboutWindow *aboutWindow; diff --git a/mednafen/snes/src/ui_qt/base/base.cpp b/mednafen/snes/src/ui_qt/base/base.cpp new file mode 100755 index 0000000..3fe11b4 --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/base.cpp @@ -0,0 +1,7 @@ +#include "../ui-base.hpp" + +#include "about.cpp" +#include "diskbrowser.cpp" +#include "htmlviewer.cpp" +#include "loader.cpp" +#include "main.cpp" diff --git a/mednafen/snes/src/ui_qt/base/diskbrowser.cpp b/mednafen/snes/src/ui_qt/base/diskbrowser.cpp new file mode 100755 index 0000000..f102016 --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/diskbrowser.cpp @@ -0,0 +1,601 @@ +#include "diskbrowser.moc" +FolderCreator *folderCreator; +DiskBrowser *diskBrowser; + +//============= +//FolderCreator +//============= + +FolderCreator::FolderCreator() : QbWindow(config().geometry.folderCreator) { + setObjectName("folder-creator"); + setWindowTitle("Create New Folder"); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + label = new QLabel("Folder name:"); + layout->addWidget(label); + + name = new QLineEdit; + layout->addWidget(name); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + ok = new QPushButton("Ok"); + controlLayout->addWidget(ok); + + cancel = new QPushButton("Cancel"); + controlLayout->addWidget(cancel); + + connect(name, SIGNAL(returnPressed()), this, SLOT(createFolder())); + connect(ok, SIGNAL(released()), this, SLOT(createFolder())); + connect(cancel, SIGNAL(released()), this, SLOT(close())); +} + +void FolderCreator::show() { + name->setText(""); + QbWindow::show(); + name->setFocus(); +} + +void FolderCreator::createFolder() { + if(name->text().length() == 0) { + QMessageBox::warning(0, "Create New Folder", "Note: you must enter a folder name."); + } else { + string folderName = string() + << diskBrowser->model->rootPath().toUtf8().constData() + << "/" + << name->text().toUtf8().constData(); + + if(mkdir(folderName) == 0) { + hide(); + } else { + QMessageBox::warning(0, "Create new Folder", "Error: failed to create folder. Please ensure only valid characters were used in the folder name."); + } + } +} + +//=============== +//DiskBrowserView +//=============== + +void DiskBrowserView::keyPressEvent(QKeyEvent *event) { + //enhance consistency: OS X by default doesn't activate items for these keypresses + if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { + emit activated(currentIndex()); + return; + } + + //simulate popular file managers; backspace = go up one directory + if(event->key() == Qt::Key_Backspace) { + emit cdUp(); + return; + } + + //fallback: unrecognized keypresses get handled by the widget itself + QTreeView::keyPressEvent(event); +} + +void DiskBrowserView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { + QAbstractItemView::currentChanged(current, previous); + emit changed(current); +} + +//================ +//DiskBrowserImage +//================ + +void DiskBrowserImage::paintEvent(QPaintEvent*) { + QPainter painter(this); + if(name != "") { + QImage image(QString::fromUtf8(name)); + painter.drawImage(0, 0, image); + } +} + +//=========== +//DiskBrowser +//=========== + +void DiskBrowser::inputEvent(uint16_t scancode) { + if(!isActiveWindow() || isMinimized()) return; + + //provide very simple support for controlling the window via gamepads + if(Joypad::isAnyHat(scancode)) { + int16_t state = mapper().state(scancode); + + if(state == Joypad::HatUp) { + QKeyEvent event((QEvent::Type)6, Qt::Key_Up, Qt::NoModifier); + view->keyPressEvent(&event); + } + + if(state == Joypad::HatDown) { + QKeyEvent event((QEvent::Type)6, Qt::Key_Down, Qt::NoModifier); + view->keyPressEvent(&event); + } + + if(state == Joypad::HatLeft) { + QKeyEvent event((QEvent::Type)6, Qt::Key_Backspace, Qt::NoModifier); + view->keyPressEvent(&event); + } + + if(state == Joypad::HatRight) { + QKeyEvent event((QEvent::Type)6, Qt::Key_Return, Qt::NoModifier); + view->keyPressEvent(&event); + } + } +} + +void DiskBrowser::chooseFolder(const function &callback_, string ¤tPath_, const char *title) { + callback = callback_; + currentPath = ¤tPath_; + browseMode = Folder; + + hide(); + group->hide(); + ok->setText("Choose"); + setWindowTitle(string() << title); + setPath(*currentPath); + setNameFilters("Folders ()"); + show(); +} + +void DiskBrowser::chooseFile(const function &callback_, string ¤tPath_, const char *title) { + callback = callback_; + currentPath = ¤tPath_; + browseMode = File; + + hide(); + group->hide(); + ok->setText("Choose"); + setWindowTitle(string() << title); + setPath(*currentPath); + setNameFilters("All Files (*)"); + show(); +} + +void DiskBrowser::loadCartridge() { + currentPath = &config().path.current.cartridge; + browseMode = Cartridge; + + hide(); + group->setVisible(config().diskBrowser.showPanel); + ok->setText("Load"); + setWindowTitle("Load Cartridge"); + setPath(config().path.rom == "" ? *currentPath : config().path.rom); + setNameFilters(string() + << "SNES cartridges (*.sfc" << reader.extensionList << reader.compressionList << ");;" + << "BS-X cartridges (*.bs" << reader.compressionList << ");;" + << "Sufami Turbo cartridges (*.st" << reader.compressionList << ");;" + << "Game Boy cartridges (*.gb *.sgb *.gbc" << reader.compressionList << ");;" + << "All files (*)" + ); + filter->setCurrentIndex(config().path.current.filter); + show(); +} + +void DiskBrowser::loadBaseCartridge() { + currentPath = &config().path.current.cartridge; + browseMode = BaseCartridge; + + hide(); + group->setVisible(config().diskBrowser.showPanel); + ok->setText("Load"); + setWindowTitle("Load Base Cartridge"); + setPath(config().path.rom == "" ? *currentPath : config().path.rom); + setNameFilters(string() + << "SNES cartridges (*.sfc" << reader.extensionList << reader.compressionList << ");;" + << "All files (*)" + ); + show(); +} + +void DiskBrowser::loadBsxCartridge() { + currentPath = &config().path.current.bsx; + browseMode = BsxCartridge; + + hide(); + group->setVisible(config().diskBrowser.showPanel); + ok->setText("Load"); + setWindowTitle("Load BS-X Cartridge"); + setPath(config().path.rom == "" ? *currentPath : config().path.rom); + setNameFilters(string() + << "BS-X cartridges (*.bs" << reader.compressionList << ");;" + << "All files (*)" + ); + show(); +} + +void DiskBrowser::loadSufamiTurboCartridge1() { + currentPath = &config().path.current.st; + browseMode = SufamiTurboCartridge1; + + hide(); + group->setVisible(config().diskBrowser.showPanel); + ok->setText("Load"); + setWindowTitle("Load Slot-A Sufami Turbo Cartridge"); + setPath(config().path.rom == "" ? *currentPath : config().path.rom); + setNameFilters(string() + << "Sufami Turbo cartridges (*.st" << reader.compressionList << ");;" + << "All files (*)" + ); + show(); +} + +void DiskBrowser::loadSufamiTurboCartridge2() { + currentPath = &config().path.current.st; + browseMode = SufamiTurboCartridge2; + + hide(); + group->setVisible(config().diskBrowser.showPanel); + ok->setText("Load"); + setWindowTitle("Load Slot-B Sufami Turbo Cartridge"); + setPath(config().path.rom == "" ? *currentPath : config().path.rom); + setNameFilters(string() + << "Sufami Turbo Cartridges (*.st" << reader.compressionList << ");;" + << "All files (*)" + ); + show(); +} + +void DiskBrowser::loadSuperGameBoyCartridge() { + currentPath = &config().path.current.sgb; + browseMode = SuperGameBoyCartridge; + + hide(); + group->setVisible(config().diskBrowser.showPanel); + ok->setText("Load"); + setWindowTitle("Load Super Game Boy Cartridge"); + setPath(config().path.rom == "" ? *currentPath : config().path.rom); + setNameFilters(string() + << "Game Boy cartridges (*.gb *.sgb *.gbc" << reader.compressionList << ");;" + << "All files (*)" + ); + show(); +} + +string DiskBrowser::queryImageInformation() { + string text; + string filename; + if(currentFilename(filename) == true) { + Cartridge::Information info; + if(cartridge.information(filename, info)) { + unsigned size = file::size(filename); + text << ""; + text << ""; + text << ""; + text << ""; + text << "" : text << "None"; + text << "
Title: " << info.name << "
Region: " << info.region << "
ROM: " << info.romSize * 8 / 1024 / 1024 << "mbit
RAM: "; + info.ramSize ? text << info.ramSize * 8 / 1024 << "kbit
"; + } + } + return text; +} + +void DiskBrowser::activateItem(const QModelIndex &item) { + if(browseMode == Folder) { + setPath(model->filePath(item)); + } else { + loadSelected(); + } +} + +void DiskBrowser::changeItem(const QModelIndex &item) { + if(browseMode == Folder) { + ok->setEnabled(model->isDir(item)); + image->name = ""; + info->setText(""); + applyPatch->setVisible(false); + } else { + string filename; + currentFilename(filename); + + if(filename.length() == 0) { + //nothing selected? + ok->setEnabled(false); + image->name = ""; + info->setText(""); + applyPatch->setVisible(false); + } else { + ok->setEnabled(true); + image->name = nall::basename(filename) << ".png"; + if(file::exists(image->name) == false) image->name = ""; + info->setText(string() << queryImageInformation()); + string patch = nall::basename(filename) << ".ups"; + applyPatch->setVisible(file::exists(patch)); + } + } + + image->update(); +} + +void DiskBrowser::loadSelected() { + string filename; + bool loadable = currentFilename(filename); + + if(browseMode == Folder || loadable == true) { + QModelIndex item = view->currentIndex(); + if(currentPath) *currentPath = dir(model->filePath(item).toUtf8().constData()); + hide(); + + if(browseMode == Folder || browseMode == File) { + callback(filename); + } else if(browseMode == Cartridge) { + //quick-loading mode: determine load type via active filter + config().path.current.filter = filter->currentIndex(); + + if(config().path.current.filter == 1) { //"BS-X cartridges" + if(config().path.bsx == "") { + loaderWindow->loadBsxCartridge("", filename); + } else { + cartridge.loadBsx(config().path.bsx, filename); + } + } else if(config().path.current.filter == 2) { //"Sufami Turbo cartridges" + if(config().path.st == "") { + loaderWindow->loadSufamiTurboCartridge("", filename, ""); + } else { + cartridge.loadSufamiTurbo(config().path.st, filename, ""); + } + } else if(config().path.current.filter == 3) { //"Game Boy cartridges" + if(SNES::supergameboy.opened() == false) { + QMessageBox::warning(0, "Warning", "Super Game Boy support missing - cartridge cannot be loaded."); + } else if(config().path.sgb == "") { + loaderWindow->loadSuperGameBoyCartridge("", filename); + } else { + cartridge.loadSuperGameBoy(config().path.sgb, filename); + } + } else { //"SNES cartridges" (0) or "All files" (4) + cartridge.loadNormal(filename); + } + } else if(browseMode == BaseCartridge) { + loaderWindow->selectBaseCartridge(filename); + } else if(browseMode == BsxCartridge) { + loaderWindow->selectSlot1Cartridge(filename); + } else if(browseMode == SufamiTurboCartridge1) { + loaderWindow->selectSlot1Cartridge(filename); + } else if(browseMode == SufamiTurboCartridge2) { + loaderWindow->selectSlot2Cartridge(filename); + } else if(browseMode == SuperGameBoyCartridge) { + loaderWindow->selectSlot1Cartridge(filename); + } + } else { + //this is a standard folder in ROM loading mode; enter into the folder + QModelIndex item = view->currentIndex(); + setPath(model->filePath(item)); + } +} + +// + +void DiskBrowser::setPath(const QString &reqPath) { + disconnect(path, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePath())); + + QString effectivePath = reqPath; + if(effectivePath == "") { + effectivePath = QString::fromUtf8(config().path.startup); + } + if(effectivePath == "" || QDir(reqPath).exists() == false) { + effectivePath = ""; + newFolder->setEnabled(false); + } else { + newFolder->setEnabled(true); + } + path->clear(); + model->setRootPath(effectivePath); + view->setRootIndex(model->index(effectivePath)); + view->setCurrentIndex(view->rootIndex()); + view->setFocus(); + + if(effectivePath.length() > 0) { + QDir directory(effectivePath); + while(true) { + path->addItem(directory.absolutePath()); + if(directory.isRoot()) break; + directory.cdUp(); + } + } + path->addItem(""); + + connect(path, SIGNAL(currentIndexChanged(int)), this, SLOT(updatePath())); +} + +void DiskBrowser::setNameFilters(const QString &filters) { + disconnect(filter, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFilter())); + + filter->clear(); + + string filterData = filters.toUtf8().constData(); + lstring filterPart; + filterPart.split(";;", filterData); + + for(unsigned i = 0; i < filterPart.size(); i++) { + filter->addItem(filterPart[i]); + } + + connect(filter, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFilter())); + updateFilter(); +} + +void DiskBrowser::cdUp() { + folderCreator->hide(); + //if we aren't already at the root node, select the second node, which is one path higher than the current + if(path->count() > 1) path->setCurrentIndex(1); +} + +void DiskBrowser::updatePath() { + setPath(path->currentText()); +} + +void DiskBrowser::updateFilter() { + QString currentText = filter->currentText(); + if(currentText.length() == 0) { + model->setNameFilters(QStringList() << "*"); + } else { + string filters = currentText.toUtf8().constData(); + filters = substr(filters, strpos(filters, "(")); + ltrim(filters, "("); + rtrim(filters, ")"); + lstring filterPart; + filterPart.split(" ", filters); + QStringList filterList; + for(unsigned i = 0; i < filterPart.size(); i++) filterList << (const char*)filterPart[i]; + model->setNameFilters(filterList); + } +} + +//true means filename can be loaded directly +//false means it cannot (eg this is a folder and we are attempting to load a ROM) +bool DiskBrowser::currentFilename(string &filename) { + bool loadable = false; + QModelIndex item = view->currentIndex(); + filename = model->filePath(item).toUtf8().constData(); + + if(browseMode != Folder) { + if(model->isDir(item)) { + if(browseMode != File) { + //folders ending in ".sfc" are treated as "packages", and loaded directly + if(striend(filename, ".sfc")) { + QDir directory(filename); + directory.setNameFilters(QStringList() << "*.sfc"); + QStringList list = directory.entryList(QDir::Files | QDir::NoDotAndDotDot); + if(list.count() == 1) { + filename << "/" << list[0].toUtf8().constData(); + loadable = true; + } + } + } + } else { + loadable = true; + } + } + + return loadable; +} + +void DiskBrowser::toggleApplyPatches() { + config().file.applyPatches = applyPatch->isChecked(); +} + +void DiskBrowser::toggleShowPanel() { + showPanel->setChecked(!showPanel->isChecked()); + config().diskBrowser.showPanel = showPanel->isChecked(); + group->setVisible(config().diskBrowser.showPanel); +} + +DiskBrowser::DiskBrowser() : QbWindow(config().geometry.diskBrowser) { + setObjectName("disk-browser"); + resize(720, 480); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + topLayout = new QHBoxLayout; + layout->addLayout(topLayout); + + browseLayout = new QVBoxLayout; + topLayout->addLayout(browseLayout); + + pathLayout = new QHBoxLayout; + browseLayout->addLayout(pathLayout); + + path = new QComboBox; + path->setEditable(true); + path->setMinimumContentsLength(16); + path->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + path->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + pathLayout->addWidget(path); + + newFolder = new QPushButton; + newFolder->setIconSize(QSize(16, 16)); + newFolder->setIcon(QIcon(":/16x16/folder-new.png")); + pathLayout->addWidget(newFolder); + + view = new DiskBrowserView; + view->setIconSize(QSize(16, 16)); + browseLayout->addWidget(view); + + panelLayout = new QVBoxLayout; + topLayout->addLayout(panelLayout); + + group = new QGroupBox; + panelLayout->addWidget(group); + + groupLayout = new QVBoxLayout; + group->setLayout(groupLayout); + + info = new QLabel; + info->setFixedWidth(256); + groupLayout->addWidget(info); + + image = new DiskBrowserImage; + image->setFixedSize(256, 239); + groupLayout->addWidget(image); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + groupLayout->addWidget(spacer); + + applyPatch = new QCheckBox("Apply UPS patch"); + applyPatch->setChecked(config().file.applyPatches); + groupLayout->addWidget(applyPatch); + + controlLayout = new QHBoxLayout; + layout->addLayout(controlLayout); + + filter = new QComboBox; + filter->setMinimumContentsLength(16); + filter->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + filter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + controlLayout->addWidget(filter); + + options = new QPushButton("Options"); + controlLayout->addWidget(options); + + menu = new QMenu; + options->setMenu(menu); + + menu->addAction(showPanel = new QbCheckAction("Show Side Panel", 0)); + showPanel->setChecked(config().diskBrowser.showPanel); + + ok = new QPushButton("Ok"); + ok->setEnabled(false); + controlLayout->addWidget(ok); + + cancel = new QPushButton("Cancel"); + controlLayout->addWidget(cancel); + + model = new QFileSystemModel; + model->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + model->setNameFilterDisables(false); + + view->setModel(model); + view->setExpandsOnDoubleClick(false); + view->setAllColumnsShowFocus(true); + view->setUniformRowHeights(true); + view->setSortingEnabled(true); + view->sortByColumn(0, Qt::AscendingOrder); + view->setColumnHidden(1, true); + view->setColumnHidden(2, true); + view->setColumnHidden(3, true); + view->setHeaderHidden(true); + + folderCreator = new FolderCreator; + + connect(newFolder, SIGNAL(released()), folderCreator, SLOT(show())); + connect(view, SIGNAL(cdUp()), this, SLOT(cdUp())); + connect(view, SIGNAL(activated(const QModelIndex&)), this, SLOT(activateItem(const QModelIndex&))); + connect(view, SIGNAL(changed(const QModelIndex&)), this, SLOT(changeItem(const QModelIndex&))); + connect(ok, SIGNAL(released()), this, SLOT(loadSelected())); + connect(cancel, SIGNAL(released()), this, SLOT(close())); + + connect(applyPatch, SIGNAL(stateChanged(int)), this, SLOT(toggleApplyPatches())); + connect(showPanel, SIGNAL(triggered()), this, SLOT(toggleShowPanel())); +} diff --git a/mednafen/snes/src/ui_qt/base/diskbrowser.moc.hpp b/mednafen/snes/src/ui_qt/base/diskbrowser.moc.hpp new file mode 100755 index 0000000..a5dbefb --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/diskbrowser.moc.hpp @@ -0,0 +1,117 @@ +class FolderCreator : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QLabel *label; + QLineEdit *name; + QHBoxLayout *controlLayout; + QPushButton *ok; + QPushButton *cancel; + + FolderCreator(); + +public slots: + void show(); + void createFolder(); +}; + +extern FolderCreator *folderCreator; + +class DiskBrowserView : public QTreeView { + Q_OBJECT + +public: + void keyPressEvent(QKeyEvent*); + +signals: + void cdUp(); + void changed(const QModelIndex&); + +public slots: + void currentChanged(const QModelIndex&, const QModelIndex&); +}; + +class DiskBrowserImage : public QWidget { +public: + string name; + void paintEvent(QPaintEvent*); +}; + +class DiskBrowser : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QHBoxLayout *topLayout; + QVBoxLayout *browseLayout; + QHBoxLayout *pathLayout; + QComboBox *path; + QPushButton *newFolder; + DiskBrowserView *view; + QVBoxLayout *panelLayout; + QGroupBox *group; + QVBoxLayout *groupLayout; + QLabel *info; + DiskBrowserImage *image; + QWidget *spacer; + QCheckBox *applyPatch; + QHBoxLayout *controlLayout; + QComboBox *filter; + QPushButton *options; + QPushButton *ok; + QPushButton *cancel; + QFileSystemModel *model; + + QMenu *menu; + QbCheckAction *showPanel; + + void inputEvent(uint16_t scancode); + + void chooseFolder(const function&, string&, const char*); + void chooseFile(const function&, string&, const char*); + + void loadCartridge(); + void loadBaseCartridge(); + void loadBsxCartridge(); + void loadSufamiTurboCartridge1(); + void loadSufamiTurboCartridge2(); + void loadSuperGameBoyCartridge(); + + string queryImageInformation(); + + void setPath(const QString&); + void setNameFilters(const QString&); + + DiskBrowser(); + +public slots: + void cdUp(); + void updatePath(); + void updateFilter(); + void activateItem(const QModelIndex&); + void changeItem(const QModelIndex&); + void loadSelected(); + + void toggleApplyPatches(); + void toggleShowPanel(); + +private: + function callback; + string *currentPath; + + enum BrowseMode { + Folder, + File, + Cartridge, + BaseCartridge, + BsxCartridge, + SufamiTurboCartridge1, + SufamiTurboCartridge2, + SuperGameBoyCartridge, + } browseMode; + + bool currentFilename(string&); +}; + +extern DiskBrowser *diskBrowser; diff --git a/mednafen/snes/src/ui_qt/base/htmlviewer.cpp b/mednafen/snes/src/ui_qt/base/htmlviewer.cpp new file mode 100755 index 0000000..e8886c2 --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/htmlviewer.cpp @@ -0,0 +1,21 @@ +#include "htmlviewer.moc" +HtmlViewerWindow *htmlViewerWindow; + +HtmlViewerWindow::HtmlViewerWindow() : QbWindow(config().geometry.htmlViewerWindow) { + setObjectName("html-window"); + resize(560, 480); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(0); + setLayout(layout); + + document = new QTextBrowser; + layout->addWidget(document); +} + +void HtmlViewerWindow::show(const char *title, const char *htmlData) { + document->setHtml(string() << htmlData); + setWindowTitle(title); + QbWindow::show(); +} diff --git a/mednafen/snes/src/ui_qt/base/htmlviewer.moc.hpp b/mednafen/snes/src/ui_qt/base/htmlviewer.moc.hpp new file mode 100755 index 0000000..8383ecc --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/htmlviewer.moc.hpp @@ -0,0 +1,14 @@ +class HtmlViewerWindow : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTextBrowser *document; + + void show(const char *title, const char *htmlData); + HtmlViewerWindow(); + +public slots: +}; + +extern HtmlViewerWindow *htmlViewerWindow; diff --git a/mednafen/snes/src/ui_qt/base/loader.cpp b/mednafen/snes/src/ui_qt/base/loader.cpp new file mode 100755 index 0000000..7fdcdd0 --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/loader.cpp @@ -0,0 +1,229 @@ +#include "loader.moc" +LoaderWindow *loaderWindow; + +LoaderWindow::LoaderWindow() : QbWindow(config().geometry.loaderWindow) { + setObjectName("loader-window"); + setMinimumWidth(520); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + grid = new QGridLayout; + layout->addLayout(grid); + + baseLabel = new QLabel("Base cartridge:"); + grid->addWidget(baseLabel, 0, 0); + + baseFile = new QLineEdit; + baseFile->setReadOnly(true); + grid->addWidget(baseFile, 0, 1); + + baseBrowse = new QPushButton("Browse ..."); + grid->addWidget(baseBrowse, 0, 2); + + baseClear = new QPushButton("Clear"); + grid->addWidget(baseClear, 0, 3); + + slot1Label = new QLabel("Slot A cartridge:"); + grid->addWidget(slot1Label, 1, 0); + + slot1File = new QLineEdit; + slot1File->setReadOnly(true); + grid->addWidget(slot1File, 1, 1); + + slot1Browse = new QPushButton("Browse ..."); + grid->addWidget(slot1Browse, 1, 2); + + slot1Clear = new QPushButton("Clear"); + grid->addWidget(slot1Clear, 1, 3); + + slot2Label = new QLabel("Slot B cartridge:"); + grid->addWidget(slot2Label, 2, 0); + + slot2File = new QLineEdit; + slot2File->setReadOnly(true); + grid->addWidget(slot2File, 2, 1); + + slot2Browse = new QPushButton("Browse ..."); + grid->addWidget(slot2Browse, 2, 2); + + slot2Clear = new QPushButton("Clear"); + grid->addWidget(slot2Clear, 2, 3); + + load = new QPushButton("Load"); + grid->addWidget(load, 3, 2); + + cancel = new QPushButton("Cancel"); + grid->addWidget(cancel, 3, 3); + + connect(baseBrowse, SIGNAL(released()), this, SLOT(selectBaseCartridge())); + connect(baseClear, SIGNAL(released()), this, SLOT(clearBaseCartridge())); + connect(slot1Browse, SIGNAL(released()), this, SLOT(selectSlot1Cartridge())); + connect(slot1Clear, SIGNAL(released()), this, SLOT(clearSlot1Cartridge())); + connect(slot2Browse, SIGNAL(released()), this, SLOT(selectSlot2Cartridge())); + connect(slot2Clear, SIGNAL(released()), this, SLOT(clearSlot2Cartridge())); + connect(load, SIGNAL(released()), this, SLOT(onLoad())); + connect(cancel, SIGNAL(released()), this, SLOT(close())); +} + +void LoaderWindow::syncUi() { + //only allow load when a base file is specified ... + load->setEnabled(baseFile->text().length() > 0); +} + +void LoaderWindow::loadBsxSlottedCartridge(const char *filebase, const char *fileSlot1) { + hide(); + baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); + slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); + slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide(); + + slot1Label->setText("Slot cartridge:"); + + baseFile->setText(filebase); + slot1File->setText(fileSlot1); + + syncUi(); + mode = SNES::Cartridge::ModeBsxSlotted; + showWindow("Load BS-X Slotted Cartridge"); +} + +void LoaderWindow::loadBsxCartridge(const char *fileBase, const char *fileSlot1) { + hide(); + baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); + slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); + slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide(); + + slot1Label->setText("Slot cartridge:"); + + baseFile->setText(fileBase); + slot1File->setText(fileSlot1); + + syncUi(); + mode = SNES::Cartridge::ModeBsx; + showWindow("Load BS-X Cartridge"); +} + +void LoaderWindow::loadSufamiTurboCartridge(const char *fileBase, const char *fileSlot1, const char *fileSlot2) { + hide(); + baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); + slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); + slot2Label->show(), slot2File->show(), slot2Browse->show(), slot2Clear->show(); + + slot1Label->setText("Slot A cartridge:"); + slot2Label->setText("Slot B cartridge:"); + + baseFile->setText(fileBase); + slot1File->setText(fileSlot1); + slot2File->setText(fileSlot2); + + syncUi(); + mode = SNES::Cartridge::ModeSufamiTurbo; + showWindow("Load Sufami Turbo Cartridge"); +} + +void LoaderWindow::loadSuperGameBoyCartridge(const char *fileBase, const char *fileSlot1) { + hide(); + baseLabel->show(), baseFile->show(), baseBrowse->show(), baseClear->show(); + slot1Label->show(), slot1File->show(), slot1Browse->show(), slot1Clear->show(); + slot2Label->hide(), slot2File->hide(), slot2Browse->hide(), slot2Clear->hide(); + + slot1Label->setText("Game Boy cartridge:"); + + baseFile->setText(fileBase); + slot1File->setText(fileSlot1); + + syncUi(); + mode = SNES::Cartridge::ModeSuperGameBoy; + showWindow("Load Super Game Boy Cartridge"); +} + +void LoaderWindow::showWindow(const char *title) { + setWindowTitle(title); + show(); + shrink(); + load->setFocus(); +} + +void LoaderWindow::selectBaseCartridge(const char *filename) { + baseFile->setText(string() << filename); + syncUi(); +} + +void LoaderWindow::selectSlot1Cartridge(const char *filename) { + slot1File->setText(string() << filename); + syncUi(); +} + +void LoaderWindow::selectSlot2Cartridge(const char *filename) { + slot2File->setText(string() << filename); + syncUi(); +} + +void LoaderWindow::selectBaseCartridge() { + diskBrowser->loadBaseCartridge(); +} + +void LoaderWindow::clearBaseCartridge() { + baseFile->setText(""); + syncUi(); +} + +void LoaderWindow::selectSlot1Cartridge() { + switch(mode) { + case SNES::Cartridge::ModeBsx: + case SNES::Cartridge::ModeBsxSlotted: + diskBrowser->loadBsxCartridge(); + break; + case SNES::Cartridge::ModeSufamiTurbo: + diskBrowser->loadSufamiTurboCartridge1(); + break; + case SNES::Cartridge::ModeSuperGameBoy: + diskBrowser->loadSuperGameBoyCartridge(); + break; + } +} + +void LoaderWindow::clearSlot1Cartridge() { + slot1File->setText(""); + syncUi(); +} + +void LoaderWindow::selectSlot2Cartridge() { + diskBrowser->loadSufamiTurboCartridge2(); +} + +void LoaderWindow::clearSlot2Cartridge() { + slot2File->setText(""); + syncUi(); +} + +void LoaderWindow::onLoad() { + hide(); + string base = baseFile->text().toUtf8().data(); + string slot1 = slot1File->text().toUtf8().data(); + string slot2 = slot2File->text().toUtf8().data(); + + switch(mode) { + case SNES::Cartridge::ModeBsxSlotted: { + cartridge.loadBsxSlotted(base, slot1); + } break; + + case SNES::Cartridge::ModeBsx: { + config().path.bsx = base; + cartridge.loadBsx(base, slot1); + } break; + + case SNES::Cartridge::ModeSufamiTurbo: { + config().path.st = base; + cartridge.loadSufamiTurbo(base, slot1, slot2); + } break; + + case SNES::Cartridge::ModeSuperGameBoy: { + config().path.sgb = base; + cartridge.loadSuperGameBoy(base, slot1); + } break; + } +} diff --git a/mednafen/snes/src/ui_qt/base/loader.moc.hpp b/mednafen/snes/src/ui_qt/base/loader.moc.hpp new file mode 100755 index 0000000..9c8b28a --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/loader.moc.hpp @@ -0,0 +1,47 @@ +class LoaderWindow : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QGridLayout *grid; + QLabel *baseLabel; + QLineEdit *baseFile; + QPushButton *baseBrowse; + QPushButton *baseClear; + QLabel *slot1Label; + QLineEdit *slot1File; + QPushButton *slot1Browse; + QPushButton *slot1Clear; + QLabel *slot2Label; + QLineEdit *slot2File; + QPushButton *slot2Browse; + QPushButton *slot2Clear; + QPushButton *load; + QPushButton *cancel; + + void syncUi(); + void loadBsxSlottedCartridge(const char*, const char*); + void loadBsxCartridge(const char*, const char*); + void loadSufamiTurboCartridge(const char*, const char*, const char*); + void loadSuperGameBoyCartridge(const char*, const char*); + LoaderWindow(); + + void selectBaseCartridge(const char*); + void selectSlot1Cartridge(const char*); + void selectSlot2Cartridge(const char*); + +public slots: + void selectBaseCartridge(); + void clearBaseCartridge(); + void selectSlot1Cartridge(); + void clearSlot1Cartridge(); + void selectSlot2Cartridge(); + void clearSlot2Cartridge(); + void onLoad(); + +private: + SNES::Cartridge::Mode mode; + void showWindow(const char *title); +}; + +extern LoaderWindow *loaderWindow; diff --git a/mednafen/snes/src/ui_qt/base/main.cpp b/mednafen/snes/src/ui_qt/base/main.cpp new file mode 100755 index 0000000..7864b1d --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/main.cpp @@ -0,0 +1,636 @@ +#include "main.moc" +MainWindow *mainWindow; + +MainWindow::MainWindow() : QbWindow(config().geometry.mainWindow) { + setObjectName("main-window"); + setWindowTitle(string() << bsnesTitle << " v" << bsnesVersion); + setCloseOnEscape(false); + + //menu bar + #if defined(PLATFORM_OSX) + menuBar = new QMenuBar(0); + #else + menuBar = new QMenuBar; + #endif + + system = menuBar->addMenu("&System"); + + system_load = system->addAction("Load &Cartridge ..."); + system_load->setIcon(QIcon(":/16x16/document-open.png")); + + system_loadSpecial = system->addMenu("Load &Special"); + system_loadSpecial->setIcon(QIcon(":/16x16/document-open.png")); + + system_loadSpecial_bsxSlotted = system_loadSpecial->addAction("Load BS-X &Slotted Cartridge ..."); + system_loadSpecial_bsxSlotted->setIcon(QIcon(":/16x16/document-open.png")); + + system_loadSpecial_bsx = system_loadSpecial->addAction("Load &BS-X Cartridge ..."); + system_loadSpecial_bsx->setIcon(QIcon(":/16x16/document-open.png")); + + system_loadSpecial_sufamiTurbo = system_loadSpecial->addAction("Load Sufami &Turbo Cartridge ..."); + system_loadSpecial_sufamiTurbo->setIcon(QIcon(":/16x16/document-open.png")); + + system_loadSpecial_superGameBoy = system_loadSpecial->addAction("Load Super &Game Boy Cartridge ..."); + system_loadSpecial_superGameBoy->setIcon(QIcon(":/16x16/document-open.png")); + + system->addSeparator(); + + system->addAction(system_power = new QbCheckAction("&Power", 0)); + + system_reset = system->addAction("&Reset"); + system_reset->setIcon(QIcon(":/16x16/view-refresh.png")); + + system->addSeparator(); + + system_port1 = system->addMenu("Controller Port &1"); + system_port1->setIcon(QIcon(":/16x16/input-gaming.png")); + system_port1->addAction(system_port1_none = new QbRadioAction("&None", 0)); + system_port1->addAction(system_port1_gamepad = new QbRadioAction("&Gamepad", 0)); + system_port1->addAction(system_port1_asciipad = new QbRadioAction("&asciiPad", 0)); + system_port1->addAction(system_port1_multitap = new QbRadioAction("&Multitap", 0)); + system_port1->addAction(system_port1_mouse = new QbRadioAction("&Mouse", 0)); + + system_port2 = system->addMenu("Controller Port &2"); + system_port2->setIcon(QIcon(":/16x16/input-gaming.png")); + system_port2->addAction(system_port2_none = new QbRadioAction("&None", 0)); + system_port2->addAction(system_port2_gamepad = new QbRadioAction("&Gamepad", 0)); + system_port2->addAction(system_port2_asciipad = new QbRadioAction("&asciiPad", 0)); + system_port2->addAction(system_port2_multitap = new QbRadioAction("&Multitap", 0)); + system_port2->addAction(system_port2_mouse = new QbRadioAction("&Mouse", 0)); + system_port2->addAction(system_port2_superscope = new QbRadioAction("&Super Scope", 0)); + system_port2->addAction(system_port2_justifier = new QbRadioAction("&Justifier", 0)); + system_port2->addAction(system_port2_justifiers = new QbRadioAction("Two &Justifiers", 0)); + + #if !defined(PLATFORM_OSX) + system->addSeparator(); + #endif + + system_exit = system->addAction("E&xit"); + system_exit->setIcon(QIcon(":/16x16/process-stop.png")); + system_exit->setMenuRole(QAction::QuitRole); + + settings = menuBar->addMenu("S&ettings"); + + settings_videoMode = settings->addMenu("Video &Mode"); + settings_videoMode->setIcon(QIcon(":/16x16/video-display.png")); + + settings_videoMode->addAction(settings_videoMode_1x = new QbRadioAction("Scale &1x", 0)); + + settings_videoMode->addAction(settings_videoMode_2x = new QbRadioAction("Scale &2x", 0)); + + settings_videoMode->addAction(settings_videoMode_3x = new QbRadioAction("Scale &3x", 0)); + + settings_videoMode->addAction(settings_videoMode_4x = new QbRadioAction("Scale &4x", 0)); + + settings_videoMode->addAction(settings_videoMode_5x = new QbRadioAction("Scale &5x", 0)); + + settings_videoMode->addSeparator(); + + settings_videoMode->addAction(settings_videoMode_correctAspectRatio = new QbCheckAction("Correct &Aspect Ratio", 0)); + + settings_videoMode->addSeparator(); + + settings_videoMode->addAction(settings_videoMode_ntsc = new QbRadioAction("&NTSC", 0)); + settings_videoMode->addAction(settings_videoMode_pal = new QbRadioAction("&PAL", 0)); + + if(filter.opened()) { + settings_videoFilter = settings->addMenu("Video &Filter"); + settings_videoFilter->setIcon(QIcon(":/16x16/image-x-generic.png")); + + settings_videoFilter_configure = settings_videoFilter->addAction("&Configure Active Filter ..."); + settings_videoFilter_configure->setIcon(QIcon(":/16x16/preferences-desktop.png")); + settings_videoFilter->addSeparator(); + + settings_videoFilter->addAction(settings_videoFilter_none = new QbRadioAction("&None", 0)); + settings_videoFilter_list.add(settings_videoFilter_none); + + lstring filterlist; + filterlist.split(";", filter.dl_supported()); + for(unsigned i = 0; i < filterlist.size(); i++) { + QbRadioAction *action = new QbRadioAction(filterlist[i], 0); + settings_videoFilter->addAction(action); + settings_videoFilter_list.add(action); + } + } + + settings->addAction(settings_smoothVideo = new QbCheckAction("&Smooth Video Output", 0)); + + settings->addSeparator(); + + settings->addAction(settings_muteAudio = new QbCheckAction("&Mute Audio Output", 0)); + + settings->addSeparator(); + + settings_emulationSpeed = settings->addMenu("Emulation &Speed"); + settings_emulationSpeed->setIcon(QIcon(":/16x16/appointment-new.png")); + + settings_emulationSpeed->addAction(settings_emulationSpeed_slowest = new QbRadioAction("Slowest", 0)); + + settings_emulationSpeed->addAction(settings_emulationSpeed_slow = new QbRadioAction("Slow", 0)); + + settings_emulationSpeed->addAction(settings_emulationSpeed_normal = new QbRadioAction("Normal", 0)); + + settings_emulationSpeed->addAction(settings_emulationSpeed_fast = new QbRadioAction("Fast", 0)); + + settings_emulationSpeed->addAction(settings_emulationSpeed_fastest = new QbRadioAction("Fastest", 0)); + + settings_emulationSpeed->addSeparator(); + + settings_emulationSpeed->addAction(settings_emulationSpeed_syncVideo = new QbCheckAction("Sync &Video", 0)); + + settings_emulationSpeed->addAction(settings_emulationSpeed_syncAudio = new QbCheckAction("Sync &Audio", 0)); + + settings_configuration = settings->addAction("&Configuration ..."); + settings_configuration->setIcon(QIcon(":/16x16/preferences-desktop.png")); + settings_configuration->setMenuRole(QAction::PreferencesRole); + + tools = menuBar->addMenu("&Tools"); + + tools_movies = tools->addMenu("&Movies"); + tools_movies->setIcon(QIcon(":/16x16/applications-multimedia.png")); + + tools_movies_play = tools_movies->addAction("Play Movie ..."); + tools_movies_play->setIcon(QIcon(":/16x16/media-playback-start.png")); + + tools_movies_stop = tools_movies->addAction("Stop"); + tools_movies_stop->setIcon(QIcon(":/16x16/media-playback-stop.png")); + + tools_movies_recordFromPowerOn = tools_movies->addAction("Record Movie (and restart system)"); + tools_movies_recordFromPowerOn->setIcon(QIcon(":/16x16/media-record.png")); + + tools_movies_recordFromHere = tools_movies->addAction("Record Movie (starting from here)"); + tools_movies_recordFromHere->setIcon(QIcon(":/16x16/media-record.png")); + + tools_captureScreenshot = tools->addAction("&Capture Screenshot"); + tools_captureScreenshot->setIcon(QIcon(":/16x16/image-x-generic.png")); + + tools->addSeparator(); + + tools_dialog = tools->addAction("&Tools Dialog ..."); + tools_dialog->setIcon(QIcon(":/16x16/preferences-desktop.png")); + + tools_debugger = tools->addAction("&Debugger ..."); + tools_debugger->setIcon(QIcon(":/16x16/utilities-terminal.png")); + #if !defined(DEBUGGER) + tools_debugger->setVisible(false); + #endif + + help = menuBar->addMenu("&Help"); + + help_documentation = help->addAction("&Documentation ..."); + help_documentation->setIcon(QIcon(":/16x16/text-x-generic.png")); + + help_license = help->addAction("&License ..."); + help_license->setIcon(QIcon(":/16x16/text-x-generic.png")); + + #if !defined(PLATFORM_OSX) + help->addSeparator(); + #endif + + help_about = help->addAction("&About ..."); + help_about->setIcon(QIcon(":/16x16/help-browser.png")); + help_about->setMenuRole(QAction::AboutRole); + + //canvas + canvasContainer = new CanvasObject; + canvasContainer->setAcceptDrops(true); { + canvasContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + canvasContainer->setObjectName("backdrop"); + + canvasLayout = new QVBoxLayout; { + canvasLayout->setMargin(0); + canvasLayout->setAlignment(Qt::AlignCenter); + + canvas = new CanvasWidget; + canvas->setAcceptDrops(true); + canvas->setFocusPolicy(Qt::StrongFocus); + canvas->setAttribute(Qt::WA_PaintOnScreen, true); //disable Qt painting on focus / resize + + QPalette palette; + palette.setColor(QPalette::Window, QColor(0, 0, 0)); + canvas->setPalette(palette); + canvas->setAutoFillBackground(true); + } + canvasLayout->addWidget(canvas); + } + canvasContainer->setLayout(canvasLayout); + + //status bar + statusBar = new QStatusBar; + statusBar->showMessage(""); + systemState = new QLabel; + statusBar->addPermanentWidget(systemState); + + //layout + layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + #if !defined(PLATFORM_OSX) + layout->addWidget(menuBar); + #endif + layout->addWidget(canvasContainer); + layout->addWidget(statusBar); + setLayout(layout); + + //slots + connect(system_load, SIGNAL(triggered()), this, SLOT(loadCartridge())); + connect(system_loadSpecial_bsxSlotted, SIGNAL(triggered()), this, SLOT(loadBsxSlottedCartridge())); + connect(system_loadSpecial_bsx, SIGNAL(triggered()), this, SLOT(loadBsxCartridge())); + connect(system_loadSpecial_sufamiTurbo, SIGNAL(triggered()), this, SLOT(loadSufamiTurboCartridge())); + connect(system_loadSpecial_superGameBoy, SIGNAL(triggered()), this, SLOT(loadSuperGameBoyCartridge())); + connect(system_power, SIGNAL(triggered()), this, SLOT(power())); + connect(system_reset, SIGNAL(triggered()), this, SLOT(reset())); + connect(system_port1_none, SIGNAL(triggered()), this, SLOT(setPort1None())); + connect(system_port1_gamepad, SIGNAL(triggered()), this, SLOT(setPort1Gamepad())); + connect(system_port1_asciipad, SIGNAL(triggered()), this, SLOT(setPort1Asciipad())); + connect(system_port1_multitap, SIGNAL(triggered()), this, SLOT(setPort1Multitap())); + connect(system_port1_mouse, SIGNAL(triggered()), this, SLOT(setPort1Mouse())); + connect(system_port2_none, SIGNAL(triggered()), this, SLOT(setPort2None())); + connect(system_port2_gamepad, SIGNAL(triggered()), this, SLOT(setPort2Gamepad())); + connect(system_port2_asciipad, SIGNAL(triggered()), this, SLOT(setPort2Asciipad())); + connect(system_port2_multitap, SIGNAL(triggered()), this, SLOT(setPort2Multitap())); + connect(system_port2_mouse, SIGNAL(triggered()), this, SLOT(setPort2Mouse())); + connect(system_port2_superscope, SIGNAL(triggered()), this, SLOT(setPort2SuperScope())); + connect(system_port2_justifier, SIGNAL(triggered()), this, SLOT(setPort2Justifier())); + connect(system_port2_justifiers, SIGNAL(triggered()), this, SLOT(setPort2Justifiers())); + connect(system_exit, SIGNAL(triggered()), this, SLOT(quit())); + connect(settings_videoMode_1x, SIGNAL(triggered()), this, SLOT(setVideoMode1x())); + connect(settings_videoMode_2x, SIGNAL(triggered()), this, SLOT(setVideoMode2x())); + connect(settings_videoMode_3x, SIGNAL(triggered()), this, SLOT(setVideoMode3x())); + connect(settings_videoMode_4x, SIGNAL(triggered()), this, SLOT(setVideoMode4x())); + connect(settings_videoMode_5x, SIGNAL(triggered()), this, SLOT(setVideoMode5x())); + connect(settings_videoMode_correctAspectRatio, SIGNAL(triggered()), this, SLOT(toggleAspectCorrection())); + connect(settings_videoMode_ntsc, SIGNAL(triggered()), this, SLOT(setVideoNtsc())); + connect(settings_videoMode_pal, SIGNAL(triggered()), this, SLOT(setVideoPal())); + if(filter.opened()) { + connect(settings_videoFilter_configure, SIGNAL(triggered()), this, SLOT(configureFilter())); + for(unsigned i = 0; i < settings_videoFilter_list.size(); i++) { + connect(settings_videoFilter_list[i], SIGNAL(triggered()), this, SLOT(setFilter())); + } + } + connect(settings_smoothVideo, SIGNAL(triggered()), this, SLOT(toggleSmoothVideo())); + connect(settings_muteAudio, SIGNAL(triggered()), this, SLOT(muteAudio())); + connect(settings_emulationSpeed_slowest, SIGNAL(triggered()), this, SLOT(setSpeedSlowest())); + connect(settings_emulationSpeed_slow, SIGNAL(triggered()), this, SLOT(setSpeedSlow())); + connect(settings_emulationSpeed_normal, SIGNAL(triggered()), this, SLOT(setSpeedNormal())); + connect(settings_emulationSpeed_fast, SIGNAL(triggered()), this, SLOT(setSpeedFast())); + connect(settings_emulationSpeed_fastest, SIGNAL(triggered()), this, SLOT(setSpeedFastest())); + connect(settings_emulationSpeed_syncVideo, SIGNAL(triggered()), this, SLOT(syncVideo())); + connect(settings_emulationSpeed_syncAudio, SIGNAL(triggered()), this, SLOT(syncAudio())); + connect(settings_configuration, SIGNAL(triggered()), this, SLOT(showConfigWindow())); + connect(tools_movies_play, SIGNAL(triggered()), this, SLOT(playMovie())); + connect(tools_movies_stop, SIGNAL(triggered()), this, SLOT(stopMovie())); + connect(tools_movies_recordFromPowerOn, SIGNAL(triggered()), this, SLOT(recordMovieFromPowerOn())); + connect(tools_movies_recordFromHere, SIGNAL(triggered()), this, SLOT(recordMovieFromHere())); + connect(tools_captureScreenshot, SIGNAL(triggered()), this, SLOT(saveScreenshot())); + connect(tools_debugger, SIGNAL(triggered()), this, SLOT(showDebugger())); + connect(tools_dialog, SIGNAL(triggered()), this, SLOT(showToolsDialog())); + connect(help_documentation, SIGNAL(triggered()), this, SLOT(showDocumentation())); + connect(help_license, SIGNAL(triggered()), this, SLOT(showLicense())); + connect(help_about, SIGNAL(triggered()), this, SLOT(showAbout())); + + syncUi(); +} + +void MainWindow::syncUi() { + system_power->setEnabled(SNES::cartridge.loaded()); + system_power->setChecked (application.power == true); + system_power->setEnabled(SNES::cartridge.loaded()); + system_reset->setEnabled(SNES::cartridge.loaded() && application.power); + + system_port1_none->setChecked (config().input.port1 == ControllerPort1::None); + system_port1_gamepad->setChecked (config().input.port1 == ControllerPort1::Gamepad); + system_port1_asciipad->setChecked (config().input.port1 == ControllerPort1::Asciipad); + system_port1_multitap->setChecked (config().input.port1 == ControllerPort1::Multitap); + system_port1_mouse->setChecked (config().input.port1 == ControllerPort1::Mouse); + + system_port2_none->setChecked (config().input.port2 == ControllerPort2::None); + system_port2_gamepad->setChecked (config().input.port2 == ControllerPort2::Gamepad); + system_port2_asciipad->setChecked (config().input.port2 == ControllerPort2::Asciipad); + system_port2_multitap->setChecked (config().input.port2 == ControllerPort2::Multitap); + system_port2_mouse->setChecked (config().input.port2 == ControllerPort2::Mouse); + system_port2_superscope->setChecked(config().input.port2 == ControllerPort2::SuperScope); + system_port2_justifier->setChecked (config().input.port2 == ControllerPort2::Justifier); + system_port2_justifiers->setChecked(config().input.port2 == ControllerPort2::Justifiers); + + settings_videoMode_1x->setChecked(config().video.context->multiplier == 1); + settings_videoMode_2x->setChecked(config().video.context->multiplier == 2); + settings_videoMode_3x->setChecked(config().video.context->multiplier == 3); + settings_videoMode_4x->setChecked(config().video.context->multiplier == 4); + settings_videoMode_5x->setChecked(config().video.context->multiplier == 5); + + settings_videoMode_correctAspectRatio->setChecked(config().video.context->correctAspectRatio); + settings_videoMode_ntsc->setChecked(config().video.context->region == 0); + settings_videoMode_pal->setChecked (config().video.context->region == 1); + + if(filter.opened()) { + //only enable configuration option if the active filter supports it ... + settings_videoFilter_configure->setEnabled(filter.settings()); + + for(unsigned i = 0; i < settings_videoFilter_list.size(); i++) { + settings_videoFilter_list[i]->setChecked(config().video.context->swFilter == i); + } + } + + settings_smoothVideo->setChecked(config().video.context->hwFilter == 1); + settings_muteAudio->setChecked(config().audio.mute); + + settings_emulationSpeed_slowest->setChecked(config().system.speed == 0); + settings_emulationSpeed_slow->setChecked (config().system.speed == 1); + settings_emulationSpeed_normal->setChecked (config().system.speed == 2); + settings_emulationSpeed_fast->setChecked (config().system.speed == 3); + settings_emulationSpeed_fastest->setChecked(config().system.speed == 4); + + settings_emulationSpeed_syncVideo->setChecked(config().video.synchronize); + settings_emulationSpeed_syncAudio->setChecked(config().audio.synchronize); + + //movies contian save states to synchronize playback to recorded input + tools_movies->setEnabled(SNES::cartridge.loaded() && cartridge.saveStatesSupported()); + if(tools_movies->isEnabled()) { + tools_movies_play->setEnabled(movie.state == Movie::Inactive); + tools_movies_stop->setEnabled(movie.state != Movie::Inactive); + tools_movies_recordFromPowerOn->setEnabled(movie.state == Movie::Inactive); + tools_movies_recordFromHere->setEnabled(movie.state == Movie::Inactive); + } +} + +bool MainWindow::isActive() { + return isActiveWindow() && !isMinimized(); +} + +void MainWindow::loadCartridge() { + diskBrowser->loadCartridge(); +} + +void MainWindow::loadBsxSlottedCartridge() { + loaderWindow->loadBsxSlottedCartridge("", ""); +} + +void MainWindow::loadBsxCartridge() { + loaderWindow->loadBsxCartridge(config().path.bsx, ""); +} + +void MainWindow::loadSufamiTurboCartridge() { + loaderWindow->loadSufamiTurboCartridge(config().path.st, "", ""); +} + +void MainWindow::loadSuperGameBoyCartridge() { + loaderWindow->loadSuperGameBoyCartridge(config().path.sgb, ""); +} + +void MainWindow::power() { + system_power->toggleChecked(); + if(system_power->isChecked()) { + utility.modifySystemState(Utility::PowerOn); + } else { + utility.modifySystemState(Utility::PowerOff); + } +} + +void MainWindow::reset() { + utility.modifySystemState(Utility::Reset); +} + +void MainWindow::setPort1None() { + config().input.port1 = ControllerPort1::None; + SNES::config.controller_port1 = SNES::Input::DeviceNone; + utility.updateControllers(); +} + +void MainWindow::setPort1Gamepad() { + config().input.port1 = ControllerPort1::Gamepad; + SNES::config.controller_port1 = SNES::Input::DeviceJoypad; + utility.updateControllers(); +} + +void MainWindow::setPort1Asciipad() { + config().input.port1 = ControllerPort1::Asciipad; + SNES::config.controller_port1 = SNES::Input::DeviceJoypad; + utility.updateControllers(); +} + +void MainWindow::setPort1Multitap() { + config().input.port1 = ControllerPort1::Multitap; + SNES::config.controller_port1 = SNES::Input::DeviceMultitap; + utility.updateControllers(); +} + +void MainWindow::setPort1Mouse() { + config().input.port1 = ControllerPort1::Mouse; + SNES::config.controller_port1 = SNES::Input::DeviceMouse; + utility.updateControllers(); +} + +void MainWindow::setPort2None() { + config().input.port2 = ControllerPort2::None; + SNES::config.controller_port2 = SNES::Input::DeviceNone; + utility.updateControllers(); +} + +void MainWindow::setPort2Gamepad() { + config().input.port2 = ControllerPort2::Gamepad; + SNES::config.controller_port2 = SNES::Input::DeviceJoypad; + utility.updateControllers(); +} + +void MainWindow::setPort2Asciipad() { + config().input.port2 = ControllerPort2::Asciipad; + SNES::config.controller_port2 = SNES::Input::DeviceJoypad; + utility.updateControllers(); +} + +void MainWindow::setPort2Multitap() { + config().input.port2 = ControllerPort2::Multitap; + SNES::config.controller_port2 = SNES::Input::DeviceMultitap; + utility.updateControllers(); +} + +void MainWindow::setPort2Mouse() { + config().input.port2 = ControllerPort2::Mouse; + SNES::config.controller_port2 = SNES::Input::DeviceMouse; + utility.updateControllers(); +} + +void MainWindow::setPort2SuperScope() { + config().input.port2 = ControllerPort2::SuperScope; + SNES::config.controller_port2 = SNES::Input::DeviceSuperScope; + utility.updateControllers(); +} + +void MainWindow::setPort2Justifier() { + config().input.port2 = ControllerPort2::Justifier; + SNES::config.controller_port2 = SNES::Input::DeviceJustifier; + utility.updateControllers(); +} + +void MainWindow::setPort2Justifiers() { + config().input.port2 = ControllerPort2::Justifiers; + SNES::config.controller_port2 = SNES::Input::DeviceJustifiers; + utility.updateControllers(); +} + +void MainWindow::quit() { + hide(); + application.terminate = true; +} + +void MainWindow::setVideoMode1x() { utility.setScale(1); } +void MainWindow::setVideoMode2x() { utility.setScale(2); } +void MainWindow::setVideoMode3x() { utility.setScale(3); } +void MainWindow::setVideoMode4x() { utility.setScale(4); } +void MainWindow::setVideoMode5x() { utility.setScale(5); } + +void MainWindow::toggleAspectCorrection() { utility.toggleAspectCorrection(); } + +void MainWindow::setVideoNtsc() { utility.setNtscMode(); } +void MainWindow::setVideoPal() { utility.setPalMode(); } + +void MainWindow::toggleSmoothVideo() { utility.toggleSmoothVideoOutput(); } + +void MainWindow::configureFilter() { + QWidget *widget = filter.settings(); + if(widget) { + widget->show(); + widget->activateWindow(); + widget->raise(); + } +} + +void MainWindow::setFilter() { + for(unsigned i = 0; i < settings_videoFilter_list.size(); i++) { + if(sender() == settings_videoFilter_list[i]) { + config().video.context->swFilter = i; + utility.updateSoftwareFilter(); + syncUi(); + return; + } + } +} + +void MainWindow::muteAudio() { + settings_muteAudio->toggleChecked(); + config().audio.mute = settings_muteAudio->isChecked(); +} + +void MainWindow::setSpeedSlowest() { config().system.speed = 0; utility.updateEmulationSpeed(); syncUi(); } +void MainWindow::setSpeedSlow() { config().system.speed = 1; utility.updateEmulationSpeed(); syncUi(); } +void MainWindow::setSpeedNormal() { config().system.speed = 2; utility.updateEmulationSpeed(); syncUi(); } +void MainWindow::setSpeedFast() { config().system.speed = 3; utility.updateEmulationSpeed(); syncUi(); } +void MainWindow::setSpeedFastest() { config().system.speed = 4; utility.updateEmulationSpeed(); syncUi(); } + +void MainWindow::syncVideo() { utility.toggleSynchronizeVideo(); } +void MainWindow::syncAudio() { utility.toggleSynchronizeAudio(); } + +void MainWindow::showConfigWindow() { settingsWindow->show(); } + +void MainWindow::playMovie() { + movie.chooseFile(); + syncUi(); +} + +void MainWindow::stopMovie() { + movie.stop(); + syncUi(); +} + +void MainWindow::recordMovieFromPowerOn() { + utility.modifySystemState(Utility::PowerCycle); + movie.record(); + syncUi(); +} + +void MainWindow::recordMovieFromHere() { + movie.record(); + syncUi(); +} + +void MainWindow::saveScreenshot() { + //tell SNES::Interface to save a screenshot at the next video_refresh() event + interface.saveScreenshot = true; +} + +void MainWindow::showDebugger() { + #if defined(DEBUGGER) + debugger->show(); + #endif +} + +void MainWindow::showToolsDialog() { toolsWindow->show(); } + +void MainWindow::showDocumentation() { + QFile file(":/documentation.html"); + if(file.open(QIODevice::ReadOnly | QIODevice::Text)) { + htmlViewerWindow->show("Usage Documentation", file.readAll().constData()); + file.close(); + } +} + +void MainWindow::showLicense() { + QFile file(":/license.html"); + if(file.open(QIODevice::ReadOnly | QIODevice::Text)) { + htmlViewerWindow->show("License Agreement", file.readAll().constData()); + file.close(); + } +} +void MainWindow::showAbout() { + aboutWindow->show(); +} + +void MainWindow::closeEvent(QCloseEvent *event) { + QbWindow::closeEvent(event); + quit(); +} + +//============ +//CanvasObject +//============ + +//implement drag-and-drop support: +//drag cartridge image onto main window canvas area to load + +void CanvasObject::dragEnterEvent(QDragEnterEvent *event) { + if(event->mimeData()->hasUrls()) { + //do not accept multiple files at once + if(event->mimeData()->urls().count() == 1) event->acceptProposedAction(); + } +} + +void CanvasObject::dropEvent(QDropEvent *event) { + if(event->mimeData()->hasUrls()) { + QList list = event->mimeData()->urls(); + if(list.count() == 1) cartridge.loadNormal(list.at(0).toLocalFile().toUtf8().constData()); + } +} + +//accept all key events for this widget to prevent system chime from playing on OS X +//key events are actually handled by Input class + +void CanvasObject::keyPressEvent(QKeyEvent *event) { +} + +void CanvasObject::keyReleaseEvent(QKeyEvent *event) { +} + +//=========== +//CanvasWidget +//============ + +//custom video render and mouse capture functionality + +QPaintEngine* CanvasWidget::paintEngine() const { + if(SNES::cartridge.loaded()) { + video.refresh(); + return 0; + } + return QWidget::paintEngine(); +} + +void CanvasWidget::mouseReleaseEvent(QMouseEvent *event) { + //acquire exclusive mode access to mouse when video output widget is clicked + //(will only acquire if cart is loaded, and mouse / lightgun is in use.) + utility.acquireMouse(); +} + +void CanvasWidget::paintEvent(QPaintEvent *event) { + event->ignore(); +} diff --git a/mednafen/snes/src/ui_qt/base/main.moc.hpp b/mednafen/snes/src/ui_qt/base/main.moc.hpp new file mode 100755 index 0000000..2a9441a --- /dev/null +++ b/mednafen/snes/src/ui_qt/base/main.moc.hpp @@ -0,0 +1,151 @@ +class CanvasObject : public QWidget { +public: + void dragEnterEvent(QDragEnterEvent*); + void dropEvent(QDropEvent*); + void keyPressEvent(QKeyEvent*); + void keyReleaseEvent(QKeyEvent*); +}; + +class CanvasWidget : public CanvasObject { +public: + QPaintEngine* paintEngine() const; + void mouseReleaseEvent(QMouseEvent*); + void paintEvent(QPaintEvent*); +}; + +class MainWindow : public QbWindow { + Q_OBJECT + +public: + QMenuBar *menuBar; + QStatusBar *statusBar; + QVBoxLayout *layout; + QMenu *system; + QAction *system_load; + QMenu *system_loadSpecial; + QAction *system_loadSpecial_bsxSlotted; + QAction *system_loadSpecial_bsx; + QAction *system_loadSpecial_sufamiTurbo; + QAction *system_loadSpecial_superGameBoy; + QbCheckAction *system_power; + QAction *system_reset; + QMenu *system_port1; + QbRadioAction *system_port1_none; + QbRadioAction *system_port1_gamepad; + QbRadioAction *system_port1_asciipad; + QbRadioAction *system_port1_multitap; + QbRadioAction *system_port1_mouse; + QMenu *system_port2; + QbRadioAction *system_port2_none; + QbRadioAction *system_port2_gamepad; + QbRadioAction *system_port2_asciipad; + QbRadioAction *system_port2_multitap; + QbRadioAction *system_port2_mouse; + QbRadioAction *system_port2_superscope; + QbRadioAction *system_port2_justifier; + QbRadioAction *system_port2_justifiers; + QAction *system_exit; + QMenu *settings; + QMenu *settings_videoMode; + QbRadioAction *settings_videoMode_1x; + QbRadioAction *settings_videoMode_2x; + QbRadioAction *settings_videoMode_3x; + QbRadioAction *settings_videoMode_4x; + QbRadioAction *settings_videoMode_5x; + QbCheckAction *settings_videoMode_correctAspectRatio; + QbRadioAction *settings_videoMode_ntsc; + QbRadioAction *settings_videoMode_pal; + QMenu *settings_videoFilter; + QAction *settings_videoFilter_configure; + QbRadioAction *settings_videoFilter_none; + array settings_videoFilter_list; + QbCheckAction *settings_smoothVideo; + QbCheckAction *settings_muteAudio; + QMenu *settings_emulationSpeed; + QbRadioAction *settings_emulationSpeed_slowest; + QbRadioAction *settings_emulationSpeed_slow; + QbRadioAction *settings_emulationSpeed_normal; + QbRadioAction *settings_emulationSpeed_fast; + QbRadioAction *settings_emulationSpeed_fastest; + QbCheckAction *settings_emulationSpeed_syncVideo; + QbCheckAction *settings_emulationSpeed_syncAudio; + QAction *settings_configuration; + QMenu *tools; + QMenu *tools_movies; + QAction *tools_movies_play; + QAction *tools_movies_stop; + QAction *tools_movies_recordFromPowerOn; + QAction *tools_movies_recordFromHere; + QAction *tools_captureScreenshot; + QAction *tools_dialog; + QAction *tools_debugger; + QMenu *help; + QAction *help_documentation; + QAction *help_license; + QAction *help_about; + + CanvasObject *canvasContainer; + QVBoxLayout *canvasLayout; + CanvasWidget *canvas; + QLabel *systemState; + + void syncUi(); + bool isActive(); + void closeEvent(QCloseEvent*); + MainWindow(); + +public slots: + void loadCartridge(); + void loadBsxSlottedCartridge(); + void loadBsxCartridge(); + void loadSufamiTurboCartridge(); + void loadSuperGameBoyCartridge(); + void power(); + void reset(); + void setPort1None(); + void setPort1Gamepad(); + void setPort1Asciipad(); + void setPort1Multitap(); + void setPort1Mouse(); + void setPort2None(); + void setPort2Gamepad(); + void setPort2Asciipad(); + void setPort2Multitap(); + void setPort2Mouse(); + void setPort2SuperScope(); + void setPort2Justifier(); + void setPort2Justifiers(); + void quit(); + void setVideoMode1x(); + void setVideoMode2x(); + void setVideoMode3x(); + void setVideoMode4x(); + void setVideoMode5x(); + void toggleAspectCorrection(); + void setVideoNtsc(); + void setVideoPal(); + void configureFilter(); + void setFilter(); + void toggleSmoothVideo(); + void muteAudio(); + void setSpeedSlowest(); + void setSpeedSlow(); + void setSpeedNormal(); + void setSpeedFast(); + void setSpeedFastest(); + void syncVideo(); + void syncAudio(); + void showConfigWindow(); + void playMovie(); + void stopMovie(); + void recordMovieFromPowerOn(); + void recordMovieFromHere(); + void saveScreenshot(); + void showDebugger(); + void showToolsDialog(); + void showDocumentation(); + void showLicense(); + void showAbout(); +}; + +extern MainWindow *mainWindow; diff --git a/mednafen/snes/src/ui_qt/cartridge/cartridge.cpp b/mednafen/snes/src/ui_qt/cartridge/cartridge.cpp new file mode 100755 index 0000000..227effc --- /dev/null +++ b/mednafen/snes/src/ui_qt/cartridge/cartridge.cpp @@ -0,0 +1,378 @@ +#include "../ui-base.hpp" +Cartridge cartridge; + +//================ +//public functions +//================ + +bool Cartridge::information(const char *filename, Cartridge::Information &info) { + if(extension(filename) != "sfc") return false; //do not parse compressed images + + file fp; + if(fp.open(filename, file::mode_read) == false) return false; + + unsigned offset = 0; + if((fp.size() & 0x7fff) == 512) offset = 512; + + uint16_t complement, checksum; + + fp.seek(0x7fdc + offset); + complement = fp.readl(2); + checksum = fp.readl(2); + + unsigned header = offset + (complement + checksum == 65535 ? 0x7fb0 : 0xffb0); + + fp.seek(header + 0x10); + char name[22]; + fp.read((uint8_t*)name, 21); + name[21] = 0; + info.name = decodeShiftJIS(name); + + fp.seek(header + 0x29); + uint8_t region = fp.read(); + info.region = (region <= 1 || region >= 13) ? "NTSC" : "PAL"; + + info.romSize = fp.size() & ~0x7fff; + + fp.seek(header + 0x28); + info.ramSize = fp.readl(1); + if(info.ramSize) info.ramSize = 1024 << (info.ramSize & 7); + + fp.close(); + return true; +} + +bool Cartridge::saveStatesSupported() { + if(SNES::cartridge.mode() == SNES::Cartridge::ModeBsx) return false; + + if(SNES::cartridge.has_dsp3()) return false; + if(SNES::cartridge.has_dsp4()) return false; + if(SNES::cartridge.has_st011()) return false; + if(SNES::cartridge.has_st018()) return false; + + return true; +} + +bool Cartridge::loadNormal(const char *base) { + unload(); + if(loadCartridge(baseName = base, SNES::memory::cartrom) == false) return false; + SNES::s21fx.base(dir(baseName)); + SNES::cartridge.load(SNES::Cartridge::ModeNormal); + + loadMemory(baseName, ".srm", SNES::memory::cartram); + loadMemory(baseName, ".rtc", SNES::memory::cartrtc); + + fileName = baseName; + name = notdir(nall::basename(baseName)); + + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadBsxSlotted(const char *base, const char *slot) { + unload(); + if(loadCartridge(baseName = base, SNES::memory::cartrom) == false) return false; + loadCartridge(slotAName = slot, SNES::memory::bsxflash); + SNES::cartridge.load(SNES::Cartridge::ModeBsxSlotted); + + loadMemory(baseName, ".srm", SNES::memory::cartram); + loadMemory(baseName, ".rtc", SNES::memory::cartrtc); + + fileName = baseName; + name = notdir(nall::basename(baseName)); + if(*slot) name << " + " << notdir(nall::basename(slotAName)); + + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadBsx(const char *base, const char *slot) { + unload(); + if(loadCartridge(baseName = base, SNES::memory::cartrom) == false) return false; + loadCartridge(slotAName = slot, SNES::memory::bsxflash); + SNES::cartridge.load(SNES::Cartridge::ModeBsx); + + loadMemory(baseName, ".srm", SNES::memory::bsxram ); + loadMemory(baseName, ".psr", SNES::memory::bsxpram); + + fileName = slotAName; + name = *slot + ? notdir(nall::basename(slotAName)) + : notdir(nall::basename(baseName)); + + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadSufamiTurbo(const char *base, const char *slotA, const char *slotB) { + unload(); + if(loadCartridge(baseName = base, SNES::memory::cartrom) == false) return false; + loadCartridge(slotAName = slotA, SNES::memory::stArom); + loadCartridge(slotBName = slotB, SNES::memory::stBrom); + SNES::cartridge.load(SNES::Cartridge::ModeSufamiTurbo); + + loadMemory(slotAName, ".srm", SNES::memory::stAram); + loadMemory(slotBName, ".srm", SNES::memory::stBram); + + fileName = slotAName; + if(!*slotA && !*slotB) name = notdir(nall::basename(baseName)); + else if(!*slotB) name = notdir(nall::basename(slotAName)); + else if(!*slotA) name = notdir(nall::basename(slotBName)); + else name = notdir(nall::basename(slotAName)) << " + " << notdir(nall::basename(slotBName)); + + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +bool Cartridge::loadSuperGameBoy(const char *base, const char *slot) { + unload(); + if(loadCartridge(baseName = base, SNES::memory::cartrom) == false) return false; + loadCartridge(slotAName = slot, SNES::memory::gbrom); + SNES::cartridge.load(SNES::Cartridge::ModeSuperGameBoy); + + loadMemory(slotAName, ".sav", SNES::memory::gbram); + loadMemory(slotBName, ".rtc", SNES::memory::gbrtc); + + fileName = slotAName; + name = *slot + ? notdir(nall::basename(slotAName)) + : notdir(nall::basename(baseName)); + + utility.modifySystemState(Utility::LoadCartridge); + return true; +} + +void Cartridge::saveMemory() { + if(SNES::cartridge.loaded() == false) return; + + switch(SNES::cartridge.mode()) { + case SNES::Cartridge::ModeNormal: + case SNES::Cartridge::ModeBsxSlotted: { + saveMemory(baseName, ".srm", SNES::memory::cartram); + saveMemory(baseName, ".rtc", SNES::memory::cartrtc); + } break; + + case SNES::Cartridge::ModeBsx: { + saveMemory(baseName, ".srm", SNES::memory::bsxram ); + saveMemory(baseName, ".psr", SNES::memory::bsxpram); + } break; + + case SNES::Cartridge::ModeSufamiTurbo: { + saveMemory(slotAName, ".srm", SNES::memory::stAram); + saveMemory(slotBName, ".srm", SNES::memory::stBram); + } break; + + case SNES::Cartridge::ModeSuperGameBoy: { + saveMemory(slotAName, ".sav", SNES::memory::gbram); + saveMemory(slotAName, ".rtc", SNES::memory::gbrtc); + } break; + } +} + +void Cartridge::unload() { + if(SNES::cartridge.loaded() == false) return; + utility.modifySystemState(Utility::UnloadCartridge); +} + +void Cartridge::loadCheats() { + string name; + name << filepath(nall::basename(baseName), config().path.cheat); + name << ".cht"; + cheatEditorWindow->load(name); +} + +void Cartridge::saveCheats() { + string name; + name << filepath(nall::basename(baseName), config().path.cheat); + name << ".cht"; + cheatEditorWindow->save(name); +} + +//================= +//private functions +//================= + +bool Cartridge::loadCartridge(string &filename, SNES::MappedRAM &memory) { + if(file::exists(filename) == false) return false; + + uint8_t *data; + unsigned size; + audio.clear(); + if(reader.load(filename, data, size) == false) return false; + + patchApplied = false; + string name; + name << filepath(nall::basename(filename), config().path.patch); + name << ".ups"; + + file fp; + if(config().file.applyPatches && fp.open(name, file::mode_read)) { + unsigned patchsize = fp.size(); + uint8_t *patchdata = new uint8_t[patchsize]; + fp.read(patchdata, patchsize); + fp.close(); + + uint8_t *outdata = 0; + unsigned outsize = 0; + ups patcher; + ups::result result = patcher.apply(patchdata, patchsize, data, size, outdata, outsize); + delete[] patchdata; + + bool apply = false; + if(result == ups::ok) apply = true; + if(config().file.bypass_patch_crc32) { + if(result == ups::input_crc32_invalid ) apply = true; + if(result == ups::output_crc32_invalid) apply = true; + } + + if(apply == true) { + delete[] data; + data = outdata; + size = outsize; + patchApplied = true; + } else { + delete[] outdata; + } + } + + memory.copy(data, size); + delete[] data; + return true; +} + +bool Cartridge::loadMemory(const char *filename, const char *extension, SNES::MappedRAM &memory) { + if(memory.size() == 0 || memory.size() == -1U) return false; + + string name; + name << filepath(nall::basename(filename), config().path.save); + name << extension; + + file fp; + if(fp.open(name, file::mode_read) == false) return false; + + unsigned size = fp.size(); + uint8_t *data = new uint8_t[size]; + fp.read(data, size); + fp.close(); + + memory.copy(data, size); + delete[] data; + return true; +} + +bool Cartridge::saveMemory(const char *filename, const char *extension, SNES::MappedRAM &memory) { + if(memory.size() == 0 || memory.size() == -1U) return false; + + string name; + name << filepath(nall::basename(filename), config().path.save); + name << extension; + + file fp; + if(fp.open(name, file::mode_write) == false) return false; + + fp.write(memory.data(), memory.size()); + fp.close(); + return true; +} + +string Cartridge::decodeShiftJIS(const char *text) { + unsigned length = strlen(text), offset = 0; + string output; + + for(unsigned i = 0; i < length;) { + unsigned code = 0; + uint8_t n = text[i++]; + + if(n == 0x00) { + //string terminator + break; + } else if(n >= 0x20 && n <= 0x7f) { + //ASCII + code = n; + } else if(n >= 0xa0 && n <= 0xdf) { + //ShiftJIS half-width katakana + unsigned dakuten = 0, handakuten = 0; + + switch(n) { + case 0xa1: code = 0xe38082; break; //(period) + case 0xa2: code = 0xe3808c; break; //(open quote) + case 0xa3: code = 0xe3808d; break; //(close quote) + case 0xa4: code = 0xe38081; break; //(comma) + case 0xa5: code = 0xe383bb; break; //(separator) + case 0xa6: code = 0xe383b2; break; //wo + case 0xa7: code = 0xe382a1; break; //la + case 0xa8: code = 0xe382a3; break; //li + case 0xa9: code = 0xe382a5; break; //lu + case 0xaa: code = 0xe382a7; break; //le + case 0xab: code = 0xe382a9; break; //lo + case 0xac: code = 0xe383a3; break; //lya + case 0xad: code = 0xe383a5; break; //lyu + case 0xae: code = 0xe383a7; break; //lyo + case 0xaf: code = 0xe38383; break; //ltsu + case 0xb0: code = 0xe383bc; break; //- + case 0xb1: code = 0xe382a2; break; //a + case 0xb2: code = 0xe382a4; break; //i + case 0xb3: code = 0xe382a6; break; //u + case 0xb4: code = 0xe382a8; break; //e + case 0xb5: code = 0xe382aa; break; //o + case 0xb6: code = 0xe382ab; dakuten = 0xe382ac; break; //ka, ga + case 0xb7: code = 0xe382ad; dakuten = 0xe382ae; break; //ki, gi + case 0xb8: code = 0xe382af; dakuten = 0xe382b0; break; //ku, gu + case 0xb9: code = 0xe382b1; dakuten = 0xe382b2; break; //ke, ge + case 0xba: code = 0xe382b3; dakuten = 0xe382b4; break; //ko, go + case 0xbb: code = 0xe382b5; dakuten = 0xe382b6; break; //sa, za + case 0xbc: code = 0xe382b7; dakuten = 0xe382b8; break; //shi, zi + case 0xbd: code = 0xe382b9; dakuten = 0xe382ba; break; //su, zu + case 0xbe: code = 0xe382bb; dakuten = 0xe382bc; break; //se, ze + case 0xbf: code = 0xe382bd; dakuten = 0xe382be; break; //so, zo + case 0xc0: code = 0xe382bf; dakuten = 0xe38380; break; //ta, da + case 0xc1: code = 0xe38381; dakuten = 0xe38382; break; //chi, di + case 0xc2: code = 0xe38384; dakuten = 0xe38385; break; //tsu, du + case 0xc3: code = 0xe38386; dakuten = 0xe38387; break; //te, de + case 0xc4: code = 0xe38388; dakuten = 0xe38389; break; //to, do + case 0xc5: code = 0xe3838a; break; //na + case 0xc6: code = 0xe3838b; break; //ni + case 0xc7: code = 0xe3838c; break; //nu + case 0xc8: code = 0xe3838d; break; //ne + case 0xc9: code = 0xe3838e; break; //no + case 0xca: code = 0xe3838f; dakuten = 0xe38390; handakuten = 0xe38391; break; //ha, ba, pa + case 0xcb: code = 0xe38392; dakuten = 0xe38393; handakuten = 0xe38394; break; //hi, bi, pi + case 0xcc: code = 0xe38395; dakuten = 0xe38396; handakuten = 0xe38397; break; //fu, bu, pu + case 0xcd: code = 0xe38398; dakuten = 0xe38399; handakuten = 0xe3839a; break; //he, be, pe + case 0xce: code = 0xe3839b; dakuten = 0xe3839c; handakuten = 0xe3839d; break; //ho, bo, po + case 0xcf: code = 0xe3839e; break; //ma + case 0xd0: code = 0xe3839f; break; //mi + case 0xd1: code = 0xe383a0; break; //mu + case 0xd2: code = 0xe383a1; break; //me + case 0xd3: code = 0xe383a2; break; //mo + case 0xd4: code = 0xe383a4; break; //ya + case 0xd5: code = 0xe383a6; break; //yu + case 0xd6: code = 0xe383a8; break; //yo + case 0xd7: code = 0xe383a9; break; //ra + case 0xd8: code = 0xe383aa; break; //ri + case 0xd9: code = 0xe383ab; break; //ru + case 0xda: code = 0xe383ac; break; //re + case 0xdb: code = 0xe383ad; break; //ro + case 0xdc: code = 0xe383af; break; //wa + case 0xdd: code = 0xe383b3; break; //n + } + + if(dakuten && ((uint8_t)text[i] == 0xde)) { + code = dakuten; + i++; + } else if(handakuten && ((uint8_t)text[i] == 0xdf)) { + code = handakuten; + i++; + } + } + + if(code) { + if((uint8_t)(code >> 16)) output[offset++] = (char)(code >> 16); + if((uint8_t)(code >> 8)) output[offset++] = (char)(code >> 8); + if((uint8_t)(code >> 0)) output[offset++] = (char)(code >> 0); + } + } + + output[offset] = 0; + return output; +} diff --git a/mednafen/snes/src/ui_qt/cartridge/cartridge.hpp b/mednafen/snes/src/ui_qt/cartridge/cartridge.hpp new file mode 100755 index 0000000..1254523 --- /dev/null +++ b/mednafen/snes/src/ui_qt/cartridge/cartridge.hpp @@ -0,0 +1,38 @@ +class Cartridge { +public: + string name; //printable name + string fileName; //ideal file name for saving data to disk + string baseName; //physical cartridge file name + string slotAName; //Sufami Turbo slot A file name or BS-X slot file name + string slotBName; //Sufami Turbo slot B file name + bool patchApplied; //true if UPS patch was applied to image + + struct Information { + string name; + string region; + unsigned romSize; + unsigned ramSize; + }; + + bool information(const char*, Information&); + bool saveStatesSupported(); + + bool loadNormal(const char*); + bool loadBsxSlotted(const char*, const char*); + bool loadBsx(const char*, const char*); + bool loadSufamiTurbo(const char*, const char *, const char*); + bool loadSuperGameBoy(const char*, const char*); + void saveMemory(); + void unload(); + + void loadCheats(); + void saveCheats(); + +private: + bool loadCartridge(string&, SNES::MappedRAM&); + bool loadMemory(const char*, const char*, SNES::MappedRAM&); + bool saveMemory(const char*, const char*, SNES::MappedRAM&); + string decodeShiftJIS(const char*); +}; + +extern Cartridge cartridge; diff --git a/mednafen/snes/src/ui_qt/config.cpp b/mednafen/snes/src/ui_qt/config.cpp new file mode 100755 index 0000000..c0b09e1 --- /dev/null +++ b/mednafen/snes/src/ui_qt/config.cpp @@ -0,0 +1,143 @@ +Configuration &config() { + static Configuration configuration; + return configuration; +} + +bool Configuration::load(const char *filename) { + if(configuration::load(filename) == false) return false; + SNES::config.superfx.speed = max(0, min(2, SNES::config.superfx.speed)); + video.context = (video.isFullscreen == false) ? &video.windowed : &video.fullscreen; + return true; +} + +Configuration::Configuration() { + //======== + //external + //======== + + attach(SNES::config.controller_port1 = SNES::Input::DeviceJoypad, "snes.controllerPort1"); + attach(SNES::config.controller_port2 = SNES::Input::DeviceJoypad, "snes.controllerPort2"); + attach(SNES::config.expansion_port = SNES::System::ExpansionBSX, "snes.expansionPort"); + attach(SNES::config.region = SNES::System::Autodetect, "snes.region"); + + attach(SNES::config.cpu.version = 2, "cpu.version", "Valid version(s) are: 1, 2"); + attach(SNES::config.cpu.ntsc_clock_rate = 21477272, "cpu.ntscClockRate"); + attach(SNES::config.cpu.pal_clock_rate = 21281370, "cpu.palClockRate"); + attach(SNES::config.cpu.alu_mul_delay = 2, "cpu.aluMulDelay"); + attach(SNES::config.cpu.alu_div_delay = 2, "cpu.aluDivDelay"); + attach(SNES::config.cpu.wram_init_value = 0x55, "cpu.wramInitValue"); + + attach(SNES::config.smp.ntsc_clock_rate = 24607104, "smp.ntscClockRate"); + attach(SNES::config.smp.pal_clock_rate = 24607104, "smp.palClockRate"); + + attach(SNES::config.ppu1.version = 1, "ppu1.version", "Valid version(s) are: 1"); + attach(SNES::config.ppu2.version = 3, "ppu2.version", "Valid version(s) are: 1, 2, 3"); + + attach(SNES::config.superfx.speed = 0, "superfx.speed", "0 = Auto-select, 1 = Force 10.74MHz, 2 = Force 21.48MHz"); + + //======== + //internal + //======== + + attach(system.video = "", "system.video"); + attach(system.audio = "", "system.audio"); + attach(system.input = "", "system.input"); + attach(system.crashedOnLastRun = false, "system.crashedOnLastRun"); + attach(system.speed = 2, "system.speed"); + attach(system.speedSlowest = 50, "system.speedSlowest"); + attach(system.speedSlow = 75, "system.speedSlow"); + attach(system.speedNormal = 100, "system.speedNormal"); + attach(system.speedFast = 150, "system.speedFast"); + attach(system.speedFastest = 200, "system.speedFastest"); + attach(system.autoSaveMemory = false, "system.autoSaveMemory", "Automatically save cartridge back-up RAM once every minute"); + attach(system.rewindEnabled = false, "system.rewindEnabled", "Automatically save states periodically to allow auto-rewind support"); + + attach(diskBrowser.showPanel = true, "diskBrowser.showPanel"); + + attach(file.autodetect_type = false, "file.autodetectType"); + attach(file.applyPatches = true, "file.applyPatches"); + attach(file.bypass_patch_crc32 = false, "file.bypassPatchCrc32"); + + attach(path.rom = "", "path.rom"); + attach(path.save = "", "path.save"); + attach(path.state = "", "path.state"); + attach(path.patch = "", "path.patch"); + attach(path.cheat = "", "path.cheat"); + attach(path.data = "", "path.data"); + attach(path.bsx = "", "path.bsx"); + attach(path.st = "", "path.st"); + attach(path.sgb = "", "path.sgb"); + attach(path.fragmentShader = "", "path.fragmentShader"); + attach(path.vertexShader = "", "path.vertexShader"); + + attach(path.current.folder = "", "path.current.folder"); + attach(path.current.movie = "", "path.current.movie"); + attach(path.current.shader = "", "path.current.shader"); + attach(path.current.cartridge = "", "path.current.cartridge"); + attach(path.current.bsx = "", "path.current.bsx"); + attach(path.current.st = "", "path.current.st"); + attach(path.current.sgb = "", "path.current.sgb"); + attach(path.current.filter = 0, "path.current.filter"); + + video.context = &video.windowed; + attach(video.isFullscreen = false, "video.isFullscreen"); + attach(video.synchronize = false, "video.synchronize"); + + attach(video.contrastAdjust = 0, "video.contrastAdjust"); + attach(video.brightnessAdjust = 0, "video.brightnessAdjust"); + attach(video.gammaAdjust = 0, "video.gammaAdjust"); + attach(video.scanlineAdjust = 100, "video.scanlineAdjust"); + attach(video.enableGammaRamp = true, "video.enableGammaRamp"); + + attach(video.ntscAspectRatio = 54.0 / 47.0, "video.ntscAspectRatio", "NTSC aspect ratio (x / y)"); + attach(video.palAspectRatio = 32.0 / 23.0, "video.palAspectRatio", "PAL aspect ratio (x / y)"); + + attach(video.windowed.correctAspectRatio = true, "video.windowed.correctAspectRatio"); + attach(video.windowed.multiplier = 2, "video.windowed.multiplier"); + attach(video.windowed.region = 0, "video.windowed.region"); + + attach(video.windowed.hwFilter = 1, "video.windowed.hwFilter"); + attach(video.windowed.swFilter = 0, "video.windowed.swFilter"); + + attach(video.fullscreen.correctAspectRatio = true, "video.fullscreen.correctAspectRatio"); + attach(video.fullscreen.multiplier = 9, "video.fullscreen.multiplier"); + attach(video.fullscreen.region = 0, "video.fullscreen.region"); + + attach(video.fullscreen.hwFilter = 1, "video.fullscreen.hwFilter"); + attach(video.fullscreen.swFilter = 0, "video.fullscreen.swFilter"); + + attach(audio.synchronize = true, "audio.synchronize"); + attach(audio.mute = false, "audio.mute"); + + attach(audio.volume = 100, "audio.volume"); + attach(audio.latency = 80, "audio.latency"); + attach(audio.outputFrequency = 48000, "audio.outputFrequency"); + attach(audio.inputFrequency = 32000, "audio.inputFrequency"); + + attach(input.port1 = ControllerPort1::Gamepad, "input.port1"); + attach(input.port2 = ControllerPort2::Gamepad, "input.port2"); + attach(input.focusPolicy = Input::FocusPolicyIgnoreInput, "input.focusPolicy"); + attach(input.allowInvalidInput = false, "input.allowInvalidInput", "Allow up+down / left+right combinations; may trigger bugs in some games"); + + attach(debugger.cacheUsageToDisk = false, "debugger.cacheUsageToDisk"); + + attach(geometry.mainWindow = "", "geometry.mainWindow"); + attach(geometry.loaderWindow = "", "geometry.loaderWindow"); + attach(geometry.htmlViewerWindow = "", "geometry.htmlViewerWindow"); + attach(geometry.aboutWindow = "", "geometry.aboutWindow"); + attach(geometry.diskBrowser = "", "geometry.diskBrowser"); + attach(geometry.folderCreator = "", "geometry.folderCreator"); + attach(geometry.settingsWindow = "", "geometry.settingsWindow"); + attach(geometry.toolsWindow = "", "geometry.toolsWindow"); + + attach(geometry.debugger = "", "geometry.debugger"); + attach(geometry.disassembler = "", "geometry.disassembler"); + attach(geometry.breakpointEditor = "", "geometry.breakpointEditor"); + attach(geometry.memoryEditor = "", "geometry.memoryEditor"); + attach(geometry.propertiesViewer = "", "geometry.propertiesViewer"); + attach(geometry.layerToggle = "", "geometry.layerToggle"); + attach(geometry.vramViewer = "", "geometry.vramViewer"); + attach(geometry.oamViewer = "", "geometry.oamViewer"); + attach(geometry.cgramViewer = "", "geometry.cgramViewer"); + attach(geometry.debuggerOptions = "", "geometry.debuggerOptions"); +} diff --git a/mednafen/snes/src/ui_qt/config.hpp b/mednafen/snes/src/ui_qt/config.hpp new file mode 100755 index 0000000..41edd9f --- /dev/null +++ b/mednafen/snes/src/ui_qt/config.hpp @@ -0,0 +1,98 @@ +class Configuration : public configuration { +public: + struct System { + string video, audio, input; + bool crashedOnLastRun; + unsigned speed; + unsigned speedSlowest; + unsigned speedSlow; + unsigned speedNormal; + unsigned speedFast; + unsigned speedFastest; + bool autoSaveMemory; + bool rewindEnabled; + } system; + + struct File { + bool autodetect_type; + bool applyPatches; + bool bypass_patch_crc32; + } file; + + struct DiskBrowser { + bool showPanel; + } diskBrowser; + + struct Path { + string base; //binary path + string user; //user profile path (bsnes.cfg, ...) + string startup; //startup path + string rom, save, state, patch, cheat, data; + string bsx, st, sgb; + string fragmentShader, vertexShader; + + struct Current { + string folder, movie, shader, cartridge, bsx, st, sgb; + unsigned filter; //current active filter for "Load Cartridge" + } current; + } path; + + struct Video { + bool isFullscreen; + bool synchronize; + signed contrastAdjust, brightnessAdjust, gammaAdjust, scanlineAdjust; + bool enableGammaRamp; + double ntscAspectRatio, palAspectRatio; + + struct Context { + bool correctAspectRatio; + unsigned multiplier, region; + unsigned hwFilter, swFilter; + } *context, windowed, fullscreen; + } video; + + struct Audio { + bool synchronize; + bool mute; + unsigned volume, latency, outputFrequency, inputFrequency; + } audio; + + struct Input { + unsigned port1; + unsigned port2; + enum policy_t { FocusPolicyPauseEmulation, FocusPolicyIgnoreInput, FocusPolicyAllowInput }; + unsigned focusPolicy; + bool allowInvalidInput; + } input; + + struct Debugger { + bool cacheUsageToDisk; + } debugger; + + struct Geometry { + string mainWindow; + string loaderWindow; + string htmlViewerWindow; + string aboutWindow; + string diskBrowser; + string folderCreator; + string settingsWindow; + string toolsWindow; + + string debugger; + string disassembler; + string breakpointEditor; + string memoryEditor; + string propertiesViewer; + string layerToggle; + string vramViewer; + string oamViewer; + string cgramViewer; + string debuggerOptions; + } geometry; + + bool load(const char *filename); + Configuration(); +}; + +Configuration &config(); diff --git a/mednafen/snes/src/ui_qt/debugger/debugger.cpp b/mednafen/snes/src/ui_qt/debugger/debugger.cpp new file mode 100755 index 0000000..29890d4 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/debugger.cpp @@ -0,0 +1,248 @@ +#include "../ui-base.hpp" + +#if defined(DEBUGGER) + +#include "debugger.moc" +Debugger *debugger; + +#include "hexeditor.cpp" +#include "tracer.cpp" + +#include "tools/disassembler.cpp" +#include "tools/breakpoint.cpp" +#include "tools/memory.cpp" +#include "tools/properties.cpp" + +#include "ppu/layer-toggle.cpp" +#include "ppu/vram-viewer.cpp" +#include "ppu/oam-viewer.cpp" +#include "ppu/cgram-viewer.cpp" + +#include "misc/debugger-options.cpp" + +Debugger::Debugger() : QbWindow(config().geometry.debugger) { + setObjectName("debugger"); + setWindowTitle("Debugger"); + + layout = new QHBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + menu = new QMenuBar; + layout->setMenuBar(menu); + + menu_tools = menu->addMenu("Tools"); + menu_tools_disassembler = menu_tools->addAction("Disassembler ..."); + menu_tools_breakpoint = menu_tools->addAction("Breakpoint Editor ..."); + menu_tools_memory = menu_tools->addAction("Memory Editor ..."); + menu_tools_propertiesViewer = menu_tools->addAction("Properties Viewer ..."); + + menu_ppu = menu->addMenu("S-PPU"); + menu_ppu_layerToggle = menu_ppu->addAction("Layer Toggle ..."); + menu_ppu_vramViewer = menu_ppu->addAction("Video RAM Viewer ..."); + menu_ppu_oamViewer = menu_ppu->addAction("Sprite Viewer ..."); + menu_ppu_cgramViewer = menu_ppu->addAction("Palette Viewer ..."); + + menu_misc = menu->addMenu("Misc"); + menu_misc_clear = menu_misc->addAction("Clear Console"); + menu_misc_options = menu_misc->addAction("Options ..."); + + console = new QTextEdit; + console->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + console->setReadOnly(true); + console->setFont(QFont(Style::Monospace)); + console->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + console->setMinimumWidth((92 + 4) * console->fontMetrics().width(' ')); + console->setMinimumHeight((25 + 1) * console->fontMetrics().height()); + layout->addWidget(console); + + controlLayout = new QVBoxLayout; + controlLayout->setSpacing(0); + layout->addLayout(controlLayout); + + commandLayout = new QHBoxLayout; + controlLayout->addLayout(commandLayout); + + runBreak = new QPushButton("Break"); + commandLayout->addWidget(runBreak); + commandLayout->addSpacing(Style::WidgetSpacing); + + stepInstruction = new QPushButton("Step"); + commandLayout->addWidget(stepInstruction); + + controlLayout->addSpacing(Style::WidgetSpacing); + + stepCPU = new QCheckBox("Step S-CPU"); + controlLayout->addWidget(stepCPU); + + stepSMP = new QCheckBox("Step S-SMP"); + controlLayout->addWidget(stepSMP); + + traceCPU = new QCheckBox("Trace S-CPU opcodes"); + controlLayout->addWidget(traceCPU); + + traceSMP = new QCheckBox("Trace S-SMP opcodes"); + controlLayout->addWidget(traceSMP); + + traceMask = new QCheckBox("Enable trace mask"); + controlLayout->addWidget(traceMask); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + controlLayout->addWidget(spacer); + + tracer = new Tracer; + disassembler = new Disassembler; + breakpointEditor = new BreakpointEditor; + memoryEditor = new MemoryEditor; + propertiesViewer = new PropertiesViewer; + layerToggle = new LayerToggle; + vramViewer = new VramViewer; + oamViewer = new OamViewer; + cgramViewer = new CgramViewer; + debuggerOptions = new DebuggerOptions; + + connect(menu_tools_disassembler, SIGNAL(triggered()), disassembler, SLOT(show())); + connect(menu_tools_breakpoint, SIGNAL(triggered()), breakpointEditor, SLOT(show())); + connect(menu_tools_memory, SIGNAL(triggered()), memoryEditor, SLOT(show())); + connect(menu_tools_propertiesViewer, SIGNAL(triggered()), propertiesViewer, SLOT(show())); + + connect(menu_ppu_layerToggle, SIGNAL(triggered()), layerToggle, SLOT(show())); + connect(menu_ppu_vramViewer, SIGNAL(triggered()), vramViewer, SLOT(show())); + connect(menu_ppu_oamViewer, SIGNAL(triggered()), oamViewer, SLOT(show())); + connect(menu_ppu_cgramViewer, SIGNAL(triggered()), cgramViewer, SLOT(show())); + + connect(menu_misc_clear, SIGNAL(triggered()), this, SLOT(clear())); + connect(menu_misc_options, SIGNAL(triggered()), debuggerOptions, SLOT(show())); + + connect(runBreak, SIGNAL(released()), this, SLOT(toggleRunStatus())); + connect(stepInstruction, SIGNAL(released()), this, SLOT(stepAction())); + connect(stepCPU, SIGNAL(released()), this, SLOT(synchronize())); + connect(stepSMP, SIGNAL(released()), this, SLOT(synchronize())); + connect(traceCPU, SIGNAL(stateChanged(int)), tracer, SLOT(setCpuTraceState(int))); + connect(traceSMP, SIGNAL(stateChanged(int)), tracer, SLOT(setSmpTraceState(int))); + connect(traceMask, SIGNAL(stateChanged(int)), tracer, SLOT(setTraceMaskState(int))); + + frameCounter = 0; + synchronize(); + resize(855, 425); +} + +void Debugger::modifySystemState(unsigned state) { + string usagefile = filepath(nall::basename(cartridge.fileName), config().path.data); + usagefile << "-usage.bin"; + file fp; + + if(state == Utility::LoadCartridge) { + if(config().debugger.cacheUsageToDisk && fp.open(usagefile, file::mode_read)) { + fp.read(SNES::cpu.usage, 1 << 24); + fp.read(SNES::smp.usage, 1 << 16); + fp.close(); + } else { + memset(SNES::cpu.usage, 0x00, 1 << 24); + memset(SNES::smp.usage, 0x00, 1 << 16); + } + } + + if(state == Utility::UnloadCartridge) { + if(config().debugger.cacheUsageToDisk && fp.open(usagefile, file::mode_write)) { + fp.write(SNES::cpu.usage, 1 << 24); + fp.write(SNES::smp.usage, 1 << 16); + fp.close(); + } + } +} + +void Debugger::synchronize() { + runBreak->setText(application.debug ? "Run" : "Break"); + stepInstruction->setEnabled(SNES::cartridge.loaded() && application.debug && (stepCPU->isChecked() || stepSMP->isChecked())); + SNES::debugger.step_cpu = application.debug && stepCPU->isChecked(); + SNES::debugger.step_smp = application.debug && stepSMP->isChecked(); + + memoryEditor->synchronize(); +} + +void Debugger::echo(const char *message) { + console->moveCursor(QTextCursor::End); + console->insertHtml(message); +} + +void Debugger::clear() { + console->setHtml(""); +} + +void Debugger::toggleRunStatus() { + application.debug = !application.debug; + if(!application.debug) application.debugrun = false; + synchronize(); +} + +void Debugger::stepAction() { + application.debugrun = true; +} + +void Debugger::event() { + char t[256]; + + switch(SNES::debugger.break_event) { + case SNES::Debugger::BreakpointHit: { + unsigned n = SNES::debugger.breakpoint_hit; + echo(string() << "Breakpoint " << n << " hit (" << SNES::debugger.breakpoint[n].counter << ").
"); + + if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::CPUBus) { + SNES::debugger.step_cpu = true; + SNES::cpu.disassemble_opcode(t, SNES::cpu.opcode_pc); + string s = t; + s.replace(" ", " "); + echo(string() << "" << s << "
"); + disassembler->refresh(Disassembler::CPU, SNES::cpu.opcode_pc); + } + + if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::APURAM) { + SNES::debugger.step_smp = true; + SNES::smp.disassemble_opcode(t, SNES::smp.opcode_pc); + string s = t; + s.replace(" ", " "); + echo(string() << "" << t << "
"); + disassembler->refresh(Disassembler::SMP, SNES::smp.opcode_pc); + } + } break; + + case SNES::Debugger::CPUStep: { + SNES::cpu.disassemble_opcode(t, SNES::cpu.regs.pc); + string s = t; + s.replace(" ", " "); + echo(string() << "" << s << "
"); + disassembler->refresh(Disassembler::CPU, SNES::cpu.regs.pc); + } break; + + case SNES::Debugger::SMPStep: { + SNES::smp.disassemble_opcode(t, SNES::smp.regs.pc); + string s = t; + s.replace(" ", " "); + echo(string() << "" << s << "
"); + disassembler->refresh(Disassembler::SMP, SNES::smp.regs.pc); + } break; + } + + autoUpdate(); +} + +//called once every time a video frame is rendered, used to update "auto refresh" tool windows +void Debugger::frameTick() { + if(++frameCounter >= (SNES::system.region() == SNES::System::NTSC ? 60 : 50)) { + frameCounter = 0; + autoUpdate(); + } +} + +void Debugger::autoUpdate() { + memoryEditor->autoUpdate(); + propertiesViewer->autoUpdate(); + vramViewer->autoUpdate(); + oamViewer->autoUpdate(); + cgramViewer->autoUpdate(); +} + +#endif diff --git a/mednafen/snes/src/ui_qt/debugger/debugger.moc.hpp b/mednafen/snes/src/ui_qt/debugger/debugger.moc.hpp new file mode 100755 index 0000000..448dce2 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/debugger.moc.hpp @@ -0,0 +1,51 @@ +class Debugger : public QbWindow { + Q_OBJECT + +public: + QMenuBar *menu; + QMenu *menu_tools; + QAction *menu_tools_disassembler; + QAction *menu_tools_breakpoint; + QAction *menu_tools_memory; + QAction *menu_tools_propertiesViewer; + QMenu *menu_ppu; + QAction *menu_ppu_layerToggle; + QAction *menu_ppu_vramViewer; + QAction *menu_ppu_oamViewer; + QAction *menu_ppu_cgramViewer; + QMenu *menu_misc; + QAction *menu_misc_clear; + QAction *menu_misc_options; + + QHBoxLayout *layout; + QTextEdit *console; + QVBoxLayout *controlLayout; + QHBoxLayout *commandLayout; + QPushButton *runBreak; + QPushButton *stepInstruction; + QCheckBox *stepCPU; + QCheckBox *stepSMP; + QCheckBox *traceCPU; + QCheckBox *traceSMP; + QCheckBox *traceMask; + QWidget *spacer; + + void modifySystemState(unsigned); + void echo(const char *message); + void event(); + void frameTick(); + void autoUpdate(); + Debugger(); + +public slots: + void clear(); + void synchronize(); + + void toggleRunStatus(); + void stepAction(); + +private: + unsigned frameCounter; +}; + +extern Debugger *debugger; diff --git a/mednafen/snes/src/ui_qt/debugger/hexeditor.cpp b/mednafen/snes/src/ui_qt/debugger/hexeditor.cpp new file mode 100755 index 0000000..fe65824 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/hexeditor.cpp @@ -0,0 +1,120 @@ +#include "hexeditor.moc" + +void HexEditor::keyPressEvent(QKeyEvent *event) { + QTextCursor cursor = textCursor(); + unsigned x = cursor.position() % 56; + unsigned y = cursor.position() / 56; + + int hexCode = -1; + switch(event->key()) { + case Qt::Key_0: hexCode = 0; break; + case Qt::Key_1: hexCode = 1; break; + case Qt::Key_2: hexCode = 2; break; + case Qt::Key_3: hexCode = 3; break; + case Qt::Key_4: hexCode = 4; break; + case Qt::Key_5: hexCode = 5; break; + case Qt::Key_6: hexCode = 6; break; + case Qt::Key_7: hexCode = 7; break; + case Qt::Key_8: hexCode = 8; break; + case Qt::Key_9: hexCode = 9; break; + case Qt::Key_A: hexCode = 10; break; + case Qt::Key_B: hexCode = 11; break; + case Qt::Key_C: hexCode = 12; break; + case Qt::Key_D: hexCode = 13; break; + case Qt::Key_E: hexCode = 14; break; + case Qt::Key_F: hexCode = 15; break; + } + + if(cursor.hasSelection() == false && hexCode != -1) { + bool cursorOffsetValid = (x >= 8 && ((x - 8) % 3) != 2); + if(cursorOffsetValid) { + bool nibble = (x - 8) % 3; //0 = top nibble, 1 = bottom nibble + unsigned cursorOffset = y * 16 + ((x - 8) / 3); + unsigned effectiveOffset = hexOffset + cursorOffset; + if(effectiveOffset >= hexSize) effectiveOffset %= hexSize; + + uint8 data = reader(effectiveOffset); + data &= (nibble == 0 ? 0x0f : 0xf0); + data |= (nibble == 0 ? (hexCode << 4) : (hexCode << 0)); + writer(effectiveOffset, data); + update(); + + cursor.setPosition(y * 56 + x + 1); //advance cursor + setTextCursor(cursor); + } + } else { + //allow navigation keys to move cursor, but block text input + setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse); + QTextEdit::keyPressEvent(event); + setTextInteractionFlags(Qt::TextEditorInteraction); + } +} + +void HexEditor::setOffset(unsigned newOffset) { + slotLock = true; + hexOffset = newOffset; + scrollbar->setSliderPosition(hexOffset / 16); + slotLock = false; +} + +void HexEditor::setSize(unsigned newSize) { + hexSize = newSize; + scrollbar->setRange(0, hexSize / 16 - 16); +} + +void HexEditor::update() { + string output; + char temp[256]; + unsigned offset = hexOffset; + + for(unsigned y = 0; y < 16; y++) { + if(offset >= hexSize) break; + sprintf(temp, "%.6x", offset & 0xffffff); + output << "" << temp << "  "; + + for(unsigned x = 0; x < 16; x++) { + if(offset >= hexSize) break; + sprintf(temp, "%.2x", reader(offset++)); + output << "" << temp << ""; + if(x != 15) output << " "; + } + + if(y != 15) output << "
"; + } + + setHtml(output); +} + +void HexEditor::sliderMoved() { + if(slotLock) return; + unsigned offset = scrollbar->sliderPosition(); + hexOffset = offset * 16; + update(); +} + +HexEditor::HexEditor() { + hexOffset = 0; + hexSize = 0; + + QFont font(Style::Monospace); + setFont(font); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setMinimumWidth((57 + 2) * fontMetrics().width(' ')); + setMinimumHeight((16 + 1) * fontMetrics().height()); + + layout = new QHBoxLayout; + layout->setAlignment(Qt::AlignRight); + layout->setMargin(0); + layout->setSpacing(0); + setLayout(layout); + + scrollbar = new QScrollBar(Qt::Vertical); + scrollbar->setSingleStep(1); + scrollbar->setPageStep(16); + layout->addWidget(scrollbar); + + slotLock = false; + connect(scrollbar, SIGNAL(actionTriggered(int)), this, SLOT(sliderMoved())); +} diff --git a/mednafen/snes/src/ui_qt/debugger/hexeditor.moc.hpp b/mednafen/snes/src/ui_qt/debugger/hexeditor.moc.hpp new file mode 100755 index 0000000..1bc4b69 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/hexeditor.moc.hpp @@ -0,0 +1,25 @@ +class HexEditor : public QTextEdit { + Q_OBJECT + +public: + QHBoxLayout *layout; + QScrollBar *scrollbar; + + function reader; + function writer; + void keyPressEvent(QKeyEvent*); + unsigned hexOffset; + unsigned hexSize; + + void setOffset(unsigned newOffset); + void setSize(unsigned newSize); + + void update(); + HexEditor(); + +private slots: + void sliderMoved(); + +private: + bool slotLock; +}; diff --git a/mednafen/snes/src/ui_qt/debugger/misc/debugger-options.cpp b/mednafen/snes/src/ui_qt/debugger/misc/debugger-options.cpp new file mode 100755 index 0000000..9ee43f3 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/misc/debugger-options.cpp @@ -0,0 +1,27 @@ +#include "debugger-options.moc" +DebuggerOptions *debuggerOptions; + +DebuggerOptions::DebuggerOptions() : QbWindow(config().geometry.debuggerOptions) { + setObjectName("debugger-options"); + setWindowTitle("Debugger Options"); + + layout = new QVBoxLayout; + layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + cacheUsageBox = new QCheckBox("Cache memory usage table to disk"); + layout->addWidget(cacheUsageBox); + + synchronize(); + connect(cacheUsageBox, SIGNAL(stateChanged(int)), this, SLOT(toggleCacheUsage())); +} + +void DebuggerOptions::synchronize() { + cacheUsageBox->setChecked(config().debugger.cacheUsageToDisk); +} + +void DebuggerOptions::toggleCacheUsage() { + config().debugger.cacheUsageToDisk = cacheUsageBox->isChecked(); +} diff --git a/mednafen/snes/src/ui_qt/debugger/misc/debugger-options.moc.hpp b/mednafen/snes/src/ui_qt/debugger/misc/debugger-options.moc.hpp new file mode 100755 index 0000000..0efa8a7 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/misc/debugger-options.moc.hpp @@ -0,0 +1,15 @@ +class DebuggerOptions : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QCheckBox *cacheUsageBox; + + void synchronize(); + DebuggerOptions(); + +public slots: + void toggleCacheUsage(); +}; + +extern DebuggerOptions *debuggerOptions; diff --git a/mednafen/snes/src/ui_qt/debugger/ppu/cgram-viewer.cpp b/mednafen/snes/src/ui_qt/debugger/ppu/cgram-viewer.cpp new file mode 100755 index 0000000..869ef85 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/ppu/cgram-viewer.cpp @@ -0,0 +1,124 @@ +#include "cgram-viewer.moc" +CgramViewer *cgramViewer; + +void CgramViewer::show() { + QbWindow::show(); + refresh(); +} + +void CgramViewer::refresh() { + if(SNES::cartridge.loaded() == false) { + canvas->image->fill(0x000000); + colorInfo->setText(""); + canvas->update(); + return; + } + + uint32_t *buffer = (uint32_t*)canvas->image->bits(); + for(unsigned i = 0; i < 256; i++) { + unsigned x = i % 16; + unsigned y = i / 16; + + uint16_t color = SNES::memory::cgram[i * 2 + 0]; + color |= SNES::memory::cgram[i * 2 + 1] << 8; + + uint8_t r = (color >> 0) & 31; + uint8_t g = (color >> 5) & 31; + uint8_t b = (color >> 10) & 31; + + r = (r << 3) | (r >> 2); + g = (g << 3) | (g >> 2); + b = (b << 3) | (b >> 2); + + uint32_t output = (r << 16) | (g << 8) | (b << 0); + + for(unsigned py = 0; py < 16; py++) { + for(unsigned px = 0; px < 16; px++) { + buffer[(y * 16 + py) * (16 * 16) + (x * 16 + px)] = output; + } + } + + if(i != currentSelection) continue; + + //draw a dotted box black-and-white around the selected color + for(unsigned py = 0; py < 16; py++) { + for(unsigned px = 0; px < 16; px++) { + if(py == 0 || py == 15 || px == 0 || px == 15) { + uint32_t color = ((px + py) & 2) ? 0xffffff : 0x000000; + buffer[(y * 16 + py) * (16 * 16) + (x * 16 + px)] = color; + } + } + } + + string text; + char temp[256]; + text << ""; + text << ""; + sprintf(temp, "%.4x", color); + text << ""; + text << ""; + text << ""; + text << ""; + text << "
Index:" << currentSelection << "
Value:0x" << temp << "
Red:" << (unsigned)(r >> 3) << "
Green:" << (unsigned)(g >> 3) << "
Blue:" << (unsigned)(b >> 3) << "
"; + colorInfo->setText(text); + } + + canvas->update(); +} + +void CgramViewer::autoUpdate() { + if(autoUpdateBox->isChecked()) refresh(); +} + +void CgramViewer::setSelection(unsigned index) { + currentSelection = index; + refresh(); +} + +CgramViewer::CgramViewer() : QbWindow(config().geometry.cgramViewer) { + currentSelection = 0; + + setObjectName("cgram-viewer"); + setWindowTitle("Palette Viewer"); + + layout = new QHBoxLayout; + layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + canvas = new Canvas; + canvas->setFixedSize(16 * 16, 16 * 16); + layout->addWidget(canvas); + + controlLayout = new QVBoxLayout; + controlLayout->setAlignment(Qt::AlignTop); + controlLayout->setSpacing(0); + layout->addLayout(controlLayout); + + autoUpdateBox = new QCheckBox("Auto update"); + controlLayout->addWidget(autoUpdateBox); + + refreshButton = new QPushButton("Refresh"); + controlLayout->addWidget(refreshButton); + controlLayout->addSpacing(Style::WidgetSpacing); + + colorInfo = new QLabel; + controlLayout->addWidget(colorInfo); + + connect(refreshButton, SIGNAL(released()), this, SLOT(refresh())); +} + +void CgramViewer::Canvas::mousePressEvent(QMouseEvent *event) { + cgramViewer->setSelection((event->y() / 16) * 16 + (event->x() / 16)); +} + +void CgramViewer::Canvas::paintEvent(QPaintEvent*) { + QPainter painter(this); + painter.drawImage(0, 0, *image); +} + +CgramViewer::Canvas::Canvas() { + image = new QImage(16 * 16, 16 * 16, QImage::Format_RGB32); + image->fill(0x000000); +} diff --git a/mednafen/snes/src/ui_qt/debugger/ppu/cgram-viewer.moc.hpp b/mednafen/snes/src/ui_qt/debugger/ppu/cgram-viewer.moc.hpp new file mode 100755 index 0000000..34b59d1 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/ppu/cgram-viewer.moc.hpp @@ -0,0 +1,29 @@ +class CgramViewer : public QbWindow { + Q_OBJECT + +public: + QHBoxLayout *layout; + struct Canvas : public QWidget { + QImage *image; + void mousePressEvent(QMouseEvent*); + void paintEvent(QPaintEvent*); + Canvas(); + } *canvas; + QVBoxLayout *controlLayout; + QCheckBox *autoUpdateBox; + QPushButton *refreshButton; + QLabel *colorInfo; + + void setSelection(unsigned); + void autoUpdate(); + CgramViewer(); + +public slots: + void show(); + void refresh(); + +private: + unsigned currentSelection; +}; + +extern CgramViewer *cgramViewer; diff --git a/mednafen/snes/src/ui_qt/debugger/ppu/layer-toggle.cpp b/mednafen/snes/src/ui_qt/debugger/ppu/layer-toggle.cpp new file mode 100755 index 0000000..462ac3b --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/ppu/layer-toggle.cpp @@ -0,0 +1,105 @@ +#include "layer-toggle.moc" +LayerToggle *layerToggle; + +LayerToggle::LayerToggle() : QbWindow(config().geometry.layerToggle) { + setObjectName("layer-toggle"); + setWindowTitle("S-PPU Layer Toggle"); + + layout = new QGridLayout; + layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setMargin(Style::WindowMargin); + layout->setHorizontalSpacing(Style::WidgetSpacing); + layout->setVerticalSpacing(0); + setLayout(layout); + + bg1Label = new QLabel("BG1:"); + layout->addWidget(bg1Label, 0, 0); + + bg1pri0 = new QCheckBox("Priority 0"); + bg1pri0->setChecked(true); + layout->addWidget(bg1pri0, 0, 1); + + bg1pri1 = new QCheckBox("Priority 1"); + bg1pri1->setChecked(true); + layout->addWidget(bg1pri1, 0, 2); + + bg2Label = new QLabel("BG2:"); + layout->addWidget(bg2Label, 1, 0); + + bg2pri0 = new QCheckBox("Priority 0"); + bg2pri0->setChecked(true); + layout->addWidget(bg2pri0, 1, 1); + + bg2pri1 = new QCheckBox("Priority 1"); + bg2pri1->setChecked(true); + layout->addWidget(bg2pri1, 1, 2); + + bg3Label = new QLabel("BG3:"); + layout->addWidget(bg3Label, 2, 0); + + bg3pri0 = new QCheckBox("Priority 0"); + bg3pri0->setChecked(true); + layout->addWidget(bg3pri0, 2, 1); + + bg3pri1 = new QCheckBox("Priority 1"); + bg3pri1->setChecked(true); + layout->addWidget(bg3pri1, 2, 2); + + bg4Label = new QLabel("BG4:"); + layout->addWidget(bg4Label, 3, 0); + + bg4pri0 = new QCheckBox("Priority 0"); + bg4pri0->setChecked(true); + layout->addWidget(bg4pri0, 3, 1); + + bg4pri1 = new QCheckBox("Priority 1"); + bg4pri1->setChecked(true); + layout->addWidget(bg4pri1, 3, 2); + + oamLabel = new QLabel("OAM:"); + layout->addWidget(oamLabel, 4, 0); + + oampri0 = new QCheckBox("Priority 0"); + oampri0->setChecked(true); + layout->addWidget(oampri0, 4, 1); + + oampri1 = new QCheckBox("Priority 1"); + oampri1->setChecked(true); + layout->addWidget(oampri1, 4, 2); + + oampri2 = new QCheckBox("Priority 2"); + oampri2->setChecked(true); + layout->addWidget(oampri2, 4, 3); + + oampri3 = new QCheckBox("Priority 3"); + oampri3->setChecked(true); + layout->addWidget(oampri3, 4, 4); + + connect(bg1pri0, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg1pri1, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg2pri0, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg2pri1, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg3pri0, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg3pri1, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg4pri0, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(bg4pri1, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(oampri0, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(oampri1, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(oampri2, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); + connect(oampri3, SIGNAL(stateChanged(int)), this, SLOT(toggleLayer())); +} + +void LayerToggle::toggleLayer() { + if(sender() == bg1pri0) SNES::ppu.bg1_enabled[0] = bg1pri0->isChecked(); + if(sender() == bg1pri1) SNES::ppu.bg1_enabled[1] = bg1pri1->isChecked(); + if(sender() == bg2pri0) SNES::ppu.bg2_enabled[0] = bg2pri0->isChecked(); + if(sender() == bg2pri1) SNES::ppu.bg2_enabled[1] = bg2pri1->isChecked(); + if(sender() == bg3pri0) SNES::ppu.bg3_enabled[0] = bg3pri0->isChecked(); + if(sender() == bg3pri1) SNES::ppu.bg3_enabled[1] = bg3pri1->isChecked(); + if(sender() == bg4pri0) SNES::ppu.bg4_enabled[0] = bg4pri0->isChecked(); + if(sender() == bg4pri1) SNES::ppu.bg4_enabled[1] = bg4pri1->isChecked(); + if(sender() == oampri0) SNES::ppu.oam_enabled[0] = oampri0->isChecked(); + if(sender() == oampri1) SNES::ppu.oam_enabled[1] = oampri1->isChecked(); + if(sender() == oampri2) SNES::ppu.oam_enabled[2] = oampri2->isChecked(); + if(sender() == oampri3) SNES::ppu.oam_enabled[3] = oampri3->isChecked(); +} diff --git a/mednafen/snes/src/ui_qt/debugger/ppu/layer-toggle.moc.hpp b/mednafen/snes/src/ui_qt/debugger/ppu/layer-toggle.moc.hpp new file mode 100755 index 0000000..ff3c50a --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/ppu/layer-toggle.moc.hpp @@ -0,0 +1,30 @@ +class LayerToggle : public QbWindow { + Q_OBJECT + +public: + QGridLayout *layout; + QLabel *bg1Label; + QCheckBox *bg1pri0; + QCheckBox *bg1pri1; + QLabel *bg2Label; + QCheckBox *bg2pri0; + QCheckBox *bg2pri1; + QLabel *bg3Label; + QCheckBox *bg3pri0; + QCheckBox *bg3pri1; + QLabel *bg4Label; + QCheckBox *bg4pri0; + QCheckBox *bg4pri1; + QLabel *oamLabel; + QCheckBox *oampri0; + QCheckBox *oampri1; + QCheckBox *oampri2; + QCheckBox *oampri3; + + LayerToggle(); + +public slots: + void toggleLayer(); +}; + +extern LayerToggle *layerToggle; diff --git a/mednafen/snes/src/ui_qt/debugger/ppu/oam-viewer.cpp b/mednafen/snes/src/ui_qt/debugger/ppu/oam-viewer.cpp new file mode 100755 index 0000000..9459c6a --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/ppu/oam-viewer.cpp @@ -0,0 +1,124 @@ +#include "oam-viewer.moc" +OamViewer *oamViewer; + +void OamViewer::show() { + QbWindow::show(); + refresh(); +} + +void OamViewer::refresh() { + canvas->image->fill(0x000000); + + QList items = list->findItems("", Qt::MatchContains); + for(unsigned v = 0; v < items.count(); v++) { + QTreeWidgetItem *item = items[v]; + unsigned i = item->data(0, Qt::UserRole).toUInt(); + + uint8_t d0 = SNES::memory::oam[(i << 2) + 0]; + uint8_t d1 = SNES::memory::oam[(i << 2) + 1]; + uint8_t d2 = SNES::memory::oam[(i << 2) + 2]; + uint8_t d3 = SNES::memory::oam[(i << 2) + 3]; + uint8_t d4 = SNES::memory::oam[512 + (i >> 2)]; + bool x = d4 & (1 << ((i & 3) << 1)); + bool size = d4 & (2 << ((i & 3) << 1)); + + unsigned width, height; + switch(SNES::ppu.oam_base_size()) { default: + case 0: width = !size ? 8 : 16; height = !size ? 8 : 16; break; + case 1: width = !size ? 8 : 32; height = !size ? 8 : 32; break; + case 2: width = !size ? 8 : 64; height = !size ? 8 : 64; break; + case 3: width = !size ? 16 : 32; height = !size ? 16 : 32; break; + case 4: width = !size ? 16 : 64; height = !size ? 16 : 64; break; + case 5: width = !size ? 32 : 64; height = !size ? 32 : 64; break; + case 6: width = !size ? 16 : 32; height = !size ? 32 : 64; break; + case 7: width = !size ? 16 : 32; height = !size ? 32 : 32; break; + } + + signed xpos = (x << 8) + d0; + if(xpos > 256) xpos = sclip<9>(xpos); + unsigned ypos = d1; + unsigned character = d2; + unsigned priority = (d3 >> 4) & 3; + unsigned palette = (d3 >> 1) & 7; + string flags; + if(d3 & 0x80) flags << "V"; + if(d3 & 0x40) flags << "H"; + if(d3 & 0x01) flags << "N"; + + item->setText(1, string() << width << "x" << height); + item->setText(2, string() << xpos); + item->setText(3, string() << ypos); + item->setText(4, string() << character); + item->setText(5, string() << priority); + item->setText(6, string() << palette); + item->setText(7, flags); + } + + for(unsigned i = 0; i <= 7; i++) list->resizeColumnToContents(i); + canvas->update(); +} + +void OamViewer::autoUpdate() { + if(autoUpdateBox->isChecked()) refresh(); +} + +OamViewer::OamViewer() : QbWindow(config().geometry.oamViewer) { + setObjectName("oam-viewer"); + setWindowTitle("Sprite Viewer"); + + layout = new QHBoxLayout; + layout->setAlignment(Qt::AlignLeft); + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + list = new QTreeWidget; + list->setColumnCount(8); + list->setHeaderLabels(QStringList() << "#" << "Size" << "X" << "Y" << "Char" << "Pri" << "Pal" << "Flags"); + list->setAllColumnsShowFocus(true); + list->setAlternatingRowColors(true); + list->setRootIsDecorated(false); + list->setSortingEnabled(false); + layout->addWidget(list); + + for(unsigned i = 0; i < 128; i++) { + QTreeWidgetItem *item = new QTreeWidgetItem(list); + item->setData(0, Qt::UserRole, QVariant(i)); + item->setTextAlignment(0, Qt::AlignHCenter); + item->setTextAlignment(1, Qt::AlignHCenter); + item->setTextAlignment(2, Qt::AlignRight); + item->setTextAlignment(3, Qt::AlignRight); + item->setTextAlignment(4, Qt::AlignRight); + item->setTextAlignment(5, Qt::AlignRight); + item->setTextAlignment(6, Qt::AlignRight); + item->setTextAlignment(7, Qt::AlignLeft); + item->setText(0, string() << i); + } + + controlLayout = new QVBoxLayout; + controlLayout->setAlignment(Qt::AlignTop); + controlLayout->setSpacing(0); + layout->addLayout(controlLayout); + + canvas = new Canvas; + canvas->setFixedSize(128, 128); + controlLayout->addWidget(canvas); + + autoUpdateBox = new QCheckBox("Auto update"); + controlLayout->addWidget(autoUpdateBox); + + refreshButton = new QPushButton("Refresh"); + controlLayout->addWidget(refreshButton); + + connect(refreshButton, SIGNAL(released()), this, SLOT(refresh())); +} + +void OamViewer::Canvas::paintEvent(QPaintEvent*) { + QPainter painter(this); + painter.drawImage(0, 0, *image); +} + +OamViewer::Canvas::Canvas() { + image = new QImage(128, 128, QImage::Format_RGB32); + image->fill(0x000000); +} diff --git a/mednafen/snes/src/ui_qt/debugger/ppu/oam-viewer.moc.hpp b/mednafen/snes/src/ui_qt/debugger/ppu/oam-viewer.moc.hpp new file mode 100755 index 0000000..5d17865 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/ppu/oam-viewer.moc.hpp @@ -0,0 +1,24 @@ +class OamViewer : public QbWindow { + Q_OBJECT + +public: + QHBoxLayout *layout; + QTreeWidget *list; + QVBoxLayout *controlLayout; + struct Canvas : public QWidget { + QImage *image; + void paintEvent(QPaintEvent*); + Canvas(); + } *canvas; + QCheckBox *autoUpdateBox; + QPushButton *refreshButton; + + void autoUpdate(); + OamViewer(); + +public slots: + void show(); + void refresh(); +}; + +extern OamViewer *oamViewer; diff --git a/mednafen/snes/src/ui_qt/debugger/ppu/vram-viewer.cpp b/mednafen/snes/src/ui_qt/debugger/ppu/vram-viewer.cpp new file mode 100755 index 0000000..9412618 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/ppu/vram-viewer.cpp @@ -0,0 +1,175 @@ +#include "vram-viewer.moc" +VramViewer *vramViewer; + +VramViewer::VramViewer() : QbWindow(config().geometry.vramViewer) { + setObjectName("vram-viewer"); + setWindowTitle("Video RAM Viewer"); + + layout = new QVBoxLayout; + layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setAlignment(Qt::AlignCenter); + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + controlLayout = new QHBoxLayout; + controlLayout->setSizeConstraint(QLayout::SetMinimumSize); + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + depth2bpp = new QRadioButton("2 BPP"); + controlLayout->addWidget(depth2bpp); + + depth4bpp = new QRadioButton("4 BPP"); + controlLayout->addWidget(depth4bpp); + + depth8bpp = new QRadioButton("8 BPP"); + controlLayout->addWidget(depth8bpp); + + depthMode7 = new QRadioButton("Mode 7"); + controlLayout->addWidget(depthMode7); + + autoUpdateBox = new QCheckBox("Auto update"); + controlLayout->addWidget(autoUpdateBox); + + refreshButton = new QPushButton("Refresh"); + controlLayout->addWidget(refreshButton); + + canvas = new Canvas; + canvas->setFixedSize(512, 512); + layout->addWidget(canvas); + + bpp = 2; + depth2bpp->setChecked(true); + + connect(refreshButton, SIGNAL(released()), this, SLOT(refresh())); + connect(depth2bpp, SIGNAL(pressed()), this, SLOT(setDepth2bpp())); + connect(depth4bpp, SIGNAL(pressed()), this, SLOT(setDepth4bpp())); + connect(depth8bpp, SIGNAL(pressed()), this, SLOT(setDepth8bpp())); + connect(depthMode7, SIGNAL(pressed()), this, SLOT(setDepthMode7())); +} + +void VramViewer::autoUpdate() { + if(autoUpdateBox->isChecked()) refresh(); +} + +void VramViewer::show() { + QbWindow::show(); + refresh(); +} + +void VramViewer::refresh() { + canvas->image->fill(0x800000); + if(SNES::cartridge.loaded()) { + const uint8_t *source = SNES::memory::vram.data(); + uint32_t *dest = (uint32_t*)canvas->image->bits(); + if(bpp == 2) refresh2bpp (source, dest); + if(bpp == 4) refresh4bpp (source, dest); + if(bpp == 8) refresh8bpp (source, dest); + if(bpp == 7) refreshMode7(source, dest); + } + canvas->update(); +} + +void VramViewer::refresh2bpp(const uint8_t *source, uint32_t *dest) { + for(unsigned ty = 0; ty < 64; ty++) { + for(unsigned tx = 0; tx < 64; tx++) { + for(unsigned py = 0; py < 8; py++) { + uint8_t d0 = source[0]; + uint8_t d1 = source[1]; + for(unsigned px = 0; px < 8; px++) { + uint8_t pixel = 0; + pixel |= (d0 & (0x80 >> px)) ? 1 : 0; + pixel |= (d1 & (0x80 >> px)) ? 2 : 0; + pixel *= 0x55; + dest[(ty * 8 + py) * 512 + (tx * 8 + px)] = (pixel << 16) + (pixel << 8) + pixel; + } + source += 2; + } + } + } +} + +void VramViewer::refresh4bpp(const uint8_t *source, uint32_t *dest) { + for(unsigned ty = 0; ty < 32; ty++) { + for(unsigned tx = 0; tx < 64; tx++) { + for(unsigned py = 0; py < 8; py++) { + uint8_t d0 = source[ 0]; + uint8_t d1 = source[ 1]; + uint8_t d2 = source[16]; + uint8_t d3 = source[17]; + for(unsigned px = 0; px < 8; px++) { + uint8_t pixel = 0; + pixel |= (d0 & (0x80 >> px)) ? 1 : 0; + pixel |= (d1 & (0x80 >> px)) ? 2 : 0; + pixel |= (d2 & (0x80 >> px)) ? 4 : 0; + pixel |= (d3 & (0x80 >> px)) ? 8 : 0; + pixel *= 0x11; + dest[(ty * 8 + py) * 512 + (tx * 8 + px)] = (pixel << 16) + (pixel << 8) + pixel; + } + source += 2; + } + source += 16; + } + } +} + +void VramViewer::refresh8bpp(const uint8_t *source, uint32_t *dest) { + for(unsigned ty = 0; ty < 16; ty++) { + for(unsigned tx = 0; tx < 64; tx++) { + for(unsigned py = 0; py < 8; py++) { + uint8_t d0 = source[ 0]; + uint8_t d1 = source[ 1]; + uint8_t d2 = source[16]; + uint8_t d3 = source[17]; + uint8_t d4 = source[32]; + uint8_t d5 = source[33]; + uint8_t d6 = source[48]; + uint8_t d7 = source[49]; + for(unsigned px = 0; px < 8; px++) { + uint8_t pixel = 0; + pixel |= (d0 & (0x80 >> px)) ? 0x01 : 0; + pixel |= (d1 & (0x80 >> px)) ? 0x02 : 0; + pixel |= (d2 & (0x80 >> px)) ? 0x04 : 0; + pixel |= (d3 & (0x80 >> px)) ? 0x08 : 0; + pixel |= (d4 & (0x80 >> px)) ? 0x10 : 0; + pixel |= (d5 & (0x80 >> px)) ? 0x20 : 0; + pixel |= (d6 & (0x80 >> px)) ? 0x40 : 0; + pixel |= (d7 & (0x80 >> px)) ? 0x80 : 0; + dest[(ty * 8 + py) * 512 + (tx * 8 + px)] = (pixel << 16) + (pixel << 8) + pixel; + } + source += 2; + } + source += 48; + } + } +} + +void VramViewer::refreshMode7(const uint8_t *source, uint32_t *dest) { + for(unsigned ty = 0; ty < 16; ty++) { + for(unsigned tx = 0; tx < 16; tx++) { + for(unsigned py = 0; py < 8; py++) { + for(unsigned px = 0; px < 8; px++) { + uint8_t pixel = source[1]; + dest[(ty * 8 + py) * 512 + (tx * 8 + px)] = (pixel << 16) + (pixel << 8) + pixel; + source += 2; + } + } + } + } +} + +void VramViewer::setDepth2bpp() { bpp = 2; refresh(); } +void VramViewer::setDepth4bpp() { bpp = 4; refresh(); } +void VramViewer::setDepth8bpp() { bpp = 8; refresh(); } +void VramViewer::setDepthMode7() { bpp = 7; refresh(); } + +void VramViewer::Canvas::paintEvent(QPaintEvent*) { + QPainter painter(this); + painter.drawImage(0, 0, *image); +} + +VramViewer::Canvas::Canvas() { + image = new QImage(512, 512, QImage::Format_RGB32); + image->fill(0x800000); +} diff --git a/mednafen/snes/src/ui_qt/debugger/ppu/vram-viewer.moc.hpp b/mednafen/snes/src/ui_qt/debugger/ppu/vram-viewer.moc.hpp new file mode 100755 index 0000000..9965601 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/ppu/vram-viewer.moc.hpp @@ -0,0 +1,38 @@ +class VramViewer : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QHBoxLayout *controlLayout; + QRadioButton *depth2bpp; + QRadioButton *depth4bpp; + QRadioButton *depth8bpp; + QRadioButton *depthMode7; + QCheckBox *autoUpdateBox; + QPushButton *refreshButton; + struct Canvas : public QWidget { + QImage *image; + void paintEvent(QPaintEvent*); + Canvas(); + } *canvas; + + void autoUpdate(); + VramViewer(); + +public slots: + void show(); + void refresh(); + void setDepth2bpp(); + void setDepth4bpp(); + void setDepth8bpp(); + void setDepthMode7(); + +private: + unsigned bpp; + void refresh2bpp(const uint8_t*, uint32_t*); + void refresh4bpp(const uint8_t*, uint32_t*); + void refresh8bpp(const uint8_t*, uint32_t*); + void refreshMode7(const uint8_t*, uint32_t*); +}; + +extern VramViewer *vramViewer; diff --git a/mednafen/snes/src/ui_qt/debugger/tools/breakpoint.cpp b/mednafen/snes/src/ui_qt/debugger/tools/breakpoint.cpp new file mode 100755 index 0000000..c09b393 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/tools/breakpoint.cpp @@ -0,0 +1,78 @@ +#include "breakpoint.moc" +BreakpointEditor *breakpointEditor; + +BreakpointItem::BreakpointItem(unsigned id_) : id(id_) { + layout = new QHBoxLayout; + layout->setMargin(0); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + enabled = new QCheckBox; + layout->addWidget(enabled); + + addr = new QLineEdit; + addr->setFixedWidth(80); + layout->addWidget(addr); + + data = new QLineEdit; + data->setFixedWidth(40); + layout->addWidget(data); + + mode = new QComboBox; + mode->addItem("Exec"); + mode->addItem("Read"); + mode->addItem("Write"); + layout->addWidget(mode); + + source = new QComboBox; + source->addItem("S-CPU bus"); + source->addItem("S-SMP bus"); + source->addItem("S-PPU VRAM"); + source->addItem("S-PPU OAM"); + source->addItem("S-PPU CGRAM"); + layout->addWidget(source); + + connect(enabled, SIGNAL(stateChanged(int)), this, SLOT(toggle())); +} + +void BreakpointItem::toggle() { + bool state = enabled->isChecked(); + + if(state) { + SNES::debugger.breakpoint[id].enabled = true; + SNES::debugger.breakpoint[id].addr = strhex(addr->text().toUtf8().data()) & 0xffffff; + SNES::debugger.breakpoint[id].data = strhex(data->text().toUtf8().data()) & 0xff; + if(data->text().length() == 0) SNES::debugger.breakpoint[id].data = -1; + SNES::debugger.breakpoint[id].mode = (SNES::Debugger::Breakpoint::Mode)mode->currentIndex(); + SNES::debugger.breakpoint[id].source = (SNES::Debugger::Breakpoint::Source)source->currentIndex(); + SNES::debugger.breakpoint[id].counter = 0; + + addr->setEnabled(false); + data->setEnabled(false); + mode->setEnabled(false); + source->setEnabled(false); + } else { + SNES::debugger.breakpoint[id].enabled = false; + + addr->setEnabled(true); + data->setEnabled(true); + mode->setEnabled(true); + source->setEnabled(true); + } +} + +BreakpointEditor::BreakpointEditor() : QbWindow(config().geometry.breakpointEditor) { + setObjectName("breakpoint-editor"); + setWindowTitle("Breakpoint Editor"); + + layout = new QVBoxLayout; + layout->setSizeConstraint(QLayout::SetFixedSize); + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + for(unsigned n = 0; n < SNES::Debugger::Breakpoints; n++) { + breakpoint[n] = new BreakpointItem(n); + layout->addWidget(breakpoint[n]); + } +} diff --git a/mednafen/snes/src/ui_qt/debugger/tools/breakpoint.moc.hpp b/mednafen/snes/src/ui_qt/debugger/tools/breakpoint.moc.hpp new file mode 100755 index 0000000..2cc109e --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/tools/breakpoint.moc.hpp @@ -0,0 +1,30 @@ +class BreakpointItem : public QWidget { + Q_OBJECT + +public: + QHBoxLayout *layout; + QCheckBox *enabled; + QLineEdit *addr; + QLineEdit *data; + QComboBox *mode; + QComboBox *source; + BreakpointItem(unsigned id); + +public slots: + void toggle(); + +private: + const unsigned id; +}; + +class BreakpointEditor : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + BreakpointItem *breakpoint[SNES::Debugger::Breakpoints]; + + BreakpointEditor(); +}; + +extern BreakpointEditor *breakpointEditor; diff --git a/mednafen/snes/src/ui_qt/debugger/tools/disassembler.cpp b/mednafen/snes/src/ui_qt/debugger/tools/disassembler.cpp new file mode 100755 index 0000000..847203a --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/tools/disassembler.cpp @@ -0,0 +1,110 @@ +#include "disassembler.moc" +CPUDisassembler *cpuDisassembler; +SMPDisassembler *smpDisassembler; +Disassembler *disassembler; + +CPUDisassembler::CPUDisassembler() { + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + view = new QTextEdit; + view->setReadOnly(true); + view->setFont(QFont(Style::Monospace)); + view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view->setMinimumHeight((25 + 1) * view->fontMetrics().height()); + layout->addWidget(view); +} + +SMPDisassembler::SMPDisassembler() { + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + view = new QTextEdit; + view->setReadOnly(true); + view->setFont(QFont(Style::Monospace)); + view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view->setMinimumHeight((25 + 1) * view->fontMetrics().height()); + layout->addWidget(view); +} + +Disassembler::Disassembler() : QbWindow(config().geometry.disassembler) { + setObjectName("disassembler"); + setWindowTitle("Disassembler"); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + cpuDisassembler = new CPUDisassembler; + smpDisassembler = new SMPDisassembler; + + tab = new QTabWidget; + tab->addTab(cpuDisassembler, "S-CPU"); + tab->addTab(smpDisassembler, "S-SMP"); + layout->addWidget(tab); +} + +void Disassembler::refresh(Source source, unsigned addr) { + uint8 *usage; + unsigned mask; + if(source == CPU) { usage = SNES::cpu.usage; mask = (1 << 24) - 1; } + if(source == SMP) { usage = SNES::smp.usage; mask = (1 << 16) - 1; } + + int line[25]; + for(unsigned i = 0; i < 25; i++) line[i] = -1; + + line[12] = addr; + + for(signed index = 11; index >= 0; index--) { + int base = line[index + 1]; + if(base == -1) break; + + for(unsigned i = 1; i <= 4; i++) { + if(usage[(base - i) & mask] & 0x20) { + line[index] = base - i; + break; + } + } + } + + for(signed index = 13; index <= 24; index++) { + int base = line[index - 1]; + if(base == -1) break; + + for(unsigned i = 1; i <= 4; i++) { + if(usage[(base + i) & mask] & 0x20) { + line[index] = base + i; + break; + } + } + } + + string output; + for(unsigned i = 0; i < 25; i++) { + if(i < 12) output << ""; + else if(i == 12) output << ""; + else output << ""; + + if(line[i] == -1) { + output << "..."; + } else { + char t[256]; + if(source == CPU) { SNES::cpu.disassemble_opcode(t, line[i]); t[20] = 0; } + if(source == SMP) { SNES::smp.disassemble_opcode(t, line[i]); t[23] = 0; } + string text = rtrim(t); + text.replace(" ", " "); + output << text; + } + + output << ""; + if(i != 24) output << "
"; + } + + if(source == CPU) cpuDisassembler->view->setHtml(output); + if(source == SMP) smpDisassembler->view->setHtml(output); +} diff --git a/mednafen/snes/src/ui_qt/debugger/tools/disassembler.moc.hpp b/mednafen/snes/src/ui_qt/debugger/tools/disassembler.moc.hpp new file mode 100755 index 0000000..d68850a --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/tools/disassembler.moc.hpp @@ -0,0 +1,29 @@ +class CPUDisassembler : public QWidget { +public: + QVBoxLayout *layout; + QTextEdit *view; + CPUDisassembler(); +}; + +class SMPDisassembler : public QWidget { +public: + QVBoxLayout *layout; + QTextEdit *view; + SMPDisassembler(); +}; + +class Disassembler : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTabWidget *tab; + + enum Source { CPU, SMP }; + void refresh(Source, unsigned); + Disassembler(); +}; + +extern CPUDisassembler *cpuDisassembler; +extern SMPDisassembler *smpDisassembler; +extern Disassembler *disassembler; diff --git a/mednafen/snes/src/ui_qt/debugger/tools/memory.cpp b/mednafen/snes/src/ui_qt/debugger/tools/memory.cpp new file mode 100755 index 0000000..f0515a0 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/tools/memory.cpp @@ -0,0 +1,162 @@ +#include "memory.moc" +MemoryEditor *memoryEditor; + +MemoryEditor::MemoryEditor() : QbWindow(config().geometry.memoryEditor) { + setObjectName("memory-editor"); + setWindowTitle("Memory Editor"); + + layout = new QHBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + editor = new HexEditor; + editor->reader = bind(&MemoryEditor::reader, this); + editor->writer = bind(&MemoryEditor::writer, this); + editor->setSize(16 * 1024 * 1024); + memorySource = SNES::Debugger::CPUBus; + layout->addWidget(editor); + + controlLayout = new QVBoxLayout; + controlLayout->setSpacing(0); + layout->addLayout(controlLayout); + + source = new QComboBox; + source->addItem("S-CPU bus"); + source->addItem("S-APU bus"); + source->addItem("S-PPU VRAM"); + source->addItem("S-PPU OAM"); + source->addItem("S-PPU CGRAM"); + controlLayout->addWidget(source); + controlLayout->addSpacing(2); + + addr = new QLineEdit; + controlLayout->addWidget(addr); + + autoUpdateBox = new QCheckBox("Auto update"); + controlLayout->addWidget(autoUpdateBox); + + refreshButton = new QPushButton("Refresh"); + controlLayout->addWidget(refreshButton); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + controlLayout->addWidget(spacer); + + exportButton = new QPushButton("Export"); + controlLayout->addWidget(exportButton); + + importButton = new QPushButton("Import"); + controlLayout->addWidget(importButton); + + connect(source, SIGNAL(currentIndexChanged(int)), this, SLOT(sourceChanged(int))); + connect(addr, SIGNAL(textEdited(const QString&)), this, SLOT(updateOffset())); + connect(addr, SIGNAL(returnPressed()), this, SLOT(updateOffset())); + connect(refreshButton, SIGNAL(released()), this, SLOT(refresh())); + connect(exportButton, SIGNAL(released()), this, SLOT(exportMemory())); + connect(importButton, SIGNAL(released()), this, SLOT(importMemory())); +} + +void MemoryEditor::autoUpdate() { + if(SNES::cartridge.loaded() && autoUpdateBox->isChecked()) editor->update(); +} + +void MemoryEditor::synchronize() { + if(SNES::cartridge.loaded() == false) { + editor->setHtml(""); + editor->scrollbar->setEnabled(false); + source->setEnabled(false); + addr->setEnabled(false); + autoUpdateBox->setEnabled(false); + refreshButton->setEnabled(false); + exportButton->setEnabled(false); + importButton->setEnabled(false); + } else { + editor->scrollbar->setEnabled(true); + source->setEnabled(true); + addr->setEnabled(true); + autoUpdateBox->setEnabled(true); + refreshButton->setEnabled(true); + exportButton->setEnabled(true); + importButton->setEnabled(true); + } +} + +void MemoryEditor::show() { + QbWindow::show(); + refresh(); +} + +void MemoryEditor::sourceChanged(int index) { + switch(index) { default: + case 0: memorySource = SNES::Debugger::CPUBus; editor->setSize(16 * 1024 * 1024); break; + case 1: memorySource = SNES::Debugger::APURAM; editor->setSize(64 * 1024); break; + case 2: memorySource = SNES::Debugger::VRAM; editor->setSize(64 * 1024); break; + case 3: memorySource = SNES::Debugger::OAM; editor->setSize(544); break; + case 4: memorySource = SNES::Debugger::CGRAM; editor->setSize(512); break; + } + + editor->setOffset(strhex(addr->text().toUtf8().data())); + editor->update(); +} + +void MemoryEditor::refresh() { + if(SNES::cartridge.loaded() == false) { + editor->setHtml(""); + } else { + editor->update(); + } +} + +void MemoryEditor::updateOffset() { + editor->setOffset(strhex(addr->text().toUtf8().data())); + refresh(); +} + +void MemoryEditor::exportMemory() { + string basename = filepath(nall::basename(cartridge.fileName), config().path.data); + + exportMemory(SNES::memory::cartram, string() << basename << "-sram.bin"); + exportMemory(SNES::memory::wram, string() << basename << "-wram.bin"); + exportMemory(SNES::memory::apuram, string() << basename << "-apuram.bin"); + exportMemory(SNES::memory::vram, string() << basename << "-vram.bin"); + exportMemory(SNES::memory::oam, string() << basename << "-oam.bin"); + exportMemory(SNES::memory::cgram, string() << basename << "-cgram.bin"); +} + +void MemoryEditor::importMemory() { + string basename = filepath(nall::basename(cartridge.fileName), config().path.data); + + importMemory(SNES::memory::cartram, string() << basename << "-sram.bin"); + importMemory(SNES::memory::wram, string() << basename << "-wram.bin"); + importMemory(SNES::memory::apuram, string() << basename << "-apuram.bin"); + importMemory(SNES::memory::vram, string() << basename << "-vram.bin"); + importMemory(SNES::memory::oam, string() << basename << "-oam.bin"); + importMemory(SNES::memory::cgram, string() << basename << "-cgram.bin"); + refresh(); //in case import changed values that are currently being displayed ... +} + +void MemoryEditor::exportMemory(SNES::Memory &memory, const string &filename) const { + file fp; + if(fp.open(filename, file::mode_write)) { + for(unsigned i = 0; i < memory.size(); i++) fp.write(memory.read(i)); + fp.close(); + } +} + +void MemoryEditor::importMemory(SNES::Memory &memory, const string &filename) const { + file fp; + if(fp.open(filename, file::mode_read)) { + unsigned filesize = fp.size(); + for(unsigned i = 0; i < memory.size() && i < filesize; i++) memory.write(i, fp.read()); + fp.close(); + } +} + +uint8 MemoryEditor::reader(unsigned addr) { + return SNES::debugger.read(memorySource, addr); +} + +void MemoryEditor::writer(unsigned addr, uint8 data) { + SNES::debugger.write(memorySource, addr, data); +} diff --git a/mednafen/snes/src/ui_qt/debugger/tools/memory.moc.hpp b/mednafen/snes/src/ui_qt/debugger/tools/memory.moc.hpp new file mode 100755 index 0000000..d63d2ab --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/tools/memory.moc.hpp @@ -0,0 +1,36 @@ +class MemoryEditor : public QbWindow { + Q_OBJECT + +public: + QHBoxLayout *layout; + HexEditor *editor; + QVBoxLayout *controlLayout; + QComboBox *source; + QLineEdit *addr; + QCheckBox *autoUpdateBox; + QPushButton *refreshButton; + QWidget *spacer; + QPushButton *exportButton; + QPushButton *importButton; + + void autoUpdate(); + void synchronize(); + + SNES::Debugger::MemorySource memorySource; + uint8 reader(unsigned addr); + void writer(unsigned addr, uint8 data); + + MemoryEditor(); + +public slots: + void show(); + void sourceChanged(int); + void refresh(); + void updateOffset(); + void exportMemory(); + void importMemory(); + void exportMemory(SNES::Memory&, const string&) const; + void importMemory(SNES::Memory&, const string&) const; +}; + +extern MemoryEditor *memoryEditor; diff --git a/mednafen/snes/src/ui_qt/debugger/tools/properties.cpp b/mednafen/snes/src/ui_qt/debugger/tools/properties.cpp new file mode 100755 index 0000000..9faeff7 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/tools/properties.cpp @@ -0,0 +1,88 @@ +#include "properties.moc" +PropertiesWidget *cpuPropertiesTab; +PropertiesWidget *ppuPropertiesTab; +PropertiesViewer *propertiesViewer; + +void PropertiesWidget::refresh() { + QList items = list->findItems("", Qt::MatchContains); + for(unsigned v = 0; v < items.count(); v++) { + QTreeWidgetItem *item = items[v]; + unsigned id = item->data(0, Qt::UserRole).toUInt(); + string name, value; + object.property(id, name, value); + item->setText(1, value); + } +} + +PropertiesWidget::PropertiesWidget(SNES::ChipDebugger &object) : object(object) { + setMinimumSize(480, 240); + + layout = new QVBoxLayout; + setLayout(layout); + + list = new QTreeWidget; + list->setColumnCount(2); + list->setHeaderLabels(QStringList() << "Name" << "Value"); + list->setAllColumnsShowFocus(true); + list->setAlternatingRowColors(true); + list->setRootIsDecorated(false); + list->setSortingEnabled(false); + layout->addWidget(list); + + unsigned counter = 0; + while(true) { + string name, value; + bool result = object.property(counter, name, value); + if(result == false) break; + + QTreeWidgetItem *item = new QTreeWidgetItem(list); + item->setData(0, Qt::UserRole, QVariant(counter++)); + item->setText(0, name); + } + for(unsigned i = 0; i <= 1; i++) list->resizeColumnToContents(i); +} + +void PropertiesViewer::refresh() { + cpuPropertiesTab->refresh(); + ppuPropertiesTab->refresh(); +} + +void PropertiesViewer::show() { + QbWindow::show(); + refresh(); +} + +void PropertiesViewer::autoUpdate() { + if(autoUpdateBox->isChecked()) refresh(); +} + +PropertiesViewer::PropertiesViewer() : QbWindow(config().geometry.propertiesViewer) { + setObjectName("properties-viewer"); + setWindowTitle("Properties"); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + tabWidget = new QTabWidget; + layout->addWidget(tabWidget); + + cpuPropertiesTab = new PropertiesWidget(SNES::cpu); + tabWidget->addTab(cpuPropertiesTab, "S-CPU"); + + ppuPropertiesTab = new PropertiesWidget(SNES::ppu); + tabWidget->addTab(ppuPropertiesTab, "S-PPU"); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + autoUpdateBox = new QCheckBox("Auto update"); + controlLayout->addWidget(autoUpdateBox); + + refreshButton = new QPushButton("Refresh"); + controlLayout->addWidget(refreshButton); + + connect(refreshButton, SIGNAL(released()), this, SLOT(refresh())); +} diff --git a/mednafen/snes/src/ui_qt/debugger/tools/properties.moc.hpp b/mednafen/snes/src/ui_qt/debugger/tools/properties.moc.hpp new file mode 100755 index 0000000..d885100 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/tools/properties.moc.hpp @@ -0,0 +1,37 @@ +class PropertiesWidget : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTreeWidget *list; + + PropertiesWidget(SNES::ChipDebugger &object); + +public slots: + void refresh(); + +private: + SNES::ChipDebugger &object; +}; + +class PropertiesViewer : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTabWidget *tabWidget; + QHBoxLayout *controlLayout; + QCheckBox *autoUpdateBox; + QPushButton *refreshButton; + + void autoUpdate(); + PropertiesViewer(); + +public slots: + void refresh(); + void show(); +}; + +extern PropertiesWidget *cpuPropertiesTab; +extern PropertiesWidget *ppuPropertiesTab; +extern PropertiesViewer *propertiesViewer; diff --git a/mednafen/snes/src/ui_qt/debugger/tracer.cpp b/mednafen/snes/src/ui_qt/debugger/tracer.cpp new file mode 100755 index 0000000..8249de1 --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/tracer.cpp @@ -0,0 +1,74 @@ +#include "tracer.moc" +Tracer *tracer; + +void Tracer::stepCpu() { + if(traceCpu) { + unsigned addr = SNES::cpu.regs.pc; + if(!traceMask || !(traceMaskCPU[addr >> 3] & (0x80 >> (addr & 7)))) { + char text[256]; + SNES::cpu.disassemble_opcode(text, addr); + tracefile.print(string() << text << "\n"); + } + traceMaskCPU[addr >> 3] |= 0x80 >> (addr & 7); + } +} + +void Tracer::stepSmp() { + if(traceSmp) { + unsigned addr = SNES::smp.regs.pc; + if(!traceMask || !(traceMaskSMP[addr >> 3] & (0x80 >> (addr & 7)))) { + char text[256]; + SNES::smp.disassemble_opcode(text, addr); + tracefile.print(string() << text << "\n"); + } + traceMaskSMP[addr >> 3] |= 0x80 >> (addr & 7); + } +} + +void Tracer::setCpuTraceState(int state) { + traceCpu = (state == Qt::Checked); + + if(traceCpu && !tracefile.open()) { + tracefile.open(string() << config().path.data << "trace.log", file::mode_write); + } else if(!traceCpu && !traceSmp && tracefile.open()) { + tracefile.close(); + } +} + +void Tracer::setSmpTraceState(int state) { + traceSmp = (state == Qt::Checked); + + if(traceSmp && !tracefile.open()) { + tracefile.open(string() << config().path.data << "trace.log", file::mode_write); + } else if(!traceCpu && !traceSmp && tracefile.open()) { + tracefile.close(); + } +} + +void Tracer::setTraceMaskState(int state) { + traceMask = (state == Qt::Checked); + + if(traceMask) { + //flush all bitmasks once enabled + memset(traceMaskCPU, 0x00, (1 << 24) >> 3); + memset(traceMaskSMP, 0x00, (1 << 16) >> 3); + } +} + +Tracer::Tracer() { + traceCpu = false; + traceSmp = false; + traceMask = false; + + traceMaskCPU = new uint8[(1 << 24) >> 3](); + traceMaskSMP = new uint8[(1 << 16) >> 3](); + + SNES::cpu.step_event = bind(&Tracer::stepCpu, this); + SNES::smp.step_event = bind(&Tracer::stepSmp, this); +} + +Tracer::~Tracer() { + delete[] traceMaskCPU; + delete[] traceMaskSMP; + if(tracefile.open()) tracefile.close(); +} diff --git a/mednafen/snes/src/ui_qt/debugger/tracer.moc.hpp b/mednafen/snes/src/ui_qt/debugger/tracer.moc.hpp new file mode 100755 index 0000000..06fad5c --- /dev/null +++ b/mednafen/snes/src/ui_qt/debugger/tracer.moc.hpp @@ -0,0 +1,26 @@ +class Tracer : public QObject { + Q_OBJECT + +public: + void stepCpu(); + void stepSmp(); + + Tracer(); + ~Tracer(); + +public slots: + void setCpuTraceState(int); + void setSmpTraceState(int); + void setTraceMaskState(int); + +private: + file tracefile; + bool traceCpu; + bool traceSmp; + bool traceMask; + + uint8 *traceMaskCPU; + uint8 *traceMaskSMP; +}; + +extern Tracer *tracer; diff --git a/mednafen/snes/src/ui_qt/input/controller.cpp b/mednafen/snes/src/ui_qt/input/controller.cpp new file mode 100755 index 0000000..5323749 --- /dev/null +++ b/mednafen/snes/src/ui_qt/input/controller.cpp @@ -0,0 +1,372 @@ +namespace Controllers { + +void TurboInput::cache() { + if(state) { + cachedState = (counter < holdHi ? state : 0); + if(++counter >= holdHi + holdLo) counter = 0; + } else { + cachedState = 0; + counter = 0; + } +} + +TurboInput::TurboInput(const char *label, const char *configName) : +DigitalInput(label, configName) { + holdHi = 2; + holdLo = 2; + counter = 0; +} + +int16_t Gamepad::status(unsigned index, unsigned id) const { + switch(id) { + case SNES::Input::JoypadUp: return up.cachedState; + case SNES::Input::JoypadDown: return down.cachedState; + case SNES::Input::JoypadLeft: return left.cachedState; + case SNES::Input::JoypadRight: return right.cachedState; + case SNES::Input::JoypadA: return a.cachedState | turboA.cachedState; + case SNES::Input::JoypadB: return b.cachedState | turboB.cachedState; + case SNES::Input::JoypadX: return x.cachedState | turboX.cachedState; + case SNES::Input::JoypadY: return y.cachedState | turboY.cachedState; + case SNES::Input::JoypadL: return l.cachedState | turboL.cachedState; + case SNES::Input::JoypadR: return r.cachedState | turboR.cachedState; + case SNES::Input::JoypadSelect: return select.cachedState; + case SNES::Input::JoypadStart: return start.cachedState; + } + return 0; +} + +Gamepad::Gamepad(unsigned category, const char *label, const char *configName) : +InputGroup(category, label), +up("Up", string() << "input." << configName << ".up"), +down("Down", string() << "input." << configName << ".down"), +left("Left", string() << "input." << configName << ".left"), +right("Right", string() << "input." << configName << ".right"), +b("B", string() << "input." << configName << ".b"), +a("A", string() << "input." << configName << ".a"), +y("Y", string() << "input." << configName << ".y"), +x("X", string() << "input." << configName << ".x"), +l("L", string() << "input." << configName << ".l"), +r("R", string() << "input." << configName << ".r"), +select("Select", string() << "input." << configName << ".select"), +start("Start", string() << "input." << configName << ".start"), +turboB("Turbo B", string() << "input." << configName << ".turboB"), +turboA("Turbo A", string() << "input." << configName << ".turboA"), +turboY("Turbo Y", string() << "input." << configName << ".turboY"), +turboX("Turbo X", string() << "input." << configName << ".turboX"), +turboL("Turbo L", string() << "input." << configName << ".turboL"), +turboR("Turbo R", string() << "input." << configName << ".turboR") { + attach(&up); attach(&down); attach(&left); attach(&right); + attach(&b); attach(&a); attach(&y); attach(&x); + attach(&l); attach(&r); attach(&select); attach(&start); + attach(&turboB); attach(&turboA); attach(&turboY); attach(&turboX); + attach(&turboL); attach(&turboR); + + if(this == &gamepad1) { + up.name = "KB0::Up"; + down.name = "KB0::Down"; + left.name = "KB0::Left"; + right.name = "KB0::Right"; + b.name = "KB0::Z"; + a.name = "KB0::X"; + y.name = "KB0::A"; + x.name = "KB0::S"; + l.name = "KB0::D"; + r.name = "KB0::C"; + select.name = "KB0::Apostrophe"; + start.name = "KB0::Return"; + } +} + +// + +int16_t Multitap::status(unsigned index, unsigned id) const { + switch(index & 3) { default: + case 0: return port1.status(index, id); + case 1: return port2.status(index, id); + case 2: return port3.status(index, id); + case 3: return port4.status(index, id); + } +} + +Multitap::Multitap(Gamepad &port1_, Gamepad &port2_, Gamepad &port3_, Gamepad &port4_) : +InputGroup(InputCategory::Hidden, "Multitap"), +port1(port1_), port2(port2_), port3(port3_), port4(port4_) { +} + +// + +void AsciiSwitch::poll() { + DigitalInput::poll(); + + //only change state when controller is active + if(!parent) return; + if(parent->category == InputCategory::Port1 && mapper().port1 != parent) return; + if(parent->category == InputCategory::Port2 && mapper().port2 != parent) return; + + if(previousState != state && state) { + switch(mode) { + case Off: mode = Turbo; utility.showMessage(string() << label << " set to turbo."); break; + case Turbo: mode = Auto; utility.showMessage(string() << label << " set to auto."); break; + case Auto: mode = Off; utility.showMessage(string() << label << " set to off."); break; + } + } +} + +AsciiSwitch::AsciiSwitch(const char *label, const char *configName) : +DigitalInput(label, configName) { + mode = Off; +} + +void AsciiInput::cache() { + if(asciiSwitch->mode == AsciiSwitch::Off) { + cachedState = state; + } else if(asciiSwitch->mode == AsciiSwitch::Turbo) { + if(state) { + cachedState = (counter < holdHi ? state : 0); + if(++counter >= holdHi + holdLo) counter = 0; + } else { + cachedState = 0; + counter = 0; + } + } else if(asciiSwitch->mode == AsciiSwitch::Auto) { + cachedState = (counter < holdHi); + if(++counter >= holdHi + holdLo) counter = 0; + } +} + +AsciiInput::AsciiInput(const char *label, const char *configName) : +DigitalInput(label, configName) { + holdHi = 2; + holdLo = 2; + counter = 0; +} + +void AsciiSlowMotion::poll() { + DigitalInput::poll(); + + //only change state when controller is active + if(!parent) return; + if(parent->category == InputCategory::Port1 && mapper().port1 != parent) return; + if(parent->category == InputCategory::Port2 && mapper().port2 != parent) return; + + if(previousState != state && state) { + if(enabled == false) { + enabled = true; + utility.showMessage(string() << label << " enabled."); + } else { + enabled = false; + utility.showMessage(string() << label << " disabled."); + } + } +} + +void AsciiSlowMotion::cache() { + if(enabled == false) { + cachedState = 0; + } else { + cachedState = counter < holdHi; + if(++counter >= holdHi + holdLo) counter = 0; + } +} + +AsciiSlowMotion::AsciiSlowMotion(const char *label, const char *configName) : +DigitalInput(label, configName) { + enabled = false; + holdHi = 2; + holdLo = 2; +} + +int16_t Asciipad::status(unsigned index, unsigned id) const { + switch(id) { + case SNES::Input::JoypadUp: return up.cachedState; + case SNES::Input::JoypadDown: return down.cachedState; + case SNES::Input::JoypadLeft: return left.cachedState; + case SNES::Input::JoypadRight: return right.cachedState; + case SNES::Input::JoypadA: return a.cachedState; + case SNES::Input::JoypadB: return b.cachedState; + case SNES::Input::JoypadX: return x.cachedState; + case SNES::Input::JoypadY: return y.cachedState; + case SNES::Input::JoypadL: return l.cachedState; + case SNES::Input::JoypadR: return r.cachedState; + case SNES::Input::JoypadSelect: return select.cachedState; + case SNES::Input::JoypadStart: return start.cachedState | slowMotion.cachedState; + } + return 0; +} + +Asciipad::Asciipad(unsigned category, const char *label, const char *configName) : +InputGroup(category, label), +up("Up", string() << "input." << configName << ".up"), +down("Down", string() << "input." << configName << ".down"), +left("Left", string() << "input." << configName << ".left"), +right("Right", string() << "input." << configName << ".right"), +b("B", string() << "input." << configName << ".b"), +a("A", string() << "input." << configName << ".a"), +y("Y", string() << "input." << configName << ".y"), +x("X", string() << "input." << configName << ".x"), +l("L", string() << "input." << configName << ".l"), +r("R", string() << "input." << configName << ".r"), +select("Select", string() << "input." << configName << ".select"), +start("Start", string() << "input." << configName << ".start"), +switchB("B Switch", string() << "input." << configName << ".bSwitch"), +switchA("A Switch", string() << "input." << configName << ".aSwitch"), +switchY("Y Switch", string() << "input." << configName << ".ySwitch"), +switchX("X Switch", string() << "input." << configName << ".xSwitch"), +switchL("L Switch", string() << "input." << configName << ".lSwitch"), +switchR("R Switch", string() << "input." << configName << ".rSwitch"), +slowMotion("Slow Motion", string() << "input." << configName << ".slowMotion") { + b.asciiSwitch = &switchB; + a.asciiSwitch = &switchA; + y.asciiSwitch = &switchY; + x.asciiSwitch = &switchX; + l.asciiSwitch = &switchL; + r.asciiSwitch = &switchR; + + attach(&up); attach(&down); attach(&left); attach(&right); + attach(&b); attach(&a); attach(&y); attach(&x); + attach(&l); attach(&r); attach(&select); attach(&start); + attach(&switchB); attach(&switchA); attach(&switchY); attach(&switchX); + attach(&switchL); attach(&switchR); attach(&slowMotion); + + if(this == &asciipad1) { + up.name = "KB0::Up"; + down.name = "KB0::Down"; + left.name = "KB0::Left"; + right.name = "KB0::Right"; + b.name = "KB0::Z"; + a.name = "KB0::X"; + y.name = "KB0::A"; + x.name = "KB0::S"; + l.name = "KB0::D"; + r.name = "KB0::C"; + select.name = "KB0::Apostrophe"; + start.name = "KB0::Return"; + } +} + +// + +int16_t Mouse::status(unsigned index, unsigned id) const { + switch(id) { + case SNES::Input::MouseX: return x.cachedState; + case SNES::Input::MouseY: return y.cachedState; + case SNES::Input::MouseLeft: return left.cachedState; + case SNES::Input::MouseRight: return right.cachedState; + } + return 0; +} + +Mouse::Mouse(unsigned category, const char *label, const char *configName) : +InputGroup(category, label), +x("X-axis", string() << "input." << configName << ".x"), +y("Y-axis", string() << "input." << configName << ".y"), +left("Left Button", string() << "input." << configName << ".left"), +right("Right Button", string() << "input." << configName << ".right") { + attach(&x); attach(&y); attach(&left); attach(&right); + + x.name = "MS0::Xaxis"; + y.name = "MS0::Yaxis"; + left.name = "MS0::Button0"; + right.name = "MS0::Button2"; +} + +// + +int16_t SuperScope::status(unsigned index, unsigned id) const { + switch(id) { + case SNES::Input::SuperScopeX: return x.cachedState; + case SNES::Input::SuperScopeY: return y.cachedState; + case SNES::Input::SuperScopeTrigger: return trigger.cachedState; + case SNES::Input::SuperScopeCursor: return cursor.cachedState; + case SNES::Input::SuperScopeTurbo: return turbo.cachedState; + case SNES::Input::SuperScopePause: return pause.cachedState; + } + return 0; +} + +SuperScope::SuperScope(unsigned category, const char *label, const char *configName) : +InputGroup(category, label), +x("X-axis", string() << "input." << configName << ".x"), +y("Y-axis", string() << "input." << configName << ".y"), +trigger("Trigger", string() << "input." << configName << ".trigger"), +cursor("Cursor", string() << "input." << configName << ".cursor"), +turbo("Turbo", string() << "input." << configName << ".turbo"), +pause("Pause", string() << "input." << configName << ".pause") { + attach(&x); attach(&y); attach(&trigger); attach(&cursor); + attach(&turbo); attach(&pause); + + x.name = "MS0::Xaxis"; + y.name = "MS0::Yaxis"; + trigger.name = "MS0::Button0"; + cursor.name = "MS0::Button2"; + turbo.name = "KB0::T"; + pause.name = "KB0::P"; +} + +// + +int16_t Justifier::status(unsigned index, unsigned id) const { + switch(id) { + case SNES::Input::JustifierX: return x.cachedState; + case SNES::Input::JustifierY: return y.cachedState; + case SNES::Input::JustifierTrigger: return trigger.cachedState; + case SNES::Input::JustifierStart: return start.cachedState; + } + return 0; +} + +Justifier::Justifier(unsigned category, const char *label, const char *configName) : +InputGroup(category, label), +x("X-axis", string() << "input." << configName << ".x"), +y("Y-axis", string() << "input." << configName << ".y"), +trigger("Trigger", string() << "input." << configName << ".trigger"), +start("Start", string() << "input." << configName << ".start") { + attach(&x); attach(&y); attach(&trigger); attach(&start); + + if(this == &justifier1) { + x.name = "MS0::Xaxis"; + y.name = "MS0::Yaxis"; + trigger.name = "MS0::Button0"; + start.name = "MS0::Button2"; + } +} + +// + +int16_t Justifiers::status(unsigned index, unsigned id) const { + switch(index & 1) { default: + case 0: return port1.status(index, id); + case 1: return port2.status(index, id); + } +} + +Justifiers::Justifiers(Justifier &port1_, Justifier &port2_) : +InputGroup(InputCategory::Hidden, "Justifiers"), +port1(port1_), port2(port2_) { +} + +// + +Gamepad gamepad1(InputCategory::Port1, "Gamepad", "gamepad1"); +Asciipad asciipad1(InputCategory::Port1, "asciiPad", "asciipad1"); +Gamepad multitap1a(InputCategory::Port1, "Multitap - Port 1", "multitap1a"); +Gamepad multitap1b(InputCategory::Port1, "Multitap - Port 2", "multitap1b"); +Gamepad multitap1c(InputCategory::Port1, "Multitap - Port 3", "multitap1c"); +Gamepad multitap1d(InputCategory::Port1, "Multitap - Port 4", "multitap1d"); +Multitap multitap1(multitap1a, multitap1b, multitap1c, multitap1d); +Mouse mouse1(InputCategory::Port1, "Mouse", "mouse1"); + +Gamepad gamepad2(InputCategory::Port2, "Gamepad", "gamepad2"); +Asciipad asciipad2(InputCategory::Port2, "asciiPad", "asciipad2"); +Gamepad multitap2a(InputCategory::Port2, "Multitap - Port 1", "multitap2a"); +Gamepad multitap2b(InputCategory::Port2, "Multitap - Port 2", "multitap2b"); +Gamepad multitap2c(InputCategory::Port2, "Multitap - Port 3", "multitap2c"); +Gamepad multitap2d(InputCategory::Port2, "Multitap - Port 4", "multitap2d"); +Multitap multitap2(multitap2a, multitap2b, multitap2c, multitap2d); +Mouse mouse2(InputCategory::Port2, "Mouse", "mouse2"); +SuperScope superscope(InputCategory::Port2, "Super Scope", "superscope"); +Justifier justifier1(InputCategory::Port2, "Justifier 1", "justifier1"); +Justifier justifier2(InputCategory::Port2, "Justifier 2", "justifier2"); +Justifiers justifiers(justifier1, justifier2); + +} diff --git a/mednafen/snes/src/ui_qt/input/controller.hpp b/mednafen/snes/src/ui_qt/input/controller.hpp new file mode 100755 index 0000000..e7920be --- /dev/null +++ b/mednafen/snes/src/ui_qt/input/controller.hpp @@ -0,0 +1,112 @@ +struct ControllerPort1 { enum { None, Gamepad, Asciipad, Multitap, Mouse }; }; +struct ControllerPort2 { enum { None, Gamepad, Asciipad, Multitap, Mouse, SuperScope, Justifier, Justifiers }; }; + +namespace Controllers { + +struct TurboInput : DigitalInput { + unsigned holdHi; + unsigned holdLo; + unsigned counter; + void cache(); + TurboInput(const char*, const char*); +}; + +struct Gamepad : InputGroup { + DigitalInput up, down, left, right, b, a, y, x, l, r, select, start; + TurboInput turboB, turboA, turboY, turboX, turboL, turboR; + int16_t status(unsigned, unsigned) const; + Gamepad(unsigned, const char*, const char*); +}; + +struct Multitap : InputGroup { + Gamepad &port1, &port2, &port3, &port4; + int16_t status(unsigned, unsigned) const; + Multitap(Gamepad&, Gamepad&, Gamepad&, Gamepad&); +}; + +struct AsciiSwitch : DigitalInput { + enum Mode { Off, Turbo, Auto } mode; + void poll(); + AsciiSwitch(const char*, const char*); +}; + +struct AsciiSlowMotion : DigitalInput { + bool enabled; + unsigned holdHi; + unsigned holdLo; + unsigned counter; + void poll(); + void cache(); + AsciiSlowMotion(const char*, const char*); +}; + +struct AsciiInput : DigitalInput { + AsciiSwitch *asciiSwitch; + unsigned holdHi; + unsigned holdLo; + unsigned counter; + void cache(); + AsciiInput(const char*, const char*); +}; + +struct Asciipad : InputGroup { + DigitalInput up, down, left, right; + AsciiInput b, a, y, x, l, r; + DigitalInput select, start; + AsciiSwitch switchB, switchA, switchY, switchX, switchL, switchR; + AsciiSlowMotion slowMotion; + int16_t status(unsigned, unsigned) const; + Asciipad(unsigned, const char*, const char*); +}; + +struct Mouse : InputGroup { + AnalogInput x, y; + DigitalInput left, right; + int16_t status(unsigned, unsigned) const; + Mouse(unsigned, const char*, const char*); +}; + +struct SuperScope : InputGroup { + AnalogInput x, y; + DigitalInput trigger, cursor, turbo, pause; + int16_t status(unsigned, unsigned) const; + SuperScope(unsigned, const char*, const char*); +}; + +struct Justifier : InputGroup { + AnalogInput x, y; + DigitalInput trigger, start; + int16_t status(unsigned, unsigned) const; + Justifier(unsigned, const char*, const char*); +}; + +struct Justifiers : InputGroup { + Justifier &port1; + Justifier &port2; + int16_t status(unsigned, unsigned) const; + Justifiers(Justifier&, Justifier&); +}; + +extern Gamepad gamepad1; +extern Asciipad asciipad1; +extern Gamepad multitap1a; +extern Gamepad multitap1b; +extern Gamepad multitap1c; +extern Gamepad multitap1d; +extern Multitap multitap1; +extern Mouse mouse1; + +extern Gamepad gamepad2; +extern Asciipad asciipad2; +extern Gamepad multitap2a; +extern Gamepad multitap2b; +extern Gamepad multitap2c; +extern Gamepad multitap2d; +extern Multitap multitap2; +extern Mouse mouse2; +extern SuperScope superscope; +extern Justifier justifier1; +extern Justifier justifier2; +extern Justifiers justifiers; + +} diff --git a/mednafen/snes/src/ui_qt/input/input.cpp b/mednafen/snes/src/ui_qt/input/input.cpp new file mode 100755 index 0000000..cfe8087 --- /dev/null +++ b/mednafen/snes/src/ui_qt/input/input.cpp @@ -0,0 +1,277 @@ +#include "../ui-base.hpp" + +#include "controller.cpp" +#include "userinterface-general.cpp" +#include "userinterface-system.cpp" +#include "userinterface-emulationspeed.cpp" +#include "userinterface-states.cpp" +#include "userinterface-videosettings.cpp" + +void MappedInput::bind() { + lstring part; + part.split("+", name); + + modifier = InputModifier::None; + for(unsigned i = 0; i < part.size(); i++) { + if(part[i] == "Shift") modifier |= InputModifier::Shift; + if(part[i] == "Control") modifier |= InputModifier::Control; + if(part[i] == "Alt") modifier |= InputModifier::Alt; + if(part[i] == "Super") modifier |= InputModifier::Super; + } + + string temp = part[part.size() - 1]; + part.split(".", temp); + scancode = Scancode::decode(part[0]); + specifier = InputSpecifier::None; + if(part[1] == "Up") specifier = InputSpecifier::Up; + if(part[1] == "Down") specifier = InputSpecifier::Down; + if(part[1] == "Left") specifier = InputSpecifier::Left; + if(part[1] == "Right") specifier = InputSpecifier::Right; + if(part[1] == "Lo") specifier = InputSpecifier::Lo; + if(part[1] == "Hi") specifier = InputSpecifier::Hi; + if(part[1] == "Trigger") specifier = InputSpecifier::Trigger; + + //bypass modifier matching if scancode is itself a modifier + modifierOverride = Keyboard::isAnyModifier(scancode); + + //re-encode name, in case previous name was invalid + name = ""; + if(modifier & InputModifier::Shift) name << "Shift+"; + if(modifier & InputModifier::Control) name << "Control+"; + if(modifier & InputModifier::Alt) name << "Alt+"; + if(modifier & InputModifier::Super) name << "Super+"; + name << Scancode::encode(scancode); + if(specifier == InputSpecifier::Up) name << ".Up"; + if(specifier == InputSpecifier::Down) name << ".Down"; + if(specifier == InputSpecifier::Left) name << ".Left"; + if(specifier == InputSpecifier::Right) name << ".Right"; + if(specifier == InputSpecifier::Lo) name << ".Lo"; + if(specifier == InputSpecifier::Hi) name << ".Hi"; + if(specifier == InputSpecifier::Trigger) name << ".Trigger"; +} + +void MappedInput::cache() { + cachedState = state; +} + +MappedInput::MappedInput(const char *label_, const char *configName) : parent(0), label(label_) { + specifier = InputSpecifier::None; + modifierOverride = false; + state = 0; + previousState = 0; + cachedState = 0; + config().attach(name = "None", configName); +} + +// + +void DigitalInput::poll() { + previousState = state; + if(modifier == mapper().modifier || modifierOverride) { + if(specifier == InputSpecifier::None) { + state = mapper().state(scancode); + } else if(specifier == InputSpecifier::Up) { + state = (bool)(mapper().state(scancode) & Joypad::HatUp); + } else if(specifier == InputSpecifier::Down) { + state = (bool)(mapper().state(scancode) & Joypad::HatDown); + } else if(specifier == InputSpecifier::Left) { + state = (bool)(mapper().state(scancode) & Joypad::HatLeft); + } else if(specifier == InputSpecifier::Right) { + state = (bool)(mapper().state(scancode) & Joypad::HatRight); + } else if(specifier == InputSpecifier::Lo) { + state = mapper().state(scancode) < -16384; + } else if(specifier == InputSpecifier::Hi) { + state = mapper().state(scancode) > +16384; + } else if(specifier == InputSpecifier::Trigger) { + state = mapper().state(scancode) < 0; + } + } else { + state = 0; + } +} + +bool DigitalInput::isPressed() const { return state; } +bool DigitalInput::wasPressed() const { return previousState; } + +DigitalInput::DigitalInput(const char *label, const char *configName) : MappedInput(label, configName) { +} + +// + +void AnalogInput::poll() { + previousState = state; + if(Mouse::isAnyAxis(scancode)) { + if(input.acquired()) { + state = mapper().state(scancode); + } else { + state = 0; + } + } else if(Joypad::isAnyAxis(scancode)) { + state = mapper().state(scancode) / 8192; + } +} + +AnalogInput::AnalogInput(const char *label, const char *configName) : MappedInput(label, configName) { +} + +// + +void HotkeyInput::poll() { + DigitalInput::poll(); + if(mainWindow->isActive() && state != previousState) { + state ? pressed() : released(); + } +} + +HotkeyInput::HotkeyInput(const char *label, const char *configName) : DigitalInput(label, configName) { +} + +// + +void InputGroup::attach(MappedInput *input) { + input->parent = this; + add(input); +} + +void InputGroup::bind() { + for(unsigned i = 0; i < size(); i++) { + (*this)[i]->bind(); + } +} + +void InputGroup::poll() { + for(unsigned i = 0; i < size(); i++) { + (*this)[i]->poll(); + } +} + +void InputGroup::cache() { + for(unsigned i = 0; i < size(); i++) { + (*this)[i]->cache(); + } +} + +void InputGroup::flushCache() { + for(unsigned i = 0; i < size(); i++) { + MappedInput &input = *((*this)[i]); + input.cachedState = 0; + } +} + +InputGroup::InputGroup(unsigned category_, const char *label_) : category(category_), label(label_) { + mapper().add(this); +} + +// + +InputMapper& mapper() { + static InputMapper mapper; + return mapper; +} + +void InputMapper::calibrate() { + calibrated = true; + audio.clear(); + QMessageBox::information(settingsWindow, "Joypad Calibration", + "Joypads must be calibrated prior to mapping. Please ensure that " + "all axes and analog buttons are not pressed or moved in any specific " + "direction, and then press ok." + ); + + poll(); + for(unsigned i = 0; i < Joypad::Count; i++) { + for(unsigned axis = 0; axis < Joypad::Axes; axis++) { + int16_t n = state(joypad(i).axis(axis)); + isTrigger[i][axis] = n < -16384 || n > +16384; + } + } +} + +void InputMapper::bind() { + for(unsigned i = 0; i < size(); i++) { + (*this)[i]->bind(); + } +} + +void InputMapper::poll() { + activeState = !activeState; + input.poll(stateTable[activeState]); + + modifier = 0; + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(state(keyboard(i)[Keyboard::Shift])) modifier |= InputModifier::Shift; + if(state(keyboard(i)[Keyboard::Control])) modifier |= InputModifier::Control; + if(state(keyboard(i)[Keyboard::Alt])) modifier |= InputModifier::Alt; + if(state(keyboard(i)[Keyboard::Super])) modifier |= InputModifier::Super; + } + + for(unsigned i = 0; i < size(); i++) { + (*this)[i]->poll(); + } + + for(unsigned i = 0; i < Scancode::Limit; i++) { + if(state(i) != previousState(i)) { + utility.inputEvent(i); + diskBrowser->inputEvent(i); + inputSettingsWindow->inputEvent(i); + } + } +} + +void InputMapper::cache() { + if(config().input.focusPolicy == Configuration::Input::FocusPolicyIgnoreInput && !mainWindow->isActive()) { + for(unsigned i = 0; i < size(); i++) { + InputGroup &group = *((*this)[i]); + group.flushCache(); + } + } else { + for(unsigned i = 0; i < size(); i++) { + InputGroup &group = *((*this)[i]); + if(group.category == InputCategory::Port1 || group.category == InputCategory::Port2) { + group.cache(); + } + } + } +} + +int16_t InputMapper::status(bool port, unsigned device, unsigned index, unsigned id) { + int16_t result = 0; + + if(port == InputCategory::Port1 && port1) result = port1->status(index, id); + if(port == InputCategory::Port2 && port2) result = port2->status(index, id); + + if(movie.state == Movie::Playback) { + result = movie.read(); + } else if(movie.state == Movie::Record) { + movie.write(result); + } + + return result; +} + +string InputMapper::modifierString() const { + string name; + if(modifier & InputModifier::Shift) name << "Shift+"; + if(modifier & InputModifier::Control) name << "Control+"; + if(modifier & InputModifier::Alt) name << "Alt+"; + if(modifier & InputModifier::Super) name << "Super+"; + return name; +} + +int16_t InputMapper::state(uint16_t scancode) const { return stateTable[activeState][scancode]; } +int16_t InputMapper::previousState(uint16_t scancode) const { return stateTable[!activeState][scancode]; } +unsigned InputMapper::distance(uint16_t scancode) const { return abs(state(scancode) - previousState(scancode)); } + +InputMapper::InputMapper() : port1(0), port2(0) { + calibrated = false; + for(unsigned i = 0; i < Joypad::Count; i++) { + for(unsigned axis = 0; axis < Joypad::Axes; axis++) { + isTrigger[i][axis] = false; + } + } + + activeState = 0; + for(unsigned i = 0; i < Scancode::Limit; i++) { + stateTable[0][i] = stateTable[1][i] = 0; + } +} diff --git a/mednafen/snes/src/ui_qt/input/input.hpp b/mednafen/snes/src/ui_qt/input/input.hpp new file mode 100755 index 0000000..88fe2ab --- /dev/null +++ b/mednafen/snes/src/ui_qt/input/input.hpp @@ -0,0 +1,91 @@ +struct InputSpecifier { enum { None, Up, Down, Left, Right, Lo, Hi, Trigger }; }; +struct InputModifier { enum { None = 0, Shift = 1, Control = 2, Alt = 4, Super = 8 }; }; +struct InputCategory { enum { Port1 = 0, Port2 = 1, UserInterface = 2, Hidden = 3 }; }; + +struct InputGroup; + +struct MappedInput { + InputGroup *parent; + string name; + string label; + unsigned specifier; + unsigned modifier; + unsigned scancode; + bool modifierOverride; + int16_t state; + int16_t previousState; + int16_t cachedState; + + void bind(); + virtual void poll() = 0; + virtual void cache(); + + MappedInput(const char*, const char*); +}; + +struct DigitalInput : MappedInput { + void poll(); + + bool isPressed() const; + bool wasPressed() const; + + DigitalInput(const char*, const char*); +}; + +struct AnalogInput : MappedInput { + void poll(); + + AnalogInput(const char*, const char*); +}; + +struct HotkeyInput : DigitalInput { + void poll(); + virtual void pressed() {} + virtual void released() {} + + HotkeyInput(const char*, const char*); +}; + +struct InputGroup : public array { + unsigned category; + string label; + + void attach(MappedInput*); + void bind(); + void poll(); + void cache(); + void flushCache(); + virtual int16_t status(unsigned, unsigned) const { return 0; } + + InputGroup(unsigned, const char*); +}; + +struct InputMapper : public array { + InputGroup *port1; + InputGroup *port2; + + bool calibrated; + bool isTrigger[Joypad::Count][Joypad::Axes]; + + bool activeState; + int16_t stateTable[2][Scancode::Limit]; + unsigned modifier; + + void calibrate(); + void bind(); + void poll(); + void cache(); + int16_t status(bool, unsigned, unsigned, unsigned); + + string modifierString() const; + int16_t state(uint16_t) const; + int16_t previousState(uint16_t) const; + unsigned distance(uint16_t) const; + + InputMapper(); +}; + +InputMapper& mapper(); + +#include "controller.hpp" +#include "userinterface.hpp" diff --git a/mednafen/snes/src/ui_qt/input/userinterface-emulationspeed.cpp b/mednafen/snes/src/ui_qt/input/userinterface-emulationspeed.cpp new file mode 100755 index 0000000..0f9a1d5 --- /dev/null +++ b/mednafen/snes/src/ui_qt/input/userinterface-emulationspeed.cpp @@ -0,0 +1,181 @@ +InputGroup userInterfaceEmulationSpeed(InputCategory::UserInterface, "Emulation Speed"); + +namespace UserInterfaceEmulationSpeed { + +//slowdown and speedup do not work well with Vsync enabled, as it locks the +//speed to the monitor refresh rate. thus, when one is pressed, it is disabled +//until the key is released. + +struct Slowdown : HotkeyInput { + bool syncVideo; + + void pressed() { + config().system.speed = 0; + utility.updateEmulationSpeed(); + syncVideo = config().video.synchronize; + if(syncVideo) { + config().video.synchronize = false; + utility.updateAvSync(); + } + mainWindow->syncUi(); + } + + void released() { + config().system.speed = 2; + utility.updateEmulationSpeed(); + if(syncVideo) { + config().video.synchronize = true; + utility.updateAvSync(); + } + mainWindow->syncUi(); + } + + Slowdown() : HotkeyInput("Slowdown", "input.userInterface.emulationSpeed.slowdown") { + userInterfaceEmulationSpeed.attach(this); + } +} slowdown; + +struct Speedup : HotkeyInput { + bool syncVideo; + + void pressed() { + config().system.speed = 4; + utility.updateEmulationSpeed(); + syncVideo = config().video.synchronize; + if(syncVideo) { + config().video.synchronize = false; + utility.updateAvSync(); + } + mainWindow->syncUi(); + } + + void released() { + config().system.speed = 2; + utility.updateEmulationSpeed(); + if(syncVideo) { + config().video.synchronize = true; + utility.updateAvSync(); + } + mainWindow->syncUi(); + } + + Speedup() : HotkeyInput("Speedup", "input.userInterface.emulationSpeed.speedup") { + name = "KB0::Tilde"; + userInterfaceEmulationSpeed.attach(this); + } +} speedup; + +struct Decrease : HotkeyInput { + void pressed() { + if(config().system.speed > 0) config().system.speed--; + utility.updateEmulationSpeed(); + mainWindow->syncUi(); + } + + Decrease() : HotkeyInput("Decrease", "input.userInterface.emulationSpeed.decrease") { + name = "Control+KB0::Divide"; + userInterfaceEmulationSpeed.attach(this); + } +} decrease; + +struct Increase : HotkeyInput { + void pressed() { + if(config().system.speed < 4) config().system.speed++; + utility.updateEmulationSpeed(); + mainWindow->syncUi(); + } + + Increase() : HotkeyInput("Increase", "input.userInterface.emulationSpeed.increase") { + name = "Control+KB0::Multiply"; + userInterfaceEmulationSpeed.attach(this); + } +} increase; + +struct SetSlowestSpeed : HotkeyInput { + void pressed() { + config().system.speed = 0; + utility.updateEmulationSpeed(); + mainWindow->syncUi(); + } + + SetSlowestSpeed() : HotkeyInput("Set Slowest Speed", "input.userInterface.emulationSpeed.setSlowest") { + name = "Control+KB0::Num1"; + userInterfaceEmulationSpeed.attach(this); + } +} setSlowestSpeed; + +struct SetSlowSpeed : HotkeyInput { + void pressed() { + config().system.speed = 1; + utility.updateEmulationSpeed(); + mainWindow->syncUi(); + } + + SetSlowSpeed() : HotkeyInput("Set Slow Speed", "input.userInterface.emulationSpeed.setSlow") { + name = "Control+KB0::Num2"; + userInterfaceEmulationSpeed.attach(this); + } +} setSlowSpeed; + +struct SetNormalSpeed : HotkeyInput { + void pressed() { + config().system.speed = 2; + utility.updateEmulationSpeed(); + mainWindow->syncUi(); + } + + SetNormalSpeed() : HotkeyInput("Set Normal Speed", "input.userInterface.emulationSpeed.setNormal") { + name = "Control+KB0::Num3"; + userInterfaceEmulationSpeed.attach(this); + } +} setNormalSpeed; + +struct SetFastSpeed : HotkeyInput { + void pressed() { + config().system.speed = 3; + utility.updateEmulationSpeed(); + mainWindow->syncUi(); + } + + SetFastSpeed() : HotkeyInput("Set Fast Speed", "input.userInterface.emulationSpeed.setFast") { + name = "Control+KB0::Num4"; + userInterfaceEmulationSpeed.attach(this); + } +} setFastSpeed; + +struct SetFastestSpeed : HotkeyInput { + void pressed() { + config().system.speed = 4; + utility.updateEmulationSpeed(); + mainWindow->syncUi(); + } + + SetFastestSpeed() : HotkeyInput("Set Fastest Speed", "input.userInterface.emulationSpeed.setFastest") { + name = "Control+KB0::Num5"; + userInterfaceEmulationSpeed.attach(this); + } +} setFastestSpeed; + +struct SynchronizeVideo : HotkeyInput { + void pressed() { + utility.toggleSynchronizeVideo(); + } + + SynchronizeVideo() : HotkeyInput("Synchronize Video", "input.userInterface.emulationSpeed.synchronizeVideo") { + name = "Control+KB0::V"; + userInterfaceEmulationSpeed.attach(this); + } +} synchronizeVideo; + +struct SynchronizeAudio : HotkeyInput { + void pressed() { + utility.toggleSynchronizeAudio(); + } + + SynchronizeAudio() : HotkeyInput("Synchronize Audio", "input.userInterface.emulationSpeed.synchronizeAudio") { + name = "Control+KB0::A"; + userInterfaceEmulationSpeed.attach(this); + } +} synchronizeAudio; + +} diff --git a/mednafen/snes/src/ui_qt/input/userinterface-general.cpp b/mednafen/snes/src/ui_qt/input/userinterface-general.cpp new file mode 100755 index 0000000..82892c7 --- /dev/null +++ b/mednafen/snes/src/ui_qt/input/userinterface-general.cpp @@ -0,0 +1,68 @@ +InputGroup userInterfaceGeneral(InputCategory::UserInterface, "General"); + +namespace UserInterfaceGeneral { + +struct ToggleMenubar : HotkeyInput { + void pressed() { + utility.toggleMenubar(); + } + + ToggleMenubar() : HotkeyInput("Toggle Menubar", "input.userInterface.general.toggleMenubar") { + name = "KB0::Tab"; + userInterfaceGeneral.attach(this); + } +} toggleMenubar; + +struct ToggleStatusbar : HotkeyInput { + void pressed() { + utility.toggleStatusbar(); + } + + ToggleStatusbar() : HotkeyInput("Toggle Statusbar", "input.userInterface.general.toggleStatusbar") { + name = "KB0::Tab"; + userInterfaceGeneral.attach(this); + } +} toggleStatusbar; + +struct ToggleCheatSystem : HotkeyInput { + void pressed() { + if(SNES::cheat.enabled() == false) { + SNES::cheat.enable(true); + utility.showMessage("Cheat system enabled."); + } else { + SNES::cheat.enable(false); + utility.showMessage("Cheat system disabled."); + } + } + + ToggleCheatSystem() : HotkeyInput("Toggle Cheat System", "input.userInterface.general.toggleCheatSystem") { + userInterfaceGeneral.attach(this); + } +} toggleCheatSystem; + +struct CaptureScreenshot : HotkeyInput { + void pressed() { + //tell SNES::Interface to save a screenshot at the next video_refresh() event + interface.saveScreenshot = true; + } + + CaptureScreenshot() : HotkeyInput("Capture Screenshot", "input.userInterface.general.captureScreenshot") { + userInterfaceGeneral.attach(this); + } +} captureScreenshot; + +//put here instead of in a separate "Audio Settings" group, +//because there is only one audio option at present +struct MuteAudioOutput : HotkeyInput { + void pressed() { + mainWindow->settings_muteAudio->toggleChecked(); + config().audio.mute = mainWindow->settings_muteAudio->isChecked(); + } + + MuteAudioOutput() : HotkeyInput("Mute Audio Output", "input.userInterface.general.muteAudioOutput") { + name = "Shift+KB0::M"; + userInterfaceGeneral.attach(this); + } +} muteAudioOutput; + +} diff --git a/mednafen/snes/src/ui_qt/input/userinterface-states.cpp b/mednafen/snes/src/ui_qt/input/userinterface-states.cpp new file mode 100755 index 0000000..32bf199 --- /dev/null +++ b/mednafen/snes/src/ui_qt/input/userinterface-states.cpp @@ -0,0 +1,148 @@ +InputGroup userInterfaceStates(InputCategory::UserInterface, "States"); + +namespace UserInterfaceStates { + +//note: interally, there are ten quick save slots: 0-9 +//for the sake of users, this is displayed as 1-10 in the GUI +unsigned activeState = 0; + +struct Rewind : HotkeyInput { + void pressed() { + ::state.rewind(); + } + + Rewind() : HotkeyInput("Rewind", "input.userInterface.states.rewind") { + name = "KB0::Backspace"; + userInterfaceStates.attach(this); + } +} rewind; + +struct LoadActiveState : HotkeyInput { + void pressed() { + ::state.load(activeState); + } + + LoadActiveState() : HotkeyInput("Load Active Quick State", "input.userInterface.states.loadActiveQuickState") { + name = "KB0::F2"; + userInterfaceStates.attach(this); + } +} loadActiveState; + +struct SaveActiveState : HotkeyInput { + void pressed() { + ::state.save(activeState); + } + + SaveActiveState() : HotkeyInput("Save Active Quick State", "input.userInterface.states.saveActiveQuickState") { + name = "Shift+KB0::F2"; + userInterfaceStates.attach(this); + } +} saveActiveState; + +struct DecrementAndLoadState : HotkeyInput { + void pressed() { + activeState = (activeState + 10 - 1) % 10; + ::state.load(activeState); + } + + DecrementAndLoadState() : HotkeyInput("Decrement and Load State", "input.userInterface.states.decrementAndLoadState") { + userInterfaceStates.attach(this); + } +} decrementAndLoadState; + +struct SaveAndIncrementState : HotkeyInput { + void pressed() { + ::state.save(activeState); + activeState = (activeState + 10 + 1) % 10; + } + + SaveAndIncrementState() : HotkeyInput("Save and Increment State", "input.userInterface.states.saveAndIncrementState") { + userInterfaceStates.attach(this); + } +} saveAndIncrementState; + +struct DecrementActiveState : HotkeyInput { + void pressed() { + activeState = (activeState + 10 - 1) % 10; + utility.showMessage(string() << "Quick state " << (activeState + 1) << " selected."); + } + + DecrementActiveState() : HotkeyInput("Decrement Active Quick State Slot", "input.userInterface.states.decrementActiveQuickState") { + name = "KB0::F3"; + userInterfaceStates.attach(this); + } +} decrementActiveState; + +struct IncrementActiveState : HotkeyInput { + void pressed() { + activeState = (activeState + 10 + 1) % 10; + utility.showMessage(string() << "Quick state " << (activeState + 1) << " selected."); + } + + IncrementActiveState() : HotkeyInput("Increment Active Quick State Slot", "input.userInterface.states.incrementActiveQuickState") { + name = "KB0::F4"; + userInterfaceStates.attach(this); + } +} incrementActiveState; + +struct LoadState1 : HotkeyInput { + void pressed() { + ::state.load(0); + } + + LoadState1() : HotkeyInput("Load Quick State 1", "input.userInterface.states.loadQuickState1") { + userInterfaceStates.attach(this); + } +} loadState1; + +struct LoadState2 : HotkeyInput { + void pressed() { + ::state.load(1); + } + + LoadState2() : HotkeyInput("Load Quick State 2", "input.userInterface.states.loadQuickState2") { + userInterfaceStates.attach(this); + } +} loadState2; + +struct LoadState3 : HotkeyInput { + void pressed() { + ::state.load(2); + } + + LoadState3() : HotkeyInput("Load Quick State 3", "input.userInterface.states.loadQuickState3") { + userInterfaceStates.attach(this); + } +} loadState3; + +struct SaveState1 : HotkeyInput { + void pressed() { + ::state.save(0); + } + + SaveState1() : HotkeyInput("Save Quick State 1", "input.userInterface.states.saveQuickState1") { + userInterfaceStates.attach(this); + } +} saveState1; + +struct SaveState2 : HotkeyInput { + void pressed() { + ::state.save(1); + } + + SaveState2() : HotkeyInput("Save Quick State 2", "input.userInterface.states.saveQuickState2") { + userInterfaceStates.attach(this); + } +} saveState2; + +struct SaveState3 : HotkeyInput { + void pressed() { + ::state.save(2); + } + + SaveState3() : HotkeyInput("Save Quick State 3", "input.userInterface.states.saveQuickState3") { + userInterfaceStates.attach(this); + } +} saveState3; + +} diff --git a/mednafen/snes/src/ui_qt/input/userinterface-system.cpp b/mednafen/snes/src/ui_qt/input/userinterface-system.cpp new file mode 100755 index 0000000..9809bba --- /dev/null +++ b/mednafen/snes/src/ui_qt/input/userinterface-system.cpp @@ -0,0 +1,88 @@ +InputGroup userInterfaceSystem(InputCategory::UserInterface, "System"); + +namespace UserInterfaceSystem { + +struct LoadCartridge : HotkeyInput { + void pressed() { + diskBrowser->loadCartridge(); + } + + LoadCartridge() : HotkeyInput("Load Cartridge", "input.userInterface.system.loadCartridge") { + name = "Shift+KB0::L"; + userInterfaceSystem.attach(this); + } +} loadCartridge; + +struct LoadBsxSlottedCartridge : HotkeyInput { + void pressed() { + loaderWindow->loadBsxSlottedCartridge("", ""); + } + + LoadBsxSlottedCartridge() : HotkeyInput("Load BS-X Slotted Cartridge", "input.userInterface.system.loadBsxSlottedcartridge") { + userInterfaceSystem.attach(this); + } +} loadBsxSlottedCartridge; + +struct LoadBsxCartridge : HotkeyInput { + void pressed() { + loaderWindow->loadBsxCartridge(config().path.bsx, ""); + } + + LoadBsxCartridge() : HotkeyInput("Load BS-X Cartridge", "input.userInterface.system.loadBsxCartridge") { + userInterfaceSystem.attach(this); + } +} loadBsxCartridge; + +struct LoadSufamiTurboCartridge : HotkeyInput { + void pressed() { + loaderWindow->loadSufamiTurboCartridge(config().path.st, "", ""); + } + + LoadSufamiTurboCartridge() : HotkeyInput("Load Sufami Turbo Cartridge", "input.userInterface.system.loadSufamiTurboCartridge") { + userInterfaceSystem.attach(this); + } +} loadSufamiTurboCartridge; + +struct LoadSuperGameBoyCartridge : HotkeyInput { + void pressed() { + loaderWindow->loadSuperGameBoyCartridge(config().path.sgb, ""); + } + + LoadSuperGameBoyCartridge() : HotkeyInput("Load Super Game Boy Cartridge", "input.userInterface.system.loadSuperGameBoyCartridge") { + userInterfaceSystem.attach(this); + } +} loadSuperGameBoyCartridge; + +struct PowerCycle : HotkeyInput { + void pressed() { + utility.modifySystemState(Utility::PowerCycle); + } + + PowerCycle() : HotkeyInput("Power Cycle", "input.userInterface.system.powerCycle") { + userInterfaceSystem.attach(this); + } +} powerCycle; + +struct Reset : HotkeyInput { + void pressed() { + utility.modifySystemState(Utility::Reset); + } + + Reset() : HotkeyInput("Reset", "input.userInterface.system.reset") { + userInterfaceSystem.attach(this); + } +} reset; + +struct Pause : HotkeyInput { + void pressed() { + application.pause = !application.pause; + if(application.pause) audio.clear(); + } + + Pause() : HotkeyInput("Pause", "input.userInterface.system.pause") { + name = "KB0::Pause"; + userInterfaceSystem.attach(this); + } +} pause; + +} diff --git a/mednafen/snes/src/ui_qt/input/userinterface-videosettings.cpp b/mednafen/snes/src/ui_qt/input/userinterface-videosettings.cpp new file mode 100755 index 0000000..0bc6446 --- /dev/null +++ b/mednafen/snes/src/ui_qt/input/userinterface-videosettings.cpp @@ -0,0 +1,115 @@ +InputGroup userInterfaceVideoSettings(InputCategory::UserInterface, "Video Settings"); + +namespace UserInterfaceVideoSettings { + +struct ToggleFullscreen : HotkeyInput { + void pressed() { + utility.toggleFullscreen(); + } + + ToggleFullscreen() : HotkeyInput("Toggle Fullscreen Mode", "input.userInterface.videoSettings.toggleFullscreen") { + name = "Alt+KB0::Return"; + userInterfaceVideoSettings.attach(this); + } +} toggleFullscreen; + +struct SmoothVideoOutput : HotkeyInput { + void pressed() { + utility.toggleSmoothVideoOutput(); + } + + SmoothVideoOutput() : HotkeyInput("Smooth Video Output", "input.userInterface.videoSettings.smoothVideoOutput") { + name = "Shift+KB0::S"; + userInterfaceVideoSettings.attach(this); + } +} smoothVideoOutput; + +struct SetNtscMode : HotkeyInput { + void pressed() { + utility.setNtscMode(); + } + + SetNtscMode() : HotkeyInput("Set NTSC Mode", "input.userInterface.videoSettings.ntscMode") { + name = "Shift+KB0::N"; + userInterfaceVideoSettings.attach(this); + } +} setNtscMode; + +struct SetPalMode : HotkeyInput { + void pressed() { + utility.setPalMode(); + } + + SetPalMode() : HotkeyInput("Set PAL Mode", "input.userInterface.videoSettings.palMode") { + name = "Shift+KB0::P"; + userInterfaceVideoSettings.attach(this); + } +} setPalMode; + +struct AspectCorrection : HotkeyInput { + void pressed() { + utility.toggleAspectCorrection(); + } + + AspectCorrection() : HotkeyInput("Aspect Correction", "input.userInterface.videoSettings.aspectCorrection") { + name = "Shift+KB0::A"; + userInterfaceVideoSettings.attach(this); + } +} aspectCorrection; + +struct Scale1x : HotkeyInput { + void pressed() { + utility.setScale(1); + } + + Scale1x() : HotkeyInput("Scale 1x", "input.userInterface.videoSettings.scale1x") { + name = "Shift+KB0::Num1"; + userInterfaceVideoSettings.attach(this); + } +} scale1x; + +struct Scale2x : HotkeyInput { + void pressed() { + utility.setScale(2); + } + + Scale2x() : HotkeyInput("Scale 2x", "input.userInterface.videoSettings.scale2x") { + name = "Shift+KB0::Num2"; + userInterfaceVideoSettings.attach(this); + } +} scale2x; + +struct Scale3x : HotkeyInput { + void pressed() { + utility.setScale(3); + } + + Scale3x() : HotkeyInput("Scale 3x", "input.userInterface.videoSettings.scale3x") { + name = "Shift+KB0::Num3"; + userInterfaceVideoSettings.attach(this); + } +} scale3x; + +struct Scale4x : HotkeyInput { + void pressed() { + utility.setScale(4); + } + + Scale4x() : HotkeyInput("Scale 4x", "input.userInterface.videoSettings.scale4x") { + name = "Shift+KB0::Num4"; + userInterfaceVideoSettings.attach(this); + } +} scale4x; + +struct Scale5x : HotkeyInput { + void pressed() { + utility.setScale(5); + } + + Scale5x() : HotkeyInput("Scale 5x", "input.userInterface.videoSettings.scale5x") { + name = "Shift+KB0::Num5"; + userInterfaceVideoSettings.attach(this); + } +} scale5x; + +} diff --git a/mednafen/snes/src/ui_qt/input/userinterface.hpp b/mednafen/snes/src/ui_qt/input/userinterface.hpp new file mode 100755 index 0000000..f291e10 --- /dev/null +++ b/mednafen/snes/src/ui_qt/input/userinterface.hpp @@ -0,0 +1,5 @@ +extern InputGroup userInterfaceGeneral; +extern InputGroup userInterfaceSystem; +extern InputGroup userInterfaceEmulationSpeed; +extern InputGroup userInterfaceStates; +extern InputGroup userInterfaceVideoSettings; diff --git a/mednafen/snes/src/ui_qt/interface.cpp b/mednafen/snes/src/ui_qt/interface.cpp new file mode 100755 index 0000000..7bd4a9d --- /dev/null +++ b/mednafen/snes/src/ui_qt/interface.cpp @@ -0,0 +1,54 @@ +Interface interface; + +void Interface::video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) { + uint32_t *output; + unsigned outwidth, outheight, outpitch; + filter.size(outwidth, outheight, width, height); + + if(video.lock(output, outpitch, outwidth, outheight) == true) { + filter.render(output, outpitch, data, pitch, line, width, height); + video.unlock(); + video.refresh(); + if(saveScreenshot == true) captureScreenshot(output, outpitch, outwidth, outheight); + } + + state.frame(); + #if defined(DEBUGGER) + debugger->frameTick(); + #endif +} + +void Interface::audio_sample(uint16_t left, uint16_t right) { + if(config().audio.mute) left = right = 0; + audio.sample(left, right); +} + +void Interface::input_poll() { + mapper().cache(); +} + +int16_t Interface::input_poll(bool port, unsigned device, unsigned index, unsigned id) { + return mapper().status(port, device, index, id); +} + +void Interface::captureScreenshot(uint32_t *data, unsigned pitch, unsigned width, unsigned height) { + saveScreenshot = false; + QImage image((const unsigned char*)data, width, height, pitch, QImage::Format_RGB32); + + string filename = nall::basename(cartridge.fileName); + time_t systemTime = time(0); + tm *currentTime = localtime(&systemTime); + char t[512]; + sprintf(t, "%.4u%.2u%.2u-%.2u%.2u%.2u", + 1900 + currentTime->tm_year, 1 + currentTime->tm_mon, currentTime->tm_mday, + currentTime->tm_hour, currentTime->tm_min, currentTime->tm_sec + ); + filename << "-" << t << ".png"; + + image.save(filepath(filename, config().path.data)); + utility.showMessage("Screenshot saved."); +} + +Interface::Interface() { + saveScreenshot = false; +} diff --git a/mednafen/snes/src/ui_qt/interface.hpp b/mednafen/snes/src/ui_qt/interface.hpp new file mode 100755 index 0000000..a3cb1ed --- /dev/null +++ b/mednafen/snes/src/ui_qt/interface.hpp @@ -0,0 +1,13 @@ +class Interface : public SNES::Interface { +public: + void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height); + void audio_sample(uint16_t left, uint16_t right); + void input_poll(); + int16_t input_poll(bool port, unsigned device, unsigned index, unsigned id); + + Interface(); + void captureScreenshot(uint32_t*, unsigned, unsigned, unsigned); + bool saveScreenshot; +}; + +extern Interface interface; diff --git a/mednafen/snes/src/ui_qt/link/filter.cpp b/mednafen/snes/src/ui_qt/link/filter.cpp new file mode 100755 index 0000000..9f93f28 --- /dev/null +++ b/mednafen/snes/src/ui_qt/link/filter.cpp @@ -0,0 +1,255 @@ +//============== +//ScanlineFilter +//============== + +ScanlineFilter scanlineFilter; + +void ScanlineFilter::size(unsigned &width, unsigned &height) { + if(enabled && height <= 240) height *= 2; +} + +void ScanlineFilter::render( + const uint16_t *&input, unsigned &pitch, + const unsigned *&line, unsigned width, unsigned &height +) { + if(enabled && height <= 240) { + pitch >>= 1; + + const uint16_t *sp = input; + uint16_t *dp = buffer; + unsigned *lp = linewidth; + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < line[y]; x++) { + uint16_t color = *sp++; + *(dp + 0) = color; + *(dp + 512) = adjust[color]; + dp++; + } + + sp += pitch - line[y]; + dp += 1024 - line[y]; + + *lp++ = line[y]; + *lp++ = line[y]; + } + + input = buffer; + pitch = 1024; + line = linewidth; + height *= 2; + } +} + +void ScanlineFilter::setIntensity(unsigned intensity) { + if(intensity >= 100) { + enabled = false; + } else { + enabled = true; + + for(unsigned i = 0; i < 32768; i++) { + unsigned r = (i >> 0) & 31; + unsigned g = (i >> 5) & 31; + unsigned b = (i >> 10) & 31; + + r = (double)r * (double)intensity / 100.0; + g = (double)g * (double)intensity / 100.0; + b = (double)b * (double)intensity / 100.0; + + adjust[i] = (r << 0) + (g << 5) + (b << 10); + } + } +} + +ScanlineFilter::ScanlineFilter() { + enabled = false; + adjust = new uint16_t[32768]; + buffer = new uint16_t[512 * 480]; + setIntensity(50); +} + +ScanlineFilter::~ScanlineFilter() { + delete[] adjust; + delete[] buffer; +} + +//====== +//Filter +//====== + +Filter filter; + +const uint8_t Filter::gamma_ramp_table[32] = { + 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, + 0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78, + 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, + 0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff, +}; + +uint8_t Filter::contrast_adjust(uint8_t input) { + signed result = input - contrast + (2 * contrast * input + 127) / 255; + return max(0, min(255, result)); +} + +uint8_t Filter::brightness_adjust(uint8_t input) { + signed result = input + brightness; + return max(0, min(255, result)); +} + +uint8_t Filter::gamma_adjust(uint8_t input) { + signed result = (signed)(pow(((double)input / 255.0), (double)gamma / 100.0) * 255.0 + 0.5); + return max(0, min(255, result)); +} + +void Filter::colortable_update() { + double kr = 0.2126, kb = 0.0722, kg = (1.0 - kr - kb); //luminance weights + + for(unsigned i = 0; i < 32768; i++) { + unsigned color //bgr555->rgb888 conversion + = ((i & 0x001f) << 19) | ((i & 0x001c) << 14) + | ((i & 0x03e0) << 6) | ((i & 0x0380) << 1) + | ((i & 0x7c00) >> 7) | ((i & 0x7000) >> 12); + + signed l; + signed r = (color >> 16) & 0xff; + signed g = (color >> 8) & 0xff; + signed b = (color ) & 0xff; + + if(gamma_ramp == true) { + r = gamma_ramp_table[r >> 3]; + g = gamma_ramp_table[g >> 3]; + b = gamma_ramp_table[b >> 3]; + } + + if(contrast != 0) { + r = contrast_adjust(r); + g = contrast_adjust(g); + b = contrast_adjust(b); + } + + if(brightness != 0) { + r = brightness_adjust(r); + g = brightness_adjust(g); + b = brightness_adjust(b); + } + + if(gamma != 100) { + r = gamma_adjust(r); + g = gamma_adjust(g); + b = gamma_adjust(b); + } + + if(sepia == true) { + l = (signed)((double)r * kr + (double)g * kg + (double)b * kb); + l = max(0, min(255, l)); + + r = (signed)((double)l * (1.0 + 0.300)); + g = (signed)((double)l * (1.0 - 0.055)); + b = (signed)((double)l * (1.0 - 0.225)); + + r = max(0, min(255, r)); + g = max(0, min(255, g)); + b = max(0, min(255, b)); + } + + if(grayscale == true) { + l = (signed)((double)r * kr + (double)g * kg + (double)b * kb); + l = max(0, min(255, l)); + r = g = b = l; + } + + if(invert == true) { + r ^= 0xff; + g ^= 0xff; + b ^= 0xff; + } + + colortable[i] = (r << 16) | (g << 8) | (b); + } +} + +void Filter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) { + scanlineFilter.size(width, height); + + if(opened() && renderer > 0) { + return dl_size(renderer, outwidth, outheight, width, height); + } + + outwidth = width; + outheight = height; +} + +void Filter::render( + uint32_t *output, unsigned outpitch, + const uint16_t *input, unsigned pitch, + const unsigned *line, unsigned width, unsigned height +) { + scanlineFilter.render(input, pitch, line, width, height); + + if(opened() && renderer > 0) { + return dl_render(renderer, output, outpitch, input, pitch, line, width, height); + } + + pitch >>= 1; + outpitch >>= 2; + + for(unsigned y = 0; y < height; y++) { + if(width == 512 && line[y] == 256) { + for(unsigned x = 0; x < 256; x++) { + uint16_t p = *input++; + *output++ = colortable[p]; + *output++ = colortable[p]; + } + input += 256; + } else { + for(unsigned x = 0; x < width; x++) { + uint16_t p = *input++; + *output++ = colortable[p]; + } + } + input += pitch - width; + output += outpitch - width; + } +} + +QWidget* Filter::settings() { + if(opened() && renderer > 0) { + return dl_settings(renderer); + } else { + return 0; + } +} + +Filter::Filter() { + renderer = 0; + + contrast = 0; + brightness = 0; + gamma = 100; + + gamma_ramp = false; + sepia = false; + grayscale = false; + invert = false; + + colortable = new uint32_t[32768]; + colortable_update(); + + if(open("snesfilter")) { + dl_supported = sym("snesfilter_supported"); + dl_colortable = sym("snesfilter_colortable"); + dl_configuration = sym("snesfilter_configuration"); + dl_size = sym("snesfilter_size"); + dl_render = sym("snesfilter_render"); + dl_settings = sym("snesfilter_settings"); + + dl_colortable(colortable); + dl_configuration(config()); + } else { + config().video.windowed.swFilter = 0; + config().video.fullscreen.swFilter = 0; + } +} + +Filter::~Filter() { + delete[] colortable; +} diff --git a/mednafen/snes/src/ui_qt/link/filter.hpp b/mednafen/snes/src/ui_qt/link/filter.hpp new file mode 100755 index 0000000..6db2998 --- /dev/null +++ b/mednafen/snes/src/ui_qt/link/filter.hpp @@ -0,0 +1,55 @@ +class ScanlineFilter { +public: + bool enabled; + + void size(unsigned&, unsigned&); + void render(const uint16_t*&, unsigned&, const unsigned*&, unsigned, unsigned&); + void setIntensity(unsigned); + + ScanlineFilter(); + ~ScanlineFilter(); + +private: + uint16_t *adjust; + uint16_t *buffer; + unsigned linewidth[480]; +}; + +class Filter : public library { +public: + function dl_supported; + function dl_colortable; + function dl_configuration; + function dl_size; + function dl_render; + function dl_settings; + + unsigned renderer; + uint32_t *colortable; + + signed contrast; + signed brightness; + signed gamma; + + bool gamma_ramp; + bool sepia; + bool grayscale; + bool invert; + + void colortable_update(); + void size(unsigned&, unsigned&, unsigned, unsigned); + void render(uint32_t*, unsigned, const uint16_t*, unsigned, const unsigned*, unsigned, unsigned); + QWidget* settings(); + + Filter(); + ~Filter(); + +private: + static const uint8_t gamma_ramp_table[32]; + uint8_t contrast_adjust(uint8_t input); + uint8_t brightness_adjust(uint8_t input); + uint8_t gamma_adjust(uint8_t input); +}; + +extern ScanlineFilter scanlineFilter; +extern Filter filter; diff --git a/mednafen/snes/src/ui_qt/link/reader.cpp b/mednafen/snes/src/ui_qt/link/reader.cpp new file mode 100755 index 0000000..e5f9d43 --- /dev/null +++ b/mednafen/snes/src/ui_qt/link/reader.cpp @@ -0,0 +1,44 @@ +Reader reader; + +const char* Reader::direct_supported() { + return ""; +} + +bool Reader::direct_load(string &filename, uint8_t *&data, unsigned &size) { + if(file::exists(filename) == false) return false; + + file fp; + if(fp.open(filename, file::mode_read) == false) return false; + + data = new uint8_t[size = fp.size()]; + fp.read(data, size); + fp.close(); + + //remove copier header, if it exists + if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512); + + return true; +} + +Reader::Reader() { + if(open("snesreader")) { + supported = sym("snesreader_supported"); + load = sym("snesreader_load"); + } + + if(!supported || !load) { + supported = bind(&Reader::direct_supported, this); + load = bind(&Reader::direct_load, this); + } + + compressionList = supported(); + if(compressionList.length() > 0) compressionList = string() << " " << compressionList; + + if(opened()) { + extensionList = string() + << " *.smc *.swc *.fig" + << " *.ufo *.gd3 *.gd7 *.dx2 *.mgd *.mgh" + << " *.048 *.058 *.068 *.078 *.bin" + << " *.usa *.eur *.jap *.aus *.bsx"; + } +} diff --git a/mednafen/snes/src/ui_qt/link/reader.hpp b/mednafen/snes/src/ui_qt/link/reader.hpp new file mode 100755 index 0000000..1cc9341 --- /dev/null +++ b/mednafen/snes/src/ui_qt/link/reader.hpp @@ -0,0 +1,15 @@ +class Reader : public library { +public: + string compressionList; + string extensionList; + + function supported; + function load; + + const char* direct_supported(); + bool direct_load(string&, uint8_t*&, unsigned&); + + Reader(); +}; + +extern Reader reader; diff --git a/mednafen/snes/src/ui_qt/main.cpp b/mednafen/snes/src/ui_qt/main.cpp new file mode 100755 index 0000000..48a659e --- /dev/null +++ b/mednafen/snes/src/ui_qt/main.cpp @@ -0,0 +1,39 @@ +#include "ui-base.hpp" +#include "resource.rcc" + +#if defined(PLATFORM_X) + #include "platform/platform_x.cpp" + const char Style::Monospace[64] = "Monospace"; +#elif defined(PLATFORM_OSX) + #include "platform/platform_osx.cpp" + const char Style::Monospace[64] = "Courier New"; +#elif defined(PLATFORM_WIN) + #include "platform/platform_win.cpp" + const char Style::Monospace[64] = "Lucida Console"; +#else + #error "unsupported platform" +#endif + +#include "config.cpp" +#include "interface.cpp" + +const char defaultStylesheet[] = + "#backdrop {" + " background: #000000;" + "}\n"; + +#include "application/application.cpp" +#include "link/filter.cpp" +#include "link/reader.cpp" +#include "utility/utility.cpp" + +//override filename's path with filepath, but only if filepath isn't empty +//used for GUI's "path selection" functionality +string filepath(const char *filename, const char *filepath) { + if(!filepath || !*filepath) return filename; + return string() << dir(filepath) << notdir(filename); +} + +int main(int argc, char **argv) { + return application.main(argc, argv); +} diff --git a/mednafen/snes/src/ui_qt/movie/movie.cpp b/mednafen/snes/src/ui_qt/movie/movie.cpp new file mode 100755 index 0000000..4990425 --- /dev/null +++ b/mednafen/snes/src/ui_qt/movie/movie.cpp @@ -0,0 +1,109 @@ +#include "../ui-base.hpp" + +Movie movie; + +void Movie::chooseFile() { + diskBrowser->chooseFile( + bind(&Movie::play, this), + config().path.current.movie, + "Select Movie" + ); +} + +void Movie::play(string filename) { + if(Movie::state != Inactive) stop(); + + if(fp.open(filename, file::mode_read)) { + if(fp.size() < 32) goto corrupt; + + unsigned signature = fp.readm(4); + if(signature != 0x42535631) goto corrupt; + + unsigned version = fp.readl(4); + if(version != bsnesSerializerVersion) goto corrupt; + + unsigned crc32 = fp.readl(4); + if(crc32 != SNES::cartridge.crc32()) goto corrupt; + + unsigned size = fp.readl(4); + uint8_t *data = new uint8_t[size]; + fp.read(data, size); + serializer state(data, size); + SNES::system.unserialize(state); + + Movie::state = Playback; + mainWindow->syncUi(); + utility.showMessage("Playback started."); + + return; + } + +corrupt: + fp.close(); + utility.showMessage("Movie file is invalid, playback cancelled."); +} + +void Movie::record() { + if(Movie::state != Inactive) { + utility.showMessage("Movie mode already active, recording cancelled."); + } else { + SNES::system.runtosave(); + serializer state = SNES::system.serialize(); + + utility.showMessage("Recording started."); + + Movie::state = Record; + mainWindow->syncUi(); + fp.open(makeFilename(), file::mode_write); + fp.writem(0x42535631, 4); + fp.writel(bsnesSerializerVersion, 4); + fp.writel(SNES::cartridge.crc32(), 4); + fp.writel(state.size(), 4); + fp.write(state.data(), state.size()); + } +} + +void Movie::stop() { + if(Movie::state != Inactive) { + Movie::state = Inactive; + mainWindow->syncUi(); + fp.close(); + utility.showMessage("Recording / playback stopped."); + } +} + +string Movie::makeFilename() const { + string filename = nall::basename(cartridge.fileName); + + time_t systemTime = time(0); + tm *currentTime = localtime(&systemTime); + char t[512]; + sprintf(t, "%.4u%.2u%.2u-%.2u%.2u%.2u", + 1900 + currentTime->tm_year, 1 + currentTime->tm_mon, currentTime->tm_mday, + currentTime->tm_hour, currentTime->tm_min, currentTime->tm_sec + ); + filename << "-" << t << ".bsv"; + + return filepath(filename, config().path.data); +} + +int16_t Movie::read() { + int16_t result = fp.readl(2); + + if(fp.end()) { + Movie::state = Inactive; + mainWindow->syncUi(); + fp.close(); + utility.showMessage("Playback finished."); + } + + return result; +} + +void Movie::write(int16_t value) { + fp.writel(value, 2); +} + +Movie::Movie() { + state = Inactive; +} diff --git a/mednafen/snes/src/ui_qt/movie/movie.hpp b/mednafen/snes/src/ui_qt/movie/movie.hpp new file mode 100755 index 0000000..f172f71 --- /dev/null +++ b/mednafen/snes/src/ui_qt/movie/movie.hpp @@ -0,0 +1,20 @@ +class Movie { +public: + enum State { Inactive, Playback, Record } state; + + void chooseFile(); + void play(string filename); + void record(); + void stop(); + + Movie(); + +//private: + file fp; + + string makeFilename() const; + int16_t read(); + void write(int16_t value); +}; + +extern Movie movie; diff --git a/mednafen/snes/src/ui_qt/platform/platform_osx.cpp b/mednafen/snes/src/ui_qt/platform/platform_osx.cpp new file mode 100755 index 0000000..2432131 --- /dev/null +++ b/mednafen/snes/src/ui_qt/platform/platform_osx.cpp @@ -0,0 +1,17 @@ +char* userpath(char *path) { + *path = 0; + struct passwd *userinfo = getpwuid(getuid()); + if(userinfo) strcpy(path, userinfo->pw_dir); + return path; +} + +char *getcwd(char *path) { + return getcwd(path, PATH_MAX); +} + +void initargs(int &argc, char **&argv) { +} + +void supressScreenSaver() { +} + diff --git a/mednafen/snes/src/ui_qt/platform/platform_win.cpp b/mednafen/snes/src/ui_qt/platform/platform_win.cpp new file mode 100755 index 0000000..e7276a3 --- /dev/null +++ b/mednafen/snes/src/ui_qt/platform/platform_win.cpp @@ -0,0 +1,57 @@ +#include + +char* realpath(const char *filename, char *resolvedname) { + wchar_t fn[_MAX_PATH] = L""; + _wfullpath(fn, nall::utf16_t(filename), _MAX_PATH); + strcpy(resolvedname, nall::utf8_t(fn)); + return resolvedname; +} + +char* userpath(char *path) { + wchar_t fp[_MAX_PATH] = L""; + SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp); + strcpy(path, nall::utf8_t(fp)); + return path; +} + +char* getcwd(char *path) { + wchar_t fp[_MAX_PATH] = L""; + _wgetcwd(fp, _MAX_PATH); + strcpy(path, nall::utf8_t(fp)); + return path; +} + +int mkdir(const char *path) { + return _wmkdir(nall::utf16_t(path)); +} + +void initargs(int &argc, char **&argv) { + wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + argv = new char*[argc]; + for(unsigned i = 0; i < argc; i++) { + argv[i] = new char[_MAX_PATH]; + strcpy(argv[i], nall::utf8_t(wargv[i])); + } +} + +bool Application::App::winEventFilter(MSG *msg, long *result) { + //supress screen saver from activating during gameplay + if(msg->message == WM_SYSCOMMAND) { + if(msg->wParam == SC_SCREENSAVE || msg->wParam == SC_MONITORPOWER) { + *result = 0; + return true; + } + } + + //prevent DirectSound audio buffer from looping during Windows modal events + if(msg->message == WM_ENTERMENULOOP || msg->message == WM_ENTERSIZEMOVE) { + audio.clear(); + } + + return false; +} + +void supressScreenSaver() { + //handled by event filter above +} + diff --git a/mednafen/snes/src/ui_qt/platform/platform_x.cpp b/mednafen/snes/src/ui_qt/platform/platform_x.cpp new file mode 100755 index 0000000..690b687 --- /dev/null +++ b/mednafen/snes/src/ui_qt/platform/platform_x.cpp @@ -0,0 +1,47 @@ +#define None XNone +#define Window XWindow +#include +#undef None +#undef Window + +struct LibXtst : public library { + function XTestFakeKeyEvent; + + LibXtst() { + if(open("Xtst")) { + XTestFakeKeyEvent = sym("XTestFakeKeyEvent"); + } + } +} libXtst; + +char* userpath(char *path) { + *path = 0; + struct passwd *userinfo = getpwuid(getuid()); + if(userinfo) strcpy(path, userinfo->pw_dir); + return path; +} + +char *getcwd(char *path) { + return getcwd(path, PATH_MAX); +} + +void initargs(int &argc, char **&argv) { +} + +void supressScreenSaver() { + if(!libXtst.XTestFakeKeyEvent) return; + + //XSetScreenSaver(timeout = 0) does not work + //XResetScreenSaver() does not work + //XScreenSaverSuspend() does not work + //DPMSDisable() does not work + //XSendEvent(KeyPressMask) does not work + //use XTest extension to send fake keypress every ~20 seconds. + //keycode of 255 does not map to any actual key, + //but it will block screensaver and power management. + Display *display = XOpenDisplay(0); + libXtst.XTestFakeKeyEvent(display, 255, True, 0); + libXtst.XTestFakeKeyEvent(display, 255, False, 0); + XCloseDisplay(display); +} + diff --git a/mednafen/snes/src/ui_qt/resource/resource.qrc b/mednafen/snes/src/ui_qt/resource/resource.qrc new file mode 100755 index 0000000..84af06d --- /dev/null +++ b/mednafen/snes/src/ui_qt/resource/resource.qrc @@ -0,0 +1,37 @@ + + + + ../../data/bsnes.png + ../../data/logo.png + ../../data/documentation.html + ../../data/license.html + + ../../data/icons-16x16/item-check-on.png + ../../data/icons-16x16/item-check-off.png + ../../data/icons-16x16/item-radio-on.png + ../../data/icons-16x16/item-radio-off.png + + ../../data/icons-16x16/accessories-text-editor.png + ../../data/icons-16x16/applications-multimedia.png + ../../data/icons-16x16/appointment-new.png + ../../data/icons-16x16/audio-volume-high.png + ../../data/icons-16x16/document-open.png + ../../data/icons-16x16/folder.png + ../../data/icons-16x16/folder-new.png + ../../data/icons-16x16/help-browser.png + ../../data/icons-16x16/image-x-generic.png + ../../data/icons-16x16/input-gaming.png + ../../data/icons-16x16/media-playback-start.png + ../../data/icons-16x16/media-playback-stop.png + ../../data/icons-16x16/media-record.png + ../../data/icons-16x16/preferences-desktop.png + ../../data/icons-16x16/preferences-system.png + ../../data/icons-16x16/process-stop.png + ../../data/icons-16x16/system-file-manager.png + ../../data/icons-16x16/system-search.png + ../../data/icons-16x16/text-x-generic.png + ../../data/icons-16x16/utilities-terminal.png + ../../data/icons-16x16/video-display.png + ../../data/icons-16x16/view-refresh.png + + diff --git a/mednafen/snes/src/ui_qt/resource/resource.rc b/mednafen/snes/src/ui_qt/resource/resource.rc new file mode 100755 index 0000000..63dfef4 --- /dev/null +++ b/mednafen/snes/src/ui_qt/resource/resource.rc @@ -0,0 +1,2 @@ +1 24 "data/bsnes.Manifest" +IDI_ICON1 ICON DISCARDABLE "data/bsnes.ico" diff --git a/mednafen/snes/src/ui_qt/settings/advanced.cpp b/mednafen/snes/src/ui_qt/settings/advanced.cpp new file mode 100755 index 0000000..f28fb2f --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/advanced.cpp @@ -0,0 +1,192 @@ +#include "advanced.moc" +AdvancedSettingsWindow *advancedSettingsWindow; + +AdvancedSettingsWindow::AdvancedSettingsWindow() { + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(0); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + driverLayout = new QGridLayout; + driverLayout->setHorizontalSpacing(Style::WidgetSpacing); + layout->addLayout(driverLayout); + layout->addSpacing(Style::WidgetSpacing); + + videoLabel = new QLabel("Video driver:"); + driverLayout->addWidget(videoLabel, 0, 0); + + audioLabel = new QLabel("Audio driver:"); + driverLayout->addWidget(audioLabel, 0, 1); + + inputLabel = new QLabel("Input driver:"); + driverLayout->addWidget(inputLabel, 0, 2); + + videoDriver = new QComboBox; + driverLayout->addWidget(videoDriver, 1, 0); + + audioDriver = new QComboBox; + driverLayout->addWidget(audioDriver, 1, 1); + + inputDriver = new QComboBox; + driverLayout->addWidget(inputDriver, 1, 2); + + driverInfo = new QLabel("Note: driver changes require restart to take effect."); + driverInfo->setStyleSheet("margin-left: -3px; margin-top: 5px;"); + driverLayout->addWidget(driverInfo, 2, 0, 1, 3); + + regionTitle = new QLabel("Hardware region:"); + layout->addWidget(regionTitle); + + regionLayout = new QHBoxLayout; + regionLayout->setSpacing(Style::WidgetSpacing); + layout->addLayout(regionLayout); + layout->addSpacing(Style::WidgetSpacing); + + regionGroup = new QButtonGroup(this); + + regionAuto = new QRadioButton("Auto-detect"); + regionAuto->setToolTip("Automatically select hardware region on cartridge load"); + regionGroup->addButton(regionAuto); + regionLayout->addWidget(regionAuto); + + regionNTSC = new QRadioButton("NTSC"); + regionNTSC->setToolTip("Force NTSC region (Japan, Korea, US)"); + regionGroup->addButton(regionNTSC); + regionLayout->addWidget(regionNTSC); + + regionPAL = new QRadioButton("PAL"); + regionPAL->setToolTip("Force PAL region (Europe, ...)"); + regionGroup->addButton(regionPAL); + regionLayout->addWidget(regionPAL); + + portTitle = new QLabel("Expansion port device:"); + layout->addWidget(portTitle); + + portLayout = new QHBoxLayout; + portLayout->setSpacing(Style::WidgetSpacing); + layout->addLayout(portLayout); + layout->addSpacing(Style::WidgetSpacing); + + portGroup = new QButtonGroup(this); + + portSatellaview = new QRadioButton("Satellaview"); + portGroup->addButton(portSatellaview); + portLayout->addWidget(portSatellaview); + + portNone = new QRadioButton("None"); + portGroup->addButton(portNone); + portLayout->addWidget(portNone); + + portSpacer = new QWidget; + portLayout->addWidget(portSpacer); + + focusTitle = new QLabel("When main window does not have focus:"); + layout->addWidget(focusTitle); + + focusLayout = new QHBoxLayout; + focusLayout->setSpacing(Style::WidgetSpacing); + layout->addLayout(focusLayout); + layout->addSpacing(Style::WidgetSpacing); + + focusButtonGroup = new QButtonGroup(this); + + focusPause = new QRadioButton("Pause emulation"); + focusPause->setToolTip("Ideal for prolonged multi-tasking"); + focusButtonGroup->addButton(focusPause); + focusLayout->addWidget(focusPause); + + focusIgnore = new QRadioButton("Ignore input"); + focusIgnore->setToolTip("Ideal for light multi-tasking when using keyboard"); + focusButtonGroup->addButton(focusIgnore); + focusLayout->addWidget(focusIgnore); + + focusAllow = new QRadioButton("Allow input"); + focusAllow->setToolTip("Ideal for light multi-tasking when using joypad(s)"); + focusButtonGroup->addButton(focusAllow); + focusLayout->addWidget(focusAllow); + + rewindTitle = new QLabel("Rewind support:"); + layout->addWidget(rewindTitle); + + rewindEnable = new QCheckBox("Enable"); + layout->addWidget(rewindEnable); + + initializeUi(); + + connect(videoDriver, SIGNAL(currentIndexChanged(int)), this, SLOT(videoDriverChange(int))); + connect(audioDriver, SIGNAL(currentIndexChanged(int)), this, SLOT(audioDriverChange(int))); + connect(inputDriver, SIGNAL(currentIndexChanged(int)), this, SLOT(inputDriverChange(int))); + connect(regionAuto, SIGNAL(pressed()), this, SLOT(setRegionAuto())); + connect(regionNTSC, SIGNAL(pressed()), this, SLOT(setRegionNTSC())); + connect(regionPAL, SIGNAL(pressed()), this, SLOT(setRegionPAL())); + connect(portSatellaview, SIGNAL(pressed()), this, SLOT(setPortSatellaview())); + connect(portNone, SIGNAL(pressed()), this, SLOT(setPortNone())); + connect(focusPause, SIGNAL(pressed()), this, SLOT(pauseWithoutFocus())); + connect(focusIgnore, SIGNAL(pressed()), this, SLOT(ignoreInputWithoutFocus())); + connect(focusAllow, SIGNAL(pressed()), this, SLOT(allowInputWithoutFocus())); + connect(rewindEnable, SIGNAL(stateChanged(int)), this, SLOT(toggleRewindEnable())); +} + +void AdvancedSettingsWindow::initializeUi() { + lstring part; + + part.split(";", video.driver_list()); + for(unsigned i = 0; i < part.size(); i++) { + videoDriver->addItem(part[i]); + if(part[i] == config().system.video) videoDriver->setCurrentIndex(i); + } + + part.split(";", audio.driver_list()); + for(unsigned i = 0; i < part.size(); i++) { + audioDriver->addItem(part[i]); + if(part[i] == config().system.audio) audioDriver->setCurrentIndex(i); + } + + part.split(";", input.driver_list()); + for(unsigned i = 0; i < part.size(); i++) { + inputDriver->addItem(part[i]); + if(part[i] == config().system.input) inputDriver->setCurrentIndex(i); + } + + regionAuto->setChecked(SNES::config.region == SNES::System::Autodetect); + regionNTSC->setChecked(SNES::config.region == SNES::System::NTSC); + regionPAL->setChecked (SNES::config.region == SNES::System::PAL); + + portSatellaview->setChecked(SNES::config.expansion_port == SNES::System::ExpansionBSX); + portNone->setChecked (SNES::config.expansion_port == SNES::System::ExpansionNone); + + focusPause->setChecked (config().input.focusPolicy == Configuration::Input::FocusPolicyPauseEmulation); + focusIgnore->setChecked(config().input.focusPolicy == Configuration::Input::FocusPolicyIgnoreInput); + focusAllow->setChecked (config().input.focusPolicy == Configuration::Input::FocusPolicyAllowInput); + + rewindEnable->setChecked(config().system.rewindEnabled); +} + +void AdvancedSettingsWindow::videoDriverChange(int index) { + if(index >= 0) config().system.video = videoDriver->itemText(index).toUtf8().data(); +} + +void AdvancedSettingsWindow::audioDriverChange(int index) { + if(index >= 0) config().system.audio = audioDriver->itemText(index).toUtf8().data(); +} + +void AdvancedSettingsWindow::inputDriverChange(int index) { + if(index >= 0) config().system.input = inputDriver->itemText(index).toUtf8().data(); +} + +void AdvancedSettingsWindow::setRegionAuto() { SNES::config.region = SNES::System::Autodetect; } +void AdvancedSettingsWindow::setRegionNTSC() { SNES::config.region = SNES::System::NTSC; } +void AdvancedSettingsWindow::setRegionPAL() { SNES::config.region = SNES::System::PAL; } + +void AdvancedSettingsWindow::setPortSatellaview() { SNES::config.expansion_port = SNES::System::ExpansionBSX; } +void AdvancedSettingsWindow::setPortNone() { SNES::config.expansion_port = SNES::System::ExpansionNone; } + +void AdvancedSettingsWindow::pauseWithoutFocus() { config().input.focusPolicy = Configuration::Input::FocusPolicyPauseEmulation; } +void AdvancedSettingsWindow::ignoreInputWithoutFocus() { config().input.focusPolicy = Configuration::Input::FocusPolicyIgnoreInput; } +void AdvancedSettingsWindow::allowInputWithoutFocus() { config().input.focusPolicy = Configuration::Input::FocusPolicyAllowInput; } + +void AdvancedSettingsWindow::toggleRewindEnable() { + config().system.rewindEnabled = rewindEnable->isChecked(); + state.resetHistory(); +} diff --git a/mednafen/snes/src/ui_qt/settings/advanced.moc.hpp b/mednafen/snes/src/ui_qt/settings/advanced.moc.hpp new file mode 100755 index 0000000..42fd41a --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/advanced.moc.hpp @@ -0,0 +1,58 @@ +class AdvancedSettingsWindow : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + + QGridLayout *driverLayout; + QLabel *videoLabel; + QLabel *audioLabel; + QLabel *inputLabel; + QComboBox *videoDriver; + QComboBox *audioDriver; + QComboBox *inputDriver; + QLabel *driverInfo; + + QLabel *regionTitle; + QHBoxLayout *regionLayout; + QButtonGroup *regionGroup; + QRadioButton *regionAuto; + QRadioButton *regionNTSC; + QRadioButton *regionPAL; + + QLabel *portTitle; + QHBoxLayout *portLayout; + QButtonGroup *portGroup; + QRadioButton *portSatellaview; + QRadioButton *portNone; + QWidget *portSpacer; + + QLabel *focusTitle; + QHBoxLayout *focusLayout; + QButtonGroup *focusButtonGroup; + QRadioButton *focusPause; + QRadioButton *focusIgnore; + QRadioButton *focusAllow; + + QLabel *rewindTitle; + QCheckBox *rewindEnable; + + void initializeUi(); + AdvancedSettingsWindow(); + +public slots: + void videoDriverChange(int index); + void audioDriverChange(int index); + void inputDriverChange(int index); + void setRegionAuto(); + void setRegionNTSC(); + void setRegionPAL(); + void setPortSatellaview(); + void setPortNone(); + void pauseWithoutFocus(); + void ignoreInputWithoutFocus(); + void allowInputWithoutFocus(); + void toggleRewindEnable(); +}; + +extern AdvancedSettingsWindow *advancedSettingsWindow; diff --git a/mednafen/snes/src/ui_qt/settings/audio.cpp b/mednafen/snes/src/ui_qt/settings/audio.cpp new file mode 100755 index 0000000..0b4f4aa --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/audio.cpp @@ -0,0 +1,130 @@ +#include "audio.moc" +AudioSettingsWindow *audioSettingsWindow; + +AudioSettingsWindow::AudioSettingsWindow() { + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + boxes = new QHBoxLayout; + layout->addLayout(boxes); + + frequencyLabel = new QLabel("Frequency:"); + frequencyLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + boxes->addWidget(frequencyLabel); + + frequency = new QComboBox; + frequency->addItem("32000hz"); + frequency->addItem("44100hz"); + frequency->addItem("48000hz"); + frequency->addItem("96000hz"); + boxes->addWidget(frequency); + + latencyLabel = new QLabel("Latency:"); + latencyLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + boxes->addWidget(latencyLabel); + + latency = new QComboBox; + latency->addItem("20ms"); + latency->addItem("40ms"); + latency->addItem("60ms"); + latency->addItem("80ms"); + latency->addItem("100ms"); + latency->addItem("120ms"); + boxes->addWidget(latency); + + sliders = new QGridLayout; + layout->addLayout(sliders); + + volumeLabel = new QLabel("Volume:"); + volumeLabel->setToolTip("Warning: any volume other than 100% will result in a slight audio quality loss"); + sliders->addWidget(volumeLabel, 0, 0); + + volumeValue = new QLabel; + volumeValue->setAlignment(Qt::AlignHCenter); + volumeValue->setMinimumWidth(volumeValue->fontMetrics().width("262144hz")); + sliders->addWidget(volumeValue, 0, 1); + + volume = new QSlider(Qt::Horizontal); + volume->setMinimum(0); + volume->setMaximum(200); + sliders->addWidget(volume, 0, 2); + + frequencySkewLabel = new QLabel("Input frequency:"); + frequencySkewLabel->setToolTip( + "Adjusts audio resampling rate.\n" + "When both video sync and audio sync are enabled, use this setting to fine-tune the output.\n" + "Lower the input frequency to clean audio output, eliminating crackling / popping.\n" + "Raise the input frequency to smooth video output, eliminating duplicated frames." + ); + sliders->addWidget(frequencySkewLabel, 1, 0); + + frequencySkewValue = new QLabel; + frequencySkewValue->setAlignment(Qt::AlignHCenter); + sliders->addWidget(frequencySkewValue, 1, 1); + + frequencySkew = new QSlider(Qt::Horizontal); + frequencySkew->setMinimum(31500); + frequencySkew->setMaximum(32500); + sliders->addWidget(frequencySkew, 1, 2); + + connect(frequency, SIGNAL(currentIndexChanged(int)), this, SLOT(frequencyChange(int))); + connect(latency, SIGNAL(currentIndexChanged(int)), this, SLOT(latencyChange(int))); + connect(volume, SIGNAL(valueChanged(int)), this, SLOT(volumeAdjust(int))); + connect(frequencySkew, SIGNAL(valueChanged(int)), this, SLOT(frequencySkewAdjust(int))); + + syncUi(); +} + +void AudioSettingsWindow::syncUi() { + int n; + + n = config().audio.outputFrequency; + if(n <= 32000) frequency->setCurrentIndex(0); + else if(n <= 44100) frequency->setCurrentIndex(1); + else if(n <= 48000) frequency->setCurrentIndex(2); + else if(n <= 96000) frequency->setCurrentIndex(3); + else frequency->setCurrentIndex(0); + + n = config().audio.latency; + latency->setCurrentIndex((n - 20) / 20); + + n = config().audio.volume; + volumeValue->setText(string() << n << "%"); + volume->setSliderPosition(n); + + n = config().audio.inputFrequency; + frequencySkewValue->setText(string() << n << "hz"); + frequencySkew->setSliderPosition(n); +} + +void AudioSettingsWindow::frequencyChange(int value) { + switch(value) { default: + case 0: config().audio.outputFrequency = 32000; break; + case 1: config().audio.outputFrequency = 44100; break; + case 2: config().audio.outputFrequency = 48000; break; + case 3: config().audio.outputFrequency = 96000; break; + } + audio.set(Audio::Frequency, config().audio.outputFrequency); + utility.updateEmulationSpeed(); +} + +void AudioSettingsWindow::latencyChange(int value) { + value = max(0, min(5, value)); + config().audio.latency = 20 + value * 20; + audio.set(Audio::Latency, config().audio.latency); +} + +void AudioSettingsWindow::volumeAdjust(int value) { + config().audio.volume = value; + audio.set(Audio::Volume, config().audio.volume); + syncUi(); +} + +void AudioSettingsWindow::frequencySkewAdjust(int value) { + config().audio.inputFrequency = value; + utility.updateEmulationSpeed(); + syncUi(); +} diff --git a/mednafen/snes/src/ui_qt/settings/audio.moc.hpp b/mednafen/snes/src/ui_qt/settings/audio.moc.hpp new file mode 100755 index 0000000..24b42a9 --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/audio.moc.hpp @@ -0,0 +1,29 @@ +class AudioSettingsWindow : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + QHBoxLayout *boxes; + QLabel *frequencyLabel; + QComboBox *frequency; + QLabel *latencyLabel; + QComboBox *latency; + QGridLayout *sliders; + QLabel *volumeLabel; + QLabel *volumeValue; + QSlider *volume; + QLabel *frequencySkewLabel; + QLabel *frequencySkewValue; + QSlider *frequencySkew; + + void syncUi(); + AudioSettingsWindow(); + +public slots: + void frequencyChange(int value); + void latencyChange(int value); + void volumeAdjust(int value); + void frequencySkewAdjust(int value); +}; + +extern AudioSettingsWindow *audioSettingsWindow; diff --git a/mednafen/snes/src/ui_qt/settings/input.cpp b/mednafen/snes/src/ui_qt/settings/input.cpp new file mode 100755 index 0000000..3698ccc --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/input.cpp @@ -0,0 +1,253 @@ +#include "input.moc" +InputSettingsWindow *inputSettingsWindow; + +InputSettingsWindow::InputSettingsWindow() { + activeInput = 0; + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + list = new QTreeWidget; + list->setColumnCount(2); + list->setAllColumnsShowFocus(true); + list->setSortingEnabled(false); + list->header()->hide(); + list->header()->setResizeMode(QHeaderView::ResizeToContents); + layout->addWidget(list); + + controlLayout = new QHBoxLayout; + layout->addLayout(controlLayout); + + message = new QLabel; + message->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + controlLayout->addWidget(message); + + optionButton = new QPushButton("Options"); + controlLayout->addWidget(optionButton); + + optionMenu = new QMenu; + optionButton->setMenu(optionMenu); + + optionAssignModifiers = new QbCheckAction("Assign Modifiers as Keys", 0); + optionMenu->addAction(optionAssignModifiers); + + assignButton = new QPushButton("Assign"); + controlLayout->addWidget(assignButton); + + unassignButton = new QPushButton("Unassign"); + controlLayout->addWidget(unassignButton); + + connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(synchronize())); + connect(list, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(assign())); + connect(assignButton, SIGNAL(released()), this, SLOT(assign())); + connect(unassignButton, SIGNAL(released()), this, SLOT(unassign())); + + connect(optionAssignModifiers, SIGNAL(triggered()), this, SLOT(toggleAssignModifiers())); + + //initialize list + + port1 = new QTreeWidgetItem(list); + port1->setData(0, Qt::UserRole, QVariant(-1)); + port1->setText(0, "Controller Port 1"); + + port2 = new QTreeWidgetItem(list); + port2->setData(0, Qt::UserRole, QVariant(-1)); + port2->setText(0, "Controller Port 2"); + + userInterface = new QTreeWidgetItem(list); + userInterface->setData(0, Qt::UserRole, QVariant(-1)); + userInterface->setText(0, "User Interface"); + + for(unsigned i = 0; i < mapper().size(); i++) { + InputGroup &group = *(mapper()[i]); + + QTreeWidgetItem *grandparent = 0; + if(group.category == InputCategory::Port1) { grandparent = port1; } + if(group.category == InputCategory::Port2) { grandparent = port2; } + if(group.category == InputCategory::UserInterface) { grandparent = userInterface; } + if(!grandparent) continue; + + QTreeWidgetItem *parent = new QTreeWidgetItem(grandparent); + parent->setData(0, Qt::UserRole, QVariant(-1)); + parent->setText(0, group.label); + + for(unsigned i = 0; i < group.size(); i++) { + QTreeWidgetItem *child = new QTreeWidgetItem(parent); + child->setData(0, Qt::UserRole, QVariant(inputTable.size())); + inputTable.add(group[i]); + } + } + + updateList(); + synchronize(); +} + +void InputSettingsWindow::synchronize() { + bool enable = false; + QList items = list->selectedItems(); + if(items.count() > 0) { + QTreeWidgetItem *item = items[0]; + signed index = item->data(0, Qt::UserRole).toInt(); + enable = (index != -1); + } + assignButton->setEnabled(enable); + unassignButton->setEnabled(enable); +} + +void InputSettingsWindow::updateList() { + QList all = list->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < all.size(); i++) { + QTreeWidgetItem *grandparent = all[i]; + for(unsigned j = 0; j < grandparent->childCount(); j++) { + QTreeWidgetItem *parent = grandparent->child(j); + for(unsigned k = 0; k < parent->childCount(); k++) { + QTreeWidgetItem *child = parent->child(k); + signed index = child->data(0, Qt::UserRole).toInt(); + if(index == -1) continue; + MappedInput *input = inputTable[index]; + child->setText(0, input->label); + child->setText(1, string() << "= " << input->name); + child->setForeground(1, QBrush(QColor(128, 128, 128))); + } + } + } +} + +void InputSettingsWindow::setAssignment(string name) { + activeInput->name = name; + activeInput = 0; + list->setFocus(); + message->setText(""); + mapper().bind(); + updateList(); +} + +void InputSettingsWindow::inputEvent(uint16_t scancode) { + if(!activeInput) return; + if(!isActiveWindow() || isMinimized()) return; + int16_t state = mapper().state(scancode); + + if(dynamic_cast(activeInput)) { + if(Keyboard::isAnyKey(scancode) && mapper().state(scancode)) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + //don't map escape key, as it is reserved by the user interface + if(scancode == keyboard(i)[Keyboard::Escape]) return; + } + + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode)); + } else if(Keyboard::isAnyModifier(scancode) && optionAssignModifiers->isChecked()) { + setAssignment(string() << Scancode::encode(scancode)); + } else if(Mouse::isAnyButton(scancode) && mapper().state(scancode)) { + //ensure button was clicked inside list box + unsigned wx = 0, wy = 0; + QWidget *widget = message; + while(widget) { + wx += widget->geometry().x(); + wy += widget->geometry().y(); + widget = widget->parentWidget(); + } + unsigned px = QCursor::pos().x(); + unsigned py = QCursor::pos().y(); + if(px < wx || px >= wx + message->size().width()) return; + if(py < wy || py >= wy + message->size().height()) return; + + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode)); + } else if(Joypad::isAnyHat(scancode)) { + string position; + if(state == Joypad::HatUp) position = ".Up"; + else if(state == Joypad::HatDown) position = ".Down"; + else if(state == Joypad::HatLeft) position = ".Left"; + else if(state == Joypad::HatRight) position = ".Right"; + else return; + + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode) << position); + } else if(Joypad::isAnyAxis(scancode) && mapper().distance(scancode) > 64) { + if(mapper().calibrated == false) { + MappedInput *temp = activeInput; + activeInput = 0; + mapper().calibrate(); + activeInput = temp; + } + + if(mapper().isTrigger[Joypad::numberDecode(scancode)][Joypad::axisDecode(scancode)] == false) { + string position; + if(state < -24576) position = ".Lo"; + else if(state > +24576) position = ".Hi"; + else return; + + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode) << position); + } else { + if(state >= 0) return; + + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode) << ".Trigger"); + } + } else if(Joypad::isAnyButton(scancode) && mapper().state(scancode)) { + setAssignment(string() << mapper().modifierString() << Scancode::encode(scancode)); + } + } else if(dynamic_cast(activeInput)) { + if(Mouse::isAnyButton(scancode)) { + //ensure button was clicked inside list box + unsigned wx = 0, wy = 0; + QWidget *widget = message; + while(widget) { + wx += widget->geometry().x(); + wy += widget->geometry().y(); + widget = widget->parentWidget(); + } + unsigned px = QCursor::pos().x(); + unsigned py = QCursor::pos().y(); + if(px < wx || px >= wx + message->size().width()) return; + if(py < wy || py >= wy + message->size().height()) return; + + unsigned number = Mouse::numberDecode(scancode); + unsigned button = Mouse::buttonDecode(scancode); + if(button == 0) setAssignment(string() << Scancode::encode(mouse(number).axis(0))); + if(button == 2) setAssignment(string() << Scancode::encode(mouse(number).axis(1))); + } else if(Joypad::isAnyAxis(scancode) && mapper().distance(scancode) > 64) { + if(mapper().calibrated == false) { + MappedInput *temp = activeInput; + activeInput = 0; + mapper().calibrate(); + activeInput = temp; + } + + if(mapper().isTrigger[Joypad::numberDecode(scancode)][Joypad::axisDecode(scancode)] == false) { + if(state < -24576 || state > +24576) { + setAssignment(string() << Scancode::encode(scancode)); + } + } + } + } +} + +void InputSettingsWindow::assign() { + QTreeWidgetItem *item = list->currentItem(); + if(!item) return; + signed index = item->data(0, Qt::UserRole).toInt(); + if(index == -1) return; + + //flush any pending events to prevent instantaneous assignment of scancodes + mapper().poll(); + + activeInput = inputTable[index]; + message->setFocus(); + message->setText(string() << "Set assignment for: " << activeInput->label); +} + +void InputSettingsWindow::unassign() { + QTreeWidgetItem *item = list->currentItem(); + if(!item) return; + signed index = item->data(0, Qt::UserRole).toInt(); + if(index == -1) return; + + MappedInput *input = inputTable[index]; + input->name = "None"; + mapper().bind(); + updateList(); +} + +void InputSettingsWindow::toggleAssignModifiers() { + optionAssignModifiers->setChecked(!optionAssignModifiers->isChecked()); +} diff --git a/mednafen/snes/src/ui_qt/settings/input.moc.hpp b/mednafen/snes/src/ui_qt/settings/input.moc.hpp new file mode 100755 index 0000000..1da10ac --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/input.moc.hpp @@ -0,0 +1,33 @@ +class InputSettingsWindow : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTreeWidget *list; + QHBoxLayout *controlLayout; + QLabel *message; + QPushButton *optionButton; + QMenu *optionMenu; + QbCheckAction *optionAssignModifiers; + QPushButton *assignButton; + QPushButton *unassignButton; + + void inputEvent(uint16_t scancode); + InputSettingsWindow(); + +private slots: + void synchronize(); + void assign(); + void unassign(); + void toggleAssignModifiers(); + +private: + QTreeWidgetItem *port1, *port2, *userInterface; + array inputTable; + MappedInput *activeInput; + + void updateList(); + void setAssignment(string); +}; + +extern InputSettingsWindow *inputSettingsWindow; diff --git a/mednafen/snes/src/ui_qt/settings/paths.cpp b/mednafen/snes/src/ui_qt/settings/paths.cpp new file mode 100755 index 0000000..52dbfee --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/paths.cpp @@ -0,0 +1,83 @@ +#include "paths.moc" +PathSettingsWindow *pathSettingsWindow; + +PathSettingWidget::PathSettingWidget(string &pathValue_, const char *labelText, const char *pathDefaultLabel_, const char *pathBrowseLabel_) : pathValue(pathValue_) { + pathDefaultLabel = pathDefaultLabel_; + pathBrowseLabel = pathBrowseLabel_; + + layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + setLayout(layout); + + label = new QLabel(labelText); + layout->addWidget(label); + + controlLayout = new QHBoxLayout; + controlLayout->setSpacing(Style::WidgetSpacing); + layout->addLayout(controlLayout); + + path = new QLineEdit; + path->setReadOnly(true); + controlLayout->addWidget(path); + + pathSelect = new QPushButton("Select ..."); + controlLayout->addWidget(pathSelect); + + pathDefault = new QPushButton("Default"); + controlLayout->addWidget(pathDefault); + + connect(pathSelect, SIGNAL(released()), this, SLOT(selectPath())); + connect(pathDefault, SIGNAL(released()), this, SLOT(defaultPath())); + updatePath(); +} + +void PathSettingWidget::assignPath(string newPath) { + pathValue = string() << newPath << "/"; + updatePath(); +} + +void PathSettingWidget::updatePath() { + if(pathValue == "") { + path->setStyleSheet("color: #808080"); + path->setText(pathDefaultLabel); + } else { + path->setStyleSheet("color: #000000"); + path->setText(pathValue); + } +} + +void PathSettingWidget::selectPath() { + diskBrowser->chooseFolder( + bind(&PathSettingWidget::assignPath, this), + config().path.current.folder, + pathBrowseLabel + ); +} + +void PathSettingWidget::defaultPath() { + pathValue = ""; + updatePath(); +} + +PathSettingsWindow::PathSettingsWindow() { + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + gamePath = new PathSettingWidget(config().path.rom, "Games:", "Remember last path", "Default Game Path"); + savePath = new PathSettingWidget(config().path.save, "Save RAM:", "Same as loaded game", "Default Save RAM Path"); + statePath = new PathSettingWidget(config().path.state, "Save states:", "Same as loaded game", "Default Save State Path"); + patchPath = new PathSettingWidget(config().path.patch, "UPS patches:", "Same as loaded game", "Default UPS Patch Path"); + cheatPath = new PathSettingWidget(config().path.cheat, "Cheat codes:", "Same as loaded game", "Default Cheat Code Path"); + dataPath = new PathSettingWidget(config().path.data, "Exported data:", "Same as loaded game", "Default Exported Data Path"); + + layout->addWidget(gamePath); + layout->addWidget(savePath); + layout->addWidget(statePath); + layout->addWidget(patchPath); + layout->addWidget(cheatPath); + layout->addWidget(dataPath); +} diff --git a/mednafen/snes/src/ui_qt/settings/paths.moc.hpp b/mednafen/snes/src/ui_qt/settings/paths.moc.hpp new file mode 100755 index 0000000..f1608d9 --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/paths.moc.hpp @@ -0,0 +1,40 @@ +class PathSettingWidget : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + QLabel *label; + QHBoxLayout *controlLayout; + QLineEdit *path; + QPushButton *pathSelect; + QPushButton *pathDefault; + + string &pathValue; + string pathDefaultLabel; + string pathBrowseLabel; + void assignPath(string); + void updatePath(); + + PathSettingWidget(string&, const char*, const char*, const char*); + +public slots: + void selectPath(); + void defaultPath(); +}; + +class PathSettingsWindow : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + PathSettingWidget *gamePath; + PathSettingWidget *savePath; + PathSettingWidget *statePath; + PathSettingWidget *patchPath; + PathSettingWidget *cheatPath; + PathSettingWidget *dataPath; + + PathSettingsWindow(); +}; + +extern PathSettingsWindow *pathSettingsWindow; diff --git a/mednafen/snes/src/ui_qt/settings/pixelshader.cpp b/mednafen/snes/src/ui_qt/settings/pixelshader.cpp new file mode 100755 index 0000000..437cd9b --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/pixelshader.cpp @@ -0,0 +1,81 @@ +#include "pixelshader.moc" +PixelShaderWindow *pixelShaderWindow; + +PixelShaderWindow::PixelShaderWindow() { + layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(Style::WidgetSpacing); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + gridLayout = new QGridLayout; + gridLayout->setVerticalSpacing(0); + layout->addLayout(gridLayout); + + fragmentLabel = new QLabel("Fragment shader:"); + gridLayout->addWidget(fragmentLabel, 0, 0, 1, 3); + + fragmentPath = new QLineEdit; + gridLayout->addWidget(fragmentPath, 1, 0); + + fragmentSelect = new QPushButton("Select ..."); + gridLayout->addWidget(fragmentSelect, 1, 1); + + fragmentDefault = new QPushButton("Default"); + gridLayout->addWidget(fragmentDefault, 1, 2); + + vertexLabel = new QLabel("Vertex shader:"); + gridLayout->addWidget(vertexLabel, 2, 0, 1, 3); + + vertexPath = new QLineEdit; + gridLayout->addWidget(vertexPath, 3, 0); + + vertexSelect = new QPushButton("Select ..."); + gridLayout->addWidget(vertexSelect, 3, 1); + + vertexDefault = new QPushButton("Default"); + gridLayout->addWidget(vertexDefault, 3, 2); + + synchronize(); + + connect(fragmentSelect, SIGNAL(released()), this, SLOT(selectFragmentShader())); + connect(vertexSelect, SIGNAL(released()), this, SLOT(selectVertexShader())); + connect(fragmentDefault, SIGNAL(released()), this, SLOT(defaultFragmentShader())); + connect(vertexDefault, SIGNAL(released()), this, SLOT(defaultVertexShader())); +} + +void PixelShaderWindow::synchronize() { + fragmentPath->setText(config().path.fragmentShader); + vertexPath->setText(config().path.vertexShader); +} + +void PixelShaderWindow::selectFragmentShader() { + diskBrowser->chooseFile( + bind(&PixelShaderWindow::assignFragmentShader, this), + config().path.current.shader, + "Select Fragment Shader" + ); +} + +void PixelShaderWindow::selectVertexShader() { + diskBrowser->chooseFile( + bind(&PixelShaderWindow::assignVertexShader, this), + config().path.current.shader, + "Select Vertex Shader" + ); +} + +void PixelShaderWindow::defaultFragmentShader() { assignFragmentShader(""); } +void PixelShaderWindow::defaultVertexShader() { assignVertexShader(""); } + +void PixelShaderWindow::assignFragmentShader(string filename) { + config().path.fragmentShader = filename; + synchronize(); + utility.updatePixelShader(); +} + +void PixelShaderWindow::assignVertexShader(string filename) { + config().path.vertexShader = filename; + synchronize(); + utility.updatePixelShader(); +} diff --git a/mednafen/snes/src/ui_qt/settings/pixelshader.moc.hpp b/mednafen/snes/src/ui_qt/settings/pixelshader.moc.hpp new file mode 100755 index 0000000..fc487fe --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/pixelshader.moc.hpp @@ -0,0 +1,29 @@ +class PixelShaderWindow : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + QGridLayout *gridLayout; + QLabel *fragmentLabel; + QLineEdit *fragmentPath; + QPushButton *fragmentSelect; + QPushButton *fragmentDefault; + QLabel *vertexLabel; + QLineEdit *vertexPath; + QPushButton *vertexSelect; + QPushButton *vertexDefault; + + void synchronize(); + PixelShaderWindow(); + +public slots: + void selectFragmentShader(); + void selectVertexShader(); + void defaultFragmentShader(); + void defaultVertexShader(); + + void assignFragmentShader(string); + void assignVertexShader(string); +}; + +extern PixelShaderWindow *pixelShaderWindow; diff --git a/mednafen/snes/src/ui_qt/settings/settings.cpp b/mednafen/snes/src/ui_qt/settings/settings.cpp new file mode 100755 index 0000000..0891d95 --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/settings.cpp @@ -0,0 +1,36 @@ +#include "../ui-base.hpp" + +#include "pixelshader.cpp" +#include "video.cpp" +#include "audio.cpp" +#include "input.cpp" +#include "paths.cpp" +#include "advanced.cpp" + +#include "settings.moc" +SettingsWindow *settingsWindow; + +SettingsWindow::SettingsWindow() : QbWindow(config().geometry.settingsWindow) { + setObjectName("settings-window"); + setWindowTitle("Configuration Settings"); + resize(600, 360); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + videoSettingsWindow = new VideoSettingsWindow; + audioSettingsWindow = new AudioSettingsWindow; + inputSettingsWindow = new InputSettingsWindow; + pathSettingsWindow = new PathSettingsWindow; + advancedSettingsWindow = new AdvancedSettingsWindow; + + tab = new QTabWidget; + tab->addTab(videoSettingsWindow, QIcon(":/16x16/video-display.png"), "Video"); + tab->addTab(audioSettingsWindow, QIcon(":/16x16/audio-volume-high.png"), "Audio"); + tab->addTab(inputSettingsWindow, QIcon(":/16x16/input-gaming.png"), "Input"); + tab->addTab(pathSettingsWindow, QIcon(":/16x16/folder.png"), "Paths"); + tab->addTab(advancedSettingsWindow, QIcon(":/16x16/preferences-system.png"), "Advanced"); + layout->addWidget(tab); +} diff --git a/mednafen/snes/src/ui_qt/settings/settings.moc.hpp b/mednafen/snes/src/ui_qt/settings/settings.moc.hpp new file mode 100755 index 0000000..1d45704 --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/settings.moc.hpp @@ -0,0 +1,13 @@ +class SettingsWindow : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTabWidget *tab; + + SettingsWindow(); + +public slots: +}; + +extern SettingsWindow *settingsWindow; diff --git a/mednafen/snes/src/ui_qt/settings/video.cpp b/mednafen/snes/src/ui_qt/settings/video.cpp new file mode 100755 index 0000000..e60a28e --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/video.cpp @@ -0,0 +1,133 @@ +#include "video.moc" +VideoSettingsWindow *videoSettingsWindow; + +VideoSettingsWindow::VideoSettingsWindow() { + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + layout->setAlignment(Qt::AlignTop); + setLayout(layout); + + sliders = new QGridLayout; + layout->addLayout(sliders); + + contrastLabel = new QLabel("Contrast adjust:"); + sliders->addWidget(contrastLabel, 0, 0); + + contrastValue = new QLabel; + contrastValue->setAlignment(Qt::AlignHCenter); + contrastValue->setMinimumWidth(contrastValue->fontMetrics().width("+100%")); + sliders->addWidget(contrastValue, 0, 1); + + contrast = new QSlider(Qt::Horizontal); + contrast->setMinimum(-95); + contrast->setMaximum(+95); + sliders->addWidget(contrast, 0, 2); + + brightnessLabel = new QLabel("Brightness adjust:"); + sliders->addWidget(brightnessLabel, 1, 0); + + brightnessValue = new QLabel; + brightnessValue->setAlignment(Qt::AlignHCenter); + sliders->addWidget(brightnessValue, 1, 1); + + brightness = new QSlider(Qt::Horizontal); + brightness->setMinimum(-95); + brightness->setMaximum(+95); + sliders->addWidget(brightness, 1, 2); + + gammaLabel = new QLabel("Gamma adjust:"); + sliders->addWidget(gammaLabel, 2, 0); + + gammaValue = new QLabel; + gammaValue->setAlignment(Qt::AlignHCenter); + sliders->addWidget(gammaValue, 2, 1); + + gamma = new QSlider(Qt::Horizontal); + gamma->setMinimum(-95); + gamma->setMaximum(+95); + sliders->addWidget(gamma, 2, 2); + + scanlineLabel = new QLabel("Scanline adjust:"); + sliders->addWidget(scanlineLabel, 3, 0); + + scanlineValue = new QLabel; + scanlineValue->setAlignment(Qt::AlignHCenter); + sliders->addWidget(scanlineValue, 3, 1); + + scanline = new QSlider(Qt::Horizontal); + scanline->setMinimum(0); + scanline->setMaximum(20); + scanline->setPageStep(4); + sliders->addWidget(scanline, 3, 2); + + options = new QHBoxLayout; + layout->addLayout(options); + + enableGammaRamp = new QCheckBox("Simulate NTSC TV gamma ramp"); + enableGammaRamp->setToolTip("Lower monitor gamma to more accurately match a CRT television"); + options->addWidget(enableGammaRamp); + + pixelShaderWindow = new PixelShaderWindow; + layout->addWidget(pixelShaderWindow); + + connect(contrast, SIGNAL(valueChanged(int)), this, SLOT(contrastAdjust(int))); + connect(brightness, SIGNAL(valueChanged(int)), this, SLOT(brightnessAdjust(int))); + connect(gamma, SIGNAL(valueChanged(int)), this, SLOT(gammaAdjust(int))); + connect(scanline, SIGNAL(valueChanged(int)), this, SLOT(scanlineAdjust(int))); + connect(enableGammaRamp, SIGNAL(stateChanged(int)), this, SLOT(gammaRampToggle(int))); + + syncUi(); +} + +void VideoSettingsWindow::syncUi() { + int n; + + n = config().video.contrastAdjust; + contrastValue->setText(string() << (n > 0 ? "+" : "") << n << "%"); + contrast->setSliderPosition(n); + + n = config().video.brightnessAdjust; + brightnessValue->setText(string() << (n > 0 ? "+" : "") << n << "%"); + brightness->setSliderPosition(n); + + n = config().video.gammaAdjust; + gammaValue->setText(string() << (n > 0 ? "+" : "") << n << "%"); + gamma->setSliderPosition(n); + + n = config().video.scanlineAdjust; + scanlineValue->setText(string() << n << "%"); + scanline->setSliderPosition(n / 5); + + enableGammaRamp->setChecked(config().video.enableGammaRamp); +} + +void VideoSettingsWindow::contrastAdjust(int value) { + config().video.contrastAdjust = value; + syncUi(); + utility.updateColorFilter(); +} + +void VideoSettingsWindow::brightnessAdjust(int value) { + config().video.brightnessAdjust = value; + syncUi(); + utility.updateColorFilter(); +} + +void VideoSettingsWindow::gammaAdjust(int value) { + config().video.gammaAdjust = value; + syncUi(); + utility.updateColorFilter(); +} + +void VideoSettingsWindow::scanlineAdjust(int value) { + config().video.scanlineAdjust = value * 5; + syncUi(); + scanlineFilter.setIntensity(value * 5); +} + +void VideoSettingsWindow::gammaRampToggle(int state) { + config().video.enableGammaRamp = (state == Qt::Checked); + syncUi(); + utility.updateColorFilter(); +} diff --git a/mednafen/snes/src/ui_qt/settings/video.moc.hpp b/mednafen/snes/src/ui_qt/settings/video.moc.hpp new file mode 100755 index 0000000..2343bcc --- /dev/null +++ b/mednafen/snes/src/ui_qt/settings/video.moc.hpp @@ -0,0 +1,33 @@ +class VideoSettingsWindow : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + QGridLayout *sliders; + QLabel *contrastLabel; + QLabel *contrastValue; + QSlider *contrast; + QLabel *brightnessLabel; + QLabel *brightnessValue; + QSlider *brightness; + QLabel *gammaLabel; + QLabel *gammaValue; + QSlider *gamma; + QLabel *scanlineLabel; + QLabel *scanlineValue; + QSlider *scanline; + QHBoxLayout *options; + QCheckBox *enableGammaRamp; + + void syncUi(); + VideoSettingsWindow(); + +public slots: + void contrastAdjust(int); + void brightnessAdjust(int); + void gammaAdjust(int); + void scanlineAdjust(int); + void gammaRampToggle(int); +}; + +extern VideoSettingsWindow *videoSettingsWindow; diff --git a/mednafen/snes/src/ui_qt/state/state.cpp b/mednafen/snes/src/ui_qt/state/state.cpp new file mode 100755 index 0000000..a35c914 --- /dev/null +++ b/mednafen/snes/src/ui_qt/state/state.cpp @@ -0,0 +1,104 @@ +#include "../ui-base.hpp" +State state; + +bool State::save(unsigned slot) { + if(!allowed()) return false; + + SNES::system.runtosave(); + serializer state = SNES::system.serialize(); + + file fp; + bool result = false; + if(fp.open(name(slot), file::mode_write)) { + fp.write(state.data(), state.size()); + fp.close(); + result = true; + } + + if(result) { + utility.showMessage(string() << "State " << (slot + 1) << " saved."); + } else { + utility.showMessage(string() << "Failed to save state " << (slot + 1) << "."); + } + return result; +} + +bool State::load(unsigned slot) { + if(!allowed()) return false; + + file fp; + bool result = false; + if(fp.open(name(slot), file::mode_read)) { + unsigned size = fp.size(); + uint8_t *data = new uint8_t[size]; + fp.read(data, size); + fp.close(); + serializer state(data, size); + delete[] data; + result = SNES::system.unserialize(state); + } + + if(result) { + utility.showMessage(string() << "State " << (slot + 1) << " loaded."); + resetHistory(); + } else { + utility.showMessage(string() << "Failed to load state " << (slot + 1) << "."); + } + return result; +} + +void State::frame() { + if(!allowed()) return; + if(!config().system.rewindEnabled) return; + + //if a full second has passed, automatically capture state + if(++frameCounter >= (SNES::system.region() == SNES::System::NTSC ? 60 : 50)) { + frameCounter = 0; + historyIndex = (historyIndex + 1) % historySize; + historyCount = min(historyCount + 1, historySize); + SNES::system.runtosave(); + history[historyIndex] = SNES::system.serialize(); + } +} + +void State::resetHistory() { + historyIndex = 0; + historyCount = 0; + frameCounter = 0; +} + +bool State::rewind() { + if(!allowed()) return false; + if(!config().system.rewindEnabled) return false; + + if(historyCount == 0) return false; + serializer state(history[historyIndex].data(), history[historyIndex].size()); + bool result = SNES::system.unserialize(state); + historyIndex = (historyIndex + historySize - 1) % historySize; //add historySize to prevent underflow + historyCount--; + return true; +} + +State::State() { + historySize = 120; + history = new serializer[historySize]; + for(unsigned i = 0; i < historySize; i++) history[i] = 0; +} + +State::~State() { + delete[] history; +} + +// + +bool State::allowed() const { + if(!SNES::cartridge.loaded() || !application.power) return false; + if(movie.state != Movie::Inactive) return false; + return cartridge.saveStatesSupported(); +} + +string State::name(unsigned slot) const { + string name = filepath(nall::basename(cartridge.fileName), config().path.state); + name << "-" << (slot + 1) << ".bst"; + return name; +} diff --git a/mednafen/snes/src/ui_qt/state/state.hpp b/mednafen/snes/src/ui_qt/state/state.hpp new file mode 100755 index 0000000..a91513b --- /dev/null +++ b/mednafen/snes/src/ui_qt/state/state.hpp @@ -0,0 +1,24 @@ +class State { +public: + bool save(unsigned); + bool load(unsigned); + + void frame(); + void resetHistory(); + bool rewind(); + + State(); + ~State(); + +private: + serializer *history; + unsigned historySize; + unsigned historyIndex; + unsigned historyCount; + unsigned frameCounter; + + bool allowed() const; + string name(unsigned slot) const; +}; + +extern State state; diff --git a/mednafen/snes/src/ui_qt/tools/cheateditor.cpp b/mednafen/snes/src/ui_qt/tools/cheateditor.cpp new file mode 100755 index 0000000..d0ffd24 --- /dev/null +++ b/mednafen/snes/src/ui_qt/tools/cheateditor.cpp @@ -0,0 +1,211 @@ +#include "cheateditor.moc" +CheatEditorWindow *cheatEditorWindow; + +CheatEditorWindow::CheatEditorWindow() { + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + list = new QTreeWidget; + list->setColumnCount(3); + list->setHeaderLabels(QStringList() << "Slot" << "Code" << "Description"); + list->setColumnWidth(1, list->fontMetrics().width(" 89AB-CDEF+... ")); + list->setAllColumnsShowFocus(true); + list->sortByColumn(0, Qt::AscendingOrder); + list->setRootIsDecorated(false); + list->setSelectionMode(QAbstractItemView::ExtendedSelection); + list->resizeColumnToContents(0); + layout->addWidget(list); + + gridLayout = new QGridLayout; + layout->addLayout(gridLayout); + + codeLabel = new QLabel("Code(s):"); + codeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + gridLayout->addWidget(codeLabel, 0, 0); + + codeEdit = new QLineEdit; + gridLayout->addWidget(codeEdit, 0, 1); + + descLabel = new QLabel("Description:"); + descLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + gridLayout->addWidget(descLabel, 1, 0); + + descEdit = new QLineEdit; + gridLayout->addWidget(descEdit, 1, 1); + + controlLayout = new QHBoxLayout; + controlLayout->setAlignment(Qt::AlignRight); + layout->addLayout(controlLayout); + + clearButton = new QPushButton("Clear Selected"); + controlLayout->addWidget(clearButton); + + synchronize(); + + connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(listChanged())); + connect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(bind())); + connect(codeEdit, SIGNAL(textEdited(const QString&)), this, SLOT(codeEdited())); + connect(descEdit, SIGNAL(textEdited(const QString&)), this, SLOT(descEdited())); + connect(clearButton, SIGNAL(released()), this, SLOT(clearSelected())); +} + +void CheatEditorWindow::synchronize() { + QList items = list->selectedItems(); + if(items.count() == 1) { + descEdit->setEnabled(true); + codeEdit->setEnabled(true); + } else { + descEdit->setText(""); + codeEdit->setText(""); + descEdit->setEnabled(false); + codeEdit->setEnabled(false); + } + clearButton->setEnabled(items.count() > 0); +} + +void CheatEditorWindow::load(const char *filename) { + list->clear(); + list->setSortingEnabled(false); + SNES::cheat.reset(); + + string data; + lstring line; + + if(data.readfile(filename)) { + data.replace("\r", ""); + line.split("\n", data); + } + + for(unsigned i = 0; i < 128; i++) { + lstring part; + if(line.size() > i) part.qsplit(",", line[i]); + for(unsigned n = 0; n <= 2; n++) trim(part[n], " "); + trim(part[2], "\""); + part[2].replace("\\q", "\""); + + QTreeWidgetItem *item = new QTreeWidgetItem(list); + item->setData(0, Qt::UserRole, QVariant(i)); + item->setText(0, string::printf("%3u", i + 1)); + item->setCheckState(0, part[0] == "enabled" ? Qt::Checked : Qt::Unchecked); + item->setText(1, part[1]); + item->setText(2, part[2]); + } + + list->resizeColumnToContents(0); + list->setSortingEnabled(true); + list->header()->setSortIndicatorShown(false); + + bind(); + update(); +} + +void CheatEditorWindow::save(const char *filename) { + bool empty = true; + string data[128]; + + QList items = list->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.count(); i++) { + QTreeWidgetItem *item = items[i]; + unsigned index = item->data(0, Qt::UserRole).toUInt(); + string code = item->text(1).toUtf8().constData(); + string desc = item->text(2).toUtf8().constData(); + desc.replace("\"", "\\q"); + if((code != "") || (desc != "")) empty = false; + + data[index] << (item->checkState(0) == Qt::Checked ? "enabled," : "disabled,"); + data[index] << code << ","; + data[index] << "\"" << desc << "\"\r\n"; + } + + if(empty == true) { + unlink(filename); + } else { + file fp; + if(fp.open(filename, file::mode_write)) { + for(unsigned i = 0; i < 128; i++) fp.print(data[i]); + fp.close(); + } + } + + list->clear(); + SNES::cheat.reset(); + SNES::cheat.synchronize(); +} + +void CheatEditorWindow::update() { + QList items = list->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.count(); i++) { + QTreeWidgetItem *item = items[i]; + string code = item->text(1).toUtf8().constData(); + string desc = item->text(2).toUtf8().constData(); + if((code != "") || (desc != "")) { + item->setForeground(0, QBrush(QColor(0, 0, 0))); + } else { + //highlight empty slots in gray + item->setForeground(0, QBrush(QColor(128, 128, 128))); + } + unsigned index = item->data(0, Qt::UserRole).toUInt(); + if(SNES::cheat[index].addr.size() > 0) { + item->setForeground(1, QBrush(QColor(0, 0, 0))); + } else { + //highlight invalid codes in red + //(this will also highlight empty codes, but as there is no text, it's not an issue) + item->setForeground(1, QBrush(QColor(255, 0, 0))); + } + } +} + +void CheatEditorWindow::bind() { + QList items = list->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.count(); i++) { + QTreeWidgetItem *item = items[i]; + unsigned index = item->data(0, Qt::UserRole).toUInt(); + SNES::cheat[index] = item->text(1).toUtf8().constData(); + SNES::cheat[index].enabled = item->checkState(0) == Qt::Checked; + } + SNES::cheat.synchronize(); +} + +void CheatEditorWindow::listChanged() { + QList items = list->selectedItems(); + if(items.count() > 0) { + QTreeWidgetItem *item = items[0]; + codeEdit->setText(item->text(1)); + descEdit->setText(item->text(2)); + } + synchronize(); +} + +void CheatEditorWindow::codeEdited() { + QList items = list->selectedItems(); + if(items.count() == 1) { + QTreeWidgetItem *item = items[0]; + item->setText(1, codeEdit->text()); + } + bind(); + update(); +} + +void CheatEditorWindow::descEdited() { + QList items = list->selectedItems(); + if(items.count() == 1) { + QTreeWidgetItem *item = items[0]; + item->setText(2, descEdit->text()); + } + update(); +} + +void CheatEditorWindow::clearSelected() { + QList items = list->selectedItems(); + for(unsigned i = 0; i < items.count(); i++) { + QTreeWidgetItem *item = items[i]; + item->setText(1, ""); + item->setText(2, ""); + } + codeEdit->setText(""); + descEdit->setText(""); + bind(); + update(); +} diff --git a/mednafen/snes/src/ui_qt/tools/cheateditor.moc.hpp b/mednafen/snes/src/ui_qt/tools/cheateditor.moc.hpp new file mode 100755 index 0000000..61ce110 --- /dev/null +++ b/mednafen/snes/src/ui_qt/tools/cheateditor.moc.hpp @@ -0,0 +1,30 @@ +class CheatEditorWindow : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTreeWidget *list; + QGridLayout *gridLayout; + QLabel *codeLabel; + QLineEdit *codeEdit; + QLabel *descLabel; + QLineEdit *descEdit; + QHBoxLayout *controlLayout; + QPushButton *clearButton; + + void load(const char *filename); + void save(const char *filename); + void update(); + + void synchronize(); + CheatEditorWindow(); + +private slots: + void bind(); + void listChanged(); + void codeEdited(); + void descEdited(); + void clearSelected(); +}; + +extern CheatEditorWindow *cheatEditorWindow; diff --git a/mednafen/snes/src/ui_qt/tools/cheatfinder.cpp b/mednafen/snes/src/ui_qt/tools/cheatfinder.cpp new file mode 100755 index 0000000..03a4d7a --- /dev/null +++ b/mednafen/snes/src/ui_qt/tools/cheatfinder.cpp @@ -0,0 +1,200 @@ +#include "cheatfinder.moc" +CheatFinderWindow *cheatFinderWindow; + +CheatFinderWindow::CheatFinderWindow() { + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + list = new QTreeWidget; + list->setColumnCount(3); + list->setHeaderLabels(QStringList() << "Address" << "Current Value" << "Previous Value"); + list->setAllColumnsShowFocus(true); + list->sortByColumn(0, Qt::AscendingOrder); + list->setRootIsDecorated(false); + layout->addWidget(list); + + controlLayout = new QGridLayout; + controlLayout->setVerticalSpacing(0); + layout->addLayout(controlLayout); + + sizeLabel = new QLabel("Data size:"); + controlLayout->addWidget(sizeLabel, 0, 0); + + sizeGroup = new QButtonGroup(this); + + size8bit = new QRadioButton("8-bit"); + size8bit->setChecked(true); + sizeGroup->addButton(size8bit); + controlLayout->addWidget(size8bit, 0, 1); + + size16bit = new QRadioButton("16-bit"); + sizeGroup->addButton(size16bit); + controlLayout->addWidget(size16bit, 0, 2); + + size24bit = new QRadioButton("24-bit"); + sizeGroup->addButton(size24bit); + controlLayout->addWidget(size24bit, 0, 3); + + size32bit = new QRadioButton("32-bit"); + sizeGroup->addButton(size32bit); + controlLayout->addWidget(size32bit, 0, 4); + + compareLabel = new QLabel("Compare mode:"); + controlLayout->addWidget(compareLabel, 1, 0); + + compareGroup = new QButtonGroup(this); + + compareEqual = new QRadioButton("Equal to"); + compareEqual->setChecked(true); + compareGroup->addButton(compareEqual); + controlLayout->addWidget(compareEqual, 1, 1); + + compareNotEqual = new QRadioButton("Not equal to"); + compareGroup->addButton(compareNotEqual); + controlLayout->addWidget(compareNotEqual, 1, 2); + + compareLessThan = new QRadioButton("Less than"); + compareGroup->addButton(compareLessThan); + controlLayout->addWidget(compareLessThan, 1, 3); + + compareGreaterThan = new QRadioButton("Greater than"); + compareGroup->addButton(compareGreaterThan); + controlLayout->addWidget(compareGreaterThan, 1, 4); + + valueLabel = new QLabel("Search value:"); + controlLayout->addWidget(valueLabel, 2, 0); + + actionLayout = new QHBoxLayout; + actionLayout->setSpacing(Style::WidgetSpacing); + controlLayout->addLayout(actionLayout, 2, 1, 1, 4); + + valueEdit = new QLineEdit; + actionLayout->addWidget(valueEdit); + + searchButton = new QPushButton("Search"); + actionLayout->addWidget(searchButton); + + resetButton = new QPushButton("Reset"); + actionLayout->addWidget(resetButton); + + connect(valueEdit, SIGNAL(returnPressed()), this, SLOT(searchMemory())); + connect(searchButton, SIGNAL(released()), this, SLOT(searchMemory())); + connect(resetButton, SIGNAL(released()), this, SLOT(resetSearch())); + synchronize(); +} + +void CheatFinderWindow::synchronize() { + if(SNES::cartridge.loaded() == false || application.power == false) { + list->clear(); + for(unsigned n = 0; n < 3; n++) list->resizeColumnToContents(n); + valueEdit->setEnabled(false); + valueEdit->setText(""); + searchButton->setEnabled(false); + resetButton->setEnabled(false); + } else { + valueEdit->setEnabled(true); + searchButton->setEnabled(true); + resetButton->setEnabled(true); + } +} + +void CheatFinderWindow::refreshList() { + list->clear(); + list->setSortingEnabled(false); + + unsigned size = 0; + if(size16bit->isChecked()) size = 1; + if(size24bit->isChecked()) size = 2; + if(size32bit->isChecked()) size = 3; + + for(unsigned i = 0; i < addrList.size() && i < 256; i++) { + QTreeWidgetItem *item = new QTreeWidgetItem(list); + + unsigned addr = addrList[i]; + unsigned data = read(addr, size); + unsigned prev = dataList[i]; + + char temp[256]; + + sprintf(temp, "%.6x", 0x7e0000 + addr); + item->setText(0, temp); + + sprintf(temp, "%u (0x%x)", data, data); + item->setText(1, temp); + + sprintf(temp, "%u (0x%x)", prev, prev); + item->setText(2, temp); + } + + list->setSortingEnabled(true); + list->header()->setSortIndicatorShown(false); + for(unsigned n = 0; n < 3; n++) list->resizeColumnToContents(n); +} + +void CheatFinderWindow::searchMemory() { + unsigned size = 0; + if(size16bit->isChecked()) size = 1; + if(size24bit->isChecked()) size = 2; + if(size32bit->isChecked()) size = 3; + + unsigned data; + string text = valueEdit->text().toUtf8().constData(); + + //auto-detect input data type + if(strbegin(text, "0x")) data = strhex((const char*)text + 2); + else if(strbegin(text, "-")) data = strsigned(text); + else data = strunsigned(text); + + if(addrList.size() == 0) { + //search for the first time: enqueue all possible values so they are all searched + for(unsigned addr = 0; addr < SNES::memory::wram.size(); addr++) { + addrList.add(addr); + dataList.add(read(addr, size)); + } + } + + array newAddrList, newDataList, oldDataList; + + for(unsigned i = 0; i < addrList.size(); i++) { + unsigned thisAddr = addrList[i]; + unsigned thisData = read(thisAddr, size); + + if((compareEqual->isChecked() && thisData == data) + || (compareNotEqual->isChecked() && thisData != data) + || (compareLessThan->isChecked() && thisData < data) + || (compareGreaterThan->isChecked() && thisData > data) + ) { + newAddrList.add(thisAddr); + newDataList.add(thisData); + oldDataList.add(dataList[i]); + } + } + + //first refresh the list with the old data values (for the previous value column) + addrList = newAddrList; + dataList = oldDataList; + refreshList(); + + //and now update the list with the new data values (for the next search) + dataList = newDataList; +} + +void CheatFinderWindow::resetSearch() { + addrList.reset(); + dataList.reset(); + refreshList(); +} + +//size = 0 (8-bit), 1 (16-bit), 2 (24-bit), 3 (32-bit) +unsigned CheatFinderWindow::read(unsigned addr, unsigned size) { + unsigned data = 0; + + for(unsigned n = 0; n <= size; n++) { + if(addr + n >= SNES::memory::wram.size()) break; + data |= SNES::memory::wram[addr + n] << (n << 3); + } + + return data; +} diff --git a/mednafen/snes/src/ui_qt/tools/cheatfinder.moc.hpp b/mednafen/snes/src/ui_qt/tools/cheatfinder.moc.hpp new file mode 100755 index 0000000..69222ce --- /dev/null +++ b/mednafen/snes/src/ui_qt/tools/cheatfinder.moc.hpp @@ -0,0 +1,41 @@ +class CheatFinderWindow : public QWidget { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTreeWidget *list; + QGridLayout *controlLayout; + QLabel *sizeLabel; + QButtonGroup *sizeGroup; + QRadioButton *size8bit; + QRadioButton *size16bit; + QRadioButton *size24bit; + QRadioButton *size32bit; + QLabel *compareLabel; + QButtonGroup *compareGroup; + QRadioButton *compareEqual; + QRadioButton *compareNotEqual; + QRadioButton *compareLessThan; + QRadioButton *compareGreaterThan; + QLabel *valueLabel; + QHBoxLayout *actionLayout; + QLineEdit *valueEdit; + QPushButton *searchButton; + QPushButton *resetButton; + + void synchronize(); + void refreshList(); + CheatFinderWindow(); + +public slots: + void searchMemory(); + void resetSearch(); + +private: + array addrList; + array dataList; + + unsigned read(unsigned addr, unsigned size); +}; + +extern CheatFinderWindow *cheatFinderWindow; diff --git a/mednafen/snes/src/ui_qt/tools/statemanager.cpp b/mednafen/snes/src/ui_qt/tools/statemanager.cpp new file mode 100755 index 0000000..66d99fd --- /dev/null +++ b/mednafen/snes/src/ui_qt/tools/statemanager.cpp @@ -0,0 +1,280 @@ +#include "statemanager.moc" +StateManagerWindow *stateManagerWindow; + +StateManagerWindow::StateManagerWindow() { + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + list = new QTreeWidget; + list->setColumnCount(2); + list->setHeaderLabels(QStringList() << "Slot" << "Description"); + list->setAllColumnsShowFocus(true); + list->sortByColumn(0, Qt::AscendingOrder); + list->setRootIsDecorated(false); + list->resizeColumnToContents(0); + layout->addWidget(list); + + infoLayout = new QHBoxLayout; + layout->addLayout(infoLayout); + + descriptionLabel = new QLabel("Description:"); + infoLayout->addWidget(descriptionLabel); + + descriptionText = new QLineEdit; + infoLayout->addWidget(descriptionText); + + controlLayout = new QHBoxLayout; + layout->addLayout(controlLayout); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + controlLayout->addWidget(spacer); + + loadButton = new QPushButton("Load"); + controlLayout->addWidget(loadButton); + + saveButton = new QPushButton("Save"); + controlLayout->addWidget(saveButton); + + eraseButton = new QPushButton("Erase"); + controlLayout->addWidget(eraseButton); + + connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(synchronize())); + connect(list, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(loadAction())); + connect(descriptionText, SIGNAL(textEdited(const QString&)), this, SLOT(writeDescription())); + connect(loadButton, SIGNAL(released()), this, SLOT(loadAction())); + connect(saveButton, SIGNAL(released()), this, SLOT(saveAction())); + connect(eraseButton, SIGNAL(released()), this, SLOT(eraseAction())); + + synchronize(); +} + +void StateManagerWindow::reload() { + list->clear(); + list->setSortingEnabled(false); + + if(SNES::cartridge.loaded() && cartridge.saveStatesSupported()) { + for(unsigned n = 0; n < StateCount; n++) { + QTreeWidgetItem *item = new QTreeWidgetItem(list); + item->setData(0, Qt::UserRole, QVariant(n)); + char slot[16]; + sprintf(slot, "%2u", n + 1); + item->setText(0, slot); + } + update(); + } + + list->setSortingEnabled(true); + list->header()->setSortIndicatorShown(false); + synchronize(); +} + +void StateManagerWindow::update() { + //iterate all list items + QList items = list->findItems("", Qt::MatchContains); + for(unsigned i = 0; i < items.count(); i++) { + QTreeWidgetItem *item = items[i]; + unsigned n = item->data(0, Qt::UserRole).toUInt(); + if(isStateValid(n) == false) { + item->setForeground(0, QBrush(QColor(128, 128, 128))); + item->setForeground(1, QBrush(QColor(128, 128, 128))); + item->setText(1, "Empty"); + } else { + item->setForeground(0, QBrush(QColor(0, 0, 0))); + item->setForeground(1, QBrush(QColor(0, 0, 0))); + item->setText(1, getStateDescription(n)); + } + } + + for(unsigned n = 0; n <= 1; n++) list->resizeColumnToContents(n); +} + +void StateManagerWindow::synchronize() { + QList items = list->selectedItems(); + if(items.count() > 0) { + QTreeWidgetItem *item = items[0]; + unsigned n = item->data(0, Qt::UserRole).toUInt(); + + if(isStateValid(n)) { + descriptionText->setText(getStateDescription(n)); + descriptionText->setEnabled(true); + loadButton->setEnabled(true); + eraseButton->setEnabled(true); + } else { + descriptionText->setText(""); + descriptionText->setEnabled(false); + loadButton->setEnabled(false); + eraseButton->setEnabled(false); + } + saveButton->setEnabled(true); + } else { + descriptionText->setText(""); + descriptionText->setEnabled(false); + loadButton->setEnabled(false); + saveButton->setEnabled(false); + eraseButton->setEnabled(false); + } +} + +void StateManagerWindow::writeDescription() { + QList items = list->selectedItems(); + if(items.count() > 0) { + QTreeWidgetItem *item = items[0]; + unsigned n = item->data(0, Qt::UserRole).toUInt(); + string description = descriptionText->text().toUtf8().constData(); + setStateDescription(n, description); + update(); + } +} + +void StateManagerWindow::loadAction() { + QList items = list->selectedItems(); + if(items.count() > 0) { + QTreeWidgetItem *item = items[0]; + unsigned n = item->data(0, Qt::UserRole).toUInt(); + loadState(n); + } +} + +void StateManagerWindow::saveAction() { + QList items = list->selectedItems(); + if(items.count() > 0) { + QTreeWidgetItem *item = items[0]; + unsigned n = item->data(0, Qt::UserRole).toUInt(); + saveState(n); + writeDescription(); + synchronize(); + descriptionText->setFocus(); + } +} + +void StateManagerWindow::eraseAction() { + QList items = list->selectedItems(); + if(items.count() > 0) { + QTreeWidgetItem *item = items[0]; + unsigned n = item->data(0, Qt::UserRole).toUInt(); + eraseState(n); + update(); + synchronize(); + } +} + +string StateManagerWindow::filename() const { + string name = filepath(nall::basename(cartridge.fileName), config().path.state); + name << ".bsa"; + return name; +} + +bool StateManagerWindow::isStateValid(unsigned slot) { + if(SNES::cartridge.loaded() == false) return false; + file fp; + if(fp.open(filename(), file::mode_read) == false) return false; + if(fp.size() < (slot + 1) * SNES::system.serialize_size()) { fp.close(); return false; } + fp.seek(slot * SNES::system.serialize_size()); + uint32_t signature = fp.readl(4); + if(signature == 0) { fp.close(); return false; } + uint32_t version = fp.readl(4); + if(version != bsnesSerializerVersion) { fp.close(); return false; } + fp.close(); + return true; +} + +string StateManagerWindow::getStateDescription(unsigned slot) { + if(isStateValid(slot) == false) return ""; + file fp; + fp.open(filename(), file::mode_read); + char description[513]; + fp.seek(slot * SNES::system.serialize_size() + 12); + fp.read((uint8_t*)description, 512); + fp.close(); + description[512] = 0; + return description; +} + +void StateManagerWindow::setStateDescription(unsigned slot, const string &text) { + if(isStateValid(slot) == false) return; + file fp; + fp.open(filename(), file::mode_readwrite); + char description[512]; + memset(&description, 0, sizeof description); + strncpy(description, text, 512); + fp.seek(slot * SNES::system.serialize_size() + 12); + fp.write((uint8_t*)description, 512); + fp.close(); +} + +void StateManagerWindow::loadState(unsigned slot) { + if(isStateValid(slot) == false) return; + file fp; + fp.open(filename(), file::mode_read); + fp.seek(slot * SNES::system.serialize_size()); + unsigned size = SNES::system.serialize_size(); + uint8_t *data = new uint8_t[size]; + fp.read(data, size); + fp.close(); + + serializer state(data, size); + delete[] data; + + if(SNES::system.unserialize(state) == true) { + //toolsWindow->close(); + } +} + +void StateManagerWindow::saveState(unsigned slot) { + file fp; + if(file::exists(filename()) == false) { + //try and create the file, bail out on failure (eg read-only device) + if(fp.open(filename(), file::mode_write) == false) return; + fp.close(); + } + + SNES::system.runtosave(); + serializer state = SNES::system.serialize(); + + fp.open(filename(), file::mode_readwrite); + + //user may save to slot #2 when slot #1 is empty; pad file to current slot if needed + unsigned stateOffset = SNES::system.serialize_size() * slot; + fp.seek(fp.size()); + while(fp.size() < stateOffset) fp.write(0x00); + + fp.seek(stateOffset); + fp.write(state.data(), state.size()); + fp.close(); +} + +void StateManagerWindow::eraseState(unsigned slot) { + if(isStateValid(slot) == false) return; + file fp; + fp.open(filename(), file::mode_readwrite); + unsigned size = SNES::system.serialize_size(); + fp.seek(slot * size); + for(unsigned i = 0; i < size; i++) fp.write(0x00); + fp.close(); + + //shrink state archive as much as possible: + //eg if only slot #2 and slot #31 were valid, but slot #31 was erased, + //file can be resized to only hold blank slot #1 + valid slot #2 + signed lastValidState = -1; + for(signed i = StateCount - 1; i >= 0; i--) { + if(isStateValid(i)) { + lastValidState = i; + break; + } + } + + if(lastValidState == -1) { + //no states used, remove empty file + unlink(filename()); + } else { + unsigned neededFileSize = (lastValidState + 1) * SNES::system.serialize_size(); + file fp; + if(fp.open(filename(), file::mode_readwrite)) { + if(fp.size() > neededFileSize) fp.truncate(neededFileSize); + fp.close(); + } + } +} diff --git a/mednafen/snes/src/ui_qt/tools/statemanager.moc.hpp b/mednafen/snes/src/ui_qt/tools/statemanager.moc.hpp new file mode 100755 index 0000000..be02653 --- /dev/null +++ b/mednafen/snes/src/ui_qt/tools/statemanager.moc.hpp @@ -0,0 +1,40 @@ +class StateManagerWindow : public QWidget { + Q_OBJECT + +public: + enum { StateCount = 32 }; + + QVBoxLayout *layout; + QTreeWidget *list; + QHBoxLayout *infoLayout; + QLabel *descriptionLabel; + QLineEdit *descriptionText; + QHBoxLayout *controlLayout; + QWidget *spacer; + QPushButton *loadButton; + QPushButton *saveButton; + QPushButton *eraseButton; + + void reload(); + void update(); + + StateManagerWindow(); + +public slots: + void synchronize(); + void writeDescription(); + void loadAction(); + void saveAction(); + void eraseAction(); + +private: + string filename() const; + bool isStateValid(unsigned slot); + string getStateDescription(unsigned slot); + void setStateDescription(unsigned slot, const string&); + void loadState(unsigned slot); + void saveState(unsigned slot); + void eraseState(unsigned slot); +}; + +extern StateManagerWindow *stateManagerWindow; diff --git a/mednafen/snes/src/ui_qt/tools/tools.cpp b/mednafen/snes/src/ui_qt/tools/tools.cpp new file mode 100755 index 0000000..f99bbab --- /dev/null +++ b/mednafen/snes/src/ui_qt/tools/tools.cpp @@ -0,0 +1,29 @@ +#include "../ui-base.hpp" + +#include "tools.moc" +ToolsWindow *toolsWindow; + +#include "cheateditor.cpp" +#include "cheatfinder.cpp" +#include "statemanager.cpp" + +ToolsWindow::ToolsWindow() : QbWindow(config().geometry.toolsWindow) { + setObjectName("tools-window"); + setWindowTitle("Tools"); + resize(600, 360); + + layout = new QVBoxLayout; + layout->setMargin(Style::WindowMargin); + layout->setSpacing(Style::WidgetSpacing); + setLayout(layout); + + cheatEditorWindow = new CheatEditorWindow; + cheatFinderWindow = new CheatFinderWindow; + stateManagerWindow = new StateManagerWindow; + + tab = new QTabWidget; + tab->addTab(cheatEditorWindow, QIcon(":/16x16/accessories-text-editor.png"), "Cheat Editor"); + tab->addTab(cheatFinderWindow, QIcon(":/16x16/system-search.png"), "Cheat Finder"); + tab->addTab(stateManagerWindow, QIcon(":/16x16/system-file-manager.png"), "State Manager"); + layout->addWidget(tab); +} diff --git a/mednafen/snes/src/ui_qt/tools/tools.moc.hpp b/mednafen/snes/src/ui_qt/tools/tools.moc.hpp new file mode 100755 index 0000000..80a316b --- /dev/null +++ b/mednafen/snes/src/ui_qt/tools/tools.moc.hpp @@ -0,0 +1,13 @@ +class ToolsWindow : public QbWindow { + Q_OBJECT + +public: + QVBoxLayout *layout; + QTabWidget *tab; + + ToolsWindow(); + +public slots: +}; + +extern ToolsWindow *toolsWindow; diff --git a/mednafen/snes/src/ui_qt/ui-base.hpp b/mednafen/snes/src/ui_qt/ui-base.hpp new file mode 100755 index 0000000..fb40460 --- /dev/null +++ b/mednafen/snes/src/ui_qt/ui-base.hpp @@ -0,0 +1,92 @@ +#define UNICODE +#define QT_NO_DEBUG +#define QT_CORE_LIB +#define QT_GUI_LIB +#define QT_THREAD_SUPPORT + +#include +#include +//Q_IMPORT_PLUGIN(QJpegPlugin) +//Q_IMPORT_PLUGIN(QMngPlugin) + +#include <../base.hpp> + +#include +#include +#include +#include +using namespace nall; + +#include +using namespace ruby; + +#include "config.hpp" +#include "interface.hpp" + +#include "application/application.moc.hpp" + +#include "base/about.moc.hpp" +#include "base/diskbrowser.moc.hpp" +#include "base/htmlviewer.moc.hpp" +#include "base/loader.moc.hpp" +#include "base/main.moc.hpp" + +#include "cartridge/cartridge.hpp" + +#if defined(DEBUGGER) + #include "debugger/debugger.moc.hpp" + #include "debugger/hexeditor.moc.hpp" + #include "debugger/tracer.moc.hpp" + + #include "debugger/tools/disassembler.moc.hpp" + #include "debugger/tools/breakpoint.moc.hpp" + #include "debugger/tools/memory.moc.hpp" + #include "debugger/tools/properties.moc.hpp" + + #include "debugger/ppu/layer-toggle.moc.hpp" + #include "debugger/ppu/vram-viewer.moc.hpp" + #include "debugger/ppu/oam-viewer.moc.hpp" + #include "debugger/ppu/cgram-viewer.moc.hpp" + + #include "debugger/misc/debugger-options.moc.hpp" +#endif + +#include "input/input.hpp" + +#include "link/filter.hpp" +#include "link/reader.hpp" + +#include "movie/movie.hpp" + +#include "settings/settings.moc.hpp" +#include "settings/pixelshader.moc.hpp" +#include "settings/video.moc.hpp" +#include "settings/audio.moc.hpp" +#include "settings/input.moc.hpp" +#include "settings/paths.moc.hpp" +#include "settings/advanced.moc.hpp" + +#include "state/state.hpp" + +#include "tools/tools.moc.hpp" +#include "tools/cheateditor.moc.hpp" +#include "tools/cheatfinder.moc.hpp" +#include "tools/statemanager.moc.hpp" + +#include "utility/utility.hpp" + +struct Style { + static const char Monospace[64]; + + enum { + WindowMargin = 5, + WidgetSpacing = 5, + SeparatorSpacing = 5, + }; +}; + +extern string filepath(const char *filename, const char *filepath); + +#if !defined(PLATFORM_WIN) + #define mkdir(path) (mkdir)(path, 0755) +#endif diff --git a/mednafen/snes/src/ui_qt/utility/system-state.cpp b/mednafen/snes/src/ui_qt/utility/system-state.cpp new file mode 100755 index 0000000..642c05d --- /dev/null +++ b/mednafen/snes/src/ui_qt/utility/system-state.cpp @@ -0,0 +1,102 @@ +void Utility::modifySystemState(system_state_t systemState) { + diskBrowser->hide(); //avoid edge case oddities (eg movie playback window still open from previous game) + state.resetHistory(); //do not allow rewinding past a destructive system action + movie.stop(); //movies cannot continue to record after destructive system actions + + video.clear(); + audio.clear(); + + switch(systemState) { + case LoadCartridge: { + //must call cartridge.load_cart_...() before calling modifySystemState(LoadCartridge) + if(SNES::cartridge.loaded() == false) break; + cartridge.loadCheats(); + + application.power = true; + application.pause = false; + SNES::system.power(); + + //warn if unsupported hardware detected + string chip; + if(0); + else if(SNES::cartridge.has_dsp3()) chip = "DSP3"; + else if(SNES::cartridge.has_st011()) chip = "ST011"; + else if(SNES::cartridge.has_st018()) chip = "ST018"; + if(chip != "") { + QMessageBox::warning(mainWindow, "Warning", string() + << "

Warning:
The " << chip << " chip was detected, which is not fully emulated yet.
" + << "It is unlikely that this title will work properly.

"); + } + + showMessage(string() + << "Loaded " << cartridge.name + << (cartridge.patchApplied ? ", and applied UPS patch." : ".")); + mainWindow->setWindowTitle(string() << cartridge.name << " - " << bsnesTitle << " v" << bsnesVersion); + #if defined(DEBUGGER) + debugger->echo(string() << "Loaded " << cartridge.name << ".
"); + #endif + } break; + + case UnloadCartridge: { + if(SNES::cartridge.loaded() == false) break; //no cart to unload? + cartridge.saveCheats(); + + SNES::system.unload(); //flush all memory to memory::* devices + cartridge.saveMemory(); //save memory to disk + SNES::cartridge.unload(); //deallocate memory + + application.power = false; + application.pause = true; + + showMessage(string() << "Unloaded " << cartridge.name << "."); + mainWindow->setWindowTitle(string() << bsnesTitle << " v" << bsnesVersion); + } break; + + case PowerOn: { + if(SNES::cartridge.loaded() == false || application.power == true) break; + + application.power = true; + application.pause = false; + SNES::system.power(); + + showMessage("Power on."); + } break; + + case PowerOff: { + if(SNES::cartridge.loaded() == false || application.power == false) break; + + application.power = false; + application.pause = true; + + showMessage("Power off."); + } break; + + case PowerCycle: { + if(SNES::cartridge.loaded() == false) break; + + application.power = true; + application.pause = false; + SNES::system.power(); + + showMessage("System power was cycled."); + } break; + + case Reset: { + if(SNES::cartridge.loaded() == false || application.power == false) break; + + application.pause = false; + SNES::system.reset(); + + showMessage("System was reset."); + } break; + } + + mainWindow->syncUi(); + #if defined(DEBUGGER) + debugger->modifySystemState(systemState); + debugger->synchronize(); + #endif + cheatEditorWindow->synchronize(); + cheatFinderWindow->synchronize(); + stateManagerWindow->reload(); +} diff --git a/mednafen/snes/src/ui_qt/utility/utility.cpp b/mednafen/snes/src/ui_qt/utility/utility.cpp new file mode 100755 index 0000000..4228d4c --- /dev/null +++ b/mednafen/snes/src/ui_qt/utility/utility.cpp @@ -0,0 +1,146 @@ +Utility utility; + +#include "system-state.cpp" +#include "window.cpp" + +void Utility::inputEvent(uint16_t scancode) { + //release mouse capture if escape key is pressed on any keyboard + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(scancode == keyboard(i)[Keyboard::Escape] && mapper().state(scancode)) { + if(mainWindow->isActive() && input.acquired()) { + input.unacquire(); + return; + } + } + } +} + +//display message in main window statusbar area for three seconds +void Utility::showMessage(const char *message) { + mainWindow->statusBar->showMessage(string() << message, 3000); +} + +//updates system state text at bottom-right of main window statusbar +void Utility::updateSystemState() { + string text; + + if(SNES::cartridge.loaded() == false) { + text = "No cartridge loaded"; + } else if(application.power == false) { + text = "Power off"; + } else if(application.pause == true || application.autopause == true) { + text = "Paused"; + } else if(SNES::ppu.status.frames_updated == true) { + SNES::ppu.status.frames_updated = false; + text << SNES::ppu.status.frames_executed; + text << " fps"; + } else { + //nothing to update + return; + } + + mainWindow->systemState->setText(text); +} + +void Utility::acquireMouse() { + if(SNES::cartridge.loaded()) { + if(SNES::config.controller_port1 == SNES::Input::DeviceMouse + || SNES::config.controller_port2 == SNES::Input::DeviceMouse + || SNES::config.controller_port2 == SNES::Input::DeviceSuperScope + || SNES::config.controller_port2 == SNES::Input::DeviceJustifier + || SNES::config.controller_port2 == SNES::Input::DeviceJustifiers + ) input.acquire(); + } +} + +void Utility::unacquireMouse() { + input.unacquire(); +} + +void Utility::updateAvSync() { + video.set(Video::Synchronize, config().video.synchronize); + audio.set(Audio::Synchronize, config().audio.synchronize); +} + +void Utility::updateVideoMode() { + if(config().video.context->region == 0) { + SNES::video.set_mode(SNES::Video::ModeNTSC); + } else { + SNES::video.set_mode(SNES::Video::ModePAL); + } +} + +void Utility::updateColorFilter() { + filter.contrast = config().video.contrastAdjust; + filter.brightness = config().video.brightnessAdjust; + filter.gamma = 100 + config().video.gammaAdjust; + filter.gamma_ramp = config().video.enableGammaRamp; + filter.colortable_update(); +} + +void Utility::updatePixelShader() { + string filedata; + + if(filedata.readfile(config().path.fragmentShader)) { + video.set(Video::FragmentShader, (const char*)filedata); + } else { + video.set(Video::FragmentShader, (const char*)0); + } + + if(filedata.readfile(config().path.vertexShader)) { + video.set(Video::VertexShader, (const char*)filedata); + } else { + video.set(Video::VertexShader, (const char*)0); + } +} + +void Utility::updateHardwareFilter() { + video.set(Video::Filter, config().video.context->hwFilter); +} + +void Utility::updateSoftwareFilter() { + filter.renderer = config().video.context->swFilter; +} + +void Utility::updateEmulationSpeed() { + config().system.speed = max(0, min(4, (signed)config().system.speed)); + + double scale[] = { + config().system.speedSlowest / 100.0, + config().system.speedSlow / 100.0, + config().system.speedNormal / 100.0, + config().system.speedFast / 100.0, + config().system.speedFastest / 100.0, + }; + unsigned outfreq = config().audio.outputFrequency; + unsigned infreq = config().audio.inputFrequency * scale[config().system.speed] + 0.5; + + audio.set(Audio::Resample, true); //always resample (required for volume adjust + frequency scaler) + audio.set(Audio::ResampleRatio, (double)infreq / (double)outfreq); +} + +void Utility::updateControllers() { + SNES::input.port_set_device(0, SNES::config.controller_port1); + SNES::input.port_set_device(1, SNES::config.controller_port2); + + switch(config().input.port1) { default: + case ControllerPort1::None: mapper().port1 = 0; break; + case ControllerPort1::Gamepad: mapper().port1 = &Controllers::gamepad1; break; + case ControllerPort1::Asciipad: mapper().port1 = &Controllers::asciipad1; break; + case ControllerPort1::Multitap: mapper().port1 = &Controllers::multitap1; break; + case ControllerPort1::Mouse: mapper().port1 = &Controllers::mouse1; break; + } + + switch(config().input.port2) { default: + case ControllerPort2::None: mapper().port2 = 0; break; + case ControllerPort2::Gamepad: mapper().port2 = &Controllers::gamepad2; break; + case ControllerPort2::Asciipad: mapper().port2 = &Controllers::asciipad2; break; + case ControllerPort2::Multitap: mapper().port2 = &Controllers::multitap2; break; + case ControllerPort2::Mouse: mapper().port2 = &Controllers::mouse2; break; + case ControllerPort2::SuperScope: mapper().port2 = &Controllers::superscope; break; + case ControllerPort2::Justifier: mapper().port2 = &Controllers::justifier1; break; + case ControllerPort2::Justifiers: mapper().port2 = &Controllers::justifiers; break; + } + + mainWindow->syncUi(); +} diff --git a/mednafen/snes/src/ui_qt/utility/utility.hpp b/mednafen/snes/src/ui_qt/utility/utility.hpp new file mode 100755 index 0000000..3564f96 --- /dev/null +++ b/mednafen/snes/src/ui_qt/utility/utility.hpp @@ -0,0 +1,39 @@ +class Utility { +public: + //utility.cpp + void inputEvent(uint16_t scancode); + void showMessage(const char *message); + void updateSystemState(); + void acquireMouse(); + void unacquireMouse(); + + void updateAvSync(); + void updateVideoMode(); + void updateColorFilter(); + void updatePixelShader(); + void updateHardwareFilter(); + void updateSoftwareFilter(); + void updateEmulationSpeed(); + void updateControllers(); + + //system-state.cpp + enum system_state_t { LoadCartridge, UnloadCartridge, PowerOn, PowerOff, PowerCycle, Reset }; + void modifySystemState(system_state_t state); + + //window.cpp + void updateFullscreenState(); + void constrainSize(unsigned &x, unsigned &y, unsigned max); + void resizeMainWindow(); + void toggleSynchronizeVideo(); + void toggleSynchronizeAudio(); + void setNtscMode(); + void setPalMode(); + void toggleSmoothVideoOutput(); + void toggleAspectCorrection(); + void setScale(unsigned); + void toggleFullscreen(); + void toggleMenubar(); + void toggleStatusbar(); +}; + +extern Utility utility; diff --git a/mednafen/snes/src/ui_qt/utility/window.cpp b/mednafen/snes/src/ui_qt/utility/window.cpp new file mode 100755 index 0000000..3c48f34 --- /dev/null +++ b/mednafen/snes/src/ui_qt/utility/window.cpp @@ -0,0 +1,158 @@ +void Utility::updateFullscreenState() { + video.clear(); + + if(config().video.isFullscreen == false) { + config().video.context = &config().video.windowed; + mainWindow->showNormal(); + mainWindow->menuBar->setVisible(true); + mainWindow->statusBar->setVisible(true); + } else { + config().video.context = &config().video.fullscreen; + mainWindow->showFullScreen(); + mainWindow->menuBar->setVisible(false); + mainWindow->statusBar->setVisible(false); + } + + QApplication::processEvents(); + #if defined(PLATFORM_X) + //Xlib requires time to propogate fullscreen state change message to window manager; + //if window is resized before this occurs, canvas may not resize correctly + usleep(50000); + #endif + + //refresh options that are unique to each video context + updateVideoMode(); + updateHardwareFilter(); + updateSoftwareFilter(); + mainWindow->syncUi(); +} + +//if max exceeds x: x is set to max, and y is scaled down to keep proportion to x +void Utility::constrainSize(unsigned &x, unsigned &y, unsigned max) { + if(x > max) { + double scalar = (double)max / (double)x; + y = (unsigned)((double)y * (double)scalar); + x = max; + } +} + +void Utility::resizeMainWindow() { + unsigned region = config().video.context->region; + unsigned multiplier = config().video.context->multiplier; + unsigned width = 256 * multiplier; + unsigned height = (region == 0 ? 224 : 239) * multiplier; + + if(config().video.context->correctAspectRatio) { + if(region == 0) { + width = (double)width * config().video.ntscAspectRatio + 0.5; //NTSC adjust + } else { + width = (double)width * config().video.palAspectRatio + 0.5; //PAL adjust + } + } + + if(config().video.isFullscreen == false) { + //get effective desktop work area region (ignore Windows taskbar, OS X dock, etc.) + QRect deskRect = QApplication::desktop()->availableGeometry(mainWindow); + + //ensure window size will not be larger than viewable desktop area + constrainSize(height, width, deskRect.height()); //- frameHeight); + constrainSize(width, height, deskRect.width()); //- frameWidth ); + + mainWindow->canvas->setFixedSize(width, height); + mainWindow->show(); + } else { + for(unsigned i = 0; i < 2; i++) { + unsigned iWidth = width, iHeight = height; + + constrainSize(iHeight, iWidth, mainWindow->canvasContainer->size().height()); + constrainSize(iWidth, iHeight, mainWindow->canvasContainer->size().width()); + + //center canvas onscreen; ensure it is not larger than viewable area + mainWindow->canvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + mainWindow->canvas->setFixedSize(iWidth, iHeight); + mainWindow->canvas->setMinimumSize(0, 0); + + usleep(2000); + QApplication::processEvents(); + } + } + + //workaround for Qt/Xlib bug: + //if window resize occurs with cursor over it, Qt shows Qt::Size*DiagCursor; + //so force it to show Qt::ArrowCursor, as expected + mainWindow->setCursor(Qt::ArrowCursor); + mainWindow->canvasContainer->setCursor(Qt::ArrowCursor); + mainWindow->canvas->setCursor(Qt::ArrowCursor); + + //workaround for DirectSound(?) bug: + //window resizing sometimes breaks audio sync, this call re-initializes it + updateAvSync(); +} + +void Utility::toggleSynchronizeVideo() { + mainWindow->settings_emulationSpeed_syncVideo->toggleChecked(); + config().video.synchronize = mainWindow->settings_emulationSpeed_syncVideo->isChecked(); + updateAvSync(); +} + +void Utility::toggleSynchronizeAudio() { + mainWindow->settings_emulationSpeed_syncAudio->toggleChecked(); + config().audio.synchronize = mainWindow->settings_emulationSpeed_syncAudio->isChecked(); + updateAvSync(); +} + +void Utility::setNtscMode() { + config().video.context->region = 0; + updateVideoMode(); + resizeMainWindow(); + mainWindow->shrink(); + mainWindow->syncUi(); +} + +void Utility::setPalMode() { + config().video.context->region = 1; + updateVideoMode(); + resizeMainWindow(); + mainWindow->shrink(); + mainWindow->syncUi(); +} + +void Utility::toggleSmoothVideoOutput() { + mainWindow->settings_smoothVideo->toggleChecked(); + config().video.context->hwFilter = mainWindow->settings_smoothVideo->isChecked(); + updateHardwareFilter(); + mainWindow->syncUi(); +} + +void Utility::toggleAspectCorrection() { + mainWindow->settings_videoMode_correctAspectRatio->toggleChecked(); + config().video.context->correctAspectRatio = mainWindow->settings_videoMode_correctAspectRatio->isChecked(); + resizeMainWindow(); + mainWindow->shrink(); +} + +void Utility::setScale(unsigned scale) { + config().video.context->multiplier = scale; + resizeMainWindow(); + mainWindow->shrink(); + mainWindow->syncUi(); +} + +void Utility::toggleFullscreen() { + config().video.isFullscreen = !config().video.isFullscreen; + updateFullscreenState(); + resizeMainWindow(); + mainWindow->syncUi(); +} + +void Utility::toggleMenubar() { + mainWindow->menuBar->setVisible(!mainWindow->menuBar->isVisibleTo(mainWindow)); + resizeMainWindow(); + mainWindow->shrink(); +} + +void Utility::toggleStatusbar() { + mainWindow->statusBar->setVisible(!mainWindow->statusBar->isVisibleTo(mainWindow)); + resizeMainWindow(); + mainWindow->shrink(); +} diff --git a/mednafen/sound/Fir_Resampler.cpp b/mednafen/sound/Fir_Resampler.cpp new file mode 100644 index 0000000..4e0a463 --- /dev/null +++ b/mednafen/sound/Fir_Resampler.cpp @@ -0,0 +1,199 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Fir_Resampler.h" + +#include +#include +#include +#include + +/* Copyright (C) 2004-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale, + int count, short* out ) +{ + double const maxh = 256; + double const step = PI / maxh * spacing; + double const to_w = maxh * 2 / width; + double const pow_a_n = pow( rolloff, maxh ); + scale /= maxh * 2; + + double angle = (count / 2 - 1 + offset) * -step; + while ( count-- ) + { + *out++ = 0; + double w = angle * to_w; + if ( fabs( w ) < PI ) + { + double rolloff_cos_a = rolloff * cos( angle ); + double num = 1 - rolloff_cos_a - + pow_a_n * cos( maxh * angle ) + + pow_a_n * rolloff * cos( (maxh - 1) * angle ); + double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + double sinc = scale * num / den - scale; + + out [-1] = (short) (cos( w ) * sinc + sinc); + } + angle += step; + } +} + +Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) : + width_( width ), + write_offset( width * stereo - stereo ), + impulses( impulses_ ) +{ + write_pos = 0; + res = 1; + imp_phase = 0; + skip_bits = 0; + step = stereo; + ratio_ = 1.0; +} + +Fir_Resampler_::~Fir_Resampler_() { } + +void Fir_Resampler_::clear() +{ + imp_phase = 0; + if ( buf.size() ) + { + write_pos = &buf [write_offset]; + memset( buf.begin(), 0, write_offset * sizeof buf [0] ); + } +} + +blargg_err_t Fir_Resampler_::buffer_size( int new_size ) +{ + RETURN_ERR( buf.resize( new_size + write_offset ) ); + clear(); + return 0; +} + +double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain ) +{ + ratio_ = new_factor; + + double fstep = 0.0; + { + double least_error = 2; + double pos = 0; + res = -1; + for ( int r = 1; r <= max_res; r++ ) + { + pos += ratio_; + double nearest = floor( pos + 0.5 ); + double error = fabs( pos - nearest ); + if ( error < least_error ) + { + res = r; + fstep = nearest / res; + least_error = error; + } + } + } + + skip_bits = 0; + + step = stereo * (int) floor( fstep ); + + ratio_ = fstep; + fstep = fmod( fstep, 1.0 ); + + double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; + double pos = 0.0; + input_per_cycle = 0; + for ( int i = 0; i < res; i++ ) + { + gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter, + double (0x7FFF * gain * filter), + (int) width_, impulses + i * width_ ); + + pos += fstep; + input_per_cycle += step; + if ( pos >= 0.9999999 ) + { + pos -= 1.0; + skip_bits |= 1 << i; + input_per_cycle++; + } + } + + clear(); + + return ratio_; +} + +int Fir_Resampler_::input_needed( blargg_long output_count ) const +{ + blargg_long input_count = 0; + + unsigned long skip = skip_bits >> imp_phase; + int remain = res - imp_phase; + while ( (output_count -= 2) > 0 ) + { + input_count += step + (skip & 1) * stereo; + skip >>= 1; + if ( !--remain ) + { + skip = skip_bits; + remain = res; + } + output_count -= 2; + } + + long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]); + if ( input_extra < 0 ) + input_extra = 0; + return input_extra; +} + +int Fir_Resampler_::avail_( blargg_long input_count ) const +{ + int cycle_count = input_count / input_per_cycle; + int output_count = cycle_count * res * stereo; + input_count -= cycle_count * input_per_cycle; + + blargg_ulong skip = skip_bits >> imp_phase; + int remain = res - imp_phase; + while ( input_count >= 0 ) + { + input_count -= step + (skip & 1) * stereo; + skip >>= 1; + if ( !--remain ) + { + skip = skip_bits; + remain = res; + } + output_count += 2; + } + return output_count; +} + +int Fir_Resampler_::skip_input( long count ) +{ + int remain = write_pos - buf.begin(); + int max_count = remain - width_ * stereo; + if ( count > max_count ) + count = max_count; + + remain -= count; + write_pos = &buf [remain]; + memmove( buf.begin(), &buf [count], remain * sizeof buf [0] ); + + return count; +}