mirror of
https://github.com/libretro/picodrive.git
synced 2026-11-07 12:38:05 -10:00
Merge remote-tracking branch 'irixxxx/master' into master
This commit is contained in:
commit
975cdafb61
40 files changed
+1087
-381
No files matched your search
@@ -239,6 +239,7 @@ jobs:
|
||||
run: |
|
||||
git config --global --add safe.directory $PWD
|
||||
ver=$(cut -d'"' -f2 platform/common/version.h)-$(git rev-parse --short HEAD)
|
||||
# toolchain has EGL, but retrofw 2.2 has not, so disable it
|
||||
./configure --platform=retrofw
|
||||
make -j$(getconf _NPROCESSORS_ONLN)
|
||||
mv PicoDrive.opk PicoDrive-retrofw-$ver.opk
|
||||
|
||||
@@ -1,3 +1,221 @@
|
||||
2.05 (2025-05-31)
|
||||
+ added lightgun support for SDL platforms, using the host mouse for Menacer, Justifier, and Light Phaser
|
||||
* improved handling of OpenGL (may enable OpenGL video mode on devices with JZ4770 CPUs)
|
||||
* improved SDL Joystick/Pad support (should now work with what devices are supported by SDL)
|
||||
* improved support for Windows platform
|
||||
* slightly improved SDL window resize handling
|
||||
* slightly improved UI for keyboard settings
|
||||
* fixed major VDP window rendering regression (games using a VDP window with color table != 0)
|
||||
* fixed interlaced mode rendering in ARMv7 assembly
|
||||
* fixed VDP sprite parsing for MD (fixes a test case in SpriteMaskingTestRom)
|
||||
* fixed crash in VDP rendering (DMA pointer wraparound)
|
||||
* fixed pads not initialized correctly after state load
|
||||
* fixed 32X VINT timing
|
||||
* improved 32X interrupt handling (needs work, still not fully as the SH7604 handles it)
|
||||
* improved 32X WDT handling
|
||||
* several 32X CPU snychronisation fixes
|
||||
|
||||
2.04 (2025-04-01)
|
||||
+ added mouse support for SDL platforms, using the host mouse for Sega Mouse, Pico Pen
|
||||
* improved scaling when resizing the window on SDL platforms (Linux, OSX, Windows)
|
||||
+ added preselect window sizes for integer scaling for SDL platforms
|
||||
+ added fullscreen mode for SDL platforms (well, sort of)
|
||||
* attempted to improve pad detection for ps2
|
||||
+ noticeably improved save state accuracy (for retroarch run-ahead)
|
||||
* made the DC filter reacting faster (closer to real MD according to notaz' measurements)
|
||||
* fixed a crash when using MD VDP hscroll in conjunction with 2-cell mode
|
||||
* fixed handling shadow/highlight in MD VDP layer A window (fixes Int. Super Star Soccer)
|
||||
* added toggling the field bit in MD VDP status for interlace modes
|
||||
+ added some support for MD VDP non-linear color DAC (Knuckles Chaotix color test)
|
||||
* fixed MD+ playing binary tracks as audio (fixes a bug in DOOM Fusion)
|
||||
* improve MCD CPU sync (fixes Shadow of the Beast)
|
||||
* fixed 32X handling of MD H32 mode (MD and 32X layer have 4 px offset)
|
||||
* fixed GG region code reading (fixes Pop Breaker)
|
||||
* fixed GG screen in 224 and 240 line mode (fixes Micro Machines)
|
||||
* fixed GG/SMS display off after scanline start (fixes Madou I)
|
||||
|
||||
2.03 (2025-02-15)
|
||||
+ added support for Pico keyboard (thanks @qufb for supplying this, literally years ago. Mea maxima culpa!)
|
||||
+ added support for SC-3000 keyboard and cassette drive
|
||||
+ added configurable mapping for physical keyboard
|
||||
+ added virtual keyboard for devices not having a physical keyboard
|
||||
* improved window handling
|
||||
* fix bug in libretro frontend for Pico pad handling (thanks @theLilaQ)
|
||||
* fixes for PS2 vsync handling (thanks @fjtrujy; this is still not being perfect)
|
||||
* fix a crash for MD when rendering in 2-cell VSRAM mode (thanks @notaz for pointing this out)
|
||||
* more fixes for MD+ (for running Doom 32x Resurrection and Fusion)
|
||||
* fix for 32X screen rendering (for running Sonic Robo Blast 2)
|
||||
* fix handling of the 32X cycles UI option
|
||||
* other minor bug fixes and optimizations
|
||||
|
||||
2.02 (2024-12-29)
|
||||
* fixes for MSU and MD+ (for running Doom 32x Resurrection and Fusion)
|
||||
+ minimal SMS 3D glasses support
|
||||
* updates to libretro common (apparently fixes some obscure bugs)
|
||||
* some CD fixes
|
||||
* fix a cartridge mapping bug for ARM32 (cyclone)
|
||||
* fixes for 32X watchdog timer handling (for running Sonic Robo Blast 2, thanks @notaz)
|
||||
* other minor bug fixes
|
||||
|
||||
2.01 (2024-10-17)
|
||||
+ added windows build
|
||||
+ added integer scaling for SDL window mode
|
||||
+ added support for mode 1
|
||||
+ added support for MD+/32X+
|
||||
* improved CD emulation compatibility
|
||||
+ added support for J-cart games (with 2 additional on-cart controller ports)
|
||||
+ added some more mappings for Mega Drive games
|
||||
+ added PS2 support for multitap and up to 4 controllers
|
||||
* fixes for PS2 and PSP (audio, menu)
|
||||
* other bug fixes
|
||||
|
||||
2.00 (2024-07-19)
|
||||
* improved system detection by extension
|
||||
* improved save/load (Z80, VDP state)
|
||||
* improved some auto mappings for SMS
|
||||
+ added stereo for YM2612 DAC output
|
||||
+ updated libchdr for zstd support
|
||||
* fixes for pandora
|
||||
* changed miyoo build to ipk
|
||||
* switched miyoo menu key to RESET
|
||||
+ added some more MD cartridges
|
||||
* added more keys for some opendingux devices
|
||||
* several fixes for SH2 DRC
|
||||
* several VDP rendering fixes
|
||||
* a lot of other bug fixes (32X reset, Z80 timing, etc.)
|
||||
|
||||
2.00-beta3 (2024-04-28)
|
||||
* bug fixes (VDP reset, Z80 reset)
|
||||
* improvements for Sega/Mega CD
|
||||
|
||||
2.00-beta2 (2024-03-26)
|
||||
+ substantial overhaul of Sega Pico UI
|
||||
supports png images for pad overlays and storyware, new hotkeys, new option (see below)
|
||||
* improved z80 and 68k timing for md and alike
|
||||
* improved timing for 68k access to z80 bus
|
||||
* improved 68k open bus read
|
||||
+ implemented "busy" status bit in ym2612
|
||||
* fixes for big endian CPUs for state save/load
|
||||
|
||||
2.00-beta1 (2024-03-03)
|
||||
+ added ps2 standalone target (thanks to fjtrujy for the fundamentals)
|
||||
* improved psp support
|
||||
* fixed menu RGB/BGR handling
|
||||
* fixed several Z80 related timing inaccuracies
|
||||
* slightly increased volume for CD audio
|
||||
* other bug fixes
|
||||
|
||||
2.00-alpha3 (2024-01-13)
|
||||
* improved pandora layer handling
|
||||
+ added handling of "fantom bitmap" (DMA to background color)
|
||||
+ added SMS option to select TMS bitmap for legacy gfx modes
|
||||
+ added stereo sound support for GG
|
||||
+ overall revision of Pico support (correct sound, better handling of pen, better save/load handling)
|
||||
|
||||
2.00-alpha2 (2023-12-14)
|
||||
* bug fixes and improvements for psp, miyoo, opendingux, pandora
|
||||
+ UI fixes and improvements
|
||||
* other bugfixes
|
||||
|
||||
2.00-alpha1 (2023-11-09)
|
||||
+ improved MD FM sound (DAC ladder effect/clipping, filtering, native rate)
|
||||
+ added support for 32X using MD H32 mode
|
||||
+ added support for MSU-MD (cartridge+audio CD for MD and 32X)
|
||||
+ added basic MD multiplayer support (team player, 4-way play)
|
||||
+ added mapping for some additional Mega Drive games
|
||||
+ added support for both Overdrive demos
|
||||
* improved save/load handling (SVP, MD mapping, MCD/32X cycle counter)
|
||||
+ added more mappers for SMS cartridges
|
||||
+ added some support for GG cartridges running in SMS mode
|
||||
* improved SMS graphics if 1st column is blanked
|
||||
+ added missing TMS9918 modes
|
||||
+ added SG-1000/SC-3000 support (no kbd yet, upcoming)
|
||||
+ added additional handhelds/host systems (miyoo, gkd, rg99, odbeta)
|
||||
* improved handling of files by extension
|
||||
+ revised menu (more consistent options handling, option profiles)
|
||||
+ added dynarec support for PS3 using libretro
|
||||
* add CI to repo (including artifact uploading for the curious)
|
||||
* improved psp support
|
||||
+ revived pandora support
|
||||
* a plethora of bug fixes and minor improvements (also most probably new bugs ;-))
|
||||
|
||||
1.99 (2021-11-14)
|
||||
+ improved Master System support
|
||||
+ added SRAM save support for SMS cartridges
|
||||
+ added a lot of mappers for SMS cartridges
|
||||
+ added Game Gear support
|
||||
+ added Master System TMS9918 modes 0 and 2
|
||||
+ added upscaling for improved aspect ratios
|
||||
* improved Sega/Mega CD support
|
||||
+ added mapping for some additional Mega Drive games
|
||||
+ improvements for libretro support (new menu structure courtesy of jdgleaver)
|
||||
* a load of bug fixes and minor improvements (also most probably new bugs ;-))
|
||||
|
||||
1.98 (2021-04-02)
|
||||
+ CHD support for CD images
|
||||
+ mp3 playback support via minimp3
|
||||
+ big endian host platform support
|
||||
+ sound low pass filtering for more realistic sound (original HW has analog low pass filter)
|
||||
+ menu revision
|
||||
* improvements for 8bit fast rendererer (mainly better PAL support)
|
||||
+ basic support for legacy dingux (somewhat limited) and retrofw devices
|
||||
+ experimental support for PSP (incomplete)
|
||||
* a load of bug fixes and minor improvements (also most probably new bugs ;-))
|
||||
|
||||
1.97 (2020-11-23)
|
||||
+ powerpc support for DRC
|
||||
+ 2x mode in SDL video overlay mode for better color resolution
|
||||
+ FM support for Master System (YM2413) (thanks to @hiroshica for supplying)
|
||||
* bug fixes
|
||||
|
||||
1.96 (2020-05-16)
|
||||
+ added YM2612 SSG-EG emulation
|
||||
* optimisations for SH2 DRC
|
||||
+ added a DC filter to 32X PWM sound to reduce clipping
|
||||
* improved VDP emulation (some Overdrive 2 support)
|
||||
+ completely rewrote VDP FIFO handling for improved timing accuracy
|
||||
+ revived SVP DRC for ARM
|
||||
|
||||
1.95 (2019-11-27)
|
||||
+ added RISC-V code emitter for SH2 DRC
|
||||
* SH2 DRC optimisations
|
||||
|
||||
1.94 (2019-10-12)
|
||||
+ revive Helix Mp3 decoder support for ARM (Helix sources not supplied due to licensing)
|
||||
* update GPH device support to use newer GCC 4.7
|
||||
* ARM specific optimisations (speedup for emulated memory access, 32X draw code, DRC disassemblers)
|
||||
* a lot of SH2 DRC and 32X emulation fixes to improve game compatibility
|
||||
* improved DRC code generation (generated code now noticeably faster)
|
||||
* improved SH2 DRC memory access functions (added 32 bit versions, sign extension)
|
||||
* improved SH2 DRC register caching
|
||||
+ added branch caching to SH2 DRC
|
||||
* optimisations to DRC ARM backend (code reordering, literal pool)
|
||||
+ added polling/idling loop detection and basic loop optimisation to SH2 DRC
|
||||
+ added call/return cache for SH2 DRC
|
||||
+ added cache for communication areas between 32X CPUs
|
||||
+ added T bit caching to ARM SH2 DRC backend (kept in host CPU flags as long as possible if possible)
|
||||
+ added x86_64. MIPS and aarch64 backends to SH2 DRC
|
||||
* 32X DMA emulation optimisation
|
||||
+ revised SH2 DRC branch handling and host code block linking
|
||||
+ added 32X PWM interrupt optimisation (configurable since it may not work with all games)
|
||||
* improved auto frameskipping
|
||||
|
||||
1.93 (2019-01-25)
|
||||
+ implemented SSF2 mapper for 32X
|
||||
+ revived x86 DRC
|
||||
+ added 68K overclocking
|
||||
+ improved FAME/C 68K emulation speed
|
||||
+ support for SMS cheats
|
||||
* 32X DRC bug fixes and improvements
|
||||
* other bug fixes
|
||||
|
||||
1.92 (2017-10-04)
|
||||
+ some support for obscure VDP features (128K, debug register)
|
||||
+ import SPI EEPROM from GPGX
|
||||
* fixes for FAME/C 68k emulation
|
||||
+ added Raspberry Pi 1,2 support
|
||||
+ other bug fixes
|
||||
|
||||
1.91 (2013-10-12)
|
||||
+ Added OpenDingux support (Paul Cercueil).
|
||||
* Save directory changed to ~/.picodrive/ for generic platform build
|
||||
|
||||
@@ -44,7 +44,7 @@ endif
|
||||
|
||||
ifeq "$(ASAN)" "1"
|
||||
CFLAGS += -fsanitize=address -fsanitize=leak -fsanitize=bounds -fno-omit-frame-pointer -fno-common -O1 -g
|
||||
LDLIBS += -fsanitize=address -fsanitize=leak -fsanitize=bounds -static-libasan
|
||||
LDFLAGS += -fsanitize=address -fsanitize=leak -fsanitize=bounds -static-libasan
|
||||
else
|
||||
ifeq "$(DEBUG)" "0"
|
||||
CFLAGS += -O3 -DNDEBUG
|
||||
@@ -173,15 +173,7 @@ use_inputmap ?= 1
|
||||
PLATFORM := generic
|
||||
endif
|
||||
ifeq ("$(PLATFORM)",$(filter "$(PLATFORM)","rpi1" "rpi2"))
|
||||
CFLAGS += -DHAVE_GLES -DRASPBERRY
|
||||
CFLAGS += -I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads/ -I/opt/vc/include/interface/vmcs_host/linux/
|
||||
LDFLAGS += -ldl -lbcm_host -L/opt/vc/lib
|
||||
# Stupid renaming occured in latest raspbian...
|
||||
ifneq (,$(wildcard /opt/vc/lib/libbrcmGLESv2.so))
|
||||
LDFLAGS += -lbrcmEGL -lbrcmGLESv2
|
||||
else
|
||||
LDFLAGS += -lEGL -lGLESv2 # on raspi GLESv1_CM is included in GLESv2
|
||||
endif
|
||||
CFLAGS += -DHAVE_GLES -DEGL_NO_X11 -DSDL_BUFFER_3X -DSDL_REDRAW_EVT
|
||||
OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME
|
||||
OBJS += platform/common/plat_sdl.o platform/common/inputmap_kbd.o
|
||||
OBJS += platform/libpicofe/plat_sdl.o platform/libpicofe/in_sdl.o
|
||||
@@ -189,10 +181,8 @@ OBJS += platform/libpicofe/linux/plat.o
|
||||
USE_FRONTEND = 1
|
||||
endif
|
||||
ifeq "$(PLATFORM)" "generic"
|
||||
#ifeq (y,$(shell echo "\#include <GLES/gl.h>" | $(CC) -E -xc - >/dev/null 2>&1 && echo y))
|
||||
ifeq "$(HAVE_GLES)" "1"
|
||||
CFLAGS += -DHAVE_GLES
|
||||
LDFLAGS += -lEGL -lGLESv1_CM
|
||||
CFLAGS += -DHAVE_GLES -DEGL_NO_X11
|
||||
endif
|
||||
CFLAGS += -DSDL_OVERLAY_2X -DSDL_BUFFER_3X -DSDL_REDRAW_EVT
|
||||
OBJS += platform/linux/emu.o platform/linux/blit.o # FIXME
|
||||
@@ -304,7 +294,7 @@ OBJS += platform/common/main.o platform/common/emu.o platform/common/upscale.o \
|
||||
OBJS += platform/libpicofe/input.o platform/libpicofe/readpng.o \
|
||||
platform/libpicofe/fonts.o
|
||||
ifneq (,$(filter %HAVE_GLES, $(CFLAGS)))
|
||||
OBJS += platform/libpicofe/gl.o platform/libpicofe/gl_platform.o
|
||||
OBJS += platform/libpicofe/gl.o platform/libpicofe/gl_platform.o platform/libpicofe/gl_loader.o
|
||||
endif
|
||||
|
||||
# libpicofe - sound
|
||||
|
||||
@@ -35,11 +35,20 @@ the pages can be found in `platform/base_readme.txt`.
|
||||
|
||||
On all platforms with physical mouse support, PicoDrive can use real mouse
|
||||
input to emulate a Sega Mouse or the Pico Pen. A physical mouse can be operated
|
||||
in either a captured or uncaptured state, selectable via the `Capture mouse` hotkey.
|
||||
Mouse mode can be activated by setting `mouse` as the input device for one
|
||||
of the pads. It depends on the game as to which pad should be used for mouse
|
||||
in either a captured or uncaptured state, selectable via the `Capture mouse`
|
||||
hotkey. Mouse mode can be activated by setting `mouse` as the input device for
|
||||
one of the pads. It depends on the game as to which pad should be used for mouse
|
||||
input. More information is located in `platform/base_readme.txt`.
|
||||
|
||||
### Sega Menacer and Light Phaser and Konami Justifier
|
||||
|
||||
PicoDrive can use a physical mouse for light gun emulation on all platforms
|
||||
with mouse support. This can be activated by selecting an input device of
|
||||
either `light gun` or `justifier`. The `light gun` setting represents the Sega
|
||||
Menacer for the Mega Drive or the Light Phaser for the Master System; it may
|
||||
be attached to either pad input port. The `justifier` is only available on
|
||||
input 2. Supplementary information is provided in `platform/base_readme.txt`.
|
||||
|
||||
### Sega Pico and SC-3000 Keyboards
|
||||
|
||||
PicoDrive provides support for the Pico and SC-3000 keyboards. This can be
|
||||
|
||||
@@ -49,8 +49,7 @@ have_armv7=""
|
||||
have_arm_oabi=""
|
||||
have_arm_neon=""
|
||||
have_libavcodec=""
|
||||
have_libchdr=""
|
||||
have_gles="no"
|
||||
have_gles=""
|
||||
need_sdl="no"
|
||||
need_zlib="no"
|
||||
|
||||
@@ -78,11 +77,9 @@ set_platform()
|
||||
case "$platform" in
|
||||
rpi1)
|
||||
MFLAGS="-mcpu=arm1176jzf-s -mfpu=vfp"
|
||||
have_gles="yes"
|
||||
;;
|
||||
rpi2)
|
||||
MFLAGS="-mcpu=cortex-a7 -mfpu=neon"
|
||||
have_gles="yes"
|
||||
;;
|
||||
generic)
|
||||
MFLAGS=""
|
||||
@@ -217,12 +214,6 @@ if ! test -f "platform/libpicofe/README"; then
|
||||
fail "libpicofe is missing, please run 'git submodule update --init'"
|
||||
fi
|
||||
|
||||
#if [ "$need_warm" = "yes" ]; then
|
||||
# if ! test -f "frontend/warm/README"; then
|
||||
# fail "wARM is missing, please run 'git submodule init && git submodule update'"
|
||||
# fi
|
||||
#fi
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
ARCH=`$CC $MFLAGS $CFLAGS -dumpmachine | awk -F '-' '{print $1}'`
|
||||
fi
|
||||
@@ -359,13 +350,40 @@ EOF
|
||||
compile_object "$@"
|
||||
}
|
||||
|
||||
check_libchdr()
|
||||
check_gles()
|
||||
{
|
||||
# check for VideoCore stuff for Raspberry Pi
|
||||
if [ VIDEOCORE != "no" -a -d /opt/vc/include -a -d /opt/vc/lib ]; then
|
||||
CFLAGS="$CFLAGS -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux"
|
||||
LDLIBS="$LDLIBS -L/opt/vc/lib"
|
||||
if [ -f /opt/vc/lib/libbcm_host.so ]; then
|
||||
LDLIBS="$LDLIBS -lbcm_host"
|
||||
fi
|
||||
VIDEOCORE="yes"
|
||||
fi
|
||||
|
||||
# check for GLES headers
|
||||
cat > $TMPC <<EOF
|
||||
#include <libchdr/chd.h>
|
||||
int main (int argc, char *argv[]) { chd_open("", 0, NULL, NULL); }
|
||||
#define EGL_NO_X11
|
||||
#include <GLES/gl.h>
|
||||
#include <EGL/egl.h>
|
||||
int main(void) {
|
||||
return (int)eglGetDisplay( (EGLNativeDisplayType)0 );
|
||||
}
|
||||
EOF
|
||||
compile_object "$@"
|
||||
if [ "$VIDEOCORE" = "yes" ] && compile_binary $CFLAGS $LDLIBS -lbrcmEGL -lbrcmGLESv2; then
|
||||
have_gles="yes"
|
||||
# LDLIBS="$LDLIBS -lbrcmEGL -lbrcmGLESv2" # non-standard names in old raspbian...
|
||||
elif [ "$VIDEOCORE" = "yes" ] && compile_binary $CFLAGS $LDLIBS -lEGL -lGLESv2; then
|
||||
have_gles="yes"
|
||||
# LDLIBS="$LDLIBS -lEGL -lGLESv2" # on raspi GLESv1_CM is included in GLESv2
|
||||
elif compile_binary $CFLAGS $LDLIBS -lEGL -lGLESv1_CM; then
|
||||
have_gles="yes"
|
||||
# LDLIBS="$LDLIBS -lEGL -lGLESv1_CM"
|
||||
elif compile_binary $CFLAGS $LDLIBS -lEGL -lGLES_CM; then
|
||||
have_gles="yes"
|
||||
# LDLIBS="$LDLIBS -lEGL -lGLES_CM"
|
||||
fi
|
||||
}
|
||||
|
||||
test -n "$SDL_CONFIG" || SDL_CONFIG="$(ls $SYSROOT/*bin*/sdl-config 2>/dev/null | grep /bin/sdl-config | head -n 1)"
|
||||
@@ -375,7 +393,7 @@ test -n "$SDL_CONFIG" || SDL_CONFIG="$(ls $SYSROOT/*/*bin*/sdl-config 2>/dev/nul
|
||||
SDLVERSION=sdl && echo $SDL_CONFIG | grep -q sdl2 && SDLVERSION=sdl2
|
||||
test -n "$SDL_CONFIG" || SDL_CONFIG=false
|
||||
|
||||
MAIN_LDLIBS="$LDLIBS -lm"
|
||||
MAIN_LDLIBS="-lm"
|
||||
|
||||
check_zlib -lz && MAIN_LDLIBS="$MAIN_LDLIBS -lz" || need_zlib="yes"
|
||||
|
||||
@@ -393,11 +411,6 @@ y|Y|yes)
|
||||
*) have_libavcodec="no" ;;
|
||||
esac
|
||||
|
||||
#if check_libchdr; then
|
||||
# have_libchdr="yes"
|
||||
# MAIN_LDLIBS="-lchdr $MAIN_LDLIBS"
|
||||
#fi
|
||||
|
||||
# find what audio support we can compile
|
||||
if [ "x$sound_drivers" = "x" ]; then
|
||||
if check_oss; then sound_drivers="$sound_drivers oss"; fi
|
||||
@@ -423,7 +436,7 @@ if [ "$need_sdl" = "yes" ]; then
|
||||
[ -x "$SDL_CONFIG" ] || \
|
||||
fail "sdl-config is missing; please install libsdl (libsdl1.2-dev)"
|
||||
CFLAGS="$CFLAGS `$SDL_CONFIG --cflags`"
|
||||
MAIN_LDLIBS="`$SDL_CONFIG --libs` $MAIN_LDLIBS"
|
||||
LDLIBS="$LDLIBS `$SDL_CONFIG --libs`"
|
||||
SYSLIBS="$SYSLIBS -ldl"
|
||||
need_dl=yes
|
||||
check_sdl `$SDL_CONFIG --libs` || fail "please install libsdl (libsdl1.2-dev)"
|
||||
@@ -440,6 +453,9 @@ if [ "$need_dl" = "yes" ]; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# check for OpenGLES with SDL
|
||||
[ "$need_sdl" = "yes" -a "$have_gles" != "no" ] && check_gles
|
||||
|
||||
if check_option -Wno-unused_result; then
|
||||
CFLAGS="$CFLAGS -Wno-unused-result"
|
||||
fi
|
||||
@@ -448,17 +464,17 @@ fi
|
||||
test "x$have_armv6" != "x" || have_armv6="no"
|
||||
test "x$have_armv7" != "x" || have_armv7="no"
|
||||
test "x$have_libavcodec" != "x" || have_libavcodec="no"
|
||||
test "x$have_libchdr" != "x" || have_libchdr="no"
|
||||
test "x$have_gles" != "x" || have_gles="no"
|
||||
|
||||
echo "architecture $ARCH"
|
||||
echo "platform $platform"
|
||||
echo "sound drivers $sound_drivers"
|
||||
echo "C compiler $CC"
|
||||
echo "C compiler flags $MFLAGS $CFLAGS"
|
||||
echo "libraries $MAIN_LDLIBS"
|
||||
echo "libraries $LDLIBS $MAIN_LDLIBS"
|
||||
echo "linker flags $LDFLAGS"
|
||||
echo "libavcodec (mp3) $have_libavcodec"
|
||||
#echo "libchdr $have_libchdr"
|
||||
echo "use SDL with GLES $have_gles"
|
||||
# echo "ARMv7 optimizations $have_armv7"
|
||||
|
||||
echo "# Automatically generated by configure" > $config_mak
|
||||
@@ -474,7 +490,7 @@ echo "LD = $LD" >> $config_mak
|
||||
echo "CFLAGS += $MFLAGS $CFLAGS" >> $config_mak
|
||||
echo "ASFLAGS += $MFLAGS $ASFLAGS" >> $config_mak
|
||||
echo "LDFLAGS += $LDFLAGS" >> $config_mak
|
||||
echo "LDLIBS += $MAIN_LDLIBS" >> $config_mak
|
||||
echo "LDLIBS += $LDLIBS $MAIN_LDLIBS" >> $config_mak
|
||||
echo >> $config_mak
|
||||
|
||||
echo "ARCH = $ARCH" >> $config_mak
|
||||
@@ -486,9 +502,6 @@ fi
|
||||
if [ "$have_libavcodec" = "yes" ]; then
|
||||
echo "HAVE_LIBAVCODEC = 1" >> $config_mak
|
||||
fi
|
||||
if [ "$have_libchdr" = "yes" ]; then
|
||||
echo "HAVE_LIBCHDR = 1" >> $config_mak
|
||||
fi
|
||||
if [ "$need_zlib" = "yes" ]; then
|
||||
echo "PLATFORM_ZLIB = 1" >> $config_mak
|
||||
fi
|
||||
|
||||
@@ -4651,7 +4651,6 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
|
||||
FLUSH_CYCLES(sr);
|
||||
rcache_get_reg_arg(0, GET_Rn(), NULL);
|
||||
tmp = emit_memhandler_read(0);
|
||||
sr = rcache_get_reg(SHR_SR, RC_GR_RMW, NULL);
|
||||
emith_clr_t_cond(sr);
|
||||
emith_cmp_r_imm(tmp, 0);
|
||||
emith_set_t_cond(sr, DCOND_EQ);
|
||||
|
||||
@@ -50,7 +50,6 @@ typedef struct SH2_
|
||||
#define SH2_STATE_CPOLL (1 << 2) // polling comm regs
|
||||
#define SH2_STATE_VPOLL (1 << 3) // polling VDP
|
||||
#define SH2_STATE_RPOLL (1 << 4) // polling address in SDRAM
|
||||
#define SH2_TIMER_RUN (1 << 6) // SOC WDT timer is running
|
||||
#define SH2_IN_DRC (1 << 7) // DRC in use
|
||||
unsigned int state;
|
||||
uint32_t poll_addr;
|
||||
|
||||
+54
-33
@@ -123,7 +123,6 @@ void Pico32xStartup(void)
|
||||
|
||||
PicoMemSetup32x();
|
||||
p32x_pwm_ctl_changed();
|
||||
p32x_timers_recalc();
|
||||
|
||||
Pico32x.regs[0] |= P32XS_ADEN;
|
||||
|
||||
@@ -166,6 +165,7 @@ void p32x_reset_sh2s(void)
|
||||
sh2_reset(&ssh2);
|
||||
sh2_peripheral_reset(&msh2);
|
||||
sh2_peripheral_reset(&ssh2);
|
||||
msh2.state = ssh2.state = 0;
|
||||
|
||||
// if we don't have BIOS set, perform it's work here.
|
||||
// MSH2
|
||||
@@ -218,6 +218,7 @@ void Pico32xInit(void)
|
||||
void PicoPower32x(void)
|
||||
{
|
||||
memset(&Pico32x, 0, sizeof(Pico32x));
|
||||
memset(p32x_event_times, 0, sizeof(p32x_event_times));
|
||||
|
||||
Pico32x.regs[0] = P32XS_REN|P32XS_nRES; // verified
|
||||
Pico32x.regs[0x10/2] = 0xffff;
|
||||
@@ -240,11 +241,10 @@ void PicoUnload32x(void)
|
||||
void PicoReset32x(void)
|
||||
{
|
||||
if (PicoIn.AHW & PAHW_32X) {
|
||||
p32x_trigger_irq(NULL, SekCyclesDone(), P32XI_VRES);
|
||||
p32x_sh2_poll_event(msh2.poll_addr, &msh2, SH2_IDLE_STATES, SekCyclesDone());
|
||||
p32x_sh2_poll_event(ssh2.poll_addr, &ssh2, SH2_IDLE_STATES, SekCyclesDone());
|
||||
p32x_pwm_ctl_changed();
|
||||
p32x_timers_recalc();
|
||||
p32x_trigger_irq(NULL, Pico.t.m68c_aim, P32XI_VRES);
|
||||
p32x_m68k_poll_event(0, -1);
|
||||
p32x_sh2_poll_event(msh2.poll_addr, &msh2, SH2_IDLE_STATES, Pico.t.m68c_aim);
|
||||
p32x_sh2_poll_event(ssh2.poll_addr, &ssh2, SH2_IDLE_STATES, Pico.t.m68c_aim);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ void Pico32xDrawSync(SH2 *sh2)
|
||||
// the fast renderer isn't operating on a line-by-line base
|
||||
if (sh2 && !(PicoIn.opt & POPT_ALT_RENDERER)) {
|
||||
unsigned int cycle = (sh2 ? sh2_cycles_done_m68k(sh2) : SekCyclesDone());
|
||||
int line = ((cycle - Pico.t.m68c_frame_start) * (long long)((1LL<<32)/488.5)) >> 32;
|
||||
int line = DIVQ32(cycle - Pico.t.m68c_frame_start, 488.5);
|
||||
|
||||
if (Pico32x.sync_line < line && line < (Pico.video.reg[1] & 8 ? 240 : 224)) {
|
||||
// make sure the MD image is also sync'ed to this line for merging
|
||||
@@ -324,7 +324,7 @@ static void p32x_start_blank(void)
|
||||
Pico32xSwapDRAM(Pico32x.pending_fb ^ P32XV_FS);
|
||||
}
|
||||
|
||||
p32x_trigger_irq(NULL, Pico.t.m68c_aim, P32XI_VINT);
|
||||
p32x_schedule_vint(NULL, Pico.t.m68c_aim);
|
||||
p32x_sh2_poll_event(msh2.poll_addr, &msh2, SH2_STATE_VPOLL, Pico.t.m68c_aim);
|
||||
p32x_sh2_poll_event(ssh2.poll_addr, &ssh2, SH2_STATE_VPOLL, Pico.t.m68c_aim);
|
||||
}
|
||||
@@ -336,7 +336,7 @@ static void p32x_end_blank(void)
|
||||
if ((Pico32x.vdp_regs[0] & P32XV_Mx) != 0) // no forced blanking
|
||||
Pico32x.vdp_regs[0x0a/2] &= ~P32XV_PEN; // no palette access
|
||||
if (!(Pico32x.sh2_regs[0] & 0x80)) {
|
||||
// NB must precede VInt per hw manual, min 4 SH-2 cycles to pass Mars Check
|
||||
// TODO must be early by min 4 SH-2 cycles to pass Mars Check?
|
||||
Pico32x.hint_counter = (int)(-1.5*0x10);
|
||||
p32x_schedule_hint(NULL, Pico.t.m68c_aim);
|
||||
}
|
||||
@@ -349,6 +349,7 @@ void p32x_schedule_hint(SH2 *sh2, unsigned int m68k_cycles)
|
||||
{
|
||||
// rather rough, 32x hint is useless in practice
|
||||
int after;
|
||||
|
||||
if (!((Pico32x.sh2irq_mask[0] | Pico32x.sh2irq_mask[1]) & 4))
|
||||
return; // nobody cares
|
||||
if (!(Pico32x.sh2_regs[0] & 0x80) && (Pico.video.status & PVS_VB2))
|
||||
@@ -363,6 +364,17 @@ void p32x_schedule_hint(SH2 *sh2, unsigned int m68k_cycles)
|
||||
p32x_event_schedule(m68k_cycles, P32X_EVENT_HINT, after);
|
||||
}
|
||||
|
||||
void p32x_schedule_vint(SH2 *sh2, unsigned int m68k_cycles)
|
||||
{
|
||||
// according to 32X hw manual, HInt and VInt are tied to HBLANK and VBLANK.
|
||||
// Moreover, VBLANK is 27 px (224 mclk, 32 68k cycles) after HBLANK.
|
||||
// TODO check if that matches HBLANK/VBLANK on the cartridge slot?
|
||||
if (sh2 != NULL)
|
||||
p32x_event_schedule_sh2(sh2, P32X_EVENT_VINT, 32);
|
||||
else
|
||||
p32x_event_schedule(m68k_cycles, P32X_EVENT_VINT, 32);
|
||||
}
|
||||
|
||||
/* events */
|
||||
static void fillend_event(unsigned int now)
|
||||
{
|
||||
@@ -377,6 +389,21 @@ static void hint_event(unsigned int now)
|
||||
p32x_schedule_hint(NULL, now);
|
||||
}
|
||||
|
||||
static void vint_event(unsigned int now)
|
||||
{
|
||||
p32x_trigger_irq(NULL, now, P32XI_VINT);
|
||||
}
|
||||
|
||||
static void mtimer_event(unsigned int now)
|
||||
{
|
||||
p32x_timer_irq(&msh2, now);
|
||||
}
|
||||
|
||||
static void stimer_event(unsigned int now)
|
||||
{
|
||||
p32x_timer_irq(&ssh2, now);
|
||||
}
|
||||
|
||||
typedef void (event_cb)(unsigned int now);
|
||||
|
||||
/* times are in m68k (7.6MHz) cycles */
|
||||
@@ -386,6 +413,9 @@ static event_cb *p32x_event_cbs[P32X_EVENT_COUNT] = {
|
||||
p32x_pwm_irq_event, // P32X_EVENT_PWM
|
||||
fillend_event, // P32X_EVENT_FILLEND
|
||||
hint_event, // P32X_EVENT_HINT
|
||||
vint_event, // P32X_EVENT_VINT
|
||||
mtimer_event, // P32X_EVENT_MTIMER
|
||||
stimer_event, // P32X_EVENT_STIMER
|
||||
};
|
||||
|
||||
// schedule event at some time 'after', in m68k clocks
|
||||
@@ -407,6 +437,9 @@ void p32x_event_schedule_sh2(SH2 *sh2, enum p32x_event event, int after)
|
||||
unsigned int now = sh2_cycles_done_m68k(sh2);
|
||||
int left_to_next;
|
||||
|
||||
if (sh2->state & SH2_IDLE_STATES)
|
||||
now = SekCyclesDone();
|
||||
|
||||
p32x_event_schedule(now, event, after);
|
||||
|
||||
left_to_next = C_M68K_TO_SH2(sh2, (int)(event_time_next - now));
|
||||
@@ -483,8 +516,10 @@ void p32x_sync_other_sh2(SH2 *sh2, unsigned int m68k_target)
|
||||
int left_to_event;
|
||||
int m68k_cycles;
|
||||
|
||||
if (osh2->state & SH2_STATE_RUN)
|
||||
if (osh2->state & SH2_STATE_RUN) {
|
||||
sh2_end_run(sh2, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
m68k_cycles = m68k_target - osh2->m68krcycles_done;
|
||||
if (m68k_cycles < 200)
|
||||
@@ -512,7 +547,7 @@ void p32x_sync_other_sh2(SH2 *sh2, unsigned int m68k_target)
|
||||
}
|
||||
|
||||
#define STEP_LS 24
|
||||
#define STEP_N 528 // at least one line (488)
|
||||
#define STEP_N 192 // NFL; TODO at least a scanline (489) for good performance?
|
||||
|
||||
#define sync_sh2s_normal p32x_sync_sh2s
|
||||
//#define sync_sh2s_lockstep p32x_sync_sh2s
|
||||
@@ -520,7 +555,7 @@ void p32x_sync_other_sh2(SH2 *sh2, unsigned int m68k_target)
|
||||
/* most timing is in 68k clock */
|
||||
void sync_sh2s_normal(unsigned int m68k_target)
|
||||
{
|
||||
unsigned int now, target, next, timer_cycles;
|
||||
unsigned int now, target, next;
|
||||
int cycles;
|
||||
|
||||
elprintf(EL_32X, "sh2 sync to %u", m68k_target);
|
||||
@@ -533,7 +568,6 @@ void sync_sh2s_normal(unsigned int m68k_target)
|
||||
now = msh2.m68krcycles_done;
|
||||
if (CYCLES_GT(now, ssh2.m68krcycles_done))
|
||||
now = ssh2.m68krcycles_done;
|
||||
timer_cycles = now;
|
||||
|
||||
pprof_start(m68k);
|
||||
while (CYCLES_GT(m68k_target, now))
|
||||
@@ -590,20 +624,7 @@ void sync_sh2s_normal(unsigned int m68k_target)
|
||||
if (!(ssh2.state & SH2_IDLE_STATES))
|
||||
now = ssh2.m68krcycles_done;
|
||||
}
|
||||
if (CYCLES_GT(now, timer_cycles+STEP_N)) {
|
||||
if (msh2.state & SH2_TIMER_RUN)
|
||||
p32x_timer_do(&msh2, now - timer_cycles);
|
||||
if (ssh2.state & SH2_TIMER_RUN)
|
||||
p32x_timer_do(&ssh2, now - timer_cycles);
|
||||
timer_cycles = now;
|
||||
}
|
||||
}
|
||||
|
||||
if (msh2.state & SH2_TIMER_RUN)
|
||||
p32x_timer_do(&msh2, now - timer_cycles);
|
||||
if (ssh2.state & SH2_TIMER_RUN)
|
||||
p32x_timer_do(&ssh2, now - timer_cycles);
|
||||
timer_cycles = now;
|
||||
}
|
||||
pprof_end_sub(m68k);
|
||||
|
||||
@@ -616,9 +637,6 @@ void sync_sh2s_normal(unsigned int m68k_target)
|
||||
if (CYCLES_GT(m68k_target, ssh2.m68krcycles_done))
|
||||
ssh2.m68krcycles_done = m68k_target;
|
||||
}
|
||||
|
||||
// everyone is in sync now
|
||||
Pico32x.comm_dirty = 0;
|
||||
}
|
||||
|
||||
void sync_sh2s_lockstep(unsigned int m68k_target)
|
||||
@@ -663,6 +681,9 @@ void PicoFrame32x(void)
|
||||
Pico.est.rendstatus |= PDRAW_SYNC_NEEDED;
|
||||
PicoFrameHints();
|
||||
|
||||
p32x_timer_do(&msh2, Pico.t.m68c_aim);
|
||||
p32x_timer_do(&ssh2, Pico.t.m68c_aim);
|
||||
|
||||
elprintf(EL_32X, "poll: %02x %02x %02x",
|
||||
Pico32x.emu_flags & 3, msh2.state, ssh2.state);
|
||||
}
|
||||
@@ -692,11 +713,11 @@ void Pico32xStateLoaded(int is_early)
|
||||
|
||||
if (CYCLES_GE(sh2s[0].m68krcycles_done - Pico.t.m68c_aim, 500) ||
|
||||
CYCLES_GE(sh2s[1].m68krcycles_done - Pico.t.m68c_aim, 500))
|
||||
sh2s[0].m68krcycles_done = sh2s[1].m68krcycles_done = SekCyclesDone();
|
||||
p32x_update_irls(NULL, SekCyclesDone());
|
||||
p32x_timers_recalc();
|
||||
sh2s[0].m68krcycles_done = sh2s[1].m68krcycles_done = Pico.t.m68c_aim;
|
||||
p32x_update_irls(NULL, Pico.t.m68c_aim);
|
||||
sh2_peripheral_state_loaded();
|
||||
p32x_pwm_state_loaded();
|
||||
p32x_run_events(SekCyclesDone());
|
||||
p32x_run_events(Pico.t.m68c_aim);
|
||||
|
||||
// TODO wakeup CPUs for now. poll detection stuff must go to the save state!
|
||||
p32x_m68k_poll_event(0, -1);
|
||||
|
||||
@@ -129,7 +129,6 @@ void FinalizeLine32xRGB555(int sh, int line, struct PicoEState *est)
|
||||
unsigned short *dst = est->DrawLineDest;
|
||||
unsigned short *pal = Pico32xMem->pal_native;
|
||||
unsigned char *pmd = est->HighCol + 8;
|
||||
unsigned short *palmd = est->HighPal;
|
||||
unsigned short *dram, *p32x;
|
||||
unsigned char mdbg;
|
||||
int h32 = !(Pico.video.reg[12] & 0x1);
|
||||
|
||||
+23
-18
@@ -198,7 +198,7 @@ static NOINLINE u32 sh2_poll_read(u32 a, u32 d, unsigned int cycles, SH2* sh2)
|
||||
int hix = (a >> 1) % PFIFO_CNT;
|
||||
struct sh2_poll_fifo *fifo = sh2_poll_fifo[hix];
|
||||
struct sh2_poll_fifo *p;
|
||||
int cpu = sh2 ? sh2->is_slave : -1;
|
||||
int cpu = 1 << (sh2 ? sh2->is_slave : 2), age = sh2 ? 60 : 360;
|
||||
unsigned idx;
|
||||
|
||||
a &= ~0x20000000; // ignore writethrough bit
|
||||
@@ -208,14 +208,14 @@ static NOINLINE u32 sh2_poll_read(u32 a, u32 d, unsigned int cycles, SH2* sh2)
|
||||
p = &fifo[idx];
|
||||
idx = (idx+1) % PFIFO_SZ;
|
||||
|
||||
if (cpu != p->cpu) {
|
||||
if (CYCLES_GT(cycles, p->cycles+60)) { // ~180 sh2 cycles, Spiderman
|
||||
if (p->cpu & cpu) {
|
||||
if (CYCLES_GT(cycles, p->cycles+age)) { // ~180 sh2 cycles, Spiderman
|
||||
// drop older fifo stores that may cause synchronisation problems.
|
||||
p->a = -1;
|
||||
p->cpu &= ~cpu;
|
||||
} else if (p->a == a) {
|
||||
// replace current data with fifo value and discard fifo entry
|
||||
d = p->d;
|
||||
p->a = -1;
|
||||
p->cpu &= ~cpu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -228,7 +228,7 @@ static NOINLINE void sh2_poll_write(u32 a, u32 d, unsigned int cycles, SH2 *sh2)
|
||||
int hix = (a >> 1) % PFIFO_CNT;
|
||||
struct sh2_poll_fifo *fifo = sh2_poll_fifo[hix];
|
||||
struct sh2_poll_fifo *q;
|
||||
int cpu = sh2 ? sh2->is_slave : -1;
|
||||
int cpu = 1 << (sh2 ? sh2->is_slave : 2);
|
||||
unsigned rd = sh2_poll_rd[hix], wr = sh2_poll_wr[hix];
|
||||
unsigned idx, nrd;
|
||||
|
||||
@@ -238,8 +238,8 @@ static NOINLINE void sh2_poll_write(u32 a, u32 d, unsigned int cycles, SH2 *sh2)
|
||||
for (idx = nrd = wr; idx != rd; ) {
|
||||
idx = (idx-1) % PFIFO_SZ;
|
||||
q = &fifo[idx];
|
||||
if (q->a == a && q->cpu != cpu) { q->a = -1; }
|
||||
if (q->a != -1) { nrd = idx; }
|
||||
if (q->a == a && (q->cpu & cpu)) { q->cpu = 0; }
|
||||
if (q->cpu) { nrd = idx; }
|
||||
}
|
||||
rd = nrd;
|
||||
|
||||
@@ -248,12 +248,12 @@ static NOINLINE void sh2_poll_write(u32 a, u32 d, unsigned int cycles, SH2 *sh2)
|
||||
// NB this can take an eternity on m68k: mov.b <addr1.l>,<addr2.l> needs
|
||||
// 28 m68k-cycles (~80 sh2-cycles) to complete (observed in Metal Head)
|
||||
q = &fifo[(sh2_poll_wr[hix]-1) % PFIFO_SZ];
|
||||
if (rd != wr && q->a == a && !CYCLES_GT(cycles,q->cycles + (cpu<0 ? 30:4))) {
|
||||
if (rd != wr && q->a == a && !CYCLES_GT(cycles,q->cycles + (!sh2 ? 30:4))) {
|
||||
q->d = d;
|
||||
} else {
|
||||
// store write to poll address in fifo
|
||||
fifo[wr] =
|
||||
(struct sh2_poll_fifo){ .cycles = cycles, .a = a, .d = d, .cpu = cpu };
|
||||
fifo[wr] = (struct sh2_poll_fifo)
|
||||
{ .cycles = cycles, .a = a, .d = d, .cpu = ~cpu & 7 };
|
||||
wr = (wr+1) % PFIFO_SZ;
|
||||
if (wr == rd)
|
||||
// fifo overflow, discard oldest value
|
||||
@@ -419,8 +419,10 @@ static void p32x_reg_write8(u32 a, u32 d)
|
||||
if ((d ^ r[0]) & ~d & P32XS_ADEN) {
|
||||
d |= P32XS_nRES;
|
||||
Pico32xShutdown();
|
||||
} else if ((d ^ r[0]) & d & P32XS_nRES)
|
||||
} else if ((d ^ r[0]) & d & P32XS_nRES) {
|
||||
p32x_sync_sh2s(SekCyclesDone());
|
||||
p32x_reset_sh2s();
|
||||
}
|
||||
REG8IN16(r, 0x01) &= ~(P32XS_nRES|P32XS_ADEN);
|
||||
REG8IN16(r, 0x01) |= d & (P32XS_nRES|P32XS_ADEN);
|
||||
return;
|
||||
@@ -587,8 +589,10 @@ static void p32x_reg_write16(u32 a, u32 d)
|
||||
if ((d ^ r[0]) & ~d & P32XS_ADEN) {
|
||||
d |= P32XS_nRES;
|
||||
Pico32xShutdown();
|
||||
} else if ((d ^ r[0]) & d & P32XS_nRES)
|
||||
} else if ((d ^ r[0]) & d & P32XS_nRES) {
|
||||
p32x_sync_sh2s(SekCyclesDone());
|
||||
p32x_reset_sh2s();
|
||||
}
|
||||
r[0] &= ~(P32XS_FM|P32XS_nRES|P32XS_ADEN);
|
||||
r[0] |= d & (P32XS_FM|P32XS_nRES|P32XS_ADEN);
|
||||
return;
|
||||
@@ -758,7 +762,7 @@ static u32 p32x_sh2reg_read16(u32 a, SH2 *sh2)
|
||||
cycles = sh2_cycles_done_m68k(sh2);
|
||||
sh2s_sync_on_read(sh2, cycles);
|
||||
return sh2_poll_read(a, Pico32x.sh2_regs[4 / 2], cycles, sh2);
|
||||
case 0x06/2:
|
||||
case 0x06/2: // DREQ ctrl
|
||||
return (r[a / 2] & ~P32XS_FULL) | 0x4000;
|
||||
case 0x08/2: // DREQ src
|
||||
case 0x0a/2:
|
||||
@@ -846,7 +850,7 @@ static void p32x_sh2reg_write8(u32 a, u32 d, SH2 *sh2)
|
||||
Pico32x.sh2_regs[4 / 2] = d;
|
||||
p32x_sh2_poll_event(a, sh2->other_sh2, SH2_STATE_CPOLL, cycles);
|
||||
if (p32x_sh2_ready(sh2->other_sh2, cycles+8))
|
||||
sh2_end_run(sh2, 4);
|
||||
p32x_sync_other_sh2(sh2, cycles);
|
||||
sh2_poll_write(a & ~1, d, cycles, sh2);
|
||||
}
|
||||
return;
|
||||
@@ -873,7 +877,7 @@ static void p32x_sh2reg_write8(u32 a, u32 d, SH2 *sh2)
|
||||
p32x_m68k_poll_event(a, P32XF_68KCPOLL);
|
||||
p32x_sh2_poll_event(a, sh2->other_sh2, SH2_STATE_CPOLL, cycles);
|
||||
if (p32x_sh2_ready(sh2->other_sh2, cycles+8))
|
||||
sh2_end_run(sh2, 0);
|
||||
p32x_sync_other_sh2(sh2, cycles);
|
||||
sh2_poll_write(a & ~1, r[a / 2], cycles, sh2);
|
||||
}
|
||||
return;
|
||||
@@ -943,6 +947,7 @@ static void p32x_sh2reg_write16(u32 a, u32 d, SH2 *sh2)
|
||||
Pico32x.sh2irqi[sh2->is_slave] &= ~P32XI_HINT;
|
||||
goto irls;
|
||||
case 0x1a/2:
|
||||
p32x_sync_other_sh2(sh2, sh2_cycles_done_m68k(sh2));
|
||||
Pico32x.regs[2 / 2] &= ~(1 << sh2->is_slave);
|
||||
p32x_update_cmd_irq(sh2, 0);
|
||||
return;
|
||||
@@ -966,7 +971,7 @@ static void p32x_sh2reg_write16(u32 a, u32 d, SH2 *sh2)
|
||||
p32x_m68k_poll_event(a, P32XF_68KCPOLL);
|
||||
p32x_sh2_poll_event(a, sh2->other_sh2, SH2_STATE_CPOLL, cycles);
|
||||
if (p32x_sh2_ready(sh2->other_sh2, cycles+8))
|
||||
sh2_end_run(sh2, 0);
|
||||
p32x_sync_other_sh2(sh2, cycles);
|
||||
sh2_poll_write(a, d, cycles, sh2);
|
||||
}
|
||||
return;
|
||||
@@ -1636,7 +1641,7 @@ static void sh2_sdram_poll(u32 a, u32 d, SH2 *sh2)
|
||||
sh2_poll_write(a, d, cycles, sh2);
|
||||
p32x_sh2_poll_event(a, sh2->other_sh2, SH2_STATE_RPOLL, cycles);
|
||||
if (p32x_sh2_ready(sh2->other_sh2, cycles+8))
|
||||
sh2_end_run(sh2, 0);
|
||||
p32x_sync_other_sh2(sh2, cycles);
|
||||
DRC_RESTORE_SR(sh2);
|
||||
}
|
||||
|
||||
|
||||
+85
-33
@@ -70,7 +70,8 @@ static void dmac_te_irq(SH2 *sh2, struct dma_chan *chan)
|
||||
dmac->vcrdma0 : dmac->vcrdma1;
|
||||
|
||||
elprintf(EL_32XP, "dmac irq %d %d", level, vector);
|
||||
sh2_internal_irq(sh2, level, vector & 0x7f);
|
||||
if (level)
|
||||
sh2_internal_irq(sh2, level, vector & 0x7f);
|
||||
}
|
||||
|
||||
static void dmac_transfer_complete(SH2 *sh2, struct dma_chan *chan)
|
||||
@@ -189,53 +190,78 @@ static void dmac_trigger(SH2 *sh2, struct dma_chan *chan)
|
||||
chan->sar, chan->dar, chan->tcr, chan->chcr, sh2->pc);
|
||||
}
|
||||
|
||||
// FIXME, timer state, in unused peri_regs space to have it in state save/load
|
||||
#define timer_cycles(sh2) *((u32 *)&(sh2)->peri_regs[0x1c])
|
||||
// wdt timers for the 2 SH2's
|
||||
|
||||
static u32 timer_tick_shift[2];
|
||||
static u32 timer_last_cycle[2];
|
||||
|
||||
// timers
|
||||
void p32x_timers_recalc(void)
|
||||
void p32x_timer_recalc(SH2 *sh2)
|
||||
{
|
||||
int tmp, i;
|
||||
int tmp, i = sh2->is_slave;
|
||||
|
||||
if (PREG8(sh2->peri_regs, 0x80) & 0x20) { // TME
|
||||
// SH2 timer step
|
||||
tmp = PREG8(sh2->peri_regs, 0x80) & 7;
|
||||
|
||||
// SH2 timer step
|
||||
for (i = 0; i < 2; i++) {
|
||||
sh2s[i].state &= ~SH2_TIMER_RUN;
|
||||
if (PREG8(sh2s[i].peri_regs, 0x80) & 0x20) // TME
|
||||
sh2s[i].state |= SH2_TIMER_RUN;
|
||||
tmp = PREG8(sh2s[i].peri_regs, 0x80) & 7;
|
||||
// Sclk cycles per timer tick
|
||||
if (tmp >= 6) tmp++;
|
||||
if (tmp) tmp += 5;
|
||||
else tmp = 1;
|
||||
timer_tick_shift[i] = tmp;
|
||||
elprintf(EL_32XP, "WDT cycles[%d] = %d", i, 1 << tmp);
|
||||
|
||||
tmp = (1 << timer_tick_shift[i]) - Pico32x.wdt_cycle[i]; // current tick
|
||||
if (tmp < 0) tmp = 0; // in case of invalid tick cycle data
|
||||
tmp += (255 - PREG8(sh2->peri_regs, 0x81)) << timer_tick_shift[i];
|
||||
|
||||
if (PREG8(sh2->peri_regs, 0xe3) >> 4)
|
||||
p32x_event_schedule_sh2(sh2, P32X_EVENT_MTIMER+i, DIVQ32(tmp,3));
|
||||
}
|
||||
}
|
||||
|
||||
NOINLINE void p32x_timer_do(SH2 *sh2, unsigned int m68k_slice)
|
||||
void p32x_timer_do(SH2 *sh2, unsigned int now)
|
||||
{
|
||||
unsigned int cycles = m68k_slice * 3;
|
||||
int i = sh2->is_slave;
|
||||
void *pregs = sh2->peri_regs;
|
||||
int cnt; int i = sh2->is_slave;
|
||||
|
||||
// WDT timer
|
||||
timer_cycles(sh2) += cycles;
|
||||
cnt = (timer_cycles(sh2) >> timer_tick_shift[i]);
|
||||
if (cnt) {
|
||||
timer_cycles(sh2) -= cnt << timer_tick_shift[i];
|
||||
if (PREG8(sh2->peri_regs, 0x80) & 0x20) { // TME
|
||||
unsigned int cycles = (now - timer_last_cycle[i]) * 3;
|
||||
int cnt;
|
||||
|
||||
cnt += PREG8(pregs, 0x81);
|
||||
if (cnt >= 0x100) {
|
||||
int level = PREG8(pregs, 0xe3) >> 4;
|
||||
int vector = PREG8(pregs, 0xe4) & 0x7f;
|
||||
PREG8(pregs, 0x80) |= 0x80; // WOVF
|
||||
elprintf(EL_32XP, "%csh2 WDT irq (%d, %d)",
|
||||
i ? 's' : 'm', level, vector);
|
||||
cycles += Pico32x.wdt_cycle[i];
|
||||
cnt = (cycles >> timer_tick_shift[i]);
|
||||
cycles -= cnt << timer_tick_shift[i];
|
||||
|
||||
PREG8(pregs, 0x81) += cnt;
|
||||
Pico32x.wdt_cycle[i] = cycles;
|
||||
timer_last_cycle[i] = now;
|
||||
}
|
||||
}
|
||||
|
||||
void p32x_timer_irq(SH2 *sh2, unsigned int now)
|
||||
{
|
||||
void *pregs = sh2->peri_regs;
|
||||
|
||||
if (PREG8(sh2->peri_regs, 0x80) & 0x20) { // TME
|
||||
int i = sh2->is_slave;
|
||||
int after = 256 << timer_tick_shift[i];
|
||||
int level = PREG8(pregs, 0xe3) >> 4;
|
||||
int vector = PREG8(pregs, 0xe4) & 0x7f;
|
||||
|
||||
PREG8(pregs, 0x80) |= 0x80; // WOVF
|
||||
elprintf(EL_32XP, "%csh2 WDT irq (%d, %d)",
|
||||
sh2->is_slave ? 's' : 'm', level, vector);
|
||||
|
||||
if (level) {
|
||||
sh2_internal_irq(sh2, level, vector);
|
||||
cnt &= 0xff;
|
||||
p32x_event_schedule_sh2(sh2, P32X_EVENT_MTIMER+i, DIVQ32(after,3));
|
||||
}
|
||||
PREG8(pregs, 0x81) = cnt;
|
||||
|
||||
// reset counting to avoid rounding errors
|
||||
PREG8(pregs, 0x81) = 0x00;
|
||||
Pico32x.wdt_cycle[i] = 0;
|
||||
timer_last_cycle[i] = now;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +275,13 @@ void sh2_peripheral_reset(SH2 *sh2)
|
||||
PREG8(sh2->peri_regs, 0x017) = 0xe0; // TOCR
|
||||
}
|
||||
|
||||
void sh2_peripheral_state_loaded(void)
|
||||
{
|
||||
timer_last_cycle[0] = timer_last_cycle[1] = Pico.t.m68c_aim;
|
||||
p32x_timer_recalc(&msh2);
|
||||
p32x_timer_recalc(&ssh2);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// SH2 internal peripheral memhandlers
|
||||
// we keep them in little endian format
|
||||
@@ -260,6 +293,8 @@ u32 REGPARM(2) sh2_peripheral_read8(u32 a, SH2 *sh2)
|
||||
|
||||
DRC_SAVE_SR(sh2);
|
||||
a &= 0x1ff;
|
||||
if (a == 0x81)
|
||||
p32x_timer_do(sh2, sh2_cycles_done_m68k(sh2));
|
||||
d = PREG8(r, a);
|
||||
|
||||
elprintf_sh2(sh2, EL_32XP, "peri r8 [%08x] %02x @%06x",
|
||||
@@ -279,6 +314,8 @@ u32 REGPARM(2) sh2_peripheral_read16(u32 a, SH2 *sh2)
|
||||
|
||||
DRC_SAVE_SR(sh2);
|
||||
a &= 0x1fe;
|
||||
if (a == 0x80)
|
||||
p32x_timer_do(sh2, sh2_cycles_done_m68k(sh2));
|
||||
d = r[MEM_BE2(a / 2)];
|
||||
|
||||
elprintf_sh2(sh2, EL_32XP, "peri r16 [%08x] %04x @%06x",
|
||||
@@ -297,6 +334,8 @@ u32 REGPARM(2) sh2_peripheral_read32(u32 a, SH2 *sh2)
|
||||
|
||||
DRC_SAVE_SR(sh2);
|
||||
a &= 0x1fc;
|
||||
if (a == 0x80)
|
||||
p32x_timer_do(sh2, sh2_cycles_done_m68k(sh2));
|
||||
d = sh2->peri_regs[a / 4];
|
||||
|
||||
elprintf_sh2(sh2, EL_32XP, "peri r32 [%08x] %08x @%06x",
|
||||
@@ -335,7 +374,8 @@ static void sci_trigger(SH2 *sh2, u8 *r)
|
||||
int vector = PREG8(oregs, 0x64) & 0x7f;
|
||||
elprintf_sh2(sh2, EL_32XP, "SCI tx irq (%d, %d)",
|
||||
level, vector);
|
||||
sh2_internal_irq(sh2, level, vector);
|
||||
if (level)
|
||||
sh2_internal_irq(sh2, level, vector);
|
||||
}
|
||||
// TODO: TEIE
|
||||
if (PREG8(oregs, 2) & 0x40) { // RIE - rx irq enabled
|
||||
@@ -343,7 +383,8 @@ static void sci_trigger(SH2 *sh2, u8 *r)
|
||||
int vector = PREG8(oregs, 0x63) & 0x7f;
|
||||
elprintf_sh2(sh2->other_sh2, EL_32XP, "SCI rx irq (%d, %d)",
|
||||
level, vector);
|
||||
sh2_internal_irq(sh2->other_sh2, level, vector);
|
||||
if (level)
|
||||
sh2_internal_irq(sh2->other_sh2, level, vector);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,6 +425,10 @@ void REGPARM(3) sh2_peripheral_write8(u32 a, u32 d, SH2 *sh2)
|
||||
d |= 0xe0;
|
||||
PREG8(r, a) = d;
|
||||
break;
|
||||
case 0x0e3:
|
||||
p32x_timer_do(sh2, sh2_cycles_done_m68k(sh2));
|
||||
p32x_timer_recalc(sh2);
|
||||
break;
|
||||
default:
|
||||
if ((a & 0x1c0) == 0x140)
|
||||
p32x_sh2_poll_event(a, sh2, SH2_STATE_CPOLL, SekCyclesDone());
|
||||
@@ -405,13 +450,20 @@ void REGPARM(3) sh2_peripheral_write16(u32 a, u32 d, SH2 *sh2)
|
||||
if (a == 0x80) {
|
||||
if ((d & 0xff00) == 0xa500) { // WTCSR
|
||||
PREG8(r, 0x80) = d;
|
||||
p32x_timers_recalc();
|
||||
timer_cycles(sh2) = 0;
|
||||
}
|
||||
if ((d & 0xff00) == 0x5a00) // WTCNT
|
||||
if ((d & 0xff00) == 0x5a00) { // WTCNT
|
||||
PREG8(r, 0x81) = d;
|
||||
}
|
||||
// reset to avoid rounding problems
|
||||
Pico32x.wdt_cycle[sh2->is_slave] = 0;
|
||||
timer_last_cycle[sh2->is_slave] = sh2_cycles_done_m68k(sh2);
|
||||
p32x_timer_recalc(sh2);
|
||||
} else {
|
||||
r[MEM_BE2(a / 2)] = d;
|
||||
if (a == 0x0e2) {
|
||||
p32x_timer_do(sh2, sh2_cycles_done_m68k(sh2));
|
||||
p32x_timer_recalc(sh2);
|
||||
}
|
||||
if ((a & 0x1c0) == 0x140)
|
||||
p32x_sh2_poll_event(a, sh2, SH2_STATE_CPOLL, SekCyclesDone());
|
||||
}
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ u32 pcd_stopwatch_read(int sub)
|
||||
{
|
||||
// ugh... stopwatch runs 384 cycles per step, divide by mult with inverse
|
||||
u32 d = sub ? SekCyclesDoneS68k() : pcd_cycles_m68k_to_s68k(SekCyclesDone());
|
||||
d = ((d - Pico_mcd->m.stopwatch_base_c) * ((1LL << 32) / 384)) >> 32;
|
||||
d = DIVQ32(d - Pico_mcd->m.stopwatch_base_c, 384);
|
||||
return d & 0x0fff;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ void pcd_pcm_sync(unsigned int to)
|
||||
if ((int)(to - cycles) < 384)
|
||||
return;
|
||||
|
||||
steps = (to - cycles) * ((1LL << 32) / 384) >> 32;
|
||||
steps = DIVQ32(to - cycles, 384);
|
||||
if (Pico_mcd->pcm_mixpos + steps > PCM_MIXBUF_LEN)
|
||||
// shouldn't happen, but occasionally does
|
||||
steps = PCM_MIXBUF_LEN - Pico_mcd->pcm_mixpos;
|
||||
|
||||
+5
-4
@@ -600,7 +600,7 @@ static void DrawWindow(int tstart, int tend, int prio, int sh,
|
||||
struct PicoVideo *pvid = &est->Pico->video;
|
||||
int tilex,ty,nametab,code,oldcode=-1,blank=-1; // The tile we know is blank
|
||||
int yshift,ymask;
|
||||
u32 pack=0;
|
||||
u32 pack=0, pal=0;
|
||||
u32 *hc=NULL, lflags=0; // referenced in DrawTile
|
||||
|
||||
yshift = 4, ymask = 0x7;
|
||||
@@ -634,7 +634,7 @@ static void DrawWindow(int tstart, int tend, int prio, int sh,
|
||||
sh = (sh ? 0x80 : 0x00); // sh and low prio -> shadow
|
||||
for (; tilex < tend; tilex++)
|
||||
{
|
||||
int dx, pal=0;
|
||||
int dx;
|
||||
|
||||
code = PicoMem.vram[nametab + tilex];
|
||||
if ((code>>15) != prio) {
|
||||
@@ -652,7 +652,7 @@ static void DrawWindow(int tstart, int tend, int prio, int sh,
|
||||
sh = lflags; // sh and high prio -> no shadow (lflags to suppress warning)
|
||||
for (; tilex < tend; tilex++)
|
||||
{
|
||||
int dx, pal=0;
|
||||
int dx;
|
||||
|
||||
code = PicoMem.vram[nametab + tilex];
|
||||
if((code>>15) != prio) {
|
||||
@@ -1245,7 +1245,6 @@ static NOINLINE void ParseSprites(int max_lines, int limit)
|
||||
{
|
||||
unsigned char *p = &HighLnSpr[y][0];
|
||||
int cnt = p[0] & 0x7f;
|
||||
if (p[1] & SPRL_MASKED) continue; // masked?
|
||||
|
||||
if (p[3] >= max_line_sprites) continue; // sprite limit?
|
||||
p[3] ++;
|
||||
@@ -1258,6 +1257,8 @@ static NOINLINE void ParseSprites(int max_lines, int limit)
|
||||
}
|
||||
p[2] += w;
|
||||
|
||||
if (p[1] & SPRL_MASKED) continue; // masked?
|
||||
|
||||
if (sx == -0x78) {
|
||||
if (p[1] & (SPRL_HAVE_X|SPRL_TILE_OVFL))
|
||||
p[1] |= SPRL_MASKED; // masked, no more sprites for this line
|
||||
|
||||
+3
-1
@@ -836,7 +836,9 @@ DrawLayer:
|
||||
|
||||
mov r0, sp
|
||||
mov r1, r9, lsr #29
|
||||
bl DrawStripInterlace @ struct TileStrip *ts, int plane_sh
|
||||
mov r2, r9, lsr #8
|
||||
and r2, r2, #0xff
|
||||
bl DrawStripInterlace @ struct TileStrip *ts, int plane_sh, int cellskip
|
||||
|
||||
add sp, sp, #6*4
|
||||
ldmfd sp!, {r4-r11,pc}
|
||||
|
||||
+136
-51
@@ -27,9 +27,9 @@ static void xmap_set(uptr *map, int shift, u32 start_addr, u32 end_addr,
|
||||
const void *func_or_mh, int is_func)
|
||||
{
|
||||
#ifdef __clang__
|
||||
// workaround bug (segfault) in
|
||||
// workaround bug (segfault) in
|
||||
// Apple LLVM version 4.2 (clang-425.0.27) (based on LLVM 3.2svn)
|
||||
volatile
|
||||
volatile
|
||||
#endif
|
||||
uptr addr = (uptr)func_or_mh;
|
||||
int mask = (1 << shift) - 1;
|
||||
@@ -236,9 +236,9 @@ static void m68k_unmapped_write16(u32 a, u32 d)
|
||||
void m68k_map_unmap(u32 start_addr, u32 end_addr)
|
||||
{
|
||||
#ifdef __clang__
|
||||
// workaround bug (segfault) in
|
||||
// workaround bug (segfault) in
|
||||
// Apple LLVM version 4.2 (clang-425.0.27) (based on LLVM 3.2svn)
|
||||
volatile
|
||||
volatile
|
||||
#endif
|
||||
uptr addr;
|
||||
int shift = M68K_MEM_SHIFT;
|
||||
@@ -457,6 +457,30 @@ static u32 read_pad_mouse(int i, u32 out_bits)
|
||||
return value;
|
||||
}
|
||||
|
||||
static u32 read_pad_menacer(int i, u32 out_bits)
|
||||
{
|
||||
u32 pad = PicoIn.padInt[i]; // Get pad MXYZ SACB RLDU; NB not(!) inverted
|
||||
u32 value;
|
||||
|
||||
value = ((pad>>4) & 0x09) | ((pad>>3) & 0x4) | ((pad>>5) & 0x2); // .... SCAB
|
||||
|
||||
value |= 0x40; // TH line is light sensor. Pretend there's always darkness...
|
||||
return value;
|
||||
}
|
||||
|
||||
static u32 read_pad_justifier(int i, u32 out_bits)
|
||||
{
|
||||
u32 pad = ~PicoIn.padInt[i]; // Get inverse of pad MXYZ SACB RLDU
|
||||
u32 value = 0x30; // .111 0000, for ID
|
||||
|
||||
// output data: .tgd ...., t = TH, g = gun select, d = disable
|
||||
if (!(PicoMem.ioports[i+1] & out_bits & 0x70)) // blue gun, enabled
|
||||
value |= ((pad>>6) & 0x03); // .011 ..SA
|
||||
|
||||
value |= (out_bits & 0x40);
|
||||
return value;
|
||||
}
|
||||
|
||||
static u32 read_nothing(int i, u32 out_bits)
|
||||
{
|
||||
return 0xff;
|
||||
@@ -464,6 +488,13 @@ static u32 read_nothing(int i, u32 out_bits)
|
||||
|
||||
typedef u32 (port_read_func)(int index, u32 out_bits);
|
||||
|
||||
int port_type[3] = {
|
||||
PICO_INPUT_PAD_3BTN,
|
||||
PICO_INPUT_PAD_3BTN,
|
||||
PICO_INPUT_NOTHING
|
||||
};
|
||||
int port_lightgun;
|
||||
|
||||
static port_read_func *port_readers[3] = {
|
||||
read_pad_3btn,
|
||||
read_pad_3btn,
|
||||
@@ -472,23 +503,31 @@ static port_read_func *port_readers[3] = {
|
||||
|
||||
static int padTHLatency[3];
|
||||
static int padTLLatency[3];
|
||||
static int padTHTimeout[3];
|
||||
|
||||
static NOINLINE u32 port_read(int i)
|
||||
{
|
||||
u32 data_reg = PicoMem.ioports[i + 1];
|
||||
u32 ctrl_reg = PicoMem.ioports[i + 4] | 0x80;
|
||||
u32 cycles = SekCyclesDone();
|
||||
u32 in, out;
|
||||
|
||||
out = data_reg & ctrl_reg;
|
||||
|
||||
// controllers have a timeout on TH, they reset if not toggled for some time
|
||||
if (CYCLES_GE(cycles, padTHTimeout[i]) && i < 2) {
|
||||
padTHTimeout[i] = cycles + 0x70000000;
|
||||
Pico.m.padTHPhase[i] = 0;
|
||||
}
|
||||
|
||||
// pull-ups: should be 0x7f, but Decap Attack has a bug where it temp.
|
||||
// disables output before doing TH-low read, so emulate RC filter for TH.
|
||||
// Decap Attack reportedly doesn't work on Nomad but works on most
|
||||
// other MD revisions (different pull-up strength?).
|
||||
u32 mask = 0x3f;
|
||||
if (CYCLES_GE(SekCyclesDone(), padTHLatency[i])) {
|
||||
if (CYCLES_GE(cycles, padTHLatency[i])) {
|
||||
mask |= 0x40;
|
||||
padTHLatency[i] = SekCyclesDone();
|
||||
padTHLatency[i] = cycles;
|
||||
}
|
||||
out |= mask & ~ctrl_reg;
|
||||
|
||||
@@ -496,14 +535,48 @@ static NOINLINE u32 port_read(int i)
|
||||
|
||||
// Sega mouse uses the TL/TR lines for req/ack. For buggy drivers, make sure
|
||||
// there's some delay before ack is sent by taking over the new TL line level
|
||||
if (CYCLES_GE(SekCyclesDone(), padTLLatency[i]))
|
||||
padTLLatency[i] = SekCyclesDone();
|
||||
if (CYCLES_GE(cycles, padTLLatency[i]))
|
||||
padTLLatency[i] = cycles;
|
||||
else
|
||||
in ^= 0x10; // TL
|
||||
|
||||
return (in & ~ctrl_reg) | (data_reg & ctrl_reg);
|
||||
}
|
||||
|
||||
// update ports
|
||||
void PicoPortUpdate(void)
|
||||
{
|
||||
if (port_lightgun || (Pico.m.hardware & PMS_HW_LG)) {
|
||||
PicoIn.mouseInt[0] += PicoIn.mouse[0] - PicoIn.mouseInt[2];
|
||||
PicoIn.mouseInt[1] += PicoIn.mouse[1] - PicoIn.mouseInt[3];
|
||||
PicoIn.mouseInt[2] = PicoIn.mouse[0];
|
||||
PicoIn.mouseInt[3] = PicoIn.mouse[1];
|
||||
if (PicoIn.mouseInt[0] < 0) PicoIn.mouseInt[0] = 0;
|
||||
if (PicoIn.mouseInt[0] > 320 ) PicoIn.mouseInt[0] = 320;
|
||||
if (PicoIn.mouseInt[1] < 0) PicoIn.mouseInt[1] = 0;
|
||||
if (PicoIn.mouseInt[1] > 240) PicoIn.mouseInt[1] = 240;
|
||||
}
|
||||
}
|
||||
|
||||
void PicoPortTrigger(void)
|
||||
{
|
||||
// TODO mx/my scaling for V28/V30, H32/H40, soft/hardscaling?
|
||||
int mx = PicoIn.mouseInt[0] + PicoIn.gunx;
|
||||
int my = PicoIn.mouseInt[1] + PicoIn.guny;
|
||||
|
||||
if (unlikely(Pico.m.scanline == my) && port_lightgun &&
|
||||
((PicoMem.ioports[4]|PicoMem.ioports[5]) & 0x80)) {
|
||||
// there's a rather massive delay from VDP, TV, light sensor, infrared
|
||||
// transmission, to the TH pin; it lasts well into the next line.
|
||||
if (port_type[1] == PICO_INPUT_JUSTIFIER) {
|
||||
// justifier should trigger only if gun enabled?
|
||||
if (!(PicoMem.ioports[2] & 0x30))
|
||||
PicoVideoTriggerTH(mx + 66, my);
|
||||
} else
|
||||
PicoVideoTriggerTH(mx + 136, my);
|
||||
}
|
||||
}
|
||||
|
||||
// pad export for J-Cart
|
||||
u32 PicoReadPad(int i, u32 out_bits)
|
||||
{
|
||||
@@ -513,40 +586,31 @@ u32 PicoReadPad(int i, u32 out_bits)
|
||||
void PicoSetInputDevice(int port, enum input_device device)
|
||||
{
|
||||
port_read_func *func;
|
||||
int is_lg = device == PICO_INPUT_LIGHT_GUN || device == PICO_INPUT_JUSTIFIER;
|
||||
|
||||
if (port < 0 || port > 2)
|
||||
return;
|
||||
|
||||
if (port == 1 && port_readers[0] == read_pad_team)
|
||||
func = read_nothing;
|
||||
|
||||
else switch (device) {
|
||||
case PICO_INPUT_PAD_3BTN:
|
||||
func = read_pad_3btn;
|
||||
break;
|
||||
|
||||
case PICO_INPUT_PAD_6BTN:
|
||||
func = read_pad_6btn;
|
||||
break;
|
||||
|
||||
case PICO_INPUT_PAD_TEAM:
|
||||
func = read_pad_team;
|
||||
break;
|
||||
|
||||
case PICO_INPUT_PAD_4WAY:
|
||||
func = read_pad_4way;
|
||||
break;
|
||||
|
||||
case PICO_INPUT_MOUSE:
|
||||
func = read_pad_mouse;
|
||||
break;
|
||||
|
||||
default:
|
||||
func = read_nothing;
|
||||
break;
|
||||
switch (device) {
|
||||
case PICO_INPUT_PAD_3BTN: func = read_pad_3btn; break;
|
||||
case PICO_INPUT_PAD_6BTN: func = read_pad_6btn; break;
|
||||
case PICO_INPUT_PAD_TEAM: func = read_pad_team; break;
|
||||
case PICO_INPUT_PAD_4WAY: func = read_pad_4way; break;
|
||||
case PICO_INPUT_MOUSE: func = read_pad_mouse; break;
|
||||
case PICO_INPUT_LIGHT_GUN: func = read_pad_menacer; break;
|
||||
case PICO_INPUT_JUSTIFIER: func = read_pad_justifier; break;
|
||||
default: func = read_nothing; break;
|
||||
}
|
||||
|
||||
if (port == 1 && port_type[0] == PICO_INPUT_PAD_TEAM)
|
||||
func = read_nothing;
|
||||
|
||||
port_lightgun &= ~(1<<port);
|
||||
port_lightgun |= is_lg<<port;
|
||||
port_type[port] = device;
|
||||
port_readers[port] = func;
|
||||
|
||||
padTLLatency[port] = padTHLatency[port] = padTHTimeout[port] = SekCyclesDone();
|
||||
}
|
||||
|
||||
NOINLINE u32 io_ports_read(u32 a)
|
||||
@@ -570,18 +634,22 @@ NOINLINE void io_ports_write(u32 a, u32 d)
|
||||
// for some controllers, changing TH/TR changes controller state
|
||||
if (1 <= a && a <= 2)
|
||||
{
|
||||
Pico.m.padDelay[a - 1] = 0;
|
||||
if (port_readers[a - 1] == read_pad_team) {
|
||||
// there's a timeout for toggling the TH line
|
||||
if (CYCLES_GE(SekCyclesDone(), padTHTimeout[a - 1]))
|
||||
Pico.m.padTHPhase[a - 1] = 0;
|
||||
padTHTimeout[a - 1] = SekCyclesDone() + 25*488;
|
||||
|
||||
if (port_type[a - 1] == PICO_INPUT_PAD_TEAM) {
|
||||
if (d & 0x40)
|
||||
Pico.m.padTHPhase[a - 1] = 0;
|
||||
else if ((d^PicoMem.ioports[a]) & 0x60)
|
||||
Pico.m.padTHPhase[a - 1]++;
|
||||
} else if (port_readers[0] == read_pad_4way) {
|
||||
} else if (port_type[0] == PICO_INPUT_PAD_4WAY) {
|
||||
if (a == 2 && ((PicoMem.ioports[a] ^ d) & 0x70))
|
||||
Pico.m.padTHPhase[0] = 0;
|
||||
if (a == 1 && !(PicoMem.ioports[a] & 0x40) && (d & 0x40))
|
||||
Pico.m.padTHPhase[0]++;
|
||||
} else if (port_readers[a - 1] == read_pad_mouse) {
|
||||
} else if (port_type[a - 1] == PICO_INPUT_MOUSE) {
|
||||
if ((d^PicoMem.ioports[a]) & 0x20) {
|
||||
if (Pico.m.padTHPhase[a - 1]) { // in readout?
|
||||
padTLLatency[a - 1] = SekCyclesDone() + 100;
|
||||
@@ -597,7 +665,7 @@ NOINLINE void io_ports_write(u32 a, u32 d)
|
||||
Pico.m.padTHPhase[a - 1]++;
|
||||
}
|
||||
|
||||
// after switching TH to input there's a latency before the pullup value is
|
||||
// after switching TH to input there's a latency before the pullup value is
|
||||
// read back as input (see Decap Attack, not in Samurai Showdown, 32x WWF Raw)
|
||||
if (4 <= a && a <= 5) {
|
||||
if ((PicoMem.ioports[a] & 0x40) && !(d & 0x40) && !(PicoMem.ioports[a - 3] & 0x40))
|
||||
@@ -609,6 +677,14 @@ NOINLINE void io_ports_write(u32 a, u32 d)
|
||||
PicoMem.ioports[a] = d;
|
||||
}
|
||||
|
||||
void io_ports_reset(void)
|
||||
{
|
||||
int port;
|
||||
|
||||
for (port = 0; port < 3; port++)
|
||||
padTLLatency[port] = padTHLatency[port] = padTHTimeout[port] = SekCyclesDone();
|
||||
}
|
||||
|
||||
int io_ports_pack(void *buf, size_t size)
|
||||
{
|
||||
size_t b, i;
|
||||
@@ -625,6 +701,8 @@ int io_ports_pack(void *buf, size_t size)
|
||||
save_u16(buf, &b, PicoIn.padInt[i]);
|
||||
for (i = 0; i < 4; i++)
|
||||
save_s32(buf, &b, PicoIn.mouseInt[i]);
|
||||
for (i = 0; i < ARRAY_SIZE(padTHTimeout); i++)
|
||||
save_u32(buf, &b, padTHTimeout[i]);
|
||||
assert(b <= size);
|
||||
return b;
|
||||
}
|
||||
@@ -645,6 +723,15 @@ void io_ports_unpack(const void *buf, size_t size)
|
||||
PicoIn.padInt[i] = load_u16(buf, &b);
|
||||
for (i = 0; i < 4; i++)
|
||||
PicoIn.mouseInt[i] = load_s32(buf, &b);
|
||||
|
||||
// convert pad delay from line count to samples
|
||||
for (i = 0; i < ARRAY_SIZE(padTHTimeout); i++) {
|
||||
if (b < size)
|
||||
padTHTimeout[i] = load_u32(buf, &b);
|
||||
else
|
||||
padTHTimeout[i] = SekCyclesDone() - (25-Pico.m.padDelay[i]) * 488;
|
||||
}
|
||||
|
||||
assert(b <= size);
|
||||
}
|
||||
|
||||
@@ -675,7 +762,7 @@ void NOINLINE ctl_write_z80busreq(u32 d)
|
||||
pprof_start(m68k);
|
||||
PicoSyncZ80(SekCyclesDone());
|
||||
pprof_end_sub(m68k);
|
||||
granted = Pico.t.z80c_aim + 6; // M cycle is 3-6 cycles
|
||||
granted = Pico.t.z80c_aim + 6; // M cycle is 3-6 cycles
|
||||
Pico.t.z80_busdelay += (Pico.t.z80c_cnt - granted) << 8;
|
||||
Pico.t.z80c_cnt = granted;
|
||||
}
|
||||
@@ -746,7 +833,7 @@ static u32 PicoRead8_sram(u32 a)
|
||||
// XXX: this is banking unfriendly
|
||||
if (a < Pico.romsize)
|
||||
return Pico.rom[MEM_BE2(a)];
|
||||
|
||||
|
||||
return m68k_unmapped_read8(a);
|
||||
}
|
||||
|
||||
@@ -835,7 +922,7 @@ static u32 PicoRead8_z80(u32 a)
|
||||
if ((a & 0x4000) == 0x0000) {
|
||||
d = PicoMem.zram[a & 0x1fff];
|
||||
} else if ((a & 0x6000) == 0x4000) // 0x4000-0x5fff
|
||||
d = ym2612_read_local_68k();
|
||||
d = ym2612_read_local_68k();
|
||||
else {
|
||||
elprintf(EL_UIO|EL_ANOMALY, "68k bad read [%06x] @%06x", a, SekPc);
|
||||
d = (u8)PicoRead16_floating(a);
|
||||
@@ -863,8 +950,7 @@ static void PicoWrite8_z80(u32 a, u32 d)
|
||||
return;
|
||||
}
|
||||
if ((a & 0x6000) == 0x4000) { // FM Sound
|
||||
if (PicoIn.opt & POPT_EN_FM)
|
||||
ym2612_write_local(a & 3, d & 0xff, 0);
|
||||
ym2612_write_local(a & 3, d & 0xff, 0);
|
||||
return;
|
||||
}
|
||||
// TODO: probably other VDP access too? Maybe more mirrors?
|
||||
@@ -1250,7 +1336,7 @@ void ym2612_sync_timers(int z80_cycles, int mode_old, int mode_new)
|
||||
* Starting a timer takes place at the next tick, so xcycles needs to be
|
||||
* rounded up to that: t = next tick# = (xcycles / TICK_ZCYCLES) + 1
|
||||
*/
|
||||
unsigned t = ((xcycles * (((1LL<<32)/TIMER_A_TICK_ZCYCLES)+1))>>32) + 1;
|
||||
unsigned t = DIVQ32(xcycles, TIMER_A_TICK_ZCYCLES) + 1;
|
||||
Pico.t.timer_a_next_oflow = t*TIMER_A_TICK_ZCYCLES + Pico.t.timer_a_step;
|
||||
}
|
||||
}
|
||||
@@ -1273,7 +1359,7 @@ void ym2612_sync_timers(int z80_cycles, int mode_old, int mode_new)
|
||||
* reset by loading timer b. The first run of timer b after loading is
|
||||
* therefore shorter by up to 15 ticks.
|
||||
*/
|
||||
unsigned t = ((xcycles * (((1LL<<32)/TIMER_A_TICK_ZCYCLES)+1))>>32) + 1;
|
||||
unsigned t = DIVQ32(xcycles, TIMER_A_TICK_ZCYCLES) + 1;
|
||||
int step = Pico.t.timer_b_step - TIMER_A_TICK_ZCYCLES*(t&15);
|
||||
Pico.t.timer_b_next_oflow = t*TIMER_A_TICK_ZCYCLES + step;
|
||||
}
|
||||
@@ -1448,9 +1534,9 @@ int ym2612_pack_timers(void *buf, size_t size)
|
||||
if (Pico.t.ym2612_busy > 0)
|
||||
busy = cycles_z80_to_68k(Pico.t.ym2612_busy);
|
||||
if (Pico.t.timer_a_next_oflow != TIMER_NO_OFLOW)
|
||||
tat = ((Pico.t.timer_a_step - Pico.t.timer_a_next_oflow) * ((1LL<<32)/TIMER_A_TICK_ZCYCLES+1))>>16;
|
||||
tat = MULQ((Pico.t.timer_a_step - Pico.t.timer_a_next_oflow), INVQ32(TIMER_A_TICK_ZCYCLES), 16);
|
||||
if (Pico.t.timer_b_next_oflow != TIMER_NO_OFLOW)
|
||||
tbt = ((Pico.t.timer_b_step - Pico.t.timer_b_next_oflow) * ((1LL<<32)/TIMER_B_TICK_ZCYCLES+1))>>16;
|
||||
tbt = MULQ((Pico.t.timer_b_step - Pico.t.timer_b_next_oflow), INVQ32(TIMER_B_TICK_ZCYCLES), 16);
|
||||
elprintf(EL_YMTIMER, "save: timer a %i/%i", tat >> 16, tac);
|
||||
elprintf(EL_YMTIMER, "save: timer b %i/%i", tbt >> 16, tbc);
|
||||
|
||||
@@ -1609,8 +1695,7 @@ static unsigned char z80_md_bank_read(unsigned short a)
|
||||
|
||||
static void z80_md_ym2612_write(unsigned int a, unsigned char data)
|
||||
{
|
||||
if (PicoIn.opt & POPT_EN_FM)
|
||||
ym2612_write_local(a, data, 1);
|
||||
ym2612_write_local(a, data, 1);
|
||||
}
|
||||
|
||||
static void z80_md_vdp_br_write(unsigned int a, unsigned char data)
|
||||
|
||||
@@ -249,6 +249,8 @@ void PicoLoopPrepare(void)
|
||||
PicoMCDPrepare();
|
||||
if (PicoIn.AHW & PAHW_32X)
|
||||
Pico32xPrepare();
|
||||
if (PicoIn.AHW & PAHW_SMS)
|
||||
PicoPrepareMS();
|
||||
}
|
||||
|
||||
#include "pico_cmn.c"
|
||||
|
||||
+6
-3
@@ -78,7 +78,6 @@ extern void *p32x_bios_g, *p32x_bios_m, *p32x_bios_s;
|
||||
#define POPT_EN_FM_DAC (1<<24) //x00 0000
|
||||
#define POPT_EN_FM_FILTER (1<<25)
|
||||
#define POPT_EN_KBD (1<<26)
|
||||
#define POPT_EN_MOUSE (1<<27)
|
||||
|
||||
#define PAHW_MCD (1<<0)
|
||||
#define PAHW_32X (1<<1)
|
||||
@@ -114,8 +113,9 @@ typedef struct PicoInterface
|
||||
unsigned short AHW; // active addon hardware: PAHW_* bitfield
|
||||
|
||||
unsigned short kbd; // SC-3000 or Pico Keyboard
|
||||
int mouse[4]; // x,y mouse coordinates
|
||||
int mouseInt[4]; // internal copy
|
||||
short mouse[4]; // x,y mouse coordinates
|
||||
short mouseInt[4]; // internal copy
|
||||
short gunx, guny; // light gun offsets
|
||||
|
||||
unsigned short quirks; // game-specific quirks: PQUIRK_*
|
||||
unsigned short overclockM68k; // overclock the emulated 68k, in %
|
||||
@@ -364,8 +364,11 @@ enum input_device {
|
||||
PICO_INPUT_PAD_3BTN,
|
||||
PICO_INPUT_PAD_6BTN,
|
||||
PICO_INPUT_MOUSE,
|
||||
PICO_INPUT_LIGHT_GUN,
|
||||
PICO_INPUT_JUSTIFIER,
|
||||
PICO_INPUT_PAD_TEAM,
|
||||
PICO_INPUT_PAD_4WAY,
|
||||
PICO_INPUT_COUNT
|
||||
};
|
||||
void PicoSetInputDevice(int port, enum input_device device);
|
||||
|
||||
|
||||
+5
-11
@@ -10,12 +10,6 @@
|
||||
#define CYCLES_M68K_LINE 488 // suitable for both PAL/NTSC
|
||||
#define CYCLES_M68K_VINT_LAG 112
|
||||
|
||||
// pad delay (for 6 button pads)
|
||||
#define PAD_DELAY() { \
|
||||
if(Pico.m.padDelay[0]++ > 25) Pico.m.padTHPhase[0]=0; \
|
||||
if(Pico.m.padDelay[1]++ > 25) Pico.m.padTHPhase[1]=0; \
|
||||
}
|
||||
|
||||
// CPUS_RUN
|
||||
#ifndef CPUS_RUN
|
||||
#define CPUS_RUN(m68k_cycles) \
|
||||
@@ -146,6 +140,7 @@ static int PicoFrameHints(void)
|
||||
|
||||
Pico.t.m68c_frame_start = Pico.t.m68c_aim;
|
||||
PsndStartFrame();
|
||||
PicoPortUpdate();
|
||||
|
||||
hint = pv->hint_cnt;
|
||||
|
||||
@@ -160,7 +155,7 @@ static int PicoFrameHints(void)
|
||||
Pico.m.scanline = y;
|
||||
pv->v_counter = PicoVideoGetV(y, 0);
|
||||
|
||||
PAD_DELAY();
|
||||
PicoPortTick();
|
||||
|
||||
// H-Interrupts:
|
||||
if (--hint < 0)
|
||||
@@ -217,7 +212,7 @@ static int PicoFrameHints(void)
|
||||
pv->v_counter = PicoVideoGetV(y, 0);
|
||||
|
||||
memcpy(PicoIn.padInt, PicoIn.pad, sizeof(PicoIn.padInt));
|
||||
PAD_DELAY();
|
||||
PicoPortTick();
|
||||
|
||||
// Last H-Int (normally):
|
||||
if (--hint < 0)
|
||||
@@ -288,7 +283,7 @@ static int PicoFrameHints(void)
|
||||
Pico.m.scanline = y;
|
||||
pv->v_counter = PicoVideoGetV(y, 1);
|
||||
|
||||
PAD_DELAY();
|
||||
PicoPortTick();
|
||||
|
||||
if (unlikely(pv->status & PVS_ACTIVE) && --hint < 0)
|
||||
{
|
||||
@@ -324,7 +319,7 @@ static int PicoFrameHints(void)
|
||||
Pico.m.scanline = y++;
|
||||
pv->v_counter = 0xff;
|
||||
|
||||
PAD_DELAY();
|
||||
PicoPortTick();
|
||||
|
||||
if (unlikely(pv->status & PVS_ACTIVE)) {
|
||||
if (--hint < 0) {
|
||||
@@ -361,7 +356,6 @@ static int PicoFrameHints(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef PAD_DELAY
|
||||
#undef CPUS_RUN
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
||||
+21
-5
@@ -302,6 +302,7 @@ extern SH2 sh2s[2];
|
||||
#define PVS_DMAFILL (1 << 20) // DMA fill is waiting for fill data
|
||||
#define PVS_DMABG (1 << 21) // background DMA operation is running
|
||||
#define PVS_FIFORUN (1 << 22) // FIFO is processing
|
||||
#define PVS_HVLATCH (1 << 23) // next hv read is latched (for lightguns)
|
||||
|
||||
struct PicoVideo
|
||||
{
|
||||
@@ -354,6 +355,7 @@ struct PicoMisc
|
||||
#define PMS_HW_FM 0x8 // FM sound
|
||||
#define PMS_HW_TMS 0x10 // assume TMS9918
|
||||
#define PMS_HW_3D 0x20 // 3D glasses
|
||||
#define PMS_HW_LG 0x40 // light phaser
|
||||
#define PMS_HW_FMUSED 0x80 // FM sound accessed
|
||||
|
||||
#define PMS_MAP_AUTO 0
|
||||
@@ -654,9 +656,7 @@ struct Pico32x
|
||||
unsigned int pad[4];
|
||||
unsigned int dmac0_fifo_ptr;
|
||||
unsigned short vdp_fbcr_fake;
|
||||
unsigned short pad2;
|
||||
unsigned char comm_dirty;
|
||||
unsigned char pad3; // was comm_dirty_sh2
|
||||
unsigned short wdt_cycle[2]; // wdt clocking (was comm_dirty)
|
||||
unsigned char pwm_irq_cnt;
|
||||
unsigned char pad1;
|
||||
unsigned short pwm_p[2]; // pwm pos in fifo
|
||||
@@ -757,9 +757,17 @@ u32 PicoRead8_io(u32 a);
|
||||
u32 PicoRead16_io(u32 a);
|
||||
void PicoWrite8_io(u32 a, u32 d);
|
||||
void PicoWrite16_io(u32 a, u32 d);
|
||||
void PicoPortUpdate(void);
|
||||
void PicoPortTrigger(void);
|
||||
u32 PicoReadPad(int i, u32 mask);
|
||||
void io_ports_reset(void);
|
||||
int io_ports_pack(void *buf, size_t size);
|
||||
void io_ports_unpack(const void *buf, size_t size);
|
||||
extern int port_type[3];
|
||||
extern int port_lightgun;
|
||||
|
||||
#define PicoPortTick() if (port_lightgun && \
|
||||
Pico.m.scanline == PicoIn.mouseInt[1]+PicoIn.guny) PicoPortTrigger()
|
||||
|
||||
// pico/memory.c
|
||||
PICO_INTERNAL void PicoMemSetupPico(void);
|
||||
@@ -977,6 +985,7 @@ void PicoVideoFIFOMode(int active, int h40);
|
||||
int PicoVideoFIFOWrite(int count, int byte_p, unsigned sr_mask, unsigned sr_flags);
|
||||
void PicoVideoInit(void);
|
||||
void PicoVideoReset(void);
|
||||
void PicoVideoTriggerTH(int x, int y);
|
||||
void PicoVideoSync(int skip);
|
||||
int PicoVideoSave(void *buf);
|
||||
void PicoVideoLoad(void *buf, int len);
|
||||
@@ -1023,6 +1032,7 @@ void PicoPowerMS(void);
|
||||
void PicoResetMS(void);
|
||||
void PicoMemSetupMS(void);
|
||||
void PicoStateLoadedMS(void);
|
||||
void PicoPrepareMS(void);
|
||||
void PicoFrameMS(void);
|
||||
void PicoFrameDrawOnlyMS(void);
|
||||
int PicoPlayTape(const char *fname);
|
||||
@@ -1047,6 +1057,9 @@ enum p32x_event {
|
||||
P32X_EVENT_PWM,
|
||||
P32X_EVENT_FILLEND,
|
||||
P32X_EVENT_HINT,
|
||||
P32X_EVENT_VINT,
|
||||
P32X_EVENT_MTIMER,
|
||||
P32X_EVENT_STIMER,
|
||||
P32X_EVENT_COUNT,
|
||||
};
|
||||
extern unsigned int p32x_event_times[P32X_EVENT_COUNT];
|
||||
@@ -1070,6 +1083,7 @@ void p32x_reset_sh2s(void);
|
||||
void p32x_event_schedule(unsigned int now, enum p32x_event event, int after);
|
||||
void p32x_event_schedule_sh2(SH2 *sh2, enum p32x_event event, int after);
|
||||
void p32x_schedule_hint(SH2 *sh2, unsigned int m68k_cycles);
|
||||
void p32x_schedule_vint(SH2 *sh2, unsigned int m68k_cycles);
|
||||
|
||||
#define p32x_sh2_ready(sh2, cycles) \
|
||||
(CYCLES_GT(cycles,sh2->m68krcycles_done) && \
|
||||
@@ -1124,9 +1138,11 @@ void p32x_pwm_state_loaded(void);
|
||||
// 32x/sh2soc.c
|
||||
void p32x_dreq0_trigger(void);
|
||||
void p32x_dreq1_trigger(void);
|
||||
void p32x_timers_recalc(void);
|
||||
void p32x_timer_do(SH2 *sh2, unsigned int m68k_slice);
|
||||
void p32x_timer_recalc(SH2 *sh2);
|
||||
void p32x_timer_do(SH2 *sh2, unsigned int now);
|
||||
void p32x_timer_irq(SH2 *sh2, unsigned int now);
|
||||
void sh2_peripheral_reset(SH2 *sh2);
|
||||
void sh2_peripheral_state_loaded(void);
|
||||
u32 REGPARM(2) sh2_peripheral_read8(u32 a, SH2 *sh2);
|
||||
u32 REGPARM(2) sh2_peripheral_read16(u32 a, SH2 *sh2);
|
||||
u32 REGPARM(2) sh2_peripheral_read32(u32 a, SH2 *sh2);
|
||||
|
||||
@@ -80,4 +80,12 @@
|
||||
(((v)<<8)&0xff0000)|(u32)((v)<<24))
|
||||
#endif
|
||||
|
||||
#define INVQ(n,m) (long long)((1LL<<(m))/(n)+1) // to replace div by mult with inverse
|
||||
#define MULQ(n,d,m) ((n)*(d) >> (m))
|
||||
#define DIVQ(n,d,m) ((n)*INVQ32(d) >> (m))
|
||||
|
||||
#define INVQ32(n) INVQ(n,32)
|
||||
#define MULQ32(n,d) MULQ(n,d,32)
|
||||
#define DIVQ32(n,d) DIVQ(n,d,32)
|
||||
|
||||
#endif // PICO_PORT_INCLUDED
|
||||
+26
-4
@@ -155,8 +155,8 @@ static u8 vdp_hcounter(int cycles)
|
||||
{
|
||||
// 171 slots per scanline of 228 clocks, counted 0xf4-0x93, 0xe9-0xf3
|
||||
// this matches h counter tables in SMSVDPTest:
|
||||
// hc = (cycles+2) * 171 /228 -1 + 0xf4;
|
||||
int hc = (((cycles+2) * ((171<<8)/228))>>8)-1 + 0xf4; // Q8 to avoid dividing
|
||||
// hc = (cycles+2) * 171 /228 -1 + 0xf4;
|
||||
int hc = DIVQ32((cycles+2) * 171, 228) -1 + 0xf4;
|
||||
if (hc > 0x193) hc += 0xe9-0x93-1;
|
||||
return hc;
|
||||
}
|
||||
@@ -503,6 +503,20 @@ static unsigned char z80_sms_in(unsigned short a)
|
||||
d = (d & ~0x08) | ((Pico.ms.io_ctl >> 3) & 0x08);
|
||||
if (Pico.ms.io_ctl & 0x08) d |= 0x80; // TH as input is unconnected
|
||||
if (Pico.ms.io_ctl & 0x02) d |= 0x40;
|
||||
if (Pico.m.hardware & PMS_HW_LG) { // light phaser
|
||||
// TODO mx/my scaling is wrong if V28/V30 mode is used?
|
||||
int mx = DIVQ32((PicoIn.mouseInt[0]+PicoIn.gunx) * 256, 320);
|
||||
int my = DIVQ32((PicoIn.mouseInt[1]+PicoIn.guny) * 192, 224);
|
||||
int x = vdp_hcounter(z80_cyclesDone() - Pico.t.z80c_line_start);
|
||||
int dx = 2*x - mx;
|
||||
int dy = Pico.m.scanline - my;
|
||||
int th = 0xc0; // TODO set bits according to port usage
|
||||
d |= th; // TH input, high if no light detected
|
||||
if (dy > -4 && dy < 4 && dx > -40 && dx < 40) {
|
||||
d &= ~th; // TH falling -> save hcounter
|
||||
Pico.ms.vdp_hlatch = (mx >> 1) + 24;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int i; // read kbd 4 bits
|
||||
kbd_update();
|
||||
@@ -1223,6 +1237,13 @@ void PicoStateLoadedMS(void)
|
||||
memcpy(Pico.ms.carthw, carthw, 16);
|
||||
}
|
||||
|
||||
void PicoPrepareMS(void)
|
||||
{
|
||||
Pico.m.hardware &= ~PMS_HW_LG;
|
||||
if (port_type[0] == PICO_INPUT_LIGHT_GUN || port_type[1] == PICO_INPUT_LIGHT_GUN)
|
||||
Pico.m.hardware |= PMS_HW_LG;
|
||||
}
|
||||
|
||||
void PicoFrameMS(void)
|
||||
{
|
||||
struct PicoVideo *pv = &Pico.video;
|
||||
@@ -1236,6 +1257,7 @@ void PicoFrameMS(void)
|
||||
int y;
|
||||
|
||||
PsndStartFrame();
|
||||
PicoPortUpdate();
|
||||
|
||||
// for SMS the pause button generates an NMI, for GG ths is not the case
|
||||
nmi = (PicoIn.pad[0] >> 7) & 1;
|
||||
@@ -1367,7 +1389,7 @@ int PicoPlayTape(const char *fname)
|
||||
}
|
||||
|
||||
pt->cycles_sample = (Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15) / rate;
|
||||
pt->cycles_mult = (1LL<<32) / pt->cycles_sample;
|
||||
pt->cycles_mult = INVQ32(pt->cycles_sample);
|
||||
pt->cycle = Pico.t.z80c_aim;
|
||||
pt->phase = Pico.t.z80c_aim;
|
||||
pt->pause = 0;
|
||||
@@ -1410,7 +1432,7 @@ int PicoRecordTape(const char *fname)
|
||||
}
|
||||
|
||||
pt->cycles_sample = (Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15) / rate;
|
||||
pt->cycles_mult = (1LL<<32) / pt->cycles_sample;
|
||||
pt->cycles_mult = INVQ32(pt->cycles_sample);
|
||||
pt->cycle = Pico.t.z80c_aim;
|
||||
pt->phase = Pico.t.z80c_aim;
|
||||
pt->pause = 0;
|
||||
|
||||
+12
-2
@@ -429,7 +429,7 @@ static int state_load(void *file)
|
||||
int len_check;
|
||||
int retval = -1;
|
||||
char header[8];
|
||||
int ver, has_32x = 0;
|
||||
int ver, has_32x = 0, has_iov2 = 0;
|
||||
int len, len_vdp = 0;
|
||||
|
||||
memset(buff_m68k, 0, sizeof(buff_m68k));
|
||||
@@ -483,6 +483,13 @@ static int state_load(void *file)
|
||||
case CHUNK_VDP: CHECKED_READ2((len_vdp = len), buff_vdp); break;
|
||||
|
||||
case CHUNK_IOPORTS: CHECKED_READ_BUFF(PicoMem.ioports); break;
|
||||
case CHUNK_IOPORTSv2:
|
||||
CHECKED_READ(len, buf);
|
||||
io_ports_unpack(buf, len);
|
||||
has_iov2 = 1;
|
||||
break;
|
||||
|
||||
|
||||
case CHUNK_PSG: CHECKED_READ2(28*4, sn76496_regs); break;
|
||||
case CHUNK_YM2413:
|
||||
CHECKED_READ(len, buf);
|
||||
@@ -496,7 +503,6 @@ static int state_load(void *file)
|
||||
break;
|
||||
case CHUNK_FM_TIMERS: CHECKED_READ(len, buf); ym2612_unpack_timers(buf, len); break;
|
||||
case CHUNK_FMv3: CHECKED_READ(len, buf); YM2612PicoStateLoad3(buf, len); break;
|
||||
case CHUNK_IOPORTSv2: CHECKED_READ(len, buf); io_ports_unpack(buf, len); break;
|
||||
|
||||
case CHUNK_PICO_PCM:
|
||||
CHECKED_READ(len, buf);
|
||||
@@ -641,6 +647,10 @@ readend:
|
||||
Pico.video.status |= (Pico.video.pending_ints << 2) & SR_F;
|
||||
}
|
||||
|
||||
// kludge: if there is no new ports data save, they must be initialised
|
||||
if (!has_iov2)
|
||||
io_ports_reset();
|
||||
|
||||
Pico.m.dirtyPal = 1;
|
||||
retval = 0;
|
||||
|
||||
|
||||
+57
-10
@@ -405,7 +405,7 @@ int PicoVideoFIFOHint(void)
|
||||
// only need to refresh sprite position if we are synced
|
||||
if (Pico.est.DrawScanline == Pico.m.scanline && !(pv->status & SR_VB))
|
||||
PicoDrawRefreshSprites();
|
||||
|
||||
|
||||
// if CPU is waiting for the bus, advance CPU and FIFO until bus is free
|
||||
if (pv->status & PVS_CPUWR)
|
||||
burn = PicoVideoFIFODrain(4, lc, 0);
|
||||
@@ -603,7 +603,7 @@ static void DmaSlow(int len, u32 source)
|
||||
!((source ^ (source + len-1)) & ~mask))
|
||||
{
|
||||
// most used DMA mode
|
||||
memcpy((char *)r + a, base + (source & mask), len * 2);
|
||||
memcpy((char *)r + (u16)a, base + (source & mask), len * 2);
|
||||
a += len * 2;
|
||||
break;
|
||||
}
|
||||
@@ -1120,10 +1120,28 @@ PICO_INTERNAL_ASM u32 PicoVideoRead(u32 a)
|
||||
unsigned int c;
|
||||
u32 d;
|
||||
|
||||
// invalidate cached value if on a new scanline
|
||||
if ((pv->status & PVS_HVLATCH) && pv->hv_latch >> 8 != Pico.m.scanline)
|
||||
pv->status &= ~PVS_HVLATCH;
|
||||
|
||||
c = SekCyclesDone() - Pico.t.m68c_line_start;
|
||||
if (pv->reg[0]&2)
|
||||
d = pv->hv_latch;
|
||||
else d = VdpFIFO.fifo_hcounts[c/clkdiv] | (pv->v_counter << 8);
|
||||
if (pv->reg[0]&2) // latched value from TH transition
|
||||
d = pv->hv_latch;
|
||||
else if (pv->status & PVS_HVLATCH) { // cached value from light gun
|
||||
c += 28; // estimate for 68k insn time
|
||||
// return cached value, but add in irq latency; since the irq was fired
|
||||
// at the start of the line, the latency in cycles already is in c.
|
||||
d = pv->hv_latch & 0xff;
|
||||
if (pv->reg[12]&1) { // H40/H32?
|
||||
c = DIVQ32(c * slots40, slcpu); // slots since line start
|
||||
if (d < gapstart40 && d+c >= gapstart40) d += gapend40-gapstart40;
|
||||
} else {
|
||||
c = DIVQ32(c * slots32, slcpu);
|
||||
if (d < gapstart32 && d+c >= gapstart32) d += gapend32-gapstart32;
|
||||
}
|
||||
d = (pv->hv_latch & 0xff00) | ((d+c) & 0x00ff);
|
||||
} else // freerunning
|
||||
d = VdpFIFO.fifo_hcounts[c/clkdiv] | (pv->v_counter << 8);
|
||||
|
||||
elprintf(EL_HVCNT, "hv: %02x %02x [%u] @ %06x", d, pv->v_counter, SekCyclesDone(), SekPc);
|
||||
return d;
|
||||
@@ -1193,16 +1211,45 @@ unsigned char PicoVideoRead8HV_L(int is_from_z80)
|
||||
|
||||
void PicoVideoReset(void)
|
||||
{
|
||||
Pico.video.pending_ints=0;
|
||||
Pico.video.reg[1] &= ~0x40; // TODO verify display disabled after reset
|
||||
Pico.video.reg[10] = 0xff; // HINT is turned off after reset
|
||||
Pico.video.status = 0x3428 | Pico.m.pal; // 'always set' bits | vblank | collision | pal
|
||||
struct PicoVideo *pv = &Pico.video;
|
||||
|
||||
pv->pending_ints=0;
|
||||
pv->reg[1] &= ~0x40; // TODO verify display disabled after reset
|
||||
pv->reg[10] = 0xff; // HINT is turned off after reset
|
||||
pv->status = 0x3428 | Pico.m.pal; // 'always set' bits | vblank | collision | pal
|
||||
|
||||
memset(&VdpFIFO, 0, sizeof(VdpFIFO));
|
||||
Pico.m.dirtyPal = 1;
|
||||
|
||||
PicoDrawBgcDMA(NULL, 0, 0, 0, 0);
|
||||
PicoVideoFIFOMode(Pico.video.reg[1]&0x40, Pico.video.reg[12]&1);
|
||||
PicoVideoFIFOMode(pv->reg[1]&0x40, pv->reg[12]&1);
|
||||
}
|
||||
|
||||
// Process triggering of the !HL pin at pixel x,y of screen
|
||||
void PicoVideoTriggerTH(int x, int y)
|
||||
{
|
||||
struct PicoVideo *pv = &Pico.video;
|
||||
int slot;
|
||||
|
||||
// TODO x/y scaling for V28/V30, H32/H40, soft/hardscaling?
|
||||
if (!(pv->reg[12]&1)) { // H32, convert from 320 to 256 px per line
|
||||
slot = DIVQ32(x * 256, 320) / 2;
|
||||
if (slot >= gapstart32) slot += gapend32-gapstart32;
|
||||
} else {
|
||||
slot = x / 2;
|
||||
if (slot >= gapstart40) slot += gapend40-gapstart40;
|
||||
}
|
||||
|
||||
// This is only called at the start of a scanline, hence the irq isn't
|
||||
// cycle correct. There are games reading hv unlatched in the irq handler,
|
||||
// so latch mouse here, and fix the value up when the irq handler reads it.
|
||||
pv->hv_latch = (slot & 0xff) | (y << 8);
|
||||
pv->status |= PVS_HVLATCH;
|
||||
|
||||
// irq here for speed reasons. It's early, but that shouldn't matter much.
|
||||
// TODO is irq really lost if another irq is already pending?
|
||||
if ((Pico.video.reg[11]&8) && SekIrqLevel < 2)
|
||||
SekInterrupt(2);
|
||||
}
|
||||
|
||||
void PicoVideoCacheSAT(int load)
|
||||
|
||||
@@ -101,6 +101,7 @@ through the D-pad if the screen has been switched to either pages or pad, or by
|
||||
mouse if physical mouse support is activated.
|
||||
|
||||
|
||||
#ifdef GENERIC
|
||||
How to use the mouse
|
||||
--------------------
|
||||
|
||||
@@ -120,11 +121,44 @@ The physical mouse buttons are mapped as follows:
|
||||
|
||||
| Physical Mouse | Sega Mouse | Pico Pen |
|
||||
|:--------------:|:----------:|:-----------:|
|
||||
| Left Button | B | Pen Button |
|
||||
| Left Button | B | Pen Button |
|
||||
| Middle Button | START | Red Button |
|
||||
| Right Button | C | Pen Up/Down |
|
||||
|
||||
|
||||
How to use a light gun
|
||||
----------------------
|
||||
|
||||
PicoDrive additionally supports light guns on systems where mouse support is
|
||||
available. It can be activated by selecting `light gun` or `justifier` as the
|
||||
input device. General mouse operation is covered in the "How to use the mouse"
|
||||
section.
|
||||
|
||||
The `light gun` setting auto configures an input device based on the currently
|
||||
emulated platform: a Sega Menacer for the Mega Drive (and derivatives) and a
|
||||
Light Phaser for the Master System. The `justifier` setting corresponds to a
|
||||
Konami Justifier. This is only available on Mega Drive based systems, and can
|
||||
only be configured as input device 2.
|
||||
|
||||
Selecting any light gun as input enables the light gun configuration submenu.
|
||||
This allows for displaying a cursor to indicate the current aiming position,
|
||||
as well as the adjustment of an aiming offset to correct for any in-game
|
||||
displacements.
|
||||
|
||||
The physical mouse buttons are mapped as follows:
|
||||
|
||||
| Physical Mouse | Sega Menacer/ |
|
||||
| | Light Phaser/ |
|
||||
| | Konami Justifier |
|
||||
|:--------------:|:----------------:|
|
||||
| Left Button | A |
|
||||
| Middle Button | B |
|
||||
| Right Button | START |
|
||||
|
||||
The pad button mappings may also be used to access other buttons.
|
||||
|
||||
|
||||
#endif
|
||||
How to use keyboard input
|
||||
-------------------------
|
||||
|
||||
|
||||
@@ -275,6 +275,7 @@ int config_readlrom(const char *fname)
|
||||
static int custom_read(menu_entry *me, const char *var, const char *val)
|
||||
{
|
||||
char *tmp;
|
||||
int i;
|
||||
|
||||
switch (me->id)
|
||||
{
|
||||
@@ -364,7 +365,7 @@ static int custom_read(menu_entry *me, const char *var, const char *val)
|
||||
currentConfig.max_skip = atoi(val);
|
||||
return 1;
|
||||
|
||||
case MA_CTRL_KEYBOARD:
|
||||
case MA_OPT_KEYBOARD:
|
||||
currentConfig.keyboard = 0;
|
||||
if (strcasecmp(val, "physical") == 0)
|
||||
currentConfig.keyboard = 2;
|
||||
@@ -372,6 +373,17 @@ static int custom_read(menu_entry *me, const char *var, const char *val)
|
||||
currentConfig.keyboard = 1;
|
||||
return 1;
|
||||
|
||||
case MA_OPT_INPUT_DEV0:
|
||||
for (i = 0; indev_names[i]; i++)
|
||||
if (strcasecmp(val, indev_names[i]) == 0)
|
||||
currentConfig.input_dev0 = i;
|
||||
return 1;
|
||||
case MA_OPT_INPUT_DEV1:
|
||||
for (i = 0; indev_names[i]; i++)
|
||||
if (strcasecmp(val, indev_names[i]) == 0)
|
||||
currentConfig.input_dev1 = i;
|
||||
return 1;
|
||||
|
||||
/* PSP */
|
||||
case MA_OPT3_VSYNC:
|
||||
// XXX: use enum
|
||||
|
||||
+53
-44
@@ -37,10 +37,8 @@
|
||||
|
||||
#ifndef _WIN32
|
||||
#define PATH_SEP "/"
|
||||
#define PATH_SEP_C '/'
|
||||
#else
|
||||
#define PATH_SEP "\\"
|
||||
#define PATH_SEP_C '\\'
|
||||
#endif
|
||||
|
||||
#define STATUS_MSG_TIMEOUT 2000
|
||||
@@ -134,7 +132,7 @@ static void fname_ext(char *dst, int dstlen, const char *prefix, const char *ext
|
||||
}
|
||||
|
||||
p = fname + strlen(fname) - 1;
|
||||
for (; p >= fname && *p != PATH_SEP_C; p--)
|
||||
for (; p >= fname && *p != *PATH_SEP; p--)
|
||||
;
|
||||
p++;
|
||||
strncpy(dst + prefix_len, p, dstlen - prefix_len - 1);
|
||||
@@ -745,6 +743,8 @@ int emu_read_config(const char *rom_fname, int no_defaults)
|
||||
|
||||
pemu_validate_config();
|
||||
PicoIn.overclockM68k = currentConfig.overclock_68k;
|
||||
PicoIn.gunx = currentConfig.gunx;
|
||||
PicoIn.guny = currentConfig.guny;
|
||||
|
||||
// some sanity checks
|
||||
if (currentConfig.volume < 0 || currentConfig.volume > 99)
|
||||
@@ -1202,7 +1202,7 @@ void run_events_pico(unsigned int events)
|
||||
return;
|
||||
|
||||
/* handle other input modes using the pen */
|
||||
if (PicoIn.opt & POPT_EN_MOUSE) {
|
||||
if (currentConfig.EmuOpt & EOPT_MOUSE) {
|
||||
pico_pen_x = PicoIn.mouse[0];
|
||||
pico_pen_y = PicoIn.mouse[1];
|
||||
} else {
|
||||
@@ -1324,12 +1324,12 @@ static void run_events_ui(unsigned int which)
|
||||
}
|
||||
if (which & PEV_GRAB_INPUT)
|
||||
{
|
||||
if (PicoIn.opt & POPT_EN_MOUSE) {
|
||||
if (currentConfig.EmuOpt & EOPT_MOUSE) {
|
||||
grab_mode = !grab_mode;
|
||||
in_update_analog(0, 2, &mouse_x);
|
||||
in_update_analog(0, 3, &mouse_y);
|
||||
in_update_analog(0, 0, &mouse_x);
|
||||
in_update_analog(0, 1, &mouse_y);
|
||||
in_update_pointer(0, 2, &mouse_x);
|
||||
in_update_pointer(0, 3, &mouse_y);
|
||||
in_update_pointer(0, 0, &mouse_x);
|
||||
in_update_pointer(0, 1, &mouse_y);
|
||||
emu_status_msg("Mouse capture %s", grab_mode ? "on" : "off");
|
||||
} else {
|
||||
grab_mode = 0;
|
||||
@@ -1356,13 +1356,34 @@ static void run_events_ui(unsigned int which)
|
||||
engineState = PGS_Menu;
|
||||
}
|
||||
|
||||
static int map_pointer_buttons(int msbtns, int device)
|
||||
{
|
||||
int buttons = 0;
|
||||
|
||||
if ((PicoIn.AHW & PAHW_PICO) && device == PICO_INPUT_MOUSE) {
|
||||
if (msbtns & 1) buttons |= 1<<GBTN_C; // pen button
|
||||
if (msbtns & 2) buttons |= 1<<GBTN_B; // red button
|
||||
if (msbtns & 4) buttons |= 1<<GBTN_START; // pen up/down
|
||||
} else if ((PicoIn.AHW & PAHW_SMS) || device == PICO_INPUT_MOUSE) {
|
||||
if (msbtns & 1) buttons |= 1<<GBTN_B; // as Sega Mouse
|
||||
if (msbtns & 2) buttons |= 1<<GBTN_START;
|
||||
if (msbtns & 4) buttons |= 1<<GBTN_C;
|
||||
} else if (device == PICO_INPUT_LIGHT_GUN || device == PICO_INPUT_JUSTIFIER) {
|
||||
if (msbtns & 1) buttons |= 1<<GBTN_A; // as Sega Menacer
|
||||
if (msbtns & 2) buttons |= 1<<GBTN_B;
|
||||
if (msbtns & 4) buttons |= 1<<GBTN_START;
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
void emu_update_input(void)
|
||||
{
|
||||
static int prev_events = 0;
|
||||
int actions[IN_BINDTYPE_COUNT] = { 0, };
|
||||
int actions_kbd[IN_BIND_LAST] = { 0, };
|
||||
int pl_actions[4];
|
||||
int count_kbd = 0, buttons = 0;
|
||||
int count_kbd = 0;
|
||||
int events, i = 0;
|
||||
|
||||
in_update(actions);
|
||||
@@ -1375,39 +1396,23 @@ void emu_update_input(void)
|
||||
events = actions[IN_BINDTYPE_EMU] & PEV_MASK;
|
||||
|
||||
// update mouse coordinates if there is an emulated mouse
|
||||
if (PicoIn.opt & POPT_EN_MOUSE) {
|
||||
if (currentConfig.EmuOpt & EOPT_MOUSE) {
|
||||
if (!grab_mode) {
|
||||
in_update_analog(0, 0, &PicoIn.mouse[0]);
|
||||
in_update_analog(0, 1, &PicoIn.mouse[1]);
|
||||
// scale mouse coordinates from -1024..1024 to 0..screen_w/h
|
||||
PicoIn.mouse[0] = (PicoIn.mouse[0]+1024) * g_screen_width /2048;
|
||||
PicoIn.mouse[1] = (PicoIn.mouse[1]+1024) * g_screen_height/2048;
|
||||
in_update_pointer(0, 0, &mouse_x);
|
||||
in_update_pointer(0, 1, &mouse_y);
|
||||
} else {
|
||||
int xrel, yrel;
|
||||
in_update_analog(0, 2, &xrel);
|
||||
in_update_analog(0, 3, &yrel);
|
||||
in_update_pointer(0, 2, &xrel);
|
||||
in_update_pointer(0, 3, &yrel);
|
||||
mouse_x += xrel, mouse_y += yrel;
|
||||
// scale mouse coordinates from -1024..1024 to 0..screen_w/h
|
||||
PicoIn.mouse[0] = (mouse_x+1024) * g_screen_width /2048;
|
||||
PicoIn.mouse[1] = (mouse_y+1024) * g_screen_height/2048;
|
||||
}
|
||||
// scale mouse coordinates from -1024..1024 to 0..screen_w/h
|
||||
PicoIn.mouse[0] = (mouse_x+1024) * 320/2048;
|
||||
PicoIn.mouse[1] = (mouse_y+1024) * 240/2048;
|
||||
|
||||
in_update_analog(0, -1, &i); // get mouse buttons, bit 2-0 = RML
|
||||
if (PicoIn.AHW & PAHW_PICO) {
|
||||
// TODO is maintaining 2 different mappings necessary?
|
||||
if (i & 1) buttons |= 1<<GBTN_C; // pen button
|
||||
if (i & 2) buttons |= 1<<GBTN_B; // red button
|
||||
if (i & 4) buttons |= 1<<GBTN_START; // pen up/down
|
||||
} else {
|
||||
if (i & 1) buttons |= 1<<GBTN_B; // as Sega Mouse
|
||||
if (i & 2) buttons |= 1<<GBTN_START;
|
||||
if (i & 4) buttons |= 1<<GBTN_C;
|
||||
}
|
||||
|
||||
if (currentConfig.input_dev0 == PICO_INPUT_MOUSE)
|
||||
pl_actions[0] |= buttons;
|
||||
if (currentConfig.input_dev1 == PICO_INPUT_MOUSE)
|
||||
pl_actions[1] |= buttons;
|
||||
in_update_pointer(0, -1, &i); // get mouse buttons, bit 2-0 = RML
|
||||
pl_actions[0] |= map_pointer_buttons(i, currentConfig.input_dev0);
|
||||
pl_actions[1] |= map_pointer_buttons(i, currentConfig.input_dev1);
|
||||
}
|
||||
|
||||
if (kbd_mode) {
|
||||
@@ -1619,8 +1624,10 @@ void emu_sound_wait(void)
|
||||
|
||||
static void emu_loop_prep(void)
|
||||
{
|
||||
static int pointer[] = { PICO_INPUT_MOUSE, PICO_INPUT_LIGHT_GUN, PICO_INPUT_JUSTIFIER };
|
||||
static int pal_old = -1;
|
||||
static int filter_old = -1;
|
||||
int i;
|
||||
|
||||
if (currentConfig.CPUclock != plat_target_cpu_clock_get())
|
||||
plat_target_cpu_clock_set(currentConfig.CPUclock);
|
||||
@@ -1646,12 +1653,14 @@ static void emu_loop_prep(void)
|
||||
if (((PicoIn.AHW & PAHW_PICO) || (PicoIn.AHW & PAHW_SC)) && currentConfig.keyboard)
|
||||
PicoIn.opt |= POPT_EN_KBD;
|
||||
|
||||
PicoIn.opt &= ~POPT_EN_MOUSE;
|
||||
if (!(PicoIn.AHW & PAHW_8BIT) && (currentConfig.input_dev0 == PICO_INPUT_MOUSE ||
|
||||
currentConfig.input_dev1 == PICO_INPUT_MOUSE)) {
|
||||
PicoIn.opt |= POPT_EN_MOUSE;
|
||||
plat_grab_cursor(grab_mode);
|
||||
}
|
||||
currentConfig.EmuOpt &= ~EOPT_MOUSE;
|
||||
for (i = 0; i < ARRAY_SIZE(pointer); i++)
|
||||
if ((currentConfig.input_dev0 == pointer[i] ||
|
||||
currentConfig.input_dev1 == pointer[i])) {
|
||||
currentConfig.EmuOpt |= EOPT_MOUSE;
|
||||
plat_grab_cursor(grab_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
pemu_loop_prep();
|
||||
}
|
||||
@@ -1828,7 +1837,7 @@ void emu_loop(void)
|
||||
}
|
||||
if (diff > target_frametime + vsync_delay) {
|
||||
// still too fast
|
||||
plat_wait_till_us(timestamp + (diff - target_frametime));
|
||||
plat_wait_till_us(timestamp + (diff - target_frametime - vsync_delay));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ extern int g_screen_ppitch; // pitch in pixels
|
||||
#define EOPT_WIZ_TEAR_FIX (1<<19)
|
||||
#define EOPT_EXT_FRMLIMIT (1<<20) // no internal frame limiter (limited by snd, etc)
|
||||
#define EOPT_PICO_PEN (1<<21)
|
||||
#define EOPT_MOUSE (1<<22)
|
||||
#define EOPT_GUN_CURSOR (1<<23)
|
||||
|
||||
enum {
|
||||
EOPT_SCALE_NONE = 0,
|
||||
@@ -94,6 +96,7 @@ typedef struct _currentConfig_t {
|
||||
int ghosting;
|
||||
int analog_deadzone;
|
||||
int keyboard;
|
||||
int gunx, guny;
|
||||
int msh2_khz;
|
||||
int ssh2_khz;
|
||||
int overclock_68k;
|
||||
@@ -196,6 +199,7 @@ void plat_finish(void);
|
||||
void plat_show_cursor(int on);
|
||||
int plat_grab_cursor(int on);
|
||||
int plat_has_wm(void);
|
||||
void plat_set_window(int w, int h);
|
||||
|
||||
/* used before things blocking for a while (these funcs redraw on return) */
|
||||
void plat_status_msg_busy_first(const char *msg);
|
||||
|
||||
+112
-21
@@ -544,13 +544,13 @@ int key_config_kbd_loop(int id, int keys)
|
||||
}
|
||||
|
||||
|
||||
const char *indev0_names[] = { "none", "3 button pad", "6 button pad", "Mouse", "Team player", "4 way play", NULL };
|
||||
const char *indev1_names[] = { "none", "3 button pad", "6 button pad", "Mouse", NULL };
|
||||
const char *indev_names[] = { "none", "3 button pad", "6 button pad", "Mouse", "Light gun", "Justifier", "Team player", "4 way play", NULL };
|
||||
|
||||
static char h_play12[55];
|
||||
static char h_kbd[55];
|
||||
static char h_play34[] = "Works only for Mega Drive/CD/32X games having\n"
|
||||
"support for Team player, 4 way play, or J-cart";
|
||||
static char h_gun[] = "Light Phaser for SMS, Menacer for Mega Drive\n"
|
||||
"note: Justifier can only be selected on input 2";
|
||||
|
||||
static char player[] = "Player 1";
|
||||
|
||||
@@ -561,6 +561,7 @@ static const char *mgn_nothing(int id, int *offs)
|
||||
|
||||
static int key_config_players(int id, int keys)
|
||||
{
|
||||
int x = me_id2offset(e_menu_keyconfig, MA_CTRL_PLAYER1);
|
||||
int pid = player[strlen(player)-1] - '0';
|
||||
|
||||
if (keys & PBTN_LEFT)
|
||||
@@ -569,7 +570,7 @@ static int key_config_players(int id, int keys)
|
||||
if (++pid > 4) pid = 1;
|
||||
|
||||
player[strlen(player)-1] = pid + '0';
|
||||
e_menu_keyconfig[0].help = (pid >= 3 ? h_play34 : h_play12);
|
||||
e_menu_keyconfig[x].help = (pid >= 3 ? h_play34 : h_play12);
|
||||
|
||||
if (keys & PBTN_MOK)
|
||||
key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, pid-1);
|
||||
@@ -579,10 +580,12 @@ static int key_config_players(int id, int keys)
|
||||
|
||||
static const char *mgn_keyboard(int id, int *offs)
|
||||
{
|
||||
static char *kbds[] = { "OFF", "virtual", "physical" };
|
||||
static const char *kbds[] = { "OFF", "virtual", "physical" };
|
||||
const char *kbd = kbds[currentConfig.keyboard];
|
||||
if (currentConfig.keyboard < 0 || currentConfig.keyboard > 2)
|
||||
return kbds[0];
|
||||
return kbds[currentConfig.keyboard];
|
||||
kbd = kbds[0];
|
||||
*offs = 3-strlen(kbd);
|
||||
return kbd;
|
||||
}
|
||||
|
||||
static int key_config_keyboard(int id, int keys)
|
||||
@@ -601,11 +604,85 @@ static int key_config_keyboard(int id, int keys)
|
||||
|
||||
currentConfig.keyboard = kid;
|
||||
|
||||
e_menu_keyconfig[2].help = (currentConfig.keyboard == 2 ? h_kbd : NULL);
|
||||
me_enable(e_menu_keyconfig, MA_CTRL_KEYBOARD, currentConfig.keyboard == 2);
|
||||
|
||||
if (keys & PBTN_MOK)
|
||||
if (currentConfig.keyboard == 2)
|
||||
key_config_kbd_loop(MA_CTRL_KEYBOARD, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *mgn_indev(int id, int *offs)
|
||||
{
|
||||
const char *dev;
|
||||
if (id == MA_OPT_INPUT_DEV0)
|
||||
dev = indev_names[currentConfig.input_dev0];
|
||||
else dev = indev_names[currentConfig.input_dev1];
|
||||
*offs = 3-strlen(dev);
|
||||
return dev;
|
||||
}
|
||||
|
||||
static int mh_indev(int id, int keys)
|
||||
{
|
||||
int is0 = id == MA_OPT_INPUT_DEV0;
|
||||
int *indev = is0 ? ¤tConfig.input_dev0 : ¤tConfig.input_dev1;
|
||||
int x = me_id2offset(e_menu_keyconfig, MA_OPT_INPUT_DEV0);
|
||||
|
||||
if (keys & PBTN_RIGHT) {
|
||||
(*indev) ++;
|
||||
#ifdef USE_SDL // TODO this info should come from platform!
|
||||
// Justifier only in pad port 2
|
||||
if (is0 && *indev == PICO_INPUT_JUSTIFIER)
|
||||
(*indev) ++;
|
||||
// Team/4way only in pad port 1
|
||||
if (!is0 && *indev > PICO_INPUT_JUSTIFIER)
|
||||
(*indev) --;
|
||||
#else
|
||||
while (*indev >= PICO_INPUT_MOUSE && *indev <= PICO_INPUT_JUSTIFIER)
|
||||
(*indev) ++;
|
||||
// Team/4way only in pad port 1
|
||||
while (!is0 && *indev >= PICO_INPUT_MOUSE)
|
||||
(*indev) --;
|
||||
#endif
|
||||
if (*indev >= PICO_INPUT_COUNT)
|
||||
(*indev) --;
|
||||
}
|
||||
if (keys & PBTN_LEFT) {
|
||||
(*indev) --;
|
||||
#ifdef USE_SDL // TODO this info should come from platform!
|
||||
if (is0 && *indev == PICO_INPUT_JUSTIFIER)
|
||||
(*indev) --;
|
||||
#else
|
||||
while (*indev >= PICO_INPUT_MOUSE && *indev <= PICO_INPUT_JUSTIFIER)
|
||||
(*indev) --;
|
||||
#endif
|
||||
if (*indev < 0)
|
||||
(*indev) ++;
|
||||
}
|
||||
if (*indev == PICO_INPUT_LIGHT_GUN)
|
||||
e_menu_keyconfig[x + !is0].help = h_gun;
|
||||
else e_menu_keyconfig[x + !is0].help = NULL;
|
||||
|
||||
x = currentConfig.input_dev0 == PICO_INPUT_LIGHT_GUN ||
|
||||
currentConfig.input_dev1 == PICO_INPUT_LIGHT_GUN ||
|
||||
currentConfig.input_dev1 == PICO_INPUT_JUSTIFIER;
|
||||
me_enable(e_menu_keyconfig, MA_CTRL_LIGHTGUN, x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static menu_entry e_menu_gunconfig[] =
|
||||
{
|
||||
mee_onoff ("Gun crosshair", MA_CTRL_GUN_CURSOR, currentConfig.EmuOpt, EOPT_GUN_CURSOR),
|
||||
mee_range ("Gun x offset", MA_CTRL_GUN_XOFFS,currentConfig.gunx, -50, 50),
|
||||
mee_range ("Gun y offset", MA_CTRL_GUN_YOFFS,currentConfig.guny, -50, 50),
|
||||
mee_end,
|
||||
};
|
||||
|
||||
static int gun_config_loop(int id, int keys)
|
||||
{
|
||||
static int sel = 0;
|
||||
|
||||
me_loop_d(e_menu_gunconfig, &sel, menu_draw_prep, NULL);
|
||||
|
||||
PicoIn.gunx = currentConfig.gunx;
|
||||
PicoIn.guny = currentConfig.guny;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -614,11 +691,13 @@ static menu_entry e_menu_keyconfig[] =
|
||||
{
|
||||
mee_cust_nosave(player, MA_CTRL_PLAYER1, key_config_players, mgn_nothing),
|
||||
mee_handler_id("Emulator hotkeys", MA_CTRL_EMU, key_config_loop_wrap),
|
||||
mee_cust_h ("Keyboard", MA_CTRL_KEYBOARD, key_config_keyboard, mgn_keyboard, h_kbd),
|
||||
mee_enum ("Input device 1", MA_OPT_INPUT_DEV0, currentConfig.input_dev0, indev0_names),
|
||||
mee_enum ("Input device 2", MA_OPT_INPUT_DEV1, currentConfig.input_dev1, indev1_names),
|
||||
mee_cust_h ("Keyboard", MA_OPT_KEYBOARD, key_config_keyboard, mgn_keyboard, NULL),
|
||||
mee_handler_id("Keys configuration",MA_CTRL_KEYBOARD, key_config_kbd_loop),
|
||||
mee_cust_h ("Input device 1", MA_OPT_INPUT_DEV0, mh_indev, mgn_indev, NULL),
|
||||
mee_cust_h ("Input device 2", MA_OPT_INPUT_DEV1, mh_indev, mgn_indev, NULL),
|
||||
mee_range ("Turbo rate", MA_CTRL_TURBO_RATE, currentConfig.turbo_rate, 1, 30),
|
||||
mee_range ("Analog deadzone", MA_CTRL_DEADZONE, currentConfig.analog_deadzone, 1, 99),
|
||||
mee_handler_id("Gun configuration", MA_CTRL_LIGHTGUN, gun_config_loop),
|
||||
mee_label (""),
|
||||
mee_label ("Input devices:"),
|
||||
mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
|
||||
@@ -645,11 +724,17 @@ static int menu_loop_keyconfig(int id, int keys)
|
||||
snprintf(l,sizeof(l),"%s", in_get_key_name(-1, -PBTN_LEFT));
|
||||
snprintf(r,sizeof(r),"%s", in_get_key_name(-1, -PBTN_RIGHT));
|
||||
snprintf(h_play12, sizeof(h_play12), "%s/%s - select, %s - configure", l, r, in_get_key_name(-1, -PBTN_MOK));
|
||||
snprintf(h_kbd, sizeof(h_kbd), "%s - configure", in_get_key_name(-1, -PBTN_MOK));
|
||||
|
||||
player[strlen(player)-1] = '1';
|
||||
e_menu_keyconfig[0].help = h_play12;
|
||||
e_menu_keyconfig[2].help = (currentConfig.keyboard == 2 ? h_kbd : NULL);
|
||||
x = me_id2offset(e_menu_keyconfig, MA_CTRL_PLAYER1);
|
||||
e_menu_keyconfig[x].help = h_play12;
|
||||
|
||||
me_enable(e_menu_keyconfig, MA_CTRL_KEYBOARD, currentConfig.keyboard == 2);
|
||||
|
||||
x = currentConfig.input_dev0 == PICO_INPUT_LIGHT_GUN ||
|
||||
currentConfig.input_dev1 == PICO_INPUT_LIGHT_GUN ||
|
||||
currentConfig.input_dev1 == PICO_INPUT_JUSTIFIER;
|
||||
me_enable(e_menu_keyconfig, MA_CTRL_LIGHTGUN, x);
|
||||
|
||||
me_loop_d(e_menu_keyconfig, &sel, menu_draw_prep, NULL);
|
||||
|
||||
@@ -1346,12 +1431,12 @@ static void draw_frame_credits(void)
|
||||
|
||||
static const char credits[] =
|
||||
"PicoDrive v" VERSION "\n"
|
||||
"(c) notaz, 2006-2013; irixxxx, 2018-2024\n\n"
|
||||
"(c) notaz, 2006-2013; irixxxx, 2018-2025\n\n"
|
||||
"Credits:\n"
|
||||
"fDave: initial code\n"
|
||||
#ifdef EMU_C68K
|
||||
" Cyclone 68000 core\n"
|
||||
"fDave: initial code, Cyclone 68000 core\n"
|
||||
#else
|
||||
"fDave: initial code\n"
|
||||
"Stef, Chui: FAME/C 68k core\n"
|
||||
#endif
|
||||
#ifdef _USE_DRZ80
|
||||
@@ -1365,13 +1450,18 @@ static const char credits[] =
|
||||
#ifdef __GP2X__
|
||||
"Squidge: mmuhack\n"
|
||||
"Dzz: ARM940 sample\n"
|
||||
#endif
|
||||
#if defined __PSP__ || defined __PS2__
|
||||
"fjtrujy: PSP and PS2 platform code\n"
|
||||
#endif
|
||||
"\n"
|
||||
"special thanks (for docs, ideas):\n"
|
||||
" Charles MacDonald, Haze,\n"
|
||||
" Stephane Dallongeville,\n"
|
||||
" Lordus, Exophase, Rokas,\n"
|
||||
" Eke, Nemesis, Tasco Deluxe";
|
||||
" Eke, Nemesis, Tasco Deluxe,\n"
|
||||
"special thanks (for testing):\n"
|
||||
" DrUm78, jdgleaver, freq_mod";
|
||||
|
||||
static void menu_main_draw_status(void)
|
||||
{
|
||||
@@ -1756,6 +1846,7 @@ static menu_entry *e_menu_table[] =
|
||||
e_menu_sms_options,
|
||||
#endif
|
||||
e_menu_keyconfig,
|
||||
e_menu_gunconfig,
|
||||
e_menu_hidden,
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ typedef enum
|
||||
MA_OPT_ARM940_SOUND,
|
||||
MA_OPT_INPUT_DEV0,
|
||||
MA_OPT_INPUT_DEV1,
|
||||
MA_OPT_KEYBOARD,
|
||||
MA_OPT_REGION,
|
||||
MA_OPT_CONFIRM_STATES,
|
||||
MA_OPT_SAVE_SLOT,
|
||||
@@ -110,6 +111,10 @@ typedef enum
|
||||
MA_CTRL_PLAYER4,
|
||||
MA_CTRL_EMU,
|
||||
MA_CTRL_KEYBOARD,
|
||||
MA_CTRL_LIGHTGUN,
|
||||
MA_CTRL_GUN_CURSOR,
|
||||
MA_CTRL_GUN_XOFFS,
|
||||
MA_CTRL_GUN_YOFFS,
|
||||
MA_CTRL_TURBO_RATE,
|
||||
MA_CTRL_DEADZONE,
|
||||
MA_CTRL_DEV_FIRST,
|
||||
@@ -117,6 +122,8 @@ typedef enum
|
||||
MA_CTRL_DONE,
|
||||
} menu_id;
|
||||
|
||||
extern const char *indev_names[];
|
||||
|
||||
void menu_init(void);
|
||||
void menu_loop(void);
|
||||
int menu_loop_tray(void);
|
||||
|
||||
+59
-62
@@ -26,6 +26,8 @@ static void *shadow_fb;
|
||||
static int shadow_size;
|
||||
static struct area { int w, h; } area;
|
||||
|
||||
static int resized;
|
||||
|
||||
static struct in_pdata in_sdl_platform_data;
|
||||
|
||||
static int hide_cursor;
|
||||
@@ -68,7 +70,8 @@ int plat_target_init(void)
|
||||
FILE *f = fopen("/proc/device-tree/compatible", "r");
|
||||
if (f) {
|
||||
char buf[10];
|
||||
int c = fread(buf, 1, sizeof(buf), f);
|
||||
int c = fread(buf, 1, sizeof(buf)-1, f);
|
||||
buf[c] = '\0';
|
||||
if (strncmp(buf, "gcw,", 4) == 0)
|
||||
plat_device = "gcw0";
|
||||
}
|
||||
@@ -204,9 +207,15 @@ static int clear_buf_cnt, clear_stat_cnt;
|
||||
|
||||
static void resize_buffers(void)
|
||||
{
|
||||
// update pitch as it is needed for resizing, and the menu bg scaler
|
||||
if (plat_sdl_overlay || plat_sdl_gl_active)
|
||||
g_menuscreen_pp = g_menuscreen_w;
|
||||
else
|
||||
g_menuscreen_pp = plat_sdl_screen->pitch / 2;
|
||||
|
||||
// make sure the shadow buffers are big enough in case of resize
|
||||
if (shadow_size < g_menuscreen_w * g_menuscreen_h * 2) {
|
||||
shadow_size = g_menuscreen_w * g_menuscreen_h * 2;
|
||||
if (shadow_size < g_menuscreen_pp * g_menuscreen_h * 2) {
|
||||
shadow_size = g_menuscreen_pp * g_menuscreen_h * 2;
|
||||
shadow_fb = realloc(shadow_fb, shadow_size);
|
||||
g_menubg_ptr = realloc(g_menubg_ptr, shadow_size);
|
||||
}
|
||||
@@ -214,7 +223,8 @@ static void resize_buffers(void)
|
||||
|
||||
void plat_video_set_size(int w, int h)
|
||||
{
|
||||
if ((plat_sdl_overlay || plat_sdl_gl_active) && w <= 320 && h <= 240) {
|
||||
if ((plat_sdl_overlay || plat_sdl_gl_active || !plat_sdl_is_windowed()) &&
|
||||
w <= 320 && h <= 240) {
|
||||
// scale to the window, but mind aspect ratio (scaled to 4:3):
|
||||
// w *= win_aspect / 4:3_aspect or h *= 4:3_aspect / win_aspect
|
||||
if (g_menuscreen_w * 3/4 >= g_menuscreen_h)
|
||||
@@ -233,6 +243,7 @@ void plat_video_set_size(int w, int h)
|
||||
area = (struct area) { g_screen_width,g_screen_height };
|
||||
plat_sdl_change_video_mode(g_screen_width, g_screen_height, 0);
|
||||
}
|
||||
plat_sdl_gl_scaling(currentConfig.filter);
|
||||
}
|
||||
if (plat_sdl_overlay || plat_sdl_gl_active) {
|
||||
// use shadow buffer for overlays
|
||||
@@ -268,8 +279,6 @@ void plat_video_set_shadow(int w, int h)
|
||||
|
||||
void plat_video_flip(void)
|
||||
{
|
||||
resize_buffers();
|
||||
|
||||
if (plat_sdl_overlay != NULL) {
|
||||
SDL_Rect dstrect =
|
||||
{ 0, 0, plat_sdl_screen->w, plat_sdl_screen->h };
|
||||
@@ -296,19 +305,7 @@ void plat_video_flip(void)
|
||||
SDL_UnlockSurface(plat_sdl_screen);
|
||||
SDL_Flip(plat_sdl_screen);
|
||||
|
||||
// take over resized settings for the physical SDL surface
|
||||
if ((plat_sdl_screen->w != g_menuscreen_w ||
|
||||
plat_sdl_screen->h != g_menuscreen_h) && plat_sdl_is_windowed() &&
|
||||
SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) {
|
||||
plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, -1);
|
||||
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
||||
g_menuscreen_pp = plat_sdl_screen->pitch/2;
|
||||
|
||||
// force upper layer to use new dimensions
|
||||
plat_video_set_shadow(g_screen_width, g_screen_height);
|
||||
plat_video_set_buffer(g_screen_ptr);
|
||||
rendstatus_old = -1;
|
||||
} else if (!copy) {
|
||||
if (!copy) {
|
||||
g_screen_ppitch = plat_sdl_screen->pitch/2;
|
||||
g_screen_ptr = plat_sdl_screen->pixels;
|
||||
plat_video_set_buffer(g_screen_ptr);
|
||||
@@ -323,13 +320,20 @@ void plat_video_flip(void)
|
||||
}
|
||||
}
|
||||
|
||||
// for overlay/gl modes buffer ptr may change on resize
|
||||
if ((plat_sdl_overlay || plat_sdl_gl_active) &&
|
||||
(g_screen_ptr != shadow_fb || g_screen_ppitch != g_screen_width)) {
|
||||
g_screen_ppitch = g_screen_width;
|
||||
g_screen_ptr = shadow_fb;
|
||||
// take over resized settings for the physical SDL surface
|
||||
if (resized && plat_sdl_is_windowed() &&
|
||||
SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) {
|
||||
plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, 1);
|
||||
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
||||
resize_buffers();
|
||||
|
||||
// force upper layer to use new dimensions
|
||||
plat_video_set_shadow(g_screen_width, g_screen_height);
|
||||
plat_video_set_buffer(g_screen_ptr);
|
||||
rendstatus_old = -1;
|
||||
resized = 0;
|
||||
}
|
||||
|
||||
if (clear_stat_cnt) {
|
||||
unsigned short *d = (unsigned short *)g_screen_ptr + g_screen_ppitch * g_screen_height;
|
||||
int l = g_screen_ppitch * 8;
|
||||
@@ -367,17 +371,12 @@ void plat_video_menu_update(void)
|
||||
int w, h;
|
||||
do {
|
||||
w = g_menuscreen_w, h = g_menuscreen_h;
|
||||
plat_sdl_change_video_mode(w, h, -1);
|
||||
plat_sdl_change_video_mode(w, h, 1);
|
||||
} while (w != g_menuscreen_w || h != g_menuscreen_h);
|
||||
plat_sdl_gl_scaling(currentConfig.filter);
|
||||
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
||||
}
|
||||
|
||||
// update pitch as it is needed by the menu bg scaler
|
||||
if (plat_sdl_overlay || plat_sdl_gl_active)
|
||||
g_menuscreen_pp = g_menuscreen_w;
|
||||
else
|
||||
g_menuscreen_pp = plat_sdl_screen->pitch / 2;
|
||||
|
||||
resize_buffers();
|
||||
}
|
||||
|
||||
@@ -385,6 +384,7 @@ void plat_video_menu_enter(int is_rom_loaded)
|
||||
{
|
||||
if (SDL_MUSTLOCK(plat_sdl_screen))
|
||||
SDL_UnlockSurface(plat_sdl_screen);
|
||||
plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, 1);
|
||||
}
|
||||
|
||||
void plat_video_menu_begin(void)
|
||||
@@ -437,8 +437,7 @@ void plat_video_loop_prepare(void)
|
||||
|
||||
// take over any new vout settings
|
||||
area = (struct area) { 0, 0 };
|
||||
plat_sdl_change_video_mode(0, 0, 0);
|
||||
resize_buffers();
|
||||
// plat_sdl_change_video_mode(0, 0, 0);
|
||||
|
||||
// switch over to scaled output if available, but keep the aspect ratio
|
||||
if (plat_sdl_overlay || plat_sdl_gl_active)
|
||||
@@ -470,38 +469,25 @@ int plat_has_wm(void)
|
||||
return plat_sdl_is_windowed();
|
||||
}
|
||||
|
||||
void plat_set_window(int w, int h)
|
||||
{
|
||||
g_menuscreen_w = w;
|
||||
g_menuscreen_h = h;
|
||||
resized = 1;
|
||||
}
|
||||
|
||||
static void plat_sdl_resize(int w, int h)
|
||||
{
|
||||
// take over new settings
|
||||
#if defined(__OPENDINGUX__)
|
||||
if (currentConfig.vscaling != EOPT_SCALE_HW &&
|
||||
plat_sdl_screen->w == 320 && plat_sdl_screen->h == 480) {
|
||||
g_menuscreen_h = 240;
|
||||
g_menuscreen_w = 320;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
g_menuscreen_h = plat_sdl_screen->h;
|
||||
g_menuscreen_w = plat_sdl_screen->w;
|
||||
#if 0 // auto resizing may be nice, but creates problems on some SDL platforms
|
||||
if (!plat_sdl_overlay && !plat_sdl_gl_active &&
|
||||
plat_sdl_is_windowed() && !plat_sdl_is_fullscreen()) {
|
||||
// in SDL window mode, adapt window to integer scaling
|
||||
if (g_menuscreen_w * 3/4 >= g_menuscreen_h)
|
||||
g_menuscreen_w = g_menuscreen_h * 4/3;
|
||||
else
|
||||
g_menuscreen_h = g_menuscreen_w * 3/4;
|
||||
g_menuscreen_w = g_menuscreen_w/320*320;
|
||||
g_menuscreen_h = g_menuscreen_h/240*240;
|
||||
if (g_menuscreen_w == 0) {
|
||||
g_menuscreen_w = 320;
|
||||
g_menuscreen_h = 240;
|
||||
}
|
||||
}
|
||||
if (currentConfig.vscaling != EOPT_SCALE_HW && w == 320 && h == 480)
|
||||
h = 240;
|
||||
#endif
|
||||
if (g_menuscreen_w != (w & ~3) || g_menuscreen_h != (h & ~3)){
|
||||
g_menuscreen_h = h & ~3;
|
||||
g_menuscreen_w = w & ~3;
|
||||
resized = 1;
|
||||
}
|
||||
|
||||
rendstatus_old = -1;
|
||||
}
|
||||
|
||||
static void plat_sdl_quit(void)
|
||||
@@ -510,6 +496,17 @@ static void plat_sdl_quit(void)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void plat_sdl_handler(void *event_)
|
||||
{
|
||||
SDL_Event *event = event_;
|
||||
|
||||
if (event->type == SDL_VIDEORESIZE)
|
||||
// handle resize here to avoid the internal plat_sdl_change_video_mode
|
||||
plat_sdl_resize(event->resize.w, event->resize.h);
|
||||
else
|
||||
plat_sdl_event_handler(event_);
|
||||
}
|
||||
|
||||
void plat_init(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -530,7 +527,7 @@ void plat_init(void)
|
||||
}
|
||||
|
||||
plat_sdl_quit_cb = plat_sdl_quit;
|
||||
plat_sdl_resize_cb = plat_sdl_resize;
|
||||
// plat_sdl_resize_cb = plat_sdl_resize;
|
||||
|
||||
SDL_WM_SetCaption("PicoDrive " VERSION, NULL);
|
||||
|
||||
@@ -561,11 +558,11 @@ void plat_init(void)
|
||||
in_sdl_platform_data.joy_map = in_sdl_joy_map,
|
||||
in_sdl_platform_data.key_names = in_sdl_key_names,
|
||||
in_sdl_platform_data.kbd_map = in_sdl_kbd_map,
|
||||
in_sdl_init(&in_sdl_platform_data, plat_sdl_event_handler);
|
||||
in_sdl_init(&in_sdl_platform_data, plat_sdl_handler);
|
||||
in_probe();
|
||||
|
||||
// create an artificial resize event to initialize mouse scaling
|
||||
SDL_Event ev;
|
||||
static SDL_Event ev;
|
||||
ev.resize.type = SDL_VIDEORESIZE;
|
||||
ev.resize.w = g_menuscreen_w;
|
||||
ev.resize.h = g_menuscreen_h;
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define VERSION "2.04" REVISION
|
||||
#define VERSION "2.05" REVISION
|
||||
+1
-1
Submodule platform/libpicofe updated: c3031dbfda...cd58299620.
+11
-1
@@ -228,6 +228,16 @@ void pemu_finalize_frame(const char *fps, const char *notice)
|
||||
draw_pico_ptr();
|
||||
}
|
||||
|
||||
// TODO correct ptr position for hard/soft/no scaling?
|
||||
#define is_lightgun(d) (d == PICO_INPUT_LIGHT_GUN || d == PICO_INPUT_JUSTIFIER)
|
||||
if ((currentConfig.EmuOpt & EOPT_GUN_CURSOR) &&
|
||||
(is_lightgun(currentConfig.input_dev0) || is_lightgun(currentConfig.input_dev1))) {
|
||||
pico_inp_mode = 2;
|
||||
pico_pen_x = PicoPicohw.pen_pos[0] = PicoIn.mouseInt[0];
|
||||
pico_pen_y = PicoPicohw.pen_pos[1] = PicoIn.mouseInt[1];
|
||||
draw_pico_ptr();
|
||||
}
|
||||
|
||||
// draw virtual keyboard on display
|
||||
if (kbd_mode && currentConfig.keyboard == 1 && vkbd)
|
||||
vkbd_draw(vkbd);
|
||||
@@ -486,7 +496,7 @@ void pemu_loop_prep(void)
|
||||
{
|
||||
apply_renderer();
|
||||
plat_video_clear_buffers();
|
||||
plat_show_cursor(!(PicoIn.opt & POPT_EN_MOUSE));
|
||||
plat_show_cursor(!(currentConfig.EmuOpt & EOPT_MOUSE));
|
||||
}
|
||||
|
||||
void pemu_loop_end(void)
|
||||
|
||||
@@ -29,8 +29,7 @@ static int mh_windowsize(int id, int keys)
|
||||
if (keys & PBTN_LEFT ) scale--;
|
||||
if (scale <= 0) scale = 1;
|
||||
if (scale > 20) scale = 20;
|
||||
g_menuscreen_w = scale*320;
|
||||
g_menuscreen_h = scale*240;
|
||||
plat_set_window(scale*320, scale*240);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# NOTES:
|
||||
## All variable values should enclosed within double quotes: "<value>"
|
||||
## CONFIGURATION FILE for `gm2xpkg` script version:
|
||||
PKGVER="0.7"
|
||||
PKGVER="0.9"
|
||||
|
||||
# EXEC commands (set to "1" anyone for desired outcome), you can instead use [OPTIONS] of `gm2xpkg`:
|
||||
PACKAGE=""
|
||||
|
||||
@@ -30,6 +30,7 @@ rel: ../../picodrive PicoDrive.sh picorestore \
|
||||
rm -rf out
|
||||
mkdir out
|
||||
cp -r $^ out/
|
||||
mv out/README.md out/README
|
||||
$(PND_MAKE) -p PicoDrive_$(VER).pnd -d out -x out/PicoDrive.pxml -i out/PicoDrive.png -c
|
||||
|
||||
FORCE:
|
||||
|
||||
@@ -7,7 +7,7 @@ test -f $verfile
|
||||
major=`head -n 1 $verfile | sed 's/[^0-9]*\([0-9]*\)\.\([0-9]*\).*/\1/g'`
|
||||
minor=`head -n 1 $verfile | sed 's/[^0-9]*\([0-9]*\)\.\([0-9]*\).*/\2/g'`
|
||||
# lame, I know..
|
||||
build=`git describe HEAD | grep -- - | sed -e 's/.*\-\(.*\)\-.*/\1/'`
|
||||
build=`git describe --tags HEAD | grep -- - | sed -e 's/.*\-\(.*\)\-.*/\1/'`
|
||||
test -n "$build" && build_post="-$build"
|
||||
test -n "$build" || build=0
|
||||
|
||||
|
||||
+1
-1
@@ -375,7 +375,7 @@ static void sound_deinit(void)
|
||||
#define is_16bit_mode() \
|
||||
(currentConfig.renderer == RT_16BIT || (PicoIn.AHW & PAHW_32X) || is_bg_frame)
|
||||
|
||||
static int vsync_handler(void)
|
||||
static int vsync_handler(int cause)
|
||||
{
|
||||
iSignalSema(vsync_sema_id);
|
||||
if (sound_stopped)
|
||||
|
||||
+65
-19
@@ -126,31 +126,68 @@ int scandir(const char *dir, struct dirent ***namelist, int (*select)(const stru
|
||||
char path[MAX_PATH];
|
||||
struct dirent **entries = NULL;
|
||||
size_t count = 0;
|
||||
int i;
|
||||
|
||||
snprintf(path, sizeof(path), "%s\\*", dir);
|
||||
handle = FindFirstFile(path, &info);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
if (dir == NULL || *dir == '\0') {
|
||||
// on top level give a list of available drives
|
||||
DWORD drives = GetLogicalDrives();
|
||||
if (drives == 0) return -1;
|
||||
|
||||
do {
|
||||
struct dirent *entry = (struct dirent *)malloc(sizeof(struct dirent));
|
||||
if (!entry) {
|
||||
free(entries);
|
||||
FindClose(handle);
|
||||
return -1;
|
||||
entries = malloc(sizeof(*entries) * 26);
|
||||
if (!entries) return -1;
|
||||
|
||||
for (i = 0; i < 26; ++i) {
|
||||
int type;
|
||||
|
||||
if (!(drives & (1 << i)))
|
||||
continue;
|
||||
|
||||
snprintf(path, MAX_PATH, "%c:\\", 'A' + i);
|
||||
type = GetDriveTypeA(path);
|
||||
if (type == DRIVE_NO_ROOT_DIR || type == DRIVE_UNKNOWN)
|
||||
continue;
|
||||
|
||||
struct dirent *entry = (struct dirent *)malloc(sizeof(struct dirent));
|
||||
if (!entry)
|
||||
goto err;
|
||||
|
||||
snprintf(entry->d_name, MAX_PATH, "%c:", 'A' + i);
|
||||
entry->d_type = DT_DIR;
|
||||
|
||||
if (!select || select(entry))
|
||||
entries[count++] = entry;
|
||||
else
|
||||
free(entry);
|
||||
}
|
||||
} else {
|
||||
|
||||
strcpy(entry->d_name, info.cFileName);
|
||||
entry->d_type = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
|
||||
// create a list of objects in the directory
|
||||
snprintf(path, sizeof(path), "%s\\*", dir);
|
||||
handle = FindFirstFile(path, &info);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
if (!select || select(entry)) {
|
||||
entries = realloc(entries, (count + 1) * sizeof(struct dirent *));
|
||||
entries[count++] = entry;
|
||||
} else
|
||||
free(entry);
|
||||
} while (FindNextFile(handle, &info));
|
||||
do {
|
||||
struct dirent *entry = (struct dirent *)malloc(sizeof(struct dirent));
|
||||
if (!entry) {
|
||||
FindClose(handle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
FindClose(handle);
|
||||
strcpy(entry->d_name, info.cFileName);
|
||||
entry->d_type = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
|
||||
|
||||
if (!select || select(entry)) {
|
||||
struct dirent **_entries = realloc(entries, (count + 1) * sizeof(struct dirent *));
|
||||
if (!_entries) goto err;
|
||||
entries = _entries;
|
||||
entries[count++] = entry;
|
||||
} else
|
||||
free(entry);
|
||||
} while (FindNextFile(handle, &info));
|
||||
|
||||
FindClose(handle);
|
||||
}
|
||||
|
||||
// Sort entries if a comparison function is provided
|
||||
if (compar) {
|
||||
@@ -159,6 +196,15 @@ int scandir(const char *dir, struct dirent ***namelist, int (*select)(const stru
|
||||
|
||||
*namelist = entries;
|
||||
return count;
|
||||
|
||||
err:
|
||||
if (entries) {
|
||||
while (count > 0)
|
||||
if (entries[--count])
|
||||
free(entries[count]);
|
||||
free(entries);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int alphasort(const struct dirent **a, const struct dirent **b) {
|
||||
|
||||
Reference in new issue
Block a user