Merge remote-tracking branch 'irixxxx/master' into master

This commit is contained in:
kub committed 2025-05-31 16:35:35 +02:00
commit 975cdafb61
40 files changed
+1087 -381

No files matched your search

+1
View File
@@ -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
+218
View File
@@ -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
+4 -14
View File
@@ -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
+12 -3
View File
@@ -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
Vendored
+41 -28
View File
@@ -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
-1
View File
@@ -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);
-1
View File
@@ -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
View File
@@ -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);
-1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+35 -1
View File
@@ -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
-------------------------
+13 -1
View File
@@ -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
View File
@@ -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));
}
}
+4
View File
@@ -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
View File
@@ -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 ? &currentConfig.input_dev0 : &currentConfig.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,
};
+7
View File
@@ -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
View File
@@ -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
View File
@@ -1 +1 @@
#define VERSION "2.04" REVISION
#define VERSION "2.05" REVISION
+11 -1
View File
@@ -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)
+1 -2
View File
@@ -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 -1
View File
@@ -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=""
+1
View File
@@ -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:
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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) {